diff --git a/browser/components/sessionstore/SessionCookies.jsm b/browser/components/sessionstore/SessionCookies.jsm index 49763ef6fd69..882ec45e66e1 100644 --- a/browser/components/sessionstore/SessionCookies.jsm +++ b/browser/components/sessionstore/SessionCookies.jsm @@ -28,8 +28,8 @@ this.SessionCookies = Object.freeze({ SessionCookiesInternal.update(windows); }, - getHostsForWindow(window, checkPrivacy = false) { - return SessionCookiesInternal.getHostsForWindow(window, checkPrivacy); + getHostsForWindow(window) { + return SessionCookiesInternal.getHostsForWindow(window); }, restore(cookies) { @@ -58,16 +58,20 @@ var SessionCookiesInternal = { 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); + } } } } @@ -87,16 +91,14 @@ var SessionCookiesInternal = { * * @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); } } @@ -171,26 +173,19 @@ var SessionCookiesInternal = { * 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); } } }, diff --git a/browser/components/sessionstore/test/browser_cookies.js b/browser/components/sessionstore/test/browser_cookies.js index be94d2046075..5e346642928c 100644 --- a/browser/components/sessionstore/test/browser_cookies.js +++ b/browser/components/sessionstore/test/browser_cookies.js @@ -6,7 +6,7 @@ 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(); }); @@ -79,6 +79,104 @@ add_task(function* test_run() { }); }); +/** + * 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 @@ -96,6 +194,10 @@ var testCookieCollection = async function(params) { urlParams.append("domain", params.domain); } + if (params.isSecure) { + urlParams.append("secure", "1"); + } + // Construct request URI. let requestUri = `${params.host}${PATH}browser_cookies.sjs?${urlParams}`; diff --git a/browser/components/sessionstore/test/browser_cookies.sjs b/browser/components/sessionstore/test/browser_cookies.sjs index bffbd66d9797..67d20ccd8407 100644 --- a/browser/components/sessionstore/test/browser_cookies.sjs +++ b/browser/components/sessionstore/test/browser_cookies.sjs @@ -12,10 +12,15 @@ function handleRequest(req, resp) { 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("hi"); } diff --git a/dom/media/webaudio/test/test_nodeCreationDocumentGone.html b/dom/media/webaudio/test/test_nodeCreationDocumentGone.html index da164401b02d..b586569bbf0f 100644 --- a/dom/media/webaudio/test/test_nodeCreationDocumentGone.html +++ b/dom/media/webaudio/test/test_nodeCreationDocumentGone.html @@ -9,28 +9,36 @@
 
+
+
diff --git a/dom/xslt/crashtests/crashtests.list b/dom/xslt/crashtests/crashtests.list
index 5958655d6473..8e9a9e72dbdf 100644
--- a/dom/xslt/crashtests/crashtests.list
+++ b/dom/xslt/crashtests/crashtests.list
@@ -18,3 +18,4 @@ load 667315.xml
 load 1089049.html
 load 1205163.xml
 load 1243337.xml
+load 1338277.html
diff --git a/dom/xslt/xpath/XPathExpression.cpp b/dom/xslt/xpath/XPathExpression.cpp
index 2f92a120fbac..88bbc1b84fe0 100644
--- a/dom/xslt/xpath/XPathExpression.cpp
+++ b/dom/xslt/xpath/XPathExpression.cpp
@@ -206,10 +206,12 @@ EvalContextImpl::getVariable(int32_t aNamespace,
     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*
diff --git a/dom/xslt/xpath/txExpr.h b/dom/xslt/xpath/txExpr.h
index 562fca7a3293..4b0b8bcbc3a6 100644
--- a/dom/xslt/xpath/txExpr.h
+++ b/dom/xslt/xpath/txExpr.h
@@ -28,6 +28,7 @@ class txIMatchContext;
 class txIEvalContext;
 class txNodeSet;
 class txXPathNode;
+class txXPathTreeWalker;
 
 /**
  * A Base Class for all XSL Expressions
@@ -394,8 +395,9 @@ public:
      * 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;
 
     /**
@@ -424,7 +426,9 @@ public:
 
 #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;
 
@@ -630,10 +634,30 @@ public:
     }
 
 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 mNodeTest;
     LocationStepType mAxisIdentifier;
diff --git a/dom/xslt/xpath/txForwardContext.cpp b/dom/xslt/xpath/txForwardContext.cpp
index e2367a3aeb71..73bd6a626938 100644
--- a/dom/xslt/xpath/txForwardContext.cpp
+++ b/dom/xslt/xpath/txForwardContext.cpp
@@ -30,10 +30,11 @@ nsresult txForwardContext::getVariable(int32_t aNamespace, nsIAtom* aLName,
     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()
diff --git a/dom/xslt/xpath/txIXPathContext.h b/dom/xslt/xpath/txIXPathContext.h
index ed5f81a53265..66b1905d363c 100644
--- a/dom/xslt/xpath/txIXPathContext.h
+++ b/dom/xslt/xpath/txIXPathContext.h
@@ -88,7 +88,8 @@ public:
      * 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
@@ -106,7 +107,7 @@ public:
 #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)
diff --git a/dom/xslt/xpath/txLocationStep.cpp b/dom/xslt/xpath/txLocationStep.cpp
index f945b750fe44..6351359290aa 100644
--- a/dom/xslt/xpath/txLocationStep.cpp
+++ b/dom/xslt/xpath/txLocationStep.cpp
@@ -49,9 +49,8 @@ LocationStep::evaluate(txIEvalContext* aContext, txAExprResult** aResult)
             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;
@@ -63,29 +62,29 @@ LocationStep::evaluate(txIEvalContext* aContext, txAExprResult** aResult)
             }
 
             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()) {
@@ -95,11 +94,11 @@ LocationStep::evaluate(txIEvalContext* aContext, txAExprResult** aResult)
                 }
             }
             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()) {
@@ -113,9 +112,8 @@ LocationStep::evaluate(txIEvalContext* aContext, txAExprResult** aResult)
         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;
         }
@@ -127,9 +125,9 @@ LocationStep::evaluate(txIEvalContext* aContext, txAExprResult** aResult)
             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;
         }
@@ -145,11 +143,11 @@ LocationStep::evaluate(txIEvalContext* aContext, txAExprResult** aResult)
                 }
             }
             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()) {
@@ -165,17 +163,15 @@ LocationStep::evaluate(txIEvalContext* aContext, txAExprResult** aResult)
             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
@@ -185,9 +181,8 @@ LocationStep::evaluate(txIEvalContext* aContext, txAExprResult** aResult)
             }
 
             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;
         }
@@ -206,42 +201,62 @@ LocationStep::evaluate(txIEvalContext* aContext, txAExprResult** aResult)
     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);
-    if (!walker.moveToFirstChild()) {
-        return;
-    }
+    bool matched;
+    const txXPathNode& child = aWalker.getCurrentPosition();
+    nsresult rv = mNodeTest->matches(child, aContext, matched);
+    NS_ENSURE_SUCCESS(rv, rv);
 
-    do {
-        const txXPathNode& child = walker.getCurrentPosition();
-        if (mNodeTest->matches(child, aCs)) {
-            aNodes->append(child);
-        }
-        fromDescendants(child, aCs, aNodes);
-    } while (walker.moveToNextSibling());
+    if (matched) {
+        aNodes->append(child);
+    }
+    return NS_OK;
 }
 
-void LocationStep::fromDescendantsRev(const txXPathNode& aNode,
-                                      txIMatchContext* aCs,
-                                      txNodeSet* aNodes)
+nsresult
+LocationStep::appendMatchingDescendants(const txXPathTreeWalker& aWalker,
+                                        txIMatchContext* aContext,
+                                        txNodeSet* aNodes)
 {
-    txXPathTreeWalker walker(aNode);
-    if (!walker.moveToLastChild()) {
-        return;
+    txXPathTreeWalker walker(aWalker);
+    if (!walker.moveToFirstChild()) {
+        return NS_OK;
     }
 
     do {
-        const txXPathNode& child = walker.getCurrentPosition();
-        fromDescendantsRev(child, aCs, aNodes);
+        nsresult rv = appendIfMatching(walker, aContext, aNodes);
+        NS_ENSURE_SUCCESS(rv, rv);
 
-        if (mNodeTest->matches(child, aCs)) {
-            aNodes->append(child);
-        }
+        rv = appendMatchingDescendants(walker, aContext, aNodes);
+        NS_ENSURE_SUCCESS(rv, rv);
+    } while (walker.moveToNextSibling());
 
+    return NS_OK;
+}
+
+nsresult
+LocationStep::appendMatchingDescendantsRev(const txXPathTreeWalker& aWalker,
+                                           txIMatchContext* aContext,
+                                           txNodeSet* aNodes)
+{
+    txXPathTreeWalker walker(aWalker);
+    if (!walker.moveToLastChild()) {
+        return NS_OK;
+    }
+
+    do {
+        nsresult rv = appendMatchingDescendantsRev(walker, aContext, aNodes);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        rv = appendIfMatching(walker, aContext, aNodes);
+        NS_ENSURE_SUCCESS(rv, rv);
     } while (walker.moveToPreviousSibling());
+
+    return NS_OK;
 }
 
 Expr::ExprType
diff --git a/dom/xslt/xpath/txNameTest.cpp b/dom/xslt/xpath/txNameTest.cpp
index bad0432825ea..d2bb87c72716 100644
--- a/dom/xslt/xpath/txNameTest.cpp
+++ b/dom/xslt/xpath/txNameTest.cpp
@@ -23,7 +23,9 @@ txNameTest::txNameTest(nsIAtom* aPrefix, nsIAtom* aLocalName, int32_t aNSID,
                  "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)) ||
@@ -31,26 +33,34 @@ bool txNameTest::matches(const txXPathNode& aNode, txIMatchContext* aContext)
          !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;
 }
 
 /*
diff --git a/dom/xslt/xpath/txNodeSetContext.cpp b/dom/xslt/xpath/txNodeSetContext.cpp
index 7d98391fdea0..578e1d82f13c 100644
--- a/dom/xslt/xpath/txNodeSetContext.cpp
+++ b/dom/xslt/xpath/txNodeSetContext.cpp
@@ -29,10 +29,11 @@ nsresult txNodeSetContext::getVariable(int32_t aNamespace, nsIAtom* aLName,
     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()
diff --git a/dom/xslt/xpath/txNodeTypeTest.cpp b/dom/xslt/xpath/txNodeTypeTest.cpp
index 650a1ae5fefc..4c519d38276c 100644
--- a/dom/xslt/xpath/txNodeTypeTest.cpp
+++ b/dom/xslt/xpath/txNodeTypeTest.cpp
@@ -8,32 +8,54 @@
 #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
diff --git a/dom/xslt/xpath/txPathExpr.cpp b/dom/xslt/xpath/txPathExpr.cpp
index bcff47e9f788..d36d615fc71c 100644
--- a/dom/xslt/xpath/txPathExpr.cpp
+++ b/dom/xslt/xpath/txPathExpr.cpp
@@ -160,7 +160,9 @@ PathExpr::evalDescendants(Expr* aStep, const txXPathNode& aNode,
 
     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()) {
diff --git a/dom/xslt/xpath/txPredicatedNodeTest.cpp b/dom/xslt/xpath/txPredicatedNodeTest.cpp
index 4726f48f280b..598f05f78299 100644
--- a/dom/xslt/xpath/txPredicatedNodeTest.cpp
+++ b/dom/xslt/xpath/txPredicatedNodeTest.cpp
@@ -16,20 +16,25 @@ txPredicatedNodeTest::txPredicatedNodeTest(txNodeTest* aNodeTest,
                  "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 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
diff --git a/dom/xslt/xpath/txSingleNodeContext.h b/dom/xslt/xpath/txSingleNodeContext.h
index e66083d80ac2..dd470294c645 100644
--- a/dom/xslt/xpath/txSingleNodeContext.h
+++ b/dom/xslt/xpath/txSingleNodeContext.h
@@ -27,10 +27,11 @@ public:
         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
diff --git a/dom/xslt/xpath/txUnionNodeTest.cpp b/dom/xslt/xpath/txUnionNodeTest.cpp
index 421ea680c0a4..b8ac34f87d4f 100644
--- a/dom/xslt/xpath/txUnionNodeTest.cpp
+++ b/dom/xslt/xpath/txUnionNodeTest.cpp
@@ -10,18 +10,22 @@
 #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
diff --git a/dom/xslt/xpath/txXPathOptimizer.cpp b/dom/xslt/xpath/txXPathOptimizer.cpp
index 756dd253d7f4..070302692b24 100644
--- a/dom/xslt/xpath/txXPathOptimizer.cpp
+++ b/dom/xslt/xpath/txXPathOptimizer.cpp
@@ -26,7 +26,7 @@ public:
     {
         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");
     }
diff --git a/dom/xslt/xslt/txExecutionState.cpp b/dom/xslt/xslt/txExecutionState.cpp
index a32fdb0b8065..de439f78953b 100644
--- a/dom/xslt/xslt/txExecutionState.cpp
+++ b/dom/xslt/xslt/txExecutionState.cpp
@@ -87,10 +87,7 @@ txExecutionState::~txExecutionState()
 
     txStackIterator handlerIter(&mResultHandlerStack);
     while (handlerIter.hasNext()) {
-        txAXMLEventHandler* handler = (txAXMLEventHandler*)handlerIter.next();
-        if (handler != mObsoleteHandler) {
-          delete handler;
-        }
+        delete (txAXMLEventHandler*)handlerIter.next();
     }
 
     txStackIterator paramIter(&mParamStack);
@@ -141,8 +138,11 @@ txExecutionState::init(const txXPathNode& aNode,
     // 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);
@@ -162,17 +162,6 @@ txExecutionState::end(nsresult aResult)
     return mOutputHandler->endDocument(aResult);
 }
 
-void
-txExecutionState::popAndDeleteEvalContext()
-{
-  if (!mEvalContextStack.isEmpty()) {
-    auto ctx = popEvalContext();
-    if (ctx != mInitialEvalContext) {
-      delete ctx;
-    }
-  }
-}
-
 void
 txExecutionState::popAndDeleteEvalContextUntil(txIEvalContext* aContext)
 {
@@ -306,10 +295,10 @@ txExecutionState::getVariable(int32_t aNamespace, nsIAtom* aLName,
     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*
diff --git a/dom/xslt/xslt/txExecutionState.h b/dom/xslt/xslt/txExecutionState.h
index 3aa6d5a660bf..44f1918c86b0 100644
--- a/dom/xslt/xslt/txExecutionState.h
+++ b/dom/xslt/xslt/txExecutionState.h
@@ -95,8 +95,6 @@ public:
     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
diff --git a/dom/xslt/xslt/txInstructions.cpp b/dom/xslt/xslt/txInstructions.cpp
index f769680474f3..e891b2f3b0bb 100644
--- a/dom/xslt/xslt/txInstructions.cpp
+++ b/dom/xslt/xslt/txInstructions.cpp
@@ -27,9 +27,11 @@ 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);
 
@@ -53,9 +55,11 @@ txApplyImports::execute(txExecutionState& aEs)
 
     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);
 
@@ -76,9 +80,11 @@ 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);
 
@@ -470,7 +476,7 @@ txLoopNodeSet::execute(txExecutionState& aEs)
     txNodeSetContext* context =
         static_cast(aEs.getEvalContext());
     if (!context->hasNext()) {
-        aEs.popAndDeleteEvalContext();
+        delete aEs.popEvalContext();
 
         return NS_OK;
     }
diff --git a/dom/xslt/xslt/txKeyFunctionCall.cpp b/dom/xslt/xslt/txKeyFunctionCall.cpp
index ae76275ffddf..d10229f24694 100644
--- a/dom/xslt/xslt/txKeyFunctionCall.cpp
+++ b/dom/xslt/xslt/txKeyFunctionCall.cpp
@@ -338,7 +338,11 @@ nsresult txXSLKey::testNode(const txXPathNode& aNode,
     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);
diff --git a/dom/xslt/xslt/txMozillaXSLTProcessor.cpp b/dom/xslt/xslt/txMozillaXSLTProcessor.cpp
index 35c4b08b3d0b..9424d1110593 100644
--- a/dom/xslt/xslt/txMozillaXSLTProcessor.cpp
+++ b/dom/xslt/xslt/txMozillaXSLTProcessor.cpp
@@ -441,9 +441,11 @@ public:
         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()
     {
diff --git a/dom/xslt/xslt/txStylesheet.cpp b/dom/xslt/xslt/txStylesheet.cpp
index b680da01f94a..37052c00f5fc 100644
--- a/dom/xslt/xslt/txStylesheet.cpp
+++ b/dom/xslt/xslt/txStylesheet.cpp
@@ -88,17 +88,18 @@ txStylesheet::~txStylesheet()
     }
 }
 
-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);
 
@@ -115,7 +116,7 @@ txStylesheet::findTemplate(const txXPathNode& aNode,
 #endif
 
     ImportFrame* frame;
-    while (!matchTemplate &&
+    while (!*aTemplate &&
            (frame = static_cast(frameIter.next())) &&
            frame != endFrame) {
 
@@ -126,10 +127,14 @@ txStylesheet::findTemplate(const txXPathNode& aNode,
         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;
@@ -145,7 +150,7 @@ txStylesheet::findTemplate(const txXPathNode& aNode,
           aMode.mLocalName->ToString(mode);
       }
       txXPathNodeUtils::getNodeName(aNode, nodeName);
-      if (matchTemplate) {
+      if (*aTemplate) {
           nsAutoString matchAttr;
 #ifdef TX_TO_STRING
           match->toString(matchAttr);
@@ -164,23 +169,23 @@ txStylesheet::findTemplate(const txXPathNode& aNode,
       }
     }
 
-    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*
@@ -219,37 +224,53 @@ 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
diff --git a/dom/xslt/xslt/txStylesheet.h b/dom/xslt/xslt/txStylesheet.h
index 978527a13e4d..370f4d6fa78f 100644
--- a/dom/xslt/xslt/txStylesheet.h
+++ b/dom/xslt/xslt/txStylesheet.h
@@ -35,19 +35,21 @@ public:
     
     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& 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.
@@ -166,8 +168,10 @@ public:
     {
     }
 
-    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() {
diff --git a/dom/xslt/xslt/txXSLTNumber.cpp b/dom/xslt/xslt/txXSLTNumber.cpp
index 237a46ec539d..96e21dfe01d1 100644
--- a/dom/xslt/xslt/txXSLTNumber.cpp
+++ b/dom/xslt/xslt/txXSLTNumber.cpp
@@ -108,13 +108,12 @@ txXSLTNumber::getValueList(Expr* aValueExpr, txPattern* aCountPattern,
     // Otherwise use count/from/level
 
     txPattern* countPattern = aCountPattern;
-    bool ownsCountPattern = false;
+    nsAutoPtr 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) {
@@ -160,7 +159,7 @@ txXSLTNumber::getValueList(Expr* aValueExpr, txPattern* aCountPattern,
             }
         }
         MOZ_ASSERT(nodeTest);
-        countPattern = new txStepPattern(nodeTest, false);
+        countPattern = newCountPattern = new txStepPattern(nodeTest, false);
     }
 
 
@@ -170,14 +169,28 @@ txXSLTNumber::getValueList(Expr* aValueExpr, txPattern* aCountPattern,
     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;
             }
 
@@ -189,7 +202,12 @@ txXSLTNumber::getValueList(Expr* aValueExpr, txPattern* aCountPattern,
         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;
                 }
             }
@@ -205,16 +223,28 @@ txXSLTNumber::getValueList(Expr* aValueExpr, txPattern* aCountPattern,
         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());
 
@@ -232,13 +262,22 @@ txXSLTNumber::getValueList(Expr* aValueExpr, txPattern* aCountPattern,
 
         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;
             }
 
@@ -256,10 +295,6 @@ txXSLTNumber::getValueList(Expr* aValueExpr, txPattern* aCountPattern,
         }
     }
 
-    if (ownsCountPattern) {
-        delete countPattern;
-    }
-    
     return NS_OK;
 }
 
@@ -388,18 +423,27 @@ txXSLTNumber::getCounters(Expr* aGroupSize, Expr* aGroupSeparator,
     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
diff --git a/dom/xslt/xslt/txXSLTNumber.h b/dom/xslt/xslt/txXSLTNumber.h
index e6cdd421009c..73326d3ab513 100644
--- a/dom/xslt/xslt/txXSLTNumber.h
+++ b/dom/xslt/xslt/txXSLTNumber.h
@@ -46,9 +46,10 @@ private:
      * 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);
 
diff --git a/dom/xslt/xslt/txXSLTPatterns.cpp b/dom/xslt/xslt/txXSLTPatterns.cpp
index c8fe71f72369..e5775dd3ef3b 100644
--- a/dom/xslt/xslt/txXSLTPatterns.cpp
+++ b/dom/xslt/xslt/txXSLTPatterns.cpp
@@ -32,15 +32,25 @@ double txUnionPattern::getDefaultPriority()
  * 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
@@ -103,7 +113,9 @@ nsresult txLocPathPattern::addStep(txPattern* aPattern, bool 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");
 
@@ -121,18 +133,39 @@ bool txLocPathPattern::matches(const txXPathNode& aNode, txIMatchContext* aConte
 
     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();
     }
@@ -142,12 +175,19 @@ bool txLocPathPattern::matches(const txXPathNode& aNode, txIMatchContext* aConte
     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;
@@ -164,7 +204,9 @@ bool txLocPathPattern::matches(const txXPathNode& aNode, txIMatchContext* aConte
         }
     }
 
-    return true;
+    aMatched = true;
+
+    return NS_OK;
 } // txLocPathPattern::matches
 
 double txLocPathPattern::getDefaultPriority()
@@ -218,9 +260,13 @@ txLocPathPattern::toString(nsAString& aDest)
  * 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()
@@ -264,10 +310,14 @@ txIdPattern::txIdPattern(const nsSubstring& aString)
     }
 }
 
-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
@@ -275,7 +325,9 @@ bool txIdPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext)
     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()
@@ -320,18 +372,22 @@ txIdPattern::toString(nsAString& aDest)
  * 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 contextDoc(txXPathNodeUtils::getOwnerDocument(aNode));
-    NS_ENSURE_TRUE(contextDoc, false);
+    NS_ENSURE_TRUE(contextDoc, NS_ERROR_FAILURE);
 
     RefPtr 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()
@@ -373,21 +429,32 @@ txKeyPattern::toString(nsAString& aDest)
  * 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;
     }
 
     /*
@@ -410,13 +477,17 @@ bool txStepPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext)
 
     // Create the context node set for evaluating the predicates
     RefPtr 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() :
@@ -426,7 +497,7 @@ bool txStepPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext)
     Expr* predicate = mPredicates[0];
     RefPtr 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) {
@@ -437,7 +508,7 @@ bool txStepPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext)
             predContext.next();
             RefPtr exprResult;
             rv = predicate->evaluate(&predContext, getter_AddRefs(exprResult));
-            NS_ENSURE_SUCCESS(rv, false);
+            NS_ENSURE_SUCCESS(rv, rv);
 
             switch(exprResult->getResultType()) {
                 case txAExprResult::NUMBER:
@@ -464,20 +535,25 @@ bool txStepPattern::matches(const txXPathNode& aNode, txIMatchContext* aContext)
         nodes->clear();
         nodes->append(*newNodes);
         if (!contextIsInPredicate) {
-            return false;
+            aMatched = false;
+
+            return NS_OK;
         }
         predicate = mPredicates[i];
     }
     txForwardContext evalContext(aContext, aNode, nodes);
     RefPtr 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()
diff --git a/dom/xslt/xslt/txXSLTPatterns.h b/dom/xslt/xslt/txXSLTPatterns.h
index d93d54fe0eec..e7bab74f05c0 100644
--- a/dom/xslt/xslt/txXSLTPatterns.h
+++ b/dom/xslt/xslt/txXSLTPatterns.h
@@ -26,8 +26,9 @@ public:
     /*
      * 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.
@@ -86,7 +87,8 @@ public:
 };
 
 #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; \
diff --git a/image/encoders/bmp/nsBMPEncoder.cpp b/image/encoders/bmp/nsBMPEncoder.cpp
index 97f03f690be5..eda273604906 100644
--- a/image/encoders/bmp/nsBMPEncoder.cpp
+++ b/image/encoders/bmp/nsBMPEncoder.cpp
@@ -10,6 +10,7 @@
 #include "nsString.h"
 #include "nsStreamUtils.h"
 #include "nsTArray.h"
+#include "mozilla/CheckedInt.h"
 
 using namespace mozilla;
 using namespace mozilla::image;
@@ -58,6 +59,11 @@ nsBMPEncoder::InitFromData(const uint8_t* aData,
     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) ||
@@ -86,19 +92,19 @@ nsBMPEncoder::InitFromData(const uint8_t* aData,
 
 // 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;
@@ -125,14 +131,21 @@ nsBMPEncoder::StartImageEncode(uint32_t aWidth,
 
   // 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(malloc(mImageBufferSize));
@@ -187,12 +200,26 @@ nsBMPEncoder::AddImageFrame(const uint8_t* aData,
     return NS_ERROR_INVALID_ARG;
   }
 
-  auto row = MakeUniqueFallible(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(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) {
@@ -256,7 +283,7 @@ nsBMPEncoder::EndImageEncode()
 // 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;
@@ -424,7 +451,7 @@ nsBMPEncoder::ConvertHostARGBRow(const uint8_t* aSrc,
                                  const UniquePtr& 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++) {
@@ -473,8 +500,8 @@ nsBMPEncoder::NotifyListener()
 }
 
 // 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));
@@ -491,13 +518,25 @@ nsBMPEncoder::InitFileHeader(Version aVersion, uint32_t aBPP, uint32_t aWidth,
   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) \
@@ -505,8 +544,8 @@ nsBMPEncoder::InitFileHeader(Version aVersion, uint32_t aBPP, uint32_t aWidth,
     *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));
@@ -516,18 +555,39 @@ nsBMPEncoder::InitInfoHeader(Version aVersion, uint32_t aBPP, uint32_t aWidth,
     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;
@@ -554,6 +614,8 @@ nsBMPEncoder::InitInfoHeader(Version aVersion, uint32_t aBPP, uint32_t aWidth,
       mBMPInfoHeader.profile_size = 0;
       mBMPInfoHeader.reserved = 0;
   }
+
+  return NS_OK;
 }
 
 // Encodes the BMP file header mBMPFileHeader
diff --git a/image/encoders/bmp/nsBMPEncoder.h b/image/encoders/bmp/nsBMPEncoder.h
index c531fc28db8b..b90af4d19744 100644
--- a/image/encoders/bmp/nsBMPEncoder.h
+++ b/image/encoders/bmp/nsBMPEncoder.h
@@ -104,7 +104,7 @@ protected:
 
   // 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& aDest,
@@ -113,11 +113,11 @@ protected:
   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();
diff --git a/image/encoders/ico/nsICOEncoder.cpp b/image/encoders/ico/nsICOEncoder.cpp
index b61e99808804..c64914a6094c 100644
--- a/image/encoders/ico/nsICOEncoder.cpp
+++ b/image/encoders/ico/nsICOEncoder.cpp
@@ -228,10 +228,11 @@ nsICOEncoder::StartImageEncode(uint32_t aWidth,
   }
 
   // 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;
 
@@ -265,7 +266,7 @@ nsICOEncoder::EndImageEncode()
 // 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
@@ -469,7 +470,7 @@ nsICOEncoder::InitFileHeader()
 
 // 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;
diff --git a/image/encoders/ico/nsICOEncoder.h b/image/encoders/ico/nsICOEncoder.h
index 37efe2f750c9..a8a9a504974c 100644
--- a/image/encoders/ico/nsICOEncoder.h
+++ b/image/encoders/ico/nsICOEncoder.h
@@ -50,14 +50,14 @@ public:
 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
diff --git a/js/src/jit/arm/Lowering-arm.cpp b/js/src/jit/arm/Lowering-arm.cpp
index b69d9e5617a5..9808cda9e8a2 100644
--- a/js/src/jit/arm/Lowering-arm.cpp
+++ b/js/src/jit/arm/Lowering-arm.cpp
@@ -902,6 +902,8 @@ LIRGeneratorARM::visitAsmJSCompareExchangeHeap(MAsmJSCompareExchangeHeap* ins)
     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),
@@ -929,6 +931,8 @@ LIRGeneratorARM::visitAsmJSAtomicExchangeHeap(MAsmJSAtomicExchangeHeap* ins)
     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),
@@ -953,6 +957,8 @@ LIRGeneratorARM::visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap* ins)
     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),
diff --git a/mfbt/Casting.h b/mfbt/Casting.h
index a7d0fb50d688..adf2c90456b4 100644
--- a/mfbt/Casting.h
+++ b/mfbt/Casting.h
@@ -238,6 +238,19 @@ AssertedCast(const From aFrom)
   return static_cast(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
+inline To
+ReleaseAssertedCast(const From aFrom)
+{
+  MOZ_RELEASE_ASSERT((detail::IsInBounds(aFrom)));
+  return static_cast(aFrom);
+}
+
 } // namespace mozilla
 
 #endif /* mozilla_Casting_h */
diff --git a/mfbt/Range.h b/mfbt/Range.h
index 47d91bb0ccab..753fe07f897e 100644
--- a/mfbt/Range.h
+++ b/mfbt/Range.h
@@ -9,6 +9,7 @@
 
 #include "mozilla/RangedPtr.h"
 #include "mozilla/TypeTraits.h"
+#include "mozilla/Span.h"
 
 #include 
 
@@ -44,6 +45,19 @@ public:
       mEnd(aOther.mEnd)
   {}
 
+  MOZ_IMPLICIT Range(Span aSpan)
+    : Range(aSpan.Elements(), aSpan.Length())
+  {
+  }
+
+  template::value,
+                                     int>::Type>
+  MOZ_IMPLICIT Range(const Span& aSpan)
+    : Range(aSpan.Elements(), aSpan.Length())
+  {
+  }
+
   RangedPtr begin() const { return mStart; }
   RangedPtr end() const { return mEnd; }
   size_t length() const { return mEnd - mStart; }
@@ -51,8 +65,26 @@ public:
   T& operator[](size_t aOffset) const { return mStart[aOffset]; }
 
   explicit operator bool() const { return mStart != nullptr; }
+
+  operator Span() { return Span(mStart.get(), length()); }
+
+  operator Span() const { return Span(mStart.get(), length()); }
 };
 
+template
+Span
+MakeSpan(Range& aRange)
+{
+  return aRange;
+}
+
+template
+Span
+MakeSpan(const Range& aRange)
+{
+  return aRange;
+}
+
 } // namespace mozilla
 
 #endif /* mozilla_Range_h */
diff --git a/mfbt/Span.h b/mfbt/Span.h
new file mode 100644
index 000000000000..5acaafcbd73c
--- /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 
+#include 
+#include 
+#include 
+
+// 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
+inline constexpr T
+narrow_cast(U&& u)
+{
+  return static_cast(mozilla::Forward(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::value;
+
+template
+class Span;
+
+// implementation details
+namespace span_details {
+
+// C++14 types that we don't have because we build as C++11.
+template
+using remove_cv_t = typename mozilla::RemoveCV::Type;
+template
+using remove_const_t = typename mozilla::RemoveConst::Type;
+template
+using conditional_t = typename mozilla::Conditional::Type;
+template
+using add_pointer_t = typename mozilla::AddPointer::Type;
+template
+using enable_if_t = typename mozilla::EnableIf::Type;
+
+template
+struct is_span_oracle : mozilla::FalseType
+{
+};
+
+template
+struct is_span_oracle> : mozilla::TrueType
+{
+};
+
+template
+struct is_span : public is_span_oracle>
+{
+};
+
+template
+struct is_std_array_oracle : mozilla::FalseType
+{
+};
+
+template
+struct is_std_array_oracle> : mozilla::TrueType
+{
+};
+
+template
+struct is_std_array : public is_std_array_oracle>
+{
+};
+
+template
+struct is_allowed_extent_conversion
+  : public mozilla::IntegralConstant
+{
+};
+
+template
+struct is_allowed_element_type_conversion
+  : public mozilla::IntegralConstant::value>
+{
+};
+
+template
+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;
+  using difference_type = typename Span::index_type;
+
+  using reference = conditional_t&;
+  using pointer = add_pointer_t;
+
+  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;
+  constexpr MOZ_IMPLICIT span_iterator(const span_iterator& other)
+    : span_iterator(other.span_, other.index_)
+  {
+  }
+
+  MOZ_SPAN_EXPLICITLY_DEFAULTED_CONSTEXPR span_iterator&
+  operator=(const span_iterator&) = 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
+inline constexpr span_iterator
+operator+(typename span_iterator::difference_type n,
+          const span_iterator& rhs)
+{
+  return rhs + n;
+}
+
+template
+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
+  MOZ_SPAN_ASSERTION_CONSTEXPR MOZ_IMPLICIT extent_type(extent_type 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
+{
+public:
+  using index_type = size_t;
+
+  template
+  explicit constexpr extent_type(extent_type 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 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 aReadFrom,
+ *                                           Span aWrittenTo);
+ *
+ * Any Span can be viewed as Span using the function
+ * AsBytes(). Any Span can be viewed as Span using the function
+ * AsWritableBytes().
+ */
+template
+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, false>;
+  using const_iterator =
+    span_details::span_iterator, true>;
+  using reverse_iterator = std::reverse_iterator;
+  using const_reverse_iterator = std::reverse_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::value)>" SFINAE,
+  // since "span_details::enable_if_t<(Extent == 0 || Extent == mozilla::MaxValue::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::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
+  constexpr MOZ_IMPLICIT Span(element_type (&aArr)[N])
+    : storage_(&aArr[0], span_details::extent_type())
+  {
+  }
+
+  /**
+   * Constructor for std::array.
+   */
+  template>
+  constexpr MOZ_IMPLICIT Span(std::array& aArr)
+    : storage_(&aArr[0], span_details::extent_type())
+  {
+  }
+
+  /**
+   * Constructor for const std::array.
+   */
+  template
+  constexpr MOZ_IMPLICIT Span(
+    const std::array, N>& aArr)
+    : storage_(&aArr[0], span_details::extent_type())
+  {
+  }
+
+  /**
+   * Constructor for mozilla::Array.
+   */
+  template>
+  constexpr MOZ_IMPLICIT Span(mozilla::Array& aArr)
+    : storage_(&aArr[0], span_details::extent_type())
+  {
+  }
+
+  /**
+   * Constructor for const mozilla::Array.
+   */
+  template
+  constexpr MOZ_IMPLICIT Span(
+    const mozilla::Array, N>& aArr)
+    : storage_(&aArr[0], span_details::extent_type())
+  {
+  }
+
+  /**
+   * Constructor for mozilla::UniquePtr holding an array and length.
+   */
+  template>
+  constexpr Span(const mozilla::UniquePtr& 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::value &&
+      !span_details::is_std_array::value &&
+      mozilla::IsConvertible::value &&
+      mozilla::IsConvertible().data())>::value>>
+  constexpr MOZ_IMPLICIT Span(Container& cont)
+    : Span(cont.data(), ReleaseAssertedCast(cont.size()))
+  {
+  }
+
+  /**
+   * Constructor for standard-library containers (const version).
+   */
+  template<
+    class Container,
+    class = span_details::enable_if_t<
+      mozilla::IsConst::value &&
+      !span_details::is_span::value &&
+      mozilla::IsConvertible::value &&
+      mozilla::IsConvertible().data())>::value>>
+  constexpr MOZ_IMPLICIT Span(const Container& cont)
+    : Span(cont.data(), ReleaseAssertedCast(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::value &&
+      span_details::is_allowed_element_type_conversion::value>>
+  constexpr MOZ_IMPLICIT Span(const Span& other)
+    : storage_(other.data(),
+               span_details::extent_type(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::value &&
+      span_details::is_allowed_element_type_conversion::value>>
+  constexpr MOZ_IMPLICIT Span(Span&& other)
+    : storage_(other.data(),
+               span_details::extent_type(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
+  MOZ_SPAN_GCC_CONSTEXPR Span First() const
+  {
+    MOZ_RELEASE_ASSERT(Count <= size());
+    return { data(), Count };
+  }
+
+  /**
+   * Subspan with last N elements with compile-time N.
+   */
+  template
+  MOZ_SPAN_GCC_CONSTEXPR Span Last() const
+  {
+    MOZ_RELEASE_ASSERT(Count <= size());
+    return { data() + (size() - Count), Count };
+  }
+
+  /**
+   * Subspan with compile-time start index and length.
+   */
+  template
+  MOZ_SPAN_GCC_CONSTEXPR Span 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 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 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 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 From(
+    index_type aStart) const
+  {
+    return Subspan(aStart);
+  }
+
+  /**
+   * Subspan with run-time exclusive end index. (Rust's &foo[..end])
+   */
+  MOZ_SPAN_GCC_CONSTEXPR Span 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 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(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 storage_type : public ExtentType
+  {
+  public:
+    template
+    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::value));
+    }
+
+    constexpr pointer data() const { return data_; }
+
+  private:
+    pointer data_;
+  };
+
+  storage_type> storage_;
+};
+
+// [Span.comparison], Span comparison operators
+template
+inline constexpr bool
+operator==(const Span& l,
+           const Span& r)
+{
+  return (l.size() == r.size()) && std::equal(l.begin(), l.end(), r.begin());
+}
+
+template
+inline constexpr bool
+operator!=(const Span& l,
+           const Span& r)
+{
+  return !(l == r);
+}
+
+template
+inline constexpr bool
+operator<(const Span& l,
+          const Span& r)
+{
+  return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end());
+}
+
+template
+inline constexpr bool
+operator<=(const Span& l,
+           const Span& r)
+{
+  return !(l > r);
+}
+
+template
+inline constexpr bool
+operator>(const Span& l,
+          const Span& r)
+{
+  return r < l;
+}
+
+template
+inline constexpr bool
+operator>=(const Span& l,
+           const Span& 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
+struct calculate_byte_size
+  : mozilla::IntegralConstant(sizeof(ElementType) *
+                                               static_cast(Extent))>
+{
+};
+
+template
+struct calculate_byte_size
+  : mozilla::IntegralConstant
+{
+};
+}
+
+// [Span.objectrep], views of object representation
+/**
+ * View span as Span.
+ */
+template
+Span::value>
+AsBytes(Span s)
+{
+  return { reinterpret_cast(s.data()), s.size_bytes() };
+}
+
+/**
+ * View span as Span.
+ */
+template::value>>
+Span::value>
+AsWritableBytes(Span s)
+{
+  return { reinterpret_cast(s.data()), s.size_bytes() };
+}
+
+//
+// MakeSpan() - Utility functions for creating Spans
+//
+/**
+ * Create span from pointer and length.
+ */
+template
+Span
+MakeSpan(ElementType* aPtr, typename Span::index_type aLength)
+{
+  return Span(aPtr, aLength);
+}
+
+/**
+ * Create span from start pointer and pointer past end.
+ */
+template
+Span
+MakeSpan(ElementType* aStartPtr, ElementType* aEndPtr)
+{
+  return Span(aStartPtr, aEndPtr);
+}
+
+/**
+ * Create span from C array.
+ */
+template
+Span MakeSpan(ElementType (&aArr)[N])
+{
+  return Span(aArr);
+}
+
+/**
+ * Create span from mozilla::Array.
+ */
+template
+Span
+MakeSpan(mozilla::Array& aArr)
+{
+  return aArr;
+}
+
+/**
+ * Create span from const mozilla::Array.
+ */
+template
+Span
+MakeSpan(const mozilla::Array& arr)
+{
+  return arr;
+}
+
+/**
+ * Create span from standard-library container.
+ */
+template
+Span
+MakeSpan(Container& cont)
+{
+  return Span(cont);
+}
+
+/**
+ * Create span from standard-library container (const version).
+ */
+template
+Span
+MakeSpan(const Container& cont)
+{
+  return Span(cont);
+}
+
+/**
+ * Create span from smart pointer and length.
+ */
+template
+Span
+MakeSpan(Ptr& aPtr, size_t aLength)
+{
+  return Span(aPtr, aLength);
+}
+
+/**
+ * Create span from C string.
+ */
+inline Span
+MakeCStringSpan(const char* aStr)
+{
+  return Span(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
diff --git a/mfbt/moz.build b/mfbt/moz.build
index f7bee9161558..1a69edf14a0c 100644
--- a/mfbt/moz.build
+++ b/mfbt/moz.build
@@ -82,6 +82,7 @@ EXPORTS.mozilla = [
     'SegmentedVector.h',
     'SHA1.h',
     'SizePrintfMacros.h',
+    'Span.h',
     'SplayTree.h',
     'Sprintf.h',
     'StaticAnalysisFunctions.h',
diff --git a/mfbt/tests/gtest/TestSpan.cpp b/mfbt/tests/gtest/TestSpan.cpp
new file mode 100644
index 000000000000..f3aa000a438e
--- /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, Span>::value,
+              "Range should convert into const");
+static_assert(IsConvertible, Span>::value,
+              "const Range should convert into const");
+static_assert(!IsConvertible, Span>::value,
+              "Range should not drop const in conversion");
+static_assert(IsConvertible, Range>::value,
+              "Span should convert into const");
+static_assert(IsConvertible, Range>::value,
+              "const Span should convert into const");
+static_assert(!IsConvertible, Range>::value,
+              "Span should not drop const in conversion");
+static_assert(IsConvertible, Span>::value,
+              "const Span should convert into const");
+static_assert(IsConvertible, Span>::value,
+              "Span should convert into const");
+static_assert(!IsConvertible, Span>::value,
+              "Span should not drop const in conversion");
+static_assert(IsConvertible, Span>::value,
+              "const nsTArray should convert into const");
+static_assert(IsConvertible, Span>::value,
+              "nsTArray should convert into const");
+static_assert(!IsConvertible, Span>::value,
+              "nsTArray should not drop const in conversion");
+static_assert(IsConvertible, Span>::value,
+              "nsTArray should convert into const");
+static_assert(!IsConvertible, Span>::value,
+              "nsTArray should not drop const in conversion");
+
+namespace {
+struct BaseClass
+{
+};
+struct DerivedClass : BaseClass
+{
+};
+}
+
+void
+AssertSpanOfThreeInts(Span s)
+{
+  ASSERT_EQ(s.size(), 3U);
+  ASSERT_EQ(s[0], 1);
+  ASSERT_EQ(s[1], 2);
+  ASSERT_EQ(s[2], 3);
+}
+
+void
+AssertSpanOfThreeChars(Span s)
+{
+  ASSERT_EQ(s.size(), 3U);
+  ASSERT_EQ(s[0], 'a');
+  ASSERT_EQ(s[1], 'b');
+  ASSERT_EQ(s[2], 'c');
+}
+
+void
+AssertSpanOfThreeChar16s(Span 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 s;
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), nullptr);
+
+    Span cs;
+    ASSERT_EQ(cs.Length(), 0U);
+    ASSERT_EQ(cs.data(), nullptr);
+  }
+
+  {
+    Span s;
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), nullptr);
+
+    Span cs;
+    ASSERT_EQ(cs.Length(), 0U);
+    ASSERT_EQ(cs.data(), nullptr);
+  }
+
+  {
+#ifdef CONFIRM_COMPILATION_ERRORS
+    Span s;
+    ASSERT_EQ(s.Length(), 1U);
+    ASSERT_EQ(s.data(), nullptr); // explains why it can't compile
+#endif
+  }
+
+  {
+    Span s{};
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), nullptr);
+
+    Span cs{};
+    ASSERT_EQ(cs.Length(), 0U);
+    ASSERT_EQ(cs.data(), nullptr);
+  }
+}
+
+SPAN_TEST(size_optimization)
+{
+  {
+    Span s;
+    ASSERT_EQ(sizeof(s), sizeof(int*) + sizeof(size_t));
+  }
+
+  {
+    Span s;
+    ASSERT_EQ(sizeof(s), sizeof(int*));
+  }
+}
+
+SPAN_TEST(from_nullptr_constructor)
+{
+  {
+    Span s = nullptr;
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), nullptr);
+
+    Span cs = nullptr;
+    ASSERT_EQ(cs.Length(), 0U);
+    ASSERT_EQ(cs.data(), nullptr);
+  }
+
+  {
+    Span s = nullptr;
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), nullptr);
+
+    Span cs = nullptr;
+    ASSERT_EQ(cs.Length(), 0U);
+    ASSERT_EQ(cs.data(), nullptr);
+  }
+
+  {
+#ifdef CONFIRM_COMPILATION_ERRORS
+    Span s = nullptr;
+    ASSERT_EQ(s.Length(), 1U);
+    ASSERT_EQ(s.data(), nullptr); // explains why it can't compile
+#endif
+  }
+
+  {
+    Span s{ nullptr };
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), nullptr);
+
+    Span cs{ nullptr };
+    ASSERT_EQ(cs.Length(), 0U);
+    ASSERT_EQ(cs.data(), nullptr);
+  }
+
+  {
+    Span s{ nullptr };
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), nullptr);
+
+    Span cs{ nullptr };
+    ASSERT_EQ(cs.Length(), 0U);
+    ASSERT_EQ(cs.data(), nullptr);
+  }
+}
+
+SPAN_TEST(from_nullptr_length_constructor)
+{
+  {
+    Span s{ nullptr, static_cast::index_type>(0) };
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), nullptr);
+
+    Span cs{ nullptr, static_cast::index_type>(0) };
+    ASSERT_EQ(cs.Length(), 0U);
+    ASSERT_EQ(cs.data(), nullptr);
+  }
+
+  {
+    Span s{ nullptr, static_cast::index_type>(0) };
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), nullptr);
+
+    Span cs{ nullptr, static_cast::index_type>(0) };
+    ASSERT_EQ(cs.Length(), 0U);
+    ASSERT_EQ(cs.data(), nullptr);
+  }
+
+#if 0
+        {
+            auto workaround_macro = []() { Span s{ nullptr, static_cast::index_type>(0) }; };
+            CHECK_THROW(workaround_macro(), fail_fast);
+        }
+
+        {
+            auto workaround_macro = []() { Span s{nullptr, 1}; };
+            CHECK_THROW(workaround_macro(), fail_fast);
+
+            auto const_workaround_macro = []() { Span cs{nullptr, 1}; };
+            CHECK_THROW(const_workaround_macro(), fail_fast);
+        }
+
+        {
+            auto workaround_macro = []() { Span s{nullptr, 1}; };
+            CHECK_THROW(workaround_macro(), fail_fast);
+
+            auto const_workaround_macro = []() { Span s{nullptr, 1}; };
+            CHECK_THROW(const_workaround_macro(), fail_fast);
+        }
+#endif
+  {
+    Span s{ nullptr, static_cast::index_type>(0) };
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), nullptr);
+
+    Span cs{ nullptr, static_cast::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 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 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 s{ p, static_cast::index_type>(0) };
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), nullptr);
+  }
+
+#if 0
+        {
+            int* p = nullptr;
+            auto workaround_macro = [=]() { Span 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::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 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 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 s{ &arr[0], &arr[0] };
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), &arr[0]);
+  }
+
+  {
+    Span 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 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 s{&arr[0], p}; };
+  //    CHECK_THROW(workaround_macro(), fail_fast);
+  //}
+
+  {
+    int* p = nullptr;
+    Span s{ p, p };
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), nullptr);
+  }
+
+  {
+    int* p = nullptr;
+    Span 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 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 s{ arr };
+    ASSERT_EQ(s.Length(), 5U);
+    ASSERT_EQ(s.data(), &arr[0]);
+  }
+
+  {
+    Span 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 s{ arr };
+  }
+
+  {
+    Span s{ arr };
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), &arr[0]);
+  }
+
+  {
+    Span 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 s{ arr2d };
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), &arr2d[0][0]);
+  }
+
+  {
+    Span s{ arr2d };
+  }
+#endif
+  {
+    Span 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 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 s{ arr3d };
+    ASSERT_EQ(s.Length(), 0U);
+    ASSERT_EQ(s.data(), &arr3d[0][0][0]);
+  }
+
+  {
+    Span s{ arr3d };
+  }
+
+  {
+    Span 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 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 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 arr = { { 1, 2, 3, 4 } };
+
+  {
+    Span s{ arr };
+    ASSERT_EQ(s.size(), narrow_cast(arr.size()));
+    ASSERT_EQ(s.data(), arr.data());
+
+    Span cs{ arr };
+    ASSERT_EQ(cs.size(), narrow_cast(arr.size()));
+    ASSERT_EQ(cs.data(), arr.data());
+  }
+
+  {
+    Span s{ arr };
+    ASSERT_EQ(s.size(), narrow_cast(arr.size()));
+    ASSERT_EQ(s.data(), arr.data());
+
+    Span cs{ arr };
+    ASSERT_EQ(cs.size(), narrow_cast(arr.size()));
+    ASSERT_EQ(cs.data(), arr.data());
+  }
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+  {
+    Span s{ arr };
+    ASSERT_EQ(s.size(), 2U);
+    ASSERT_EQ(s.data(), arr.data());
+
+    Span cs{ arr };
+    ASSERT_EQ(cs.size(), 2U);
+    ASSERT_EQ(cs.data(), arr.data());
+  }
+
+  {
+    Span s{ arr };
+    ASSERT_EQ(s.size(), 0U);
+    ASSERT_EQ(s.data(), arr.data());
+
+    Span cs{ arr };
+    ASSERT_EQ(cs.size(), 0U);
+    ASSERT_EQ(cs.data(), arr.data());
+  }
+
+  {
+    Span s{ arr };
+  }
+
+  {
+    auto get_an_array = []() -> std::array { return { 1, 2, 3, 4 }; };
+    auto take_a_Span = [](Span s) { static_cast(s); };
+    // try to take a temporary std::array
+    take_a_Span(get_an_array());
+  }
+#endif
+
+  {
+    auto get_an_array = []() -> std::array {
+      return { { 1, 2, 3, 4 } };
+    };
+    auto take_a_Span = [](Span s) { static_cast(s); };
+    // try to take a temporary std::array
+    take_a_Span(get_an_array());
+  }
+
+  {
+    auto s = MakeSpan(arr);
+    ASSERT_EQ(s.size(), narrow_cast(arr.size()));
+    ASSERT_EQ(s.data(), arr.data());
+  }
+}
+
+SPAN_TEST(from_const_std_array_constructor)
+{
+  const std::array arr = { { 1, 2, 3, 4 } };
+
+  {
+    Span s{ arr };
+    ASSERT_EQ(s.size(), narrow_cast(arr.size()));
+    ASSERT_EQ(s.data(), arr.data());
+  }
+
+  {
+    Span s{ arr };
+    ASSERT_EQ(s.size(), narrow_cast(arr.size()));
+    ASSERT_EQ(s.data(), arr.data());
+  }
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+  {
+    Span s{ arr };
+    ASSERT_EQ(s.size(), 2U);
+    ASSERT_EQ(s.data(), arr.data());
+  }
+
+  {
+    Span s{ arr };
+    ASSERT_EQ(s.size(), 0U);
+    ASSERT_EQ(s.data(), arr.data());
+  }
+
+  {
+    Span s{ arr };
+  }
+#endif
+
+  {
+    auto get_an_array = []() -> const std::array {
+      return { { 1, 2, 3, 4 } };
+    };
+    auto take_a_Span = [](Span s) { static_cast(s); };
+    // try to take a temporary std::array
+    take_a_Span(get_an_array());
+  }
+
+  {
+    auto s = MakeSpan(arr);
+    ASSERT_EQ(s.size(), narrow_cast(arr.size()));
+    ASSERT_EQ(s.data(), arr.data());
+  }
+}
+
+SPAN_TEST(from_std_array_const_constructor)
+{
+  std::array arr = { { 1, 2, 3, 4 } };
+
+  {
+    Span s{ arr };
+    ASSERT_EQ(s.size(), narrow_cast(arr.size()));
+    ASSERT_EQ(s.data(), arr.data());
+  }
+
+  {
+    Span s{ arr };
+    ASSERT_EQ(s.size(), narrow_cast(arr.size()));
+    ASSERT_EQ(s.data(), arr.data());
+  }
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+  {
+    Span s{ arr };
+    ASSERT_EQ(s.size(), 2U);
+    ASSERT_EQ(s.data(), arr.data());
+  }
+
+  {
+    Span s{ arr };
+    ASSERT_EQ(s.size(), 0U);
+    ASSERT_EQ(s.data(), arr.data());
+  }
+
+  {
+    Span s{ arr };
+  }
+
+  {
+    Span s{ arr };
+  }
+#endif
+
+  {
+    auto s = MakeSpan(arr);
+    ASSERT_EQ(s.size(), narrow_cast(arr.size()));
+    ASSERT_EQ(s.data(), arr.data());
+  }
+}
+
+SPAN_TEST(from_mozilla_array_constructor)
+{
+  mozilla::Array arr(1, 2, 3, 4);
+
+  {
+    Span s{ arr };
+    ASSERT_EQ(s.size(), narrow_cast(arr.cend() - arr.cbegin()));
+    ASSERT_EQ(s.data(), &arr[0]);
+
+    Span cs{ arr };
+    ASSERT_EQ(cs.size(), narrow_cast(arr.cend() - arr.cbegin()));
+    ASSERT_EQ(cs.data(), &arr[0]);
+  }
+
+  {
+    Span s{ arr };
+    ASSERT_EQ(s.size(), narrow_cast(arr.cend() - arr.cbegin()));
+    ASSERT_EQ(s.data(), &arr[0]);
+
+    Span cs{ arr };
+    ASSERT_EQ(cs.size(), narrow_cast(arr.cend() - arr.cbegin()));
+    ASSERT_EQ(cs.data(), &arr[0]);
+  }
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+  {
+    Span s{ arr };
+    ASSERT_EQ(s.size(), 2U);
+    ASSERT_EQ(s.data(), &arr[0]);
+
+    Span cs{ arr };
+    ASSERT_EQ(cs.size(), 2U);
+    ASSERT_EQ(cs.data(), &arr[0]);
+  }
+
+  {
+    Span s{ arr };
+    ASSERT_EQ(s.size(), 0U);
+    ASSERT_EQ(s.data(), &arr[0]);
+
+    Span cs{ arr };
+    ASSERT_EQ(cs.size(), 0U);
+    ASSERT_EQ(cs.data(), &arr[0]);
+  }
+
+  {
+    Span s{ arr };
+  }
+
+  {
+    auto get_an_array = []() -> mozilla::Array {
+      return { 1, 2, 3, 4 };
+    };
+    auto take_a_Span = [](Span s) { static_cast(s); };
+    // try to take a temporary mozilla::Array
+    take_a_Span(get_an_array());
+  }
+#endif
+
+  {
+    auto get_an_array = []() -> mozilla::Array {
+      return { 1, 2, 3, 4 };
+    };
+    auto take_a_Span = [](Span s) { static_cast(s); };
+    // try to take a temporary mozilla::Array
+    take_a_Span(get_an_array());
+  }
+
+  {
+    auto s = MakeSpan(arr);
+    ASSERT_EQ(s.size(), narrow_cast(arr.cend() - arr.cbegin()));
+    ASSERT_EQ(s.data(), &arr[0]);
+  }
+}
+
+SPAN_TEST(from_const_mozilla_array_constructor)
+{
+  const mozilla::Array arr(1, 2, 3, 4);
+
+  {
+    Span s{ arr };
+    ASSERT_EQ(s.size(), narrow_cast(arr.cend() - arr.cbegin()));
+    ASSERT_EQ(s.data(), &arr[0]);
+  }
+
+  {
+    Span s{ arr };
+    ASSERT_EQ(s.size(), narrow_cast(arr.cend() - arr.cbegin()));
+    ASSERT_EQ(s.data(), &arr[0]);
+  }
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+  {
+    Span s{ arr };
+    ASSERT_EQ(s.size(), 2U);
+    ASSERT_EQ(s.data(), &arr[0]);
+  }
+
+  {
+    Span s{ arr };
+    ASSERT_EQ(s.size(), 0U);
+    ASSERT_EQ(s.data(), &arr[0]);
+  }
+
+  {
+    Span s{ arr };
+  }
+#endif
+
+#if 0
+  {
+    auto get_an_array = []() -> const mozilla::Array {
+      return { 1, 2, 3, 4 };
+    };
+    auto take_a_Span = [](Span s) { static_cast(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(arr.cend() - arr.cbegin()));
+    ASSERT_EQ(s.data(), &arr[0]);
+  }
+}
+
+SPAN_TEST(from_mozilla_array_const_constructor)
+{
+  mozilla::Array arr(1, 2, 3, 4);
+
+  {
+    Span s{ arr };
+    ASSERT_EQ(s.size(), narrow_cast(arr.cend() - arr.cbegin()));
+    ASSERT_EQ(s.data(), &arr[0]);
+  }
+
+  {
+    Span s{ arr };
+    ASSERT_EQ(s.size(), narrow_cast(arr.cend() - arr.cbegin()));
+    ASSERT_EQ(s.data(), &arr[0]);
+  }
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+  {
+    Span s{ arr };
+    ASSERT_EQ(s.size(), 2U);
+    ASSERT_EQ(s.data(), &arr[0]);
+  }
+
+  {
+    Span s{ arr };
+    ASSERT_EQ(s.size(), 0U);
+    ASSERT_EQ(s.data(), &arr[0]);
+  }
+
+  {
+    Span s{ arr };
+  }
+
+  {
+    Span s{ arr };
+  }
+#endif
+
+  {
+    auto s = MakeSpan(arr);
+    ASSERT_EQ(s.size(), narrow_cast(arr.cend() - arr.cbegin()));
+    ASSERT_EQ(s.data(), &arr[0]);
+  }
+}
+
+SPAN_TEST(from_container_constructor)
+{
+  std::vector v = { 1, 2, 3 };
+  const std::vector cv = v;
+
+  {
+    AssertSpanOfThreeInts(v);
+
+    Span s{ v };
+    ASSERT_EQ(s.size(), narrow_cast(v.size()));
+    ASSERT_EQ(s.data(), v.data());
+
+    Span cs{ v };
+    ASSERT_EQ(cs.size(), narrow_cast(v.size()));
+    ASSERT_EQ(cs.data(), v.data());
+  }
+
+  std::string str = "hello";
+  const std::string cstr = "hello";
+
+  {
+#ifdef CONFIRM_COMPILATION_ERRORS
+    Span s{ str };
+    ASSERT_EQ(s.size(), narrow_cast(str.size()));
+    ASSERT_EQ(s.data(), str.data());
+#endif
+    Span cs{ str };
+    ASSERT_EQ(cs.size(), narrow_cast(str.size()));
+    ASSERT_EQ(cs.data(), str.data());
+  }
+
+  {
+#ifdef CONFIRM_COMPILATION_ERRORS
+    Span s{ cstr };
+#endif
+    Span cs{ cstr };
+    ASSERT_EQ(cs.size(), narrow_cast(cstr.size()));
+    ASSERT_EQ(cs.data(), cstr.data());
+  }
+
+  {
+#ifdef CONFIRM_COMPILATION_ERRORS
+    auto get_temp_vector = []() -> std::vector { return {}; };
+    auto use_Span = [](Span s) { static_cast(s); };
+    use_Span(get_temp_vector());
+#endif
+  }
+
+  {
+    auto get_temp_vector = []() -> std::vector { return {}; };
+    auto use_Span = [](Span s) { static_cast(s); };
+    use_Span(get_temp_vector());
+  }
+
+  {
+#ifdef CONFIRM_COMPILATION_ERRORS
+    auto get_temp_string = []() -> std::string { return {}; };
+    auto use_Span = [](Span s) { static_cast(s); };
+    use_Span(get_temp_string());
+#endif
+  }
+
+  {
+    auto get_temp_string = []() -> std::string { return {}; };
+    auto use_Span = [](Span s) { static_cast(s); };
+    use_Span(get_temp_string());
+  }
+
+  {
+#ifdef CONFIRM_COMPILATION_ERRORS
+    auto get_temp_vector = []() -> const std::vector { return {}; };
+    auto use_Span = [](Span s) { static_cast(s); };
+    use_Span(get_temp_vector());
+#endif
+  }
+
+  {
+    auto get_temp_string = []() -> const std::string { return {}; };
+    auto use_Span = [](Span s) { static_cast(s); };
+    use_Span(get_temp_string());
+  }
+
+  {
+#ifdef CONFIRM_COMPILATION_ERRORS
+    std::map m;
+    Span s{ m };
+#endif
+  }
+
+  {
+    auto s = MakeSpan(v);
+    ASSERT_EQ(s.size(), narrow_cast(v.size()));
+    ASSERT_EQ(s.data(), v.data());
+
+    auto cs = MakeSpan(cv);
+    ASSERT_EQ(cs.size(), narrow_cast(cv.size()));
+    ASSERT_EQ(cs.data(), cv.data());
+  }
+}
+
+SPAN_TEST(from_xpcom_collections)
+{
+  {
+    nsTArray v;
+    v.AppendElement(1);
+    v.AppendElement(2);
+    v.AppendElement(3);
+
+    AssertSpanOfThreeInts(v);
+
+    Span s{ v };
+    ASSERT_EQ(s.size(), narrow_cast(v.Length()));
+    ASSERT_EQ(s.data(), v.Elements());
+    ASSERT_EQ(s[2], 3);
+
+    Span cs{ v };
+    ASSERT_EQ(cs.size(), narrow_cast(v.Length()));
+    ASSERT_EQ(cs.data(), v.Elements());
+    ASSERT_EQ(cs[2], 3);
+  }
+  {
+    nsTArray v;
+    v.AppendElement(1);
+    v.AppendElement(2);
+    v.AppendElement(3);
+
+    AssertSpanOfThreeInts(v);
+
+    auto s = MakeSpan(v);
+    ASSERT_EQ(s.size(), narrow_cast(v.Length()));
+    ASSERT_EQ(s.data(), v.Elements());
+    ASSERT_EQ(s[2], 3);
+  }
+  {
+    AutoTArray v;
+    v.AppendElement(1);
+    v.AppendElement(2);
+    v.AppendElement(3);
+
+    AssertSpanOfThreeInts(v);
+
+    Span s{ v };
+    ASSERT_EQ(s.size(), narrow_cast(v.Length()));
+    ASSERT_EQ(s.data(), v.Elements());
+    ASSERT_EQ(s[2], 3);
+
+    Span cs{ v };
+    ASSERT_EQ(cs.size(), narrow_cast(v.Length()));
+    ASSERT_EQ(cs.data(), v.Elements());
+    ASSERT_EQ(cs[2], 3);
+  }
+  {
+    AutoTArray v;
+    v.AppendElement(1);
+    v.AppendElement(2);
+    v.AppendElement(3);
+
+    AssertSpanOfThreeInts(v);
+
+    auto s = MakeSpan(v);
+    ASSERT_EQ(s.size(), narrow_cast(v.Length()));
+    ASSERT_EQ(s.data(), v.Elements());
+    ASSERT_EQ(s[2], 3);
+  }
+  {
+    FallibleTArray v;
+    *(v.AppendElement(fallible)) = 1;
+    *(v.AppendElement(fallible)) = 2;
+    *(v.AppendElement(fallible)) = 3;
+
+    AssertSpanOfThreeInts(v);
+
+    Span s{ v };
+    ASSERT_EQ(s.size(), narrow_cast(v.Length()));
+    ASSERT_EQ(s.data(), v.Elements());
+    ASSERT_EQ(s[2], 3);
+
+    Span cs{ v };
+    ASSERT_EQ(cs.size(), narrow_cast(v.Length()));
+    ASSERT_EQ(cs.data(), v.Elements());
+    ASSERT_EQ(cs[2], 3);
+  }
+  {
+    FallibleTArray 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(v.Length()));
+    ASSERT_EQ(s.data(), v.Elements());
+    ASSERT_EQ(s[2], 3);
+  }
+  {
+    nsAutoString str;
+    str.AssignLiteral("abc");
+
+    AssertSpanOfThreeChar16s(str);
+    AssertSpanOfThreeChar16sViaString(str);
+
+    Span s{ str };
+    ASSERT_EQ(s.size(), narrow_cast(str.Length()));
+    ASSERT_EQ(s.data(), str.BeginWriting());
+    ASSERT_EQ(s[2], 'c');
+
+    Span cs{ str };
+    ASSERT_EQ(cs.size(), narrow_cast(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(str.Length()));
+    ASSERT_EQ(s.data(), str.BeginWriting());
+    ASSERT_EQ(s[2], 'c');
+  }
+  {
+    nsAutoCString str;
+    str.AssignLiteral("abc");
+
+    AssertSpanOfThreeChars(str);
+    AssertSpanOfThreeCharsViaString(str);
+
+    Span s{ str };
+    ASSERT_EQ(s.size(), narrow_cast(str.Length()));
+    ASSERT_EQ(s.data(), reinterpret_cast(str.BeginWriting()));
+    ASSERT_EQ(s[2], 'c');
+
+    Span cs{ str };
+    ASSERT_EQ(cs.size(), narrow_cast(str.Length()));
+    ASSERT_EQ(cs.data(), reinterpret_cast(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(str.Length()));
+    ASSERT_EQ(s.data(), str.BeginWriting());
+    ASSERT_EQ(s[2], 'c');
+  }
+  {
+    nsTArray v;
+    v.AppendElement(1);
+    v.AppendElement(2);
+    v.AppendElement(3);
+
+    Range r(v.Elements(), v.Length());
+
+    AssertSpanOfThreeInts(r);
+
+    Span s{ r };
+    ASSERT_EQ(s.size(), narrow_cast(v.Length()));
+    ASSERT_EQ(s.data(), v.Elements());
+    ASSERT_EQ(s[2], 3);
+
+    Span cs{ r };
+    ASSERT_EQ(cs.size(), narrow_cast(v.Length()));
+    ASSERT_EQ(cs.data(), v.Elements());
+    ASSERT_EQ(cs[2], 3);
+  }
+  {
+    nsTArray v;
+    v.AppendElement(1);
+    v.AppendElement(2);
+    v.AppendElement(3);
+
+    Range r(v.Elements(), v.Length());
+
+    AssertSpanOfThreeInts(r);
+
+    auto s = MakeSpan(r);
+    ASSERT_EQ(s.size(), narrow_cast(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 avd;
+    Span avcd = avd;
+    static_cast(avcd);
+  }
+
+  {
+#ifdef CONFIRM_COMPILATION_ERRORS
+  Span avd;
+  Span avb = avd;
+  static_cast(avb);
+#endif
+  }
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+  {
+    Span s;
+    Span s2 = s;
+    static_cast(s2);
+  }
+
+  {
+    Span s;
+    Span s2 = s;
+    static_cast(s2);
+  }
+
+  {
+    Span s;
+    Span s2 = s;
+    static_cast(s2);
+  }
+#endif
+}
+
+SPAN_TEST(copy_move_and_assignment)
+{
+  Span s1;
+  ASSERT_TRUE(s1.empty());
+
+  int arr[] = { 3, 4, 5 };
+
+  Span s2 = arr;
+  ASSERT_EQ(s2.Length(), 3U);
+  ASSERT_EQ(s2.data(), &arr[0]);
+
+  s2 = s1;
+  ASSERT_TRUE(s2.empty());
+
+  auto get_temp_Span = [&]() -> Span { return { &arr[1], 2 }; };
+  auto use_Span = [&](Span 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 av = arr;
+    ASSERT_EQ(av.First<2>().Length(), 2U);
+    ASSERT_EQ(av.First(2).Length(), 2U);
+  }
+
+  {
+    Span av = arr;
+    ASSERT_EQ(av.First<0>().Length(), 0U);
+    ASSERT_EQ(av.First(0).Length(), 0U);
+  }
+
+  {
+    Span av = arr;
+    ASSERT_EQ(av.First<5>().Length(), 5U);
+    ASSERT_EQ(av.First(5).Length(), 5U);
+  }
+
+#if 0
+        {
+            Span 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 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 av = arr;
+    ASSERT_EQ(av.Last<2>().Length(), 2U);
+    ASSERT_EQ(av.Last(2).Length(), 2U);
+  }
+
+  {
+    Span av = arr;
+    ASSERT_EQ(av.Last<0>().Length(), 0U);
+    ASSERT_EQ(av.Last(0).Length(), 0U);
+  }
+
+  {
+    Span av = arr;
+    ASSERT_EQ(av.Last<5>().Length(), 5U);
+    ASSERT_EQ(av.Last(5).Length(), 5U);
+  }
+
+#if 0
+        {
+            Span av = arr;
+#ifdef CONFIRM_COMPILATION_ERRORS
+            ASSERT_EQ(av.Last<6>().Length() , 6U);
+#endif
+            CHECK_THROW(av.Last(6).Length(), fail_fast);
+        }
+#endif
+
+  {
+    Span 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 av = arr;
+    ASSERT_EQ(av.From(3).Length(), 2U);
+    ASSERT_EQ(av.From(2)[1], 4);
+  }
+
+  {
+    Span av = arr;
+    ASSERT_EQ(av.From(5).Length(), 0U);
+  }
+
+  {
+    Span av = arr;
+    ASSERT_EQ(av.From(0).Length(), 5U);
+  }
+
+  {
+    Span av = arr;
+    ASSERT_EQ(av.To(3).Length(), 3U);
+    ASSERT_EQ(av.To(3)[1], 2);
+  }
+
+  {
+    Span av = arr;
+    ASSERT_EQ(av.To(0).Length(), 0U);
+  }
+
+  {
+    Span av = arr;
+    ASSERT_EQ(av.To(5).Length(), 5U);
+  }
+
+  {
+    Span av = arr;
+    ASSERT_EQ(av.FromTo(1, 4).Length(), 3U);
+    ASSERT_EQ(av.FromTo(1, 4)[1], 3);
+  }
+
+  {
+    Span av = arr;
+    ASSERT_EQ(av.FromTo(2, 2).Length(), 0U);
+  }
+
+  {
+    Span av = arr;
+    ASSERT_EQ(av.FromTo(0, 5).Length(), 5U);
+  }
+}
+
+SPAN_TEST(Subspan)
+{
+  int arr[5] = { 1, 2, 3, 4, 5 };
+
+  {
+    Span 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 av = arr;
+    ASSERT_EQ((av.Subspan<0, 0>().Length()), 0U);
+    ASSERT_EQ(av.Subspan(0, 0).Length(), 0U);
+  }
+
+  {
+    Span 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 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 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 av;
+    ASSERT_EQ(av.Subspan(0).Length(), 0U);
+    CHECK_THROW(av.Subspan(1).Length(), fail_fast);
+  }
+
+  {
+    Span 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 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 s = arr;
+    ASSERT_EQ(s.at(0), 1);
+    CHECK_THROW(s.at(5), fail_fast);
+  }
+
+  {
+    int arr2d[2] = { 1, 6 };
+    Span 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 s = arr;
+    ASSERT_EQ(s(0), 1);
+    CHECK_THROW(s(5), fail_fast);
+  }
+
+  {
+    int arr2d[2] = { 1, 6 };
+    Span s = arr2d;
+    ASSERT_EQ(s(0), 1);
+    ASSERT_EQ(s(1), 6);
+    CHECK_THROW(s(2), fail_fast);
+  }
+}
+
+SPAN_TEST(iterator_default_init)
+{
+  Span::iterator it1;
+  Span::iterator it2;
+  ASSERT_EQ(it1, it2);
+}
+
+SPAN_TEST(const_iterator_default_init)
+{
+  Span::const_iterator it1;
+  Span::const_iterator it2;
+  ASSERT_EQ(it1, it2);
+}
+
+SPAN_TEST(iterator_conversions)
+{
+  Span::iterator badIt;
+  Span::const_iterator badConstIt;
+  ASSERT_EQ(badIt, badConstIt);
+
+  int a[] = { 1, 2, 3, 4 };
+  Span s = a;
+
+  auto it = s.begin();
+  auto cit = s.cbegin();
+
+  ASSERT_EQ(it, cit);
+  ASSERT_EQ(cit, it);
+
+  Span::const_iterator cit2 = it;
+  ASSERT_EQ(cit2, cit);
+
+  Span::const_iterator cit3 = it + 4;
+  ASSERT_EQ(cit3, s.cend());
+}
+
+SPAN_TEST(iterator_comparisons)
+{
+  int a[] = { 1, 2, 3, 4 };
+  {
+    Span s = a;
+    Span::iterator it = s.begin();
+    auto it2 = it + 1;
+    Span::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 s = a;
+
+    Span::iterator it = s.begin();
+    Span::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 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 s = a;
+
+              Span::const_iterator cit = s.cbegin();
+              Span::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 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 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 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 s1 = nullptr;
+    Span 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 s1 = arr;
+    Span 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 s1 = nullptr;
+    Span 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 s1 = arr1;
+    Span 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 s1 = { &arr[0], 2 }; // shorter
+    Span 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 s1 = arr1;
+    Span 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 s = a;
+    ASSERT_EQ(s.Length(), 4U);
+    Span bs = AsBytes(s);
+    ASSERT_EQ(static_cast(bs.data()),
+              static_cast(s.data()));
+    ASSERT_EQ(bs.Length(), s.LengthBytes());
+  }
+
+  {
+    Span 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(bs.data()),
+              static_cast(s.data()));
+    ASSERT_EQ(bs.data(), nullptr);
+  }
+
+  {
+    Span s = a;
+    auto bs = AsBytes(s);
+    ASSERT_EQ(static_cast(bs.data()),
+              static_cast(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 s = a;
+    ASSERT_EQ(s.Length(), 4U);
+    Span bs = AsWritableBytes(s);
+    ASSERT_EQ(static_cast(bs.data()), static_cast(s.data()));
+    ASSERT_EQ(bs.Length(), s.LengthBytes());
+#endif
+  }
+
+  {
+    Span 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(bs.data()), static_cast(s.data()));
+    ASSERT_EQ(bs.data(), nullptr);
+  }
+
+  {
+    Span s = a;
+    auto bs = AsWritableBytes(s);
+    ASSERT_EQ(static_cast(bs.data()), static_cast(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 s4 = arr;
+  ASSERT_EQ(s4.Length(), 4U);
+
+  // converting to dynamic_range is always ok
+  {
+    Span s = s4;
+    ASSERT_EQ(s.Length(), s4.Length());
+    static_cast(s);
+  }
+
+// initialization or assignment to static Span that REDUCES size is NOT ok
+#ifdef CONFIRM_COMPILATION_ERRORS
+  {
+    Span s = arr;
+  }
+  {
+    Span s2 = s4;
+    static_cast(s2);
+  }
+#endif
+
+#if 0
+        // even when done dynamically
+        {
+            Span s = arr;
+            auto f = [&]() {
+                Span s2 = s;
+                static_cast(s2);
+            };
+            CHECK_THROW(f(), fail_fast);
+        }
+#endif
+
+  // but doing so explicitly is ok
+
+  // you can convert statically
+  {
+    Span s2 = { arr, 2 };
+    static_cast(s2);
+  }
+  {
+    Span s1 = s4.First<1>();
+    static_cast(s1);
+  }
+
+  // ...or dynamically
+  {
+    // NB: implicit conversion to Span from Span
+    Span s1 = s4.First(1);
+    static_cast(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 s3 = arr2;
+  }
+  {
+    Span s2 = arr2;
+    Span s4a = s2;
+  }
+#endif
+
+#if 0
+        {
+            auto f = [&]() {
+                Span _s4 = {arr2, 2};
+                static_cast(_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 av = arr2;
+        auto f = [&]() {
+            Span _s4 = av;
+            static_cast(_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 s = lat;
+        auto f_it = s.begin() + 7;
+
+        std::match_results::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 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>::value));
+  ASSERT_TRUE((std::is_default_constructible>::value));
+  ASSERT_TRUE((!std::is_default_constructible>::value));
+}
diff --git a/mfbt/tests/gtest/moz.build b/mfbt/tests/gtest/moz.build
new file mode 100644
index 000000000000..bd559d60b94e
--- /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'
diff --git a/mfbt/tests/moz.build b/mfbt/tests/moz.build
index 62125ee31979..1850cfb2518d 100644
--- a/mfbt/tests/moz.build
+++ b/mfbt/tests/moz.build
@@ -4,6 +4,11 @@
 # 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',
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/EventDispatcher.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/EventDispatcher.java
index 96bcf4c09a68..8074e3bf8269 100644
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/EventDispatcher.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/EventDispatcher.java
@@ -55,7 +55,7 @@ public final class EventDispatcher extends JNIObject {
         new HashMap>(DEFAULT_BACKGROUND_EVENTS_COUNT);
 
     private boolean mAttachedToGecko;
-    private final StateHolder mStateHolder;
+    private volatile StateHolder mStateHolder;
 
     @ReflectionTarget
     @WrapForJNI(calledFrom = "gecko")
@@ -71,6 +71,13 @@ public final class EventDispatcher extends JNIObject {
         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();
     }
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java
index 195a6ce204d8..e5a84dc33aea 100644
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java
@@ -47,10 +47,10 @@ public class GeckoView extends LayerView
         @WrapForJNI INITIAL(0),
         @WrapForJNI READY(1);
 
-        private int rank;
+        private int mRank;
 
         private State(int rank) {
-            this.rank = rank;
+            mRank = rank;
         }
 
         @Override
@@ -61,22 +61,17 @@ public class GeckoView extends LayerView
         @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;
@@ -92,6 +87,10 @@ public class GeckoView extends LayerView
 
     @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() {}
 
@@ -101,9 +100,18 @@ public class GeckoView extends LayerView
                                 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.
@@ -197,9 +205,9 @@ public class GeckoView extends LayerView
         }
     }
 
-    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);
@@ -227,7 +235,7 @@ public class GeckoView extends LayerView
         GeckoAppShell.setLayerView(this);
 
         initializeView();
-        listener.registerListeners();
+        mListener.registerListeners();
 
         mSettings = new GeckoViewSettings(getEventDispatcher());
     }
@@ -235,8 +243,8 @@ public class GeckoView extends LayerView
     @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
@@ -244,9 +252,9 @@ public class GeckoView extends LayerView
         final StateBinder stateBinder = (StateBinder) state;
 
         if (stateBinder.window != null) {
-            this.window = stateBinder.window;
+            mWindow = stateBinder.window;
         }
-        stateSaved = false;
+        mStateSaved = false;
 
         if (mOnAttachedToWindowCalled) {
             reattachWindow();
@@ -263,12 +271,12 @@ public class GeckoView extends LayerView
         }
 
         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,
@@ -279,11 +287,13 @@ public class GeckoView extends LayerView
     }
 
     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);
         }
     }
@@ -292,9 +302,10 @@ public class GeckoView extends LayerView
     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();
@@ -310,21 +321,22 @@ public class GeckoView extends LayerView
         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;
     }
 
@@ -339,14 +351,14 @@ public class GeckoView extends LayerView
     * @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);
         }
     }
 
diff --git a/netwerk/cache2/moz.build b/netwerk/cache2/moz.build
index cd50ef4a11a3..fb489a56a499 100644
--- a/netwerk/cache2/moz.build
+++ b/netwerk/cache2/moz.build
@@ -25,6 +25,7 @@ EXPORTS += [
 ]
 
 UNIFIED_SOURCES += [
+    'AppCacheStorage.cpp',
     'CacheEntry.cpp',
     'CacheFile.cpp',
     'CacheFileChunk.cpp',
@@ -46,11 +47,6 @@ UNIFIED_SOURCES += [
     'OldWrappers.cpp',
 ]
 
-# AppCacheStorage.cpp cannot be built in unified mode because it uses plarena.h.
-SOURCES += [
-    'AppCacheStorage.cpp',
-]
-
 LOCAL_INCLUDES += [
     '/netwerk/base',
     '/netwerk/cache',
diff --git a/netwerk/protocol/http/HttpChannelChild.cpp b/netwerk/protocol/http/HttpChannelChild.cpp
index 35d78ff1e075..934aa6f4d807 100644
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -63,9 +63,6 @@ using namespace mozilla::ipc;
 namespace mozilla {
 namespace net {
 
-extern bool
-WillRedirect(nsHttpResponseHead * response);
-
 namespace {
 
 const uint32_t kMaxFileDescriptorsPerMessage = 250;
@@ -3179,14 +3176,14 @@ HttpChannelChild::OverrideWithSynthesizedResponse(nsAutoPtr&
 
   // 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();
diff --git a/netwerk/protocol/http/InterceptedChannel.cpp b/netwerk/protocol/http/InterceptedChannel.cpp
index 21fd5c37c8f5..0e4185910e4f 100644
--- a/netwerk/protocol/http/InterceptedChannel.cpp
+++ b/netwerk/protocol/http/InterceptedChannel.cpp
@@ -21,9 +21,6 @@
 namespace mozilla {
 namespace net {
 
-extern bool
-WillRedirect(const nsHttpResponseHead * response);
-
 extern nsresult
 DoUpdateExpirationTime(nsHttpChannel* aSelf,
                        nsICacheEntry* aCacheEntry,
@@ -246,7 +243,7 @@ InterceptedChannelChrome::FinishSynthesizedResponse(const nsACString& aFinalURLS
 
   // 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);
   }
diff --git a/netwerk/protocol/http/moz.build b/netwerk/protocol/http/moz.build
index 93623575c055..8f45248af5bb 100644
--- a/netwerk/protocol/http/moz.build
+++ b/netwerk/protocol/http/moz.build
@@ -49,18 +49,15 @@ EXPORTS.mozilla.net += [
     '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',
@@ -78,6 +75,7 @@ UNIFIED_SOURCES += [
     'nsCORSListenerProxy.cpp',
     'nsHttp.cpp',
     'nsHttpActivityDistributor.cpp',
+    'nsHttpAuthCache.cpp',
     'nsHttpAuthManager.cpp',
     'nsHttpBasicAuth.cpp',
     'nsHttpChannel.cpp',
diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp
index 6505c1c22b19..8ba1b9ce73e6 100644
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -192,9 +192,9 @@ Hash(const char *buf, nsACString &hash)
 // 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)
+nsHttpChannel::WillRedirect(nsHttpResponseHead * response)
 {
-    return nsHttpChannel::IsRedirectStatus(response->Status()) &&
+    return IsRedirectStatus(response->Status()) &&
            response->HasHeader(nsHttp::Location);
 }
 
diff --git a/netwerk/protocol/http/nsHttpChannel.h b/netwerk/protocol/http/nsHttpChannel.h
index ab98bec342e5..4fa4f17a0872 100644
--- a/netwerk/protocol/http/nsHttpChannel.h
+++ b/netwerk/protocol/http/nsHttpChannel.h
@@ -138,6 +138,7 @@ public:
                                  Http2PushedStream *pushedStream);
 
     static bool IsRedirectStatus(uint32_t status);
+    static bool WillRedirect(nsHttpResponseHead * response);
 
 
     // Methods HttpBaseChannel didn't implement for us or that we override.
diff --git a/netwerk/protocol/http/nsHttpDigestAuth.cpp b/netwerk/protocol/http/nsHttpDigestAuth.cpp
index a4b1b0c9fde3..7922309dbff8 100644
--- a/netwerk/protocol/http/nsHttpDigestAuth.cpp
+++ b/netwerk/protocol/http/nsHttpDigestAuth.cpp
@@ -8,6 +8,7 @@
 #include "HttpLog.h"
 
 #include "mozilla/Sprintf.h"
+#include "mozilla/Unused.h"
 
 #include "nsHttp.h"
 #include "nsHttpDigestAuth.h"
diff --git a/netwerk/streamconv/converters/nsIndexedToHTML.cpp b/netwerk/streamconv/converters/nsIndexedToHTML.cpp
index 821c49a22304..ac28970dc6cf 100644
--- a/netwerk/streamconv/converters/nsIndexedToHTML.cpp
+++ b/netwerk/streamconv/converters/nsIndexedToHTML.cpp
@@ -699,6 +699,10 @@ nsIndexedToHTML::OnIndexAvailable(nsIRequest *aRequest,
 
     // 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\"");
 
diff --git a/toolkit/content/license.html b/toolkit/content/license.html
index cbeee2e76c13..320e252b378b 100644
--- a/toolkit/content/license.html
+++ b/toolkit/content/license.html
@@ -101,6 +101,7 @@
       
  • Google Gears License
  • Google Gears/iStumbler License
  • Google VP8 License
  • +
  • GSL License
  • gyp License
  • halloc License
  • HarfBuzz License
  • @@ -3415,6 +3416,38 @@ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    +
    + +

    GSL License

    + +

    This license applies to mfbt/Span.h and + mfbt/tests/gtest/TestSpan.cpp.

    + +
    +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.
    +
    + +

    gyp License

    diff --git a/toolkit/crashreporter/nsExceptionHandler.cpp b/toolkit/crashreporter/nsExceptionHandler.cpp index f7ccda818481..78a20aa54c3d 100644 --- a/toolkit/crashreporter/nsExceptionHandler.cpp +++ b/toolkit/crashreporter/nsExceptionHandler.cpp @@ -116,6 +116,13 @@ using google_breakpad::PageAllocator; 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 @@ -1131,7 +1138,17 @@ bool MinidumpCallback( 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); } @@ -1577,6 +1594,8 @@ nsresult SetExceptionHandler(nsIFile* aXREDirectory, 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. diff --git a/toolkit/crashreporter/test/unit/test_crash_rust_panic.js b/toolkit/crashreporter/test/unit/test_crash_rust_panic.js new file mode 100644 index 000000000000..68c50a8b5365 --- /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); +} diff --git a/toolkit/crashreporter/test/unit/xpcshell.ini b/toolkit/crashreporter/test/unit/xpcshell.ini index bd7be8c765c0..f39c0a931034 100644 --- a/toolkit/crashreporter/test/unit/xpcshell.ini +++ b/toolkit/crashreporter/test/unit/xpcshell.ini @@ -7,6 +7,9 @@ support-files = [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] diff --git a/toolkit/library/rust/shared/lib.rs b/toolkit/library/rust/shared/lib.rs index 4f572bd4b1c6..936fd1875511 100644 --- a/toolkit/library/rust/shared/lib.rs +++ b/toolkit/library/rust/shared/lib.rs @@ -10,3 +10,59 @@ 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::() { + 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, + } + } +} diff --git a/widget/android/GeneratedJNIWrappers.cpp b/widget/android/GeneratedJNIWrappers.cpp index c497f519b3a9..bf8a2eba3d25 100644 --- a/widget/android/GeneratedJNIWrappers.cpp +++ b/widget/android/GeneratedJNIWrappers.cpp @@ -967,14 +967,6 @@ auto GeckoThread::State::RUNNING() -> State::LocalRef 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::Call(GeckoView::mCtx, nullptr, a0); -} - const char GeckoView::State::name[] = "org/mozilla/gecko/GeckoView$State"; @@ -1012,6 +1004,14 @@ 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::Call(Window::mCtx, nullptr, a0); +} + const char PrefsHelper::name[] = "org/mozilla/gecko/PrefsHelper"; diff --git a/widget/android/GeneratedJNIWrappers.h b/widget/android/GeneratedJNIWrappers.h index 1988950426d0..5b97d1a95894 100644 --- a/widget/android/GeneratedJNIWrappers.h +++ b/widget/android/GeneratedJNIWrappers.h @@ -2883,26 +2883,6 @@ public: 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; @@ -2910,7 +2890,7 @@ public: static const int32_t LOAD_SWITCH_TAB = 2; static const mozilla::jni::CallingThread callingThread = - mozilla::jni::CallingThread::GECKO; + mozilla::jni::CallingThread::ANY; }; @@ -3068,6 +3048,26 @@ public: 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; diff --git a/widget/android/nsWindow.cpp b/widget/android/nsWindow.cpp index a5f63bdc45e3..4be5ac55144c 100644 --- a/widget/android/nsWindow.cpp +++ b/widget/android/nsWindow.cpp @@ -242,7 +242,7 @@ class nsWindow::GeckoViewSupport final , public SupportsWeakPtr { nsWindow& window; - GeckoView::GlobalRef mView; + GeckoView::Window::GlobalRef mGeckoViewWindow; public: typedef GeckoView::Window::Natives Base; @@ -265,10 +265,9 @@ public: } 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(this)); } @@ -1252,7 +1251,7 @@ nsWindow::GeckoViewSupport::Open(const jni::Class::LocalRef& aCls, // Attach a new GeckoView support object to the new window. window->mGeckoViewSupport = mozilla::MakeUnique( - window, GeckoView::Window::LocalRef(aCls.Env(), aWindow), aView); + window, (GeckoView::Window::LocalRef(aCls.Env(), aWindow))); window->mGeckoViewSupport->mDOMWindow = pdomWindow; @@ -1297,6 +1296,7 @@ nsWindow::GeckoViewSupport::Close() mDOMWindow->ForceClose(); mDOMWindow = nullptr; + mGeckoViewWindow = nullptr; } void @@ -2078,8 +2078,10 @@ nsWindow::GetEventTimeStamp(int64_t aEventTime) void nsWindow::GeckoViewSupport::EnableEventDispatcher() { - MOZ_ASSERT(mView); - mView->SetState(GeckoView::State::READY()); + if (!mGeckoViewWindow) { + return; + } + mGeckoViewWindow->SetState(GeckoView::State::READY()); } void diff --git a/xpcom/base/nsDebugImpl.cpp b/xpcom/base/nsDebugImpl.cpp index da04743a1002..94b8e3ebb129 100644 --- a/xpcom/base/nsDebugImpl.cpp +++ b/xpcom/base/nsDebugImpl.cpp @@ -148,6 +148,16 @@ nsDebugImpl::Abort(const char* aFile, int32_t 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) { diff --git a/xpcom/base/nsIDebug2.idl b/xpcom/base/nsIDebug2.idl index 4401f8a917af..1fa69190c8d1 100644 --- a/xpcom/base/nsIDebug2.idl +++ b/xpcom/base/nsIDebug2.idl @@ -79,4 +79,11 @@ interface nsIDebug2 : nsISupports */ 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); }; diff --git a/xpcom/ds/nsTArray.h b/xpcom/ds/nsTArray.h index 71f9e93e75a8..8d9015ccce89 100644 --- a/xpcom/ds/nsTArray.h +++ b/xpcom/ds/nsTArray.h @@ -19,6 +19,7 @@ #include "mozilla/Move.h" #include "mozilla/ReverseIterator.h" #include "mozilla/TypeTraits.h" +#include "mozilla/Span.h" #include @@ -1116,6 +1117,18 @@ public: const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } const_reverse_iterator crend() const { return rend(); } + // Span integration + + operator mozilla::Span() + { + return mozilla::Span(Elements(), Length()); + } + + operator mozilla::Span() const + { + return mozilla::Span(Elements(), Length()); + } + // // Search methods // @@ -1340,6 +1353,16 @@ protected: return ReplaceElementsAt( aStart, aCount, aArray.Elements(), aArray.Length()); } + + template + elem_type* ReplaceElementsAt(index_type aStart, + size_type aCount, + mozilla::Span aSpan) + { + return ReplaceElementsAt( + aStart, aCount, aSpan.Elements(), aSpan.Length()); + } + public: template @@ -1351,6 +1374,15 @@ public: return ReplaceElementsAt(aStart, aCount, aArray); } + template + MOZ_MUST_USE elem_type* ReplaceElementsAt(index_type aStart, + size_type aCount, + mozilla::Span aSpan, + const mozilla::fallible_t&) + { + return ReplaceElementsAt(aStart, aCount, aSpan); + } + // A variation on the ReplaceElementsAt method defined above. protected: template @@ -1403,6 +1435,15 @@ protected: return ReplaceElementsAt( aIndex, 0, aArray.Elements(), aArray.Length()); } + + template + elem_type* InsertElementsAt(index_type aIndex, + mozilla::Span aSpan) + { + return ReplaceElementsAt( + aIndex, 0, aSpan.Elements(), aSpan.Length()); + } + public: template @@ -1414,6 +1455,14 @@ public: return InsertElementsAt(aIndex, aArray); } + template + MOZ_MUST_USE elem_type* InsertElementsAt(index_type aIndex, + mozilla::Span aSpan, + const mozilla::fallible_t&) + { + return InsertElementsAt(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. @@ -1548,6 +1597,13 @@ protected: template elem_type* AppendElements(const Item* aArray, size_type aArrayLen); + template + elem_type* AppendElements(mozilla::Span aSpan) + { + return AppendElements(aSpan.Elements(), + aSpan.Length()); + } + template elem_type* AppendElements(const mozilla::Array& aArray) { @@ -1564,6 +1620,15 @@ public: return AppendElements(aArray, aArrayLen); } + template + /* MOZ_MUST_USE */ + elem_type* AppendElements(mozilla::Span aSpan, + const mozilla::fallible_t&) + { + return AppendElements(aSpan.Elements(), + aSpan.Length()); + } + // A variation on the AppendElements method defined above. protected: template @@ -2397,6 +2462,25 @@ struct nsTArray_CopyChooser> typedef nsTArray_CopyWithConstructors> Type; }; +// Span integration +namespace mozilla { + +template +Span +MakeSpan(nsTArray_Impl& aTArray) +{ + return aTArray; +} + +template +Span +MakeSpan(const nsTArray_Impl& 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 diff --git a/xpcom/string/nsTSubstring.h b/xpcom/string/nsTSubstring.h index 1e83f6e591f3..8b07ef8d0244 100644 --- a/xpcom/string/nsTSubstring.h +++ b/xpcom/string/nsTSubstring.h @@ -9,6 +9,8 @@ #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." @@ -922,6 +924,68 @@ public: } #endif + /** + * Span integration + */ + + operator mozilla::Span() + { + return mozilla::MakeSpan(BeginWriting(), Length()); + } + + operator mozilla::Span() const + { + return mozilla::MakeSpan(BeginReading(), Length()); + } + + void Append(mozilla::Span aSpan) + { + auto len = aSpan.Length(); + MOZ_RELEASE_ASSERT(len <= mozilla::MaxValue::value); + Append(aSpan.Elements(), len); + } + + MOZ_MUST_USE bool Append(mozilla::Span aSpan, + const fallible_t& aFallible) + { + auto len = aSpan.Length(); + if (len > mozilla::MaxValue::value) { + return false; + } + return Append(aSpan.Elements(), len, aFallible); + } + +#if !defined(CharT_is_PRUnichar) + operator mozilla::Span() + { + return mozilla::MakeSpan(reinterpret_cast(BeginWriting()), + Length()); + } + + operator mozilla::Span() const + { + return mozilla::MakeSpan(reinterpret_cast(BeginReading()), + Length()); + } + + void Append(mozilla::Span aSpan) + { + auto len = aSpan.Length(); + MOZ_RELEASE_ASSERT(len <= mozilla::MaxValue::value); + Append(reinterpret_cast(aSpan.Elements()), len); + } + + MOZ_MUST_USE bool Append(mozilla::Span aSpan, + const fallible_t& aFallible) + { + auto len = aSpan.Length(); + if (len > mozilla::MaxValue::value) { + return false; + } + return Append( + reinterpret_cast(aSpan.Elements()), len, aFallible); + } +#endif /** * string data is never null, but can be marked void. if true, the @@ -1272,3 +1336,22 @@ public: return mArray[index]; } }; + +/** + * Span integration + */ +namespace mozilla { + +inline Span +MakeSpan(nsTSubstring_CharT& aString) +{ + return aString; +} + +inline Span +MakeSpan(const nsTSubstring_CharT& aString) +{ + return aString; +} + +} // namespace mozilla