Merge m-c to autoland, a=merge
authorWes Kocher <wkocher@mozilla.com>
Tue, 26 Jul 2016 16:54:33 -0700
changeset 348847 6bd2b7d2ebe3f81c513e4519812b2a8f3d1ca5a0
parent 348846 6e79dedcd251514c129c87c998bf44ee63d197f6 (current diff)
parent 348831 462dc6b44adb4573e8ce6b0dd688c206ebd516f7 (diff)
child 348848 acec32a2a1762b73b7dbce4c87fde3bd05562919
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
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to autoland, a=merge
dom/tests/mochitest/beacon/beacon-cors-redirect-handler.sjs
dom/tests/mochitest/beacon/test_beaconCORSRedirect.html
dom/tests/mochitest/beacon/test_beaconPreflight.html
dom/tests/mochitest/beacon/test_beaconPreflightFailure.html
--- a/devtools/client/inspector/layout/test/browser.ini
+++ b/devtools/client/inspector/layout/test/browser.ini
@@ -19,11 +19,12 @@ support-files =
 [browser_layout_editablemodel_bluronclick.js]
 [browser_layout_editablemodel_border.js]
 [browser_layout_editablemodel_stylerules.js]
 [browser_layout_guides.js]
 [browser_layout_rotate-labels-on-sides.js]
 [browser_layout_sync.js]
 [browser_layout_tooltips.js]
 [browser_layout_update-after-navigation.js]
-[browser_layout_update-after-reload.js]
+# [browser_layout_update-after-reload.js]
+# Disabled for too many intermittent failures (bug 1287745)
 # [browser_layout_update-in-iframes.js]
 # Bug 1020038 layout-view updates for iframe elements changes
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -10779,17 +10779,18 @@ nsDocShell::DoURILoad(nsIURI* aURI,
       return NS_OK;
     }
   }
 
   bool isSandBoxed = mSandboxFlags & SANDBOXED_ORIGIN;
   // only inherit if we have a triggeringPrincipal
   bool inherit = false;
 
-  // Get triggeringPrincipal.  This code should be updated by bug 1181370.
+  // Getting the right triggeringPrincipal needs to be updated and is only
+  // ready for use once bug 1182569 landed.
   // Until then, we cannot rely on the triggeringPrincipal for TYPE_DOCUMENT
   // or TYPE_SUBDOCUMENT loads.  Notice the triggeringPrincipal falls back to
   // systemPrincipal below.
   nsCOMPtr<nsIPrincipal> triggeringPrincipal = do_QueryInterface(aOwner);
   if (triggeringPrincipal) {
     inherit = nsContentUtils::ChannelShouldInheritPrincipal(
       triggeringPrincipal,
       aURI,
new file mode 100644
--- /dev/null
+++ b/docshell/test/navigation/file_triggeringprincipal_frame_1.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML>
+<html>
+<head><meta charset="utf-8"></head>
+<body>
+<b>Frame 1</b><br/>
+<a href="#"" id="testlink" onclick="parent.frames[1].frames[0].location='http://test2.mochi.test:8888/tests/docshell/test/navigation/file_triggeringprincipal_subframe_nav.html'">click me</a>
+
+<script type="application/javascript">
+  // make sure to set document.domain to the same domain as the subframe
+  window.onload = function() {
+    document.domain = 'mochi.test';
+  };
+  window.addEventListener('message', receiveMessage, false);
+  function receiveMessage(event) {
+    // make sure to get the right start command, otherwise
+    // let the parent know and fail the test
+    if (event.data.start !== 'startTest') {
+      window.removeEventListener("message", receiveMessage, false);
+      window.parent.postMessage({triggeringPrincipalURI: 'false'}, '*');
+    }
+    // click the link to navigate the subframe
+    document.getElementById('testlink').click();
+  }
+</script>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/docshell/test/navigation/file_triggeringprincipal_frame_2.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML>
+<html>
+<head><meta charset="utf-8"></head>
+<body>
+<b>Frame 2</b><br/>
+<iframe src="http://test2.mochi.test:8888/tests/docshell/test/navigation/file_triggeringprincipal_subframe.html"></iframe>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/docshell/test/navigation/file_triggeringprincipal_subframe.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+<head><meta charset='utf-8'></head>
+<body>
+<b>Sub Frame 2</b><br/>
+<script type='application/javascript'>
+  // make sure to set document.domain to same domain as frame 1
+  window.onload = function() {
+    document.domain = 'mochi.test';
+    //   let Frame 1 know that we are ready to run the test
+    window.parent.parent.frames[0].postMessage({start: 'startTest'}, '*');
+  };
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/docshell/test/navigation/file_triggeringprincipal_subframe_nav.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<html>
+<head><meta charset="utf-8"></head>
+<body onload="checkResults()">
+<b>Sub Frame 2 Navigated</b><br/>
+
+<script type='application/javascript'>
+  function checkResults() {
+  	// query the uri of the loadingPrincipal and the TriggeringPrincipal and pass
+  	// that information on to the parent for verification.
+    var channel = SpecialPowers.wrap(document).docShell.currentDocumentChannel;
+    var triggeringPrincipalURI = channel.loadInfo.triggeringPrincipal.URI.asciiSpec;
+    var loadingPrincipalURI = channel.loadInfo.loadingPrincipal.URI.asciiSpec;
+    var referrerURI = document.referrer;
+    window.parent.parent.postMessage({triggeringPrincipalURI,
+    	                              loadingPrincipalURI,
+    	                              referrerURI}, '*');
+  }
+</script>
+</body>
+</html>
--- a/docshell/test/navigation/mochitest.ini
+++ b/docshell/test/navigation/mochitest.ini
@@ -17,16 +17,20 @@ support-files =
   frame1.html
   frame2.html
   frame3.html
   goback.html
   iframe.html
   navigate.html
   open.html
   parent.html
+  file_triggeringprincipal_frame_1.html
+  file_triggeringprincipal_frame_2.html
+  file_triggeringprincipal_subframe.html
+  file_triggeringprincipal_subframe_nav.html
 
 [test_bug13871.html]
 skip-if = buildapp == 'b2g' || buildapp == 'mulet' || toolkit == 'android' #RANDOM # Bug 1136180 disabled on B2G Desktop and Mulet for intermittent failures
 [test_bug270414.html]
 skip-if = buildapp == 'b2g' || buildapp == 'mulet' || toolkit == "android" # Bug 1136180 disabled on B2G Desktop and Mulet for intermittent failures
 [test_bug278916.html]
 [test_bug279495.html]
 [test_bug344861.html]
@@ -43,8 +47,9 @@ skip-if = buildapp == 'b2g'
 [test_popup-navigates-children.html]
 skip-if = buildapp == 'b2g' # b2g(Needs multiple window.open support, also uses docshelltreenode) b2g-debug(Needs multiple window.open support, also uses docshelltreenode) b2g-desktop(Needs multiple window.open support, also uses docshelltreenode)
 [test_reserved.html]
 skip-if = (buildapp == 'b2g' && debug) || (toolkit == 'android') || (debug && e10s) #too slow on Android 4.3 aws only; bug 1030403; bug 1263213 for debug e10s
 [test_sessionhistory.html]
 skip-if = (buildapp == 'b2g' && debug) || toolkit == 'android' #RANDOM # b2g-debug(Perma-orange on debug emulator builds) b2g-desktop(Bug 931116, b2g desktop specific, initial triage)
 [test_sibling-matching-parent.html]
 [test_sibling-off-domain.html]
+[test_triggeringprincipal_frame_nav.html]
new file mode 100644
--- /dev/null
+++ b/docshell/test/navigation/test_triggeringprincipal_frame_nav.html
@@ -0,0 +1,69 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Bug 1181370 - Test triggeringPrincipal for iframe navigations</title>
+  <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe style="width:100%;" id="testframe1"></iframe>
+<iframe style="width:100%;" id="testframe2"></iframe>
+
+<script class="testbody" type="text/javascript">
+
+/* Description of the test:
+ *
+ * +------------------------------------+
+ * |  +----------+   +--------------+   |
+ * |  | Frame 1  |   | Frame 2      |   |
+ * |  +----------+   |              |   |
+ * |                 | +----------+ |   |
+ * |                 | | Subframe | |   |
+ * |                 | +----------+ |   |
+ * |                 +--------------+   |
+ * +------------------------------------+
+ *
+ * Frame1:   test1.mochi.test
+ * Frame2:   test2.mochi.test
+ * Subframe: test2.mochi.test
+ *
+ * (*) Frame1 and Subframe set their document.domain to mochi.test
+ * (*) Frame1 navigates the Subframe
+ * (*) TriggeringPrincipal for the Subframe navigation should be
+ *     ==> test1.mochi.test
+ * (*) LoadingPrincipal for the Subframe navigation should be
+ *     ==> test2.mochi.test
+ */
+
+const BASEURL1 = "http://test1.mochi.test:8888/tests/docshell/test/navigation/";
+const BASEURL2 = "http://test2.mochi.test:8888/tests/docshell/test/navigation/";
+const TRIGGERINGPRINCIPALURI = BASEURL1 + "file_triggeringprincipal_frame_1.html";
+const LOADINGPRINCIPALURI = BASEURL2 + "file_triggeringprincipal_frame_2.html";
+
+SimpleTest.waitForExplicitFinish();
+
+window.addEventListener("message", receiveMessage, false);
+
+function receiveMessage(event) {
+  is(event.data.triggeringPrincipalURI, TRIGGERINGPRINCIPALURI,
+  	 "TriggeringPrincipal should be the navigating iframe (Frame 1)");
+  is(event.data.loadingPrincipalURI, LOADINGPRINCIPALURI,
+  	 "LoadingPrincipal should be the enclosing iframe (Frame 2)");
+  is(event.data.referrerURI, TRIGGERINGPRINCIPALURI,
+     "Referrer and TriggeringPrincipal should be identical (Frame 1)");
+
+  window.removeEventListener("message", receiveMessage, false);
+  SimpleTest.finish();
+}
+
+var frame1 = document.getElementById("testframe1");
+frame1.src = BASEURL1 + "file_triggeringprincipal_frame_1.html";
+
+var frame2 = document.getElementById("testframe2");
+frame2.src = BASEURL2 + "file_triggeringprincipal_frame_2.html";
+
+</script>
+</body>
+</html>
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -696,18 +696,18 @@ Element::Scroll(const CSSIntPoint& aScro
     sf->ScrollToCSSPixels(aScroll, scrollMode);
   }
 }
 
 void
 Element::Scroll(double aXScroll, double aYScroll)
 {
   // Convert -Inf, Inf, and NaN to 0; otherwise, convert by C-style cast.
-  CSSIntPoint scrollPos(mozilla::ToZeroIfNonfinite(aXScroll),
-                        mozilla::ToZeroIfNonfinite(aYScroll));
+  auto scrollPos = CSSIntPoint::Truncate(mozilla::ToZeroIfNonfinite(aXScroll),
+                                         mozilla::ToZeroIfNonfinite(aYScroll));
 
   Scroll(scrollPos, ScrollOptions());
 }
 
 void
 Element::Scroll(const ScrollToOptions& aOptions)
 {
   nsIScrollableFrame *sf = GetScrollFrame();
@@ -736,18 +736,18 @@ Element::ScrollTo(const ScrollToOptions&
 }
 
 void
 Element::ScrollBy(double aXScrollDif, double aYScrollDif)
 {
   nsIScrollableFrame *sf = GetScrollFrame();
   if (sf) {
     CSSIntPoint scrollPos = sf->GetScrollPositionCSSPixels();
-    scrollPos += CSSIntPoint(mozilla::ToZeroIfNonfinite(aXScrollDif),
-                             mozilla::ToZeroIfNonfinite(aYScrollDif));
+    scrollPos += CSSIntPoint::Truncate(mozilla::ToZeroIfNonfinite(aXScrollDif),
+                                       mozilla::ToZeroIfNonfinite(aYScrollDif));
     Scroll(scrollPos, ScrollOptions());
   }
 }
 
 void
 Element::ScrollBy(const ScrollToOptions& aOptions)
 {
   nsIScrollableFrame *sf = GetScrollFrame();
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -1327,22 +1327,27 @@ Navigator::SendBeacon(const nsAString& a
   if (NS_FAILED(rv) || isDataScheme) {
     aRv.Throw(NS_ERROR_CONTENT_BLOCKED);
     return false;
   }
 
   nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL |
     nsIChannel::LOAD_CLASSIFY_URI;
 
+  // No need to use CORS for sendBeacon unless it's a BLOB
+  nsSecurityFlags securityFlags = (!aData.IsNull() && aData.Value().IsBlob())
+   ? nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS
+   : nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS;
+  securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE;
+
   nsCOMPtr<nsIChannel> channel;
   rv = NS_NewChannel(getter_AddRefs(channel),
                      uri,
                      doc,
-                     nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS |
-                       nsILoadInfo::SEC_COOKIES_INCLUDE,
+                     securityFlags,
                      nsIContentPolicy::TYPE_BEACON,
                      nullptr, // aLoadGroup
                      nullptr, // aCallbacks
                      loadFlags);
 
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return false;
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -2803,17 +2803,17 @@ nsDOMWindowUtils::WrapDOMFile(nsIFile *a
 static bool
 CheckLeafLayers(Layer* aLayer, const nsIntPoint& aOffset, nsIntRegion* aCoveredRegion)
 {
   gfx::Matrix transform;
   if (!aLayer->GetTransform().Is2D(&transform) ||
       transform.HasNonIntegerTranslation())
     return false;
   transform.NudgeToIntegers();
-  nsIntPoint offset = aOffset + nsIntPoint(transform._31, transform._32);
+  IntPoint offset = aOffset + IntPoint::Truncate(transform._31, transform._32);
 
   Layer* child = aLayer->GetFirstChild();
   if (child) {
     while (child) {
       if (!CheckLeafLayers(child, offset, aCoveredRegion))
         return false;
       child = child->GetNextSibling();
     }
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -7719,27 +7719,27 @@ nsGlobalWindow::GetTopWindowRoot()
   nsCOMPtr<nsPIWindowRoot> window = do_QueryInterface(piWin->GetChromeEventHandler());
   return window.forget();
 }
 
 void
 nsGlobalWindow::Scroll(double aXScroll, double aYScroll)
 {
   // Convert -Inf, Inf, and NaN to 0; otherwise, convert by C-style cast.
-  CSSIntPoint scrollPos(mozilla::ToZeroIfNonfinite(aXScroll),
-                        mozilla::ToZeroIfNonfinite(aYScroll));
+  auto scrollPos = CSSIntPoint::Truncate(mozilla::ToZeroIfNonfinite(aXScroll),
+                                         mozilla::ToZeroIfNonfinite(aYScroll));
   ScrollTo(scrollPos, ScrollOptions());
 }
 
 void
 nsGlobalWindow::ScrollTo(double aXScroll, double aYScroll)
 {
   // Convert -Inf, Inf, and NaN to 0; otherwise, convert by C-style cast.
-  CSSIntPoint scrollPos(mozilla::ToZeroIfNonfinite(aXScroll),
-                        mozilla::ToZeroIfNonfinite(aYScroll));
+  auto scrollPos = CSSIntPoint::Truncate(mozilla::ToZeroIfNonfinite(aXScroll),
+                                         mozilla::ToZeroIfNonfinite(aYScroll));
   ScrollTo(scrollPos, ScrollOptions());
 }
 
 void
 nsGlobalWindow::ScrollTo(const ScrollToOptions& aOptions)
 {
   FlushPendingNotifications(Flush_Layout);
   nsIScrollableFrame *sf = GetScrollFrame();
@@ -7798,18 +7798,18 @@ nsGlobalWindow::ScrollTo(const CSSIntPoi
 void
 nsGlobalWindow::ScrollBy(double aXScrollDif, double aYScrollDif)
 {
   FlushPendingNotifications(Flush_Layout);
   nsIScrollableFrame *sf = GetScrollFrame();
 
   if (sf) {
     // Convert -Inf, Inf, and NaN to 0; otherwise, convert by C-style cast.
-    CSSIntPoint scrollDif(mozilla::ToZeroIfNonfinite(aXScrollDif),
-                          mozilla::ToZeroIfNonfinite(aYScrollDif));
+    auto scrollDif = CSSIntPoint::Truncate(mozilla::ToZeroIfNonfinite(aXScrollDif),
+                                           mozilla::ToZeroIfNonfinite(aYScrollDif));
     // It seems like it would make more sense for ScrollBy to use
     // SMOOTH mode, but tests seem to depend on the synchronous behaviour.
     // Perhaps Web content does too.
     ScrollTo(sf->GetScrollPositionCSSPixels() + scrollDif, ScrollOptions());
   }
 }
 
 void
--- a/dom/base/nsLocation.cpp
+++ b/dom/base/nsLocation.cpp
@@ -128,56 +128,68 @@ nsLocation::CheckURL(nsIURI* aURI, nsIDo
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Make the load's referrer reflect changes to the document's URI caused by
     // push/replaceState, if possible.  First, get the document corresponding to
     // fp.  If the document's original URI (i.e. its URI before
     // push/replaceState) matches the principal's URI, use the document's
     // current URI as the referrer.  If they don't match, use the principal's
     // URI.
+    //
+    // The triggering principal for this load should be the principal of the
+    // incumbent document (which matches where the referrer information is
+    // coming from) when there is an incumbent document, and the subject
+    // principal otherwise.  Note that the URI in the triggering principal
+    // may not match the referrer URI in various cases, notably including
+    // the cases when the incumbent document's document URI was modified
+    // after the document was loaded.
 
-    nsCOMPtr<nsIDocument> doc;
-    nsCOMPtr<nsIURI> docOriginalURI, docCurrentURI, principalURI;
     nsCOMPtr<nsPIDOMWindowInner> incumbent =
       do_QueryInterface(mozilla::dom::GetIncumbentGlobal());
-    if (incumbent) {
-      doc = incumbent->GetDoc();
-    }
+    nsCOMPtr<nsIDocument> doc = incumbent ? incumbent->GetDoc() : nullptr;
+
     if (doc) {
+      nsCOMPtr<nsIURI> docOriginalURI, docCurrentURI, principalURI;
       docOriginalURI = doc->GetOriginalURI();
       docCurrentURI = doc->GetDocumentURI();
       rv = doc->NodePrincipal()->GetURI(getter_AddRefs(principalURI));
       NS_ENSURE_SUCCESS(rv, rv);
+
+      owner = doc->NodePrincipal();
       referrerPolicy = doc->GetReferrerPolicy();
-    }
-
-    bool urisEqual = false;
-    if (docOriginalURI && docCurrentURI && principalURI) {
-      principalURI->Equals(docOriginalURI, &urisEqual);
-    }
 
-    if (urisEqual) {
-      sourceURI = docCurrentURI;
-    }
-    else {
-      // Use principalURI as long as it is not an nsNullPrincipalURI.
-      // We could add a method such as GetReferrerURI to principals to make this
-      // cleaner, but given that we need to start using Source Browsing Context
-      // for referrer (see Bug 960639) this may be wasted effort at this stage.
-      if (principalURI) {
-        bool isNullPrincipalScheme;
-        rv = principalURI->SchemeIs(NS_NULLPRINCIPAL_SCHEME,
-                                    &isNullPrincipalScheme);
-        if (NS_SUCCEEDED(rv) && !isNullPrincipalScheme) {
-          sourceURI = principalURI;
+      bool urisEqual = false;
+      if (docOriginalURI && docCurrentURI && principalURI) {
+        principalURI->Equals(docOriginalURI, &urisEqual);
+      }
+      if (urisEqual) {
+        sourceURI = docCurrentURI;
+      }
+      else {
+        // Use principalURI as long as it is not an nsNullPrincipalURI.
+        // We could add a method such as GetReferrerURI to principals to make this
+        // cleaner, but given that we need to start using Source Browsing Context
+        // for referrer (see Bug 960639) this may be wasted effort at this stage.
+        if (principalURI) {
+          bool isNullPrincipalScheme;
+          rv = principalURI->SchemeIs(NS_NULLPRINCIPAL_SCHEME,
+                                     &isNullPrincipalScheme);
+          if (NS_SUCCEEDED(rv) && !isNullPrincipalScheme) {
+            sourceURI = principalURI;
+          }
         }
       }
     }
-
-    owner = nsContentUtils::SubjectPrincipal();
+    else {
+      // No document; determine triggeringPrincipal by quering the
+      // subjectPrincipal, wich is the principal of the current JS
+      // compartment, or a null principal in case there is no
+      // compartment yet.
+      owner = nsContentUtils::SubjectPrincipal();
+    }
   }
 
   // Create load info
   nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
   docShell->CreateLoadInfo(getter_AddRefs(loadInfo));
   NS_ENSURE_TRUE(loadInfo, NS_ERROR_FAILURE);
 
   loadInfo->SetOwner(owner);
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -4815,18 +4815,18 @@ CanvasRenderingContext2D::DrawDirectlyTo
   }
   gfxSize contextScale(contextMatrix.ScaleFactors(true));
 
   // Scale the dest rect to include the context scale.
   aDest.Scale(contextScale.width, contextScale.height);
 
   // Scale the image size to the dest rect, and adjust the source rect to match.
   gfxSize scale(aDest.width / aSrc.width, aDest.height / aSrc.height);
-  nsIntSize scaledImageSize(std::ceil(aImgSize.width * scale.width),
-                            std::ceil(aImgSize.height * scale.height));
+  IntSize scaledImageSize = IntSize::Ceil(aImgSize.width * scale.width,
+                                          aImgSize.height * scale.height);
   aSrc.Scale(scale.width, scale.height);
 
   // We're wrapping tempTarget's (our) DrawTarget here, so we need to restore
   // the matrix even though this is a temp gfxContext.
   AutoRestoreTransform autoRestoreTransform(mTarget);
 
   RefPtr<gfxContext> context = gfxContext::CreateOrNull(tempTarget);
   if (!context) {
@@ -5040,17 +5040,17 @@ CanvasRenderingContext2D::DrawWindow(nsG
   {
     thebes = gfxContext::CreateOrNull(mTarget);
     MOZ_ASSERT(thebes); // already checked the draw target above
                         // (in SupportsAzureContentForDrawTarget)
     thebes->SetMatrix(gfxMatrix(matrix._11, matrix._12, matrix._21,
                                 matrix._22, matrix._31, matrix._32));
   } else {
     drawDT =
-      gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(IntSize(ceil(sw), ceil(sh)),
+      gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(IntSize::Ceil(sw, sh),
                                                                    SurfaceFormat::B8G8R8A8);
     if (!drawDT || !drawDT->IsValid()) {
       aError.Throw(NS_ERROR_FAILURE);
       return;
     }
 
     thebes = gfxContext::CreateOrNull(drawDT);
     MOZ_ASSERT(thebes); // alrady checked the draw target above
--- a/dom/media/raw/RawReader.cpp
+++ b/dom/media/raw/RawReader.cpp
@@ -55,20 +55,20 @@ nsresult RawReader::ReadMetadata(MediaIn
 
   if (mMetadata.aspectDenominator == 0 ||
       mMetadata.framerateDenominator == 0)
     return NS_ERROR_FAILURE; // Invalid data
 
   // Determine and verify frame display size.
   float pixelAspectRatio = static_cast<float>(mMetadata.aspectNumerator) /
                             mMetadata.aspectDenominator;
-  nsIntSize display(mMetadata.frameWidth, mMetadata.frameHeight);
+  nsIntSize display(uint32_t(mMetadata.frameWidth), uint32_t(mMetadata.frameHeight));
   ScaleDisplayByAspectRatio(display, pixelAspectRatio);
   mPicture = nsIntRect(0, 0, mMetadata.frameWidth, mMetadata.frameHeight);
-  nsIntSize frameSize(mMetadata.frameWidth, mMetadata.frameHeight);
+  nsIntSize frameSize(uint32_t(mMetadata.frameWidth), uint32_t(mMetadata.frameHeight));
   if (!IsValidVideoRegion(frameSize, mPicture, display)) {
     // Video track's frame sizes will overflow. Fail.
     return NS_ERROR_FAILURE;
   }
 
   mInfo.mVideo.mDisplay = display;
 
   mFrameRate = static_cast<float>(mMetadata.framerateNumerator) /
--- a/dom/plugins/base/nsPluginInstanceOwner.cpp
+++ b/dom/plugins/base/nsPluginInstanceOwner.cpp
@@ -176,17 +176,17 @@ AttachToContainerAsSurfaceTexture(ImageC
 
   mozilla::gl::AndroidSurfaceTexture* surfTex = instance->AsSurfaceTexture();
   if (!surfTex) {
     return;
   }
 
   RefPtr<Image> img = new SurfaceTextureImage(
     surfTex,
-    gfx::IntSize(rect.width, rect.height),
+    gfx::IntSize::Truncate(rect.width, rect.height),
     instance->OriginPos());
   *out_image = img;
 }
 #endif
 
 bool
 nsPluginInstanceOwner::NeedsScrollImageLayer()
 {
@@ -217,17 +217,17 @@ nsPluginInstanceOwner::GetImageContainer
     return nullptr;
 
   LayoutDeviceRect r = GetPluginRect();
 
   // NotifySize() causes Flash to do a bunch of stuff like ask for surfaces to render
   // into, set y-flip flags, etc, so we do this at the beginning.
   float resolution = mPluginFrame->PresContext()->PresShell()->GetCumulativeResolution();
   ScreenSize screenSize = (r * LayoutDeviceToScreenScale(resolution)).Size();
-  mInstance->NotifySize(nsIntSize(screenSize.width, screenSize.height));
+  mInstance->NotifySize(nsIntSize::Truncate(screenSize.width, screenSize.height));
 
   container = LayerManager::CreateImageContainer();
 
   // Try to get it as an EGLImage first.
   RefPtr<Image> img;
   AttachToContainerAsSurfaceTexture(container, mInstance, r, &img);
 
   if (img) {
@@ -1579,17 +1579,17 @@ nsPluginInstanceOwner::GetVideos(nsTArra
 
 already_AddRefed<ImageContainer>
 nsPluginInstanceOwner::GetImageContainerForVideo(nsNPAPIPluginInstance::VideoInfo* aVideoInfo)
 {
   RefPtr<ImageContainer> container = LayerManager::CreateImageContainer();
 
   RefPtr<Image> img = new SurfaceTextureImage(
     aVideoInfo->mSurfaceTexture,
-    gfx::IntSize(aVideoInfo->mDimensions.width, aVideoInfo->mDimensions.height),
+    gfx::IntSize::Truncate(aVideoInfo->mDimensions.width, aVideoInfo->mDimensions.height),
     gl::OriginPos::BottomLeft);
   container->SetCurrentImageInTransaction(img);
 
   return container.forget();
 }
 
 void nsPluginInstanceOwner::Invalidate() {
   NPRect rect;
--- a/dom/svg/SVGFETurbulenceElement.cpp
+++ b/dom/svg/SVGFETurbulenceElement.cpp
@@ -142,17 +142,17 @@ SVGFETurbulenceElement::GetPrimitiveDesc
   // depending on the filter primitive region.
   gfxRect firstPeriodInUserSpace(0, 0, 1 / fX, 1 / fY);
   gfxRect firstPeriodInFilterSpace = aInstance->UserSpaceToFilterSpace(firstPeriodInUserSpace);
   Size frequencyInFilterSpace(1 / firstPeriodInFilterSpace.width,
                               1 / firstPeriodInFilterSpace.height);
   gfxPoint offset = firstPeriodInFilterSpace.TopLeft();
 
   FilterPrimitiveDescription descr(PrimitiveType::Turbulence);
-  descr.Attributes().Set(eTurbulenceOffset, IntPoint(offset.x, offset.y));
+  descr.Attributes().Set(eTurbulenceOffset, IntPoint::Truncate(offset.x, offset.y));
   descr.Attributes().Set(eTurbulenceBaseFrequency, frequencyInFilterSpace);
   descr.Attributes().Set(eTurbulenceSeed, seed);
   descr.Attributes().Set(eTurbulenceNumOctaves, octaves);
   descr.Attributes().Set(eTurbulenceStitchable, stitch == SVG_STITCHTYPE_STITCH);
   descr.Attributes().Set(eTurbulenceType, type);
   return descr;
 }
 
--- a/dom/tests/mochitest/beacon/beacon-originheader-handler.sjs
+++ b/dom/tests/mochitest/beacon/beacon-originheader-handler.sjs
@@ -1,11 +1,11 @@
 /*
  * TestSever customized specifically for the needs of:
- * Bug 1080987 - navigator.sendBeacon() needs to sent origin header
+ * Bug 1280692 - navigator.sendBeacon() should not send origin header
  */
 
 function handleRequest(request, response)
 {
   response.setHeader("Cache-Control", "no-cache", false);
   response.setHeader("Content-Type", "text/plain", false);
 
   // case XHR-REQUEST: the xhr-request tries to query the
@@ -21,19 +21,26 @@ function handleRequest(request, response
     // otherwise wait for the beacon request
     response.processAsync();
     setObjectState("xhr-response", response);
     return;
   }
 
   // case BEACON-REQUEST: get the beacon header and
   // store the header on the server.
-  var header = request.getHeader("origin");
+  var header = "reset";
+  try {
+    header = request.getHeader("origin");
+  }
+  catch(e) {
+    header = "no-header";
+  }
   setState("originHeader", header);
 
+
   // if there is an xhr-request waiting, return the header now.
   getObjectState("xhr-response", function(xhrResponse) {
     if (!xhrResponse) {
       return;
     }
     setState("originHeader", "");
     xhrResponse.write(header);
     xhrResponse.finish();
rename from dom/tests/mochitest/beacon/beacon-cors-redirect-handler.sjs
rename to dom/tests/mochitest/beacon/beacon-redirect-handler.sjs
--- a/dom/tests/mochitest/beacon/beacon-cors-redirect-handler.sjs
+++ b/dom/tests/mochitest/beacon/beacon-redirect-handler.sjs
@@ -1,52 +1,47 @@
 /*
  * TestSever customized specifically for the needs of:
- * Bug 1111834 - sendBeacon() should not follow 30x redirect after preflight
+ * Bug 1280692 - sendBeacon() should follow 30x redirect
  *
  * Here is a sequence of the test:
- * [1] preflight channel (identified by the queryString 'beacon' and method 'OPTIONS')
- * [2] actual channel (identified by the queryString 'beacon') which gets redirected
- * [3] should never happen (the actual redirected request)
- * [4] xhr request (identified by the queryString 'verifyRedirectDidNotSucceed')
- *     which checks if the state was not changed from 'green' to 'red'. If the channel
+ * [1] sendBeacon (identified by the queryString 'beacon') which gets redirected
+ * [2] redirected sendBeacon (identified by the queryString 'redirected') which
+ *     updates the state idniciating that redirected sendBeacon succeeds.
+ * [3] xhr request (identified by the queryString 'verifyRedirectDidSucceed')
+ *     which checks if the state was not changed from 'reset' to 'gree'. If the channel
  *     woulnd't be blocked correctly the redirected channel would set the state to 'red'.
  *
  */
 
 function handleRequest(request, response)
 {
   response.setHeader("Cache-Control", "no-cache, must-revalidate", false);
 
-  // [Sequence 4]
-  if (request.queryString === "verifyRedirectDidNotSucceed") {
+  // [Sequence 3]
+  if (request.queryString === "verifyRedirectDidSucceed") {
     var redirectState = getState("redirectState");
     response.write(redirectState);
     return;
   }
 
-  var originHeader = request.getHeader("origin");
-  response.setHeader("Cache-Control", "no-cache, must-revalidate", false);
-  response.setHeader("Access-Control-Allow-Headers", "content-type", false);
-  response.setHeader("Access-Control-Allow-Methods", "POST, GET", false);
-  response.setHeader("Access-Control-Allow-Origin", originHeader, false);
-  response.setHeader("Access-Control-Allow-Credentials", "true", false);
-
-  // [Sequence 1,2]
+  // [Sequence 1]
   if (request.queryString === "beacon") {
-    setState("redirectState", "green");
-    // [1]
-    if (request.method == "OPTIONS") {
-      response.setStatusLine(null, 200, "OK");
-      return;
-    }
-    // [Sequence 2]
+    setState("redirectState", "reset");
     var newLocation =
-      "http://mochi.test:8888/tests/dom/tests/mochitest/beacon/beacon-cors-redirect-handler.sjs?redirected";
+      "http://mochi.test:8888/tests/dom/tests/mochitest/beacon/beacon-redirect-handler.sjs?redirected";
     response.setStatusLine("1.1", 302, "Found");
     response.setHeader("Location", newLocation, false);
     return;
   }
 
-  // [Sequence 3]
+  // [Sequence 2]
+  if (request.queryString === "redirected") {
+    setState("redirectState", "green");
+    response.setStatusLine(null, 200, "OK");
+    return;
+  }
+
+  // we should never get here, but just in case let's
+  // set the state to something unexpected
   setState("redirectState", "red");
   response.setStatusLine(null, 200, "OK");
 }
--- a/dom/tests/mochitest/beacon/mochitest.ini
+++ b/dom/tests/mochitest/beacon/mochitest.ini
@@ -1,16 +1,14 @@
 [DEFAULT]
 skip-if = buildapp == 'b2g'
 support-files = beacon-frame.html
                 beacon-handler.sjs
                 beacon-preflight-handler.sjs
                 beacon-originheader-handler.sjs
-                beacon-cors-redirect-handler.sjs
+                beacon-redirect-handler.sjs
 
 [test_beacon.html]
 [test_beaconFrame.html]
-[test_beaconPreflight.html]
-[test_beaconPreflightFailure.html]
 [test_beaconPreflightWithCustomContentType.html]
 [test_beaconContentPolicy.html]
 [test_beaconOriginHeader.html]
-[test_beaconCORSRedirect.html]
+[test_beaconRedirect.html]
--- a/dom/tests/mochitest/beacon/test_beaconOriginHeader.html
+++ b/dom/tests/mochitest/beacon/test_beaconOriginHeader.html
@@ -1,49 +1,52 @@
 <!DOCTYPE HTML>
 <html>
 <head>
-  <title>Bug 1080987 - navigator.sendBeacon() needs to sent origin header</title>
+  <title>Bug 1280692 - navigator.sendBeacon() should not send origin header</title>
   <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
   <p id="display"></p>
   <div id="content" style="visibility: hidden">
     <iframe style="width:100%;" id="testframe"></iframe>
   </div>
 
 <script class="testbody" type="text/javascript">
 
 SimpleTest.waitForExplicitFinish();
 
 const BEACON_URL = "http://example.com/tests/dom/tests/mochitest/beacon/beacon-originheader-handler.sjs";
-const ORIGIN_HEADER = "http://mochi.test:8888";
+// no origin header should be sent with sendBeacon request;
+// server returns any origin-header or 'no-header' if there is no header sent.
+const ORIGIN_HEADER = "no-header";
 
 /* Description of the test:
  *   We call sendBeacon() cross origin and make sure that the
- *   origin header is actually set in the request.
+ *   origin header is actually *not* set in the request.
  *
- * Since sendBeacon() does not expect any response, we are storing the
+ * Since sendBeacon() does not expect any response, we are storing any
  * header on the server (*.sjs) and use an XMLHttpRequest to actually
- * retrieve the header back from the server. We assert that the header
- * is indeed correct. Since sendBeacon() and also the XMLHttpRequest()
- * are performed in an asynchronous fashion, there is no guarantee that
- * the sendBeacon() is actually executed before the XMLHttpRequest().
+ * retrieve the potentially set header back from the server. We assert
+ * that the header is indeed *not* sent with the request. Since sendBeacon()
+ * and also the XMLHttpRequest() are performed in an asynchronous fashion,
+ * there is no guarantee that the sendBeacon() is actually executed before
+ * the XMLHttpRequest().
  * Hence the xhr-response might be processed asynchronously.
  */
 
 SpecialPowers.pushPrefEnv({'set': [["beacon.enabled", true]]}, runTest);
 
 function queryHeaderFromServer() {
   var xhr = new XMLHttpRequest();
   xhr.open("GET", "beacon-originheader-handler.sjs?queryheader", true);
   xhr.onload = function() {
-    is(xhr.responseText, ORIGIN_HEADER, "SendBeacon sends right origin header");
+    is(xhr.responseText, ORIGIN_HEADER, "SendBeacon should not send origin header");
     SimpleTest.finish();
   };
   xhr.onerror = function() {
     ok(false, "xhr request returned error");
     SimpleTest.finish();
   };
   xhr.send();
 }
deleted file mode 100644
--- a/dom/tests/mochitest/beacon/test_beaconPreflight.html
+++ /dev/null
@@ -1,56 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=936340
--->
-<head>
-  <title>Test for Bug 936340</title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-</head>
-<body>
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=936340">Mozilla Bug 936340</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-  
-</div>
-<pre id="test">
-<script class="testbody" type="text/javascript">
-
-var beaconUrl = "http://example.com/tests/dom/tests/mochitest/beacon/beacon-preflight-handler.sjs?beacon";
-
-var intervalID = null;
-
-function queryIfBeaconSucceeded() {
-  clearInterval(intervalID);
-  var xhr = new XMLHttpRequest();
-  xhr.open("GET", "beacon-preflight-handler.sjs?verify", true);
-  xhr.onload = function() {
-    is(xhr.responseText, "green", "SendBeacon should have succeeded after preflight!");
-    SimpleTest.finish();
-  };
-  xhr.onerror = function() {
-    ok(false, "xhr request returned error");
-    SimpleTest.finish();
-  };
-  xhr.send();
-}
-
-// not enabled by default yet.
-SimpleTest.waitForExplicitFinish();
-SpecialPowers.pushPrefEnv({'set': [["beacon.enabled", true]]}, beginTest);
-
-function beginTest() {
-  var abv = new Uint8Array([0,1,2,3]);
-  var sent = navigator.sendBeacon(beaconUrl, abv);
-  ok(sent, "sending the beacon should start successfully");
-
-  // we have to make sure sending the beacon did not fail, so
-  // we have to wait for 2 seconds before we can query the result.
-  intervalID = setInterval(queryIfBeaconSucceeded, 2000);
-}
-
-</script>
-</pre>
-</body>
-</html>
deleted file mode 100644
--- a/dom/tests/mochitest/beacon/test_beaconPreflightFailure.html
+++ /dev/null
@@ -1,56 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1207556
--->
-<head>
-  <title>Test for Bug 1207556</title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-</head>
-<body>
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1207556">Mozilla Bug 1207556</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-  
-</div>
-<pre id="test">
-<script class="testbody" type="text/javascript">
-
-var beaconUrl = "http://example.com/tests/dom/tests/mochitest/beacon/beacon-preflight-handler.sjs?fail";
-
-var intervalID = null;
-
-function queryIfBeaconSucceeded() {
-  clearInterval(intervalID);
-  var xhr = new XMLHttpRequest();
-  xhr.open("GET", "beacon-preflight-handler.sjs?verify", true);
-  xhr.onload = function() {
-    is(xhr.responseText, "green", "SendBeacon should have failed because of a failed preflight!");
-    SimpleTest.finish();
-  };
-  xhr.onerror = function() {
-    ok(false, "xhr request returned error");
-    SimpleTest.finish();
-  };
-  xhr.send();
-}
-
-// not enabled by default yet.
-SimpleTest.waitForExplicitFinish();
-SpecialPowers.pushPrefEnv({'set': [["beacon.enabled", true]]}, beginTest);
-
-function beginTest() {
-  var abv = new Uint8Array([0,1,2,3]);
-  var sent = navigator.sendBeacon(beaconUrl, abv);
-  ok(sent, "sending the beacon should start successfully");
-
-  // we have to make sure sending the beacon did not fail, so
-  // we have to wait for 2 seconds before we can query the result.
-  intervalID = setInterval(queryIfBeaconSucceeded, 2000);
-}
-
-</script>
-</pre>
-</body>
-</html>
rename from dom/tests/mochitest/beacon/test_beaconCORSRedirect.html
rename to dom/tests/mochitest/beacon/test_beaconRedirect.html
--- a/dom/tests/mochitest/beacon/test_beaconCORSRedirect.html
+++ b/dom/tests/mochitest/beacon/test_beaconRedirect.html
@@ -1,57 +1,57 @@
 <!DOCTYPE HTML>
 <html>
 <head>
-  <title>Bug 1111834 - sendBeacon() should not follow 30x redirect after preflight</title>
+  <title>Bug 1280692 - sendBeacon() should follow 30x redirect</title>
   <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
   <p id="display"></p>
   <div id="content" style="visibility: hidden">
     <iframe style="width:100%;" id="testframe"></iframe>
   </div>
 
 <script class="testbody" type="text/javascript">
 
 /* Description of the test:
- *   We do perform a non simple sendBeacon request. After the preflight channel returns correctly
- *   the actual channel is about to follow a 30x cross origin redirect, which is forbidden by the spec.
+ *   We do perform a non simple sendBeacon request which should not use CORS and should follow
+ *   a 30x cross origin redirect, which is allowed by the spec.
  */
 
 SimpleTest.waitForExplicitFinish();
 
-const BEACON_URL = "http://example.com/tests/dom/tests/mochitest/beacon/beacon-cors-redirect-handler.sjs?beacon";
+const BEACON_URL = "http://example.com/tests/dom/tests/mochitest/beacon/beacon-redirect-handler.sjs?beacon";
 
 SpecialPowers.pushPrefEnv({'set': [["beacon.enabled", true]]}, runTest);
 
 var intervalID = null;
 
 function queryIfRedirectSucceeded() {
   clearInterval(intervalID);
   var xhr = new XMLHttpRequest();
-  xhr.open("GET", "beacon-cors-redirect-handler.sjs?verifyRedirectDidNotSucceed", true);
+  xhr.open("GET", "beacon-redirect-handler.sjs?verifyRedirectDidSucceed", true);
   xhr.onload = function() {
-    is(xhr.responseText, "green", "SendBeacon does not follow cross origin redirects after preflight!");
+    is(xhr.responseText, "green", "SendBeacon should follow cross origin redirects!");
     SimpleTest.finish();
   };
   xhr.onerror = function() {
     ok(false, "xhr request returned error");
     SimpleTest.finish();
   };
   xhr.send();
 }
 
 function runTest() {
   var data = new Uint8Array([0,1,2,3]);
   navigator.sendBeacon(BEACON_URL, data);
 
-  // we have to make sure the channel did not follow the redirect hence
+  // we have to make sure the channel did follow the redirect hence
   // we have to wait for 2 seconds before we can query the result.
   intervalID = setInterval(queryIfRedirectSucceeded, 2000);
 }
 
 </script>
 </pre>
 </body>
 </html>
--- a/gfx/2d/DrawTargetD2D1.cpp
+++ b/gfx/2d/DrawTargetD2D1.cpp
@@ -334,17 +334,17 @@ DrawTargetD2D1::MaskSurface(const Patter
   mDC->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);
 
   image->QueryInterface((ID2D1Bitmap**)getter_AddRefs(bitmap));
   if (!bitmap) {
     gfxWarning() << "FillOpacityMask only works with Bitmap source surfaces.";
     return;
   }
 
-  IntSize size = IntSize(bitmap->GetSize().width, bitmap->GetSize().height);
+  IntSize size = IntSize::Truncate(bitmap->GetSize().width, bitmap->GetSize().height);
 
   Rect maskRect = Rect(0.f, 0.f, Float(size.width), Float(size.height));
 
   Rect dest = Rect(aOffset.x, aOffset.y, Float(size.width), Float(size.height));
   RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aSource, aOptions.mAlpha);
   mDC->FillOpacityMask(bitmap, brush, D2D1_OPACITY_MASK_CONTENT_GRAPHICS, D2DRect(dest), D2DRect(maskRect));
 
   mDC->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
--- a/gfx/2d/DrawTargetSkia.cpp
+++ b/gfx/2d/DrawTargetSkia.cpp
@@ -533,17 +533,17 @@ DrawTargetSkia::DrawSurfaceWithShadow(So
   // uses src-over to composite the resulting shadow.
   // The canvas spec, however, states that the composite op must be used to
   // composite the resulting shadow, so we must instead use a SkBlurImageFilter
   // to blur the image ourselves.
 
   SkPaint shadowPaint;
   shadowPaint.setXfermodeMode(GfxOpToSkiaOp(aOperator));
 
-  IntPoint shadowDest = RoundedToInt(aDest + aOffset);
+  auto shadowDest = IntPoint::Round(aDest + aOffset);
 
   SkBitmap blurMask;
   if (!UsingSkiaGPU() &&
       bitmap.extractAlpha(&blurMask)) {
     // Prefer using our own box blur instead of Skia's when we're
     // not using the GPU. It currently performs much better than
     // SkBlurImageFilter or SkBlurMaskFilter on the CPU.
     AlphaBoxBlur blur(Rect(0, 0, blurMask.width(), blurMask.height()),
@@ -564,17 +564,17 @@ DrawTargetSkia::DrawSurfaceWithShadow(So
 
     shadowPaint.setImageFilter(blurFilter);
     shadowPaint.setColorFilter(colorFilter);
 
     mCanvas->drawBitmap(bitmap, shadowDest.x, shadowDest.y, &shadowPaint);
   }
 
   // Composite the original image after the shadow
-  IntPoint dest = RoundedToInt(aDest);
+  auto dest = IntPoint::Round(aDest);
   mCanvas->drawBitmap(bitmap, dest.x, dest.y, &paint);
 
   mCanvas->restore();
 }
 
 void
 DrawTargetSkia::FillRect(const Rect &aRect,
                          const Pattern &aPattern,
--- a/gfx/2d/Point.h
+++ b/gfx/2d/Point.h
@@ -28,33 +28,80 @@ namespace gfx {
 struct UnknownUnits {};
 
 } // namespace gfx
 
 template<> struct IsPixel<gfx::UnknownUnits> : TrueType {};
 
 namespace gfx {
 
+/// Use this for parameters of functions to allow implicit conversions to
+/// integer types but not floating point types.
+/// We use this wrapper to prevent IntSize and IntPoint's constructors to
+/// take foating point values as parameters, and not require their constructors
+/// to have implementations for each permutation of integer types.
+template<typename T>
+struct IntParam {
+  constexpr MOZ_IMPLICIT IntParam(char val) : value(val) {}
+  constexpr MOZ_IMPLICIT IntParam(unsigned char val) : value(val) {}
+  constexpr MOZ_IMPLICIT IntParam(short val) : value(val) {}
+  constexpr MOZ_IMPLICIT IntParam(unsigned short val) : value(val) {}
+  constexpr MOZ_IMPLICIT IntParam(int val) : value(val) {}
+  constexpr MOZ_IMPLICIT IntParam(unsigned int val) : value(val) {}
+  constexpr MOZ_IMPLICIT IntParam(long val) : value(val) {}
+  constexpr MOZ_IMPLICIT IntParam(unsigned long val) : value(val) {}
+  constexpr MOZ_IMPLICIT IntParam(long long val) : value(val) {}
+  constexpr MOZ_IMPLICIT IntParam(unsigned long long val) : value(val) {}
+  template<typename Unit>
+  constexpr MOZ_IMPLICIT IntParam(IntCoordTyped<Unit> val) : value(val) {}
+
+  // Disable the evil ones!
+  MOZ_IMPLICIT IntParam(float val) = delete;
+  MOZ_IMPLICIT IntParam(double val) = delete;
+
+  T value;
+};
+
+template<class units, class> struct PointTyped;
+template<class units, class> struct SizeTyped;
+
 template<class units>
 struct IntPointTyped :
   public BasePoint< int32_t, IntPointTyped<units>, IntCoordTyped<units> >,
   public units {
   static_assert(IsPixel<units>::value,
                 "'units' must be a coordinate system tag");
 
+  typedef IntParam<int32_t> ToInt;
   typedef IntCoordTyped<units> Coord;
   typedef BasePoint< int32_t, IntPointTyped<units>, IntCoordTyped<units> > Super;
 
   constexpr IntPointTyped() : Super() {}
-  constexpr IntPointTyped(int32_t aX, int32_t aY) : Super(Coord(aX), Coord(aY)) {}
-  // The mixed-type constructors (int, Coord) and (Coord, int) are needed to
-  // avoid ambiguities because Coord is implicitly convertible to int.
-  constexpr IntPointTyped(int32_t aX, Coord aY) : Super(Coord(aX), aY) {}
-  constexpr IntPointTyped(Coord aX, int32_t aY) : Super(aX, Coord(aY)) {}
-  constexpr IntPointTyped(Coord aX, Coord aY) : Super(aX, aY) {}
+  constexpr IntPointTyped(ToInt aX, ToInt aY) : Super(Coord(aX.value), Coord(aY.value)) {}
+
+  static IntPointTyped<units> Round(float aX, float aY) {
+    return IntPointTyped(int32_t(floorf(aX + 0.5)), int32_t(floorf(aY + 0.5)));
+  }
+
+  static IntPointTyped<units> Ceil(float aX, float aY) {
+    return IntPointTyped(int32_t(ceil(aX)), int32_t(ceil(aY)));
+  }
+
+  static IntPointTyped<units> Floor(float aX, float aY) {
+    return IntPointTyped(int32_t(floorf(aX)), int32_t(floorf(aY)));
+  }
+
+  static IntPointTyped<units> Truncate(float aX, float aY) {
+    return IntPointTyped(int32_t(aX), int32_t(aY));
+  }
+
+  static IntPointTyped<units> Round(const PointTyped<units, float>& aPoint);
+  static IntPointTyped<units> Ceil(const PointTyped<units, float>& aPoint);
+  static IntPointTyped<units> Floor(const PointTyped<units, float>& aPoint);
+  static IntPointTyped<units> Truncate(const PointTyped<units, float>& aPoint);
 
   // XXX When all of the code is ported, the following functions to convert to and from
   // unknown types should be removed.
 
   static IntPointTyped<units> FromUnknownPoint(const IntPointTyped<UnknownUnits>& aPoint) {
     return IntPointTyped<units>(aPoint.x, aPoint.y);
   }
 
@@ -94,24 +141,22 @@ struct PointTyped :
     return PointTyped<UnknownUnits, F>(this->x, this->y);
   }
 };
 typedef PointTyped<UnknownUnits> Point;
 typedef PointTyped<UnknownUnits, double> PointDouble;
 
 template<class units>
 IntPointTyped<units> RoundedToInt(const PointTyped<units>& aPoint) {
-  return IntPointTyped<units>(int32_t(floorf(aPoint.x + 0.5f)),
-                              int32_t(floorf(aPoint.y + 0.5f)));
+  return IntPointTyped<units>::Round(aPoint.x, aPoint.y);
 }
-
+  
 template<class units>
 IntPointTyped<units> TruncatedToInt(const PointTyped<units>& aPoint) {
-  return IntPointTyped<units>(int32_t(aPoint.x),
-                              int32_t(aPoint.y));
+  return IntPointTyped<units>::Truncate(aPoint.x, aPoint.y);
 }
 
 template<class units, class F = Float>
 struct Point3DTyped :
   public BasePoint3D< F, Point3DTyped<units, F> > {
   static_assert(IsPixel<units>::value,
                 "'units' must be a coordinate system tag");
 
@@ -129,16 +174,44 @@ struct Point3DTyped :
 
   Point3DTyped<UnknownUnits, F> ToUnknownPoint() const {
     return Point3DTyped<UnknownUnits, F>(this->x, this->y, this->z);
   }
 };
 typedef Point3DTyped<UnknownUnits> Point3D;
 typedef Point3DTyped<UnknownUnits, double> PointDouble3D;
 
+template<typename units>
+IntPointTyped<units>
+IntPointTyped<units>::Round(const PointTyped<units, float>& aPoint)
+{
+  return IntPointTyped::Round(aPoint.x, aPoint.y);
+}
+
+template<typename units>
+IntPointTyped<units>
+IntPointTyped<units>::Ceil(const PointTyped<units, float>& aPoint)
+{
+  return IntPointTyped::Ceil(aPoint.x, aPoint.y);
+}
+
+template<typename units>
+IntPointTyped<units>
+IntPointTyped<units>::Floor(const PointTyped<units, float>& aPoint)
+{
+  return IntPointTyped::Floor(aPoint.x, aPoint.y);
+}
+
+template<typename units>
+IntPointTyped<units>
+IntPointTyped<units>::Truncate(const PointTyped<units, float>& aPoint)
+{
+  return IntPointTyped::Truncate(aPoint.x, aPoint.y);
+}
+
 template<class units, class F = Float>
 struct Point4DTyped :
   public BasePoint4D< F, Point4DTyped<units, F> > {
   static_assert(IsPixel<units>::value,
                 "'units' must be a coordinate system tag");
 
   typedef BasePoint4D< F, Point4DTyped<units, F> > Super;
 
@@ -165,20 +238,42 @@ typedef Point4DTyped<UnknownUnits, doubl
 
 template<class units>
 struct IntSizeTyped :
   public BaseSize< int32_t, IntSizeTyped<units> >,
   public units {
   static_assert(IsPixel<units>::value,
                 "'units' must be a coordinate system tag");
 
+  typedef IntParam<int32_t> ToInt;
   typedef BaseSize< int32_t, IntSizeTyped<units> > Super;
 
   constexpr IntSizeTyped() : Super() {}
-  constexpr IntSizeTyped(int32_t aWidth, int32_t aHeight) : Super(aWidth, aHeight) {}
+  constexpr IntSizeTyped(ToInt aWidth, ToInt aHeight) : Super(aWidth.value, aHeight.value) {}
+
+  static IntSizeTyped<units> Round(float aWidth, float aHeight) {
+    return IntSizeTyped(int32_t(floorf(aWidth + 0.5)), int32_t(floorf(aHeight + 0.5)));
+  }
+
+  static IntSizeTyped<units> Truncate(float aWidth, float aHeight) {
+    return IntSizeTyped(int32_t(aWidth), int32_t(aHeight));
+  }
+
+  static IntSizeTyped<units> Ceil(float aWidth, float aHeight) {
+    return IntSizeTyped(int32_t(ceil(aWidth)), int32_t(ceil(aHeight)));
+  }
+
+  static IntSizeTyped<units> Floor(float aWidth, float aHeight) {
+    return IntSizeTyped(int32_t(floorf(aWidth)), int32_t(floorf(aHeight)));
+  }
+
+  static IntSizeTyped<units> Round(const SizeTyped<units, float>& aSize);
+  static IntSizeTyped<units> Ceil(const SizeTyped<units, float>& aSize);
+  static IntSizeTyped<units> Floor(const SizeTyped<units, float>& aSize);
+  static IntSizeTyped<units> Truncate(const SizeTyped<units, float>& aSize);
 
   // XXX When all of the code is ported, the following functions to convert to and from
   // unknown types should be removed.
 
   static IntSizeTyped<units> FromUnknownSize(const IntSizeTyped<UnknownUnits>& aSize) {
     return IntSizeTyped<units>(aSize.width, aSize.height);
   }
 
@@ -217,12 +312,32 @@ typedef SizeTyped<UnknownUnits> Size;
 typedef SizeTyped<UnknownUnits, double> SizeDouble;
 
 template<class units>
 IntSizeTyped<units> RoundedToInt(const SizeTyped<units>& aSize) {
   return IntSizeTyped<units>(int32_t(floorf(aSize.width + 0.5f)),
                              int32_t(floorf(aSize.height + 0.5f)));
 }
 
+template<typename units> IntSizeTyped<units>
+IntSizeTyped<units>::Round(const SizeTyped<units, float>& aSize) {
+  return IntSizeTyped::Round(aSize.width, aSize.height);
+}
+
+template<typename units> IntSizeTyped<units>
+IntSizeTyped<units>::Ceil(const SizeTyped<units, float>& aSize) {
+  return IntSizeTyped::Ceil(aSize.width, aSize.height);
+}
+
+template<typename units> IntSizeTyped<units>
+IntSizeTyped<units>::Floor(const SizeTyped<units, float>& aSize) {
+  return IntSizeTyped::Floor(aSize.width, aSize.height);
+}
+
+template<typename units> IntSizeTyped<units>
+IntSizeTyped<units>::Truncate(const SizeTyped<units, float>& aSize) {
+  return IntSizeTyped::Truncate(aSize.width, aSize.height);
+}
+
 } // namespace gfx
 } // namespace mozilla
 
 #endif /* MOZILLA_GFX_POINT_H_ */
--- a/gfx/gl/SharedSurfaceIO.cpp
+++ b/gfx/gl/SharedSurfaceIO.cpp
@@ -17,17 +17,17 @@ namespace gl {
 /*static*/ UniquePtr<SharedSurface_IOSurface>
 SharedSurface_IOSurface::Create(const RefPtr<MacIOSurface>& ioSurf,
                                 GLContext* gl,
                                 bool hasAlpha)
 {
     MOZ_ASSERT(ioSurf);
     MOZ_ASSERT(gl);
 
-    gfx::IntSize size(ioSurf->GetWidth(), ioSurf->GetHeight());
+    auto size = gfx::IntSize::Truncate(ioSurf->GetWidth(), ioSurf->GetHeight());
 
     typedef SharedSurface_IOSurface ptrT;
     UniquePtr<ptrT> ret( new ptrT(ioSurf, gl, size, hasAlpha) );
     return Move(ret);
 }
 
 void
 SharedSurface_IOSurface::ProducerReleaseImpl()
@@ -209,18 +209,18 @@ SharedSurface_IOSurface::ReadbackByShare
 ////////////////////////////////////////////////////////////////////////
 // SurfaceFactory_IOSurface
 
 /*static*/ UniquePtr<SurfaceFactory_IOSurface>
 SurfaceFactory_IOSurface::Create(GLContext* gl, const SurfaceCaps& caps,
                                  const RefPtr<layers::ClientIPCAllocator>& allocator,
                                  const layers::TextureFlags& flags)
 {
-    gfx::IntSize maxDims(MacIOSurface::GetMaxWidth(),
-                         MacIOSurface::GetMaxHeight());
+    auto maxDims = gfx::IntSize::Truncate(MacIOSurface::GetMaxWidth(),
+                                          MacIOSurface::GetMaxHeight());
 
     typedef SurfaceFactory_IOSurface ptrT;
     UniquePtr<ptrT> ret( new ptrT(gl, caps, allocator, flags, maxDims) );
     return Move(ret);
 }
 
 UniquePtr<SharedSurface>
 SurfaceFactory_IOSurface::CreateShared(const gfx::IntSize& size)
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -626,17 +626,17 @@ Layer::SnapTransformTranslation(const Ma
     return aTransform;
   }
 
   Matrix matrix2D;
   Matrix4x4 result;
   if (aTransform.CanDraw2D(&matrix2D) &&
       !matrix2D.HasNonTranslation() &&
       matrix2D.HasNonIntegerTranslation()) {
-    IntPoint snappedTranslation = RoundedToInt(matrix2D.GetTranslation());
+    auto snappedTranslation = IntPoint::Round(matrix2D.GetTranslation());
     Matrix snappedMatrix = Matrix::Translation(snappedTranslation.x,
                                                snappedTranslation.y);
     result = Matrix4x4::From2D(snappedMatrix);
     if (aResidualTransform) {
       // set aResidualTransform so that aResidual * snappedMatrix == matrix2D.
       // (I.e., appying snappedMatrix after aResidualTransform gives the
       // ideal transform.)
       *aResidualTransform =
@@ -658,18 +658,17 @@ Layer::SnapTransformTranslation(const Ma
   }
 
   // Snap for 3D Transforms
 
   Point3D transformedOrigin = aTransform * Point3D();
 
   // Compute the transformed snap by rounding the values of
   // transformed origin.
-  IntPoint transformedSnapXY =
-    RoundedToInt(Point(transformedOrigin.x, transformedOrigin.y));
+  auto transformedSnapXY = IntPoint::Round(transformedOrigin.x, transformedOrigin.y);
   Matrix4x4 inverse = aTransform;
   inverse.Invert();
   // see Matrix4x4::ProjectPoint()
   Float transformedSnapZ =
     inverse._33 == 0 ? 0 : (-(transformedSnapXY.x * inverse._13 +
                               transformedSnapXY.y * inverse._23 +
                               inverse._43) / inverse._33);
   Point3D transformedSnap =
@@ -717,19 +716,19 @@ Layer::SnapTransform(const Matrix4x4& aT
   }
 
   Matrix matrix2D;
   Matrix4x4 result;
   if (mManager->IsSnappingEffectiveTransforms() &&
       aTransform.Is2D(&matrix2D) &&
       gfxSize(1.0, 1.0) <= aSnapRect.Size() &&
       matrix2D.PreservesAxisAlignedRectangles()) {
-    IntPoint transformedTopLeft = RoundedToInt(matrix2D * ToPoint(aSnapRect.TopLeft()));
-    IntPoint transformedTopRight = RoundedToInt(matrix2D * ToPoint(aSnapRect.TopRight()));
-    IntPoint transformedBottomRight = RoundedToInt(matrix2D * ToPoint(aSnapRect.BottomRight()));
+    auto transformedTopLeft = IntPoint::Round(matrix2D * ToPoint(aSnapRect.TopLeft()));
+    auto transformedTopRight = IntPoint::Round(matrix2D * ToPoint(aSnapRect.TopRight()));
+    auto transformedBottomRight = IntPoint::Round(matrix2D * ToPoint(aSnapRect.BottomRight()));
 
     Matrix snappedMatrix = gfxUtils::TransformRectToRect(aSnapRect,
       transformedTopLeft, transformedTopRight, transformedBottomRight);
 
     result = Matrix4x4::From2D(snappedMatrix);
     if (aResidualTransform && !snappedMatrix.IsSingular()) {
       // set aResidualTransform so that aResidual * snappedMatrix == matrix2D.
       // (i.e., appying snappedMatrix after aResidualTransform gives the
@@ -1023,17 +1022,17 @@ Layer::TransformRectToRenderTarget(const
   LayerRect rect(aRect);
   RenderTargetRect quad = RenderTargetRect::FromUnknownRect(
     GetEffectiveTransform().TransformBounds(rect.ToUnknownRect()));
   return quad;
 }
 
 bool
 Layer::GetVisibleRegionRelativeToRootLayer(nsIntRegion& aResult,
-                                           nsIntPoint* aLayerOffset)
+                                           IntPoint* aLayerOffset)
 {
   MOZ_ASSERT(aLayerOffset, "invalid offset pointer");
 
   if (!GetParent()) {
     return false;
   }
 
   IntPoint offset;
@@ -1041,17 +1040,17 @@ Layer::GetVisibleRegionRelativeToRootLay
   for (Layer* layer = this; layer; layer = layer->GetParent()) {
     gfx::Matrix matrix;
     if (!layer->GetLocalTransform().Is2D(&matrix) ||
         !matrix.IsTranslation()) {
       return false;
     }
 
     // The offset of |layer| to its parent.
-    IntPoint currentLayerOffset = RoundedToInt(matrix.GetTranslation());
+    auto currentLayerOffset = IntPoint::Round(matrix.GetTranslation());
 
     // Translate the accumulated visible region of |this| by the offset of
     // |layer|.
     aResult.MoveBy(currentLayerOffset.x, currentLayerOffset.y);
 
     // If the parent layer clips its lower layers, clip the visible region
     // we're accumulating.
     if (layer->GetLocalClipRect()) {
@@ -1068,17 +1067,17 @@ Layer::GetVisibleRegionRelativeToRootLay
       gfx::Matrix siblingMatrix;
       if (!sibling->GetLocalTransform().Is2D(&siblingMatrix) ||
           !siblingMatrix.IsTranslation()) {
         continue;
       }
 
       // Retreive the translation from sibling to |layer|. The accumulated
       // visible region is currently oriented with |layer|.
-      IntPoint siblingOffset = RoundedToInt(siblingMatrix.GetTranslation());
+      auto siblingOffset = IntPoint::Round(siblingMatrix.GetTranslation());
       nsIntRegion siblingVisibleRegion(sibling->GetLocalVisibleRegion().ToUnknownRegion());
       // Translate the siblings region to |layer|'s origin.
       siblingVisibleRegion.MoveBy(-siblingOffset.x, -siblingOffset.y);
       // Apply the sibling's clip.
       // Layer clip rects are not affected by the layer's transform.
       Maybe<ParentLayerIntRect> clipRect = sibling->GetLocalClipRect();
       if (clipRect) {
         siblingVisibleRegion.AndWith(clipRect->ToUnknownRect());
@@ -1087,17 +1086,17 @@ Layer::GetVisibleRegionRelativeToRootLay
       aResult.SubOut(siblingVisibleRegion);
     }
 
     // Keep track of the total offset for aLayerOffset.  We use this in plugin
     // positioning code.
     offset += currentLayerOffset;
   }
 
-  *aLayerOffset = nsIntPoint(offset.x, offset.y);
+  *aLayerOffset = IntPoint(offset.x, offset.y);
   return true;
 }
 
 Maybe<ParentLayerIntRect>
 Layer::GetCombinedClipRect() const
 {
   Maybe<ParentLayerIntRect> clip = GetClipRect();
 
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -1382,17 +1382,17 @@ public:
   Maybe<ParentLayerIntRect> GetCombinedClipRect() const;
 
   /**
    * Retrieve the root level visible region for |this| taking into account
    * clipping applied to parent layers of |this| as well as subtracting
    * visible regions of higher siblings of this layer and each ancestor.
    *
    * Note translation values for offsets of visible regions and accumulated
-   * aLayerOffset are integer rounded using Point's RoundedToInt.
+   * aLayerOffset are integer rounded using IntPoint::Round.
    *
    * @param aResult - the resulting visible region of this layer.
    * @param aLayerOffset - this layer's total offset from the root layer.
    * @return - false if during layer tree traversal a parent or sibling
    *  transform is found to be non-translational. This method returns early
    *  in this case, results will not be valid. Returns true on successful
    *  traversal.
    */
--- a/gfx/layers/MacIOSurfaceHelpers.cpp
+++ b/gfx/layers/MacIOSurfaceHelpers.cpp
@@ -31,17 +31,17 @@ CreateSourceSurfaceFromLockedMacIOSurfac
   }
 
   SurfaceFormat format =
     (ioFormat == SurfaceFormat::NV12 || ioFormat == SurfaceFormat::YUV422)
       ? SurfaceFormat::B8G8R8X8
       : SurfaceFormat::B8G8R8A8;
 
   RefPtr<DataSourceSurface> dataSurface =
-    Factory::CreateDataSourceSurface(IntSize(ioWidth, ioHeight), format);
+    Factory::CreateDataSourceSurface(IntSize::Truncate(ioWidth, ioHeight), format);
   if (NS_WARN_IF(!dataSurface)) {
     return nullptr;
   }
 
   DataSourceSurface::MappedSurface mappedSurface;
   if (!dataSurface->Map(DataSourceSurface::WRITE, &mappedSurface)) {
     return nullptr;
   }
@@ -70,24 +70,24 @@ CreateSourceSurfaceFromLockedMacIOSurfac
         rowSrc++;
       }
     }
 
     /* Convert to RGB */
     PlanarYCbCrData data;
     data.mYChannel = (uint8_t*)aSurface->GetBaseAddressOfPlane(0);
     data.mYStride = aSurface->GetBytesPerRow(0);
-    data.mYSize = IntSize(ioWidth, ioHeight);
+    data.mYSize = IntSize::Truncate(ioWidth, ioHeight);
     data.mCbChannel = cbPlane.get();
     data.mCrChannel = crPlane.get();
     data.mCbCrStride = cbCrWidth;
-    data.mCbCrSize = IntSize(cbCrWidth, cbCrHeight);
+    data.mCbCrSize = IntSize::Truncate(cbCrWidth, cbCrHeight);
     data.mPicSize = data.mYSize;
 
-    ConvertYCbCrToRGB(data, SurfaceFormat::B8G8R8X8, IntSize(ioWidth, ioHeight), mappedSurface.mData, mappedSurface.mStride);
+    ConvertYCbCrToRGB(data, SurfaceFormat::B8G8R8X8, IntSize::Truncate(ioWidth, ioHeight), mappedSurface.mData, mappedSurface.mStride);
   } else if (ioFormat == SurfaceFormat::YUV422) {
     /* Convert to YV16 */
     size_t cbCrWidth = (ioWidth+1)>>1;
     size_t cbCrHeight = ioHeight;
     // Ensure our stride is a multiple of 32 to allow for memory aligned rows.
     size_t cbCrStride = ALIGNED_32(cbCrWidth);
     size_t strideDelta = cbCrStride - cbCrWidth;
     MOZ_ASSERT(strideDelta <= 31);
@@ -123,24 +123,24 @@ CreateSourceSurfaceFromLockedMacIOSurfac
         yDest  += strideDelta << 1;
       }
     }
 
     /* Convert to RGB */
     PlanarYCbCrData data;
     data.mYChannel = ALIGNEDPTR_32(yPlane.get());
     data.mYStride = cbCrStride * 2;
-    data.mYSize = IntSize(ioWidth, ioHeight);
+    data.mYSize = IntSize::Truncate(ioWidth, ioHeight);
     data.mCbChannel = ALIGNEDPTR_32(cbPlane.get());
     data.mCrChannel = ALIGNEDPTR_32(crPlane.get());
     data.mCbCrStride = cbCrStride;
-    data.mCbCrSize = IntSize(cbCrWidth, cbCrHeight);
+    data.mCbCrSize = IntSize::Truncate(cbCrWidth, cbCrHeight);
     data.mPicSize = data.mYSize;
 
-    ConvertYCbCrToRGB(data, SurfaceFormat::B8G8R8X8, IntSize(ioWidth, ioHeight), mappedSurface.mData, mappedSurface.mStride);
+    ConvertYCbCrToRGB(data, SurfaceFormat::B8G8R8X8, IntSize::Truncate(ioWidth, ioHeight), mappedSurface.mData, mappedSurface.mStride);
   } else {
     unsigned char* ioData = (unsigned char*)aSurface->GetBaseAddress();
 
     for (size_t i = 0; i < ioHeight; ++i) {
       memcpy(mappedSurface.mData + i * mappedSurface.mStride,
              ioData + i * bytesPerRow,
              ioWidth * 4);
     }
--- a/gfx/layers/MacIOSurfaceImage.h
+++ b/gfx/layers/MacIOSurfaceImage.h
@@ -20,17 +20,18 @@ public:
   explicit MacIOSurfaceImage(MacIOSurface* aSurface)
    : Image(nullptr, ImageFormat::MAC_IOSURFACE),
      mSurface(aSurface)
   {}
 
   MacIOSurface* GetSurface() { return mSurface; }
 
   gfx::IntSize GetSize() override {
-    return gfx::IntSize(mSurface->GetDevicePixelWidth(), mSurface->GetDevicePixelHeight());
+    return gfx::IntSize::Truncate(mSurface->GetDevicePixelWidth(),
+                                  mSurface->GetDevicePixelHeight());
   }
 
   virtual already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override;
 
   virtual TextureClient* GetTextureClient(CompositableClient* aClient) override;
 
   virtual MacIOSurfaceImage* AsMacIOSurfaceImage() override {
     return this;
--- a/gfx/layers/TiledLayerBuffer.h
+++ b/gfx/layers/TiledLayerBuffer.h
@@ -153,17 +153,17 @@ public:
     return gfx::IntPoint(aPosition.x * scaledTileSize.width,
                          aPosition.y * scaledTileSize.height) + mTileOrigin;
   }
 
   const TilesPlacement& GetPlacement() const { return mTiles; }
 
   const gfx::IntSize& GetTileSize() const { return mTileSize; }
 
-  gfx::IntSize GetScaledTileSize() const { return RoundedToInt(gfx::Size(mTileSize) / mResolution); }
+  gfx::IntSize GetScaledTileSize() const { return gfx::IntSize::Round(gfx::Size(mTileSize) / mResolution); }
 
   unsigned int GetTileCount() const { return mRetainedTiles.Length(); }
 
   Tile& GetTile(size_t i) { return mRetainedTiles[i]; }
 
   const nsIntRegion& GetValidRegion() const { return mValidRegion; }
   const nsIntRegion& GetPaintedRegion() const { return mPaintedRegion; }
   void ClearPaintedRegion() { mPaintedRegion.SetEmpty(); }
--- a/gfx/layers/apz/src/HitTestingTreeNode.cpp
+++ b/gfx/layers/apz/src/HitTestingTreeNode.cpp
@@ -262,17 +262,17 @@ HitTestingTreeNode::HitTest(const Parent
     return HitTestResult::HitNothing;
   }
 
   // convert into Layer coordinate space
   Maybe<LayerPoint> pointInLayerPixels = Untransform(aPoint);
   if (!pointInLayerPixels) {
     return HitTestResult::HitNothing;
   }
-  LayerIntPoint point = RoundedToInt(pointInLayerPixels.ref());
+  auto point = LayerIntPoint::Round(pointInLayerPixels.ref());
 
   // test against event regions in Layer coordinate space
   if (!mEventRegions.mHitRegion.Contains(point.x, point.y)) {
     return HitTestResult::HitNothing;
   }
   if ((mOverride & EventRegionsOverride::ForceDispatchToContent) ||
       mEventRegions.mDispatchToContentHitRegion.Contains(point.x, point.y))
   {
--- a/gfx/layers/apz/util/APZCCallbackHelper.cpp
+++ b/gfx/layers/apz/util/APZCCallbackHelper.cpp
@@ -425,17 +425,17 @@ APZCCallbackHelper::ApplyCallbackTransfo
 
 LayoutDeviceIntPoint
 APZCCallbackHelper::ApplyCallbackTransform(const LayoutDeviceIntPoint& aPoint,
                                            const ScrollableLayerGuid& aGuid,
                                            const CSSToLayoutDeviceScale& aScale)
 {
     LayoutDevicePoint point = LayoutDevicePoint(aPoint.x, aPoint.y);
     point = ApplyCallbackTransform(point / aScale, aGuid) * aScale;
-    return gfx::RoundedToInt(point);
+    return LayoutDeviceIntPoint::Round(point);
 }
 
 void
 APZCCallbackHelper::ApplyCallbackTransform(WidgetEvent& aEvent,
                                            const ScrollableLayerGuid& aGuid,
                                            const CSSToLayoutDeviceScale& aScale)
 {
   if (aEvent.AsTouchEvent()) {
@@ -466,17 +466,17 @@ APZCCallbackHelper::DispatchSynthesizedM
                                                   Modifiers aModifiers,
                                                   nsIWidget* aWidget)
 {
   MOZ_ASSERT(aMsg == eMouseMove || aMsg == eMouseDown ||
              aMsg == eMouseUp || aMsg == eMouseLongTap);
 
   WidgetMouseEvent event(true, aMsg, aWidget,
                          WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal);
-  event.mRefPoint = LayoutDeviceIntPoint(aRefPoint.x, aRefPoint.y);
+  event.mRefPoint = LayoutDeviceIntPoint::Truncate(aRefPoint.x, aRefPoint.y);
   event.mTime = aTime;
   event.button = WidgetMouseEvent::eLeftButton;
   event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
   event.mIgnoreRootScrollFrame = true;
   if (aMsg != eMouseMove) {
     event.mClickCount = 1;
   }
   event.mModifiers = aModifiers;
--- a/gfx/layers/apz/util/APZEventState.cpp
+++ b/gfx/layers/apz/util/APZEventState.cpp
@@ -251,17 +251,17 @@ APZEventState::ProcessLongTap(const nsCO
 
   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);
-    LayoutDeviceIntPoint ldPoint = RoundedToInt(point * widget->GetDefaultScale());
+    auto ldPoint = LayoutDeviceIntPoint::Round(point * widget->GetDefaultScale());
     cancelTouchEvent.mTouches.AppendElement(new mozilla::dom::Touch(mLastTouchIdentifier,
         ldPoint, LayoutDeviceIntPoint(), 0, 0));
     APZCCallbackHelper::DispatchWidgetEvent(cancelTouchEvent);
   }
 }
 
 void
 APZEventState::ProcessLongTapUp()
--- a/gfx/layers/basic/BasicCompositor.cpp
+++ b/gfx/layers/basic/BasicCompositor.cpp
@@ -355,19 +355,19 @@ DrawSurfaceWithTextureCoords(DrawTarget 
 
   // Floating point error can accumulate above and we know our visible region
   // is integer-aligned, so round it out.
   sourceRect.Round();
 
   // Compute a transform that maps sourceRect to aDestRect.
   Matrix matrix =
     gfxUtils::TransformRectToRect(sourceRect,
-                                  gfx::IntPoint(aDestRect.x, aDestRect.y),
-                                  gfx::IntPoint(aDestRect.XMost(), aDestRect.y),
-                                  gfx::IntPoint(aDestRect.XMost(), aDestRect.YMost()));
+                                  gfx::IntPoint::Truncate(aDestRect.x, aDestRect.y),
+                                  gfx::IntPoint::Truncate(aDestRect.XMost(), aDestRect.y),
+                                  gfx::IntPoint::Truncate(aDestRect.XMost(), aDestRect.YMost()));
 
   // Only use REPEAT if aTextureCoords is outside (0, 0, 1, 1).
   gfx::Rect unitRect(0, 0, 1, 1);
   ExtendMode mode = unitRect.Contains(aTextureCoords) ? ExtendMode::CLAMP : ExtendMode::REPEAT;
 
   FillRectWithMask(aDest, aDestRect, aSource, aSamplingFilter, aOptions,
                    mode, aMask, aMaskTransform, &matrix);
 }
@@ -621,17 +621,17 @@ BasicCompositor::DrawQuad(const gfx::Rec
     dest->Flush();
 
     RefPtr<SourceSurface> destSnapshot = dest->Snapshot();
 
     SetupMask(aEffectChain, buffer, offset, sourceMask, maskTransform);
 
     if (sourceMask) {
       RefPtr<DrawTarget> transformDT =
-        dest->CreateSimilarDrawTarget(IntSize(transformBounds.width, transformBounds.height),
+        dest->CreateSimilarDrawTarget(IntSize::Truncate(transformBounds.width, transformBounds.height),
                                       SurfaceFormat::B8G8R8A8);
       new3DTransform.PostTranslate(-transformBounds.x, -transformBounds.y, 0);
       if (transformDT &&
           transformDT->Draw3DTransformedSurface(destSnapshot, new3DTransform)) {
         RefPtr<SourceSurface> transformSnapshot = transformDT->Snapshot();
 
         // Transform the source by it's normal transform, and then the inverse
         // of the mask transform so that it's in the mask's untransformed
--- a/gfx/layers/basic/BasicLayerManager.cpp
+++ b/gfx/layers/basic/BasicLayerManager.cpp
@@ -931,17 +931,17 @@ BasicLayerManager::PaintLayer(gfxContext
       effectiveTransform.TransformAndClipBounds(Rect(bounds),
                                                 ToRect(aTarget->GetClipExtents()));
     xformBounds.RoundOut();
     effectiveTransform.PostTranslate(-xformBounds.x, -xformBounds.y, 0);
     effectiveTransform.PreTranslate(bounds.x, bounds.y, 0);
 
     RefPtr<SourceSurface> untransformedSurf = untransformedDT->Snapshot();
     RefPtr<DrawTarget> xformDT =
-      untransformedDT->CreateSimilarDrawTarget(IntSize(xformBounds.width, xformBounds.height),
+      untransformedDT->CreateSimilarDrawTarget(IntSize::Truncate(xformBounds.width, xformBounds.height),
                                                SurfaceFormat::B8G8R8A8);
     RefPtr<SourceSurface> xformSurf;
     if(xformDT && untransformedSurf &&
        xformDT->Draw3DTransformedSurface(untransformedSurf, effectiveTransform)) {
       xformSurf = xformDT->Snapshot();
     }
 
     if (xformSurf) {
--- a/gfx/layers/client/ClientLayerManager.cpp
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -844,17 +844,22 @@ ClientLayerManager::DependsOnStaleDevice
   return gfxPlatform::GetPlatform()->GetDeviceCounter() != mDeviceCounter;
 }
 
 
 already_AddRefed<PersistentBufferProvider>
 ClientLayerManager::CreatePersistentBufferProvider(const gfx::IntSize& aSize,
                                                    gfx::SurfaceFormat aFormat)
 {
-  if (gfxPrefs::PersistentBufferProviderSharedEnabled()) {
+  // Don't use a shared buffer provider if compositing is considered "not cheap"
+  // because the canvas will most likely be flattened into a thebes layer instead
+  // of being sent to the compositor, in which case rendering into shared memory
+  // is wasteful.
+  if (IsCompositingCheap() &&
+      gfxPrefs::PersistentBufferProviderSharedEnabled()) {
     RefPtr<PersistentBufferProvider> provider
       = PersistentBufferProviderShared::Create(aSize, aFormat, AsShadowForwarder());
     if (provider) {
       return provider.forget();
     }
   }
 
   return LayerManager::CreatePersistentBufferProvider(aSize, aFormat);
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -355,20 +355,21 @@ DeallocateTextureClient(TextureDeallocPa
     actor->mOwnsTextureData = params.clientDeallocation;
     actor->Destroy(actor->mCompositableForwarder);
     // DestroyTextureData will be called by TextureChild::ActorDestroy
   }
 }
 
 void TextureClient::Destroy(bool aForceSync)
 {
-  if (mActor) {
+  if (mActor && !mIsLocked) {
     mActor->Lock();
   }
 
+  mBorrowedDrawTarget = nullptr;
   mReadLock = nullptr;
 
   CancelWaitFenceHandleOnImageBridge();
   RefPtr<TextureChild> actor = mActor;
   mActor = nullptr;
 
   if (actor && !actor->mDestroyed.compareExchange(false, true)) {
     actor->Unlock();
@@ -514,17 +515,19 @@ TextureClient::Unlock()
 
     mBorrowedDrawTarget = nullptr;
   }
 
   if (mOpenMode & OpenMode::OPEN_WRITE) {
     mUpdated = true;
   }
 
-  mData->Unlock();
+  if (mData) {
+    mData->Unlock();
+  }
   mIsLocked = false;
   mOpenMode = OpenMode::OPEN_NONE;
 
   UnlockActor();
 }
 
 void
 TextureClient::EnableReadLock()
--- a/gfx/layers/composite/ContainerLayerComposite.cpp
+++ b/gfx/layers/composite/ContainerLayerComposite.cpp
@@ -721,17 +721,17 @@ CreateTemporaryTargetAndCopyFromBackgrou
   gfx::IntRect surfaceRect = gfx::IntRect(visibleRect.x, visibleRect.y,
                                           visibleRect.width, visibleRect.height);
 
   gfx::IntPoint sourcePoint = gfx::IntPoint(visibleRect.x, visibleRect.y);
 
   gfx::Matrix4x4 transform = aContainer->GetEffectiveTransform();
   DebugOnly<gfx::Matrix> transform2d;
   MOZ_ASSERT(transform.Is2D(&transform2d) && !gfx::ThebesMatrix(transform2d).HasNonIntegerTranslation());
-  sourcePoint += gfx::IntPoint(transform._41, transform._42);
+  sourcePoint += gfx::IntPoint::Truncate(transform._41, transform._42);
 
   sourcePoint -= compositor->GetCurrentRenderTarget()->GetOrigin();
 
   return compositor->CreateRenderTargetFromSource(surfaceRect, previousTarget, sourcePoint);
 }
 
 template<class ContainerT> void
 RenderIntermediate(ContainerT* aContainer,
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -246,23 +246,23 @@ LayerManagerComposite::PostProcessLayers
     return;
   }
 
   nsIntRegion localOpaque;
   // Treat layers on the path to the root of the 3D rendering context as
   // a giant layer if it is a leaf.
   Matrix4x4 transform = GetAccTransformIn3DContext(aLayer);
   Matrix transform2d;
-  Maybe<nsIntPoint> integerTranslation;
+  Maybe<IntPoint> integerTranslation;
   // If aLayer has a simple transform (only an integer translation) then we
   // can easily convert aOpaqueRegion into pre-transform coordinates and include
   // that region.
   if (transform.Is2D(&transform2d)) {
     if (transform2d.IsIntegerTranslation()) {
-      integerTranslation = Some(TruncatedToInt(transform2d.GetTranslation()));
+      integerTranslation = Some(IntPoint::Truncate(transform2d.GetTranslation()));
       localOpaque = aOpaqueRegion;
       localOpaque.MoveBy(-*integerTranslation);
     }
   }
 
   // Compute a clip that's the combination of our layer clip with the clip
   // from our ancestors.
   LayerComposite* composite = aLayer->AsLayerComposite();
--- a/gfx/thebes/PrintTargetWindows.cpp
+++ b/gfx/thebes/PrintTargetWindows.cpp
@@ -29,17 +29,17 @@ PrintTargetWindows::CreateOrNull(HDC aDC
   // Figure out the cairo surface size - Windows we need to use the printable
   // area of the page.  Note: we only scale the printing using the LOGPIXELSY,
   // so we use that when calculating the surface width as well as the height.
   int32_t heightDPI = ::GetDeviceCaps(aDC, LOGPIXELSY);
   float width =
     (::GetDeviceCaps(aDC, HORZRES) * POINTS_PER_INCH_FLOAT) / heightDPI;
   float height =
     (::GetDeviceCaps(aDC, VERTRES) * POINTS_PER_INCH_FLOAT) / heightDPI;
-  IntSize size(width, height);
+  IntSize size = IntSize::Truncate(width, height);
 
   if (!Factory::CheckSurfaceSize(size)) {
     return nullptr;
   }
 
   cairo_surface_t* surface = cairo_win32_printing_surface_create(aDC);
 
   if (cairo_surface_status(surface)) {
--- a/image/ClippedImage.cpp
+++ b/image/ClippedImage.cpp
@@ -515,35 +515,35 @@ ClippedImage::OptimalImageSizeForDest(co
   }
 
   if (needScale) {
     // To avoid ugly sampling artifacts, ClippedImage needs the image size to
     // be chosen such that the clipping region lies on pixel boundaries.
 
     // First, we select a scale that's good for ClippedImage. An integer
     // multiple of the size of the clipping region is always fine.
-    nsIntSize scale(ceil(aDest.width / mClip.width),
-                    ceil(aDest.height / mClip.height));
+    IntSize scale = IntSize::Ceil(aDest.width / mClip.width,
+                                  aDest.height / mClip.height);
 
     if (forceUniformScaling) {
       scale.width = scale.height = max(scale.height, scale.width);
     }
 
     // Determine the size we'd prefer to render the inner image at, and ask the
     // inner image what size we should actually use.
     gfxSize desiredSize(imgWidth * scale.width, imgHeight * scale.height);
     nsIntSize innerDesiredSize =
       InnerImage()->OptimalImageSizeForDest(desiredSize, aWhichFrame,
                                             aSamplingFilter, aFlags);
 
     // To get our final result, we take the inner image's desired size and
     // determine how large the clipped region would be at that scale. (Again, we
     // ensure an integer multiple of the size of the clipping region.)
-    nsIntSize finalScale(ceil(double(innerDesiredSize.width) / imgWidth),
-                         ceil(double(innerDesiredSize.height) / imgHeight));
+    IntSize finalScale = IntSize::Ceil(double(innerDesiredSize.width) / imgWidth,
+                                       double(innerDesiredSize.height) / imgHeight);
     return mClip.Size() * finalScale;
   }
 
   MOZ_ASSERT(false,
              "If ShouldClip() led us to draw then we should never get here");
   return InnerImage()->OptimalImageSizeForDest(aDest, aWhichFrame,
                                                aSamplingFilter, aFlags);
 }
--- a/image/RasterImage.cpp
+++ b/image/RasterImage.cpp
@@ -1818,17 +1818,17 @@ RasterImage::OptimalImageSizeForDest(con
   MOZ_ASSERT(aDest.width >= 0 || ceil(aDest.width) <= INT32_MAX ||
              aDest.height >= 0 || ceil(aDest.height) <= INT32_MAX,
              "Unexpected destination size");
 
   if (mSize.IsEmpty() || aDest.IsEmpty()) {
     return IntSize(0, 0);
   }
 
-  IntSize destSize(ceil(aDest.width), ceil(aDest.height));
+  IntSize destSize = IntSize::Ceil(aDest.width, aDest.height);
 
   if (aSamplingFilter == SamplingFilter::GOOD &&
       CanDownscaleDuringDecode(destSize, aFlags)) {
     return destSize;
   }
 
   // We can't scale to this size. Use our intrinsic size for now.
   return mSize;
--- a/image/VectorImage.cpp
+++ b/image/VectorImage.cpp
@@ -1297,17 +1297,17 @@ VectorImage::OptimalImageSizeForDest(con
                                      SamplingFilter aSamplingFilter,
                                      uint32_t aFlags)
 {
   MOZ_ASSERT(aDest.width >= 0 || ceil(aDest.width) <= INT32_MAX ||
              aDest.height >= 0 || ceil(aDest.height) <= INT32_MAX,
              "Unexpected destination size");
 
   // We can rescale SVGs freely, so just return the provided destination size.
-  return nsIntSize(ceil(aDest.width), ceil(aDest.height));
+  return nsIntSize::Ceil(aDest.width, aDest.height);
 }
 
 already_AddRefed<imgIContainer>
 VectorImage::Unwrap()
 {
   nsCOMPtr<imgIContainer> self(this);
   return self.forget();
 }
--- a/js/src/asmjs/AsmJS.cpp
+++ b/js/src/asmjs/AsmJS.cpp
@@ -106,17 +106,16 @@ class AsmJSGlobal
     enum VarInitKind { InitConstant, InitImport };
     enum ConstantKind { GlobalConstant, MathConstant };
 
   private:
     struct CacheablePod {
         Which which_;
         union {
             struct {
-                uint32_t globalDataOffset_;
                 VarInitKind initKind_;
                 union {
                     ValType importType_;
                     Val val_;
                 } u;
             } var;
             uint32_t ffiIndex_;
             Scalar::Type viewType_;
@@ -145,20 +144,16 @@ class AsmJSGlobal
         field_ = Move(field);
     }
     const char* field() const {
         return field_.get();
     }
     Which which() const {
         return pod.which_;
     }
-    uint32_t varGlobalDataOffset() const {
-        MOZ_ASSERT(pod.which_ == Variable);
-        return pod.u.var.globalDataOffset_;
-    }
     VarInitKind varInitKind() const {
         MOZ_ASSERT(pod.which_ == Variable);
         return pod.u.var.initKind_;
     }
     Val varInitVal() const {
         MOZ_ASSERT(pod.which_ == Variable);
         MOZ_ASSERT(pod.u.var.initKind_ == InitConstant);
         return pod.u.var.u.val_;
@@ -1442,17 +1437,16 @@ class MOZ_STACK_CLASS ModuleValidator
             return which_;
         }
         Type varOrConstType() const {
             MOZ_ASSERT(which_ == Variable || which_ == ConstantLiteral || which_ == ConstantImport);
             return u.varOrConst.type_;
         }
         unsigned varOrConstIndex() const {
             MOZ_ASSERT(which_ == Variable || which_ == ConstantImport);
-            MOZ_ASSERT(u.varOrConst.index_ != -1u);
             return u.varOrConst.index_;
         }
         bool isConst() const {
             return which_ == ConstantLiteral || which_ == ConstantImport;
         }
         NumLit constLiteralValue() const {
             MOZ_ASSERT(which_ == ConstantLiteral);
             return u.varOrConst.literalValue_;
@@ -1846,61 +1840,59 @@ class MOZ_STACK_CLASS ModuleValidator
         }
         return true;
     }
     bool addGlobalVarInit(PropertyName* var, const NumLit& lit, Type type, bool isConst) {
         MOZ_ASSERT(type.isGlobalVarType());
         MOZ_ASSERT(type == Type::canonicalize(Type::lit(lit)));
 
         uint32_t index;
-        if (!mg_.allocateGlobal(type.canonicalToValType(), isConst, &index))
+        if (!mg_.addGlobal(type.canonicalToValType(), isConst, &index))
             return false;
 
         Global::Which which = isConst ? Global::ConstantLiteral : Global::Variable;
         Global* global = validationLifo_.new_<Global>(which);
         if (!global)
             return false;
         global->u.varOrConst.index_ = index;
         global->u.varOrConst.type_ = (isConst ? Type::lit(lit) : type).which();
         if (isConst)
             global->u.varOrConst.literalValue_ = lit;
         if (!globalMap_.putNew(var, global))
             return false;
 
         AsmJSGlobal g(AsmJSGlobal::Variable, nullptr);
         g.pod.u.var.initKind_ = AsmJSGlobal::InitConstant;
         g.pod.u.var.u.val_ = lit.value();
-        g.pod.u.var.globalDataOffset_ = mg_.global(index).globalDataOffset;
         return asmJSMetadata_->asmJSGlobals.append(Move(g));
     }
     bool addGlobalVarImport(PropertyName* var, PropertyName* field, Type type, bool isConst) {
         MOZ_ASSERT(type.isGlobalVarType());
 
         UniqueChars fieldChars = StringToNewUTF8CharsZ(cx_, *field);
         if (!fieldChars)
             return false;
 
         uint32_t index;
         ValType valType = type.canonicalToValType();
-        if (!mg_.allocateGlobal(valType, isConst, &index))
+        if (!mg_.addGlobal(valType, isConst, &index))
             return false;
 
         Global::Which which = isConst ? Global::ConstantImport : Global::Variable;
         Global* global = validationLifo_.new_<Global>(which);
         if (!global)
             return false;
         global->u.varOrConst.index_ = index;
         global->u.varOrConst.type_ = type.which();
         if (!globalMap_.putNew(var, global))
             return false;
 
         AsmJSGlobal g(AsmJSGlobal::Variable, Move(fieldChars));
         g.pod.u.var.initKind_ = AsmJSGlobal::InitImport;
         g.pod.u.var.u.importType_ = valType;
-        g.pod.u.var.globalDataOffset_ = mg_.global(index).globalDataOffset;
         return asmJSMetadata_->asmJSGlobals.append(Move(g));
     }
     bool addArrayView(PropertyName* var, Scalar::Type vt, PropertyName* maybeField) {
         UniqueChars fieldChars;
         if (maybeField) {
             fieldChars = StringToNewUTF8CharsZ(cx_, *maybeField);
             if (!fieldChars)
                 return false;
@@ -3900,17 +3892,17 @@ CheckVarRef(FunctionValidator& f, ParseN
     if (const ModuleValidator::Global* global = f.lookupGlobal(name)) {
         switch (global->which()) {
           case ModuleValidator::Global::ConstantLiteral:
             *type = global->varOrConstType();
             return f.writeConstExpr(global->constLiteralValue());
           case ModuleValidator::Global::ConstantImport:
           case ModuleValidator::Global::Variable: {
             *type = global->varOrConstType();
-            return f.encoder().writeExpr(Expr::LoadGlobal) &&
+            return f.encoder().writeExpr(Expr::GetGlobal) &&
                    f.encoder().writeVarU32(global->varOrConstIndex());
           }
           case ModuleValidator::Global::Function:
           case ModuleValidator::Global::FFI:
           case ModuleValidator::Global::MathBuiltinFunction:
           case ModuleValidator::Global::AtomicsBuiltinFunction:
           case ModuleValidator::Global::FuncPtrTable:
           case ModuleValidator::Global::ArrayView:
@@ -4192,17 +4184,17 @@ CheckAssignName(FunctionValidator& f, Pa
 
         Type rhsType;
         if (!CheckExpr(f, rhs, &rhsType))
             return false;
 
         Type globType = global->varOrConstType();
         if (!(rhsType <= globType))
             return f.failf(lhs, "%s is not a subtype of %s", rhsType.toChars(), globType.toChars());
-        if (!f.encoder().writeExpr(Expr::StoreGlobal))
+        if (!f.encoder().writeExpr(Expr::SetGlobal))
             return false;
         if (!f.encoder().writeVarU32(global->varOrConstIndex()))
             return false;
 
         *type = rhsType;
         return true;
     }
 
@@ -7812,48 +7804,30 @@ CheckBuffer(JSContext* cx, const AsmJSMe
         if (!ArrayBufferObject::prepareForAsmJS(cx, abheap, useSignalHandlers))
             return LinkFail(cx, "Unable to prepare ArrayBuffer for asm.js use");
     }
 
     return true;
 }
 
 static bool
-TryInstantiate(JSContext* cx, CallArgs args, Module& module, const AsmJSMetadata& metadata,
-               MutableHandleWasmInstanceObject instanceObj, MutableHandleObject exportObj)
-{
-    HandleValue globalVal = args.get(0);
-    HandleValue importVal = args.get(1);
-    HandleValue bufferVal = args.get(2);
-
-    RootedArrayBufferObjectMaybeShared buffer(cx);
-    RootedWasmMemoryObject memory(cx);
-    if (module.metadata().usesMemory()) {
-        if (!CheckBuffer(cx, metadata, bufferVal, &buffer))
-            return false;
-
-        memory = WasmMemoryObject::create(cx, buffer, nullptr);
-        if (!memory)
-            return false;
-    }
-
-    Vector<Val> valImports(cx);
+GetImports(JSContext* cx, const AsmJSMetadata& metadata, HandleValue globalVal,
+           HandleValue importVal, MutableHandle<FunctionVector> funcImports, ValVector* valImports)
+{
     Rooted<FunctionVector> ffis(cx, FunctionVector(cx));
     if (!ffis.resize(metadata.numFFIs))
         return false;
 
     for (const AsmJSGlobal& global : metadata.asmJSGlobals) {
         switch (global.which()) {
           case AsmJSGlobal::Variable: {
-            // We don't have any global data into which to write the imported
-            // values until after instantiation, so save them in a Vector.
             Val val;
             if (!ValidateGlobalVariable(cx, global, importVal, &val))
                 return false;
-            if (!valImports.append(val))
+            if (!valImports->append(val))
                 return false;
             break;
           }
           case AsmJSGlobal::FFI:
             if (!ValidateFFI(cx, global, importVal, &ffis))
                 return false;
             break;
           case AsmJSGlobal::ArrayView:
@@ -7879,42 +7853,58 @@ TryInstantiate(JSContext* cx, CallArgs a
             break;
           case AsmJSGlobal::SimdOp:
             if (!ValidateSimdOperation(cx, global, globalVal))
                 return false;
             break;
         }
     }
 
+    for (const AsmJSImport& import : metadata.asmJSImports) {
+        if (!funcImports.append(ffis[import.ffiIndex()]))
+            return false;
+    }
+
+    return true;
+}
+
+static bool
+TryInstantiate(JSContext* cx, CallArgs args, Module& module, const AsmJSMetadata& metadata,
+               MutableHandleWasmInstanceObject instanceObj, MutableHandleObject exportObj)
+{
+    HandleValue globalVal = args.get(0);
+    HandleValue importVal = args.get(1);
+    HandleValue bufferVal = args.get(2);
+
+    RootedArrayBufferObjectMaybeShared buffer(cx);
+    RootedWasmMemoryObject memory(cx);
+    if (module.metadata().usesMemory()) {
+        if (!CheckBuffer(cx, metadata, bufferVal, &buffer))
+            return false;
+
+        memory = WasmMemoryObject::create(cx, buffer, nullptr);
+        if (!memory)
+            return false;
+    }
+
+    ValVector valImports;
     Rooted<FunctionVector> funcs(cx, FunctionVector(cx));
-    for (const AsmJSImport& import : metadata.asmJSImports) {
-        if (!funcs.append(ffis[import.ffiIndex()]))
-            return false;
-    }
+    if (!GetImports(cx, metadata, globalVal, importVal, &funcs, &valImports))
+        return false;
 
     RootedWasmTableObject table(cx);
-    if (!module.instantiate(cx, funcs, table, memory, nullptr, instanceObj))
+    if (!module.instantiate(cx, funcs, table, memory, valImports, nullptr, instanceObj))
         return false;
 
     RootedValue exportObjVal(cx);
     if (!JS_GetProperty(cx, instanceObj, InstanceExportField, &exportObjVal))
         return false;
 
     MOZ_RELEASE_ASSERT(exportObjVal.isObject());
     exportObj.set(&exportObjVal.toObject());
-
-    // Now write the imported values into global data.
-    uint8_t* globalData = instanceObj->instance().codeSegment().globalData();
-    uint32_t valIndex = 0;
-    for (const AsmJSGlobal& global : metadata.asmJSGlobals) {
-        if (global.which() == AsmJSGlobal::Variable)
-            valImports[valIndex++].writePayload(globalData + global.varGlobalDataOffset());
-    }
-    MOZ_ASSERT(valIndex == valImports.length());
-
     return true;
 }
 
 static MOZ_MUST_USE bool
 MaybeAppendUTF8Name(JSContext* cx, const char* utf8Chars, MutableHandle<PropertyNameVector> names)
 {
     if (!utf8Chars)
         return true;
--- a/js/src/asmjs/WasmAST.h
+++ b/js/src/asmjs/WasmAST.h
@@ -190,21 +190,23 @@ enum class AstExprKind
     Block,
     Branch,
     BranchTable,
     Call,
     CallIndirect,
     ComparisonOperator,
     Const,
     ConversionOperator,
+    GetGlobal,
     GetLocal,
     If,
     Load,
     Nop,
     Return,
+    SetGlobal,
     SetLocal,
     Store,
     TernaryOperator,
     UnaryOperator,
     Unreachable
 };
 
 class AstExpr : public AstNode
@@ -285,16 +287,51 @@ class AstSetLocal : public AstExpr
     AstRef& local() {
         return local_;
     }
     AstExpr& value() const {
         return value_;
     }
 };
 
+class AstGetGlobal : public AstExpr
+{
+    AstRef global_;
+
+  public:
+    static const AstExprKind Kind = AstExprKind::GetGlobal;
+    explicit AstGetGlobal(AstRef global)
+      : AstExpr(Kind),
+        global_(global)
+    {}
+    AstRef& global() {
+        return global_;
+    }
+};
+
+class AstSetGlobal : public AstExpr
+{
+    AstRef global_;
+    AstExpr& value_;
+
+  public:
+    static const AstExprKind Kind = AstExprKind::SetGlobal;
+    AstSetGlobal(AstRef global, AstExpr& value)
+      : AstExpr(Kind),
+        global_(global),
+        value_(value)
+    {}
+    AstRef& global() {
+        return global_;
+    }
+    AstExpr& value() const {
+        return value_;
+    }
+};
+
 class AstBlock : public AstExpr
 {
     Expr expr_;
     AstName breakName_;
     AstName continueName_;
     AstExprVector exprs_;
 
   public:
@@ -522,56 +559,102 @@ class AstResizable
     AstResizable() : initial_(0), maximum_() {}
     AstResizable(uint32_t initial, Maybe<uint32_t> maximum)
       : initial_(initial), maximum_(maximum)
     {}
     uint32_t initial() const { return initial_; }
     const Maybe<uint32_t>& maximum() const { return maximum_; }
 };
 
+class AstGlobal : public AstNode
+{
+    AstName name_;
+    uint32_t flags_;
+    ValType type_;
+    Maybe<AstExpr*> init_;
+
+  public:
+    AstGlobal() : flags_(0), type_(ValType::Limit)
+    {}
+
+    explicit AstGlobal(AstName name, ValType type, uint32_t flags,
+                       Maybe<AstExpr*> init = Maybe<AstExpr*>())
+      : name_(name), flags_(flags), type_(type), init_(init)
+    {}
+
+    AstName name() const { return name_; }
+    uint32_t flags() const { return flags_; }
+    ValType type() const { return type_; }
+
+    bool hasInit() const { return !!init_; }
+    AstExpr& init() const { MOZ_ASSERT(hasInit()); return **init_; }
+};
+
+typedef AstVector<AstGlobal*> AstGlobalVector;
+
 class AstImport : public AstNode
 {
     AstName name_;
     AstName module_;
     AstName field_;
     DefinitionKind kind_;
+
     AstRef funcSig_;
     AstResizable resizable_;
+    AstGlobal global_;
 
   public:
     AstImport(AstName name, AstName module, AstName field, AstRef funcSig)
       : name_(name), module_(module), field_(field), kind_(DefinitionKind::Function), funcSig_(funcSig)
     {}
     AstImport(AstName name, AstName module, AstName field, DefinitionKind kind, AstResizable resizable)
       : name_(name), module_(module), field_(field), kind_(kind), resizable_(resizable)
     {}
+    AstImport(AstName name, AstName module, AstName field, AstGlobal global)
+      : name_(name), module_(module), field_(field), kind_(DefinitionKind::Global), global_(global)
+    {}
+
     AstName name() const { return name_; }
     AstName module() const { return module_; }
     AstName field() const { return field_; }
+
     DefinitionKind kind() const { return kind_; }
-    AstRef& funcSig() { MOZ_ASSERT(kind_ == DefinitionKind::Function); return funcSig_; }
-    AstResizable resizable() const { MOZ_ASSERT(kind_ != DefinitionKind::Function); return resizable_; }
+    AstRef& funcSig() {
+        MOZ_ASSERT(kind_ == DefinitionKind::Function);
+        return funcSig_;
+    }
+    AstResizable resizable() const {
+        MOZ_ASSERT(kind_ == DefinitionKind::Memory || kind_ == DefinitionKind::Table);
+        return resizable_;
+    }
+    const AstGlobal& global() const {
+        MOZ_ASSERT(kind_ == DefinitionKind::Global);
+        return global_;
+    }
 };
 
 class AstExport : public AstNode
 {
     AstName name_;
     DefinitionKind kind_;
-    AstRef func_;
+    AstRef ref_;
 
   public:
-    AstExport(AstName name, AstRef func)
-      : name_(name), kind_(DefinitionKind::Function), func_(func)
+    AstExport(AstName name, DefinitionKind kind, AstRef ref)
+      : name_(name), kind_(kind), ref_(ref)
     {}
     explicit AstExport(AstName name, DefinitionKind kind)
       : name_(name), kind_(kind)
     {}
     AstName name() const { return name_; }
     DefinitionKind kind() const { return kind_; }
-    AstRef& func() { MOZ_ASSERT(kind_ == DefinitionKind::Function); return func_; }
+    AstRef& ref() {
+        MOZ_ASSERT(kind_ == DefinitionKind::Function || kind_ == DefinitionKind::Global);
+        return ref_;
+    }
 };
 
 class AstDataSegment : public AstNode
 {
     uint32_t offset_;
     AstName text_;
 
   public:
@@ -581,24 +664,25 @@ class AstDataSegment : public AstNode
     uint32_t offset() const { return offset_; }
     AstName text() const { return text_; }
 };
 
 typedef AstVector<AstDataSegment*> AstDataSegmentVector;
 
 class AstElemSegment : public AstNode
 {
-    uint32_t offset_;
+    AstExpr* offset_;
     AstRefVector elems_;
 
   public:
-    AstElemSegment(uint32_t offset, AstRefVector&& elems)
+    AstElemSegment(AstExpr* offset, AstRefVector&& elems)
       : offset_(offset), elems_(Move(elems))
     {}
-    uint32_t offset() const { return offset_; }
+
+    AstExpr* offset() const { return offset_; }
     AstRefVector& elems() { return elems_; }
     const AstRefVector& elems() const { return elems_; }
 };
 
 typedef AstVector<AstElemSegment*> AstElemSegmentVector;
 
 class AstStartFunc : public AstNode
 {
@@ -631,27 +715,29 @@ class AstModule : public AstNode
     ImportVector         imports_;
     Maybe<AstResizable>  table_;
     Maybe<AstResizable>  memory_;
     ExportVector         exports_;
     Maybe<AstStartFunc>  startFunc_;
     FuncVector           funcs_;
     AstDataSegmentVector dataSegments_;
     AstElemSegmentVector elemSegments_;
+    AstGlobalVector      globals_;
 
   public:
     explicit AstModule(LifoAlloc& lifo)
       : lifo_(lifo),
         sigs_(lifo),
         sigMap_(lifo),
         imports_(lifo),
         exports_(lifo),
         funcs_(lifo),
         dataSegments_(lifo),
-        elemSegments_(lifo)
+        elemSegments_(lifo),
+        globals_(lifo)
     {}
     bool init() {
         return sigMap_.init();
     }
     bool setMemory(AstResizable memory) {
         if (memory_)
             return false;
         memory_.emplace(memory);
@@ -722,28 +808,34 @@ class AstModule : public AstNode
         return sigs_;
     }
     bool append(AstFunc* func) {
         return funcs_.append(func);
     }
     const FuncVector& funcs() const {
         return funcs_;
     }
+    bool append(AstImport* imp) {
+        return imports_.append(imp);
+    }
     const ImportVector& imports() const {
         return imports_;
     }
-    bool append(AstImport* imp) {
-        return imports_.append(imp);
-    }
     bool append(AstExport* exp) {
         return exports_.append(exp);
     }
     const ExportVector& exports() const {
         return exports_;
     }
+    bool append(AstGlobal* glob) {
+        return globals_.append(glob);
+    }
+    const AstGlobalVector& globals() const {
+        return globals_;
+    }
 };
 
 class AstUnaryOperator final : public AstExpr
 {
     Expr expr_;
     AstExpr* op_;
 
   public:
--- a/js/src/asmjs/WasmBaselineCompile.cpp
+++ b/js/src/asmjs/WasmBaselineCompile.cpp
@@ -5463,38 +5463,59 @@ BaseCompiler::emitGetGlobal()
     if (!iter_.readGetGlobal(mg_.globals, &id))
         return false;
 
     if (deadCode_)
         return true;
 
     const GlobalDesc& global = mg_.globals[id];
 
-    switch (global.type) {
+    if (global.isConstant()) {
+        Val value = global.constantValue();
+        switch (value.type()) {
+          case ValType::I32:
+            pushI32(value.i32());
+            break;
+          case ValType::I64:
+            pushI64(value.i64());
+            break;
+          case ValType::F32:
+            pushF32(value.f32());
+            break;
+          case ValType::F64:
+            pushF64(value.f64());
+            break;
+          default:
+            MOZ_CRASH("Global constant type");
+        }
+        return true;
+    }
+
+    switch (global.type()) {
       case ValType::I32: {
         RegI32 rv = needI32();
-        loadGlobalVarI32(global.globalDataOffset, rv);
+        loadGlobalVarI32(global.offset(), rv);
         pushI32(rv);
         break;
       }
       case ValType::I64: {
         RegI64 rv = needI64();
-        loadGlobalVarI64(global.globalDataOffset, rv);
+        loadGlobalVarI64(global.offset(), rv);
         pushI64(rv);
         break;
       }
       case ValType::F32: {
         RegF32 rv = needF32();
-        loadGlobalVarF32(global.globalDataOffset, rv);
+        loadGlobalVarF32(global.offset(), rv);
         pushF32(rv);
         break;
       }
       case ValType::F64: {
         RegF64 rv = needF64();
-        loadGlobalVarF64(global.globalDataOffset, rv);
+        loadGlobalVarF64(global.offset(), rv);
         pushF64(rv);
         break;
       }
       default:
         MOZ_CRASH("Global variable type");
         break;
     }
     return true;
@@ -5508,38 +5529,38 @@ BaseCompiler::emitSetGlobal()
     if (!iter_.readSetGlobal(mg_.globals, &id, &unused_value))
         return false;
 
     if (deadCode_)
         return true;
 
     const GlobalDesc& global = mg_.globals[id];
 
-    switch (global.type) {
+    switch (global.type()) {
       case ValType::I32: {
         RegI32 rv = popI32();
-        storeGlobalVarI32(global.globalDataOffset, rv);
+        storeGlobalVarI32(global.offset(), rv);
         pushI32(rv);
         break;
       }
       case ValType::I64: {
         RegI64 rv = popI64();
-        storeGlobalVarI64(global.globalDataOffset, rv);
+        storeGlobalVarI64(global.offset(), rv);
         pushI64(rv);
         break;
       }
       case ValType::F32: {
         RegF32 rv = popF32();
-        storeGlobalVarF32(global.globalDataOffset, rv);
+        storeGlobalVarF32(global.offset(), rv);
         pushF32(rv);
         break;
       }
       case ValType::F64: {
         RegF64 rv = popF64();
-        storeGlobalVarF64(global.globalDataOffset, rv);
+        storeGlobalVarF64(global.offset(), rv);
         pushF64(rv);
         break;
       }
       default:
         MOZ_CRASH("Global variable type");
         break;
     }
     return true;
@@ -6031,19 +6052,19 @@ BaseCompiler::emitBody()
           case Expr::CallImport:
             CHECK_NEXT(emitCallImport(exprOffset));
 
           // Locals and globals
           case Expr::GetLocal:
             CHECK_NEXT(emitGetLocal());
           case Expr::SetLocal:
             CHECK_NEXT(emitSetLocal());
-          case Expr::LoadGlobal:
+          case Expr::GetGlobal:
             CHECK_NEXT(emitGetGlobal());
-          case Expr::StoreGlobal:
+          case Expr::SetGlobal:
             CHECK_NEXT(emitSetGlobal());
 
           // Select
           case Expr::Select:
             CHECK_NEXT(emitSelect());
 
           // I32
           case Expr::I32Const: {
--- a/js/src/asmjs/WasmBinary.h
+++ b/js/src/asmjs/WasmBinary.h
@@ -23,16 +23,17 @@
 
 namespace js {
 namespace wasm {
 
 static const uint32_t MagicNumber        = 0x6d736100; // "\0asm"
 static const uint32_t EncodingVersion    = 0x0b;
 
 static const char TypeSectionId[]        = "type";
+static const char GlobalSectionId[]      = "global";
 static const char ImportSectionId[]      = "import";
 static const char FunctionSectionId[]    = "function";
 static const char TableSectionId[]       = "table";
 static const char MemorySectionId[]      = "memory";
 static const char ExportSectionId[]      = "export";
 static const char StartSectionId[]       = "start";
 static const char CodeSectionId[]        = "code";
 static const char ElemSectionId[]        = "elem";
@@ -65,26 +66,33 @@ enum class TypeConstructor
 {
     Function                             = 0x40
 };
 
 enum class DefinitionKind
 {
     Function                             = 0x00,
     Table                                = 0x01,
-    Memory                               = 0x02
+    Memory                               = 0x02,
+    Global                               = 0x03
 };
 
 enum class ResizableFlags
 {
     Default                              = 0x1,
     HasMaximum                           = 0x2,
     AllowedMask                          = 0x3
 };
 
+enum class GlobalFlags
+{
+    IsMutable                            = 0x1,
+    AllowedMask                          = 0x1
+};
+
 enum class Expr
 {
     // Control flow operators
     Nop                                  = 0x00,
     Block                                = 0x01,
     Loop                                 = 0x02,
     If                                   = 0x03,
     Else                                 = 0x04,
@@ -266,23 +274,25 @@ enum class Expr
     I32Rotr                              = 0xb6,
     I32Rotl                              = 0xb7,
     I64Rotr                              = 0xb8,
     I64Rotl                              = 0xb9,
 
     // i64.eqz.
     I64Eqz                               = 0xba,
 
+    // Global access.
+    GetGlobal                            = 0xc0,
+    SetGlobal                            = 0xc1,
+
     // ------------------------------------------------------------------------
     // The rest of these operators are currently only emitted internally when
     // compiling asm.js and are rejected by wasm validation.
 
     // asm.js-specific operators
-    LoadGlobal                           = 0xc0,
-    StoreGlobal,
     I32Min,
     I32Max,
     I32Neg,
     I32BitNot,
     I32Abs,
     F32StoreF64,
     F64StoreF32,
     F64Mod,
--- a/js/src/asmjs/WasmBinaryIterator.cpp
+++ b/js/src/asmjs/WasmBinaryIterator.cpp
@@ -324,20 +324,20 @@ wasm::Classify(Expr expr)
       case Expr::F32x4store:
       case Expr::F32x4store1:
       case Expr::F32x4store2:
       case Expr::F32x4store3:
         return ExprKind::Store;
       case Expr::Select:
         return ExprKind::Select;
       case Expr::GetLocal:
-      case Expr::LoadGlobal:
+      case Expr::GetGlobal:
         return ExprKind::GetVar;
       case Expr::SetLocal:
-      case Expr::StoreGlobal:
+      case Expr::SetGlobal:
         return ExprKind::SetVar;
       case Expr::Call:
         return ExprKind::Call;
       case Expr::CallIndirect:
         return ExprKind::CallIndirect;
       case Expr::CallImport:
         return ExprKind::CallImport;
       case Expr::Return:
--- a/js/src/asmjs/WasmBinaryIterator.h
+++ b/js/src/asmjs/WasmBinaryIterator.h
@@ -1206,17 +1206,17 @@ ExprIter<Policy>::readGetGlobal(const Gl
 
     uint32_t validateId;
     if (!readVarU32(&validateId))
         return false;
 
     if (Validate && validateId >= globals.length())
         return fail("get_global index out of range");
 
-    if (!push(ToExprType(globals[validateId].type)))
+    if (!push(ToExprType(globals[validateId].type())))
         return false;
 
     if (Output)
         *id = validateId;
 
     return true;
 }
 
@@ -1228,17 +1228,20 @@ ExprIter<Policy>::readSetGlobal(const Gl
 
     uint32_t validateId;
     if (!readVarU32(&validateId))
         return false;
 
     if (Validate && validateId >= globals.length())
         return fail("set_global index out of range");
 
-    if (!topWithType(ToExprType(globals[validateId].type), value))
+    if (Validate && !globals[validateId].isMutable())
+        return fail("can't write an immutable global");
+
+    if (!topWithType(ToExprType(globals[validateId].type()), value))
         return false;
 
     if (Output)
         *id = validateId;
 
     return true;
 }
 
--- a/js/src/asmjs/WasmBinaryToAST.cpp
+++ b/js/src/asmjs/WasmBinaryToAST.cpp
@@ -1387,30 +1387,96 @@ AstDecodeMemorySection(AstDecodeContext&
     if (!c.d.finishSection(sectionStart, sectionSize))
         return AstDecodeFail(c, "memory section byte size mismatch");
 
     c.module().setMemory(AstResizable(initialSizePages, Some(maxSizePages)));
     return true;
 }
 
 static bool
+AstDecodeGlobal(AstDecodeContext& c, uint32_t i, AstGlobal* global)
+{
+    AstName name;
+    if (!AstDecodeGenerateName(c, AstName(u"global"), i, &name))
+        return false;
+
+    ValType type;
+    if (!c.d.readValType(&type))
+        return AstDecodeFail(c, "bad global type");
+
+    uint32_t flags;
+    if (!c.d.readVarU32(&flags))
+        return AstDecodeFail(c, "expected flags");
+
+    if (flags & ~uint32_t(GlobalFlags::AllowedMask))
+        return AstDecodeFail(c, "unexpected bits set in flags");
+
+    if (!AstDecodeExpr(c))
+        return false;
+
+    AstDecodeStackItem item = c.iter().getResult();
+    MOZ_ASSERT(item.popped == 0);
+    if (!item.expr)
+        return AstDecodeFail(c, "missing initializer expression");
+
+    AstExpr* init = item.expr;
+
+    if (!AstDecodeEnd(c))
+        return AstDecodeFail(c, "missing initializer end");
+
+    item = c.iter().getResult();
+    MOZ_ASSERT(item.terminationKind == AstDecodeTerminationKind::End);
+
+    *global = AstGlobal(name, type, flags, Some(init));
+    return true;
+}
+
+static bool
+AstDecodeGlobalSection(AstDecodeContext& c)
+{
+    uint32_t sectionStart, sectionSize;
+    if (!c.d.startSection(GlobalSectionId, &sectionStart, &sectionSize))
+        return AstDecodeFail(c, "failed to start section");
+    if (sectionStart == Decoder::NotStarted)
+        return true;
+
+    uint32_t numGlobals;
+    if (!c.d.readVarU32(&numGlobals))
+        return AstDecodeFail(c, "expected number of globals");
+
+    for (uint32_t i = 0; i < numGlobals; i++) {
+        auto* global = new(c.lifo) AstGlobal;
+        if (!AstDecodeGlobal(c, i, global))
+            return false;
+        if (!c.module().append(global))
+            return false;
+    }
+
+    if (!c.d.finishSection(sectionStart, sectionSize))
+        return AstDecodeFail(c, "globals section byte size mismatch");
+
+    return true;
+}
+
+static bool
 AstDecodeFunctionExport(AstDecodeContext& c, AstExport** export_)
 {
     uint32_t funcIndex;
     if (!c.d.readVarU32(&funcIndex))
         return AstDecodeFail(c, "expected export internal index");
 
     if (funcIndex >= c.funcSigs().length())
         return AstDecodeFail(c, "export function index out of range");
 
     AstName fieldName;
     if (!AstDecodeName(c, &fieldName))
         return AstDecodeFail(c, "expected export name");
 
-    *export_ = new(c.lifo) AstExport(fieldName, AstRef(AstName(), funcIndex));
+    *export_ = new(c.lifo) AstExport(fieldName, DefinitionKind::Function,
+                                     AstRef(AstName(), funcIndex));
     if (!*export_)
         return false;
 
     return true;
 }
 
 static bool
 AstDecodeExportSection(AstDecodeContext& c)
@@ -1642,16 +1708,19 @@ wasm::BinaryToAst(JSContext* cx, const u
         return false;
 
     if (!AstDecodeTableSection(c))
         return false;
 
     if (!AstDecodeMemorySection(c))
         return false;
 
+    if (!AstDecodeGlobalSection(c))
+        return false;
+
     if (!AstDecodeExportSection(c))
         return false;
 
     if (!AstDecodeCodeSection(c))
         return false;
 
     if (!AstDecodeDataSection(c))
         return false;
--- a/js/src/asmjs/WasmBinaryToExperimentalText.cpp
+++ b/js/src/asmjs/WasmBinaryToExperimentalText.cpp
@@ -1560,19 +1560,19 @@ PrintExport(WasmPrintContext& c, AstExpo
     if (!PrintIndent(c))
         return false;
     if (!c.buffer.append("export "))
         return false;
     if (export_.kind() == DefinitionKind::Memory) {
         if (!c.buffer.append("memory"))
           return false;
     } else {
-        const AstFunc* func = funcs[export_.func().index()];
+        const AstFunc* func = funcs[export_.ref().index()];
         if (func->name().empty()) {
-            if (!PrintInt32(c, export_.func().index()))
+            if (!PrintInt32(c, export_.ref().index()))
                 return false;
         } else {
             if (!PrintName(c, func->name()))
                 return false;
         }
     }
     if (!c.buffer.append(" as \""))
         return false;
--- a/js/src/asmjs/WasmBinaryToText.cpp
+++ b/js/src/asmjs/WasmBinaryToText.cpp
@@ -1169,19 +1169,19 @@ RenderExport(WasmRenderContext& c, AstEx
     if (!RenderEscapedString(c, export_.name()))
         return false;
     if (!c.buffer.append("\" "))
         return false;
     if (export_.kind() == DefinitionKind::Memory) {
         if (!c.buffer.append("memory"))
           return false;
     } else {
-        const AstFunc* func = funcs[export_.func().index()];
+        const AstFunc* func = funcs[export_.ref().index()];
         if (func->name().empty()) {
-            if (!RenderInt32(c, export_.func().index()))
+            if (!RenderInt32(c, export_.ref().index()))
                 return false;
         } else {
             if (!RenderName(c, func->name()))
                 return false;
         }
     }
     if (!c.buffer.append(")\n"))
         return false;
--- a/js/src/asmjs/WasmCode.cpp
+++ b/js/src/asmjs/WasmCode.cpp
@@ -412,16 +412,17 @@ CacheableChars::sizeOfExcludingThis(Mall
 
 size_t
 Metadata::serializedSize() const
 {
     return sizeof(pod()) +
            SerializedVectorSize(funcImports) +
            SerializedVectorSize(funcExports) +
            SerializedVectorSize(sigIds) +
+           SerializedPodVectorSize(globals) +
            SerializedPodVectorSize(tables) +
            SerializedPodVectorSize(memoryAccesses) +
            SerializedPodVectorSize(boundsChecks) +
            SerializedPodVectorSize(codeRanges) +
            SerializedPodVectorSize(callSites) +
            SerializedPodVectorSize(callThunks) +
            SerializedPodVectorSize(funcNames) +
            filename.serializedSize() +
@@ -430,16 +431,17 @@ Metadata::serializedSize() const
 
 uint8_t*
 Metadata::serialize(uint8_t* cursor) const
 {
     cursor = WriteBytes(cursor, &pod(), sizeof(pod()));
     cursor = SerializeVector(cursor, funcImports);
     cursor = SerializeVector(cursor, funcExports);
     cursor = SerializeVector(cursor, sigIds);
+    cursor = SerializePodVector(cursor, globals);
     cursor = SerializePodVector(cursor, tables);
     cursor = SerializePodVector(cursor, memoryAccesses);
     cursor = SerializePodVector(cursor, boundsChecks);
     cursor = SerializePodVector(cursor, codeRanges);
     cursor = SerializePodVector(cursor, callSites);
     cursor = SerializePodVector(cursor, callThunks);
     cursor = SerializePodVector(cursor, funcNames);
     cursor = filename.serialize(cursor);
@@ -449,16 +451,17 @@ Metadata::serialize(uint8_t* cursor) con
 
 /* static */ const uint8_t*
 Metadata::deserialize(const uint8_t* cursor)
 {
     (cursor = ReadBytes(cursor, &pod(), sizeof(pod()))) &&
     (cursor = DeserializeVector(cursor, &funcImports)) &&
     (cursor = DeserializeVector(cursor, &funcExports)) &&
     (cursor = DeserializeVector(cursor, &sigIds)) &&
+    (cursor = DeserializePodVector(cursor, &globals)) &&
     (cursor = DeserializePodVector(cursor, &tables)) &&
     (cursor = DeserializePodVector(cursor, &memoryAccesses)) &&
     (cursor = DeserializePodVector(cursor, &boundsChecks)) &&
     (cursor = DeserializePodVector(cursor, &codeRanges)) &&
     (cursor = DeserializePodVector(cursor, &callSites)) &&
     (cursor = DeserializePodVector(cursor, &callThunks)) &&
     (cursor = DeserializePodVector(cursor, &funcNames)) &&
     (cursor = filename.deserialize(cursor)) &&
@@ -467,16 +470,17 @@ Metadata::deserialize(const uint8_t* cur
 }
 
 size_t
 Metadata::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
 {
     return SizeOfVectorExcludingThis(funcImports, mallocSizeOf) +
            SizeOfVectorExcludingThis(funcExports, mallocSizeOf) +
            SizeOfVectorExcludingThis(sigIds, mallocSizeOf) +
+           globals.sizeOfExcludingThis(mallocSizeOf) +
            tables.sizeOfExcludingThis(mallocSizeOf) +
            memoryAccesses.sizeOfExcludingThis(mallocSizeOf) +
            boundsChecks.sizeOfExcludingThis(mallocSizeOf) +
            codeRanges.sizeOfExcludingThis(mallocSizeOf) +
            callSites.sizeOfExcludingThis(mallocSizeOf) +
            callThunks.sizeOfExcludingThis(mallocSizeOf) +
            funcNames.sizeOfExcludingThis(mallocSizeOf) +
            filename.sizeOfExcludingThis(mallocSizeOf) +
--- a/js/src/asmjs/WasmCode.h
+++ b/js/src/asmjs/WasmCode.h
@@ -419,17 +419,16 @@ typedef Vector<char16_t, 64> TwoByteName
 
 // Metadata holds all the data that is needed to describe compiled wasm code
 // at runtime (as opposed to data that is only used to statically link or
 // instantiate a module).
 //
 // Metadata is built incrementally by ModuleGenerator and then shared immutably
 // between modules.
 
-
 class MetadataCacheablePod
 {
     static const uint32_t NO_START_FUNCTION = UINT32_MAX;
     static_assert(NO_START_FUNCTION > MaxFuncs, "sentinel value");
 
     uint32_t              startFuncIndex_;
 
   public:
@@ -462,16 +461,17 @@ struct Metadata : ShareableBase<Metadata
     virtual ~Metadata() {}
 
     MetadataCacheablePod& pod() { return *this; }
     const MetadataCacheablePod& pod() const { return *this; }
 
     FuncImportVector      funcImports;
     FuncExportVector      funcExports;
     SigWithIdVector       sigIds;
+    GlobalDescVector      globals;
     TableDescVector       tables;
     MemoryAccessVector    memoryAccesses;
     BoundsCheckVector     boundsChecks;
     CodeRangeVector       codeRanges;
     CallSiteVector        callSites;
     CallThunkVector       callThunks;
     NameInBytecodeVector  funcNames;
     CacheableChars        filename;
--- a/js/src/asmjs/WasmCompile.cpp
+++ b/js/src/asmjs/WasmCompile.cpp
@@ -231,16 +231,20 @@ DecodeExpr(FunctionDecoder& f)
       case Expr::F32Const:
         return f.iter().readF32Const(nullptr);
       case Expr::F64Const:
         return f.iter().readF64Const(nullptr);
       case Expr::GetLocal:
         return f.iter().readGetLocal(f.locals(), nullptr);
       case Expr::SetLocal:
         return f.iter().readSetLocal(f.locals(), nullptr, nullptr);
+      case Expr::GetGlobal:
+        return f.iter().readGetGlobal(f.mg().globals(), nullptr);
+      case Expr::SetGlobal:
+        return f.iter().readSetGlobal(f.mg().globals(), nullptr, nullptr);
       case Expr::Select:
         return f.iter().readSelect(nullptr, nullptr, nullptr, nullptr);
       case Expr::Block:
         return f.iter().readBlock();
       case Expr::Loop:
         return f.iter().readLoop();
       case Expr::If:
         return f.iter().readIf(nullptr);
@@ -742,16 +746,58 @@ DecodeResizableTable(Decoder& d, ModuleG
 
     TableDesc table(TableKind::AnyFunction);
     table.initial = resizable.initial;
     table.maximum = resizable.maximum ? *resizable.maximum : UINT32_MAX;
     return init->tables.append(table);
 }
 
 static bool
+DecodeGlobalType(Decoder& d, ValType* type, bool* isMutable)
+{
+    if (!d.readValType(type))
+        return Fail(d, "bad global type");
+
+    if (*type == ValType::I64 && !IsI64Implemented())
+        return Fail(d, "int64 NYI");
+
+    uint32_t flags;
+    if (!d.readVarU32(&flags))
+        return Fail(d, "expected flags");
+
+    if (flags & ~uint32_t(GlobalFlags::AllowedMask))
+        return Fail(d, "unexpected bits set in flags");
+
+    *isMutable = flags & uint32_t(GlobalFlags::IsMutable);
+    return true;
+}
+
+static bool
+GlobalIsJSCompatible(Decoder& d, ValType type, bool isMutable)
+{
+    switch (type) {
+      case ValType::I32:
+      case ValType::F32:
+      case ValType::F64:
+        break;
+      case ValType::I64:
+        if (!JitOptions.wasmTestMode)
+            return Fail(d, "can't import/export an Int64 global to JS");
+        break;
+      default:
+        return Fail(d, "unexpected variable type in global import/export");
+    }
+
+    if (isMutable)
+        return Fail(d, "can't import/export mutable globals in the MVP");
+
+    return true;
+}
+
+static bool
 DecodeImport(Decoder& d, bool newFormat, ModuleGeneratorData* init, ImportVector* imports)
 {
     if (!newFormat) {
         const SigWithId* sig = nullptr;
         if (!DecodeSignatureIndex(d, *init, &sig))
             return false;
 
         if (!CheckTypeForJS(d, *sig))
@@ -805,16 +851,27 @@ DecodeImport(Decoder& d, bool newFormat,
             return false;
         break;
       }
       case DefinitionKind::Memory: {
         if (!DecodeResizableMemory(d, init))
             return false;
         break;
       }
+      case DefinitionKind::Global: {
+        ValType type;
+        bool isMutable;
+        if (!DecodeGlobalType(d, &type, &isMutable))
+            return false;
+        if (!GlobalIsJSCompatible(d, type, isMutable))
+            return false;
+        if (!init->globals.append(GlobalDesc(type, isMutable, init->globals.length())))
+            return false;
+        break;
+      }
       default:
         return Fail(d, "unsupported import kind");
     }
 
     return imports->emplaceBack(Move(moduleName), Move(funcName), DefinitionKind(importKind));
 }
 
 static bool
@@ -941,16 +998,115 @@ DecodeMemorySection(Decoder& d, bool new
     }
 
     if (!d.finishSection(sectionStart, sectionSize))
         return Fail(d, "memory section byte size mismatch");
 
     return true;
 }
 
+static bool
+DecodeInitializerExpression(Decoder& d, const GlobalDescVector& globals, ValType expected,
+                            InitExpr* init)
+{
+    Expr expr;
+    if (!d.readExpr(&expr))
+        return Fail(d, "failed to read initializer type");
+
+    switch (expr) {
+      case Expr::I32Const: {
+        int32_t i32;
+        if (!d.readVarS32(&i32))
+            return Fail(d, "failed to read initializer i32 expression");
+        *init = InitExpr(Val(uint32_t(i32)));
+        break;
+      }
+      case Expr::I64Const: {
+        int64_t i64;
+        if (!d.readVarS64(&i64))
+            return Fail(d, "failed to read initializer i64 expression");
+        *init = InitExpr(Val(uint64_t(i64)));
+        break;
+      }
+      case Expr::F32Const: {
+        float f32;
+        if (!d.readFixedF32(&f32))
+            return Fail(d, "failed to read initializer f32 expression");
+        *init = InitExpr(Val(f32));
+        break;
+      }
+      case Expr::F64Const: {
+        double f64;
+        if (!d.readFixedF64(&f64))
+            return Fail(d, "failed to read initializer f64 expression");
+        *init = InitExpr(Val(f64));
+        break;
+      }
+      case Expr::GetGlobal: {
+        uint32_t i;
+        if (!d.readVarU32(&i))
+            return Fail(d, "failed to read get_global index in initializer expression");
+        if (i >= globals.length())
+            return Fail(d, "global index out of range in initializer expression");
+        if (!globals[i].isImport() || globals[i].isMutable())
+            return Fail(d, "initializer expression must reference a global immutable import");
+        *init = InitExpr(i, globals[i].type());
+        break;
+      }
+      default: {
+        return Fail(d, "unexpected initializer expression");
+      }
+    }
+
+    if (expected != init->type())
+        return Fail(d, "type mismatch: initializer type and expected type don't match");
+
+    Expr end;
+    if (!d.readExpr(&end) || end != Expr::End)
+        return Fail(d, "failed to read end of initializer expression");
+
+    return true;
+}
+
+static bool
+DecodeGlobalSection(Decoder& d, ModuleGeneratorData* init)
+{
+    uint32_t sectionStart, sectionSize;
+    if (!d.startSection(GlobalSectionId, &sectionStart, &sectionSize))
+        return Fail(d, "failed to start section");
+    if (sectionStart == Decoder::NotStarted)
+        return true;
+
+    uint32_t numGlobals;
+    if (!d.readVarU32(&numGlobals))
+        return Fail(d, "expected number of globals");
+
+    if (numGlobals > MaxGlobals)
+        return Fail(d, "too many globals");
+
+    for (uint32_t i = 0; i < numGlobals; i++) {
+        ValType type;
+        bool isMutable;
+        if (!DecodeGlobalType(d, &type, &isMutable))
+            return false;
+
+        InitExpr initializer;
+        if (!DecodeInitializerExpression(d, init->globals, type, &initializer))
+            return false;
+
+        if (!init->globals.append(GlobalDesc(initializer, isMutable)))
+            return false;
+    }
+
+    if (!d.finishSection(sectionStart, sectionSize))
+        return Fail(d, "globals section byte size mismatch");
+
+    return true;
+}
+
 typedef HashSet<const char*, CStringHasher, SystemAllocPolicy> CStringSet;
 
 static UniqueChars
 DecodeExportName(Decoder& d, CStringSet* dupSet)
 {
     UniqueChars exportName = MaybeDecodeName(d);
     if (!exportName) {
         Fail(d, "expected valid export name");
@@ -1027,16 +1183,30 @@ DecodeExport(Decoder& d, bool newFormat,
         if (!d.readVarU32(&memoryIndex))
             return Fail(d, "expected memory index");
 
         if (memoryIndex > 0 || !mg.usesMemory())
             return Fail(d, "exported memory index out of bounds");
 
         return mg.addMemoryExport(Move(fieldName));
       }
+      case DefinitionKind::Global: {
+        uint32_t globalIndex;
+        if (!d.readVarU32(&globalIndex))
+            return Fail(d, "expected global index");
+
+        if (globalIndex >= mg.globals().length())
+            return Fail(d, "exported global index out of bounds");
+
+        const GlobalDesc& global = mg.globals()[globalIndex];
+        if (!GlobalIsJSCompatible(d, global.type(), global.isMutable()))
+            return false;
+
+        return mg.addGlobalExport(Move(fieldName), globalIndex);
+      }
       default:
         return Fail(d, "unexpected export kind");
     }
 
     MOZ_CRASH("unreachable");
 }
 
 static bool
@@ -1128,17 +1298,16 @@ DecodeFunctionBody(Decoder& d, ModuleGen
     memcpy(fg.bytes().begin(), bodyBegin, bodySize);
 
     return mg.finishFuncDef(funcIndex, &fg);
 }
 
 static bool
 DecodeStartSection(Decoder& d, ModuleGenerator& mg)
 {
-
     uint32_t sectionStart, sectionSize;
     if (!d.startSection(StartSectionId, &sectionStart, &sectionSize))
         return Fail(d, "failed to start section");
     if (sectionStart == Decoder::NotStarted)
         return true;
 
     uint32_t startFuncIndex;
     if (!d.readVarU32(&startFuncIndex))
@@ -1202,17 +1371,17 @@ static bool
 DecodeElemSection(Decoder& d, bool newFormat, Uint32Vector&& oldElems, ModuleGenerator& mg)
 {
     if (!newFormat) {
         if (mg.tables().empty()) {
             MOZ_ASSERT(oldElems.empty());
             return true;
         }
 
-        return mg.addElemSegment(ElemSegment(0, 0, Move(oldElems)));
+        return mg.addElemSegment(ElemSegment(0, InitExpr(Val(uint32_t(0))), Move(oldElems)));
     }
 
     uint32_t sectionStart, sectionSize;
     if (!d.startSection(ElemSectionId, &sectionStart, &sectionSize))
         return Fail(d, "failed to start section");
     if (sectionStart == Decoder::NotStarted)
         return true;
 
@@ -1226,48 +1395,45 @@ DecodeElemSection(Decoder& d, bool newFo
     for (uint32_t i = 0, prevEnd = 0; i < numSegments; i++) {
         ElemSegment seg;
         if (!d.readVarU32(&seg.tableIndex))
             return Fail(d, "expected table index");
 
         if (seg.tableIndex >= mg.tables().length())
             return Fail(d, "table index out of range");
 
-        Expr expr;
-        if (!d.readExpr(&expr))
-            return Fail(d, "failed to read initializer expression");
+        if (!DecodeInitializerExpression(d, mg.globals(), ValType::I32, &seg.offset))
+            return false;
 
-        if (expr != Expr::I32Const)
-            return Fail(d, "expected i32.const initializer expression");
-
-        if (!d.readVarU32(&seg.offset))
-            return Fail(d, "expected elem segment destination offset");
-
-        if (seg.offset < prevEnd)
+        if (seg.offset.isVal() && seg.offset.val().i32() < prevEnd)
             return Fail(d, "elem segments must be disjoint and ordered");
 
         uint32_t numElems;
         if (!d.readVarU32(&numElems))
             return Fail(d, "expected segment size");
 
         uint32_t tableLength = mg.tables()[seg.tableIndex].initial;
-        if (seg.offset > tableLength || tableLength - seg.offset < numElems)
-            return Fail(d, "element segment does not fit");
+        if (seg.offset.isVal()) {
+            uint32_t offset = seg.offset.val().i32();
+            if (offset > tableLength || tableLength - offset < numElems)
+                return Fail(d, "element segment does not fit");
+        }
 
         if (!seg.elems.resize(numElems))
             return false;
 
         for (uint32_t i = 0; i < numElems; i++) {
             if (!d.readVarU32(&seg.elems[i]))
                 return Fail(d, "failed to read element function index");
             if (seg.elems[i] >= mg.numFuncSigs())
                 return Fail(d, "table element out of range");
         }
 
-        prevEnd = seg.offset + seg.elems.length();
+        if (seg.offset.isVal())
+            prevEnd = seg.offset.val().i32() + seg.elems.length();
 
         if (!mg.addElemSegment(Move(seg)))
             return false;
     }
 
     if (!d.finishSection(sectionStart, sectionSize))
         return Fail(d, "data section byte size mismatch");
 
@@ -1454,16 +1620,19 @@ wasm::Compile(const ShareableBytes& byte
     Uint32Vector oldElems;
     if (!DecodeTableSection(d, newFormat, init.get(), &oldElems))
         return nullptr;
 
     bool memoryExported = false;
     if (!DecodeMemorySection(d, newFormat, init.get(), &memoryExported))
         return nullptr;
 
+    if (!DecodeGlobalSection(d, init.get()))
+        return nullptr;
+
     ModuleGenerator mg(Move(imports));
     if (!mg.init(Move(init), Move(args)))
         return nullptr;
 
     if (!DecodeExportSection(d, newFormat, memoryExported, mg))
         return nullptr;
 
     if (!DecodeStartSection(d, mg))
--- a/js/src/asmjs/WasmGenerator.cpp
+++ b/js/src/asmjs/WasmGenerator.cpp
@@ -167,16 +167,23 @@ ModuleGenerator::init(UniqueModuleGenera
                     return false;
 
                 if (!metadata_->sigIds.emplaceBack(Move(copy), sig.id))
                     return false;
             } else {
                 sig.id = SigIdDesc::immediate(sig);
             }
         }
+
+        for (GlobalDesc& global : shared_->globals) {
+            if (global.isConstant())
+                continue;
+            if (!allocateGlobal(&global))
+                return false;
+        }
     } else {
         MOZ_ASSERT(shared_->sigs.length() == MaxSigs);
         MOZ_ASSERT(shared_->tables.length() == MaxTables);
         MOZ_ASSERT(shared_->asmJSSigToTableIndex.length() == MaxSigs);
     }
 
     return true;
 }
@@ -585,21 +592,21 @@ ModuleGenerator::allocateGlobalBytes(uin
     if (!newGlobalDataLength.isValid())
         return false;
 
     linkData_.globalDataLength = newGlobalDataLength.value();
     return true;
 }
 
 bool
-ModuleGenerator::allocateGlobal(ValType type, bool isConst, uint32_t* index)
+ModuleGenerator::allocateGlobal(GlobalDesc* global)
 {
     MOZ_ASSERT(!startedFuncDefs_);
     unsigned width = 0;
-    switch (type) {
+    switch (global->type()) {
       case ValType::I32:
       case ValType::F32:
         width = 4;
         break;
       case ValType::I64:
       case ValType::F64:
         width = 8;
         break;
@@ -616,18 +623,32 @@ ModuleGenerator::allocateGlobal(ValType 
         MOZ_CRASH("Limit");
         break;
     }
 
     uint32_t offset;
     if (!allocateGlobalBytes(width, width, &offset))
         return false;
 
+    global->setOffset(offset);
+    return true;
+}
+
+bool
+ModuleGenerator::addGlobal(ValType type, bool isConst, uint32_t* index)
+{
+    MOZ_ASSERT(isAsmJS());
+    MOZ_ASSERT(!startedFuncDefs_);
+
     *index = shared_->globals.length();
-    return shared_->globals.append(GlobalDesc(type, offset, isConst));
+    GlobalDesc global(type, !isConst, *index);
+    if (!allocateGlobal(&global))
+        return false;
+
+    return shared_->globals.append(global);
 }
 
 void
 ModuleGenerator::initSig(uint32_t sigIndex, Sig&& sig)
 {
     MOZ_ASSERT(isAsmJS());
     MOZ_ASSERT(sigIndex == numSigs_);
     numSigs_++;
@@ -708,17 +729,17 @@ ModuleGenerator::funcImport(uint32_t fun
 {
     MOZ_ASSERT(shared_->funcImports[funcImportIndex].sig);
     return shared_->funcImports[funcImportIndex];
 }
 
 bool
 ModuleGenerator::addFuncExport(UniqueChars fieldName, uint32_t funcIndex)
 {
-    return exports_.emplaceBack(Move(fieldName), funcIndex) &&
+    return exports_.emplaceBack(Move(fieldName), funcIndex, DefinitionKind::Function) &&
            exportedFuncs_.put(funcIndex);
 }
 
 bool
 ModuleGenerator::addTableExport(UniqueChars fieldName)
 {
     MOZ_ASSERT(elemSegments_.empty());
     externalTable_ = true;
@@ -727,16 +748,22 @@ ModuleGenerator::addTableExport(UniqueCh
 
 bool
 ModuleGenerator::addMemoryExport(UniqueChars fieldName)
 {
     return exports_.emplaceBack(Move(fieldName), DefinitionKind::Memory);
 }
 
 bool
+ModuleGenerator::addGlobalExport(UniqueChars fieldName, uint32_t globalIndex)
+{
+    return exports_.emplaceBack(Move(fieldName), globalIndex, DefinitionKind::Global);
+}
+
+bool
 ModuleGenerator::setStartFunction(uint32_t funcIndex)
 {
     metadata_->initStartFuncIndex(funcIndex);
     return exportedFuncs_.put(funcIndex);
 }
 
 bool
 ModuleGenerator::startFuncDefs()
@@ -864,18 +891,16 @@ ModuleGenerator::finishFuncDefs()
     linkData_.functionCodeLength = masm_.size();
     finishedFuncDefs_ = true;
     return true;
 }
 
 bool
 ModuleGenerator::addElemSegment(ElemSegment&& seg)
 {
-    MOZ_ASSERT(seg.offset + seg.elems.length() <= shared_->tables[seg.tableIndex].initial);
-
     if (externalTable_) {
         for (uint32_t funcIndex : seg.elems) {
             if (!exportedFuncs_.put(funcIndex))
                 return false;
         }
     }
 
     return elemSegments_.append(Move(seg));
@@ -910,17 +935,17 @@ ModuleGenerator::initSigTableLength(uint
 bool
 ModuleGenerator::initSigTableElems(uint32_t sigIndex, Uint32Vector&& elemFuncIndices)
 {
     MOZ_ASSERT(isAsmJS());
 
     uint32_t tableIndex = shared_->asmJSSigToTableIndex[sigIndex];
     MOZ_ASSERT(shared_->tables[tableIndex].initial == elemFuncIndices.length());
 
-    return elemSegments_.emplaceBack(tableIndex, 0, Move(elemFuncIndices));
+    return elemSegments_.emplaceBack(tableIndex, InitExpr(Val(uint32_t(0))), Move(elemFuncIndices));
 }
 
 SharedModule
 ModuleGenerator::finish(const ShareableBytes& bytecode)
 {
     MOZ_ASSERT(!activeFunc_);
     MOZ_ASSERT(finishedFuncDefs_);
 
@@ -965,16 +990,17 @@ ModuleGenerator::finish(const ShareableB
     metadata_->memoryAccesses = masm_.extractMemoryAccesses();
     metadata_->boundsChecks = masm_.extractBoundsChecks();
 
     // Copy over data from the ModuleGeneratorData.
     metadata_->memoryUsage = shared_->memoryUsage;
     metadata_->minMemoryLength = shared_->minMemoryLength;
     metadata_->maxMemoryLength = shared_->maxMemoryLength;
     metadata_->tables = Move(shared_->tables);
+    metadata_->globals = Move(shared_->globals);
 
     // These Vectors can get large and the excess capacity can be significant,
     // so realloc them down to size.
     metadata_->memoryAccesses.podResizeToFit();
     metadata_->boundsChecks.podResizeToFit();
     metadata_->codeRanges.podResizeToFit();
     metadata_->callSites.podResizeToFit();
     metadata_->callThunks.podResizeToFit();
--- a/js/src/asmjs/WasmGenerator.h
+++ b/js/src/asmjs/WasmGenerator.h
@@ -131,16 +131,17 @@ class MOZ_STACK_CLASS ModuleGenerator
     const CodeRange& funcCodeRange(uint32_t funcIndex) const;
     MOZ_MUST_USE bool convertOutOfRangeBranchesToThunks();
     MOZ_MUST_USE bool finishTask(IonCompileTask* task);
     MOZ_MUST_USE bool finishFuncExports();
     MOZ_MUST_USE bool finishCodegen();
     MOZ_MUST_USE bool finishLinkData(Bytes& code);
     MOZ_MUST_USE bool addFuncImport(const Sig& sig, uint32_t globalDataOffset);
     MOZ_MUST_USE bool allocateGlobalBytes(uint32_t bytes, uint32_t align, uint32_t* globalDataOff);
+    MOZ_MUST_USE bool allocateGlobal(GlobalDesc* global);
 
   public:
     explicit ModuleGenerator(ImportVector&& imports);
     ~ModuleGenerator();
 
     MOZ_MUST_USE bool init(UniqueModuleGeneratorData shared, CompileArgs&& args,
                            Metadata* maybeAsmJSMetadata = nullptr);
 
@@ -160,27 +161,27 @@ class MOZ_STACK_CLASS ModuleGenerator
     uint32_t numSigs() const { return numSigs_; }
     const SigWithId& sig(uint32_t sigIndex) const;
 
     // Function declarations:
     uint32_t numFuncSigs() const { return shared_->funcSigs.length(); }
     const SigWithId& funcSig(uint32_t funcIndex) const;
 
     // Globals:
-    MOZ_MUST_USE bool allocateGlobal(ValType type, bool isConst, uint32_t* index);
-    const GlobalDesc& global(unsigned index) const { return shared_->globals[index]; }
+    const GlobalDescVector& globals() const { return shared_->globals; }
 
     // Imports:
     uint32_t numFuncImports() const;
     const FuncImportGenDesc& funcImport(uint32_t funcImportIndex) const;
 
     // Exports:
     MOZ_MUST_USE bool addFuncExport(UniqueChars fieldName, uint32_t funcIndex);
     MOZ_MUST_USE bool addTableExport(UniqueChars fieldName);
     MOZ_MUST_USE bool addMemoryExport(UniqueChars fieldName);
+    MOZ_MUST_USE bool addGlobalExport(UniqueChars fieldName, uint32_t globalIndex);
 
     // Function definitions:
     MOZ_MUST_USE bool startFuncDefs();
     MOZ_MUST_USE bool startFuncDef(uint32_t lineOrBytecode, FunctionGenerator* fg);
     MOZ_MUST_USE bool finishFuncDef(uint32_t funcIndex, FunctionGenerator* fg);
     MOZ_MUST_USE bool finishFuncDefs();
 
     // Start function:
@@ -196,16 +197,17 @@ class MOZ_STACK_CLASS ModuleGenerator
     // asm.js lazy initialization:
     void initSig(uint32_t sigIndex, Sig&& sig);
     void initFuncSig(uint32_t funcIndex, uint32_t sigIndex);
     MOZ_MUST_USE bool initImport(uint32_t importIndex, uint32_t sigIndex);
     MOZ_MUST_USE bool initSigTableLength(uint32_t sigIndex, uint32_t length);
     MOZ_MUST_USE bool initSigTableElems(uint32_t sigIndex, Uint32Vector&& elemFuncIndices);
     void initMemoryUsage(MemoryUsage memoryUsage);
     void bumpMinMemoryLength(uint32_t newMinMemoryLength);
+    MOZ_MUST_USE bool addGlobal(ValType type, bool isConst, uint32_t* index);
 
     // Finish compilation, provided the list of imports and source bytecode.
     // Both these Vectors may be empty (viz., b/c asm.js does different things
     // for imports and source).
     SharedModule finish(const ShareableBytes& bytecode);
 };
 
 // A FunctionGenerator encapsulates the generation of a single function body.
--- a/js/src/asmjs/WasmInstance.cpp
+++ b/js/src/asmjs/WasmInstance.cpp
@@ -198,61 +198,16 @@ Instance::toggleProfiling(JSContext* cx)
             MOZ_ASSERT(array[i] == from);
             array[i] = to;
         }
     }
 
     return true;
 }
 
-static bool
-ReadI64Object(JSContext* cx, HandleValue v, int64_t* i64)
-{
-    if (!v.isObject()) {
-        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_FAIL,
-                             "i64 JS value must be an object");
-        return false;
-    }
-
-    RootedObject obj(cx, &v.toObject());
-
-    int32_t* i32 = (int32_t*)i64;
-
-    RootedValue val(cx);
-    if (!JS_GetProperty(cx, obj, "low", &val))
-        return false;
-    if (!ToInt32(cx, val, &i32[0]))
-        return false;
-
-    if (!JS_GetProperty(cx, obj, "high", &val))
-        return false;
-    if (!ToInt32(cx, val, &i32[1]))
-        return false;
-
-    return true;
-}
-
-static JSObject*
-CreateI64Object(JSContext* cx, int64_t i64)
-{
-    RootedObject result(cx, JS_NewPlainObject(cx));
-    if (!result)
-        return nullptr;
-
-    RootedValue val(cx, Int32Value(uint32_t(i64)));
-    if (!JS_DefineProperty(cx, result, "low", val, JSPROP_ENUMERATE))
-        return nullptr;
-
-    val = Int32Value(uint32_t(i64 >> 32));
-    if (!JS_DefineProperty(cx, result, "high", val, JSPROP_ENUMERATE))
-        return nullptr;
-
-    return result;
-}
-
 bool
 Instance::callImport(JSContext* cx, uint32_t funcImportIndex, unsigned argc, const uint64_t* argv,
                      MutableHandleValue rval)
 {
     const FuncImport& fi = metadata_->funcImports[funcImportIndex];
 
     InvokeArgs args(cx);
     if (!args.init(argc))
@@ -418,17 +373,18 @@ Instance::callImport_f64(int32_t funcImp
 }
 
 Instance::Instance(JSContext* cx,
                    UniqueCodeSegment codeSegment,
                    const Metadata& metadata,
                    const ShareableBytes* maybeBytecode,
                    HandleWasmMemoryObject memory,
                    SharedTableVector&& tables,
-                   Handle<FunctionVector> funcImports)
+                   Handle<FunctionVector> funcImports,
+                   const ValVector& globalImports)
   : codeSegment_(Move(codeSegment)),
     metadata_(&metadata),
     maybeBytecode_(maybeBytecode),
     memory_(memory),
     tables_(Move(tables)),
     profilingEnabled_(false)
 {
     MOZ_ASSERT(funcImports.length() == metadata.funcImports.length());
@@ -437,16 +393,50 @@ Instance::Instance(JSContext* cx,
     for (size_t i = 0; i < metadata.funcImports.length(); i++) {
         const FuncImport& fi = metadata.funcImports[i];
         FuncImportExit& exit = funcImportToExit(fi);
         exit.code = codeSegment_->code() + fi.interpExitCodeOffset();
         exit.fun = funcImports[i];
         exit.baselineScript = nullptr;
     }
 
+    uint8_t* globalData = codeSegment_->globalData();
+
+    for (size_t i = 0; i < metadata.globals.length(); i++) {
+        const GlobalDesc& global = metadata.globals[i];
+        if (global.isConstant())
+            continue;
+
+        uint8_t* globalAddr = globalData + global.offset();
+        switch (global.kind()) {
+          case GlobalKind::Import: {
+            globalImports[global.importIndex()].writePayload(globalAddr);
+            break;
+          }
+          case GlobalKind::Variable: {
+            const InitExpr& init = global.initExpr();
+            switch (init.kind()) {
+              case InitExpr::Kind::Constant: {
+                init.val().writePayload(globalAddr);
+                break;
+              }
+              case InitExpr::Kind::GetGlobal: {
+                const GlobalDesc& imported = metadata.globals[init.globalIndex()];
+                globalImports[imported.importIndex()].writePayload(globalAddr);
+                break;
+              }
+            }
+            break;
+          }
+          case GlobalKind::Constant: {
+            MOZ_CRASH("skipped at the top");
+          }
+        }
+    }
+
     if (memory)
         *addressOfMemoryBase() = memory->buffer().dataPointerEither().unwrap();
 
     for (size_t i = 0; i < tables_.length(); i++)
         *addressOfTableBase(i) = tables_[i]->array();
 
    updateStackLimit(cx);
 }
--- a/js/src/asmjs/WasmInstance.h
+++ b/js/src/asmjs/WasmInstance.h
@@ -84,17 +84,18 @@ class Instance
 
   public:
     Instance(JSContext* cx,
              UniqueCodeSegment codeSegment,
              const Metadata& metadata,
              const ShareableBytes* maybeBytecode,
              HandleWasmMemoryObject memory,
              SharedTableVector&& tables,
-             Handle<FunctionVector> funcImports);
+             Handle<FunctionVector> funcImports,
+             const ValVector& globalImports);
     ~Instance();
     bool init(JSContext* cx);
     void trace(JSTracer* trc);
 
     const CodeSegment& codeSegment() const { return *codeSegment_; }
     const Metadata& metadata() const { return *metadata_; }
     const SharedTableVector& tables() const { return tables_; }
     SharedMem<uint8_t*> memoryBase() const;
--- a/js/src/asmjs/WasmIonCompile.cpp
+++ b/js/src/asmjs/WasmIonCompile.cpp
@@ -1843,31 +1843,71 @@ EmitSetLocal(FunctionCompiler& f)
 static bool
 EmitGetGlobal(FunctionCompiler& f)
 {
     uint32_t id;
     if (!f.iter().readGetGlobal(f.mg().globals, &id))
         return false;
 
     const GlobalDesc& global = f.mg().globals[id];
-    f.iter().setResult(f.loadGlobalVar(global.globalDataOffset, global.isConst,
-                                       ToMIRType(global.type)));
+    if (!global.isConstant()) {
+        f.iter().setResult(f.loadGlobalVar(global.offset(), !global.isMutable(),
+                                           ToMIRType(global.type())));
+        return true;
+    }
+
+    Val value = global.constantValue();
+    MIRType mirType = ToMIRType(value.type());
+
+    MDefinition* result;
+    switch (value.type()) {
+      case ValType::I32:
+        result = f.constant(Int32Value(value.i32()), mirType);
+        break;
+      case ValType::I64:
+        result = f.constant(value.i64());
+        break;
+      case ValType::F32:
+        result = f.constant(Float32Value(value.f32()), mirType);
+        break;
+      case ValType::F64:
+        result = f.constant(DoubleValue(value.f64()), mirType);
+        break;
+      case ValType::I8x16:
+        result = f.constant(SimdConstant::CreateX16(value.i8x16()), mirType);
+        break;
+      case ValType::I16x8:
+        result = f.constant(SimdConstant::CreateX8(value.i16x8()), mirType);
+        break;
+      case ValType::I32x4:
+        result = f.constant(SimdConstant::CreateX4(value.i32x4()), mirType);
+        break;
+      case ValType::F32x4:
+        result = f.constant(SimdConstant::CreateX4(value.f32x4()), mirType);
+        break;
+      default:
+        MOZ_CRASH("unexpected type in EmitGetGlobal");
+    }
+
+    f.iter().setResult(result);
     return true;
 }
 
 static bool
 EmitSetGlobal(FunctionCompiler& f)
 {
     uint32_t id;
     MDefinition* value;
     if (!f.iter().readSetGlobal(f.mg().globals, &id, &value))
         return false;
 
     const GlobalDesc& global = f.mg().globals[id];
-    f.storeGlobalVar(global.globalDataOffset, value);
+    MOZ_ASSERT(global.isMutable());
+
+    f.storeGlobalVar(global.offset(), value);
     return true;
 }
 
 template <typename MIRClass>
 static bool
 EmitUnary(FunctionCompiler& f, ValType operandType)
 {
     MDefinition* input;
@@ -2824,19 +2864,19 @@ EmitExpr(FunctionCompiler& f)
       case Expr::CallImport:
         return EmitCallImport(f, exprOffset);
 
       // Locals and globals
       case Expr::GetLocal:
         return EmitGetLocal(f);
       case Expr::SetLocal:
         return EmitSetLocal(f);
-      case Expr::LoadGlobal:
+      case Expr::GetGlobal:
         return EmitGetGlobal(f);
-      case Expr::StoreGlobal:
+      case Expr::SetGlobal:
         return EmitSetGlobal(f);
 
       // Select
       case Expr::Select:
         return EmitSelect(f);
 
       // I32
       case Expr::I32Const: {
--- a/js/src/asmjs/WasmJS.cpp
+++ b/js/src/asmjs/WasmJS.cpp
@@ -17,21 +17,24 @@
  */
 
 #include "asmjs/WasmJS.h"
 
 #include "asmjs/WasmCompile.h"
 #include "asmjs/WasmInstance.h"
 #include "asmjs/WasmModule.h"
 
+#include "jit/JitOptions.h"
+
 #include "jsobjinlines.h"
 
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
+using namespace js::jit;
 using namespace js::wasm;
 
 bool
 wasm::HasCompilerSupport(ExclusiveContext* cx)
 {
     if (!cx->jitSupportsFloatingPoint())
         return false;
 
@@ -84,25 +87,31 @@ GetProperty(JSContext* cx, HandleObject 
         return false;
 
     RootedId id(cx, AtomToId(atom));
     return GetProperty(cx, obj, obj, id, v);
 }
 
 static bool
 GetImports(JSContext* cx,
+           const Module& module,
            HandleObject importObj,
-           const ImportVector& imports,
            MutableHandle<FunctionVector> funcImports,
            MutableHandleWasmTableObject tableImport,
-           MutableHandleWasmMemoryObject memoryImport)
+           MutableHandleWasmMemoryObject memoryImport,
+           ValVector* globalImports)
 {
+    const ImportVector& imports = module.imports();
     if (!imports.empty() && !importObj)
         return Throw(cx, "no import object given");
 
+    const Metadata& metadata = module.metadata();
+
+    uint32_t globalIndex = 0;
+    const GlobalDescVector& globals = metadata.globals;
     for (const Import& import : imports) {
         RootedValue v(cx);
         if (!GetProperty(cx, importObj, import.module.get(), &v))
             return false;
 
         if (strlen(import.func.get()) > 0) {
             if (!v.isObject())
                 return Throw(cx, JSMSG_WASM_BAD_IMPORT_FIELD, "an Object");
@@ -130,19 +139,63 @@ GetImports(JSContext* cx,
             break;
           case DefinitionKind::Memory:
             if (!v.isObject() || !v.toObject().is<WasmMemoryObject>())
                 return Throw(cx, JSMSG_WASM_BAD_IMPORT_FIELD, "a Memory");
 
             MOZ_ASSERT(!memoryImport);
             memoryImport.set(&v.toObject().as<WasmMemoryObject>());
             break;
+
+          case DefinitionKind::Global:
+            Val val;
+            const GlobalDesc& global = globals[globalIndex++];
+            MOZ_ASSERT(global.importIndex() == globalIndex - 1);
+            MOZ_ASSERT(!global.isMutable());
+            switch (global.type()) {
+              case ValType::I32: {
+                int32_t i32;
+                if (!ToInt32(cx, v, &i32))
+                    return false;
+                val = Val(uint32_t(i32));
+                break;
+              }
+              case ValType::I64: {
+                MOZ_ASSERT(JitOptions.wasmTestMode, "no int64 in JS");
+                int64_t i64;
+                if (!ReadI64Object(cx, v, &i64))
+                    return false;
+                val = Val(uint64_t(i64));
+                break;
+              }
+              case ValType::F32: {
+                double d;
+                if (!ToNumber(cx, v, &d))
+                    return false;
+                val = Val(float(d));
+                break;
+              }
+              case ValType::F64: {
+                double d;
+                if (!ToNumber(cx, v, &d))
+                    return false;
+                val = Val(d);
+                break;
+              }
+              default: {
+                MOZ_CRASH("unexpected import value type");
+              }
+            }
+            if (!globalImports->append(val))
+                return false;
         }
     }
 
+    MOZ_ASSERT(globalIndex == globals.length() || !globals[globalIndex].isImport());
+
     return true;
 }
 
 bool
 wasm::Eval(JSContext* cx, Handle<TypedArrayObject*> code, HandleObject importObj,
            MutableHandleWasmInstanceObject instanceObj)
 {
     if (!CheckCompilerSupport(cx))
@@ -179,20 +232,21 @@ wasm::Eval(JSContext* cx, Handle<TypedAr
         else
             ReportOutOfMemory(cx);
         return false;
     }
 
     Rooted<FunctionVector> funcs(cx, FunctionVector(cx));
     RootedWasmTableObject table(cx);
     RootedWasmMemoryObject memory(cx);
-    if (!GetImports(cx, importObj, module->imports(), &funcs, &table, &memory))
+    ValVector globals;
+    if (!GetImports(cx, *module, importObj, &funcs, &table, &memory, &globals))
         return false;
 
-    return module->instantiate(cx, funcs, table, memory, nullptr, instanceObj);
+    return module->instantiate(cx, funcs, table, memory, globals, nullptr, instanceObj);
 }
 
 static bool
 InstantiateModule(JSContext* cx, unsigned argc, Value* vp)
 {
     MOZ_ASSERT(cx->options().wasm());
     CallArgs args = CallArgsFromVp(argc, vp);
 
@@ -506,22 +560,23 @@ WasmInstanceObject::construct(JSContext*
             return false;
         }
         importObj = &args[1].toObject();
     }
 
     Rooted<FunctionVector> funcs(cx, FunctionVector(cx));
     RootedWasmTableObject table(cx);
     RootedWasmMemoryObject memory(cx);
-    if (!GetImports(cx, importObj, module.imports(), &funcs, &table, &memory))
+    ValVector globals;
+    if (!GetImports(cx, module, importObj, &funcs, &table, &memory, &globals))
         return false;
 
     RootedObject instanceProto(cx, &cx->global()->getPrototype(JSProto_WasmInstance).toObject());
     RootedWasmInstanceObject instanceObj(cx);
-    if (!module.instantiate(cx, funcs, table, memory, instanceProto, &instanceObj))
+    if (!module.instantiate(cx, funcs, table, memory, globals, instanceProto, &instanceObj))
         return false;
 
     args.rval().setObject(*instanceObj);
     return true;
 }
 
 Instance&
 WasmInstanceObject::instance() const
--- a/js/src/asmjs/WasmModule.cpp
+++ b/js/src/asmjs/WasmModule.cpp
@@ -16,25 +16,72 @@
  * limitations under the License.
  */
 
 #include "asmjs/WasmModule.h"
 
 #include "asmjs/WasmInstance.h"
 #include "asmjs/WasmJS.h"
 #include "asmjs/WasmSerialize.h"
+#include "jit/JitOptions.h"
 
 #include "vm/ArrayBufferObject-inl.h"
 #include "vm/Debugger-inl.h"
 
 using namespace js;
+using namespace js::jit;
 using namespace js::wasm;
 
 const char wasm::InstanceExportField[] = "exports";
 
+JSObject*
+js::wasm::CreateI64Object(JSContext* cx, int64_t i64)
+{
+    RootedObject result(cx, JS_NewPlainObject(cx));
+    if (!result)
+        return nullptr;
+
+    RootedValue val(cx, Int32Value(uint32_t(i64)));
+    if (!JS_DefineProperty(cx, result, "low", val, JSPROP_ENUMERATE))
+        return nullptr;
+
+    val = Int32Value(uint32_t(i64 >> 32));
+    if (!JS_DefineProperty(cx, result, "high", val, JSPROP_ENUMERATE))
+        return nullptr;
+
+    return result;
+}
+
+bool
+js::wasm::ReadI64Object(JSContext* cx, HandleValue v, int64_t* i64)
+{
+    if (!v.isObject()) {
+        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_FAIL,
+                             "i64 JS value must be an object");
+        return false;
+    }
+
+    RootedObject obj(cx, &v.toObject());
+
+    int32_t* i32 = (int32_t*)i64;
+
+    RootedValue val(cx);
+    if (!JS_GetProperty(cx, obj, "low", &val))
+        return false;
+    if (!ToInt32(cx, val, &i32[0]))
+        return false;
+
+    if (!JS_GetProperty(cx, obj, "high", &val))
+        return false;
+    if (!ToInt32(cx, val, &i32[1]))
+        return false;
+
+    return true;
+}
+
 #if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
 // On MIPS, CodeLabels are instruction immediates so InternalLinks only
 // patch instruction immediates.
 LinkData::InternalLink::InternalLink(Kind kind)
 {
     MOZ_ASSERT(kind == CodeLabel || kind == InstructionImmediate);
 }
 
@@ -153,35 +200,42 @@ Import::deserialize(const uint8_t* curso
 
 size_t
 Import::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
 {
     return module.sizeOfExcludingThis(mallocSizeOf) +
            func.sizeOfExcludingThis(mallocSizeOf);
 }
 
-Export::Export(UniqueChars fieldName, uint32_t funcIndex)
+Export::Export(UniqueChars fieldName, uint32_t index, DefinitionKind kind)
   : fieldName_(Move(fieldName))
 {
-    pod.kind_ = DefinitionKind::Function;
-    pod.funcIndex_ = funcIndex;
+    pod.kind_ = kind;
+    pod.index_ = index;
 }
 
 Export::Export(UniqueChars fieldName, DefinitionKind kind)
   : fieldName_(Move(fieldName))
 {
     pod.kind_ = kind;
-    pod.funcIndex_ = 0;
+    pod.index_ = 0;
 }
 
 uint32_t
 Export::funcIndex() const
 {
     MOZ_ASSERT(pod.kind_ == DefinitionKind::Function);
-    return pod.funcIndex_;
+    return pod.index_;
+}
+
+uint32_t
+Export::globalIndex() const
+{
+    MOZ_ASSERT(pod.kind_ == DefinitionKind::Global);
+    return pod.index_;
 }
 
 size_t
 Export::serializedSize() const
 {
     return fieldName_.serializedSize() +
            sizeof(pod);
 }
@@ -207,31 +261,34 @@ Export::sizeOfExcludingThis(MallocSizeOf
 {
     return fieldName_.sizeOfExcludingThis(mallocSizeOf);
 }
 
 size_t
 ElemSegment::serializedSize() const
 {
     return sizeof(tableIndex) +
+           sizeof(offset) +
            SerializedPodVectorSize(elems);
 }
 
 uint8_t*
 ElemSegment::serialize(uint8_t* cursor) const
 {
     cursor = WriteBytes(cursor, &tableIndex, sizeof(tableIndex));
+    cursor = WriteBytes(cursor, &offset, sizeof(offset));
     cursor = SerializePodVector(cursor, elems);
     return cursor;
 }
 
 const uint8_t*
 ElemSegment::deserialize(const uint8_t* cursor)
 {
     (cursor = ReadBytes(cursor, &tableIndex, sizeof(tableIndex))) &&
+    (cursor = ReadBytes(cursor, &offset, sizeof(offset))) &&
     (cursor = DeserializePodVector(cursor, &elems));
     return cursor;
 }
 
 size_t
 ElemSegment::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
 {
     return elems.sizeOfExcludingThis(mallocSizeOf);
@@ -346,17 +403,17 @@ Module::addSizeOfMisc(MallocSizeOf mallo
              dataSegments_.sizeOfExcludingThis(mallocSizeOf) +
              SizeOfVectorExcludingThis(elemSegments_, mallocSizeOf) +
              metadata_->sizeOfIncludingThisIfNotSeen(mallocSizeOf, seenMetadata) +
              bytecode_->sizeOfIncludingThisIfNotSeen(mallocSizeOf, seenBytes);
 }
 
 bool
 Module::initElems(JSContext* cx, HandleWasmInstanceObject instanceObj,
-                  HandleWasmTableObject tableObj) const
+                  const ValVector& globalImports, HandleWasmTableObject tableObj) const
 {
     Instance& instance = instanceObj->instance();
     const CodeSegment& codeSegment = instance.codeSegment();
     const SharedTableVector& tables = instance.tables();
 
     // Initialize tables that have a WasmTableObject first, so that this
     // initialization can be done atomically.
     if (tableObj && !tableObj->initialized() && !tableObj->init(cx, instanceObj))
@@ -364,30 +421,63 @@ Module::initElems(JSContext* cx, HandleW
 
     // Initialize all remaining Tables that do not have objects.
     for (const SharedTable& table : tables) {
         if (!table->initialized())
             table->init(codeSegment);
     }
 
     // Now that all tables have been initialized, write elements.
+    Vector<uint32_t> prevEnds(cx);
+    if (!prevEnds.appendN(0, tables.length()))
+        return false;
+
     for (const ElemSegment& seg : elemSegments_) {
         Table& table = *tables[seg.tableIndex];
-        MOZ_ASSERT(seg.offset + seg.elems.length() <= table.length());
+
+        uint32_t offset;
+        switch (seg.offset.kind()) {
+          case InitExpr::Kind::Constant: {
+            offset = seg.offset.val().i32();
+            break;
+          }
+          case InitExpr::Kind::GetGlobal: {
+            const GlobalDesc& global = metadata_->globals[seg.offset.globalIndex()];
+            offset = globalImports[global.importIndex()].i32();
+            break;
+          }
+        }
+
+        uint32_t& prevEnd = prevEnds[seg.tableIndex];
+
+        if (offset < prevEnd) {
+            JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_FAIL,
+                                 "elem segments must be disjoint and ordered");
+            return false;
+        }
+
+        uint32_t tableLength = instance.metadata().tables[seg.tableIndex].initial;
+        if (offset > tableLength || tableLength - offset < seg.elems.length()) {
+            JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_FAIL,
+                                 "element segment does not fit");
+            return false;
+        }
 
         if (tableObj) {
             MOZ_ASSERT(seg.tableIndex == 0);
             for (uint32_t i = 0; i < seg.elems.length(); i++) {
-                if (!tableObj->setInstance(cx, seg.offset + i, instanceObj))
+                if (!tableObj->setInstance(cx, offset + i, instanceObj))
                     return false;
             }
         }
 
         for (uint32_t i = 0; i < seg.elems.length(); i++)
-            table.array()[seg.offset + i] = codeSegment.code() + seg.elems[i];
+            table.array()[offset + i] = codeSegment.code() + seg.elems[i];
+
+        prevEnd = offset + seg.elems.length();
     }
 
     return true;
 }
 
 // asm.js module instantiation supplies its own buffer, but for wasm, create and
 // initialize the buffer if one is requested. Either way, the buffer is wrapped
 // in a WebAssembly.Memory object which is what the Instance stores.
@@ -464,20 +554,63 @@ Module::instantiateTable(JSContext* cx, 
             return false;
         }
     }
 
     return true;
 }
 
 static bool
+ExportGlobalValue(JSContext* cx, const GlobalDescVector& globals, uint32_t globalIndex,
+                  const ValVector& globalImports, MutableHandleValue jsval)
+{
+    const GlobalDesc& global = globals[globalIndex];
+
+    // Imports are located upfront in the globals array.
+    Val val;
+    switch (global.kind()) {
+      case GlobalKind::Import:   val = globalImports[globalIndex]; break;
+      case GlobalKind::Variable: MOZ_CRASH("mutable variables can't be exported");
+      case GlobalKind::Constant: val = global.constantValue(); break;
+    }
+
+    switch (global.type()) {
+      case ValType::I32: {
+        jsval.set(Int32Value(val.i32()));
+        return true;
+      }
+      case ValType::I64: {
+        MOZ_ASSERT(JitOptions.wasmTestMode, "no int64 in asm.js/wasm");
+        RootedObject obj(cx, CreateI64Object(cx, val.i64()));
+        if (!obj)
+            return false;
+        jsval.set(ObjectValue(*obj));
+        return true;
+      }
+      case ValType::F32: {
+        jsval.set(DoubleValue(double(val.f32())));
+        return true;
+      }
+      case ValType::F64: {
+        jsval.set(DoubleValue(val.f64()));
+        return true;
+      }
+      default: {
+        break;
+      }
+    }
+    MOZ_CRASH("unexpected type when creating global exports");
+}
+
+static bool
 CreateExportObject(JSContext* cx,
                    HandleWasmInstanceObject instanceObj,
                    MutableHandleWasmTableObject tableObj,
                    HandleWasmMemoryObject memoryObj,
+                   const ValVector& globalImports,
                    const ExportVector& exports,
                    MutableHandleObject exportObj)
 {
     const Instance& instance = instanceObj->instance();
     const Metadata& metadata = instance.metadata();
 
     if (metadata.isAsmJS() && exports.length() == 1 && strlen(exports[0].fieldName()) == 0) {
         RootedFunction fun(cx);
@@ -518,30 +651,36 @@ CreateExportObject(JSContext* cx,
           }
           case DefinitionKind::Memory: {
             if (metadata.assumptions.newFormat)
                 val = ObjectValue(*memoryObj);
             else
                 val = ObjectValue(memoryObj->buffer());
             break;
           }
+          case DefinitionKind::Global: {
+            if (!ExportGlobalValue(cx, metadata.globals, exp.globalIndex(), globalImports, &val))
+                return false;
+            break;
+          }
         }
 
         if (!JS_DefinePropertyById(cx, exportObj, id, val, JSPROP_ENUMERATE))
             return false;
     }
 
     return true;
 }
 
 bool
 Module::instantiate(JSContext* cx,
                     Handle<FunctionVector> funcImports,
                     HandleWasmTableObject tableImport,
                     HandleWasmMemoryObject memoryImport,
+                    const ValVector& globalImports,
                     HandleObject instanceProto,
                     MutableHandleWasmInstanceObject instanceObj) const
 {
     MOZ_ASSERT(funcImports.length() == metadata_->funcImports.length());
 
     RootedWasmMemoryObject memory(cx, memoryImport);
     if (!instantiateMemory(cx, &memory))
         return false;
@@ -575,48 +714,49 @@ Module::instantiate(JSContext* cx,
             return false;
 
         auto instance = cx->make_unique<Instance>(cx,
                                                   Move(codeSegment),
                                                   *metadata_,
                                                   maybeBytecode,
                                                   memory,
                                                   Move(tables),
-                                                  funcImports);
+                                                  funcImports,
+                                                  globalImports);
         if (!instance)
             return false;
 
         instanceObj->init(Move(instance));
     }
 
     if (!instanceObj->instance().init(cx))
         return false;
 
     // Create the export object.
 
     RootedObject exportObj(cx);
     RootedWasmTableObject table(cx, tableImport);
-    if (!CreateExportObject(cx, instanceObj, &table, memory, exports_, &exportObj))
+    if (!CreateExportObject(cx, instanceObj, &table, memory, globalImports, exports_, &exportObj))
         return false;
 
     JSAtom* atom = Atomize(cx, InstanceExportField, strlen(InstanceExportField));
     if (!atom)
         return false;
     RootedId id(cx, AtomToId(atom));
 
     RootedValue val(cx, ObjectValue(*exportObj));
     if (!JS_DefinePropertyById(cx, instanceObj, id, val, JSPROP_ENUMERATE))
         return false;
 
     // Initialize table elements only after the instance is fully initialized
     // since the Table object needs to point to a valid instance object. Perform
     // initialization as the final step after the instance is fully live since
     // it is observable (in the case of an imported Table object).
 
-    if (!initElems(cx, instanceObj, table))
+    if (!initElems(cx, instanceObj, globalImports, table))
         return false;
 
     // Done! Notify the Debugger of the new Instance.
 
     Debugger::onNewWasmInstance(cx, instanceObj);
 
     // Call the start function, if there's one. By specification, it does not
     // take any arguments nor does it return a value, so just create a dummy
--- a/js/src/asmjs/WasmModule.h
+++ b/js/src/asmjs/WasmModule.h
@@ -21,16 +21,28 @@
 
 #include "asmjs/WasmCode.h"
 #include "asmjs/WasmTable.h"
 #include "js/TypeDecls.h"
 
 namespace js {
 namespace wasm {
 
+// Creates a JS object containing two fields (low: low 32 bits; high: high 32
+// bits) of a given Int64 value. For testing purposes only.
+
+JSObject*
+CreateI64Object(JSContext* cx, int64_t i64);
+
+// Reads an int64 from a JS object with the same shape as described in the
+// comment above. For testing purposes only.
+
+bool
+ReadI64Object(JSContext* cx, HandleValue v, int64_t* i64);
+
 // LinkData contains all the metadata necessary to patch all the locations
 // that depend on the absolute address of a CodeSegment.
 //
 // LinkData is built incrementing by ModuleGenerator and then stored immutably
 // in Module.
 
 struct LinkDataCacheablePod
 {
@@ -108,28 +120,29 @@ typedef Vector<Import, 0, SystemAllocPol
 // ExportVector is built incrementally by ModuleGenerator and then stored
 // immutably by Module.
 
 class Export
 {
     CacheableChars fieldName_;
     struct CacheablePod {
         DefinitionKind kind_;
-        uint32_t funcIndex_;
+        uint32_t index_;
     } pod;
 
   public:
     Export() = default;
-    explicit Export(UniqueChars fieldName, uint32_t funcIndex);
+    explicit Export(UniqueChars fieldName, uint32_t index, DefinitionKind kind);
     explicit Export(UniqueChars fieldName, DefinitionKind kind);
 
     const char* fieldName() const { return fieldName_.get(); }
 
     DefinitionKind kind() const { return pod.kind_; }
     uint32_t funcIndex() const;
+    uint32_t globalIndex() const;
 
     WASM_DECLARE_SERIALIZABLE(Export)
 };
 
 typedef Vector<Export, 0, SystemAllocPolicy> ExportVector;
 
 // DataSegment describes the offset of a data segment in the bytecode that is
 // to be copied at a given offset into linear memory upon instantiation.
@@ -144,21 +157,21 @@ struct DataSegment
 typedef Vector<DataSegment, 0, SystemAllocPolicy> DataSegmentVector;
 
 // ElemSegment represents an element segment in the module where each element's
 // function index has been translated to its offset in the code section.
 
 struct ElemSegment
 {
     uint32_t tableIndex;
-    uint32_t offset;
+    InitExpr offset;
     Uint32Vector elems;
 
     ElemSegment() = default;
-    ElemSegment(uint32_t tableIndex, uint32_t offset, Uint32Vector&& elems)
+    ElemSegment(uint32_t tableIndex, InitExpr offset, Uint32Vector&& elems)
       : tableIndex(tableIndex), offset(offset), elems(Move(elems))
     {}
 
     WASM_DECLARE_SERIALIZABLE(ElemSegment)
 };
 
 typedef Vector<ElemSegment, 0, SystemAllocPolicy> ElemSegmentVector;
 
@@ -184,17 +197,17 @@ class Module : public RefCounted<Module>
     const ElemSegmentVector elemSegments_;
     const SharedMetadata    metadata_;
     const SharedBytes       bytecode_;
 
     bool instantiateMemory(JSContext* cx, MutableHandleWasmMemoryObject memory) const;
     bool instantiateTable(JSContext* cx, HandleWasmTableObject tableImport,
                           SharedTableVector* tables) const;
     bool initElems(JSContext* cx, HandleWasmInstanceObject instanceObj,
-                   HandleWasmTableObject tableObj) const;
+                   const ValVector& globalImports, HandleWasmTableObject tableObj) const;
 
   public:
     Module(Bytes&& code,
            LinkData&& linkData,
            ImportVector&& imports,
            ExportVector&& exports,
            DataSegmentVector&& dataSegments,
            ElemSegmentVector&& elemSegments,
@@ -214,16 +227,17 @@ class Module : public RefCounted<Module>
     const ImportVector& imports() const { return imports_; }
 
     // Instantiate this module with the given imports:
 
     bool instantiate(JSContext* cx,
                      Handle<FunctionVector> funcImports,
                      HandleWasmTableObject tableImport,
                      HandleWasmMemoryObject memoryImport,
+                     const ValVector& globalImports,
                      HandleObject instanceProto,
                      MutableHandleWasmInstanceObject instanceObj) const;
 
     // Structured clone support:
 
     size_t serializedSize() const;
     uint8_t* serialize(uint8_t* cursor) const;
     static const uint8_t* deserialize(const uint8_t* cursor, RefPtr<Module>* module,
--- a/js/src/asmjs/WasmTextToBinary.cpp
+++ b/js/src/asmjs/WasmTextToBinary.cpp
@@ -81,18 +81,21 @@ class WasmToken
         Elem,
         Else,
         EndOfFile,
         Equal,
         Error,
         Export,
         Float,
         Func,
+        GetGlobal,
         GetLocal,
+        Global,
         If,
+        Immutable,
         Import,
         Index,
         Memory,
         NegativeZero,
         Load,
         Local,
         Loop,
         Module,
@@ -100,16 +103,17 @@ class WasmToken
         Nop,
         Offset,
         OpenParen,
         Param,
         Resizable,
         Result,
         Return,
         Segment,
+        SetGlobal,
         SetLocal,
         SignedInteger,
         Start,
         Store,
         Table,
         TernaryOpcode,
         Text,
         Then,
@@ -973,18 +977,22 @@ WasmTokenStream::next()
                     return WasmToken(WasmToken::UnaryOpcode, Expr::F64Trunc, begin, cur_);
                 break;
             }
             break;
         }
         break;
 
       case 'g':
+        if (consume(u"get_global"))
+            return WasmToken(WasmToken::GetGlobal, begin, cur_);
         if (consume(u"get_local"))
             return WasmToken(WasmToken::GetLocal, begin, cur_);
+        if (consume(u"global"))
+            return WasmToken(WasmToken::Global, begin, cur_);
         break;
 
       case 'i':
         if (consume(u"i32")) {
             if (!consume(u"."))
                 return WasmToken(WasmToken::ValueType, ValType::I32, begin, cur_);
 
             switch (*cur_) {
@@ -1261,16 +1269,18 @@ WasmTokenStream::next()
                 break;
               case 'x':
                 if (consume(u"xor"))
                     return WasmToken(WasmToken::BinaryOpcode, Expr::I64Xor, begin, cur_);
                 break;
             }
             break;
         }
+        if (consume(u"immutable"))
+            return WasmToken(WasmToken::Immutable, begin, cur_);
         if (consume(u"import"))
             return WasmToken(WasmToken::Import, begin, cur_);
         if (consume(u"infinity"))
             return WasmToken(WasmToken::Infinity, begin, cur_);
         if (consume(u"if"))
             return WasmToken(WasmToken::If, begin, cur_);
         break;
 
@@ -1312,16 +1322,18 @@ WasmTokenStream::next()
             return WasmToken(WasmToken::Result, begin, cur_);
         if (consume(u"return"))
             return WasmToken(WasmToken::Return, begin, cur_);
         break;
 
       case 's':
         if (consume(u"select"))
             return WasmToken(WasmToken::TernaryOpcode, Expr::Select, begin, cur_);
+        if (consume(u"set_global"))
+            return WasmToken(WasmToken::SetGlobal, begin, cur_);
         if (consume(u"set_local"))
             return WasmToken(WasmToken::SetLocal, begin, cur_);
         if (consume(u"segment"))
             return WasmToken(WasmToken::Segment, begin, cur_);
         if (consume(u"start"))
             return WasmToken(WasmToken::Start, begin, cur_);
         break;
 
@@ -1836,16 +1848,39 @@ ParseGetLocal(WasmParseContext& c)
 {
     AstRef local;
     if (!c.ts.matchRef(&local, c.error))
         return nullptr;
 
     return new(c.lifo) AstGetLocal(local);
 }
 
+static AstGetGlobal*
+ParseGetGlobal(WasmParseContext& c)
+{
+    AstRef local;
+    if (!c.ts.matchRef(&local, c.error))
+        return nullptr;
+    return new(c.lifo) AstGetGlobal(local);
+}
+
+static AstSetGlobal*
+ParseSetGlobal(WasmParseContext& c)
+{
+    AstRef global;
+    if (!c.ts.matchRef(&global, c.error))
+        return nullptr;
+
+    AstExpr* value = ParseExpr(c);
+    if (!value)
+        return nullptr;
+
+    return new(c.lifo) AstSetGlobal(global, *value);
+}
+
 static AstSetLocal*
 ParseSetLocal(WasmParseContext& c)
 {
     AstRef local;
     if (!c.ts.matchRef(&local, c.error))
         return nullptr;
 
     AstExpr* value = ParseExpr(c);
@@ -2171,24 +2206,28 @@ ParseExprInsideParens(WasmParseContext& 
       case WasmToken::ComparisonOpcode:
         return ParseComparisonOperator(c, token.expr());
       case WasmToken::Const:
         return ParseConst(c, token);
       case WasmToken::ConversionOpcode:
         return ParseConversionOperator(c, token.expr());
       case WasmToken::If:
         return ParseIf(c);
+      case WasmToken::GetGlobal:
+        return ParseGetGlobal(c);
       case WasmToken::GetLocal:
         return ParseGetLocal(c);
       case WasmToken::Load:
         return ParseLoad(c, token.expr());
       case WasmToken::Loop:
         return ParseBlock(c, Expr::Loop);
       case WasmToken::Return:
         return ParseReturn(c);
+      case WasmToken::SetGlobal:
+        return ParseSetGlobal(c);
       case WasmToken::SetLocal:
         return ParseSetLocal(c);
       case WasmToken::Store:
         return ParseStore(c, token.expr());
       case WasmToken::TernaryOpcode:
         return ParseTernaryOperator(c, token.expr());
       case WasmToken::UnaryOpcode:
         return ParseUnaryOperator(c, token.expr());
@@ -2418,16 +2457,30 @@ ParseStartFunc(WasmParseContext& c, Wasm
     if (!module->setStartFunc(AstStartFunc(func))) {
         c.ts.generateError(token, c.error);
         return false;
     }
 
     return true;
 }
 
+static bool
+ParseGlobalType(WasmParseContext& c, WasmToken* typeToken, uint32_t* flags)
+{
+    if (!c.ts.match(WasmToken::ValueType, typeToken, c.error))
+        return false;
+
+    // Mutable by default.
+    *flags = 0x1;
+    if (c.ts.getIf(WasmToken::Immutable))
+        *flags = 0x0;
+
+    return true;
+}
+
 static AstImport*
 ParseImport(WasmParseContext& c, bool newFormat, AstModule* module)
 {
     AstName name = c.ts.getIfName();
 
     WasmToken moduleName;
     if (!c.ts.match(WasmToken::Text, &moduleName, c.error))
         return nullptr;
@@ -2453,16 +2506,26 @@ ParseImport(WasmParseContext& c, bool ne
                 AstResizable table;
                 if (!ParseResizable(c, &table))
                     return nullptr;
                 if (!c.ts.match(WasmToken::CloseParen, c.error))
                     return nullptr;
                 return new(c.lifo) AstImport(name, moduleName.text(), fieldName.text(),
                                              DefinitionKind::Table, table);
             }
+            if (c.ts.getIf(WasmToken::Global)) {
+                WasmToken typeToken;
+                uint32_t flags = 0;
+                if (!ParseGlobalType(c, &typeToken, &flags))
+                    return nullptr;
+                if (!c.ts.match(WasmToken::CloseParen, c.error))
+                    return nullptr;
+                return new(c.lifo) AstImport(name, moduleName.text(), fieldName.text(),
+                                             AstGlobal(AstName(), typeToken.valueType(), flags));
+            }
         }
 
         if (c.ts.getIf(WasmToken::Type)) {
             if (!c.ts.matchRef(&sigRef, c.error))
                 return nullptr;
             if (!c.ts.match(WasmToken::CloseParen, c.error))
                 return nullptr;
         } else {
@@ -2489,23 +2552,31 @@ ParseExport(WasmParseContext& c)
 {
     WasmToken name;
     if (!c.ts.match(WasmToken::Text, &name, c.error))
         return nullptr;
 
     WasmToken exportee = c.ts.get();
     switch (exportee.kind()) {
       case WasmToken::Index:
-        return new(c.lifo) AstExport(name.text(), AstRef(AstName(), exportee.index()));
+        return new(c.lifo) AstExport(name.text(), DefinitionKind::Function,
+                                     AstRef(AstName(), exportee.index()));
       case WasmToken::Name:
-        return new(c.lifo) AstExport(name.text(), AstRef(exportee.name(), AstNoIndex));
+        return new(c.lifo) AstExport(name.text(), DefinitionKind::Function,
+                                     AstRef(exportee.name(), AstNoIndex));
       case WasmToken::Table:
         return new(c.lifo) AstExport(name.text(), DefinitionKind::Table);
       case WasmToken::Memory:
         return new(c.lifo) AstExport(name.text(), DefinitionKind::Memory);
+      case WasmToken::Global: {
+        AstRef ref;
+        if (!c.ts.matchRef(&ref, c.error))
+            return nullptr;
+        return new(c.lifo) AstExport(name.text(), DefinitionKind::Global, ref);
+      }
       default:
         break;
     }
 
     c.ts.generateError(exportee, c.error);
     return nullptr;
 
 }
@@ -2536,36 +2607,57 @@ ParseTable(WasmParseContext& c, WasmToke
             return false;
     }
 
     if (!module->setTable(AstResizable(elems.length(), Some<uint32_t>(elems.length())))) {
         c.ts.generateError(token, c.error);
         return false;
     }
 
-    AstElemSegment* segment = new(c.lifo) AstElemSegment(0, Move(elems));
+    auto* zero = new(c.lifo) AstConst(Val(uint32_t(0)));
+    if (!zero)
+        return false;
+
+    AstElemSegment* segment = new(c.lifo) AstElemSegment(zero, Move(elems));
     return segment && module->append(segment);
 }
 
 static AstElemSegment*
 ParseElemSegment(WasmParseContext& c)
 {
-    WasmToken offset;
-    if (!c.ts.match(WasmToken::Index, &offset, c.error))
+    AstExpr* offset = ParseExpr(c);
+    if (!offset)
         return nullptr;
 
     AstRefVector elems(c.lifo);
 
     AstRef elem;
     while (c.ts.getIfRef(&elem)) {
         if (!elems.append(elem))
             return nullptr;
     }
 
-    return new(c.lifo) AstElemSegment(offset.index(), Move(elems));
+    return new(c.lifo) AstElemSegment(offset, Move(elems));
+}
+
+static AstGlobal*
+ParseGlobal(WasmParseContext& c)
+{
+    AstName name = c.ts.getIfName();
+
+    WasmToken typeToken;
+    uint32_t flags = 0;
+    if (!ParseGlobalType(c, &typeToken, &flags))
+        return nullptr;
+
+    AstExpr* init = ParseExpr(c);
+    if (!init)
+        return nullptr;
+
+    return new(c.lifo) AstGlobal(name, typeToken.valueType(), flags, Some(init));
 }
 
 static AstModule*
 ParseModule(const char16_t* text, bool newFormat, LifoAlloc& lifo, UniqueChars* error)
 {
     WasmParseContext c(text, lifo, error);
 
     if (!c.ts.match(WasmToken::OpenParen, c.error))
@@ -2592,16 +2684,22 @@ ParseModule(const char16_t* text, bool n
                 return nullptr;
             break;
           }
           case WasmToken::Memory: {
             if (!ParseMemory(c, section, module))
                 return nullptr;
             break;
           }
+          case WasmToken::Global: {
+            AstGlobal* global = ParseGlobal(c);
+            if (!global || !module->append(global))
+                return nullptr;
+            break;
+          }
           case WasmToken::Data: {
             AstDataSegment* segment = ParseDataSegment(c);
             if (!segment || !module->append(segment))
                 return nullptr;
             break;
           }
           case WasmToken::Import: {
             AstImport* imp = ParseImport(c, newFormat, module);
@@ -2653,16 +2751,17 @@ ParseModule(const char16_t* text, bool n
 // wasm name resolution
 
 namespace {
 
 class Resolver
 {
     UniqueChars* error_;
     AstNameMap varMap_;
+    AstNameMap globalMap_;
     AstNameMap sigMap_;
     AstNameMap funcMap_;
     AstNameMap importMap_;
     AstNameVector targetStack_;
 
     bool registerName(AstNameMap& map, AstName name, size_t index) {
         AstNameMap::AddPtr p = map.lookupForAdd(name);
         if (!p) {
@@ -2698,23 +2797,28 @@ class Resolver
         error_->reset(JS_smprintf("%s label '%hs' not found", kind, nameWithNull.begin()));
         return false;
     }
 
   public:
     explicit Resolver(LifoAlloc& lifo, UniqueChars* error)
       : error_(error),
         varMap_(lifo),
+        globalMap_(lifo),
         sigMap_(lifo),
         funcMap_(lifo),
         importMap_(lifo),
         targetStack_(lifo)
     {}
     bool init() {
-        return sigMap_.init() && funcMap_.init() && importMap_.init() && varMap_.init();
+        return sigMap_.init() &&
+               funcMap_.init() &&
+               importMap_.init() &&
+               varMap_.init() &&
+               globalMap_.init();
     }
     void beginFunc() {
         varMap_.clear();
         MOZ_ASSERT(targetStack_.empty());
     }
     bool registerSigName(AstName name, size_t index) {
         return name.empty() || registerName(sigMap_, name, index);
     }
@@ -2722,16 +2826,19 @@ class Resolver
         return name.empty() || registerName(funcMap_, name, index);
     }
     bool registerImportName(AstName name, size_t index) {
         return name.empty() || registerName(importMap_, name, index);
     }
     bool registerVarName(AstName name, size_t index) {
         return name.empty() || registerName(varMap_, name, index);
     }
+    bool registerGlobalName(AstName name, size_t index) {
+        return name.empty() || registerName(globalMap_, name, index);
+    }
     bool pushTarget(AstName name) {
         return targetStack_.append(name);
     }
     void popTarget(AstName name) {
         MOZ_ASSERT(targetStack_.back() == name);
         targetStack_.popBack();
     }
 
@@ -2750,16 +2857,21 @@ class Resolver
             return failResolveLabel("import", ref.name());
         return true;
     }
     bool resolveLocal(AstRef& ref) {
         if (!ref.name().empty() && !resolveRef(varMap_, ref))
             return failResolveLabel("local", ref.name());
         return true;
     }
+    bool resolveGlobal(AstRef& ref) {
+        if (!ref.name().empty() && !resolveRef(globalMap_, ref))
+            return failResolveLabel("global", ref.name());
+        return true;
+    }
     bool resolveBranchTarget(AstRef& ref) {
         if (ref.name().empty())
             return true;
         for (size_t i = 0, e = targetStack_.length(); i < e; i++) {
             if (targetStack_[e - i - 1] == ref.name()) {
                 ref.setIndex(i);
                 return true;
             }
@@ -2883,16 +2995,34 @@ ResolveSetLocal(Resolver& r, AstSetLocal
 
     if (!r.resolveLocal(sl.local()))
         return false;
 
     return true;
 }
 
 static bool
+ResolveGetGlobal(Resolver& r, AstGetGlobal& gl)
+{
+    return r.resolveGlobal(gl.global());
+}
+
+static bool
+ResolveSetGlobal(Resolver& r, AstSetGlobal& sl)
+{
+    if (!ResolveExpr(r, sl.value()))
+        return false;
+
+    if (!r.resolveGlobal(sl.global()))
+        return false;
+
+    return true;
+}
+
+static bool
 ResolveUnaryOperator(Resolver& r, AstUnaryOperator& b)
 {
     return ResolveExpr(r, *b.op());
 }
 
 static bool
 ResolveBinaryOperator(Resolver& r, AstBinaryOperator& b)
 {
@@ -3001,24 +3131,28 @@ ResolveExpr(Resolver& r, AstExpr& expr)
       case AstExprKind::CallIndirect:
         return ResolveCallIndirect(r, expr.as<AstCallIndirect>());
       case AstExprKind::ComparisonOperator:
         return ResolveComparisonOperator(r, expr.as<AstComparisonOperator>());
       case AstExprKind::Const:
         return true;
       case AstExprKind::ConversionOperator:
         return ResolveConversionOperator(r, expr.as<AstConversionOperator>());
+      case AstExprKind::GetGlobal:
+        return ResolveGetGlobal(r, expr.as<AstGetGlobal>());
       case AstExprKind::GetLocal:
         return ResolveGetLocal(r, expr.as<AstGetLocal>());
       case AstExprKind::If:
         return ResolveIfElse(r, expr.as<AstIf>());
       case AstExprKind::Load:
         return ResolveLoad(r, expr.as<AstLoad>());
       case AstExprKind::Return:
         return ResolveReturn(r, expr.as<AstReturn>());
+      case AstExprKind::SetGlobal:
+        return ResolveSetGlobal(r, expr.as<AstSetGlobal>());
       case AstExprKind::SetLocal:
         return ResolveSetLocal(r, expr.as<AstSetLocal>());
       case AstExprKind::Store:
         return ResolveStore(r, expr.as<AstStore>());
       case AstExprKind::BranchTable:
         return ResolveBranchTable(r, expr.as<AstBranchTable>());
       case AstExprKind::TernaryOperator:
         return ResolveTernaryOperator(r, expr.as<AstTernaryOperator>());
@@ -3028,18 +3162,17 @@ ResolveExpr(Resolver& r, AstExpr& expr)
     MOZ_CRASH("Bad expr kind");
 }
 
 static bool
 ResolveFunc(Resolver& r, AstFunc& func)
 {
     r.beginFunc();
 
-    size_t numVars = func.locals().length();
-    for (size_t i = 0; i < numVars; i++) {
+    for (size_t i = 0; i < func.locals().length(); i++) {
         if (!r.registerVarName(func.locals()[i], i))
             return r.fail("duplicate var");
     }
 
     for (AstExpr* expr : func.body()) {
         if (!ResolveExpr(r, *expr))
             return false;
     }
@@ -3073,49 +3206,75 @@ ResolveModule(LifoAlloc& lifo, AstModule
     for (AstElemSegment* seg : module->elemSegments()) {
         for (AstRef& ref : seg->elems()) {
             if (!r.resolveFunction(ref))
                 return false;
         }
     }
 
     size_t numImports = module->imports().length();
+    size_t lastGlobalIndex = 0;
     for (size_t i = 0; i < numImports; i++) {
         AstImport* imp = module->imports()[i];
-        if (!r.registerImportName(imp->name(), i))
-            return r.fail("duplicate import");
-
         switch (imp->kind()) {
           case DefinitionKind::Function:
+            if (!r.registerImportName(imp->name(), i))
+                return r.fail("duplicate import");
             if (!r.resolveSignature(imp->funcSig()))
                 return false;
             break;
+          case DefinitionKind::Global:
+            if (!r.registerGlobalName(imp->name(), lastGlobalIndex++))
+                return r.fail("duplicate import");
+            break;
           case DefinitionKind::Memory:
           case DefinitionKind::Table:
             break;
         }
     }
 
+    const AstGlobalVector& globals = module->globals();
+    for (const AstGlobal* global : globals) {
+        if (!r.registerGlobalName(global->name(), lastGlobalIndex++))
+            return r.fail("duplicate import");
+        if (global->hasInit() && !ResolveExpr(r, global->init()))
+            return false;
+    }
+
     for (AstExport* export_ : module->exports()) {
-        if (export_->kind() != DefinitionKind::Function)
-            continue;
-        if (!r.resolveFunction(export_->func()))
-            return false;
+        switch (export_->kind()) {
+          case DefinitionKind::Function:
+            if (!r.resolveFunction(export_->ref()))
+                return false;
+            break;
+          case DefinitionKind::Global:
+            if (!r.resolveGlobal(export_->ref()))
+                return false;
+            break;
+          case DefinitionKind::Table:
+          case DefinitionKind::Memory:
+            break;
+        }
     }
 
     for (AstFunc* func : module->funcs()) {
         if (!ResolveFunc(r, *func))
             return false;
     }
 
     if (module->hasStartFunc()) {
         if (!r.resolveFunction(module->startFunc().func()))
             return false;
     }
 
+    for (AstElemSegment* segment : module->elemSegments()) {
+        if (!ResolveExpr(r, *segment->offset()))
+            return false;
+    }
+
     return true;
 }
 
 /*****************************************************************************/
 // wasm function body serialization
 
 static bool
 EncodeExpr(Encoder& e, AstExpr& expr);
@@ -3257,16 +3416,31 @@ static bool
 EncodeSetLocal(Encoder& e, AstSetLocal& sl)
 {
     return EncodeExpr(e, sl.value()) &&
            e.writeExpr(Expr::SetLocal) &&
            e.writeVarU32(sl.local().index());
 }
 
 static bool
+EncodeGetGlobal(Encoder& e, AstGetGlobal& gg)
+{
+    return e.writeExpr(Expr::GetGlobal) &&
+           e.writeVarU32(gg.global().index());
+}
+
+static bool
+EncodeSetGlobal(Encoder& e, AstSetGlobal& sg)
+{
+    return EncodeExpr(e, sg.value()) &&
+           e.writeExpr(Expr::SetGlobal) &&
+           e.writeVarU32(sg.global().index());
+}
+
+static bool
 EncodeUnaryOperator(Encoder& e, AstUnaryOperator& b)
 {
     return EncodeExpr(e, *b.op()) &&
            e.writeExpr(b.expr());
 }
 
 static bool
 EncodeBinaryOperator(Encoder& e, AstBinaryOperator& b)
@@ -3422,24 +3596,28 @@ EncodeExpr(Encoder& e, AstExpr& expr)
       case AstExprKind::ComparisonOperator:
         return EncodeComparisonOperator(e, expr.as<AstComparisonOperator>());
       case AstExprKind::Const:
         return EncodeConst(e, expr.as<AstConst>());
       case AstExprKind::ConversionOperator:
         return EncodeConversionOperator(e, expr.as<AstConversionOperator>());
       case AstExprKind::GetLocal:
         return EncodeGetLocal(e, expr.as<AstGetLocal>());
+      case AstExprKind::GetGlobal:
+        return EncodeGetGlobal(e, expr.as<AstGetGlobal>());
       case AstExprKind::If:
         return EncodeIf(e, expr.as<AstIf>());
       case AstExprKind::Load:
         return EncodeLoad(e, expr.as<AstLoad>());
       case AstExprKind::Return:
         return EncodeReturn(e, expr.as<AstReturn>());
       case AstExprKind::SetLocal:
         return EncodeSetLocal(e, expr.as<AstSetLocal>());
+      case AstExprKind::SetGlobal:
+        return EncodeSetGlobal(e, expr.as<AstSetGlobal>());
       case AstExprKind::Store:
         return EncodeStore(e, expr.as<AstStore>());
       case AstExprKind::BranchTable:
         return EncodeBranchTable(e, expr.as<AstBranchTable>());
       case AstExprKind::TernaryOperator:
         return EncodeTernaryOperator(e, expr.as<AstTernaryOperator>());
       case AstExprKind::UnaryOperator:
         return EncodeUnaryOperator(e, expr.as<AstUnaryOperator>());
@@ -3564,16 +3742,23 @@ EncodeImport(Encoder& e, bool newFormat,
     if (!e.writeVarU32(uint32_t(imp.kind())))
         return false;
 
     switch (imp.kind()) {
       case DefinitionKind::Function:
         if (!e.writeVarU32(imp.funcSig().index()))
             return false;
         break;
+      case DefinitionKind::Global:
+        MOZ_ASSERT(!imp.global().hasInit());
+        if (!e.writeValType(imp.global().type()))
+            return false;
+        if (!e.writeVarU32(imp.global().flags()))
+            return false;
+        break;
       case DefinitionKind::Table:
       case DefinitionKind::Memory:
         if (!EncodeResizable(e, imp.resizable()))
             return false;
         break;
     }
 
     return true;
@@ -3636,40 +3821,69 @@ EncodeMemorySection(Encoder& e, bool new
             return false;
     }
 
     e.finishSection(offset);
     return true;
 }
 
 static bool
+EncodeGlobalSection(Encoder& e, AstModule& module)
+{
+    size_t offset;
+    if (!e.startSection(GlobalSectionId, &offset))
+        return false;
+
+    const AstGlobalVector& globals = module.globals();
+
+    if (!e.writeVarU32(globals.length()))
+        return false;
+
+    for (const AstGlobal* global : globals) {
+        MOZ_ASSERT(global->hasInit());
+        if (!e.writeValType(global->type()))
+            return false;
+        if (!e.writeVarU32(global->flags()))
+            return false;
+        if (!EncodeExpr(e, global->init()))
+            return false;
+        if (!e.writeExpr(Expr::End))
+            return false;
+    }
+
+    e.finishSection(offset);
+    return true;
+}
+
+static bool
 EncodeExport(Encoder& e, bool newFormat, AstExport& exp)
 {
     if (!newFormat) {
         if (exp.kind() != DefinitionKind::Function)
             return true;
 
-        if (!e.writeVarU32(exp.func().index()))
+        if (!e.writeVarU32(exp.ref().index()))
             return false;
 
         if (!EncodeBytes(e, exp.name()))
             return false;
 
         return true;
     }
 
     if (!EncodeBytes(e, exp.name()))
         return false;
 
     if (!e.writeVarU32(uint32_t(exp.kind())))
         return false;
 
     switch (exp.kind()) {
       case DefinitionKind::Function:
-        if (!e.writeVarU32(exp.func().index()))
+      case DefinitionKind::Global:
+        if (!e.writeVarU32(exp.ref().index()))
             return false;
         break;
       case DefinitionKind::Table:
       case DefinitionKind::Memory:
         if (!e.writeVarU32(0))
             return false;
         break;
     }
@@ -3863,19 +4077,19 @@ EncodeDataSection(Encoder& e, bool newFo
 }
 
 static bool
 EncodeElemSegment(Encoder& e, AstElemSegment& segment)
 {
     if (!e.writeVarU32(0)) // table index
         return false;
 
-    if (!e.writeExpr(Expr::I32Const))
+    if (!EncodeExpr(e, *segment.offset()))
         return false;
-    if (!e.writeVarU32(segment.offset()))
+    if (!e.writeExpr(Expr::End))
         return false;
 
     if (!e.writeVarU32(segment.elems().length()))
         return false;
 
     for (const AstRef& elem : segment.elems()) {
         if (!e.writeVarU32(elem.index()))
             return false;
@@ -3927,16 +4141,19 @@ EncodeModule(AstModule& module, bool new
         return false;
 
     if (!EncodeTableSection(e, newFormat, module))
         return false;
 
     if (!EncodeMemorySection(e, newFormat, module))
         return false;
 
+    if (!EncodeGlobalSection(e, module))
+        return false;
+
     if (!EncodeExportSection(e, newFormat, module))
         return false;
 
     if (!EncodeStartSection(e, module))
         return false;
 
     if (!EncodeCodeSection(e, module))
         return false;
--- a/js/src/asmjs/WasmTypes.cpp
+++ b/js/src/asmjs/WasmTypes.cpp
@@ -32,17 +32,17 @@
 
 #include "vm/Stack-inl.h"
 
 using namespace js;
 using namespace js::jit;
 using namespace js::wasm;
 
 void
-Val::writePayload(uint8_t* dst)
+Val::writePayload(uint8_t* dst) const
 {
     switch (type_) {
       case ValType::I32:
       case ValType::F32:
         memcpy(dst, &u.i32_, sizeof(u.i32_));
         return;
       case ValType::I64:
       case ValType::F64:
--- a/js/src/asmjs/WasmTypes.h
+++ b/js/src/asmjs/WasmTypes.h
@@ -356,19 +356,21 @@ class Val
         MOZ_ASSERT(type_ == ValType::I32x4 || type_ == ValType::B32x4);
         return u.i32x4_;
     }
     const F32x4& f32x4() const {
         MOZ_ASSERT(type_ == ValType::F32x4);
         return u.f32x4_;
     }
 
-    void writePayload(uint8_t* dst);
+    void writePayload(uint8_t* dst) const;
 };
 
+typedef Vector<Val, 0, SystemAllocPolicy> ValVector;
+
 // The Sig class represents a WebAssembly function signature which takes a list
 // of value types and returns an expression type. The engine uses two in-memory
 // representations of the argument Vector's memory (when elements do not fit
 // inline): normal malloc allocation (via SystemAllocPolicy) and allocation in
 // a LifoAlloc (via LifoAllocPolicy). The former Sig objects can have any
 // lifetime since they own the memory. The latter Sig objects must not outlive
 // the associated LifoAlloc mark/release interval (which is currently the
 // duration of module validation+compilation). Thus, long-lived objects like
@@ -408,16 +410,152 @@ class Sig
 
 struct SigHashPolicy
 {
     typedef const Sig& Lookup;
     static HashNumber hash(Lookup sig) { return sig.hash(); }
     static bool match(const Sig* lhs, Lookup rhs) { return *lhs == rhs; }
 };
 
+// An InitExpr describes a deferred initializer expression, used to initialize
+// a global or a table element offset. Such expressions are created during
+// decoding and actually executed on module instantiation.
+
+class InitExpr
+{
+  public:
+    enum class Kind {
+        Constant,
+        GetGlobal
+    };
+
+  private:
+    Kind kind_;
+    union {
+        Val val_;
+        struct {
+            uint32_t index_;
+            ValType type_;
+        } global;
+    } u;
+
+  public:
+    InitExpr() = default;
+
+    explicit InitExpr(Val val) : kind_(Kind::Constant) {
+        u.val_ = val;
+    }
+
+    explicit InitExpr(uint32_t globalIndex, ValType type) : kind_(Kind::GetGlobal) {
+        u.global.index_ = globalIndex;
+        u.global.type_ = type;
+    }
+
+    Kind kind() const { return kind_; }
+
+    bool isVal() const { return kind() == Kind::Constant; }
+    Val val() const { MOZ_ASSERT(isVal()); return u.val_; }
+
+    uint32_t globalIndex() const { MOZ_ASSERT(kind() == Kind::GetGlobal); return u.global.index_; }
+
+    ValType type() const {
+        switch (kind()) {
+          case Kind::Constant: return u.val_.type();
+          case Kind::GetGlobal: return u.global.type_;
+        }
+        MOZ_CRASH("unexpected initExpr type");
+    }
+};
+
+// A GlobalDesc describes a single global variable. Currently, asm.js and wasm
+// exposes mutable and immutable private globals, but can't import nor export
+// mutable globals.
+
+enum class GlobalKind
+{
+    Import,
+    Constant,
+    Variable
+};
+
+class GlobalDesc
+{
+    union {
+        struct {
+            union {
+                InitExpr initial_;
+                struct {
+                    ValType type_;
+                    uint32_t index_;
+                } import;
+            } val;
+            unsigned offset_;
+            bool isMutable_;
+        } var;
+        Val cst_;
+    } u;
+    GlobalKind kind_;
+
+  public:
+    GlobalDesc() = default;
+
+    explicit GlobalDesc(InitExpr initial, bool isMutable)
+      : kind_((isMutable || !initial.isVal()) ? GlobalKind::Variable : GlobalKind::Constant)
+    {
+        if (isVariable()) {
+            u.var.val.initial_ = initial;
+            u.var.isMutable_ = isMutable;
+            u.var.offset_ = UINT32_MAX;
+        } else {
+            u.cst_ = initial.val();
+        }
+    }
+
+    explicit GlobalDesc(ValType type, bool isMutable, uint32_t importIndex)
+      : kind_(GlobalKind::Import)
+    {
+        u.var.val.import.type_ = type;
+        u.var.val.import.index_ = importIndex;
+        u.var.isMutable_ = isMutable;
+        u.var.offset_ = UINT32_MAX;
+    }
+
+    void setOffset(unsigned offset) {
+        MOZ_ASSERT(!isConstant());
+        MOZ_ASSERT(u.var.offset_ == UINT32_MAX);
+        u.var.offset_ = offset;
+    }
+    unsigned offset() const {
+        MOZ_ASSERT(!isConstant());
+        MOZ_ASSERT(u.var.offset_ != UINT32_MAX);
+        return u.var.offset_;
+    }
+
+    GlobalKind kind() const { return kind_; }
+    bool isVariable() const { return kind_ == GlobalKind::Variable; }
+    bool isConstant() const { return kind_ == GlobalKind::Constant; }
+    bool isImport() const { return kind_ == GlobalKind::Import; }
+
+    bool isMutable() const { return !isConstant() && u.var.isMutable_; }
+    Val constantValue() const { MOZ_ASSERT(isConstant()); return u.cst_; }
+    const InitExpr& initExpr() const { MOZ_ASSERT(isVariable()); return u.var.val.initial_; }
+    uint32_t importIndex() const { MOZ_ASSERT(isImport()); return u.var.val.import.index_; }
+
+    ValType type() const {
+        switch (kind_) {
+          case GlobalKind::Import:   return u.var.val.import.type_;
+          case GlobalKind::Variable: return u.var.val.initial_.type();
+          case GlobalKind::Constant: return u.cst_.type();
+        }
+        MOZ_CRASH("unexpected global kind");
+    }
+};
+
+typedef Vector<GlobalDesc, 0, SystemAllocPolicy> GlobalDescVector;
+
 // SigIdDesc describes a signature id that can be used by call_indirect and
 // table-entry prologues to structurally compare whether the caller and callee's
 // signatures *structurally* match. To handle the general case, a Sig is
 // allocated and stored in a process-wide hash table, so that pointer equality
 // implies structural equality. As an optimization for the 99% case where the
 // Sig has a small number of parameters, the Sig is bit-packed into a uint32
 // immediate value so that integer equality implies structural equality. Both
 // cases can be handled with a single comparison by always setting the LSB for
@@ -463,31 +601,16 @@ struct SigWithId : Sig
     void operator=(Sig&& rhs) { Sig::operator=(Move(rhs)); }
 
     WASM_DECLARE_SERIALIZABLE(SigWithId)
 };
 
 typedef Vector<SigWithId, 0, SystemAllocPolicy> SigWithIdVector;
 typedef Vector<const SigWithId*, 0, SystemAllocPolicy> SigWithIdPtrVector;
 
-// A GlobalDesc describes a single global variable. Currently, globals are only
-// exposed through asm.js.
-
-struct GlobalDesc
-{
-    ValType type;
-    unsigned globalDataOffset;
-    bool isConst;
-    GlobalDesc(ValType type, unsigned offset, bool isConst)
-      : type(type), globalDataOffset(offset), isConst(isConst)
-    {}
-};
-
-typedef Vector<GlobalDesc, 0, SystemAllocPolicy> GlobalDescVector;
-
 // The (,Profiling,Func)Offsets classes are used to record the offsets of
 // different key points in a CodeRange during compilation.
 
 struct Offsets
 {
     explicit Offsets(uint32_t begin = 0, uint32_t end = 0)
       : begin(begin), end(end)
     {}
@@ -935,16 +1058,17 @@ static const uint64_t MappedSize = 2 * U
 static const unsigned ActivationGlobalDataOffset = 0;
 static const unsigned HeapGlobalDataOffset       = ActivationGlobalDataOffset + sizeof(void*);
 static const unsigned NaN64GlobalDataOffset      = HeapGlobalDataOffset + sizeof(void*);
 static const unsigned NaN32GlobalDataOffset      = NaN64GlobalDataOffset + sizeof(double);
 static const unsigned InitialGlobalDataBytes     = NaN32GlobalDataOffset + sizeof(float);
 
 static const unsigned MaxSigs                    =        4 * 1024;
 static const unsigned MaxFuncs                   =      512 * 1024;
+static const unsigned MaxGlobals                 =        4 * 1024;
 static const unsigned MaxLocals                  =       64 * 1024;
 static const unsigned MaxImports                 =       64 * 1024;
 static const unsigned MaxExports                 =       64 * 1024;
 static const unsigned MaxTables                  =        4 * 1024;
 static const unsigned MaxTableElems              =      128 * 1024;
 static const unsigned MaxDataSegments            =       64 * 1024;
 static const unsigned MaxElemSegments            =       64 * 1024;
 static const unsigned MaxArgsPerFunc             =        4 * 1024;
--- a/js/src/jit-test/lib/wasm.js
+++ b/js/src/jit-test/lib/wasm.js
@@ -31,18 +31,19 @@ function jsify(wasmVal) {
     return wasmVal;
 }
 
 // Assert that the expected value is equal to the int64 value, as passed by
 // Baldr with --wasm-extra-tests {low: int32, high: int32}.
 // - if the expected value is in the int32 range, it can be just a number.
 // - otherwise, an object with the properties "high" and "low".
 function assertEqI64(observed, expect) {
-    assertEq(typeof observed, 'object');
-    assertEq(typeof expect === 'object' || typeof expect === 'number', true);
+    assertEq(typeof observed, 'object', "observed must be an i64 object");
+    assertEq(typeof expect === 'object' || typeof expect === 'number', true,
+             "expect must be an i64 object or number");
 
     let {low, high} = observed;
     if (typeof expect === 'number') {
         assertEq(expect, expect | 0, "in int32 range");
         assertEq(low, expect | 0, "low 32 bits don't match");
         assertEq(high, expect < 0 ? -1 : 0, "high 32 bits don't match"); // sign extension
     } else {
         assertEq(typeof expect.low, 'number');
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/globals.js
@@ -0,0 +1,209 @@
+// |jit-test| test-also-wasm-baseline
+load(libdir + "wasm.js");
+
+const { Instance, Module } = WebAssembly;
+const evalText = (txt, imports = {}) => new Instance(new Module(wasmTextToBinary(txt, 'new-format')), imports).exports;
+
+// Locally-defined globals
+assertErrorMessage(() => evalText(`(module (global))`), SyntaxError, /parsing/);
+assertErrorMessage(() => evalText(`(module (global i32))`), SyntaxError, /parsing/);
+assertErrorMessage(() => evalText(`(module (global i32 immutable))`), SyntaxError, /parsing/);
+
+// Initializer expressions.
+assertErrorMessage(() => evalText(`(module (global i32 (f32.const 13.37)))`), TypeError, /type mismatch/);
+assertErrorMessage(() => evalText(`(module (global f64 (f32.const 13.37)))`), TypeError, /type mismatch/);
+assertErrorMessage(() => evalText(`(module (global i32 (i32.add (i32.const 13) (i32.const 37))))`), TypeError, /failed to read end/);
+
+assertErrorMessage(() => evalText(`(module (global i32 (get_global 0)))`), TypeError, /out of range/);
+assertErrorMessage(() => evalText(`(module (global i32 (get_global 1)) (global i32 immutable (i32.const 1)))`), TypeError, /out of range/);
+
+// Test a well-defined global section.
+function testInner(type, initialValue, nextValue, coercion, assertFunc = assertEq)
+{
+    var module = evalText(`(module
+        (global ${type} (${type}.const ${initialValue}))
+        (global ${type} immutable (${type}.const ${initialValue}))
+
+        (func $get (result ${type}) (get_global 0))
+        (func $set (param ${type}) (set_global 0 (get_local 0)))
+
+        (func $get_cst (result ${type}) (get_global 1))
+
+        (export "get" $get)
+        (export "get_cst" $get_cst)
+
+        (export "set" $set)
+    )`);
+
+    assertFunc(module.get(), coercion(initialValue));
+    assertEq(module.set(coercion(nextValue)), undefined);
+    assertFunc(module.get(), coercion(nextValue));
+
+    assertFunc(module.get_cst(), coercion(initialValue));
+}
+
+testInner('i32', 13, 37, x => x|0);
+testInner('f32', 13.37, 0.1989, Math.fround);
+testInner('f64', 13.37, 0.1989, x => +x);
+
+// Semantic errors.
+assertErrorMessage(() => evalText(`(module (global i32 (i32.const 1337)) (func (set_global 1 (i32.const 0))))`), TypeError, /out of range/);
+assertErrorMessage(() => evalText(`(module (global i32 immutable (i32.const 1337)) (func (set_global 0 (i32.const 0))))`), TypeError, /can't write an immutable global/);
+
+// Big module with many variables: test that setting one doesn't overwrite the
+// other ones.
+function get_set(i, type) { return `
+    (func $get_${i} (result ${type}) (get_global ${i}))
+    (func $set_${i} (param ${type}) (set_global ${i} (get_local 0)))
+`
+}
+
+var module = evalText(`(module
+    (global i32 (i32.const 42))
+    (global i32 (i32.const 10))
+    (global f32 (f32.const 13.37))
+    (global f64 (f64.const 13.37))
+    (global i32 (i32.const -18))
+
+    ${get_set(0, 'i32')}
+    ${get_set(1, 'i32')}
+    ${get_set(2, 'f32')}
+    ${get_set(3, 'f64')}
+    ${get_set(4, 'i32')}
+
+    (export "get0" $get_0) (export "set0" $set_0)
+    (export "get1" $get_1) (export "set1" $set_1)
+    (export "get2" $get_2) (export "set2" $set_2)
+    (export "get3" $get_3) (export "set3" $set_3)
+    (export "get4" $get_4) (export "set4" $set_4)
+)`);
+
+let values = [42, 10, Math.fround(13.37), 13.37, -18];
+let nextValues = [13, 37, Math.fround(-17.89), 9.3, -13];
+for (let i = 0; i < 5; i++) {
+    assertEq(module[`get${i}`](), values[i]);
+    assertEq(module[`set${i}`](nextValues[i]), undefined);
+    assertEq(module[`get${i}`](), nextValues[i]);
+    for (let j = 0; j < 5; j++) {
+        if (i === j)
+            continue;
+        assertEq(module[`get${j}`](), values[j]);
+    }
+    assertEq(module[`set${i}`](values[i]), undefined);
+    assertEq(module[`get${i}`](), values[i]);
+}
+
+// Initializer expressions can also be used in elem section initializers.
+assertErrorMessage(() => evalText(`(module (import "globals" "a" (global f32 immutable)) (table (resizable 4)) (elem (get_global 0) $f) (func $f))`), TypeError, /type mismatch/);
+
+module = evalText(`(module
+    (import "globals" "a" (global i32 immutable))
+    (table (resizable 4))
+    (elem (get_global 0) $f)
+    (func $f)
+    (export "f" $f)
+    (export "tbl" table)
+)`, {
+    globals: {
+        a: 1
+    }
+});
+assertEq(module.f, module.tbl.get(1));
+
+// Import/export rules.
+assertErrorMessage(() => evalText(`(module (import "globals" "x" (global i32)))`), TypeError, /can't import.* mutable globals in the MVP/);
+assertErrorMessage(() => evalText(`(module (global i32 (i32.const 42)) (export "" global 0))`), TypeError, /can't .*export mutable globals in the MVP/);
+
+// Import/export semantics.
+module = evalText(`(module
+ (import $g "globals" "x" (global i32 immutable))
+ (func $get (result i32) (get_global $g))
+ (export "getter" $get)
+ (export "value" global 0)
+)`, { globals: {x: 42} });
+
+assertEq(module.getter(), 42);
+assertEq(module.value, 42);
+
+// Imported globals and locally defined globals use the same index space.
+module = evalText(`(module
+ (import "globals" "x" (global i32 immutable))
+ (global i32 immutable (i32.const 1337))
+ (export "imported" global 0)
+ (export "defined" global 1)
+)`, { globals: {x: 42} });
+
+assertEq(module.imported, 42);
+assertEq(module.defined, 1337);
+
+// Initializer expressions can reference an imported immutable global.
+assertErrorMessage(() => evalText(`(module (global f32 immutable (f32.const 13.37)) (global i32 (get_global 0)))`), TypeError, /must reference a global immutable import/);
+assertErrorMessage(() => evalText(`(module (global f32 (f32.const 13.37)) (global i32 (get_global 0)))`), TypeError, /must reference a global immutable import/);
+assertErrorMessage(() => evalText(`(module (global i32 (i32.const 0)) (global i32 (get_global 0)))`), TypeError, /must reference a global immutable import/);
+
+assertErrorMessage(() => evalText(`(module (import "globals" "a" (global f32 immutable)) (global i32 (get_global 0)))`), TypeError, /type mismatch/);
+
+function testInitExpr(type, initialValue, nextValue, coercion, assertFunc = assertEq) {
+    var module = evalText(`(module
+        (import "globals" "a" (global ${type} immutable))
+
+        (global ${type} (get_global 0))
+        (global ${type} immutable (get_global 0))
+
+        (func $get0 (result ${type}) (get_global 0))
+
+        (func $get1 (result ${type}) (get_global 1))
+        (func $set1 (param ${type}) (set_global 1 (get_local 0)))
+
+        (func $get_cst (result ${type}) (get_global 2))
+
+        (export "get0" $get0)
+        (export "get1" $get1)
+        (export "get_cst" $get_cst)
+
+        (export "set1" $set1)
+    )`, {
+        globals: {
+            a: coercion(initialValue)
+        }
+    });
+
+    assertFunc(module.get0(), coercion(initialValue));
+    assertFunc(module.get1(), coercion(initialValue));
+
+    assertEq(module.set1(coercion(nextValue)), undefined);
+    assertFunc(module.get1(), coercion(nextValue));
+    assertFunc(module.get0(), coercion(initialValue));
+
+    assertFunc(module.get_cst(), coercion(initialValue));
+}
+
+testInitExpr('i32', 13, 37, x => x|0);
+testInitExpr('f32', 13.37, 0.1989, Math.fround);
+testInitExpr('f64', 13.37, 0.1989, x => +x);
+
+// Int64.
+if (hasI64()) {
+    assertErrorMessage(() => evalText(`(module (import "globals" "x" (global i64 immutable)))`), TypeError, /can't import.* an Int64 global/);
+    assertErrorMessage(() => evalText(`(module (global i64 immutable (i64.const 42)) (export "" global 0))`), TypeError, /can't .*export an Int64 global/);
+
+    setJitCompilerOption('wasm.test-mode', 1);
+    testInner('i64', '0x531642753864975F', '0x123456789abcdef0', createI64, assertEqI64);
+    testInitExpr('i64', '0x531642753864975F', '0x123456789abcdef0', createI64, assertEqI64);
+
+    module = evalText(`(module
+     (import "globals" "x" (global i64 immutable))
+     (global i64 immutable (i64.const 0xFAFADADABABA))
+     (export "imported" global 0)
+     (export "defined" global 1)
+    )`, { globals: {x: createI64('0x1234567887654321')} });
+
+    assertEqI64(module.imported, createI64('0x1234567887654321'));
+    assertEqI64(module.defined, createI64('0xFAFADADABABA'));
+
+    setJitCompilerOption('wasm.test-mode', 0);
+} else {
+    assertErrorMessage(() => evalText(`(module (global i64 (i64.const 0)))`), TypeError, /NYI/);
+    assertErrorMessage(() => evalText(`(module (import "globals" "x" (global i64 immutable)))`), TypeError, /NYI/);
+}
+
--- a/js/src/jit-test/tests/wasm/import-export.js
+++ b/js/src/jit-test/tests/wasm/import-export.js
@@ -215,18 +215,18 @@ assertEq(e[""] instanceof Table, true);
 
 // Table export function identity
 
 var code = textToBinary(`(module
     (func $f (result i32) (i32.const 1))
     (func $g (result i32) (i32.const 2))
     (func $h (result i32) (i32.const 3))
     (table (resizable 4))
-    (elem 0 $f)
-    (elem 2 $g)
+    (elem (i32.const 0) $f)
+    (elem (i32.const 2) $g)
     (export "f1" $f)
     (export "tbl1" table)
     (export "f2" $f)
     (export "tbl2" table)
     (export "f3" $h)
 )`);
 var e = new Instance(new Module(code)).exports;
 assertEq(String(Object.keys(e)), "f1,tbl1,f2,tbl2,f3");
@@ -262,16 +262,17 @@ var code = textToBinary('(module (import
 var tbl = new Table({initial:1});
 var e = new Instance(new Module(code), {a:{b:tbl}}).exports;
 assertEq(tbl, e.foo);
 assertEq(tbl, e.bar);
 
 // Non-existent export errors
 
 assertErrorMessage(() => new Module(textToBinary('(module (export "a" 0))')), TypeError, /exported function index out of bounds/);
+assertErrorMessage(() => new Module(textToBinary('(module (export "a" global 0))')), TypeError, /exported global index out of bounds/);
 assertErrorMessage(() => new Module(textToBinary('(module (export "a" memory))')), TypeError, /exported memory index out of bounds/);
 assertErrorMessage(() => new Module(textToBinary('(module (export "a" table))')), TypeError, /exported table index out of bounds/);
 
 // Default memory/table rules
 
 assertErrorMessage(() => new Module(textToBinary('(module (import "a" "b" (memory 1 1)) (memory 1 1))')), TypeError, /already have default memory/);
 assertErrorMessage(() => new Module(textToBinary('(module (import "a" "b" (memory 1 1)) (import "x" "y" (memory 2 2)))')), TypeError, /already have default memory/);
 assertErrorMessage(() => new Module(textToBinary('(module (import "a" "b" (table 1 1)) (table 1 1))')), TypeError, /already have default table/);
@@ -304,18 +305,18 @@ assertEq(i8[100], 0xc);
 assertEq(i8[101], 0xd);
 assertEq(i8[102], 0x0);
 
 // Elem segments on imports
 
 var m = new Module(textToBinary(`
     (module
         (import "a" "b" (table 10))
-        (elem 0 $one $two)
-        (elem 3 $three $four)
+        (elem (i32.const 0) $one $two)
+        (elem (i32.const 3) $three $four)
         (func $one (result i32) (i32.const 1))
         (func $two (result i32) (i32.const 2))
         (func $three (result i32) (i32.const 3))
         (func $four (result i32) (i32.const 4)))
 `));
 var tbl = new Table({initial:10});
 new Instance(m, {a:{b:tbl}});
 assertEq(tbl.get(0)(), 1);
--- a/js/src/jit-test/tests/wasm/table-gc.js
+++ b/js/src/jit-test/tests/wasm/table-gc.js
@@ -16,17 +16,17 @@ const evalText = (str, imports) => new I
 
 var caller = `(type $v2i (func (result i32))) (func $call (param $i i32) (result i32) (call_indirect $v2i (get_local $i))) (export "call" $call)`
 var callee = i => `(func $f${i} (type $v2i) (result i32) (i32.const ${i}))`;
 
 // A table should not hold exported functions alive and exported functions
 // should not hold their originating table alive. Live exported functions should
 // hold instances alive. Nothing should hold the export object alive.
 resetFinalizeCount();
-var i = evalText(`(module (table (resizable 2)) (export "tbl" table) (elem 0 $f0) ${callee(0)} ${caller})`);
+var i = evalText(`(module (table (resizable 2)) (export "tbl" table) (elem (i32.const 0) $f0) ${callee(0)} ${caller})`);
 var e = i.exports;
 var t = e.tbl;
 var f = t.get(0);
 assertEq(f(), e.call(0));
 assertErrorMessage(() => e.call(1), Error, /bad wasm indirect call/);
 assertErrorMessage(() => e.call(2), Error, /out-of-range/);
 assertEq(finalizeCount(), 0);
 i.edge = makeFinalizeObserver();
@@ -54,17 +54,17 @@ gc();
 assertEq(finalizeCount(), 3);
 assertEq(f(), 0);
 f = null;
 gc();
 assertEq(finalizeCount(), 5);
 
 // A table should hold the instance of any of its elements alive.
 resetFinalizeCount();
-var i = evalText(`(module (table (resizable 1)) (export "tbl" table) (elem 0 $f0) ${callee(0)} ${caller})`);
+var i = evalText(`(module (table (resizable 1)) (export "tbl" table) (elem (i32.const 0) $f0) ${callee(0)} ${caller})`);
 var e = i.exports;
 var t = e.tbl;
 var f = t.get(0);
 i.edge = makeFinalizeObserver();
 e.edge = makeFinalizeObserver();
 t.edge = makeFinalizeObserver();
 f.edge = makeFinalizeObserver();
 gc();
--- a/js/src/jit-test/tests/wasm/tables.js
+++ b/js/src/jit-test/tests/wasm/tables.js
@@ -9,53 +9,68 @@ const Table = WebAssembly.Table;
 // Explicitly opt into the new binary format for imports and exports until it
 // is used by default everywhere.
 const textToBinary = str => wasmTextToBinary(str, 'new-format');
 
 const evalText = (str, imports) => new Instance(new Module(textToBinary(str)), imports);
 
 var callee = i => `(func $f${i} (result i32) (i32.const ${i}))`;
 
-assertErrorMessage(() => new Module(textToBinary(`(module (elem 0 $f0) ${callee(0)})`)), TypeError, /table index out of range/);
-assertErrorMessage(() => new Module(textToBinary(`(module (table (resizable 10)) (elem 0 0))`)), TypeError, /table element out of range/);
-assertErrorMessage(() => new Module(textToBinary(`(module (table (resizable 10)) (func) (elem 0 0 1))`)), TypeError, /table element out of range/);
-assertErrorMessage(() => new Module(textToBinary(`(module (table (resizable 10)) (elem 10 $f0) ${callee(0)})`)), TypeError, /element segment does not fit/);
-assertErrorMessage(() => new Module(textToBinary(`(module (table (resizable 10)) (elem 8 $f0 $f0 $f0) ${callee(0)})`)), TypeError, /element segment does not fit/);
-assertErrorMessage(() => new Module(textToBinary(`(module (table (resizable 10)) (elem 1 $f0 $f0) (elem 0 $f0) ${callee(0)})`)), TypeError, /must be.*ordered/);
-assertErrorMessage(() => new Module(textToBinary(`(module (table (resizable 10)) (elem 1 $f0 $f0) (elem 2 $f0) ${callee(0)})`)), TypeError, /must be.*disjoint/);
+assertErrorMessage(() => new Module(textToBinary(`(module (elem (i32.const 0) $f0) ${callee(0)})`)), TypeError, /table index out of range/);
+assertErrorMessage(() => new Module(textToBinary(`(module (table (resizable 10)) (elem (i32.const 0) 0))`)), TypeError, /table element out of range/);
+assertErrorMessage(() => new Module(textToBinary(`(module (table (resizable 10)) (func) (elem (i32.const 0) 0 1))`)), TypeError, /table element out of range/);
+assertErrorMessage(() => new Module(textToBinary(`(module (table (resizable 10)) (func) (elem (f32.const 0) 0) ${callee(0)})`)), TypeError, /type mismatch/);
+
+assertErrorMessage(() => new Module(textToBinary(`(module (table (resizable 10)) (elem (i32.const 10) $f0) ${callee(0)})`)), TypeError, /element segment does not fit/);
+assertErrorMessage(() => new Module(textToBinary(`(module (table (resizable 10)) (elem (i32.const 8) $f0 $f0 $f0) ${callee(0)})`)), TypeError, /element segment does not fit/);
+assertErrorMessage(() => new Module(textToBinary(`(module (table (resizable 10)) (elem (i32.const 1) $f0 $f0) (elem (i32.const 0) $f0) ${callee(0)})`)), TypeError, /must be.*ordered/);
+assertErrorMessage(() => new Module(textToBinary(`(module (table (resizable 10)) (elem (i32.const 1) $f0 $f0) (elem (i32.const 2) $f0) ${callee(0)})`)), TypeError, /must be.*disjoint/);
+
+assertErrorMessage(() => evalText(`(module (table (resizable 10)) (import "globals" "a" (global i32 immutable)) (elem (get_global 0) $f0) ${callee(0)})`, {globals:{a:10}}), TypeError, /element segment does not fit/);
+assertErrorMessage(() => evalText(`(module (table (resizable 10)) (import "globals" "a" (global i32 immutable)) (elem (get_global 0) $f0 $f0 $f0) ${callee(0)})`, {globals:{a:8}}), TypeError, /element segment does not fit/);
+assertErrorMessage(() => evalText(`(module (table (resizable 10)) (import "globals" "a" (global i32 immutable)) (elem (i32.const 1) $f0 $f0) (elem (get_global 0) $f0) ${callee(0)})`, {globals:{a:0}}), TypeError, /must be.*ordered/);
+assertErrorMessage(() => evalText(`(module (table (resizable 10)) (import "globals" "a" (global i32 immutable)) (elem (get_global 0) $f0 $f0) (elem (i32.const 2) $f0) ${callee(0)})`, {globals:{a:1}}), TypeError, /must be.*disjoint/);
+
+var tbl = new Table({initial:50});
+assertErrorMessage(() => evalText(`(module
+    (import "globals" "table" (table 10 100))
+    (import "globals" "a" (global i32 immutable))
+    (elem (get_global 0) $f0 $f0)
+    ${callee(0)})
+`, {globals:{a:20, table:tbl}}), Error, /element segment does not fit/);
 
 var caller = `(type $v2i (func (result i32))) (func $call (param $i i32) (result i32) (call_indirect $v2i (get_local $i))) (export "call" $call)`
 var callee = i => `(func $f${i} (type $v2i) (result i32) (i32.const ${i}))`;
 
 var call = evalText(`(module (table (resizable 10)) ${callee(0)} ${caller})`).exports.call;
 assertErrorMessage(() => call(0), Error, /bad wasm indirect call/);
 assertErrorMessage(() => call(10), Error, /out-of-range/);
 
-var call = evalText(`(module (table (resizable 10)) (elem 0) ${callee(0)} ${caller})`).exports.call;
+var call = evalText(`(module (table (resizable 10)) (elem (i32.const 0)) ${callee(0)} ${caller})`).exports.call;
 assertErrorMessage(() => call(0), Error, /bad wasm indirect call/);
 assertErrorMessage(() => call(10), Error, /out-of-range/);
 
-var call = evalText(`(module (table (resizable 10)) (elem 0 $f0) ${callee(0)} ${caller})`).exports.call;
+var call = evalText(`(module (table (resizable 10)) (elem (i32.const 0) $f0) ${callee(0)} ${caller})`).exports.call;
 assertEq(call(0), 0);
 assertErrorMessage(() => call(1), Error, /bad wasm indirect call/);
 assertErrorMessage(() => call(2), Error, /bad wasm indirect call/);
 assertErrorMessage(() => call(10), Error, /out-of-range/);
 
-var call = evalText(`(module (table (resizable 10)) (elem 1 $f0 $f1) (elem 4 $f0 $f2) ${callee(0)} ${callee(1)} ${callee(2)} ${caller})`).exports.call;
+var call = evalText(`(module (table (resizable 10)) (elem (i32.const 1) $f0 $f1) (elem (i32.const 4) $f0 $f2) ${callee(0)} ${callee(1)} ${callee(2)} ${caller})`).exports.call;
 assertErrorMessage(() => call(0), Error, /bad wasm indirect call/);
 assertEq(call(1), 0);
 assertEq(call(2), 1);
 assertErrorMessage(() => call(3), Error, /bad wasm indirect call/);
 assertEq(call(4), 0);
 assertEq(call(5), 2);
 assertErrorMessage(() => call(6), Error, /bad wasm indirect call/);
 assertErrorMessage(() => call(10), Error, /out-of-range/);
 
 var tbl = new Table({initial:3});
-var call = evalText(`(module (import "a" "b" (table 2)) (export "tbl" table) (elem 0 $f0 $f1) ${callee(0)} ${callee(1)} ${caller})`, {a:{b:tbl}}).exports.call;
+var call = evalText(`(module (import "a" "b" (table 2)) (export "tbl" table) (elem (i32.const 0) $f0 $f1) ${callee(0)} ${callee(1)} ${caller})`, {a:{b:tbl}}).exports.call;
 assertEq(call(0), 0);
 assertEq(call(1), 1);
 assertEq(tbl.get(0)(), 0);
 assertEq(tbl.get(1)(), 1);
 
 // Call signatures are matched structurally:
 
 var call = evalText(`(module
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -2134,16 +2134,18 @@ jit::MakeMRegExpHoistable(MIRGraph& grap
             regexp->setMovable();
             regexp->setDoNotClone();
 
             // That would be incorrect for global/sticky, because lastIndex
             // could be wrong.  Therefore setting the lastIndex to 0. That is
             // faster than a not movable regexp.
             RegExpObject* source = regexp->source();
             if (source->sticky() || source->global()) {
+                if (!graph.alloc().ensureBallast())
+                    return false;
                 MConstant* zero = MConstant::New(graph.alloc(), Int32Value(0));
                 regexp->block()->insertAfter(regexp, zero);
 
                 MStoreFixedSlot* lastIndex =
                     MStoreFixedSlot::New(graph.alloc(), regexp, RegExpObject::lastIndexSlot(), zero);
                 regexp->block()->insertAfter(zero, lastIndex);
             }
         }
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -4078,23 +4078,30 @@ LIRGenerator::visitHasClass(MHasClass* i
     MOZ_ASSERT(ins->object()->type() == MIRType::Object);
     MOZ_ASSERT(ins->type() == MIRType::Boolean);
     define(new(alloc()) LHasClass(useRegister(ins->object())), ins);
 }
 
 void
 LIRGenerator::visitWasmLoadGlobalVar(MWasmLoadGlobalVar* ins)
 {
-    define(new(alloc()) LWasmLoadGlobalVar, ins);
+    if (ins->type() == MIRType::Int64)
+        defineInt64(new(alloc()) LWasmLoadGlobalVarI64, ins);
+    else
+        define(new(alloc()) LWasmLoadGlobalVar, ins);
 }
 
 void
 LIRGenerator::visitWasmStoreGlobalVar(MWasmStoreGlobalVar* ins)
 {
-    add(new(alloc()) LWasmStoreGlobalVar(useRegisterAtStart(ins->value())), ins);
+    MDefinition* value = ins->value();
+    if (value->type() == MIRType::Int64)
+        add(new(alloc()) LWasmStoreGlobalVarI64(useInt64RegisterAtStart(value)), ins);
+    else
+        add(new(alloc()) LWasmStoreGlobalVar(useRegisterAtStart(value)), ins);
 }
 
 void
 LIRGenerator::visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr* ins)
 {
     define(new(alloc()) LAsmJSLoadFuncPtr(useRegister(ins->index())), ins);
 }
 
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -8057,31 +8057,53 @@ class LWasmLoadGlobalVar : public LInstr
 {
   public:
     LIR_HEADER(WasmLoadGlobalVar);
     MWasmLoadGlobalVar* mir() const {
         return mir_->toWasmLoadGlobalVar();
     }
 };
 
+class LWasmLoadGlobalVarI64 : public LInstructionHelper<INT64_PIECES, 0, 0>
+{
+  public:
+    LIR_HEADER(WasmLoadGlobalVarI64);
+    MWasmLoadGlobalVar* mir() const {
+        return mir_->toWasmLoadGlobalVar();
+    }
+};
+
 class LWasmStoreGlobalVar : public LInstructionHelper<0, 1, 0>
 {
   public:
     LIR_HEADER(WasmStoreGlobalVar);
     explicit LWasmStoreGlobalVar(const LAllocation& value) {
         setOperand(0, value);
     }
     MWasmStoreGlobalVar* mir() const {
         return mir_->toWasmStoreGlobalVar();
     }
     const LAllocation* value() {
         return getOperand(0);
     }
 };
 
+class LWasmStoreGlobalVarI64 : public LInstructionHelper<0, INT64_PIECES, 0>
+{
+  public:
+    LIR_HEADER(WasmStoreGlobalVarI64);
+    explicit LWasmStoreGlobalVarI64(const LInt64Allocation& value) {
+        setInt64Operand(0, value);
+    }
+    MWasmStoreGlobalVar* mir() const {
+        return mir_->toWasmStoreGlobalVar();
+    }
+    static const uint32_t InputIndex = 0;
+};
+
 class LAsmJSLoadFuncPtr : public LInstructionHelper<1, 1, 0>
 {
   public:
     LIR_HEADER(AsmJSLoadFuncPtr);
     explicit LAsmJSLoadFuncPtr(const LAllocation& index) {
         setOperand(0, index);
     }
     const MAsmJSLoadFuncPtr* mir() const {
--- a/js/src/jit/shared/LOpcodes-shared.h
+++ b/js/src/jit/shared/LOpcodes-shared.h
@@ -385,17 +385,19 @@
     _(HasClass)                     \
     _(AsmSelect)                    \
     _(AsmSelectI64)                 \
     _(WasmLoad)                     \
     _(WasmLoadI64)                  \
     _(WasmStore)                    \
     _(WasmBoundsCheck)              \
     _(WasmLoadGlobalVar)            \
+    _(WasmLoadGlobalVarI64)         \
     _(WasmStoreGlobalVar)           \
+    _(WasmStoreGlobalVarI64)        \
     _(AsmJSLoadHeap)                \
     _(AsmJSStoreHeap)               \
     _(AsmJSLoadFuncPtr)             \
     _(AsmJSLoadFFIFunc)             \
     _(AsmJSParameter)               \
     _(AsmJSReturn)                  \
     _(AsmJSVoidReturn)              \
     _(AsmJSPassStackArg)            \
--- a/js/src/jit/x64/CodeGenerator-x64.cpp
+++ b/js/src/jit/x64/CodeGenerator-x64.cpp
@@ -1207,16 +1207,25 @@ CodeGeneratorX64::visitWasmLoadGlobalVar
       default:
         MOZ_CRASH("unexpected type in visitWasmLoadGlobalVar");
     }
 
     masm.append(wasm::GlobalAccess(label, mir->globalDataOffset()));
 }
 
 void
+CodeGeneratorX64::visitWasmLoadGlobalVarI64(LWasmLoadGlobalVarI64* ins)
+{
+    MWasmLoadGlobalVar* mir = ins->mir();
+    MOZ_ASSERT(mir->type() == MIRType::Int64);
+    CodeOffset label = masm.loadRipRelativeInt64(ToRegister(ins->output()));
+    masm.append(wasm::GlobalAccess(label, mir->globalDataOffset()));
+}
+
+void
 CodeGeneratorX64::visitWasmStoreGlobalVar(LWasmStoreGlobalVar* ins)
 {
     MWasmStoreGlobalVar* mir = ins->mir();
 
     MIRType type = mir->value()->type();
     MOZ_ASSERT(IsNumberType(type) || IsSimdType(type));
 
     CodeOffset label;
@@ -1242,16 +1251,26 @@ CodeGeneratorX64::visitWasmStoreGlobalVa
       default:
         MOZ_CRASH("unexpected type in visitWasmStoreGlobalVar");
     }
 
     masm.append(wasm::GlobalAccess(label, mir->globalDataOffset()));
 }
 
 void
+CodeGeneratorX64::visitWasmStoreGlobalVarI64(LWasmStoreGlobalVarI64* ins)
+{
+    MWasmStoreGlobalVar* mir = ins->mir();
+    MOZ_ASSERT(mir->value()->type() == MIRType::Int64);
+    Register value = ToRegister(ins->getOperand(LWasmStoreGlobalVarI64::InputIndex));
+    CodeOffset label = masm.storeRipRelativeInt64(value);
+    masm.append(wasm::GlobalAccess(label, mir->globalDataOffset()));
+}
+
+void
 CodeGeneratorX64::visitTruncateDToInt32(LTruncateDToInt32* ins)
 {
     FloatRegister input = ToFloatRegister(ins->input());
     Register output = ToRegister(ins->output());
 
     // On x64, branchTruncateDouble uses vcvttsd2sq. Unlike the x86
     // implementation, this should handle most doubles and we can just
     // call a stub if it fails.
--- a/js/src/jit/x64/CodeGenerator-x64.h
+++ b/js/src/jit/x64/CodeGenerator-x64.h
@@ -72,16 +72,18 @@ class CodeGeneratorX64 : public CodeGene
     void visitInt64ToFloatingPoint(LInt64ToFloatingPoint* lir);
     void visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic* ins);
     void visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic* ins);
     void visitWasmLoad(LWasmLoad* ins);
     void visitWasmLoadI64(LWasmLoadI64* ins);
     void visitWasmStore(LWasmStore* ins);
     void visitWasmLoadGlobalVar(LWasmLoadGlobalVar* ins);
     void visitWasmStoreGlobalVar(LWasmStoreGlobalVar* ins);
+    void visitWasmLoadGlobalVarI64(LWasmLoadGlobalVarI64* ins);
+    void visitWasmStoreGlobalVarI64(LWasmStoreGlobalVarI64* ins);
     void visitAsmSelectI64(LAsmSelectI64* ins);
     void visitAsmJSCall(LAsmJSCall* ins);
     void visitAsmJSLoadHeap(LAsmJSLoadHeap* ins);
     void visitAsmJSStoreHeap(LAsmJSStoreHeap* ins);
     void visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins);
     void visitAsmJSAtomicExchangeHeap(LAsmJSAtomicExchangeHeap* ins);
     void visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap* ins);
     void visitAsmJSAtomicBinopHeapForEffect(LAsmJSAtomicBinopHeapForEffect* ins);
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -5549,17 +5549,17 @@ static void DebugPaintItem(DrawTarget& a
                            nsDisplayItem *aItem,
                            nsDisplayListBuilder* aBuilder)
 {
   bool snap;
   Rect bounds = NSRectToRect(aItem->GetBounds(aBuilder, &snap),
                              aPresContext->AppUnitsPerDevPixel());
 
   RefPtr<DrawTarget> tempDT =
-    aDrawTarget.CreateSimilarDrawTarget(IntSize(bounds.width, bounds.height),
+    aDrawTarget.CreateSimilarDrawTarget(IntSize::Truncate(bounds.width, bounds.height),
                                         SurfaceFormat::B8G8R8A8);
   RefPtr<gfxContext> context = gfxContext::CreateOrNull(tempDT);
   if (!context) {
     // Leave this as crash, it's in the debugging code, we want to know
     gfxDevCrash(LogReason::InvalidContext) << "DebugPaintItem context problem " << gfx::hexa(tempDT);
     return;
   }
   context->SetMatrix(gfxMatrix::Translation(-bounds.x, -bounds.y));
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -6536,17 +6536,17 @@ ComputeSnappedImageDrawingParameters(gfx
     aImage->OptimalImageSizeForDest(snappedDestSize,
                                     imgIContainer::FRAME_CURRENT,
                                     aSamplingFilter, aImageFlags);
   gfxSize imageSize(intImageSize.width, intImageSize.height);
 
   // XXX(seth): May be buggy; see bug 1151016.
   CSSIntSize svgViewportSize = currentMatrix.IsIdentity()
     ? CSSIntSize(intImageSize.width, intImageSize.height)
-    : CSSIntSize(devPixelDest.width, devPixelDest.height);
+    : CSSIntSize::Truncate(devPixelDest.width, devPixelDest.height);
 
   // Compute the set of pixels that would be sampled by an ideal rendering
   gfxPoint subimageTopLeft =
     MapToFloatImagePixels(imageSize, devPixelDest, devPixelFill.TopLeft());
   gfxPoint subimageBottomRight =
     MapToFloatImagePixels(imageSize, devPixelDest, devPixelFill.BottomRight());
   gfxRect subimage;
   subimage.MoveTo(NSToIntFloor(subimageTopLeft.x),
--- a/layout/generic/nsCanvasFrame.cpp
+++ b/layout/generic/nsCanvasFrame.cpp
@@ -302,18 +302,18 @@ nsDisplayCanvasBackgroundImage::Paint(ns
     RefPtr<DrawTarget> dt = 
       Frame()->Properties().Get(nsIFrame::CachedBackgroundImageDT());
     DrawTarget* destDT = dest->GetDrawTarget();
     if (dt) {
       BlitSurface(destDT, destRect, dt);
       return;
     }
 
-    dt = destDT->CreateSimilarDrawTarget(IntSize(ceil(destRect.width),
-                                                 ceil(destRect.height)),
+    dt = destDT->CreateSimilarDrawTarget(IntSize::Ceil(destRect.width,
+                                                       destRect.height),
                                          SurfaceFormat::B8G8R8A8);
     if (dt && dt->IsValid()) {
       RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(dt);
       MOZ_ASSERT(ctx); // already checked draw target above
       ctx->SetMatrix(ctx->CurrentMatrix().Translate(-destRect.x, -destRect.y));
       nsRenderingContext context(ctx);
       PaintInternal(aBuilder, &context, bgClipRect, &bgClipRect);
       BlitSurface(dest->GetDrawTarget(), destRect, dt);
--- a/layout/svg/nsSVGImageFrame.cpp
+++ b/layout/svg/nsSVGImageFrame.cpp
@@ -398,17 +398,17 @@ nsSVGImageFrame::PaintSVG(gfxContext& aC
 
     if (mImageContainer->GetType() == imgIContainer::TYPE_VECTOR) {
       // Package up the attributes of this image element which can override the
       // attributes of mImageContainer's internal SVG document.  The 'width' &
       // 'height' values we're passing in here are in CSS units (though they
       // come from width/height *attributes* in SVG). They influence the region
       // of the SVG image's internal document that is visible, in combination
       // with preserveAspectRatio and viewBox.
-      SVGImageContext context(CSSIntSize(width, height),
+      SVGImageContext context(CSSIntSize::Truncate(width, height),
                               Some(imgElem->mPreserveAspectRatio.GetAnimValue()),
                               1.0, true);
 
       // For the actual draw operation to draw crisply (and at the right size),
       // our destination rect needs to be |width|x|height|, *in dev pixels*.
       LayoutDeviceSize devPxSize(width, height);
       nsRect destRect(nsPoint(),
                       LayoutDevicePixel::ToAppUnits(devPxSize,
--- a/netwerk/test/unit/test_cache2-29c-concurrent_read_half-interrupted.js
+++ b/netwerk/test/unit/test_cache2-29c-concurrent_read_half-interrupted.js
@@ -55,21 +55,16 @@ function contentHandler(metadata, respon
     let len = responseBody.length;
 	  response.setHeader("Content-Range", "0-" + (len - 1) + "/" + len);
   }
   response.bodyOutputStream.write(responseBody, responseBody.length);
 }
 
 function run_test()
 {
-  if (!newCacheBackEndUsed()) {
-    do_check_true(true, "This test doesn't run when the old cache back end is used since the behavior is different");
-    return;
-  }
-
   // Static check
   do_check_true(responseBody.length > 1024);
 
   do_get_profile();
 
   Services.prefs.setIntPref("browser.cache.disk.max_entry_size", 1);
 
   httpServer = new HttpServer();
--- a/netwerk/test/unit/test_cache2-29d-concurrent_read_half-corrupted-206.js
+++ b/netwerk/test/unit/test_cache2-29d-concurrent_read_half-corrupted-206.js
@@ -60,16 +60,21 @@ function contentHandler(metadata, respon
 
 function run_test()
 {
   // Static check
   do_check_true(responseBody.length > 1024);
 
   do_get_profile();
 
+  if (!newCacheBackEndUsed()) {
+    do_check_true(true, "This test doesn't run when the old cache back end is used since the behavior is different");
+    return;
+  }
+
   Services.prefs.setIntPref("browser.cache.disk.max_entry_size", 1);
 
   httpServer = new HttpServer();
   httpServer.registerPathHandler("/content", contentHandler);
   httpServer.start(-1);
 
   var chan1 = make_channel(URL + "/content");
   chan1.asyncOpen2(new ChannelListener(firstTimeThrough, null));
--- a/netwerk/test/unit/test_cache2-29e-concurrent_read_half-non-206-response.js
+++ b/netwerk/test/unit/test_cache2-29e-concurrent_read_half-non-206-response.js
@@ -50,26 +50,26 @@ function contentHandler(metadata, respon
   response.setHeader("Cache-Control", "max-age=99999");
   response.setHeader("Accept-Ranges", "bytes");
   response.setHeader("Content-Length", "" + responseBody.length);
   response.bodyOutputStream.write(responseBody, responseBody.length);
 }
 
 function run_test()
 {
+  // Static check
+  do_check_true(responseBody.length > 1024);
+
+  do_get_profile();
+
   if (!newCacheBackEndUsed()) {
     do_check_true(true, "This test doesn't run when the old cache back end is used since the behavior is different");
     return;
   }
 
-  // Static check
-  do_check_true(responseBody.length > 1024);
-
-  do_get_profile();
-
   Services.prefs.setIntPref("browser.cache.disk.max_entry_size", 1);
 
   httpServer = new HttpServer();
   httpServer.registerPathHandler("/content", contentHandler);
   httpServer.start(-1);
 
   var chan1 = make_channel(URL + "/content");
   chan1.asyncOpen2(new ChannelListener(firstTimeThrough, null));
--- a/security/sandbox/linux/common/SandboxInfo.cpp
+++ b/security/sandbox/linux/common/SandboxInfo.cpp
@@ -277,19 +277,21 @@ SandboxInfo::SubmitTelemetry()
   Telemetry::Accumulate(Telemetry::SANDBOX_HAS_USER_NAMESPACES,
                         sandboxInfo.Test(SandboxInfo::kHasUserNamespaces));
   Telemetry::Accumulate(Telemetry::SANDBOX_CONTENT_ENABLED,
                         sandboxInfo.Test(SandboxInfo::kEnabledForContent));
   Telemetry::Accumulate(Telemetry::SANDBOX_MEDIA_ENABLED,
                         sandboxInfo.Test(SandboxInfo::kEnabledForMedia));
 }
 
+#ifdef MOZ_CRASHREPORTER
 void
 SandboxInfo::AnnotateCrashReport() const
 {
   nsAutoCString flagsString;
   flagsString.AppendInt(mFlags);
 
   CrashReporter::AnnotateCrashReport(
     NS_LITERAL_CSTRING("ContentSandboxCapabilities"), flagsString);
 }
+#endif
 
 } // namespace mozilla
--- a/security/sandbox/linux/common/SandboxInfo.h
+++ b/security/sandbox/linux/common/SandboxInfo.h
@@ -52,17 +52,19 @@ public:
   }
 
   // Returns true if SetMediaPluginSandbox may be called.
   bool CanSandboxMedia() const
   {
     return !Test(kEnabledForMedia) || Test(kHasSeccompBPF);
   }
 
+#ifdef MOZ_CRASHREPORTER
   MOZ_EXPORT void AnnotateCrashReport() const;
+#endif
 
   static void SubmitTelemetry();
 
   // For bug 1222500 or anything else like it: On desktop, this is
   // called in the parent process at a point when it should still be
   // single-threaded, to check that the SandboxEarlyInit() call in a
   // child process is early enough to be single-threaded.  If not,
   // kUnexpectedThreads is set and affected flags (user namespaces;
--- a/widget/InputData.cpp
+++ b/widget/InputData.cpp
@@ -16,18 +16,18 @@ namespace mozilla {
 
 using namespace dom;
 
 already_AddRefed<Touch> SingleTouchData::ToNewDOMTouch() const
 {
   MOZ_ASSERT(NS_IsMainThread(),
              "Can only create dom::Touch instances on main thread");
   RefPtr<Touch> touch = new Touch(mIdentifier,
-                                  LayoutDeviceIntPoint(mScreenPoint.x, mScreenPoint.y),
-                                  LayoutDeviceIntPoint(mRadius.width, mRadius.height),
+                                  LayoutDeviceIntPoint::Truncate(mScreenPoint.x, mScreenPoint.y),
+                                  LayoutDeviceIntPoint::Truncate(mRadius.width, mRadius.height),
                                   mRotationAngle,
                                   mForce);
   return touch.forget();
 }
 
 MouseInput::MouseInput(const WidgetMouseEventBase& aMouseEvent)
   : InputData(MOUSE_INPUT, aMouseEvent.mTime, aMouseEvent.mTimeStamp,
               aMouseEvent.mModifiers)
--- a/widget/android/AndroidJavaWrappers.cpp
+++ b/widget/android/AndroidJavaWrappers.cpp
@@ -502,20 +502,20 @@ AndroidGeckoEvent::MakeTouchEvent(nsIWid
     const LayoutDeviceIntPoint& offset = widget->WidgetToScreenOffset();
     event.mTouches.SetCapacity(endIndex - startIndex);
     for (int i = startIndex; i < endIndex; i++) {
         // In this code branch, we are dispatching this event directly
         // into Gecko (as opposed to going through the AsyncPanZoomController),
         // and the Points() array has points in CSS pixels, which we need
         // to convert.
         CSSToLayoutDeviceScale scale = widget->GetDefaultScale();
-        LayoutDeviceIntPoint pt(
+        auto pt = LayoutDeviceIntPoint::Truncate(
             (Points()[i].x * scale.scale) - offset.x,
             (Points()[i].y * scale.scale) - offset.y);
-        LayoutDeviceIntPoint radius(
+        auto radius = LayoutDeviceIntPoint::Truncate(
             PointRadii()[i].x * scale.scale,
             PointRadii()[i].y * scale.scale);
         RefPtr<Touch> t = new Touch(PointIndicies()[i],
                                     pt,
                                     radius,
                                     Orientations()[i],
                                     Pressures()[i]);
         event.mTouches.AppendElement(t);
@@ -620,18 +620,18 @@ AndroidGeckoEvent::MakeMouseEvent(nsIWid
     event.mTime = Time();
 
     // We are dispatching this event directly into Gecko (as opposed to going
     // through the AsyncPanZoomController), and the Points() array has points
     // in CSS pixels, which we need to convert to LayoutDevice pixels.
     const LayoutDeviceIntPoint& offset = widget->WidgetToScreenOffset();
     CSSToLayoutDeviceScale scale = widget->GetDefaultScale();
     event.mRefPoint =
-        LayoutDeviceIntPoint((Points()[0].x * scale.scale) - offset.x,
-                             (Points()[0].y * scale.scale) - offset.y);
+        LayoutDeviceIntPoint::Truncate((Points()[0].x * scale.scale) - offset.x,
+                                       (Points()[0].y * scale.scale) - offset.y);
     return event;
 }
 
 Modifiers
 AndroidGeckoEvent::DOMModifiers() const
 {
     Modifiers result = 0;
     if (mMetaState & AMETA_ALT_MASK) {
--- a/widget/android/nsWindow.cpp
+++ b/widget/android/nsWindow.cpp
@@ -1615,18 +1615,19 @@ nsWindow::Resize(double aX,
 
     bool needSizeDispatch = aWidth != mBounds.width || aHeight != mBounds.height;
 
     mBounds.x = NSToIntRound(aX);
     mBounds.y = NSToIntRound(aY);
     mBounds.width = NSToIntRound(aWidth);
     mBounds.height = NSToIntRound(aHeight);
 
-    if (needSizeDispatch)
-        OnSizeChanged(gfx::IntSize(aWidth, aHeight));
+    if (needSizeDispatch) {
+        OnSizeChanged(gfx::IntSize::Truncate(aWidth, aHeight));
+    }
 
     // Should we skip honoring aRepaint here?
     if (aRepaint && FindTopLevel() == nsWindow::TopWindow())
         RedrawAll();
 
     nsIWidgetListener* listener = GetWidgetListener();
     if (mAwaitingFullScreen && listener) {
       listener->FullscreenChanged(mIsFullScreen);
--- a/widget/cocoa/nsChildView.mm
+++ b/widget/cocoa/nsChildView.mm
@@ -3741,17 +3741,17 @@ NSEvent* gLastDragMouseDownEvent = nil;
   // scales one user space unit to one Cocoa point, which can consist of
   // multiple dev pixels. But Gecko expects its supplied context to be scaled
   // to device pixels, so we need to reverse the scaling.
   double scale = mGeckoChild->BackingScaleFactor();
   CGContextSaveGState(aContext);
   CGContextScaleCTM(aContext, 1.0 / scale, 1.0 / scale);
 
   NSSize viewSize = [self bounds].size;
-  nsIntSize backingSize(viewSize.width * scale, viewSize.height * scale);
+  gfx::IntSize backingSize = gfx::IntSize::Truncate(viewSize.width * scale, viewSize.height * scale);
   LayoutDeviceIntRegion region = [self nativeDirtyRegionWithBoundingRect:aRect];
 
   bool painted = mGeckoChild->PaintWindowInContext(aContext, region, backingSize);
 
   // Undo the scale transform so that from now on the context is in
   // CocoaPoints again.
   CGContextRestoreGState(aContext);
 
@@ -4121,17 +4121,17 @@ NSEvent* gLastDragMouseDownEvent = nil;
           break;
         }
       }
 
       if (shouldRollup) {
         if ([theEvent type] == NSLeftMouseDown) {
           NSPoint point = [NSEvent mouseLocation];
           FlipCocoaScreenCoordinate(point);
-          nsIntPoint pos(point.x, point.y);
+          gfx::IntPoint pos = gfx::IntPoint::Truncate(point.x, point.y);
           consumeEvent = (BOOL)rollupListener->Rollup(popupsToRollup, true, &pos, nullptr);
         }
         else {
           consumeEvent = (BOOL)rollupListener->Rollup(popupsToRollup, true, nullptr, nullptr);
         }
       }
     }
   }
@@ -5765,17 +5765,17 @@ PanGestureTypeForEvent(NSEvent* aEvent)
     NS_ASSERTION(mDragService, "Couldn't get a drag service - big problem!");
   }
 
   if (mDragService) {
     // set the dragend point from the current mouse location
     nsDragService* dragService = static_cast<nsDragService *>(mDragService);
     NSPoint pnt = [NSEvent mouseLocation];
     FlipCocoaScreenCoordinate(pnt);
-    dragService->SetDragEndPoint(nsIntPoint(NSToIntRound(pnt.x), NSToIntRound(pnt.y)));
+    dragService->SetDragEndPoint(gfx::IntPoint::Round(pnt.x, pnt.y));
 
     // XXX: dropEffect should be updated per |operation|.
     // As things stand though, |operation| isn't well handled within "our"
     // events, that is, when the drop happens within the window: it is set
     // either to NSDragOperationGeneric or to NSDragOperationNone.
     // For that reason, it's not yet possible to override dropEffect per the
     // given OS value, and it's also unclear what's the correct dropEffect
     // value for NSDragOperationGeneric that is passed by other applications.
--- a/widget/cocoa/nsCocoaUtils.mm
+++ b/widget/cocoa/nsCocoaUtils.mm
@@ -472,17 +472,17 @@ nsresult nsCocoaUtils::CreateNSImageFrom
 {
   RefPtr<SourceSurface> surface;
   int32_t width = 0, height = 0;
   aImage->GetWidth(&width);
   aImage->GetHeight(&height);
 
   // Render a vector image at the correct resolution on a retina display
   if (aImage->GetType() == imgIContainer::TYPE_VECTOR && scaleFactor != 1.0f) {
-    IntSize scaledSize(ceil(width * scaleFactor), ceil(height * scaleFactor));
+    IntSize scaledSize = IntSize::Ceil(width * scaleFactor, height * scaleFactor);
 
     RefPtr<DrawTarget> drawTarget = gfxPlatform::GetPlatform()->
       CreateOffscreenContentDrawTarget(scaledSize, SurfaceFormat::B8G8R8A8);
     if (!drawTarget || !drawTarget->IsValid()) {
       NS_ERROR("Failed to create valid DrawTarget");
       return NS_ERROR_FAILURE;
     }
 
--- a/widget/cocoa/nsDeviceContextSpecX.mm
+++ b/widget/cocoa/nsDeviceContextSpecX.mm
@@ -141,17 +141,17 @@ void nsDeviceContextSpecX::GetPaperRect(
 already_AddRefed<PrintTarget> nsDeviceContextSpecX::MakePrintTarget()
 {
     NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
 
     double top, left, bottom, right;
     GetPaperRect(&top, &left, &bottom, &right);
     const double width = right - left;
     const double height = bottom - top;
-    IntSize size(floor(width), floor(height));
+    IntSize size = IntSize::Floor(width, height);
 
     CGContextRef context;
     ::PMSessionGetCGGraphicsContext(mPrintSession, &context);
 
     if (context) {
         // Initially, origin is at bottom-left corner of the paper.
         // Here, we translate it to top-left corner of the paper.
         CGContextTranslateCTM(context, 0, height);
--- a/widget/gtk/nsDeviceContextSpecG.cpp
+++ b/widget/gtk/nsDeviceContextSpecG.cpp
@@ -156,17 +156,17 @@ already_AddRefed<PrintTarget> nsDeviceCo
     if (mIsPPreview) {
       // There is nothing to detect on Print Preview, use PS.
       format = nsIPrintSettings::kOutputFormatPS;
     } else {
       return nullptr;
     }
   }
 
-  IntSize size(width, height);
+  IntSize size = IntSize::Truncate(width, height);
 
   if (format == nsIPrintSettings::kOutputFormatPDF) {
     return PrintTargetPDF::CreateOrNull(stream, size);
   }
 
   int32_t orientation;
   mPrintSettings->GetOrientation(&orientation);
   return PrintTargetPS::CreateOrNull(stream,
--- a/widget/gtk/nsDragService.cpp
+++ b/widget/gtk/nsDragService.cpp
@@ -232,18 +232,18 @@ OnSourceGrabEventAfter(GtkWidget *widget
             gdk_event_free(sMotionEvent);
         }
         sMotionEvent = gdk_event_copy(event);
 
         // Update the cursor position.  The last of these recorded gets used for
         // the eDragEnd event.
         nsDragService *dragService = static_cast<nsDragService*>(user_data);
         gint scale = nsScreenGtk::GetGtkMonitorScaleFactor();
-        LayoutDeviceIntPoint p(floor(event->motion.x_root * scale + 0.5),
-                               floor(event->motion.y_root * scale + 0.5));
+        auto p = LayoutDeviceIntPoint::Round(event->motion.x_root * scale,
+                                             event->motion.y_root * scale);
         dragService->SetDragEndPoint(p);
     } else if (sMotionEvent && (event->type == GDK_KEY_PRESS ||
                                 event->type == GDK_KEY_RELEASE)) {
         // Update modifier state from key events.
         sMotionEvent->motion.state = event->key.state;
     } else {
         return;
     }
--- a/widget/gtk/nsWindow.cpp
+++ b/widget/gtk/nsWindow.cpp
@@ -5205,17 +5205,17 @@ nsWindow::CheckForRollup(gdouble aMouseX
                   }
                   break;
                 }
             } // foreach parent menu widget
         } // if rollup listener knows about menus
 
         // if we've determined that we should still rollup, do it.
         bool usePoint = !aIsWheel && !aAlwaysRollup;
-        nsIntPoint point(aMouseX, aMouseY);
+        IntPoint point = IntPoint::Truncate(aMouseX, aMouseY);
         if (rollup && rollupListener->Rollup(popupsToRollup, true, usePoint ? &point : nullptr, nullptr)) {
             retVal = true;
         }
     }
     return retVal;
 }
 
 /* static */
@@ -6806,17 +6806,17 @@ int
 nsWindow::GdkCoordToDevicePixels(gint coord) {
     return coord * GdkScaleFactor();
 }
 
 LayoutDeviceIntPoint
 nsWindow::GdkEventCoordsToDevicePixels(gdouble x, gdouble y)
 {
     gint scale = GdkScaleFactor();
-    return LayoutDeviceIntPoint(floor(x * scale + 0.5), floor(y * scale + 0.5));
+    return LayoutDeviceIntPoint::Round(x * scale, y * scale);
 }
 
 LayoutDeviceIntPoint
 nsWindow::GdkPointToDevicePixels(GdkPoint point) {
     gint scale = GdkScaleFactor();
     return LayoutDeviceIntPoint(point.x * scale,
                                 point.y * scale);
 }
--- a/widget/nsDeviceContextSpecProxy.cpp
+++ b/widget/nsDeviceContextSpecProxy.cpp
@@ -76,17 +76,17 @@ nsDeviceContextSpecProxy::MakePrintTarge
     return nullptr;
   }
 
   // convert twips to points
   width /= TWIPS_PER_POINT_FLOAT;
   height /= TWIPS_PER_POINT_FLOAT;
 
   RefPtr<gfxASurface> surface = gfxPlatform::GetPlatform()->
-    CreateOffscreenSurface(mozilla::gfx::IntSize(width, height),
+    CreateOffscreenSurface(mozilla::gfx::IntSize::Truncate(width, height),
                            mozilla::gfx::SurfaceFormat::A8R8G8B8_UINT32);
   if (!surface) {
     return nullptr;
   }
 
   // The type of PrintTarget that we return here shouldn't really matter since
   // our implementation of GetDrawEventRecorder returns an object, which means
   // the DrawTarget returned by the PrintTarget will be a DrawTargetRecording.
--- a/widget/windows/nsDeviceContextSpecWin.cpp
+++ b/widget/windows/nsDeviceContextSpecWin.cpp
@@ -247,17 +247,17 @@ already_AddRefed<PrintTarget> nsDeviceCo
     }
 
     nsCOMPtr<nsIFileOutputStream> stream = do_CreateInstance("@mozilla.org/network/file-output-stream;1");
     rv = stream->Init(file, -1, -1, 0);
     if (NS_FAILED(rv)) {
       return nullptr;
     }
 
-    return PrintTargetPDF::CreateOrNull(stream, IntSize(width, height));
+    return PrintTargetPDF::CreateOrNull(stream, IntSize::Truncate(width, height));
   }
 
   if (mDevMode) {
     NS_WARN_IF_FALSE(mDriverName, "No driver!");
     HDC dc = ::CreateDCW(mDriverName, mDeviceName, nullptr, mDevMode);
     if (!dc) {
       gfxCriticalError(gfxCriticalError::DefaultOptions(false))
         << "Failed to create device context in GetSurfaceForPrinter";