Merge inbound to central, a=merge
authorWes Kocher <wkocher@mozilla.com>
Fri, 31 Mar 2017 17:48:18 -0700
changeset 350766 00a166a8640dffa2e0f48650f966d75ca3c1836e
parent 350764 7fe20a149f135533746ecaa9a1cf99619101eae0 (current diff)
parent 350765 7b2da53804c611d5c766ac93aee90fbd6bd3d1ed (diff)
child 350767 1e69ab4e04cc26fdcd130952515d8462405dbdb3
child 350822 c531393dd51fedcc7ab4c8691b2ba33e577e1dd1
push id88710
push userkwierso@gmail.com
push dateSat, 01 Apr 2017 00:54:06 +0000
treeherdermozilla-inbound@1e69ab4e04cc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone55.0a1
first release with
nightly linux32
00a166a8640d / 55.0a1 / 20170401100144 / files
nightly linux64
00a166a8640d / 55.0a1 / 20170401100144 / files
nightly mac
00a166a8640d / 55.0a1 / 20170401030204 / files
nightly win32
00a166a8640d / 55.0a1 / 20170401030204 / files
nightly win64
00a166a8640d / 55.0a1 / 20170401030204 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to central, a=merge
layout/ipc/RenderFrameParent.cpp
--- a/browser/components/sessionstore/SessionCookies.jsm
+++ b/browser/components/sessionstore/SessionCookies.jsm
@@ -23,18 +23,18 @@ const MAX_EXPIRY = Math.pow(2, 62);
 /**
  * The external API implemented by the SessionCookies module.
  */
 this.SessionCookies = Object.freeze({
   update(windows) {
     SessionCookiesInternal.update(windows);
   },
 
-  getHostsForWindow(window, checkPrivacy = false) {
-    return SessionCookiesInternal.getHostsForWindow(window, checkPrivacy);
+  getHostsForWindow(window) {
+    return SessionCookiesInternal.getHostsForWindow(window);
   },
 
   restore(cookies) {
     SessionCookiesInternal.restore(cookies);
   }
 });
 
 /**
@@ -53,26 +53,30 @@ var SessionCookiesInternal = {
    *
    * @param windows
    *        Array of window state objects.
    *        [{ tabs: [...], cookies: [...] }, ...]
    */
   update(windows) {
     this._ensureInitialized();
 
+    // Check whether we're allowed to store cookies.
+    let storeAnyCookies = PrivacyLevel.canSave(false);
+    let storeSecureCookies = PrivacyLevel.canSave(true);
+
     for (let window of windows) {
       let cookies = [];
 
-      // Collect all cookies for the current window.
-      for (let host of this.getHostsForWindow(window, true)) {
-        for (let cookie of CookieStore.getCookiesForHost(host)) {
-          // getCookiesForHost() will only return hosts with the right privacy
-          // rules. Check again here to exclude HTTPS-only cookies if needed.
-          if (PrivacyLevel.canSave(cookie.secure)) {
-            cookies.push(cookie);
+      if (storeAnyCookies) {
+        // Collect all cookies for the current window.
+        for (let host of this.getHostsForWindow(window)) {
+          for (let cookie of CookieStore.getCookiesForHost(host)) {
+            if (!cookie.secure || storeSecureCookies) {
+              cookies.push(cookie);
+            }
           }
         }
       }
 
       // Don't include/keep empty cookie sections.
       if (cookies.length) {
         window.cookies = cookies;
       } else if ("cookies" in window) {
@@ -82,26 +86,24 @@ var SessionCookiesInternal = {
   },
 
   /**
    * Returns a map of all hosts for a given window that we might want to
    * collect cookies for.
    *
    * @param window
    *        A window state object containing tabs with history entries.
-   * @param checkPrivacy (bool)
-   *        Whether to check the privacy level for each host.
    * @return {set} A set of hosts for a given window state object.
    */
-  getHostsForWindow(window, checkPrivacy = false) {
+  getHostsForWindow(window) {
     let hosts = new Set();
 
     for (let tab of window.tabs) {
       for (let entry of tab.entries) {
-        this._extractHostsFromEntry(entry, hosts, checkPrivacy);
+        this._extractHostsFromEntry(entry, hosts);
       }
     }
 
     return hosts;
   },
 
   /**
    * Restores a given list of session cookies.
@@ -166,36 +168,29 @@ var SessionCookiesInternal = {
   /**
    * Fill a given map with hosts found in the given entry's session history and
    * any child entries.
    *
    * @param entry
    *        the history entry, serialized
    * @param hosts
    *        the set that will be used to store hosts
-   * @param checkPrivacy
-   *        should we check the privacy level for https
    */
-  _extractHostsFromEntry(entry, hosts, checkPrivacy) {
+  _extractHostsFromEntry(entry, hosts) {
     try {
       // It's alright if this throws for about: URIs.
       let {host, scheme} = Utils.makeURI(entry.url);
-
-      if (scheme == "file") {
+      if (/^(file|https?)$/.test(scheme)) {
         hosts.add(host);
-      } else if (/https?/.test(scheme)) {
-        if (!checkPrivacy || PrivacyLevel.canSave(scheme == "https")) {
-          hosts.add(host);
-        }
       }
     } catch (ex) { }
 
     if (entry.children) {
       for (let child of entry.children) {
-        this._extractHostsFromEntry(child, hosts, checkPrivacy);
+        this._extractHostsFromEntry(child, hosts);
       }
     }
   },
 
   /**
    * Updates or adds a given cookie to the store.
    */
   _updateCookie(cookie) {
--- a/browser/components/sessionstore/test/browser_cookies.js
+++ b/browser/components/sessionstore/test/browser_cookies.js
@@ -1,17 +1,17 @@
 "use strict";
 
 const PATH = "/browser/browser/components/sessionstore/test/";
 
 /**
  * Remove all cookies to start off a clean slate.
  */
 add_task(function* test_setup() {
-  requestLongerTimeout(2);
+  requestLongerTimeout(3);
   Services.cookies.removeAll();
 });
 
 /**
  * Test multiple scenarios with different Set-Cookie header domain= params.
  */
 add_task(function* test_run() {
   // Set-Cookie: foobar=random()
@@ -75,32 +75,134 @@ add_task(function* test_run() {
     domain: ".www.example.com",
     cookieHost: ".www.example.com",
     cookieURIs: ["http://www.example.com/" + PATH],
     noCookieURIs: ["http://example.com"]
   });
 });
 
 /**
+ * Test multiple scenarios with different privacy levels.
+ */
+add_task(function* test_run_privacy_level() {
+  registerCleanupFunction(() => {
+    Services.prefs.clearUserPref("browser.sessionstore.privacy_level");
+  });
+
+  // With the default privacy level we collect all cookies.
+  yield testCookieCollection({
+    host: "http://example.com",
+    domain: ".example.com",
+    cookieHost: ".example.com",
+    cookieURIs: ["https://example.com/" + PATH, "http://example.com/" + PATH],
+    noCookieURIs: ["about:robots"]
+  });
+
+  // With the default privacy level we collect all cookies.
+  yield testCookieCollection({
+    host: "https://example.com",
+    domain: ".example.com",
+    cookieHost: ".example.com",
+    cookieURIs: ["https://example.com/" + PATH, "http://example.com/" + PATH],
+    noCookieURIs: ["about:robots"]
+  });
+
+  // With the default privacy level we collect all cookies.
+  yield testCookieCollection({
+    isSecure: true,
+    host: "https://example.com",
+    domain: ".example.com",
+    cookieHost: ".example.com",
+    cookieURIs: ["https://example.com/" + PATH, "http://example.com/" + PATH],
+    noCookieURIs: ["about:robots"]
+  });
+
+  // Set level=encrypted, don't store any secure cookies.
+  Services.prefs.setIntPref("browser.sessionstore.privacy_level", 1);
+
+  // With level=encrypted, non-secure cookies will be stored.
+  yield testCookieCollection({
+    host: "http://example.com",
+    domain: ".example.com",
+    cookieHost: ".example.com",
+    cookieURIs: ["https://example.com/" + PATH, "http://example.com/" + PATH],
+    noCookieURIs: ["about:robots"]
+  });
+
+  // With level=encrypted, non-secure cookies will be stored,
+  // even if sent by an HTTPS site.
+  yield testCookieCollection({
+    host: "https://example.com",
+    domain: ".example.com",
+    cookieHost: ".example.com",
+    cookieURIs: ["https://example.com/" + PATH, "http://example.com/" + PATH],
+    noCookieURIs: ["about:robots"]
+  });
+
+  // With level=encrypted, secure cookies will NOT be stored.
+  yield testCookieCollection({
+    isSecure: true,
+    host: "https://example.com",
+    domain: ".example.com",
+    cookieHost: ".example.com",
+    noCookieURIs: ["https://example.com/" + PATH, "http://example.com/" + PATH]
+  });
+
+  // Set level=full, don't store any cookies.
+  Services.prefs.setIntPref("browser.sessionstore.privacy_level", 2);
+
+  // With level=full we must not store any cookies.
+  yield testCookieCollection({
+    host: "http://example.com",
+    domain: ".example.com",
+    cookieHost: ".example.com",
+    noCookieURIs: ["https://example.com/" + PATH, "http://example.com/" + PATH]
+  });
+
+  // With level=full we must not store any cookies.
+  yield testCookieCollection({
+    host: "https://example.com",
+    domain: ".example.com",
+    cookieHost: ".example.com",
+    noCookieURIs: ["https://example.com/" + PATH, "http://example.com/" + PATH]
+  });
+
+  // With level=full we must not store any cookies.
+  yield testCookieCollection({
+    isSecure: true,
+    host: "https://example.com",
+    domain: ".example.com",
+    cookieHost: ".example.com",
+    noCookieURIs: ["https://example.com/" + PATH, "http://example.com/" + PATH]
+  });
+
+  Services.prefs.clearUserPref("browser.sessionstore.privacy_level");
+});
+
+/**
  * Generic test function to check sessionstore's cookie collection module with
  * different cookie domains given in the Set-Cookie header. See above for some
  * usage examples.
  */
 var testCookieCollection = async function(params) {
   let tab = gBrowser.addTab("about:blank");
   let browser = tab.linkedBrowser;
 
   let urlParams = new URLSearchParams();
   let value = Math.random();
   urlParams.append("value", value);
 
   if (params.domain) {
     urlParams.append("domain", params.domain);
   }
 
+  if (params.isSecure) {
+    urlParams.append("secure", "1");
+  }
+
   // Construct request URI.
   let requestUri = `${params.host}${PATH}browser_cookies.sjs?${urlParams}`;
 
   // Wait for the browser to load and the cookie to be set.
   // These two events can probably happen in no particular order,
   // so let's wait for them in parallel.
   await Promise.all([
     waitForNewCookie(),
--- a/browser/components/sessionstore/test/browser_cookies.sjs
+++ b/browser/components/sessionstore/test/browser_cookies.sjs
@@ -7,15 +7,20 @@ Components.utils.importGlobalProperties(
 
 function handleRequest(req, resp) {
   resp.setStatusLine(req.httpVersion, 200);
 
   let params = new URLSearchParams(req.queryString);
   let value = params.get("value");
 
   let domain = "";
-  if  (params.has("domain")) {
+  if (params.has("domain")) {
     domain = `; Domain=${params.get("domain")}`;
   }
 
-  resp.setHeader("Set-Cookie", `foobar=${value}${domain}`);
+  let secure = "";
+  if (params.has("secure")) {
+    secure = "; Secure";
+  }
+
+  resp.setHeader("Set-Cookie", `foobar=${value}${domain}${secure}`);
   resp.write("<meta charset=utf-8>hi");
 }
--- a/dom/media/webaudio/test/test_nodeCreationDocumentGone.html
+++ b/dom/media/webaudio/test/test_nodeCreationDocumentGone.html
@@ -4,38 +4,46 @@
   <title>Test whether we can create an AudioContext interface</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
+SimpleTest.requestCompleteLog();
 SimpleTest.waitForExplicitFinish();
 
+
 SpecialPowers.pushPrefEnv({
   set: [
     [ "dom.disable_open_during_load", false ]
   ]
 }).then(function() {
+  info("after pref");
   var sub = encodeURI("data:text/html,<!DOCTYPE html>\n"+
                       "<html><script>"+
                       "var context = new AudioContext();"+
                       "setTimeout(function(){window.close();},1000);\x3C/script></html>");
   window.onload = function(){
+    info("after onload");
     var a = window.open(sub);
+    info("after open: " + a);
     a.onbeforeunload = function(){
       setTimeout(function(){
         try {
+          info("before sp");
           a.context.createScriptProcessor(512, 1, 1);
+          info("after sp");
         } catch(e) {
           ok (true,"got exception");
         }
         setTimeout(function() {
-        ok (true,"no crash");
+          info("finish");
+          ok (true,"no crash");
           SimpleTest.finish();
         }, 0);
       }, 0);
     };
   };
 });
 
 
new file mode 100644
--- /dev/null
+++ b/dom/xslt/crashtests/1338277.html
@@ -0,0 +1,21 @@
+<script id=o_xml type="text/plain"><?xml version="1.0" encoding="UTF-8"?>
+<xb></xb>
+</script>
+<script id=o_xslt type="text/plain"><?xml version="1.0" encoding="UTF-8"?>
+<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+  <xsl:template match="xb[$v1]"></xsl:template>
+  <xsl:variable name="v1">
+    <xsl:attribute name="a1" namespace="z">
+      <xsl:variable name="v2" select="$v2"></xsl:variable>
+    </xsl:attribute>
+  </xsl:variable>
+</xsl:transform>
+</script>
+<script>
+addEventListener("DOMContentLoaded", function(){
+  let doc = new DOMParser();
+  let xsltPrs = new XSLTProcessor();
+  xsltPrs.importStylesheet(doc.parseFromString(o_xslt.textContent, "text/xml"));
+  xsltPrs.transformToDocument(doc.parseFromString(o_xml.textContent, "text/xml"));
+});
+</script>
--- a/dom/xslt/crashtests/crashtests.list
+++ b/dom/xslt/crashtests/crashtests.list
@@ -13,8 +13,9 @@ load 528963.xml
 load 545927.html
 load 601543.html
 load 602115.html
 load 603844.html
 load 667315.xml
 load 1089049.html
 load 1205163.xml
 load 1243337.xml
+load 1338277.html
--- a/dom/xslt/xpath/XPathExpression.cpp
+++ b/dom/xslt/xpath/XPathExpression.cpp
@@ -201,20 +201,22 @@ nsresult
 EvalContextImpl::getVariable(int32_t aNamespace,
                              nsIAtom* aLName,
                              txAExprResult*& aResult)
 {
     aResult = 0;
     return NS_ERROR_INVALID_ARG;
 }
 
-bool
-EvalContextImpl::isStripSpaceAllowed(const txXPathNode& aNode)
+nsresult
+EvalContextImpl::isStripSpaceAllowed(const txXPathNode& aNode, bool& aAllowed)
 {
-    return false;
+    aAllowed = false;
+
+    return NS_OK;
 }
 
 void*
 EvalContextImpl::getPrivateContext()
 {
     // we don't have a private context here.
     return nullptr;
 }
--- a/dom/xslt/xpath/txExpr.h
+++ b/dom/xslt/xpath/txExpr.h
@@ -23,16 +23,17 @@
   Much of this code was ported from XSL:P.
 */
 
 class nsIAtom;
 class txIMatchContext;
 class txIEvalContext;
 class txNodeSet;
 class txXPathNode;
+class txXPathTreeWalker;
 
 /**
  * A Base Class for all XSL Expressions
 **/
 class Expr
 {
 public:
     Expr()
@@ -389,18 +390,19 @@ public:
     }
 
     /*
      * Virtual methods
      * pretty much a txPattern, but not supposed to be used 
      * standalone. The NodeTest node() is different to the
      * Pattern "node()" (document node isn't matched)
      */
-    virtual bool matches(const txXPathNode& aNode,
-                           txIMatchContext* aContext) = 0;
+    virtual nsresult matches(const txXPathNode& aNode,
+                             txIMatchContext* aContext,
+                             bool& aMatched) = 0;
     virtual double getDefaultPriority() = 0;
 
     /**
      * Returns the type of this nodetest.
      */
     enum NodeTestType {
         NAME_TEST,
         NODETYPE_TEST,
@@ -419,17 +421,19 @@ public:
 
 #ifdef TX_TO_STRING
     virtual void toString(nsAString& aDest) = 0;
 #endif
 };
 
 #define TX_DECL_NODE_TEST \
     TX_DECL_TOSTRING \
-    bool matches(const txXPathNode& aNode, txIMatchContext* aContext) override; \
+    nsresult matches(const txXPathNode& aNode, \
+                     txIMatchContext* aContext, \
+                     bool& aMatched) override; \
     double getDefaultPriority() override; \
     bool isSensitiveTo(Expr::ContextSensitivity aContext) override;
 
 /*
  * This class represents a NameTest as defined by the XPath spec
  */
 class txNameTest : public txNodeTest
 {
@@ -625,20 +629,40 @@ public:
         return mAxisIdentifier;
     }
     void setAxisIdentifier(LocationStepType aAxisIdentifier)
     {
         mAxisIdentifier = aAxisIdentifier;
     }
 
 private:
-    void fromDescendants(const txXPathNode& aNode, txIMatchContext* aCs,
-                         txNodeSet* aNodes);
-    void fromDescendantsRev(const txXPathNode& aNode, txIMatchContext* aCs,
-                            txNodeSet* aNodes);
+    /**
+     * Append the current position of aWalker to aNodes if it matches mNodeTest,
+     * using aContext as the context for matching.
+     */
+    nsresult appendIfMatching(const txXPathTreeWalker& aWalker,
+                              txIMatchContext* aContext,
+                              txNodeSet* aNodes);
+
+    /**
+     * Append the descendants of the current position of aWalker to aNodes if
+     * they match mNodeTest, using aContext as the context for matching.
+     */
+    nsresult appendMatchingDescendants(const txXPathTreeWalker& aWalker,
+                                       txIMatchContext* aContext,
+                                       txNodeSet* aNodes);
+
+    /**
+     * Append the descendants of the current position of aWalker to aNodes in
+     * reverse order if they match mNodeTest, using aContext as the context for
+     * matching.
+     */
+    nsresult appendMatchingDescendantsRev(const txXPathTreeWalker& aWalker,
+                                          txIMatchContext* aContext,
+                                          txNodeSet* aNodes);
 
     nsAutoPtr<txNodeTest> mNodeTest;
     LocationStepType mAxisIdentifier;
 };
 
 class FilterExpr : public Expr,
                    public PredicateList
 {
--- a/dom/xslt/xpath/txForwardContext.cpp
+++ b/dom/xslt/xpath/txForwardContext.cpp
@@ -25,20 +25,21 @@ uint32_t txForwardContext::position()
 
 nsresult txForwardContext::getVariable(int32_t aNamespace, nsIAtom* aLName,
                                        txAExprResult*& aResult)
 {
     NS_ASSERTION(mInner, "mInner is null!!!");
     return mInner->getVariable(aNamespace, aLName, aResult);
 }
 
-bool txForwardContext::isStripSpaceAllowed(const txXPathNode& aNode)
+nsresult
+txForwardContext::isStripSpaceAllowed(const txXPathNode& aNode, bool& aAllowed)
 {
     NS_ASSERTION(mInner, "mInner is null!!!");
-    return mInner->isStripSpaceAllowed(aNode);
+    return mInner->isStripSpaceAllowed(aNode, aAllowed);
 }
 
 void* txForwardContext::getPrivateContext()
 {
     NS_ASSERTION(mInner, "mInner is null!!!");
     return mInner->getPrivateContext();
 }
 
--- a/dom/xslt/xpath/txIXPathContext.h
+++ b/dom/xslt/xpath/txIXPathContext.h
@@ -83,17 +83,18 @@ public:
      */
     virtual nsresult getVariable(int32_t aNamespace, nsIAtom* aLName,
                                  txAExprResult*& aResult) = 0;
 
     /*
      * Is whitespace stripping allowed for the given node?
      * See http://www.w3.org/TR/xslt#strip
      */
-    virtual bool isStripSpaceAllowed(const txXPathNode& aNode) = 0;
+    virtual nsresult isStripSpaceAllowed(const txXPathNode& aNode,
+                                         bool& aAllowed) = 0;
 
     /**
      * Returns a pointer to the private context
      */
     virtual void* getPrivateContext() = 0;
 
     virtual txResultRecycler* recycler() = 0;
 
@@ -101,17 +102,17 @@ public:
      * Callback to be used by the expression/pattern if errors are detected.
      */
     virtual void receiveError(const nsAString& aMsg, nsresult aRes) = 0;
 };
 
 #define TX_DECL_MATCH_CONTEXT \
     nsresult getVariable(int32_t aNamespace, nsIAtom* aLName, \
                          txAExprResult*& aResult); \
-    bool isStripSpaceAllowed(const txXPathNode& aNode); \
+    nsresult isStripSpaceAllowed(const txXPathNode& aNode, bool& aAllowed); \
     void* getPrivateContext(); \
     txResultRecycler* recycler(); \
     void receiveError(const nsAString& aMsg, nsresult aRes)
 
 class txIEvalContext : public txIMatchContext
 {
 public:
     /*
--- a/dom/xslt/xpath/txLocationStep.cpp
+++ b/dom/xslt/xpath/txLocationStep.cpp
@@ -44,155 +44,150 @@ LocationStep::evaluate(txIEvalContext* a
             }
             MOZ_FALLTHROUGH;
         }
         case ANCESTOR_OR_SELF_AXIS:
         {
             nodes->setReverse();
 
             do {
-                if (mNodeTest->matches(walker.getCurrentPosition(), aContext)) {
-                    nodes->append(walker.getCurrentPosition());
-                }
+                rv = appendIfMatching(walker, aContext, nodes);
+                NS_ENSURE_SUCCESS(rv, rv);
             } while (walker.moveToParent());
 
             break;
         }
         case ATTRIBUTE_AXIS:
         {
             if (!walker.moveToFirstAttribute()) {
                 break;
             }
 
             do {
-                if (mNodeTest->matches(walker.getCurrentPosition(), aContext)) {
-                    nodes->append(walker.getCurrentPosition());
-                }
+                rv = appendIfMatching(walker, aContext, nodes);
+                NS_ENSURE_SUCCESS(rv, rv);
             } while (walker.moveToNextAttribute());
             break;
         }
         case DESCENDANT_OR_SELF_AXIS:
         {
-            if (mNodeTest->matches(walker.getCurrentPosition(), aContext)) {
-                nodes->append(walker.getCurrentPosition());
-            }
+            rv = appendIfMatching(walker, aContext, nodes);
+            NS_ENSURE_SUCCESS(rv, rv);
             MOZ_FALLTHROUGH;
         }
         case DESCENDANT_AXIS:
         {
-            fromDescendants(walker.getCurrentPosition(), aContext, nodes);
+            rv = appendMatchingDescendants(walker, aContext, nodes);
+            NS_ENSURE_SUCCESS(rv, rv);
             break;
         }
         case FOLLOWING_AXIS:
         {
             if (txXPathNodeUtils::isAttribute(walker.getCurrentPosition())) {
                 walker.moveToParent();
-                fromDescendants(walker.getCurrentPosition(), aContext, nodes);
+                rv = appendMatchingDescendants(walker, aContext, nodes);
+                NS_ENSURE_SUCCESS(rv, rv);
             }
             bool cont = true;
             while (!walker.moveToNextSibling()) {
                 if (!walker.moveToParent()) {
                     cont = false;
                     break;
                 }
             }
             while (cont) {
-                if (mNodeTest->matches(walker.getCurrentPosition(), aContext)) {
-                    nodes->append(walker.getCurrentPosition());
-                }
+                rv = appendIfMatching(walker, aContext, nodes);
+                NS_ENSURE_SUCCESS(rv, rv);
 
-                fromDescendants(walker.getCurrentPosition(), aContext, nodes);
+                rv = appendMatchingDescendants(walker, aContext, nodes);
+                NS_ENSURE_SUCCESS(rv, rv);
 
                 while (!walker.moveToNextSibling()) {
                     if (!walker.moveToParent()) {
                         cont = false;
                         break;
                     }
                 }
             }
             break;
         }
         case FOLLOWING_SIBLING_AXIS:
         {
             while (walker.moveToNextSibling()) {
-                if (mNodeTest->matches(walker.getCurrentPosition(), aContext)) {
-                    nodes->append(walker.getCurrentPosition());
-                }
+                rv = appendIfMatching(walker, aContext, nodes);
+                NS_ENSURE_SUCCESS(rv, rv);
             }
             break;
         }
         case NAMESPACE_AXIS: //-- not yet implemented
 #if 0
             // XXX DEBUG OUTPUT
             cout << "namespace axis not yet implemented"<<endl;
 #endif
             break;
         case PARENT_AXIS :
         {
-            if (walker.moveToParent() &&
-                mNodeTest->matches(walker.getCurrentPosition(), aContext)) {
-                nodes->append(walker.getCurrentPosition());
+            if (walker.moveToParent()) {
+                rv = appendIfMatching(walker, aContext, nodes);
+                NS_ENSURE_SUCCESS(rv, rv);
             }
             break;
         }
         case PRECEDING_AXIS:
         {
             nodes->setReverse();
 
             bool cont = true;
             while (!walker.moveToPreviousSibling()) {
                 if (!walker.moveToParent()) {
                     cont = false;
                     break;
                 }
             }
             while (cont) {
-                fromDescendantsRev(walker.getCurrentPosition(), aContext, nodes);
+                rv = appendMatchingDescendantsRev(walker, aContext, nodes);
+                NS_ENSURE_SUCCESS(rv, rv);
 
-                if (mNodeTest->matches(walker.getCurrentPosition(), aContext)) {
-                    nodes->append(walker.getCurrentPosition());
-                }
+                rv = appendIfMatching(walker, aContext, nodes);
+                NS_ENSURE_SUCCESS(rv, rv);
 
                 while (!walker.moveToPreviousSibling()) {
                     if (!walker.moveToParent()) {
                         cont = false;
                         break;
                     }
                 }
             }
             break;
         }
         case PRECEDING_SIBLING_AXIS:
         {
             nodes->setReverse();
 
             while (walker.moveToPreviousSibling()) {
-                if (mNodeTest->matches(walker.getCurrentPosition(), aContext)) {
-                    nodes->append(walker.getCurrentPosition());
-                }
+                rv = appendIfMatching(walker, aContext, nodes);
+                NS_ENSURE_SUCCESS(rv, rv);
             }
             break;
         }
         case SELF_AXIS:
         {
-            if (mNodeTest->matches(walker.getCurrentPosition(), aContext)) {
-                nodes->append(walker.getCurrentPosition());
-            }
+            rv = appendIfMatching(walker, aContext, nodes);
+            NS_ENSURE_SUCCESS(rv, rv);
             break;
         }
         default: // Children Axis
         {
             if (!walker.moveToFirstChild()) {
                 break;
             }
 
             do {
-                if (mNodeTest->matches(walker.getCurrentPosition(), aContext)) {
-                    nodes->append(walker.getCurrentPosition());
-                }
+                rv = appendIfMatching(walker, aContext, nodes);
+                NS_ENSURE_SUCCESS(rv, rv);
             } while (walker.moveToNextSibling());
             break;
         }
     }
 
     // Apply predicates
     if (!isEmpty()) {
         rv = evaluatePredicates(nodes, aContext);
@@ -201,52 +196,72 @@ LocationStep::evaluate(txIEvalContext* a
 
     nodes->unsetReverse();
 
     NS_ADDREF(*aResult = nodes);
 
     return NS_OK;
 }
 
-void LocationStep::fromDescendants(const txXPathNode& aNode,
-                                   txIMatchContext* aCs,
-                                   txNodeSet* aNodes)
+nsresult
+LocationStep::appendIfMatching(const txXPathTreeWalker& aWalker,
+                               txIMatchContext* aContext,
+                               txNodeSet* aNodes)
 {
-    txXPathTreeWalker walker(aNode);
+    bool matched;
+    const txXPathNode& child = aWalker.getCurrentPosition();
+    nsresult rv = mNodeTest->matches(child, aContext, matched);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (matched) {
+        aNodes->append(child);
+    }
+    return NS_OK;
+}
+
+nsresult
+LocationStep::appendMatchingDescendants(const txXPathTreeWalker& aWalker,
+                                        txIMatchContext* aContext,
+                                        txNodeSet* aNodes)
+{
+    txXPathTreeWalker walker(aWalker);
     if (!walker.moveToFirstChild()) {
-        return;
+        return NS_OK;
     }
 
     do {
-        const txXPathNode& child = walker.getCurrentPosition();
-        if (mNodeTest->matches(child, aCs)) {
-            aNodes->append(child);
-        }
-        fromDescendants(child, aCs, aNodes);
+        nsresult rv = appendIfMatching(walker, aContext, aNodes);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        rv = appendMatchingDescendants(walker, aContext, aNodes);
+        NS_ENSURE_SUCCESS(rv, rv);
     } while (walker.moveToNextSibling());
+
+    return NS_OK;
 }
 
-void LocationStep::fromDescendantsRev(const txXPathNode& aNode,
-                                      txIMatchContext* aCs,
-                                      txNodeSet* aNodes)
+nsresult
+LocationStep::appendMatchingDescendantsRev(const txXPathTreeWalker& aWalker,
+                                           txIMatchContext* aContext,
+                                           txNodeSet* aNodes)
 {
-    txXPathTreeWalker walker(aNode);
+    txXPathTreeWalker walker(aWalker);
     if (!walker.moveToLastChild()) {
-        return;
+        return NS_OK;
     }
 
     do {
-        const txXPathNode& child = walker.getCurrentPosition();
-        fromDescendantsRev(child, aCs, aNodes);
+        nsresult rv = appendMatchingDescendantsRev(walker, aContext, aNodes);
+        NS_ENSURE_SUCCESS(rv, rv);
 
-        if (mNodeTest->matches(child, aCs)) {
-            aNodes->append(child);
-        }
+        rv = appendIfMatching(walker, aContext, aNodes);
+        NS_ENSURE_SUCCESS(rv, rv);
+    } while (walker.moveToPreviousSibling());
 
-    } while (walker.moveToPreviousSibling());
+    return NS_OK;
 }
 
 Expr::ExprType
 LocationStep::getType()
 {
   return LOCATIONSTEP_EXPR;
 }
 
--- a/dom/xslt/xpath/txNameTest.cpp
+++ b/dom/xslt/xpath/txNameTest.cpp
@@ -18,44 +18,54 @@ txNameTest::txNameTest(nsIAtom* aPrefix,
         mPrefix = nullptr;
     NS_ASSERTION(aLocalName, "txNameTest without a local name?");
     NS_ASSERTION(aNodeType == txXPathNodeType::DOCUMENT_NODE ||
                  aNodeType == txXPathNodeType::ELEMENT_NODE ||
                  aNodeType == txXPathNodeType::ATTRIBUTE_NODE,
                  "Go fix txNameTest::matches");
 }
 
-bool txNameTest::matches(const txXPathNode& aNode, txIMatchContext* aContext)
+nsresult
+txNameTest::matches(const txXPathNode& aNode, txIMatchContext* aContext,
+                    bool& aMatched)
 {
     if ((mNodeType == txXPathNodeType::ELEMENT_NODE &&
          !txXPathNodeUtils::isElement(aNode)) ||
         (mNodeType == txXPathNodeType::ATTRIBUTE_NODE &&
          !txXPathNodeUtils::isAttribute(aNode)) ||
         (mNodeType == txXPathNodeType::DOCUMENT_NODE &&
          !txXPathNodeUtils::isRoot(aNode))) {
-        return false;
+        aMatched = false;
+        return NS_OK;
     }
 
     // Totally wild?
-    if (mLocalName == nsGkAtoms::_asterisk && !mPrefix)
-        return true;
+    if (mLocalName == nsGkAtoms::_asterisk && !mPrefix) {
+        aMatched = true;
+        return NS_OK;
+    }
 
     // Compare namespaces
     if (mNamespace != txXPathNodeUtils::getNamespaceID(aNode) 
         && !(mNamespace == kNameSpaceID_None &&
              txXPathNodeUtils::isHTMLElementInHTMLDocument(aNode))
-       )
-        return false;
+       ) {
+        aMatched = false;
+        return NS_OK;
+    }
 
     // Name wild?
-    if (mLocalName == nsGkAtoms::_asterisk)
-        return true;
+    if (mLocalName == nsGkAtoms::_asterisk) {
+        aMatched = true;
+        return NS_OK;
+    }
 
     // Compare local-names
-    return txXPathNodeUtils::localNameEquals(aNode, mLocalName);
+    aMatched = txXPathNodeUtils::localNameEquals(aNode, mLocalName);
+    return NS_OK;
 }
 
 /*
  * Returns the default priority of this txNodeTest
  */
 double txNameTest::getDefaultPriority()
 {
     if (mLocalName == nsGkAtoms::_asterisk) {
--- a/dom/xslt/xpath/txNodeSetContext.cpp
+++ b/dom/xslt/xpath/txNodeSetContext.cpp
@@ -24,20 +24,21 @@ uint32_t txNodeSetContext::position()
 
 nsresult txNodeSetContext::getVariable(int32_t aNamespace, nsIAtom* aLName,
                                        txAExprResult*& aResult)
 {
     NS_ASSERTION(mInner, "mInner is null!!!");
     return mInner->getVariable(aNamespace, aLName, aResult);
 }
 
-bool txNodeSetContext::isStripSpaceAllowed(const txXPathNode& aNode)
+nsresult
+txNodeSetContext::isStripSpaceAllowed(const txXPathNode& aNode, bool& aAllowed)
 {
     NS_ASSERTION(mInner, "mInner is null!!!");
-    return mInner->isStripSpaceAllowed(aNode);
+    return mInner->isStripSpaceAllowed(aNode, aAllowed);
 }
 
 void* txNodeSetContext::getPrivateContext()
 {
     NS_ASSERTION(mInner, "mInner is null!!!");
     return mInner->getPrivateContext();
 }
 
--- a/dom/xslt/xpath/txNodeTypeTest.cpp
+++ b/dom/xslt/xpath/txNodeTypeTest.cpp
@@ -3,42 +3,64 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "txExpr.h"
 #include "nsIAtom.h"
 #include "txIXPathContext.h"
 #include "txXPathTreeWalker.h"
 
-bool txNodeTypeTest::matches(const txXPathNode& aNode,
-                               txIMatchContext* aContext)
+nsresult
+txNodeTypeTest::matches(const txXPathNode& aNode, txIMatchContext* aContext,
+                        bool& aMatched)
 {
     switch (mNodeType) {
         case COMMENT_TYPE:
         {
-            return txXPathNodeUtils::isComment(aNode);
+            aMatched = txXPathNodeUtils::isComment(aNode);
+            return NS_OK;
         }
         case TEXT_TYPE:
         {
-            return txXPathNodeUtils::isText(aNode) &&
-                   !aContext->isStripSpaceAllowed(aNode);
+            aMatched = txXPathNodeUtils::isText(aNode);
+            if (aMatched) {
+                bool allowed;
+                nsresult rv = aContext->isStripSpaceAllowed(aNode, allowed);
+                NS_ENSURE_SUCCESS(rv, rv);
+
+                aMatched = !allowed;
+            }
+            return NS_OK;
         }
         case PI_TYPE:
         {
-            return txXPathNodeUtils::isProcessingInstruction(aNode) &&
-                   (!mNodeName ||
-                    txXPathNodeUtils::localNameEquals(aNode, mNodeName));
+            aMatched = txXPathNodeUtils::isProcessingInstruction(aNode) &&
+                       (!mNodeName ||
+                        txXPathNodeUtils::localNameEquals(aNode, mNodeName));
+            return NS_OK;
         }
         case NODE_TYPE:
         {
-            return !txXPathNodeUtils::isText(aNode) ||
-                   !aContext->isStripSpaceAllowed(aNode);
+            if (txXPathNodeUtils::isText(aNode)) {
+                bool allowed;
+                nsresult rv = aContext->isStripSpaceAllowed(aNode, allowed);
+                NS_ENSURE_SUCCESS(rv, rv);
+
+                aMatched = !allowed;
+            } else {
+                aMatched = true;
+            }
+            return NS_OK;
         }
     }
-    return true;
+
+    NS_NOTREACHED("Didn't deal with all values of the NodeType enum!");
+
+    aMatched = false;
+    return NS_OK;
 }
 
 txNodeTest::NodeTestType
 txNodeTypeTest::getType()
 {
     return NODETYPE_TEST;
 }
 
--- a/dom/xslt/xpath/txPathExpr.cpp
+++ b/dom/xslt/xpath/txPathExpr.cpp
@@ -155,17 +155,19 @@ PathExpr::evalDescendants(Expr* aStep, c
                                    (static_cast<txAExprResult*>(res));
     RefPtr<txNodeSet> newSet;
     rv = aContext->recycler()->getNonSharedNodeSet(oldSet,
                                                    getter_AddRefs(newSet));
     NS_ENSURE_SUCCESS(rv, rv);
 
     resNodes->addAndTransfer(newSet);
 
-    bool filterWS = aContext->isStripSpaceAllowed(aNode);
+    bool filterWS;
+    rv = aContext->isStripSpaceAllowed(aNode, filterWS);
+    NS_ENSURE_SUCCESS(rv, rv);
 
     txXPathTreeWalker walker(aNode);
     if (!walker.moveToFirstChild()) {
         return NS_OK;
     }
 
     do {
         const txXPathNode& node = walker.getCurrentPosition();
--- a/dom/xslt/xpath/txPredicatedNodeTest.cpp
+++ b/dom/xslt/xpath/txPredicatedNodeTest.cpp
@@ -11,30 +11,35 @@ txPredicatedNodeTest::txPredicatedNodeTe
                                            Expr* aPredicate)
     : mNodeTest(aNodeTest),
       mPredicate(aPredicate)
 {
     NS_ASSERTION(!mPredicate->isSensitiveTo(Expr::NODESET_CONTEXT),
                  "predicate must not be context-nodeset-sensitive");
 }
 
-bool
+nsresult
 txPredicatedNodeTest::matches(const txXPathNode& aNode,
-                              txIMatchContext* aContext)
+                              txIMatchContext* aContext,
+                              bool& aMatched)
 {
-    if (!mNodeTest->matches(aNode, aContext)) {
-        return false;
+    nsresult rv = mNodeTest->matches(aNode, aContext, aMatched);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (!aMatched) {
+        return NS_OK;
     }
 
     txSingleNodeContext context(aNode, aContext);
     RefPtr<txAExprResult> res;
-    nsresult rv = mPredicate->evaluate(&context, getter_AddRefs(res));
-    NS_ENSURE_SUCCESS(rv, false);
+    rv = mPredicate->evaluate(&context, getter_AddRefs(res));
+    NS_ENSURE_SUCCESS(rv, rv);
 
-    return res->booleanValue();
+    aMatched = res->booleanValue();
+    return NS_OK;
 }
 
 double
 txPredicatedNodeTest::getDefaultPriority()
 {
     return 0.5;
 }
 
--- a/dom/xslt/xpath/txSingleNodeContext.h
+++ b/dom/xslt/xpath/txSingleNodeContext.h
@@ -22,20 +22,21 @@ public:
 
     nsresult getVariable(int32_t aNamespace, nsIAtom* aLName,
                          txAExprResult*& aResult) override
     {
         NS_ASSERTION(mInner, "mInner is null!!!");
         return mInner->getVariable(aNamespace, aLName, aResult);
     }
 
-    bool isStripSpaceAllowed(const txXPathNode& aNode) override
+    nsresult isStripSpaceAllowed(const txXPathNode& aNode,
+                                 bool& aAllowed) override
     {
         NS_ASSERTION(mInner, "mInner is null!!!");
-        return mInner->isStripSpaceAllowed(aNode);
+        return mInner->isStripSpaceAllowed(aNode, aAllowed);
     }
 
     void* getPrivateContext() override
     {
         NS_ASSERTION(mInner, "mInner is null!!!");
         return mInner->getPrivateContext();
     }
 
--- a/dom/xslt/xpath/txUnionNodeTest.cpp
+++ b/dom/xslt/xpath/txUnionNodeTest.cpp
@@ -5,28 +5,32 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/FloatingPoint.h"
 
 #include "txExpr.h"
 #include "txExprResult.h"
 #include "txSingleNodeContext.h"
 
-bool
-txUnionNodeTest::matches(const txXPathNode& aNode,
-                         txIMatchContext* aContext)
+nsresult
+txUnionNodeTest::matches(const txXPathNode& aNode, txIMatchContext* aContext,
+                         bool& aMatched)
 {
     uint32_t i, len = mNodeTests.Length();
     for (i = 0; i < len; ++i) {
-        if (mNodeTests[i]->matches(aNode, aContext)) {
-            return true;
+        nsresult rv = mNodeTests[i]->matches(aNode, aContext, aMatched);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        if (aMatched) {
+            return NS_OK;
         }
     }
 
-    return false;
+    aMatched = false;
+    return NS_OK;
 }
 
 double
 txUnionNodeTest::getDefaultPriority()
 {
     NS_ERROR("Don't call getDefaultPriority on txUnionPattern");
     return mozilla::UnspecifiedNaN<double>();
 }
--- a/dom/xslt/xpath/txXPathOptimizer.cpp
+++ b/dom/xslt/xpath/txXPathOptimizer.cpp
@@ -21,17 +21,17 @@ public:
     }
 
     // txIEvalContext
     nsresult getVariable(int32_t aNamespace, nsIAtom* aLName,
                          txAExprResult*& aResult)
     {
         MOZ_CRASH("shouldn't depend on this context");
     }
-    bool isStripSpaceAllowed(const txXPathNode& aNode)
+    nsresult isStripSpaceAllowed(const txXPathNode& aNode, bool& aAllowed)
     {
         MOZ_CRASH("shouldn't depend on this context");
     }
     void* getPrivateContext()
     {
         MOZ_CRASH("shouldn't depend on this context");
     }
     txResultRecycler* recycler()
--- a/dom/xslt/xslt/txExecutionState.cpp
+++ b/dom/xslt/xslt/txExecutionState.cpp
@@ -82,20 +82,17 @@ txExecutionState::~txExecutionState()
         txIEvalContext* context = (txIEvalContext*)contextIter.next();
         if (context != mInitialEvalContext) {
             delete context;
         }
     }
 
     txStackIterator handlerIter(&mResultHandlerStack);
     while (handlerIter.hasNext()) {
-        txAXMLEventHandler* handler = (txAXMLEventHandler*)handlerIter.next();
-        if (handler != mObsoleteHandler) {
-          delete handler;
-        }
+        delete (txAXMLEventHandler*)handlerIter.next();
     }
 
     txStackIterator paramIter(&mParamStack);
     while (paramIter.hasNext()) {
         delete (txVariableMap*)paramIter.next();
     }
 
     delete mInitialEvalContext;
@@ -136,18 +133,21 @@ txExecutionState::init(const txXPathNode
     // The actual value here doesn't really matter since noone should use this
     // value. But lets put something errorlike in just in case
     mGlobalVarPlaceholderValue = new StringResult(NS_LITERAL_STRING("Error"), nullptr);
 
     // Initiate first instruction. This has to be done last since findTemplate
     // might use us.
     txStylesheet::ImportFrame* frame = 0;
     txExpandedName nullName;
-    txInstruction* templ = mStylesheet->findTemplate(aNode, nullName,
-                                                     this, nullptr, &frame);
+    txInstruction* templ;
+    rv = mStylesheet->findTemplate(aNode, nullName, this, nullptr, &templ,
+                                   &frame);
+    NS_ENSURE_SUCCESS(rv, rv);
+
     pushTemplateRule(frame, nullName, nullptr);
 
     return runTemplate(templ);
 }
 
 nsresult
 txExecutionState::end(nsresult aResult)
 {
@@ -158,27 +158,16 @@ txExecutionState::end(nsresult aResult)
     }
     else if (!mOutputHandler) {
         return NS_OK;
     }
     return mOutputHandler->endDocument(aResult);
 }
 
 void
-txExecutionState::popAndDeleteEvalContext()
-{
-  if (!mEvalContextStack.isEmpty()) {
-    auto ctx = popEvalContext();
-    if (ctx != mInitialEvalContext) {
-      delete ctx;
-    }
-  }
-}
-
-void
 txExecutionState::popAndDeleteEvalContextUntil(txIEvalContext* aContext)
 {
   auto ctx = popEvalContext();
   while (ctx && ctx != aContext) {
     MOZ_RELEASE_ASSERT(ctx != mInitialEvalContext);
     delete ctx;
     ctx = popEvalContext();
   }
@@ -301,20 +290,20 @@ txExecutionState::getVariable(int32_t aN
         NS_RELEASE(aResult);
 
         return rv;
     }
 
     return NS_OK;
 }
 
-bool
-txExecutionState::isStripSpaceAllowed(const txXPathNode& aNode)
+nsresult
+txExecutionState::isStripSpaceAllowed(const txXPathNode& aNode, bool& aAllowed)
 {
-    return mStylesheet->isStripSpaceAllowed(aNode, this);
+    return mStylesheet->isStripSpaceAllowed(aNode, this, aAllowed);
 }
 
 void*
 txExecutionState::getPrivateContext()
 {
     return this;
 }
 
--- a/dom/xslt/xslt/txExecutionState.h
+++ b/dom/xslt/xslt/txExecutionState.h
@@ -90,18 +90,16 @@ public:
         nsCOMPtr<nsIAtom> mModeLocalName;
         txVariableMap* mParams;
     };
 
     // Stack functions
     nsresult pushEvalContext(txIEvalContext* aContext);
     txIEvalContext* popEvalContext();
 
-    void popAndDeleteEvalContext();
-
     /**
      * Helper that deletes all entries before |aContext| and then
      * pops it off the stack. The caller must delete |aContext| if
      * desired.
      */
     void popAndDeleteEvalContextUntil(txIEvalContext* aContext);
 
     nsresult pushBool(bool aBool);
--- a/dom/xslt/xslt/txInstructions.cpp
+++ b/dom/xslt/xslt/txInstructions.cpp
@@ -22,19 +22,21 @@
 using mozilla::Move;
 
 nsresult
 txApplyDefaultElementTemplate::execute(txExecutionState& aEs)
 {
     txExecutionState::TemplateRule* rule = aEs.getCurrentTemplateRule();
     txExpandedName mode(rule->mModeNsId, rule->mModeLocalName);
     txStylesheet::ImportFrame* frame = 0;
-    txInstruction* templ =
+    txInstruction* templ;
+    nsresult rv =
         aEs.mStylesheet->findTemplate(aEs.getEvalContext()->getContextNode(),
-                                      mode, &aEs, nullptr, &frame);
+                                      mode, &aEs, nullptr, &templ, &frame);
+    NS_ENSURE_SUCCESS(rv, rv);
 
     aEs.pushTemplateRule(frame, mode, aEs.mTemplateParams);
 
     return aEs.runTemplate(templ);
 }
 
 nsresult
 txApplyImports::execute(txExecutionState& aEs)
@@ -48,19 +50,21 @@ txApplyImports::execute(txExecutionState
         return NS_ERROR_XSLT_EXECUTION_FAILURE;
     }
 
     nsresult rv = aEs.pushParamMap(rule->mParams);
     NS_ENSURE_SUCCESS(rv, rv);
 
     txStylesheet::ImportFrame* frame = 0;
     txExpandedName mode(rule->mModeNsId, rule->mModeLocalName);
-    txInstruction* templ =
-        aEs.mStylesheet->findTemplate(aEs.getEvalContext()->getContextNode(),
-                                      mode, &aEs, rule->mFrame, &frame);
+    txInstruction* templ;
+    rv = aEs.mStylesheet->findTemplate(aEs.getEvalContext()->getContextNode(),
+                                       mode, &aEs, rule->mFrame, &templ,
+                                       &frame);
+    NS_ENSURE_SUCCESS(rv, rv);
 
     aEs.pushTemplateRule(frame, mode, rule->mParams);
 
     rv = aEs.runTemplate(templ);
 
     aEs.popTemplateRule();
     aEs.popParamMap();
 
@@ -71,19 +75,21 @@ txApplyTemplates::txApplyTemplates(const
     : mMode(aMode)
 {
 }
 
 nsresult
 txApplyTemplates::execute(txExecutionState& aEs)
 {
     txStylesheet::ImportFrame* frame = 0;
-    txInstruction* templ =
+    txInstruction* templ;
+    nsresult rv =
         aEs.mStylesheet->findTemplate(aEs.getEvalContext()->getContextNode(),
-                                      mMode, &aEs, nullptr, &frame);
+                                      mMode, &aEs, nullptr, &templ, &frame);
+    NS_ENSURE_SUCCESS(rv, rv);
 
     aEs.pushTemplateRule(frame, mMode, aEs.mTemplateParams);
 
     return aEs.runTemplate(templ);
 }
 
 txAttribute::txAttribute(nsAutoPtr<Expr>&& aName, nsAutoPtr<Expr>&& aNamespace,
                          txNamespaceMap* aMappings)
@@ -465,17 +471,17 @@ txLoopNodeSet::txLoopNodeSet(txInstructi
 
 nsresult
 txLoopNodeSet::execute(txExecutionState& aEs)
 {
     aEs.popTemplateRule();
     txNodeSetContext* context =
         static_cast<txNodeSetContext*>(aEs.getEvalContext());
     if (!context->hasNext()) {
-        aEs.popAndDeleteEvalContext();
+        delete aEs.popEvalContext();
 
         return NS_OK;
     }
 
     context->next();
     aEs.gotoInstruction(mTarget);
     
     return NS_OK;
--- a/dom/xslt/xslt/txKeyFunctionCall.cpp
+++ b/dom/xslt/xslt/txKeyFunctionCall.cpp
@@ -333,17 +333,21 @@ nsresult txXSLKey::indexTree(const txXPa
 nsresult txXSLKey::testNode(const txXPathNode& aNode,
                             txKeyValueHashKey& aKey,
                             txKeyValueHash& aKeyValueHash,
                             txExecutionState& aEs)
 {
     nsAutoString val;
     uint32_t currKey, numKeys = mKeys.Length();
     for (currKey = 0; currKey < numKeys; ++currKey) {
-        if (mKeys[currKey].matchPattern->matches(aNode, &aEs)) {
+        bool matched;
+        nsresult rv = mKeys[currKey].matchPattern->matches(aNode, &aEs, matched);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        if (matched) {
             txSingleNodeContext *evalContext =
                 new txSingleNodeContext(aNode, &aEs);
             NS_ENSURE_TRUE(evalContext, NS_ERROR_OUT_OF_MEMORY);
 
             nsresult rv = aEs.pushEvalContext(evalContext);
             NS_ENSURE_SUCCESS(rv, rv);
 
             RefPtr<txAExprResult> exprResult;
--- a/dom/xslt/xslt/txMozillaXSLTProcessor.cpp
+++ b/dom/xslt/xslt/txMozillaXSLTProcessor.cpp
@@ -436,19 +436,21 @@ public:
 
     // txIEvalContext
     nsresult getVariable(int32_t aNamespace, nsIAtom* aLName,
                          txAExprResult*& aResult)
     {
         aResult = nullptr;
         return NS_ERROR_INVALID_ARG;
     }
-    bool isStripSpaceAllowed(const txXPathNode& aNode)
+    nsresult isStripSpaceAllowed(const txXPathNode& aNode, bool& aAllowed)
     {
-        return false;
+        aAllowed = false;
+
+        return NS_OK;
     }
     void* getPrivateContext()
     {
         return nullptr;
     }
     txResultRecycler* recycler()
     {
         return mRecycler;
--- a/dom/xslt/xslt/txStylesheet.cpp
+++ b/dom/xslt/xslt/txStylesheet.cpp
@@ -83,74 +83,79 @@ txStylesheet::~txStylesheet()
     // We can't make the map own its values because then we wouldn't be able
     // to merge attributesets of the same name
     txExpandedNameMap<txInstruction>::iterator attrSetIter(mAttributeSets);
     while (attrSetIter.next()) {
         delete attrSetIter.value();
     }
 }
 
-txInstruction*
+nsresult
 txStylesheet::findTemplate(const txXPathNode& aNode,
                            const txExpandedName& aMode,
                            txIMatchContext* aContext,
                            ImportFrame* aImportedBy,
+                           txInstruction** aTemplate,
                            ImportFrame** aImportFrame)
 {
     NS_ASSERTION(aImportFrame, "missing ImportFrame pointer");
 
+    *aTemplate = nullptr;
     *aImportFrame = nullptr;
-    txInstruction* matchTemplate = nullptr;
     ImportFrame* endFrame = nullptr;
     txListIterator frameIter(&mImportFrames);
 
     if (aImportedBy) {
         ImportFrame* curr = static_cast<ImportFrame*>(frameIter.next());
         while (curr != aImportedBy) {
                curr = static_cast<ImportFrame*>(frameIter.next());
         }
         endFrame = aImportedBy->mFirstNotImported;
     }
 
 #if defined(TX_TO_STRING)
     txPattern* match = 0;
 #endif
 
     ImportFrame* frame;
-    while (!matchTemplate &&
+    while (!*aTemplate &&
            (frame = static_cast<ImportFrame*>(frameIter.next())) &&
            frame != endFrame) {
 
         // get templatelist for this mode
         nsTArray<MatchableTemplate>* templates =
             frame->mMatchableTemplates.get(aMode);
 
         if (templates) {
             // Find template with highest priority
             uint32_t i, len = templates->Length();
-            for (i = 0; i < len && !matchTemplate; ++i) {
+            for (i = 0; i < len && !*aTemplate; ++i) {
                 MatchableTemplate& templ = (*templates)[i];
-                if (templ.mMatch->matches(aNode, aContext)) {
-                    matchTemplate = templ.mFirstInstruction;
+                bool matched;
+                nsresult rv = templ.mMatch->matches(aNode, aContext, matched);
+                NS_ENSURE_SUCCESS(rv, rv);
+
+                if (matched) {
+                    *aTemplate = templ.mFirstInstruction;
                     *aImportFrame = frame;
 #if defined(TX_TO_STRING)
                     match = templ.mMatch;
 #endif
                 }
             }
         }
     }
 
     if (MOZ_LOG_TEST(txLog::xslt, LogLevel::Debug)) {
       nsAutoString mode, nodeName;
       if (aMode.mLocalName) {
           aMode.mLocalName->ToString(mode);
       }
       txXPathNodeUtils::getNodeName(aNode, nodeName);
-      if (matchTemplate) {
+      if (*aTemplate) {
           nsAutoString matchAttr;
 #ifdef TX_TO_STRING
           match->toString(matchAttr);
 #endif
           MOZ_LOG(txLog::xslt, LogLevel::Debug,
                  ("MatchTemplate, Pattern %s, Mode %s, Node %s\n",
                   NS_LossyConvertUTF16toASCII(matchAttr).get(),
                   NS_LossyConvertUTF16toASCII(mode).get(),
@@ -159,33 +164,33 @@ txStylesheet::findTemplate(const txXPath
       else {
           MOZ_LOG(txLog::xslt, LogLevel::Debug,
                  ("No match, Node %s, Mode %s\n", 
                   NS_LossyConvertUTF16toASCII(nodeName).get(),
                   NS_LossyConvertUTF16toASCII(mode).get()));
       }
     }
 
-    if (!matchTemplate) {
+    if (!*aTemplate) {
         // Test for these first since a node can be both a text node
         // and a root (if it is orphaned)
         if (txXPathNodeUtils::isAttribute(aNode) ||
             txXPathNodeUtils::isText(aNode)) {
-            matchTemplate = mCharactersTemplate;
+            *aTemplate = mCharactersTemplate;
         }
         else if (txXPathNodeUtils::isElement(aNode) ||
                  txXPathNodeUtils::isRoot(aNode)) {
-            matchTemplate = mContainerTemplate;
+            *aTemplate = mContainerTemplate;
         }
         else {
-            matchTemplate = mEmptyTemplate;
+            *aTemplate = mEmptyTemplate;
         }
     }
 
-    return matchTemplate;
+    return NS_OK;
 }
 
 txDecimalFormat*
 txStylesheet::getDecimalFormat(const txExpandedName& aName)
 {
     return mDecimalFormats.get(aName);
 }
 
@@ -214,47 +219,63 @@ txStylesheet::getGlobalVariable(const tx
 }
 
 const txOwningExpandedNameMap<txXSLKey>&
 txStylesheet::getKeyMap()
 {
     return mKeys;
 }
 
-bool
-txStylesheet::isStripSpaceAllowed(const txXPathNode& aNode, txIMatchContext* aContext)
+nsresult
+txStylesheet::isStripSpaceAllowed(const txXPathNode& aNode,
+                                  txIMatchContext* aContext, bool& aAllowed)
 {
     int32_t frameCount = mStripSpaceTests.Length();
     if (frameCount == 0) {
-        return false;
+        aAllowed = false;
+
+        return NS_OK;
     }
 
     txXPathTreeWalker walker(aNode);
 
     if (txXPathNodeUtils::isText(walker.getCurrentPosition()) &&
         (!txXPathNodeUtils::isWhitespace(aNode) || !walker.moveToParent())) {
-        return false;
+        aAllowed = false;
+
+        return NS_OK;
     }
 
     const txXPathNode& node = walker.getCurrentPosition();
 
     if (!txXPathNodeUtils::isElement(node)) {
-        return false;
+        aAllowed = false;
+
+        return NS_OK;
     }
 
     // check Whitespace stipping handling list against given Node
     int32_t i;
     for (i = 0; i < frameCount; ++i) {
         txStripSpaceTest* sst = mStripSpaceTests[i];
-        if (sst->matches(node, aContext)) {
-            return sst->stripsSpace() && !XMLUtils::getXMLSpacePreserve(node);
+        bool matched;
+        nsresult rv = sst->matches(node, aContext, matched);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        if (matched) {
+            aAllowed = sst->stripsSpace() &&
+                       !XMLUtils::getXMLSpacePreserve(node);
+
+            return NS_OK;
         }
     }
 
-    return false;
+    aAllowed = false;
+
+    return NS_OK;
 }
 
 nsresult
 txStylesheet::doneCompiling()
 {
     nsresult rv = NS_OK;
     // Collect all importframes into a single ordered list
     txListIterator frameIter(&mImportFrames);
--- a/dom/xslt/xslt/txStylesheet.h
+++ b/dom/xslt/xslt/txStylesheet.h
@@ -30,29 +30,31 @@ public:
     // To be able to do some cleaning up in destructor
     friend class ImportFrame;
 
     txStylesheet();
     nsresult init();
     
     NS_INLINE_DECL_REFCOUNTING(txStylesheet)
 
-    txInstruction* findTemplate(const txXPathNode& aNode,
-                                const txExpandedName& aMode,
-                                txIMatchContext* aContext,
-                                ImportFrame* aImportedBy,
-                                ImportFrame** aImportFrame);
+    nsresult findTemplate(const txXPathNode& aNode,
+                          const txExpandedName& aMode,
+                          txIMatchContext* aContext,
+                          ImportFrame* aImportedBy,
+                          txInstruction** aTemplate,
+                          ImportFrame** aImportFrame);
     txDecimalFormat* getDecimalFormat(const txExpandedName& aName);
     txInstruction* getAttributeSet(const txExpandedName& aName);
     txInstruction* getNamedTemplate(const txExpandedName& aName);
     txOutputFormat* getOutputFormat();
     GlobalVariable* getGlobalVariable(const txExpandedName& aName);
     const txOwningExpandedNameMap<txXSLKey>& getKeyMap();
-    bool isStripSpaceAllowed(const txXPathNode& aNode,
-                               txIMatchContext* aContext);
+    nsresult isStripSpaceAllowed(const txXPathNode& aNode,
+                                 txIMatchContext* aContext,
+                                 bool& aAllowed);
 
     /**
      * Called by the stylesheet compiler once all stylesheets has been read.
      */
     nsresult doneCompiling();
 
     /**
      * Add a key to the stylesheet
@@ -161,18 +163,20 @@ class txStripSpaceTest {
 public:
     txStripSpaceTest(nsIAtom* aPrefix, nsIAtom* aLocalName, int32_t aNSID,
                      bool stripSpace)
         : mNameTest(aPrefix, aLocalName, aNSID, txXPathNodeType::ELEMENT_NODE),
           mStrips(stripSpace)
     {
     }
 
-    bool matches(const txXPathNode& aNode, txIMatchContext* aContext) {
-        return mNameTest.matches(aNode, aContext);
+    nsresult matches(const txXPathNode& aNode, txIMatchContext* aContext,
+                     bool& aMatched)
+    {
+        return mNameTest.matches(aNode, aContext, aMatched);
     }
 
     bool stripsSpace() {
         return mStrips;
     }
 
     double getDefaultPriority() {
         return mNameTest.getDefaultPriority();
--- a/dom/xslt/xslt/txXSLTNumber.cpp
+++ b/dom/xslt/xslt/txXSLTNumber.cpp
@@ -103,23 +103,22 @@ txXSLTNumber::getValueList(Expr* aValueE
         aValues.add(NS_INT32_TO_PTR((int32_t)floor(value + 0.5)));
         return NS_OK;
     }
 
 
     // Otherwise use count/from/level
 
     txPattern* countPattern = aCountPattern;
-    bool ownsCountPattern = false;
+    nsAutoPtr<txPattern> newCountPattern;
     const txXPathNode& currNode = aContext->getContextNode();
 
     // Parse count- and from-attributes
 
     if (!aCountPattern) {
-        ownsCountPattern = true;
         txNodeTest* nodeTest;
         uint16_t nodeType = txXPathNodeUtils::getNodeType(currNode);
         switch (nodeType) {
             case txXPathNodeType::ELEMENT_NODE:
             {
                 nsCOMPtr<nsIAtom> localName =
                     txXPathNodeUtils::getLocalName(currNode);
                 int32_t namespaceID = txXPathNodeUtils::getNamespaceID(currNode);
@@ -155,71 +154,102 @@ txXSLTNumber::getValueList(Expr* aValueE
                 // this won't match anything as we walk up the tree
                 // but it's what the spec says to do
                 nodeTest = new txNameTest(0, nsGkAtoms::_asterisk, 0,
                                           nodeType);
                 break;
             }
         }
         MOZ_ASSERT(nodeTest);
-        countPattern = new txStepPattern(nodeTest, false);
+        countPattern = newCountPattern = new txStepPattern(nodeTest, false);
     }
 
 
     // Generate list of values depending on the value of the level-attribute
 
     // level = "single"
     if (aLevel == eLevelSingle) {
         txXPathTreeWalker walker(currNode);
         do {
-            if (aFromPattern && !walker.isOnNode(currNode) &&
-                aFromPattern->matches(walker.getCurrentPosition(), aContext)) {
-                break;
+            if (aFromPattern && !walker.isOnNode(currNode)) {
+                bool matched;
+                rv = aFromPattern->matches(walker.getCurrentPosition(),
+                                           aContext, matched);
+                NS_ENSURE_SUCCESS(rv, rv);
+
+                if (matched) {
+                    break;
+                }
             }
 
-            if (countPattern->matches(walker.getCurrentPosition(), aContext)) {
-                aValues.add(NS_INT32_TO_PTR(getSiblingCount(walker, countPattern,
-                                                            aContext)));
+            bool matched;
+            rv = countPattern->matches(walker.getCurrentPosition(), aContext,
+                                       matched);
+            NS_ENSURE_SUCCESS(rv, rv);
+
+            if (matched) {
+                int32_t count;
+                rv = getSiblingCount(walker, countPattern, aContext, &count);
+                NS_ENSURE_SUCCESS(rv, rv);
+
+                aValues.add(NS_INT32_TO_PTR(count));
                 break;
             }
 
         } while (walker.moveToParent());
 
         // Spec says to only match ancestors that are decendants of the
         // ancestor that matches the from-pattern, so keep going to make
         // sure that there is an ancestor that does.
         if (aFromPattern && aValues.getLength()) {
             bool hasParent;
             while ((hasParent = walker.moveToParent())) {
-                if (aFromPattern->matches(walker.getCurrentPosition(), aContext)) {
+                bool matched;
+                rv = aFromPattern->matches(walker.getCurrentPosition(),
+                                           aContext, matched);
+                NS_ENSURE_SUCCESS(rv, rv);
+
+                if (matched) {
                     break;
                 }
             }
 
             if (!hasParent) {
                 aValues.clear();
             }
         }
     }
     // level = "multiple"
     else if (aLevel == eLevelMultiple) {
         // find all ancestor-or-selfs that matches count until...
         txXPathTreeWalker walker(currNode);
         bool matchedFrom = false;
         do {
-            if (aFromPattern && !walker.isOnNode(currNode) &&
-                aFromPattern->matches(walker.getCurrentPosition(), aContext)) {
-                //... we find one that matches from
-                matchedFrom = true;
-                break;
+            if (aFromPattern && !walker.isOnNode(currNode)) {
+                rv = aFromPattern->matches(walker.getCurrentPosition(),
+                                           aContext, matchedFrom);
+                NS_ENSURE_SUCCESS(rv, rv);
+
+                if (matchedFrom) {
+                    //... we find one that matches from
+                    break;
+                }
             }
 
-            if (countPattern->matches(walker.getCurrentPosition(), aContext)) {
-                aValues.add(NS_INT32_TO_PTR(getSiblingCount(walker, countPattern,
-                                                            aContext)));
+            bool matched;
+            rv = countPattern->matches(walker.getCurrentPosition(), aContext,
+                                       matched);
+            NS_ENSURE_SUCCESS(rv, rv);
+
+            if (matched) {
+                int32_t count;
+                rv = getSiblingCount(walker, countPattern, aContext, &count);
+                NS_ENSURE_SUCCESS(rv, rv);
+
+                aValues.add(NS_INT32_TO_PTR(count));
             }
         } while (walker.moveToParent());
 
         // Spec says to only match ancestors that are decendants of the
         // ancestor that matches the from-pattern, so if none did then
         // we shouldn't search anything
         if (aFromPattern && !matchedFrom) {
             aValues.clear();
@@ -227,23 +257,32 @@ txXSLTNumber::getValueList(Expr* aValueE
     }
     // level = "any"
     else if (aLevel == eLevelAny) {
         int32_t value = 0;
         bool matchedFrom = false;
 
         txXPathTreeWalker walker(currNode);
         do {
-            if (aFromPattern && !walker.isOnNode(currNode) &&
-                aFromPattern->matches(walker.getCurrentPosition(), aContext)) {
-                matchedFrom = true;
-                break;
+            if (aFromPattern && !walker.isOnNode(currNode)) {
+                rv = aFromPattern->matches(walker.getCurrentPosition(),
+                                           aContext, matchedFrom);
+                NS_ENSURE_SUCCESS(rv, rv);
+
+                if (matchedFrom) {
+                    break;
+                }
             }
 
-            if (countPattern->matches(walker.getCurrentPosition(), aContext)) {
+            bool matched;
+            rv = countPattern->matches(walker.getCurrentPosition(), aContext,
+                                       matched);
+            NS_ENSURE_SUCCESS(rv, rv);
+
+            if (matched) {
                 ++value;
             }
 
         } while (getPrevInDocumentOrder(walker));
 
         // Spec says to only count nodes that follows the first node that
         // matches the from pattern. So so if none did then we shouldn't
         // count any
@@ -251,20 +290,16 @@ txXSLTNumber::getValueList(Expr* aValueE
             value = 0;
         }
 
         if (value) {
             aValues.add(NS_INT32_TO_PTR(value));
         }
     }
 
-    if (ownsCountPattern) {
-        delete countPattern;
-    }
-    
     return NS_OK;
 }
 
 
 nsresult
 txXSLTNumber::getCounters(Expr* aGroupSize, Expr* aGroupSeparator,
                           Expr* aFormat, txIEvalContext* aContext,
                           txList& aCounters, nsAString& aHead,
@@ -383,28 +418,37 @@ txXSLTNumber::getCounters(Expr* aGroupSi
             aCounters.clear();
             return rv;
         }
     }
     
     return NS_OK;
 }
 
-int32_t
+nsresult
 txXSLTNumber::getSiblingCount(txXPathTreeWalker& aWalker,
                               txPattern* aCountPattern,
-                              txIMatchContext* aContext)
+                              txIMatchContext* aContext,
+                              int32_t* aCount)
 {
     int32_t value = 1;
     while (aWalker.moveToPreviousSibling()) {
-        if (aCountPattern->matches(aWalker.getCurrentPosition(), aContext)) {
+        bool matched;
+        nsresult rv = aCountPattern->matches(aWalker.getCurrentPosition(),
+                                             aContext, matched);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        if (matched) {
             ++value;
         }
     }
-    return value;
+
+    *aCount = value;
+
+    return NS_OK;
 }
 
 bool
 txXSLTNumber::getPrevInDocumentOrder(txXPathTreeWalker& aWalker)
 {
     if (aWalker.moveToPreviousSibling()) {
         while (aWalker.moveToLastChild()) {
             // do nothing
--- a/dom/xslt/xslt/txXSLTNumber.h
+++ b/dom/xslt/xslt/txXSLTNumber.h
@@ -41,19 +41,20 @@ private:
                                 txList& aCounters, nsAString& aHead,
                                 nsAString& aTail);
 
     /**
      * getSiblingCount uses aWalker to walk the siblings of aWalker's current
      * position.
      *
      */
-    static int32_t getSiblingCount(txXPathTreeWalker& aWalker,
-                                   txPattern* aCountPattern,
-                                   txIMatchContext* aContext);
+    static nsresult getSiblingCount(txXPathTreeWalker& aWalker,
+                                    txPattern* aCountPattern,
+                                    txIMatchContext* aContext,
+                                    int32_t* aCount);
     
     static bool getPrevInDocumentOrder(txXPathTreeWalker& aWalker);
 
     static bool isAlphaNumeric(char16_t ch);
 };
 
 class txFormattedCounter {
 public:
--- a/dom/xslt/xslt/txXSLTPatterns.cpp
+++ b/dom/xslt/xslt/txXSLTPatterns.cpp
@@ -27,25 +27,35 @@ double txUnionPattern::getDefaultPriorit
 }
 
 /*
  * Determines whether this Pattern matches the given node within
  * the given context
  * This should be called on the simple patterns for xsl:template,
  * but is fine for xsl:key and xsl:number
  */
-bool txUnionPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext)
+nsresult
+txUnionPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext,
+                        bool& aMatched)
 {
     uint32_t i, len = mLocPathPatterns.Length();
     for (i = 0; i < len; ++i) {
-        if (mLocPathPatterns[i]->matches(aNode, aContext)) {
-            return true;
+        nsresult rv = mLocPathPatterns[i]->matches(aNode, aContext, aMatched);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        if (aMatched) {
+            aMatched = true;
+
+            return NS_OK;
         }
     }
-    return false;
+
+    aMatched = false;
+
+    return NS_OK;
 }
 
 txPattern::Type
 txUnionPattern::getType()
 {
   return UNION_PATTERN;
 }
 
@@ -98,17 +108,19 @@ nsresult txLocPathPattern::addStep(txPat
         return NS_ERROR_OUT_OF_MEMORY;
 
     step->pattern = aPattern;
     step->isChild = isChild;
 
     return NS_OK;
 }
 
-bool txLocPathPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext)
+nsresult
+txLocPathPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext,
+                          bool& aMatched)
 {
     NS_ASSERTION(mSteps.Length() > 1, "Internal error");
 
     /*
      * The idea is to split up a path into blocks separated by descendant
      * operators. For example "foo/bar//baz/bop//ying/yang" is split up into
      * three blocks. The "ying/yang" block is handled by the first while-loop
      * and the "foo/bar" and "baz/bop" blocks are handled by the second
@@ -116,60 +128,90 @@ bool txLocPathPattern::matches(const txX
      * A block is considered matched when we find a list of ancestors that
      * match the block. If there are more than one list of ancestors that
      * match a block we only need to find the one furthermost down in the
      * tree.
      */
 
     uint32_t pos = mSteps.Length();
     Step* step = &mSteps[--pos];
-    if (!step->pattern->matches(aNode, aContext))
-        return false;
+    nsresult rv = step->pattern->matches(aNode, aContext, aMatched);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (!aMatched) {
+        return NS_OK;
+    }
 
     txXPathTreeWalker walker(aNode);
     bool hasParent = walker.moveToParent();
 
     while (step->isChild) {
-        if (!pos)
-            return true; // all steps matched
+        if (!pos) {
+            aMatched = true;
+
+            return NS_OK; // all steps matched
+        }
+
+        if (!hasParent) {
+            // no more ancestors
+            aMatched = false;
+
+            return NS_OK;
+        }
+
         step = &mSteps[--pos];
-        if (!hasParent || !step->pattern->matches(walker.getCurrentPosition(), aContext))
-            return false; // no more ancestors or no match
+        rv = step->pattern->matches(walker.getCurrentPosition(), aContext,
+                                    aMatched);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        if (!aMatched) {
+            // no match
+            return NS_OK;
+        }
 
         hasParent = walker.moveToParent();
     }
 
     // We have at least one // path separator
     txXPathTreeWalker blockWalker(walker);
     uint32_t blockPos = pos;
 
     while (pos) {
-        if (!hasParent)
-            return false; // There are more steps in the current block 
-                             // than ancestors of the tested node
+        if (!hasParent) {
+            aMatched = false; // There are more steps in the current block
+                              // than ancestors of the tested node
+            return NS_OK;
+        }
 
         step = &mSteps[--pos];
-        if (!step->pattern->matches(walker.getCurrentPosition(), aContext)) {
+        bool matched;
+        rv = step->pattern->matches(walker.getCurrentPosition(), aContext,
+                                    matched);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        if (!matched) {
             // Didn't match. We restart at beginning of block using a new
             // start node
             pos = blockPos;
             hasParent = blockWalker.moveToParent();
             walker.moveTo(blockWalker);
         }
         else {
             hasParent = walker.moveToParent();
             if (!step->isChild) {
                 // We've matched an entire block. Set new start pos and start node
                 blockPos = pos;
                 blockWalker.moveTo(walker);
             }
         }
     }
 
-    return true;
+    aMatched = true;
+
+    return NS_OK;
 } // txLocPathPattern::matches
 
 double txLocPathPattern::getDefaultPriority()
 {
     NS_ASSERTION(mSteps.Length() > 1, "Internal error");
 
     return 0.5;
 }
@@ -213,19 +255,23 @@ txLocPathPattern::toString(nsAString& aD
 #endif
 
 /*
  * txRootPattern
  *
  * a txPattern matching the document node, or '/'
  */
 
-bool txRootPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext)
+nsresult
+txRootPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext,
+                       bool& aMatched)
 {
-    return txXPathNodeUtils::isRoot(aNode);
+    aMatched = txXPathNodeUtils::isRoot(aNode);
+
+    return NS_OK;
 }
 
 double txRootPattern::getDefaultPriority()
 {
     return 0.5;
 }
 
 TX_IMPL_PATTERN_STUBS_NO_SUB_EXPR(txRootPattern)
@@ -259,28 +305,34 @@ txIdPattern::txIdPattern(const nsSubstri
     nsWhitespaceTokenizer tokenizer(aString);
     while (tokenizer.hasMoreTokens()) {
         // this can fail, XXX move to a Init(aString) method
         nsCOMPtr<nsIAtom> atom = NS_Atomize(tokenizer.nextToken());
         mIds.AppendObject(atom);
     }
 }
 
-bool txIdPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext)
+nsresult
+txIdPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext,
+                     bool& aMatched)
 {
     if (!txXPathNodeUtils::isElement(aNode)) {
-        return false;
+        aMatched = false;
+
+        return NS_OK;
     }
 
     // Get a ID attribute, if there is
     nsIContent* content = txXPathNativeNode::getContent(aNode);
     NS_ASSERTION(content, "a Element without nsIContent");
 
     nsIAtom* id = content->GetID();
-    return id && mIds.IndexOf(id) > -1;
+    aMatched = id && mIds.IndexOf(id) > -1;
+
+    return NS_OK;
 }
 
 double txIdPattern::getDefaultPriority()
 {
     return 0.5;
 }
 
 TX_IMPL_PATTERN_STUBS_NO_SUB_EXPR(txIdPattern)
@@ -315,28 +367,32 @@ txIdPattern::toString(nsAString& aDest)
  * txKeyPattern
  *
  * txKeyPattern matches if the given node is in the evalation of 
  * the key() function
  * This resembles the key() function, but may only have LITERALs as
  * argument.
  */
 
-bool txKeyPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext)
+nsresult
+txKeyPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext,
+                      bool& aMatched)
 {
     txExecutionState* es = (txExecutionState*)aContext->getPrivateContext();
     nsAutoPtr<txXPathNode> contextDoc(txXPathNodeUtils::getOwnerDocument(aNode));
-    NS_ENSURE_TRUE(contextDoc, false);
+    NS_ENSURE_TRUE(contextDoc, NS_ERROR_FAILURE);
 
     RefPtr<txNodeSet> nodes;
     nsresult rv = es->getKeyNodes(mName, *contextDoc, mValue, true,
                                   getter_AddRefs(nodes));
-    NS_ENSURE_SUCCESS(rv, false);
+    NS_ENSURE_SUCCESS(rv, rv);
 
-    return nodes->contains(aNode);
+    aMatched = nodes->contains(aNode);
+
+    return NS_OK;
 }
 
 double txKeyPattern::getDefaultPriority()
 {
     return 0.5;
 }
 
 TX_IMPL_PATTERN_STUBS_NO_SUB_EXPR(txKeyPattern)
@@ -368,31 +424,42 @@ txKeyPattern::toString(nsAString& aDest)
 #endif
 
 /*
  * txStepPattern
  *
  * a txPattern to hold the NodeTest and the Predicates of a StepPattern
  */
 
-bool txStepPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext)
+nsresult
+txStepPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext,
+                       bool& aMatched)
 {
     NS_ASSERTION(mNodeTest, "Internal error");
 
-    if (!mNodeTest->matches(aNode, aContext))
-        return false;
+    nsresult rv = mNodeTest->matches(aNode, aContext, aMatched);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (!aMatched) {
+        return NS_OK;
+    }
 
     txXPathTreeWalker walker(aNode);
     if ((!mIsAttr &&
          txXPathNodeUtils::isAttribute(walker.getCurrentPosition())) ||
         !walker.moveToParent()) {
-        return false;
+        aMatched = false;
+
+        return NS_OK;
     }
+
     if (isEmpty()) {
-        return true;
+        aMatched = true;
+
+        return NS_OK;
     }
 
     /*
      * Evaluate Predicates
      *
      * Copy all siblings/attributes matching mNodeTest to nodes
      * Up to the last Predicate do
      *  Foreach node in nodes
@@ -405,44 +472,48 @@ bool txStepPattern::matches(const txXPat
      *
      * For the last Predicate, evaluate Predicate with aNode as
      *  context node, if the result is a number, check the position,
      *  otherwise return the result converted to boolean
      */
 
     // Create the context node set for evaluating the predicates
     RefPtr<txNodeSet> nodes;
-    nsresult rv = aContext->recycler()->getNodeSet(getter_AddRefs(nodes));
-    NS_ENSURE_SUCCESS(rv, false);
+    rv = aContext->recycler()->getNodeSet(getter_AddRefs(nodes));
+    NS_ENSURE_SUCCESS(rv, rv);
 
     bool hasNext = mIsAttr ? walker.moveToFirstAttribute() :
                                walker.moveToFirstChild();
     while (hasNext) {
-        if (mNodeTest->matches(walker.getCurrentPosition(), aContext)) {
+        bool matched;
+        rv = mNodeTest->matches(walker.getCurrentPosition(), aContext, matched);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        if (matched) {
             nodes->append(walker.getCurrentPosition());
         }
         hasNext = mIsAttr ? walker.moveToNextAttribute() :
                             walker.moveToNextSibling();
     }
 
     Expr* predicate = mPredicates[0];
     RefPtr<txNodeSet> newNodes;
     rv = aContext->recycler()->getNodeSet(getter_AddRefs(newNodes));
-    NS_ENSURE_SUCCESS(rv, false);
+    NS_ENSURE_SUCCESS(rv, rv);
 
     uint32_t i, predLen = mPredicates.Length();
     for (i = 1; i < predLen; ++i) {
         newNodes->clear();
         bool contextIsInPredicate = false;
         txNodeSetContext predContext(nodes, aContext);
         while (predContext.hasNext()) {
             predContext.next();
             RefPtr<txAExprResult> exprResult;
             rv = predicate->evaluate(&predContext, getter_AddRefs(exprResult));
-            NS_ENSURE_SUCCESS(rv, false);
+            NS_ENSURE_SUCCESS(rv, rv);
 
             switch(exprResult->getResultType()) {
                 case txAExprResult::NUMBER:
                     // handle default, [position() == numberValue()]
                     if ((double)predContext.position() ==
                         exprResult->numberValue()) {
                         const txXPathNode& tmp = predContext.getContextNode();
                         if (tmp == aNode)
@@ -459,30 +530,35 @@ bool txStepPattern::matches(const txXPat
                     }
                     break;
             }
         }
         // Move new NodeSet to the current one
         nodes->clear();
         nodes->append(*newNodes);
         if (!contextIsInPredicate) {
-            return false;
+            aMatched = false;
+
+            return NS_OK;
         }
         predicate = mPredicates[i];
     }
     txForwardContext evalContext(aContext, aNode, nodes);
     RefPtr<txAExprResult> exprResult;
     rv = predicate->evaluate(&evalContext, getter_AddRefs(exprResult));
-    NS_ENSURE_SUCCESS(rv, false);
+    NS_ENSURE_SUCCESS(rv, rv);
 
-    if (exprResult->getResultType() == txAExprResult::NUMBER)
+    if (exprResult->getResultType() == txAExprResult::NUMBER) {
         // handle default, [position() == numberValue()]
-        return ((double)evalContext.position() == exprResult->numberValue());
+        aMatched = ((double)evalContext.position() == exprResult->numberValue());
+    } else {
+        aMatched = exprResult->booleanValue();
+    }
 
-    return exprResult->booleanValue();
+    return NS_OK;
 } // matches
 
 double txStepPattern::getDefaultPriority()
 {
     if (isEmpty())
         return mNodeTest->getDefaultPriority();
     return 0.5;
 }
--- a/dom/xslt/xslt/txXSLTPatterns.h
+++ b/dom/xslt/xslt/txXSLTPatterns.h
@@ -21,18 +21,19 @@ public:
     virtual ~txPattern()
     {
         MOZ_COUNT_DTOR(txPattern);
     }
 
     /*
      * Determines whether this Pattern matches the given node.
      */
-    virtual bool matches(const txXPathNode& aNode,
-                          txIMatchContext* aContext) = 0;
+    virtual nsresult matches(const txXPathNode& aNode,
+                             txIMatchContext* aContext,
+                             bool& aMatched) = 0;
 
     /*
      * Returns the default priority of this Pattern.
      *
      * Simple Patterns return the values as specified in XPath 5.5.
      * Returns -Inf for union patterns, as it shouldn't be called on them.
      */
     virtual double getDefaultPriority() = 0;
@@ -81,17 +82,18 @@ public:
      * other #toString() methods for Patterns.
      * @return the String representation of this Pattern.
      */
     virtual void toString(nsAString& aDest) = 0;
 #endif
 };
 
 #define TX_DECL_PATTERN_BASE \
-    bool matches(const txXPathNode& aNode, txIMatchContext* aContext) override; \
+    nsresult matches(const txXPathNode& aNode, txIMatchContext* aContext, \
+                     bool& aMatched) override; \
     double getDefaultPriority() override; \
     virtual Expr* getSubExprAt(uint32_t aPos) override; \
     virtual void setSubExprAt(uint32_t aPos, Expr* aExpr) override; \
     virtual txPattern* getSubPatternAt(uint32_t aPos) override; \
     virtual void setSubPatternAt(uint32_t aPos, txPattern* aPattern) override
 
 #ifndef TX_TO_STRING
 #define TX_DECL_PATTERN TX_DECL_PATTERN_BASE
--- a/image/encoders/bmp/nsBMPEncoder.cpp
+++ b/image/encoders/bmp/nsBMPEncoder.cpp
@@ -5,16 +5,17 @@
 
 #include "nsCRT.h"
 #include "mozilla/EndianUtils.h"
 #include "mozilla/UniquePtrExtensions.h"
 #include "nsBMPEncoder.h"
 #include "nsString.h"
 #include "nsStreamUtils.h"
 #include "nsTArray.h"
+#include "mozilla/CheckedInt.h"
 
 using namespace mozilla;
 using namespace mozilla::image;
 using namespace mozilla::image::bmp;
 
 NS_IMPL_ISUPPORTS(nsBMPEncoder, imgIEncoder, nsIInputStream,
                   nsIAsyncInputStream)
 
@@ -53,16 +54,21 @@ nsBMPEncoder::InitFromData(const uint8_t
 {
   // validate input format
   if (aInputFormat != INPUT_FORMAT_RGB &&
       aInputFormat != INPUT_FORMAT_RGBA &&
       aInputFormat != INPUT_FORMAT_HOSTARGB) {
     return NS_ERROR_INVALID_ARG;
   }
 
+  CheckedInt32 check = CheckedInt32(aWidth) * 4;
+  if (MOZ_UNLIKELY(!check.isValid())) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
   // Stride is the padded width of each row, so it better be longer
   if ((aInputFormat == INPUT_FORMAT_RGB &&
        aStride < aWidth * 3) ||
       ((aInputFormat == INPUT_FORMAT_RGBA ||
         aInputFormat == INPUT_FORMAT_HOSTARGB) &&
        aStride < aWidth * 4)) {
       NS_WARNING("Invalid stride for InitFromData");
       return NS_ERROR_INVALID_ARG;
@@ -81,29 +87,29 @@ nsBMPEncoder::InitFromData(const uint8_t
   }
 
   rv = EndImageEncode();
   return rv;
 }
 
 // Just a helper method to make it explicit in calculations that we are dealing
 // with bytes and not bits
-static inline uint32_t
-BytesPerPixel(uint32_t aBPP)
+static inline uint16_t
+BytesPerPixel(uint16_t aBPP)
 {
   return aBPP / 8;
 }
 
 // Calculates the number of padding bytes that are needed per row of image data
 static inline uint32_t
-PaddingBytes(uint32_t aBPP, uint32_t aWidth)
+PaddingBytes(uint16_t aBPP, uint32_t aWidth)
 {
   uint32_t rowSize = aWidth * BytesPerPixel(aBPP);
   uint8_t paddingSize = 0;
-  if(rowSize % 4) {
+  if (rowSize % 4) {
     paddingSize = (4 - (rowSize % 4));
   }
   return paddingSize;
 }
 
 // See ::InitFromData for other info.
 NS_IMETHODIMP
 nsBMPEncoder::StartImageEncode(uint32_t aWidth,
@@ -120,24 +126,31 @@ nsBMPEncoder::StartImageEncode(uint32_t 
   if (aInputFormat != INPUT_FORMAT_RGB &&
       aInputFormat != INPUT_FORMAT_RGBA &&
       aInputFormat != INPUT_FORMAT_HOSTARGB) {
     return NS_ERROR_INVALID_ARG;
   }
 
   // parse and check any provided output options
   Version version;
-  uint32_t bpp;
+  uint16_t bpp;
   nsresult rv = ParseOptions(aOutputOptions, version, bpp);
   if (NS_FAILED(rv)) {
     return rv;
   }
+  MOZ_ASSERT(bpp <= 32);
 
-  InitFileHeader(version, bpp, aWidth, aHeight);
-  InitInfoHeader(version, bpp, aWidth, aHeight);
+  rv = InitFileHeader(version, bpp, aWidth, aHeight);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  rv = InitInfoHeader(version, bpp, aWidth, aHeight);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
 
   mImageBufferSize = mBMPFileHeader.filesize;
   mImageBufferStart = static_cast<uint8_t*>(malloc(mImageBufferSize));
   if (!mImageBufferStart) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
   mImageBufferCurr = mImageBufferStart;
 
@@ -182,22 +195,36 @@ nsBMPEncoder::AddImageFrame(const uint8_
 
   // validate input format
   if (aInputFormat != INPUT_FORMAT_RGB &&
       aInputFormat != INPUT_FORMAT_RGBA &&
       aInputFormat != INPUT_FORMAT_HOSTARGB) {
     return NS_ERROR_INVALID_ARG;
   }
 
-  auto row = MakeUniqueFallible<uint8_t[]>(mBMPInfoHeader.width *
-                                           BytesPerPixel(mBMPInfoHeader.bpp));
+  if (mBMPInfoHeader.width < 0) {
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+
+  CheckedUint32 size =
+    CheckedUint32(mBMPInfoHeader.width) * CheckedUint32(BytesPerPixel(mBMPInfoHeader.bpp));
+  if (MOZ_UNLIKELY(!size.isValid())) {
+    return NS_ERROR_FAILURE;
+  }
+
+  auto row = MakeUniqueFallible<uint8_t[]>(size.value());
   if (!row) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
+  CheckedUint32 check = CheckedUint32(mBMPInfoHeader.height) * aStride;
+  if (MOZ_UNLIKELY(!check.isValid())) {
+    return NS_ERROR_FAILURE;
+  }
+
   // write each row: if we add more input formats, we may want to
   // generalize the conversions
   if (aInputFormat == INPUT_FORMAT_HOSTARGB) {
     // BMP requires RGBA with post-multiplied alpha, so we need to convert
     for (int32_t y = mBMPInfoHeader.height - 1; y >= 0 ; y --) {
       ConvertHostARGBRow(&aData[y * aStride], row, mBMPInfoHeader.width);
       if(mBMPInfoHeader.bpp == 24) {
         EncodeImageDataRow24(row.get());
@@ -251,17 +278,17 @@ nsBMPEncoder::EndImageEncode()
   return NS_OK;
 }
 
 
 // Parses the encoder options and sets the bits per pixel to use
 // See InitFromData for a description of the parse options
 nsresult
 nsBMPEncoder::ParseOptions(const nsAString& aOptions, Version& aVersionOut,
-                           uint32_t& aBppOut)
+                           uint16_t& aBppOut)
 {
   aVersionOut = VERSION_3;
   aBppOut = 24;
 
   // Parse the input string into a set of name/value pairs.
   // From a format like: name=value;bpp=<bpp_value>;name=value
   // to format: [0] = name=value, [1] = bpp=<bpp_value>, [2] = name=value
   nsTArray<nsCString> nameValuePairs;
@@ -419,17 +446,17 @@ nsBMPEncoder::CloseWithStatus(nsresult a
 //    Our colors are stored with premultiplied alphas, but we need
 //    an output with no alpha in machine-independent byte order.
 //
 void
 nsBMPEncoder::ConvertHostARGBRow(const uint8_t* aSrc,
                                  const UniquePtr<uint8_t[]>& aDest,
                                  uint32_t aPixelWidth)
 {
-  int bytes = BytesPerPixel(mBMPInfoHeader.bpp);
+  uint16_t bytes = BytesPerPixel(mBMPInfoHeader.bpp);
 
   if (mBMPInfoHeader.bpp == 32) {
     for (uint32_t x = 0; x < aPixelWidth; x++) {
       const uint32_t& pixelIn = ((const uint32_t*)(aSrc))[x];
       uint8_t* pixelOut = &aDest[x * bytes];
 
       pixelOut[0] = (pixelIn & 0x00ff0000) >> 16;
       pixelOut[1] = (pixelIn & 0x0000ff00) >>  8;
@@ -468,71 +495,104 @@ nsBMPEncoder::NotifyListener()
     mCallbackTarget = nullptr;
     mNotifyThreshold = 0;
 
     callback->OnInputStreamReady(this);
   }
 }
 
 // Initializes the BMP file header mBMPFileHeader to the passed in values
-void
-nsBMPEncoder::InitFileHeader(Version aVersion, uint32_t aBPP, uint32_t aWidth,
+nsresult
+nsBMPEncoder::InitFileHeader(Version aVersion, uint16_t aBPP, uint32_t aWidth,
                              uint32_t aHeight)
 {
   memset(&mBMPFileHeader, 0, sizeof(mBMPFileHeader));
   mBMPFileHeader.signature[0] = 'B';
   mBMPFileHeader.signature[1] = 'M';
 
   if (aVersion == VERSION_3) {
     mBMPFileHeader.dataoffset = FILE_HEADER_LENGTH + InfoHeaderLength::WIN_V3;
   } else { // aVersion == 5
     mBMPFileHeader.dataoffset = FILE_HEADER_LENGTH + InfoHeaderLength::WIN_V5;
   }
 
   // The color table is present only if BPP is <= 8
   if (aBPP <= 8) {
     uint32_t numColors = 1 << aBPP;
     mBMPFileHeader.dataoffset += 4 * numColors;
-    mBMPFileHeader.filesize = mBMPFileHeader.dataoffset + aWidth * aHeight;
+    CheckedUint32 filesize =
+      CheckedUint32(mBMPFileHeader.dataoffset) + CheckedUint32(aWidth) * aHeight;
+    if (MOZ_UNLIKELY(!filesize.isValid())) {
+      return NS_ERROR_INVALID_ARG;
+    }
+    mBMPFileHeader.filesize = filesize.value();
   } else {
-    mBMPFileHeader.filesize = mBMPFileHeader.dataoffset +
-      (aWidth * BytesPerPixel(aBPP) + PaddingBytes(aBPP, aWidth)) * aHeight;
+    CheckedUint32 filesize =
+      CheckedUint32(mBMPFileHeader.dataoffset) +
+        (CheckedUint32(aWidth) * BytesPerPixel(aBPP) + PaddingBytes(aBPP, aWidth)) * aHeight;
+    if (MOZ_UNLIKELY(!filesize.isValid())) {
+      return NS_ERROR_INVALID_ARG;
+    }
+    mBMPFileHeader.filesize = filesize.value();
   }
 
   mBMPFileHeader.reserved = 0;
+
+  return NS_OK;
 }
 
 #define ENCODE(pImageBufferCurr, value) \
     memcpy(*pImageBufferCurr, &value, sizeof value); \
     *pImageBufferCurr += sizeof value;
 
 // Initializes the bitmap info header mBMPInfoHeader to the passed in values
-void
-nsBMPEncoder::InitInfoHeader(Version aVersion, uint32_t aBPP, uint32_t aWidth,
+nsresult
+nsBMPEncoder::InitInfoHeader(Version aVersion, uint16_t aBPP, uint32_t aWidth,
                              uint32_t aHeight)
 {
   memset(&mBMPInfoHeader, 0, sizeof(mBMPInfoHeader));
   if (aVersion == VERSION_3) {
     mBMPInfoHeader.bihsize = InfoHeaderLength::WIN_V3;
   } else {
     MOZ_ASSERT(aVersion == VERSION_5);
     mBMPInfoHeader.bihsize = InfoHeaderLength::WIN_V5;
   }
-  mBMPInfoHeader.width = aWidth;
-  mBMPInfoHeader.height = aHeight;
+
+  CheckedInt32 width(aWidth);
+  CheckedInt32 height(aHeight);
+  if (MOZ_UNLIKELY(!width.isValid() || !height.isValid())) {
+    return NS_ERROR_INVALID_ARG;
+  }
+  mBMPInfoHeader.width = width.value();
+  mBMPInfoHeader.height = height.value();
+
   mBMPInfoHeader.planes = 1;
   mBMPInfoHeader.bpp = aBPP;
   mBMPInfoHeader.compression = 0;
   mBMPInfoHeader.colors = 0;
   mBMPInfoHeader.important_colors = 0;
+
+  CheckedUint32 check = CheckedUint32(aWidth) * BytesPerPixel(aBPP);
+  if (MOZ_UNLIKELY(!check.isValid())) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
   if (aBPP <= 8) {
-    mBMPInfoHeader.image_size = aWidth * aHeight;
+    CheckedUint32 imagesize = CheckedUint32(aWidth) * aHeight;
+    if (MOZ_UNLIKELY(!imagesize.isValid())) {
+      return NS_ERROR_INVALID_ARG;
+    }
+    mBMPInfoHeader.image_size = imagesize.value();
   } else {
-    mBMPInfoHeader.image_size =
-      (aWidth * BytesPerPixel(aBPP) + PaddingBytes(aBPP, aWidth)) * aHeight;
+    CheckedUint32 imagesize =
+      (CheckedUint32(aWidth) * BytesPerPixel(aBPP) + PaddingBytes(aBPP, aWidth)) * CheckedUint32(aHeight);
+    if (MOZ_UNLIKELY(!imagesize.isValid())) {
+      return NS_ERROR_INVALID_ARG;
+    }
+    mBMPInfoHeader.image_size = imagesize.value();
   }
   mBMPInfoHeader.xppm = 0;
   mBMPInfoHeader.yppm = 0;
   if (aVersion >= VERSION_5) {
       mBMPInfoHeader.red_mask   = 0x000000FF;
       mBMPInfoHeader.green_mask = 0x0000FF00;
       mBMPInfoHeader.blue_mask  = 0x00FF0000;
       mBMPInfoHeader.alpha_mask = 0xFF000000;
@@ -549,16 +609,18 @@ nsBMPEncoder::InitInfoHeader(Version aVe
       mBMPInfoHeader.gamma_red = 0;
       mBMPInfoHeader.gamma_green = 0;
       mBMPInfoHeader.gamma_blue = 0;
       mBMPInfoHeader.intent = 0;
       mBMPInfoHeader.profile_offset = 0;
       mBMPInfoHeader.profile_size = 0;
       mBMPInfoHeader.reserved = 0;
   }
+
+  return NS_OK;
 }
 
 // Encodes the BMP file header mBMPFileHeader
 void
 nsBMPEncoder::EncodeFileHeader()
 {
   FileHeader littleEndianBFH = mBMPFileHeader;
   NativeEndian::swapToLittleEndianInPlace(&littleEndianBFH.filesize, 1);
--- a/image/encoders/bmp/nsBMPEncoder.h
+++ b/image/encoders/bmp/nsBMPEncoder.h
@@ -99,30 +99,30 @@ protected:
   enum Version
   {
       VERSION_3 = 3,
       VERSION_5 = 5
   };
 
   // See InitData in the cpp for valid parse options
   nsresult ParseOptions(const nsAString& aOptions, Version& aVersionOut,
-                        uint32_t& aBppOut);
+                        uint16_t& aBppOut);
   // Obtains data with no alpha in machine-independent byte order
   void ConvertHostARGBRow(const uint8_t* aSrc,
                           const mozilla::UniquePtr<uint8_t[]>& aDest,
                           uint32_t aPixelWidth);
   // Thread safe notify listener
   void NotifyListener();
 
   // Initializes the bitmap file header member mBMPFileHeader
-  void InitFileHeader(Version aVersion, uint32_t aBPP, uint32_t aWidth,
-                      uint32_t aHeight);
+  nsresult InitFileHeader(Version aVersion, uint16_t aBPP, uint32_t aWidth,
+                          uint32_t aHeight);
   // Initializes the bitmap info header member mBMPInfoHeader
-  void InitInfoHeader(Version aVersion, uint32_t aBPP, uint32_t aWidth,
-                      uint32_t aHeight);
+  nsresult InitInfoHeader(Version aVersion, uint16_t aBPP, uint32_t aWidth,
+                          uint32_t aHeight);
 
   // Encodes the bitmap file header member mBMPFileHeader
   void EncodeFileHeader();
   // Encodes the bitmap info header member mBMPInfoHeader
   void EncodeInfoHeader();
   // Encodes a row of image data which does not have alpha data
   void EncodeImageDataRow24(const uint8_t* aData);
   // Encodes a row of image data which does have alpha data
--- a/image/encoders/ico/nsICOEncoder.cpp
+++ b/image/encoders/ico/nsICOEncoder.cpp
@@ -223,20 +223,21 @@ nsICOEncoder::StartImageEncode(uint32_t 
   }
 
   // Icons are only 1 byte, so make sure our bitmap is in range
   if (aWidth > 256 || aHeight > 256) {
     return NS_ERROR_INVALID_ARG;
   }
 
   // parse and check any provided output options
-  uint32_t bpp = 24;
+  uint16_t bpp = 24;
   bool usePNG = true;
   nsresult rv = ParseOptions(aOutputOptions, bpp, usePNG);
   NS_ENSURE_SUCCESS(rv, rv);
+  MOZ_ASSERT(bpp <= 32);
 
   mUsePNG = usePNG;
 
   InitFileHeader();
   // The width and height are stored as 0 when we have a value of 256
   InitInfoHeader(bpp, aWidth == 256 ? 0 : (uint8_t)aWidth,
                  aHeight == 256 ? 0 : (uint8_t)aHeight);
 
@@ -260,17 +261,17 @@ nsICOEncoder::EndImageEncode()
   }
 
   return NS_OK;
 }
 
 // Parses the encoder options and sets the bits per pixel to use and PNG or BMP
 // See InitFromData for a description of the parse options
 nsresult
-nsICOEncoder::ParseOptions(const nsAString& aOptions, uint32_t& aBppOut,
+nsICOEncoder::ParseOptions(const nsAString& aOptions, uint16_t& aBppOut,
                            bool& aUsePNGOut)
 {
   // If no parsing options just use the default of 24BPP and PNG yes
   if (aOptions.Length() == 0) {
     aUsePNGOut = true;
     aBppOut = 24;
   }
 
@@ -464,17 +465,17 @@ nsICOEncoder::InitFileHeader()
   memset(&mICOFileHeader, 0, sizeof(mICOFileHeader));
   mICOFileHeader.mReserved = 0;
   mICOFileHeader.mType = 1;
   mICOFileHeader.mCount = 1;
 }
 
 // Initializes the icon directory info header mICODirEntry
 void
-nsICOEncoder::InitInfoHeader(uint32_t aBPP, uint8_t aWidth, uint8_t aHeight)
+nsICOEncoder::InitInfoHeader(uint16_t aBPP, uint8_t aWidth, uint8_t aHeight)
 {
   memset(&mICODirEntry, 0, sizeof(mICODirEntry));
   mICODirEntry.mBitCount = aBPP;
   mICODirEntry.mBytesInRes = 0;
   mICODirEntry.mColorCount = 0;
   mICODirEntry.mWidth = aWidth;
   mICODirEntry.mHeight = aHeight;
   mICODirEntry.mImageOffset = ICONFILEHEADERSIZE + ICODIRENTRYSIZE;
--- a/image/encoders/ico/nsICOEncoder.h
+++ b/image/encoders/ico/nsICOEncoder.h
@@ -45,24 +45,24 @@ public:
   uint32_t GetRealHeight() const
   {
     return mICODirEntry.mHeight == 0 ? 256 : mICODirEntry.mHeight;
   }
 
 protected:
   ~nsICOEncoder();
 
-  nsresult ParseOptions(const nsAString& aOptions, uint32_t& aBppOut,
+  nsresult ParseOptions(const nsAString& aOptions, uint16_t& aBppOut,
                         bool& aUsePNGOut);
   void NotifyListener();
 
   // Initializes the icon file header mICOFileHeader
   void InitFileHeader();
   // Initializes the icon directory info header mICODirEntry
-  void InitInfoHeader(uint32_t aBPP, uint8_t aWidth, uint8_t aHeight);
+  void InitInfoHeader(uint16_t aBPP, uint8_t aWidth, uint8_t aHeight);
   // Encodes the icon file header mICOFileHeader
   void EncodeFileHeader();
   // Encodes the icon directory info header mICODirEntry
   void EncodeInfoHeader();
   // Obtains the current offset filled up to for the image buffer
   inline int32_t GetCurrentImageBufferOffset()
   {
     return static_cast<int32_t>(mImageBufferCurr - mImageBufferStart);
--- a/js/src/jit/arm/Lowering-arm.cpp
+++ b/js/src/jit/arm/Lowering-arm.cpp
@@ -897,16 +897,18 @@ LIRGeneratorARM::visitAsmJSCompareExchan
 {
     MOZ_ASSERT(ins->access().type() < Scalar::Float32);
     MOZ_ASSERT(ins->access().offset() == 0);
 
     MDefinition* base = ins->base();
     MOZ_ASSERT(base->type() == MIRType::Int32);
 
     if (byteSize(ins->access().type()) != 4 && !HasLDSTREXBHD()) {
+        gen->setPerformsCall();
+
         LAsmJSCompareExchangeCallout* lir =
             new(alloc()) LAsmJSCompareExchangeCallout(useFixedAtStart(base, IntArgReg2),
                                                       useFixedAtStart(ins->oldValue(), IntArgReg3),
                                                       useFixedAtStart(ins->newValue(), CallTempReg0),
                                                       useFixedAtStart(ins->tls(), WasmTlsReg),
                                                       tempFixed(IntArgReg0),
                                                       tempFixed(IntArgReg1));
         defineReturn(lir, ins);
@@ -924,16 +926,18 @@ LIRGeneratorARM::visitAsmJSCompareExchan
 void
 LIRGeneratorARM::visitAsmJSAtomicExchangeHeap(MAsmJSAtomicExchangeHeap* ins)
 {
     MOZ_ASSERT(ins->base()->type() == MIRType::Int32);
     MOZ_ASSERT(ins->access().type() < Scalar::Float32);
     MOZ_ASSERT(ins->access().offset() == 0);
 
     if (byteSize(ins->access().type()) < 4 && !HasLDSTREXBHD()) {
+        gen->setPerformsCall();
+
         // Call out on ARMv6.
         defineReturn(new(alloc()) LAsmJSAtomicExchangeCallout(useFixedAtStart(ins->base(), IntArgReg2),
                                                               useFixedAtStart(ins->value(), IntArgReg3),
                                                               useFixedAtStart(ins->tls(), WasmTlsReg),
                                                               tempFixed(IntArgReg0),
                                                               tempFixed(IntArgReg1)), ins);
         return;
     }
@@ -948,16 +952,18 @@ LIRGeneratorARM::visitAsmJSAtomicBinopHe
 {
     MOZ_ASSERT(ins->access().type() < Scalar::Float32);
     MOZ_ASSERT(ins->access().offset() == 0);
 
     MDefinition* base = ins->base();
     MOZ_ASSERT(base->type() == MIRType::Int32);
 
     if (byteSize(ins->access().type()) != 4 && !HasLDSTREXBHD()) {
+        gen->setPerformsCall();
+
         LAsmJSAtomicBinopCallout* lir =
             new(alloc()) LAsmJSAtomicBinopCallout(useFixedAtStart(base, IntArgReg2),
                                                   useFixedAtStart(ins->value(), IntArgReg3),
                                                   useFixedAtStart(ins->tls(), WasmTlsReg),
                                                   tempFixed(IntArgReg0),
                                                   tempFixed(IntArgReg1));
         defineReturn(lir, ins);
         return;
--- a/mfbt/Casting.h
+++ b/mfbt/Casting.h
@@ -233,11 +233,24 @@ IsInBounds(const From aFrom)
 template<typename To, typename From>
 inline To
 AssertedCast(const From aFrom)
 {
   MOZ_ASSERT((detail::IsInBounds<From, To>(aFrom)));
   return static_cast<To>(aFrom);
 }
 
+/**
+ * Cast a value of integral type |From| to a value of integral type |To|,
+ * release asserting that the cast will be a safe cast per C++ (that is, that
+ * |to| is in the range of values permitted for the type |From|).
+ */
+template<typename To, typename From>
+inline To
+ReleaseAssertedCast(const From aFrom)
+{
+  MOZ_RELEASE_ASSERT((detail::IsInBounds<From, To>(aFrom)));
+  return static_cast<To>(aFrom);
+}
+
 } // namespace mozilla
 
 #endif /* mozilla_Casting_h */
--- a/mfbt/Range.h
+++ b/mfbt/Range.h
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_Range_h
 #define mozilla_Range_h
 
 #include "mozilla/RangedPtr.h"
 #include "mozilla/TypeTraits.h"
+#include "mozilla/Span.h"
 
 #include <stddef.h>
 
 namespace mozilla {
 
 // Range<T> is a tuple containing a pointer and a length.
 template <typename T>
 class Range
@@ -39,20 +40,51 @@ public:
   template<typename U,
            class = typename EnableIf<IsConvertible<U (*)[], T (*)[]>::value,
                                      int>::Type>
   MOZ_IMPLICIT Range(const Range<U>& aOther)
     : mStart(aOther.mStart),
       mEnd(aOther.mEnd)
   {}
 
+  MOZ_IMPLICIT Range(Span<T> aSpan)
+    : Range(aSpan.Elements(), aSpan.Length())
+  {
+  }
+
+  template<typename U,
+           class = typename EnableIf<IsConvertible<U (*)[], T (*)[]>::value,
+                                     int>::Type>
+  MOZ_IMPLICIT Range(const Span<U>& aSpan)
+    : Range(aSpan.Elements(), aSpan.Length())
+  {
+  }
+
   RangedPtr<T> begin() const { return mStart; }
   RangedPtr<T> end() const { return mEnd; }
   size_t length() const { return mEnd - mStart; }
 
   T& operator[](size_t aOffset) const { return mStart[aOffset]; }
 
   explicit operator bool() const { return mStart != nullptr; }
+
+  operator Span<T>() { return Span<T>(mStart.get(), length()); }
+
+  operator Span<const T>() const { return Span<T>(mStart.get(), length()); }
 };
 
+template<class T>
+Span<T>
+MakeSpan(Range<T>& aRange)
+{
+  return aRange;
+}
+
+template<class T>
+Span<const T>
+MakeSpan(const Range<T>& aRange)
+{
+  return aRange;
+}
+
 } // namespace mozilla
 
 #endif /* mozilla_Range_h */
new file mode 100644
--- /dev/null
+++ b/mfbt/Span.h
@@ -0,0 +1,1041 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2015 Microsoft Corporation. All rights reserved.
+//
+// This code is licensed under the MIT License (MIT).
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+// Adapted from https://github.com/Microsoft/GSL/blob/3819df6e378ffccf0e29465afe99c3b324c2aa70/include/gsl/span
+// and https://github.com/Microsoft/GSL/blob/3819df6e378ffccf0e29465afe99c3b324c2aa70/include/gsl/gsl_util
+
+#ifndef mozilla_Span_h
+#define mozilla_Span_h
+
+#include "mozilla/Array.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Casting.h"
+#include "mozilla/IntegerTypeTraits.h"
+#include "mozilla/Move.h"
+#include "mozilla/TypeTraits.h"
+#include "mozilla/UniquePtr.h"
+
+#include <algorithm>
+#include <array>
+#include <cstring>
+#include <iterator>
+
+// Classifications for reasons why constexpr was removed in C++14 to C++11
+// conversion. Once we upgrade compilers, we can try defining each of these
+// to constexpr to restore a category of constexprs at a time.
+#define MOZ_SPAN_ASSERTION_CONSTEXPR
+#define MOZ_SPAN_GCC_CONSTEXPR
+#define MOZ_SPAN_EXPLICITLY_DEFAULTED_CONSTEXPR
+#define MOZ_SPAN_CONSTEXPR_NOT_JUST_RETURN
+#define MOZ_SPAN_NON_CONST_CONSTEXPR
+
+#ifdef _MSC_VER
+#pragma warning(push)
+
+// turn off some warnings that are noisy about our MOZ_RELEASE_ASSERT statements
+#pragma warning(disable : 4127) // conditional expression is constant
+
+// blanket turn off warnings from CppCoreCheck for now
+// so people aren't annoyed by them when running the tool.
+// more targeted suppressions will be added in a future update to the GSL
+#pragma warning(disable : 26481 26482 26483 26485 26490 26491 26492 26493 26495)
+
+#if _MSC_VER < 1910
+#pragma push_macro("constexpr")
+#define constexpr /*constexpr*/
+
+#endif            // _MSC_VER < 1910
+#endif            // _MSC_VER
+
+namespace mozilla {
+
+// Stuff from gsl_util
+
+// narrow_cast(): a searchable way to do narrowing casts of values
+template<class T, class U>
+inline constexpr T
+narrow_cast(U&& u)
+{
+  return static_cast<T>(mozilla::Forward<U>(u));
+}
+
+// end gsl_util
+
+// [views.constants], constants
+// This was -1 in gsl::span, but using size_t for sizes instead of ptrdiff_t
+// and reserving a magic value that realistically doesn't occur in
+// compile-time-constant Span sizes makes things a lot less messy in terms of
+// comparison between signed and unsigned.
+constexpr const size_t dynamic_extent = mozilla::MaxValue<size_t>::value;
+
+template<class ElementType, size_t Extent = dynamic_extent>
+class Span;
+
+// implementation details
+namespace span_details {
+
+// C++14 types that we don't have because we build as C++11.
+template<class T>
+using remove_cv_t = typename mozilla::RemoveCV<T>::Type;
+template<class T>
+using remove_const_t = typename mozilla::RemoveConst<T>::Type;
+template<bool B, class T, class F>
+using conditional_t = typename mozilla::Conditional<B, T, F>::Type;
+template<class T>
+using add_pointer_t = typename mozilla::AddPointer<T>::Type;
+template<bool B, class T = void>
+using enable_if_t = typename mozilla::EnableIf<B, T>::Type;
+
+template<class T>
+struct is_span_oracle : mozilla::FalseType
+{
+};
+
+template<class ElementType, size_t Extent>
+struct is_span_oracle<mozilla::Span<ElementType, Extent>> : mozilla::TrueType
+{
+};
+
+template<class T>
+struct is_span : public is_span_oracle<remove_cv_t<T>>
+{
+};
+
+template<class T>
+struct is_std_array_oracle : mozilla::FalseType
+{
+};
+
+template<class ElementType, size_t Extent>
+struct is_std_array_oracle<std::array<ElementType, Extent>> : mozilla::TrueType
+{
+};
+
+template<class T>
+struct is_std_array : public is_std_array_oracle<remove_cv_t<T>>
+{
+};
+
+template<size_t From, size_t To>
+struct is_allowed_extent_conversion
+  : public mozilla::IntegralConstant<bool,
+                                  From == To ||
+                                    From == mozilla::dynamic_extent ||
+                                    To == mozilla::dynamic_extent>
+{
+};
+
+template<class From, class To>
+struct is_allowed_element_type_conversion
+  : public mozilla::IntegralConstant<bool, mozilla::IsConvertible<From (*)[], To (*)[]>::value>
+{
+};
+
+template<class Span, bool IsConst>
+class span_iterator
+{
+  using element_type_ = typename Span::element_type;
+
+public:
+  using iterator_category = std::random_access_iterator_tag;
+  using value_type = remove_const_t<element_type_>;
+  using difference_type = typename Span::index_type;
+
+  using reference = conditional_t<IsConst, const element_type_, element_type_>&;
+  using pointer = add_pointer_t<reference>;
+
+  constexpr span_iterator() : span_iterator(nullptr, 0) {}
+
+  MOZ_SPAN_ASSERTION_CONSTEXPR span_iterator(const Span* span,
+                                             typename Span::index_type index)
+    : span_(span)
+    , index_(index)
+  {
+    MOZ_RELEASE_ASSERT(span == nullptr ||
+                       (index_ >= 0 && index <= span_->Length()));
+  }
+
+  friend class span_iterator<Span, true>;
+  constexpr MOZ_IMPLICIT span_iterator(const span_iterator<Span, false>& other)
+    : span_iterator(other.span_, other.index_)
+  {
+  }
+
+  MOZ_SPAN_EXPLICITLY_DEFAULTED_CONSTEXPR span_iterator<Span, IsConst>&
+  operator=(const span_iterator<Span, IsConst>&) = default;
+
+  MOZ_SPAN_GCC_CONSTEXPR reference operator*() const
+  {
+    MOZ_RELEASE_ASSERT(span_);
+    return (*span_)[index_];
+  }
+
+  MOZ_SPAN_GCC_CONSTEXPR pointer operator->() const
+  {
+    MOZ_RELEASE_ASSERT(span_);
+    return &((*span_)[index_]);
+  }
+
+  MOZ_SPAN_NON_CONST_CONSTEXPR span_iterator& operator++()
+  {
+    MOZ_RELEASE_ASSERT(span_ && index_ >= 0 && index_ < span_->Length());
+    ++index_;
+    return *this;
+  }
+
+  MOZ_SPAN_NON_CONST_CONSTEXPR span_iterator operator++(int)
+  {
+    auto ret = *this;
+    ++(*this);
+    return ret;
+  }
+
+  MOZ_SPAN_NON_CONST_CONSTEXPR span_iterator& operator--()
+  {
+    MOZ_RELEASE_ASSERT(span_ && index_ > 0 && index_ <= span_->Length());
+    --index_;
+    return *this;
+  }
+
+  MOZ_SPAN_NON_CONST_CONSTEXPR span_iterator operator--(int)
+  {
+    auto ret = *this;
+    --(*this);
+    return ret;
+  }
+
+  MOZ_SPAN_CONSTEXPR_NOT_JUST_RETURN span_iterator
+  operator+(difference_type n) const
+  {
+    auto ret = *this;
+    return ret += n;
+  }
+
+  MOZ_SPAN_GCC_CONSTEXPR span_iterator& operator+=(difference_type n)
+  {
+    MOZ_RELEASE_ASSERT(span_ && (index_ + n) >= 0 &&
+                       (index_ + n) <= span_->Length());
+    index_ += n;
+    return *this;
+  }
+
+  MOZ_SPAN_CONSTEXPR_NOT_JUST_RETURN span_iterator
+  operator-(difference_type n) const
+  {
+    auto ret = *this;
+    return ret -= n;
+  }
+
+  MOZ_SPAN_NON_CONST_CONSTEXPR span_iterator& operator-=(difference_type n)
+
+  {
+    return *this += -n;
+  }
+
+  MOZ_SPAN_GCC_CONSTEXPR difference_type
+  operator-(const span_iterator& rhs) const
+  {
+    MOZ_RELEASE_ASSERT(span_ == rhs.span_);
+    return index_ - rhs.index_;
+  }
+
+  constexpr reference operator[](difference_type n) const
+  {
+    return *(*this + n);
+  }
+
+  constexpr friend bool operator==(const span_iterator& lhs,
+                                   const span_iterator& rhs)
+  {
+    return lhs.span_ == rhs.span_ && lhs.index_ == rhs.index_;
+  }
+
+  constexpr friend bool operator!=(const span_iterator& lhs,
+                                   const span_iterator& rhs)
+  {
+    return !(lhs == rhs);
+  }
+
+  MOZ_SPAN_GCC_CONSTEXPR friend bool operator<(const span_iterator& lhs,
+                                               const span_iterator& rhs)
+  {
+    MOZ_RELEASE_ASSERT(lhs.span_ == rhs.span_);
+    return lhs.index_ < rhs.index_;
+  }
+
+  constexpr friend bool operator<=(const span_iterator& lhs,
+                                   const span_iterator& rhs)
+  {
+    return !(rhs < lhs);
+  }
+
+  constexpr friend bool operator>(const span_iterator& lhs,
+                                  const span_iterator& rhs)
+  {
+    return rhs < lhs;
+  }
+
+  constexpr friend bool operator>=(const span_iterator& lhs,
+                                   const span_iterator& rhs)
+  {
+    return !(rhs > lhs);
+  }
+
+  void swap(span_iterator& rhs)
+  {
+    std::swap(index_, rhs.index_);
+    std::swap(span_, rhs.span_);
+  }
+
+protected:
+  const Span* span_;
+  size_t index_;
+};
+
+template<class Span, bool IsConst>
+inline constexpr span_iterator<Span, IsConst>
+operator+(typename span_iterator<Span, IsConst>::difference_type n,
+          const span_iterator<Span, IsConst>& rhs)
+{
+  return rhs + n;
+}
+
+template<size_t Ext>
+class extent_type
+{
+public:
+  using index_type = size_t;
+
+  static_assert(Ext >= 0, "A fixed-size Span must be >= 0 in size.");
+
+  constexpr extent_type() {}
+
+  template<index_type Other>
+  MOZ_SPAN_ASSERTION_CONSTEXPR MOZ_IMPLICIT extent_type(extent_type<Other> ext)
+  {
+    static_assert(
+      Other == Ext || Other == dynamic_extent,
+      "Mismatch between fixed-size extent and size of initializing data.");
+    MOZ_RELEASE_ASSERT(ext.size() == Ext);
+  }
+
+  MOZ_SPAN_ASSERTION_CONSTEXPR MOZ_IMPLICIT extent_type(index_type length)
+  {
+    MOZ_RELEASE_ASSERT(length == Ext);
+  }
+
+  constexpr index_type size() const { return Ext; }
+};
+
+template<>
+class extent_type<dynamic_extent>
+{
+public:
+  using index_type = size_t;
+
+  template<index_type Other>
+  explicit constexpr extent_type(extent_type<Other> ext)
+    : size_(ext.size())
+  {
+  }
+
+  explicit constexpr extent_type(index_type length)
+    : size_(length)
+  {
+  }
+
+  constexpr index_type size() const { return size_; }
+
+private:
+  index_type size_;
+};
+} // namespace span_details
+
+/**
+ * Span - slices for C++
+ *
+ * Span implements Rust's slice concept for C++. It's called "Span" instead of
+ * "Slice" to follow the naming used in C++ Core Guidelines.
+ *
+ * A Span wraps a pointer and a length that identify a non-owning view to a
+ * contiguous block of memory of objects of the same type. Various types,
+ * including (pre-decay) C arrays, XPCOM strings, nsTArray, mozilla::Array,
+ * mozilla::Range and contiguous standard-library containers, auto-convert
+ * into Spans when attempting to pass them as arguments to methods that take
+ * Spans. MakeSpan() functions can be used for explicit conversion in other
+ * contexts. (Span itself autoconverts into mozilla::Range.)
+ *
+ * Like Rust's slices, Span provides safety against out-of-bounds access by
+ * performing run-time bound checks. However, unlike Rust's slices, Span
+ * cannot provide safety against use-after-free.
+ *
+ * (Note: Span is like Rust's slice only conceptually. Due to the lack of
+ * ABI guarantees, you should still decompose spans/slices to raw pointer
+ * and length parts when crossing the FFI.)
+ *
+ * In addition to having constructors and MakeSpan() functions that take
+ * various well-known types, a Span for an arbitrary type can be constructed
+ * (via constructor or MakeSpan()) from a pointer and a length or a pointer
+ * and another pointer pointing just past the last element.
+ *
+ * A Span<const char> can be obtained for const char* pointing to a
+ * zero-terminated C string using the MakeCStringSpan() function. A
+ * corresponding implicit constructor does not exist in order to avoid
+ * accidental construction in cases where const char* does not point to a
+ * zero-terminated C string.
+ *
+ * Span has methods that follow the Mozilla naming style and methods that
+ * don't. The methods that follow the Mozilla naming style are meant to be
+ * used directly from Mozilla code. The methods that don't are meant for
+ * integration with C++11 range-based loops and with meta-programming that
+ * expects the same methods that are found on the standard-library
+ * containers. For example, to decompose a Span into its parts in Mozilla
+ * code, use Elements() and Length() (as with nsTArray) instead of data()
+ * and size() (as with std::vector).
+ *
+ * The pointer and length wrapped by a Span cannot be changed after a Span has
+ * been created. When new values are required, simply create a new Span. Span
+ * has a method called Subspan() that works analogously to the Substring()
+ * method of XPCOM strings taking a start index and an optional length. As a
+ * Mozilla extension (relative to Microsoft's gsl::span that mozilla::Span is
+ * based on), Span has methods From(start), To(end) and FromTo(start, end)
+ * that correspond to Rust's &slice[start..], &slice[..end] and
+ * &slice[start..end], respectively. (That is, the end index is the index of
+ * the first element not to be included in the new subspan.)
+ *
+ * When indicating a Span that's only read from, const goes inside the type
+ * parameter. Don't put const in front of Span. That is:
+ * size_t ReadsFromOneSpanAndWritesToAnother(Span<const uint8_t> aReadFrom,
+ *                                           Span<uint8_t> aWrittenTo);
+ *
+ * Any Span<const T> can be viewed as Span<const uint8_t> using the function
+ * AsBytes(). Any Span<T> can be viewed as Span<uint8_t> using the function
+ * AsWritableBytes().
+ */
+template<class ElementType, size_t Extent>
+class Span
+{
+public:
+  // constants and types
+  using element_type = ElementType;
+  using index_type = size_t;
+  using pointer = element_type*;
+  using reference = element_type&;
+
+  using iterator =
+    span_details::span_iterator<Span<ElementType, Extent>, false>;
+  using const_iterator =
+    span_details::span_iterator<Span<ElementType, Extent>, true>;
+  using reverse_iterator = std::reverse_iterator<iterator>;
+  using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+
+  constexpr static const index_type extent = Extent;
+
+  // [Span.cons], Span constructors, copy, assignment, and destructor
+  // "Dependent" is needed to make "span_details::enable_if_t<(Dependent || Extent == 0 || Extent == mozilla::MaxValue<size_t>::value)>" SFINAE,
+  // since "span_details::enable_if_t<(Extent == 0 || Extent == mozilla::MaxValue<size_t>::value)>" is ill-formed when Extent is neither of the extreme values.
+  /**
+   * Constructor with no args.
+   */
+  template<
+    bool Dependent = false,
+    class = span_details::enable_if_t<
+      (Dependent || Extent == 0 || Extent == mozilla::MaxValue<size_t>::value)>>
+  constexpr Span()
+    : storage_(nullptr, span_details::extent_type<0>())
+  {
+  }
+
+  /**
+   * Constructor for nullptr.
+   */
+  constexpr MOZ_IMPLICIT Span(std::nullptr_t) : Span() {}
+
+  /**
+   * Constructor for pointer and length.
+   */
+  constexpr Span(pointer aPtr, index_type aLength)
+    : storage_(aPtr, aLength)
+  {
+  }
+
+  /**
+   * Constructor for start pointer and pointer past end.
+   */
+  constexpr Span(pointer aStartPtr, pointer aEndPtr)
+    : storage_(aStartPtr, std::distance(aStartPtr, aEndPtr))
+  {
+  }
+
+  /**
+   * Constructor for C array.
+   */
+  template<size_t N>
+  constexpr MOZ_IMPLICIT Span(element_type (&aArr)[N])
+    : storage_(&aArr[0], span_details::extent_type<N>())
+  {
+  }
+
+  /**
+   * Constructor for std::array.
+   */
+  template<size_t N,
+           class ArrayElementType = span_details::remove_const_t<element_type>>
+  constexpr MOZ_IMPLICIT Span(std::array<ArrayElementType, N>& aArr)
+    : storage_(&aArr[0], span_details::extent_type<N>())
+  {
+  }
+
+  /**
+   * Constructor for const std::array.
+   */
+  template<size_t N>
+  constexpr MOZ_IMPLICIT Span(
+    const std::array<span_details::remove_const_t<element_type>, N>& aArr)
+    : storage_(&aArr[0], span_details::extent_type<N>())
+  {
+  }
+
+  /**
+   * Constructor for mozilla::Array.
+   */
+  template<size_t N,
+           class ArrayElementType = span_details::remove_const_t<element_type>>
+  constexpr MOZ_IMPLICIT Span(mozilla::Array<ArrayElementType, N>& aArr)
+    : storage_(&aArr[0], span_details::extent_type<N>())
+  {
+  }
+
+  /**
+   * Constructor for const mozilla::Array.
+   */
+  template<size_t N>
+  constexpr MOZ_IMPLICIT Span(
+    const mozilla::Array<span_details::remove_const_t<element_type>, N>& aArr)
+    : storage_(&aArr[0], span_details::extent_type<N>())
+  {
+  }
+
+  /**
+   * Constructor for mozilla::UniquePtr holding an array and length.
+   */
+  template<class ArrayElementType = std::add_pointer<element_type>>
+  constexpr Span(const mozilla::UniquePtr<ArrayElementType>& aPtr,
+                 index_type aLength)
+    : storage_(aPtr.get(), aLength)
+  {
+  }
+
+  // NB: the SFINAE here uses .data() as a incomplete/imperfect proxy for the requirement
+  // on Container to be a contiguous sequence container.
+  /**
+   * Constructor for standard-library containers.
+   */
+  template<
+    class Container,
+    class = span_details::enable_if_t<
+      !span_details::is_span<Container>::value &&
+      !span_details::is_std_array<Container>::value &&
+      mozilla::IsConvertible<typename Container::pointer, pointer>::value &&
+      mozilla::IsConvertible<typename Container::pointer,
+                          decltype(mozilla::DeclVal<Container>().data())>::value>>
+  constexpr MOZ_IMPLICIT Span(Container& cont)
+    : Span(cont.data(), ReleaseAssertedCast<index_type>(cont.size()))
+  {
+  }
+
+  /**
+   * Constructor for standard-library containers (const version).
+   */
+  template<
+    class Container,
+    class = span_details::enable_if_t<
+      mozilla::IsConst<element_type>::value &&
+      !span_details::is_span<Container>::value &&
+      mozilla::IsConvertible<typename Container::pointer, pointer>::value &&
+      mozilla::IsConvertible<typename Container::pointer,
+                          decltype(mozilla::DeclVal<Container>().data())>::value>>
+  constexpr MOZ_IMPLICIT Span(const Container& cont)
+    : Span(cont.data(), ReleaseAssertedCast<index_type>(cont.size()))
+  {
+  }
+
+  /**
+   * Constructor from other Span.
+   */
+  constexpr Span(const Span& other) = default;
+
+  /**
+   * Constructor from other Span.
+   */
+  constexpr Span(Span&& other) = default;
+
+  /**
+   * Constructor from other Span with conversion of element type.
+   */
+  template<
+    class OtherElementType,
+    size_t OtherExtent,
+    class = span_details::enable_if_t<
+      span_details::is_allowed_extent_conversion<OtherExtent, Extent>::value &&
+      span_details::is_allowed_element_type_conversion<OtherElementType,
+                                                       element_type>::value>>
+  constexpr MOZ_IMPLICIT Span(const Span<OtherElementType, OtherExtent>& other)
+    : storage_(other.data(),
+               span_details::extent_type<OtherExtent>(other.size()))
+  {
+  }
+
+  /**
+   * Constructor from other Span with conversion of element type.
+   */
+  template<
+    class OtherElementType,
+    size_t OtherExtent,
+    class = span_details::enable_if_t<
+      span_details::is_allowed_extent_conversion<OtherExtent, Extent>::value &&
+      span_details::is_allowed_element_type_conversion<OtherElementType,
+                                                       element_type>::value>>
+  constexpr MOZ_IMPLICIT Span(Span<OtherElementType, OtherExtent>&& other)
+    : storage_(other.data(),
+               span_details::extent_type<OtherExtent>(other.size()))
+  {
+  }
+
+  ~Span() = default;
+  MOZ_SPAN_EXPLICITLY_DEFAULTED_CONSTEXPR Span& operator=(const Span& other)
+    = default;
+
+  MOZ_SPAN_EXPLICITLY_DEFAULTED_CONSTEXPR Span& operator=(Span&& other)
+    = default;
+
+  // [Span.sub], Span subviews
+  /**
+   * Subspan with first N elements with compile-time N.
+   */
+  template<size_t Count>
+  MOZ_SPAN_GCC_CONSTEXPR Span<element_type, Count> First() const
+  {
+    MOZ_RELEASE_ASSERT(Count <= size());
+    return { data(), Count };
+  }
+
+  /**
+   * Subspan with last N elements with compile-time N.
+   */
+  template<size_t Count>
+  MOZ_SPAN_GCC_CONSTEXPR Span<element_type, Count> Last() const
+  {
+    MOZ_RELEASE_ASSERT(Count <= size());
+    return { data() + (size() - Count), Count };
+  }
+
+  /**
+   * Subspan with compile-time start index and length.
+   */
+  template<size_t Offset, size_t Count = dynamic_extent>
+  MOZ_SPAN_GCC_CONSTEXPR Span<element_type, Count> Subspan() const
+  {
+    MOZ_RELEASE_ASSERT(Offset <= size() &&
+      (Count == dynamic_extent || (Offset + Count <= size())));
+    return { data() + Offset,
+             Count == dynamic_extent ? size() - Offset : Count };
+  }
+
+  /**
+   * Subspan with first N elements with run-time N.
+   */
+  MOZ_SPAN_GCC_CONSTEXPR Span<element_type, dynamic_extent> First(
+    index_type aCount) const
+  {
+    MOZ_RELEASE_ASSERT(aCount <= size());
+    return { data(), aCount };
+  }
+
+  /**
+   * Subspan with last N elements with run-time N.
+   */
+  MOZ_SPAN_GCC_CONSTEXPR Span<element_type, dynamic_extent> Last(
+    index_type aCount) const
+  {
+    MOZ_RELEASE_ASSERT(aCount <= size());
+    return { data() + (size() - aCount), aCount };
+  }
+
+  /**
+   * Subspan with run-time start index and length.
+   */
+  MOZ_SPAN_GCC_CONSTEXPR Span<element_type, dynamic_extent> Subspan(
+    index_type aStart,
+    index_type aLength = dynamic_extent) const
+  {
+    MOZ_RELEASE_ASSERT(aStart <= size() &&
+                       (aLength == dynamic_extent ||
+                        (aStart + aLength <= size())));
+    return { data() + aStart,
+             aLength == dynamic_extent ? size() - aStart : aLength };
+  }
+
+  /**
+   * Subspan with run-time start index. (Rust's &foo[start..])
+   */
+  MOZ_SPAN_GCC_CONSTEXPR Span<element_type, dynamic_extent> From(
+    index_type aStart) const
+  {
+    return Subspan(aStart);
+  }
+
+  /**
+   * Subspan with run-time exclusive end index. (Rust's &foo[..end])
+   */
+  MOZ_SPAN_GCC_CONSTEXPR Span<element_type, dynamic_extent> To(
+    index_type aEnd) const
+  {
+    return Subspan(0, aEnd);
+  }
+
+  /**
+   * Subspan with run-time start index and exclusive end index.
+   * (Rust's &foo[start..end])
+   */
+  MOZ_SPAN_GCC_CONSTEXPR Span<element_type, dynamic_extent> FromTo(
+    index_type aStart,
+    index_type aEnd) const
+  {
+    MOZ_RELEASE_ASSERT(aStart <= aEnd);
+    return Subspan(aStart, aEnd - aStart);
+  }
+
+  // [Span.obs], Span observers
+  /**
+   * Number of elements in the span.
+   */
+  constexpr index_type Length() const { return size(); }
+
+  /**
+   * Number of elements in the span (standard-libray duck typing version).
+   */
+  constexpr index_type size() const { return storage_.size(); }
+
+  /**
+   * Size of the span in bytes.
+   */
+  constexpr index_type LengthBytes() const { return size_bytes(); }
+
+  /**
+   * Size of the span in bytes (standard-library naming style version).
+   */
+  constexpr index_type size_bytes() const
+  {
+    return size() * narrow_cast<index_type>(sizeof(element_type));
+  }
+
+  /**
+   * Checks if the the length of the span is zero.
+   */
+  constexpr bool IsEmpty() const { return empty(); }
+
+  /**
+   * Checks if the the length of the span is zero (standard-libray duck
+   * typing version).
+   */
+  constexpr bool empty() const { return size() == 0; }
+
+  // [Span.elem], Span element access
+  MOZ_SPAN_GCC_CONSTEXPR reference operator[](index_type idx) const
+  {
+    MOZ_RELEASE_ASSERT(idx < storage_.size());
+    return data()[idx];
+  }
+
+  /**
+   * Access element of span by index (standard-library duck typing version).
+   */
+  constexpr reference at(index_type idx) const { return this->operator[](idx); }
+
+  constexpr reference operator()(index_type idx) const
+  {
+    return this->operator[](idx);
+  }
+
+  /**
+   * Pointer to the first element of the span.
+   */
+  constexpr pointer Elements() const { return data(); }
+
+  /**
+   * Pointer to the first element of the span (standard-libray duck typing version).
+   */
+  constexpr pointer data() const { return storage_.data(); }
+
+  // [Span.iter], Span iterator support
+  iterator begin() const { return { this, 0 }; }
+  iterator end() const { return { this, Length() }; }
+
+  const_iterator cbegin() const { return { this, 0 }; }
+  const_iterator cend() const { return { this, Length() }; }
+
+  reverse_iterator rbegin() const
+  {
+    return reverse_iterator{ end() };
+  }
+  reverse_iterator rend() const
+  {
+    return reverse_iterator{ begin() };
+  }
+
+  const_reverse_iterator crbegin() const
+  {
+    return const_reverse_iterator{ cend() };
+  }
+  const_reverse_iterator crend() const
+  {
+    return const_reverse_iterator{ cbegin() };
+  }
+
+private:
+  // this implementation detail class lets us take advantage of the
+  // empty base class optimization to pay for only storage of a single
+  // pointer in the case of fixed-size Spans
+  template<class ExtentType>
+  class storage_type : public ExtentType
+  {
+  public:
+    template<class OtherExtentType>
+    MOZ_SPAN_ASSERTION_CONSTEXPR storage_type(pointer elements,
+                                              OtherExtentType ext)
+      : ExtentType(ext)
+      , data_(elements)
+    {
+      MOZ_RELEASE_ASSERT(
+        (!elements && ExtentType::size() == 0) ||
+        (elements && ExtentType::size() != mozilla::MaxValue<size_t>::value));
+    }
+
+    constexpr pointer data() const { return data_; }
+
+  private:
+    pointer data_;
+  };
+
+  storage_type<span_details::extent_type<Extent>> storage_;
+};
+
+// [Span.comparison], Span comparison operators
+template<class ElementType, size_t FirstExtent, size_t SecondExtent>
+inline constexpr bool
+operator==(const Span<ElementType, FirstExtent>& l,
+           const Span<ElementType, SecondExtent>& r)
+{
+  return (l.size() == r.size()) && std::equal(l.begin(), l.end(), r.begin());
+}
+
+template<class ElementType, size_t Extent>
+inline constexpr bool
+operator!=(const Span<ElementType, Extent>& l,
+           const Span<ElementType, Extent>& r)
+{
+  return !(l == r);
+}
+
+template<class ElementType, size_t Extent>
+inline constexpr bool
+operator<(const Span<ElementType, Extent>& l,
+          const Span<ElementType, Extent>& r)
+{
+  return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end());
+}
+
+template<class ElementType, size_t Extent>
+inline constexpr bool
+operator<=(const Span<ElementType, Extent>& l,
+           const Span<ElementType, Extent>& r)
+{
+  return !(l > r);
+}
+
+template<class ElementType, size_t Extent>
+inline constexpr bool
+operator>(const Span<ElementType, Extent>& l,
+          const Span<ElementType, Extent>& r)
+{
+  return r < l;
+}
+
+template<class ElementType, size_t Extent>
+inline constexpr bool
+operator>=(const Span<ElementType, Extent>& l,
+           const Span<ElementType, Extent>& r)
+{
+  return !(l < r);
+}
+
+namespace span_details {
+// if we only supported compilers with good constexpr support then
+// this pair of classes could collapse down to a constexpr function
+
+// we should use a narrow_cast<> to go to size_t, but older compilers may not see it as
+// constexpr
+// and so will fail compilation of the template
+template<class ElementType, size_t Extent>
+struct calculate_byte_size
+  : mozilla::IntegralConstant<size_t,
+                           static_cast<size_t>(sizeof(ElementType) *
+                                               static_cast<size_t>(Extent))>
+{
+};
+
+template<class ElementType>
+struct calculate_byte_size<ElementType, dynamic_extent>
+  : mozilla::IntegralConstant<size_t, dynamic_extent>
+{
+};
+}
+
+// [Span.objectrep], views of object representation
+/**
+ * View span as Span<const uint8_t>.
+ */
+template<class ElementType, size_t Extent>
+Span<const uint8_t,
+     span_details::calculate_byte_size<ElementType, Extent>::value>
+AsBytes(Span<ElementType, Extent> s)
+{
+  return { reinterpret_cast<const uint8_t*>(s.data()), s.size_bytes() };
+}
+
+/**
+ * View span as Span<uint8_t>.
+ */
+template<class ElementType,
+         size_t Extent,
+         class = span_details::enable_if_t<!mozilla::IsConst<ElementType>::value>>
+Span<uint8_t, span_details::calculate_byte_size<ElementType, Extent>::value>
+AsWritableBytes(Span<ElementType, Extent> s)
+{
+  return { reinterpret_cast<uint8_t*>(s.data()), s.size_bytes() };
+}
+
+//
+// MakeSpan() - Utility functions for creating Spans
+//
+/**
+ * Create span from pointer and length.
+ */
+template<class ElementType>
+Span<ElementType>
+MakeSpan(ElementType* aPtr, typename Span<ElementType>::index_type aLength)
+{
+  return Span<ElementType>(aPtr, aLength);
+}
+
+/**
+ * Create span from start pointer and pointer past end.
+ */
+template<class ElementType>
+Span<ElementType>
+MakeSpan(ElementType* aStartPtr, ElementType* aEndPtr)
+{
+  return Span<ElementType>(aStartPtr, aEndPtr);
+}
+
+/**
+ * Create span from C array.
+ */
+template<class ElementType, size_t N>
+Span<ElementType> MakeSpan(ElementType (&aArr)[N])
+{
+  return Span<ElementType>(aArr);
+}
+
+/**
+ * Create span from mozilla::Array.
+ */
+template<class ElementType, size_t N>
+Span<ElementType>
+MakeSpan(mozilla::Array<ElementType, N>& aArr)
+{
+  return aArr;
+}
+
+/**
+ * Create span from const mozilla::Array.
+ */
+template<class ElementType, size_t N>
+Span<const ElementType>
+MakeSpan(const mozilla::Array<ElementType, N>& arr)
+{
+  return arr;
+}
+
+/**
+ * Create span from standard-library container.
+ */
+template<class Container>
+Span<typename Container::value_type>
+MakeSpan(Container& cont)
+{
+  return Span<typename Container::value_type>(cont);
+}
+
+/**
+ * Create span from standard-library container (const version).
+ */
+template<class Container>
+Span<const typename Container::value_type>
+MakeSpan(const Container& cont)
+{
+  return Span<const typename Container::value_type>(cont);
+}
+
+/**
+ * Create span from smart pointer and length.
+ */
+template<class Ptr>
+Span<typename Ptr::element_type>
+MakeSpan(Ptr& aPtr, size_t aLength)
+{
+  return Span<typename Ptr::element_type>(aPtr, aLength);
+}
+
+/**
+ * Create span from C string.
+ */
+inline Span<const char>
+MakeCStringSpan(const char* aStr)
+{
+  return Span<const char>(aStr, std::strlen(aStr));
+}
+
+} // namespace mozilla
+
+#ifdef _MSC_VER
+#if _MSC_VER < 1910
+#undef constexpr
+#pragma pop_macro("constexpr")
+
+#endif // _MSC_VER < 1910
+
+#pragma warning(pop)
+#endif // _MSC_VER
+
+#undef MOZ_SPAN_ASSERTION_CONSTEXPR
+#undef MOZ_SPAN_GCC_CONSTEXPR
+#undef MOZ_SPAN_EXPLICITLY_DEFAULTED_CONSTEXPR
+#undef MOZ_SPAN_CONSTEXPR_NOT_JUST_RETURN
+#undef MOZ_SPAN_NON_CONST_CONSTEXPR
+
+#endif // mozilla_Span_h
--- a/mfbt/moz.build
+++ b/mfbt/moz.build
@@ -77,16 +77,17 @@ EXPORTS.mozilla = [
     'ReverseIterator.h',
     'RollingMean.h',
     'Saturate.h',
     'Scoped.h',
     'ScopeExit.h',
     'SegmentedVector.h',
     'SHA1.h',
     'SizePrintfMacros.h',
+    'Span.h',
     'SplayTree.h',
     'Sprintf.h',
     'StaticAnalysisFunctions.h',
     'TaggedAnonymousMemory.h',
     'TemplateLib.h',
     'ThreadLocal.h',
     'ToString.h',
     'Tuple.h',
new file mode 100644
--- /dev/null
+++ b/mfbt/tests/gtest/TestSpan.cpp
@@ -0,0 +1,2079 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2015 Microsoft Corporation. All rights reserved.
+//
+// This code is licensed under the MIT License (MIT).
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+// Adapted from https://github.com/Microsoft/GSL/blob/3819df6e378ffccf0e29465afe99c3b324c2aa70/tests/Span_tests.cpp
+
+#include "gtest/gtest.h"
+
+#include "mozilla/Span.h"
+
+#include "nsString.h"
+#include "nsTArray.h"
+#include "mozilla/Range.h"
+#include "mozilla/TypeTraits.h"
+
+#define SPAN_TEST(name) TEST(SpanTest, name)
+#define CHECK_THROW(a, b)
+
+using namespace std;
+using namespace mozilla;
+
+static_assert(IsConvertible<Range<int>, Span<const int>>::value,
+              "Range should convert into const");
+static_assert(IsConvertible<Range<const int>, Span<const int>>::value,
+              "const Range should convert into const");
+static_assert(!IsConvertible<Range<const int>, Span<int>>::value,
+              "Range should not drop const in conversion");
+static_assert(IsConvertible<Span<int>, Range<const int>>::value,
+              "Span should convert into const");
+static_assert(IsConvertible<Span<const int>, Range<const int>>::value,
+              "const Span should convert into const");
+static_assert(!IsConvertible<Span<const int>, Range<int>>::value,
+              "Span should not drop const in conversion");
+static_assert(IsConvertible<Span<const int>, Span<const int>>::value,
+              "const Span should convert into const");
+static_assert(IsConvertible<Span<int>, Span<const int>>::value,
+              "Span should convert into const");
+static_assert(!IsConvertible<Span<const int>, Span<int>>::value,
+              "Span should not drop const in conversion");
+static_assert(IsConvertible<const nsTArray<int>, Span<const int>>::value,
+              "const nsTArray should convert into const");
+static_assert(IsConvertible<nsTArray<int>, Span<const int>>::value,
+              "nsTArray should convert into const");
+static_assert(!IsConvertible<const nsTArray<int>, Span<int>>::value,
+              "nsTArray should not drop const in conversion");
+static_assert(IsConvertible<nsTArray<const int>, Span<const int>>::value,
+              "nsTArray should convert into const");
+static_assert(!IsConvertible<nsTArray<const int>, Span<int>>::value,
+              "nsTArray should not drop const in conversion");
+
+namespace {
+struct BaseClass
+{
+};
+struct DerivedClass : BaseClass
+{
+};
+}
+
+void
+AssertSpanOfThreeInts(Span<const int> s)
+{
+  ASSERT_EQ(s.size(), 3U);
+  ASSERT_EQ(s[0], 1);
+  ASSERT_EQ(s[1], 2);
+  ASSERT_EQ(s[2], 3);
+}
+
+void
+AssertSpanOfThreeChars(Span<const char> s)
+{
+  ASSERT_EQ(s.size(), 3U);
+  ASSERT_EQ(s[0], 'a');
+  ASSERT_EQ(s[1], 'b');
+  ASSERT_EQ(s[2], 'c');
+}
+
+void
+AssertSpanOfThreeChar16s(Span<const char16_t> s)
+{
+  ASSERT_EQ(s.size(), 3U);
+  ASSERT_EQ(s[0], 'a');
+  ASSERT_EQ(s[1], 'b');
+  ASSERT_EQ(s[2], 'c');
+}
+
+void
+AssertSpanOfThreeCharsViaString(const nsACString& aStr)
+{
+  AssertSpanOfThreeChars(aStr);
+}
+
+void
+AssertSpanOfThreeChar16sViaString(const nsAString& aStr)
+{
+  AssertSpanOfThreeChar16s(aStr);
+}
+
+SPAN_TEST(default_constructor)
+{
+  {
+    Span<int> s;
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), nullptr);
+
+    Span<const int> cs;
+    ASSERT_EQ(cs.Length(), 0U);
+    ASSERT_EQ(cs.data(), nullptr);
+  }
+
+  {
+    Span<int, 0> s;
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), nullptr);
+
+    Span<const int, 0> cs;
+    ASSERT_EQ(cs.Length(), 0U);
+    ASSERT_EQ(cs.data(), nullptr);
+  }
+
+  {
+#ifdef CONFIRM_COMPILATION_ERRORS
+    Span<int, 1> s;
+    ASSERT_EQ(s.Length(), 1U);
+    ASSERT_EQ(s.data(), nullptr); // explains why it can't compile
+#endif
+  }
+
+  {
+    Span<int> s{};
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), nullptr);
+
+    Span<const int> cs{};
+    ASSERT_EQ(cs.Length(), 0U);
+    ASSERT_EQ(cs.data(), nullptr);
+  }
+}
+
+SPAN_TEST(size_optimization)
+{
+  {
+    Span<int> s;
+    ASSERT_EQ(sizeof(s), sizeof(int*) + sizeof(size_t));
+  }
+
+  {
+    Span<int, 0> s;
+    ASSERT_EQ(sizeof(s), sizeof(int*));
+  }
+}
+
+SPAN_TEST(from_nullptr_constructor)
+{
+  {
+    Span<int> s = nullptr;
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), nullptr);
+
+    Span<const int> cs = nullptr;
+    ASSERT_EQ(cs.Length(), 0U);
+    ASSERT_EQ(cs.data(), nullptr);
+  }
+
+  {
+    Span<int, 0> s = nullptr;
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), nullptr);
+
+    Span<const int, 0> cs = nullptr;
+    ASSERT_EQ(cs.Length(), 0U);
+    ASSERT_EQ(cs.data(), nullptr);
+  }
+
+  {
+#ifdef CONFIRM_COMPILATION_ERRORS
+    Span<int, 1> s = nullptr;
+    ASSERT_EQ(s.Length(), 1U);
+    ASSERT_EQ(s.data(), nullptr); // explains why it can't compile
+#endif
+  }
+
+  {
+    Span<int> s{ nullptr };
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), nullptr);
+
+    Span<const int> cs{ nullptr };
+    ASSERT_EQ(cs.Length(), 0U);
+    ASSERT_EQ(cs.data(), nullptr);
+  }
+
+  {
+    Span<int*> s{ nullptr };
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), nullptr);
+
+    Span<const int*> cs{ nullptr };
+    ASSERT_EQ(cs.Length(), 0U);
+    ASSERT_EQ(cs.data(), nullptr);
+  }
+}
+
+SPAN_TEST(from_nullptr_length_constructor)
+{
+  {
+    Span<int> s{ nullptr, static_cast<Span<int>::index_type>(0) };
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), nullptr);
+
+    Span<const int> cs{ nullptr, static_cast<Span<int>::index_type>(0) };
+    ASSERT_EQ(cs.Length(), 0U);
+    ASSERT_EQ(cs.data(), nullptr);
+  }
+
+  {
+    Span<int, 0> s{ nullptr, static_cast<Span<int>::index_type>(0) };
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), nullptr);
+
+    Span<const int, 0> cs{ nullptr, static_cast<Span<int>::index_type>(0) };
+    ASSERT_EQ(cs.Length(), 0U);
+    ASSERT_EQ(cs.data(), nullptr);
+  }
+
+#if 0
+        {
+            auto workaround_macro = []() { Span<int, 1> s{ nullptr, static_cast<Span<int>::index_type>(0) }; };
+            CHECK_THROW(workaround_macro(), fail_fast);
+        }
+
+        {
+            auto workaround_macro = []() { Span<int> s{nullptr, 1}; };
+            CHECK_THROW(workaround_macro(), fail_fast);
+
+            auto const_workaround_macro = []() { Span<const int> cs{nullptr, 1}; };
+            CHECK_THROW(const_workaround_macro(), fail_fast);
+        }
+
+        {
+            auto workaround_macro = []() { Span<int, 0> s{nullptr, 1}; };
+            CHECK_THROW(workaround_macro(), fail_fast);
+
+            auto const_workaround_macro = []() { Span<const int, 0> s{nullptr, 1}; };
+            CHECK_THROW(const_workaround_macro(), fail_fast);
+        }
+#endif
+  {
+    Span<int*> s{ nullptr, static_cast<Span<int>::index_type>(0) };
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), nullptr);
+
+    Span<const int*> cs{ nullptr, static_cast<Span<int>::index_type>(0) };
+    ASSERT_EQ(cs.Length(), 0U);
+    ASSERT_EQ(cs.data(), nullptr);
+  }
+}
+
+SPAN_TEST(from_pointer_length_constructor)
+{
+  int arr[4] = { 1, 2, 3, 4 };
+
+  {
+    Span<int> s{ &arr[0], 2 };
+    ASSERT_EQ(s.Length(), 2U);
+    ASSERT_EQ(s.data(), &arr[0]);
+    ASSERT_EQ(s[0], 1);
+    ASSERT_EQ(s[1], 2);
+  }
+
+  {
+    Span<int, 2> s{ &arr[0], 2 };
+    ASSERT_EQ(s.Length(), 2U);
+    ASSERT_EQ(s.data(), &arr[0]);
+    ASSERT_EQ(s[0], 1);
+    ASSERT_EQ(s[1], 2);
+  }
+
+  {
+    int* p = nullptr;
+    Span<int> s{ p, static_cast<Span<int>::index_type>(0) };
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), nullptr);
+  }
+
+#if 0
+        {
+            int* p = nullptr;
+            auto workaround_macro = [=]() { Span<int> s{p, 2}; };
+            CHECK_THROW(workaround_macro(), fail_fast);
+        }
+#endif
+
+  {
+    auto s = MakeSpan(&arr[0], 2);
+    ASSERT_EQ(s.Length(), 2U);
+    ASSERT_EQ(s.data(), &arr[0]);
+    ASSERT_EQ(s[0], 1);
+    ASSERT_EQ(s[1], 2);
+  }
+
+  {
+    int* p = nullptr;
+    auto s = MakeSpan(p, static_cast<Span<int>::index_type>(0));
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), nullptr);
+  }
+
+#if 0
+        {
+            int* p = nullptr;
+            auto workaround_macro = [=]() { MakeSpan(p, 2); };
+            CHECK_THROW(workaround_macro(), fail_fast);
+        }
+#endif
+}
+
+SPAN_TEST(from_pointer_pointer_constructor)
+{
+  int arr[4] = { 1, 2, 3, 4 };
+
+  {
+    Span<int> s{ &arr[0], &arr[2] };
+    ASSERT_EQ(s.Length(), 2U);
+    ASSERT_EQ(s.data(), &arr[0]);
+    ASSERT_EQ(s[0], 1);
+    ASSERT_EQ(s[1], 2);
+  }
+
+  {
+    Span<int, 2> s{ &arr[0], &arr[2] };
+    ASSERT_EQ(s.Length(), 2U);
+    ASSERT_EQ(s.data(), &arr[0]);
+    ASSERT_EQ(s[0], 1);
+    ASSERT_EQ(s[1], 2);
+  }
+
+  {
+    Span<int> s{ &arr[0], &arr[0] };
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), &arr[0]);
+  }
+
+  {
+    Span<int, 0> s{ &arr[0], &arr[0] };
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), &arr[0]);
+  }
+
+  // this will fail the std::distance() precondition, which asserts on MSVC debug builds
+  //{
+  //    auto workaround_macro = [&]() { Span<int> s{&arr[1], &arr[0]}; };
+  //    CHECK_THROW(workaround_macro(), fail_fast);
+  //}
+
+  // this will fail the std::distance() precondition, which asserts on MSVC debug builds
+  //{
+  //    int* p = nullptr;
+  //    auto workaround_macro = [&]() { Span<int> s{&arr[0], p}; };
+  //    CHECK_THROW(workaround_macro(), fail_fast);
+  //}
+
+  {
+    int* p = nullptr;
+    Span<int> s{ p, p };
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), nullptr);
+  }
+
+  {
+    int* p = nullptr;
+    Span<int, 0> s{ p, p };
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), nullptr);
+  }
+
+  // this will fail the std::distance() precondition, which asserts on MSVC debug builds
+  //{
+  //    int* p = nullptr;
+  //    auto workaround_macro = [&]() { Span<int> s{&arr[0], p}; };
+  //    CHECK_THROW(workaround_macro(), fail_fast);
+  //}
+
+  {
+    auto s = MakeSpan(&arr[0], &arr[2]);
+    ASSERT_EQ(s.Length(), 2U);
+    ASSERT_EQ(s.data(), &arr[0]);
+    ASSERT_EQ(s[0], 1);
+    ASSERT_EQ(s[1], 2);
+  }
+
+  {
+    auto s = MakeSpan(&arr[0], &arr[0]);
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), &arr[0]);
+  }
+
+  {
+    int* p = nullptr;
+    auto s = MakeSpan(p, p);
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), nullptr);
+  }
+}
+
+SPAN_TEST(from_array_constructor)
+{
+  int arr[5] = { 1, 2, 3, 4, 5 };
+
+  {
+    Span<int> s{ arr };
+    ASSERT_EQ(s.Length(), 5U);
+    ASSERT_EQ(s.data(), &arr[0]);
+  }
+
+  {
+    Span<int, 5> s{ arr };
+    ASSERT_EQ(s.Length(), 5U);
+    ASSERT_EQ(s.data(), &arr[0]);
+  }
+
+  int arr2d[2][3] = { { 1, 2, 3 }, { 4, 5, 6 } };
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+  {
+    Span<int, 6> s{ arr };
+  }
+
+  {
+    Span<int, 0> s{ arr };
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), &arr[0]);
+  }
+
+  {
+    Span<int> s{ arr2d };
+    ASSERT_EQ(s.Length(), 6U);
+    ASSERT_EQ(s.data(), &arr2d[0][0]);
+    ASSERT_EQ(s[0], 1);
+    ASSERT_EQ(s[5], 6);
+  }
+
+  {
+    Span<int, 0> s{ arr2d };
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), &arr2d[0][0]);
+  }
+
+  {
+    Span<int, 6> s{ arr2d };
+  }
+#endif
+  {
+    Span<int[3]> s{ &(arr2d[0]), 1 };
+    ASSERT_EQ(s.Length(), 1U);
+    ASSERT_EQ(s.data(), &arr2d[0]);
+  }
+
+  int arr3d[2][3][2] = { { { 1, 2 }, { 3, 4 }, { 5, 6 } },
+                         { { 7, 8 }, { 9, 10 }, { 11, 12 } } };
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+  {
+    Span<int> s{ arr3d };
+    ASSERT_EQ(s.Length(), 12U);
+    ASSERT_EQ(s.data(), &arr3d[0][0][0]);
+    ASSERT_EQ(s[0], 1);
+    ASSERT_EQ(s[11], 12);
+  }
+
+  {
+    Span<int, 0> s{ arr3d };
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), &arr3d[0][0][0]);
+  }
+
+  {
+    Span<int, 11> s{ arr3d };
+  }
+
+  {
+    Span<int, 12> s{ arr3d };
+    ASSERT_EQ(s.Length(), 12U);
+    ASSERT_EQ(s.data(), &arr3d[0][0][0]);
+    ASSERT_EQ(s[0], 1);
+    ASSERT_EQ(s[5], 6);
+  }
+#endif
+  {
+    Span<int[3][2]> s{ &arr3d[0], 1 };
+    ASSERT_EQ(s.Length(), 1U);
+    ASSERT_EQ(s.data(), &arr3d[0]);
+  }
+
+  {
+    auto s = MakeSpan(arr);
+    ASSERT_EQ(s.Length(), 5U);
+    ASSERT_EQ(s.data(), &arr[0]);
+  }
+
+  {
+    auto s = MakeSpan(&(arr2d[0]), 1);
+    ASSERT_EQ(s.Length(), 1U);
+    ASSERT_EQ(s.data(), &arr2d[0]);
+  }
+
+  {
+    auto s = MakeSpan(&arr3d[0], 1);
+    ASSERT_EQ(s.Length(), 1U);
+    ASSERT_EQ(s.data(), &arr3d[0]);
+  }
+}
+
+SPAN_TEST(from_dynamic_array_constructor)
+{
+  double(*arr)[3][4] = new double[100][3][4];
+
+  {
+    Span<double> s(&arr[0][0][0], 10);
+    ASSERT_EQ(s.Length(), 10U);
+    ASSERT_EQ(s.data(), &arr[0][0][0]);
+  }
+
+  {
+    auto s = MakeSpan(&arr[0][0][0], 10);
+    ASSERT_EQ(s.Length(), 10U);
+    ASSERT_EQ(s.data(), &arr[0][0][0]);
+  }
+
+  delete[] arr;
+}
+
+SPAN_TEST(from_std_array_constructor)
+{
+  std::array<int, 4> arr = { { 1, 2, 3, 4 } };
+
+  {
+    Span<int> s{ arr };
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.size()));
+    ASSERT_EQ(s.data(), arr.data());
+
+    Span<const int> cs{ arr };
+    ASSERT_EQ(cs.size(), narrow_cast<size_t>(arr.size()));
+    ASSERT_EQ(cs.data(), arr.data());
+  }
+
+  {
+    Span<int, 4> s{ arr };
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.size()));
+    ASSERT_EQ(s.data(), arr.data());
+
+    Span<const int, 4> cs{ arr };
+    ASSERT_EQ(cs.size(), narrow_cast<size_t>(arr.size()));
+    ASSERT_EQ(cs.data(), arr.data());
+  }
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+  {
+    Span<int, 2> s{ arr };
+    ASSERT_EQ(s.size(), 2U);
+    ASSERT_EQ(s.data(), arr.data());
+
+    Span<const int, 2> cs{ arr };
+    ASSERT_EQ(cs.size(), 2U);
+    ASSERT_EQ(cs.data(), arr.data());
+  }
+
+  {
+    Span<int, 0> s{ arr };
+    ASSERT_EQ(s.size(), 0U);
+    ASSERT_EQ(s.data(), arr.data());
+
+    Span<const int, 0> cs{ arr };
+    ASSERT_EQ(cs.size(), 0U);
+    ASSERT_EQ(cs.data(), arr.data());
+  }
+
+  {
+    Span<int, 5> s{ arr };
+  }
+
+  {
+    auto get_an_array = []() -> std::array<int, 4> { return { 1, 2, 3, 4 }; };
+    auto take_a_Span = [](Span<int> s) { static_cast<void>(s); };
+    // try to take a temporary std::array
+    take_a_Span(get_an_array());
+  }
+#endif
+
+  {
+    auto get_an_array = []() -> std::array<int, 4> {
+      return { { 1, 2, 3, 4 } };
+    };
+    auto take_a_Span = [](Span<const int> s) { static_cast<void>(s); };
+    // try to take a temporary std::array
+    take_a_Span(get_an_array());
+  }
+
+  {
+    auto s = MakeSpan(arr);
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.size()));
+    ASSERT_EQ(s.data(), arr.data());
+  }
+}
+
+SPAN_TEST(from_const_std_array_constructor)
+{
+  const std::array<int, 4> arr = { { 1, 2, 3, 4 } };
+
+  {
+    Span<const int> s{ arr };
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.size()));
+    ASSERT_EQ(s.data(), arr.data());
+  }
+
+  {
+    Span<const int, 4> s{ arr };
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.size()));
+    ASSERT_EQ(s.data(), arr.data());
+  }
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+  {
+    Span<const int, 2> s{ arr };
+    ASSERT_EQ(s.size(), 2U);
+    ASSERT_EQ(s.data(), arr.data());
+  }
+
+  {
+    Span<const int, 0> s{ arr };
+    ASSERT_EQ(s.size(), 0U);
+    ASSERT_EQ(s.data(), arr.data());
+  }
+
+  {
+    Span<const int, 5> s{ arr };
+  }
+#endif
+
+  {
+    auto get_an_array = []() -> const std::array<int, 4> {
+      return { { 1, 2, 3, 4 } };
+    };
+    auto take_a_Span = [](Span<const int> s) { static_cast<void>(s); };
+    // try to take a temporary std::array
+    take_a_Span(get_an_array());
+  }
+
+  {
+    auto s = MakeSpan(arr);
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.size()));
+    ASSERT_EQ(s.data(), arr.data());
+  }
+}
+
+SPAN_TEST(from_std_array_const_constructor)
+{
+  std::array<const int, 4> arr = { { 1, 2, 3, 4 } };
+
+  {
+    Span<const int> s{ arr };
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.size()));
+    ASSERT_EQ(s.data(), arr.data());
+  }
+
+  {
+    Span<const int, 4> s{ arr };
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.size()));
+    ASSERT_EQ(s.data(), arr.data());
+  }
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+  {
+    Span<const int, 2> s{ arr };
+    ASSERT_EQ(s.size(), 2U);
+    ASSERT_EQ(s.data(), arr.data());
+  }
+
+  {
+    Span<const int, 0> s{ arr };
+    ASSERT_EQ(s.size(), 0U);
+    ASSERT_EQ(s.data(), arr.data());
+  }
+
+  {
+    Span<const int, 5> s{ arr };
+  }
+
+  {
+    Span<int, 4> s{ arr };
+  }
+#endif
+
+  {
+    auto s = MakeSpan(arr);
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.size()));
+    ASSERT_EQ(s.data(), arr.data());
+  }
+}
+
+SPAN_TEST(from_mozilla_array_constructor)
+{
+  mozilla::Array<int, 4> arr(1, 2, 3, 4);
+
+  {
+    Span<int> s{ arr };
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.cend() - arr.cbegin()));
+    ASSERT_EQ(s.data(), &arr[0]);
+
+    Span<const int> cs{ arr };
+    ASSERT_EQ(cs.size(), narrow_cast<size_t>(arr.cend() - arr.cbegin()));
+    ASSERT_EQ(cs.data(), &arr[0]);
+  }
+
+  {
+    Span<int, 4> s{ arr };
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.cend() - arr.cbegin()));
+    ASSERT_EQ(s.data(), &arr[0]);
+
+    Span<const int, 4> cs{ arr };
+    ASSERT_EQ(cs.size(), narrow_cast<size_t>(arr.cend() - arr.cbegin()));
+    ASSERT_EQ(cs.data(), &arr[0]);
+  }
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+  {
+    Span<int, 2> s{ arr };
+    ASSERT_EQ(s.size(), 2U);
+    ASSERT_EQ(s.data(), &arr[0]);
+
+    Span<const int, 2> cs{ arr };
+    ASSERT_EQ(cs.size(), 2U);
+    ASSERT_EQ(cs.data(), &arr[0]);
+  }
+
+  {
+    Span<int, 0> s{ arr };
+    ASSERT_EQ(s.size(), 0U);
+    ASSERT_EQ(s.data(), &arr[0]);
+
+    Span<const int, 0> cs{ arr };
+    ASSERT_EQ(cs.size(), 0U);
+    ASSERT_EQ(cs.data(), &arr[0]);
+  }
+
+  {
+    Span<int, 5> s{ arr };
+  }
+
+  {
+    auto get_an_array = []() -> mozilla::Array<int, 4> {
+      return { 1, 2, 3, 4 };
+    };
+    auto take_a_Span = [](Span<int> s) { static_cast<void>(s); };
+    // try to take a temporary mozilla::Array
+    take_a_Span(get_an_array());
+  }
+#endif
+
+  {
+    auto get_an_array = []() -> mozilla::Array<int, 4> {
+      return { 1, 2, 3, 4 };
+    };
+    auto take_a_Span = [](Span<const int> s) { static_cast<void>(s); };
+    // try to take a temporary mozilla::Array
+    take_a_Span(get_an_array());
+  }
+
+  {
+    auto s = MakeSpan(arr);
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.cend() - arr.cbegin()));
+    ASSERT_EQ(s.data(), &arr[0]);
+  }
+}
+
+SPAN_TEST(from_const_mozilla_array_constructor)
+{
+  const mozilla::Array<int, 4> arr(1, 2, 3, 4);
+
+  {
+    Span<const int> s{ arr };
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.cend() - arr.cbegin()));
+    ASSERT_EQ(s.data(), &arr[0]);
+  }
+
+  {
+    Span<const int, 4> s{ arr };
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.cend() - arr.cbegin()));
+    ASSERT_EQ(s.data(), &arr[0]);
+  }
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+  {
+    Span<const int, 2> s{ arr };
+    ASSERT_EQ(s.size(), 2U);
+    ASSERT_EQ(s.data(), &arr[0]);
+  }
+
+  {
+    Span<const int, 0> s{ arr };
+    ASSERT_EQ(s.size(), 0U);
+    ASSERT_EQ(s.data(), &arr[0]);
+  }
+
+  {
+    Span<const int, 5> s{ arr };
+  }
+#endif
+
+#if 0
+  {
+    auto get_an_array = []() -> const mozilla::Array<int, 4> {
+      return { 1, 2, 3, 4 };
+    };
+    auto take_a_Span = [](Span<const int> s) { static_cast<void>(s); };
+    // try to take a temporary mozilla::Array
+    take_a_Span(get_an_array());
+  }
+#endif
+
+  {
+    auto s = MakeSpan(arr);
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.cend() - arr.cbegin()));
+    ASSERT_EQ(s.data(), &arr[0]);
+  }
+}
+
+SPAN_TEST(from_mozilla_array_const_constructor)
+{
+  mozilla::Array<const int, 4> arr(1, 2, 3, 4);
+
+  {
+    Span<const int> s{ arr };
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.cend() - arr.cbegin()));
+    ASSERT_EQ(s.data(), &arr[0]);
+  }
+
+  {
+    Span<const int, 4> s{ arr };
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.cend() - arr.cbegin()));
+    ASSERT_EQ(s.data(), &arr[0]);
+  }
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+  {
+    Span<const int, 2> s{ arr };
+    ASSERT_EQ(s.size(), 2U);
+    ASSERT_EQ(s.data(), &arr[0]);
+  }
+
+  {
+    Span<const int, 0> s{ arr };
+    ASSERT_EQ(s.size(), 0U);
+    ASSERT_EQ(s.data(), &arr[0]);
+  }
+
+  {
+    Span<const int, 5> s{ arr };
+  }
+
+  {
+    Span<int, 4> s{ arr };
+  }
+#endif
+
+  {
+    auto s = MakeSpan(arr);
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(arr.cend() - arr.cbegin()));
+    ASSERT_EQ(s.data(), &arr[0]);
+  }
+}
+
+SPAN_TEST(from_container_constructor)
+{
+  std::vector<int> v = { 1, 2, 3 };
+  const std::vector<int> cv = v;
+
+  {
+    AssertSpanOfThreeInts(v);
+
+    Span<int> s{ v };
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(v.size()));
+    ASSERT_EQ(s.data(), v.data());
+
+    Span<const int> cs{ v };
+    ASSERT_EQ(cs.size(), narrow_cast<size_t>(v.size()));
+    ASSERT_EQ(cs.data(), v.data());
+  }
+
+  std::string str = "hello";
+  const std::string cstr = "hello";
+
+  {
+#ifdef CONFIRM_COMPILATION_ERRORS
+    Span<char> s{ str };
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(str.size()));
+    ASSERT_EQ(s.data(), str.data());
+#endif
+    Span<const char> cs{ str };
+    ASSERT_EQ(cs.size(), narrow_cast<size_t>(str.size()));
+    ASSERT_EQ(cs.data(), str.data());
+  }
+
+  {
+#ifdef CONFIRM_COMPILATION_ERRORS
+    Span<char> s{ cstr };
+#endif
+    Span<const char> cs{ cstr };
+    ASSERT_EQ(cs.size(), narrow_cast<size_t>(cstr.size()));
+    ASSERT_EQ(cs.data(), cstr.data());
+  }
+
+  {
+#ifdef CONFIRM_COMPILATION_ERRORS
+    auto get_temp_vector = []() -> std::vector<int> { return {}; };
+    auto use_Span = [](Span<int> s) { static_cast<void>(s); };
+    use_Span(get_temp_vector());
+#endif
+  }
+
+  {
+    auto get_temp_vector = []() -> std::vector<int> { return {}; };
+    auto use_Span = [](Span<const int> s) { static_cast<void>(s); };
+    use_Span(get_temp_vector());
+  }
+
+  {
+#ifdef CONFIRM_COMPILATION_ERRORS
+    auto get_temp_string = []() -> std::string { return {}; };
+    auto use_Span = [](Span<char> s) { static_cast<void>(s); };
+    use_Span(get_temp_string());
+#endif
+  }
+
+  {
+    auto get_temp_string = []() -> std::string { return {}; };
+    auto use_Span = [](Span<const char> s) { static_cast<void>(s); };
+    use_Span(get_temp_string());
+  }
+
+  {
+#ifdef CONFIRM_COMPILATION_ERRORS
+    auto get_temp_vector = []() -> const std::vector<int> { return {}; };
+    auto use_Span = [](Span<const char> s) { static_cast<void>(s); };
+    use_Span(get_temp_vector());
+#endif
+  }
+
+  {
+    auto get_temp_string = []() -> const std::string { return {}; };
+    auto use_Span = [](Span<const char> s) { static_cast<void>(s); };
+    use_Span(get_temp_string());
+  }
+
+  {
+#ifdef CONFIRM_COMPILATION_ERRORS
+    std::map<int, int> m;
+    Span<int> s{ m };
+#endif
+  }
+
+  {
+    auto s = MakeSpan(v);
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(v.size()));
+    ASSERT_EQ(s.data(), v.data());
+
+    auto cs = MakeSpan(cv);
+    ASSERT_EQ(cs.size(), narrow_cast<size_t>(cv.size()));
+    ASSERT_EQ(cs.data(), cv.data());
+  }
+}
+
+SPAN_TEST(from_xpcom_collections)
+{
+  {
+    nsTArray<int> v;
+    v.AppendElement(1);
+    v.AppendElement(2);
+    v.AppendElement(3);
+
+    AssertSpanOfThreeInts(v);
+
+    Span<int> s{ v };
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(v.Length()));
+    ASSERT_EQ(s.data(), v.Elements());
+    ASSERT_EQ(s[2], 3);
+
+    Span<const int> cs{ v };
+    ASSERT_EQ(cs.size(), narrow_cast<size_t>(v.Length()));
+    ASSERT_EQ(cs.data(), v.Elements());
+    ASSERT_EQ(cs[2], 3);
+  }
+  {
+    nsTArray<int> v;
+    v.AppendElement(1);
+    v.AppendElement(2);
+    v.AppendElement(3);
+
+    AssertSpanOfThreeInts(v);
+
+    auto s = MakeSpan(v);
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(v.Length()));
+    ASSERT_EQ(s.data(), v.Elements());
+    ASSERT_EQ(s[2], 3);
+  }
+  {
+    AutoTArray<int, 5> v;
+    v.AppendElement(1);
+    v.AppendElement(2);
+    v.AppendElement(3);
+
+    AssertSpanOfThreeInts(v);
+
+    Span<int> s{ v };
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(v.Length()));
+    ASSERT_EQ(s.data(), v.Elements());
+    ASSERT_EQ(s[2], 3);
+
+    Span<const int> cs{ v };
+    ASSERT_EQ(cs.size(), narrow_cast<size_t>(v.Length()));
+    ASSERT_EQ(cs.data(), v.Elements());
+    ASSERT_EQ(cs[2], 3);
+  }
+  {
+    AutoTArray<int, 5> v;
+    v.AppendElement(1);
+    v.AppendElement(2);
+    v.AppendElement(3);
+
+    AssertSpanOfThreeInts(v);
+
+    auto s = MakeSpan(v);
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(v.Length()));
+    ASSERT_EQ(s.data(), v.Elements());
+    ASSERT_EQ(s[2], 3);
+  }
+  {
+    FallibleTArray<int> v;
+    *(v.AppendElement(fallible)) = 1;
+    *(v.AppendElement(fallible)) = 2;
+    *(v.AppendElement(fallible)) = 3;
+
+    AssertSpanOfThreeInts(v);
+
+    Span<int> s{ v };
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(v.Length()));
+    ASSERT_EQ(s.data(), v.Elements());
+    ASSERT_EQ(s[2], 3);
+
+    Span<const int> cs{ v };
+    ASSERT_EQ(cs.size(), narrow_cast<size_t>(v.Length()));
+    ASSERT_EQ(cs.data(), v.Elements());
+    ASSERT_EQ(cs[2], 3);
+  }
+  {
+    FallibleTArray<int> v;
+    *(v.AppendElement(fallible)) = 1;
+    *(v.AppendElement(fallible)) = 2;
+    *(v.AppendElement(fallible)) = 3;
+
+    AssertSpanOfThreeInts(v);
+
+    auto s = MakeSpan(v);
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(v.Length()));
+    ASSERT_EQ(s.data(), v.Elements());
+    ASSERT_EQ(s[2], 3);
+  }
+  {
+    nsAutoString str;
+    str.AssignLiteral("abc");
+
+    AssertSpanOfThreeChar16s(str);
+    AssertSpanOfThreeChar16sViaString(str);
+
+    Span<char16_t> s{ str };
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(str.Length()));
+    ASSERT_EQ(s.data(), str.BeginWriting());
+    ASSERT_EQ(s[2], 'c');
+
+    Span<const char16_t> cs{ str };
+    ASSERT_EQ(cs.size(), narrow_cast<size_t>(str.Length()));
+    ASSERT_EQ(cs.data(), str.BeginReading());
+    ASSERT_EQ(cs[2], 'c');
+  }
+  {
+    nsAutoString str;
+    str.AssignLiteral("abc");
+
+    AssertSpanOfThreeChar16s(str);
+    AssertSpanOfThreeChar16sViaString(str);
+
+    auto s = MakeSpan(str);
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(str.Length()));
+    ASSERT_EQ(s.data(), str.BeginWriting());
+    ASSERT_EQ(s[2], 'c');
+  }
+  {
+    nsAutoCString str;
+    str.AssignLiteral("abc");
+
+    AssertSpanOfThreeChars(str);
+    AssertSpanOfThreeCharsViaString(str);
+
+    Span<uint8_t> s{ str };
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(str.Length()));
+    ASSERT_EQ(s.data(), reinterpret_cast<uint8_t*>(str.BeginWriting()));
+    ASSERT_EQ(s[2], 'c');
+
+    Span<const uint8_t> cs{ str };
+    ASSERT_EQ(cs.size(), narrow_cast<size_t>(str.Length()));
+    ASSERT_EQ(cs.data(), reinterpret_cast<const uint8_t*>(str.BeginReading()));
+    ASSERT_EQ(cs[2], 'c');
+  }
+  {
+    nsAutoCString str;
+    str.AssignLiteral("abc");
+
+    AssertSpanOfThreeChars(str);
+    AssertSpanOfThreeCharsViaString(str);
+
+    auto s = MakeSpan(str);
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(str.Length()));
+    ASSERT_EQ(s.data(), str.BeginWriting());
+    ASSERT_EQ(s[2], 'c');
+  }
+  {
+    nsTArray<int> v;
+    v.AppendElement(1);
+    v.AppendElement(2);
+    v.AppendElement(3);
+
+    Range<int> r(v.Elements(), v.Length());
+
+    AssertSpanOfThreeInts(r);
+
+    Span<int> s{ r };
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(v.Length()));
+    ASSERT_EQ(s.data(), v.Elements());
+    ASSERT_EQ(s[2], 3);
+
+    Span<const int> cs{ r };
+    ASSERT_EQ(cs.size(), narrow_cast<size_t>(v.Length()));
+    ASSERT_EQ(cs.data(), v.Elements());
+    ASSERT_EQ(cs[2], 3);
+  }
+  {
+    nsTArray<int> v;
+    v.AppendElement(1);
+    v.AppendElement(2);
+    v.AppendElement(3);
+
+    Range<int> r(v.Elements(), v.Length());
+
+    AssertSpanOfThreeInts(r);
+
+    auto s = MakeSpan(r);
+    ASSERT_EQ(s.size(), narrow_cast<size_t>(v.Length()));
+    ASSERT_EQ(s.data(), v.Elements());
+    ASSERT_EQ(s[2], 3);
+  }
+}
+
+SPAN_TEST(from_cstring)
+{
+  {
+    const char* str = "abc";
+
+    auto cs = MakeCStringSpan(str);
+    ASSERT_EQ(cs.size(), 3U);
+    ASSERT_EQ(cs.data(), str);
+    ASSERT_EQ(cs[2], 'c');
+  }
+}
+
+SPAN_TEST(from_convertible_Span_constructor){
+  {
+    Span<DerivedClass> avd;
+    Span<const DerivedClass> avcd = avd;
+    static_cast<void>(avcd);
+  }
+
+  {
+#ifdef CONFIRM_COMPILATION_ERRORS
+  Span<DerivedClass> avd;
+  Span<BaseClass> avb = avd;
+  static_cast<void>(avb);
+#endif
+  }
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+  {
+    Span<int> s;
+    Span<unsigned int> s2 = s;
+    static_cast<void>(s2);
+  }
+
+  {
+    Span<int> s;
+    Span<const unsigned int> s2 = s;
+    static_cast<void>(s2);
+  }
+
+  {
+    Span<int> s;
+    Span<short> s2 = s;
+    static_cast<void>(s2);
+  }
+#endif
+}
+
+SPAN_TEST(copy_move_and_assignment)
+{
+  Span<int> s1;
+  ASSERT_TRUE(s1.empty());
+
+  int arr[] = { 3, 4, 5 };
+
+  Span<const int> s2 = arr;
+  ASSERT_EQ(s2.Length(), 3U);
+  ASSERT_EQ(s2.data(), &arr[0]);
+
+  s2 = s1;
+  ASSERT_TRUE(s2.empty());
+
+  auto get_temp_Span = [&]() -> Span<int> { return { &arr[1], 2 }; };
+  auto use_Span = [&](Span<const int> s) {
+    ASSERT_EQ(s.Length(), 2U);
+    ASSERT_EQ(s.data(), &arr[1]);
+  };
+  use_Span(get_temp_Span());
+
+  s1 = get_temp_Span();
+  ASSERT_EQ(s1.Length(), 2U);
+  ASSERT_EQ(s1.data(), &arr[1]);
+}
+
+SPAN_TEST(first)
+{
+  int arr[5] = { 1, 2, 3, 4, 5 };
+
+  {
+    Span<int, 5> av = arr;
+    ASSERT_EQ(av.First<2>().Length(), 2U);
+    ASSERT_EQ(av.First(2).Length(), 2U);
+  }
+
+  {
+    Span<int, 5> av = arr;
+    ASSERT_EQ(av.First<0>().Length(), 0U);
+    ASSERT_EQ(av.First(0).Length(), 0U);
+  }
+
+  {
+    Span<int, 5> av = arr;
+    ASSERT_EQ(av.First<5>().Length(), 5U);
+    ASSERT_EQ(av.First(5).Length(), 5U);
+  }
+
+#if 0
+        {
+            Span<int, 5> av = arr;
+#ifdef CONFIRM_COMPILATION_ERRORS
+            ASSERT_EQ(av.First<6>().Length() , 6U);
+            ASSERT_EQ(av.First<-1>().Length() , -1);
+#endif
+            CHECK_THROW(av.First(6).Length(), fail_fast);
+        }
+#endif
+
+  {
+    Span<int> av;
+    ASSERT_EQ(av.First<0>().Length(), 0U);
+    ASSERT_EQ(av.First(0).Length(), 0U);
+  }
+}
+
+SPAN_TEST(last)
+{
+  int arr[5] = { 1, 2, 3, 4, 5 };
+
+  {
+    Span<int, 5> av = arr;
+    ASSERT_EQ(av.Last<2>().Length(), 2U);
+    ASSERT_EQ(av.Last(2).Length(), 2U);
+  }
+
+  {
+    Span<int, 5> av = arr;
+    ASSERT_EQ(av.Last<0>().Length(), 0U);
+    ASSERT_EQ(av.Last(0).Length(), 0U);
+  }
+
+  {
+    Span<int, 5> av = arr;
+    ASSERT_EQ(av.Last<5>().Length(), 5U);
+    ASSERT_EQ(av.Last(5).Length(), 5U);
+  }
+
+#if 0
+        {
+            Span<int, 5> av = arr;
+#ifdef CONFIRM_COMPILATION_ERRORS
+            ASSERT_EQ(av.Last<6>().Length() , 6U);
+#endif
+            CHECK_THROW(av.Last(6).Length(), fail_fast);
+        }
+#endif
+
+  {
+    Span<int> av;
+    ASSERT_EQ(av.Last<0>().Length(), 0U);
+    ASSERT_EQ(av.Last(0).Length(), 0U);
+  }
+}
+
+SPAN_TEST(from_to)
+{
+  int arr[5] = { 1, 2, 3, 4, 5 };
+
+  {
+    Span<int, 5> av = arr;
+    ASSERT_EQ(av.From(3).Length(), 2U);
+    ASSERT_EQ(av.From(2)[1], 4);
+  }
+
+  {
+    Span<int, 5> av = arr;
+    ASSERT_EQ(av.From(5).Length(), 0U);
+  }
+
+  {
+    Span<int, 5> av = arr;
+    ASSERT_EQ(av.From(0).Length(), 5U);
+  }
+
+  {
+    Span<int, 5> av = arr;
+    ASSERT_EQ(av.To(3).Length(), 3U);
+    ASSERT_EQ(av.To(3)[1], 2);
+  }
+
+  {
+    Span<int, 5> av = arr;
+    ASSERT_EQ(av.To(0).Length(), 0U);
+  }
+
+  {
+    Span<int, 5> av = arr;
+    ASSERT_EQ(av.To(5).Length(), 5U);
+  }
+
+  {
+    Span<int, 5> av = arr;
+    ASSERT_EQ(av.FromTo(1, 4).Length(), 3U);
+    ASSERT_EQ(av.FromTo(1, 4)[1], 3);
+  }
+
+  {
+    Span<int, 5> av = arr;
+    ASSERT_EQ(av.FromTo(2, 2).Length(), 0U);
+  }
+
+  {
+    Span<int, 5> av = arr;
+    ASSERT_EQ(av.FromTo(0, 5).Length(), 5U);
+  }
+}
+
+SPAN_TEST(Subspan)
+{
+  int arr[5] = { 1, 2, 3, 4, 5 };
+
+  {
+    Span<int, 5> av = arr;
+    ASSERT_EQ((av.Subspan<2, 2>().Length()), 2U);
+    ASSERT_EQ(av.Subspan(2, 2).Length(), 2U);
+    ASSERT_EQ(av.Subspan(2, 3).Length(), 3U);
+  }
+
+  {
+    Span<int, 5> av = arr;
+    ASSERT_EQ((av.Subspan<0, 0>().Length()), 0U);
+    ASSERT_EQ(av.Subspan(0, 0).Length(), 0U);
+  }
+
+  {
+    Span<int, 5> av = arr;
+    ASSERT_EQ((av.Subspan<0, 5>().Length()), 5U);
+    ASSERT_EQ(av.Subspan(0, 5).Length(), 5U);
+    CHECK_THROW(av.Subspan(0, 6).Length(), fail_fast);
+    CHECK_THROW(av.Subspan(1, 5).Length(), fail_fast);
+  }
+
+  {
+    Span<int, 5> av = arr;
+    ASSERT_EQ((av.Subspan<4, 0>().Length()), 0U);
+    ASSERT_EQ(av.Subspan(4, 0).Length(), 0U);
+    ASSERT_EQ(av.Subspan(5, 0).Length(), 0U);
+    CHECK_THROW(av.Subspan(6, 0).Length(), fail_fast);
+  }
+
+  {
+    Span<int> av;
+    ASSERT_EQ((av.Subspan<0, 0>().Length()), 0U);
+    ASSERT_EQ(av.Subspan(0, 0).Length(), 0U);
+    CHECK_THROW((av.Subspan<1, 0>().Length()), fail_fast);
+  }
+
+  {
+    Span<int> av;
+    ASSERT_EQ(av.Subspan(0).Length(), 0U);
+    CHECK_THROW(av.Subspan(1).Length(), fail_fast);
+  }
+
+  {
+    Span<int> av = arr;
+    ASSERT_EQ(av.Subspan(0).Length(), 5U);
+    ASSERT_EQ(av.Subspan(1).Length(), 4U);
+    ASSERT_EQ(av.Subspan(4).Length(), 1U);
+    ASSERT_EQ(av.Subspan(5).Length(), 0U);
+    CHECK_THROW(av.Subspan(6).Length(), fail_fast);
+    auto av2 = av.Subspan(1);
+    for (int i = 0; i < 4; ++i)
+      ASSERT_EQ(av2[i], i + 2);
+  }
+
+  {
+    Span<int, 5> av = arr;
+    ASSERT_EQ(av.Subspan(0).Length(), 5U);
+    ASSERT_EQ(av.Subspan(1).Length(), 4U);
+    ASSERT_EQ(av.Subspan(4).Length(), 1U);
+    ASSERT_EQ(av.Subspan(5).Length(), 0U);
+    CHECK_THROW(av.Subspan(6).Length(), fail_fast);
+    auto av2 = av.Subspan(1);
+    for (int i = 0; i < 4; ++i)
+      ASSERT_EQ(av2[i], i + 2);
+  }
+}
+
+SPAN_TEST(at_call)
+{
+  int arr[4] = { 1, 2, 3, 4 };
+
+  {
+    Span<int> s = arr;
+    ASSERT_EQ(s.at(0), 1);
+    CHECK_THROW(s.at(5), fail_fast);
+  }
+
+  {
+    int arr2d[2] = { 1, 6 };
+    Span<int, 2> s = arr2d;
+    ASSERT_EQ(s.at(0), 1);
+    ASSERT_EQ(s.at(1), 6);
+    CHECK_THROW(s.at(2), fail_fast);
+  }
+}
+
+SPAN_TEST(operator_function_call)
+{
+  int arr[4] = { 1, 2, 3, 4 };
+
+  {
+    Span<int> s = arr;
+    ASSERT_EQ(s(0), 1);
+    CHECK_THROW(s(5), fail_fast);
+  }
+
+  {
+    int arr2d[2] = { 1, 6 };
+    Span<int, 2> s = arr2d;
+    ASSERT_EQ(s(0), 1);
+    ASSERT_EQ(s(1), 6);
+    CHECK_THROW(s(2), fail_fast);
+  }
+}
+
+SPAN_TEST(iterator_default_init)
+{
+  Span<int>::iterator it1;
+  Span<int>::iterator it2;
+  ASSERT_EQ(it1, it2);
+}
+
+SPAN_TEST(const_iterator_default_init)
+{
+  Span<int>::const_iterator it1;
+  Span<int>::const_iterator it2;
+  ASSERT_EQ(it1, it2);
+}
+
+SPAN_TEST(iterator_conversions)
+{
+  Span<int>::iterator badIt;
+  Span<int>::const_iterator badConstIt;
+  ASSERT_EQ(badIt, badConstIt);
+
+  int a[] = { 1, 2, 3, 4 };
+  Span<int> s = a;
+
+  auto it = s.begin();
+  auto cit = s.cbegin();
+
+  ASSERT_EQ(it, cit);
+  ASSERT_EQ(cit, it);
+
+  Span<int>::const_iterator cit2 = it;
+  ASSERT_EQ(cit2, cit);
+
+  Span<int>::const_iterator cit3 = it + 4;
+  ASSERT_EQ(cit3, s.cend());
+}
+
+SPAN_TEST(iterator_comparisons)
+{
+  int a[] = { 1, 2, 3, 4 };
+  {
+    Span<int> s = a;
+    Span<int>::iterator it = s.begin();
+    auto it2 = it + 1;
+    Span<int>::const_iterator cit = s.cbegin();
+
+    ASSERT_EQ(it, cit);
+    ASSERT_EQ(cit, it);
+    ASSERT_EQ(it, it);
+    ASSERT_EQ(cit, cit);
+    ASSERT_EQ(cit, s.begin());
+    ASSERT_EQ(s.begin(), cit);
+    ASSERT_EQ(s.cbegin(), cit);
+    ASSERT_EQ(it, s.begin());
+    ASSERT_EQ(s.begin(), it);
+
+    ASSERT_NE(it, it2);
+    ASSERT_NE(it2, it);
+    ASSERT_NE(it, s.end());
+    ASSERT_NE(it2, s.end());
+    ASSERT_NE(s.end(), it);
+    ASSERT_NE(it2, cit);
+    ASSERT_NE(cit, it2);
+
+    ASSERT_LT(it, it2);
+    ASSERT_LE(it, it2);
+    ASSERT_LE(it2, s.end());
+    ASSERT_LT(it, s.end());
+    ASSERT_LE(it, cit);
+    ASSERT_LE(cit, it);
+    ASSERT_LT(cit, it2);
+    ASSERT_LE(cit, it2);
+    ASSERT_LT(cit, s.end());
+    ASSERT_LE(cit, s.end());
+
+    ASSERT_GT(it2, it);
+    ASSERT_GE(it2, it);
+    ASSERT_GT(s.end(), it2);
+    ASSERT_GE(s.end(), it2);
+    ASSERT_GT(it2, cit);
+    ASSERT_GE(it2, cit);
+  }
+}
+
+SPAN_TEST(begin_end)
+{
+  {
+    int a[] = { 1, 2, 3, 4 };
+    Span<int> s = a;
+
+    Span<int>::iterator it = s.begin();
+    Span<int>::iterator it2 = std::begin(s);
+    ASSERT_EQ(it, it2);
+
+    it = s.end();
+    it2 = std::end(s);
+    ASSERT_EQ(it, it2);
+  }
+
+  {
+    int a[] = { 1, 2, 3, 4 };
+    Span<int> s = a;
+
+    auto it = s.begin();
+    auto first = it;
+    ASSERT_EQ(it, first);
+    ASSERT_EQ(*it, 1);
+
+    auto beyond = s.end();
+    ASSERT_NE(it, beyond);
+    CHECK_THROW(*beyond, fail_fast);
+
+    ASSERT_EQ(beyond - first, 4U);
+    ASSERT_EQ(first - first, 0U);
+    ASSERT_EQ(beyond - beyond, 0U);
+
+    ++it;
+    ASSERT_EQ(it - first, 1U);
+    ASSERT_EQ(*it, 2);
+    *it = 22;
+    ASSERT_EQ(*it, 22);
+    ASSERT_EQ(beyond - it, 3U);
+
+    it = first;
+    ASSERT_EQ(it, first);
+    while (it != s.end()) {
+      *it = 5;
+      ++it;
+    }
+
+    ASSERT_EQ(it, beyond);
+    ASSERT_EQ(it - beyond, 0U);
+
+    for (auto& n : s) {
+      ASSERT_EQ(n, 5);
+    }
+  }
+}
+
+SPAN_TEST(cbegin_cend)
+{
+#if 0
+          {
+              int a[] = { 1, 2, 3, 4 };
+              Span<int> s = a;
+
+              Span<int>::const_iterator cit = s.cbegin();
+              Span<int>::const_iterator cit2 = std::cbegin(s);
+              ASSERT_EQ(cit , cit2);
+
+              cit = s.cend();
+              cit2 = std::cend(s);
+              ASSERT_EQ(cit , cit2);
+          }
+#endif
+  {
+    int a[] = { 1, 2, 3, 4 };
+    Span<int> s = a;
+
+    auto it = s.cbegin();
+    auto first = it;
+    ASSERT_EQ(it, first);
+    ASSERT_EQ(*it, 1);
+
+    auto beyond = s.cend();
+    ASSERT_NE(it, beyond);
+    CHECK_THROW(*beyond, fail_fast);
+
+    ASSERT_EQ(beyond - first, 4U);
+    ASSERT_EQ(first - first, 0U);
+    ASSERT_EQ(beyond - beyond, 0U);
+
+    ++it;
+    ASSERT_EQ(it - first, 1U);
+    ASSERT_EQ(*it, 2);
+    ASSERT_EQ(beyond - it, 3U);
+
+    int last = 0;
+    it = first;
+    ASSERT_EQ(it, first);
+    while (it != s.cend()) {
+      ASSERT_EQ(*it, last + 1);
+
+      last = *it;
+      ++it;
+    }
+
+    ASSERT_EQ(it, beyond);
+    ASSERT_EQ(it - beyond, 0U);
+  }
+}
+
+SPAN_TEST(rbegin_rend)
+{
+  {
+    int a[] = { 1, 2, 3, 4 };
+    Span<int> s = a;
+
+    auto it = s.rbegin();
+    auto first = it;
+    ASSERT_EQ(it, first);
+    ASSERT_EQ(*it, 4);
+
+    auto beyond = s.rend();
+    ASSERT_NE(it, beyond);
+    CHECK_THROW(*beyond, fail_fast);
+
+    ASSERT_EQ(beyond - first, 4U);
+    ASSERT_EQ(first - first, 0U);
+    ASSERT_EQ(beyond - beyond, 0U);
+
+    ++it;
+    ASSERT_EQ(it - first, 1U);
+    ASSERT_EQ(*it, 3);
+    *it = 22;
+    ASSERT_EQ(*it, 22);
+    ASSERT_EQ(beyond - it, 3U);
+
+    it = first;
+    ASSERT_EQ(it, first);
+    while (it != s.rend()) {
+      *it = 5;
+      ++it;
+    }
+
+    ASSERT_EQ(it, beyond);
+    ASSERT_EQ(it - beyond, 0U);
+
+    for (auto& n : s) {
+      ASSERT_EQ(n, 5);
+    }
+  }
+}
+
+SPAN_TEST(crbegin_crend)
+{
+  {
+    int a[] = { 1, 2, 3, 4 };
+    Span<int> s = a;
+
+    auto it = s.crbegin();
+    auto first = it;
+    ASSERT_EQ(it, first);
+    ASSERT_EQ(*it, 4);
+
+    auto beyond = s.crend();
+    ASSERT_NE(it, beyond);
+    CHECK_THROW(*beyond, fail_fast);
+
+    ASSERT_EQ(beyond - first, 4U);
+    ASSERT_EQ(first - first, 0U);
+    ASSERT_EQ(beyond - beyond, 0U);
+
+    ++it;
+    ASSERT_EQ(it - first, 1U);
+    ASSERT_EQ(*it, 3);
+    ASSERT_EQ(beyond - it, 3U);
+
+    it = first;
+    ASSERT_EQ(it, first);
+    int last = 5;
+    while (it != s.crend()) {
+      ASSERT_EQ(*it, last - 1);
+      last = *it;
+
+      ++it;
+    }
+
+    ASSERT_EQ(it, beyond);
+    ASSERT_EQ(it - beyond, 0U);
+  }
+}
+
+SPAN_TEST(comparison_operators)
+{
+  {
+    Span<int> s1 = nullptr;
+    Span<int> s2 = nullptr;
+    ASSERT_EQ(s1, s2);
+    ASSERT_FALSE(s1 != s2);
+    ASSERT_FALSE(s1 < s2);
+    ASSERT_LE(s1, s2);
+    ASSERT_FALSE(s1 > s2);
+    ASSERT_GE(s1, s2);
+    ASSERT_EQ(s2, s1);
+    ASSERT_FALSE(s2 != s1);
+    ASSERT_FALSE(s2 < s1);
+    ASSERT_LE(s2, s1);
+    ASSERT_FALSE(s2 > s1);
+    ASSERT_GE(s2, s1);
+  }
+
+  {
+    int arr[] = { 2, 1 };
+    Span<int> s1 = arr;
+    Span<int> s2 = arr;
+
+    ASSERT_EQ(s1, s2);
+    ASSERT_FALSE(s1 != s2);
+    ASSERT_FALSE(s1 < s2);
+    ASSERT_LE(s1, s2);
+    ASSERT_FALSE(s1 > s2);
+    ASSERT_GE(s1, s2);
+    ASSERT_EQ(s2, s1);
+    ASSERT_FALSE(s2 != s1);
+    ASSERT_FALSE(s2 < s1);
+    ASSERT_LE(s2, s1);
+    ASSERT_FALSE(s2 > s1);
+    ASSERT_GE(s2, s1);
+  }
+
+  {
+    int arr[] = { 2, 1 }; // bigger
+
+    Span<int> s1 = nullptr;
+    Span<int> s2 = arr;
+
+    ASSERT_NE(s1, s2);
+    ASSERT_NE(s2, s1);
+    ASSERT_NE(s1, s2);
+    ASSERT_NE(s2, s1);
+    ASSERT_LT(s1, s2);
+    ASSERT_FALSE(s2 < s1);
+    ASSERT_LE(s1, s2);
+    ASSERT_FALSE(s2 <= s1);
+    ASSERT_GT(s2, s1);
+    ASSERT_FALSE(s1 > s2);
+    ASSERT_GE(s2, s1);
+    ASSERT_FALSE(s1 >= s2);
+  }
+
+  {
+    int arr1[] = { 1, 2 };
+    int arr2[] = { 1, 2 };
+    Span<int> s1 = arr1;
+    Span<int> s2 = arr2;
+
+    ASSERT_EQ(s1, s2);
+    ASSERT_FALSE(s1 != s2);
+    ASSERT_FALSE(s1 < s2);
+    ASSERT_LE(s1, s2);
+    ASSERT_FALSE(s1 > s2);
+    ASSERT_GE(s1, s2);
+    ASSERT_EQ(s2, s1);
+    ASSERT_FALSE(s2 != s1);
+    ASSERT_FALSE(s2 < s1);
+    ASSERT_LE(s2, s1);
+    ASSERT_FALSE(s2 > s1);
+    ASSERT_GE(s2, s1);
+  }
+
+  {
+    int arr[] = { 1, 2, 3 };
+
+    AssertSpanOfThreeInts(arr);
+
+    Span<int> s1 = { &arr[0], 2 }; // shorter
+    Span<int> s2 = arr;            // longer
+
+    ASSERT_NE(s1, s2);
+    ASSERT_NE(s2, s1);
+    ASSERT_NE(s1, s2);
+    ASSERT_NE(s2, s1);
+    ASSERT_LT(s1, s2);
+    ASSERT_FALSE(s2 < s1);
+    ASSERT_LE(s1, s2);
+    ASSERT_FALSE(s2 <= s1);
+    ASSERT_GT(s2, s1);
+    ASSERT_FALSE(s1 > s2);
+    ASSERT_GE(s2, s1);
+    ASSERT_FALSE(s1 >= s2);
+  }
+
+  {
+    int arr1[] = { 1, 2 }; // smaller
+    int arr2[] = { 2, 1 }; // bigger
+
+    Span<int> s1 = arr1;
+    Span<int> s2 = arr2;
+
+    ASSERT_NE(s1, s2);
+    ASSERT_NE(s2, s1);
+    ASSERT_NE(s1, s2);
+    ASSERT_NE(s2, s1);
+    ASSERT_LT(s1, s2);
+    ASSERT_FALSE(s2 < s1);
+    ASSERT_LE(s1, s2);
+    ASSERT_FALSE(s2 <= s1);
+    ASSERT_GT(s2, s1);
+    ASSERT_FALSE(s1 > s2);
+    ASSERT_GE(s2, s1);
+    ASSERT_FALSE(s1 >= s2);
+  }
+}
+
+SPAN_TEST(as_bytes)
+{
+  int a[] = { 1, 2, 3, 4 };
+
+  {
+    Span<const int> s = a;
+    ASSERT_EQ(s.Length(), 4U);
+    Span<const uint8_t> bs = AsBytes(s);
+    ASSERT_EQ(static_cast<const void*>(bs.data()),
+              static_cast<const void*>(s.data()));
+    ASSERT_EQ(bs.Length(), s.LengthBytes());
+  }
+
+  {
+    Span<int> s;
+    auto bs = AsBytes(s);
+    ASSERT_EQ(bs.Length(), s.Length());
+    ASSERT_EQ(bs.Length(), 0U);
+    ASSERT_EQ(bs.size_bytes(), 0U);
+    ASSERT_EQ(static_cast<const void*>(bs.data()),
+              static_cast<const void*>(s.data()));
+    ASSERT_EQ(bs.data(), nullptr);
+  }
+
+  {
+    Span<int> s = a;
+    auto bs = AsBytes(s);
+    ASSERT_EQ(static_cast<const void*>(bs.data()),
+              static_cast<const void*>(s.data()));
+    ASSERT_EQ(bs.Length(), s.LengthBytes());
+  }
+}
+
+SPAN_TEST(as_writable_bytes)
+{
+  int a[] = { 1, 2, 3, 4 };
+
+  {
+#ifdef CONFIRM_COMPILATION_ERRORS
+    // you should not be able to get writeable bytes for const objects
+    Span<const int> s = a;
+    ASSERT_EQ(s.Length(), 4U);
+    Span<const byte> bs = AsWritableBytes(s);
+    ASSERT_EQ(static_cast<void*>(bs.data()), static_cast<void*>(s.data()));
+    ASSERT_EQ(bs.Length(), s.LengthBytes());
+#endif
+  }
+
+  {
+    Span<int> s;
+    auto bs = AsWritableBytes(s);
+    ASSERT_EQ(bs.Length(), s.Length());
+    ASSERT_EQ(bs.Length(), 0U);
+    ASSERT_EQ(bs.size_bytes(), 0U);
+    ASSERT_EQ(static_cast<void*>(bs.data()), static_cast<void*>(s.data()));
+    ASSERT_EQ(bs.data(), nullptr);
+  }
+
+  {
+    Span<int> s = a;
+    auto bs = AsWritableBytes(s);
+    ASSERT_EQ(static_cast<void*>(bs.data()), static_cast<void*>(s.data()));
+    ASSERT_EQ(bs.Length(), s.LengthBytes());
+  }
+}
+
+SPAN_TEST(fixed_size_conversions)
+{
+  int arr[] = { 1, 2, 3, 4 };
+
+  // converting to an Span from an equal size array is ok
+  Span<int, 4> s4 = arr;
+  ASSERT_EQ(s4.Length(), 4U);
+
+  // converting to dynamic_range is always ok
+  {
+    Span<int> s = s4;
+    ASSERT_EQ(s.Length(), s4.Length());
+    static_cast<void>(s);
+  }
+
+// initialization or assignment to static Span that REDUCES size is NOT ok
+#ifdef CONFIRM_COMPILATION_ERRORS
+  {
+    Span<int, 2> s = arr;
+  }
+  {
+    Span<int, 2> s2 = s4;
+    static_cast<void>(s2);
+  }
+#endif
+
+#if 0
+        // even when done dynamically
+        {
+            Span<int> s = arr;
+            auto f = [&]() {
+                Span<int, 2> s2 = s;
+                static_cast<void>(s2);
+            };
+            CHECK_THROW(f(), fail_fast);
+        }
+#endif
+
+  // but doing so explicitly is ok
+
+  // you can convert statically
+  {
+    Span<int, 2> s2 = { arr, 2 };
+    static_cast<void>(s2);
+  }
+  {
+    Span<int, 1> s1 = s4.First<1>();
+    static_cast<void>(s1);
+  }
+
+  // ...or dynamically
+  {
+    // NB: implicit conversion to Span<int,1> from Span<int>
+    Span<int, 1> s1 = s4.First(1);
+    static_cast<void>(s1);
+  }
+
+#if 0
+        // initialization or assignment to static Span that requires size INCREASE is not ok.
+        int arr2[2] = {1, 2};
+#endif
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+  {
+    Span<int, 4> s3 = arr2;
+  }
+  {
+    Span<int, 2> s2 = arr2;
+    Span<int, 4> s4a = s2;
+  }
+#endif
+
+#if 0
+        {
+            auto f = [&]() {
+                Span<int, 4> _s4 = {arr2, 2};
+                static_cast<void>(_s4);
+            };
+            CHECK_THROW(f(), fail_fast);
+        }
+
+        // this should fail - we are trying to assign a small dynamic Span to a fixed_size larger one
+        Span<int> av = arr2;
+        auto f = [&]() {
+            Span<int, 4> _s4 = av;
+            static_cast<void>(_s4);
+        };
+        CHECK_THROW(f(), fail_fast);
+#endif
+}
+
+#if 0
+    SPAN_TEST(interop_with_std_regex)
+    {
+        char lat[] = { '1', '2', '3', '4', '5', '6', 'E', 'F', 'G' };
+        Span<char> s = lat;
+        auto f_it = s.begin() + 7;
+
+        std::match_results<Span<char>::iterator> match;
+
+        std::regex_match(s.begin(), s.end(), match, std::regex(".*"));
+        ASSERT_EQ(match.ready());
+        ASSERT_TRUE(!match.empty());
+        ASSERT_TRUE(match[0].matched);
+        ASSERT_TRUE(match[0].first , s.begin());
+        ASSERT_EQ(match[0].second , s.end());
+
+        std::regex_search(s.begin(), s.end(), match, std::regex("F"));
+        ASSERT_TRUE(match.ready());
+        ASSERT_TRUE(!match.empty());
+        ASSERT_TRUE(match[0].matched);
+        ASSERT_EQ(match[0].first , f_it);
+        ASSERT_EQ(match[0].second , (f_it + 1));
+    }
+
+SPAN_TEST(interop_with_gsl_at)
+{
+  int arr[5] = { 1, 2, 3, 4, 5 };
+  Span<int> s{ arr };
+  ASSERT_EQ(at(s, 0) , 1 );
+ASSERT_EQ(at(s, 1) , 2U);
+}
+#endif
+
+SPAN_TEST(default_constructible)
+{
+  ASSERT_TRUE((std::is_default_constructible<Span<int>>::value));
+  ASSERT_TRUE((std::is_default_constructible<Span<int, 0>>::value));
+  ASSERT_TRUE((!std::is_default_constructible<Span<int, 42>>::value));
+}
new file mode 100644
--- /dev/null
+++ b/mfbt/tests/gtest/moz.build
@@ -0,0 +1,15 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+UNIFIED_SOURCES += [
+    'TestSpan.cpp',
+]
+
+#LOCAL_INCLUDES += [
+#    '../../base',
+#]
+
+FINAL_LIBRARY = 'xul-gtest'
--- a/mfbt/tests/moz.build
+++ b/mfbt/tests/moz.build
@@ -1,14 +1,19 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+if not CONFIG['JS_STANDALONE']:
+    TEST_DIRS += [
+        'gtest',
+    ]
+
 CppUnitTests([
     'TestArray',
     'TestArrayUtils',
     'TestAtomics',
     'TestBinarySearch',
     'TestBloomFilter',
     'TestBufferList',
     'TestCasting',
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/EventDispatcher.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/EventDispatcher.java
@@ -50,32 +50,39 @@ public final class EventDispatcher exten
     private final Map<String, List<BundleEventListener>> mGeckoThreadListeners =
         new HashMap<String, List<BundleEventListener>>(DEFAULT_GECKO_EVENTS_COUNT);
     private final Map<String, List<BundleEventListener>> mUiThreadListeners =
         new HashMap<String, List<BundleEventListener>>(DEFAULT_UI_EVENTS_COUNT);
     private final Map<String, List<BundleEventListener>> mBackgroundThreadListeners =
         new HashMap<String, List<BundleEventListener>>(DEFAULT_BACKGROUND_EVENTS_COUNT);
 
     private boolean mAttachedToGecko;
-    private final StateHolder mStateHolder;
+    private volatile StateHolder mStateHolder;
 
     @ReflectionTarget
     @WrapForJNI(calledFrom = "gecko")
     public static EventDispatcher getInstance() {
         return INSTANCE;
     }
 
     /* package */ EventDispatcher() {
         mStateHolder = GeckoThread.getStateHolder();
     }
 
     /* package */ EventDispatcher(final NativeQueue.StateHolder stateHolder) {
         mStateHolder = stateHolder;
     }
 
+    /* package */ void setStateHolder(final NativeQueue.StateHolder stateHolder) {
+        mStateHolder = stateHolder;
+        // Force queue flushing.
+        final NativeQueue.State state = mStateHolder.getState();
+        mStateHolder.checkAndSetState(state, state);
+    }
+
     private boolean isReadyForDispatchingToGecko() {
         return mStateHolder.isReady();
     }
 
     @WrapForJNI(dispatchTo = "gecko") @Override // JNIObject
     protected native void disposeNative();
 
     @WrapForJNI private static final int DETACHED = 0;
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java
@@ -42,73 +42,81 @@ public class GeckoView extends LayerView
     private static final String LOGTAG = "GeckoView";
 
     private static final boolean DEBUG = false;
 
     /* package */ enum State implements NativeQueue.State {
         @WrapForJNI INITIAL(0),
         @WrapForJNI READY(1);
 
-        private int rank;
+        private int mRank;
 
         private State(int rank) {
-            this.rank = rank;
+            mRank = rank;
         }
 
         @Override
         public boolean is(final NativeQueue.State other) {
             return this == other;
         }
 
         @Override
         public boolean isAtLeast(final NativeQueue.State other) {
             if (other instanceof State) {
-                return this.rank >= ((State) other).rank;
+                return mRank >= ((State) other).mRank;
             }
             return false;
         }
     }
 
-    private final StateHolder mStateHolder =
+    private static final StateHolder sDummyStateHolder =
         new StateHolder(State.INITIAL, State.READY);
 
-    @WrapForJNI(calledFrom = "gecko")
-    private void setState(final State newState) {
-        mStateHolder.setState(newState);
-    }
-
     private final EventDispatcher mEventDispatcher =
-        new EventDispatcher(mStateHolder);
+        new EventDispatcher(sDummyStateHolder);
 
     private ChromeDelegate mChromeDelegate;
     /* package */ ContentListener mContentListener;
     /* package */ NavigationListener mNavigationListener;
     /* package */ ProgressListener mProgressListener;
     private InputConnectionListener mInputConnectionListener;
 
     private GeckoViewSettings mSettings;
 
     protected boolean mOnAttachedToWindowCalled;
     protected String mChromeUri;
     protected int mScreenId = 0; // default to the primary screen
 
     @WrapForJNI(dispatchTo = "proxy")
     protected static final class Window extends JNIObject {
         @WrapForJNI(skip = true)
+        /* package */ final StateHolder mStateHolder =
+            new StateHolder(State.INITIAL, State.READY);
+
+        @WrapForJNI(skip = true)
         /* package */ Window() {}
 
         static native void open(Window instance, GeckoView view,
                                 Object compositor, EventDispatcher dispatcher,
                                 String chromeUri, GeckoBundle settings,
                                 int screenId);
 
         @Override protected native void disposeNative();
+
         native void close();
-        native void reattach(GeckoView view, Object compositor, EventDispatcher dispatcher);
+
+        native void reattach(GeckoView view, Object compositor,
+                             EventDispatcher dispatcher);
+
         native void loadUri(String uri, int flags);
+
+        @WrapForJNI(calledFrom = "gecko")
+        private void setState(final State newState) {
+            mStateHolder.setState(newState);
+        }
     }
 
     // Object to hold onto our nsWindow connection when GeckoView gets destroyed.
     private static class StateBinder extends Binder implements Parcelable {
         public final Parcelable superState;
         public final Window window;
 
         public StateBinder(Parcelable superState, Window window) {
@@ -192,19 +200,19 @@ public class GeckoView extends LayerView
             } else if ("GeckoView:SecurityChanged".equals(event)) {
                 if (mProgressListener != null) {
                     mProgressListener.onSecurityChanged(GeckoView.this, message.getInt("status"));
                 }
             }
         }
     }
 
-    protected Window window;
-    private boolean stateSaved;
-    private final Listener listener = new Listener();
+    protected Window mWindow;
+    private boolean mStateSaved;
+    private final Listener mListener = new Listener();
 
     public GeckoView(Context context) {
         super(context);
         init(context);
     }
 
     public GeckoView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -222,136 +230,140 @@ public class GeckoView extends LayerView
             setGeckoInterface(new BaseGeckoInterface(context));
             GeckoAppShell.setContextGetter(this);
         }
 
         // Perform common initialization for Fennec/GeckoView.
         GeckoAppShell.setLayerView(this);
 
         initializeView();
-        listener.registerListeners();
+        mListener.registerListeners();
 
         mSettings = new GeckoViewSettings(getEventDispatcher());
     }
 
     @Override
     protected Parcelable onSaveInstanceState() {
         final Parcelable superState = super.onSaveInstanceState();
-        stateSaved = true;
-        return new StateBinder(superState, this.window);
+        mStateSaved = true;
+        return new StateBinder(superState, mWindow);
     }
 
     @Override
     protected void onRestoreInstanceState(final Parcelable state) {
         final StateBinder stateBinder = (StateBinder) state;
 
         if (stateBinder.window != null) {
-            this.window = stateBinder.window;
+            mWindow = stateBinder.window;
         }
-        stateSaved = false;
+        mStateSaved = false;
 
         if (mOnAttachedToWindowCalled) {
             reattachWindow();
         }
 
         // We have to always call super.onRestoreInstanceState because View keeps
         // track of these calls and throws an exception when we don't call it.
         super.onRestoreInstanceState(stateBinder.superState);
     }
 
     protected void openWindow() {
         if (mChromeUri == null) {
             mChromeUri = getGeckoInterface().getDefaultChromeURI();
         }
 
         if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
-            Window.open(window, this, getCompositor(), mEventDispatcher,
+            Window.open(mWindow, this, getCompositor(), mEventDispatcher,
                         mChromeUri, mSettings.asBundle(), mScreenId);
         } else {
             GeckoThread.queueNativeCallUntil(
                 GeckoThread.State.PROFILE_READY,
-                Window.class, "open", window,
+                Window.class, "open", mWindow,
                 GeckoView.class, this,
                 Object.class, getCompositor(),
                 EventDispatcher.class, mEventDispatcher,
                 String.class, mChromeUri,
                 GeckoBundle.class, mSettings.asBundle(),
                 mScreenId);
         }
     }
 
     protected void reattachWindow() {
+        mEventDispatcher.setStateHolder(mWindow.mStateHolder);
+
         if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
-            window.reattach(this, getCompositor(), mEventDispatcher);
+            mWindow.reattach(this, getCompositor(), mEventDispatcher);
         } else {
             GeckoThread.queueNativeCallUntil(GeckoThread.State.PROFILE_READY,
-                    window, "reattach", GeckoView.class, this,
+                    mWindow, "reattach", GeckoView.class, this,
                     Object.class, getCompositor(), EventDispatcher.class, mEventDispatcher);
         }
     }
 
     @Override
     public void onAttachedToWindow() {
         final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
 
-        if (window == null) {
+        if (mWindow == null) {
             // Open a new nsWindow if we didn't have one from before.
-            window = new Window();
+            mWindow = new Window();
+            mEventDispatcher.setStateHolder(mWindow.mStateHolder);
             openWindow();
         } else {
             reattachWindow();
         }
 
         super.onAttachedToWindow();
 
         mOnAttachedToWindowCalled = true;
     }
 
     @Override
     public void onDetachedFromWindow() {
         super.onDetachedFromWindow();
         super.destroy();
 
-        if (stateSaved) {
+        if (mStateSaved) {
             // If we saved state earlier, we don't want to close the nsWindow.
             return;
         }
 
         if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
-            window.close();
-            window.disposeNative();
+            mWindow.close();
+            mWindow.disposeNative();
         } else {
             GeckoThread.queueNativeCallUntil(GeckoThread.State.PROFILE_READY,
-                    window, "close");
+                    mWindow, "close");
             GeckoThread.queueNativeCallUntil(GeckoThread.State.PROFILE_READY,
-                    window, "disposeNative");
+                    mWindow, "disposeNative");
         }
 
+        mEventDispatcher.setStateHolder(sDummyStateHolder);
         mOnAttachedToWindowCalled = false;
     }
 
     @WrapForJNI public static final int LOAD_DEFAULT = 0;
     @WrapForJNI public static final int LOAD_NEW_TAB = 1;
     @WrapForJNI public static final int LOAD_SWITCH_TAB = 2;
 
     /**
     * Load the given URI.
     * Note: Only for Fennec support.
     * @param uri The URI of the resource to load.
     * @param flags The load flags (TODO).
     */
     public void loadUri(String uri, int flags) {
-        if (window == null) {
+        if (mWindow == null) {
             throw new IllegalStateException("Not attached to window");
         }
 
         if (GeckoThread.isRunning()) {
-            window.loadUri(uri, flags);
+            mWindow.loadUri(uri, flags);
         }  else {
-            GeckoThread.queueNativeCall(window, "loadUri", String.class, uri, flags);
+            GeckoThread.queueNativeCall(mWindow, "loadUri", String.class, uri, flags);
         }
     }
 
     /**
     * Load the given URI.
     * @param uri The URI of the resource to load.
     */
     public void loadUri(String uri) {
--- a/netwerk/cache2/moz.build
+++ b/netwerk/cache2/moz.build
@@ -20,16 +20,17 @@ XPIDL_SOURCES += [
 XPIDL_MODULE = 'necko_cache2'
 
 EXPORTS += [
     'CacheObserver.h',
     'CacheStorageService.h',
 ]
 
 UNIFIED_SOURCES += [
+    'AppCacheStorage.cpp',
     'CacheEntry.cpp',
     'CacheFile.cpp',
     'CacheFileChunk.cpp',
     'CacheFileContextEvictor.cpp',
     'CacheFileInputStream.cpp',
     'CacheFileIOManager.cpp',
     'CacheFileMetadata.cpp',
     'CacheFileOutputStream.cpp',
@@ -41,21 +42,16 @@ UNIFIED_SOURCES += [
     'CacheIOThread.cpp',
     'CacheLog.cpp',
     'CacheObserver.cpp',
     'CacheStorage.cpp',
     'CacheStorageService.cpp',
     'OldWrappers.cpp',
 ]
 
-# AppCacheStorage.cpp cannot be built in unified mode because it uses plarena.h.
-SOURCES += [
-    'AppCacheStorage.cpp',
-]
-
 LOCAL_INCLUDES += [
     '/netwerk/base',
     '/netwerk/cache',
 ]
 
 FINAL_LIBRARY = 'xul'
 
 if CONFIG['GNU_CXX']:
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -58,19 +58,16 @@
 #endif
 
 using namespace mozilla::dom;
 using namespace mozilla::ipc;
 
 namespace mozilla {
 namespace net {
 
-extern bool
-WillRedirect(nsHttpResponseHead * response);
-
 namespace {
 
 const uint32_t kMaxFileDescriptorsPerMessage = 250;
 
 #ifdef OS_POSIX
 // Keep this in sync with other platforms.
 static_assert(FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE == 250,
               "MAX_DESCRIPTORS_PER_MESSAGE mismatch!");
@@ -3174,24 +3171,24 @@ void
 HttpChannelChild::OverrideWithSynthesizedResponse(nsAutoPtr<nsHttpResponseHead>& aResponseHead,
                                                   nsIInputStream* aSynthesizedInput,
                                                   InterceptStreamListener* aStreamListener)
 {
   mInterceptListener = aStreamListener;
 
   // Intercepted responses should already be decoded.  If its a redirect,
   // however, we want to respect the encoding of the final result instead.
-  if (!WillRedirect(aResponseHead)) {
+  if (!nsHttpChannel::WillRedirect(aResponseHead)) {
     SetApplyConversion(false);
   }
 
   mResponseHead = aResponseHead;
   mSynthesizedResponse = true;
 
-  if (WillRedirect(mResponseHead)) {
+  if (nsHttpChannel::WillRedirect(mResponseHead)) {
     mShouldInterceptSubsequentRedirect = true;
     // Continue with the original cross-process request
     nsresult rv = ContinueAsyncOpen();
     if (NS_WARN_IF(NS_FAILED(rv))) {
       rv = AsyncAbort(rv);
       MOZ_ASSERT(NS_SUCCEEDED(rv));
     }
     return;
--- a/netwerk/protocol/http/InterceptedChannel.cpp
+++ b/netwerk/protocol/http/InterceptedChannel.cpp
@@ -16,19 +16,16 @@
 #include "nsNetUtil.h"
 #include "mozilla/ConsoleReportCollector.h"
 #include "mozilla/dom/ChannelInfo.h"
 #include "nsIChannelEventSink.h"
 
 namespace mozilla {
 namespace net {
 
-extern bool
-WillRedirect(const nsHttpResponseHead * response);
-
 extern nsresult
 DoUpdateExpirationTime(nsHttpChannel* aSelf,
                        nsICacheEntry* aCacheEntry,
                        nsHttpResponseHead* aResponseHead,
                        uint32_t& aExpirationTime);
 extern nsresult
 DoAddCacheEntryHeaders(nsHttpChannel *self,
                        nsICacheEntry *entry,
@@ -241,17 +238,17 @@ InterceptedChannelChrome::FinishSynthesi
   mResponseBody->Close();
 
   mReportCollector->FlushConsoleReports(mChannel);
 
   EnsureSynthesizedResponse();
 
   // If the synthesized response is a redirect, then we want to respect
   // the encoding of whatever is loaded as a result.
-  if (WillRedirect(mSynthesizedResponseHead.ref())) {
+  if (nsHttpChannel::WillRedirect(mSynthesizedResponseHead.ref())) {
     nsresult rv = mChannel->SetApplyConversion(mOldApplyConversion);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   mChannel->MarkIntercepted();
 
   // First we ensure the appropriate metadata is set on the synthesized cache entry
   // (i.e. the flattened response head)
--- a/netwerk/protocol/http/moz.build
+++ b/netwerk/protocol/http/moz.build
@@ -44,28 +44,25 @@ EXPORTS.mozilla.net += [
     'HttpChannelParent.h',
     'HttpInfo.h',
     'NullHttpChannel.h',
     'PHttpChannelParams.h',
     'PSpdyPush.h',
     'TimingStruct.h',
 ]
 
-# ASpdySession.cpp and nsHttpAuthCache cannot be built in unified mode because
-# they use plarena.h.
 SOURCES += [
-    'AlternateServices.cpp',
-    'ASpdySession.cpp',
-    'nsHttpAuthCache.cpp',
     'nsHttpChannelAuthProvider.cpp', # redefines GetAuthType
 ]
 
 UNIFIED_SOURCES += [
     'AltDataOutputStreamChild.cpp',
     'AltDataOutputStreamParent.cpp',
+    'AlternateServices.cpp',
+    'ASpdySession.cpp',
     'CacheControlParser.cpp',
     'ConnectionDiagnostics.cpp',
     'HSTSPrimerListener.cpp',
     'Http2Compression.cpp',
     'Http2Push.cpp',
     'Http2Session.cpp',
     'Http2Stream.cpp',
     'HttpAuthUtils.cpp',
@@ -73,16 +70,17 @@ UNIFIED_SOURCES += [
     'HttpChannelChild.cpp',
     'HttpChannelParent.cpp',
     'HttpChannelParentListener.cpp',
     'HttpInfo.cpp',
     'InterceptedChannel.cpp',
     'nsCORSListenerProxy.cpp',
     'nsHttp.cpp',
     'nsHttpActivityDistributor.cpp',
+    'nsHttpAuthCache.cpp',
     'nsHttpAuthManager.cpp',
     'nsHttpBasicAuth.cpp',
     'nsHttpChannel.cpp',
     'nsHttpChunkedDecoder.cpp',
     'nsHttpConnection.cpp',
     'nsHttpConnectionInfo.cpp',
     'nsHttpConnectionMgr.cpp',
     'nsHttpDigestAuth.cpp',
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -187,19 +187,19 @@ Hash(const char *buf, nsACString &hash)
     return NS_OK;
 }
 
 } // unnamed namespace
 
 // We only treat 3xx responses as redirects if they have a Location header and
 // the status code is in a whitelist.
 bool
-WillRedirect(nsHttpResponseHead * response)
-{
-    return nsHttpChannel::IsRedirectStatus(response->Status()) &&
+nsHttpChannel::WillRedirect(nsHttpResponseHead * response)
+{
+    return IsRedirectStatus(response->Status()) &&
            response->HasHeader(nsHttp::Location);
 }
 
 nsresult
 StoreAuthorizationMetaData(nsICacheEntry *entry, nsHttpRequestHead *requestHead);
 
 class AutoRedirectVetoNotifier
 {
--- a/netwerk/protocol/http/nsHttpChannel.h
+++ b/netwerk/protocol/http/nsHttpChannel.h
@@ -133,16 +133,17 @@ public:
                                        uint32_t aProxyResolveFlags,
                                        nsIURI *aProxyURI,
                                        const nsID& aChannelId) override;
 
     MOZ_MUST_USE nsresult OnPush(const nsACString &uri,
                                  Http2PushedStream *pushedStream);
 
     static bool IsRedirectStatus(uint32_t status);
+    static bool WillRedirect(nsHttpResponseHead * response);
 
 
     // Methods HttpBaseChannel didn't implement for us or that we override.
     //
     // nsIRequest
     NS_IMETHOD Cancel(nsresult status) override;
     NS_IMETHOD Suspend() override;
     NS_IMETHOD Resume() override;
--- a/netwerk/protocol/http/nsHttpDigestAuth.cpp
+++ b/netwerk/protocol/http/nsHttpDigestAuth.cpp
@@ -3,16 +3,17 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // HttpLog.h should generally be included first
 #include "HttpLog.h"
 
 #include "mozilla/Sprintf.h"
+#include "mozilla/Unused.h"
 
 #include "nsHttp.h"
 #include "nsHttpDigestAuth.h"
 #include "nsIHttpAuthenticableChannel.h"
 #include "nsISupportsPrimitives.h"
 #include "nsIURI.h"
 #include "nsString.h"
 #include "nsEscape.h"
--- a/netwerk/streamconv/converters/nsIndexedToHTML.cpp
+++ b/netwerk/streamconv/converters/nsIndexedToHTML.cpp
@@ -694,16 +694,20 @@ nsIndexedToHTML::OnIndexAvailable(nsIReq
 
     // We don't know the file's character set yet, so retrieve the raw bytes
     // which will be decoded by the HTML parser.
     nsXPIDLCString loc;
     aIndex->GetLocation(getter_Copies(loc));
 
     // Adjust the length in case unescaping shortened the string.
     loc.Truncate(nsUnescapeCount(loc.BeginWriting()));
+
+    if (loc.IsEmpty()) {
+        return NS_ERROR_ILLEGAL_VALUE;
+    }
     if (loc.First() == char16_t('.'))
         pushBuffer.AppendLiteral(" class=\"hidden-object\"");
 
     pushBuffer.AppendLiteral(">\n <td sortable-data=\"");
 
     // The sort key is the name of the item, prepended by either 0, 1 or 2
     // in order to group items.
     uint32_t type;
--- a/toolkit/content/license.html
+++ b/toolkit/content/license.html
@@ -96,16 +96,17 @@
       <li><a href="about:license#hunspell-ee">Estonian Spellchecking Dictionary License</a></li>
       <li><a href="about:license#expat">Expat License</a></li>
       <li><a href="about:license#firebug">Firebug License</a></li>
       <li><a href="about:license#gfx-font-list">gfxFontList License</a></li>
       <li><a href="about:license#google-bsd">Google BSD License</a></li>
       <li><a href="about:license#gears">Google Gears License</a></li>
       <li><a href="about:license#gears-istumbler">Google Gears/iStumbler License</a></li>
       <li><a href="about:license#vp8">Google VP8 License</a></li>
+      <li><a href="about:license#gsl">GSL License</a></li>
       <li><a href="about:license#gyp">gyp License</a></li>
       <li><a href="about:license#halloc">halloc License</a></li>
       <li><a href="about:license#harfbuzz">HarfBuzz License</a></li>
       <li><a href="about:license#icu">ICU License</a></li>
       <li><a href="about:license#immutable">Immutable.js License</a></li>
       <li><a href="about:license#jpnic">Japan Network Information Center License</a></li>
       <li><a href="about:license#jsmn">JSMN License</a></li>
       <li><a href="about:license#jszip">JSZip License</a></li>
@@ -3412,16 +3413,48 @@ PROCUREMENT OF SUBSTITUTE GOODS OR SERVI
 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 </pre>
 
     <hr>
 
+    <h1><a id="gsl"></a>GSL License</h1>
+
+    <p>This license applies to <span class="path">mfbt/Span.h</span> and
+    <span class="path">mfbt/tests/gtest/TestSpan.cpp</span>.</p>
+    <!-- https://github.com/Microsoft/GSL/blob/3819df6e378ffccf0e29465afe99c3b324c2aa70/LICENSE -->
+<pre>
+Copyright (c) 2015 Microsoft Corporation. All rights reserved.
+
+This code is licensed under the MIT License (MIT).
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+</pre>
+
+
+    <hr>
+
     <h1><a id="gyp"></a>gyp License</h1>
 
     <p>This license applies to certain files in the directory
     <span class="path">media/webrtc/trunk/tools/gyp</span>.</p>
 <pre>
 Copyright (c) 2009 Google Inc. All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
--- a/toolkit/crashreporter/nsExceptionHandler.cpp
+++ b/toolkit/crashreporter/nsExceptionHandler.cpp
@@ -111,16 +111,23 @@ using google_breakpad::MinidumpDescripto
 #if defined(MOZ_WIDGET_ANDROID)
 using google_breakpad::auto_wasteful_vector;
 using google_breakpad::FileID;
 using google_breakpad::PageAllocator;
 #endif
 using namespace mozilla;
 using mozilla::ipc::CrashReporterClient;
 
+// From toolkit/library/rust/shared/lib.rs
+extern "C" {
+  void install_rust_panic_hook();
+  bool get_rust_panic_reason(char** reason, size_t* length);
+}
+
+
 namespace CrashReporter {
 
 #ifdef XP_WIN32
 typedef wchar_t XP_CHAR;
 typedef std::wstring xpstring;
 #define XP_TEXT(x) L##x
 #define CONVERT_XP_CHAR_TO_UTF16(x) x
 #define XP_STRLEN(x) wcslen(x)
@@ -1126,17 +1133,27 @@ bool MinidumpCallback(
     if (apiData.Valid()) {
       DllBlocklist_WriteNotes(apiData.Handle());
       DllBlocklist_WriteNotes(eventFile.Handle());
     }
 #endif
     WriteGlobalMemoryStatus(&apiData, &eventFile);
 #endif // XP_WIN
 
-    if (gMozCrashReason) {
+    char* rust_panic_reason;
+    size_t rust_panic_len;
+    if (get_rust_panic_reason(&rust_panic_reason, &rust_panic_len)) {
+      // rust_panic_reason is not null-terminated.
+      WriteLiteral(apiData, "MozCrashReason=");
+      apiData.WriteBuffer(rust_panic_reason, rust_panic_len);
+      WriteLiteral(apiData, "\n");
+      WriteLiteral(eventFile, "MozCrashReason=");
+      eventFile.WriteBuffer(rust_panic_reason, rust_panic_len);
+      WriteLiteral(eventFile, "\n");
+    } else if (gMozCrashReason) {
       WriteAnnotation(apiData, "MozCrashReason", gMozCrashReason);
       WriteAnnotation(eventFile, "MozCrashReason", gMozCrashReason);
     }
 
     if (oomAllocationSizeBuffer[0]) {
       WriteAnnotation(apiData, "OOMAllocationSize", oomAllocationSizeBuffer);
       WriteAnnotation(eventFile, "OOMAllocationSize", oomAllocationSizeBuffer);
     }
@@ -1572,16 +1589,18 @@ LocateExecutable(nsIFile* aXREDirectory,
 #endif // !defined(MOZ_WIDGET_ANDROID)
 
 nsresult SetExceptionHandler(nsIFile* aXREDirectory,
                              bool force/*=false*/)
 {
   if (gExceptionHandler)
     return NS_ERROR_ALREADY_INITIALIZED;
 
+  install_rust_panic_hook();
+
 #if !defined(DEBUG) || defined(MOZ_WIDGET_GONK)
   // In non-debug builds, enable the crash reporter by default, and allow
   // disabling it with the MOZ_CRASHREPORTER_DISABLE environment variable.
   // Also enable it by default in debug gonk builds as it is difficult to
   // set environment on startup.
   const char *envvar = PR_GetEnv("MOZ_CRASHREPORTER_DISABLE");
   if (envvar && *envvar && !force)
     return NS_OK;
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/test/unit/test_crash_rust_panic.js
@@ -0,0 +1,11 @@
+function run_test() {
+  // Try crashing with a Rust panic
+  do_crash(function() {
+             Components.classes["@mozilla.org/xpcom/debug;1"].getService(Components.interfaces.nsIDebug2).rustPanic("OH NO");
+           },
+           function(mdump, extra) {
+             do_check_eq(extra.MozCrashReason, "OH NO");
+           },
+          // process will exit with a zero exit status
+          true);
+}
--- a/toolkit/crashreporter/test/unit/xpcshell.ini
+++ b/toolkit/crashreporter/test/unit/xpcshell.ini
@@ -2,16 +2,19 @@
 head = head_crashreporter.js
 skip-if = toolkit == 'android'
 support-files =
   crasher_subprocess_head.js
   crasher_subprocess_tail.js
 
 [test_crash_moz_crash.js]
 [test_crash_purevirtual.js]
+[test_crash_rust_panic.js]
+# Fails on Win64, bug 1302078.
+fail-if = os == 'win' && bits == 64
 [test_crash_after_js_oom_reported.js]
 [test_crash_after_js_oom_recovered.js]
 [test_crash_after_js_oom_reported_2.js]
 [test_crash_after_js_large_allocation_failure.js]
 [test_crash_after_js_large_allocation_failure_reporting.js]
 [test_crash_oom.js]
 [test_oom_annotation_windows.js]
 skip-if = os != 'win'
--- a/toolkit/library/rust/shared/lib.rs
+++ b/toolkit/library/rust/shared/lib.rs
@@ -5,8 +5,64 @@
 #[cfg(feature="servo")]
 extern crate geckoservo;
 
 extern crate mp4parse_capi;
 extern crate nsstring;
 extern crate rust_url_capi;
 #[cfg(feature = "quantum_render")]
 extern crate webrender_bindings;
+
+use std::boxed::Box;
+use std::ffi::CStr;
+use std::os::raw::c_char;
+use std::panic;
+
+/// Used to implement `nsIDebug2::RustPanic` for testing purposes.
+#[no_mangle]
+pub extern "C" fn intentional_panic(message: *const c_char) {
+    panic!("{}", unsafe { CStr::from_ptr(message) }.to_string_lossy());
+}
+
+/// Contains the panic message, if set.
+static mut PANIC_REASON: Option<(*const str, usize)> = None;
+
+/// Configure a panic hook to capture panic messages for crash reports.
+///
+/// We don't store this in `gMozCrashReason` because:
+/// a) Rust strings aren't null-terminated, so we'd have to allocate
+///    memory to get a null-terminated string
+/// b) The panic=abort handler is going to call `abort()` on non-Windows,
+///    which is `mozalloc_abort` for us, which will use `MOZ_CRASH` and
+///    overwrite `gMozCrashReason` with an unhelpful string.
+#[no_mangle]
+pub extern "C" fn install_rust_panic_hook() {
+    panic::set_hook(Box::new(|info| {
+        // Try to handle &str/String payloads, which should handle 99% of cases.
+        let payload = info.payload();
+        // We'll hold a raw *const str here, but it will be OK because
+        // Rust is going to abort the process before the payload could be
+        // deallocated.
+        if let Some(s) = payload.downcast_ref::<&str>() {
+            unsafe { PANIC_REASON = Some((*s as *const str, s.len())) }
+        } else if let Some(s) = payload.downcast_ref::<String>() {
+            unsafe { PANIC_REASON = Some((s.as_str() as *const str, s.len())) }
+        } else {
+            // Not the most helpful thing, but seems unlikely to happen
+            // in practice.
+            println!("Unhandled panic payload!");
+        }
+    }));
+}
+
+#[no_mangle]
+pub extern "C" fn get_rust_panic_reason(reason: *mut *const c_char, length: *mut usize) -> bool {
+    unsafe {
+        match PANIC_REASON {
+            Some((s, len)) => {
+                *reason = s as *const c_char;
+                *length = len;
+                true
+            }
+            None => false,
+        }
+    }
+}
--- a/widget/android/GeneratedJNIWrappers.cpp
+++ b/widget/android/GeneratedJNIWrappers.cpp
@@ -962,24 +962,16 @@ constexpr char GeckoThread::State::RUNNI
 auto GeckoThread::State::RUNNING() -> State::LocalRef
 {
     return mozilla::jni::Field<RUNNING_t>::Get(State::Context(), nullptr);
 }
 
 const char GeckoView::name[] =
         "org/mozilla/gecko/GeckoView";
 
-constexpr char GeckoView::SetState_t::name[];
-constexpr char GeckoView::SetState_t::signature[];
-
-auto GeckoView::SetState(mozilla::jni::Object::Param a0) const -> void
-{
-    return mozilla::jni::Method<SetState_t>::Call(GeckoView::mCtx, nullptr, a0);
-}
-
 const char GeckoView::State::name[] =
         "org/mozilla/gecko/GeckoView$State";
 
 constexpr char GeckoView::State::INITIAL_t::name[];
 constexpr char GeckoView::State::INITIAL_t::signature[];
 
 auto GeckoView::State::INITIAL() -> State::LocalRef
 {
@@ -1007,16 +999,24 @@ constexpr char GeckoView::Window::LoadUr
 constexpr char GeckoView::Window::LoadUri_t::signature[];
 
 constexpr char GeckoView::Window::Open_t::name[];
 constexpr char GeckoView::Window::Open_t::signature[];
 
 constexpr char GeckoView::Window::Reattach_t::name[];
 constexpr char GeckoView::Window::Reattach_t::signature[];
 
+constexpr char GeckoView::Window::SetState_t::name[];
+constexpr char GeckoView::Window::SetState_t::signature[];
+
+auto GeckoView::Window::SetState(mozilla::jni::Object::Param a0) const -> void
+{
+    return mozilla::jni::Method<SetState_t>::Call(Window::mCtx, nullptr, a0);
+}
+
 const char PrefsHelper::name[] =
         "org/mozilla/gecko/PrefsHelper";
 
 constexpr char PrefsHelper::CallPrefHandler_t::name[];
 constexpr char PrefsHelper::CallPrefHandler_t::signature[];
 
 auto PrefsHelper::CallPrefHandler(mozilla::jni::Object::Param a0, int32_t a1, mozilla::jni::String::Param a2, bool a3, int32_t a4, mozilla::jni::String::Param a5) -> void
 {
--- a/widget/android/GeneratedJNIWrappers.h
+++ b/widget/android/GeneratedJNIWrappers.h
@@ -2878,44 +2878,24 @@ class GeckoView : public mozilla::jni::O
 public:
     static const char name[];
 
     explicit GeckoView(const Context& ctx) : ObjectBase<GeckoView>(ctx) {}
 
     class State;
     class Window;
 
-    struct SetState_t {
-        typedef GeckoView Owner;
-        typedef void ReturnType;
-        typedef void SetterType;
-        typedef mozilla::jni::Args<
-                mozilla::jni::Object::Param> Args;
-        static constexpr char name[] = "setState";
-        static constexpr char signature[] =
-                "(Lorg/mozilla/gecko/GeckoView$State;)V";
-        static const bool isStatic = false;
-        static const mozilla::jni::ExceptionMode exceptionMode =
-                mozilla::jni::ExceptionMode::ABORT;
-        static const mozilla::jni::CallingThread callingThread =
-                mozilla::jni::CallingThread::GECKO;
-        static const mozilla::jni::DispatchTarget dispatchTarget =
-                mozilla::jni::DispatchTarget::CURRENT;
-    };
-
-    auto SetState(mozilla::jni::Object::Param) const -> void;
-
     static const int32_t LOAD_DEFAULT = 0;
 
     static const int32_t LOAD_NEW_TAB = 1;
 
     static const int32_t LOAD_SWITCH_TAB = 2;
 
     static const mozilla::jni::CallingThread callingThread =
-            mozilla::jni::CallingThread::GECKO;
+            mozilla::jni::CallingThread::ANY;
 
 };
 
 class GeckoView::State : public mozilla::jni::ObjectBase<State>
 {
 public:
     static const char name[];
 
@@ -3063,16 +3043,36 @@ public:
         static const mozilla::jni::ExceptionMode exceptionMode =
                 mozilla::jni::ExceptionMode::ABORT;
         static const mozilla::jni::CallingThread callingThread =
                 mozilla::jni::CallingThread::ANY;
         static const mozilla::jni::DispatchTarget dispatchTarget =
                 mozilla::jni::DispatchTarget::PROXY;
     };
 
+    struct SetState_t {
+        typedef Window Owner;
+        typedef void ReturnType;
+        typedef void SetterType;
+        typedef mozilla::jni::Args<
+                mozilla::jni::Object::Param> Args;
+        static constexpr char name[] = "setState";
+        static constexpr char signature[] =
+                "(Lorg/mozilla/gecko/GeckoView$State;)V";
+        static const bool isStatic = false;
+        static const mozilla::jni::ExceptionMode exceptionMode =
+                mozilla::jni::ExceptionMode::ABORT;
+        static const mozilla::jni::CallingThread callingThread =
+                mozilla::jni::CallingThread::GECKO;
+        static const mozilla::jni::DispatchTarget dispatchTarget =
+                mozilla::jni::DispatchTarget::CURRENT;
+    };
+
+    auto SetState(mozilla::jni::Object::Param) const -> void;
+
     static const mozilla::jni::CallingThread callingThread =
             mozilla::jni::CallingThread::ANY;
 
     template<class Impl> class Natives;
 };
 
 class PrefsHelper : public mozilla::jni::ObjectBase<PrefsHelper>
 {
--- a/widget/android/nsWindow.cpp
+++ b/widget/android/nsWindow.cpp
@@ -237,17 +237,17 @@ public:
     Impl* operator->() const { return mImpl; }
 };
 
 class nsWindow::GeckoViewSupport final
     : public GeckoView::Window::Natives<GeckoViewSupport>
     , public SupportsWeakPtr<GeckoViewSupport>
 {
     nsWindow& window;
-    GeckoView::GlobalRef mView;
+    GeckoView::Window::GlobalRef mGeckoViewWindow;
 
 public:
     typedef GeckoView::Window::Natives<GeckoViewSupport> Base;
     typedef SupportsWeakPtr<GeckoViewSupport> SupportsWeakPtr;
 
     MOZ_DECLARE_WEAKREFERENCE_TYPENAME(GeckoViewSupport);
 
     template<typename Functor>
@@ -260,20 +260,19 @@ public:
             return aCall();
         }
 
         nsAppShell::PostEvent(mozilla::MakeUnique<WindowEvent<Functor>>(
                 mozilla::Move(aCall)));
     }
 
     GeckoViewSupport(nsWindow* aWindow,
-                     const GeckoView::Window::LocalRef& aInstance,
-                     GeckoView::Param aView)
+                     const GeckoView::Window::LocalRef& aInstance)
         : window(*aWindow)
-        , mView(aView)
+        , mGeckoViewWindow(aInstance)
     {
         Base::AttachNative(aInstance, static_cast<SupportsWeakPtr*>(this));
     }
 
     ~GeckoViewSupport();
 
     using Base::DisposeNative;
 
@@ -1247,17 +1246,17 @@ nsWindow::GeckoViewSupport::Open(const j
     nsCOMPtr<nsIWidget> widget = WidgetUtils::DOMWindowToWidget(pdomWindow);
     MOZ_ASSERT(widget);
 
     const auto window = static_cast<nsWindow*>(widget.get());
     window->SetScreenId(aScreenId);
 
     // Attach a new GeckoView support object to the new window.
     window->mGeckoViewSupport = mozilla::MakeUnique<GeckoViewSupport>(
-            window, GeckoView::Window::LocalRef(aCls.Env(), aWindow), aView);
+        window, (GeckoView::Window::LocalRef(aCls.Env(), aWindow)));
 
     window->mGeckoViewSupport->mDOMWindow = pdomWindow;
 
     // Attach a new GeckoEditable support object to the new window.
     auto editable = GeckoEditable::New(aView);
     auto editableChild = GeckoEditableChild::New(editable);
     editable->SetDefaultEditableChild(editableChild);
     window->mEditable = editable;
@@ -1292,16 +1291,17 @@ nsWindow::GeckoViewSupport::Close()
     }
 
     if (!mDOMWindow) {
         return;
     }
 
     mDOMWindow->ForceClose();
     mDOMWindow = nullptr;
+    mGeckoViewWindow = nullptr;
 }
 
 void
 nsWindow::GeckoViewSupport::Reattach(const GeckoView::Window::LocalRef& inst,
                                      GeckoView::Param aView,
                                      jni::Object::Param aCompositor,
                                      jni::Object::Param aDispatcher)
 {
@@ -2073,18 +2073,20 @@ nsWindow::GetEventTimeStamp(int64_t aEve
     int64_t tick =
         BaseTimeDurationPlatformUtils::TicksFromMilliseconds(aEventTime);
     return TimeStamp::FromSystemTime(tick);
 }
 
 void
 nsWindow::GeckoViewSupport::EnableEventDispatcher()
 {
-    MOZ_ASSERT(mView);
-    mView->SetState(GeckoView::State::READY());
+    if (!mGeckoViewWindow) {
+        return;
+    }
+    mGeckoViewWindow->SetState(GeckoView::State::READY());
 }
 
 void
 nsWindow::UserActivity()
 {
   if (!mIdleService) {
     mIdleService = do_GetService("@mozilla.org/widget/idleservice;1");
   }
--- a/xpcom/base/nsDebugImpl.cpp
+++ b/xpcom/base/nsDebugImpl.cpp
@@ -143,16 +143,26 @@ nsDebugImpl::Break(const char* aFile, in
 
 NS_IMETHODIMP
 nsDebugImpl::Abort(const char* aFile, int32_t aLine)
 {
   NS_DebugBreak(NS_DEBUG_ABORT, nullptr, nullptr, aFile, aLine);
   return NS_OK;
 }
 
+// From toolkit/library/rust/lib.rs
+extern "C" void intentional_panic(const char* message);
+
+NS_IMETHODIMP
+nsDebugImpl::RustPanic(const char* aMessage)
+{
+  intentional_panic(aMessage);
+  return NS_OK;
+}
+
 NS_IMETHODIMP
 nsDebugImpl::GetIsDebugBuild(bool* aResult)
 {
 #ifdef DEBUG
   *aResult = true;
 #else
   *aResult = false;
 #endif
--- a/xpcom/base/nsIDebug2.idl
+++ b/xpcom/base/nsIDebug2.idl
@@ -74,9 +74,16 @@ interface nsIDebug2 : nsISupports
     /**
      * Request the process to trigger a fatal abort.
      *
      * @param aFile file containing abort request
      * @param aLine line number of abort request
      */
     void abort(in string aFile, 
                in long aLine);
+
+    /**
+     * Request the process to trigger a fatal panic!() from Rust code.
+     *
+     * @param aMessage the string to pass to panic!().
+     */
+    void rustPanic(in string aMessage);
 };
--- a/xpcom/ds/nsTArray.h
+++ b/xpcom/ds/nsTArray.h
@@ -14,16 +14,17 @@
 #include "mozilla/Attributes.h"
 #include "mozilla/BinarySearch.h"
 #include "mozilla/fallible.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Move.h"
 #include "mozilla/ReverseIterator.h"
 #include "mozilla/TypeTraits.h"
+#include "mozilla/Span.h"
 
 #include <string.h>
 
 #include "nsCycleCollectionNoteChild.h"
 #include "nsAlgorithm.h"
 #include "nscore.h"
 #include "nsQuickSort.h"
 #include "nsDebug.h"
@@ -1111,16 +1112,28 @@ public:
   // Methods for reverse iterating.
   reverse_iterator rbegin() { return reverse_iterator(end()); }
   const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); }
   const_reverse_iterator crbegin() const { return rbegin(); }
   reverse_iterator rend() { return reverse_iterator(begin()); }
   const_reverse_iterator rend() const { return const_reverse_iterator(begin()); }
   const_reverse_iterator crend() const { return rend(); }
 
+  // Span integration
+
+  operator mozilla::Span<elem_type>()
+  {
+    return mozilla::Span<elem_type>(Elements(), Length());
+  }
+
+  operator mozilla::Span<const elem_type>() const
+  {
+    return mozilla::Span<const elem_type>(Elements(), Length());
+  }
+
   //
   // Search methods
   //
 
   // This method searches for the first element in this array that is equal
   // to the given element.
   // @param aItem  The item to search for.
   // @param aComp  The Comparator used to determine element equality.
@@ -1335,27 +1348,46 @@ public:
 protected:
   template<class Item, typename ActualAlloc = Alloc>
   elem_type* ReplaceElementsAt(index_type aStart, size_type aCount,
                                const nsTArray<Item>& aArray)
   {
     return ReplaceElementsAt<Item, ActualAlloc>(
       aStart, aCount, aArray.Elements(), aArray.Length());
   }
+
+  template<class Item, typename ActualAlloc = Alloc>
+  elem_type* ReplaceElementsAt(index_type aStart,
+                               size_type aCount,
+                               mozilla::Span<const Item> aSpan)
+  {
+    return ReplaceElementsAt<Item, ActualAlloc>(
+      aStart, aCount, aSpan.Elements(), aSpan.Length());
+  }
+
 public:
 
   template<class Item>
   MOZ_MUST_USE
   elem_type* ReplaceElementsAt(index_type aStart, size_type aCount,
                                const nsTArray<Item>& aArray,
                                const mozilla::fallible_t&)
   {
     return ReplaceElementsAt<Item, FallibleAlloc>(aStart, aCount, aArray);
   }
 
+  template<class Item>
+  MOZ_MUST_USE elem_type* ReplaceElementsAt(index_type aStart,
+                                            size_type aCount,
+                                            mozilla::Span<const Item> aSpan,
+                                            const mozilla::fallible_t&)
+  {
+    return ReplaceElementsAt<Item, FallibleAlloc>(aStart, aCount, aSpan);
+  }
+
   // A variation on the ReplaceElementsAt method defined above.
 protected:
   template<class Item, typename ActualAlloc = Alloc>
   elem_type* ReplaceElementsAt(index_type aStart, size_type aCount,
                                const Item& aItem)
   {
     return ReplaceElementsAt<Item, ActualAlloc>(aStart, aCount, &aItem, 1);
   }
@@ -1398,27 +1430,44 @@ public:
 protected:
   template<class Item, class Allocator, typename ActualAlloc = Alloc>
   elem_type* InsertElementsAt(index_type aIndex,
                               const nsTArray_Impl<Item, Allocator>& aArray)
   {
     return ReplaceElementsAt<Item, ActualAlloc>(
       aIndex, 0, aArray.Elements(), aArray.Length());
   }
+
+  template<class Item, typename ActualAlloc = Alloc>
+  elem_type* InsertElementsAt(index_type aIndex,
+                              mozilla::Span<const Item> aSpan)
+  {
+    return ReplaceElementsAt<Item, ActualAlloc>(
+      aIndex, 0, aSpan.Elements(), aSpan.Length());
+  }
+
 public:
 
   template<class Item, class Allocator>
   MOZ_MUST_USE
   elem_type* InsertElementsAt(index_type aIndex,
                               const nsTArray_Impl<Item, Allocator>& aArray,
                               const mozilla::fallible_t&)
   {
     return InsertElementsAt<Item, Allocator, FallibleAlloc>(aIndex, aArray);
   }
 
+  template<class Item>
+  MOZ_MUST_USE elem_type* InsertElementsAt(index_type aIndex,
+                                           mozilla::Span<const Item> aSpan,
+                                           const mozilla::fallible_t&)
+  {
+    return InsertElementsAt<Item, FallibleAlloc>(aIndex, aSpan);
+  }
+
   // Insert a new element without copy-constructing. This is useful to avoid
   // temporaries.
   // @return A pointer to the newly inserted element, or null on OOM.
 protected:
   template<typename ActualAlloc = Alloc>
   elem_type* InsertElementAt(index_type aIndex);
 
 public:
@@ -1543,32 +1592,48 @@ public:
   // @param aArray    The elements to append to this array.
   // @param aArrayLen The number of elements to append to this array.
   // @return          A pointer to the new elements in the array, or null if
   //                  the operation failed due to insufficient memory.
 protected:
   template<class Item, typename ActualAlloc = Alloc>
   elem_type* AppendElements(const Item* aArray, size_type aArrayLen);
 
+  template<class Item, typename ActualAlloc = Alloc>
+  elem_type* AppendElements(mozilla::Span<const Item> aSpan)
+  {
+    return AppendElements<Item, FallibleAlloc>(aSpan.Elements(),
+                                               aSpan.Length());
+  }
+
   template<class Item, size_t Length, typename ActualAlloc = Alloc>
   elem_type* AppendElements(const mozilla::Array<Item, Length>& aArray)
   {
     return AppendElements<Item, ActualAlloc>(&aArray[0], Length);
   }
 
 public:
 
   template<class Item>
   /* MOZ_MUST_USE */
   elem_type* AppendElements(const Item* aArray, size_type aArrayLen,
                             const mozilla::fallible_t&)
   {
     return AppendElements<Item, FallibleAlloc>(aArray, aArrayLen);
   }
 
+  template<class Item>
+  /* MOZ_MUST_USE */
+  elem_type* AppendElements(mozilla::Span<const Item> aSpan,
+                            const mozilla::fallible_t&)
+  {
+    return AppendElements<Item, FallibleAlloc>(aSpan.Elements(),
+                                               aSpan.Length());
+  }
+
   // A variation on the AppendElements method defined above.
 protected:
   template<class Item, class Allocator, typename ActualAlloc = Alloc>
   elem_type* AppendElements(const nsTArray_Impl<Item, Allocator>& aArray)
   {
     return AppendElements<Item, ActualAlloc>(aArray.Elements(), aArray.Length());
   }
 public:
@@ -2392,16 +2457,35 @@ class AutoTArray<E, 0> : public nsTArray
 };
 
 template<class E, size_t N>
 struct nsTArray_CopyChooser<AutoTArray<E, N>>
 {
   typedef nsTArray_CopyWithConstructors<AutoTArray<E, N>> Type;
 };
 
+// Span integration
+namespace mozilla {
+
+template<class ElementType, class TArrayAlloc>
+Span<ElementType>
+MakeSpan(nsTArray_Impl<ElementType, TArrayAlloc>& aTArray)
+{
+  return aTArray;
+}
+
+template<class ElementType, class TArrayAlloc>
+Span<const ElementType>
+MakeSpan(const nsTArray_Impl<ElementType, TArrayAlloc>& aTArray)
+{
+  return aTArray;
+}
+
+} // namespace mozilla
+
 // Assert that AutoTArray doesn't have any extra padding inside.
 //
 // It's important that the data stored in this auto array takes up a multiple of
 // 8 bytes; e.g. AutoTArray<uint32_t, 1> wouldn't work.  Since AutoTArray
 // contains a pointer, its size must be a multiple of alignof(void*).  (This is
 // because any type may be placed into an array, and there's no padding between
 // elements of an array.)  The compiler pads the end of the structure to
 // enforce this rule.
--- a/xpcom/string/nsTSubstring.h
+++ b/xpcom/string/nsTSubstring.h
@@ -4,16 +4,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 // IWYU pragma: private, include "nsString.h"
 
 #include "mozilla/Casting.h"
 #include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/MemoryReporting.h"
+#include "mozilla/IntegerTypeTraits.h"
+#include "mozilla/Span.h"
 
 #ifndef MOZILLA_INTERNAL_API
 #error "Using XPCOM strings is limited to code linked into libxul."
 #endif
 
 /**
  * The base for string comparators
  */
@@ -917,16 +919,78 @@ public:
   size_type GetMutableData(wchar_t** aData, size_type aNewLen,
                            const fallible_t& aFallible)
   {
     return GetMutableData(reinterpret_cast<char16_t**>(aData), aNewLen,
                           aFallible);
   }
 #endif
 
+  /**
+   * Span integration
+   */
+
+  operator mozilla::Span<char_type>()
+  {
+    return mozilla::MakeSpan(BeginWriting(), Length());
+  }
+
+  operator mozilla::Span<const char_type>() const
+  {
+    return mozilla::MakeSpan(BeginReading(), Length());
+  }
+
+  void Append(mozilla::Span<const char_type> aSpan)
+  {
+    auto len = aSpan.Length();
+    MOZ_RELEASE_ASSERT(len <= mozilla::MaxValue<size_type>::value);
+    Append(aSpan.Elements(), len);
+  }
+
+  MOZ_MUST_USE bool Append(mozilla::Span<const char_type> aSpan,
+                           const fallible_t& aFallible)
+  {
+    auto len = aSpan.Length();
+    if (len > mozilla::MaxValue<size_type>::value) {
+      return false;
+    }
+    return Append(aSpan.Elements(), len, aFallible);
+  }
+
+#if !defined(CharT_is_PRUnichar)
+  operator mozilla::Span<uint8_t>()
+  {
+    return mozilla::MakeSpan(reinterpret_cast<uint8_t*>(BeginWriting()),
+                             Length());
+  }
+
+  operator mozilla::Span<const uint8_t>() const
+  {
+    return mozilla::MakeSpan(reinterpret_cast<const uint8_t*>(BeginReading()),
+                             Length());
+  }
+
+  void Append(mozilla::Span<const uint8_t> aSpan)
+  {
+    auto len = aSpan.Length();
+    MOZ_RELEASE_ASSERT(len <= mozilla::MaxValue<size_type>::value);
+    Append(reinterpret_cast<const char*>(aSpan.Elements()), len);
+  }
+
+  MOZ_MUST_USE bool Append(mozilla::Span<const uint8_t> aSpan,
+                           const fallible_t& aFallible)
+  {
+    auto len = aSpan.Length();
+    if (len > mozilla::MaxValue<size_type>::value) {
+      return false;
+    }
+    return Append(
+      reinterpret_cast<const char*>(aSpan.Elements()), len, aFallible);
+  }
+#endif
 
   /**
    * string data is never null, but can be marked void.  if true, the
    * string will be truncated.  @see nsTSubstring::IsVoid
    */
 
   void NS_FASTCALL SetIsVoid(bool);
 
@@ -1267,8 +1331,27 @@ public:
   }
 
   const nsTDependentSubstring_CharT& Get(const size_type index) const
   {
     MOZ_ASSERT(index < mArraySize);
     return mArray[index];
   }
 };
+
+/**
+ * Span integration
+ */
+namespace mozilla {
+
+inline Span<CharT>
+MakeSpan(nsTSubstring_CharT& aString)
+{
+  return aString;
+}
+
+inline Span<const CharT>
+MakeSpan(const nsTSubstring_CharT& aString)
+{
+  return aString;
+}
+
+} // namespace mozilla