merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Sat, 30 Jul 2016 16:20:57 +0200
changeset 349490 e5859dfe0bcbd40f4e33f4a633f73ea3473a7849
parent 349354 c3565c8b1cdb575db1c80c7791984a6490598b84 (current diff)
parent 349489 974cfc29c1e30561d40882c051f07724eff99491 (diff)
child 349557 9e48f74f44c387f4601ba0aab3d651703183848f
child 349585 5b500a963fd364f41d32422d5249233a124f85e7
child 349592 1b848daf2102cb0636c9475ae424836abef5ca7f
push id1230
push userjlund@mozilla.com
push dateMon, 31 Oct 2016 18:13:35 +0000
treeherdermozilla-release@5e06e3766db2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone50.0a1
first release with
nightly linux32
e5859dfe0bcb / 50.0a1 / 20160731030203 / files
nightly linux64
e5859dfe0bcb / 50.0a1 / 20160731030203 / files
nightly mac
e5859dfe0bcb / 50.0a1 / 20160731030203 / files
nightly win32
e5859dfe0bcb / 50.0a1 / 20160731030203 / files
nightly win64
e5859dfe0bcb / 50.0a1 / 20160731030203 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
CLOBBER
dom/events/test/pointerevents/test_pointerevent_touch-action-auto-css_touch-manual.html
dom/events/test/pointerevents/test_pointerevent_touch-action-button-test_touch-manual.html
dom/events/test/pointerevents/test_pointerevent_touch-action-illegal.html
dom/events/test/pointerevents/test_pointerevent_touch-action-inherit_child-auto-child-none_touch-manual.html
dom/events/test/pointerevents/test_pointerevent_touch-action-inherit_child-none_touch-manual.html
dom/events/test/pointerevents/test_pointerevent_touch-action-inherit_child-pan-x-child-pan-x_touch-manual.html
dom/events/test/pointerevents/test_pointerevent_touch-action-inherit_child-pan-x-child-pan-y_touch-manual.html
dom/events/test/pointerevents/test_pointerevent_touch-action-inherit_highest-parent-none_touch-manual.html
dom/events/test/pointerevents/test_pointerevent_touch-action-inherit_parent-none_touch-manual.html
dom/events/test/pointerevents/test_pointerevent_touch-action-keyboard-manual.html
dom/events/test/pointerevents/test_pointerevent_touch-action-mouse-manual.html
dom/events/test/pointerevents/test_pointerevent_touch-action-none-css_touch-manual.html
dom/events/test/pointerevents/test_pointerevent_touch-action-pan-x-css_touch-manual.html
dom/events/test/pointerevents/test_pointerevent_touch-action-pan-x-pan-y-pan-y_touch-manual.html
dom/events/test/pointerevents/test_pointerevent_touch-action-pan-x-pan-y_touch-manual.html
dom/events/test/pointerevents/test_pointerevent_touch-action-pan-y-css_touch-manual.html
dom/events/test/pointerevents/test_pointerevent_touch-action-span-test_touch-manual.html
dom/events/test/pointerevents/test_pointerevent_touch-action-svg-test_touch-manual.html
dom/events/test/pointerevents/test_pointerevent_touch-action-table-test_touch-manual.html
dom/events/test/pointerevents/test_pointerevent_touch-action-verification.html
layout/base/nsCSSRendering.cpp
mobile/android/base/java/org/mozilla/gecko/AppNotificationClient.java
mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
mobile/android/base/java/org/mozilla/gecko/NotificationClient.java
mobile/android/base/java/org/mozilla/gecko/NotificationHandler.java
mobile/android/base/java/org/mozilla/gecko/NotificationHelper.java
mobile/android/base/java/org/mozilla/gecko/NotificationReceiver.java
mobile/android/base/java/org/mozilla/gecko/NotificationService.java
mobile/android/base/java/org/mozilla/gecko/ServiceNotificationClient.java
testing/web-platform/meta/cors/allow-headers.htm.ini
testing/web-platform/meta/cors/origin.htm.ini
testing/web-platform/meta/cors/remote-origin.htm.ini
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,9 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Bug 1287827 - Clobber needed because this patch removes files, second landing.
+Bug 1287827 - Clobber needed because this patch removes files, second landing AND Bug 1272693 - Clobber required to rebuild NSS.
--- a/accessible/xpcom/xpcAccessibleHyperText.cpp
+++ b/accessible/xpcom/xpcAccessibleHyperText.cpp
@@ -36,158 +36,225 @@ NS_IMPL_RELEASE_INHERITED(xpcAccessibleH
 // nsIAccessibleText
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::GetCharacterCount(int32_t* aCharacterCount)
 {
   NS_ENSURE_ARG_POINTER(aCharacterCount);
   *aCharacterCount = 0;
 
-  if (!Intl())
+  if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
-  *aCharacterCount = Intl()->CharacterCount();
+  if (mIntl.IsAccessible()) {
+    *aCharacterCount = Intl()->CharacterCount();
+  } else {
+    *aCharacterCount = mIntl.AsProxy()->CharacterCount();
+  }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::GetText(int32_t aStartOffset, int32_t aEndOffset,
                                 nsAString& aText)
 {
   aText.Truncate();
 
-  if (!Intl())
+  if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
-  Intl()->TextSubstring(aStartOffset, aEndOffset, aText);
+  if (mIntl.IsAccessible()) {
+    Intl()->TextSubstring(aStartOffset, aEndOffset, aText);
+  } else {
+    nsString text;
+    mIntl.AsProxy()->TextSubstring(aStartOffset, aEndOffset, text);
+    aText = text;
+  }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::GetTextBeforeOffset(int32_t aOffset,
                                             AccessibleTextBoundary aBoundaryType,
                                             int32_t* aStartOffset,
                                             int32_t* aEndOffset,
                                             nsAString& aText)
 {
   NS_ENSURE_ARG_POINTER(aStartOffset);
   NS_ENSURE_ARG_POINTER(aEndOffset);
   *aStartOffset = *aEndOffset = 0;
   aText.Truncate();
 
-  if (!Intl())
+  if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
-  Intl()->TextBeforeOffset(aOffset, aBoundaryType, aStartOffset, aEndOffset, aText);
+  if (mIntl.IsAccessible()) {
+    Intl()->TextBeforeOffset(aOffset, aBoundaryType, aStartOffset, aEndOffset, 
+                             aText);
+  } else {
+    nsString text;
+    mIntl.AsProxy()->GetTextBeforeOffset(aOffset, aBoundaryType, text,
+                                         aStartOffset, aEndOffset);
+    aText = text;
+  }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::GetTextAtOffset(int32_t aOffset,
                                         AccessibleTextBoundary aBoundaryType,
                                         int32_t* aStartOffset,
                                         int32_t* aEndOffset, nsAString& aText)
 {
   NS_ENSURE_ARG_POINTER(aStartOffset);
   NS_ENSURE_ARG_POINTER(aEndOffset);
   *aStartOffset = *aEndOffset = 0;
   aText.Truncate();
 
-  if (!Intl())
+  if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
-  Intl()->TextAtOffset(aOffset, aBoundaryType, aStartOffset, aEndOffset, aText);
+  if (mIntl.IsAccessible()) {
+    Intl()->TextAtOffset(aOffset, aBoundaryType, aStartOffset, aEndOffset, 
+                         aText);
+  } else {
+    nsString text;
+    mIntl.AsProxy()->GetTextAtOffset(aOffset, aBoundaryType, text, 
+                                     aStartOffset, aEndOffset);
+    aText = text;
+  }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::GetTextAfterOffset(int32_t aOffset,
                                            AccessibleTextBoundary aBoundaryType,
                                            int32_t* aStartOffset,
                                            int32_t* aEndOffset, nsAString& aText)
 {
   NS_ENSURE_ARG_POINTER(aStartOffset);
   NS_ENSURE_ARG_POINTER(aEndOffset);
   *aStartOffset = *aEndOffset = 0;
   aText.Truncate();
 
-  if (!Intl())
+  if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
-  Intl()->TextAfterOffset(aOffset, aBoundaryType, aStartOffset, aEndOffset, aText);
+  if (mIntl.IsAccessible()) {
+    Intl()->TextAfterOffset(aOffset, aBoundaryType, aStartOffset, aEndOffset, 
+                            aText);
+  } else {
+    nsString text;
+    mIntl.AsProxy()->GetTextAfterOffset(aOffset, aBoundaryType, text, 
+                                        aStartOffset, aEndOffset);
+    aText = text;
+  }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::GetCharacterAtOffset(int32_t aOffset,
                                              char16_t* aCharacter)
 {
   NS_ENSURE_ARG_POINTER(aCharacter);
   *aCharacter = L'\0';
 
-  if (!Intl())
+  if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
-  *aCharacter = Intl()->CharAt(aOffset);
+  if (mIntl.IsAccessible()) {
+    *aCharacter = Intl()->CharAt(aOffset);
+  } else {
+    *aCharacter = mIntl.AsProxy()->CharAt(aOffset);
+  }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::GetTextAttributes(bool aIncludeDefAttrs,
                                           int32_t aOffset,
                                           int32_t* aStartOffset,
                                           int32_t* aEndOffset,
                                           nsIPersistentProperties** aAttributes)
 {
   NS_ENSURE_ARG_POINTER(aStartOffset);
   NS_ENSURE_ARG_POINTER(aEndOffset);
   NS_ENSURE_ARG_POINTER(aAttributes);
   *aStartOffset = *aEndOffset = 0;
   *aAttributes = nullptr;
 
-  if (!Intl())
+  if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
-  nsCOMPtr<nsIPersistentProperties> attrs =
-   Intl()->TextAttributes(aIncludeDefAttrs, aOffset, aStartOffset, aEndOffset);
-  attrs.swap(*aAttributes);
+  nsCOMPtr<nsIPersistentProperties> props;
+  if (mIntl.IsAccessible()) {
+    props = Intl()->TextAttributes(aIncludeDefAttrs, aOffset, aStartOffset,
+                                   aEndOffset);
+  } else {
+    AutoTArray<Attribute, 10> attrs;
+    mIntl.AsProxy()->TextAttributes(aIncludeDefAttrs, aOffset, &attrs,
+        aStartOffset, aEndOffset);
+    uint32_t attrCount = attrs.Length();
+    nsAutoString unused;
+    for (uint32_t i = 0; i < attrCount; i++) {
+      props->SetStringProperty(attrs[i].Name(), attrs[i].Value(), unused);
+    }
+  }
+  props.forget(aAttributes);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::GetDefaultTextAttributes(nsIPersistentProperties** aAttributes)
 {
   NS_ENSURE_ARG_POINTER(aAttributes);
   *aAttributes = nullptr;
 
-  if (!Intl())
+  if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
-  nsCOMPtr<nsIPersistentProperties> attrs = Intl()->DefaultTextAttributes();
-  attrs.swap(*aAttributes);
+  nsCOMPtr<nsIPersistentProperties> props;
+  if (mIntl.IsAccessible()) {
+    props = Intl()->DefaultTextAttributes();
+  } else {
+    AutoTArray<Attribute, 10> attrs;
+    mIntl.AsProxy()->DefaultTextAttributes(&attrs);
+    uint32_t attrCount = attrs.Length();
+    nsAutoString unused;
+    for (uint32_t i = 0; i < attrCount; i++) {
+      props->SetStringProperty(attrs[i].Name(), attrs[i].Value(), unused);
+    }
+  }
+  props.forget(aAttributes);
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::GetCharacterExtents(int32_t aOffset,
                                             int32_t* aX, int32_t* aY,
                                             int32_t* aWidth, int32_t* aHeight,
                                             uint32_t aCoordType)
 {
   NS_ENSURE_ARG_POINTER(aX);
   NS_ENSURE_ARG_POINTER(aY);
   NS_ENSURE_ARG_POINTER(aWidth);
   NS_ENSURE_ARG_POINTER(aHeight);
   *aX = *aY = *aWidth = *aHeight;
 
-  if (!Intl())
+  if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
-  nsIntRect rect = Intl()->CharBounds(aOffset, aCoordType);
+  nsIntRect rect;
+  if (mIntl.IsAccessible()) {
+    rect = Intl()->CharBounds(aOffset, aCoordType);
+  } else {
+    rect = mIntl.AsProxy()->CharBounds(aOffset, aCoordType);
+  }
   *aX = rect.x; *aY = rect.y;
   *aWidth = rect.width; *aHeight = rect.height;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::GetRangeExtents(int32_t aStartOffset, int32_t aEndOffset,
                                         int32_t* aX, int32_t* aY,
@@ -195,151 +262,209 @@ xpcAccessibleHyperText::GetRangeExtents(
                                         uint32_t aCoordType)
 {
   NS_ENSURE_ARG_POINTER(aX);
   NS_ENSURE_ARG_POINTER(aY);
   NS_ENSURE_ARG_POINTER(aWidth);
   NS_ENSURE_ARG_POINTER(aHeight);
   *aX = *aY = *aWidth = *aHeight = 0;
 
-  if (!Intl())
+  if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
-  nsIntRect rect = Intl()->TextBounds(aStartOffset, aEndOffset, aCoordType);
+  nsIntRect rect;
+  if (mIntl.IsAccessible()) {
+    rect = Intl()->TextBounds(aStartOffset, aEndOffset, aCoordType);
+  } else {
+    rect = mIntl.AsProxy()->TextBounds(aStartOffset, aEndOffset, aCoordType);
+  }
   *aX = rect.x; *aY = rect.y;
   *aWidth = rect.width; *aHeight = rect.height;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::GetOffsetAtPoint(int32_t aX, int32_t aY,
                                          uint32_t aCoordType, int32_t* aOffset)
 {
   NS_ENSURE_ARG_POINTER(aOffset);
   *aOffset = -1;
 
-  if (!Intl())
+  if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
-  *aOffset = Intl()->OffsetAtPoint(aX, aY, aCoordType);
+  if (mIntl.IsAccessible()) {
+    *aOffset = Intl()->OffsetAtPoint(aX, aY, aCoordType);
+  } else {
+    *aOffset = mIntl.AsProxy()->OffsetAtPoint(aX, aY, aCoordType);
+  }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::GetCaretOffset(int32_t* aCaretOffset)
 {
   NS_ENSURE_ARG_POINTER(aCaretOffset);
   *aCaretOffset = -1;
 
-  if (!Intl())
+  if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
-  *aCaretOffset = Intl()->CaretOffset();
+  if (mIntl.IsAccessible()) {
+    *aCaretOffset = Intl()->CaretOffset();
+  } else {
+    *aCaretOffset = mIntl.AsProxy()->CaretOffset();
+  }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::SetCaretOffset(int32_t aCaretOffset)
 {
-  if (!Intl())
+  if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
-  Intl()->SetCaretOffset(aCaretOffset);
+  if (mIntl.IsAccessible()) {
+    Intl()->SetCaretOffset(aCaretOffset);
+  } else {
+    mIntl.AsProxy()->SetCaretOffset(aCaretOffset);
+  }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::GetSelectionCount(int32_t* aSelectionCount)
 {
   NS_ENSURE_ARG_POINTER(aSelectionCount);
   *aSelectionCount = 0;
 
-  if (!Intl())
+  if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
-  *aSelectionCount = Intl()->SelectionCount();
+  if (mIntl.IsAccessible()) {
+    *aSelectionCount = Intl()->SelectionCount();
+  } else {
+    *aSelectionCount = mIntl.AsProxy()->SelectionCount();
+  }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::GetSelectionBounds(int32_t aSelectionNum,
                                            int32_t* aStartOffset,
                                            int32_t* aEndOffset)
 {
   NS_ENSURE_ARG_POINTER(aStartOffset);
   NS_ENSURE_ARG_POINTER(aEndOffset);
   *aStartOffset = *aEndOffset = 0;
 
-  if (!Intl())
+  if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
-  if (aSelectionNum < 0 || aSelectionNum >= Intl()->SelectionCount())
+  if (aSelectionNum < 0)
     return NS_ERROR_INVALID_ARG;
 
-  Intl()->SelectionBoundsAt(aSelectionNum, aStartOffset, aEndOffset);
+  if (mIntl.IsAccessible()) {
+    if (aSelectionNum >= Intl()->SelectionCount())
+      return NS_ERROR_INVALID_ARG;
+      
+    Intl()->SelectionBoundsAt(aSelectionNum, aStartOffset, aEndOffset);
+  } else {
+    nsString unused;
+    mIntl.AsProxy()->SelectionBoundsAt(aSelectionNum, unused, aStartOffset, 
+                                       aEndOffset);
+  }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::SetSelectionBounds(int32_t aSelectionNum,
                                            int32_t aStartOffset,
                                            int32_t aEndOffset)
 {
-  if (!Intl())
+  if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
-  if (aSelectionNum < 0 ||
-      !Intl()->SetSelectionBoundsAt(aSelectionNum, aStartOffset, aEndOffset))
+  if (aSelectionNum < 0)
     return NS_ERROR_INVALID_ARG;
 
+  if (mIntl.IsAccessible()) {
+      if (!Intl()->SetSelectionBoundsAt(aSelectionNum, aStartOffset, 
+                                        aEndOffset)) {
+        return NS_ERROR_INVALID_ARG;
+      }
+  } else {
+      if (!mIntl.AsProxy()->SetSelectionBoundsAt(aSelectionNum, aStartOffset, 
+                                                aEndOffset)) {
+        return NS_ERROR_INVALID_ARG;
+      }
+  }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::AddSelection(int32_t aStartOffset, int32_t aEndOffset)
 {
-  if (!Intl())
+  if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
-  Intl()->AddToSelection(aStartOffset, aEndOffset);
+  if (mIntl.IsAccessible()) {
+    Intl()->AddToSelection(aStartOffset, aEndOffset);
+  } else {
+    mIntl.AsProxy()->AddToSelection(aStartOffset, aEndOffset);
+  }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::RemoveSelection(int32_t aSelectionNum)
 {
-  if (!Intl())
+  if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
-  Intl()->RemoveFromSelection(aSelectionNum);
+  if (mIntl.IsAccessible()) {
+    Intl()->RemoveFromSelection(aSelectionNum);
+  } else {
+    mIntl.AsProxy()->RemoveFromSelection(aSelectionNum);
+  }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::ScrollSubstringTo(int32_t aStartOffset,
                                           int32_t aEndOffset,
                                           uint32_t aScrollType)
 {
-  if (!Intl())
+  if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
-  Intl()->ScrollSubstringTo(aStartOffset, aEndOffset, aScrollType);
+  if (mIntl.IsAccessible()) {
+    Intl()->ScrollSubstringTo(aStartOffset, aEndOffset, aScrollType);
+  } else {
+    mIntl.AsProxy()->ScrollSubstringTo(aStartOffset, aEndOffset, aScrollType);
+  }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::ScrollSubstringToPoint(int32_t aStartOffset,
                                                int32_t aEndOffset,
                                                uint32_t aCoordinateType,
                                                int32_t aX, int32_t aY)
 {
-  if (!Intl())
+  if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
-  Intl()->ScrollSubstringToPoint(aStartOffset, aEndOffset, aCoordinateType, aX, aY);
+  if (mIntl.IsAccessible()) {
+    Intl()->ScrollSubstringToPoint(aStartOffset, aEndOffset, aCoordinateType,
+                                   aX, aY);
+  } else {
+    mIntl.AsProxy()->ScrollSubstringToPoint(aStartOffset, aEndOffset,
+                                            aCoordinateType, aX, aY);
+  }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::GetEnclosingRange(nsIAccessibleTextRange** aRange)
 {
   NS_ENSURE_ARG_POINTER(aRange);
   *aRange = nullptr;
@@ -447,126 +572,171 @@ xpcAccessibleHyperText::GetRangeAtPoint(
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsIAccessibleEditableText
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::SetTextContents(const nsAString& aText)
 {
-  if (!Intl())
+  if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
-  Intl()->ReplaceText(aText);
+  if (mIntl.IsAccessible()) {
+    Intl()->ReplaceText(aText);
+  } else {
+    nsString text(aText);
+    mIntl.AsProxy()->ReplaceText(text);
+  }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::InsertText(const nsAString& aText, int32_t aOffset)
 {
-  if (!Intl())
+  if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
-  Intl()->InsertText(aText, aOffset);
+  if (mIntl.IsAccessible()) {
+    Intl()->InsertText(aText, aOffset);
+  } else {
+    nsString text(aText);
+    mIntl.AsProxy()->InsertText(text, aOffset);
+  }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::CopyText(int32_t aStartOffset, int32_t aEndOffset)
 {
-  if (!Intl())
+  if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
-  Intl()->CopyText(aStartOffset, aEndOffset);
+  if (mIntl.IsAccessible()) {
+    Intl()->CopyText(aStartOffset, aEndOffset);
+  } else {
+    mIntl.AsProxy()->CopyText(aStartOffset, aEndOffset);
+  }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::CutText(int32_t aStartOffset, int32_t aEndOffset)
 {
-  if (!Intl())
+  if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
-  Intl()->CutText(aStartOffset, aEndOffset);
+  if (mIntl.IsAccessible()) {
+    Intl()->CutText(aStartOffset, aEndOffset);
+  } else {
+    mIntl.AsProxy()->CutText(aStartOffset, aEndOffset);
+  }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::DeleteText(int32_t aStartOffset, int32_t aEndOffset)
 {
-  if (!Intl())
+  if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
-  Intl()->DeleteText(aStartOffset, aEndOffset);
+  if (mIntl.IsAccessible()) {
+    Intl()->DeleteText(aStartOffset, aEndOffset);
+  } else {
+    mIntl.AsProxy()->DeleteText(aStartOffset, aEndOffset);
+  }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::PasteText(int32_t aOffset)
 {
-  if (!Intl())
+  if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
-  Intl()->PasteText(aOffset);
+  if (mIntl.IsAccessible()) {
+    Intl()->PasteText(aOffset);
+  } else {
+    mIntl.AsProxy()->PasteText(aOffset);
+  }
   return NS_OK;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsIAccessibleHyperText
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::GetLinkCount(int32_t* aLinkCount)
 {
   NS_ENSURE_ARG_POINTER(aLinkCount);
   *aLinkCount = 0;
 
-  if (!Intl())
+  if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
-  *aLinkCount = Intl()->LinkCount();
+  if (mIntl.IsAccessible()) {
+    *aLinkCount = Intl()->LinkCount();
+  } else {
+    *aLinkCount = mIntl.AsProxy()->LinkCount();
+  }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::GetLinkAt(int32_t aIndex, nsIAccessibleHyperLink** aLink)
 {
   NS_ENSURE_ARG_POINTER(aLink);
   *aLink = nullptr;
 
-  if (!Intl())
+  if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
-  NS_IF_ADDREF(*aLink = ToXPC(Intl()->LinkAt(aIndex)));
+  if (mIntl.IsAccessible()) {
+    NS_IF_ADDREF(*aLink = ToXPC(Intl()->LinkAt(aIndex)));
+  } else {
+    NS_IF_ADDREF(*aLink = ToXPC(mIntl.AsProxy()->LinkAt(aIndex)));
+  }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::GetLinkIndex(nsIAccessibleHyperLink* aLink,
                                      int32_t* aIndex)
 {
   NS_ENSURE_ARG_POINTER(aLink);
   NS_ENSURE_ARG_POINTER(aIndex);
   *aIndex = -1;
 
-  if (!Intl())
+  if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
   nsCOMPtr<nsIAccessible> xpcLink(do_QueryInterface(aLink));
-  Accessible* link = xpcLink->ToInternalAccessible();
-  if (link)
-    *aIndex = Intl()->LinkIndexOf(link);
+  if (Accessible* accLink = xpcLink->ToInternalAccessible()) {
+    *aIndex = Intl()->LinkIndexOf(accLink);
+  } else {
+    xpcAccessibleHyperText* linkHyperText =
+      static_cast<xpcAccessibleHyperText*>(xpcLink.get());
+    ProxyAccessible* proxyLink = linkHyperText->mIntl.AsProxy();
+    if (proxyLink) {
+      *aIndex = mIntl.AsProxy()->LinkIndexOf(proxyLink);
+    }
+  }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleHyperText::GetLinkIndexAtOffset(int32_t aOffset,
                                              int32_t* aLinkIndex)
 {
   NS_ENSURE_ARG_POINTER(aLinkIndex);
   *aLinkIndex = -1; // API says this magic value means 'not found'
 
-  if (!Intl())
+  if (mIntl.IsNull())
     return NS_ERROR_FAILURE;
 
-  *aLinkIndex = Intl()->LinkIndexAtOffset(aOffset);
+  if (mIntl.IsAccessible()) {
+    *aLinkIndex = Intl()->LinkIndexAtOffset(aOffset);
+  } else {
+    *aLinkIndex = mIntl.AsProxy()->LinkIndexAtOffset(aOffset);
+  }
   return NS_OK;
 }
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -3365,16 +3365,19 @@ var SessionStoreInternal = {
     let window = aTab.ownerGlobal;
     let tabbrowser = window.gBrowser;
     let tabData = TabState.clone(aTab);
     let activeIndex = tabData.index - 1;
     let activePageData = tabData.entries[activeIndex] || null;
     let uri = activePageData ? activePageData.url || null : null;
     if (aLoadArguments) {
       uri = aLoadArguments.uri;
+      if (aLoadArguments.userContextId) {
+        browser.setAttribute("usercontextid", aLoadArguments.userContextId);
+      }
     }
 
     // We have to mark this tab as restoring first, otherwise
     // the "pending" attribute will be applied to the linked
     // browser, which removes it from the display list. We cannot
     // flip the remoteness of any browser that is not being displayed.
     this.markTabAsRestoring(aTab);
 
--- a/build/autoconf/icu.m4
+++ b/build/autoconf/icu.m4
@@ -80,17 +80,17 @@ if test -n "$USE_ICU"; then
     # but we'd need to check in a big-endian version of the file.
     ICU_DATA_FILE="icudt${version}l.dat"
 
     dnl We won't build ICU data as a separate file when building
     dnl JS standalone so that embedders don't have to deal with it.
     dnl We also don't do it on Windows because sometimes the file goes
     dnl missing -- possibly due to overzealous antivirus software? --
     dnl which prevents the browser from starting up :(
-    if test -z "$JS_STANDALONE" -a -z "$MOZ_SYSTEM_ICU" -a "$OS_TARGET" != WINNT; then
+    if test -z "$JS_STANDALONE" -a -z "$MOZ_SYSTEM_ICU" -a "$OS_TARGET" != WINNT -a "$MOZ_WIDGET_TOOLKIT" != "android"; then
         MOZ_ICU_DATA_ARCHIVE=1
     else
         MOZ_ICU_DATA_ARCHIVE=
     fi
 fi
 
 AC_SUBST(MOZ_ICU_VERSION)
 AC_SUBST(ENABLE_INTL_API)
--- a/build/mobile/remoteautomation.py
+++ b/build/mobile/remoteautomation.py
@@ -340,19 +340,23 @@ class RemoteAutomation(Automation):
                 print newLogContent
                 return True
 
             self.logBuffer += newLogContent
             lines = self.logBuffer.split('\n')
             lines = [l for l in lines if l]
 
             if lines:
-                # We only keep the last (unfinished) line in the buffer
-                self.logBuffer = lines[-1]
-                del lines[-1]
+                if self.logBuffer.endswith('\n'):
+                    # all lines are complete; no need to buffer
+                    self.logBuffer = ""
+                else:
+                    # keep the last (unfinished) line in the buffer
+                    self.logBuffer = lines[-1]
+                    del lines[-1]
 
             if not lines:
                 return False
 
             for line in lines:
                 # This passes the line to the logger (to be logged or buffered)
                 parsed_messages = self.messageLogger.write(line)
                 for message in parsed_messages:
--- a/build/moz.configure/rust.configure
+++ b/build/moz.configure/rust.configure
@@ -9,46 +9,45 @@ option('--enable-rust', help='Include Ru
 @depends('--enable-rust')
 def rust_compiler_names(value):
     if value:
         return ['rustc']
 
 rustc = check_prog('RUSTC', rust_compiler_names, allow_missing=True)
 
 @depends_if(rustc)
-@checking('rustc version')
-@imports('subprocess')
-def rustc_version(rustc):
-    try:
-        # TODO: We should run `rustc --version -v` and parse that output instead.
-        version = Version(subprocess.check_output(
-            [rustc, '--version']
-        ).splitlines()[0].split()[1])
-        return version
-    except subprocess.CalledProcessError as e:
-        die('Failed to get rustc version: %s', e.message)
+@checking('rustc version', lambda info: info.version)
+def rustc_info(rustc):
+        out = check_cmd_output(rustc, '--version', '--verbose').splitlines()
+        info = dict((s.strip() for s in line.split(':', 1)) for line in out[1:])
+        return namespace(
+            version=Version(info.get('release', '0')),
+            commit=info.get('commit-hash', 'unknown'),
+        )
 
-@depends('--enable-rust', rustc, rustc_version)
+@depends('--enable-rust', rustc, rustc_info)
 @imports(_from='textwrap', _import='dedent')
-def rust_compiler(value, rustc, rustc_version):
+def rust_compiler(value, rustc, rustc_info):
     if value:
         if not rustc:
             die(dedent('''\
             Rust compiler not found.
             To compile rust language sources, you must have 'rustc' in your path.
-            See http://www.rust-lang.org/ for more information.
+            See https//www.rust-lang.org/ for more information.
             '''))
-        if rustc_version < '1.5':
+        version = rustc_info.version
+        min_version = Version('1.5')
+        if version < min_version:
             die(dedent('''\
             Rust compiler {} is too old.
             To compile Rust language sources please install at least
-            version 1.5 of the 'rustc' toolchain and make sure it is
+            version {} of the 'rustc' toolchain and make sure it is
             first in your path.
             You can verify this by typing 'rustc --version'.
-            '''.format(rustc_version)))
+            '''.format(version, min_version)))
         return True
 
 set_config('MOZ_RUST', rust_compiler)
 
 @depends(rust_compiler, rustc, target, cross_compiling)
 @imports('os')
 @imports('subprocess')
 @imports(_from='mozbuild.configure.util', _import='LineIO')
--- a/config/external/nss/Makefile.in
+++ b/config/external/nss/Makefile.in
@@ -318,17 +318,20 @@ ifdef MOZ_FOLD_LIBS
 NSS_STATIC_LIBS := $(strip $(shell $(MAKE) --no-print-directory -f $(srcdir)/nss.mk DEPTH='$(DEPTH)' topsrcdir='$(topsrcdir)' srcdir='$(srcdir)' echo-variable-libs))
 # Corresponding build directories
 NSS_STATIC_DIRS := $(foreach lib,$(NSS_STATIC_LIBS),$(patsubst %/,%,$(dir $(lib))))
 NSS_DIRS += $(NSS_STATIC_DIRS)
 
 # TODO: The following can be replaced by something simpler when bug 844884
 # is fixed.
 # Remaining nss/lib directories
-NSS_DIRS += nss/lib/freebl nss/lib/softoken nss/lib/jar nss/lib/crmf nss/lib/ckfw nss/lib/libpkix
+NSS_DIRS += nss/lib/freebl nss/lib/softoken nss/lib/jar nss/lib/crmf nss/lib/ckfw
+
+DEFAULT_GMAKE_FLAGS += NSS_DISABLE_LIBPKIX=1
+
 ifeq (WINNT,$(OS_TARGET))
 NSS_DIRS += nss/lib/zlib
 endif
 endif # MOZ_FOLD_LIBS
 
 # Filter-out $(LIBRARY_NAME) because it's already handled in config/rules.mk.
 NSS_DIST_DLL_FILES := $(addprefix $(DIST)/lib/$(DLL_PREFIX),$(addsuffix $(DLL_SUFFIX),$(filter-out $(LIBRARY_NAME),$(NSS_DLLS)) $(NSS_EXTRA_DLLS)))
 NSS_DIST_DLL_DEST := $(DIST)/bin
--- a/config/external/nss/nss.symbols
+++ b/config/external/nss/nss.symbols
@@ -137,17 +137,16 @@ CERT_IsUserCert
 CERT_MakeCANickname
 CERT_MergeExtensions
 CERT_NameTemplate @DATA@
 CERT_NameToAscii
 CERT_NewCertList
 CERT_NewTempCertificate
 CERT_NicknameStringsFromCertList
 CERT_OCSPCacheSettings
-CERT_PKIXVerifyCert
 CERT_RemoveCertListNode
 CERT_RFC1485_EscapeAndQuote
 CERT_SaveSMimeProfile
 CERT_SequenceOfCertExtensionTemplate @DATA@
 CERT_SetOCSPFailureMode
 CERT_SetOCSPTimeout
 CERT_SignedCrlTemplate @DATA@
 CERT_SignedDataTemplate @DATA@
--- a/docshell/shistory/nsSHistory.cpp
+++ b/docshell/shistory/nsSHistory.cpp
@@ -792,17 +792,17 @@ NS_IMETHODIMP
 nsSHistory::EvictAllContentViewers()
 {
   // XXXbz we don't actually do a good job of evicting things as we should, so
   // we might have viewers quite far from mIndex.  So just evict everything.
   nsCOMPtr<nsISHTransaction> trans = mListRoot;
   while (trans) {
     EvictContentViewerForTransaction(trans);
 
-    nsISHTransaction* temp = trans;
+    nsCOMPtr<nsISHTransaction> temp = trans;
     temp->GetNext(getter_AddRefs(trans));
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSHistory::GetCanGoBack(bool* aCanGoBack)
@@ -969,29 +969,29 @@ nsSHistory::EvictOutOfRangeWindowContent
   // evicted.  Collect a set of them so we don't accidentally evict one of them
   // if it appears outside this range.
   nsCOMArray<nsIContentViewer> safeViewers;
   nsCOMPtr<nsISHTransaction> trans;
   GetTransactionAtIndex(startSafeIndex, getter_AddRefs(trans));
   for (int32_t i = startSafeIndex; trans && i <= endSafeIndex; i++) {
     nsCOMPtr<nsIContentViewer> viewer = GetContentViewerForTransaction(trans);
     safeViewers.AppendObject(viewer);
-    nsISHTransaction* temp = trans;
+    nsCOMPtr<nsISHTransaction> temp = trans;
     temp->GetNext(getter_AddRefs(trans));
   }
 
   // Walk the SHistory list and evict any content viewers that aren't safe.
   GetTransactionAtIndex(0, getter_AddRefs(trans));
   while (trans) {
     nsCOMPtr<nsIContentViewer> viewer = GetContentViewerForTransaction(trans);
     if (safeViewers.IndexOf(viewer) == -1) {
       EvictContentViewerForTransaction(trans);
     }
 
-    nsISHTransaction* temp = trans;
+    nsCOMPtr<nsISHTransaction> temp = trans;
     temp->GetNext(getter_AddRefs(trans));
   }
 }
 
 namespace {
 
 class TransactionAndDistance
 {
@@ -1103,17 +1103,17 @@ nsSHistory::GloballyEvictContentViewers(
         // make a new one.
         if (!found) {
           TransactionAndDistance container(trans,
                                            DeprecatedAbs(i - shist->mIndex));
           shTransactions.AppendElement(container);
         }
       }
 
-      nsISHTransaction* temp = trans;
+      nsCOMPtr<nsISHTransaction> temp = trans;
       temp->GetNext(getter_AddRefs(trans));
     }
 
     // We've found all the transactions belonging to shist which have viewers.
     // Add those transactions to our global list and move on.
     transactions.AppendElements(shTransactions);
     listEntry = PR_NEXT_LINK(shist);
   }
@@ -1149,17 +1149,17 @@ nsSHistory::EvictExpiredContentViewerFor
     nsCOMPtr<nsISHEntry> entry;
     trans->GetSHEntry(getter_AddRefs(entry));
 
     // Does entry have the same BFCacheEntry as the argument to this method?
     if (entry->HasBFCacheEntry(aEntry)) {
       break;
     }
 
-    nsISHTransaction* temp = trans;
+    nsCOMPtr<nsISHTransaction> temp = trans;
     temp->GetNext(getter_AddRefs(trans));
   }
   if (i > endIndex) {
     return NS_OK;
   }
 
   if (i == mIndex) {
     NS_WARNING("How did the current SHEntry expire?");
--- a/dom/animation/AnimationPerformanceWarning.cpp
+++ b/dom/animation/AnimationPerformanceWarning.cpp
@@ -60,16 +60,19 @@ AnimationPerformanceWarning::ToLocalized
       key = "CompositorAnimationWarningTransformWithGeometricProperties";
       break;
     case Type::TransformFrameInactive:
       key = "CompositorAnimationWarningTransformFrameInactive";
       break;
     case Type::OpacityFrameInactive:
       key = "CompositorAnimationWarningOpacityFrameInactive";
       break;
+    case Type::HasRenderingObserver:
+      key = "CompositorAnimationWarningHasRenderingObserver";
+      break;
   }
 
   nsresult rv =
     nsContentUtils::GetLocalizedString(nsContentUtils::eLAYOUT_PROPERTIES,
                                        key, aLocalizedString);
   return NS_SUCCEEDED(rv);
 }
 
--- a/dom/animation/AnimationPerformanceWarning.h
+++ b/dom/animation/AnimationPerformanceWarning.h
@@ -20,16 +20,17 @@ struct AnimationPerformanceWarning
     ContentTooSmall,
     ContentTooLarge,
     TransformBackfaceVisibilityHidden,
     TransformPreserve3D,
     TransformSVG,
     TransformWithGeometricProperties,
     TransformFrameInactive,
     OpacityFrameInactive,
+    HasRenderingObserver,
   };
 
   explicit AnimationPerformanceWarning(Type aType)
     : mType(aType) { }
 
   AnimationPerformanceWarning(Type aType,
                               std::initializer_list<int32_t> aParams)
     : mType(aType)
--- a/dom/animation/EffectCompositor.cpp
+++ b/dom/animation/EffectCompositor.cpp
@@ -96,16 +96,31 @@ FindAnimationsForCompositor(const nsIFra
       nsCString message;
       message.AppendLiteral("Performance warning: Async animations are "
                             "disabled");
       AnimationUtils::LogAsyncAnimationFailure(message);
     }
     return false;
   }
 
+  // Disable async animations if we have a rendering observer that
+  // depends on our content (svg masking, -moz-element etc) so that
+  // it gets updated correctly.
+  nsIContent* content = aFrame->GetContent();
+  while (content) {
+    if (content->HasRenderingObservers()) {
+      EffectCompositor::SetPerformanceWarning(
+        aFrame, aProperty,
+        AnimationPerformanceWarning(
+          AnimationPerformanceWarning::Type::HasRenderingObserver));
+      return false;
+    }
+    content = content->GetParent();
+  }
+
   bool foundSome = false;
   for (KeyframeEffectReadOnly* effect : *effects) {
     MOZ_ASSERT(effect && effect->GetAnimation());
     Animation* animation = effect->GetAnimation();
 
     if (!animation->IsPlaying()) {
       continue;
     }
--- a/dom/animation/test/chrome/test_animation_performance_warning.html
+++ b/dom/animation/test/chrome/test_animation_performance_warning.html
@@ -233,17 +233,18 @@ var gAnimationWithGeometricKeyframeTests
           runningOnCompositor: false,
           warning: 'CompositorAnimationWarningTransformWithGeometricProperties'
         }
       ]
     }
   },
 ];
 
-var gPerformanceWarningTests = [
+// Performance warning tests that set and clear a style property.
+var gPerformanceWarningTestsStyle = [
   {
     desc: 'preserve-3d transform',
     frames: {
       transform: ['translate(0px)', 'translate(100px)']
     },
     style: 'transform-style: preserve-3d',
     expected: [
       {
@@ -302,16 +303,35 @@ var gPerformanceWarningTests = [
         property: 'transform',
         runningOnCompositor: false,
         warning: 'CompositorAnimationWarningTransformBackfaceVisibilityHidden'
       }
     ]
   },
 ];
 
+// Performance warning tests that set and clear the id property
+var gPerformanceWarningTestsId= [
+  {
+    desc: 'moz-element referencing a transform',
+    frames: {
+      transform: ['translate(0px)', 'translate(100px)']
+    },
+    id: 'transformed',
+    createelement: 'width:100px; height:100px; background: -moz-element(#transformed)',
+    expected: [
+      {
+        property: 'transform',
+        runningOnCompositor: false,
+        warning: 'CompositorAnimationWarningHasRenderingObserver'
+      }
+    ]
+  },
+];
+
 var gMultipleAsyncAnimationsTests = [
   {
     desc: 'opacity and transform with preserve-3d',
     style: 'transform-style: preserve-3d',
     animations: [
       {
         frames: {
           transform: ['translate(0px)', 'translate(100px)']
@@ -632,17 +652,17 @@ function start() {
         // Finally, the transform animation is running on compositor.
         assert_animation_property_state_equals(
           animation.effect.getProperties(),
           subtest.expected.withoutGeometric);
       });
     }, 'An animation has: ' + subtest.desc);
   });
 
-  gPerformanceWarningTests.forEach(function(subtest) {
+  gPerformanceWarningTestsStyle.forEach(function(subtest) {
     promise_test(function(t) {
       var animation = addDivAndAnimate(t,
                                        { class: 'compositable' },
                                        subtest.frames, 100 * MS_PER_SEC);
       return animation.ready.then(function() {
         assert_property_state_on_compositor(
           animation.effect.getProperties(),
           subtest.expected);
@@ -657,16 +677,45 @@ function start() {
       }).then(function() {
         assert_property_state_on_compositor(
           animation.effect.getProperties(),
           subtest.expected);
       });
     }, subtest.desc);
   });
 
+  gPerformanceWarningTestsId.forEach(function(subtest) {
+    promise_test(function(t) {
+      if (subtest.createelement) {
+        addDiv(t, { style: subtest.createelement });
+      }
+
+      var animation = addDivAndAnimate(t,
+                                       { class: 'compositable' },
+                                       subtest.frames, 100 * MS_PER_SEC);
+      return animation.ready.then(function() {
+        assert_property_state_on_compositor(
+          animation.effect.getProperties(),
+          subtest.expected);
+        animation.effect.target.id = subtest.id;
+        return waitForFrame();
+      }).then(function() {
+        assert_animation_property_state_equals(
+          animation.effect.getProperties(),
+          subtest.expected);
+        animation.effect.target.id = '';
+        return waitForFrame();
+      }).then(function() {
+        assert_property_state_on_compositor(
+          animation.effect.getProperties(),
+          subtest.expected);
+      });
+    }, subtest.desc);
+  });
+
   gMultipleAsyncAnimationsTests.forEach(function(subtest) {
     promise_test(function(t) {
       var div = addDiv(t, { class: 'compositable' });
       var animations = subtest.animations.map(function(anim) {
         var animation = div.animate(anim.frames, 100 * MS_PER_SEC);
 
         // Bind expected values to animation object.
         animation.expected = anim.expected;
--- a/dom/base/File.cpp
+++ b/dom/base/File.cpp
@@ -986,16 +986,26 @@ BlobImplFile::GetInternalStream(nsIInput
     aRv = NS_NewLocalFileInputStream(aStream, mFile, -1, -1, sFileStreamFlags);
     return;
   }
 
   aRv = NS_NewPartialLocalFileInputStream(aStream, mFile, mStart, mLength,
                                           -1, -1, sFileStreamFlags);
 }
 
+bool
+BlobImplFile::IsDirectory() const
+{
+  bool isDirectory = false;
+  if (mFile) {
+    mFile->IsDirectory(&isDirectory);
+  }
+  return isDirectory;
+}
+
 ////////////////////////////////////////////////////////////////////////////
 // EmptyBlobImpl implementation
 
 NS_IMPL_ISUPPORTS_INHERITED0(EmptyBlobImpl, BlobImpl)
 
 already_AddRefed<BlobImpl>
 EmptyBlobImpl::CreateSlice(uint64_t aStart, uint64_t aLength,
                            const nsAString& aContentType,
--- a/dom/base/File.h
+++ b/dom/base/File.h
@@ -324,16 +324,23 @@ public:
   virtual bool IsMemoryFile() const = 0;
 
   virtual bool IsSizeUnknown() const = 0;
 
   virtual bool IsDateUnknown() const = 0;
 
   virtual bool IsFile() const = 0;
 
+  // Returns true if the BlobImpl is backed by an nsIFile and the underlying
+  // file is a directory.
+  virtual bool IsDirectory() const
+  {
+    return false;
+  }
+
   // True if this implementation can be sent to other threads.
   virtual bool MayBeClonedToOtherThreads() const
   {
     return true;
   }
 
 protected:
   virtual ~BlobImpl() {}
@@ -707,16 +714,18 @@ public:
   virtual void GetType(nsAString& aType) override;
   virtual int64_t GetLastModified(ErrorResult& aRv) override;
   virtual void SetLastModified(int64_t aLastModified) override;
   virtual void GetMozFullPathInternal(nsAString& aFullPath,
                                       ErrorResult& aRv) const override;
   virtual void GetInternalStream(nsIInputStream** aInputStream,
                                  ErrorResult& aRv) override;
 
+  virtual bool IsDirectory() const override;
+
   // We always have size and date for this kind of blob.
   virtual bool IsSizeUnknown() const override { return false; }
   virtual bool IsDateUnknown() const override { return false; }
 
 protected:
   virtual ~BlobImplFile() {
     if (mFile && mIsTemporary) {
       NS_WARNING("In temporary ~BlobImplFile");
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -8342,21 +8342,18 @@ nsGlobalWindow::PostMessageMozOuter(JSCo
       return;
     }
   }
 
   // Convert the provided origin string into a URI for comparison purposes.
   nsCOMPtr<nsIPrincipal> providedPrincipal;
 
   if (aTargetOrigin.EqualsASCII("/")) {
-    providedPrincipal = GetEntryGlobal()->PrincipalOrNull();
-    if (NS_WARN_IF(!providedPrincipal))
-      return;
-  }
-
+    providedPrincipal = callerPrin;
+  }
   // "*" indicates no specific origin is required.
   else if (!aTargetOrigin.EqualsASCII("*")) {
     nsCOMPtr<nsIURI> originURI;
     if (NS_FAILED(NS_NewURI(getter_AddRefs(originURI), aTargetOrigin))) {
       aError.Throw(NS_ERROR_DOM_SYNTAX_ERR);
       return;
     }
 
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -2242,16 +2242,17 @@ class MethodDefiner(PropertyDefiner):
                 "length": 0,
                 "flags": "JSPROP_ENUMERATE",
                 "condition": MemberCondition()
             })
 
         # Generate the keys/values/entries aliases for value iterables.
         maplikeOrSetlikeOrIterable = descriptor.interface.maplikeOrSetlikeOrIterable
         if (not static and
+            not unforgeable and
             maplikeOrSetlikeOrIterable and
             maplikeOrSetlikeOrIterable.isIterable() and
             maplikeOrSetlikeOrIterable.isValueIterator()):
             # Add our keys/values/entries/forEach
             self.regular.append({
                 "name": "keys",
                 "methodInfo": False,
                 "selfHostedName": "ArrayKeys",
@@ -2277,17 +2278,17 @@ class MethodDefiner(PropertyDefiner):
                 "flags": "JSPROP_ENUMERATE",
                 "condition": PropertyDefiner.getControllingCondition(m,
                                                                      descriptor)
             })
             self.regular.append({
                 "name": "forEach",
                 "methodInfo": False,
                 "selfHostedName": "ArrayForEach",
-                "length": 0,
+                "length": 1,
                 "flags": "JSPROP_ENUMERATE",
                 "condition": PropertyDefiner.getControllingCondition(m,
                                                                      descriptor)
             })
 
         if not static:
             stringifier = descriptor.operations['Stringifier']
             if (stringifier and
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -1332,53 +1332,64 @@ bool CanvasRenderingContext2D::SwitchRen
       !gfxPlatform::GetPlatform()->UseAcceleratedCanvas()) {
       return false;
   }
 #endif
 
   RefPtr<SourceSurface> snapshot;
   Matrix transform;
   RefPtr<PersistentBufferProvider> oldBufferProvider = mBufferProvider;
+  RefPtr<DrawTarget> oldTarget = mTarget;
+
   AutoReturnSnapshot autoReturn(nullptr);
 
   if (mTarget) {
     snapshot = mTarget->Snapshot();
     transform = mTarget->GetTransform();
   } else {
     MOZ_ASSERT(mBufferProvider);
     // When mBufferProvider is true but we have no mTarget, our current state's
     // transform is always valid. See ReturnTarget().
     transform = CurrentState().transform;
     snapshot = mBufferProvider->BorrowSnapshot();
     autoReturn.mBufferProvider = mBufferProvider;
     autoReturn.mSnapshot = &snapshot;
   }
+
   mTarget = nullptr;
   mBufferProvider = nullptr;
   mResetLayer = true;
 
   // Recreate target using the new rendering mode
   RenderingMode attemptedMode = EnsureTarget(nullptr, aRenderingMode);
-  if (!IsTargetValid())
+  if (!IsTargetValid()) {
+    if (oldBufferProvider && oldTarget) {
+      oldBufferProvider->ReturnDrawTarget(oldTarget.forget());
+    }
     return false;
+  }
 
   // We succeeded, so update mRenderingMode to reflect reality
   mRenderingMode = attemptedMode;
 
   // Restore the content from the old DrawTarget
   gfx::Rect r(0, 0, mWidth, mHeight);
   mTarget->DrawSurface(snapshot, r, r);
 
   // Restore the clips and transform
   for (uint32_t i = 0; i < CurrentState().clipsPushed.Length(); i++) {
     mTarget->PushClip(CurrentState().clipsPushed[i]);
   }
 
   mTarget->SetTransform(transform);
 
+  if (oldBufferProvider && oldTarget) {
+    oldBufferProvider->ReturnDrawTarget(oldTarget.forget());
+  }
+
   return true;
 }
 
 void CanvasRenderingContext2D::Demote()
 {
   if (SwitchRenderingMode(RenderingMode::SoftwareBackendMode)) {
     RemoveDemotableContext(this);
   }
--- a/dom/canvas/WebGL2ContextSamplers.cpp
+++ b/dom/canvas/WebGL2ContextSamplers.cpp
@@ -198,27 +198,27 @@ WebGL2Context::SamplerParameterfv(WebGLS
 
     sampler->SamplerParameter1f(pname, param[0]);
     WebGLContextUnchecked::SamplerParameterfv(sampler, pname, param.Elements());
 }
 
 void
 WebGL2Context::GetSamplerParameter(JSContext*, WebGLSampler* sampler, GLenum pname, JS::MutableHandleValue retval)
 {
+    retval.setNull();
+
     if (IsContextLost())
         return;
 
     if (!sampler || sampler->IsDeleted())
         return ErrorInvalidOperation("getSamplerParameter: invalid sampler");
 
     if (!ValidateSamplerParameterName(pname, "getSamplerParameter"))
         return;
 
-    retval.set(JS::NullValue());
-
     switch (pname) {
     case LOCAL_GL_TEXTURE_MIN_FILTER:
     case LOCAL_GL_TEXTURE_MAG_FILTER:
     case LOCAL_GL_TEXTURE_WRAP_S:
     case LOCAL_GL_TEXTURE_WRAP_T:
     case LOCAL_GL_TEXTURE_WRAP_R:
     case LOCAL_GL_TEXTURE_COMPARE_MODE:
     case LOCAL_GL_TEXTURE_COMPARE_FUNC:
--- a/dom/canvas/WebGL2ContextUniforms.cpp
+++ b/dom/canvas/WebGL2ContextUniforms.cpp
@@ -272,16 +272,17 @@ WebGL2Context::GetActiveUniformBlockPara
 
     ErrorInvalidEnumInfo("getActiveUniformBlockParameter: parameter", pname);
 }
 
 void
 WebGL2Context::GetActiveUniformBlockName(WebGLProgram* program, GLuint uniformBlockIndex,
                                          nsAString& retval)
 {
+    retval.SetIsVoid(true);
     if (IsContextLost())
         return;
 
     if (!ValidateObject("getActiveUniformBlockName: program", program))
         return;
 
     program->GetActiveUniformBlockName(uniformBlockIndex, retval);
 }
--- a/dom/canvas/test/webgl-conf/checkout/js/tests/gl-object-get-calls.js
+++ b/dom/canvas/test/webgl-conf/checkout/js/tests/gl-object-get-calls.js
@@ -758,17 +758,17 @@ if (contextVersion > 1) {
         gl.RG16F, gl.RGB16F, gl.RGBA16F, gl.R32F,
         gl.RG32F, gl.RGB32F, gl.RGBA32F, gl.R11F_G11F_B10F,
         gl.RGB9_E5, gl.R8I, gl.R8UI, gl.R16I,
         gl.R16UI, gl.R32I, gl.R32UI, gl.RG8I,
         gl.RG8UI, gl.RG16I, gl.RG16UI, gl.RG32I,
         gl.RG32UI, gl.RGB8I, gl.RGB8UI, gl.RGB16I,
         gl.RGB16UI, gl.RGB32I, gl.RGB32UI, gl.RGBA8I,
         gl.RGBA8UI, gl.RGBA16I, gl.RGBA16UI, gl.RGBA32I,
-        gl.RGBA32UI, gl.RGB, gl.RGBA, gl.DEPTH_COMPONENT16,
+        gl.RGBA32UI, gl.RGB, gl.RGBA, gl.DEPTH_STENCIL, gl.DEPTH_COMPONENT16,
         gl.DEPTH_COMPONENT24, gl.DEPTH_COMPONENT32F, gl.DEPTH24_STENCIL8,
         gl.DEPTH32F_STENCIL8, gl.STENCIL_INDEX8
     );
     testInvalidArgument(
         "getInternalformatParameter",
         "internalformat",
         validArrayForInterformat,
         function(internalformat) {
--- a/dom/events/DataTransferItem.cpp
+++ b/dom/events/DataTransferItem.cpp
@@ -4,16 +4,21 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "DataTransferItem.h"
 #include "DataTransferItemList.h"
 
 #include "mozilla/ContentEvents.h"
 #include "mozilla/EventForwards.h"
 #include "mozilla/dom/DataTransferItemBinding.h"
+#include "mozilla/dom/Directory.h"
+#include "mozilla/dom/DirectoryEntry.h"
+#include "mozilla/dom/DOMFileSystem.h"
+#include "mozilla/dom/Event.h"
+#include "mozilla/dom/FileEntry.h"
 #include "nsIClipboard.h"
 #include "nsISupportsPrimitives.h"
 #include "nsNetUtil.h"
 #include "nsQueryObject.h"
 #include "nsContentUtils.h"
 #include "nsVariant.h"
 
 namespace {
@@ -263,16 +268,88 @@ DataTransferItem::GetAsFile(ErrorResult&
       MOZ_ASSERT(false, "One of the above code paths should be taken");
     }
   }
 
   RefPtr<File> file = mCachedFile;
   return file.forget();
 }
 
+already_AddRefed<Entry>
+DataTransferItem::GetAsEntry(ErrorResult& aRv)
+{
+  RefPtr<File> file = GetAsFile(aRv);
+  if (NS_WARN_IF(aRv.Failed()) || !file) {
+    return nullptr;
+  }
+
+  nsCOMPtr<nsIGlobalObject> global;
+  RefPtr<DataTransfer> dataTransfer;
+  RefPtr<DataTransferItemList> list = GetParentObject();
+  if (!list) {
+    return nullptr;
+  }
+
+  dataTransfer = list->GetParentObject();
+  if (!dataTransfer) {
+    return nullptr;
+  }
+
+  // This is annoying, but DataTransfer may have various things as parent.
+  nsCOMPtr<EventTarget> target =
+    do_QueryInterface(dataTransfer->GetParentObject());
+  if (target) {
+    global = target->GetOwnerGlobal();
+  } else {
+    nsCOMPtr<nsIDOMEvent> event =
+      do_QueryInterface(dataTransfer->GetParentObject());
+    if (event) {
+      global = event->InternalDOMEvent()->GetParentObject();
+    }
+  }
+
+  if (!global) {
+    return nullptr;
+  }
+
+  RefPtr<DOMFileSystem> fs = DOMFileSystem::Create(global);
+  RefPtr<Entry> entry;
+  BlobImpl* impl = file->Impl();
+  MOZ_ASSERT(impl);
+
+  if (impl->IsDirectory()) {
+    nsAutoString fullpath;
+    impl->GetMozFullPathInternal(fullpath, aRv);
+    if (aRv.Failed()) {
+      aRv.SuppressException();
+      return nullptr;
+    }
+
+    nsCOMPtr<nsIFile> directoryFile;
+    nsresult rv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(fullpath),
+                                        true, getter_AddRefs(directoryFile));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return nullptr;
+    }
+
+    RefPtr<Directory> directory = Directory::Create(global, directoryFile);
+    entry = new DirectoryEntry(global, directory, fs);
+  } else {
+    entry = new FileEntry(global, file, fs);
+  }
+
+  Sequence<RefPtr<Entry>> entries;
+  if (!entries.AppendElement(entry, fallible)) {
+    return nullptr;
+  }
+
+  fs->CreateRoot(entries);
+  return entry.forget();
+}
+
 already_AddRefed<File>
 DataTransferItem::CreateFileFromInputStream(nsIInputStream* aStream)
 {
   const char* key = nullptr;
   for (uint32_t i = 0; i < ArrayLength(kFileMimeNameMap); ++i) {
     if (mType.EqualsASCII(kFileMimeNameMap[i].mMimeName)) {
       key = kFileMimeNameMap[i].mFileName;
       break;
--- a/dom/events/DataTransferItem.h
+++ b/dom/events/DataTransferItem.h
@@ -9,16 +9,17 @@
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/DataTransfer.h"
 #include "mozilla/dom/DOMString.h"
 #include "mozilla/dom/File.h"
 
 namespace mozilla {
 namespace dom {
 
+class Entry;
 class FunctionStringCallback;
 
 class DataTransferItem final : public nsISupports
                              , public nsWrapperCache
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DataTransferItem);
@@ -69,16 +70,18 @@ public:
   }
   void SetKind(eKind aKind)
   {
     mKind = aKind;
   }
 
   already_AddRefed<File> GetAsFile(ErrorResult& aRv);
 
+  already_AddRefed<Entry> GetAsEntry(ErrorResult& aRv);
+
   DataTransferItemList* GetParentObject() const
   {
     return mParent;
   }
 
   nsIPrincipal* Principal() const
   {
     return mPrincipal;
--- a/dom/events/Event.h
+++ b/dom/events/Event.h
@@ -238,16 +238,23 @@ public:
   /**
    * For a given current target, returns the related target adjusted with
    * shadow DOM retargeting rules. Returns nullptr if related target
    * is not adjusted.
    */
   static nsIContent* GetShadowRelatedTarget(nsIContent* aCurrentTarget,
                                             nsIContent* aRelatedTarget);
 
+  void MarkUninitialized()
+  {
+    mEvent->mMessage = eVoidEvent;
+    mEvent->mSpecifiedEventTypeString.Truncate();
+    mEvent->mSpecifiedEventType = nullptr;
+  }
+
 protected:
 
   // Internal helper functions
   void SetEventType(const nsAString& aEventTypeArg);
   already_AddRefed<nsIContent> GetTargetFromFrame();
 
   friend class EventMessageAutoOverride;
   friend class WantsPopupControlCheck;
--- a/dom/events/EventDispatcher.cpp
+++ b/dom/events/EventDispatcher.cpp
@@ -874,17 +874,20 @@ EventDispatcher::CreateEvent(EventTarget
       aEventType.LowerCaseEqualsLiteral("textevents")) {
     return NS_NewDOMCompositionEvent(aOwner, aPresContext, nullptr);
   }
   if (aEventType.LowerCaseEqualsLiteral("mutationevent") ||
         aEventType.LowerCaseEqualsLiteral("mutationevents"))
     return NS_NewDOMMutationEvent(aOwner, aPresContext, nullptr);
   if (aEventType.LowerCaseEqualsLiteral("deviceorientationevent")) {
     DeviceOrientationEventInit init;
-    return DeviceOrientationEvent::Constructor(aOwner, EmptyString(), init);
+    RefPtr<Event> event =
+      DeviceOrientationEvent::Constructor(aOwner, EmptyString(), init);
+    event->MarkUninitialized();
+    return event.forget();
   }
   if (aEventType.LowerCaseEqualsLiteral("devicemotionevent"))
     return NS_NewDOMDeviceMotionEvent(aOwner, aPresContext, nullptr);
   if (aEventType.LowerCaseEqualsLiteral("uievent") ||
       aEventType.LowerCaseEqualsLiteral("uievents"))
     return NS_NewDOMUIEvent(aOwner, aPresContext, nullptr);
   if (aEventType.LowerCaseEqualsLiteral("event") ||
       aEventType.LowerCaseEqualsLiteral("events") ||
@@ -913,34 +916,43 @@ EventDispatcher::CreateEvent(EventTarget
     return NS_NewDOMNotifyPaintEvent(aOwner, aPresContext, nullptr);
   if (aEventType.LowerCaseEqualsLiteral("simplegestureevent"))
     return NS_NewDOMSimpleGestureEvent(aOwner, aPresContext, nullptr);
   if (aEventType.LowerCaseEqualsLiteral("beforeunloadevent"))
     return NS_NewDOMBeforeUnloadEvent(aOwner, aPresContext, nullptr);
   // XXXkhuey this is broken
   if (aEventType.LowerCaseEqualsLiteral("pagetransition")) {
     PageTransitionEventInit init;
-    return PageTransitionEvent::Constructor(aOwner, EmptyString(), init);
+    RefPtr<Event> event =
+      PageTransitionEvent::Constructor(aOwner, EmptyString(), init);
+    event->MarkUninitialized();
+    return event.forget();
   }
   if (aEventType.LowerCaseEqualsLiteral("scrollareaevent"))
     return NS_NewDOMScrollAreaEvent(aOwner, aPresContext, nullptr);
   // XXXkhuey Chrome supports popstateevent here, even though it provides no
   // initPopStateEvent method.  This is nuts ... but copying it is unlikely to
   // break the web.
   if (aEventType.LowerCaseEqualsLiteral("popstateevent")) {
     AutoJSContext cx;
     RootedDictionary<PopStateEventInit> init(cx);
-    return PopStateEvent::Constructor(aOwner, EmptyString(), init);
+    RefPtr<Event> event =
+      PopStateEvent::Constructor(aOwner, EmptyString(), init);
+    event->MarkUninitialized();
+    return event.forget();
   }
   if (aEventType.LowerCaseEqualsLiteral("touchevent") &&
       TouchEvent::PrefEnabled(nsContentUtils::GetDocShellForEventTarget(aOwner)))
     return NS_NewDOMTouchEvent(aOwner, aPresContext, nullptr);
   if (aEventType.LowerCaseEqualsLiteral("hashchangeevent")) {
     HashChangeEventInit init;
-    return HashChangeEvent::Constructor(aOwner, EmptyString(), init);
+    RefPtr<Event> event =
+      HashChangeEvent::Constructor(aOwner, EmptyString(), init);
+    event->MarkUninitialized();
+    return event.forget();
   }
   if (aEventType.LowerCaseEqualsLiteral("customevent"))
     return NS_NewDOMCustomEvent(aOwner, aPresContext, nullptr);
   if (aEventType.LowerCaseEqualsLiteral("storageevent")) {
     return NS_NewDOMStorageEvent(aOwner);
   }
 
 
--- a/dom/events/test/pointerevents/mochitest.ini
+++ b/dom/events/test/pointerevents/mochitest.ini
@@ -96,68 +96,34 @@ support-files =
   support-files = pointerevent_setpointercapture_disconnected-manual.html
 [test_pointerevent_setpointercapture_inactive_button_mouse-manual.html]
   support-files = pointerevent_setpointercapture_inactive_button_mouse-manual.html
   skip-if = (os == 'win') || (os == 'linux') # see bug1270903
 [test_pointerevent_setpointercapture_invalid_pointerid-manual.html]
   support-files = pointerevent_setpointercapture_invalid_pointerid-manual.html
 [test_pointerevent_setpointercapture_relatedtarget-manual.html]
   support-files = pointerevent_setpointercapture_relatedtarget-manual.html
-[test_pointerevent_touch-action-auto-css_touch-manual.html]
-  support-files = pointerevent_touch-action-auto-css_touch-manual.html
-  disabled = disabled
-[test_pointerevent_touch-action-button-test_touch-manual.html]
-  support-files = pointerevent_touch-action-button-test_touch-manual.html
-  disabled = disabled
-[test_pointerevent_touch-action-illegal.html]
-  support-files = pointerevent_touch-action-illegal.html
-[test_pointerevent_touch-action-inherit_child-auto-child-none_touch-manual.html]
-  support-files = pointerevent_touch-action-inherit_child-auto-child-none_touch-manual.html
-  disabled = disabled
-[test_pointerevent_touch-action-inherit_child-none_touch-manual.html]
-  support-files = pointerevent_touch-action-inherit_child-none_touch-manual.html
-  disabled = disabled
-[test_pointerevent_touch-action-inherit_child-pan-x-child-pan-x_touch-manual.html]
-  support-files = pointerevent_touch-action-inherit_child-pan-x-child-pan-x_touch-manual.html
-  disabled = disabled
-[test_pointerevent_touch-action-inherit_child-pan-x-child-pan-y_touch-manual.html]
-  support-files = pointerevent_touch-action-inherit_child-pan-x-child-pan-y_touch-manual.html
-  disabled = disabled
-[test_pointerevent_touch-action-inherit_highest-parent-none_touch-manual.html]
-  support-files = pointerevent_touch-action-inherit_highest-parent-none_touch-manual.html
-  disabled = disabled
-[test_pointerevent_touch-action-inherit_parent-none_touch-manual.html]
-  support-files = pointerevent_touch-action-inherit_parent-none_touch-manual.html
-  disabled = disabled
-[test_pointerevent_touch-action-keyboard-manual.html]
-  support-files = pointerevent_touch-action-keyboard-manual.html
-  disabled = disabled
-[test_pointerevent_touch-action-mouse-manual.html]
-  support-files = pointerevent_touch-action-mouse-manual.html
-  disabled = disabled
-[test_pointerevent_touch-action-none-css_touch-manual.html]
-  support-files = pointerevent_touch-action-none-css_touch-manual.html
-  disabled = disabled
-[test_pointerevent_touch-action-pan-x-css_touch-manual.html]
-  support-files = pointerevent_touch-action-pan-x-css_touch-manual.html
-  disabled = disabled
-[test_pointerevent_touch-action-pan-x-pan-y-pan-y_touch-manual.html]
-  support-files = pointerevent_touch-action-pan-x-pan-y-pan-y_touch-manual.html
-  disabled = disabled
-[test_pointerevent_touch-action-pan-x-pan-y_touch-manual.html]
-  support-files = pointerevent_touch-action-pan-x-pan-y_touch-manual.html
-  disabled = disabled
-[test_pointerevent_touch-action-pan-y-css_touch-manual.html]
-  support-files = pointerevent_touch-action-pan-y-css_touch-manual.html
-  disabled = disabled
-[test_pointerevent_touch-action-span-test_touch-manual.html]
-  support-files = pointerevent_touch-action-span-test_touch-manual.html
-  disabled = disabled
-[test_pointerevent_touch-action-svg-test_touch-manual.html]
-  support-files = pointerevent_touch-action-svg-test_touch-manual.html
-  disabled = disabled
-[test_pointerevent_touch-action-table-test_touch-manual.html]
-  support-files = pointerevent_touch-action-table-test_touch-manual.html
-  disabled = disabled
-[test_pointerevent_touch-action-verification.html]
-  support-files = pointerevent_touch-action-verification.html
+[test_touch_action.html]
+  # Windows touch injection doesn't work in automation, but this test can be run locally on a windows touch device.
+  skip-if = (toolkit == 'windows')
+  support-files =
+    ../../../../gfx/layers/apz/test/mochitest/apz_test_utils.js
+    ../../../../gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js
+    touch_action_helpers.js
+    pointerevent_touch-action-auto-css_touch-manual.html
+    pointerevent_touch-action-button-test_touch-manual.html
+    pointerevent_touch-action-inherit_child-auto-child-none_touch-manual.html
+    pointerevent_touch-action-inherit_child-none_touch-manual.html
+    pointerevent_touch-action-inherit_child-pan-x-child-pan-x_touch-manual.html
+    pointerevent_touch-action-inherit_child-pan-x-child-pan-y_touch-manual.html
+    pointerevent_touch-action-inherit_highest-parent-none_touch-manual.html
+    pointerevent_touch-action-inherit_parent-none_touch-manual.html
+    pointerevent_touch-action-none-css_touch-manual.html
+    pointerevent_touch-action-pan-x-css_touch-manual.html
+    pointerevent_touch-action-pan-x-pan-y-pan-y_touch-manual.html
+    pointerevent_touch-action-pan-x-pan-y_touch-manual.html
+    pointerevent_touch-action-pan-y-css_touch-manual.html
+    pointerevent_touch-action-span-test_touch-manual.html
+    pointerevent_touch-action-svg-test_touch-manual.html
+    pointerevent_touch-action-table-test_touch-manual.html
+
 [test_empty_file.html]
   disabled = disabled # Bug 1150091 - Issue with support-files
--- a/dom/events/test/pointerevents/pointerevent_touch-action-auto-css_touch-manual.html
+++ b/dom/events/test/pointerevents/pointerevent_touch-action-auto-css_touch-manual.html
@@ -1,18 +1,17 @@
 <!doctype html>
 <html>
     <head>
         <title>touch-action: auto</title>
         <meta name="viewport" content="width=device-width">
         <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
         <script src="/resources/testharness.js"></script>
-        <!--script src="/resources/testharnessreport.js"></script-->
+        <script src="/resources/testharnessreport.js"></script>
         <script src="pointerevent_support.js"></script>
-        <script src="mochitest_support_internal.js"></script>
         <style>
             #target0 {
             width: 700px;
             height: 430px;
             touch-action: auto;
             }
         </style>
     </head>
--- a/dom/events/test/pointerevents/pointerevent_touch-action-button-test_touch-manual.html
+++ b/dom/events/test/pointerevents/pointerevent_touch-action-button-test_touch-manual.html
@@ -1,19 +1,18 @@
 <!doctype html>
 <html>
     <head>
         <title>Button touch-action test</title>
         <meta name="assert" content="TA15.11 -The touch-action CSS property applies to button elements.">
         <meta name="viewport" content="width=device-width">
         <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
         <script src="/resources/testharness.js"></script>
-        <!--script src="/resources/testharnessreport.js"></script-->
+        <script src="/resources/testharnessreport.js"></script>
         <script src="pointerevent_support.js"></script>
-        <script src="mochitest_support_internal.js"></script>
         <style>
             #target0 {
             height: 150px;
             width: 200px;
             overflow-y: auto;
             background: black;
             padding: 100px;
             position: relative;
@@ -22,21 +21,19 @@
             touch-action: none;
             width: 350px;
             height: 350px;
             border: 2px solid red;
             }
         </style>
     </head>
     <body onload="run()">
-        <!--
         <h2>Pointer Events touch-action attribute support</h2>
         <h4 id="desc">Test Description: Try to scroll black element DOWN moving your touch outside of the red border. Wait for description update.</h4>
         <p>Note: this test is for touch only</p>
-        -->
         <div id="target0">
             <button>Test Button</button>
         </div>
         <br>
         <input type="button" id="btnComplete" value="Complete test">
 
         <script type='text/javascript'>
             var detected_pointertypes = {};
--- a/dom/events/test/pointerevents/pointerevent_touch-action-illegal.html
+++ b/dom/events/test/pointerevents/pointerevent_touch-action-illegal.html
@@ -1,18 +1,17 @@
 <!doctype html>
 <html>
     <head>
         <title>touch-action: illegal</title>
         <meta name="viewport" content="width=device-width">
         <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
         <script src="/resources/testharness.js"></script>
-        <!--script src="/resources/testharnessreport.js"></script-->
+        <script src="/resources/testharnessreport.js"></script>
         <script src="pointerevent_support.js"></script>
-        <script src="mochitest_support_internal.js"></script>
         <style>
             #target0 {
             width: 700px;
             height: 50px;
             touch-action: pan-x none;
             }
             #target1 {
             width: 700px;
--- a/dom/events/test/pointerevents/pointerevent_touch-action-inherit_child-auto-child-none_touch-manual.html
+++ b/dom/events/test/pointerevents/pointerevent_touch-action-inherit_child-auto-child-none_touch-manual.html
@@ -1,19 +1,18 @@
 <!doctype html>
 <html>
     <head>
         <title>touch-action: parent > child: auto > child: none</title>
         <meta name="assert" content="TA15.5 - when a user touches an element, the effect of that touch is determined by the value of the touch-action property and the default touch behaviors on the element and its ancestors. Scrollable-Parent, Child: `auto`, Grand-Child: `none`">
         <meta name="viewport" content="width=device-width">
         <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
         <script src="/resources/testharness.js"></script>
-        <!--script src="/resources/testharnessreport.js"></script-->
+        <script src="/resources/testharnessreport.js"></script>
         <script src="pointerevent_support.js"></script>
-        <script src="mochitest_support_internal.js"></script>
         <style>
             .scroller > div {
             touch-action: auto;
             }
             .scroller > div div {
             touch-action: none;
             }
         </style>
--- a/dom/events/test/pointerevents/pointerevent_touch-action-inherit_child-none_touch-manual.html
+++ b/dom/events/test/pointerevents/pointerevent_touch-action-inherit_child-none_touch-manual.html
@@ -1,19 +1,18 @@
 <!doctype html>
 <html>
     <head>
         <title>touch-action: child: none</title>
         <meta name="assert" content="TA15.9 - when a user touches an element, the effect of that touch is determined by the value of the touch-action property and the default touch behaviors on the element and its ancestors. Scrollable-Parent, Child: `none`">
         <meta name="viewport" content="width=device-width">
         <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
         <script src="/resources/testharness.js"></script>
-        <!--script src="/resources/testharnessreport.js"></script-->
+        <script src="/resources/testharnessreport.js"></script>
         <script src="pointerevent_support.js"></script>
-        <script src="mochitest_support_internal.js"></script>
         <style>
             .scroller > div {
             touch-action: none;
             }
         </style>
     </head>
     <body onload="run()">
         <h1>Pointer Events touch-action attribute support</h1>
--- a/dom/events/test/pointerevents/pointerevent_touch-action-inherit_child-pan-x-child-pan-x_touch-manual.html
+++ b/dom/events/test/pointerevents/pointerevent_touch-action-inherit_child-pan-x-child-pan-x_touch-manual.html
@@ -1,19 +1,18 @@
 <!doctype html>
 <html>
     <head>
         <title>touch-action: parent > child: pan-x > child: pan-x</title>
         <meta name="assert" content="TA15.6 - when a user touches an element, the effect of that touch is determined by the value of the touch-action property and the default touch behaviors on the element and its ancestors. Scrollable-Parent, Child: `pan-x`, Grand-Child: `pan-x`">
         <meta name="viewport" content="width=device-width">
         <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
         <script src="/resources/testharness.js"></script>
-        <!--script src="/resources/testharnessreport.js"></script-->
+        <script src="/resources/testharnessreport.js"></script>
         <script src="pointerevent_support.js"></script>
-        <script src="mochitest_support_internal.js"></script>
         <style>
             .scroller > div {
             touch-action: pan-x;
             }
             .scroller > div div {
             touch-action: pan-x;
             }
         </style>
--- a/dom/events/test/pointerevents/pointerevent_touch-action-inherit_child-pan-x-child-pan-y_touch-manual.html
+++ b/dom/events/test/pointerevents/pointerevent_touch-action-inherit_child-pan-x-child-pan-y_touch-manual.html
@@ -1,19 +1,18 @@
 <!doctype html>
 <html>
     <head>
         <title>touch-action: parent > child: pan-x > child: pan-y</title>
         <meta name="assert" content="TA15.13 - Touch action inherits child 'pan-x' -> child 'pan-y' test">
         <meta name="viewport" content="width=device-width">
         <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
         <script src="/resources/testharness.js"></script>
-        <!--script src="/resources/testharnessreport.js"></script-->
+        <script src="/resources/testharnessreport.js"></script>
         <script src="pointerevent_support.js"></script>
-        <script src="mochitest_support_internal.js"></script>
         <style>
             .scroller > div {
             touch-action: pan-x;
             }
             .scroller > div div {
             touch-action: pan-y;
             }
         </style>
--- a/dom/events/test/pointerevents/pointerevent_touch-action-inherit_highest-parent-none_touch-manual.html
+++ b/dom/events/test/pointerevents/pointerevent_touch-action-inherit_highest-parent-none_touch-manual.html
@@ -1,18 +1,17 @@
 <!doctype html>
 <html>
     <head>
         <title>touch-action: parent: none + two embedded children</title>
         <meta name="viewport" content="width=device-width">
         <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
         <script src="/resources/testharness.js"></script>
-        <!--script src="/resources/testharnessreport.js"></script-->
+        <script src="/resources/testharnessreport.js"></script>
         <script src="pointerevent_support.js"></script>
-        <script src="mochitest_support_internal.js"></script>
         <style>
             #divParent {
             touch-action: none;
             }
         </style>
     </head>
     <body onload="run()">
         <h1>Pointer Events touch-action attribute support</h1>
--- a/dom/events/test/pointerevents/pointerevent_touch-action-inherit_parent-none_touch-manual.html
+++ b/dom/events/test/pointerevents/pointerevent_touch-action-inherit_parent-none_touch-manual.html
@@ -1,19 +1,18 @@
 <!doctype html>
 <html>
     <head>
         <title>touch-action: inherit from parent: none</title>
         <meta name="assert" content="TA15.8 - when a user touches an element, the effect of that touch is determined by the value of the touch-action property and the default touch behaviors on the element and its ancestors. Scrollable-Parent: `none` Child: `auto`">
         <meta name="viewport" content="width=device-width">
         <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
         <script src="/resources/testharness.js"></script>
-        <!--script src="/resources/testharnessreport.js"></script-->
+        <script src="/resources/testharnessreport.js"></script>
         <script src="pointerevent_support.js"></script>
-        <script src="mochitest_support_internal.js"></script>
         <style>
             .scroller {
             touch-action: none;
             }
         </style>
     </head>
     <body onload="run()">
         <h1>Pointer Events touch-action attribute support</h1>
--- a/dom/events/test/pointerevents/pointerevent_touch-action-keyboard-manual.html
+++ b/dom/events/test/pointerevents/pointerevent_touch-action-keyboard-manual.html
@@ -1,18 +1,17 @@
 <!doctype html>
 <html>
     <head>
         <title>touch-action: keyboard</title>
         <meta name="viewport" content="width=device-width">
         <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
         <script src="/resources/testharness.js"></script>
-        <!--script src="/resources/testharnessreport.js"></script-->
+        <script src="/resources/testharnessreport.js"></script>
         <script src="pointerevent_support.js"></script>
-        <script src="mochitest_support_internal.js"></script>
         <style>
             #target0 {
             width: 700px;
             height: 430px;
             touch-action: none;
             }
         </style>
     </head>
--- a/dom/events/test/pointerevents/pointerevent_touch-action-mouse-manual.html
+++ b/dom/events/test/pointerevents/pointerevent_touch-action-mouse-manual.html
@@ -1,18 +1,17 @@
 <!doctype html>
 <html>
     <head>
         <title>touch-action: mouse</title>
         <meta name="viewport" content="width=device-width">
         <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
         <script src="/resources/testharness.js"></script>
-        <!--script src="/resources/testharnessreport.js"></script-->
+        <script src="/resources/testharnessreport.js"></script>
         <script src="pointerevent_support.js"></script>
-        <script src="mochitest_support_internal.js"></script>
         <style>
             #target0 {
             width: 700px;
             height: 430px;
             touch-action: none;
             }
         </style>
     </head>
--- a/dom/events/test/pointerevents/pointerevent_touch-action-none-css_touch-manual.html
+++ b/dom/events/test/pointerevents/pointerevent_touch-action-none-css_touch-manual.html
@@ -1,19 +1,18 @@
 <!doctype html>
 <html>
     <head>
         <title>touch-action: none</title>
         <meta name="assert" content="TA15.2 - With `touch-action: none` on a swiped or click/dragged element, `pointerdown+(optional pointermove)+pointerup` must be dispatched.">
         <meta name="viewport" content="width=device-width">
         <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
         <script src="/resources/testharness.js"></script>
-        <!--script src="/resources/testharnessreport.js"></script-->
+        <script src="/resources/testharnessreport.js"></script>
         <script src="pointerevent_support.js"></script>
-        <script src="mochitest_support_internal.js"></script>
         <style>
             #target0 {
             width: 700px;
             height: 430px;
             touch-action: none;
             }
         </style>
     </head>
--- a/dom/events/test/pointerevents/pointerevent_touch-action-pan-x-css_touch-manual.html
+++ b/dom/events/test/pointerevents/pointerevent_touch-action-pan-x-css_touch-manual.html
@@ -1,19 +1,18 @@
 <!doctype html>
 <html>
     <head>
         <title>touch-action: pan-x</title>
         <meta name="assert" content="TA15.3 - With `touch-action: pan-x` on a swiped or click/dragged element, only panning on the x-axis should be possible.">
         <meta name="viewport" content="width=device-width">
         <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
         <script src="/resources/testharness.js"></script>
-        <!--script src="/resources/testharnessreport.js"></script-->
+        <script src="/resources/testharnessreport.js"></script>
         <script src="pointerevent_support.js"></script>
-        <script src="mochitest_support_internal.js"></script>
         <style>
             #target0 {
             width: 700px;
             height: 430px;
             touch-action: pan-x;
             }
         </style>
     </head>
--- a/dom/events/test/pointerevents/pointerevent_touch-action-pan-x-pan-y-pan-y_touch-manual.html
+++ b/dom/events/test/pointerevents/pointerevent_touch-action-pan-x-pan-y-pan-y_touch-manual.html
@@ -1,19 +1,18 @@
 <!doctype html>
 <html>
     <head>
         <title>touch-action: parent > child: pan-x pan-y > child: pan-y</title>
         <meta name="assert" content="TA15.17 - Touch action 'pan-x pan-y' 'pan-y' test">
         <meta name="viewport" content="width=device-width">
         <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
         <script src="/resources/testharness.js"></script>
-        <!--script src="/resources/testharnessreport.js"></script-->
+        <script src="/resources/testharnessreport.js"></script>
         <script src="pointerevent_support.js"></script>
-        <script src="mochitest_support_internal.js"></script>
         <style>
             .scroller > div {
             touch-action: pan-x pan-y;
             }
             .scroller > div div {
             touch-action: pan-y;
             }
         </style>
--- a/dom/events/test/pointerevents/pointerevent_touch-action-pan-x-pan-y_touch-manual.html
+++ b/dom/events/test/pointerevents/pointerevent_touch-action-pan-x-pan-y_touch-manual.html
@@ -1,18 +1,17 @@
 <!doctype html>
 <html>
     <head>
         <title>touch-action: pan-x pan-y</title>
         <meta name="viewport" content="width=device-width">
         <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
         <script src="/resources/testharness.js"></script>
-        <!--script src="/resources/testharnessreport.js"></script-->
+        <script src="/resources/testharnessreport.js"></script>
         <script src="pointerevent_support.js"></script>
-        <script src="mochitest_support_internal.js"></script>
         <style>
             #target0 {
             width: 700px;
             height: 430px;
             touch-action: pan-x pan-y;
             }
         </style>
     </head>
--- a/dom/events/test/pointerevents/pointerevent_touch-action-pan-y-css_touch-manual.html
+++ b/dom/events/test/pointerevents/pointerevent_touch-action-pan-y-css_touch-manual.html
@@ -1,19 +1,18 @@
 <!doctype html>
 <html>
     <head>
         <title>touch-action: pan-y</title>
         <meta name="assert" content="TA15.4 - With `touch-action: pan-y` on a swiped or click/dragged element, only panning in the y-axis should be possible.">
         <meta name="viewport" content="width=device-width">
         <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
         <script src="/resources/testharness.js"></script>
-        <!--script src="/resources/testharnessreport.js"></script-->
+        <script src="/resources/testharnessreport.js"></script>
         <script src="pointerevent_support.js"></script>
-        <script src="mochitest_support_internal.js"></script>
         <style>
             #target0 {
             width: 700px;
             height: 430px;
             touch-action: pan-y;
             }
         </style>
     </head>
--- a/dom/events/test/pointerevents/pointerevent_touch-action-span-test_touch-manual.html
+++ b/dom/events/test/pointerevents/pointerevent_touch-action-span-test_touch-manual.html
@@ -1,19 +1,18 @@
 <!doctype html>
 <html>
     <head>
         <title>Span touch-action test</title>
         <meta name="assert" content="TA15.18 - The touch-action CSS property applies to all elements except non-replaced inline elements."
         <meta name="viewport" content="width=device-width">
         <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
         <script src="/resources/testharness.js"></script>
-        <!--script src="/resources/testharnessreport.js"></script-->
+        <script src="/resources/testharnessreport.js"></script>
         <script src="pointerevent_support.js"></script>
-        <script src="mochitest_support_internal.js"></script>
         <style>
             #target0 {
             height: 150px;
             width: 200px;
             overflow-y: auto;
             background: black;
             padding: 100px;
             position: relative;
--- a/dom/events/test/pointerevents/pointerevent_touch-action-svg-test_touch-manual.html
+++ b/dom/events/test/pointerevents/pointerevent_touch-action-svg-test_touch-manual.html
@@ -1,18 +1,17 @@
 <!doctype html>
 <html>
     <head>
         <title>SVG test</title>
         <meta name="viewport" content="width=device-width">
         <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
         <script src="/resources/testharness.js"></script>
-        <!--script src="/resources/testharnessreport.js"></script-->
+        <script src="/resources/testharnessreport.js"></script>
         <script src="pointerevent_support.js"></script>
-        <script src="mochitest_support_internal.js"></script>
         <style>
             #target0 {
             height: 350px;
             width: 300px;
             overflow-y: auto;
             background: black;
             padding: 100px;
             position: relative;
--- a/dom/events/test/pointerevents/pointerevent_touch-action-table-test_touch-manual.html
+++ b/dom/events/test/pointerevents/pointerevent_touch-action-table-test_touch-manual.html
@@ -1,19 +1,18 @@
 <!doctype html>
 <html>
     <head>
         <title>Table touch-action test</title>
         <meta name="assert" content="TA15.19 The touch-action CSS property applies to all elements except table rows, row groups, table columns, and column groups.">
         <meta name="viewport" content="width=device-width">
         <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
         <script src="/resources/testharness.js"></script>
-        <!--script src="/resources/testharnessreport.js"></script-->
+        <script src="/resources/testharnessreport.js"></script>
         <script src="pointerevent_support.js"></script>
-        <script src="mochitest_support_internal.js"></script>
         <style>
             #target0 {
             height: 150px;
             width: 200px;
             overflow-y: auto;
             background: black;
             padding: 100px;
             position: relative;
--- a/dom/events/test/pointerevents/pointerevent_touch-action-verification.html
+++ b/dom/events/test/pointerevents/pointerevent_touch-action-verification.html
@@ -6,19 +6,18 @@
         auto: The user agent MAY determine any permitted touch behaviors, such as panning and zooming manipulations of the viewport, for touches that begin on the element. 
         none: Touches that begin on the element MUST NOT trigger default touch behaviors.
         pan-x: The user agent MAY consider touches that begin on the element only for the purposes of horizontally scrolling the element's nearest ancestor with horizontally scrollable content.
         pan-y: The user agent MAY consider touches that begin on the element only for the purposes of vertically scrolling the element's nearest ancestor with vertically scrollable content.
         manipulation: The user agent MAY consider touches that begin on the element only for the purposes of scrolling and continuous zooming. Any additional behaviors supported by auto are out of scope for this specification.">
         <meta name="viewport" content="width=device-width">
         <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
         <script src="/resources/testharness.js"></script>
-        <!--script src="/resources/testharnessreport.js"></script-->
+        <script src="/resources/testharnessreport.js"></script>
         <script src="pointerevent_support.js"></script>
-        <script src="mochitest_support_internal.js"></script>
         <style>
             #target0 {
             width: 700px;
             height: 20px;
             touch-action: auto;
             }
             #target1 {
             width: 700px;
deleted file mode 100644
--- a/dom/events/test/pointerevents/test_pointerevent_touch-action-auto-css_touch-manual.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
--->
-  <head>
-    <meta charset="utf-8">
-    <title>Test for Bug 1000870</title>
-    <meta name="author" content="Maksim Lebedev" />
-    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-    <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-    <script type="text/javascript" src="mochitest_support_external.js"></script>
-    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-    <script type="text/javascript">
-      SimpleTest.waitForExplicitFinish();
-      function startTest() {
-        var iframe = document.getElementById("testFrame");
-        iframe.src = "pointerevent_touch-action-auto-css_touch-manual.html";
-      }
-      function executeTest(int_win) {
-        sendPointerEvent(int_win, "target0", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE, {button:-1});
-      }
-    </script>
-  </head>
-  <body>
-    <iframe id="testFrame" height="800" width="1000"></iframe>
-  </body>
-</html>
deleted file mode 100644
--- a/dom/events/test/pointerevents/test_pointerevent_touch-action-button-test_touch-manual.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
--->
-  <head>
-    <meta charset="utf-8">
-    <title>Test for Bug 1000870</title>
-    <meta name="author" content="Maksim Lebedev" />
-    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-    <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-    <script type="text/javascript" src="mochitest_support_external.js"></script>
-    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-    <script type="text/javascript">
-      SimpleTest.waitForExplicitFinish();
-      function startTest() {
-        var iframe = document.getElementById("testFrame");
-        iframe.src = "pointerevent_touch-action-button-test_touch-manual.html";
-      }
-      function executeTest(int_win) {
-        sendPointerEvent(int_win, "target0", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE, {button:-1});
-      }
-    </script>
-  </head>
-  <body>
-    <iframe id="testFrame" height="800" width="1000"></iframe>
-  </body>
-</html>
deleted file mode 100644
--- a/dom/events/test/pointerevents/test_pointerevent_touch-action-illegal.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
--->
-  <head>
-    <meta charset="utf-8">
-    <title>Test for Bug 1000870</title>
-    <meta name="author" content="Maksim Lebedev" />
-    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-    <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-    <script type="text/javascript" src="mochitest_support_external.js"></script>
-    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-    <script type="text/javascript">
-      SimpleTest.waitForExplicitFinish();
-      function startTest() {
-        var iframe = document.getElementById("testFrame");
-        iframe.src = "pointerevent_touch-action-illegal.html";
-      }
-      function executeTest(int_win) {
-        // Function should be, but can be empty
-      }
-    </script>
-  </head>
-  <body>
-    <iframe id="testFrame" height="800" width="1000"></iframe>
-  </body>
-</html>
deleted file mode 100644
--- a/dom/events/test/pointerevents/test_pointerevent_touch-action-inherit_child-auto-child-none_touch-manual.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
--->
-  <head>
-    <meta charset="utf-8">
-    <title>Test for Bug 1000870</title>
-    <meta name="author" content="Maksim Lebedev" />
-    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-    <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-    <script type="text/javascript" src="mochitest_support_external.js"></script>
-    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-    <script type="text/javascript">
-      SimpleTest.waitForExplicitFinish();
-      function startTest() {
-        var iframe = document.getElementById("testFrame");
-        iframe.src = "pointerevent_touch-action-inherit_child-auto-child-none_touch-manual.html";
-      }
-      function executeTest(int_win) {
-        sendPointerEvent(int_win, "target0", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE, {button:-1});
-      }
-    </script>
-  </head>
-  <body>
-    <iframe id="testFrame" height="800" width="1000"></iframe>
-  </body>
-</html>
deleted file mode 100644
--- a/dom/events/test/pointerevents/test_pointerevent_touch-action-inherit_child-none_touch-manual.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
--->
-  <head>
-    <meta charset="utf-8">
-    <title>Test for Bug 1000870</title>
-    <meta name="author" content="Maksim Lebedev" />
-    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-    <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-    <script type="text/javascript" src="mochitest_support_external.js"></script>
-    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-    <script type="text/javascript">
-      SimpleTest.waitForExplicitFinish();
-      function startTest() {
-        var iframe = document.getElementById("testFrame");
-        iframe.src = "pointerevent_touch-action-inherit_child-none_touch-manual.html";
-      }
-      function executeTest(int_win) {
-        sendPointerEvent(int_win, "target0", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE, {button:-1});
-      }
-    </script>
-  </head>
-  <body>
-    <iframe id="testFrame" height="800" width="1000"></iframe>
-  </body>
-</html>
deleted file mode 100644
--- a/dom/events/test/pointerevents/test_pointerevent_touch-action-inherit_child-pan-x-child-pan-x_touch-manual.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
--->
-  <head>
-    <meta charset="utf-8">
-    <title>Test for Bug 1000870</title>
-    <meta name="author" content="Maksim Lebedev" />
-    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-    <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-    <script type="text/javascript" src="mochitest_support_external.js"></script>
-    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-    <script type="text/javascript">
-      SimpleTest.waitForExplicitFinish();
-      function startTest() {
-        var iframe = document.getElementById("testFrame");
-        iframe.src = "pointerevent_touch-action-inherit_child-pan-x-child-pan-x_touch-manual.html";
-      }
-      function executeTest(int_win) {
-        sendPointerEvent(int_win, "target0", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE, {button:-1});
-      }
-    </script>
-  </head>
-  <body>
-    <iframe id="testFrame" height="800" width="1000"></iframe>
-  </body>
-</html>
deleted file mode 100644
--- a/dom/events/test/pointerevents/test_pointerevent_touch-action-inherit_child-pan-x-child-pan-y_touch-manual.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
--->
-  <head>
-    <meta charset="utf-8">
-    <title>Test for Bug 1000870</title>
-    <meta name="author" content="Maksim Lebedev" />
-    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-    <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-    <script type="text/javascript" src="mochitest_support_external.js"></script>
-    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-    <script type="text/javascript">
-      SimpleTest.waitForExplicitFinish();
-      function startTest() {
-        var iframe = document.getElementById("testFrame");
-        iframe.src = "pointerevent_touch-action-inherit_child-pan-x-child-pan-y_touch-manual.html";
-      }
-      function executeTest(int_win) {
-        sendPointerEvent(int_win, "target0", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE, {button:-1});
-      }
-    </script>
-  </head>
-  <body>
-    <iframe id="testFrame" height="800" width="1000"></iframe>
-  </body>
-</html>
deleted file mode 100644
--- a/dom/events/test/pointerevents/test_pointerevent_touch-action-inherit_highest-parent-none_touch-manual.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
--->
-  <head>
-    <meta charset="utf-8">
-    <title>Test for Bug 1000870</title>
-    <meta name="author" content="Maksim Lebedev" />
-    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-    <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-    <script type="text/javascript" src="mochitest_support_external.js"></script>
-    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-    <script type="text/javascript">
-      SimpleTest.waitForExplicitFinish();
-      function startTest() {
-        var iframe = document.getElementById("testFrame");
-        iframe.src = "pointerevent_touch-action-inherit_highest-parent-none_touch-manual.html";
-      }
-      function executeTest(int_win) {
-        sendPointerEvent(int_win, "target0", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE, {button:-1});
-      }
-    </script>
-  </head>
-  <body>
-    <iframe id="testFrame" height="800" width="1000"></iframe>
-  </body>
-</html>
deleted file mode 100644
--- a/dom/events/test/pointerevents/test_pointerevent_touch-action-inherit_parent-none_touch-manual.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
--->
-  <head>
-    <meta charset="utf-8">
-    <title>Test for Bug 1000870</title>
-    <meta name="author" content="Maksim Lebedev" />
-    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-    <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-    <script type="text/javascript" src="mochitest_support_external.js"></script>
-    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-    <script type="text/javascript">
-      SimpleTest.waitForExplicitFinish();
-      function startTest() {
-        var iframe = document.getElementById("testFrame");
-        iframe.src = "pointerevent_touch-action-inherit_parent-none_touch-manual.html";
-      }
-      function executeTest(int_win) {
-        sendPointerEvent(int_win, "target0", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE, {button:-1});
-      }
-    </script>
-  </head>
-  <body>
-    <iframe id="testFrame" height="800" width="1000"></iframe>
-  </body>
-</html>
deleted file mode 100644
--- a/dom/events/test/pointerevents/test_pointerevent_touch-action-keyboard-manual.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
--->
-  <head>
-    <meta charset="utf-8">
-    <title>Test for Bug 1000870</title>
-    <meta name="author" content="Maksim Lebedev" />
-    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-    <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-    <script type="text/javascript" src="mochitest_support_external.js"></script>
-    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-    <script type="text/javascript">
-      SimpleTest.waitForExplicitFinish();
-      function startTest() {
-        var iframe = document.getElementById("testFrame");
-        iframe.src = "pointerevent_touch-action-keyboard-manual.html";
-      }
-      function executeTest(int_win) {
-        sendPointerEvent(int_win, "target0", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE, {button:-1});
-      }
-    </script>
-  </head>
-  <body>
-    <iframe id="testFrame" height="800" width="1000"></iframe>
-  </body>
-</html>
deleted file mode 100644
--- a/dom/events/test/pointerevents/test_pointerevent_touch-action-mouse-manual.html
+++ /dev/null
@@ -1,31 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
--->
-  <head>
-    <meta charset="utf-8">
-    <title>Test for Bug 1000870</title>
-    <meta name="author" content="Maksim Lebedev" />
-    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-    <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-    <script type="text/javascript" src="mochitest_support_external.js"></script>
-    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-    <script type="text/javascript">
-      SimpleTest.waitForExplicitFinish();
-      function startTest() {
-        var iframe = document.getElementById("testFrame");
-        iframe.src = "pointerevent_touch-action-mouse-manual.html";
-      }
-      function executeTest(int_win) {
-        sendPointerEvent(int_win, "target0", "pointerdown", MouseEvent.MOZ_SOURCE_MOUSE);
-        sendPointerEvent(int_win, "target0", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
-        sendPointerEvent(int_win, "log",     "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
-        sendPointerEvent(int_win, "log",     "pointerup",   MouseEvent.MOZ_SOURCE_MOUSE);
-      }
-    </script>
-  </head>
-  <body>
-    <iframe id="testFrame" height="800" width="1000"></iframe>
-  </body>
-</html>
deleted file mode 100644
--- a/dom/events/test/pointerevents/test_pointerevent_touch-action-none-css_touch-manual.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
--->
-  <head>
-    <meta charset="utf-8">
-    <title>Test for Bug 1000870</title>
-    <meta name="author" content="Maksim Lebedev" />
-    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-    <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-    <script type="text/javascript" src="mochitest_support_external.js"></script>
-    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-    <script type="text/javascript">
-      SimpleTest.waitForExplicitFinish();
-      function startTest() {
-        var iframe = document.getElementById("testFrame");
-        iframe.src = "pointerevent_touch-action-none-css_touch-manual.html";
-      }
-      function executeTest(int_win) {
-        sendPointerEvent(int_win, "target0", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE, {button:-1});
-      }
-    </script>
-  </head>
-  <body>
-    <iframe id="testFrame" height="800" width="1000"></iframe>
-  </body>
-</html>
deleted file mode 100644
--- a/dom/events/test/pointerevents/test_pointerevent_touch-action-pan-x-css_touch-manual.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
--->
-  <head>
-    <meta charset="utf-8">
-    <title>Test for Bug 1000870</title>
-    <meta name="author" content="Maksim Lebedev" />
-    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-    <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-    <script type="text/javascript" src="mochitest_support_external.js"></script>
-    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-    <script type="text/javascript">
-      SimpleTest.waitForExplicitFinish();
-      function startTest() {
-        var iframe = document.getElementById("testFrame");
-        iframe.src = "pointerevent_touch-action-pan-x-css_touch-manual.html";
-      }
-      function executeTest(int_win) {
-        sendPointerEvent(int_win, "target0", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE, {button:-1});
-      }
-    </script>
-  </head>
-  <body>
-    <iframe id="testFrame" height="800" width="1000"></iframe>
-  </body>
-</html>
deleted file mode 100644
--- a/dom/events/test/pointerevents/test_pointerevent_touch-action-pan-x-pan-y-pan-y_touch-manual.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
--->
-  <head>
-    <meta charset="utf-8">
-    <title>Test for Bug 1000870</title>
-    <meta name="author" content="Maksim Lebedev" />
-    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-    <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-    <script type="text/javascript" src="mochitest_support_external.js"></script>
-    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-    <script type="text/javascript">
-      SimpleTest.waitForExplicitFinish();
-      function startTest() {
-        var iframe = document.getElementById("testFrame");
-        iframe.src = "pointerevent_touch-action-pan-x-pan-y-pan-y_touch-manual.html";
-      }
-      function executeTest(int_win) {
-        sendPointerEvent(int_win, "target0", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE, {button:-1});
-      }
-    </script>
-  </head>
-  <body>
-    <iframe id="testFrame" height="800" width="1000"></iframe>
-  </body>
-</html>
deleted file mode 100644
--- a/dom/events/test/pointerevents/test_pointerevent_touch-action-pan-x-pan-y_touch-manual.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
--->
-  <head>
-    <meta charset="utf-8">
-    <title>Test for Bug 1000870</title>
-    <meta name="author" content="Maksim Lebedev" />
-    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-    <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-    <script type="text/javascript" src="mochitest_support_external.js"></script>
-    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-    <script type="text/javascript">
-      SimpleTest.waitForExplicitFinish();
-      function startTest() {
-        var iframe = document.getElementById("testFrame");
-        iframe.src = "pointerevent_touch-action-pan-x-pan-y_touch-manual.html";
-      }
-      function executeTest(int_win) {
-        sendPointerEvent(int_win, "target0", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE, {button:-1});
-      }
-    </script>
-  </head>
-  <body>
-    <iframe id="testFrame" height="800" width="1000"></iframe>
-  </body>
-</html>
deleted file mode 100644
--- a/dom/events/test/pointerevents/test_pointerevent_touch-action-pan-y-css_touch-manual.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
--->
-  <head>
-    <meta charset="utf-8">
-    <title>Test for Bug 1000870</title>
-    <meta name="author" content="Maksim Lebedev" />
-    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-    <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-    <script type="text/javascript" src="mochitest_support_external.js"></script>
-    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-    <script type="text/javascript">
-      SimpleTest.waitForExplicitFinish();
-      function startTest() {
-        var iframe = document.getElementById("testFrame");
-        iframe.src = "pointerevent_touch-action-pan-y-css_touch-manual.html";
-      }
-      function executeTest(int_win) {
-        sendPointerEvent(int_win, "target0", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE, {button:-1});
-      }
-    </script>
-  </head>
-  <body>
-    <iframe id="testFrame" height="800" width="1000"></iframe>
-  </body>
-</html>
deleted file mode 100644
--- a/dom/events/test/pointerevents/test_pointerevent_touch-action-span-test_touch-manual.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
--->
-  <head>
-    <meta charset="utf-8">
-    <title>Test for Bug 1000870</title>
-    <meta name="author" content="Maksim Lebedev" />
-    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-    <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-    <script type="text/javascript" src="mochitest_support_external.js"></script>
-    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-    <script type="text/javascript">
-      SimpleTest.waitForExplicitFinish();
-      function startTest() {
-        var iframe = document.getElementById("testFrame");
-        iframe.src = "pointerevent_touch-action-span-test_touch-manual.html";
-      }
-      function executeTest(int_win) {
-        sendPointerEvent(int_win, "target0", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE, {button:-1});
-      }
-    </script>
-  </head>
-  <body>
-    <iframe id="testFrame" height="800" width="1000"></iframe>
-  </body>
-</html>
deleted file mode 100644
--- a/dom/events/test/pointerevents/test_pointerevent_touch-action-svg-test_touch-manual.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
--->
-  <head>
-    <meta charset="utf-8">
-    <title>Test for Bug 1000870</title>
-    <meta name="author" content="Maksim Lebedev" />
-    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-    <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-    <script type="text/javascript" src="mochitest_support_external.js"></script>
-    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-    <script type="text/javascript">
-      SimpleTest.waitForExplicitFinish();
-      function startTest() {
-        var iframe = document.getElementById("testFrame");
-        iframe.src = "pointerevent_touch-action-svg-test_touch-manual.html";
-      }
-      function executeTest(int_win) {
-        sendPointerEvent(int_win, "target0", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE, {button:-1});
-      }
-    </script>
-  </head>
-  <body>
-    <iframe id="testFrame" height="800" width="1000"></iframe>
-  </body>
-</html>
deleted file mode 100644
--- a/dom/events/test/pointerevents/test_pointerevent_touch-action-table-test_touch-manual.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
--->
-  <head>
-    <meta charset="utf-8">
-    <title>Test for Bug 1000870</title>
-    <meta name="author" content="Maksim Lebedev" />
-    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-    <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-    <script type="text/javascript" src="mochitest_support_external.js"></script>
-    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-    <script type="text/javascript">
-      SimpleTest.waitForExplicitFinish();
-      function startTest() {
-        var iframe = document.getElementById("testFrame");
-        iframe.src = "pointerevent_touch-action-table-test_touch-manual.html";
-      }
-      function executeTest(int_win) {
-        sendPointerEvent(int_win, "target0", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE, {button:-1});
-      }
-    </script>
-  </head>
-  <body>
-    <iframe id="testFrame" height="800" width="1000"></iframe>
-  </body>
-</html>
deleted file mode 100644
--- a/dom/events/test/pointerevents/test_pointerevent_touch-action-verification.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
--->
-  <head>
-    <meta charset="utf-8">
-    <title>Test for Bug 1000870</title>
-    <meta name="author" content="Maksim Lebedev" />
-    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-    <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-    <script type="text/javascript" src="mochitest_support_external.js"></script>
-    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-    <script type="text/javascript">
-      SimpleTest.waitForExplicitFinish();
-      function startTest() {
-        var iframe = document.getElementById("testFrame");
-        iframe.src = "pointerevent_touch-action-verification.html";
-      }
-      function executeTest(int_win) {
-        // Function should be, but can be empty
-      }
-    </script>
-  </head>
-  <body>
-    <iframe id="testFrame" height="800" width="1000"></iframe>
-  </body>
-</html>
new file mode 100644
--- /dev/null
+++ b/dom/events/test/pointerevents/test_touch_action.html
@@ -0,0 +1,99 @@
+<!DOCTYPE html>
+<html>
+ <head>
+  <meta charset="utf-8">
+  <title>W3C pointerevents/*touch-action*.html tests in Mochitest form</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="apz_test_utils.js"></script>
+  <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript">
+var apz_touch_action_prefs = [
+  // Obviously we need touch-action support enabled for testing touch-action.
+  ["layout.css.touch_action.enabled", true],
+  // Dropping the touch slop to 0 makes the tests easier to write because
+  // we can just do a one-pixel drag to get over the pan threshold rather
+  // than having to hard-code some larger value.
+  ["apz.touch_start_tolerance", "0.0"],
+  // The touchstart from the drag can turn into a long-tap if the touch-move
+  // events get held up. Try to prevent that by making long-taps require
+  // a 10 second hold. Note that we also cannot enable chaos mode on this
+  // test for this reason, since chaos mode can cause the long-press timer
+  // to fire sooner than the pref dictates.
+  ["ui.click_hold_context_menus.delay", 10000],
+  // The subtests in this test do touch-drags to pan the page, but we don't
+  // want those pans to turn into fling animations, so we increase the
+  // fling-stop threshold velocity to absurdly high.
+  ["apz.fling_stopped_threshold", "10000"],
+  // The helper_div_pan's div gets a displayport on scroll, but if the
+  // test takes too long the displayport can expire before the new scroll
+  // position is synced back to the main thread. So we disable displayport
+  // expiry for these tests.
+  ["apz.displayport_expiry_ms", 0],
+];
+
+function apzScriptInjector(name) {
+  return function(childWin) {
+    childWin._ACTIVE_TEST_NAME = name;
+    injectScript('/tests/SimpleTest/paint_listener.js', childWin)()
+    .then(injectScript('apz_test_utils.js', childWin))
+    .then(injectScript('apz_test_native_event_utils.js', childWin))
+    .then(injectScript('touch_action_helpers.js', childWin));
+  };
+}
+
+// Each of these test names is turned into an entry in the |subtests| array
+// below.
+var testnames = [
+  'pointerevent_touch-action-auto-css_touch-manual',
+  'pointerevent_touch-action-button-test_touch-manual',
+  // this one runs as a web-platform-test since it's not a manual test
+  // 'pointerevent_touch-action-illegal',
+  'pointerevent_touch-action-inherit_child-auto-child-none_touch-manual',
+  'pointerevent_touch-action-inherit_child-none_touch-manual',
+  'pointerevent_touch-action-inherit_child-pan-x-child-pan-x_touch-manual',
+  'pointerevent_touch-action-inherit_child-pan-x-child-pan-y_touch-manual',
+  'pointerevent_touch-action-inherit_highest-parent-none_touch-manual',
+  'pointerevent_touch-action-inherit_parent-none_touch-manual',
+  // the keyboard-manual and mouse-manual tests require simulating keyboard/
+  // mouse input, rather than touch, so we're not going to do that here.
+  //'pointerevent_touch-action-keyboard-manual',
+  //'pointerevent_touch-action-mouse-manual',
+  'pointerevent_touch-action-none-css_touch-manual',
+  'pointerevent_touch-action-pan-x-css_touch-manual',
+  'pointerevent_touch-action-pan-x-pan-y-pan-y_touch-manual',
+  'pointerevent_touch-action-pan-x-pan-y_touch-manual',
+  'pointerevent_touch-action-pan-y-css_touch-manual',
+  'pointerevent_touch-action-span-test_touch-manual',
+  'pointerevent_touch-action-svg-test_touch-manual',
+  'pointerevent_touch-action-table-test_touch-manual',
+  // this one runs as a web-platform-test since it's not a manual test
+  //'pointerevent_touch-action-verification',
+];
+
+// Each entry in |subtests| is loaded in a new window. When loaded, it runs
+// the function returned by apzScriptInjector, which injects some helper JS
+// files into the vanilla unmodified W3C testcase, and simulates the necessary
+// user input to run the test.
+var subtests = [];
+for (var name of testnames) {
+  subtests.push({
+    'file': name + '.html',
+    'prefs': apz_touch_action_prefs,
+    'onload': apzScriptInjector(name),
+  });
+}
+
+if (isApzEnabled()) {
+  SimpleTest.waitForExplicitFinish();
+  window.onload = function() {
+    runSubtestsSeriallyInFreshWindows(subtests)
+    .then(SimpleTest.finish);
+  };
+}
+
+  </script>
+ </head>
+ <body>
+ </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/events/test/pointerevents/touch_action_helpers.js
@@ -0,0 +1,206 @@
+// Some common helpers
+
+function touchActionSetup(testDriver) {
+  add_completion_callback(subtestDone);
+  document.body.addEventListener('touchend', testDriver, { passive: true });
+}
+
+function touchScrollRight(aSelector = '#target0', aX = 20, aY = 20) {
+  var target = document.querySelector(aSelector);
+  return ok(synthesizeNativeTouchDrag(target, aX + 40, aY, -40, 0), "Synthesized horizontal drag");
+}
+
+function touchScrollDown(aSelector = '#target0', aX = 20, aY = 20) {
+  var target = document.querySelector(aSelector);
+  return ok(synthesizeNativeTouchDrag(target, aX, aY + 40, 0, -40), "Synthesized vertical drag");
+}
+
+function tapComplete() {
+  var button = document.getElementById('btnComplete');
+  return button.click();
+}
+
+// The main body functions to simulate the input events required for the named test
+
+function* pointerevent_touch_action_auto_css_touch_manual(testDriver) {
+  touchActionSetup(testDriver);
+
+  yield touchScrollRight();
+  yield waitForApzFlushedRepaints(testDriver);
+  yield touchScrollDown();
+}
+
+function* pointerevent_touch_action_button_test_touch_manual(testDriver) {
+  touchActionSetup(testDriver);
+
+  yield touchScrollDown();
+  yield waitForApzFlushedRepaints(testDriver);
+  yield setTimeout(testDriver, 2 * scrollReturnInterval);
+  yield touchScrollRight();
+  yield waitForApzFlushedRepaints(testDriver);
+  yield setTimeout(testDriver, 2 * scrollReturnInterval);
+  yield touchScrollDown('#target0 > button');
+  yield waitForApzFlushedRepaints(testDriver);
+  yield touchScrollRight('#target0 > button');
+  yield waitForApzFlushedRepaints(testDriver);
+  yield tapComplete();
+}
+
+function* pointerevent_touch_action_inherit_child_auto_child_none_touch_manual(testDriver) {
+  touchActionSetup(testDriver);
+
+  yield touchScrollDown('#target0 > div div');
+  yield waitForApzFlushedRepaints(testDriver);
+  yield touchScrollRight('#target0 > div div');
+  yield waitForApzFlushedRepaints(testDriver);
+  yield tapComplete();
+}
+
+function* pointerevent_touch_action_inherit_child_none_touch_manual(testDriver) {
+  touchActionSetup(testDriver);
+
+  yield touchScrollDown('#target0 > div');
+  yield waitForApzFlushedRepaints(testDriver);
+  yield touchScrollRight('#target0 > div');
+  yield waitForApzFlushedRepaints(testDriver);
+  yield tapComplete();
+}
+
+function* pointerevent_touch_action_inherit_child_pan_x_child_pan_x_touch_manual(testDriver) {
+  touchActionSetup(testDriver);
+
+  yield touchScrollDown('#target0 > div div');
+  yield waitForApzFlushedRepaints(testDriver);
+  yield touchScrollRight('#target0 > div div');
+  yield waitForApzFlushedRepaints(testDriver);
+  yield tapComplete();
+}
+
+function* pointerevent_touch_action_inherit_child_pan_x_child_pan_y_touch_manual(testDriver) {
+  touchActionSetup(testDriver);
+
+  yield touchScrollDown('#target0 > div div');
+  yield waitForApzFlushedRepaints(testDriver);
+  yield touchScrollRight('#target0 > div div');
+  yield waitForApzFlushedRepaints(testDriver);
+  yield tapComplete();
+}
+
+function* pointerevent_touch_action_inherit_highest_parent_none_touch_manual(testDriver) {
+  touchActionSetup(testDriver);
+
+  yield touchScrollDown('#target0 > div');
+  yield waitForApzFlushedRepaints(testDriver);
+  yield touchScrollRight('#target0 > div');
+}
+
+function* pointerevent_touch_action_inherit_parent_none_touch_manual(testDriver) {
+  touchActionSetup(testDriver);
+
+  yield touchScrollDown();
+  yield waitForApzFlushedRepaints(testDriver);
+  yield touchScrollRight();
+  yield waitForApzFlushedRepaints(testDriver);
+  yield tapComplete();
+}
+
+function* pointerevent_touch_action_none_css_touch_manual(testDriver) {
+  touchActionSetup(testDriver);
+
+  yield touchScrollDown();
+  yield waitForApzFlushedRepaints(testDriver);
+  yield touchScrollRight();
+  yield waitForApzFlushedRepaints(testDriver);
+  yield tapComplete();
+}
+
+function* pointerevent_touch_action_pan_x_css_touch_manual(testDriver) {
+  touchActionSetup(testDriver);
+
+  yield touchScrollDown();
+  yield waitForApzFlushedRepaints(testDriver);
+  yield touchScrollRight();
+  yield waitForApzFlushedRepaints(testDriver);
+  yield tapComplete();
+}
+
+function* pointerevent_touch_action_pan_x_pan_y_pan_y_touch_manual(testDriver) {
+  touchActionSetup(testDriver);
+
+  yield touchScrollDown('#target0 > div div');
+  yield waitForApzFlushedRepaints(testDriver);
+  yield touchScrollRight('#target0 > div div');
+  yield waitForApzFlushedRepaints(testDriver);
+  yield tapComplete();
+}
+
+function* pointerevent_touch_action_pan_x_pan_y_touch_manual(testDriver) {
+  touchActionSetup(testDriver);
+
+  yield touchScrollDown();
+  yield waitForApzFlushedRepaints(testDriver);
+  yield touchScrollRight();
+}
+
+function* pointerevent_touch_action_pan_y_css_touch_manual(testDriver) {
+  touchActionSetup(testDriver);
+
+  yield touchScrollDown();
+  yield waitForApzFlushedRepaints(testDriver);
+  yield touchScrollRight();
+  yield waitForApzFlushedRepaints(testDriver);
+  yield tapComplete();
+}
+
+function* pointerevent_touch_action_span_test_touch_manual(testDriver) {
+  touchActionSetup(testDriver);
+
+  yield touchScrollDown();
+  yield waitForApzFlushedRepaints(testDriver);
+  yield setTimeout(testDriver, 2 * scrollReturnInterval);
+  yield touchScrollRight();
+  yield waitForApzFlushedRepaints(testDriver);
+  yield setTimeout(testDriver, 2 * scrollReturnInterval);
+  yield touchScrollDown('#testspan');
+  yield waitForApzFlushedRepaints(testDriver);
+  yield touchScrollRight('#testspan');
+  yield waitForApzFlushedRepaints(testDriver);
+  yield tapComplete();
+}
+
+function* pointerevent_touch_action_svg_test_touch_manual(testDriver) {
+  touchActionSetup(testDriver);
+
+  yield touchScrollDown();
+  yield waitForApzFlushedRepaints(testDriver);
+  yield setTimeout(testDriver, 2 * scrollReturnInterval);
+  yield touchScrollRight();
+  yield waitForApzFlushedRepaints(testDriver);
+  yield setTimeout(testDriver, 2 * scrollReturnInterval);
+  yield touchScrollDown('#target0', 250, 250);
+  yield waitForApzFlushedRepaints(testDriver);
+  yield touchScrollRight('#target0', 250, 250);
+  yield waitForApzFlushedRepaints(testDriver);
+  yield tapComplete();
+}
+
+function* pointerevent_touch_action_table_test_touch_manual(testDriver) {
+  touchActionSetup(testDriver);
+
+  yield touchScrollDown('#row1');
+  yield waitForApzFlushedRepaints(testDriver);
+  yield setTimeout(testDriver, 2 * scrollReturnInterval);
+  yield touchScrollRight('#row1');
+  yield waitForApzFlushedRepaints(testDriver);
+  yield setTimeout(testDriver, 2 * scrollReturnInterval);
+  yield touchScrollDown('#cell3');
+  yield waitForApzFlushedRepaints(testDriver);
+  yield touchScrollRight('#cell3');
+  yield waitForApzFlushedRepaints(testDriver);
+  yield tapComplete();
+}
+
+// This the stuff that runs the appropriate body function above
+
+var test = eval(_ACTIVE_TEST_NAME.replace(/-/g, '_'));
+waitUntilApzStable().then(runContinuation(test));
--- a/dom/filesystem/compat/FileEntry.cpp
+++ b/dom/filesystem/compat/FileEntry.cpp
@@ -70,16 +70,26 @@ FileEntry::GetName(nsAString& aName, Err
 {
   mFile->GetName(aName);
 }
 
 void
 FileEntry::GetFullPath(nsAString& aPath, ErrorResult& aRv) const
 {
   mFile->GetPath(aPath);
+  if (aPath.IsEmpty()) {
+    // We're under the root directory. webkitRelativePath
+    // (implemented as GetPath) is for cases when file is selected because its
+    // ancestor directory is selected. But that is not the case here, so need to
+    // manually prepend '/'.
+    nsAutoString name;
+    mFile->GetName(name);
+    aPath.AssignLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL);
+    aPath.Append(name);
+  }
 }
 
 void
 FileEntry::CreateWriter(VoidCallback& aSuccessCallback,
                         const Optional<OwningNonNull<ErrorCallback>>& aErrorCallback) const
 {
   ErrorCallbackHelper::Call(GetParentObject(), aErrorCallback,
                             NS_ERROR_DOM_SECURITY_ERR);
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -2834,52 +2834,62 @@ HTMLInputElement::GetDisplayFileName(nsA
 }
 
 void
 HTMLInputElement::SetFilesOrDirectories(const nsTArray<OwningFileOrDirectory>& aFilesOrDirectories,
                                         bool aSetValueChanged)
 {
   ClearGetFilesHelpers();
 
+  if (Preferences::GetBool("dom.webkitBlink.filesystem.enabled", false)) {
+    HTMLInputElementBinding::ClearCachedWebkitEntriesValue(this);
+    mEntries.Clear();
+  }
+
   mFilesOrDirectories.Clear();
   mFilesOrDirectories.AppendElements(aFilesOrDirectories);
 
   AfterSetFilesOrDirectories(aSetValueChanged);
 }
 
 void
 HTMLInputElement::SetFiles(nsIDOMFileList* aFiles,
                            bool aSetValueChanged)
 {
   RefPtr<FileList> files = static_cast<FileList*>(aFiles);
   mFilesOrDirectories.Clear();
   ClearGetFilesHelpers();
 
+  if (Preferences::GetBool("dom.webkitBlink.filesystem.enabled", false)) {
+    HTMLInputElementBinding::ClearCachedWebkitEntriesValue(this);
+    mEntries.Clear();
+  }
+
   if (aFiles) {
     uint32_t listLength;
     aFiles->GetLength(&listLength);
     for (uint32_t i = 0; i < listLength; i++) {
       OwningFileOrDirectory* element = mFilesOrDirectories.AppendElement();
       element->SetAsFile() = files->Item(i);
     }
   }
 
   AfterSetFilesOrDirectories(aSetValueChanged);
 }
 
 // This method is used for testing only.
 void
 HTMLInputElement::MozSetDndFilesAndDirectories(const nsTArray<OwningFileOrDirectory>& aFilesOrDirectories)
 {
+  SetFilesOrDirectories(aFilesOrDirectories, true);
+
   if (Preferences::GetBool("dom.webkitBlink.filesystem.enabled", false)) {
     UpdateEntries(aFilesOrDirectories);
   }
 
-  SetFilesOrDirectories(aFilesOrDirectories, true);
-
   RefPtr<DispatchChangeEventCallback> dispatchChangeEventCallback =
     new DispatchChangeEventCallback(this);
 
   if (Preferences::GetBool("dom.webkitBlink.dirPicker.enabled", false) &&
       HasAttr(kNameSpaceID_None, nsGkAtoms::webkitdirectory)) {
     ErrorResult rv;
     GetFilesHelper* helper = GetOrCreateGetFilesHelper(true /* recursionFlag */,
                                                        rv);
@@ -8095,17 +8105,17 @@ HTMLInputElement::GetOrCreateGetFilesHel
   }
 
   return mGetFilesNonRecursiveHelper;
 }
 
 void
 HTMLInputElement::UpdateEntries(const nsTArray<OwningFileOrDirectory>& aFilesOrDirectories)
 {
-  mEntries.Clear();
+  MOZ_ASSERT(mEntries.IsEmpty());
 
   nsCOMPtr<nsIGlobalObject> global = OwnerDoc()->GetScopeObject();
   MOZ_ASSERT(global);
 
   RefPtr<DOMFileSystem> fs = DOMFileSystem::Create(global);
   if (NS_WARN_IF(!fs)) {
     return;
   }
--- a/dom/html/HTMLInputElement.h
+++ b/dom/html/HTMLInputElement.h
@@ -801,16 +801,18 @@ public:
    *
    *   http://www.whatwg.org/specs/web-apps/current-work/multipage/common-microsyntaxes.html#floating-point-numbers
    *
    * then this function will return the number parsed as a Decimal, otherwise
    * it will return a Decimal for which Decimal::isFinite() will return false.
    */
   static Decimal StringToDecimal(const nsAString& aValue);
 
+  void UpdateEntries(const nsTArray<OwningFileOrDirectory>& aFilesOrDirectories);
+
 protected:
   virtual ~HTMLInputElement();
 
   virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   // Pull IsSingleLineTextControl into our scope, otherwise it'd be hidden
   // by the nsITextControlElement version.
   using nsGenericHTMLFormElementWithState::IsSingleLineTextControl;
@@ -961,18 +963,16 @@ protected:
    */
   nsresult MaybeSubmitForm(nsPresContext* aPresContext);
 
   /**
    * Update mFileList with the currently selected file.
    */
   void UpdateFileList();
 
-  void UpdateEntries(const nsTArray<OwningFileOrDirectory>& aFilesOrDirectories);
-
   /**
    * Called after calling one of the SetFilesOrDirectories() functions.
    * This method can explore the directory recursively if needed.
    */
   void AfterSetFilesOrDirectories(bool aSetValueChanged);
 
   /**
    * Recursively explore the directory and populate mFileOrDirectories correctly
--- a/dom/ipc/Blob.cpp
+++ b/dom/ipc/Blob.cpp
@@ -1708,25 +1708,35 @@ protected:
   // SendCreatedFromKnownBlob() is received. This is used only with KnownBlob
   // params in the CTOR of a IPC BlobImpl.
   RefPtr<BlobImpl> mDifferentProcessBlobImpl;
 
   RefPtr<BlobImpl> mSameProcessBlobImpl;
 
   const bool mIsSlice;
 
+  const bool mIsDirectory;
+
 public:
+
+  enum BlobImplIsDirectory
+  {
+    eNotDirectory,
+    eDirectory
+  };
+
   // For File.
   RemoteBlobImpl(BlobChild* aActor,
                  BlobImpl* aRemoteBlobImpl,
                  const nsAString& aName,
                  const nsAString& aContentType,
                  const nsAString& aPath,
                  uint64_t aLength,
                  int64_t aModDate,
+                 BlobImplIsDirectory aIsDirectory,
                  bool aIsSameProcessBlob);
 
   // For Blob.
   RemoteBlobImpl(BlobChild* aActor,
                  BlobImpl* aRemoteBlobImpl,
                  const nsAString& aContentType,
                  uint64_t aLength,
                  bool aIsSameProcessBlob);
@@ -1770,16 +1780,19 @@ public:
   RemoteBlobImpl*
   BaseRemoteBlobImpl() const;
 
   NS_DECL_ISUPPORTS_INHERITED
 
   virtual void
   GetMozFullPathInternal(nsAString& aFileName, ErrorResult& aRv) const override;
 
+  virtual bool
+  IsDirectory() const override;
+
   virtual already_AddRefed<BlobImpl>
   CreateSlice(uint64_t aStart,
               uint64_t aLength,
               const nsAString& aContentType,
               ErrorResult& aRv) override;
 
   virtual void
   GetInternalStream(nsIInputStream** aStream, ErrorResult& aRv) override;
@@ -1943,16 +1956,19 @@ public:
   SetLastModified(int64_t aLastModified) override;
 
   virtual void
   GetMozFullPath(nsAString& aName, ErrorResult& aRv) const override;
 
   virtual void
   GetMozFullPathInternal(nsAString& aFileName, ErrorResult& aRv) const override;
 
+  virtual bool
+  IsDirectory() const override;
+
   virtual uint64_t
   GetSize(ErrorResult& aRv) override;
 
   virtual void
   GetType(nsAString& aType) override;
 
   virtual uint64_t
   GetSerialNumber() const override;
@@ -2029,19 +2045,20 @@ private:
 BlobChild::
 RemoteBlobImpl::RemoteBlobImpl(BlobChild* aActor,
                                BlobImpl* aRemoteBlobImpl,
                                const nsAString& aName,
                                const nsAString& aContentType,
                                const nsAString& aPath,
                                uint64_t aLength,
                                int64_t aModDate,
+                               BlobImplIsDirectory aIsDirectory,
                                bool aIsSameProcessBlob)
   : BlobImplBase(aName, aContentType, aLength, aModDate)
-  , mIsSlice(false)
+  , mIsSlice(false), mIsDirectory(aIsDirectory == eDirectory)
 {
   SetPath(aPath);
 
   if (aIsSameProcessBlob) {
     MOZ_ASSERT(aRemoteBlobImpl);
     mSameProcessBlobImpl = aRemoteBlobImpl;
     MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
   } else {
@@ -2053,42 +2070,43 @@ RemoteBlobImpl::RemoteBlobImpl(BlobChild
 
 BlobChild::
 RemoteBlobImpl::RemoteBlobImpl(BlobChild* aActor,
                                BlobImpl* aRemoteBlobImpl,
                                const nsAString& aContentType,
                                uint64_t aLength,
                                bool aIsSameProcessBlob)
   : BlobImplBase(aContentType, aLength)
-  , mIsSlice(false)
+  , mIsSlice(false), mIsDirectory(false)
 {
   if (aIsSameProcessBlob) {
     MOZ_ASSERT(aRemoteBlobImpl);
     mSameProcessBlobImpl = aRemoteBlobImpl;
     MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
   } else {
     mDifferentProcessBlobImpl = aRemoteBlobImpl;
   }
 
   CommonInit(aActor);
 }
 
 BlobChild::
 RemoteBlobImpl::RemoteBlobImpl(BlobChild* aActor)
   : BlobImplBase(EmptyString(), EmptyString(), UINT64_MAX, INT64_MAX)
-  , mIsSlice(false)
+  , mIsSlice(false), mIsDirectory(false)
 {
   CommonInit(aActor);
 }
 
 BlobChild::
 RemoteBlobImpl::RemoteBlobImpl(const nsAString& aContentType, uint64_t aLength)
   : BlobImplBase(aContentType, aLength)
   , mActor(nullptr)
   , mIsSlice(true)
+  , mIsDirectory(false)
 {
   mImmutable = true;
 }
 
 void
 BlobChild::
 RemoteBlobImpl::CommonInit(BlobChild* aActor)
 {
@@ -2191,16 +2209,23 @@ RemoteBlobImpl::GetMozFullPathInternal(n
   if (!mActor->SendGetFilePath(&filePath)) {
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
 
   aFilePath = filePath;
 }
 
+bool
+BlobChild::
+RemoteBlobImpl::IsDirectory() const
+{
+  return mIsDirectory;
+}
+
 already_AddRefed<BlobImpl>
 BlobChild::
 RemoteBlobImpl::CreateSlice(uint64_t aStart,
                             uint64_t aLength,
                             const nsAString& aContentType,
                             ErrorResult& aRv)
 {
   // May be called on any thread.
@@ -2638,16 +2663,23 @@ RemoteBlobImpl::GetMozFullPath(nsAString
 
 void
 BlobParent::
 RemoteBlobImpl::GetMozFullPathInternal(nsAString& aFileName, ErrorResult& aRv) const
 {
   mBlobImpl->GetMozFullPathInternal(aFileName, aRv);
 }
 
+bool
+BlobParent::
+RemoteBlobImpl::IsDirectory() const
+{
+  return mBlobImpl->IsDirectory();
+}
+
 uint64_t
 BlobParent::
 RemoteBlobImpl::GetSize(ErrorResult& aRv)
 {
   return mBlobImpl->GetSize(aRv);
 }
 
 void
@@ -2948,19 +2980,24 @@ BlobChild::CommonInit(BlobChild* aOther,
     otherImpl->GetName(name);
 
     nsAutoString path;
     otherImpl->GetPath(path);
 
     int64_t modDate = otherImpl->GetLastModified(rv);
     MOZ_ASSERT(!rv.Failed());
 
+    RemoteBlobImpl::BlobImplIsDirectory directory = otherImpl->IsDirectory() ?
+      RemoteBlobImpl::BlobImplIsDirectory::eDirectory :
+      RemoteBlobImpl::BlobImplIsDirectory::eNotDirectory;
+
     remoteBlob =
       new RemoteBlobImpl(this, otherImpl, name, contentType, path,
-                         length, modDate, false /* SameProcessBlobImpl */);
+                         length, modDate, directory,
+                         false /* SameProcessBlobImpl */);
   } else {
     remoteBlob = new RemoteBlobImpl(this, otherImpl, contentType, length,
                                     false /* SameProcessBlobImpl */);
   }
 
   // This RemoteBlob must be kept alive untill RecvCreatedFromKnownBlob is
   // called because the parent will send this notification and we must be able
   // to manage it.
@@ -2996,23 +3033,27 @@ BlobChild::CommonInit(const ChildBlobCon
         new RemoteBlobImpl(this, nullptr, params.contentType(), params.length(),
                            false /* SameProcessBlobImpl */);
       break;
     }
 
     case AnyBlobConstructorParams::TFileBlobConstructorParams: {
       const FileBlobConstructorParams& params =
         blobParams.get_FileBlobConstructorParams();
+      RemoteBlobImpl::BlobImplIsDirectory directory = params.isDirectory() ?
+        RemoteBlobImpl::BlobImplIsDirectory::eDirectory :
+        RemoteBlobImpl::BlobImplIsDirectory::eNotDirectory;
       remoteBlob = new RemoteBlobImpl(this,
                                       nullptr,
                                       params.name(),
                                       params.contentType(),
                                       params.path(),
                                       params.length(),
                                       params.modDate(),
+                                      directory,
                                       false /* SameProcessBlobImpl */);
       break;
     }
 
     case AnyBlobConstructorParams::TSameProcessBlobConstructorParams: {
       MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
 
       const SameProcessBlobConstructorParams& params =
@@ -3034,24 +3075,30 @@ BlobChild::CommonInit(const ChildBlobCon
         blobImpl->GetName(name);
 
         nsAutoString path;
         blobImpl->GetPath(path);
 
         int64_t lastModifiedDate = blobImpl->GetLastModified(rv);
         MOZ_ASSERT(!rv.Failed());
 
+        RemoteBlobImpl::BlobImplIsDirectory directory =
+          blobImpl->IsDirectory() ?
+            RemoteBlobImpl::BlobImplIsDirectory::eDirectory :
+            RemoteBlobImpl::BlobImplIsDirectory::eNotDirectory;
+
         remoteBlob =
           new RemoteBlobImpl(this,
                              blobImpl,
                              name,
                              contentType,
                              path,
                              size,
                              lastModifiedDate,
+                             directory,
                              true /* SameProcessBlobImpl */);
       } else {
         remoteBlob = new RemoteBlobImpl(this, blobImpl, contentType, size,
                                         true /* SameProcessBlobImpl */);
       }
 
       break;
     }
@@ -3226,17 +3273,17 @@ BlobChild::GetOrCreateFromImpl(ChildMana
       nsAutoString path;
       aBlobImpl->GetPath(path);
 
       int64_t modDate = aBlobImpl->GetLastModified(rv);
       MOZ_ASSERT(!rv.Failed());
 
       blobParams =
         FileBlobConstructorParams(name, contentType, path, length, modDate,
-                                  blobData);
+                                  aBlobImpl->IsDirectory(), blobData);
     } else {
       blobParams = NormalBlobConstructorParams(contentType, length, blobData);
     }
   }
 
   BlobChild* actor = new BlobChild(aManager, aBlobImpl);
 
   ParentBlobConstructorParams params(blobParams);
@@ -3403,26 +3450,29 @@ BlobChild::GetBlobImpl()
 bool
 BlobChild::SetMysteryBlobInfo(const nsString& aName,
                               const nsString& aContentType,
                               uint64_t aLength,
                               int64_t aLastModifiedDate)
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mBlobImpl);
+  MOZ_ASSERT(!mBlobImpl->IsDirectory());
   MOZ_ASSERT(mRemoteBlobImpl);
+  MOZ_ASSERT(!mRemoteBlobImpl->IsDirectory());
   MOZ_ASSERT(aLastModifiedDate != INT64_MAX);
 
   mBlobImpl->SetLazyData(aName, aContentType, aLength, aLastModifiedDate);
 
   FileBlobConstructorParams params(aName,
                                    aContentType,
                                    EmptyString(),
                                    aLength,
                                    aLastModifiedDate,
+                                   mBlobImpl->IsDirectory(),
                                    void_t() /* optionalBlobData */);
   return SendResolveMystery(params);
 }
 
 bool
 BlobChild::SetMysteryBlobInfo(const nsString& aContentType, uint64_t aLength)
 {
   AssertIsOnOwningThread();
@@ -3778,17 +3828,17 @@ BlobParent::GetOrCreateFromImpl(ParentMa
         nsAutoString path;
         aBlobImpl->GetPath(path);
 
         int64_t modDate = aBlobImpl->GetLastModified(rv);
         MOZ_ASSERT(!rv.Failed());
 
         blobParams =
           FileBlobConstructorParams(name, contentType, path, length, modDate,
-                                    void_t());
+                                    aBlobImpl->IsDirectory(), void_t());
       } else {
         blobParams = NormalBlobConstructorParams(contentType, length, void_t());
       }
     }
   }
 
   nsID id;
   MOZ_ALWAYS_SUCCEEDS(gUUIDGenerator->GenerateUUIDInPlace(&id));
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -574,16 +574,19 @@ ContentChild::ContentChild()
 {
   // This process is a content process, so it's clearly running in
   // multiprocess mode!
   nsDebugImpl::SetMultiprocessMode("Child");
 }
 
 ContentChild::~ContentChild()
 {
+#ifndef NS_FREE_PERMANENT_DATA
+  NS_RUNTIMEABORT("Content Child shouldn't be destroyed.");
+#endif
 }
 
 NS_INTERFACE_MAP_BEGIN(ContentChild)
   NS_INTERFACE_MAP_ENTRY(nsIContentChild)
   NS_INTERFACE_MAP_ENTRY(nsIWindowProvider)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentChild)
 NS_INTERFACE_MAP_END
 
--- a/dom/ipc/DOMTypes.ipdlh
+++ b/dom/ipc/DOMTypes.ipdlh
@@ -67,16 +67,17 @@ struct NormalBlobConstructorParams
 
 struct FileBlobConstructorParams
 {
   nsString name;
   nsString contentType;
   nsString path;
   uint64_t length;
   int64_t modDate;
+  bool isDirectory;
 
   // This must be of type BlobData in a child->parent message, and will always
   // be of type void_t in a parent->child message.
   OptionalBlobData optionalBlobData;
 };
 
 struct SlicedBlobConstructorParams
 {
--- a/dom/ipc/PBlob.ipdl
+++ b/dom/ipc/PBlob.ipdl
@@ -38,17 +38,16 @@ parent:
     returns (InputStreamParams params, OptionalFileDescriptorSet fds);
 
   sync WaitForSliceCreation();
 
   // Use only for testing!
   sync GetFileId()
     returns (int64_t fileId);
 
-  // Use only for testing!
   sync GetFilePath()
     returns (nsString filePath);
 
 child:
   // This method must be called by the parent when the PBlobParent is fully
   // created in order to release the known blob.
   async CreatedFromKnownBlob();
 };
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -1738,54 +1738,63 @@ TabChild::HandleDoubleTap(const CSSPoint
     Stringify(aPoint).c_str(), mGlobal.get(), mTabChildGlobal.get());
 
   if (!mGlobal || !mTabChildGlobal) {
     return;
   }
 
   // Note: there is nothing to do with the modifiers here, as we are not
   // synthesizing any sort of mouse event.
-  CSSPoint point = APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid);
   nsCOMPtr<nsIDocument> document = GetDocument();
-  CSSRect zoomToRect = CalculateRectToZoomTo(document, point);
+  CSSRect zoomToRect = CalculateRectToZoomTo(document, aPoint);
   // The double-tap can be dispatched by any scroll frame (so |aGuid| could be
   // the guid of any scroll frame), but the zoom-to-rect operation must be
   // performed by the root content scroll frame, so query its identifiers
   // for the SendZoomToRect() call rather than using the ones from |aGuid|.
   uint32_t presShellId;
   ViewID viewId;
   if (APZCCallbackHelper::GetOrCreateScrollIdentifiers(
       document->GetDocumentElement(), &presShellId, &viewId) &&
       mAPZChild) {
     mAPZChild->SendZoomToRect(presShellId, viewId, zoomToRect,
                               DEFAULT_BEHAVIOR);
   }
 }
 
 void
 TabChild::HandleTap(GeckoContentController::TapType aType,
-                    const CSSPoint& aPoint, const Modifiers& aModifiers,
+                    const LayoutDevicePoint& aPoint, const Modifiers& aModifiers,
                     const ScrollableLayerGuid& aGuid, const uint64_t& aInputBlockId,
                     bool aCallTakeFocusForClickFromTap)
 {
+  nsCOMPtr<nsIPresShell> presShell = GetPresShell();
+  if (!presShell) {
+    return;
+  }
+  if (!presShell->GetPresContext()) {
+    return;
+  }
+  CSSToLayoutDeviceScale scale(presShell->GetPresContext()->CSSToDevPixelScale());
+  CSSPoint point = APZCCallbackHelper::ApplyCallbackTransform(aPoint / scale, aGuid);
+
   switch (aType) {
   case GeckoContentController::TapType::eSingleTap:
     if (aCallTakeFocusForClickFromTap && mRemoteFrame) {
       mRemoteFrame->SendTakeFocusForClickFromTap();
     }
     if (mGlobal && mTabChildGlobal) {
-      mAPZEventState->ProcessSingleTap(aPoint, aModifiers, aGuid);
+      mAPZEventState->ProcessSingleTap(point, scale, aModifiers, aGuid);
     }
     break;
   case GeckoContentController::TapType::eDoubleTap:
-    HandleDoubleTap(aPoint, aModifiers, aGuid);
+    HandleDoubleTap(point, aModifiers, aGuid);
     break;
   case GeckoContentController::TapType::eLongTap:
     if (mGlobal && mTabChildGlobal) {
-      mAPZEventState->ProcessLongTap(GetPresShell(), aPoint, aModifiers, aGuid,
+      mAPZEventState->ProcessLongTap(presShell, point, scale, aModifiers, aGuid,
           aInputBlockId);
     }
     break;
   case GeckoContentController::TapType::eLongTapUp:
     if (mGlobal && mTabChildGlobal) {
       mAPZEventState->ProcessLongTapUp();
     }
     break;
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -618,17 +618,17 @@ public:
                   const ShowInfo& aShowInfo);
 
   void ContentReceivedInputBlock(const ScrollableLayerGuid& aGuid,
                                  uint64_t aInputBlockId,
                                  bool aPreventDefault) const;
   void SetTargetAPZC(uint64_t aInputBlockId,
                     const nsTArray<ScrollableLayerGuid>& aTargets) const;
   void HandleTap(layers::GeckoContentController::TapType aType,
-                 const CSSPoint& aPoint,
+                 const LayoutDevicePoint& aPoint,
                  const Modifiers& aModifiers,
                  const mozilla::layers::ScrollableLayerGuid& aGuid,
                  const uint64_t& aInputBlockId,
                  bool aCallTakeFocusForClickFromTap);
   void SetAllowedTouchBehavior(uint64_t aInputBlockId,
                                const nsTArray<TouchBehaviorFlags>& aFlags) const;
 
   bool UpdateFrame(const FrameMetrics& aFrameMetrics);
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -1293,19 +1293,19 @@ TabParent::SendRealDragEvent(WidgetDragE
 {
   if (mIsDestroyed) {
     return false;
   }
   event.mRefPoint += GetChildProcessOffset();
   return PBrowserParent::SendRealDragEvent(event, aDragAction, aDropEffect);
 }
 
-CSSPoint TabParent::AdjustTapToChildWidget(const CSSPoint& aPoint)
+LayoutDevicePoint TabParent::AdjustTapToChildWidget(const LayoutDevicePoint& aPoint)
 {
-  return aPoint + (LayoutDevicePoint(GetChildProcessOffset()) * GetLayoutDeviceToCSSScale());
+  return aPoint + LayoutDevicePoint(GetChildProcessOffset());
 }
 
 bool TabParent::SendMouseWheelEvent(WidgetWheelEvent& event)
 {
   if (mIsDestroyed) {
     return false;
   }
 
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -563,17 +563,17 @@ public:
   already_AddRefed<nsIWidget> GetWidget() const;
 
   const TabId GetTabId() const
   {
     return mTabId;
   }
 
   LayoutDeviceIntPoint GetChildProcessOffset();
-  CSSPoint AdjustTapToChildWidget(const CSSPoint& aPoint);
+  LayoutDevicePoint AdjustTapToChildWidget(const LayoutDevicePoint& aPoint);
 
   /**
    * Native widget remoting protocol for use with windowed plugins with e10s.
    */
   virtual PPluginWidgetParent* AllocPPluginWidgetParent() override;
 
   virtual bool
   DeallocPPluginWidgetParent(PPluginWidgetParent* aActor) override;
--- a/dom/locales/en-US/chrome/layout/layout_errors.properties
+++ b/dom/locales/en-US/chrome/layout/layout_errors.properties
@@ -30,8 +30,9 @@ CompositorAnimationWarningTransformPreserve3D=Animations of ‘transform-style: preserve-3d’ transforms cannot be run on the compositor
 ##                   CompositorAnimationWarningTransformWithGeometricProperties,
 ##                   CompositorAnimationWarningTransformFrameInactive,
 ##                   CompositorAnimationWarningOpacityFrameInactive):
 ## 'transform' and 'opacity' mean CSS property names, don't translate it.
 CompositorAnimationWarningTransformSVG=Animations of ‘transform’ on elements with SVG transforms cannot be run on the compositor
 CompositorAnimationWarningTransformWithGeometricProperties=Animations of ‘transform’ cannot be run on the compositor when geometric properties are animated on the same element at the same time
 CompositorAnimationWarningTransformFrameInactive=Animation cannot be run on the compositor because the frame was not marked active for ‘transform’ animation
 CompositorAnimationWarningOpacityFrameInactive=Animation cannot be run on the compositor because the frame was not marked active for ‘opacity’ animation
+CompositorAnimationWarningHasRenderingObserver=Animation cannot be run on the compositor because the element has rendering observers (-moz-element or SVG clipping/masking)
--- a/dom/media/platforms/PlatformDecoderModule.h
+++ b/dom/media/platforms/PlatformDecoderModule.h
@@ -34,20 +34,20 @@ class CDMProxy;
 static LazyLogModule sPDMLog("PlatformDecoderModule");
 
 struct CreateDecoderParams {
   explicit CreateDecoderParams(const TrackInfo& aConfig)
     : mConfig(aConfig)
   {}
 
   template <typename T1, typename... Ts>
-  CreateDecoderParams(const TrackInfo& aConfig, T1 a1, Ts... as)
+  CreateDecoderParams(const TrackInfo& aConfig, T1&& a1, Ts&&... args)
     : mConfig(aConfig)
   {
-    Set(a1, as...);
+    Set(mozilla::Forward<T1>(a1), mozilla::Forward<Ts>(args)...);
   }
 
   const VideoInfo& VideoConfig() const
   {
     MOZ_ASSERT(mConfig.IsVideo());
     return *mConfig.GetAsVideoInfo();
   }
 
@@ -68,23 +68,20 @@ struct CreateDecoderParams {
 private:
   void Set(TaskQueue* aTaskQueue) { mTaskQueue = aTaskQueue; }
   void Set(MediaDataDecoderCallback* aCallback) { mCallback = aCallback; }
   void Set(DecoderDoctorDiagnostics* aDiagnostics) { mDiagnostics = aDiagnostics; }
   void Set(layers::ImageContainer* aImageContainer) { mImageContainer = aImageContainer; }
   void Set(layers::LayersBackend aLayersBackend) { mLayersBackend = aLayersBackend; }
   void Set(GMPCrashHelper* aCrashHelper) { mCrashHelper = aCrashHelper; }
   template <typename T1, typename T2, typename... Ts>
-  void Set(T1 a1, T2 a2, Ts... as)
+  void Set(T1&& a1, T2&& a2, Ts&&... args)
   {
-    // Parameter pack expansion trick, to call Set() on each argument.
-    using expander = int[];
-    (void)expander {
-      (Set(a1), 0), (Set(a2), 0), (Set(as), 0)...
-    };
+    Set(mozilla::Forward<T1>(a1));
+    Set(mozilla::Forward<T2>(a2), mozilla::Forward<Ts>(args)...);
   }
 };
 
 // The PlatformDecoderModule interface is used by the MediaFormatReader to
 // abstract access to decoders provided by various
 // platforms.
 // Each platform (Windows, MacOSX, Linux, B2G etc) must implement a
 // PlatformDecoderModule to provide access to its decoders in order to get
new file mode 100644
--- /dev/null
+++ b/dom/media/test/crashtests/1236639.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Bug 1236639: Crash on audio playback due to invalid XING headers</title>
+</head>
+<body>
+<audio autoplay src="1236639.mp3"></audio>
+</body>
+</html>
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..8ef96e8cc76e366b50b3947600a896d87816bac9
GIT binary patch
literal 1080
zc%1WeF=l1}fuaycA16iz238=uB(=CC#52T*0f`&r><wfx0kNSokZ53F5Cl^7K->)j
zAY}orhRC`iU4x)pBLf425N8iRDAzGLGv)v92`pf{MZqM)3y4clKtyI<I*{VWltBg-
z*7i;=?w&q=fg$0MF>#5>De0Lxd4<Jg<u!H9t?k{t6DCcaK5Nc`MN3z#T(f@TmhC(D
z>^pem)QMAP&tJZJ<JR2=kDfex`TE_5&tJd){PPcJprwzaudA`1CClIS_u3g4L<&?`
z-P0Ht3K$q0ueDw>e~4_k0)DsA1{}pBcb9Ii7&1VCepWppsN(-af=UPj5u#N6|NS6k
zW&_Wp!ti(H9RhF8yp}y<%c8YQ(O;Hz_H)O-X+F#B9tT-Ys9Cl20i&{$l9H6}>NW;R
p#nazB5_Y6bon-w@Zbyz<&XNavJra8u+8H!uwy5a}o^>ka002GKg3<s0
--- a/dom/media/test/crashtests/crashtests.list
+++ b/dom/media/test/crashtests/crashtests.list
@@ -90,8 +90,9 @@ load buffer-source-resampling-start-1.ht
 load doppler-1.html
 HTTP load media-element-source-seek-1.html
 load offline-buffer-source-ended-1.html
 load oscillator-ended-1.html
 load oscillator-ended-2.html
 load video-replay-after-audio-end.html
 # This needs to run at the end to avoid leaking busted state into other tests.
 skip-if(B2G) load 691096-1.html # bug 852821
+load 1236639.html
--- a/dom/webidl/DOMTokenList.webidl
+++ b/dom/webidl/DOMTokenList.webidl
@@ -22,9 +22,10 @@ interface DOMTokenList {
   void replace(DOMString token, DOMString newToken);
   [Throws]
   boolean toggle(DOMString token, optional boolean force);
   [Throws]
   boolean supports(DOMString token);
   [SetterThrows]
   attribute DOMString value;
   stringifier DOMString ();
+  iterable<DOMString?>;
 };
--- a/dom/webidl/DataTransferItem.webidl
+++ b/dom/webidl/DataTransferItem.webidl
@@ -12,8 +12,13 @@ interface DataTransferItem {
   readonly attribute DOMString type;
   [Throws]
   void getAsString(FunctionStringCallback? _callback);
   [Throws]
   File? getAsFile();
 };
 
 callback FunctionStringCallback = void (DOMString data);
+
+partial interface DataTransferItem {
+  [Pref="dom.webkitBlink.filesystem.enabled", BinaryName="getAsEntry", Throws]
+  Entry? webkitGetAsEntry();
+};
--- a/dom/webidl/HTMLInputElement.webidl
+++ b/dom/webidl/HTMLInputElement.webidl
@@ -221,14 +221,14 @@ interface MozPhonetic {
   readonly attribute DOMString phonetic;
 };
 
 HTMLInputElement implements MozImageLoadingContent;
 HTMLInputElement implements MozPhonetic;
 
 // Webkit/Blink
 partial interface HTMLInputElement {
-  [Pref="dom.webkitBlink.filesystem.enabled", Cached, Constant]
+  [Pref="dom.webkitBlink.filesystem.enabled", Frozen, Cached, Pure]
   readonly attribute sequence<Entry> webkitEntries;
 
   [Pref="dom.webkitBlink.dirPicker.enabled", BinaryName="WebkitDirectoryAttr", SetterThrows]
           attribute boolean webkitdirectory;
 };
--- a/dom/webidl/NodeList.webidl
+++ b/dom/webidl/NodeList.webidl
@@ -8,9 +8,10 @@
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 interface NodeList {
   getter Node? item(unsigned long index);
   readonly attribute unsigned long length;
+  iterable<Node?>;
 };
--- a/embedding/components/windowwatcher/nsWindowWatcher.cpp
+++ b/embedding/components/windowwatcher/nsWindowWatcher.cpp
@@ -373,17 +373,23 @@ nsWindowWatcher::OpenWindow(mozIDOMWindo
                             /* calledFromJS = */ false, dialog,
                             /* navigate = */ true, argv,
                             /* openerFullZoom = */ nullptr, aResult);
 }
 
 struct SizeSpec
 {
   SizeSpec()
-    : mLeftSpecified(false)
+    : mLeft(0)
+    , mTop(0)
+    , mOuterWidth(0)
+    , mOuterHeight(0)
+    , mInnerWidth(0)
+    , mInnerHeight(0)
+    , mLeftSpecified(false)
     , mTopSpecified(false)
     , mOuterWidthSpecified(false)
     , mOuterHeightSpecified(false)
     , mInnerWidthSpecified(false)
     , mInnerHeightSpecified(false)
     , mUseDefaultWidth(false)
     , mUseDefaultHeight(false)
   {
--- a/gfx/layers/CopyableCanvasLayer.cpp
+++ b/gfx/layers/CopyableCanvasLayer.cpp
@@ -45,18 +45,16 @@ CopyableCanvasLayer::CopyableCanvasLayer
 CopyableCanvasLayer::~CopyableCanvasLayer()
 {
   MOZ_COUNT_DTOR(CopyableCanvasLayer);
 }
 
 void
 CopyableCanvasLayer::Initialize(const Data& aData)
 {
-  NS_ASSERTION(mSurface == nullptr, "BasicCanvasLayer::Initialize called twice!");
-
   if (aData.mGLContext) {
     mGLContext = aData.mGLContext;
     mIsAlphaPremultiplied = aData.mIsGLAlphaPremult;
     mOriginPos = gl::OriginPos::BottomLeft;
     mIsMirror = aData.mIsMirror;
 
     MOZ_ASSERT(mGLContext->IsOffscreen(), "canvas gl context isn't offscreen");
 
@@ -67,150 +65,38 @@ CopyableCanvasLayer::Initialize(const Da
       mBufferProvider = aData.mBufferProvider;
     }
   } else if (aData.mBufferProvider) {
     mBufferProvider = aData.mBufferProvider;
   } else if (aData.mRenderer) {
     mAsyncRenderer = aData.mRenderer;
     mOriginPos = gl::OriginPos::BottomLeft;
   } else {
-    MOZ_CRASH("GFX: CanvasLayer created without mSurface, mDrawTarget or mGLContext?");
+    MOZ_CRASH("GFX: CanvasLayer created without BufferProvider, DrawTarget or GLContext?");
   }
 
   mBounds.SetRect(0, 0, aData.mSize.width, aData.mSize.height);
 }
 
 bool
 CopyableCanvasLayer::IsDataValid(const Data& aData)
 {
   return mGLContext == aData.mGLContext;
 }
 
-void
-CopyableCanvasLayer::UpdateTarget(DrawTarget* aDestTarget)
-{
-  AutoReturnSnapshot autoReturn;
-
-  if (mAsyncRenderer) {
-    mSurface = mAsyncRenderer->GetSurface();
-  } else if (!mGLFrontbuffer && mBufferProvider) {
-    mSurface = mBufferProvider->BorrowSnapshot();
-    if (aDestTarget) {
-      // If !aDestTarget we'll end up painting using mSurface later,
-      // so we can't return it to the provider (note that this will trigger a
-      // copy of the snapshot behind the scenes when the provider is unlocked).
-      autoReturn.mSnapshot = &mSurface;
-    }
-    // Either way we need to call ReturnSnapshot because ther may be an
-    // underlying TextureClient that has to be unlocked.
-    autoReturn.mBufferProvider = mBufferProvider;
-  }
-
-  if (!mGLContext && aDestTarget) {
-    NS_ASSERTION(mSurface, "Must have surface to draw!");
-    if (mSurface) {
-      aDestTarget->CopySurface(mSurface,
-                               IntRect(0, 0, mBounds.width, mBounds.height),
-                               IntPoint(0, 0));
-      mSurface = nullptr;
-    }
-
-    return;
-  }
-
-  if ((!mGLFrontbuffer && mBufferProvider) || mAsyncRenderer) {
-    return;
-  }
-
-  MOZ_ASSERT(mGLContext);
-
-  SharedSurface* frontbuffer = nullptr;
-  if (mGLFrontbuffer) {
-    frontbuffer = mGLFrontbuffer.get();
-  } else {
-    GLScreenBuffer* screen = mGLContext->Screen();
-    const auto& front = screen->Front();
-    if (front) {
-      frontbuffer = front->Surf();
-    }
-  }
-
-  if (!frontbuffer) {
-    NS_WARNING("Null frame received.");
-    return;
-  }
-
-  IntSize readSize(frontbuffer->mSize);
-  SurfaceFormat format = (GetContentFlags() & CONTENT_OPAQUE)
-                          ? SurfaceFormat::B8G8R8X8
-                          : SurfaceFormat::B8G8R8A8;
-  bool needsPremult = frontbuffer->mHasAlpha && !mIsAlphaPremultiplied;
-
-  // Try to read back directly into aDestTarget's output buffer
-  if (aDestTarget) {
-    uint8_t* destData;
-    IntSize destSize;
-    int32_t destStride;
-    SurfaceFormat destFormat;
-    if (aDestTarget->LockBits(&destData, &destSize, &destStride, &destFormat)) {
-      if (destSize == readSize && destFormat == format) {
-        RefPtr<DataSourceSurface> data =
-          Factory::CreateWrappingDataSourceSurface(destData, destStride, destSize, destFormat);
-        mGLContext->Readback(frontbuffer, data);
-        if (needsPremult) {
-          gfxUtils::PremultiplyDataSurface(data, data);
-        }
-        aDestTarget->ReleaseBits(destData);
-        return;
-      }
-      aDestTarget->ReleaseBits(destData);
-    }
-  }
-
-  RefPtr<DataSourceSurface> resultSurf = GetTempSurface(readSize, format);
-  // There will already be a warning from inside of GetTempSurface, but
-  // it doesn't hurt to complain:
-  if (NS_WARN_IF(!resultSurf)) {
-    return;
-  }
-
-  // Readback handles Flush/MarkDirty.
-  mGLContext->Readback(frontbuffer, resultSurf);
-  if (needsPremult) {
-    gfxUtils::PremultiplyDataSurface(resultSurf, resultSurf);
-  }
-  MOZ_ASSERT(resultSurf);
-
-  if (aDestTarget) {
-    aDestTarget->CopySurface(resultSurf,
-                             IntRect(0, 0, readSize.width, readSize.height),
-                             IntPoint(0, 0));
-  } else {
-    // If !aDestSurface then we will end up painting using mSurface, so
-    // stick our surface into mSurface, so that the Paint() path is the same.
-    mSurface = resultSurf;
-  }
-}
-
 DataSourceSurface*
 CopyableCanvasLayer::GetTempSurface(const IntSize& aSize,
                                     const SurfaceFormat aFormat)
 {
   if (!mCachedTempSurface ||
       aSize != mCachedTempSurface->GetSize() ||
       aFormat != mCachedTempSurface->GetFormat())
   {
     // Create a surface aligned to 8 bytes since that's the highest alignment WebGL can handle.
     uint32_t stride = GetAlignedStride<8>(aSize.width * BytesPerPixel(aFormat));
     mCachedTempSurface = Factory::CreateDataSourceSurfaceWithStride(aSize, aFormat, stride);
   }
 
   return mCachedTempSurface;
 }
 
-void
-CopyableCanvasLayer::DiscardTempSurface()
-{
-  mCachedTempSurface = nullptr;
-}
-
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/CopyableCanvasLayer.h
+++ b/gfx/layers/CopyableCanvasLayer.h
@@ -42,33 +42,26 @@ protected:
 public:
   virtual void Initialize(const Data& aData) override;
 
   virtual bool IsDataValid(const Data& aData) override;
 
   bool IsGLLayer() { return !!mGLContext; }
 
 protected:
-  void UpdateTarget(gfx::DrawTarget* aDestTarget = nullptr);
-
-  RefPtr<gfx::SourceSurface> mSurface;
   RefPtr<gl::GLContext> mGLContext;
-  GLuint mCanvasFrontbufferTexID;
   RefPtr<PersistentBufferProvider> mBufferProvider;
-
   UniquePtr<gl::SharedSurface> mGLFrontbuffer;
 
   bool mIsAlphaPremultiplied;
   gl::OriginPos mOriginPos;
   bool mIsMirror;
 
   RefPtr<gfx::DataSourceSurface> mCachedTempSurface;
 
   gfx::DataSourceSurface* GetTempSurface(const gfx::IntSize& aSize,
                                          const gfx::SurfaceFormat aFormat);
-
-  void DiscardTempSurface();
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif
--- a/gfx/layers/apz/public/GeckoContentController.h
+++ b/gfx/layers/apz/public/GeckoContentController.h
@@ -50,21 +50,21 @@ public:
     eDoubleTap,
     eLongTap,
     eLongTapUp,
 
     eSentinel,
   };
 
   /**
-   * Requests handling of a tap event. |aPoint| is in CSS pixels, relative to the
+   * Requests handling of a tap event. |aPoint| is in LD pixels, relative to the
    * current scroll offset.
    */
   virtual void HandleTap(TapType aType,
-                         const CSSPoint& aPoint,
+                         const LayoutDevicePoint& aPoint,
                          Modifiers aModifiers,
                          const ScrollableLayerGuid& aGuid,
                          uint64_t aInputBlockId) = 0;
 
   /**
    * Schedules a runnable to run on the controller/UI thread at some time
    * in the future.
    * This method must always be called on the controller thread.
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -885,16 +885,19 @@ ConvertToTouchBehavior(HitTestResult res
            | AllowedTouchBehavior::PINCH_ZOOM
            | AllowedTouchBehavior::DOUBLE_TAP_ZOOM;
     case HitLayerTouchActionNone:
       return AllowedTouchBehavior::NONE;
     case HitLayerTouchActionPanX:
       return AllowedTouchBehavior::HORIZONTAL_PAN;
     case HitLayerTouchActionPanY:
       return AllowedTouchBehavior::VERTICAL_PAN;
+    case HitLayerTouchActionPanXY:
+      return AllowedTouchBehavior::HORIZONTAL_PAN
+           | AllowedTouchBehavior::VERTICAL_PAN;
     case HitDispatchToContentRegion:
     default:
       return AllowedTouchBehavior::UNKNOWN;
   }
 }
 
 already_AddRefed<AsyncPanZoomController>
 APZCTreeManager::GetTouchInputBlockAPZC(const MultiTouchInput& aEvent,
--- a/gfx/layers/apz/src/APZUtils.h
+++ b/gfx/layers/apz/src/APZUtils.h
@@ -17,16 +17,17 @@ namespace mozilla {
 namespace layers {
 
 enum HitTestResult {
   HitNothing,
   HitLayer,
   HitLayerTouchActionNone,
   HitLayerTouchActionPanX,
   HitLayerTouchActionPanY,
+  HitLayerTouchActionPanXY,
   HitDispatchToContentRegion,
 };
 
 enum CancelAnimationFlags : uint32_t {
   Default = 0x0,            /* Cancel all animations */
   ExcludeOverscroll = 0x1,  /* Don't clear overscroll */
   ScrollSnap = 0x2          /* Snap to snap points */
 };
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -1459,37 +1459,31 @@ nsEventStatus AsyncPanZoomController::On
     // snap point as appropriate.
     ScrollSnap();
   }
 
   return nsEventStatus_eConsumeNoDefault;
 }
 
 bool
-AsyncPanZoomController::ConvertToGecko(const ScreenIntPoint& aPoint, CSSPoint* aOut)
+AsyncPanZoomController::ConvertToGecko(const ScreenIntPoint& aPoint, LayoutDevicePoint* aOut)
 {
   if (APZCTreeManager* treeManagerLocal = GetApzcTreeManager()) {
     ScreenToScreenMatrix4x4 transformScreenToGecko =
         treeManagerLocal->GetScreenToApzcTransform(this)
       * treeManagerLocal->GetApzcToGeckoTransform(this);
 
     Maybe<ScreenIntPoint> layoutPoint = UntransformBy(
         transformScreenToGecko, aPoint);
     if (!layoutPoint) {
       return false;
     }
 
-    { // scoped lock to access mFrameMetrics
-      ReentrantMonitorAutoEnter lock(mMonitor);
-      // NOTE: This isn't *quite* LayoutDevicePoint, we just don't have a name
-      // for this coordinate space and it maps the closest to LayoutDevicePoint.
-      *aOut = LayoutDevicePoint(ViewAs<LayoutDevicePixel>(*layoutPoint,
-                  PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent))
-            / mFrameMetrics.GetDevPixelsPerCSSPixel();
-    }
+    *aOut = LayoutDevicePoint(ViewAs<LayoutDevicePixel>(*layoutPoint,
+                PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent));
     return true;
   }
   return false;
 }
 
 static bool
 AllowsScrollingMoreThanOnePage(double aMultiplier)
 {
@@ -1983,17 +1977,17 @@ nsEventStatus AsyncPanZoomController::On
 
   return nsEventStatus_eConsumeNoDefault;
 }
 
 nsEventStatus AsyncPanZoomController::OnLongPress(const TapGestureInput& aEvent) {
   APZC_LOG("%p got a long-press in state %d\n", this, mState);
   RefPtr<GeckoContentController> controller = GetGeckoContentController();
   if (controller) {
-    CSSPoint geckoScreenPoint;
+    LayoutDevicePoint geckoScreenPoint;
     if (ConvertToGecko(aEvent.mPoint, &geckoScreenPoint)) {
       CancelableBlockState* block = CurrentInputBlock();
       MOZ_ASSERT(block);
       if (!block->AsTouchBlock()) {
         APZC_LOG("%p dropping long-press because some non-touch block interrupted it\n", this);
         return nsEventStatus_eIgnore;
       }
       if (block->AsTouchBlock()->IsDuringFastFling()) {
@@ -2012,17 +2006,17 @@ nsEventStatus AsyncPanZoomController::On
   APZC_LOG("%p got a long-tap-up in state %d\n", this, mState);
   return GenerateSingleTap(TapType::eLongTapUp, aEvent.mPoint, aEvent.modifiers);
 }
 
 nsEventStatus AsyncPanZoomController::GenerateSingleTap(TapType aType,
       const ScreenIntPoint& aPoint, mozilla::Modifiers aModifiers) {
   RefPtr<GeckoContentController> controller = GetGeckoContentController();
   if (controller) {
-    CSSPoint geckoScreenPoint;
+    LayoutDevicePoint geckoScreenPoint;
     if (ConvertToGecko(aPoint, &geckoScreenPoint)) {
       CancelableBlockState* block = CurrentInputBlock();
       MOZ_ASSERT(block);
       TouchBlockState* touch = block->AsTouchBlock();
       // |block| may be a non-touch block in the case where this function is
       // invoked by GestureEventListener on a timeout. In that case we already
       // verified that the single tap is allowed so we let it through.
       // XXX there is a bug here that in such a case the touch block that
@@ -2036,17 +2030,17 @@ nsEventStatus AsyncPanZoomController::Ge
         touch->SetSingleTapOccurred();
       }
       // Because this may be being running as part of APZCTreeManager::ReceiveInputEvent,
       // calling controller->HandleTap directly might mean that content receives
       // the single tap message before the corresponding touch-up. To avoid that we
       // schedule the singletap message to run on the next spin of the event loop.
       // See bug 965381 for the issue this was causing.
       RefPtr<Runnable> runnable =
-        NewRunnableMethod<TapType, CSSPoint, mozilla::Modifiers,
+        NewRunnableMethod<TapType, LayoutDevicePoint, mozilla::Modifiers,
                           ScrollableLayerGuid, uint64_t>(controller,
                             &GeckoContentController::HandleTap,
                             aType, geckoScreenPoint,
                             aModifiers, GetGuid(),
                             touch ? touch->GetBlockId() : 0);
 
       controller->PostDelayedTask(runnable.forget(), 0);
       return nsEventStatus_eConsumeNoDefault;
@@ -2077,17 +2071,17 @@ nsEventStatus AsyncPanZoomController::On
   return GenerateSingleTap(TapType::eSingleTap, aEvent.mPoint, aEvent.modifiers);
 }
 
 nsEventStatus AsyncPanZoomController::OnDoubleTap(const TapGestureInput& aEvent) {
   APZC_LOG("%p got a double-tap in state %d\n", this, mState);
   RefPtr<GeckoContentController> controller = GetGeckoContentController();
   if (controller) {
     if (mZoomConstraints.mAllowDoubleTapZoom && CurrentTouchBlock()->TouchActionAllowsDoubleTapZoom()) {
-      CSSPoint geckoScreenPoint;
+      LayoutDevicePoint geckoScreenPoint;
       if (ConvertToGecko(aEvent.mPoint, &geckoScreenPoint)) {
         controller->HandleTap(TapType::eDoubleTap, geckoScreenPoint,
             aEvent.modifiers, GetGuid(), CurrentTouchBlock()->GetBlockId());
       }
     }
     return nsEventStatus_eConsumeNoDefault;
   }
   return nsEventStatus_eIgnore;
--- a/gfx/layers/apz/src/AsyncPanZoomController.h
+++ b/gfx/layers/apz/src/AsyncPanZoomController.h
@@ -603,22 +603,22 @@ protected:
   /**
    * Gets the pointer to the apzc tree manager. All the access to tree manager
    * should be made via this method and not via private variable since this method
    * ensures that no lock is set.
    */
   APZCTreeManager* GetApzcTreeManager() const;
 
   /**
-   * Convert ScreenPoint relative to the screen to CSSPoint relative
+   * Convert ScreenPoint relative to the screen to LayoutDevicePoint relative
    * to the parent document. This excludes the transient compositor transform.
-   * NOTE: This must be converted to CSSPoint relative to the child
+   * NOTE: This must be converted to LayoutDevicePoint relative to the child
    * document before sending over IPC to a child process.
    */
-  bool ConvertToGecko(const ScreenIntPoint& aPoint, CSSPoint* aOut);
+  bool ConvertToGecko(const ScreenIntPoint& aPoint, LayoutDevicePoint* aOut);
 
   enum AxisLockMode {
     FREE,     /* No locking at all */
     STANDARD, /* Default axis locking mode that remains locked until pan ends*/
     STICKY,   /* Allow lock to be broken, with hysteresis */
   };
 
   static AxisLockMode GetAxisLockMode();
--- a/gfx/layers/apz/src/HitTestingTreeNode.cpp
+++ b/gfx/layers/apz/src/HitTestingTreeNode.cpp
@@ -277,20 +277,23 @@ HitTestingTreeNode::HitTest(const Parent
       mEventRegions.mDispatchToContentHitRegion.Contains(point.x, point.y))
   {
     return HitTestResult::HitDispatchToContentRegion;
   }
   if (gfxPrefs::TouchActionEnabled()) {
     if (mEventRegions.mNoActionRegion.Contains(point.x, point.y)) {
       return HitTestResult::HitLayerTouchActionNone;
     }
-    if (mEventRegions.mHorizontalPanRegion.Contains(point.x, point.y)) {
+    bool panX = mEventRegions.mHorizontalPanRegion.Contains(point.x, point.y);
+    bool panY = mEventRegions.mVerticalPanRegion.Contains(point.x, point.y);
+    if (panX && panY) {
+      return HitTestResult::HitLayerTouchActionPanXY;
+    } else if (panX) {
       return HitTestResult::HitLayerTouchActionPanX;
-    }
-    if (mEventRegions.mVerticalPanRegion.Contains(point.x, point.y)) {
+    } else if (panY) {
       return HitTestResult::HitLayerTouchActionPanY;
     }
   }
   return HitTestResult::HitLayer;
 }
 
 EventRegionsOverride
 HitTestingTreeNode::GetEventRegionsOverride() const
--- a/gfx/layers/apz/test/gtest/APZTestCommon.h
+++ b/gfx/layers/apz/test/gtest/APZTestCommon.h
@@ -72,17 +72,17 @@ static TimeStamp GetStartupTime() {
   return sStartupTime;
 }
 
 class MockContentController : public GeckoContentController {
 public:
   MOCK_METHOD1(RequestContentRepaint, void(const FrameMetrics&));
   MOCK_METHOD2(RequestFlingSnap, void(const FrameMetrics::ViewID& aScrollId, const mozilla::CSSPoint& aDestination));
   MOCK_METHOD2(AcknowledgeScrollUpdate, void(const FrameMetrics::ViewID&, const uint32_t& aScrollGeneration));
-  MOCK_METHOD5(HandleTap, void(TapType, const CSSPoint&, Modifiers, const ScrollableLayerGuid&, uint64_t));
+  MOCK_METHOD5(HandleTap, void(TapType, const LayoutDevicePoint&, Modifiers, const ScrollableLayerGuid&, uint64_t));
   // Can't use the macros with already_AddRefed :(
   void PostDelayedTask(already_AddRefed<Runnable> aTask, int aDelayMs) {
     RefPtr<Runnable> task = aTask;
   }
   MOCK_METHOD3(NotifyAPZStateChange, void(const ScrollableLayerGuid& aGuid, APZStateChange aChange, int aArg));
   MOCK_METHOD0(NotifyFlushComplete, void());
 };
 
--- a/gfx/layers/apz/test/gtest/TestEventRegions.cpp
+++ b/gfx/layers/apz/test/gtest/TestEventRegions.cpp
@@ -259,14 +259,14 @@ TEST_F(APZEventRegionsTester, Bug1117712
   TestAsyncPanZoomController* apzc2 = ApzcOf(layers[2]);
 
   // These touch events should hit the dispatch-to-content region of layers[3]
   // and so get queued with that APZC as the tentative target.
   uint64_t inputBlockId = 0;
   Tap(manager, ScreenIntPoint(55, 5), TimeDuration::FromMilliseconds(100), nullptr, &inputBlockId);
   // But now we tell the APZ that really it hit layers[2], and expect the tap
   // to be delivered at the correct coordinates.
-  EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, CSSPoint(55, 5), 0, apzc2->GetGuid(), _)).Times(1);
+  EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(55, 5), 0, apzc2->GetGuid(), _)).Times(1);
 
   nsTArray<ScrollableLayerGuid> targets;
   targets.AppendElement(apzc2->GetGuid());
   manager->SetTargetAPZC(inputBlockId, targets);
 }
--- a/gfx/layers/apz/test/gtest/TestGestureDetector.cpp
+++ b/gfx/layers/apz/test/gtest/TestGestureDetector.cpp
@@ -295,17 +295,17 @@ TEST_F(APZCGestureDetectorTester, ShortP
 
   MockFunction<void(std::string checkPointName)> check;
   {
     InSequence s;
     // This verifies that the single tap notification is sent after the
     // touchup is fully processed. The ordering here is important.
     EXPECT_CALL(check, Call("pre-tap"));
     EXPECT_CALL(check, Call("post-tap"));
-    EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, CSSPoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
+    EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
   }
 
   check.Call("pre-tap");
   TapAndCheckStatus(apzc, ScreenIntPoint(10, 10), TimeDuration::FromMilliseconds(100));
   check.Call("post-tap");
 
   apzc->AssertStateIsReset();
 }
@@ -315,17 +315,17 @@ TEST_F(APZCGestureDetectorTester, Medium
 
   MockFunction<void(std::string checkPointName)> check;
   {
     InSequence s;
     // This verifies that the single tap notification is sent after the
     // touchup is fully processed. The ordering here is important.
     EXPECT_CALL(check, Call("pre-tap"));
     EXPECT_CALL(check, Call("post-tap"));
-    EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, CSSPoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
+    EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
   }
 
   check.Call("pre-tap");
   TapAndCheckStatus(apzc, ScreenIntPoint(10, 10), TimeDuration::FromMilliseconds(400));
   check.Call("post-tap");
 
   apzc->AssertStateIsReset();
 }
@@ -351,21 +351,21 @@ protected:
 
     MockFunction<void(std::string checkPointName)> check;
 
     {
       InSequence s;
 
       EXPECT_CALL(check, Call("preHandleLongTap"));
       blockId++;
-      EXPECT_CALL(*mcc, HandleTap(TapType::eLongTap, CSSPoint(10, 10), 0, apzc->GetGuid(), blockId)).Times(1);
+      EXPECT_CALL(*mcc, HandleTap(TapType::eLongTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), blockId)).Times(1);
       EXPECT_CALL(check, Call("postHandleLongTap"));
 
       EXPECT_CALL(check, Call("preHandleLongTapUp"));
-      EXPECT_CALL(*mcc, HandleTap(TapType::eLongTapUp, CSSPoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
+      EXPECT_CALL(*mcc, HandleTap(TapType::eLongTapUp, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
       EXPECT_CALL(check, Call("postHandleLongTapUp"));
     }
 
     // Manually invoke the longpress while the touch is currently down.
     check.Call("preHandleLongTap");
     mcc->RunThroughDelayedTasks();
     check.Call("postHandleLongTap");
 
@@ -412,17 +412,17 @@ protected:
 
     MockFunction<void(std::string checkPointName)> check;
 
     {
       InSequence s;
 
       EXPECT_CALL(check, Call("preHandleLongTap"));
       blockId++;
-      EXPECT_CALL(*mcc, HandleTap(TapType::eLongTap, CSSPoint(touchX, touchStartY), 0, apzc->GetGuid(), blockId)).Times(1);
+      EXPECT_CALL(*mcc, HandleTap(TapType::eLongTap, LayoutDevicePoint(touchX, touchStartY), 0, apzc->GetGuid(), blockId)).Times(1);
       EXPECT_CALL(check, Call("postHandleLongTap"));
     }
 
     // Manually invoke the longpress while the touch is currently down.
     check.Call("preHandleLongTap");
     mcc->RunThroughDelayedTasks();
     check.Call("postHandleLongTap");
 
@@ -434,17 +434,17 @@ protected:
     apzc->ContentReceivedInputBlock(blockId, true);
     mcc->AdvanceByMillis(1000);
 
     MultiTouchInput mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, mcc->Time());
     mti.mTouches.AppendElement(SingleTouchData(0, ParentLayerPoint(touchX, touchEndY), ScreenSize(0, 0), 0, 0));
     status = apzc->ReceiveInputEvent(mti, nullptr);
     EXPECT_EQ(nsEventStatus_eConsumeDoDefault, status);
 
-    EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, CSSPoint(touchX, touchEndY), 0, apzc->GetGuid(), _)).Times(0);
+    EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(touchX, touchEndY), 0, apzc->GetGuid(), _)).Times(0);
     status = TouchUp(apzc, ScreenIntPoint(touchX, touchEndY), mcc->Time());
     EXPECT_EQ(nsEventStatus_eConsumeDoDefault, status);
 
     ParentLayerPoint pointOut;
     AsyncTransform viewTransformOut;
     apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
 
     EXPECT_EQ(ParentLayerPoint(), pointOut);
@@ -477,86 +477,86 @@ TEST_F(APZCLongPressTester, LongPressPre
                                 | mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN
                                 | mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM);
 }
 
 TEST_F(APZCGestureDetectorTester, DoubleTap) {
   MakeApzcWaitForMainThread();
   MakeApzcZoomable();
 
-  EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, CSSPoint(10, 10), 0, apzc->GetGuid(), _)).Times(0);
-  EXPECT_CALL(*mcc, HandleTap(TapType::eDoubleTap, CSSPoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
+  EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(0);
+  EXPECT_CALL(*mcc, HandleTap(TapType::eDoubleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
 
   uint64_t blockIds[2];
   DoubleTapAndCheckStatus(apzc, ScreenIntPoint(10, 10), &blockIds);
 
   // responses to the two touchstarts
   apzc->ContentReceivedInputBlock(blockIds[0], false);
   apzc->ContentReceivedInputBlock(blockIds[1], false);
 
   apzc->AssertStateIsReset();
 }
 
 TEST_F(APZCGestureDetectorTester, DoubleTapNotZoomable) {
   MakeApzcWaitForMainThread();
   MakeApzcUnzoomable();
 
-  EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, CSSPoint(10, 10), 0, apzc->GetGuid(), _)).Times(2);
-  EXPECT_CALL(*mcc, HandleTap(TapType::eDoubleTap, CSSPoint(10, 10), 0, apzc->GetGuid(), _)).Times(0);
+  EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(2);
+  EXPECT_CALL(*mcc, HandleTap(TapType::eDoubleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(0);
 
   uint64_t blockIds[2];
   DoubleTapAndCheckStatus(apzc, ScreenIntPoint(10, 10), &blockIds);
 
   // responses to the two touchstarts
   apzc->ContentReceivedInputBlock(blockIds[0], false);
   apzc->ContentReceivedInputBlock(blockIds[1], false);
 
   apzc->AssertStateIsReset();
 }
 
 TEST_F(APZCGestureDetectorTester, DoubleTapPreventDefaultFirstOnly) {
   MakeApzcWaitForMainThread();
   MakeApzcZoomable();
 
-  EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, CSSPoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
-  EXPECT_CALL(*mcc, HandleTap(TapType::eDoubleTap, CSSPoint(10, 10), 0, apzc->GetGuid(), _)).Times(0);
+  EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
+  EXPECT_CALL(*mcc, HandleTap(TapType::eDoubleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(0);
 
   uint64_t blockIds[2];
   DoubleTapAndCheckStatus(apzc, ScreenIntPoint(10, 10), &blockIds);
 
   // responses to the two touchstarts
   apzc->ContentReceivedInputBlock(blockIds[0], true);
   apzc->ContentReceivedInputBlock(blockIds[1], false);
 
   apzc->AssertStateIsReset();
 }
 
 TEST_F(APZCGestureDetectorTester, DoubleTapPreventDefaultBoth) {
   MakeApzcWaitForMainThread();
   MakeApzcZoomable();
 
-  EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, CSSPoint(10, 10), 0, apzc->GetGuid(), _)).Times(0);
-  EXPECT_CALL(*mcc, HandleTap(TapType::eDoubleTap, CSSPoint(10, 10), 0, apzc->GetGuid(), _)).Times(0);
+  EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(0);
+  EXPECT_CALL(*mcc, HandleTap(TapType::eDoubleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(0);
 
   uint64_t blockIds[2];
   DoubleTapAndCheckStatus(apzc, ScreenIntPoint(10, 10), &blockIds);
 
   // responses to the two touchstarts
   apzc->ContentReceivedInputBlock(blockIds[0], true);
   apzc->ContentReceivedInputBlock(blockIds[1], true);
 
   apzc->AssertStateIsReset();
 }
 
 // Test for bug 947892
 // We test whether we dispatch tap event when the tap is followed by pinch.
 TEST_F(APZCGestureDetectorTester, TapFollowedByPinch) {
   MakeApzcZoomable();
 
-  EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, CSSPoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
+  EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
 
   Tap(apzc, ScreenIntPoint(10, 10), TimeDuration::FromMilliseconds(100));
 
   int inputId = 0;
   MultiTouchInput mti;
   mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time());
   mti.mTouches.AppendElement(SingleTouchData(inputId, ParentLayerPoint(20, 20), ScreenSize(0, 0), 0, 0));
   mti.mTouches.AppendElement(SingleTouchData(inputId + 1, ParentLayerPoint(10, 10), ScreenSize(0, 0), 0, 0));
@@ -568,17 +568,17 @@ TEST_F(APZCGestureDetectorTester, TapFol
   apzc->ReceiveInputEvent(mti, nullptr);
 
   apzc->AssertStateIsReset();
 }
 
 TEST_F(APZCGestureDetectorTester, TapFollowedByMultipleTouches) {
   MakeApzcZoomable();
 
-  EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, CSSPoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
+  EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
 
   Tap(apzc, ScreenIntPoint(10, 10), TimeDuration::FromMilliseconds(100));
 
   int inputId = 0;
   MultiTouchInput mti;
   mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time());
   mti.mTouches.AppendElement(SingleTouchData(inputId, ParentLayerPoint(20, 20), ScreenSize(0, 0), 0, 0));
   apzc->ReceiveInputEvent(mti, nullptr);
@@ -613,17 +613,17 @@ TEST_F(APZCGestureDetectorTester, LongPr
   EXPECT_NE(touchBlockId, wheelBlockId);
   mcc->AdvanceByMillis(1000);
 }
 
 TEST_F(APZCGestureDetectorTester, TapTimeoutInterruptedByWheel) {
   // In this test, even though the wheel block comes right after the tap, the
   // tap should still be dispatched because it completes fully before the wheel
   // block arrived.
-  EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, CSSPoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
+  EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(10, 10), 0, apzc->GetGuid(), _)).Times(1);
 
   // We make the APZC zoomable so the gesture detector needs to wait to
   // distinguish between tap and double-tap. During that timeout is when we
   // insert the wheel event.
   MakeApzcZoomable();
 
   uint64_t touchBlockId = 0;
   uint64_t wheelBlockId = 0;
--- a/gfx/layers/apz/test/gtest/TestHitTesting.cpp
+++ b/gfx/layers/apz/test/gtest/TestHitTesting.cpp
@@ -504,19 +504,19 @@ TEST_F(APZHitTestingTester, TestForceDis
 TEST_F(APZHitTestingTester, Bug1148350) {
   CreateBug1148350LayerTree();
   ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
   manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
 
   MockFunction<void(std::string checkPointName)> check;
   {
     InSequence s;
-    EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, CSSPoint(100, 100), 0, ApzcOf(layers[1])->GetGuid(), _)).Times(1);
+    EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(100, 100), 0, ApzcOf(layers[1])->GetGuid(), _)).Times(1);
     EXPECT_CALL(check, Call("Tapped without transform"));
-    EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, CSSPoint(100, 100), 0, ApzcOf(layers[1])->GetGuid(), _)).Times(1);
+    EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(100, 100), 0, ApzcOf(layers[1])->GetGuid(), _)).Times(1);
     EXPECT_CALL(check, Call("Tapped with interleaved transform"));
   }
 
   Tap(manager, ScreenIntPoint(100, 100), TimeDuration::FromMilliseconds(100));
   mcc->RunThroughDelayedTasks();
   check.Call("Tapped without transform");
 
   uint64_t blockId;
--- a/gfx/layers/apz/test/mochitest/apz_test_utils.js
+++ b/gfx/layers/apz/test/mochitest/apz_test_utils.js
@@ -159,20 +159,24 @@ function waitForApzFlushedRepaints(aCall
 // windows, and returns a Promise that is resolved once all the subtests are
 // done running.
 //
 // The aSubtests array is an array of objects with the following keys:
 //   file: required, the filename of the subtest.
 //   prefs: optional, an array of arrays containing key-value prefs to set.
 //   dp_suppression: optional, a boolean on whether or not to respect displayport
 //                   suppression during the test.
+//   onload: optional, a function that will be registered as a load event listener
+//           for the child window that will hold the subtest. the function will be
+//           passed exactly one argument, which will be the child window.
 // An example of an array is:
 //   aSubtests = [
 //     { 'file': 'test_file_name.html' },
 //     { 'file': 'test_file_2.html', 'prefs': [['pref.name', true], ['other.pref', 1000]], 'dp_suppression': false }
+//     { 'file': 'file_3.html', 'onload': function(w) { w.subtestDone(); } }
 //   ];
 //
 // Each subtest should call the subtestDone() function when it is done, to
 // indicate that the window should be torn down and the next text should run.
 // The subtestDone() function is injected into the subtest's window by this
 // function prior to loading the subtest. For convenience, the |is| and |ok|
 // functions provided by SimpleTest are also mapped into the subtest's window.
 // For other things from the parent, the subtest can use window.opener.<whatever>
@@ -220,16 +224,19 @@ function runSubtestsSeriallyInFreshWindo
       }
 
       function spawnTest(aFile) {
         w = window.open('', "_blank");
         w.subtestDone = advanceSubtestExecution;
         w.SimpleTest = SimpleTest;
         w.is = function(a, b, msg) { return is(a, b, aFile + " | " + msg); };
         w.ok = function(cond, name, diag) { return ok(cond, aFile + " | " + name, diag); };
+        if (test.onload) {
+          w.addEventListener('load', function(e) { test.onload(w); }, { once: true });
+        }
         w.location = location.href.substring(0, location.href.lastIndexOf('/') + 1) + aFile;
         return w;
       }
 
       if (test.prefs) {
         // Got some prefs for this subtest, push them
         SpecialPowers.pushPrefEnv({"set": test.prefs}, function() {
           w = spawnTest(test.file);
@@ -362,8 +369,35 @@ function getQueryArgs() {
     var params = location.search.substr(1).split('&');
     for (var p of params) {
       var [k, v] = p.split('=');
       args[k] = JSON.parse(v);
     }
   }
   return args;
 }
+
+// Return a function that returns a promise to create a script element with the
+// given URI and append it to the head of the document in the given window.
+// As with runContinuation(), the extra function wrapper is for convenience
+// at the call site, so that this can be chained with other promises:
+//   waitUntilApzStable().then(injectScript('foo'))
+//                       .then(injectScript('bar'));
+// If you want to do the injection right away, run the function returned by
+// this function:
+//   injectScript('foo')();
+function injectScript(aScript, aWindow = window) {
+  return function() {
+    return new Promise(function(resolve, reject) {
+      var e = aWindow.document.createElement('script');
+      e.type = 'text/javascript';
+      e.onload = function() {
+        resolve();
+      };
+      e.onerror = function() {
+        dump('Script [' + aScript + '] errored out\n');
+        reject();
+      };
+      e.src = aScript;
+      aWindow.document.getElementsByTagName('head')[0].appendChild(e);
+    });
+  };
+}
new file mode 100644
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/helper_tap_fullzoom.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <meta name="viewport" content="width=device-width; initial-scale=1.0">
+  <title>Sanity touch-tapping test with fullzoom</title>
+  <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+  <script type="application/javascript" src="apz_test_utils.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+  <script type="application/javascript">
+
+function clickButton() {
+  document.addEventListener('click', clicked, false);
+
+  synthesizeNativeTap(document.getElementById('b'), 5, 5, function() {
+    dump("Finished synthesizing tap, waiting for button to be clicked...\n");
+  });
+}
+
+function clicked(e) {
+  is(e.target, document.getElementById('b'), "Clicked on button, yay! (at " + e.clientX + "," + e.clientY + ")");
+  subtestDone();
+}
+
+SpecialPowers.setFullZoom(window, 2.0);
+waitUntilApzStable().then(clickButton);
+
+  </script>
+</head>
+<body>
+ <button id="b" style="width: 10px; height: 10px; position: relative; top: 100px"></button>
+</body>
+</html>
--- a/gfx/layers/apz/test/mochitest/mochitest.ini
+++ b/gfx/layers/apz/test/mochitest/mochitest.ini
@@ -21,16 +21,17 @@ support-files =
   helper_touch_action.html
   helper_touch_action_regions.html
   helper_scroll_inactive_perspective.html
   helper_scroll_inactive_zindex.html
   helper_bug1280013.html
   helper_tall.html
   helper_drag_scroll.html
   helper_touch_action_complex.html
+  helper_tap_fullzoom.html
 tags = apz
 [test_bug982141.html]
 [test_bug1151663.html]
 [test_bug1277814.html]
 skip-if = (os == 'android') || (os == 'b2g') || (buildapp == 'mulet') # wheel events not supported on mobile; see bug 1164274 for mulet
 [test_wheel_scroll.html]
 skip-if = (os == 'android') || (os == 'b2g') || (buildapp == 'mulet') # wheel events not supported on mobile; see bug 1164274 for mulet
 [test_wheel_transactions.html]
--- a/gfx/layers/apz/test/mochitest/test_group_touchevents.html
+++ b/gfx/layers/apz/test/mochitest/test_group_touchevents.html
@@ -37,16 +37,18 @@ touch_action_prefs.push(["layout.css.tou
 var subtests = [
   // Simple tests to exercise basic panning behaviour
   {'file': 'helper_basic_pan.html', 'prefs': basic_pan_prefs},
   {'file': 'helper_div_pan.html', 'prefs': basic_pan_prefs},
   {'file': 'helper_iframe_pan.html', 'prefs': basic_pan_prefs},
 
   // Simple test to exercise touch-tapping behaviour
   {'file': 'helper_tap.html'},
+  // Tapping, but with a full-zoom applied
+  {'file': 'helper_tap_fullzoom.html'},
 
   // For the following two tests, disable displayport suppression to make sure it
   // doesn't interfere with the test by scheduling paints non-deterministically.
   {'file': 'helper_scrollto_tap.html?true', 'prefs': [["apz.paint_skipping.enabled", true]], 'dp_suppression': false},
   {'file': 'helper_scrollto_tap.html?false', 'prefs': [["apz.paint_skipping.enabled", false]], 'dp_suppression': false},
 
   // For the long-tap test, reduce the content response timeout because the touchstart
   // event doesn't get processed (because of the event listener) until this expires.
--- a/gfx/layers/apz/util/APZEventState.cpp
+++ b/gfx/layers/apz/util/APZEventState.cpp
@@ -161,59 +161,59 @@ private:
   Modifiers mModifiers;
   nsCOMPtr<nsITimer> mTimer;
 };
 
 NS_IMPL_ISUPPORTS(DelayedFireSingleTapEvent, nsITimerCallback)
 
 void
 APZEventState::ProcessSingleTap(const CSSPoint& aPoint,
+                                const CSSToLayoutDeviceScale& aScale,
                                 Modifiers aModifiers,
                                 const ScrollableLayerGuid& aGuid)
 {
   APZES_LOG("Handling single tap at %s on %s with %d\n",
     Stringify(aPoint).c_str(), Stringify(aGuid).c_str(), mTouchEndCancelled);
 
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (!widget) {
     return;
   }
 
   if (mTouchEndCancelled) {
     return;
   }
 
-  LayoutDevicePoint currentPoint =
-      APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid)
-    * widget->GetDefaultScale();
+  LayoutDevicePoint ldPoint = aPoint * aScale;
   if (!mActiveElementManager->ActiveElementUsesStyle()) {
     // If the active element isn't visually affected by the :active style, we
     // have no need to wait the extra sActiveDurationMs to make the activation
     // visually obvious to the user.
-    APZCCallbackHelper::FireSingleTapEvent(currentPoint, aModifiers, widget);
+    APZCCallbackHelper::FireSingleTapEvent(ldPoint, aModifiers, widget);
     return;
   }
 
   APZES_LOG("Active element uses style, scheduling timer for click event\n");
   nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
   RefPtr<DelayedFireSingleTapEvent> callback =
-    new DelayedFireSingleTapEvent(mWidget, currentPoint, aModifiers, timer);
+    new DelayedFireSingleTapEvent(mWidget, ldPoint, aModifiers, timer);
   nsresult rv = timer->InitWithCallback(callback,
                                         sActiveDurationMs,
                                         nsITimer::TYPE_ONE_SHOT);
   if (NS_FAILED(rv)) {
     // Make |callback| not hold the timer, so they will both be destructed when
     // we leave the scope of this function.
     callback->ClearTimer();
   }
 }
 
 void
 APZEventState::ProcessLongTap(const nsCOMPtr<nsIPresShell>& aPresShell,
                               const CSSPoint& aPoint,
+                              const CSSToLayoutDeviceScale& aScale,
                               Modifiers aModifiers,
                               const ScrollableLayerGuid& aGuid,
                               uint64_t aInputBlockId)
 {
   APZES_LOG("Handling long tap at %s\n", Stringify(aPoint).c_str());
 
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (!widget) {
@@ -221,47 +221,46 @@ APZEventState::ProcessLongTap(const nsCO
   }
 
   SendPendingTouchPreventedResponse(false);
 
   // Converting the modifiers to DOM format for the DispatchMouseEvent call
   // is the most useless thing ever because nsDOMWindowUtils::SendMouseEvent
   // just converts them back to widget format, but that API has many callers,
   // including in JS code, so it's not trivial to change.
-  CSSPoint point = APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid);
   bool eventHandled =
       APZCCallbackHelper::DispatchMouseEvent(aPresShell, NS_LITERAL_STRING("contextmenu"),
-                         point, 2, 1, WidgetModifiersToDOMModifiers(aModifiers), true,
+                         aPoint, 2, 1, WidgetModifiersToDOMModifiers(aModifiers), true,
                          nsIDOMMouseEvent::MOZ_SOURCE_TOUCH);
 
   APZES_LOG("Contextmenu event handled: %d\n", eventHandled);
   if (eventHandled) {
     // If the contextmenu event was handled then we're showing a contextmenu,
     // and so we should remove any activation
     mActiveElementManager->ClearActivation();
   } else {
     // If no one handle context menu, fire MOZLONGTAP event
-    LayoutDevicePoint currentPoint = point * widget->GetDefaultScale();
+    LayoutDevicePoint ldPoint = aPoint * aScale;
     int time = 0;
     nsEventStatus status =
         APZCCallbackHelper::DispatchSynthesizedMouseEvent(eMouseLongTap, time,
-                                                          currentPoint,
+                                                          ldPoint,
                                                           aModifiers, widget);
     eventHandled = (status == nsEventStatus_eConsumeNoDefault);
     APZES_LOG("MOZLONGTAP event handled: %d\n", eventHandled);
   }
 
   mContentReceivedInputBlockCallback(aGuid, aInputBlockId, eventHandled);
 
   if (eventHandled) {
     // Also send a touchcancel to content, so that listeners that might be
     // waiting for a touchend don't trigger.
     WidgetTouchEvent cancelTouchEvent(true, eTouchCancel, widget.get());
     cancelTouchEvent.mModifiers = WidgetModifiersToDOMModifiers(aModifiers);
-    auto ldPoint = LayoutDeviceIntPoint::Round(point * widget->GetDefaultScale());
+    auto ldPoint = LayoutDeviceIntPoint::Round(aPoint * aScale);
     cancelTouchEvent.mTouches.AppendElement(new mozilla::dom::Touch(mLastTouchIdentifier,
         ldPoint, LayoutDeviceIntPoint(), 0, 0));
     APZCCallbackHelper::DispatchWidgetEvent(cancelTouchEvent);
   }
 }
 
 void
 APZEventState::ProcessLongTapUp()
--- a/gfx/layers/apz/util/APZEventState.h
+++ b/gfx/layers/apz/util/APZEventState.h
@@ -42,20 +42,22 @@ class APZEventState {
   typedef FrameMetrics::ViewID ViewID;
 public:
   APZEventState(nsIWidget* aWidget,
                 ContentReceivedInputBlockCallback&& aCallback);
 
   NS_INLINE_DECL_REFCOUNTING(APZEventState);
 
   void ProcessSingleTap(const CSSPoint& aPoint,
+                        const CSSToLayoutDeviceScale& aScale,
                         Modifiers aModifiers,
                         const ScrollableLayerGuid& aGuid);
   void ProcessLongTap(const nsCOMPtr<nsIPresShell>& aUtils,
                       const CSSPoint& aPoint,
+                      const CSSToLayoutDeviceScale& aScale,
                       Modifiers aModifiers,
                       const ScrollableLayerGuid& aGuid,
                       uint64_t aInputBlockId);
   void ProcessLongTapUp();
   void ProcessTouchEvent(const WidgetTouchEvent& aEvent,
                          const ScrollableLayerGuid& aGuid,
                          uint64_t aInputBlockId,
                          nsEventStatus aApzResponse,
--- a/gfx/layers/apz/util/ChromeProcessController.cpp
+++ b/gfx/layers/apz/util/ChromeProcessController.cpp
@@ -120,59 +120,68 @@ ChromeProcessController::HandleDoubleTap
 {
   MOZ_ASSERT(MessageLoop::current() == mUILoop);
 
   nsCOMPtr<nsIDocument> document = GetRootContentDocument(aGuid.mScrollId);
   if (!document.get()) {
     return;
   }
 
-  CSSPoint point = APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid);
   // CalculateRectToZoomTo performs a hit test on the frame associated with the
   // Root Content Document. Unfortunately that frame does not know about the
   // resolution of the document and so we must remove it before calculating
   // the zoomToRect.
   nsIPresShell* presShell = document->GetShell();
   const float resolution = presShell->ScaleToResolution() ? presShell->GetResolution () : 1.0f;
-  point.x = point.x / resolution;
-  point.y = point.y / resolution;
+  CSSPoint point(aPoint.x / resolution, aPoint.y / resolution);
   CSSRect zoomToRect = CalculateRectToZoomTo(document, point);
 
   uint32_t presShellId;
   FrameMetrics::ViewID viewId;
   if (APZCCallbackHelper::GetOrCreateScrollIdentifiers(
       document->GetDocumentElement(), &presShellId, &viewId)) {
     mAPZCTreeManager->ZoomToRect(
       ScrollableLayerGuid(aGuid.mLayersId, presShellId, viewId), zoomToRect);
   }
 }
 
 void
 ChromeProcessController::HandleTap(TapType aType,
-                                   const mozilla::CSSPoint& aPoint, Modifiers aModifiers,
+                                   const mozilla::LayoutDevicePoint& aPoint,
+                                   Modifiers aModifiers,
                                    const ScrollableLayerGuid& aGuid,
                                    uint64_t aInputBlockId)
 {
   if (MessageLoop::current() != mUILoop) {
-    mUILoop->PostTask(NewRunnableMethod<TapType, mozilla::CSSPoint, Modifiers,
+    mUILoop->PostTask(NewRunnableMethod<TapType, mozilla::LayoutDevicePoint, Modifiers,
                                         ScrollableLayerGuid, uint64_t>(this,
                         &ChromeProcessController::HandleTap,
                         aType, aPoint, aModifiers, aGuid, aInputBlockId));
     return;
   }
 
+  nsCOMPtr<nsIPresShell> presShell = GetPresShell();
+  if (!presShell) {
+    return;
+  }
+  if (!presShell->GetPresContext()) {
+    return;
+  }
+  CSSToLayoutDeviceScale scale(presShell->GetPresContext()->CSSToDevPixelScale());
+  CSSPoint point = APZCCallbackHelper::ApplyCallbackTransform(aPoint / scale, aGuid);
+
   switch (aType) {
   case TapType::eSingleTap:
-    mAPZEventState->ProcessSingleTap(aPoint, aModifiers, aGuid);
+    mAPZEventState->ProcessSingleTap(point, scale, aModifiers, aGuid);
     break;
   case TapType::eDoubleTap:
-    HandleDoubleTap(aPoint, aModifiers, aGuid);
+    HandleDoubleTap(point, aModifiers, aGuid);
     break;
   case TapType::eLongTap:
-    mAPZEventState->ProcessLongTap(GetPresShell(), aPoint, aModifiers, aGuid,
+    mAPZEventState->ProcessLongTap(presShell, point, scale, aModifiers, aGuid,
         aInputBlockId);
     break;
   case TapType::eLongTapUp:
     mAPZEventState->ProcessLongTapUp();
     break;
   case TapType::eSentinel:
     // Should never happen, but we need to handle this case branch for the
     // compiler to be happy.
--- a/gfx/layers/apz/util/ChromeProcessController.h
+++ b/gfx/layers/apz/util/ChromeProcessController.h
@@ -36,17 +36,18 @@ public:
   explicit ChromeProcessController(nsIWidget* aWidget, APZEventState* aAPZEventState, IAPZCTreeManager* aAPZCTreeManager);
   ~ChromeProcessController();
   virtual void Destroy() override;
 
   // GeckoContentController interface
   virtual void RequestContentRepaint(const FrameMetrics& aFrameMetrics) override;
   virtual void PostDelayedTask(already_AddRefed<Runnable> aTask, int aDelayMs) override;
   virtual void HandleTap(TapType aType,
-                         const mozilla::CSSPoint& aPoint, Modifiers aModifiers,
+                         const mozilla::LayoutDevicePoint& aPoint,
+                         Modifiers aModifiers,
                          const ScrollableLayerGuid& aGuid,
                          uint64_t aInputBlockId) override;
   virtual void NotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
                                     APZStateChange aChange,
                                     int aArg) override;
   virtual void NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& aScrollId,
                                          const nsString& aEvent) override;
   virtual void NotifyFlushComplete() override;
--- a/gfx/layers/basic/BasicCanvasLayer.cpp
+++ b/gfx/layers/basic/BasicCanvasLayer.cpp
@@ -6,64 +6,132 @@
 #include "BasicCanvasLayer.h"
 #include "AsyncCanvasRenderer.h"
 #include "basic/BasicLayers.h"          // for BasicLayerManager
 #include "basic/BasicLayersImpl.h"      // for GetEffectiveOperator
 #include "mozilla/mozalloc.h"           // for operator new
 #include "nsCOMPtr.h"                   // for already_AddRefed
 #include "nsISupportsImpl.h"            // for Layer::AddRef, etc
 #include "gfx2DGlue.h"
+#include "GLScreenBuffer.h"
+#include "GLContext.h"
+#include "gfxUtils.h"
+#include "mozilla/layers/PersistentBufferProvider.h"
+#include "client/TextureClientSharedSurface.h"
 
 class gfxContext;
 
 using namespace mozilla::gfx;
 using namespace mozilla::gl;
 
 namespace mozilla {
 namespace layers {
 
+already_AddRefed<SourceSurface>
+BasicCanvasLayer::UpdateSurface()
+{
+  if (mAsyncRenderer) {
+    MOZ_ASSERT(!mBufferProvider);
+    MOZ_ASSERT(!mGLContext);
+    return mAsyncRenderer->GetSurface();
+  }
+
+  if (!mGLContext) {
+    return nullptr;
+  }
+
+  SharedSurface* frontbuffer = nullptr;
+  if (mGLFrontbuffer) {
+    frontbuffer = mGLFrontbuffer.get();
+  } else {
+    GLScreenBuffer* screen = mGLContext->Screen();
+    const auto& front = screen->Front();
+    if (front) {
+      frontbuffer = front->Surf();
+    }
+  }
+
+  if (!frontbuffer) {
+    NS_WARNING("Null frame received.");
+    return nullptr;
+  }
+
+  IntSize readSize(frontbuffer->mSize);
+  SurfaceFormat format = (GetContentFlags() & CONTENT_OPAQUE)
+                          ? SurfaceFormat::B8G8R8X8
+                          : SurfaceFormat::B8G8R8A8;
+  bool needsPremult = frontbuffer->mHasAlpha && !mIsAlphaPremultiplied;
+
+  RefPtr<DataSourceSurface> resultSurf = GetTempSurface(readSize, format);
+  // There will already be a warning from inside of GetTempSurface, but
+  // it doesn't hurt to complain:
+  if (NS_WARN_IF(!resultSurf)) {
+    return nullptr;
+  }
+
+  // Readback handles Flush/MarkDirty.
+  mGLContext->Readback(frontbuffer, resultSurf);
+  if (needsPremult) {
+    gfxUtils::PremultiplyDataSurface(resultSurf, resultSurf);
+  }
+  MOZ_ASSERT(resultSurf);
+
+  return resultSurf.forget();
+}
+
 void
 BasicCanvasLayer::Paint(DrawTarget* aDT,
                         const Point& aDeviceOffset,
                         Layer* aMaskLayer)
 {
   if (IsHidden())
     return;
 
+  RefPtr<SourceSurface> surface;
   if (IsDirty()) {
     Painted();
 
     FirePreTransactionCallback();
-    UpdateTarget();
+    surface = UpdateSurface();
     FireDidTransactionCallback();
   }
 
-  if (!mSurface) {
+  bool bufferPoviderSnapshot = false;
+  if (!surface && mBufferProvider) {
+    surface = mBufferProvider->BorrowSnapshot();
+    bufferPoviderSnapshot = !!surface;
+  }
+
+  if (!surface) {
     return;
   }
 
   const bool needsYFlip = (mOriginPos == gl::OriginPos::BottomLeft);
 
   Matrix oldTM;
   if (needsYFlip) {
     oldTM = aDT->GetTransform();
     aDT->SetTransform(Matrix(oldTM).
                         PreTranslate(0.0f, mBounds.height).
                         PreScale(1.0f, -1.0f));
   }
 
   FillRectWithMask(aDT, aDeviceOffset,
                    Rect(0, 0, mBounds.width, mBounds.height),
-                   mSurface, mSamplingFilter,
+                   surface, mSamplingFilter,
                    DrawOptions(GetEffectiveOpacity(), GetEffectiveOperator(this)),
                    aMaskLayer);
 
   if (needsYFlip) {
     aDT->SetTransform(oldTM);
   }
+
+  if (bufferPoviderSnapshot) {
+    mBufferProvider->ReturnSnapshot(surface.forget());
+  }
 }
 
 already_AddRefed<CanvasLayer>
 BasicLayerManager::CreateCanvasLayer()
 {
   NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
   RefPtr<CanvasLayer> layer = new BasicCanvasLayer(this);
   return layer.forget();
--- a/gfx/layers/basic/BasicCanvasLayer.h
+++ b/gfx/layers/basic/BasicCanvasLayer.h
@@ -31,16 +31,19 @@ public:
     CanvasLayer::SetVisibleRegion(aRegion);
   }
 
   virtual void Paint(gfx::DrawTarget* aDT,
                      const gfx::Point& aDeviceOffset,
                      Layer* aMaskLayer) override;
 
 protected:
+
+  already_AddRefed<gfx::SourceSurface> UpdateSurface();
+
   BasicLayerManager* BasicManager()
   {
     return static_cast<BasicLayerManager*>(mManager);
   }
 };
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/client/CanvasClient.cpp
+++ b/gfx/layers/client/CanvasClient.cpp
@@ -93,18 +93,17 @@ CanvasClient2D::UpdateFromTexture(Textur
 }
 
 void
 CanvasClient2D::Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer)
 {
   mBufferProviderTexture = nullptr;
 
   AutoRemoveTexture autoRemove(this);
-  if (mBackBuffer &&
-      (mBackBuffer->IsImmutable() || mBackBuffer->GetSize() != aSize)) {
+  if (mBackBuffer && (mBackBuffer->IsReadLocked() || mBackBuffer->GetSize() != aSize)) {
     autoRemove.mTexture = mBackBuffer;
     mBackBuffer = nullptr;
   }
 
   bool bufferCreated = false;
   if (!mBackBuffer) {
     bool isOpaque = (aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE);
     gfxContentType contentType = isOpaque
@@ -117,32 +116,36 @@ CanvasClient2D::Update(gfx::IntSize aSiz
       flags |= TextureFlags::ORIGIN_BOTTOM_LEFT;
     }
 
     mBackBuffer = CreateTextureClientForCanvas(surfaceFormat, aSize, flags, aLayer);
     if (!mBackBuffer) {
       NS_WARNING("Failed to allocate the TextureClient");
       return;
     }
+    mBackBuffer->EnableReadLock();
     MOZ_ASSERT(mBackBuffer->CanExposeDrawTarget());
 
     bufferCreated = true;
   }
 
   bool updated = false;
   {
     TextureClientAutoLock autoLock(mBackBuffer, OpenMode::OPEN_WRITE_ONLY);
     if (!autoLock.Succeeded()) {
       mBackBuffer = nullptr;
       return;
     }
 
     RefPtr<DrawTarget> target = mBackBuffer->BorrowDrawTarget();
     if (target) {
-      aLayer->UpdateTarget(target);
+      if (!aLayer->UpdateTarget(target)) {
+        NS_WARNING("Failed to copy the canvas into a TextureClient.");
+        return;
+      }
       updated = true;
     }
   }
 
   if (bufferCreated && !AddTextureClient(mBackBuffer)) {
     mBackBuffer = nullptr;
     return;
   }
--- a/gfx/layers/client/ClientCanvasLayer.cpp
+++ b/gfx/layers/client/ClientCanvasLayer.cpp
@@ -85,26 +85,21 @@ void
 ClientCanvasLayer::RenderLayer()
 {
   PROFILER_LABEL("ClientCanvasLayer", "RenderLayer",
     js::ProfileEntry::Category::GRAPHICS);
 
   RenderMaskLayers(this);
 
   if (!mCanvasClient) {
-    TextureFlags flags = TextureFlags::IMMEDIATE_UPLOAD;
+    TextureFlags flags = TextureFlags::DEFAULT;
     if (mOriginPos == gl::OriginPos::BottomLeft) {
       flags |= TextureFlags::ORIGIN_BOTTOM_LEFT;
     }
 
-    if (!mGLContext) {
-      // We don't support locking for buffer surfaces currently
-      flags |= TextureFlags::IMMEDIATE_UPLOAD;
-    }
-
     if (!mIsAlphaPremultiplied) {
       flags |= TextureFlags::NON_PREMULTIPLIED;
     }
 
     mCanvasClient = CanvasClient::CreateCanvasClient(GetCanvasClientType(),
                                                      ClientManager()->AsShadowForwarder(),
                                                      flags);
     if (!mCanvasClient) {
@@ -137,16 +132,109 @@ ClientCanvasLayer::RenderLayer()
   }
 
   FireDidTransactionCallback();
 
   ClientManager()->Hold(this);
   mCanvasClient->Updated();
 }
 
+bool
+ClientCanvasLayer::UpdateTarget(DrawTarget* aDestTarget)
+{
+  MOZ_ASSERT(aDestTarget);
+  if (!aDestTarget) {
+    return false;
+  }
+
+  RefPtr<SourceSurface> surface;
+
+  if (!mGLContext) {
+    AutoReturnSnapshot autoReturn;
+
+    if (mAsyncRenderer) {
+      surface = mAsyncRenderer->GetSurface();
+    } else if (mBufferProvider) {
+      surface = mBufferProvider->BorrowSnapshot();
+      autoReturn.mSnapshot = &surface;
+      autoReturn.mBufferProvider = mBufferProvider;
+    }
+
+    MOZ_ASSERT(surface);
+    if (!surface) {
+      return false;
+    }
+
+    aDestTarget->CopySurface(surface,
+                             IntRect(0, 0, mBounds.width, mBounds.height),
+                             IntPoint(0, 0));
+    return true;
+  }
+
+  SharedSurface* frontbuffer = nullptr;
+  if (mGLFrontbuffer) {
+    frontbuffer = mGLFrontbuffer.get();
+  } else {
+    GLScreenBuffer* screen = mGLContext->Screen();
+    const auto& front = screen->Front();
+    if (front) {
+      frontbuffer = front->Surf();
+    }
+  }
+
+  if (!frontbuffer) {
+    NS_WARNING("Null frame received.");
+    return false;
+  }
+
+  IntSize readSize(frontbuffer->mSize);
+  SurfaceFormat format = (GetContentFlags() & CONTENT_OPAQUE)
+                          ? SurfaceFormat::B8G8R8X8
+                          : SurfaceFormat::B8G8R8A8;
+  bool needsPremult = frontbuffer->mHasAlpha && !mIsAlphaPremultiplied;
+
+  // Try to read back directly into aDestTarget's output buffer
+  uint8_t* destData;
+  IntSize destSize;
+  int32_t destStride;
+  SurfaceFormat destFormat;
+  if (aDestTarget->LockBits(&destData, &destSize, &destStride, &destFormat)) {
+    if (destSize == readSize && destFormat == format) {
+      RefPtr<DataSourceSurface> data =
+        Factory::CreateWrappingDataSourceSurface(destData, destStride, destSize, destFormat);
+      mGLContext->Readback(frontbuffer, data);
+      if (needsPremult) {
+        gfxUtils::PremultiplyDataSurface(data, data);
+      }
+      aDestTarget->ReleaseBits(destData);
+      return true;
+    }
+    aDestTarget->ReleaseBits(destData);
+  }
+
+  RefPtr<DataSourceSurface> resultSurf = GetTempSurface(readSize, format);
+  // There will already be a warning from inside of GetTempSurface, but
+  // it doesn't hurt to complain:
+  if (NS_WARN_IF(!resultSurf)) {
+    return false;
+  }
+
+  // Readback handles Flush/MarkDirty.
+  mGLContext->Readback(frontbuffer, resultSurf);
+  if (needsPremult) {
+    gfxUtils::PremultiplyDataSurface(resultSurf, resultSurf);
+  }
+
+  aDestTarget->CopySurface(resultSurf,
+                           IntRect(0, 0, readSize.width, readSize.height),
+                           IntPoint(0, 0));
+
+  return true;
+}
+
 CanvasClient::CanvasClientType
 ClientCanvasLayer::GetCanvasClientType()
 {
   if (mAsyncRenderer) {
     return CanvasClient::CanvasClientAsync;
   }
 
   if (mGLContext) {
--- a/gfx/layers/client/ClientCanvasLayer.h
+++ b/gfx/layers/client/ClientCanvasLayer.h
@@ -85,16 +85,19 @@ public:
   virtual CompositableClient* GetCompositableClient() override
   {
     return mCanvasClient;
   }
 
   const TextureFlags& Flags() const { return mFlags; }
 
 protected:
+
+  bool UpdateTarget(gfx::DrawTarget* aDestTarget = nullptr);
+
   ClientLayerManager* ClientManager()
   {
     return static_cast<ClientLayerManager*>(mManager);
   }
 
   CanvasClientType GetCanvasClientType();
 
   RefPtr<CanvasClient> mCanvasClient;
--- a/gfx/layers/client/TextureClientPool.cpp
+++ b/gfx/layers/client/TextureClientPool.cpp
@@ -20,24 +20,24 @@ namespace mozilla {
 namespace layers {
 
 
 TextureClientPool::TextureClientPool(LayersBackend aLayersBackend,
                                      gfx::SurfaceFormat aFormat,
                                      gfx::IntSize aSize,
                                      TextureFlags aFlags,
                                      uint32_t aInitialPoolSize,
-                                     uint32_t aPoolIncrementSize,
+                                     uint32_t aPoolUnusedSize,
                                      TextureForwarder* aAllocator)
   : mBackend(aLayersBackend)
   , mFormat(aFormat)
   , mSize(aSize)
   , mFlags(aFlags)
   , mInitialPoolSize(aInitialPoolSize)
-  , mPoolIncrementSize(aPoolIncrementSize)
+  , mPoolUnusedSize(aPoolUnusedSize)
   , mOutstandingClients(0)
   , mSurfaceAllocator(aAllocator)
   , mDestroyed(false)
 {
   TCP_LOG("TexturePool %p created with maximum unused texture clients %u\n",
       this, mInitialPoolSize);
   mTimer = do_CreateInstance("@mozilla.org/timer;1");
   if (aFormat == gfx::SurfaceFormat::UNKNOWN) {
@@ -86,20 +86,19 @@ static bool TestClientPool(const char* w
 already_AddRefed<TextureClient>
 TextureClientPool::GetTextureClient()
 {
   // Try to fetch a client from the pool
   RefPtr<TextureClient> textureClient;
 
   // We initially allocate mInitialPoolSize for our pool. If we run
   // out of TextureClients, we allocate additional TextureClients to try and keep around
-  // mPoolIncrementSize
+  // mPoolUnusedSize
   if (!mTextureClients.size()) {
-    size_t size = mOutstandingClients ? mPoolIncrementSize : mInitialPoolSize;
-    AllocateTextureClients(size);
+    AllocateTextureClient();
   }
 
   if (!mTextureClients.size()) {
     // All our allocations failed, return nullptr
     return nullptr;
   }
 
   mOutstandingClients++;
@@ -114,41 +113,40 @@ TextureClientPool::GetTextureClient()
 #endif
   TCP_LOG("TexturePool %p giving %p from pool; size %u outstanding %u\n",
       this, textureClient.get(), mTextureClients.size(), mOutstandingClients);
 
   return textureClient.forget();
 }
 
 void
-TextureClientPool::AllocateTextureClients(size_t aSize)
+TextureClientPool::AllocateTextureClient()
 {
-  TCP_LOG("TexturePool %p allocating %u clients, outstanding %u\n",
-      this, aSize, mOutstandingClients);
+  TCP_LOG("TexturePool %p allocating TextureClient, outstanding %u\n",
+      this, mOutstandingClients);
 
   RefPtr<TextureClient> newClient;
-  while (mTextureClients.size() < aSize) {
-    if (gfxPrefs::ForceShmemTiles()) {
-      // gfx::BackendType::NONE means use the content backend
-      newClient =
-        TextureClient::CreateForRawBufferAccess(mSurfaceAllocator,
-                                                mFormat, mSize,
-                                                gfx::BackendType::NONE,
-                                                mFlags, ALLOC_DEFAULT);
-    } else {
-      newClient =
-        TextureClient::CreateForDrawing(mSurfaceAllocator,
-                                        mFormat, mSize,
-                                        mBackend,
-                                        BackendSelector::Content,
-                                        mFlags);
-    }
-    if (newClient) {
-      mTextureClients.push(newClient);
-    }
+  if (gfxPrefs::ForceShmemTiles()) {
+    // gfx::BackendType::NONE means use the content backend
+    newClient =
+      TextureClient::CreateForRawBufferAccess(mSurfaceAllocator,
+                                              mFormat, mSize,
+                                              gfx::BackendType::NONE,
+                                              mFlags, ALLOC_DEFAULT);
+  } else {
+    newClient =
+      TextureClient::CreateForDrawing(mSurfaceAllocator,
+                                      mFormat, mSize,
+                                      mBackend,
+                                      BackendSelector::Content,
+                                      mFlags);
+  }
+
+  if (newClient) {
+    mTextureClients.push(newClient);
   }
 }
 
 void
 TextureClientPool::ReturnTextureClient(TextureClient *aClient)
 {
   if (!aClient || mDestroyed) {
     return;
@@ -191,27 +189,27 @@ TextureClientPool::ShrinkToMaximumSize()
   // We're over our desired maximum size, immediately shrink down to the
   // maximum.
   //
   // We cull from the deferred TextureClients first, as we can't reuse those
   // until they get returned.
   uint32_t totalUnusedTextureClients = mTextureClients.size() + mTextureClientsDeferred.size();
 
   // If we have > mInitialPoolSize outstanding, then we want to keep around
-  // mPoolIncrementSize at a maximum. If we have fewer than mInitialPoolSize
+  // mPoolUnusedSize at a maximum. If we have fewer than mInitialPoolSize
   // outstanding, then keep around the entire initial pool size.
   uint32_t targetUnusedClients;
   if (mOutstandingClients > mInitialPoolSize) {
-    targetUnusedClients = mPoolIncrementSize;
+    targetUnusedClients = mPoolUnusedSize;
   } else {
     targetUnusedClients = mInitialPoolSize;
   }
 
-  TCP_LOG("TexturePool %p shrinking to maximum unused size %u; total outstanding %u\n",
-      this, targetUnusedClients, mOutstandingClients);
+  TCP_LOG("TexturePool %p shrinking to maximum unused size %u; current pool size %u; total outstanding %u\n",
+      this, targetUnusedClients, totalUnusedTextureClients, mOutstandingClients);
 
   while (totalUnusedTextureClients > targetUnusedClients) {
     if (mTextureClientsDeferred.size()) {
       mOutstandingClients--;
       TCP_LOG("TexturePool %p dropped deferred client %p; %u remaining\n",
           this, mTextureClientsDeferred.front().get(),
           mTextureClientsDeferred.size() - 1);
       mTextureClientsDeferred.pop_front();
@@ -283,13 +281,13 @@ TextureClientPool::Clear()
   }
 }
 
 void TextureClientPool::Destroy()
 {
   Clear();
   mDestroyed = true;
   mInitialPoolSize = 0;
-  mPoolIncrementSize = 0;
+  mPoolUnusedSize = 0;
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/client/TextureClientPool.h
+++ b/gfx/layers/client/TextureClientPool.h
@@ -44,17 +44,17 @@ class TextureClientPool final : public T
   ~TextureClientPool();
 
 public:
   TextureClientPool(LayersBackend aBackend,
                     gfx::SurfaceFormat aFormat,
                     gfx::IntSize aSize,
                     TextureFlags aFlags,
                     uint32_t aInitialPoolSize,
-                    uint32_t aPoolIncrementSize,
+                    uint32_t aPoolUnusedSize,
                     TextureForwarder* aAllocator);
 
   /**
    * Gets an allocated TextureClient of size and format that are determined
    * by the initialisation parameters given to the pool. This will either be
    * a cached client that was returned to the pool, or a newly allocated
    * client if one isn't available.
    *
@@ -106,21 +106,18 @@ public:
   /**
    * Clear the pool and put it in a state where it won't recycle any new texture.
    */
   void Destroy();
 
 private:
   void ReturnUnlockedClients();
 
-  /// We maintain a number of unused texture clients for immediate return from
-  /// GetTextureClient(). This will normally be called if there are no
-  /// TextureClients available in the pool, which ideally should only ever
-  /// be at startup.
-  void AllocateTextureClients(size_t aSize);
+  /// Allocate a single TextureClient to be returned from the pool.
+  void AllocateTextureClient();
 
   /// Backend passed to the TextureClient for buffer creation.
   LayersBackend mBackend;
 
   /// Format is passed to the TextureClient for buffer creation.
   gfx::SurfaceFormat mFormat;
 
   /// The width and height of the tiles to be used.
@@ -130,17 +127,17 @@ private:
   const TextureFlags mFlags;
 
   // The initial number of unused texture clients to seed the pool with
   // on construction
   uint32_t mInitialPoolSize;
 
   // How many unused texture clients to try and keep around if we go over
   // the initial allocation
-  uint32_t mPoolIncrementSize;
+  uint32_t mPoolUnusedSize;
 
   /// This is a total number of clients in the wild and in the stack of
   /// deferred clients (see below).  So, the total number of clients in
   /// existence is always mOutstandingClients + the size of mTextureClients.
   uint32_t mOutstandingClients;
 
   // On b2g gonk, std::queue might be a better choice.
   // On ICS, fence wait happens implicitly before drawing.
--- a/gfx/layers/ipc/APZChild.cpp
+++ b/gfx/layers/ipc/APZChild.cpp
@@ -96,17 +96,17 @@ APZChild::~APZChild()
 bool
 APZChild::RecvUpdateFrame(const FrameMetrics& aFrameMetrics)
 {
   return mBrowser->UpdateFrame(aFrameMetrics);
 }
 
 bool
 APZChild::RecvHandleTap(const TapType& aType,
-                        const CSSPoint& aPoint,
+                        const LayoutDevicePoint& aPoint,
                         const Modifiers& aModifiers,
                         const ScrollableLayerGuid& aGuid,
                         const uint64_t& aInputBlockId,
                         const bool& aCallTakeFocusForClickFromTap)
 {
   mBrowser->HandleTap(aType, aPoint, aModifiers, aGuid,
       aInputBlockId, aCallTakeFocusForClickFromTap);
   return true;
--- a/gfx/layers/ipc/APZChild.h
+++ b/gfx/layers/ipc/APZChild.h
@@ -24,17 +24,17 @@ class APZChild final : public PAPZChild
 public:
   static APZChild* Create(const dom::TabId& aTabId);
 
   ~APZChild();
 
   virtual bool RecvUpdateFrame(const FrameMetrics& frame) override;
 
   virtual bool RecvHandleTap(const TapType& aType,
-                             const CSSPoint& aPoint,
+                             const LayoutDevicePoint& aPoint,
                              const Modifiers& aModifiers,
                              const ScrollableLayerGuid& aGuid,
                              const uint64_t& aInputBlockId,
                              const bool& aCallTakeFocusForClickFromTap) override;
 
   virtual bool RecvNotifyAPZStateChange(const ViewID& aViewId,
                                         const APZStateChange& aChange,
                                         const int& aArg) override;
--- a/gfx/layers/ipc/CompositorBridgeChild.cpp
+++ b/gfx/layers/ipc/CompositorBridgeChild.cpp
@@ -943,17 +943,17 @@ CompositorBridgeChild::GetTexturePool(La
   }
 
   mTexturePools.AppendElement(
       new TextureClientPool(aBackend, aFormat,
                             IntSize(gfxPlatform::GetPlatform()->GetTileWidth(),
                                     gfxPlatform::GetPlatform()->GetTileHeight()),
                             aFlags,
                             gfxPrefs::LayersTileInitialPoolSize(),
-                            gfxPrefs::LayersTilePoolIncrementSize(),
+                            gfxPrefs::LayersTilePoolUnusedSize(),
                             this));
 
   return mTexturePools.LastElement();
 }
 
 void
 CompositorBridgeChild::HandleMemoryPressure()
 {
--- a/gfx/layers/ipc/PAPZ.ipdl
+++ b/gfx/layers/ipc/PAPZ.ipdl
@@ -4,17 +4,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include "mozilla/GfxMessageUtils.h";
 
 include protocol PContent;
 
-using mozilla::CSSPoint from "Units.h";
+using mozilla::LayoutDevicePoint from "Units.h";
 using CSSRect from "Units.h";
 using struct mozilla::layers::FrameMetrics from "FrameMetrics.h";
 using struct mozilla::layers::ScrollableLayerGuid from "FrameMetrics.h";
 using mozilla::layers::FrameMetrics::ViewID from "FrameMetrics.h";
 using mozilla::layers::MaybeZoomConstraints from "FrameMetrics.h";
 using mozilla::layers::TouchBehaviorFlags from "mozilla/layers/APZUtils.h";
 using mozilla::layers::GeckoContentController::TapType from "mozilla/layers/GeckoContentController.h";
 using mozilla::layers::GeckoContentController::APZStateChange from "mozilla/layers/GeckoContentController.h";
@@ -90,17 +90,17 @@ child:
   async UpdateFrame(FrameMetrics frame);
 
   // The following methods correspond to functions on the GeckoContentController
   // interface in gfx/layers/apz/public/GeckoContentController.h. Refer to documentation
   // in that file for these functions.
   // The aCallTakeFocusForClickFromTap argument is used for eSingleTap types,
   // to request that the child take focus before dispatching the mouse events
   // for the tap (otherwise the resulting focus behaviour is incorrect).
-  async HandleTap(TapType aType, CSSPoint point, Modifiers aModifiers,
+  async HandleTap(TapType aType, LayoutDevicePoint point, Modifiers aModifiers,
                   ScrollableLayerGuid aGuid, uint64_t aInputBlockId,
                   bool aCallTakeFocusForClickFromTap);
   async NotifyAPZStateChange(ViewID aViewId, APZStateChange aChange, int aArg);
   async NotifyFlushComplete();
 
   async Destroy();
 };
 
--- a/gfx/layers/ipc/RemoteContentController.cpp
+++ b/gfx/layers/ipc/RemoteContentController.cpp
@@ -49,25 +49,25 @@ RemoteContentController::RequestContentR
   MOZ_ASSERT(NS_IsMainThread());
   if (CanSend()) {
     Unused << SendUpdateFrame(aFrameMetrics);
   }
 }
 
 void
 RemoteContentController::HandleTap(TapType aTapType,
-                                   const CSSPoint& aPoint,
+                                   const LayoutDevicePoint& aPoint,
                                    Modifiers aModifiers,
                                    const ScrollableLayerGuid& aGuid,
                                    uint64_t aInputBlockId)
 {
   if (MessageLoop::current() != mUILoop) {
     // We have to send this message from the "UI thread" (main
     // thread).
-    mUILoop->PostTask(NewRunnableMethod<TapType, CSSPoint, Modifiers,
+    mUILoop->PostTask(NewRunnableMethod<TapType, LayoutDevicePoint, Modifiers,
                                         ScrollableLayerGuid, uint64_t>(this,
                                           &RemoteContentController::HandleTap,
                                           aTapType, aPoint, aModifiers, aGuid,
                                           aInputBlockId));
     return;
   }
 
   bool callTakeFocusForClickFromTap = (aTapType == TapType::eSingleTap);
--- a/gfx/layers/ipc/RemoteContentController.h
+++ b/gfx/layers/ipc/RemoteContentController.h
@@ -39,17 +39,17 @@ public:
                                    dom::TabParent* aBrowserParent);
 
   virtual ~RemoteContentController();
 
   // Needs to be called on the main thread.
   virtual void RequestContentRepaint(const FrameMetrics& aFrameMetrics) override;
 
   virtual void HandleTap(TapType aTapType,
-                         const CSSPoint& aPoint,
+                         const LayoutDevicePoint& aPoint,
                          Modifiers aModifiers,
                          const ScrollableLayerGuid& aGuid,
                          uint64_t aInputBlockId) override;
 
   virtual void PostDelayedTask(already_AddRefed<Runnable> aTask, int aDelayMs) override;
 
   virtual bool GetTouchSensitiveRegion(CSSRect* aOutRegion) override;
 
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -480,17 +480,17 @@ private:
   DECL_GFX_PREF(Once, "layers.stereo-video.enabled",           StereoVideoEnabled, bool, false);
 
   // We allow for configurable and rectangular tile size to avoid wasting memory on devices whose
   // screen size does not align nicely to the default tile size. Although layers can be any size,
   // they are often the same size as the screen, especially for width.
   DECL_GFX_PREF(Once, "layers.tile-width",                     LayersTileWidth, int32_t, 256);
   DECL_GFX_PREF(Once, "layers.tile-height",                    LayersTileHeight, int32_t, 256);
   DECL_GFX_PREF(Once, "layers.tile-initial-pool-size",         LayersTileInitialPoolSize, uint32_t, (uint32_t)50);
-  DECL_GFX_PREF(Once, "layers.tile-pool-increment-size",       LayersTilePoolIncrementSize, uint32_t, (uint32_t)10);
+  DECL_GFX_PREF(Once, "layers.tile-pool-unused-size",          LayersTilePoolUnusedSize, uint32_t, (uint32_t)10);
   DECL_GFX_PREF(Once, "layers.tiles.adjust",                   LayersTilesAdjust, bool, true);
   DECL_GFX_PREF(Once, "layers.tiles.edge-padding",             TileEdgePaddingEnabled, bool, true);
   DECL_GFX_PREF(Live, "layers.tiles.fade-in.enabled",          LayerTileFadeInEnabled, bool, false);
   DECL_GFX_PREF(Live, "layers.tiles.fade-in.duration-ms",      LayerTileFadeInDuration, uint32_t, 250);
   DECL_GFX_PREF(Live, "layers.transaction.warning-ms",         LayerTransactionWarning, uint32_t, 200);
   DECL_GFX_PREF(Once, "layers.uniformity-info",                UniformityInfo, bool, false);
   DECL_GFX_PREF(Once, "layers.use-image-offscreen-surfaces",   UseImageOffscreenSurfaces, bool, true);
 
--- a/ipc/mscom/InterceptorLog.cpp
+++ b/ipc/mscom/InterceptorLog.cpp
@@ -88,18 +88,23 @@ Logger::Logger(const nsACString& aLeafBa
   GeckoProcessType procType = XRE_GetProcessType();
   nsAutoCString leafName(aLeafBaseName);
   nsresult rv;
   if (procType == GeckoProcessType_Default) {
     leafName.AppendLiteral("-Parent-");
     rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(logFileName));
   } else if (procType == GeckoProcessType_Content) {
     leafName.AppendLiteral("-Content-");
+#if defined(MOZ_CONTENT_SANDBOX)
     rv = NS_GetSpecialDirectory(NS_APP_CONTENT_PROCESS_TEMP_DIR,
                                 getter_AddRefs(logFileName));
+#else
+    rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR,
+                                getter_AddRefs(logFileName));
+#endif // defined(MOZ_CONTENT_SANDBOX)
   } else {
     return;
   }
   if (NS_FAILED(rv)) {
     return;
   }
   DWORD pid = GetCurrentProcessId();
   leafName.AppendPrintf("%u.log", pid);
--- a/js/public/HeapAPI.h
+++ b/js/public/HeapAPI.h
@@ -121,19 +121,18 @@ struct Zone
 
     bool needsIncrementalBarrier_;
 
     Zone(JSRuntime* runtime, JSTracer* barrierTracerArg)
       : runtime_(runtime),
         barrierTracer_(barrierTracerArg),
         needsIncrementalBarrier_(false)
     {
-        for (auto& stackRootPtr : stackRoots_) {
+        for (auto& stackRootPtr : stackRoots_)
             stackRootPtr = nullptr;
-        }
     }
 
     bool needsIncrementalBarrier() const {
         return needsIncrementalBarrier_;
     }
 
     JSTracer* barrierTracer() {
         MOZ_ASSERT(needsIncrementalBarrier_);
--- a/js/public/Vector.h
+++ b/js/public/Vector.h
@@ -29,23 +29,17 @@ struct TypeIsGCThing : mozilla::FalseTyp
 //template <>
 //struct TypeIsGCThing<JS::Value> : mozilla::TrueType
 //{};
 
 } // namespace detail
 
 template <typename T,
           size_t MinInlineCapacity = 0,
-          class AllocPolicy = TempAllocPolicy
-// 1800 is MSVC2013.  Optimistically assume MSVC2015 (1900) is fixed.
-// If you're porting to MSVC2015 and this doesn't work, extend the
-// condition to encompass that additional version (but *do* keep the
-// version-check so we know when MSVC's fixed).
-#if !defined(_MSC_VER) || (1800 <= _MSC_VER && _MSC_VER <= 1800)
+          class AllocPolicy = TempAllocPolicy,
          // Don't use this with JS::Value!  Use JS::AutoValueVector instead.
-         , typename = typename mozilla::EnableIf<!detail::TypeIsGCThing<T>::value>::Type
-#endif
+         typename = typename mozilla::EnableIf<!detail::TypeIsGCThing<T>::value>::Type
          >
 using Vector = mozilla::Vector<T, MinInlineCapacity, AllocPolicy>;
 
 } // namespace js
 
 #endif /* js_Vector_h */
--- a/js/src/asmjs/WasmBaselineCompile.cpp
+++ b/js/src/asmjs/WasmBaselineCompile.cpp
@@ -2458,25 +2458,25 @@ class BaseCompiler
         masm.rolq_cl(srcDest.reg.reg);
 #else
         MOZ_CRASH("BaseCompiler platform hook: rotateLeftI64");
 #endif
     }
 
     void clzI64(RegI64 srcDest) {
 #if defined(JS_CODEGEN_X64)
-        masm.clz64(srcDest.reg, srcDest.reg);
+        masm.clz64(srcDest.reg, srcDest.reg.reg);
 #else
         MOZ_CRASH("BaseCompiler platform hook: clzI64");
 #endif
     }
 
     void ctzI64(RegI64 srcDest) {
 #if defined(JS_CODEGEN_X64)
-        masm.ctz64(srcDest.reg, srcDest.reg);
+        masm.ctz64(srcDest.reg, srcDest.reg.reg);
 #else
         MOZ_CRASH("BaseCompiler platform hook: ctzI64");
 #endif
     }
 
     bool popcnt32NeedsTemp() const {
 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
         return !AssemblerX86Shared::HasPOPCNT();
@@ -2498,17 +2498,17 @@ class BaseCompiler
         return !AssemblerX86Shared::HasPOPCNT();
 #else
         MOZ_CRASH("BaseCompiler platform hook: popcnt64NeedsTemp");
 #endif
     }
 
     void popcntI64(RegI64 srcDest, RegI64 tmp) {
 #if defined(JS_CODEGEN_X64)
-        masm.popcnt64(srcDest.reg, srcDest.reg, tmp.reg);
+        masm.popcnt64(srcDest.reg, srcDest.reg, tmp.reg.reg);
 #else
         MOZ_CRASH("BaseCompiler platform hook: popcntI64");
 #endif
     }
 
     void reinterpretI64AsF64(RegI64 src, RegF64 dest) {
 #if defined(JS_CODEGEN_X64)
         masm.vmovq(src.reg.reg, dest.reg);
@@ -2574,29 +2574,29 @@ class BaseCompiler
 
     MOZ_MUST_USE
     bool truncateF32ToI32(RegF32 src, RegI32 dest) {
         OutOfLineCode* ool =
             addOutOfLineCode(new (alloc_) OutOfLineTruncateF32OrF64ToI32(AnyReg(src),
                                                                          dest));
         if (!ool)
             return false;
-        masm.branchTruncateFloat32(src.reg, dest.reg, ool->entry());
+        masm.branchTruncateFloat32ToInt32(src.reg, dest.reg, ool->entry());
         masm.bind(ool->rejoin());
         return true;
     }
 
     MOZ_MUST_USE
     bool truncateF64ToI32(RegF64 src, RegI32 dest) {
         OutOfLineCode* ool =
             addOutOfLineCode(new (alloc_) OutOfLineTruncateF32OrF64ToI32(AnyReg(src),
                                                                          dest));
         if (!ool)
             return false;
-        masm.branchTruncateDouble(src.reg, dest.reg, ool->entry());
+        masm.branchTruncateDoubleToInt32(src.reg, dest.reg, ool->entry());
         masm.bind(ool->rejoin());
         return true;
     }
 
 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
     class OutOfLineTruncateCheckF32OrF64ToI64 : public OutOfLineCode
     {
         AnyReg src;
@@ -6771,17 +6771,17 @@ volatileReturnGPR()
 LiveRegisterSet BaseCompiler::VolatileReturnGPR = volatileReturnGPR();
 
 } // wasm
 } // js
 
 bool
 js::wasm::BaselineCanCompile(const FunctionGenerator* fg)
 {
-#if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)
+#if defined(JS_CODEGEN_X64)
     if (!fg->usesSignalsForInterrupts())
         return false;
 
     if (fg->usesAtomics())
         return false;
 
     if (fg->usesSimd())
         return false;
--- a/js/src/asmjs/WasmCompile.cpp
+++ b/js/src/asmjs/WasmCompile.cpp
@@ -35,26 +35,16 @@ using mozilla::IsNaN;
 static bool
 Fail(Decoder& d, const char* str)
 {
     uint32_t offset = d.currentOffset();
     d.fail(UniqueChars(JS_smprintf("compile error at offset %" PRIu32 ": %s", offset, str)));
     return false;
 }
 
-static bool
-IsI64Implemented()
-{
-#ifdef JS_CPU_X64
-    return true;
-#else
-    return false;
-#endif
-}
-
 namespace {
 
 struct ValidatingPolicy : ExprIterPolicy
 {
     // Validation is what we're all about here.
     static const bool Validate = true;
 };
 
--- a/js/src/asmjs/WasmIonCompile.cpp
+++ b/js/src/asmjs/WasmIonCompile.cpp
@@ -215,19 +215,18 @@ class FunctionCompiler
     void finish()
     {
         mirGen().initWasmMaxStackArgBytes(maxStackArgBytes_);
 
         MOZ_ASSERT(callStack_.empty());
         MOZ_ASSERT(loopDepth_ == 0);
         MOZ_ASSERT(blockDepth_ == 0);
 #ifdef DEBUG
-        for (ControlFlowPatchVector& patches : blockPatches_) {
+        for (ControlFlowPatchVector& patches : blockPatches_)
             MOZ_ASSERT(patches.empty());
-        }
 #endif
         MOZ_ASSERT(inDeadCode());
         MOZ_ASSERT(done(), "all bytes must be consumed");
         MOZ_ASSERT(func_.callSiteLineNums().length() == lastReadCallSite_);
     }
 
     /************************* Read-only interface (after local scope setup) */
 
@@ -812,22 +811,38 @@ class FunctionCompiler
     }
 
     bool passArg(MDefinition* argDef, ValType type, CallArgs* args)
     {
         if (inDeadCode())
             return true;
 
         ABIArg arg = args->abi_.next(ToMIRType(type));
-        if (arg.kind() != ABIArg::Stack)
+        switch (arg.kind()) {
+#ifdef JS_CODEGEN_REGISTER_PAIR
+          case ABIArg::GPR_PAIR: {
+            auto mirLow = MWrapInt64ToInt32::NewAsmJS(alloc(), argDef, /* bottomHalf = */ true);
+            curBlock_->add(mirLow);
+            auto mirHigh = MWrapInt64ToInt32::NewAsmJS(alloc(), argDef, /* bottomHalf = */ false);
+            curBlock_->add(mirHigh);
+            return args->regArgs_.append(MAsmJSCall::Arg(AnyRegister(arg.gpr64().low), mirLow)) &&
+                   args->regArgs_.append(MAsmJSCall::Arg(AnyRegister(arg.gpr64().high), mirHigh));
+          }
+#endif
+          case ABIArg::GPR:
+          case ABIArg::FPU:
             return args->regArgs_.append(MAsmJSCall::Arg(arg.reg(), argDef));
-
-        auto* mir = MAsmJSPassStackArg::New(alloc(), arg.offsetFromArgBase(), argDef);
-        curBlock_->add(mir);
-        return args->stackArgs_.append(mir);
+          case ABIArg::Stack: {
+            auto* mir = MAsmJSPassStackArg::New(alloc(), arg.offsetFromArgBase(), argDef);
+            curBlock_->add(mir);
+            return args->stackArgs_.append(mir);
+          }
+          default:
+            MOZ_CRASH("Unknown ABIArg kind.");
+        }
     }
 
     // Add the hidden TLS pointer argument to CallArgs, and assume that it will
     // be preserved by the call.
     bool passTlsPointer(CallArgs* args)
     {
         if (inDeadCode())
             return true;
--- a/js/src/asmjs/WasmJS.cpp
+++ b/js/src/asmjs/WasmJS.cpp
@@ -57,16 +57,26 @@ CheckCompilerSupport(JSContext* cx)
 #endif
         JS_ReportError(cx, "WebAssembly is not supported on the current device.");
         return false;
     }
 
     return true;
 }
 
+bool
+wasm::IsI64Implemented()
+{
+#if defined(JS_CPU_X64) || defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_ARM)
+    return true;
+#else
+    return false;
+#endif
+}
+
 // ============================================================================
 // (Temporary) Wasm class and static methods
 
 static bool
 Throw(JSContext* cx, const char* str)
 {
     JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_FAIL, str);
     return false;
--- a/js/src/asmjs/WasmJS.h
+++ b/js/src/asmjs/WasmJS.h
@@ -40,16 +40,20 @@ typedef UniquePtr<Instance> UniqueInstan
 
 // Return whether WebAssembly can be compiled on this platform.
 // This must be checked and must be true to call any of the top-level wasm
 // eval/compile methods.
 
 bool
 HasCompilerSupport(ExclusiveContext* cx);
 
+// Return whether WebAssembly has int64 support on this platform.
+bool
+IsI64Implemented();
+
 // Compiles the given binary wasm module given the ArrayBufferObject
 // and links the module's imports with the given import object.
 
 MOZ_MUST_USE bool
 Eval(JSContext* cx, Handle<TypedArrayObject*> code, HandleObject importObj,
      MutableHandle<WasmInstanceObject*> instanceObj);
 
 // The field name of the export object on the instance object.
--- a/js/src/asmjs/WasmStubs.cpp
+++ b/js/src/asmjs/WasmStubs.cpp
@@ -146,17 +146,16 @@ wasm::GenerateEntry(MacroAssembler& masm
     MOZ_ASSERT(WasmTlsReg != ABINonArgReg1, "TLS pointer can't be scratch reg");
 
     // Put the 'argv' argument into a non-argument/return register so that we
     // can use 'argv' while we fill in the arguments for the asm.js callee.
     // Also, save 'argv' on the stack so that we can recover it after the call.
     // Use a second non-argument/return register as temporary scratch.
     Register argv = ABINonArgReturnReg0;
     Register scratch = ABINonArgReturnReg1;
-    Register64 scratch64(scratch);
 
 #if defined(JS_CODEGEN_X86)
     masm.loadPtr(Address(masm.getStackPointer(), EntryFrameSize + masm.framePushed()), argv);
 #else
     masm.movePtr(IntArgReg0, argv);
 #endif
     masm.Push(argv);
 
@@ -187,17 +186,20 @@ wasm::GenerateEntry(MacroAssembler& masm
           case ABIArg::GPR:
             if (type == MIRType::Int32)
                 masm.load32(src, iter->gpr());
             else if (type == MIRType::Int64)
                 masm.load64(src, iter->gpr64());
             break;
 #ifdef JS_CODEGEN_REGISTER_PAIR
           case ABIArg::GPR_PAIR:
-            MOZ_CRASH("wasm uses hardfp for function calls.");
+            if (type == MIRType::Int64)
+                masm.load64(src, iter->gpr64());
+            else
+                MOZ_CRASH("wasm uses hardfp for function calls.");
             break;
 #endif
           case ABIArg::FPU: {
             static_assert(sizeof(ExportArg) >= jit::Simd128DataSize,
                           "ExportArg must be big enough to store SIMD values");
             switch (type) {
               case MIRType::Int8x16:
               case MIRType::Int16x8:
@@ -223,20 +225,30 @@ wasm::GenerateEntry(MacroAssembler& masm
             break;
           }
           case ABIArg::Stack:
             switch (type) {
               case MIRType::Int32:
                 masm.load32(src, scratch);
                 masm.storePtr(scratch, Address(masm.getStackPointer(), iter->offsetFromArgBase()));
                 break;
-              case MIRType::Int64:
+              case MIRType::Int64: {
+                Register sp = masm.getStackPointer();
+#if JS_BITS_PER_WORD == 32
+                masm.load32(Address(src.base, src.offset + INT64LOW_OFFSET), scratch);
+                masm.store32(scratch, Address(sp, iter->offsetFromArgBase() + INT64LOW_OFFSET));
+                masm.load32(Address(src.base, src.offset + INT64HIGH_OFFSET), scratch);
+                masm.store32(scratch, Address(sp, iter->offsetFromArgBase() + INT64HIGH_OFFSET));
+#else
+                Register64 scratch64(scratch);
                 masm.load64(src, scratch64);
-                masm.store64(scratch64, Address(masm.getStackPointer(), iter->offsetFromArgBase()));
+                masm.store64(scratch64, Address(sp, iter->offsetFromArgBase()));
+#endif
                 break;
+              }
               case MIRType::Double:
                 masm.loadDouble(src, ScratchDoubleReg);
                 masm.storeDouble(ScratchDoubleReg,
                                  Address(masm.getStackPointer(), iter->offsetFromArgBase()));
                 break;
               case MIRType::Float32:
                 masm.loadFloat32(src, ScratchFloat32Reg);
                 masm.storeFloat32(ScratchFloat32Reg,
@@ -323,17 +335,16 @@ wasm::GenerateEntry(MacroAssembler& masm
 }
 
 typedef bool ToValue;
 
 static void
 FillArgumentArray(MacroAssembler& masm, const ValTypeVector& args, unsigned argOffset,
                   unsigned offsetToCallerStackArgs, Register scratch, ToValue toValue)
 {
-    Register64 scratch64(scratch);
     for (ABIArgValTypeIter i(args); !i.done(); i++) {
         Address dstAddr(masm.getStackPointer(), argOffset + i.index() * sizeof(Value));
 
         MIRType type = i.mirType();
         MOZ_ASSERT_IF(type == MIRType::Int64, JitOptions.wasmTestMode);
 
         switch (i->kind()) {
           case ABIArg::GPR:
@@ -349,17 +360,20 @@ FillArgumentArray(MacroAssembler& masm, 
                 else
                     masm.store64(i->gpr64(), dstAddr);
             } else {
                 MOZ_CRASH("unexpected input type?");
             }
             break;
 #ifdef JS_CODEGEN_REGISTER_PAIR
           case ABIArg::GPR_PAIR:
-            MOZ_CRASH("AsmJS uses hardfp for function calls.");
+            if (type == MIRType::Int64)
+                masm.store64(i->gpr64(), dstAddr);
+            else
+                MOZ_CRASH("AsmJS uses hardfp for function calls.");
             break;
 #endif
           case ABIArg::FPU: {
             MOZ_ASSERT(IsFloatingPointType(type));
             FloatRegister srcReg = i->fpu();
             if (type == MIRType::Double) {
                 if (toValue) {
                     // Preserve the NaN pattern in the input.
@@ -393,18 +407,26 @@ FillArgumentArray(MacroAssembler& masm, 
                 else
                     masm.store32(scratch, dstAddr);
             } else if (type == MIRType::Int64) {
                 // We can't box int64 into Values (yet).
                 if (toValue) {
                     masm.breakpoint();
                 } else {
                     Address src(masm.getStackPointer(), offsetToCallerStackArgs + i->offsetFromArgBase());
+#if JS_BITS_PER_WORD == 32
+                    masm.load32(Address(src.base, src.offset + INT64LOW_OFFSET), scratch);
+                    masm.store32(scratch, Address(dstAddr.base, dstAddr.offset + INT64LOW_OFFSET));
+                    masm.load32(Address(src.base, src.offset + INT64HIGH_OFFSET), scratch);
+                    masm.store32(scratch, Address(dstAddr.base, dstAddr.offset + INT64HIGH_OFFSET));
+#else
+                    Register64 scratch64(scratch);
                     masm.load64(src, scratch64);
                     masm.store64(scratch64, dstAddr);
+#endif
                 }
             } else {
                 MOZ_ASSERT(IsFloatingPointType(type));
                 Address src(masm.getStackPointer(), offsetToCallerStackArgs + i->offsetFromArgBase());
                 if (toValue) {
                     if (type == MIRType::Float32) {
                         masm.loadFloat32(src, ScratchFloat32Reg);
                         masm.convertFloat32ToDouble(ScratchFloat32Reg, ScratchDoubleReg);
--- a/js/src/asmjs/WasmTypes.cpp
+++ b/js/src/asmjs/WasmTypes.cpp
@@ -157,16 +157,92 @@ CoerceInPlace_ToNumber(MutableHandleValu
     double dbl;
     if (!ToNumber(cx, val, &dbl))
         return false;
     val.set(DoubleValue(dbl));
 
     return true;
 }
 
+static int64_t
+DivI64(uint32_t x_hi, uint32_t x_lo, uint32_t y_hi, uint32_t y_lo)
+{
+    int64_t x = ((uint64_t)x_hi << 32) + x_lo;
+    int64_t y = ((uint64_t)y_hi << 32) + y_lo;
+    MOZ_ASSERT(x != INT64_MIN || y != -1);
+    MOZ_ASSERT(y != 0);
+    return x / y;
+}
+
+static int64_t
+UDivI64(uint32_t x_hi, uint32_t x_lo, uint32_t y_hi, uint32_t y_lo)
+{
+    uint64_t x = ((uint64_t)x_hi << 32) + x_lo;
+    uint64_t y = ((uint64_t)y_hi << 32) + y_lo;
+    MOZ_ASSERT(y != 0);
+    return x / y;
+}
+
+static int64_t
+ModI64(uint32_t x_hi, uint32_t x_lo, uint32_t y_hi, uint32_t y_lo)
+{
+    int64_t x = ((uint64_t)x_hi << 32) + x_lo;
+    int64_t y = ((uint64_t)y_hi << 32) + y_lo;
+    MOZ_ASSERT(x != INT64_MIN || y != -1);
+    MOZ_ASSERT(y != 0);
+    return x % y;
+}
+
+static int64_t
+UModI64(uint32_t x_hi, uint32_t x_lo, uint32_t y_hi, uint32_t y_lo)
+{
+    uint64_t x = ((uint64_t)x_hi << 32) + x_lo;
+    uint64_t y = ((uint64_t)y_hi << 32) + y_lo;
+    MOZ_ASSERT(y != 0);
+    return x % y;
+}
+
+static int64_t
+TruncateDoubleToInt64(double input)
+{
+    // Note: INT64_MAX is not representable in double. It is actually INT64_MAX + 1.
+    // Therefore also sending the failure value.
+    if (input >= double(INT64_MAX))
+        return 0x8000000000000000;
+    if (input < double(INT64_MIN))
+        return 0x8000000000000000;
+    return int64_t(input);
+}
+
+static uint64_t
+TruncateDoubleToUint64(double input)
+{
+    // Note: UINT64_MAX is not representable in double. It is actually UINT64_MAX + 1.
+    // Therefore also sending the failure value.
+    if (input >= double(UINT64_MAX))
+        return 0x8000000000000000;
+    if (input <= -1.0)
+        return 0x8000000000000000;
+    return uint64_t(input);
+}
+
+static double
+Int64ToFloatingPoint(int32_t x_hi, uint32_t x_lo)
+{
+    int64_t x = int64_t((uint64_t(x_hi) << 32)) + int64_t(x_lo);
+    return double(x);
+}
+
+static double
+Uint64ToFloatingPoint(int32_t x_hi, uint32_t x_lo)
+{
+    uint64_t x = (uint64_t(x_hi) << 32) + uint64_t(x_lo);
+    return double(x);
+}
+
 template <class F>
 static inline void*
 FuncCast(F* pf, ABIFunctionType type)
 {
     void *pv = JS_FUNC_TO_DATA_PTR(void*, pf);
 #ifdef JS_SIMULATOR
     pv = Simulator::RedirectNativeFunction(pv, type);
 #endif
@@ -196,16 +272,32 @@ wasm::AddressOf(SymbolicAddress imm, Exc
       case SymbolicAddress::CallImport_F64:
         return FuncCast(Instance::callImport_f64, Args_General3);
       case SymbolicAddress::CoerceInPlace_ToInt32:
         return FuncCast(CoerceInPlace_ToInt32, Args_General1);
       case SymbolicAddress::CoerceInPlace_ToNumber:
         return FuncCast(CoerceInPlace_ToNumber, Args_General1);
       case SymbolicAddress::ToInt32:
         return FuncCast<int32_t (double)>(JS::ToInt32, Args_Int_Double);
+      case SymbolicAddress::DivI64:
+        return FuncCast(DivI64, Args_General4);
+      case SymbolicAddress::UDivI64:
+        return FuncCast(UDivI64, Args_General4);
+      case SymbolicAddress::ModI64:
+        return FuncCast(ModI64, Args_General4);
+      case SymbolicAddress::UModI64:
+        return FuncCast(UModI64, Args_General4);
+      case SymbolicAddress::TruncateDoubleToUint64:
+        return FuncCast(TruncateDoubleToUint64, Args_Int64_Double);
+      case SymbolicAddress::TruncateDoubleToInt64:
+        return FuncCast(TruncateDoubleToInt64, Args_Int64_Double);
+      case SymbolicAddress::Uint64ToFloatingPoint:
+        return FuncCast(Uint64ToFloatingPoint, Args_Double_IntInt);
+      case SymbolicAddress::Int64ToFloatingPoint:
+        return FuncCast(Int64ToFloatingPoint, Args_Double_IntInt);
 #if defined(JS_CODEGEN_ARM)
       case SymbolicAddress::aeabi_idivmod:
         return FuncCast(__aeabi_idivmod, Args_General2);
       case SymbolicAddress::aeabi_uidivmod:
         return FuncCast(__aeabi_uidivmod, Args_General2);
       case SymbolicAddress::AtomicCmpXchg:
         return FuncCast<int32_t (int32_t, int32_t, int32_t, int32_t)>(js::atomics_cmpxchg_asm_callout, Args_General4);
       case SymbolicAddress::AtomicXchg:
@@ -219,22 +311,17 @@ wasm::AddressOf(SymbolicAddress imm, Exc
       case SymbolicAddress::AtomicFetchOr:
         return FuncCast<int32_t (int32_t, int32_t, int32_t)>(js::atomics_or_asm_callout, Args_General3);
       case SymbolicAddress::AtomicFetchXor:
         return FuncCast<int32_t (int32_t, int32_t, int32_t)>(js::atomics_xor_asm_callout, Args_General3);
 #endif
       case SymbolicAddress::ModD:
         return FuncCast(NumberMod, Args_Double_DoubleDouble);
       case SymbolicAddress::SinD:
-#ifdef _WIN64
-        // Workaround a VS 2013 sin issue, see math_sin_uncached.
-        return FuncCast<double (double)>(js::math_sin_uncached, Args_Double_Double);
-#else
         return FuncCast<double (double)>(sin, Args_Double_Double);
-#endif
       case SymbolicAddress::CosD:
         return FuncCast<double (double)>(cos, Args_Double_Double);
       case SymbolicAddress::TanD:
         return FuncCast<double (double)>(tan, Args_Double_Double);
       case SymbolicAddress::ASinD:
         return FuncCast<double (double)>(fdlibm::asin, Args_Double_Double);
       case SymbolicAddress::ACosD:
         return FuncCast<double (double)>(fdlibm::acos, Args_Double_Double);
--- a/js/src/asmjs/WasmTypes.h
+++ b/js/src/asmjs/WasmTypes.h
@@ -892,16 +892,24 @@ enum class SymbolicAddress
     HandleExecutionInterrupt,
     HandleTrap,
     CallImport_Void,
     CallImport_I32,
     CallImport_I64,
     CallImport_F64,
     CoerceInPlace_ToInt32,
     CoerceInPlace_ToNumber,
+    DivI64,
+    UDivI64,
+    ModI64,
+    UModI64,
+    TruncateDoubleToInt64,
+    TruncateDoubleToUint64,
+    Uint64ToFloatingPoint,
+    Int64ToFloatingPoint,
     Limit
 };
 
 void*
 AddressOf(SymbolicAddress imm, ExclusiveContext* cx);
 
 // A wasm::Trap is a reason for why we reached a trap in executed code. Each
 // different trap is mapped to a different error message.
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -531,16 +531,24 @@ SuppressSignalHandlers(JSContext* cx, un
 
     wasm::SuppressSignalHandlersForTesting(ToBoolean(args[0]));
 
     args.rval().setUndefined();
     return true;
 }
 
 static bool
+WasmInt64IsSupported(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    args.rval().setBoolean(wasm::IsI64Implemented());
+    return true;
+}
+
+static bool
 WasmTextToBinary(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     RootedObject callee(cx, &args.callee());
 
     if (!args.requireAtLeast(cx, "wasmTextToBinary", 1))
         return false;
 
@@ -3848,16 +3856,20 @@ gc::ZealModeHelpText),
 "wasmUsesSignalForOOB()",
 "  Return whether wasm and asm.js use a signal handler for detecting out-of-bounds."),
 
     JS_FN_HELP("suppressSignalHandlers", SuppressSignalHandlers, 1, 0,
 "suppressSignalHandlers(suppress)",
 "  This function allows artificially suppressing signal handler support, even if the underlying "
 "  platform supports it."),
 
+    JS_FN_HELP("wasmInt64IsSupported", WasmInt64IsSupported, 0, 0,
+"wasmInt64IsSupported()",
+"  Returns a boolean indicating whether WebAssembly has 64bit integer support on the current device."),
+
     JS_FN_HELP("wasmTextToBinary", WasmTextToBinary, 1, 0,
 "wasmTextToBinary(str)",
 "  Translates the given text wasm module into its binary encoding."),
 
     JS_FN_HELP("wasmBinaryToText", WasmBinaryToText, 1, 0,
 "wasmBinaryToText(bin)",
 "  Translates binary encoding to text format"),
 
--- a/js/src/builtin/WeakMapObject.cpp
+++ b/js/src/builtin/WeakMapObject.cpp
@@ -126,18 +126,17 @@ TryPreserveReflector(JSContext* cx, Hand
 }
 
 static MOZ_ALWAYS_INLINE bool
 SetWeakMapEntryInternal(JSContext* cx, Handle<WeakMapObject*> mapObj,
                         HandleObject key, HandleValue value)
 {
     ObjectValueMap* map = mapObj->getMap();
     if (!map) {
-        AutoInitGCManagedObject<ObjectValueMap> newMap(
-            cx->make_unique<ObjectValueMap>(cx, mapObj.get()));
+        auto newMap = cx->make_unique<ObjectValueMap>(cx, mapObj.get());
         if (!newMap)
             return false;
         if (!newMap->init()) {
             JS_ReportOutOfMemory(cx);
             return false;
         }
         map = newMap.release();
         mapObj->setPrivate(map);
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -7884,16 +7884,21 @@ ReadStringCommon(JSContext* cx, InflateU
     size_t length = strnlen(bytes, maxLength);
 
     // Determine the length.
     char16_t* dst = inflateUTF8(cx, JS::UTF8Chars(bytes, length), &length).get();
     if (!dst)
       return false;
 
     result = JS_NewUCString(cx, dst, length);
+    if (!result) {
+      js_free(dst);
+      return false;
+    }
+
     break;
   }
   case TYPE_int16_t:
   case TYPE_uint16_t:
   case TYPE_short:
   case TYPE_unsigned_short:
   case TYPE_char16_t: {
     char16_t* chars = static_cast<char16_t*>(data);
--- a/js/src/frontend/TokenStream.h
+++ b/js/src/frontend/TokenStream.h
@@ -170,34 +170,16 @@ struct Token
         RegExpFlag      reflags;        // regexp flags; use tokenbuf to access
                                         //   regexp chars
     } u;
 #ifdef DEBUG
     Modifier modifier;                  // Modifier used to get this token
     ModifierException modifierException; // Exception for this modifier
 #endif
 
-    // This constructor is necessary only for MSVC 2013 and how it compiles the
-    // initialization of TokenStream::tokens.  That field is initialized as
-    // tokens() in the constructor init-list.  This *should* zero the entire
-    // array, then (because Token has a non-trivial constructor, because
-    // TokenPos has a user-provided constructor) call the implicit Token
-    // constructor on each element, which would call the TokenPos constructor
-    // for Token::pos and do nothing.  (All of which is equivalent to just
-    // zeroing TokenStream::tokens.)  But MSVC 2013 (2010/2012 don't have this
-    // bug) doesn't zero out each element, so we need this extra constructor to
-    // make it do the right thing.  (Token is used primarily by reference or
-    // pointer, and it's only initialized a very few places, so having a
-    // user-defined constructor won't hurt perf.)  See also bug 920318.
-    Token()
-      : pos(0, 0)
-    {
-        MOZ_MAKE_MEM_UNDEFINED(&type, sizeof(type));
-    }
-
     // Mutators
 
     void setName(PropertyName* name) {
         MOZ_ASSERT(type == TOK_NAME);
         u.name = name;
     }
 
     void setAtom(JSAtom* atom) {
--- a/js/src/gc/Barrier.cpp
+++ b/js/src/gc/Barrier.cpp
@@ -60,20 +60,20 @@ CurrentThreadIsIonCompilingSafeForMinorG
 
 bool
 CurrentThreadIsGCSweeping()
 {
     return TlsPerThreadData.get()->gcSweeping;
 }
 
 bool
-CurrentThreadIsHandlingInitFailure()
+CurrentThreadCanSkipPostBarrier(bool inNursery)
 {
-    JSRuntime* rt = TlsPerThreadData.get()->runtimeIfOnOwnerThread();
-    return rt && rt->handlingInitFailure;
+    bool onMainThread = TlsPerThreadData.get()->runtimeIfOnOwnerThread() != nullptr;
+    return !onMainThread && !inNursery;
 }
 
 #endif // DEBUG
 
 template <typename S>
 template <typename T>
 void
 ReadBarrierFunctor<S>::operator()(T* t)
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -238,17 +238,17 @@ CurrentThreadIsIonCompiling();
 
 bool
 CurrentThreadIsIonCompilingSafeForMinorGC();
 
 bool
 CurrentThreadIsGCSweeping();
 
 bool
-CurrentThreadIsHandlingInitFailure();
+CurrentThreadCanSkipPostBarrier(bool inNursery);
 #endif
 
 namespace gc {
 
 // Marking.h depends on these barrier definitions, so we need a separate
 // entry point for marking to implement the pre-barrier.
 void MarkValueForBarrier(JSTracer* trc, Value* v, const char* name);
 void MarkIdForBarrier(JSTracer* trc, jsid* idp, const char* name);
@@ -265,16 +265,18 @@ struct InternalBarrierMethods<T*>
 
     static bool isMarkableTaggedPointer(T* v) { return !IsNullTaggedPointer(v); }
 
     static void preBarrier(T* v) { T::writeBarrierPre(v); }
 
     static void postBarrier(T** vp, T* prev, T* next) { T::writeBarrierPost(vp, prev, next); }
 
     static void readBarrier(T* v) { T::readBarrier(v); }
+
+    static bool isInsideNursery(T* v) { return IsInsideNursery(v); }
 };
 
 template <typename S> struct PreBarrierFunctor : public VoidDefaultAdaptor<S> {
     template <typename T> void operator()(T* t);
 };
 
 template <typename S> struct ReadBarrierFunctor : public VoidDefaultAdaptor<S> {
     template <typename T> void operator()(T* t);
@@ -309,26 +311,32 @@ struct InternalBarrierMethods<Value>
         // Remove the prev entry if the new value does not need it.
         if (prev.isObject() && (sb = reinterpret_cast<gc::Cell*>(&prev.toObject())->storeBuffer()))
             sb->unputValue(vp);
     }
 
     static void readBarrier(const Value& v) {
         DispatchTyped(ReadBarrierFunctor<Value>(), v);
     }
+
+    static bool isInsideNursery(const Value& v) {
+        return v.isMarkable() && IsInsideNursery(v.toGCThing());
+    }
 };
 
 template <>
 struct InternalBarrierMethods<jsid>
 {
     static bool isMarkable(jsid id) { return JSID_IS_STRING(id) || JSID_IS_SYMBOL(id); }
     static bool isMarkableTaggedPointer(jsid id) { return isMarkable(id); }
 
     static void preBarrier(jsid id) { DispatchTyped(PreBarrierFunctor<jsid>(), id); }
     static void postBarrier(jsid* idp, jsid prev, jsid next) {}
+
+    static bool isInsideNursery(jsid id) { return false; }
 };
 
 // Barrier classes can use Mixins to add methods to a set of barrier
 // instantiations, to make the barriered thing look and feel more like the
 // thing itself.
 template <typename T>
 class BarrieredBaseMixins {};
 
@@ -438,18 +446,21 @@ class GCPtr : public WriteBarrieredBase<
         this->post(JS::GCPolicy<T>::initial(), v);
     }
     explicit GCPtr(const GCPtr<T>& v) : WriteBarrieredBase<T>(v) {
         this->post(JS::GCPolicy<T>::initial(), v);
     }
 #ifdef DEBUG
     ~GCPtr() {
         // No prebarrier necessary as this only happens when we are sweeping or
-        // before the containing object becomes part of the GC graph.
-        MOZ_ASSERT(CurrentThreadIsGCSweeping() || CurrentThreadIsHandlingInitFailure());
+        // after we have just collected the nursery.
+        bool inNursery = InternalBarrierMethods<T>::isInsideNursery(this->value);
+        MOZ_ASSERT(CurrentThreadIsGCSweeping() ||
+                   CurrentThreadCanSkipPostBarrier(inNursery));
+        Poison(this, JS_FREED_HEAP_PTR_PATTERN, sizeof(*this));
     }
 #endif
 
     void init(T v) {
         this->value = v;
         this->post(JS::GCPolicy<T>::initial(), v);
     }
 
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -872,16 +872,21 @@ class GCRuntime
     bool isVerifyPreBarriersEnabled() const { return false; }
 #endif
 
     // Free certain LifoAlloc blocks when it is safe to do so.
     void freeUnusedLifoBlocksAfterSweeping(LifoAlloc* lifo);
     void freeAllLifoBlocksAfterSweeping(LifoAlloc* lifo);
     void freeAllLifoBlocksAfterMinorGC(LifoAlloc* lifo);
 
+    // Queue a thunk to run after the next minor GC.
+    void callAfterMinorGC(void (*thunk)(void* data), void* data) {
+        nursery.queueSweepAction(thunk, data);
+    }
+
     // Public here for ReleaseArenaLists and FinalizeTypedArenas.
     void releaseArena(Arena* arena, const AutoLockGC& lock);
 
     void releaseHeldRelocatedArenas();
     void releaseHeldRelocatedArenasWithoutUnlocking(const AutoLockGC& lock);
 
     // Allocator
     template <AllowGC allowGC>
@@ -1446,11 +1451,12 @@ GCRuntime::needZealousGC() {
 #else
 inline bool GCRuntime::hasZealMode(ZealMode mode) { return false; }
 inline void GCRuntime::clearZealMode(ZealMode mode) { }
 inline bool GCRuntime::upcomingZealousGC() { return false; }
 inline bool GCRuntime::needZealousGC() { return false; }
 #endif
 
 } /* namespace gc */
+
 } /* namespace js */
 
 #endif
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -53,31 +53,48 @@ struct js::Nursery::FreeMallocedBuffersT
 
   private:
     FreeOp* fop_;
     MallocedBuffersSet buffers_;
 
     virtual void run() override;
 };
 
+struct js::Nursery::SweepAction
+{
+    SweepAction(SweepThunk thunk, void* data, SweepAction* next)
+      : thunk(thunk), data(data), next(next)
+    {}
+
+    SweepThunk thunk;
+    void* data;
+    SweepAction* next;
+
+#if JS_BITS_PER_WORD == 32
+  protected:
+    uint32_t padding;
+#endif
+};
+
 js::Nursery::Nursery(JSRuntime* rt)
   : runtime_(rt)
   , position_(0)
   , currentStart_(0)
   , currentEnd_(0)
   , heapStart_(0)
   , heapEnd_(0)
   , currentChunk_(0)
   , numActiveChunks_(0)
   , numNurseryChunks_(0)
   , previousPromotionRate_(0)
   , profileThreshold_(0)
   , enableProfiling_(false)
   , minorGcCount_(0)
   , freeMallocedBuffersTask(nullptr)
+  , sweepActions_(nullptr)
 #ifdef JS_GC_ZEAL
   , lastCanary_(nullptr)
 #endif
 {}
 
 bool
 js::Nursery::init(uint32_t maxNurseryBytes)
 {
@@ -231,16 +248,18 @@ js::Nursery::allocateObject(JSContext* c
 }
 
 void*
 js::Nursery::allocate(size_t size)
 {
     MOZ_ASSERT(isEnabled());
     MOZ_ASSERT(!runtime()->isHeapBusy());
     MOZ_ASSERT(position() >= currentStart_);
+    MOZ_ASSERT(position() % gc::CellSize == 0);
+    MOZ_ASSERT(size % gc::CellSize == 0);
 
 #ifdef JS_GC_ZEAL
     static const size_t CanarySize = (sizeof(Nursery::Canary) + CellSize - 1) & ~CellMask;
     if (runtime()->gc.hasZealMode(ZealMode::CheckNursery))
         size += CanarySize;
 #endif
 
     if (currentEnd() < position() + size) {
@@ -718,16 +737,18 @@ js::Nursery::sweep()
         JSObject* obj = static_cast<JSObject*>(e.front());
         if (!IsForwarded(obj))
             obj->zone()->removeUniqueId(obj);
         else
             MOZ_ASSERT(Forwarded(obj)->zone()->hasUniqueId(Forwarded(obj)));
     }
     cellsWithUid_.clear();
 
+    runSweepActions();
+
 #ifdef JS_GC_ZEAL
     /* Poison the nursery contents so touching a freed object will crash. */
     JS_POISON((void*)start(), JS_SWEPT_NURSERY_PATTERN, nurserySize());
     for (int i = 0; i < numNurseryChunks_; ++i)
         initChunk(i);
 
     if (runtime()->hasZealMode(ZealMode::GenerationalGC)) {
         MOZ_ASSERT(numActiveChunks_ == numNurseryChunks_);
@@ -788,8 +809,42 @@ js::Nursery::updateNumActiveChunks(int n
         uintptr_t decommitSize = chunk(priorChunks - 1).start() + ChunkSize - decommitStart;
         MOZ_ASSERT(decommitSize != 0);
         MOZ_ASSERT(decommitStart == AlignBytes(decommitStart, Alignment));
         MOZ_ASSERT(decommitSize == AlignBytes(decommitSize, Alignment));
         MarkPagesUnused((void*)decommitStart, decommitSize);
     }
 #endif // !defined(JS_GC_ZEAL)
 }
+
+void
+js::Nursery::queueSweepAction(SweepThunk thunk, void* data)
+{
+    static_assert(sizeof(SweepAction) % CellSize == 0,
+                  "SweepAction size must be a multiple of cell size");
+    MOZ_ASSERT(!runtime()->mainThread.suppressGC);
+
+    SweepAction* action = nullptr;
+    if (isEnabled() && !js::oom::ShouldFailWithOOM())
+        action = reinterpret_cast<SweepAction*>(allocate(sizeof(SweepAction)));
+
+    if (!action) {
+        runtime()->gc.evictNursery();
+        AutoSetThreadIsSweeping threadIsSweeping;
+        thunk(data);
+        return;
+    }
+
+    new (action) SweepAction(thunk, data, sweepActions_);
+    sweepActions_ = action;
+}
+
+void
+js::Nursery::runSweepActions()
+{
+    // The hazard analysis doesn't know whether the thunks can GC.
+    JS::AutoSuppressGCAnalysis nogc;
+
+    AutoSetThreadIsSweeping threadIsSweeping;
+    for (auto action = sweepActions_; action; action = action->next)
+        action->thunk(action->data);
+    sweepActions_ = nullptr;
+}
--- a/js/src/gc/Nursery.h
+++ b/js/src/gc/Nursery.h
@@ -203,16 +203,19 @@ class Nursery
     MOZ_MUST_USE bool addedUniqueIdToCell(gc::Cell* cell) {
         if (!IsInsideNursery(cell) || !isEnabled())
             return true;
         MOZ_ASSERT(cellsWithUid_.initialized());
         MOZ_ASSERT(!cellsWithUid_.has(cell));
         return cellsWithUid_.put(cell);
     }
 
+    using SweepThunk = void (*)(void *data);
+    void queueSweepAction(SweepThunk thunk, void* data);
+
     size_t sizeOfHeapCommitted() const {
         return numActiveChunks_ * gc::ChunkSize;
     }
     size_t sizeOfHeapDecommitted() const {
         return (numNurseryChunks_ - numActiveChunks_) * gc::ChunkSize;
     }
     size_t sizeOfMallocedBuffers(mozilla::MallocSizeOf mallocSizeOf) const {
         size_t total = 0;
@@ -330,16 +333,20 @@ class Nursery
      *
      * Note: we store the pointers as Cell* here, resulting in an ugly cast in
      *       sweep. This is because this structure is used to help implement
      *       stable object hashing and we have to break the cycle somehow.
      */
     using CellsWithUniqueIdSet = HashSet<gc::Cell*, PointerHasher<gc::Cell*, 3>, SystemAllocPolicy>;
     CellsWithUniqueIdSet cellsWithUid_;
 
+    struct SweepAction;
+    SweepAction* sweepActions_;
+    SweepAction* reservedSweepAction_;
+
 #ifdef JS_GC_ZEAL
     struct Canary
     {
         uintptr_t magicValue;
         Canary* next;
     };
 
     Canary* lastCanary_;
@@ -424,16 +431,18 @@ class Nursery
     void freeMallocedBuffers();
 
     /*
      * Frees all non-live nursery-allocated things at the end of a minor
      * collection.
      */
     void sweep();
 
+    void runSweepActions();
+
     /* Change the allocable space provided by the nursery. */
     void growAllocableSpace();
     void shrinkAllocableSpace();
 
     /* Profile recording and printing. */
     void startProfile(ProfileKey key);
     void endProfile(ProfileKey key);
     void maybeStartProfile(ProfileKey key);
--- a/js/src/jit-test/lib/wasm.js
+++ b/js/src/jit-test/lib/wasm.js
@@ -11,17 +11,17 @@ function wasmEvalText(str, imports) {
 }
 
 function mismatchError(actual, expect) {
     var str = `type mismatch: expression has type ${actual} but expected ${expect}`;
     return RegExp(str);
 }
 
 function hasI64() {
-    return getBuildConfiguration().x64;
+    return wasmInt64IsSupported();
 }
 
 function jsify(wasmVal) {
     if (wasmVal === 'nan')
         return NaN;
     if (wasmVal === 'infinity')
         return Infinity;
     if (wasmVal === '-infinity')
--- a/js/src/jit-test/tests/wasm/basic-conversion.js
+++ b/js/src/jit-test/tests/wasm/basic-conversion.js
@@ -91,23 +91,29 @@ if (hasI64()) {
     testConversion('i64', 'extend_u', 'i32', 0x80000000, "0x0000000080000000");
 
     testConversion('f32', 'convert_s', 'i64', 1, 1.0);
     testConversion('f32', 'convert_s', 'i64', -1, -1.0);
     testConversion('f32', 'convert_s', 'i64', 0, 0.0);
     testConversion('f32', 'convert_s', 'i64', "0x7fffffffffffffff", 9223372036854775807.0);
     testConversion('f32', 'convert_s', 'i64', "0x8000000000000000", -9223372036854775808.0);
     testConversion('f32', 'convert_s', 'i64', "0x11db9e76a2483", 314159275180032.0);
+    testConversion('f32', 'convert_s', 'i64', "0x7fffffff", 2147483648.0); // closesth approx.
+    testConversion('f32', 'convert_s', 'i64', "0x80000000", 2147483648.0);
+    testConversion('f32', 'convert_s', 'i64', "0x80000001", 2147483648.0); // closesth approx.
 
     testConversion('f64', 'convert_s', 'i64', 1, 1.0);
     testConversion('f64', 'convert_s', 'i64', -1, -1.0);
     testConversion('f64', 'convert_s', 'i64', 0, 0.0);
     testConversion('f64', 'convert_s', 'i64', "0x7fffffffffffffff", 9223372036854775807.0);
     testConversion('f64', 'convert_s', 'i64', "0x8000000000000000", -9223372036854775808.0);
     testConversion('f64', 'convert_s', 'i64', "0x10969d374b968e", 4669201609102990);
+    testConversion('f64', 'convert_s', 'i64', "0x7fffffff", 2147483647.0);
+    testConversion('f64', 'convert_s', 'i64', "0x80000000", 2147483648.0);
+    testConversion('f64', 'convert_s', 'i64', "0x80000001", 2147483649.0);
 
     testConversion('f32', 'convert_u', 'i64', 1, 1.0);
     testConversion('f32', 'convert_u', 'i64', 0, 0.0);
     testConversion('f32', 'convert_u', 'i64', "0x7fffffffffffffff", 9223372036854775807.0);
     testConversion('f32', 'convert_u', 'i64', "0x8000000000000000", 9223372036854775808.0);
     testConversion('f32', 'convert_u', 'i64', -1, 18446744073709551616.0);
     testConversion('f32', 'convert_u', 'i64', "0xffff0000ffff0000", 18446462598732840000.0);
 
--- a/js/src/jit-test/tests/wasm/basic-integer.js
+++ b/js/src/jit-test/tests/wasm/basic-integer.js
@@ -123,16 +123,18 @@ testTrap32('div_s', 0x80000000 | 0, -1, 
 testTrap32('div_u', 42, 0, /integer divide by zero/);
 testTrap32('rem_s', 42, 0, /integer divide by zero/);
 testTrap32('rem_u', 42, 0, /integer divide by zero/);
 
 testBinary32('rotl', 40, 2, 160);
 testBinary32('rotl', 40, 34, 160);
 testBinary32('rotr', 40, 2, 10);
 testBinary32('rotr', 40, 34, 10);
+testBinary32('rotr', 40, 0, 40);
+testBinary32('rotl', 40, 0, 40);
 
 testComparison32('eq', 40, 40, 1);
 testComparison32('ne', 40, 40, 0);
 testComparison32('lt_s', 40, 40, 0);
 testComparison32('lt_u', 40, 40, 0);
 testComparison32('le_s', 40, 40, 1);
 testComparison32('le_u', 40, 40, 1);
 testComparison32('gt_s', 40, 40, 0);
@@ -153,16 +155,23 @@ if (hasI64()) {
     testBinary64('add', "0x1234567887654321", -1, "0x1234567887654320");
     testBinary64('add', "0xffffffffffffffff", 1, 0);
     testBinary64('sub', 40, 2, 38);
     testBinary64('sub', "0x1234567887654321", "0x123456789", "0x12345677641fdb98");
     testBinary64('sub', 3, 5, -2);
     testBinary64('mul', 40, 2, 80);
     testBinary64('mul', -1, 2, -2);
     testBinary64('mul', 0x123456, "0x9876543210", "0xad77d2c5f941160");
+    testBinary64('mul', 2, -1, -2);
+    testBinary64('mul', "0x80000000", -1, -2147483648);
+    testBinary64('mul', "0x7fffffff", -1, -2147483647);
+    testBinary64('mul', "0x7fffffffffffffff", -1, "0x8000000000000001");
+    testBinary64('mul', 2, 2, 4);
+    testBinary64('mul', "0x80000000", 2, "0x100000000");
+    testBinary64('mul', "0x7fffffff", 2, "0xfffffffe");
     testBinary64('div_s', -40, 2, -20);
     testBinary64('div_s', "0x1234567887654321", 2, "0x91a2b3c43b2a190");
     testBinary64('div_s', "0x1234567887654321", "0x1000000000", "0x1234567");
     testBinary64('div_u', -40, 2, "0x7fffffffffffffec");
     testBinary64('div_u', "0x1234567887654321", 9, "0x205d0b80f0b4059");
     testBinary64('rem_s', 40, -3, 1);
     testBinary64('rem_s', "0x1234567887654321", "0x1000000000", "0x887654321");
     testBinary64('rem_s', "0x7fffffffffffffff", -1, 0);
@@ -190,16 +199,24 @@ if (hasI64()) {
     testBinary64('shr_u', -40, 2, "0x3ffffffffffffff6");
     testBinary64('shl', 0xff00ff, 28, "0xff00ff0000000");
     testBinary64('shl', 1, 63, "0x8000000000000000");
     testBinary64('shl', 1, 64, 1);
     testBinary64('shr_s', "0xff00ff0000000", 28, 0xff00ff);
     testBinary64('shr_u', "0x8ffff00ff0000000", 56, 0x8f);
     testBinary64('rotl', 40, 2, 160);
     testBinary64('rotr', 40, 2, 10);
+    testBinary64('rotr', "0x1234567812345678", 4, "0x8123456781234567");
+    testBinary64('rotl', "0x1234567812345678", 4, "0x2345678123456781");
+    testBinary64('rotl', "0x1234567812345678", 60, "0x8123456781234567");
+    testBinary64('rotr', "0x1234567812345678", 60, "0x2345678123456781");
+    testBinary64('rotr', 40, 0, 40);
+    testBinary64('rotl', 40, 0, 40);
+    testBinary64('and', 42, 0, 0);
+    testBinary64('and', "0x0000000012345678", "0xffff0000ffff0000", "0x0000000012340000");
 
     testComparison64('eq', 40, 40, 1);
     testComparison64('ne', 40, 40, 0);
     testComparison64('lt_s', 40, 40, 0);
     testComparison64('lt_u', 40, 40, 0);
     testComparison64('le_s', 40, 40, 1);
     testComparison64('le_u', 40, 40, 1);
     testComparison64('gt_s', 40, 40, 0);
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -3180,17 +3180,17 @@ StoreToTypedArray(JSContext* cx, MacroAs
         masm.jump(&done);
 
         // If the value is a double, truncate and jump back.
         // Else, jump to failure.
         masm.bind(&notInt32);
         if (cx->runtime()->jitSupportsFloatingPoint) {
             masm.branchTestDouble(Assembler::NotEqual, value, failure);
             masm.unboxDouble(value, FloatReg0);
-            masm.branchTruncateDouble(FloatReg0, scratch, failureModifiedScratch);
+            masm.branchTruncateDoubleMaybeModUint32(FloatReg0, scratch, failureModifiedScratch);
             masm.jump(&isInt32);
         } else {
             masm.jump(failure);
         }
     }
 
     masm.bind(&done);
 }
@@ -6186,21 +6186,16 @@ ICCallStubCompiler::pushSpreadCallArgume
     regs.add(startReg);
     regs.add(endReg);
 
     // Push the callee and |this|.
     masm.pushValue(Address(BaselineFrameReg, STUB_FRAME_SIZE + (1 + isConstructing) * sizeof(Value)));
     masm.pushValue(Address(BaselineFrameReg, STUB_FRAME_SIZE + (2 + isConstructing) * sizeof(Value)));
 }
 
-// (see Bug 1149377 comment 31) MSVC 2013 PGO miss-compiles branchTestObjClass
-// calls from this function.
-#if defined(_MSC_VER) && _MSC_VER == 1800
-# pragma optimize("g", off)
-#endif
 Register
 ICCallStubCompiler::guardFunApply(MacroAssembler& masm, AllocatableGeneralRegisterSet regs,
                                   Register argcReg, bool checkNative, FunApplyThing applyThing,
                                   Label* failure)
 {
     // Ensure argc == 2
     masm.branch32(Assembler::NotEqual, argcReg, Imm32(2), failure);
 
@@ -6305,19 +6300,16 @@ ICCallStubCompiler::guardFunApply(MacroA
         masm.branchIfFunctionHasNoScript(target, failure);
         Register temp = regs.takeAny();
         masm.loadPtr(Address(target, JSFunction::offsetOfNativeOrScript()), temp);
         masm.loadBaselineOrIonRaw(temp, temp, failure);
         regs.add(temp);
     }
     return target;
 }
-#if defined(_MSC_VER) && _MSC_VER == 1800
-# pragma optimize("", on)
-#endif
 
 void
 ICCallStubCompiler::pushCallerArguments(MacroAssembler& masm, AllocatableGeneralRegisterSet regs)
 {
     // Initialize copyReg to point to start caller arguments vector.
     // Initialize argcReg to poitn to the end of it.
     Register startReg = regs.takeAny();
     Register endReg = regs.takeAny();
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -646,39 +646,16 @@ CodeGenerator::testValueTruthy(const Val
                                Label* ifTruthy, Label* ifFalsy,
                                OutOfLineTestObject* ool,
                                MDefinition* valueMIR)
 {
     testValueTruthyKernel(value, scratch1, scratch2, fr, ifTruthy, ifFalsy, ool, valueMIR);
     masm.jump(ifTruthy);
 }
 
-Label*
-CodeGenerator::getJumpLabelForBranch(MBasicBlock* block)
-{
-    // Skip past trivial blocks.
-    block = skipTrivialBlocks(block);
-
-    if (!labelForBackedgeWithImplicitCheck(block))
-        return block->lir()->label();
-
-    // We need to use a patchable jump for this backedge, but want to treat
-    // this as a normal label target to simplify codegen. Efficiency isn't so
-    // important here as these tests are extremely unlikely to be used in loop
-    // backedges, so emit inline code for the patchable jump. Heap allocating
-    // the label allows it to be used by out of line blocks.
-    Label* res = alloc().lifoAlloc()->newInfallible<Label>();
-    Label after;
-    masm.jump(&after);
-    masm.bind(res);
-    jumpToBlock(block);
-    masm.bind(&after);
-    return res;
-}
-
 void
 CodeGenerator::visitTestOAndBranch(LTestOAndBranch* lir)
 {
     MIRType inputType = lir->mir()->input()->type();
     MOZ_ASSERT(inputType == MIRType::ObjectOrNull || lir->mir()->operandMightEmulateUndefined(),
                "If the object couldn't emulate undefined, this should have been folded.");
 
     Label* truthy = getJumpLabelForBranch(lir->ifTruthy());
@@ -11210,24 +11187,37 @@ CodeGenerator::visitHasClass(LHasClass* 
 }
 
 void
 CodeGenerator::visitAsmJSParameter(LAsmJSParameter* lir)
 {
 }
 
 void
+CodeGenerator::visitAsmJSParameterI64(LAsmJSParameterI64* lir)
+{
+}
+
+void
 CodeGenerator::visitAsmJSReturn(LAsmJSReturn* lir)
 {
     // Don't emit a jump to the return label if this is the last block.
     if (current->mir() != *gen->graph().poBegin())
         masm.jump(&returnLabel_);
 }
 
 void
+CodeGenerator::visitAsmJSReturnI64(LAsmJSReturnI64* lir)
+{
+    // Don't emit a jump to the return label if this is the last block.
+    if (current->mir() != *gen->graph().poBegin())
+        masm.jump(&returnLabel_);
+}
+
+void
 CodeGenerator::visitAsmJSVoidReturn(LAsmJSVoidReturn* lir)
 {
     // Don't emit a jump to the return label if this is the last block.
     if (current->mir() != *gen->graph().poBegin())
         masm.jump(&returnLabel_);
 }
 
 void
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -356,17 +356,19 @@ class CodeGenerator final : public CodeG
     void visitIsCallable(LIsCallable* lir);
     void visitOutOfLineIsCallable(OutOfLineIsCallable* ool);
     void visitIsConstructor(LIsConstructor* lir);
     void visitOutOfLineIsConstructor(OutOfLineIsConstructor* ool);
     void visitIsObject(LIsObject* lir);
     void visitIsObjectAndBranch(LIsObjectAndBranch* lir);
     void visitHasClass(LHasClass* lir);
     void visitAsmJSParameter(LAsmJSParameter* lir);
+    void visitAsmJSParameterI64(LAsmJSParameterI64* lir);
     void visitAsmJSReturn(LAsmJSReturn* ret);
+    void visitAsmJSReturnI64(LAsmJSReturnI64* ret);
     void visitAsmJSVoidReturn(LAsmJSVoidReturn* ret);
     void visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr* ins);
     void visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc* ins);
     void visitLexicalCheck(LLexicalCheck* ins);
     void visitThrowRuntimeLexicalError(LThrowRuntimeLexicalError* ins);
     void visitGlobalNameConflictsCheck(LGlobalNameConflictsCheck* ins);
     void visitDebugger(LDebugger* ins);
     void visitNewTarget(LNewTarget* ins);
@@ -502,20 +504,16 @@ class CodeGenerator final : public CodeG
                                      Label* ifEmulatesUndefined,
                                      Label* ifDoesntEmulateUndefined,
                                      Register scratch, OutOfLineTestObject* ool);
 
     // Branch to target unless obj has an emptyObjectElements or emptyObjectElementsShared
     // elements pointer.
     void branchIfNotEmptyObjectElements(Register obj, Label* target);
 
-    // Get a label for the start of block which can be used for jumping, in
-    // place of jumpToBlock.
-    Label* getJumpLabelForBranch(MBasicBlock* block);
-
     void emitStoreElementTyped(const LAllocation* value, MIRType valueType, MIRType elementType,
                                Register elements, const LAllocation* index,
                                int32_t offsetAdjustment);
 
     // Bailout if an element about to be written to is a hole.
     void emitStoreHoleCheck(Register elements, const LAllocation* index, int32_t offsetAdjustment,
                             LSnapshot* snapshot);
 
--- a/js/src/jit/IonTypes.h
+++ b/js/src/jit/IonTypes.h
@@ -757,51 +757,58 @@ ScalarTypeToLength(Scalar::Type type)
 #define CHECK_OSIPOINT_REGISTERS 1
 
 #endif // DEBUG
 
 enum {
     ArgType_General = 0x1,
     ArgType_Double  = 0x2,
     ArgType_Float32 = 0x3,
+    ArgType_Int64 = 0x4,
 
     RetType_Shift   = 0x0,
-    ArgType_Shift   = 0x2,
-    ArgType_Mask    = 0x3
+    ArgType_Shift   = 0x3,
+    ArgType_Mask    = 0x7
 };
 
 enum ABIFunctionType
 {
     // VM functions that take 0-9 non-double arguments
     // and return a non-double value.
     Args_General0 = ArgType_General << RetType_Shift,
     Args_General1 = Args_General0 | (ArgType_General << (ArgType_Shift * 1)),
     Args_General2 = Args_General1 | (ArgType_General << (ArgType_Shift * 2)),
     Args_General3 = Args_General2 | (ArgType_General << (ArgType_Shift * 3)),
     Args_General4 = Args_General3 | (ArgType_General << (ArgType_Shift * 4)),
     Args_General5 = Args_General4 | (ArgType_General << (ArgType_Shift * 5)),
     Args_General6 = Args_General5 | (ArgType_General << (ArgType_Shift * 6)),
     Args_General7 = Args_General6 | (ArgType_General << (ArgType_Shift * 7)),
     Args_General8 = Args_General7 | (ArgType_General << (ArgType_Shift * 8)),
 
+    // int64 f(double)
+    Args_Int64_Double = (ArgType_Int64 << RetType_Shift) | (ArgType_Double << ArgType_Shift),
+
     // double f()
     Args_Double_None = ArgType_Double << RetType_Shift,
 
     // int f(double)
     Args_Int_Double = Args_General0 | (ArgType_Double << ArgType_Shift),
 
     // float f(float)
     Args_Float32_Float32 = (ArgType_Float32 << RetType_Shift) | (ArgType_Float32 << ArgType_Shift),
 
     // double f(double)
     Args_Double_Double = Args_Double_None | (ArgType_Double << ArgType_Shift),
 
     // double f(int)
     Args_Double_Int = Args_Double_None | (ArgType_General << ArgType_Shift),
 
+    // double f(int, int)
+    Args_Double_IntInt = Args_Double_Int | (ArgType_General << (ArgType_Shift * 2)),
+
     // double f(double, int)
     Args_Double_DoubleInt = Args_Double_None |
         (ArgType_General << (ArgType_Shift * 1)) |
         (ArgType_Double << (ArgType_Shift * 2)),
 
     // double f(double, double)
     Args_Double_DoubleDouble = Args_Double_Double | (ArgType_Double << (ArgType_Shift * 2)),
 
--- a/js/src/jit/LIR.cpp
+++ b/js/src/jit/LIR.cpp
@@ -84,33 +84,42 @@ LBlock::LBlock(MBasicBlock* from)
 
 bool
 LBlock::init(TempAllocator& alloc)
 {
     // Count the number of LPhis we'll need.
     size_t numLPhis = 0;
     for (MPhiIterator i(block_->phisBegin()), e(block_->phisEnd()); i != e; ++i) {
         MPhi* phi = *i;
-        numLPhis += (phi->type() == MIRType::Value) ? BOX_PIECES : 1;
+        switch (phi->type()) {
+          case MIRType::Value: numLPhis += BOX_PIECES; break;
+          case MIRType::Int64: numLPhis += INT64_PIECES; break;
+          default: numLPhis += 1; break;
+        }
     }
 
     // Allocate space for the LPhis.
     if (!phis_.init(alloc, numLPhis))
         return false;
 
     // For each MIR phi, set up LIR phis as appropriate. We'll fill in their
     // operands on each incoming edge, and set their definitions at the start of
     // their defining block.
     size_t phiIndex = 0;
     size_t numPreds = block_->numPredecessors();
     for (MPhiIterator i(block_->phisBegin()), e(block_->phisEnd()); i != e; ++i) {
         MPhi* phi = *i;
         MOZ_ASSERT(phi->numOperands() == numPreds);
 
-        int numPhis = (phi->type() == MIRType::Value) ? BOX_PIECES : 1;
+        int numPhis;
+        switch (phi->type()) {
+          case MIRType::Value: numPhis = BOX_PIECES; break;
+          case MIRType::Int64: numPhis = INT64_PIECES; break;
+          default: numPhis = 1; break;
+        }
         for (int i = 0; i < numPhis; i++) {
             LAllocation* inputs = alloc.allocateArray<LAllocation>(numPreds);
             if (!inputs)
                 return false;
 
             void* addr = &phis_[phiIndex++];
             LPhi* lphi = new (addr) LPhi(phi, inputs);
             lphi->setBlock(this);
@@ -338,16 +347,17 @@ LAllocation::aliases(const LAllocation& 
         return toFloatReg()->reg().aliases(other.toFloatReg()->reg());
     return *this == other;
 }
 
 static const char * const TypeChars[] =
 {
     "g",            // GENERAL
     "i",            // INT32
+    "i64",          // INT64
     "o",            // OBJECT
     "s",            // SLOTS
     "f",            // FLOAT32
     "d",            // DOUBLE
     "simd128int",   // SIMD128INT
     "simd128float", // SIMD128FLOAT
     "sincos",       // SINCOS
 #ifdef JS_NUNBOX32
--- a/js/src/jit/LIR.h
+++ b/js/src/jit/LIR.h
@@ -39,16 +39,18 @@ static const uint32_t VREG_INCREMENT = 1
 static const uint32_t THIS_FRAME_ARGSLOT = 0;
 
 #if defined(JS_NUNBOX32)
 # define BOX_PIECES         2
 static const uint32_t VREG_TYPE_OFFSET = 0;
 static const uint32_t VREG_DATA_OFFSET = 1;
 static const uint32_t TYPE_INDEX = 0;
 static const uint32_t PAYLOAD_INDEX = 1;
+static const uint32_t INT64LOW_INDEX = 0;
+static const uint32_t INT64HIGH_INDEX = 1;
 #elif defined(JS_PUNBOX64)
 # define BOX_PIECES         1
 #else
 # error "Unknown!"
 #endif
 
 static const uint32_t INT64_PIECES = sizeof(int64_t) / sizeof(uintptr_t);
 
@@ -611,20 +613,21 @@ class LDefinition
             return LDefinition::BOX;
 #endif
           case MIRType::SinCosDouble:
             return LDefinition::SINCOS;
           case MIRType::Slots:
           case MIRType::Elements:
             return LDefinition::SLOTS;
           case MIRType::Pointer:
-#if JS_BITS_PER_WORD == 64
+            return LDefinition::GENERAL;
+#if defined(JS_PUNBOX64)
           case MIRType::Int64:
+            return LDefinition::GENERAL;
 #endif
-            return LDefinition::GENERAL;
           case MIRType::Int8x16:
           case MIRType::Int16x8:
           case MIRType::Int32x4:
           case MIRType::Bool8x16:
           case MIRType::Bool16x8:
           case MIRType::Bool32x4:
             return LDefinition::SIMD128INT;
           case MIRType::Float32x4:
@@ -1120,30 +1123,38 @@ class LInstructionHelper : public detail
     LAllocation* getOperand(size_t index) final override {
         return &operands_[index];
     }
     void setOperand(size_t index, const LAllocation& a) final override {
         operands_[index] = a;
     }
     void setBoxOperand(size_t index, const LBoxAllocation& alloc) {
 #ifdef JS_NUNBOX32
-        operands_[index] = alloc.type();
-        operands_[index + 1] = alloc.payload();
+        operands_[index + TYPE_INDEX] = alloc.type();
+        operands_[index + PAYLOAD_INDEX] = 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();
+        operands_[index + INT64LOW_INDEX] = alloc.low();
+        operands_[index + INT64HIGH_INDEX] = alloc.high();
 #else
         operands_[index] = alloc.value();
 #endif
     }
+    const LInt64Allocation getInt64Operand(size_t offset) {
+#if JS_BITS_PER_WORD == 32
+        return LInt64Allocation(operands_[offset + INT64HIGH_INDEX],
+                                operands_[offset + INT64LOW_INDEX]);
+#else
+        return LInt64Allocation(operands_[offset]);
+#endif
+    }
 };
 
 template<size_t Defs, size_t Temps>
 class LVariadicInstruction : public details::LInstructionFixedDefsTempsHelper<Defs, Temps>
 {
     FixedList<LAllocation> operands_;
 
   public:
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -847,20 +847,20 @@ LIRGenerator::visitTest(MTest* 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);
+            LCompareI64AndBranch* lir = new(alloc()) LCompareI64AndBranch(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);
@@ -1093,17 +1093,17 @@ LIRGenerator::visitCompare(MCompare* com
         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)),
+        define(new(alloc()) LCompareI64(op, useInt64Register(left), useInt64OrConstant(right)),
                comp);
         return;
     }
 
     // Compare doubles.
     if (comp->isDoubleComparison()) {
         define(new(alloc()) LCompareD(useRegister(left), useRegister(right)), comp);
         return;
@@ -1299,17 +1299,17 @@ LIRGenerator::visitRotate(MRotate* ins)
 {
     MDefinition* input = ins->input();
     MDefinition* count = ins->count();
 
     if (ins->type() == MIRType::Int32) {
         auto* lir = new(alloc()) LRotate();
         lowerForShift(lir, ins, input, count);
     } else if (ins->type() == MIRType::Int64) {
-        auto* lir = new(alloc()) LRotate64();
+        auto* lir = new(alloc()) LRotateI64();
         lowerForShiftInt64(lir, ins, input, count);
     } else {
         MOZ_CRASH("unexpected type in visitRotate");
     }
 }
 
 void
 LIRGenerator::visitFloor(MFloor* ins)
@@ -1454,17 +1454,17 @@ LIRGenerator::visitPopcnt(MPopcnt* ins)
     MOZ_ASSERT(IsIntType(ins->type()));
 
     if (ins->type() == MIRType::Int32) {
         LPopcntI* lir = new(alloc()) LPopcntI(useRegisterAtStart(num), temp());
         define(lir, ins);
         return;
     }
 
-    auto* lir = new(alloc()) LPopcntI64(useInt64RegisterAtStart(num), tempInt64());
+    auto* lir = new(alloc()) LPopcntI64(useInt64RegisterAtStart(num), temp());
     defineInt64(lir, ins);
 }
 
 void
 LIRGenerator::visitSqrt(MSqrt* ins)
 {
     MDefinition* num = ins->input();
     MOZ_ASSERT(IsFloatingPointType(num->type()));
@@ -1711,17 +1711,17 @@ LIRGenerator::visitMul(MMul* ins)
             lowerMulI(ins, lhs, rhs);
         return;
     }
 
     if (ins->specialization() == MIRType::Int64) {
         MOZ_ASSERT(lhs->type() == MIRType::Int64);
         ReorderCommutative(&lhs, &rhs, ins);
         LMulI64* lir = new(alloc()) LMulI64;
-        lowerForALUInt64(lir, ins, lhs, rhs);
+        lowerForMulInt64(lir, ins, lhs, rhs);
         return;
     }
 
     if (ins->specialization() == MIRType::Double) {
         MOZ_ASSERT(lhs->type() == MIRType::Double);
         ReorderCommutative(&lhs, &rhs, ins);
 
         // If our RHS is a constant -1.0, we can optimize to an LNegD.
@@ -2163,22 +2163,16 @@ LIRGenerator::visitWasmTruncateToInt32(M
 
 void
 LIRGenerator::visitWrapInt64ToInt32(MWrapInt64ToInt32* ins)
 {
     define(new(alloc()) LWrapInt64ToInt32(useInt64AtStart(ins->input())), ins);
 }
 
 void
-LIRGenerator::visitExtendInt32ToInt64(MExtendInt32ToInt64* ins)
-{
-    defineInt64(new(alloc()) LExtendInt32ToInt64(useAtStart(ins->input())), ins);
-}
-
-void
 LIRGenerator::visitToString(MToString* ins)
 {
     MDefinition* opd = ins->input();
 
     switch (opd->type()) {
       case MIRType::Null: {
         const JSAtomState& names = GetJitContext()->runtime->names();
         LPointer* lir = new(alloc()) LPointer(names.null);
@@ -4111,40 +4105,63 @@ LIRGenerator::visitAsmJSLoadFFIFunc(MAsm
     define(new(alloc()) LAsmJSLoadFFIFunc, ins);
 }
 
 void
 LIRGenerator::visitAsmJSParameter(MAsmJSParameter* ins)
 {
     ABIArg abi = ins->abi();
     if (abi.argInRegister()) {
+#if defined(JS_NUNBOX32)
+        if (abi.isGeneralRegPair()) {
+            defineInt64Fixed(new(alloc()) LAsmJSParameterI64, ins,
+                LInt64Allocation(LAllocation(AnyRegister(abi.gpr64().high)),
+                                 LAllocation(AnyRegister(abi.gpr64().low))));
+            return;
+        }
+#endif
         defineFixed(new(alloc()) LAsmJSParameter, ins, LAllocation(abi.reg()));
+        return;
+    }
+    if (ins->type() == MIRType::Int64) {
+        MOZ_ASSERT(!abi.argInRegister());
+        defineInt64Fixed(new(alloc()) LAsmJSParameterI64, ins,
+#if defined(JS_NUNBOX32)
+            LInt64Allocation(LArgument(abi.offsetFromArgBase() + INT64HIGH_OFFSET),
+                             LArgument(abi.offsetFromArgBase() + INT64LOW_OFFSET))
+#else
+            LInt64Allocation(LArgument(abi.offsetFromArgBase()))
+#endif
+        );
     } else {
         MOZ_ASSERT(IsNumberType(ins->type()) || IsSimdType(ins->type()));
         defineFixed(new(alloc()) LAsmJSParameter, ins, LArgument(abi.offsetFromArgBase()));
     }
 }
 
 void
 LIRGenerator::visitAsmJSReturn(MAsmJSReturn* ins)
 {
     MDefinition* rval = ins->getOperand(0);
+
+    if (rval->type() == MIRType::Int64) {
+        LAsmJSReturnI64* lir = new(alloc()) LAsmJSReturnI64(useInt64Fixed(rval, ReturnReg64));
+        add(lir);
+        return;
+    }
+
     LAsmJSReturn* lir = new(alloc()) LAsmJSReturn;
     if (rval->type() == MIRType::Float32)
         lir->setOperand(0, useFixed(rval, ReturnFloat32Reg));
     else if (rval->type() == MIRType::Double)
         lir->setOperand(0, useFixed(rval, ReturnDoubleReg));
     else if (IsSimdType(rval->type()))
         lir->setOperand(0, useFixed(rval, ReturnSimd128Reg));
     else if (rval->type() == MIRType::Int32)
         lir->setOperand(0, useFixed(rval, ReturnReg));
-#if JS_BITS_PER_WORD == 64
-    else if (rval->type() == MIRType::Int64)
-        lir->setOperand(0, useFixed(rval, ReturnReg));
-#endif
     else
         MOZ_CRASH("Unexpected asm.js return type");
 
     // Preserve the TLS pointer we were passed in `WasmTlsReg`.
     MDefinition* tlsPtr = ins->getOperand(1);
     lir->setOperand(1, useFixed(tlsPtr, WasmTlsReg));
 
     add(lir);
@@ -4160,17 +4177,19 @@ LIRGenerator::visitAsmJSVoidReturn(MAsmJ
     lir->setOperand(0, useFixed(tlsPtr, WasmTlsReg));
 
     add(lir);
 }
 
 void
 LIRGenerator::visitAsmJSPassStackArg(MAsmJSPassStackArg* ins)
 {
-    if (IsFloatingPointType(ins->arg()->type()) || IsSimdType(ins->arg()->type())) {
+    if (ins->arg()->type() == MIRType::Int64) {
+        add(new(alloc()) LAsmJSPassStackArgI64(useInt64OrConstantAtStart(ins->arg())), ins);
+    } else if (IsFloatingPointType(ins->arg()->type()) || IsSimdType(ins->arg()->type())) {
         MOZ_ASSERT(!ins->arg()->isEmittedAtUses());
         add(new(alloc()) LAsmJSPassStackArg(useRegisterAtStart(ins->arg())), ins);
     } else {
         add(new(alloc()) LAsmJSPassStackArg(useRegisterOrConstantAtStart(ins->arg())), ins);
     }
 }
 
 void
@@ -4187,17 +4206,22 @@ LIRGenerator::visitAsmJSCall(MAsmJSCall*
     for (unsigned i = 0; i < ins->numArgs(); i++)
         args[i] = useFixed(ins->getOperand(i), ins->registerForArg(i));
 
     if (ins->callee().which() == MAsmJSCall::Callee::Dynamic) {
         args[ins->dynamicCalleeOperandIndex()] =
             useFixed(ins->callee().dynamicPtr(), WasmTableCallPtrReg);
     }
 
-    LInstruction* lir = new(alloc()) LAsmJSCall(args, ins->numOperands());
+    LInstruction* lir;
+    if (ins->type() == MIRType::Int64)
+        lir = new(alloc()) LAsmJSCallI64(args, ins->numOperands());
+    else
+        lir = new(alloc()) LAsmJSCall(args, ins->numOperands());
+
     if (ins->type() == MIRType::None)
         add(lir, ins);
     else
         defineReturn(lir, ins);
 }
 
 void
 LIRGenerator::visitSetDOMProperty(MSetDOMProperty* ins)
@@ -4627,16 +4651,19 @@ void
 LIRGenerator::definePhis()
 {
     size_t lirIndex = 0;
     MBasicBlock* block = current->mir();
     for (MPhiIterator phi(block->phisBegin()); phi != block->phisEnd(); phi++) {
         if (phi->type() == MIRType::Value) {
             defineUntypedPhi(*phi, lirIndex);
             lirIndex += BOX_PIECES;
+        } else if (phi->type() == MIRType::Int64) {
+            defineInt64Phi(*phi, lirIndex);
+            lirIndex += INT64_PIECES;
         } else {
             defineTypedPhi(*phi, lirIndex);
             lirIndex += 1;
         }
     }
 }
 
 void
@@ -4695,16 +4722,19 @@ LIRGenerator::visitBlock(MBasicBlock* bl
             MDefinition* opd = phi->getOperand(position);
             ensureDefined(opd);
 
             MOZ_ASSERT(opd->type() == phi->type());
 
             if (phi->type() == MIRType::Value) {
                 lowerUntypedPhiInput(*phi, position, successor->lir(), lirIndex);
                 lirIndex += BOX_PIECES;
+            } else if (phi->type() == MIRType::Int64) {
+                lowerInt64PhiInput(*phi, position, successor->lir(), lirIndex);
+                lirIndex += INT64_PIECES;
             } else {
                 lowerTypedPhiInput(*phi, position, successor->lir(), lirIndex);
                 lirIndex += 1;
             }
         }
     }
 
     // Now emit the last instruction, which is some form of branch.
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -157,17 +157,16 @@ class LIRGenerator : public LIRGenerator
     void visitOsrReturnValue(MOsrReturnValue* value);
     void visitOsrArgumentsObject(MOsrArgumentsObject* object);
     void visitToDouble(MToDouble* convert);
     void visitToFloat32(MToFloat32* convert);
     void visitToInt32(MToInt32* convert);
     void visitTruncateToInt32(MTruncateToInt32* truncate);
     void visitWasmTruncateToInt32(MWasmTruncateToInt32* truncate);
     void visitWrapInt64ToInt32(MWrapInt64ToInt32* ins);
-    void visitExtendInt32ToInt64(MExtendInt32ToInt64* ins);
     void visitToString(MToString* convert);
     void visitToObjectOrNull(MToObjectOrNull* convert);
     void visitRegExp(MRegExp* ins);
     void visitRegExpMatcher(MRegExpMatcher* ins);
     void visitRegExpSearcher(MRegExpSearcher* ins);
     void visitRegExpTester(MRegExpTester* ins);
     void visitRegExpPrototypeOptimizable(MRegExpPrototypeOptimizable* ins);
     void visitRegExpInstanceOptimizable(MRegExpInstanceOptimizable* ins);
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -4065,18 +4065,19 @@ MWasmTruncateToInt32::foldsTo(TempAlloca
     return this;
 }
 
 MDefinition*
 MWrapInt64ToInt32::foldsTo(TempAllocator& alloc)
 {
     MDefinition* input = this->input();
     if (input->isConstant()) {
-        int64_t c = input->toConstant()->toInt64();
-        return MConstant::New(alloc, Int32Value(int32_t(c)));
+        uint64_t c = input->toConstant()->toInt64();
+        int32_t output = bottomHalf() ? int32_t(c) : int32_t(c >> 32);
+        return MConstant::New(alloc, Int32Value(output));
     }
 
     return this;
 }
 
 MDefinition*
 MExtendInt32ToInt64::foldsTo(TempAllocator& alloc)
 {
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -5189,35 +5189,48 @@ class MAsmJSUnsignedToFloat32
 
     bool canProduceFloat32() const override { return true; }
 };
 
 class MWrapInt64ToInt32
   : public MUnaryInstruction,
     public NoTypePolicy::Data
 {
-    explicit MWrapInt64ToInt32(MDefinition* def)
-      : MUnaryInstruction(def)
+    bool bottomHalf_;
+
+    explicit MWrapInt64ToInt32(MDefinition* def, bool bottomHalf)
+      : MUnaryInstruction(def),
+        bottomHalf_(bottomHalf)
     {
         setResultType(MIRType::Int32);
         setMovable();
     }
 
   public:
     INSTRUCTION_HEADER(WrapInt64ToInt32)
-    static MWrapInt64ToInt32* NewAsmJS(TempAllocator& alloc, MDefinition* def) {
-        return new(alloc) MWrapInt64ToInt32(def);
+    static MWrapInt64ToInt32* NewAsmJS(TempAllocator& alloc, MDefinition* def,
+                                       bool bottomHalf = true)
+    {
+        return new(alloc) MWrapInt64ToInt32(def, bottomHalf);
     }
 
     MDefinition* foldsTo(TempAllocator& alloc) override;
     bool congruentTo(const MDefinition* ins) const override {
-        return congruentIfOperandsEqual(ins);
-    }
-    AliasSet getAliasSet() const override {
-        return AliasSet::None();
+        if (!ins->isWrapInt64ToInt32())
+            return false;
+        if (ins->toWrapInt64ToInt32()->bottomHalf() != bottomHalf())
+            return false;
+        return congruentIfOperandsEqual(ins);
+    }
+    AliasSet getAliasSet() const override {
+        return AliasSet::None();
+    }
+
+    bool bottomHalf() const {
+        return bottomHalf_;
     }
 };
 
 class MExtendInt32ToInt64
   : public MUnaryInstruction,
     public NoTypePolicy::Data
 {
     bool isUnsigned_;
--- a/js/src/jit/MacroAssembler-inl.h
+++ b/js/src/jit/MacroAssembler-inl.h
@@ -546,16 +546,72 @@ MacroAssembler::branchTestNeedsIncrement
 void
 MacroAssembler::branchTestMagicValue(Condition cond, const ValueOperand& val, JSWhyMagic why,
                                      Label* label)
 {
     MOZ_ASSERT(cond == Equal || cond == NotEqual);
     branchTestValue(cond, val, MagicValue(why), label);
 }
 
+void
+MacroAssembler::branchDoubleNotInInt64Range(Address src, Register temp, Label* fail)
+{
+    // Tests if double is in [INT64_MIN; INT64_MAX] range
+    uint32_t EXPONENT_MASK = 0x7ff00000;
+    uint32_t EXPONENT_SHIFT = FloatingPoint<double>::kExponentShift - 32;
+    uint32_t TOO_BIG_EXPONENT = (FloatingPoint<double>::kExponentBias + 63) << EXPONENT_SHIFT;
+
+    load32(Address(src.base, src.offset + sizeof(int32_t)), temp);
+    and32(Imm32(EXPONENT_MASK), temp);
+    branch32(Assembler::GreaterThanOrEqual, temp, Imm32(TOO_BIG_EXPONENT), fail);
+}
+
+void
+MacroAssembler::branchDoubleNotInUInt64Range(Address src, Register temp, Label* fail)
+{
+    // Note: returns failure on -0.0
+    // Tests if double is in [0; UINT64_MAX] range
+    // Take the sign also in the equation. That way we can compare in one test?
+    uint32_t EXPONENT_MASK = 0xfff00000;
+    uint32_t EXPONENT_SHIFT = FloatingPoint<double>::kExponentShift - 32;
+    uint32_t TOO_BIG_EXPONENT = (FloatingPoint<double>::kExponentBias + 64) << EXPONENT_SHIFT;
+
+    load32(Address(src.base, src.offset + sizeof(int32_t)), temp);
+    and32(Imm32(EXPONENT_MASK), temp);
+    branch32(Assembler::AboveOrEqual, temp, Imm32(TOO_BIG_EXPONENT), fail);
+}
+
+void
+MacroAssembler::branchFloat32NotInInt64Range(Address src, Register temp, Label* fail)
+{
+    // Tests if float is in [INT64_MIN; INT64_MAX] range
+    uint32_t EXPONENT_MASK = 0x7f800000;
+    uint32_t EXPONENT_SHIFT = FloatingPoint<float>::kExponentShift;
+    uint32_t TOO_BIG_EXPONENT = (FloatingPoint<float>::kExponentBias + 63) << EXPONENT_SHIFT;
+
+    load32(src, temp);
+    and32(Imm32(EXPONENT_MASK), temp);
+    branch32(Assembler::GreaterThanOrEqual, temp, Imm32(TOO_BIG_EXPONENT), fail);
+}
+
+void
+MacroAssembler::branchFloat32NotInUInt64Range(Address src, Register temp, Label* fail)
+{
+    // Note: returns failure on -0.0
+    // Tests if float is in [0; UINT64_MAX] range
+    // Take the sign also in the equation. That way we can compare in one test?
+    uint32_t EXPONENT_MASK = 0xff800000;
+    uint32_t EXPONENT_SHIFT = FloatingPoint<float>::kExponentShift;
+    uint32_t TOO_BIG_EXPONENT = (FloatingPoint<float>::kExponentBias + 64) << EXPONENT_SHIFT;
+
+    load32(src, temp);
+    and32(Imm32(EXPONENT_MASK), temp);
+    branch32(Assembler::AboveOrEqual, temp, Imm32(TOO_BIG_EXPONENT), fail);
+}
+
 // ========================================================================
 // Canonicalization primitives.
 void
 MacroAssembler::canonicalizeFloat(FloatRegister reg)
 {
     Label notNaN;
     branchFloat(DoubleOrdered, reg, reg, &notNaN);
     loadConstantFloat32(float(JS::GenericNaN()), reg);
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -776,16 +776,17 @@ MacroAssembler::nurseryAllocate(Register
         return;
     }
 
     // No explicit check for nursery.isEnabled() is needed, as the comparison
     // with the nursery's end will always fail in such cases.
     const Nursery& nursery = GetJitContext()->runtime->gcNursery();
     int thingSize = int(gc::Arena::thingSize(allocKind));
     int totalSize = thingSize + nDynamicSlots * sizeof(HeapSlot);
+    MOZ_ASSERT(totalSize % gc::CellSize == 0);
     loadPtr(AbsoluteAddress(nursery.addressOfPosition()), result);
     computeEffectiveAddress(Address(result, totalSize), temp);
     branchPtr(Assembler::Below, AbsoluteAddress(nursery.addressOfCurrentEnd()), temp, fail);
     storePtr(temp, AbsoluteAddress(nursery.addressOfPosition()));
 
     if (nDynamicSlots) {
         computeEffectiveAddress(Address(result, thingSize), temp);
         storePtr(temp, Address(result, NativeObject::offsetOfSlots()));
@@ -1967,17 +1968,17 @@ MacroAssembler::convertDoubleToInt(Float
                                    IntConversionBehavior behavior)
 {
     switch (behavior) {
       case IntConversion_Normal:
       case IntConversion_NegativeZeroCheck:
         convertDoubleToInt32(src, output, fail, behavior == IntConversion_NegativeZeroCheck);
         break;
       case IntConversion_Truncate:
-        branchTruncateDouble(src, output, truncateFail ? truncateFail : fail);
+        branchTruncateDoubleMaybeModUint32(src, output, truncateFail ? truncateFail : fail);
         break;
       case IntConversion_ClampToUint8:
         // Clamping clobbers the input register, so use a temp.
         moveDouble(src, temp);
         clampDoubleToUint8(temp, output);
         break;
     }
 }
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -32,16 +32,18 @@
 #include "jit/AtomicOp.h"
 #include "jit/IonInstrumentation.h"
 #include "jit/JitCompartment.h"
 #include "jit/VMFunctions.h"
 #include "vm/ProxyObject.h"
 #include "vm/Shape.h"
 #include "vm/UnboxedObject.h"
 
+using mozilla::FloatingPoint;
+
 // * How to read/write MacroAssembler method declarations:
 //
 // The following macros are made to avoid #ifdef around each method declarations
 // of the Macro Assembler, and they are also used as an hint on the location of
 // the implementations of each method.  For example, the following declaration
 //
 //   void Pop(FloatRegister t) DEFINED_ON(x86_shared, arm);
 //
@@ -714,25 +716,30 @@ class MacroAssembler : public MacroAssem
 
     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;
 
+    inline void and64(Register64 src, Register64 dest) DEFINED_ON(x86, arm);
     inline void or64(Register64 src, Register64 dest) PER_ARCH;
     inline void xor64(Register64 src, Register64 dest) PER_ARCH;
 
     inline void xor32(Register src, Register dest) PER_SHARED_ARCH;
     inline void xor32(Imm32 imm, Register dest) PER_SHARED_ARCH;
 
     inline void xorPtr(Register src, Register dest) PER_ARCH;
     inline void xorPtr(Imm32 imm, Register dest) PER_ARCH;
 
+    inline void and64(const Operand& src, Register64 dest) DEFINED_ON(x64);
+    inline void or64(const Operand& src, Register64 dest) DEFINED_ON(x64);
+    inline void xor64(const Operand& src, Register64 dest) DEFINED_ON(x64);
+
     // ===============================================================
     // Arithmetic functions
 
     inline void add32(Register src, Register dest) PER_SHARED_ARCH;
     inline void add32(Imm32 imm, Register dest) PER_SHARED_ARCH;
     inline void add32(Imm32 imm, const Address& dest) PER_SHARED_ARCH;
     inline void add32(Imm32 imm, const AbsoluteAddress& dest) DEFINED_ON(x86_shared);
 
@@ -743,42 +750,55 @@ class MacroAssembler : public MacroAssem
     inline void addPtr(ImmWord imm, Register dest) PER_ARCH;
     inline void addPtr(ImmPtr imm, Register dest);
     inline void addPtr(Imm32 imm, const Address& dest) DEFINED_ON(mips_shared, arm, arm64, x86, x64);
     inline void addPtr(Imm32 imm, const AbsoluteAddress& dest) DEFINED_ON(x86, x64);
     inline void addPtr(const Address& src, Register dest) DEFINED_ON(mips_shared, arm, arm64, x86, x64);
 
     inline void add64(Register64 src, Register64 dest) PER_ARCH;
     inline void add64(Imm32 imm, Register64 dest) PER_ARCH;
+    inline void add64(Imm64 imm, Register64 dest) DEFINED_ON(x86, x64, arm);
+    inline void add64(const Operand& src, Register64 dest) DEFINED_ON(x64);
 
     inline void addFloat32(FloatRegister src, FloatRegister dest) PER_SHARED_ARCH;
 
     inline void addDouble(FloatRegister src, FloatRegister dest) PER_SHARED_ARCH;
     inline void addConstantDouble(double d, FloatRegister dest) DEFINED_ON(x86);
 
     inline void sub32(const Address& src, Register dest) PER_SHARED_ARCH;
     inline void sub32(Register src, Register dest) PER_SHARED_ARCH;
     inline void sub32(Imm32 imm, Register dest) PER_SHARED_ARCH;
 
     inline void subPtr(Register src, Register dest) PER_ARCH;
     inline void subPtr(Register src, const Address& dest) DEFINED_ON(mips_shared, arm, arm64, x86, x64);
     inline void subPtr(Imm32 imm, Register dest) PER_ARCH;
     inline void subPtr(ImmWord imm, Register dest) DEFINED_ON(x64);
     inline void subPtr(const Address& addr, Register dest) DEFINED_ON(mips_shared, arm, arm64, x86, x64);
 
+    inline void sub64(Register64 src, Register64 dest) DEFINED_ON(x86, x64, arm);
+    inline void sub64(Imm64 imm, Register64 dest) DEFINED_ON(x86, x64, arm);
+    inline void sub64(const Operand& src, Register64 dest) DEFINED_ON(x64);
+
     inline void subFloat32(FloatRegister src, FloatRegister dest) PER_SHARED_ARCH;
 
     inline void subDouble(FloatRegister src, FloatRegister dest) PER_SHARED_ARCH;
 
     // On x86-shared, srcDest must be eax and edx will be clobbered.
     inline void mul32(Register rhs, Register srcDest) PER_SHARED_ARCH;
 
     inline void mul32(Register src1, Register src2, Register dest, Label* onOver, Label* onZero) DEFINED_ON(arm64);
 
+    inline void mul64(const Operand& src, const Register64& dest) DEFINED_ON(x64);
+    inline void mul64(const Operand& src, const Register64& dest, const Register temp)
+        DEFINED_ON(x64);
     inline void mul64(Imm64 imm, const Register64& dest) PER_ARCH;
+    inline void mul64(Imm64 imm, const Register64& dest, const Register temp)
+        DEFINED_ON(x86, x64, arm);
+    inline void mul64(const Register64& src, const Register64& dest, const Register temp)
+        DEFINED_ON(x86, x64, arm);
 
     inline void mulBy3(Register src, Register dest) PER_ARCH;
 
     inline void mulFloat32(FloatRegister src, FloatRegister dest) PER_SHARED_ARCH;
     inline void mulDouble(FloatRegister src, FloatRegister dest) PER_SHARED_ARCH;
 
     inline void mulDoublePtr(ImmPtr imm, Register temp, FloatRegister dest) DEFINED_ON(mips_shared, arm, arm64, x86, x64);
 
@@ -800,16 +820,17 @@ class MacroAssembler : public MacroAssem
     inline void divDouble(FloatRegister src, FloatRegister dest) PER_SHARED_ARCH;
 
     inline void inc32(RegisterOrInt32Constant* key);
     inline void inc64(AbsoluteAddress dest) PER_ARCH;
 
     inline void dec32(RegisterOrInt32Constant* key);
 
     inline void neg32(Register reg) PER_SHARED_ARCH;
+    inline void neg64(Register64 reg) DEFINED_ON(x86, x64, arm);
 
     inline void negateFloat(FloatRegister reg) PER_SHARED_ARCH;
 
     inline void negateDouble(FloatRegister reg) PER_SHARED_ARCH;
 
     inline void absFloat32(FloatRegister src, FloatRegister dest) PER_SHARED_ARCH;
     inline void absDouble(FloatRegister src, FloatRegister dest) PER_SHARED_ARCH;
 
@@ -831,59 +852,77 @@ class MacroAssembler : public MacroAssem
     // For shift-by-register there may be platform-specific
     // variations, for example, x86 will perform the shift mod 32 but
     // ARM will perform the shift mod 256.
     //
     // For shift-by-immediate the platform assembler may restrict the
     // immediate, for example, the ARM assembler requires the count
     // for 32-bit shifts to be in the range [0,31].
 
+    inline void lshift32(Imm32 shift, Register srcDest) PER_SHARED_ARCH;
+    inline void rshift32(Imm32 shift, Register srcDest) PER_SHARED_ARCH;
+    inline void rshift32Arithmetic(Imm32 shift, Register srcDest) PER_SHARED_ARCH;
+
     inline void lshiftPtr(Imm32 imm, Register dest) PER_ARCH;
+    inline void rshiftPtr(Imm32 imm, Register dest) PER_ARCH;
+    inline void rshiftPtr(Imm32 imm, Register src, Register dest) DEFINED_ON(arm64);
+    inline void rshiftPtrArithmetic(Imm32 imm, Register dest) PER_ARCH;
 
     inline void lshift64(Imm32 imm, Register64 dest) PER_ARCH;
-
-    inline void rshiftPtr(Imm32 imm, Register dest) PER_ARCH;
-    inline void rshiftPtr(Imm32 imm, Register src, Register dest) DEFINED_ON(arm64);
-
-    inline void rshiftPtrArithmetic(Imm32 imm, Register dest) PER_ARCH;
-
     inline void rshift64(Imm32 imm, Register64 dest) PER_ARCH;
+    inline void rshift64Arithmetic(Imm32 imm, Register64 dest) DEFINED_ON(x86, x64, arm);
 
     // On x86_shared these have the constraint that shift must be in CL.
     inline void lshift32(Register shift, Register srcDest) PER_SHARED_ARCH;
     inline void rshift32(Register shift, Register srcDest) PER_SHARED_ARCH;
     inline void rshift32Arithmetic(Register shift, Register srcDest) PER_SHARED_ARCH;
 
-    inline void lshift32(Imm32 shift, Register srcDest) PER_SHARED_ARCH;
-    inline void rshift32(Imm32 shift, Register srcDest) PER_SHARED_ARCH;
-    inline void rshift32Arithmetic(Imm32 shift, Register srcDest) PER_SHARED_ARCH;
+    inline void lshift64(Register shift, Register64 srcDest) DEFINED_ON(x86, x64, arm);
+    inline void rshift64(Register shift, Register64 srcDest) DEFINED_ON(x86, x64, arm);
+    inline void rshift64Arithmetic(Register shift, Register64 srcDest) DEFINED_ON(x86, x64, arm);
 
     // ===============================================================
     // Rotation functions
+    // Note: - on x86 and x64 the count register must be in CL.
+    //       - on x64 the temp register should be InvalidReg.
+
     inline void rotateLeft(Imm32 count, Register input, Register dest) PER_SHARED_ARCH;
     inline void rotateLeft(Register count, Register input, Register dest) PER_SHARED_ARCH;
+    inline void rotateLeft64(Imm32 count, Register64 input, Register64 dest) DEFINED_ON(x64);
+    inline void rotateLeft64(Register count, Register64 input, Register64 dest) DEFINED_ON(x64);
+    inline void rotateLeft64(Imm32 count, Register64 input, Register64 dest, Register temp)
+        DEFINED_ON(x86, x64, arm);
+    inline void rotateLeft64(Register count, Register64 input, Register64 dest, Register temp)
+        DEFINED_ON(x86, x64, arm);
+
     inline void rotateRight(Imm32 count, Register input, Register dest) PER_SHARED_ARCH;
     inline void rotateRight(Register count, Register input, Register dest) PER_SHARED_ARCH;
+    inline void rotateRight64(Imm32 count, Register64 input, Register64 dest) DEFINED_ON(x64);
+    inline void rotateRight64(Register count, Register64 input, Register64 dest) DEFINED_ON(x64);
+    inline void rotateRight64(Imm32 count, Register64 input, Register64 dest, Register temp)
+        DEFINED_ON(x86, x64, arm);
+    inline void rotateRight64(Register count, Register64 input, Register64 dest, Register temp)
+        DEFINED_ON(x86, x64, arm);
 
     // ===============================================================
     // Bit counting functions
 
     // knownNotZero may be true only if the src is known not to be zero.
     inline void clz32(Register src, Register dest, bool knownNotZero) PER_SHARED_ARCH;
     inline void ctz32(Register src, Register dest, bool knownNotZero) PER_SHARED_ARCH;
 
-    inline void clz64(Register64 src, Register64 dest) DEFINED_ON(x64);
-    inline void ctz64(Register64 src, Register64 dest) DEFINED_ON(x64);
+    inline void clz64(Register64 src, Register dest) DEFINED_ON(x86, x64, arm);
+    inline void ctz64(Register64 src, Register dest) DEFINED_ON(x86, x64, arm);
 
     // On x86_shared, temp may be Invalid only if the chip has the POPCNT instruction.
     // On ARM, temp may never be Invalid.
     inline void popcnt32(Register src, Register dest, Register temp) DEFINED_ON(arm, x86_shared);
 
     // temp may be invalid only if the chip has the POPCNT instruction.
-    inline void popcnt64(Register64 src, Register64 dest, Register64 temp) DEFINED_ON(x64);
+    inline void popcnt64(Register64 src, Register64 dest, Register temp) DEFINED_ON(x86, x64, arm);
 
     // ===============================================================
     // Branch functions
 
     template <class L>
     inline void branch32(Condition cond, Register lhs, Register rhs, L label) PER_SHARED_ARCH;
     template <class L>
     inline void branch32(Condition cond, Register lhs, Imm32 rhs, L label) PER_SHARED_ARCH;
@@ -905,16 +944,26 @@ class MacroAssembler : public MacroAssem
     inline void branch32(Condition cond, const BaseIndex& lhs, Imm32 rhs, Label* label) PER_SHARED_ARCH;
 
     inline void branch32(Condition cond, const Operand& lhs, Register rhs, Label* label) DEFINED_ON(x86_shared);
     inline void branch32(Condition cond, const Operand& lhs, Imm32 rhs, Label* label) DEFINED_ON(x86_shared);
 
     inline void branch32(Condition cond, wasm::SymbolicAddress lhs, Imm32 rhs, Label* label)
         DEFINED_ON(arm, arm64, mips_shared, x86, x64);
 
+    // The supported condition are Equal, NotEqual, LessThan(orEqual), GreaterThan(orEqual),
+    // Below(orEqual) and Above(orEqual).
+    // When a fail label is not defined it will fall through to next instruction,
+    // else jump to the fail label.
+    inline void branch64(Condition cond, Register64 lhs, Imm64 val, Label* success,
+                         Label* fail = nullptr) DEFINED_ON(x86, x64, arm);
+    inline void branch64(Condition cond, Register64 lhs, Register64 rhs, Label* success,
+                         Label* fail = nullptr) DEFINED_ON(x86, x64, arm);
+    // On x86 and x64 NotEqual and Equal conditions are allowed for the branch64 variants
+    // with Address as lhs. On others only the NotEqual condition.
     inline void branch64(Condition cond, const Address& lhs, Imm64 val, Label* label) PER_ARCH;
 
     // Compare the value at |lhs| with the value at |rhs|.  The scratch
     // register *must not* be the base of |lhs| or |rhs|.
     inline void branch64(Condition cond, const Address& lhs, const Address& rhs, Register scratch,
                          Label* label) PER_ARCH;
 
     inline void branchPtr(Condition cond, Register lhs, Register rhs, Label* label) PER_SHARED_ARCH;
@@ -949,45 +998,69 @@ class MacroAssembler : public MacroAssem
     void branchValueIsNurseryObject(Condition cond, ValueOperand value, Register temp, Label* label) PER_ARCH;
 
     // This function compares a Value (lhs) which is having a private pointer
     // boxed inside a js::Value, with a raw pointer (rhs).
     inline void branchPrivatePtr(Condition cond, const Address& lhs, Register rhs, Label* label) PER_ARCH;
 
     inline void branchFloat(DoubleCondition cond, FloatRegister lhs, FloatRegister rhs,
                             Label* label) PER_SHARED_ARCH;
-    inline void branchTruncateFloat32(FloatRegister src, Register dest, Label* fail)
+
+    // Truncate a double/float32 to int32 and when it doesn't fit an int32 it will jump to
+    // the failure label. This particular variant is allowed to return the value module 2**32,
+    // which isn't implemented on all architectures.
+    // E.g. the x64 variants will do this only in the int64_t range.
+    inline void branchTruncateFloat32MaybeModUint32(FloatRegister src, Register dest, Label* fail)
+        DEFINED_ON(arm, arm64, mips_shared, x86, x64);
+    inline void branchTruncateDoubleMaybeModUint32(FloatRegister src, Register dest, Label* fail)
+        DEFINED_ON(arm, arm64, mips_shared, x86, x64);
+
+    // Truncate a double/float32 to intptr and when it doesn't fit jump to the failure label.
+    inline void branchTruncateFloat32ToPtr(FloatRegister src, Register dest, Label* fail)
+        DEFINED_ON(x86, x64);
+    inline void branchTruncateDoubleToPtr(FloatRegister src, Register dest, Label* fail)
+        DEFINED_ON(x86, x64);
+
+    // Truncate a double/float32 to int32 and when it doesn't fit jump to the failure label.
+    inline void branchTruncateFloat32ToInt32(FloatRegister src, Register dest, Label* fail)
+        DEFINED_ON(arm, arm64, mips_shared, x86, x64);
+    inline void branchTruncateDoubleToInt32(FloatRegister src, Register dest, Label* fail)
         DEFINED_ON(arm, arm64, mips_shared, x86, x64);
 
     inline void branchDouble(DoubleCondition cond, FloatRegister lhs, FloatRegister rhs,
                              Label* label) PER_SHARED_ARCH;
-    inline void branchTruncateDouble(FloatRegister src, Register dest, Label* fail)
-        DEFINED_ON(arm, arm64, mips_shared, x86, x64);
+
+    inline void branchDoubleNotInInt64Range(Address src, Register temp, Label* fail);
+    inline void branchDoubleNotInUInt64Range(Address src, Register temp, Label* fail);
+    inline void branchFloat32NotInInt64Range(Address src, Register temp, Label* fail);
+    inline void branchFloat32NotInUInt64Range(Address src, Register temp, Label* fail);
 
     template <typename T>
     inline void branchAdd32(Condition cond, T src, Register dest, Label* label) PER_SHARED_ARCH;
     template <typename T>
     inline void branchSub32(Condition cond, T src, Register dest, Label* label) PER_SHARED_ARCH;
 
     inline void decBranchPtr(Condition cond, Register lhs, Imm32 rhs, Label* label) PER_SHARED_ARCH;
 
     template <class L>
     inline void branchTest32(Condition cond, Register lhs, Register rhs, L label) PER_SHARED_ARCH;
     template <class L>
     inline void branchTest32(Condition cond, Register lhs, Imm32 rhs, L label) PER_SHARED_ARCH;
     inline void branchTest32(Condition cond, const Address& lhs, Imm32 rhh, Label* label) PER_SHARED_ARCH;
     inline void branchTest32(Condition cond, const AbsoluteAddress& lhs, Imm32 rhs, Label* label)
         DEFINED_ON(arm, arm64, mips_shared, x86, x64);
 
-    inline void branchTestPtr(Condition cond, Register lhs, Register rhs, Label* label) PER_SHARED_ARCH;
+    template <class L>
+    inline void branchTestPtr(Condition cond, Register lhs, Register rhs, L label) PER_SHARED_ARCH;
     inline void branchTestPtr(Condition cond, Register lhs, Imm32 rhs, Label* label) PER_SHARED_ARCH;
     inline void branchTestPtr(Condition cond, const Address& lhs, Imm32 rhs, Label* label) PER_SHARED_ARCH;
 
+    template <class L>
     inline void branchTest64(Condition cond, Register64 lhs, Register64 rhs, Register temp,
-                             Label* label) PER_ARCH;
+                             L label) PER_ARCH;
 
     // Branches to |label| if |reg| is false. |reg| should be a C++ bool.
     template <class L>
     inline void branchIfFalseBool(Register reg, L label);
 
     // Branches to |label| if |reg| is true. |reg| should be a C++ bool.
     inline void branchIfTrueBool(Register reg, Label* label);
 
@@ -1212,16 +1285,33 @@ class MacroAssembler : public MacroAssem
 
     inline void storeFloat32x3(FloatRegister src, const Address& dest) PER_SHARED_ARCH;
     inline void storeFloat32x3(FloatRegister src, const BaseIndex& dest) PER_SHARED_ARCH;
 
     template <typename T>
     void storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const T& dest,
                            MIRType slotType) PER_ARCH;
 
+  public:
+    // ========================================================================
+    // Truncate floating point.
+
+    // Undefined behaviour when truncation is outside Int64 range.
+    // Needs a temp register if SSE3 is not present.
+    inline void truncateFloat32ToInt64(Address src, Address dest, Register temp)
+        DEFINED_ON(x86_shared);
+    inline void truncateFloat32ToUInt64(Address src, Address dest, Register temp,
+                                        FloatRegister floatTemp)
+        DEFINED_ON(x86, x64);
+    inline void truncateDoubleToInt64(Address src, Address dest, Register temp)
+        DEFINED_ON(x86_shared);
+    inline void truncateDoubleToUInt64(Address src, Address dest, Register temp,
+                                       FloatRegister floatTemp)
+        DEFINED_ON(x86, x64);
+
     //}}} check_macroassembler_style
   public:
 
     // Emits a test of a value against all types in a TypeSet. A scratch
     // register is required.
     template <typename Source>
     void guardTypeSet(const Source& address, const TypeSet* types, BarrierKind kind, Register scratch, Label* miss);
 
--- a/js/src/jit/RegisterSets.h
+++ b/js/src/jit/RegisterSets.h
@@ -1250,17 +1250,17 @@ class ABIArg
     Register gpr() const {
         MOZ_ASSERT(kind() == GPR);
         return Register::FromCode(u.gpr_);
     }
     Register64 gpr64() const {
 #ifdef JS_PUNBOX64
         return Register64(gpr());
 #else
-        MOZ_CRASH("NYI");
+        return Register64(oddGpr(), evenGpr());
 #endif
     }
     Register evenGpr() const {
         MOZ_ASSERT(isGeneralRegPair());
         return Register::FromCode(u.gpr_);
     }
     Register oddGpr() const {
         MOZ_ASSERT(isGeneralRegPair());
--- a/js/src/jit/Registers.h
+++ b/js/src/jit/Registers.h
@@ -94,16 +94,21 @@ struct Register {
     static uint32_t FirstBit(SetType x) {
         return Codes::FirstBit(x);
     }
     static uint32_t LastBit(SetType x) {
         return Codes::LastBit(x);
     }
 };
 
+#if defined(JS_NUNBOX32)
+static const uint32_t INT64LOW_OFFSET = 0 * sizeof(int32_t);
+static const uint32_t INT64HIGH_OFFSET = 1 * sizeof(int32_t);
+#endif
+
 struct Register64
 {
 #ifdef JS_PUNBOX64
     Register reg;
 #else
     Register high;
     Register low;
 #endif
--- a/js/src/jit/SharedIC.cpp
+++ b/js/src/jit/SharedIC.cpp
@@ -1400,17 +1400,17 @@ ICBinaryArith_DoubleWithInt32::Compiler:
         masm.unboxDouble(R1, FloatReg0);
         scratchReg = R1.scratchReg();
     }
 
     // Truncate the double to an int32.
     {
         Label doneTruncate;
         Label truncateABICall;
-        masm.branchTruncateDouble(FloatReg0, scratchReg, &truncateABICall);
+        masm.branchTruncateDoubleMaybeModUint32(FloatReg0, scratchReg, &truncateABICall);
         masm.jump(&doneTruncate);
 
         masm.bind(&truncateABICall);
         masm.push(intReg);
         masm.setupUnalignedABICall(scratchReg);
         masm.passABIArg(FloatReg0, MoveOp::DOUBLE);
         masm.callWithABI(mozilla::BitwiseCast<void*, int32_t(*)(double)>(JS::ToInt32));
         masm.storeCallResult(scratchReg);
@@ -1553,17 +1553,17 @@ ICUnaryArith_Double::Compiler::generateS
         masm.negateDouble(FloatReg0);
         masm.boxDouble(FloatReg0, R0);
     } else {
         // Truncate the double to an int32.
         Register scratchReg = R1.scratchReg();
 
         Label doneTruncate;
         Label truncateABICall;
-        masm.branchTruncateDouble(FloatReg0, scratchReg, &truncateABICall);
+        masm.branchTruncateDoubleMaybeModUint32(FloatReg0, scratchReg, &truncateABICall);
         masm.jump(&doneTruncate);
 
         masm.bind(&truncateABICall);
         masm.setupUnalignedABICall(scratchReg);
         masm.passABIArg(FloatReg0, MoveOp::DOUBLE);
         masm.callWithABI(BitwiseCast<void*, int32_t(*)(double)>(JS::ToInt32));
         masm.storeCallResult(scratchReg);
 
--- a/js/src/jit/arm/Assembler-arm.cpp
+++ b/js/src/jit/arm/Assembler-arm.cpp
@@ -52,32 +52,47 @@ ABIArgGenerator::softNext(MIRType type)
         if (intRegIndex_ == NumIntArgRegs) {
             current_ = ABIArg(stackOffset_);
             stackOffset_ += sizeof(uint32_t);
             break;
         }
         current_ = ABIArg(Register::FromCode(intRegIndex_));
         intRegIndex_++;
         break;
+      case MIRType::Int64:
+        // Make sure to use an even register index. Increase to next even number
+        // when odd.
+        intRegIndex_ = (intRegIndex_ + 1) & ~1;
+        if (intRegIndex_ == NumIntArgRegs) {
+            // Align the stack on 8 bytes.
+            static const uint32_t align = sizeof(uint64_t) - 1;
+            stackOffset_ = (stackOffset_ + align) & ~align;
+            current_ = ABIArg(stackOffset_);
+            stackOffset_ += sizeof(uint64_t);
+            break;
+        }
+        current_ = ABIArg(Register::FromCode(intRegIndex_), Register::FromCode(intRegIndex_ + 1));
+        intRegIndex_ += 2;
+        break;
       case MIRType::Float32:
         if (intRegIndex_ == NumIntArgRegs) {
             current_ = ABIArg(stackOffset_);
             stackOffset_ += sizeof(uint32_t);
             break;
         }
         current_ = ABIArg(Register::FromCode(intRegIndex_));
         intRegIndex_++;
         break;
       case MIRType::Double:
         // Make sure to use an even register index. Increase to next even number
         // when odd.
         intRegIndex_ = (intRegIndex_ + 1) & ~1;
         if (intRegIndex_ == NumIntArgRegs) {
             // Align the stack on 8 bytes.
-            static const int align = sizeof(double) - 1;
+            static const uint32_t align = sizeof(double) - 1;
             stackOffset_ = (stackOffset_ + align) & ~align;
             current_ = ABIArg(stackOffset_);
             stackOffset_ += sizeof(double);
             break;
         }
         current_ = ABIArg(Register::FromCode(intRegIndex_), Register::FromCode(intRegIndex_ + 1));
         intRegIndex_ += 2;
         break;
@@ -97,34 +112,49 @@ ABIArgGenerator::hardNext(MIRType type)
         if (intRegIndex_ == NumIntArgRegs) {
             current_ = ABIArg(stackOffset_);
             stackOffset_ += sizeof(uint32_t);
             break;
         }
         current_ = ABIArg(Register::FromCode(intRegIndex_));
         intRegIndex_++;
         break;
+      case MIRType::Int64:
+        // Make sure to use an even register index. Increase to next even number
+        // when odd.
+        intRegIndex_ = (intRegIndex_ + 1) & ~1;
+        if (intRegIndex_ == NumIntArgRegs) {
+            // Align the stack on 8 bytes.
+            static const uint32_t align = sizeof(uint64_t) - 1;
+            stackOffset_ = (stackOffset_ + align) & ~align;
+            current_ = ABIArg(stackOffset_);
+            stackOffset_ += sizeof(uint64_t);
+            break;
+        }
+        current_ = ABIArg(Register::FromCode(intRegIndex_), Register::FromCode(intRegIndex_ + 1));
+        intRegIndex_ += 2;
+        break;
       case MIRType::Float32:
         if (floatRegIndex_ == NumFloatArgRegs) {
-            static const int align = sizeof(double) - 1;
+            static const uint32_t align = sizeof(double) - 1;
             stackOffset_ = (stackOffset_ + align) & ~align;
             current_ = ABIArg(stackOffset_);
             stackOffset_ += sizeof(uint64_t);
             break;
         }
         current_ = ABIArg(VFPRegister(floatRegIndex_, VFPRegister::Single));
         floatRegIndex_++;
         break;
       case MIRType::Double:
         // Double register are composed of 2 float registers, thus we have to
         // skip any float register which cannot be used in a pair of float
         // registers in which a double value can be stored.
         floatRegIndex_ = (floatRegIndex_ + 1) & ~1;
         if (floatRegIndex_ == NumFloatArgRegs) {
-            static const int align = sizeof(double) - 1;
+            static const uint32_t align = sizeof(double) - 1;
             stackOffset_ = (stackOffset_ + align) & ~align;
             current_ = ABIArg(stackOffset_);
             stackOffset_ += sizeof(uint64_t);
             break;
         }
         current_ = ABIArg(VFPRegister(floatRegIndex_ >> 1, VFPRegister::Double));
         floatRegIndex_ += 2;
         break;
@@ -943,16 +973,60 @@ Assembler::Bind(uint8_t* rawCode, CodeOf
 
 Assembler::Condition
 Assembler::InvertCondition(Condition cond)
 {
     const uint32_t ConditionInversionBit = 0x10000000;
     return Condition(ConditionInversionBit ^ cond);
 }
 
+Assembler::Condition
+Assembler::UnsignedCondition(Condition cond)
+{
+    switch (cond) {
+      case Zero:
+      case NonZero:
+        return cond;
+      case LessThan:
+      case Below:
+        return Below;
+      case LessThanOrEqual:
+      case BelowOrEqual:
+        return BelowOrEqual;
+      case GreaterThan:
+      case Above:
+        return Above;
+      case AboveOrEqual:
+      case GreaterThanOrEqual:
+        return AboveOrEqual;
+      default:
+        MOZ_CRASH("unexpected condition");
+    }
+}
+
+Assembler::Condition
+Assembler::ConditionWithoutEqual(Condition cond)
+{
+    switch (cond) {
+      case LessThan:
+      case LessThanOrEqual:
+          return LessThan;
+      case Below:
+      case BelowOrEqual:
+        return Below;
+      case GreaterThan:
+      case GreaterThanOrEqual:
+        return GreaterThan;
+      case Above:
+      case AboveOrEqual:
+        return Above;
+      default:
+        MOZ_CRASH("unexpected condition");
+    }
+}
 Imm8::TwoImm8mData
 Imm8::EncodeTwoImms(uint32_t imm)
 {
     // In the ideal case, we are looking for a number that (in binary) looks
     // like:
     //   0b((00)*)n_1((00)*)n_2((00)*)
     //      left  n1   mid  n2
     //   where both n_1 and n_2 fit into 8 bits.
@@ -1179,17 +1253,17 @@ jit::ror(Register r, int amt)
 O2RegImmShift
 jit::rol(Register r, int amt)
 {
     MOZ_ASSERT(1 <= amt && amt <= 31);
     return O2RegImmShift(r, ROR, 32 - amt);
 }
 
 O2RegImmShift
-jit::asr (Register r, int amt)
+jit::asr(Register r, int amt)
 {
     MOZ_ASSERT(1 <= amt && amt <= 32);
     return O2RegImmShift(r, ASR, amt);
 }
 
 
 O2RegRegShift
 jit::lsl(Register r, Register amt)
@@ -1205,17 +1279,17 @@ jit::lsr(Register r, Register amt)
 
 O2RegRegShift
 jit::ror(Register r, Register amt)
 {
     return O2RegRegShift(r, ROR, amt);
 }
 
 O2RegRegShift
-jit::asr (Register r, Register amt)
+jit::asr(Register r, Register amt)
 {
     return O2RegRegShift(r, ASR, amt);
 }
 
 static js::jit::DoubleEncoder doubleEncoder;
 
 /* static */ const js::jit::VFPImm js::jit::VFPImm::One(0x3FF00000);
 
--- a/js/src/jit/arm/Assembler-arm.h
+++ b/js/src/jit/arm/Assembler-arm.h
@@ -123,17 +123,17 @@ static constexpr Register PreBarrierReg 
 static constexpr Register InvalidReg = { Registers::invalid_reg };
 static constexpr FloatRegister InvalidFloatReg;
 
 static constexpr Register JSReturnReg_Type = r3;
 static constexpr Register JSReturnReg_Data = r2;
 static constexpr Register StackPointer = sp;
 static constexpr Register FramePointer = InvalidReg;
 static constexpr Register ReturnReg = r0;
-static constexpr Register64 ReturnReg64(InvalidReg, InvalidReg);
+static constexpr Register64 ReturnReg64(r1, r0);
 static constexpr FloatRegister ReturnFloat32Reg = { FloatRegisters::d0, VFPRegister::Single };
 static constexpr FloatRegister ReturnDoubleReg = { FloatRegisters::d0, VFPRegister::Double};
 static constexpr FloatRegister ReturnSimd128Reg = InvalidFloatReg;
 static constexpr FloatRegister ScratchFloat32Reg = { FloatRegisters::d30, VFPRegister::Single };
 static constexpr FloatRegister ScratchDoubleReg = { FloatRegisters::d15, VFPRegister::Double };
 static constexpr FloatRegister ScratchSimd128Reg = InvalidFloatReg;
 static constexpr FloatRegister ScratchUIntReg = { FloatRegisters::d15, VFPRegister::UInt };
 static constexpr FloatRegister ScratchIntReg = { FloatRegisters::d15, VFPRegister::Int };
@@ -1377,16 +1377,18 @@ class Assembler : public AssemblerShared
 
     // We need to wait until an AutoJitContextAlloc is created by the
     // MacroAssembler, before allocating any space.
     void initWithAllocator() {
         m_buffer.initWithAllocator();
     }
 
     static Condition InvertCondition(Condition cond);
+    static Condition UnsignedCondition(Condition cond);
+    static Condition ConditionWithoutEqual(Condition cond);
 
     // MacroAssemblers hold onto gcthings, so they are traced by the GC.
     void trace(JSTracer* trc);
     void writeRelocation(BufferOffset src) {
         jumpRelocations_.writeUnsigned(src.getOffset());
     }
 
     // As opposed to x86/x64 version, the data relocation has to be executed
--- a/js/src/jit/arm/CodeGenerator-arm.cpp
+++ b/js/src/jit/arm/CodeGenerator-arm.cpp
@@ -35,16 +35,22 @@ using JS::GenericNaN;
 using JS::ToInt32;
 
 // shared
 CodeGeneratorARM::CodeGeneratorARM(MIRGenerator* gen, LIRGraph* graph, MacroAssembler* masm)
   : CodeGeneratorShared(gen, graph, masm)
 {
 }
 
+Register64
+CodeGeneratorARM::ToOperandOrRegister64(const LInt64Allocation input)
+{
+    return ToRegister64(input);
+}
+
 void
 CodeGeneratorARM::emitBranch(Assembler::Condition cond, MBasicBlock* mirTrue, MBasicBlock* mirFalse)
 {
     if (isNextBlock(mirFalse->lir())) {
         jumpToBlock(mirTrue, cond);
     } else {
         jumpToBlock(mirFalse, Assembler::InvertCondition(cond));
         jumpToBlock(mirTrue);
@@ -282,16 +288,33 @@ CodeGeneratorARM::visitAddI(LAddI* ins)
     else
         masm.ma_add(ToRegister(lhs), Operand(ToAddress(rhs)), ToRegister(dest), SetCC);
 
     if (ins->snapshot())
         bailoutIf(Assembler::Overflow, ins->snapshot());
 }
 
 void
+CodeGeneratorARM::visitAddI64(LAddI64* lir)
+{
+    const LInt64Allocation lhs = lir->getInt64Operand(LAddI64::Lhs);
+    const LInt64Allocation rhs = lir->getInt64Operand(LAddI64::Rhs);
+    Register64 out = ToOutRegister64(lir);
+
+    masm.move64(ToRegister64(lhs), out);
+
+    if (IsConstant(rhs)) {
+        masm.add64(Imm64(ToInt64(rhs)), out);
+        return;
+    }
+
+    masm.add64(ToOperandOrRegister64(rhs), out);
+}
+
+void
 CodeGeneratorARM::visitSubI(LSubI* ins)
 {
     const LAllocation* lhs = ins->getOperand(0);
     const LAllocation* rhs = ins->getOperand(1);
     const LDefinition* dest = ins->getDef(0);
 
     if (rhs->isConstant())
         masm.ma_sub(ToRegister(lhs), Imm32(ToInt32(rhs)), ToRegister(dest), SetCC);
@@ -300,16 +323,33 @@ CodeGeneratorARM::visitSubI(LSubI* ins)
     else
         masm.ma_sub(ToRegister(lhs), Operand(ToAddress(rhs)), ToRegister(dest), SetCC);
 
     if (ins->snapshot())
         bailoutIf(Assembler::Overflow, ins->snapshot());
 }
 
 void
+CodeGeneratorARM::visitSubI64(LSubI64* lir)
+{
+    const LInt64Allocation lhs = lir->getInt64Operand(LSubI64::Lhs);
+    const LInt64Allocation rhs = lir->getInt64Operand(LSubI64::Rhs);
+    Register64 out = ToOutRegister64(lir);
+
+    masm.move64(ToRegister64(lhs), out);
+
+    if (IsConstant(rhs)) {
+        masm.sub64(Imm64(ToInt64(rhs)), out);
+        return;
+    }
+
+    masm.sub64(ToOperandOrRegister64(rhs), out);
+}
+
+void
 CodeGeneratorARM::visitMulI(LMulI* ins)
 {
     const LAllocation* lhs = ins->getOperand(0);
     const LAllocation* rhs = ins->getOperand(1);
     const LDefinition* dest = ins->getDef(0);
     MMul* mul = ins->mir();
     MOZ_ASSERT_IF(mul->mode() == MMul::Integer, !mul->canBeNegativeZero() && !mul->canOverflow());
 
@@ -416,16 +456,58 @@ CodeGeneratorARM::visitMulI(LMulI* ins)
             bailoutIf(Assembler::Signed, ins->snapshot());
 
             masm.bind(&done);
         }
     }
 }
 
 void
+CodeGeneratorARM::visitMulI64(LMulI64* lir)
+{
+    const LInt64Allocation lhs = lir->getInt64Operand(LMulI64::Lhs);
+    const LInt64Allocation rhs = lir->getInt64Operand(LMulI64::Rhs);
+    Register64 out = ToOutRegister64(lir);
+
+    masm.move64(ToRegister64(lhs), out);
+
+    if (IsConstant(rhs)) {
+        int64_t constant = ToInt64(rhs);
+        switch (constant) {
+          case -1:
+            masm.neg64(out);
+            return;
+          case 0:
+            masm.xor64(out, out);
+            return;
+          case 1:
+            // nop
+            return;
+          case 2:
+            masm.add64(out, out);
+            return;
+          default:
+            if (constant > 0) {
+                // Use shift if constant is power of 2.
+                int32_t shift = mozilla::FloorLog2(constant);
+                if (int64_t(1) << shift == constant) {
+                    masm.lshift64(Imm32(shift), out);
+                    return;
+                }
+            }
+            Register temp = ToTempRegisterOrInvalid(lir->temp());
+            masm.mul64(Imm64(constant), out, temp);
+        }
+    } else {
+        Register temp = ToTempRegisterOrInvalid(lir->temp());
+        masm.mul64(ToOperandOrRegister64(rhs), out, temp);
+    }
+}
+
+void
 CodeGeneratorARM::divICommon(MDiv* mir, Register lhs, Register rhs, Register output,
                              LSnapshot* snapshot, Label& done)
 {
     if (mir->canBeNegativeOverflow()) {
         // Handle INT32_MIN / -1;
         // The integer division will give INT32_MIN, but we want -(double)INT32_MIN.
 
         // Sets EQ if lhs == INT32_MIN.
@@ -898,30 +980,26 @@ CodeGeneratorARM::visitUrshD(LUrshD* ins
 }
 
 void
 CodeGeneratorARM::visitClzI(LClzI* ins)
 {
     Register input = ToRegister(ins->input());
     Register output = ToRegister(ins->output());
 
-    masm.ma_clz(input, output);
+    masm.clz32(input, output, /* knownNotZero = */ false);
 }
 
 void
 CodeGeneratorARM::visitCtzI(LCtzI* ins)
 {
     Register input = ToRegister(ins->input());
     Register output = ToRegister(ins->output());
-    ScratchRegisterScope scratch(masm);
-
-    masm.ma_rsb(input, Imm32(0), scratch, SetCC);
-    masm.ma_and(input, scratch, input);
-    masm.ma_clz(input, output);
-    masm.ma_rsb(input, Imm32(0x1f), output, LeaveCC, Assembler::NotEqual);
+
+    masm.ctz32(input, output, /* knownNotZero = */ false);
 }
 
 void
 CodeGeneratorARM::visitPopcntI(LPopcntI* ins)
 {
     Register input = ToRegister(ins->input());
     Register output = ToRegister(ins->output());
 
@@ -1532,16 +1610,27 @@ void
 CodeGeneratorARM::visitNotI(LNotI* ins)
 {
     // It is hard to optimize !x, so just do it the basic way for now.
     masm.ma_cmp(ToRegister(ins->input()), Imm32(0));
     masm.emitSet(Assembler::Equal, ToRegister(ins->output()));
 }
 
 void
+CodeGeneratorARM::visitNotI64(LNotI64* lir)
+{
+    Register64 input = ToRegister64(lir->getInt64Operand(0));
+    Register output = ToRegister(lir->output());
+
+    masm.ma_orr(input.low, input.high, output);
+    masm.ma_cmp(output, Imm32(0));
+    masm.emitSet(Assembler::Equal, output);
+}
+
+void
 CodeGeneratorARM::visitNotD(LNotD* ins)
 {
     // Since this operation is not, we want to set a bit if the double is
     // falsey, which means 0.0, -0.0 or NaN. When comparing with 0, an input of
     // 0 will set the Z bit (30) and NaN will set the V bit (28) of the APSR.
     FloatRegister opd = ToFloatRegister(ins->input());
     Register dest = ToRegister(ins->output());
 
@@ -2090,22 +2179,22 @@ CodeGeneratorARM::visitAsmReinterpret(LA
       case MIRType::Int64:
         MOZ_CRASH("not handled by this LIR opcode");
       default:
         MOZ_CRASH("unexpected AsmReinterpret");
     }
 }
 
 void
-CodeGeneratorARM::visitAsmJSCall(LAsmJSCall* ins)
+CodeGeneratorARM::emitAsmJSCall(LAsmJSCallBase* ins)
 {
     MAsmJSCall* mir = ins->mir();
 
     if (UseHardFpABI() || mir->callee().which() != MAsmJSCall::Callee::Builtin) {
-        emitAsmJSCall(ins);
+        emitAsmJSCallBase(ins);
         return;
     }
 
     // The soft ABI passes floating point arguments in GPRs. Since basically
     // nothing is set up to handle this, the values are placed in the
     // corresponding VFP registers, then transferred to GPRs immediately
     // before the call. The mapping is sN <-> rN, where double registers
     // can be treated as their two component single registers.
@@ -2119,31 +2208,43 @@ CodeGeneratorARM::visitAsmJSCall(LAsmJSC
                 masm.ma_vxfer(fr, Register::FromCode(srcId), Register::FromCode(srcId + 1));
             } else {
                 uint32_t srcId = fr.id();
                 masm.ma_vxfer(fr, Register::FromCode(srcId));
             }
         }
     }
 
-    emitAsmJSCall(ins);
+    emitAsmJSCallBase(ins);
 
     switch (mir->type()) {
       case MIRType::Double:
         masm.ma_vxfer(r0, r1, d0);
         break;
       case MIRType::Float32:
         masm.as_vxfer(r0, InvalidReg, VFPRegister(d0).singleOverlay(), Assembler::CoreToFloat);
         break;
       default:
         break;
     }
 }
 
 void
+CodeGeneratorARM::visitAsmJSCall(LAsmJSCall* ins)
+{
+    emitAsmJSCall(ins);
+}
+
+void
+CodeGeneratorARM::visitAsmJSCallI64(LAsmJSCallI64* ins)
+{
+    emitAsmJSCall(ins);
+}
+
+void
 CodeGeneratorARM::visitAsmJSLoadHeap(LAsmJSLoadHeap* ins)
 {
     const MAsmJSLoadHeap* mir = ins->mir();
     bool isSigned;
     int size;
     bool isFloat = false;
     switch (mir->accessType()) {
       case Scalar::Int8:    isSigned = true;  size =  8; break;
@@ -2236,101 +2337,151 @@ CodeGeneratorARM::visitWasmBoundsCheck(L
         masm.append(wasm::BoundsCheck(cmpOffset));
         masm.as_b(&ok2, Assembler::BelowOrEqual);
         masm.assumeUnreachable("Redundant bounds check failed!");
         masm.bind(&ok2);
 #endif
     }
 }
 
+template <typename T>
 void
-CodeGeneratorARM::visitWasmLoad(LWasmLoad* lir)
+CodeGeneratorARM::emitWasmLoad(T* lir)
 {
     const MWasmLoad* mir = lir->mir();
 
     MOZ_ASSERT(!mir->barrierBefore() && !mir->barrierAfter(), "atomics NYI");
 
     uint32_t offset = mir->offset();
     if (offset > INT32_MAX) {
         // This is unreachable because of bounds checks.
         masm.breakpoint();
         return;
     }
 
     Register ptr = ToRegister(lir->ptr());
-    AnyRegister output = ToAnyRegister(lir->output());
+    Scalar::Type type = mir->accessType();
 
     // Maybe add the offset.
-    if (offset) {
+    if (offset || type == Scalar::Int64) {
         Register ptrPlusOffset = ToRegister(lir->ptrCopy());
         masm.ma_add(Imm32(offset), ptrPlusOffset);
         ptr = ptrPlusOffset;
     } else {
         MOZ_ASSERT(lir->ptrCopy()->isBogusTemp());
     }
 
-    Scalar::Type type = mir->accessType();
-    bool isSigned = type == Scalar::Int8 || type == Scalar::Int16 || type == Scalar::Int32;
-    bool isFloat = output.isFloat();
-
+    bool isSigned = type == Scalar::Int8 || type == Scalar::Int16 || type == Scalar::Int32 ||
+                    type == Scalar::Int64;
     unsigned byteSize = mir->byteSize();
 
-    if (isFloat) {
-        MOZ_ASSERT((byteSize == 4) == output.fpu().isSingle());
-        ScratchRegisterScope scratch(masm);
-        masm.ma_add(HeapReg, ptr, scratch);
-        masm.ma_vldr(Address(scratch, 0), output.fpu());
+    if (mir->type() == MIRType::Int64) {
+        Register64 output = ToOutRegister64(lir);
+        if (type == Scalar::Int64) {
+            MOZ_ASSERT(INT64LOW_OFFSET == 0);
+            masm.ma_dataTransferN(IsLoad, 32, /* signed = */ false, HeapReg, ptr, output.low);
+            masm.ma_add(Imm32(INT64HIGH_OFFSET), ptr);
+            masm.ma_dataTransferN(IsLoad, 32, isSigned, HeapReg, ptr, output.high);
+        } else {
+            masm.ma_dataTransferN(IsLoad, byteSize * 8, isSigned, HeapReg, ptr, output.low);
+            if (isSigned)
+                masm.ma_asr(Imm32(31), output.low, output.high);
+            else
+                masm.ma_mov(Imm32(0), output.high);
+        }
     } else {
-        masm.ma_dataTransferN(IsLoad, byteSize * 8, isSigned, HeapReg, ptr, output.gpr());
+        AnyRegister output = ToAnyRegister(lir->output());
+        bool isFloat = output.isFloat();
+        if (isFloat) {
+            MOZ_ASSERT((byteSize == 4) == output.fpu().isSingle());
+            ScratchRegisterScope scratch(masm);
+            masm.ma_add(HeapReg, ptr, scratch);
+            masm.ma_vldr(Address(scratch, 0), output.fpu());
+        } else {
+            masm.ma_dataTransferN(IsLoad, byteSize * 8, isSigned, HeapReg, ptr, output.gpr());
+        }
     }
 }
 
 void
-CodeGeneratorARM::visitWasmStore(LWasmStore* lir)
+CodeGeneratorARM::visitWasmLoad(LWasmLoad* lir)
+{
+    emitWasmLoad(lir);
+}
+
+void
+CodeGeneratorARM::visitWasmLoadI64(LWasmLoadI64* lir)
+{
+    emitWasmLoad(lir);
+}
+
+template <typename T>
+void
+CodeGeneratorARM::emitWasmStore(T* lir)
 {
     const MWasmStore* mir = lir->mir();
 
     MOZ_ASSERT(!mir->barrierBefore() && !mir->barrierAfter(), "atomics NYI");
 
     uint32_t offset = mir->offset();
     if (offset > INT32_MAX) {
         // This is unreachable because of bounds checks.
         masm.breakpoint();
         return;
     }
 
     Register ptr = ToRegister(lir->ptr());
+    unsigned byteSize = mir->byteSize();
+    Scalar::Type type = mir->accessType();
 
     // Maybe add the offset.
-    if (offset) {
+    if (offset || type == Scalar::Int64) {
         Register ptrPlusOffset = ToRegister(lir->ptrCopy());
         masm.ma_add(Imm32(offset), ptrPlusOffset);
         ptr = ptrPlusOffset;
     } else {
         MOZ_ASSERT(lir->ptrCopy()->isBogusTemp());
     }
 
-    AnyRegister value = ToAnyRegister(lir->value());
-    unsigned byteSize = mir->byteSize();
-    Scalar::Type type = mir->accessType();
-
-    if (value.isFloat()) {
-        FloatRegister val = value.fpu();
-        MOZ_ASSERT((byteSize == 4) == val.isSingle());
-        ScratchRegisterScope scratch(masm);
-        masm.ma_add(HeapReg, ptr, scratch);
-        masm.ma_vstr(val, Address(scratch, 0));
+    if (type == Scalar::Int64) {
+        MOZ_ASSERT(INT64LOW_OFFSET == 0);
+
+        Register64 value = ToRegister64(lir->getInt64Operand(lir->ValueIndex));
+        masm.ma_dataTransferN(IsStore, 32 /* bits */, /* signed */ false, HeapReg, ptr, value.low);
+        masm.ma_add(Imm32(INT64HIGH_OFFSET), ptr);
+        masm.ma_dataTransferN(IsStore, 32 /* bits */, /* signed */ true, HeapReg, ptr, value.high);
     } else {
-        bool isSigned = type == Scalar::Uint32 || type == Scalar::Int32; // see AsmJSStoreHeap;
-        Register val = value.gpr();
-        masm.ma_dataTransferN(IsStore, 8 * byteSize /* bits */, isSigned, HeapReg, ptr, val);
+        AnyRegister value = ToAnyRegister(lir->getOperand(lir->ValueIndex));
+        if (value.isFloat()) {
+            FloatRegister val = value.fpu();
+            MOZ_ASSERT((byteSize == 4) == val.isSingle());
+            ScratchRegisterScope scratch(masm);
+            masm.ma_add(HeapReg, ptr, scratch);
+            masm.ma_vstr(val, Address(scratch, 0));
+        } else {
+            bool isSigned = type == Scalar::Uint32 || type == Scalar::Int32; // see AsmJSStoreHeap;
+            Register val = value.gpr();
+            masm.ma_dataTransferN(IsStore, 8 * byteSize /* bits */, isSigned, HeapReg, ptr, val);
+        }
     }
 }
 
 void
+CodeGeneratorARM::visitWasmStore(LWasmStore* lir)
+{
+    emitWasmStore(lir);
+}
+
+void
+CodeGeneratorARM::visitWasmStoreI64(LWasmStoreI64* lir)
+{
+    emitWasmStore(lir);
+}
+
+void
 CodeGeneratorARM::visitAsmJSStoreHeap(LAsmJSStoreHeap* ins)
 {
     const MAsmJSStoreHeap* mir = ins->mir();
     bool isSigned;
     int size;
     bool isFloat = false;
     switch (mir->accessType()) {
       case Scalar::Int8:
@@ -2561,17 +2712,16 @@ CodeGeneratorARM::visitAsmJSAtomicBinopC
 }
 
 void
 CodeGeneratorARM::visitAsmJSPassStackArg(LAsmJSPassStackArg* ins)
 {
     const MAsmJSPassStackArg* mir = ins->mir();
     Address dst(StackPointer, mir->spOffset());
     if (ins->arg()->isConstant()) {
-        //masm.as_bkpt();
         masm.ma_storeImm(Imm32(ToInt32(ins->arg())), dst);
     } else {
         if (ins->arg()->isGeneralReg())
             masm.ma_str(ToRegister(ins->arg()), dst);
         else
             masm.ma_vstr(ToFloatRegister(ins->arg()), dst);
     }
 }
@@ -2730,16 +2880,28 @@ CodeGeneratorARM::visitWasmLoadGlobalVar
         masm.ma_vldr(Address(GlobalReg, addr), vd.singleOverlay());
     } else {
         MOZ_ASSERT(mir->type() == MIRType::Double);
         masm.ma_vldr(Address(GlobalReg, addr), ToFloatRegister(ins->output()));
     }
 }
 
 void
+CodeGeneratorARM::visitWasmLoadGlobalVarI64(LWasmLoadGlobalVarI64* ins)
+{
+    const MWasmLoadGlobalVar* mir = ins->mir();
+    unsigned addr = mir->globalDataOffset() - AsmJSGlobalRegBias;
+    MOZ_ASSERT(mir->type() == MIRType::Int64);
+    Register64 output = ToOutRegister64(ins);
+
+    masm.ma_dtr(IsLoad, GlobalReg, Imm32(addr + INT64LOW_OFFSET), output.low);
+    masm.ma_dtr(IsLoad, GlobalReg, Imm32(addr + INT64HIGH_OFFSET), output.high);
+}
+
+void
 CodeGeneratorARM::visitWasmStoreGlobalVar(LWasmStoreGlobalVar* ins)
 {
     const MWasmStoreGlobalVar* mir = ins->mir();
     MIRType type = mir->value()->type();
 
     unsigned addr = mir->globalDataOffset() - AsmJSGlobalRegBias;
     if (type == MIRType::Int32) {
         masm.ma_dtr(IsStore, GlobalReg, Imm32(addr), ToRegister(ins->value()));
@@ -2748,16 +2910,28 @@ CodeGeneratorARM::visitWasmStoreGlobalVa
         masm.ma_vstr(vd.singleOverlay(), Address(GlobalReg, addr));
     } else {
         MOZ_ASSERT(type == MIRType::Double);
         masm.ma_vstr(ToFloatRegister(ins->value()), Address(GlobalReg, addr));
     }
 }
 
 void
+CodeGeneratorARM::visitWasmStoreGlobalVarI64(LWasmStoreGlobalVarI64* ins)
+{
+    const MWasmStoreGlobalVar* mir = ins->mir();
+    unsigned addr = mir->globalDataOffset() - AsmJSGlobalRegBias;
+    MOZ_ASSERT (mir->value()->type() == MIRType::Int64);
+    Register64 input = ToRegister64(ins->value());
+
+    masm.ma_dtr(IsStore, GlobalReg, Imm32(addr + INT64LOW_OFFSET), input.low);
+    masm.ma_dtr(IsStore, GlobalReg, Imm32(addr + INT64HIGH_OFFSET), input.high);
+}
+
+void
 CodeGeneratorARM::visitNegI(LNegI* ins)
 {
     Register input = ToRegister(ins->input());
     masm.ma_neg(input, ToRegister(ins->output()));
 }
 
 void
 CodeGeneratorARM::visitNegD(LNegD* ins)
@@ -2866,16 +3040,55 @@ CodeGeneratorARM::visitWasmTruncateToInt
     masm.ma_cmp(output, Imm32(INT32_MAX));
     masm.ma_cmp(output, Imm32(INT32_MIN), Assembler::NotEqual);
     masm.ma_b(ool->entry(), Assembler::Equal);
 
     masm.bind(ool->rejoin());
 }
 
 void
+CodeGeneratorARM::visitWasmTruncateToInt64(LWasmTruncateToInt64* lir)
+{
+    FloatRegister input = ToFloatRegister(lir->input());
+    FloatRegister inputDouble = input;
+    Register64 output = ToOutRegister64(lir);
+
+    MWasmTruncateToInt64* mir = lir->mir();
+    MIRType fromType = mir->input()->type();
+
+    auto* ool = new(alloc()) OutOfLineWasmTruncateCheck(mir, input);
+    addOutOfLineCode(ool, mir);
+
+    ScratchDoubleScope scratchScope(masm);
+    if (fromType == MIRType::Float32) {
+        inputDouble = ScratchDoubleReg;
+        masm.convertFloat32ToDouble(input, inputDouble);
+    }
+
+    masm.Push(input);
+
+    masm.setupUnalignedABICall(output.high);
+    masm.passABIArg(inputDouble, MoveOp::DOUBLE);
+    if (lir->mir()->isUnsigned())
+        masm.callWithABI(wasm::SymbolicAddress::TruncateDoubleToUint64);
+    else
+        masm.callWithABI(wasm::SymbolicAddress::TruncateDoubleToInt64);
+
+    masm.Pop(input);
+
+    masm.ma_cmp(output.high, Imm32(0x80000000));
+    masm.ma_cmp(output.low, Imm32(0x00000000), Assembler::Equal);
+    masm.ma_b(ool->entry(), Assembler::Equal);
+
+    masm.bind(ool->rejoin());
+
+    MOZ_ASSERT(ReturnReg64 == output);
+}
+
+void
 CodeGeneratorARM::visitOutOfLineWasmTruncateCheck(OutOfLineWasmTruncateCheck* ool)
 {
     MIRType fromType = ool->fromType();
     FloatRegister input = ool->input();
 
     ScratchDoubleScope scratchScope(masm);
     FloatRegister scratch;
 
@@ -2886,63 +3099,111 @@ CodeGeneratorARM::visitOutOfLineWasmTrun
     else if (fromType == MIRType::Float32)
         masm.branchFloat(Assembler::DoubleUnordered, input, input, &inputIsNaN);
     else
         MOZ_CRASH("unexpected type in visitOutOfLineWasmTruncateCheck");
 
     // Handle special values.
     Label fail;
 
+    // By default test for the following inputs and bail:
+    // signed:   ] -Inf, INTXX_MIN - 1.0 ] and [ INTXX_MAX + 1.0 : +Inf [
+    // unsigned: ] -Inf, -1.0 ] and [ UINTXX_MAX + 1.0 : +Inf [
+    // Note: we cannot always represent those exact values. As a result
+    // this changes the actual comparison a bit.
     double minValue, maxValue;
-    if (ool->isUnsigned()) {
-        minValue = -1;
-        maxValue = double(UINT32_MAX) + 1.0;
+    Assembler::DoubleCondition minCond = Assembler::DoubleLessThanOrEqual;
+    Assembler::DoubleCondition maxCond = Assembler::DoubleGreaterThanOrEqual;
+    if (ool->toType() == MIRType::Int64) {
+        if (ool->isUnsigned()) {
+            minValue = -1;
+            maxValue = double(UINT64_MAX) + 1.0;
+        } else {
+            // In the float32/double range there exists no value between
+            // INT64_MIN and INT64_MIN - 1.0. Making INT64_MIN the lower-bound.
+            minValue = double(INT64_MIN);
+            minCond = Assembler::DoubleLessThan;
+            maxValue = double(INT64_MAX) + 1.0;
+        }
     } else {
-        minValue = double(INT32_MIN) - 1.0;
-        maxValue = double(INT32_MAX) + 1.0;
+        if (ool->isUnsigned()) {
+            minValue = -1;
+            maxValue = double(UINT32_MAX) + 1.0;
+        } else {
+            if (fromType == MIRType::Float32) {
+                // In the float32 range there exists no value between
+                // INT32_MIN and INT32_MIN - 1.0. Making INT32_MIN the lower-bound.
+                minValue = double(INT32_MIN);
+                minCond = Assembler::DoubleLessThan;
+            } else {
+                minValue = double(INT32_MIN) - 1.0;
+            }
+            maxValue = double(INT32_MAX) + 1.0;
+        }
     }
 
     if (fromType == MIRType::Double) {
         scratch = scratchScope.doubleOverlay();
         masm.loadConstantDouble(minValue, scratch);
-        masm.branchDouble(Assembler::DoubleLessThanOrEqual, input, scratch, &fail);
+        masm.branchDouble(minCond, input, scratch, &fail);
 
         masm.loadConstantDouble(maxValue, scratch);
-        masm.branchDouble(Assembler::DoubleGreaterThanOrEqual, input, scratch, &fail);
+        masm.branchDouble(maxCond, input, scratch, &fail);
     } else {
         MOZ_ASSERT(fromType == MIRType::Float32);
         scratch = scratchScope.singleOverlay();
-
-        // For int32, float(minValue) rounds to INT32_MIN, we want to fail when
-        // input < float(minValue).
-        // For uint32, float(minValue) == -1, we want to fail when input <= -1.
-        auto condition = minValue == -1.0
-                         ? Assembler::DoubleLessThanOrEqual
-                         : Assembler::DoubleLessThan;
-
         masm.loadConstantFloat32(float(minValue), scratch);
-        masm.branchFloat(condition, input, scratch, &fail);
-
-        // maxValue is exactly represented in both cases.
+        masm.branchFloat(minCond, input, scratch, &fail);
+
         masm.loadConstantFloat32(float(maxValue), scratch);
-        masm.branchFloat(Assembler::DoubleGreaterThanOrEqual, input, scratch, &fail);
+        masm.branchFloat(maxCond, input, scratch, &fail);
     }
 
     // We had an actual correct value, get back to where we were.
     masm.ma_b(ool->rejoin());
 
     // Handle errors.
     masm.bind(&fail);
     masm.jump(wasm::JumpTarget::IntegerOverflow);
 
     masm.bind(&inputIsNaN);
     masm.jump(wasm::JumpTarget::InvalidConversionToInteger);
 }
 
 void
+CodeGeneratorARM::visitInt64ToFloatingPointCall(LInt64ToFloatingPointCall* lir)
+{
+    Register64 input = ToRegister64(lir->getInt64Operand(0));
+    FloatRegister output = ToFloatRegister(lir->output());
+
+    MInt64ToFloatingPoint* mir = lir->mir();
+    MIRType toType = mir->type();
+
+    // We are free to clobber all registers, since this is a call instruction.
+    AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
+    regs.take(input.low);
+    regs.take(input.high);
+    Register temp = regs.takeAny();
+
+    masm.setupUnalignedABICall(temp);
+    masm.passABIArg(input.high);
+    masm.passABIArg(input.low);
+    if (lir->mir()->isUnsigned())
+        masm.callWithABI(wasm::SymbolicAddress::Uint64ToFloatingPoint, MoveOp::DOUBLE);
+    else
+        masm.callWithABI(wasm::SymbolicAddress::Int64ToFloatingPoint, MoveOp::DOUBLE);
+
+    MOZ_ASSERT_IF(toType == MIRType::Double, output == ReturnDoubleReg);
+    if (toType == MIRType::Float32) {
+        MOZ_ASSERT(output == ReturnFloat32Reg);
+        masm.convertDoubleToFloat32(ReturnDoubleReg, output);
+    }
+}
+
+void
 CodeGeneratorARM::visitCopySignF(LCopySignF* ins)
 {
     FloatRegister lhs = ToFloatRegister(ins->getOperand(0));
     FloatRegister rhs = ToFloatRegister(ins->getOperand(1));
     FloatRegister output = ToFloatRegister(ins->getDef(0));
 
     Register lhsi = ToRegister(ins->getTemp(0));
     Register rhsi = ToRegister(ins->getTemp(1));
@@ -2984,8 +3245,386 @@ CodeGeneratorARM::visitCopySignD(LCopySi
 
     // Combine.
     masm.ma_orr(lhsi, rhsi, rhsi);
 
     // Reconstruct the output.
     masm.as_vxfer(lhsi, InvalidReg, lhs, Assembler::FloatToCore, Assembler::Always, 0);
     masm.ma_vxfer(lhsi, rhsi, output);
 }
+
+void
+CodeGeneratorARM::visitWrapInt64ToInt32(LWrapInt64ToInt32* lir)
+{
+    const LInt64Allocation& input = lir->getInt64Operand(0);
+    Register output = ToRegister(lir->output());
+
+    if (lir->mir()->bottomHalf())
+        masm.move32(ToRegister(input.low()), output);
+    else
+        masm.move32(ToRegister(input.high()), output);
+}
+
+void
+CodeGeneratorARM::visitExtendInt32ToInt64(LExtendInt32ToInt64* lir)
+{
+    Register64 output = ToOutRegister64(lir);
+    MOZ_ASSERT(ToRegister(lir->input()) == output.low);
+
+    if (lir->mir()->isUnsigned())
+        masm.ma_mov(Imm32(0), output.high);
+    else
+        masm.ma_asr(Imm32(31), output.low, output.high);
+}
+
+void
+CodeGeneratorARM::visitDivOrModI64(LDivOrModI64* lir)
+{
+    Register64 lhs = ToRegister64(lir->getInt64Operand(LDivOrModI64::Lhs));
+    Register64 rhs = ToRegister64(lir->getInt64Operand(LDivOrModI64::Rhs));
+    Register64 output = ToOutRegister64(lir);
+
+    MOZ_ASSERT(output == ReturnReg64);
+
+    // All inputs are useAtStart for a call instruction. As a result we cannot
+    // ask for a non-aliasing temp. Using the following to get such a temp.
+    AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
+    regs.take(lhs.low);
+    regs.take(lhs.high);
+    regs.take(rhs.low);
+    regs.take(rhs.high);
+    Register temp = regs.takeAny();
+
+    Label done;
+
+    // Handle divide by zero.
+    if (lir->canBeDivideByZero())
+        masm.branchTest64(Assembler::Zero, rhs, rhs, temp, wasm::JumpTarget::IntegerDivideByZero);
+
+    // Handle an integer overflow exception from INT64_MIN / -1.
+    if (lir->canBeNegativeOverflow()) {
+        Label notmin;
+        masm.branch64(Assembler::NotEqual, lhs, Imm64(INT64_MIN), &notmin);
+        masm.branch64(Assembler::NotEqual, rhs, Imm64(-1), &notmin);
+        if (lir->mir()->isMod())
+            masm.xor64(output, output);
+        else
+            masm.jump(wasm::JumpTarget::IntegerOverflow);
+        masm.jump(&done);
+        masm.bind(&notmin);
+    }
+
+    masm.setupUnalignedABICall(temp);
+    masm.passABIArg(lhs.high);
+    masm.passABIArg(lhs.low);
+    masm.passABIArg(rhs.high);
+    masm.passABIArg(rhs.low);
+
+    MOZ_ASSERT(gen->compilingAsmJS());
+    if (lir->mir()->isMod())
+        masm.callWithABI(wasm::SymbolicAddress::ModI64);
+    else
+        masm.callWithABI(wasm::SymbolicAddress::DivI64);
+
+    MOZ_ASSERT(ReturnReg64 == output);
+
+    masm.bind(&done);
+}
+
+void
+CodeGeneratorARM::visitUDivOrModI64(LUDivOrModI64* lir)
+{
+    Register64 lhs = ToRegister64(lir->getInt64Operand(LDivOrModI64::Lhs));
+    Register64 rhs = ToRegister64(lir->getInt64Operand(LDivOrModI64::Rhs));
+
+    MOZ_ASSERT(ToOutRegister64(lir) == ReturnReg64);
+
+    // All inputs are useAtStart for a call instruction. As a result we cannot
+    // ask for a non-aliasing temp. Using the following to get such a temp.
+    AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
+    regs.take(lhs.low);
+    regs.take(lhs.high);
+    regs.take(rhs.low);
+    regs.take(rhs.high);
+    Register temp = regs.takeAny();
+
+    // Prevent divide by zero.
+    if (lir->canBeDivideByZero())
+        masm.branchTest64(Assembler::Zero, rhs, rhs, temp, wasm::JumpTarget::IntegerDivideByZero);
+
+    masm.setupUnalignedABICall(temp);
+    masm.passABIArg(lhs.high);
+    masm.passABIArg(lhs.low);
+    masm.passABIArg(rhs.high);
+    masm.passABIArg(rhs.low);
+
+    MOZ_ASSERT(gen->compilingAsmJS());
+    if (lir->mir()->isMod())
+        masm.callWithABI(wasm::SymbolicAddress::UModI64);
+    else
+        masm.callWithABI(wasm::SymbolicAddress::UDivI64);
+}
+
+void
+CodeGeneratorARM::visitCompareI64(LCompareI64* lir)
+{
+    MCompare* mir = lir->mir();
+    MOZ_ASSERT(mir->compareType() == MCompare::Compare_Int64 ||
+               mir->compareType() == MCompare::Compare_UInt64);
+
+    const LInt64Allocation lhs = lir->getInt64Operand(LCompareI64::Lhs);
+    const LInt64Allocation rhs = lir->getInt64Operand(LCompareI64::Rhs);
+    Register64 lhsRegs = ToRegister64(lhs);
+    Register output = ToRegister(lir->output());
+
+    bool isSigned = mir->compareType() == MCompare::Compare_Int64;
+    Assembler::Condition condition = JSOpToCondition(lir->jsop(), isSigned);
+    Label done;
+
+    masm.move32(Imm32(1), output);
+
+    if (IsConstant(rhs)) {
+        Imm64 imm = Imm64(ToInt64(rhs));
+        masm.branch64(condition, lhsRegs, imm, &done);
+    } else {
+        Register64 rhsRegs = ToRegister64(rhs);
+        masm.branch64(condition, lhsRegs, rhsRegs, &done);
+    }
+
+    masm.move32(Imm32(0), output);
+    masm.bind(&done);
+}
+
+void
+CodeGeneratorARM::visitCompareI64AndBranch(LCompareI64AndBranch* lir)
+{
+    MCompare* mir = lir->cmpMir();
+    MOZ_ASSERT(mir->compareType() == MCompare::Compare_Int64 ||
+               mir->compareType() == MCompare::Compare_UInt64);
+
+    const LInt64Allocation lhs = lir->getInt64Operand(LCompareI64::Lhs);
+    const LInt64Allocation rhs = lir->getInt64Operand(LCompareI64::Rhs);
+    Register64 lhsRegs = ToRegister64(lhs);
+
+    bool isSigned = mir->compareType() == MCompare::Compare_Int64;
+    Assembler::Condition condition = JSOpToCondition(lir->jsop(), isSigned);
+
+    Label* trueLabel = getJumpLabelForBranch(lir->ifTrue());
+    Label* falseLabel = getJumpLabelForBranch(lir->ifFalse());
+
+    if (isNextBlock(lir->ifFalse()->lir())) {
+        falseLabel = nullptr;
+    } else if (isNextBlock(lir->ifTrue()->lir())) {
+        condition = Assembler::InvertCondition(condition);
+        trueLabel = falseLabel;
+        falseLabel = nullptr;
+    }
+
+    if (IsConstant(rhs)) {
+        Imm64 imm = Imm64(ToInt64(rhs));
+        masm.branch64(condition, lhsRegs, imm, trueLabel, falseLabel);
+    } else {
+        Register64 rhsRegs = ToRegister64(rhs);
+        masm.branch64(condition, lhsRegs, rhsRegs, trueLabel, falseLabel);
+    }
+}
+
+void
+CodeGeneratorARM::visitShiftI64(LShiftI64* lir)
+{
+    const LInt64Allocation lhs = lir->getInt64Operand(LShiftI64::Lhs);
+    LAllocation* rhs = lir->getOperand(LShiftI64::Rhs);
+    Register64 out = ToOutRegister64(lir);
+
+    masm.move64(ToRegister64(lhs), out);
+
+    if (rhs->isConstant()) {
+        int32_t shift = int32_t(rhs->toConstant()->toInt64() & 0x3F);
+        switch (lir->bitop()) {
+          case JSOP_LSH:
+            if (shift)
+                masm.lshift64(Imm32(shift), out);
+            break;
+          case JSOP_RSH:
+            if (shift)
+                masm.rshift64Arithmetic(Imm32(shift), out);
+            break;
+          case JSOP_URSH:
+            if (shift)
+                masm.rshift64(Imm32(shift), out);
+            break;
+          default:
+            MOZ_CRASH("Unexpected shift op");
+        }
+        return;
+    }
+
+    switch (lir->bitop()) {
+      case JSOP_LSH:
+        masm.lshift64(ToRegister(rhs), out);
+        break;
+      case JSOP_RSH:
+        masm.rshift64Arithmetic(ToRegister(rhs), out);
+        break;
+      case JSOP_URSH:
+        masm.rshift64(ToRegister(rhs), out);
+        break;
+      default:
+        MOZ_CRASH("Unexpected shift op");
+    }
+}
+
+void
+CodeGeneratorARM::visitBitOpI64(LBitOpI64* lir)
+{
+    const LInt64Allocation lhs = lir->getInt64Operand(LBitOpI64::Lhs);
+    const LInt64Allocation rhs = lir->getInt64Operand(LBitOpI64::Rhs);
+    Register64 out = ToOutRegister64(lir);
+
+    masm.move64(ToRegister64(lhs), out);
+
+    switch (lir->bitop()) {
+      case JSOP_BITOR:
+        if (IsConstant(rhs))
+            masm.or64(Imm64(ToInt64(rhs)), out);
+        else
+            masm.or64(ToOperandOrRegister64(rhs), out);
+        break;
+      case JSOP_BITXOR:
+        if (IsConstant(rhs))
+            masm.xor64(Imm64(ToInt64(rhs)), out);
+        else
+            masm.xor64(ToOperandOrRegister64(rhs), out);
+        break;
+      case JSOP_BITAND:
+        if (IsConstant(rhs))
+            masm.and64(Imm64(ToInt64(rhs)), out);
+        else
+            masm.and64(ToOperandOrRegister64(rhs), out);
+        break;
+      default:
+        MOZ_CRASH("unexpected binary opcode");
+    }
+}
+
+void
+CodeGeneratorARM::visitRotateI64(LRotateI64* lir)
+{
+    MRotate* mir = lir->mir();
+    LAllocation* count = lir->count();
+
+    Register64 input = ToRegister64(lir->input());
+    Register64 output = ToOutRegister64(lir);
+    Register temp = ToTempRegisterOrInvalid(lir->temp());
+
+    masm.move64(input, output);
+
+    if (count->isConstant()) {
+        int32_t c = int32_t(count->toConstant()->toInt64() & 0x3F);
+        if (!c) {
+            masm.move64(input, output);
+            return;
+        }
+        if (mir->isLeftRotate())
+            masm.rotateLeft64(Imm32(c), output, output, temp);
+        else
+            masm.rotateRight64(Imm32(c), output, output, temp);
+    } else {
+        if (mir->isLeftRotate())
+            masm.rotateLeft64(ToRegister(count), output, output, temp);
+        else
+            masm.rotateRight64(ToRegister(count), output, output, temp);
+    }
+}
+
+void
+CodeGeneratorARM::visitAsmJSPassStackArgI64(LAsmJSPassStackArgI64* ins)
+{
+    const MAsmJSPassStackArg* mir = ins->mir();
+    Address dst(StackPointer, mir->spOffset());
+    if (IsConstant(ins->arg()))
+        masm.store64(Imm64(ToInt64(ins->arg())), dst);
+    else
+        masm.store64(ToRegister64(ins->arg()), dst);
+}
+
+void
+CodeGeneratorARM::visitAsmSelectI64(LAsmSelectI64* lir)
+{
+    Register cond = ToRegister(lir->condExpr());
+    const LInt64Allocation trueExpr = lir->trueExpr();
+    const LInt64Allocation falseExpr = lir->falseExpr();
+    Register64 out = ToOutRegister64(lir);
+
+    masm.move64(ToRegister64(trueExpr), out);
+
+    masm.ma_cmp(cond, Imm32(0));
+    if (falseExpr.low().isRegister()) {
+        masm.ma_mov(ToRegister(falseExpr.low()), out.low, LeaveCC, Assembler::Equal);
+        masm.ma_mov(ToRegister(falseExpr.high()), out.high, LeaveCC, Assembler::Equal);
+    } else {
+        masm.ma_ldr(ToAddress(falseExpr.low()), out.low, Offset, Assembler::Equal);
+        masm.ma_ldr(ToAddress(falseExpr.high()), out.high, Offset, Assembler::Equal);
+    }
+}
+
+void
+CodeGeneratorARM::visitAsmReinterpretFromI64(LAsmReinterpretFromI64* lir)
+{
+    MOZ_ASSERT(lir->mir()->type() == MIRType::Double);
+    MOZ_ASSERT(lir->mir()->input()->type() == MIRType::Int64);
+    Register64 input = ToRegister64(lir->getInt64Operand(0));
+    FloatRegister output = ToFloatRegister(lir->output());
+
+    masm.ma_vxfer(input.low, input.high, output);
+}
+
+void
+CodeGeneratorARM::visitAsmReinterpretToI64(LAsmReinterpretToI64* lir)
+{
+    MOZ_ASSERT(lir->mir()->type() == MIRType::Int64);
+    MOZ_ASSERT(lir->mir()->input()->type() == MIRType::Double);
+    FloatRegister input = ToFloatRegister(lir->getOperand(0));
+    Register64 output = ToOutRegister64(lir);
+
+    masm.ma_vxfer(input, output.low, output.high);
+}
+
+void
+CodeGeneratorARM::visitPopcntI64(LPopcntI64* lir)
+{
+    Register64 input = ToRegister64(lir->getInt64Operand(0));
+    Register64 output = ToOutRegister64(lir);
+    Register temp = ToRegister(lir->getTemp(0));
+
+    masm.popcnt64(input, output, temp);
+}
+
+void
+CodeGeneratorARM::visitClzI64(LClzI64* lir)
+{
+    Register64 input = ToRegister64(lir->getInt64Operand(0));
+    Register64 output = ToOutRegister64(lir);
+
+    masm.clz64(input, output.low);
+    masm.move32(Imm32(0), output.high);
+}
+
+void
+CodeGeneratorARM::visitCtzI64(LCtzI64* lir)
+{
+    Register64 input = ToRegister64(lir->getInt64Operand(0));
+    Register64 output = ToOutRegister64(lir);
+
+    masm.ctz64(input, output.low);
+    masm.move32(Imm32(0), output.high);
+}
+
+void
+CodeGeneratorARM::visitTestI64AndBranch(LTestI64AndBranch* lir)
+{
+    Register64 input = ToRegister64(lir->getInt64Operand(0));
+
+    masm.ma_cmp(input.high, Imm32(0));
+    jumpToBlock(lir->ifTrue(), Assembler::NonZero);
+    masm.ma_cmp(input.low, Imm32(0));
+    emitBranch(Assembler::NonZero, lir->ifTrue(), lir->ifFalse());
+}
--- a/js/src/jit/arm/CodeGenerator-arm.h
+++ b/js/src/jit/arm/CodeGenerator-arm.h
@@ -91,16 +91,21 @@ class CodeGeneratorARM : public CodeGene
     {
         MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
         masm.cmpPtr(reg, ImmWord(0));
         emitBranch(cond, ifTrue, ifFalse);
     }
 
     void emitTableSwitchDispatch(MTableSwitch* mir, Register index, Register base);
 
+    template <typename T>
+    void emitWasmLoad(T* ins);
+    template <typename T>
+    void emitWasmStore(T* ins);
+
   public:
     // Instruction visitors.
     virtual void visitMinMaxD(LMinMaxD* ins);
     virtual void visitMinMaxF(LMinMaxF* ins);
     virtual void visitAbsD(LAbsD* ins);
     virtual void visitAbsF(LAbsF* ins);
     virtual void visitSqrtD(LSqrtD* ins);
     virtual void visitSqrtF(LSqrtF* ins);
@@ -115,16 +120,17 @@ class CodeGeneratorARM : public CodeGene
     virtual void visitSoftDivI(LSoftDivI* ins);
     virtual void visitDivPowTwoI(LDivPowTwoI* ins);
     virtual void visitModI(LModI* ins);
     virtual void visitSoftModI(LSoftModI* ins);
     virtual void visitModPowTwoI(LModPowTwoI* ins);
     virtual void visitModMaskI(LModMaskI* ins);
     virtual void visitPowHalfD(LPowHalfD* ins);
     virtual void visitShiftI(LShiftI* ins);
+    virtual void visitShiftI64(LShiftI64* ins);
     virtual void visitUrshD(LUrshD* ins);
 
     virtual void visitClzI(LClzI* ins);
     virtual void visitCtzI(LCtzI* ins);
     virtual void visitPopcntI(LPopcntI* ins);
 
     virtual void visitTestIAndBranch(LTestIAndBranch* test);
     virtual void visitCompare(LCompare* comp);
@@ -152,25 +158,50 @@ class CodeGeneratorARM : public CodeGene
     virtual void visitFloorF(LFloorF* lir);
     virtual void visitCeil(LCeil* lir);
     virtual void visitCeilF(LCeilF* lir);
     virtual void visitRound(LRound* lir);
     virtual void visitRoundF(LRoundF* lir);
     virtual void visitTruncateDToInt32(LTruncateDToInt32* ins);
     virtual void visitTruncateFToInt32(LTruncateFToInt32* ins);
 
+    virtual void visitWrapInt64ToInt32(LWrapInt64ToInt32* lir);
+    virtual void visitExtendInt32ToInt64(LExtendInt32ToInt64* lir);
+    virtual void visitAddI64(LAddI64* lir);
+    virtual void visitSubI64(LSubI64* lir);
+    virtual void visitMulI64(LMulI64* lir);
+    virtual void visitDivOrModI64(LDivOrModI64* lir);
+    virtual void visitUDivOrModI64(LUDivOrModI64* lir);
+    virtual void visitCompareI64(LCompareI64* lir);
+    virtual void visitCompareI64AndBranch(LCompareI64AndBranch* lir);
+    virtual void visitBitOpI64(LBitOpI64* lir);
+    virtual void visitRotateI64(LRotateI64* lir);
+    virtual void visitAsmJSPassStackArgI64(LAsmJSPassStackArgI64* lir);
+    virtual void visitAsmSelectI64(LAsmSelectI64* lir);
+    virtual void visitAsmReinterpretFromI64(LAsmReinterpretFromI64* lir);
+    virtual void visitAsmReinterpretToI64(LAsmReinterpretToI64* lir);
+    virtual void visitPopcntI64(LPopcntI64* ins);
+    virtual void visitClzI64(LClzI64* ins);
+    virtual void visitCtzI64(LCtzI64* ins);
+    virtual void visitNotI64(LNotI64* ins);
+    virtual void visitWasmTruncateToInt64(LWasmTruncateToInt64* ins);
+    virtual void visitInt64ToFloatingPointCall(LInt64ToFloatingPointCall* lir);
+    virtual void visitTestI64AndBranch(LTestI64AndBranch* lir);
+
     // Out of line visitors.
     void visitOutOfLineBailout(OutOfLineBailout* ool);
     void visitOutOfLineTableSwitch(OutOfLineTableSwitch* ool);
 
   protected:
     ValueOperand ToValue(LInstruction* ins, size_t pos);
     ValueOperand ToOutValue(LInstruction* ins);
     ValueOperand ToTempValue(LInstruction* ins, size_t pos);
 
+    Register64 ToOperandOrRegister64(const LInt64Allocation input);
+
     // Functions for LTestVAndBranch.
     Register splitTagForTest(const ValueOperand& value);
 
     void divICommon(MDiv* mir, Register lhs, Register rhs, Register output, LSnapshot* snapshot,
                     Label& done);
     void modICommon(MMod* mir, Register lhs, Register rhs, Register output, LSnapshot* snapshot,
                     Label& done);
 
@@ -197,22 +228,28 @@ class CodeGeneratorARM : public CodeGene
     void visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic* ins);
     void visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic* ins);
     void visitAtomicTypedArrayElementBinop(LAtomicTypedArrayElementBinop* lir);
     void visitAtomicTypedArrayElementBinopForEffect(LAtomicTypedArrayElementBinopForEffect* lir);
     void visitCompareExchangeTypedArrayElement(LCompareExchangeTypedArrayElement* lir);
     void visitAtomicExchangeTypedArrayElement(LAtomicExchangeTypedArrayElement* lir);
     void visitAsmSelect(LAsmSelect* ins);
     void visitAsmReinterpret(LAsmReinterpret* ins);
+    void emitAsmJSCall(LAsmJSCallBase* ins);
     void visitAsmJSCall(LAsmJSCall* ins);
+    void visitAsmJSCallI64(LAsmJSCallI64* ins);
     void visitWasmBoundsCheck(LWasmBoundsCheck* ins);
     void visitWasmLoad(LWasmLoad* ins);
+    void visitWasmLoadI64(LWasmLoadI64* ins);
     void visitWasmStore(LWasmStore* ins);
+    void visitWasmStoreI64(LWasmStoreI64* ins);
     void visitWasmLoadGlobalVar(LWasmLoadGlobalVar* ins);
+    void visitWasmLoadGlobalVarI64(LWasmLoadGlobalVarI64* ins);
     void visitWasmStoreGlobalVar(LWasmStoreGlobalVar* ins);
+    void visitWasmStoreGlobalVarI64(LWasmStoreGlobalVarI64* ins);
     void visitAsmJSLoadHeap(LAsmJSLoadHeap* ins);
     void visitAsmJSStoreHeap(LAsmJSStoreHeap* ins);
     void visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins);
     void visitAsmJSCompareExchangeCallout(LAsmJSCompareExchangeCallout* ins);
     void visitAsmJSAtomicExchangeHeap(LAsmJSAtomicExchangeHeap* ins);
     void visitAsmJSAtomicExchangeCallout(LAsmJSAtomicExchangeCallout* ins);
     void visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap* ins);
     void visitAsmJSAtomicBinopHeapForEffect(LAsmJSAtomicBinopHeapForEffect* ins);
--- a/js/src/jit/arm/LIR-arm.h
+++ b/js/src/jit/arm/LIR-arm.h
@@ -112,16 +112,76 @@ class LDivI : public LBinaryMath<1>
         setTemp(0, temp);
     }
 
     MDiv* mir() const {
         return mir_->toDiv();
     }
 };
 
+class LDivOrModI64 : public LCallInstructionHelper<INT64_PIECES, INT64_PIECES*2, 0>
+{
+  public:
+    LIR_HEADER(DivOrModI64)
+
+    static const size_t Lhs = 0;
+    static const size_t Rhs = INT64_PIECES;
+
+    LDivOrModI64(const LInt64Allocation& lhs, const LInt64Allocation& rhs)
+    {
+        setInt64Operand(Lhs, lhs);
+        setInt64Operand(Rhs, rhs);
+    }
+
+    MBinaryArithInstruction* mir() const {
+        MOZ_ASSERT(mir_->isDiv() || mir_->isMod());
+        return static_cast<MBinaryArithInstruction*>(mir_);
+    }
+    bool canBeDivideByZero() const {
+        if (mir_->isMod())
+            return mir_->toMod()->canBeDivideByZero();
+        return mir_->toDiv()->canBeDivideByZero();
+    }
+    bool canBeNegativeOverflow() const {
+        if (mir_->isMod())
+            return mir_->toMod()->canBeNegativeDividend();
+        return mir_->toDiv()->canBeNegativeOverflow();
+    }
+};
+
+class LUDivOrModI64 : public LCallInstructionHelper<INT64_PIECES, INT64_PIECES*2, 0>
+{
+  public:
+    LIR_HEADER(UDivOrModI64)
+
+    static const size_t Lhs = 0;
+    static const size_t Rhs = INT64_PIECES;
+
+    LUDivOrModI64(const LInt64Allocation& lhs, const LInt64Allocation& rhs)
+    {
+        setInt64Operand(Lhs, lhs);
+        setInt64Operand(Rhs, rhs);
+    }
+
+    MBinaryArithInstruction* mir() const {
+        MOZ_ASSERT(mir_->isDiv() || mir_->isMod());
+        return static_cast<MBinaryArithInstruction*>(mir_);
+    }
+    bool canBeDivideByZero() const {
+        if (mir_->isMod())
+            return mir_->toMod()->canBeDivideByZero();
+        return mir_->toDiv()->canBeDivideByZero();
+    }
+    bool canBeNegativeOverflow() const {
+        if (mir_->isMod())
+            return mir_->toMod()->canBeNegativeDividend();
+        return mir_->toDiv()->canBeNegativeOverflow();
+    }
+};
+
 // LSoftDivI is a software divide for ARM cores that don't support a hardware
 // divide instruction.
 //
 // It is implemented as a proper C function so it trashes r0, r1, r2 and r3.
 // The call also trashes lr, and has the ability to trash ip. The function also
 // takes two arguments (dividend in r0, divisor in r1). The LInstruction gets
 // encoded such that the divisor and dividend are passed in their apropriate
 // registers and end their life at the start of the instruction by the use of
@@ -476,12 +536,37 @@ class LAsmJSAtomicBinopCallout : public 
         return getOperan