Bug 992096 - Implement Sub Resource Integrity [1/2]. r=baku,r=ckerschb
authorFrancois Marier <francois@mozilla.com>
Wed, 12 Aug 2015 20:19:11 -0700
changeset 278205 bab5913ea6cbb558457670e1785b0296cc13acc0
parent 278204 bd4a7f61176e9b5407ba2c8f6efa37f55decb23f
child 278206 948b2e9d1fa282b3acd35a67e6b020b3e68510c7
push id8456
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:31:52 +0000
treeherdermozilla-aurora@7f2f0fb041b1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku, ckerschb
bugs992096
milestone43.0a1
Bug 992096 - Implement Sub Resource Integrity [1/2]. r=baku,r=ckerschb Code changes
AUTHORS
browser/devtools/webconsole/webconsole.js
dom/base/moz.build
dom/base/nsContentSink.cpp
dom/base/nsDocument.cpp
dom/base/nsDocument.h
dom/base/nsGkAtomList.h
dom/base/nsIDocument.h
dom/base/nsScriptLoader.cpp
dom/base/nsScriptLoader.h
dom/base/nsStyleLinkElement.cpp
dom/html/HTMLLinkElement.cpp
dom/html/HTMLLinkElement.h
dom/html/HTMLScriptElement.cpp
dom/html/HTMLScriptElement.h
dom/locales/en-US/chrome/security/security.properties
dom/security/SRICheck.cpp
dom/security/SRICheck.h
dom/security/SRIMetadata.cpp
dom/security/SRIMetadata.h
dom/security/moz.build
dom/webidl/HTMLLinkElement.webidl
dom/webidl/HTMLScriptElement.webidl
layout/style/CSSStyleSheet.cpp
layout/style/CSSStyleSheet.h
layout/style/Loader.cpp
layout/style/Loader.h
modules/libpref/init/all.js
parser/html/nsHtml5SpeculativeLoad.cpp
parser/html/nsHtml5SpeculativeLoad.h
parser/html/nsHtml5TreeBuilderCppSupplement.h
parser/html/nsHtml5TreeOpExecutor.cpp
parser/html/nsHtml5TreeOpExecutor.h
xpcom/base/ErrorList.h
--- a/AUTHORS
+++ b/AUTHORS
@@ -354,16 +354,17 @@ Fernando Jimenez <ferjmoreno@gmail.com>
 Flock Inc.
 Florian Boesch <pyalot@gmail.com>
 Florian Hänel <heeen@gmx.de>
 Florian Queze <florian@queze.net>
 Florian Scholz <elchi3@elchi3.de>
 <flying@dom.natm.ru>
 France Telecom Research and Development
 Franck
+Francois Marier <francois@fmarier.org>
 Frank Tang <ftang@netscape.com>
 Frank Yan <fyan@mozilla.com>
 Franky Braem
 <franky@pacificconnections.com>
 Franz Sirl <Franz.Sirl-kernel@lauterbach.com>
 Frederic Plourde <frederic.plourde@polymtl.ca>
 Frederic Wang <fred.wang@free.fr>
 Fredrik Holmqvist <thesuckiestemail@yahoo.se>
--- a/browser/devtools/webconsole/webconsole.js
+++ b/browser/devtools/webconsole/webconsole.js
@@ -4802,16 +4802,17 @@ var Utils = {
       case "Invalid HSTS Headers":
       case "Invalid HPKP Headers":
       case "SHA-1 Signature":
       case "Insecure Password Field":
       case "SSL":
       case "CORS":
       case "Iframe Sandbox":
       case "Tracking Protection":
+      case "Sub-resource Integrity":
         return CATEGORY_SECURITY;
 
       default:
         return CATEGORY_JS;
     }
   },
 
   /**
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -427,16 +427,17 @@ LOCAL_INCLUDES += [
     '/js/xpconnect/src',
     '/js/xpconnect/wrappers',
     '/layout/base',
     '/layout/generic',
     '/layout/style',
     '/layout/svg',
     '/layout/xul',
     '/netwerk/base',
+    '/security/manager/ssl',
     '/widget',
     '/xpcom/ds',
 ]
 
 if CONFIG['MOZ_B2G_BT_API_V1']:
     LOCAL_INCLUDES += [
         '../bluetooth/bluetooth1',
     ]
--- a/dom/base/nsContentSink.cpp
+++ b/dom/base/nsContentSink.cpp
@@ -46,16 +46,26 @@
 #include "nsIWebNavigation.h"
 #include "nsGenericHTMLElement.h"
 #include "nsHTMLDNSPrefetch.h"
 #include "nsIObserverService.h"
 #include "mozilla/Preferences.h"
 #include "nsParserConstants.h"
 #include "nsSandboxFlags.h"
 
+static PRLogModuleInfo*
+GetSriLog()
+{
+  static PRLogModuleInfo *gSriPRLog;
+  if (!gSriPRLog) {
+    gSriPRLog = PR_NewLogModule("SRI");
+  }
+  return gSriPRLog;
+}
+
 using namespace mozilla;
 
 PRLogModuleInfo* gContentSinkLogModuleInfo;
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsContentSink)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsContentSink)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsContentSink)
@@ -745,22 +755,33 @@ nsContentSink::ProcessStyleLink(nsIConte
     // The URI is bad, move along, don't propagate the error (for now)
     return NS_OK;
   }
 
   NS_ASSERTION(!aElement ||
                aElement->NodeType() == nsIDOMNode::PROCESSING_INSTRUCTION_NODE,
                "We only expect processing instructions here");
 
+  nsAutoString integrity;
+  if (aElement) {
+    aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::integrity, integrity);
+  }
+  if (!integrity.IsEmpty()) {
+    MOZ_LOG(GetSriLog(), mozilla::LogLevel::Debug,
+            ("nsContentSink::ProcessStyleLink, integrity=%s",
+             NS_ConvertUTF16toUTF8(integrity).get()));
+  }
+
   // If this is a fragment parser, we don't want to observe.
   // We don't support CORS for processing instructions
   bool isAlternate;
   rv = mCSSLoader->LoadStyleLink(aElement, url, aTitle, aMedia, aAlternate,
                                  CORS_NONE, mDocument->GetReferrerPolicy(),
-                                 mRunsToCompletion ? nullptr : this, &isAlternate);
+                                 integrity, mRunsToCompletion ? nullptr : this,
+                                 &isAlternate);
   NS_ENSURE_SUCCESS(rv, rv);
   
   if (!isAlternate && !mRunsToCompletion) {
     ++mPendingSheetCount;
     mScriptLoader->AddExecuteBlocker();
   }
 
   return NS_OK;
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -9871,27 +9871,28 @@ public:
 };
 NS_IMPL_ISUPPORTS(StubCSSLoaderObserver, nsICSSLoaderObserver)
 
 } // namespace
 
 void
 nsDocument::PreloadStyle(nsIURI* uri, const nsAString& charset,
                          const nsAString& aCrossOriginAttr,
-                         const ReferrerPolicy aReferrerPolicy)
+                         const ReferrerPolicy aReferrerPolicy,
+                         const nsAString& aIntegrity)
 {
   // The CSSLoader will retain this object after we return.
   nsCOMPtr<nsICSSLoaderObserver> obs = new StubCSSLoaderObserver();
 
   // Charset names are always ASCII.
   CSSLoader()->LoadSheet(uri, NodePrincipal(),
                          NS_LossyConvertUTF16toASCII(charset),
                          obs,
                          Element::StringToCORSMode(aCrossOriginAttr),
-                         aReferrerPolicy);
+                         aReferrerPolicy, aIntegrity);
 }
 
 nsresult
 nsDocument::LoadChromeSheetSync(nsIURI* uri, bool isAgentSheet,
                                 CSSStyleSheet** sheet)
 {
   return CSSLoader()->LoadSheetSync(uri, isAgentSheet, isAgentSheet, sheet);
 }
--- a/dom/base/nsDocument.h
+++ b/dom/base/nsDocument.h
@@ -1142,17 +1142,18 @@ public:
                                  ReferrerPolicy aReferrerPolicy) override;
   virtual void ForgetImagePreload(nsIURI* aURI) override;
 
   virtual void MaybePreconnect(nsIURI* uri,
                                mozilla::CORSMode aCORSMode) override;
 
   virtual void PreloadStyle(nsIURI* uri, const nsAString& charset,
                             const nsAString& aCrossOriginAttr,
-                            ReferrerPolicy aReferrerPolicy) override;
+                            ReferrerPolicy aReferrerPolicy,
+                            const nsAString& aIntegrity) override;
 
   virtual nsresult LoadChromeSheetSync(nsIURI* uri, bool isAgentSheet,
                                        mozilla::CSSStyleSheet** sheet) override;
 
   virtual nsISupports* GetCurrentContentSink() override;
 
   virtual mozilla::EventStates GetDocumentState() override;
 
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -484,16 +484,17 @@ GK_ATOM(input, "input")
 GK_ATOM(inputmode, "inputmode")
 GK_ATOM(ins, "ins")
 GK_ATOM(insertafter, "insertafter")
 GK_ATOM(insertbefore, "insertbefore")
 GK_ATOM(instanceOf, "instanceOf")
 GK_ATOM(int32, "int32")
 GK_ATOM(int64, "int64")
 GK_ATOM(integer, "integer")
+GK_ATOM(integrity, "integrity")
 GK_ATOM(intersection, "intersection")
 GK_ATOM(is, "is")
 GK_ATOM(iscontainer, "iscontainer")
 GK_ATOM(isempty, "isempty")
 GK_ATOM(ismap, "ismap")
 GK_ATOM(itemid, "itemid")
 GK_ATOM(itemprop, "itemprop")
 GK_ATOM(itemref, "itemref")
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -147,18 +147,18 @@ template<typename> class Sequence;
 
 template<typename, typename> class CallbackObjectHolder;
 typedef CallbackObjectHolder<NodeFilter, nsIDOMNodeFilter> NodeFilterHolder;
 
 } // namespace dom
 } // namespace mozilla
 
 #define NS_IDOCUMENT_IID \
-{ 0xbbce44c8, 0x22fe, 0x404f, \
-  { 0x9e, 0x71, 0x23, 0x1d, 0xf4, 0xcc, 0x8e, 0x34 } }
+{ 0x6d18ec0b, 0x1f68, 0x4ae6, \
+  { 0x8b, 0x3d, 0x8d, 0x7d, 0x8b, 0x8e, 0x28, 0xd4 } }
 
 // Enum for requesting a particular type of document when creating a doc
 enum DocumentFlavor {
   DocumentFlavorLegacyGuess, // compat with old code until made HTML5-compliant
   DocumentFlavorHTML, // HTMLDocument with HTMLness bit set to true
   DocumentFlavorSVG, // SVGDocument
   DocumentFlavorPlain, // Just a Document
 };
@@ -2018,17 +2018,18 @@ public:
 
   /**
    * Called by nsParser to preload style sheets.  Can also be merged into the
    * parser if and when the parser is merged with libgklayout.  aCrossOriginAttr
    * should be a void string if the attr is not present.
    */
   virtual void PreloadStyle(nsIURI* aURI, const nsAString& aCharset,
                             const nsAString& aCrossOriginAttr,
-                            ReferrerPolicyEnum aReferrerPolicy) = 0;
+                            ReferrerPolicyEnum aReferrerPolicy,
+                            const nsAString& aIntegrity) = 0;
 
   /**
    * Called by the chrome registry to load style sheets.  Can be put
    * back there if and when when that module is merged with libgklayout.
    *
    * This always does a synchronous load.  If aIsAgentSheet is true,
    * it also uses the system principal and enables unsafe rules.
    * DO NOT USE FOR UNTRUSTED CONTENT.
--- a/dom/base/nsScriptLoader.cpp
+++ b/dom/base/nsScriptLoader.cpp
@@ -48,19 +48,31 @@
 #include "nsSandboxFlags.h"
 #include "nsContentTypeParser.h"
 #include "nsINetworkPredictor.h"
 #include "ImportManager.h"
 #include "mozilla/dom/EncodingUtils.h"
 
 #include "mozilla/Attributes.h"
 #include "mozilla/unused.h"
+#include "mozilla/dom/SRICheck.h"
+#include "nsIScriptError.h"
 
 static PRLogModuleInfo* gCspPRLog;
 
+static PRLogModuleInfo*
+GetSriLog()
+{
+  static PRLogModuleInfo *gSriPRLog;
+  if (!gSriPRLog) {
+    gSriPRLog = PR_NewLogModule("SRI");
+  }
+  return gSriPRLog;
+}
+
 using namespace mozilla;
 using namespace mozilla::dom;
 
 // The nsScriptLoadRequest is passed as the context to necko, and thus
 // it needs to be threadsafe. Necko won't do anything with this
 // context, but it will AddRef and Release it on other threads.
 NS_IMPL_ISUPPORTS0(nsScriptLoadRequest)
 
@@ -601,17 +613,32 @@ nsScriptLoader::ProcessScriptElement(nsI
       } else {
         // Drop the preload
         request = nullptr;
       }
     }
 
     if (!request) {
       // no usable preload
-      request = new nsScriptLoadRequest(aElement, version, ourCORSMode);
+
+      SRIMetadata sriMetadata;
+      {
+        nsAutoString integrity;
+        scriptContent->GetAttr(kNameSpaceID_None, nsGkAtoms::integrity,
+                               integrity);
+        if (!integrity.IsEmpty()) {
+          MOZ_LOG(GetSriLog(), mozilla::LogLevel::Debug,
+                 ("nsScriptLoader::ProcessScriptElement, integrity=%s",
+                  NS_ConvertUTF16toUTF8(integrity).get()));
+          SRICheck::IntegrityMetadata(integrity, mDocument, &sriMetadata);
+        }
+      }
+
+      request = new nsScriptLoadRequest(aElement, version, ourCORSMode,
+                                        sriMetadata);
       request->mURI = scriptURI;
       request->mIsInline = false;
       request->mLoading = true;
       request->mReferrerPolicy = ourRefPolicy;
 
       // set aScriptFromHead to false so we don't treat non preloaded scripts as
       // blockers for full page load. See bug 792438.
       rv = StartLoad(request, type, false);
@@ -715,17 +742,18 @@ nsScriptLoader::ProcessScriptElement(nsI
   }
 
   // Does CSP allow this inline script to run?
   if (!CSPAllowsInlineScript(aElement, mDocument)) {
     return false;
   }
 
   // Inline scripts ignore ther CORS mode and are always CORS_NONE
-  request = new nsScriptLoadRequest(aElement, version, CORS_NONE);
+  request = new nsScriptLoadRequest(aElement, version, CORS_NONE,
+                                    SRIMetadata()); // SRI doesn't apply
   request->mJSVersion = version;
   request->mLoading = false;
   request->mIsInline = true;
   request->mURI = mDocument->GetDocumentURI();
   request->mLineNo = aElement->GetScriptLineNumber();
 
   if (aElement->GetParserCreated() == FROM_PARSER_XSLT &&
       (!ReadyToExecuteScripts() || !mXSLTRequests.isEmpty())) {
@@ -1403,18 +1431,25 @@ nsScriptLoader::OnStreamComplete(nsIStre
                                  nsresult aStatus,
                                  uint32_t aStringLen,
                                  const uint8_t* aString)
 {
   nsScriptLoadRequest* request = static_cast<nsScriptLoadRequest*>(aContext);
   NS_ASSERTION(request, "null request in stream complete handler");
   NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
 
-  nsresult rv = PrepareLoadedRequest(request, aLoader, aStatus, aStringLen,
-                                     aString);
+  nsresult rv = NS_ERROR_SRI_CORRUPT;
+  if (request->mIntegrity.IsEmpty() ||
+      NS_SUCCEEDED(SRICheck::VerifyIntegrity(request->mIntegrity,
+                                             request->mURI,
+                                             request->mCORSMode, aStringLen,
+                                             aString, mDocument))) {
+    rv = PrepareLoadedRequest(request, aLoader, aStatus, aStringLen, aString);
+  }
+
   if (NS_FAILED(rv)) {
     /*
      * Handle script not loading error because source was a tracking URL.
      * We make a note of this script node by including it in a dedicated
      * array of blocked tracking nodes under its parent document.
      */
     if (rv == NS_ERROR_TRACKING_URI) {
       nsCOMPtr<nsIContent> cont = do_QueryInterface(request->mElement);
@@ -1598,27 +1633,37 @@ nsScriptLoader::ParsingComplete(bool aTe
   // onload and all.
   ProcessPendingRequests();
 }
 
 void
 nsScriptLoader::PreloadURI(nsIURI *aURI, const nsAString &aCharset,
                            const nsAString &aType,
                            const nsAString &aCrossOrigin,
+                           const nsAString& aIntegrity,
                            bool aScriptFromHead,
                            const mozilla::net::ReferrerPolicy aReferrerPolicy)
 {
   // Check to see if scripts has been turned off.
   if (!mEnabled || !mDocument->IsScriptEnabled()) {
     return;
   }
 
+  SRIMetadata sriMetadata;
+  if (!aIntegrity.IsEmpty()) {
+    MOZ_LOG(GetSriLog(), mozilla::LogLevel::Debug,
+           ("nsScriptLoader::PreloadURI, integrity=%s",
+            NS_ConvertUTF16toUTF8(aIntegrity).get()));
+    SRICheck::IntegrityMetadata(aIntegrity, mDocument, &sriMetadata);
+  }
+
   nsRefPtr<nsScriptLoadRequest> request =
     new nsScriptLoadRequest(nullptr, 0,
-                            Element::StringToCORSMode(aCrossOrigin));
+                            Element::StringToCORSMode(aCrossOrigin),
+                            sriMetadata);
   request->mURI = aURI;
   request->mIsInline = false;
   request->mLoading = true;
   request->mReferrerPolicy = aReferrerPolicy;
 
   nsresult rv = StartLoad(request, aType, aScriptFromHead);
   if (NS_FAILED(rv)) {
     return;
--- a/dom/base/nsScriptLoader.h
+++ b/dom/base/nsScriptLoader.h
@@ -14,16 +14,17 @@
 #include "nsCOMPtr.h"
 #include "nsIScriptElement.h"
 #include "nsCOMArray.h"
 #include "nsTArray.h"
 #include "nsAutoPtr.h"
 #include "nsIDocument.h"
 #include "nsIStreamLoader.h"
 #include "mozilla/CORSMode.h"
+#include "mozilla/dom/SRIMetadata.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/net/ReferrerPolicy.h"
 
 class nsScriptLoadRequestList;
 class nsIURI;
 
 namespace JS {
   class SourceBufferHolder;
@@ -51,31 +52,33 @@ class nsScriptLoadRequest final : public
 
   // Allow LinkedListElement<nsScriptLoadRequest> to cast us to itself as needed.
   friend class mozilla::LinkedListElement<nsScriptLoadRequest>;
   friend class nsScriptLoadRequestList;
 
 public:
   nsScriptLoadRequest(nsIScriptElement* aElement,
                       uint32_t aVersion,
-                      mozilla::CORSMode aCORSMode)
+                      mozilla::CORSMode aCORSMode,
+                      const mozilla::dom::SRIMetadata &aIntegrity)
     : mElement(aElement),
       mLoading(true),
       mIsInline(true),
       mHasSourceMapURL(false),
       mIsDefer(false),
       mIsAsync(false),
       mIsNonAsyncScriptInserted(false),
       mIsXSLT(false),
       mIsCanceled(false),
       mScriptTextBuf(nullptr),
       mScriptTextLength(0),
       mJSVersion(aVersion),
       mLineNo(1),
       mCORSMode(aCORSMode),
+      mIntegrity(aIntegrity),
       mReferrerPolicy(mozilla::net::RP_Default)
   {
   }
 
   NS_DECL_THREADSAFE_ISUPPORTS
 
   void FireScriptAvailable(nsresult aResult)
   {
@@ -117,16 +120,17 @@ public:
   char16_t* mScriptTextBuf; // Holds script text for non-inline scripts. Don't
   size_t mScriptTextLength; // use nsString so we can give ownership to jsapi.
   uint32_t mJSVersion;
   nsCOMPtr<nsIURI> mURI;
   nsCOMPtr<nsIPrincipal> mOriginPrincipal;
   nsAutoCString mURL;   // Keep the URI's filename alive during off thread parsing.
   int32_t mLineNo;
   const mozilla::CORSMode mCORSMode;
+  const mozilla::dom::SRIMetadata mIntegrity;
   mozilla::net::ReferrerPolicy mReferrerPolicy;
 };
 
 class nsScriptLoadRequestList : private mozilla::LinkedList<nsScriptLoadRequest>
 {
   typedef mozilla::LinkedList<nsScriptLoadRequest> super;
 
 public:
@@ -362,21 +366,23 @@ public:
   /**
    * Adds aURI to the preload list and starts loading it.
    *
    * @param aURI The URI of the external script.
    * @param aCharset The charset parameter for the script.
    * @param aType The type parameter for the script.
    * @param aCrossOrigin The crossorigin attribute for the script.
    *                     Void if not present.
+   * @param aIntegrity The expect hash url, if avail, of the request
    * @param aScriptFromHead Whether or not the script was a child of head
    */
   virtual void PreloadURI(nsIURI *aURI, const nsAString &aCharset,
                           const nsAString &aType,
                           const nsAString &aCrossOrigin,
+                          const nsAString& aIntegrity,
                           bool aScriptFromHead,
                           const mozilla::net::ReferrerPolicy aReferrerPolicy);
 
   /**
    * Process a request that was deferred so that the script could be compiled
    * off thread.
    */
   nsresult ProcessOffThreadRequest(nsScriptLoadRequest *aRequest,
--- a/dom/base/nsStyleLinkElement.cpp
+++ b/dom/base/nsStyleLinkElement.cpp
@@ -416,23 +416,31 @@ nsStyleLinkElement::DoUpdateStyleSheet(n
       return rv;
 
     // Parse the style sheet.
     rv = doc->CSSLoader()->
       LoadInlineStyle(thisContent, text, mLineNumber, title, media,
                       scopeElement, aObserver, &doneLoading, &isAlternate);
   }
   else {
+    nsAutoString integrity;
+    thisContent->GetAttr(kNameSpaceID_None, nsGkAtoms::integrity, integrity);
+    if (!integrity.IsEmpty()) {
+      MOZ_LOG(GetSriLog(), mozilla::LogLevel::Debug,
+              ("nsStyleLinkElement::DoUpdateStyleSheet, integrity=%s",
+               NS_ConvertUTF16toUTF8(integrity).get()));
+    }
+
     // XXXbz clone the URI here to work around content policies modifying URIs.
     nsCOMPtr<nsIURI> clonedURI;
     uri->Clone(getter_AddRefs(clonedURI));
     NS_ENSURE_TRUE(clonedURI, NS_ERROR_OUT_OF_MEMORY);
     rv = doc->CSSLoader()->
       LoadStyleLink(thisContent, clonedURI, title, media, isAlternate,
-                    GetCORSMode(), doc->GetReferrerPolicy(),
+                    GetCORSMode(), doc->GetReferrerPolicy(), integrity,
                     aObserver, &isAlternate);
     if (NS_FAILED(rv)) {
       // Don't propagate LoadStyleLink() errors further than this, since some
       // consumers (e.g. nsXMLContentSink) will completely abort on innocuous
       // things like a stylesheet load being blocked by the security system.
       doneLoading = true;
       isAlternate = false;
       rv = NS_OK;
--- a/dom/html/HTMLLinkElement.cpp
+++ b/dom/html/HTMLLinkElement.cpp
@@ -209,16 +209,21 @@ HTMLLinkElement::ParseAttribute(int32_t 
       ParseCORSValue(aValue, aResult);
       return true;
     }
 
     if (aAttribute == nsGkAtoms::sizes) {
       aResult.ParseAtomArray(aValue);
       return true;
     }
+
+    if (aAttribute == nsGkAtoms::integrity) {
+      aResult.ParseStringOrAtom(aValue);
+      return true;
+    }
   }
 
   return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
                                               aResult);
 }
 
 void
 HTMLLinkElement::CreateAndDispatchEvent(nsIDocument* aDoc,
--- a/dom/html/HTMLLinkElement.h
+++ b/dom/html/HTMLLinkElement.h
@@ -138,16 +138,24 @@ public:
   {
     SetHTMLAttr(nsGkAtoms::rev, aRev, aRv);
   }
   // XPCOM GetTarget is fine.
   void SetTarget(const nsAString& aTarget, ErrorResult& aRv)
   {
     SetHTMLAttr(nsGkAtoms::target, aTarget, aRv);
   }
+  void GetIntegrity(nsAString& aIntegrity) const
+  {
+    GetHTMLAttr(nsGkAtoms::integrity, aIntegrity);
+  }
+  void SetIntegrity(const nsAString& aIntegrity, ErrorResult& aRv)
+  {
+    SetHTMLAttr(nsGkAtoms::integrity, aIntegrity, aRv);
+  }
 
   already_AddRefed<nsIDocument> GetImport();
   already_AddRefed<ImportLoader> GetImportLoader()
   {
     return nsRefPtr<ImportLoader>(mImportLoader).forget();
   }
 
 protected:
--- a/dom/html/HTMLScriptElement.cpp
+++ b/dom/html/HTMLScriptElement.cpp
@@ -70,20 +70,26 @@ HTMLScriptElement::BindToTree(nsIDocumen
 }
 
 bool
 HTMLScriptElement::ParseAttribute(int32_t aNamespaceID,
                                   nsIAtom* aAttribute,
                                   const nsAString& aValue,
                                   nsAttrValue& aResult)
 {
-  if (aNamespaceID == kNameSpaceID_None &&
-      aAttribute == nsGkAtoms::crossorigin) {
-    ParseCORSValue(aValue, aResult);
-    return true;
+  if (aNamespaceID == kNameSpaceID_None) {
+    if (aAttribute == nsGkAtoms::crossorigin) {
+      ParseCORSValue(aValue, aResult);
+      return true;
+    }
+
+    if (aAttribute == nsGkAtoms::integrity) {
+      aResult.ParseStringOrAtom(aValue);
+      return true;
+    }
   }
 
   return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
                                               aResult);
 }
 
 nsresult
 HTMLScriptElement::Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const
--- a/dom/html/HTMLScriptElement.h
+++ b/dom/html/HTMLScriptElement.h
@@ -74,16 +74,24 @@ public:
     // always parse to an enum value, so we don't need an invalid
     // default, and we _want_ the missing default to be null.
     GetEnumAttr(nsGkAtoms::crossorigin, nullptr, aResult);
   }
   void SetCrossOrigin(const nsAString& aCrossOrigin, ErrorResult& aError)
   {
     SetOrRemoveNullableStringAttr(nsGkAtoms::crossorigin, aCrossOrigin, aError);
   }
+  void GetIntegrity(nsAString& aIntegrity)
+  {
+    GetHTMLAttr(nsGkAtoms::integrity, aIntegrity);
+  }
+  void SetIntegrity(const nsAString& aIntegrity, ErrorResult& rv)
+  {
+    SetHTMLAttr(nsGkAtoms::integrity, aIntegrity, rv);
+  }
   bool Async();
   void SetAsync(bool aValue, ErrorResult& rv);
 
 protected:
   virtual ~HTMLScriptElement();
 
   virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override;
   // nsScriptElement
--- a/dom/locales/en-US/chrome/security/security.properties
+++ b/dom/locales/en-US/chrome/security/security.properties
@@ -48,12 +48,27 @@ InsecurePasswordsPresentOnPage=Password 
 InsecureFormActionPasswordsPresent=Password fields present in a form with an insecure (http://) form action. This is a security risk that allows user login credentials to be stolen.
 InsecurePasswordsPresentOnIframe=Password fields present on an insecure (http://) iframe. This is a security risk that allows user login credentials to be stolen.
 # LOCALIZATION NOTE: "%1$S" is the URI of the insecure mixed content resource
 LoadingMixedActiveContent2=Loading mixed (insecure) active content "%1$S" on a secure page
 LoadingMixedDisplayContent2=Loading mixed (insecure) display content "%1$S" on a secure page
 # LOCALIZATION NOTE: Do not translate "allow-scripts", "allow-same-origin", "sandbox" or "iframe"
 BothAllowScriptsAndSameOriginPresent=An iframe which has both allow-scripts and allow-same-origin for its sandbox attribute can remove its sandboxing.
 
+# Sub-Resource Integrity
+# LOCALIZATION NOTE: Do not translate "script" or "integrity"
+MalformedIntegrityURI=The script element has a malformed URI in its integrity attribute: "%1$S". The correct format is "<hash algorithm>-<hash value>".
+# LOCALIZATION NOTE: Do not translate "integrity"
+InvalidIntegrityLength=The hash contained in the integrity attribute has the wrong length.
+# LOCALIZATION NOTE: Do not translate "integrity"
+InvalidIntegrityBase64=The hash contained in the integrity attribute could not be decoded.
+# LOCALIZATION NOTE: Do not translate "integrity"
+IntegrityMismatch=None of the "%1$S" hashes in the integrity attribute match the content of the subresource.
+IneligibleResource="%1$S" is not eligible for integrity checks since it's neither CORS-enabled nor same-origin.
+# LOCALIZATION NOTE: Do not translate "integrity"
+UnsupportedHashAlg=Unsupported hash algorithm in the integrity attribute: "%1$S"
+# LOCALIZATION NOTE: Do not translate "integrity"
+NoValidMetadata=The integrity attribute does not contain any valid metadata.
+
 # LOCALIZATION NOTE: Do not translate "SSL 3.0".
 WeakProtocolVersionWarning=This site uses the protocol SSL 3.0 for encryption, which is deprecated and insecure.
 # LOCALIZATION NOTE: Do not translate "RC4".
 WeakCipherSuiteWarning=This site uses the cipher RC4 for encryption, which is deprecated and insecure.
new file mode 100644
--- /dev/null
+++ b/dom/security/SRICheck.cpp
@@ -0,0 +1,295 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SRICheck.h"
+
+#include "mozilla/Base64.h"
+#include "mozilla/Logging.h"
+#include "mozilla/Preferences.h"
+#include "nsContentUtils.h"
+#include "nsICryptoHash.h"
+#include "nsIDocument.h"
+#include "nsIProtocolHandler.h"
+#include "nsIScriptError.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsIURI.h"
+#include "nsNetUtil.h"
+#include "nsWhitespaceTokenizer.h"
+
+static PRLogModuleInfo*
+GetSriLog()
+{
+  static PRLogModuleInfo *gSriPRLog;
+  if (!gSriPRLog) {
+    gSriPRLog = PR_NewLogModule("SRI");
+  }
+  return gSriPRLog;
+}
+
+#define SRILOG(args) MOZ_LOG(GetSriLog(), mozilla::LogLevel::Debug, args)
+#define SRIERROR(args) MOZ_LOG(GetSriLog(), mozilla::LogLevel::Error, args)
+
+namespace mozilla {
+namespace dom {
+
+/**
+ * Returns whether or not the sub-resource about to be loaded is eligible
+ * for integrity checks. If it's not, the checks will be skipped and the
+ * sub-resource will be loaded.
+ */
+static nsresult
+IsEligible(nsIURI* aRequestURI, const CORSMode aCORSMode,
+           const nsIDocument* aDocument)
+{
+  NS_ENSURE_ARG_POINTER(aRequestURI);
+  NS_ENSURE_ARG_POINTER(aDocument);
+
+  nsAutoCString requestSpec;
+  nsresult rv = aRequestURI->GetSpec(requestSpec);
+  NS_ENSURE_SUCCESS(rv, rv);
+  NS_ConvertUTF8toUTF16 requestSpecUTF16(requestSpec);
+
+  // Was the sub-resource loaded via CORS?
+  if (aCORSMode != CORS_NONE) {
+    SRILOG(("SRICheck::IsEligible, CORS mode"));
+    return NS_OK;
+  }
+
+  // Is the sub-resource same-origin?
+  nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
+  if (NS_SUCCEEDED(ssm->CheckSameOriginURI(aDocument->GetDocumentURI(),
+                                           aRequestURI, false))) {
+    SRILOG(("SRICheck::IsEligible, same-origin"));
+    return NS_OK;
+  }
+  if (MOZ_LOG_TEST(GetSriLog(), mozilla::LogLevel::Debug)) {
+    nsAutoCString documentURI;
+    aDocument->GetDocumentURI()->GetAsciiSpec(documentURI);
+    // documentURI will be empty if GetAsciiSpec failed
+    SRILOG(("SRICheck::IsEligible, NOT same origin: documentURI=%s; requestURI=%s",
+            documentURI.get(), requestSpec.get()));
+  }
+
+  const char16_t* params[] = { requestSpecUTF16.get() };
+  nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
+                                  NS_LITERAL_CSTRING("Sub-resource Integrity"),
+                                  aDocument,
+                                  nsContentUtils::eSECURITY_PROPERTIES,
+                                  "IneligibleResource",
+                                  params, ArrayLength(params));
+  return NS_ERROR_SRI_NOT_ELIGIBLE;
+}
+
+/**
+ * Compute the hash of a sub-resource and compare it with the expected
+ * value.
+ */
+static nsresult
+VerifyHash(const SRIMetadata& aMetadata, uint32_t aHashIndex,
+           uint32_t aStringLen, const uint8_t* aString,
+           const nsIDocument* aDocument)
+{
+  NS_ENSURE_ARG_POINTER(aString);
+  NS_ENSURE_ARG_POINTER(aDocument);
+
+  nsAutoCString base64Hash;
+  aMetadata.GetHash(aHashIndex, &base64Hash);
+  SRILOG(("SRICheck::VerifyHash, hash[%u]=%s", aHashIndex, base64Hash.get()));
+
+  nsAutoCString binaryHash;
+  if (NS_WARN_IF(NS_FAILED(Base64Decode(base64Hash, binaryHash)))) {
+    nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
+                                    NS_LITERAL_CSTRING("Sub-resource Integrity"),
+                                    aDocument,
+                                    nsContentUtils::eSECURITY_PROPERTIES,
+                                    "InvalidIntegrityBase64");
+    return NS_ERROR_SRI_CORRUPT;
+  }
+
+  uint32_t hashLength;
+  int8_t hashType;
+  aMetadata.GetHashType(&hashType, &hashLength);
+  if (binaryHash.Length() != hashLength) {
+    nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
+                                    NS_LITERAL_CSTRING("Sub-resource Integrity"),
+                                    aDocument,
+                                    nsContentUtils::eSECURITY_PROPERTIES,
+                                    "InvalidIntegrityLength");
+    return NS_ERROR_SRI_CORRUPT;
+  }
+
+  nsresult rv;
+  nsCOMPtr<nsICryptoHash> cryptoHash =
+    do_CreateInstance("@mozilla.org/security/hash;1", &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = cryptoHash->Init(hashType);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = cryptoHash->Update(aString, aStringLen);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoCString computedHash;
+  rv = cryptoHash->Finish(false, computedHash);
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (!binaryHash.Equals(computedHash)) {
+    SRILOG(("SRICheck::VerifyHash, hash[%u] did not match", aHashIndex));
+    return NS_ERROR_SRI_CORRUPT;
+  }
+
+  SRILOG(("SRICheck::VerifyHash, hash[%u] verified successfully", aHashIndex));
+  return NS_OK;
+}
+
+/* static */ nsresult
+SRICheck::IntegrityMetadata(const nsAString& aMetadataList,
+                            const nsIDocument* aDocument,
+                            SRIMetadata* outMetadata)
+{
+  NS_ENSURE_ARG_POINTER(outMetadata);
+  NS_ENSURE_ARG_POINTER(aDocument);
+  MOZ_ASSERT(outMetadata->IsEmpty()); // caller must pass empty metadata
+
+  if (!Preferences::GetBool("security.sri.enable", false)) {
+    SRILOG(("SRICheck::IntegrityMetadata, sri is disabled (pref)"));
+    return NS_ERROR_SRI_DISABLED;
+  }
+
+  // put a reasonable bound on the length of the metadata
+  NS_ConvertUTF16toUTF8 metadataList(aMetadataList);
+  if (metadataList.Length() > SRICheck::MAX_METADATA_LENGTH) {
+    metadataList.Truncate(SRICheck::MAX_METADATA_LENGTH);
+  }
+  MOZ_ASSERT(metadataList.Length() <= aMetadataList.Length());
+
+  // the integrity attribute is a list of whitespace-separated hashes
+  // and options so we need to look at them one by one and pick the
+  // strongest (valid) one
+  nsCWhitespaceTokenizer tokenizer(metadataList);
+  nsAutoCString token;
+  for (uint32_t i=0; tokenizer.hasMoreTokens() &&
+         i < SRICheck::MAX_METADATA_TOKENS; ++i) {
+    token = tokenizer.nextToken();
+
+    SRIMetadata metadata(token);
+    if (metadata.IsMalformed()) {
+      NS_ConvertUTF8toUTF16 tokenUTF16(token);
+      const char16_t* params[] = { tokenUTF16.get() };
+      nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
+                                      NS_LITERAL_CSTRING("Sub-resource Integrity"),
+                                      aDocument,
+                                      nsContentUtils::eSECURITY_PROPERTIES,
+                                      "MalformedIntegrityURI",
+                                      params, ArrayLength(params));
+    } else if (!metadata.IsAlgorithmSupported()) {
+      nsAutoCString alg;
+      metadata.GetAlgorithm(&alg);
+      NS_ConvertUTF8toUTF16 algUTF16(alg);
+      const char16_t* params[] = { algUTF16.get() };
+      nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
+                                      NS_LITERAL_CSTRING("Sub-resource Integrity"),
+                                      aDocument,
+                                      nsContentUtils::eSECURITY_PROPERTIES,
+                                      "UnsupportedHashAlg",
+                                      params, ArrayLength(params));
+    }
+
+    nsAutoCString alg1, alg2;
+    if (MOZ_LOG_TEST(GetSriLog(), mozilla::LogLevel::Debug)) {
+      outMetadata->GetAlgorithm(&alg1);
+      metadata.GetAlgorithm(&alg2);
+    }
+    if (*outMetadata == metadata) {
+      SRILOG(("SRICheck::IntegrityMetadata, alg '%s' is the same as '%s'",
+              alg1.get(), alg2.get()));
+      *outMetadata += metadata; // add new hash to strongest metadata
+    } else if (*outMetadata < metadata) {
+      SRILOG(("SRICheck::IntegrityMetadata, alg '%s' is weaker than '%s'",
+              alg1.get(), alg2.get()));
+      *outMetadata = metadata; // replace strongest metadata with current
+    }
+  }
+
+  if (MOZ_LOG_TEST(GetSriLog(), mozilla::LogLevel::Debug)) {
+    if (outMetadata->IsValid()) {
+      nsAutoCString alg;
+      outMetadata->GetAlgorithm(&alg);
+      SRILOG(("SRICheck::IntegrityMetadata, using a '%s' hash", alg.get()));
+    } else if (outMetadata->IsEmpty()) {
+      SRILOG(("SRICheck::IntegrityMetadata, no metadata"));
+    } else {
+      SRILOG(("SRICheck::IntegrityMetadata, no valid metadata found"));
+    }
+  }
+  return NS_OK;
+}
+
+/* static */ nsresult
+SRICheck::VerifyIntegrity(const SRIMetadata& aMetadata,
+                          nsIURI* aRequestURI,
+                          const CORSMode aCORSMode,
+                          const nsAString& aString,
+                          const nsIDocument* aDocument)
+{
+  NS_ConvertUTF16toUTF8 utf8Hash(aString);
+  return VerifyIntegrity(aMetadata, aRequestURI, aCORSMode, utf8Hash.Length(),
+                         (uint8_t*)utf8Hash.get(), aDocument);
+}
+
+/* static */ nsresult
+SRICheck::VerifyIntegrity(const SRIMetadata& aMetadata,
+                          nsIURI* aRequestURI,
+                          const CORSMode aCORSMode,
+                          uint32_t aStringLen,
+                          const uint8_t* aString,
+                          const nsIDocument* aDocument)
+{
+  if (MOZ_LOG_TEST(GetSriLog(), mozilla::LogLevel::Debug)) {
+    nsAutoCString requestURL;
+    aRequestURI->GetAsciiSpec(requestURL);
+    // requestURL will be empty if GetAsciiSpec fails
+    SRILOG(("SRICheck::VerifyIntegrity, url=%s (length=%u)",
+            requestURL.get(), aStringLen));
+  }
+
+  MOZ_ASSERT(!aMetadata.IsEmpty()); // should be checked by caller
+
+  // IntegrityMetadata() checks this and returns "no metadata" if
+  // it's disabled so we should never make it this far
+  MOZ_ASSERT(Preferences::GetBool("security.sri.enable", false));
+
+  if (NS_FAILED(IsEligible(aRequestURI, aCORSMode, aDocument))) {
+    return NS_OK; // ignore non-CORS resources for forward-compatibility
+  }
+  if (!aMetadata.IsValid()) {
+    nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
+                                    NS_LITERAL_CSTRING("Sub-resource Integrity"),
+                                    aDocument,
+                                    nsContentUtils::eSECURITY_PROPERTIES,
+                                    "NoValidMetadata");
+    return NS_OK; // ignore invalid metadata for forward-compatibility
+  }
+
+  for (uint32_t i = 0; i < aMetadata.HashCount(); i++) {
+    if (NS_SUCCEEDED(VerifyHash(aMetadata, i, aStringLen,
+                                aString, aDocument))) {
+      return NS_OK; // stop at the first valid hash
+    }
+  }
+
+  nsAutoCString alg;
+  aMetadata.GetAlgorithm(&alg);
+  NS_ConvertUTF8toUTF16 algUTF16(alg);
+  const char16_t* params[] = { algUTF16.get() };
+  nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
+                                  NS_LITERAL_CSTRING("Sub-resource Integrity"),
+                                  aDocument,
+                                  nsContentUtils::eSECURITY_PROPERTIES,
+                                  "IntegrityMismatch",
+                                  params, ArrayLength(params));
+  return NS_ERROR_SRI_CORRUPT;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/security/SRICheck.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef mozilla_dom_SRICheck_h
+#define mozilla_dom_SRICheck_h
+
+#include "mozilla/CORSMode.h"
+#include "nsCOMPtr.h"
+#include "SRIMetadata.h"
+
+class nsIDocument;
+class nsIHttpChannel;
+class nsIScriptSecurityManager;
+class nsIStreamLoader;
+class nsIURI;
+
+namespace mozilla {
+namespace dom {
+
+class SRICheck final
+{
+public:
+  static const uint32_t MAX_METADATA_LENGTH = 24*1024;
+  static const uint32_t MAX_METADATA_TOKENS = 512;
+
+  /**
+   * Parse the multiple hashes specified in the integrity attribute and
+   * return the strongest supported hash.
+   */
+  static nsresult IntegrityMetadata(const nsAString& aMetadataList,
+                                    const nsIDocument* aDocument,
+                                    SRIMetadata* outMetadata);
+
+  /**
+   * Process the integrity attribute of the element.  A result of false
+   * must prevent the resource from loading.
+   */
+  static nsresult VerifyIntegrity(const SRIMetadata& aMetadata,
+                                  nsIURI* aRequestURI,
+                                  const CORSMode aCORSMode,
+                                  const nsAString& aString,
+                                  const nsIDocument* aDocument);
+
+  /**
+   * Process the integrity attribute of the element.  A result of false
+   * must prevent the resource from loading.
+   */
+  static nsresult VerifyIntegrity(const SRIMetadata& aMetadata,
+                                  nsIURI* aRequestURI,
+                                  const CORSMode aCORSMode,
+                                  uint32_t aStringLen,
+                                  const uint8_t* aString,
+                                  const nsIDocument* aDocument);
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_SRICheck_h
new file mode 100644
--- /dev/null
+++ b/dom/security/SRIMetadata.cpp
@@ -0,0 +1,172 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SRIMetadata.h"
+
+#include "hasht.h"
+#include "mozilla/dom/URLSearchParams.h"
+#include "mozilla/Logging.h"
+#include "nsICryptoHash.h"
+
+static PRLogModuleInfo*
+GetSriMetadataLog()
+{
+  static PRLogModuleInfo *gSriMetadataPRLog;
+  if (!gSriMetadataPRLog) {
+    gSriMetadataPRLog = PR_NewLogModule("SRIMetadata");
+  }
+  return gSriMetadataPRLog;
+}
+
+#define SRIMETADATALOG(args) MOZ_LOG(GetSriMetadataLog(), mozilla::LogLevel::Debug, args)
+#define SRIMETADATAERROR(args) MOZ_LOG(GetSriMetadataLog(), mozilla::LogLevel::Error, args)
+
+namespace mozilla {
+namespace dom {
+
+SRIMetadata::SRIMetadata(const nsACString& aToken)
+  : mAlgorithmType(SRIMetadata::UNKNOWN_ALGORITHM), mEmpty(false)
+{
+  MOZ_ASSERT(!aToken.IsEmpty()); // callers should check this first
+
+  SRIMETADATALOG(("SRIMetadata::SRIMetadata, aToken='%s'",
+                  PromiseFlatCString(aToken).get()));
+
+  int32_t hyphen = aToken.FindChar('-');
+  if (hyphen == -1) {
+    SRIMETADATAERROR(("SRIMetadata::SRIMetadata, invalid (no hyphen)"));
+    return; // invalid metadata
+  }
+
+  // split the token into its components
+  mAlgorithm = Substring(aToken, 0, hyphen);
+  uint32_t hashStart = hyphen + 1;
+  if (hashStart >= aToken.Length()) {
+    SRIMETADATAERROR(("SRIMetadata::SRIMetadata, invalid (missing digest)"));
+    return; // invalid metadata
+  }
+  int32_t question = aToken.FindChar('?');
+  if (question == -1) {
+    mHashes.AppendElement(Substring(aToken, hashStart,
+                                    aToken.Length() - hashStart));
+  } else {
+    MOZ_ASSERT(question > 0);
+    if (static_cast<uint32_t>(question) <= hashStart) {
+      SRIMETADATAERROR(("SRIMetadata::SRIMetadata, invalid (options w/o digest)"));
+      return; // invalid metadata
+    }
+    mHashes.AppendElement(Substring(aToken, hashStart,
+                                    question - hashStart));
+  }
+
+  if (mAlgorithm.EqualsLiteral("sha256")) {
+    mAlgorithmType = nsICryptoHash::SHA256;
+  } else if (mAlgorithm.EqualsLiteral("sha384")) {
+    mAlgorithmType = nsICryptoHash::SHA384;
+  } else if (mAlgorithm.EqualsLiteral("sha512")) {
+    mAlgorithmType = nsICryptoHash::SHA512;
+  }
+
+  SRIMETADATALOG(("SRIMetadata::SRIMetadata, hash='%s'; alg='%s'",
+                  mHashes[0].get(), mAlgorithm.get()));
+}
+
+bool
+SRIMetadata::operator<(const SRIMetadata& aOther) const
+{
+  static_assert(nsICryptoHash::SHA256 < nsICryptoHash::SHA384,
+                "We rely on the order indicating relative alg strength");
+  static_assert(nsICryptoHash::SHA384 < nsICryptoHash::SHA512,
+                "We rely on the order indicating relative alg strength");
+  MOZ_ASSERT(mAlgorithmType == SRIMetadata::UNKNOWN_ALGORITHM ||
+             mAlgorithmType == nsICryptoHash::SHA256 ||
+             mAlgorithmType == nsICryptoHash::SHA384 ||
+             mAlgorithmType == nsICryptoHash::SHA512);
+  MOZ_ASSERT(aOther.mAlgorithmType == SRIMetadata::UNKNOWN_ALGORITHM ||
+             aOther.mAlgorithmType == nsICryptoHash::SHA256 ||
+             aOther.mAlgorithmType == nsICryptoHash::SHA384 ||
+             aOther.mAlgorithmType == nsICryptoHash::SHA512);
+
+  if (mEmpty) {
+    SRIMETADATALOG(("SRIMetadata::operator<, first metadata is empty"));
+    return true; // anything beats the empty metadata (incl. invalid ones)
+  }
+
+  SRIMETADATALOG(("SRIMetadata::operator<, alg1='%d'; alg2='%d'",
+                  mAlgorithmType, aOther.mAlgorithmType));
+  return (mAlgorithmType < aOther.mAlgorithmType);
+}
+
+bool
+SRIMetadata::operator>(const SRIMetadata& aOther) const
+{
+  MOZ_ASSERT(false);
+  return false;
+}
+
+SRIMetadata&
+SRIMetadata::operator+=(const SRIMetadata& aOther)
+{
+  MOZ_ASSERT(!aOther.IsEmpty() && !IsEmpty());
+  MOZ_ASSERT(aOther.IsValid() && IsValid());
+  MOZ_ASSERT(mAlgorithmType == aOther.mAlgorithmType);
+
+  // We only pull in the first element of the other metadata
+  MOZ_ASSERT(aOther.mHashes.Length() == 1);
+  if (mHashes.Length() < SRIMetadata::MAX_ALTERNATE_HASHES) {
+    SRIMETADATALOG(("SRIMetadata::operator+=, appending another '%s' hash (new length=%d)",
+                    mAlgorithm.get(), mHashes.Length()));
+    mHashes.AppendElement(aOther.mHashes[0]);
+  }
+
+  MOZ_ASSERT(mHashes.Length() > 1);
+  MOZ_ASSERT(mHashes.Length() <= SRIMetadata::MAX_ALTERNATE_HASHES);
+  return *this;
+}
+
+bool
+SRIMetadata::operator==(const SRIMetadata& aOther) const
+{
+  if (IsEmpty() || !IsValid()) {
+    return false;
+  }
+  return mAlgorithmType == aOther.mAlgorithmType;
+}
+
+void
+SRIMetadata::GetHash(uint32_t aIndex, nsCString* outHash) const
+{
+  MOZ_ASSERT(aIndex < SRIMetadata::MAX_ALTERNATE_HASHES);
+  if (NS_WARN_IF(aIndex >= mHashes.Length())) {
+    *outHash = nullptr;
+    return;
+  }
+  *outHash = mHashes[aIndex];
+}
+
+void
+SRIMetadata::GetHashType(int8_t* outType, uint32_t* outLength) const
+{
+  // these constants are defined in security/nss/lib/util/hasht.h and
+  // netwerk/base/public/nsICryptoHash.idl
+  switch (mAlgorithmType) {
+    case nsICryptoHash::SHA256:
+      *outLength = SHA256_LENGTH;
+      break;
+    case nsICryptoHash::SHA384:
+      *outLength = SHA384_LENGTH;
+      break;
+    case nsICryptoHash::SHA512:
+      *outLength = SHA512_LENGTH;
+      break;
+    default:
+      *outLength = 0;
+  }
+  *outType = mAlgorithmType;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/security/SRIMetadata.h
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef mozilla_dom_SRIMetadata_h
+#define mozilla_dom_SRIMetadata_h
+
+#include "nsTArray.h"
+#include "nsString.h"
+
+namespace mozilla {
+namespace dom {
+
+class SRIMetadata final
+{
+public:
+  static const uint32_t MAX_ALTERNATE_HASHES = 256;
+  static const int8_t UNKNOWN_ALGORITHM = -1;
+
+  /**
+   * Create an empty metadata object.
+   */
+  SRIMetadata() : mAlgorithmType(UNKNOWN_ALGORITHM), mEmpty(true) {}
+
+  /**
+   * Split a string token into the components of an SRI metadata
+   * attribute.
+   */
+  explicit SRIMetadata(const nsACString& aToken);
+
+  /**
+   * Returns true when this object's hash algorithm is weaker than the
+   * other object's hash algorithm.
+   */
+  bool operator<(const SRIMetadata& aOther) const;
+
+  /**
+   * Not implemented. Should not be used.
+   */
+  bool operator>(const SRIMetadata& aOther) const;
+
+  /**
+   * Add another metadata's hash to this one.
+   */
+  SRIMetadata& operator+=(const SRIMetadata& aOther);
+
+  /**
+   * Returns true when the two metadata use the same hash algorithm.
+   */
+  bool operator==(const SRIMetadata& aOther) const;
+
+  bool IsEmpty() const { return mEmpty; }
+  bool IsMalformed() const { return mHashes.IsEmpty() || mAlgorithm.IsEmpty(); }
+  bool IsAlgorithmSupported() const { return mAlgorithmType != UNKNOWN_ALGORITHM; }
+  bool IsValid() const { return !IsMalformed() && IsAlgorithmSupported(); }
+
+  uint32_t HashCount() const { return mHashes.Length(); }
+  void GetHash(uint32_t aIndex, nsCString* outHash) const;
+  void GetAlgorithm(nsCString* outAlg) const { *outAlg = mAlgorithm; }
+  void GetHashType(int8_t* outType, uint32_t* outLength) const;
+
+private:
+  nsTArray<nsCString> mHashes;
+  nsCString mAlgorithm;
+  int8_t mAlgorithmType;
+  bool mEmpty;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_SRIMetadata_h
--- a/dom/security/moz.build
+++ b/dom/security/moz.build
@@ -7,31 +7,35 @@
 TEST_DIRS += ['test']
 
 EXPORTS.mozilla.dom += [
     'nsContentSecurityManager.h',
     'nsCSPContext.h',
     'nsCSPService.h',
     'nsCSPUtils.h',
     'nsMixedContentBlocker.h',
+    'SRICheck.h',
+    'SRIMetadata.h',
 ]
 
 EXPORTS += [
     'nsContentSecurityManager.h',
     'nsCORSListenerProxy.h'
 ]
 
 UNIFIED_SOURCES += [
     'nsContentSecurityManager.cpp',
     'nsCORSListenerProxy.cpp',
     'nsCSPContext.cpp',
     'nsCSPParser.cpp',
     'nsCSPService.cpp',
     'nsCSPUtils.cpp',
     'nsMixedContentBlocker.cpp',
+    'SRICheck.cpp',
+    'SRIMetadata.cpp',
 ]
 
 FAIL_ON_WARNINGS = True
 
 FINAL_LIBRARY = 'xul'
 LOCAL_INCLUDES += [
     '/caps',
     '/netwerk/base',
--- a/dom/webidl/HTMLLinkElement.webidl
+++ b/dom/webidl/HTMLLinkElement.webidl
@@ -43,8 +43,13 @@ partial interface HTMLLinkElement {
 };
 
 // http://w3c.github.io/webcomponents/spec/imports/#interface-import
 partial interface HTMLLinkElement {
     [Func="nsDocument::IsWebComponentsEnabled"]
     readonly attribute Document? import;
 };
 
+// https://w3c.github.io/webappsec/specs/subresourceintegrity/#htmllinkelement-1
+partial interface HTMLLinkElement {
+  [SetterThrows]
+  attribute DOMString integrity;
+};
--- a/dom/webidl/HTMLScriptElement.webidl
+++ b/dom/webidl/HTMLScriptElement.webidl
@@ -28,8 +28,13 @@ interface HTMLScriptElement : HTMLElemen
 // http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis
 partial interface HTMLScriptElement {
   [SetterThrows]
   attribute DOMString event;
   [SetterThrows]
   attribute DOMString htmlFor;
 };
 
+// https://w3c.github.io/webappsec/specs/subresourceintegrity/#htmlscriptelement-1
+partial interface HTMLScriptElement {
+  [SetterThrows]
+  attribute DOMString integrity;
+};
--- a/layout/style/CSSStyleSheet.cpp
+++ b/layout/style/CSSStyleSheet.cpp
@@ -810,20 +810,22 @@ namespace mozilla {
 
 // -------------------------------
 // CSS Style Sheet Inner Data Container
 //
 
 
 CSSStyleSheetInner::CSSStyleSheetInner(CSSStyleSheet* aPrimarySheet,
                                        CORSMode aCORSMode,
-                                       ReferrerPolicy aReferrerPolicy)
+                                       ReferrerPolicy aReferrerPolicy,
+                                       const SRIMetadata& aIntegrity)
   : mSheets()
   , mCORSMode(aCORSMode)
   , mReferrerPolicy (aReferrerPolicy)
+  , mIntegrity(aIntegrity)
   , mComplete(false)
 #ifdef DEBUG
   , mPrincipalSet(false)
 #endif
 {
   MOZ_COUNT_CTOR(CSSStyleSheetInner);
   mSheets.AppendElement(aPrimarySheet);
 
@@ -935,16 +937,17 @@ CSSStyleSheetInner::CSSStyleSheetInner(C
                                        CSSStyleSheet* aPrimarySheet)
   : mSheets(),
     mSheetURI(aCopy.mSheetURI),
     mOriginalSheetURI(aCopy.mOriginalSheetURI),
     mBaseURI(aCopy.mBaseURI),
     mPrincipal(aCopy.mPrincipal),
     mCORSMode(aCopy.mCORSMode),
     mReferrerPolicy(aCopy.mReferrerPolicy),
+    mIntegrity(aCopy.mIntegrity),
     mComplete(aCopy.mComplete)
 #ifdef DEBUG
     , mPrincipalSet(aCopy.mPrincipalSet)
 #endif
 {
   MOZ_COUNT_CTOR(CSSStyleSheetInner);
   AddSheet(aPrimarySheet);
   aCopy.mOrderedRules.EnumerateForwards(css::GroupRule::CloneRuleInto, &mOrderedRules);
@@ -1075,17 +1078,36 @@ CSSStyleSheet::CSSStyleSheet(CORSMode aC
     mDocument(nullptr),
     mOwningNode(nullptr),
     mDisabled(false),
     mDirty(false),
     mInRuleProcessorCache(false),
     mScopeElement(nullptr),
     mRuleProcessors(nullptr)
 {
-  mInner = new CSSStyleSheetInner(this, aCORSMode, aReferrerPolicy);
+  mInner = new CSSStyleSheetInner(this, aCORSMode, aReferrerPolicy,
+                                  SRIMetadata());
+}
+
+CSSStyleSheet::CSSStyleSheet(CORSMode aCORSMode,
+                             ReferrerPolicy aReferrerPolicy,
+                             const SRIMetadata& aIntegrity)
+  : mTitle(),
+    mParent(nullptr),
+    mOwnerRule(nullptr),
+    mDocument(nullptr),
+    mOwningNode(nullptr),
+    mDisabled(false),
+    mDirty(false),
+    mInRuleProcessorCache(false),
+    mScopeElement(nullptr),
+    mRuleProcessors(nullptr)
+{
+  mInner = new CSSStyleSheetInner(this, aCORSMode, aReferrerPolicy,
+                                  aIntegrity);
 }
 
 CSSStyleSheet::CSSStyleSheet(const CSSStyleSheet& aCopy,
                              CSSStyleSheet* aParentToUse,
                              css::ImportRule* aOwnerRuleToUse,
                              nsIDocument* aDocumentToUse,
                              nsINode* aOwningNodeToUse)
   : mTitle(aCopy.mTitle),
--- a/layout/style/CSSStyleSheet.h
+++ b/layout/style/CSSStyleSheet.h
@@ -21,16 +21,17 @@
 #include "nsIDOMCSSStyleSheet.h"
 #include "nsICSSLoaderObserver.h"
 #include "nsTArrayForwardDeclare.h"
 #include "nsString.h"
 #include "mozilla/CORSMode.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsWrapperCache.h"
 #include "mozilla/net/ReferrerPolicy.h"
+#include "mozilla/dom/SRIMetadata.h"
 
 class CSSRuleListImpl;
 class nsCSSRuleProcessor;
 class nsIPrincipal;
 class nsIURI;
 class nsMediaList;
 class nsMediaQueryResultCacheKey;
 class nsPresContext;
@@ -58,17 +59,18 @@ class CSSStyleSheetInner
 public:
   friend class mozilla::CSSStyleSheet;
   friend class ::nsCSSRuleProcessor;
   typedef net::ReferrerPolicy ReferrerPolicy;
 
 private:
   CSSStyleSheetInner(CSSStyleSheet* aPrimarySheet,
                      CORSMode aCORSMode,
-                     ReferrerPolicy aReferrerPolicy);
+                     ReferrerPolicy aReferrerPolicy,
+                     const dom::SRIMetadata& aIntegrity);
   CSSStyleSheetInner(CSSStyleSheetInner& aCopy,
                      CSSStyleSheet* aPrimarySheet);
   ~CSSStyleSheetInner();
 
   CSSStyleSheetInner* CloneFor(CSSStyleSheet* aPrimarySheet);
   void AddSheet(CSSStyleSheet* aSheet);
   void RemoveSheet(CSSStyleSheet* aSheet);
 
@@ -91,16 +93,17 @@ private:
   // currently this is the case) that any time page JS can get ts hands on a
   // child sheet that means we've already ensured unique inners throughout its
   // parent chain and things are good.
   nsRefPtr<CSSStyleSheet> mFirstChild;
   CORSMode               mCORSMode;
   // The Referrer Policy of a stylesheet is used for its child sheets, so it is
   // stored here.
   ReferrerPolicy         mReferrerPolicy;
+  dom::SRIMetadata       mIntegrity;
   bool                   mComplete;
 
 #ifdef DEBUG
   bool                   mPrincipalSet;
 #endif
 };
 
 
@@ -118,16 +121,18 @@ private:
 class CSSStyleSheet final : public nsIStyleSheet,
                             public nsIDOMCSSStyleSheet,
                             public nsICSSLoaderObserver,
                             public nsWrapperCache
 {
 public:
   typedef net::ReferrerPolicy ReferrerPolicy;
   CSSStyleSheet(CORSMode aCORSMode, ReferrerPolicy aReferrerPolicy);
+  CSSStyleSheet(CORSMode aCORSMode, ReferrerPolicy aReferrerPolicy,
+                const dom::SRIMetadata& aIntegrity);
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(CSSStyleSheet,
                                                          nsIStyleSheet)
 
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_CSS_STYLE_SHEET_IMPL_CID)
 
   // nsIStyleSheet interface
@@ -254,16 +259,19 @@ public:
   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override;
 
   // Get this style sheet's CORS mode
   CORSMode GetCORSMode() const { return mInner->mCORSMode; }
 
   // Get this style sheet's Referrer Policy
   ReferrerPolicy GetReferrerPolicy() const { return mInner->mReferrerPolicy; }
 
+  // Get this style sheet's integrity metadata
+  dom::SRIMetadata GetIntegrity() const { return mInner->mIntegrity; }
+
   dom::Element* GetScopeElement() const { return mScopeElement; }
   void SetScopeElement(dom::Element* aScopeElement)
   {
     mScopeElement = aScopeElement;
   }
 
   // WebIDL StyleSheet API
   // Our nsIStyleSheet::GetType is a const method, so it ends up
--- a/layout/style/Loader.cpp
+++ b/layout/style/Loader.cpp
@@ -57,16 +57,17 @@
 #include "nsXULPrototypeCache.h"
 #endif
 
 #include "nsIMediaList.h"
 #include "nsIDOMStyleSheet.h"
 #include "nsError.h"
 
 #include "nsIContentSecurityPolicy.h"
+#include "mozilla/dom/SRICheck.h"
 
 #include "mozilla/dom/EncodingUtils.h"
 using mozilla::dom::EncodingUtils;
 
 using namespace mozilla::dom;
 
 /**
  * OVERALL ARCHITECTURE
@@ -260,16 +261,26 @@ static PRLogModuleInfo *
 GetLoaderLog()
 {
   static PRLogModuleInfo *sLog;
   if (!sLog)
     sLog = PR_NewLogModule("nsCSSLoader");
   return sLog;
 }
 
+static PRLogModuleInfo*
+GetSriLog()
+{
+  static PRLogModuleInfo *gSriPRLog;
+  if (!gSriPRLog) {
+    gSriPRLog = PR_NewLogModule("SRI");
+  }
+  return gSriPRLog;
+}
+
 #define LOG_ERROR(args) MOZ_LOG(GetLoaderLog(), mozilla::LogLevel::Error, args)
 #define LOG_WARN(args) MOZ_LOG(GetLoaderLog(), mozilla::LogLevel::Warning, args)
 #define LOG_DEBUG(args) MOZ_LOG(GetLoaderLog(), mozilla::LogLevel::Debug, args)
 #define LOG(args) LOG_DEBUG(args)
 
 #define LOG_ERROR_ENABLED() MOZ_LOG_TEST(GetLoaderLog(), mozilla::LogLevel::Error)
 #define LOG_WARN_ENABLED() MOZ_LOG_TEST(GetLoaderLog(), mozilla::LogLevel::Warning)
 #define LOG_DEBUG_ENABLED() MOZ_LOG_TEST(GetLoaderLog(), mozilla::LogLevel::Debug)
@@ -923,16 +934,28 @@ SheetLoadData::OnStreamComplete(nsIUnich
     if (errorFlag == nsIScriptError::errorFlag) {
       LOG_WARN(("  Ignoring sheet with improper MIME type %s",
                 contentType.get()));
       mLoader->SheetComplete(this, NS_ERROR_NOT_AVAILABLE);
       return NS_OK;
     }
   }
 
+  SRIMetadata sriMetadata = mSheet->GetIntegrity();
+  if (!sriMetadata.IsEmpty() &&
+      NS_FAILED(SRICheck::VerifyIntegrity(sriMetadata, channelURI,
+                                          mSheet->GetCORSMode(), aBuffer,
+                                          mLoader->mDocument))) {
+    LOG(("  Load was blocked by SRI"));
+    MOZ_LOG(GetSriLog(), mozilla::LogLevel::Debug,
+            ("css::Loader::OnStreamComplete, bad metadata"));
+    mLoader->SheetComplete(this, NS_ERROR_SRI_CORRUPT);
+    return NS_OK;
+  }
+
   // Enough to set the URIs on mSheet, since any sibling datas we have share
   // the same mInner as mSheet and will thus get the same URI.
   mSheet->SetURIs(channelURI, originalURI, channelURI);
 
   bool completed;
   result = mLoader->ParseSheet(aBuffer, this, completed);
   NS_ASSERTION(completed || !mSyncLoad, "sync load did not complete");
   return result;
@@ -1052,16 +1075,17 @@ Loader::CheckLoadAllowed(nsIPrincipal* a
  * CreateSheet().
  */
 nsresult
 Loader::CreateSheet(nsIURI* aURI,
                     nsIContent* aLinkingContent,
                     nsIPrincipal* aLoaderPrincipal,
                     CORSMode aCORSMode,
                     ReferrerPolicy aReferrerPolicy,
+                    const nsAString& aIntegrity,
                     bool aSyncLoad,
                     bool aHasAlternateRel,
                     const nsAString& aTitle,
                     StyleSheetState& aSheetState,
                     bool *aIsAlternate,
                     CSSStyleSheet** aSheet)
 {
   LOG(("css::Loader::CreateSheet"));
@@ -1199,17 +1223,27 @@ Loader::CreateSheet(nsIURI* aURI,
       sheetURI = aLinkingContent->OwnerDoc()->GetDocumentURI();
       originalURI = nullptr;
     } else {
       baseURI = aURI;
       sheetURI = aURI;
       originalURI = aURI;
     }
 
-    nsRefPtr<CSSStyleSheet> sheet = new CSSStyleSheet(aCORSMode, aReferrerPolicy);
+    SRIMetadata sriMetadata;
+    if (!aIntegrity.IsEmpty()) {
+      MOZ_LOG(GetSriLog(), mozilla::LogLevel::Debug,
+              ("css::Loader::CreateSheet, integrity=%s",
+               NS_ConvertUTF16toUTF8(aIntegrity).get()));
+      SRICheck::IntegrityMetadata(aIntegrity, mDocument, &sriMetadata);
+    }
+
+    nsRefPtr<CSSStyleSheet> sheet = new CSSStyleSheet(aCORSMode,
+                                                      aReferrerPolicy,
+                                                      sriMetadata);
     sheet->SetURIs(sheetURI, originalURI, baseURI);
     sheet.forget(aSheet);
   }
 
   NS_ASSERTION(*aSheet, "We should have a sheet by now!");
   NS_ASSERTION(aSheetState != eSheetStateUnknown, "Have to set a state!");
   LOG(("  State: %s", gStateStrings[aSheetState]));
 
@@ -1410,16 +1444,18 @@ Loader::LoadSheet(SheetLoadData* aLoadDa
        (nsContentUtils::URIIsLocalFile(aLoadData->mURI) &&
         NS_SUCCEEDED(aLoadData->mLoaderPrincipal->
                      CheckMayLoad(aLoadData->mURI, false, false))));
   }
   else {
     triggeringPrincipal = nsContentUtils::GetSystemPrincipal();
   }
 
+  SRIMetadata sriMetadata = aLoadData->mSheet->GetIntegrity();
+
   if (aLoadData->mSyncLoad) {
     LOG(("  Synchronous load"));
     NS_ASSERTION(!aLoadData->mObserver, "Observer for a sync load?");
     NS_ASSERTION(aSheetState == eSheetNeedsParser,
                  "Sync loads can't reuse existing async loads");
 
     // Create a nsIUnicharStreamLoader instance to which we will feed
     // the data from the sync load.  Do this before creating the
@@ -1594,17 +1630,17 @@ Loader::LoadSheet(SheetLoadData* aLoadDa
     nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(channel));
     if (cos) {
       cos->AddClassFlags(nsIClassOfService::Leader);
     }
   }
 
   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
   if (httpChannel) {
-    // send a minimal Accept header for text/css
+    // Send a minimal Accept header for text/css
     httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
                                   NS_LITERAL_CSTRING("text/css,*/*;q=0.1"),
                                   false);
     nsCOMPtr<nsIURI> referrerURI = aLoadData->GetReferrerURI();
     if (referrerURI)
       httpChannel->SetReferrerWithPolicy(referrerURI,
                                          aLoadData->mSheet->GetReferrerPolicy());
 
@@ -1927,18 +1963,20 @@ Loader::LoadInlineStyle(nsIContent* aEle
   NS_ASSERTION(owningElement, "Element is not a style linking element!");
 
   // Since we're not planning to load a URI, no need to hand a principal to the
   // load data or to CreateSheet().  Also, OK to use CORS_NONE for the CORS
   // mode and mDocument's ReferrerPolicy.
   StyleSheetState state;
   nsRefPtr<CSSStyleSheet> sheet;
   nsresult rv = CreateSheet(nullptr, aElement, nullptr, CORS_NONE,
-                            mDocument->GetReferrerPolicy(), false, false,
-                            aTitle, state, aIsAlternate, getter_AddRefs(sheet));
+                            mDocument->GetReferrerPolicy(),
+                            EmptyString(), // no inline integrity checks
+                            false, false, aTitle, state, aIsAlternate,
+                            getter_AddRefs(sheet));
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ASSERTION(state == eSheetNeedsParser,
                "Inline sheets should not be cached");
 
   LOG(("  Sheet is alternate: %d", *aIsAlternate));
 
   PrepareSheet(sheet, aTitle, aMedia, nullptr, aScopeElement, *aIsAlternate);
 
@@ -1974,16 +2012,17 @@ Loader::LoadInlineStyle(nsIContent* aEle
 nsresult
 Loader::LoadStyleLink(nsIContent* aElement,
                       nsIURI* aURL,
                       const nsAString& aTitle,
                       const nsAString& aMedia,
                       bool aHasAlternateRel,
                       CORSMode aCORSMode,
                       ReferrerPolicy aReferrerPolicy,
+                      const nsAString& aIntegrity,
                       nsICSSLoaderObserver* aObserver,
                       bool* aIsAlternate)
 {
   LOG(("css::Loader::LoadStyleLink"));
   NS_PRECONDITION(aURL, "Must have URL to load");
   NS_ASSERTION(mParsingDatas.Length() == 0, "We're in the middle of a parse?");
 
   LOG_URI("  Link uri: '%s'", aURL);
@@ -2008,17 +2047,17 @@ Loader::LoadStyleLink(nsIContent* aEleme
   nsresult rv = CheckLoadAllowed(principal, aURL, context);
   if (NS_FAILED(rv)) return rv;
 
   LOG(("  Passed load check"));
 
   StyleSheetState state;
   nsRefPtr<CSSStyleSheet> sheet;
   rv = CreateSheet(aURL, aElement, principal, aCORSMode,
-                   aReferrerPolicy, false,
+                   aReferrerPolicy, aIntegrity, false,
                    aHasAlternateRel, aTitle, state, aIsAlternate,
                    getter_AddRefs(sheet));
   NS_ENSURE_SUCCESS(rv, rv);
 
   LOG(("  Sheet is alternate: %d", *aIsAlternate));
 
   PrepareSheet(sheet, aTitle, aMedia, nullptr, nullptr, *aIsAlternate);
 
@@ -2172,16 +2211,17 @@ Loader::LoadChildSheet(CSSStyleSheet* aP
   // loop) do so.
   nsRefPtr<CSSStyleSheet> sheet;
   bool isAlternate;
   StyleSheetState state;
   const nsSubstring& empty = EmptyString();
   // For now, use CORS_NONE for child sheets
   rv = CreateSheet(aURL, nullptr, principal, CORS_NONE,
                    aParentSheet->GetReferrerPolicy(),
+                   EmptyString(), // integrity is only checked on main sheet
                    parentData ? parentData->mSyncLoad : false,
                    false, empty, state, &isAlternate, getter_AddRefs(sheet));
   NS_ENSURE_SUCCESS(rv, rv);
 
   PrepareSheet(sheet, empty, empty, aMedia, nullptr, isAlternate);
 
   rv = InsertChildSheet(sheet, aParentSheet, aParentRule);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -2238,35 +2278,37 @@ Loader::LoadSheet(nsIURI* aURL,
 }
 
 nsresult
 Loader::LoadSheet(nsIURI* aURL,
                   nsIPrincipal* aOriginPrincipal,
                   const nsCString& aCharset,
                   nsICSSLoaderObserver* aObserver,
                   CORSMode aCORSMode,
-                  ReferrerPolicy aReferrerPolicy)
+                  ReferrerPolicy aReferrerPolicy,
+                  const nsAString& aIntegrity)
 {
   LOG(("css::Loader::LoadSheet(aURL, aObserver) api call"));
   return InternalLoadNonDocumentSheet(aURL, false, false,
                                       aOriginPrincipal, aCharset,
                                       nullptr, aObserver, aCORSMode,
-                                      aReferrerPolicy);
+                                      aReferrerPolicy, aIntegrity);
 }
 
 nsresult
 Loader::InternalLoadNonDocumentSheet(nsIURI* aURL,
                                      bool aAllowUnsafeRules,
                                      bool aUseSystemPrincipal,
                                      nsIPrincipal* aOriginPrincipal,
                                      const nsCString& aCharset,
                                      CSSStyleSheet** aSheet,
                                      nsICSSLoaderObserver* aObserver,
                                      CORSMode aCORSMode,
-                                     ReferrerPolicy aReferrerPolicy)
+                                     ReferrerPolicy aReferrerPolicy,
+                                     const nsAString& aIntegrity)
 {
   NS_PRECONDITION(aURL, "Must have a URI to load");
   NS_PRECONDITION(aSheet || aObserver, "Sheet and observer can't both be null");
   NS_PRECONDITION(!aUseSystemPrincipal || !aObserver,
                   "Shouldn't load system-principal sheets async");
   NS_ASSERTION(mParsingDatas.Length() == 0, "We're in the middle of a parse?");
 
   LOG_URI("  Non-document sheet uri: '%s'", aURL);
@@ -2287,17 +2329,17 @@ Loader::InternalLoadNonDocumentSheet(nsI
 
   StyleSheetState state;
   bool isAlternate;
   nsRefPtr<CSSStyleSheet> sheet;
   bool syncLoad = (aObserver == nullptr);
   const nsSubstring& empty = EmptyString();
 
   rv = CreateSheet(aURL, nullptr, aOriginPrincipal, aCORSMode,
-                   aReferrerPolicy, syncLoad, false,
+                   aReferrerPolicy, aIntegrity, syncLoad, false,
                    empty, state, &isAlternate, getter_AddRefs(sheet));
   NS_ENSURE_SUCCESS(rv, rv);
 
   PrepareSheet(sheet, empty, empty, nullptr, nullptr, isAlternate);
 
   if (state == eSheetComplete) {
     LOG(("  Sheet already complete"));
     if (aObserver || !mObservers.IsEmpty()) {
--- a/layout/style/Loader.h
+++ b/layout/style/Loader.h
@@ -218,16 +218,17 @@ public:
    */
   nsresult LoadStyleLink(nsIContent* aElement,
                          nsIURI* aURL,
                          const nsAString& aTitle,
                          const nsAString& aMedia,
                          bool aHasAlternateRel,
                          CORSMode aCORSMode,
                          ReferrerPolicy aReferrerPolicy,
+                         const nsAString& aIntegrity,
                          nsICSSLoaderObserver* aObserver,
                          bool* aIsAlternate);
 
   /**
    * Load a child (@import-ed) style sheet.  In addition to loading the sheet,
    * this method will insert it into the child sheet list of aParentSheet.  If
    * there is no sheet currently being parsed and the child sheet is not
    * complete when this method returns, then when the child sheet becomes
@@ -315,17 +316,18 @@ public:
    * Same as above, to be used when the caller doesn't care about the
    * not-yet-loaded sheet.
    */
   nsresult LoadSheet(nsIURI* aURL,
                      nsIPrincipal* aOriginPrincipal,
                      const nsCString& aCharset,
                      nsICSSLoaderObserver* aObserver,
                      CORSMode aCORSMode = CORS_NONE,
-                     ReferrerPolicy aReferrerPolicy = mozilla::net::RP_Default);
+                     ReferrerPolicy aReferrerPolicy = mozilla::net::RP_Default,
+                     const nsAString& aIntegrity = EmptyString());
 
   /**
    * Stop loading all sheets.  All nsICSSLoaderObservers involved will be
    * notified with NS_BINDING_ABORTED as the status, possibly synchronously.
    */
   nsresult Stop(void);
 
   /**
@@ -412,16 +414,17 @@ private:
   // must be non-null then.  The loader principal must never be null
   // if aURI is not null.
   // *aIsAlternate is set based on aTitle and aHasAlternateRel.
   nsresult CreateSheet(nsIURI* aURI,
                        nsIContent* aLinkingContent,
                        nsIPrincipal* aLoaderPrincipal,
                        CORSMode aCORSMode,
                        ReferrerPolicy aReferrerPolicy,
+                       const nsAString& aIntegrity,
                        bool aSyncLoad,
                        bool aHasAlternateRel,
                        const nsAString& aTitle,
                        StyleSheetState& aSheetState,
                        bool *aIsAlternate,
                        CSSStyleSheet** aSheet);
 
   // Pass in either a media string or the nsMediaList from the
@@ -445,17 +448,18 @@ private:
   nsresult InternalLoadNonDocumentSheet(nsIURI* aURL,
                                         bool aAllowUnsafeRules,
                                         bool aUseSystemPrincipal,
                                         nsIPrincipal* aOriginPrincipal,
                                         const nsCString& aCharset,
                                         CSSStyleSheet** aSheet,
                                         nsICSSLoaderObserver* aObserver,
                                         CORSMode aCORSMode = CORS_NONE,
-                                        ReferrerPolicy aReferrerPolicy = mozilla::net::RP_Default);
+                                        ReferrerPolicy aReferrerPolicy = mozilla::net::RP_Default,
+                                        const nsAString& aIntegrity = EmptyString());
 
   // Post a load event for aObserver to be notified about aSheet.  The
   // notification will be sent with status NS_OK unless the load event is
   // canceled at some point (in which case it will be sent with
   // NS_BINDING_ABORTED).  aWasAlternate indicates the state when the load was
   // initiated, not the state at some later time.  aURI should be the URI the
   // sheet was loaded from (may be null for inline sheets).  aElement is the
   // owning element for this sheet.
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1953,16 +1953,19 @@ pref("security.csp.experimentalEnabled",
 
 // Default Content Security Policy to apply to privileged apps.
 pref("security.apps.privileged.CSP.default", "default-src * data: blob:; script-src 'self'; object-src 'none'; style-src 'self' 'unsafe-inline'");
 
 // Mixed content blocking
 pref("security.mixed_content.block_active_content", false);
 pref("security.mixed_content.block_display_content", false);
 
+// Sub-resource integrity
+pref("security.sri.enable", false);
+
 // Disable pinning checks by default.
 pref("security.cert_pinning.enforcement_level", 0);
 // Do not process hpkp headers rooted by not built in roots by default.
 // This is to prevent accidental pinning from MITM devices and is used
 // for tests.
 pref("security.cert_pinning.process_headers_from_non_builtin_roots", false);
 
 // Modifier key prefs: default to Windows settings,
--- a/parser/html/nsHtml5SpeculativeLoad.cpp
+++ b/parser/html/nsHtml5SpeculativeLoad.cpp
@@ -40,24 +40,24 @@ nsHtml5SpeculativeLoad::Perform(nsHtml5T
       aExecutor->PreloadEndPicture();
       break;
     case eSpeculativeLoadPictureSource:
       aExecutor->PreloadPictureSource(mSrcset, mSizes, mTypeOrCharsetSource,
                                       mMedia);
       break;
     case eSpeculativeLoadScript:
       aExecutor->PreloadScript(mUrl, mCharset, mTypeOrCharsetSource,
-                               mCrossOrigin, false);
+                               mCrossOrigin, mIntegrity, false);
       break;
     case eSpeculativeLoadScriptFromHead:
       aExecutor->PreloadScript(mUrl, mCharset, mTypeOrCharsetSource,
-                               mCrossOrigin, true);
+                               mCrossOrigin, mIntegrity, true);
       break;
     case eSpeculativeLoadStyle:
-      aExecutor->PreloadStyle(mUrl, mCharset, mCrossOrigin);
+      aExecutor->PreloadStyle(mUrl, mCharset, mCrossOrigin, mIntegrity);
       break;
     case eSpeculativeLoadManifest:  
       aExecutor->ProcessOfflineManifest(mUrl);
       break;
     case eSpeculativeLoadSetDocumentCharset: {
         nsAutoCString narrowName;
         CopyUTF16toUTF8(mCharset, narrowName);
         NS_ASSERTION(mTypeOrCharsetSource.Length() == 1,
--- a/parser/html/nsHtml5SpeculativeLoad.h
+++ b/parser/html/nsHtml5SpeculativeLoad.h
@@ -100,37 +100,41 @@ class nsHtml5SpeculativeLoad {
       mTypeOrCharsetSource.Assign(aType);
       mMedia.Assign(aMedia);
     }
 
     inline void InitScript(const nsAString& aUrl,
                            const nsAString& aCharset,
                            const nsAString& aType,
                            const nsAString& aCrossOrigin,
+                           const nsAString& aIntegrity,
                            bool aParserInHead)
     {
       NS_PRECONDITION(mOpCode == eSpeculativeLoadUninitialized,
                       "Trying to reinitialize a speculative load!");
       mOpCode = aParserInHead ?
           eSpeculativeLoadScriptFromHead : eSpeculativeLoadScript;
       mUrl.Assign(aUrl);
       mCharset.Assign(aCharset);
       mTypeOrCharsetSource.Assign(aType);
       mCrossOrigin.Assign(aCrossOrigin);
+      mIntegrity.Assign(aIntegrity);
     }
 
     inline void InitStyle(const nsAString& aUrl, const nsAString& aCharset,
-                          const nsAString& aCrossOrigin)
+                          const nsAString& aCrossOrigin,
+                          const nsAString& aIntegrity)
     {
       NS_PRECONDITION(mOpCode == eSpeculativeLoadUninitialized,
                       "Trying to reinitialize a speculative load!");
       mOpCode = eSpeculativeLoadStyle;
       mUrl.Assign(aUrl);
       mCharset.Assign(aCharset);
       mCrossOrigin.Assign(aCrossOrigin);
+      mIntegrity.Assign(aIntegrity);
     }
 
     /**
      * "Speculative" manifest loads aren't truly speculative--if a manifest
      * gets loaded, we are committed to it. There can never be a <script>
      * before the manifest, so the situation of having to undo a manifest due
      * to document.write() never arises. The reason why a parser
      * thread-discovered manifest gets loaded via the speculative load queue
@@ -214,11 +218,17 @@ class nsHtml5SpeculativeLoad {
      * attribute.  If the attribute is not set, this will be a void string.
      */
     nsString mSizes;
     /**
      * If mOpCode is eSpeculativeLoadPictureSource, this is the value of "media"
      * attribute.  If the attribute is not set, this will be a void string.
      */
     nsString mMedia;
+    /**
+     * If mOpCode is eSpeculativeLoadScript[FromHead], this is the value of the
+     * "integrity" attribute.  If the attribute is not set, this will be a void
+     * string.
+     */
+    nsString mIntegrity;
 };
 
 #endif // nsHtml5SpeculativeLoad_h
--- a/parser/html/nsHtml5TreeBuilderCppSupplement.h
+++ b/parser/html/nsHtml5TreeBuilderCppSupplement.h
@@ -160,41 +160,47 @@ nsHtml5TreeBuilder::createElement(int32_
           treeOp->Init(eTreeOpSetScriptLineNumberAndFreeze, content, tokenizer->getLineNumber());
 
           nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_SRC);
           if (url) {
             nsString* charset = aAttributes->getValue(nsHtml5AttributeName::ATTR_CHARSET);
             nsString* type = aAttributes->getValue(nsHtml5AttributeName::ATTR_TYPE);
             nsString* crossOrigin =
               aAttributes->getValue(nsHtml5AttributeName::ATTR_CROSSORIGIN);
+            nsString* integrity =
+              aAttributes->getValue(nsHtml5AttributeName::ATTR_INTEGRITY);
             mSpeculativeLoadQueue.AppendElement()->
               InitScript(*url,
                          (charset) ? *charset : EmptyString(),
                          (type) ? *type : EmptyString(),
                          (crossOrigin) ? *crossOrigin : NullString(),
+                         (integrity) ? *integrity : NullString(),
                          mode == NS_HTML5TREE_BUILDER_IN_HEAD);
             mCurrentHtmlScriptIsAsyncOrDefer =
               aAttributes->contains(nsHtml5AttributeName::ATTR_ASYNC) ||
               aAttributes->contains(nsHtml5AttributeName::ATTR_DEFER);
           }
         } else if (nsHtml5Atoms::link == aName) {
           nsString* rel = aAttributes->getValue(nsHtml5AttributeName::ATTR_REL);
           // Not splitting on space here is bogus but the old parser didn't even
           // do a case-insensitive check.
           if (rel) {
             if (rel->LowerCaseEqualsASCII("stylesheet")) {
               nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_HREF);
               if (url) {
                 nsString* charset = aAttributes->getValue(nsHtml5AttributeName::ATTR_CHARSET);
                 nsString* crossOrigin =
                   aAttributes->getValue(nsHtml5AttributeName::ATTR_CROSSORIGIN);
+                nsString* integrity =
+                  aAttributes->getValue(nsHtml5AttributeName::ATTR_INTEGRITY);
                 mSpeculativeLoadQueue.AppendElement()->
                   InitStyle(*url,
                             (charset) ? *charset : EmptyString(),
-                            (crossOrigin) ? *crossOrigin : NullString());
+                            (crossOrigin) ? *crossOrigin : NullString(),
+                            (integrity) ? *integrity : NullString());
               }
             } else if (rel->LowerCaseEqualsASCII("preconnect")) {
               nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_HREF);
               if (url) {
                 nsString* crossOrigin =
                   aAttributes->getValue(nsHtml5AttributeName::ATTR_CROSSORIGIN);
                 mSpeculativeLoadQueue.AppendElement()->
                   InitPreconnect(*url, (crossOrigin) ? *crossOrigin : NullString());
@@ -251,35 +257,41 @@ nsHtml5TreeBuilder::createElement(int32_
           NS_ASSERTION(treeOp, "Tree op allocation failed.");
           treeOp->Init(eTreeOpSetScriptLineNumberAndFreeze, content, tokenizer->getLineNumber());
 
           nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_XLINK_HREF);
           if (url) {
             nsString* type = aAttributes->getValue(nsHtml5AttributeName::ATTR_TYPE);
             nsString* crossOrigin =
               aAttributes->getValue(nsHtml5AttributeName::ATTR_CROSSORIGIN);
+            nsString* integrity =
+              aAttributes->getValue(nsHtml5AttributeName::ATTR_INTEGRITY);
             mSpeculativeLoadQueue.AppendElement()->
               InitScript(*url,
                          EmptyString(),
                          (type) ? *type : EmptyString(),
                          (crossOrigin) ? *crossOrigin : NullString(),
+                         (integrity) ? *integrity : NullString(),
                          mode == NS_HTML5TREE_BUILDER_IN_HEAD);
           }
         } else if (nsHtml5Atoms::style == aName) {
           nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
           NS_ASSERTION(treeOp, "Tree op allocation failed.");
           treeOp->Init(eTreeOpSetStyleLineNumber, content, tokenizer->getLineNumber());
 
           nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_XLINK_HREF);
           if (url) {
             nsString* crossOrigin =
               aAttributes->getValue(nsHtml5AttributeName::ATTR_CROSSORIGIN);
+            nsString* integrity =
+              aAttributes->getValue(nsHtml5AttributeName::ATTR_INTEGRITY);
             mSpeculativeLoadQueue.AppendElement()->
               InitStyle(*url, EmptyString(),
-                        (crossOrigin) ? *crossOrigin : NullString());
+                        (crossOrigin) ? *crossOrigin : NullString(),
+                        (integrity) ? *integrity : NullString());
           }
         }
         break;
     }
   } else if (aNamespace != kNameSpaceID_MathML) {
     // No speculative loader--just line numbers and defer/async check
     if (nsHtml5Atoms::style == aName) {
       nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
--- a/parser/html/nsHtml5TreeOpExecutor.cpp
+++ b/parser/html/nsHtml5TreeOpExecutor.cpp
@@ -906,38 +906,40 @@ nsHtml5TreeOpExecutor::ShouldPreloadURI(
   return true;
 }
 
 void
 nsHtml5TreeOpExecutor::PreloadScript(const nsAString& aURL,
                                      const nsAString& aCharset,
                                      const nsAString& aType,
                                      const nsAString& aCrossOrigin,
+                                     const nsAString& aIntegrity,
                                      bool aScriptFromHead)
 {
   nsCOMPtr<nsIURI> uri = ConvertIfNotPreloadedYet(aURL);
   if (!uri) {
     return;
   }
   mDocument->ScriptLoader()->PreloadURI(uri, aCharset, aType, aCrossOrigin,
-                                        aScriptFromHead,
+                                        aIntegrity, aScriptFromHead,
                                         mSpeculationReferrerPolicy);
 }
 
 void
 nsHtml5TreeOpExecutor::PreloadStyle(const nsAString& aURL,
                                     const nsAString& aCharset,
-                                    const nsAString& aCrossOrigin)
+                                    const nsAString& aCrossOrigin,
+                                    const nsAString& aIntegrity)
 {
   nsCOMPtr<nsIURI> uri = ConvertIfNotPreloadedYet(aURL);
   if (!uri) {
     return;
   }
   mDocument->PreloadStyle(uri, aCharset, aCrossOrigin,
-                          mSpeculationReferrerPolicy);
+                          mSpeculationReferrerPolicy, aIntegrity);
 }
 
 void
 nsHtml5TreeOpExecutor::PreloadImage(const nsAString& aURL,
                                     const nsAString& aCrossOrigin,
                                     const nsAString& aSrcset,
                                     const nsAString& aSizes,
                                     const nsAString& aImageReferrerPolicy)
--- a/parser/html/nsHtml5TreeOpExecutor.h
+++ b/parser/html/nsHtml5TreeOpExecutor.h
@@ -243,20 +243,22 @@ class nsHtml5TreeOpExecutor final : publ
 #endif
 
     nsIURI* GetViewSourceBaseURI();
 
     void PreloadScript(const nsAString& aURL,
                        const nsAString& aCharset,
                        const nsAString& aType,
                        const nsAString& aCrossOrigin,
+                       const nsAString& aIntegrity,
                        bool aScriptFromHead);
 
     void PreloadStyle(const nsAString& aURL, const nsAString& aCharset,
-                      const nsAString& aCrossOrigin);
+                      const nsAString& aCrossOrigin,
+                      const nsAString& aIntegrity);
 
     void PreloadImage(const nsAString& aURL,
                       const nsAString& aCrossOrigin,
                       const nsAString& aSrcset,
                       const nsAString& aSizes,
                       const nsAString& aImageReferrerPolicy);
 
     void PreloadOpenPicture();
--- a/xpcom/base/ErrorList.h
+++ b/xpcom/base/ErrorList.h
@@ -652,16 +652,21 @@
   /* ======================================================================= */
   /* 21: NS_ERROR_MODULE_SECURITY */
   /* ======================================================================= */
 #define MODULE NS_ERROR_MODULE_SECURITY
   /* Error code for CSP */
   ERROR(NS_ERROR_CSP_FORM_ACTION_VIOLATION,        FAILURE(98)),
   ERROR(NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION,     FAILURE(99)),
 
+  /* Error code for Sub-Resource Integrity */
+  ERROR(NS_ERROR_SRI_CORRUPT,                      FAILURE(200)),
+  ERROR(NS_ERROR_SRI_DISABLED,                     FAILURE(201)),
+  ERROR(NS_ERROR_SRI_NOT_ELIGIBLE,                 FAILURE(202)),
+
   /* CMS specific nsresult error codes.  Note: the numbers used here correspond
    * to the values in nsICMSMessageErrors.idl. */
   ERROR(NS_ERROR_CMS_VERIFY_NOT_SIGNED,            FAILURE(1024)),
   ERROR(NS_ERROR_CMS_VERIFY_NO_CONTENT_INFO,       FAILURE(1025)),
   ERROR(NS_ERROR_CMS_VERIFY_BAD_DIGEST,            FAILURE(1026)),
   ERROR(NS_ERROR_CMS_VERIFY_NOCERT,                FAILURE(1028)),
   ERROR(NS_ERROR_CMS_VERIFY_UNTRUSTED,             FAILURE(1029)),
   ERROR(NS_ERROR_CMS_VERIFY_ERROR_UNVERIFIED,      FAILURE(1031)),