Merge m-c to autoland, a=merge
authorWes Kocher <wkocher@mozilla.com>
Tue, 26 Jul 2016 16:54:33 -0700
changeset 331811 6bd2b7d2ebe3f81c513e4519812b2a8f3d1ca5a0
parent 331810 6e79dedcd251514c129c87c998bf44ee63d197f6 (current diff)
parent 331795 462dc6b44adb4573e8ce6b0dd688c206ebd516f7 (diff)
child 331812 acec32a2a1762b73b7dbce4c87fde3bd05562919
push id9858
push userjlund@mozilla.com
push dateMon, 01 Aug 2016 14:37:10 +0000
treeherdermozilla-aurora@203106ef6cb6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone50.0a1
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";