Bug 1246540 - HSTS Priming Proof of Concept. r=ckerschb, r=mayhemer, r=jld, r=smaug, r=dkeeler, r=jmaher, p=ally
authorKate McKinley <kmckinley@mozilla.com>
Tue, 27 Sep 2016 11:27:00 -0400
changeset 315476 380eebfd9d8928b656236d08e535fe528258e947
parent 315475 11a470398b1f22a7be23b4a02d42fcb3fbf343da
child 315477 9fba3876cab0f889bea8f9cc5d9de02fb399b89b
push id30750
push usercbook@mozilla.com
push dateWed, 28 Sep 2016 13:57:20 +0000
treeherdermozilla-central@b1d60f2f68c7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersckerschb, mayhemer, jld, smaug, dkeeler, jmaher
bugs1246540
milestone52.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1246540 - HSTS Priming Proof of Concept. r=ckerschb, r=mayhemer, r=jld, r=smaug, r=dkeeler, r=jmaher, p=ally HSTS priming changes the order of mixed-content blocking and HSTS upgrades, and adds a priming request to check if a mixed-content load is accesible over HTTPS and the server supports upgrading via the Strict-Transport-Security header. Every call site that uses AsyncOpen2 passes through the mixed-content blocker, and has a LoadInfo. If the mixed-content blocker marks the load as needing HSTS priming, nsHttpChannel will build and send an HSTS priming request on the same URI with the scheme upgraded to HTTPS. If the server allows the upgrade, then channel performs an internal redirect to the HTTPS URI, otherwise use the result of mixed-content blocker to allow or block the load. nsISiteSecurityService adds an optional boolean out parameter to determine if the HSTS state is already cached for negative assertions. If the host has been probed within the previous 24 hours, no HSTS priming check will be sent. MozReview-Commit-ID: ES1JruCtDdX
browser/base/content/test/general/browser.ini
devtools/client/webconsole/test/browser_webconsole_block_mixedcontent_securityerrors.js
devtools/client/webconsole/test/browser_webconsole_bug_632817.js
docshell/base/nsDocShell.cpp
dom/base/nsIDocument.h
dom/base/test/bug704320.sjs
dom/base/test/referrerHelper.js
dom/html/test/test_anchor_ping.html
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PContent.ipdl
dom/security/moz.build
dom/security/nsContentSecurityManager.cpp
dom/security/nsMixedContentBlocker.cpp
dom/security/nsMixedContentBlocker.h
dom/security/test/hsts/browser.ini
dom/security/test/hsts/browser_hsts-priming_main.js
dom/security/test/hsts/file_1x1.png
dom/security/test/hsts/file_priming-top.html
dom/security/test/hsts/file_priming.js
dom/security/test/hsts/file_stylesheet.css
dom/security/test/hsts/file_testserver.sjs
dom/security/test/mixedcontentblocker/test_main.html
dom/security/test/moz.build
image/imgLoader.cpp
ipc/glue/BackgroundUtils.cpp
modules/libpref/init/all.js
netwerk/base/LoadInfo.cpp
netwerk/base/LoadInfo.h
netwerk/base/nsILoadInfo.idl
netwerk/base/nsNetUtil.cpp
netwerk/base/security-prefs.js
netwerk/ipc/NeckoChannelParams.ipdlh
netwerk/protocol/http/HSTSPrimerListener.cpp
netwerk/protocol/http/HSTSPrimerListener.h
netwerk/protocol/http/HttpChannelChild.cpp
netwerk/protocol/http/HttpChannelParent.cpp
netwerk/protocol/http/HttpChannelParent.h
netwerk/protocol/http/PHttpChannel.ipdl
netwerk/protocol/http/moz.build
netwerk/protocol/http/nsHttpChannel.cpp
netwerk/protocol/http/nsHttpChannel.h
netwerk/protocol/http/nsHttpHandler.cpp
netwerk/protocol/http/nsIHstsPrimingCallback.idl
security/manager/ssl/SSLServerCertVerification.cpp
security/manager/ssl/nsISiteSecurityService.idl
security/manager/ssl/nsSiteSecurityService.cpp
security/manager/ssl/nsSiteSecurityService.h
testing/specialpowers/content/SpecialPowersObserverAPI.js
testing/web-platform/meta/mixed-content/blockable/no-opt-in/cross-origin-http/fetch-request/top-level/keep-scheme-redirect/no-opt-in-blocks.https.html.ini
testing/web-platform/meta/mixed-content/blockable/no-opt-in/cross-origin-http/fetch-request/top-level/no-redirect/no-opt-in-blocks.https.html.ini
testing/web-platform/meta/mixed-content/blockable/no-opt-in/cross-origin-http/fetch-request/top-level/swap-scheme-redirect/no-opt-in-blocks.https.html.ini
testing/web-platform/meta/mixed-content/blockable/no-opt-in/cross-origin-http/link-css-tag/top-level/keep-scheme-redirect/no-opt-in-blocks.https.html.ini
testing/web-platform/meta/mixed-content/blockable/no-opt-in/cross-origin-http/link-css-tag/top-level/no-redirect/no-opt-in-blocks.https.html.ini
testing/web-platform/meta/mixed-content/blockable/no-opt-in/cross-origin-http/link-css-tag/top-level/swap-scheme-redirect/no-opt-in-blocks.https.html.ini
testing/web-platform/meta/mixed-content/blockable/no-opt-in/cross-origin-http/object-tag/top-level/keep-scheme-redirect/no-opt-in-blocks.https.html.ini
testing/web-platform/meta/mixed-content/blockable/no-opt-in/cross-origin-http/object-tag/top-level/no-redirect/no-opt-in-blocks.https.html.ini
testing/web-platform/meta/mixed-content/blockable/no-opt-in/cross-origin-http/object-tag/top-level/swap-scheme-redirect/no-opt-in-blocks.https.html.ini
testing/web-platform/meta/mixed-content/blockable/no-opt-in/cross-origin-http/picture-tag/top-level/keep-scheme-redirect/no-opt-in-blocks.https.html.ini
testing/web-platform/meta/mixed-content/blockable/no-opt-in/cross-origin-http/picture-tag/top-level/no-redirect/no-opt-in-blocks.https.html.ini
testing/web-platform/meta/mixed-content/blockable/no-opt-in/cross-origin-http/picture-tag/top-level/swap-scheme-redirect/no-opt-in-blocks.https.html.ini
testing/web-platform/meta/mixed-content/blockable/no-opt-in/cross-origin-http/script-tag/top-level/keep-scheme-redirect/no-opt-in-blocks.https.html.ini
testing/web-platform/meta/mixed-content/blockable/no-opt-in/cross-origin-http/script-tag/top-level/no-redirect/no-opt-in-blocks.https.html.ini
testing/web-platform/meta/mixed-content/blockable/no-opt-in/cross-origin-http/script-tag/top-level/swap-scheme-redirect/no-opt-in-blocks.https.html.ini
testing/web-platform/meta/mixed-content/blockable/no-opt-in/cross-origin-http/xhr-request/top-level/keep-scheme-redirect/no-opt-in-blocks.https.html.ini
testing/web-platform/meta/mixed-content/blockable/no-opt-in/cross-origin-http/xhr-request/top-level/no-redirect/no-opt-in-blocks.https.html.ini
testing/web-platform/meta/mixed-content/blockable/no-opt-in/cross-origin-http/xhr-request/top-level/swap-scheme-redirect/no-opt-in-blocks.https.html.ini
testing/web-platform/meta/mixed-content/blockable/no-opt-in/same-host-http/fetch-request/top-level/keep-scheme-redirect/no-opt-in-blocks.https.html.ini
testing/web-platform/meta/mixed-content/blockable/no-opt-in/same-host-http/fetch-request/top-level/no-redirect/no-opt-in-blocks.https.html.ini
testing/web-platform/meta/mixed-content/blockable/no-opt-in/same-host-http/fetch-request/top-level/swap-scheme-redirect/no-opt-in-blocks.https.html.ini
testing/web-platform/meta/mixed-content/blockable/no-opt-in/same-host-http/link-css-tag/top-level/keep-scheme-redirect/no-opt-in-blocks.https.html.ini
testing/web-platform/meta/mixed-content/blockable/no-opt-in/same-host-http/link-css-tag/top-level/no-redirect/no-opt-in-blocks.https.html.ini
testing/web-platform/meta/mixed-content/blockable/no-opt-in/same-host-http/link-css-tag/top-level/swap-scheme-redirect/no-opt-in-blocks.https.html.ini
testing/web-platform/meta/mixed-content/blockable/no-opt-in/same-host-http/object-tag/top-level/keep-scheme-redirect/no-opt-in-blocks.https.html.ini
testing/web-platform/meta/mixed-content/blockable/no-opt-in/same-host-http/object-tag/top-level/no-redirect/no-opt-in-blocks.https.html.ini
testing/web-platform/meta/mixed-content/blockable/no-opt-in/same-host-http/object-tag/top-level/swap-scheme-redirect/no-opt-in-blocks.https.html.ini
testing/web-platform/meta/mixed-content/blockable/no-opt-in/same-host-http/picture-tag/top-level/keep-scheme-redirect/no-opt-in-blocks.https.html.ini
testing/web-platform/meta/mixed-content/blockable/no-opt-in/same-host-http/picture-tag/top-level/no-redirect/no-opt-in-blocks.https.html.ini
testing/web-platform/meta/mixed-content/blockable/no-opt-in/same-host-http/picture-tag/top-level/swap-scheme-redirect/no-opt-in-blocks.https.html.ini
testing/web-platform/meta/mixed-content/blockable/no-opt-in/same-host-http/script-tag/top-level/keep-scheme-redirect/no-opt-in-blocks.https.html.ini
testing/web-platform/meta/mixed-content/blockable/no-opt-in/same-host-http/script-tag/top-level/no-redirect/no-opt-in-blocks.https.html.ini
testing/web-platform/meta/mixed-content/blockable/no-opt-in/same-host-http/script-tag/top-level/swap-scheme-redirect/no-opt-in-blocks.https.html.ini
testing/web-platform/meta/mixed-content/blockable/no-opt-in/same-host-http/xhr-request/top-level/keep-scheme-redirect/no-opt-in-blocks.https.html.ini
testing/web-platform/meta/mixed-content/blockable/no-opt-in/same-host-http/xhr-request/top-level/no-redirect/no-opt-in-blocks.https.html.ini
testing/web-platform/meta/mixed-content/blockable/no-opt-in/same-host-http/xhr-request/top-level/swap-scheme-redirect/no-opt-in-blocks.https.html.ini
testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/audio-tag/top-level/keep-scheme-redirect/no-opt-in-allows.https.html.ini
testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/audio-tag/top-level/no-redirect/no-opt-in-allows.https.html.ini
testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/audio-tag/top-level/swap-scheme-redirect/no-opt-in-allows.https.html.ini
testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/img-tag/top-level/keep-scheme-redirect/no-opt-in-allows.https.html.ini
testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/img-tag/top-level/no-redirect/no-opt-in-allows.https.html.ini
testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/img-tag/top-level/swap-scheme-redirect/no-opt-in-allows.https.html.ini
testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/link-prefetch-tag/top-level/no-redirect/no-opt-in-allows.https.html.ini
testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/link-prefetch-tag/top-level/swap-scheme-redirect/no-opt-in-allows.https.html.ini
testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/video-tag/top-level/keep-scheme-redirect/no-opt-in-allows.https.html.ini
testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/video-tag/top-level/no-redirect/no-opt-in-allows.https.html.ini
testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/video-tag/top-level/swap-scheme-redirect/no-opt-in-allows.https.html.ini
testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/same-host-http/audio-tag/top-level/keep-scheme-redirect/no-opt-in-allows.https.html.ini
testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/same-host-http/audio-tag/top-level/no-redirect/no-opt-in-allows.https.html.ini
testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/same-host-http/audio-tag/top-level/swap-scheme-redirect/no-opt-in-allows.https.html.ini
testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/same-host-http/img-tag/top-level/keep-scheme-redirect/no-opt-in-allows.https.html.ini
testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/same-host-http/img-tag/top-level/no-redirect/no-opt-in-allows.https.html.ini
testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/same-host-http/img-tag/top-level/swap-scheme-redirect/no-opt-in-allows.https.html.ini
testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/same-host-http/link-prefetch-tag/top-level/keep-scheme-redirect/no-opt-in-allows.https.html.ini
testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/same-host-http/link-prefetch-tag/top-level/no-redirect/no-opt-in-allows.https.html.ini
testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/same-host-http/link-prefetch-tag/top-level/swap-scheme-redirect/no-opt-in-allows.https.html.ini
testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/same-host-http/video-tag/top-level/keep-scheme-redirect/no-opt-in-allows.https.html.ini
testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/same-host-http/video-tag/top-level/no-redirect/no-opt-in-allows.https.html.ini
testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/same-host-http/video-tag/top-level/swap-scheme-redirect/no-opt-in-allows.https.html.ini
toolkit/components/telemetry/Histograms.json
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -113,16 +113,17 @@ support-files =
   test_mcb_redirect_image.html
   test_mcb_double_redirect_image.html
   test_mcb_redirect.js
   test_mcb_redirect.sjs
   file_bug1045809_1.html
   file_bug1045809_2.html
   file_csp_block_all_mixedcontent.html
   file_csp_block_all_mixedcontent.js
+  !/image/test/mochitest/blue.png
   !/toolkit/components/passwordmgr/test/browser/form_basic.html
   !/toolkit/components/passwordmgr/test/browser/insecure_test.html
   !/toolkit/components/passwordmgr/test/browser/insecure_test_subframe.html
   !/toolkit/content/tests/browser/common/mockTransfer.js
   !/toolkit/modules/tests/browser/metadata_*.html
   !/toolkit/mozapps/extensions/test/xpinstall/amosigned.xpi
   !/toolkit/mozapps/extensions/test/xpinstall/corrupt.xpi
   !/toolkit/mozapps/extensions/test/xpinstall/incompatible.xpi
--- a/devtools/client/webconsole/test/browser_webconsole_block_mixedcontent_securityerrors.js
+++ b/devtools/client/webconsole/test/browser_webconsole_block_mixedcontent_securityerrors.js
@@ -55,17 +55,19 @@ add_task(function* () {
   yield testClickOpenNewTab(hud, results2[0]);
 });
 
 function pushPrefEnv() {
   let deferred = promise.defer();
   let options = {
     "set": [
       ["security.mixed_content.block_active_content", true],
-      ["security.mixed_content.block_display_content", true]
+      ["security.mixed_content.block_display_content", true],
+      ["security.mixed_content.use_hsts", false],
+      ["security.mixed_content.send_hsts_priming", false],
     ]
   };
   SpecialPowers.pushPrefEnv(options, deferred.resolve);
   return deferred.promise;
 }
 
 function mixedContentOverrideTest2(hud, browser) {
   let deferred = promise.defer();
--- a/devtools/client/webconsole/test/browser_webconsole_bug_632817.js
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_632817.js
@@ -89,16 +89,23 @@ function testXhrGet() {
 
 function testXhrWarn() {
   // Start the XMLHttpRequest() warn test.
   ContentTask.spawn(gBrowser.selectedBrowser, {}, function*() {
     content.wrappedJSObject.testXhrWarn();
   });
 
   let lastRequest = yield waitForFinishedRequest(XHR_WARN_REQUEST_PREDICATE);
+  if (lastRequest.request.method == "HEAD") {
+    // in non-e10s, we get the HEAD request that priming sends, so make sure
+    // a priming request should be sent, and then get the actual request
+    is(Services.prefs.getBoolPref("security.mixed_content.send_hsts_priming"),
+        true, "Found HSTS Priming Request");
+    lastRequest = yield waitForFinishedRequest(XHR_WARN_REQUEST_PREDICATE);
+  }
 
   ok(lastRequest, "testXhrWarn() was logged");
   is(lastRequest.request.method, "GET", "Method is correct");
   ok(lastRequest.isXHR, "It's an XHR request");
   is(lastRequest.securityInfo, "insecure", "It's an insecure request");
 }
 
 function testXhrPost() {
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -4981,20 +4981,20 @@ nsDocShell::DisplayLoadError(nsresult aE
           UsePrivateBrowsing() ? nsISocketProvider::NO_PERMANENT_STORAGE : 0;
         bool isStsHost = false;
         bool isPinnedHost = false;
         if (XRE_IsParentProcess()) {
           nsCOMPtr<nsISiteSecurityService> sss =
             do_GetService(NS_SSSERVICE_CONTRACTID, &rv);
           NS_ENSURE_SUCCESS(rv, rv);
           rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, aURI,
-                                flags, &isStsHost);
+                                flags, nullptr, &isStsHost);
           NS_ENSURE_SUCCESS(rv, rv);
           rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HPKP, aURI,
-                                flags, &isPinnedHost);
+                                flags, nullptr, &isPinnedHost);
           NS_ENSURE_SUCCESS(rv, rv);
         } else {
           mozilla::dom::ContentChild* cc =
             mozilla::dom::ContentChild::GetSingleton();
           mozilla::ipc::URIParams uri;
           SerializeURI(aURI, uri);
           cc->SendIsSecureURI(nsISiteSecurityService::HEADER_HSTS, uri, flags,
                               &isStsHost);
@@ -9857,16 +9857,35 @@ nsDocShell::InternalLoad(nsIURI* aURI,
   if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
     if (NS_SUCCEEDED(rv) && shouldLoad == nsIContentPolicy::REJECT_TYPE) {
       return NS_ERROR_CONTENT_BLOCKED_SHOW_ALT;
     }
 
     return NS_ERROR_CONTENT_BLOCKED;
   }
 
+  // If HSTS priming was set by nsMixedContentBlocker::ShouldLoad, and we
+  // would block due to mixed content, go ahead and block here. If we try to
+  // proceed with priming, we will error out later on.
+  nsCOMPtr<nsIDocShell> docShell = NS_CP_GetDocShellFromContext(context);
+  NS_ENSURE_TRUE(docShell, NS_OK);
+  if (docShell) {
+    nsIDocument* document = docShell->GetDocument();
+    NS_ENSURE_TRUE(document, NS_OK);
+
+    HSTSPrimingState state = document->GetHSTSPrimingStateForLocation(aURI);
+    if (state == HSTSPrimingState::eHSTS_PRIMING_BLOCK) {
+      // HSTS Priming currently disabled for InternalLoad, so we need to clear
+      // the location that was added by nsMixedContentBlocker::ShouldLoad
+      // Bug 1269815 will address images loaded via InternalLoad
+      document->ClearHSTSPrimingLocation(aURI);
+      return NS_ERROR_CONTENT_BLOCKED;
+    }
+  }
+
   nsCOMPtr<nsIPrincipal> principalToInherit = aPrincipalToInherit;
   //
   // Get a principal from the current document if necessary.  Note that we only
   // do this for URIs that inherit a security context and local file URIs;
   // in particular we do NOT do this for about:blank.  This way, random
   // about:blank loads that have no principal (which basically means they were
   // done by someone from chrome manually messing with our nsIWebNavigation
   // or by C++ setting document.location) don't get a funky principal.  If
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -16,17 +16,18 @@
 #include "nsIDocumentObserver.h"         // for typedef (nsUpdateType)
 #include "nsILoadGroup.h"                // for member (in nsCOMPtr)
 #include "nsINode.h"                     // for base class
 #include "nsIScriptGlobalObject.h"       // for member (in nsCOMPtr)
 #include "nsIServiceManager.h"
 #include "nsIUUIDGenerator.h"
 #include "nsPIDOMWindow.h"               // for use in inline functions
 #include "nsPropertyTable.h"             // for member
-#include "nsTHashtable.h"                // for member
+#include "nsDataHashtable.h"             // for member
+#include "nsURIHashKey.h"                // for member
 #include "mozilla/net/ReferrerPolicy.h"  // for member
 #include "nsWeakReference.h"
 #include "mozilla/UseCounter.h"
 #include "mozilla/WeakPtr.h"
 #include "Units.h"
 #include "nsContentListDeclarations.h"
 #include "nsExpirationTracker.h"
 #include "nsClassHashtable.h"
@@ -170,16 +171,23 @@ typedef CallbackObjectHolder<NodeFilter,
 // 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
 };
 
+// Enum for HSTS priming states
+enum class HSTSPrimingState {
+  eNO_HSTS_PRIMING = 0,    // don't do HSTS Priming
+  eHSTS_PRIMING_ALLOW = 1, // if HSTS priming fails, allow the load to proceed
+  eHSTS_PRIMING_BLOCK = 2  // if HSTS priming fails, block the load
+};
+
 // Document states
 
 // RTL locale: specific to the XUL localedir attribute
 #define NS_DOCUMENT_STATE_RTL_LOCALE              NS_DEFINE_EVENT_STATE_MACRO(0)
 // Window activation status
 #define NS_DOCUMENT_STATE_WINDOW_INACTIVE         NS_DEFINE_EVENT_STATE_MACRO(1)
 
 // Some function forward-declarations
@@ -360,16 +368,44 @@ public:
     return mUpgradeInsecureRequests;
   }
 
   void SetReferrer(const nsACString& aReferrer) {
     mReferrer = aReferrer;
   }
 
   /**
+   * Check to see if a subresource we want to load requires HSTS priming
+   * to be done.
+   */
+  HSTSPrimingState GetHSTSPrimingStateForLocation(nsIURI* aContentLocation) const
+  {
+    HSTSPrimingState state;
+    if (mHSTSPrimingURIList.Get(aContentLocation, &state)) {
+      return state;
+    }
+    return HSTSPrimingState::eNO_HSTS_PRIMING;
+  }
+
+  /**
+   * Add a subresource to the HSTS priming list. If this URI is
+   * not in the HSTS cache, it will trigger an HSTS priming request
+   * when we try to load it.
+   */
+  void AddHSTSPrimingLocation(nsIURI* aContentLocation, HSTSPrimingState aState)
+  {
+    mHSTSPrimingURIList.Put(aContentLocation, aState);
+  }
+
+  void ClearHSTSPrimingLocation(nsIURI* aContentLocation)
+  {
+    mHSTSPrimingURIList.Remove(aContentLocation);
+  }
+
+  /**
    * Set the principal responsible for this document.
    */
   virtual void SetPrincipal(nsIPrincipal *aPrincipal) = 0;
 
   /**
    * Return the LoadGroup for the document. May return null.
    */
   already_AddRefed<nsILoadGroup> GetDocumentLoadGroup() const
@@ -2851,16 +2887,21 @@ protected:
   bool mReferrerPolicySet;
   ReferrerPolicyEnum mReferrerPolicy;
 
   bool mBlockAllMixedContent;
   bool mBlockAllMixedContentPreloads;
   bool mUpgradeInsecureRequests;
   bool mUpgradeInsecurePreloads;
 
+  // if nsMixedContentBlocker requires sending an HSTS priming request,
+  // temporarily store that in the document so that it can be propogated to the
+  // LoadInfo and eventually the HTTP Channel
+  nsDataHashtable<nsURIHashKey, HSTSPrimingState> mHSTSPrimingURIList;
+
   mozilla::WeakPtr<nsDocShell> mDocumentContainer;
 
   nsCString mCharacterSet;
   int32_t mCharacterSetSource;
 
   // This is just a weak pointer; the parent document owns its children.
   nsIDocument* mParentDocument;
 
--- a/dom/base/test/bug704320.sjs
+++ b/dom/base/test/bug704320.sjs
@@ -189,16 +189,22 @@ function createPolicyTest(policy, option
                     onload="incrementLoad2(\'img\', 2);">\n\
             <img src="http://example.com/tests/dom/base/test/bug704320_counter.sjs?type=img"\n\
                     onload="incrementLoad2(\'img\', 2);">\n\
           </body>\n\
           </html>';
 }
 
 function handleRequest(request, response) {
+  if (request.method == 'HEAD') {
+    // respond to a HEAD request with a 418 so that we can easily distinguish
+    // HSTS priming responses and ignore them
+    response.setStatusLine('1.1', 418, "I'm a teapot");
+    return;
+  }
   var sharedKey = 'bug704320.sjs';
   var params = request.queryString.split('&');
   var action = params[0].split('=')[1];
 
   if (action === 'create-1st-level-iframe') {
     // ?action=create-1st-level-iframe&scheme-from=http&scheme-to=https&policy=origin
     var schemeFrom = params[1].split('=')[1];
     var schemeTo = params[2].split('=')[1];
--- a/dom/base/test/referrerHelper.js
+++ b/dom/base/test/referrerHelper.js
@@ -20,16 +20,19 @@ window.addEventListener("message", funct
 /**
  * helper to perform an XHR.
  */
 function doXHR(url, onSuccess, onFail) {
   var xhr = new XMLHttpRequest();
   xhr.onload = function () {
     if (xhr.status == 200) {
       onSuccess(xhr);
+    } else if (xhr.status == 418) {
+      // Ignore HSTS priming responses
+      return;
     } else {
       onFail(xhr);
     }
   };
   xhr.open('GET', url, true);
   xhr.send(null);
 }
 
--- a/dom/html/test/test_anchor_ping.html
+++ b/dom/html/test/test_anchor_ping.html
@@ -36,21 +36,24 @@ addLoadEvent(function () {
 
 let tests = [
 
   // Ensure that sending pings is enabled.
   function* setup() {
     Services.prefs.setBoolPref("browser.send_pings", true);
     Services.prefs.setIntPref("browser.send_pings.max_per_link", -1);
     Services.prefs.setBoolPref("security.mixed_content.block_active_content", false);
+    // The server we create can't handle the priming HEAD requests
+    Services.prefs.setBoolPref("security.mixed_content.send_hsts_priming", false);
 
     SimpleTest.registerCleanupFunction(() => {
       Services.prefs.clearUserPref("browser.send_pings");
       Services.prefs.clearUserPref("browser.send_pings.max_per_link");
       Services.prefs.clearUserPref("security.mixed_content.block_active_content");
+      Services.prefs.clearUserPref("security.mixed_content.send_hsts_priming");
     });
   },
 
   // If both the address of the document containing the hyperlink being audited
   // and ping URL have the same origin then the request must include a Ping-From
   // HTTP header with, as its value, the address of the document containing the
   // hyperlink, and a Ping-To HTTP header with, as its value, the target URL.
   // The request must not include a Referer (sic) HTTP header.
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -3896,28 +3896,28 @@ ContentParent::RecvIsSecureURI(const uin
   nsCOMPtr<nsISiteSecurityService> sss(do_GetService(NS_SSSERVICE_CONTRACTID));
   if (!sss) {
     return false;
   }
   nsCOMPtr<nsIURI> ourURI = DeserializeURI(uri);
   if (!ourURI) {
     return false;
   }
-  nsresult rv = sss->IsSecureURI(type, ourURI, flags, isSecureURI);
+  nsresult rv = sss->IsSecureURI(type, ourURI, flags, nullptr, isSecureURI);
   return NS_SUCCEEDED(rv);
 }
 
 bool
-ContentParent::RecvAccumulateMixedContentHSTS(const URIParams& aURI, const bool& aActive)
+ContentParent::RecvAccumulateMixedContentHSTS(const URIParams& aURI, const bool& aActive, const bool& aHSTSPriming)
 {
   nsCOMPtr<nsIURI> ourURI = DeserializeURI(aURI);
   if (!ourURI) {
     return false;
   }
-  nsMixedContentBlocker::AccumulateMixedContentHSTS(ourURI, aActive);
+  nsMixedContentBlocker::AccumulateMixedContentHSTS(ourURI, aActive, aHSTSPriming);
   return true;
 }
 
 bool
 ContentParent::RecvLoadURIExternal(const URIParams& uri,
                                    PBrowserParent* windowContext)
 {
   nsCOMPtr<nsIExternalProtocolService> extProtService(do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID));
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -784,17 +784,18 @@ private:
                                    nsTArray<uint8_t>&& aChallenge,
                                    nsTArray<uint8_t>&& aKeyHandle,
                                    nsTArray<uint8_t>* aSignature) override;
 
   virtual bool RecvIsSecureURI(const uint32_t& aType, const URIParams& aURI,
                                const uint32_t& aFlags, bool* aIsSecureURI) override;
 
   virtual bool RecvAccumulateMixedContentHSTS(const URIParams& aURI,
-                                              const bool& aActive) override;
+                                              const bool& aActive,
+                                              const bool& aHSTSPriming) override;
 
   virtual bool DeallocPHalParent(PHalParent*) override;
 
   virtual bool
   DeallocPHeapSnapshotTempFileHelperParent(PHeapSnapshotTempFileHelperParent*) override;
 
   virtual PIccParent* AllocPIccParent(const uint32_t& aServiceId) override;
 
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -833,17 +833,17 @@ parent:
      */
     sync NSSU2FTokenSign(uint8_t[] application, uint8_t[] challenge,
                          uint8_t[] keyHandle)
         returns (uint8_t[] signature);
 
     sync IsSecureURI(uint32_t type, URIParams uri, uint32_t flags)
         returns (bool isSecureURI);
 
-    async AccumulateMixedContentHSTS(URIParams uri, bool active);
+    async AccumulateMixedContentHSTS(URIParams uri, bool active, bool hasHSTSPriming);
 
     sync GetLookAndFeelCache()
         returns (LookAndFeelInt[] lookAndFeelIntCache);
 
     prio(urgent) async PHal();
 
     async PHeapSnapshotTempFileHelper();
 
--- a/dom/security/moz.build
+++ b/dom/security/moz.build
@@ -15,16 +15,17 @@ EXPORTS.mozilla.dom += [
     'nsMixedContentBlocker.h',
     'SRICheck.h',
     'SRILogHelper.h',
     'SRIMetadata.h',
 ]
 
 EXPORTS += [
     'nsContentSecurityManager.h',
+    'nsMixedContentBlocker.h',
 ]
 
 UNIFIED_SOURCES += [
     'ContentVerifier.cpp',
     'nsContentSecurityManager.cpp',
     'nsCSPContext.cpp',
     'nsCSPParser.cpp',
     'nsCSPService.cpp',
--- a/dom/security/nsContentSecurityManager.cpp
+++ b/dom/security/nsContentSecurityManager.cpp
@@ -1,16 +1,18 @@
 #include "nsContentSecurityManager.h"
 #include "nsIChannel.h"
 #include "nsIHttpChannelInternal.h"
 #include "nsIStreamListener.h"
 #include "nsILoadInfo.h"
 #include "nsContentUtils.h"
 #include "nsCORSListenerProxy.h"
 #include "nsIStreamListener.h"
+#include "nsIDocument.h"
+#include "nsMixedContentBlocker.h"
 
 #include "mozilla/dom/Element.h"
 
 NS_IMPL_ISUPPORTS(nsContentSecurityManager,
                   nsIContentSecurityManager,
                   nsIChannelEventSink)
 
 static nsresult
@@ -376,16 +378,24 @@ DoContentSecurityChecks(nsIChannel* aCha
                                  nullptr,        //extra,
                                  &shouldLoad,
                                  nsContentUtils::GetContentPolicy(),
                                  nsContentUtils::GetSecurityManager());
   NS_ENSURE_SUCCESS(rv, rv);
   if (NS_CP_REJECTED(shouldLoad)) {
     return NS_ERROR_CONTENT_BLOCKED;
   }
+
+  if (nsMixedContentBlocker::sSendHSTSPriming) {
+    rv = nsMixedContentBlocker::MarkLoadInfoForPriming(uri,
+                                                       requestingContext,
+                                                       aLoadInfo);
+    return rv;
+  }
+
   return NS_OK;
 }
 
 /*
  * Based on the security flags provided in the loadInfo of the channel,
  * doContentSecurityCheck() performs the following content security checks
  * before opening the channel:
  *
@@ -484,17 +494,17 @@ nsContentSecurityManager::AsyncOnChannel
       nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT |
       nsIScriptSecurityManager::DISALLOW_SCRIPT;
   nsresult rv = nsContentUtils::GetSecurityManager()->
     CheckLoadURIWithPrincipal(oldPrincipal, newURI, flags);
   if (NS_SUCCEEDED(rv) && newOriginalURI != newURI) {
       rv = nsContentUtils::GetSecurityManager()->
         CheckLoadURIWithPrincipal(oldPrincipal, newOriginalURI, flags);
   }
-  NS_ENSURE_SUCCESS(rv, rv);  
+  NS_ENSURE_SUCCESS(rv, rv);
 
   aCb->OnRedirectVerifyCallback(NS_OK);
   return NS_OK;
 }
 
 static void
 AddLoadFlags(nsIRequest *aRequest, nsLoadFlags aNewFlags)
 {
--- a/dom/security/nsMixedContentBlocker.cpp
+++ b/dom/security/nsMixedContentBlocker.cpp
@@ -49,16 +49,21 @@ enum nsMixedContentBlockerMessageType {
 
 // Is mixed script blocking (fonts, plugin content, scripts, stylesheets,
 // iframes, websockets, XHR) enabled?
 bool nsMixedContentBlocker::sBlockMixedScript = false;
 
 // Is mixed display content blocking (images, audio, video, <a ping>) enabled?
 bool nsMixedContentBlocker::sBlockMixedDisplay = false;
 
+// Do we move HSTS before mixed-content
+bool nsMixedContentBlocker::sUseHSTS = false;
+// Do we send an HSTS priming request
+bool nsMixedContentBlocker::sSendHSTSPriming = false;
+
 // Fired at the document that attempted to load mixed content.  The UI could
 // handle this event, for example, by displaying an info bar that offers the
 // choice to reload the page with mixed content permitted.
 class nsMixedContentEvent : public Runnable
 {
 public:
   nsMixedContentEvent(nsISupports *aContext, MixedContentTypes aType, bool aRootHasSecureConnection)
     : mContext(aContext), mType(aType), mRootHasSecureConnection(aRootHasSecureConnection)
@@ -190,16 +195,24 @@ nsMixedContentBlocker::nsMixedContentBlo
 {
   // Cache the pref for mixed script blocking
   Preferences::AddBoolVarCache(&sBlockMixedScript,
                                "security.mixed_content.block_active_content");
 
   // Cache the pref for mixed display blocking
   Preferences::AddBoolVarCache(&sBlockMixedDisplay,
                                "security.mixed_content.block_display_content");
+
+  // Cache the pref for HSTS
+  Preferences::AddBoolVarCache(&sUseHSTS,
+                               "security.mixed_content.use_hsts");
+
+  // Cache the pref for sending HSTS priming
+  Preferences::AddBoolVarCache(&sSendHSTSPriming,
+                               "security.mixed_content.send_hsts_priming");
 }
 
 nsMixedContentBlocker::~nsMixedContentBlocker()
 {
 }
 
 NS_IMPL_ISUPPORTS(nsMixedContentBlocker, nsIContentPolicy, nsIChannelEventSink)
 
@@ -233,18 +246,16 @@ LogMixedContentMessage(MixedContentTypes
 
   NS_ConvertUTF8toUTF16 locationSpecUTF16(aContentLocation->GetSpecOrDefault());
   const char16_t* strings[] = { locationSpecUTF16.get() };
   nsContentUtils::ReportToConsole(severityFlag, messageCategory, aRootDoc,
                                   nsContentUtils::eSECURITY_PROPERTIES,
                                   messageLookupKey.get(), strings, ArrayLength(strings));
 }
 
-
-
 /* nsIChannelEventSink implementation
  * This code is called when a request is redirected.
  * We check the channel associated with the new uri is allowed to load
  * in the current context
  */
 NS_IMETHODIMP
 nsMixedContentBlocker::AsyncOnChannelRedirect(nsIChannel* aOldChannel,
                                               nsIChannel* aNewChannel,
@@ -304,27 +315,45 @@ nsMixedContentBlocker::AsyncOnChannelRed
     if (nsContentUtils::IsSystemPrincipal(requestingPrincipal)) {
       return NS_OK;
     }
     // We set the requestingLocation from the RequestingPrincipal.
     rv = requestingPrincipal->GetURI(getter_AddRefs(requestingLocation));
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
+  nsCOMPtr<nsISupports> requestingContext = loadInfo->LoadingNode();
+
   int16_t decision = REJECT_REQUEST;
   rv = ShouldLoad(contentPolicyType,
                   newUri,
                   requestingLocation,
-                  loadInfo->LoadingNode(),
+                  requestingContext,
                   EmptyCString(),       // aMimeGuess
                   nullptr,              // aExtra
                   requestingPrincipal,
                   &decision);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  if (nsMixedContentBlocker::sSendHSTSPriming) {
+    // The LoadInfo passed in is for the original channel, HSTS priming needs to
+    // be set on the new channel, if required. If the redirect changes
+    // http->https, or vice-versa, the need for priming may change.
+    nsCOMPtr<nsILoadInfo> newLoadInfo;
+    rv = aNewChannel->GetLoadInfo(getter_AddRefs(newLoadInfo));
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = nsMixedContentBlocker::MarkLoadInfoForPriming(newUri,
+                                                       requestingContext,
+                                                       newLoadInfo);
+    if (NS_FAILED(rv)) {
+      decision = REJECT_REQUEST;
+      newLoadInfo->ClearHSTSPriming();
+    }
+  }
+
   // If the channel is about to load mixed content, abort the channel
   if (!NS_CP_ACCEPTED(decision)) {
     autoCallback.DontCallback();
     return NS_BINDING_FAILED;
   }
 
   return NS_OK;
 }
@@ -458,17 +487,16 @@ nsMixedContentBlocker::ShouldLoad(bool a
       return NS_OK;
     // Creating insecure websocket connections in a secure page is blocked already
     // in the websocket constructor. We don't need to check the blocking here
     // and we don't want to un-block
     case TYPE_WEBSOCKET:
       *aDecision = ACCEPT;
       return NS_OK;
 
-
     // Static display content is considered moderate risk for mixed content so
     // these will be blocked according to the mixed display preference
     case TYPE_IMAGE:
     case TYPE_MEDIA:
     case TYPE_OBJECT_SUBREQUEST:
       classification = eMixedDisplay;
       break;
 
@@ -492,17 +520,16 @@ nsMixedContentBlocker::ShouldLoad(bool a
     case TYPE_XSLT:
     case TYPE_OTHER:
       break;
 
 
     // This content policy works as a whitelist.
     default:
       MOZ_ASSERT(false, "Mixed content of unknown type");
-      break;
   }
 
   // Make sure to get the URI the load started with. No need to check
   // outer schemes because all the wrapping pseudo protocols inherit the
   // security properties of the actual network request represented
   // by the innerMost URL.
   nsCOMPtr<nsIURI> innerContentLocation = NS_GetInnermostURI(aContentLocation);
   if (!innerContentLocation) {
@@ -672,43 +699,45 @@ nsMixedContentBlocker::ShouldLoad(bool a
   // http: and ws: (for websockets). Websockets are not subject to mixed content
   // blocking since insecure websockets are not allowed within secure pages. Hence,
   // we only have to check against http: here. Skip mixed content blocking if the
   // subresource load uses http: and the CSP directive 'upgrade-insecure-requests'
   // is present on the page.
   bool isHttpScheme = false;
   rv = innerContentLocation->SchemeIs("http", &isHttpScheme);
   NS_ENSURE_SUCCESS(rv, rv);
-  if (isHttpScheme && docShell->GetDocument()->GetUpgradeInsecureRequests(isPreload)) {
+  nsIDocument* document = docShell->GetDocument();
+  MOZ_ASSERT(document, "Expected a document");
+  if (isHttpScheme && document->GetUpgradeInsecureRequests(isPreload)) {
     *aDecision = ACCEPT;
     return NS_OK;
   }
 
   // The page might have set the CSP directive 'block-all-mixed-content' which
   // should block not only active mixed content loads but in fact all mixed content
   // loads, see https://www.w3.org/TR/mixed-content/#strict-checking
   // Block all non secure loads in case the CSP directive is present. Please note
   // that at this point we already know, based on |schemeSecure| that the load is
   // not secure, so we can bail out early at this point.
-  if (docShell->GetDocument()->GetBlockAllMixedContent(isPreload)) {
+  if (document->GetBlockAllMixedContent(isPreload)) {
     // log a message to the console before returning.
     nsAutoCString spec;
     rv = aContentLocation->GetSpec(spec);
     NS_ENSURE_SUCCESS(rv, rv);
     NS_ConvertUTF8toUTF16 reportSpec(spec);
 
     const char16_t* params[] = { reportSpec.get()};
     CSP_LogLocalizedStr(u"blockAllMixedContent",
                         params, ArrayLength(params),
                         EmptyString(), // aSourceFile
                         EmptyString(), // aScriptSample
                         0, // aLineNumber
                         0, // aColumnNumber
                         nsIScriptError::errorFlag, "CSP",
-                        docShell->GetDocument()->InnerWindowID());
+                        document->InnerWindowID());
     *aDecision = REJECT_REQUEST;
     return NS_OK;
   }
 
   // Determine if the rootDoc is https and if the user decided to allow Mixed Content
   bool rootHasSecureConnection = false;
   bool allowMixedContent = false;
   bool isRootDocShell = false;
@@ -791,39 +820,67 @@ nsMixedContentBlocker::ShouldLoad(bool a
   // If there is no securityUI, document doesn't have a security state.
   // Allow load and return early.
   if (!securityUI) {
     *aDecision = nsIContentPolicy::ACCEPT;
     return NS_OK;
   }
   nsresult stateRV = securityUI->GetState(&state);
 
+  bool doHSTSPriming = false;
+  if (isHttpScheme) {
+    bool hsts = false;
+    bool cached = false;
+    nsCOMPtr<nsISiteSecurityService> sss =
+      do_GetService(NS_SSSERVICE_CONTRACTID, &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, aContentLocation,
+        0, &cached, &hsts);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (hsts && sUseHSTS) {
+      // assume we will be upgraded later
+      *aDecision = ACCEPT;
+      return NS_OK;
+    }
+
+    // Send a priming request if the result is not already cached and priming
+    // requests are allowed
+    if (!cached && sSendHSTSPriming) {
+      // add this URI as a priming location
+      doHSTSPriming = true;
+      document->AddHSTSPrimingLocation(innerContentLocation,
+          HSTSPrimingState::eHSTS_PRIMING_ALLOW);
+      *aDecision = ACCEPT;
+    }
+  }
+
   // At this point we know that the request is mixed content, and the only
   // question is whether we block it.  Record telemetry at this point as to
   // whether HSTS would have fixed things by making the content location
   // into an HTTPS URL.
   //
   // Note that we count this for redirects as well as primary requests. This
   // will cause some degree of double-counting, especially when mixed content
   // is not blocked (e.g., for images).  For more detail, see:
   //   https://bugzilla.mozilla.org/show_bug.cgi?id=1198572#c19
   //
   // We do not count requests aHadInsecureImageRedirect=true, since these are
   // just an artifact of the image caching system.
   bool active = (classification == eMixedScript);
   if (!aHadInsecureImageRedirect) {
     if (XRE_IsParentProcess()) {
-      AccumulateMixedContentHSTS(innerContentLocation, active);
+      AccumulateMixedContentHSTS(innerContentLocation, active, doHSTSPriming);
     } else {
       // Ask the parent process to do the same call
       mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton();
       if (cc) {
         mozilla::ipc::URIParams uri;
         SerializeURI(innerContentLocation, uri);
-        cc->SendAccumulateMixedContentHSTS(uri, active);
+        cc->SendAccumulateMixedContentHSTS(uri, active, doHSTSPriming);
       }
     }
   }
 
   // set hasMixedContentObjectSubrequest on this object if necessary
   if (aContentType == TYPE_OBJECT_SUBREQUEST) {
     rootDoc->SetHasMixedContentObjectSubrequest(true);
   }
@@ -856,17 +913,23 @@ nsMixedContentBlocker::ShouldLoad(bool a
       } else {
         // User has overriden the pref and the root is not https;
         // mixed display content was allowed on an https subframe.
         if (NS_SUCCEEDED(stateRV)) {
           eventSink->OnSecurityChange(aRequestingContext, (state | nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT));
         }
       }
     } else {
-      *aDecision = nsIContentPolicy::REJECT_REQUEST;
+      if (doHSTSPriming) {
+        document->AddHSTSPrimingLocation(innerContentLocation,
+            HSTSPrimingState::eHSTS_PRIMING_BLOCK);
+        *aDecision = nsIContentPolicy::ACCEPT;
+      } else {
+        *aDecision = nsIContentPolicy::REJECT_REQUEST;
+      }
       LogMixedContentMessage(classification, aContentLocation, rootDoc, eBlocked);
       if (!rootDoc->GetHasMixedDisplayContentBlocked() && NS_SUCCEEDED(stateRV)) {
         rootDoc->SetHasMixedDisplayContentBlocked(true);
         eventSink->OnSecurityChange(aRequestingContext, (state | nsIWebProgressListener::STATE_BLOCKED_MIXED_DISPLAY_CONTENT));
       }
     }
     return NS_OK;
 
@@ -902,32 +965,37 @@ nsMixedContentBlocker::ShouldLoad(bool a
         // mixed active content was allowed on an https subframe.
         if (NS_SUCCEEDED(stateRV)) {
           eventSink->OnSecurityChange(aRequestingContext, (state | nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT));
         }
         return NS_OK;
       }
     } else {
       //User has not overriden the pref by Disabling protection. Reject the request and update the security state.
-      *aDecision = nsIContentPolicy::REJECT_REQUEST;
+      if (doHSTSPriming) {
+        document->AddHSTSPrimingLocation(innerContentLocation,
+            HSTSPrimingState::eHSTS_PRIMING_BLOCK);
+        *aDecision = nsIContentPolicy::ACCEPT;
+      } else {
+        *aDecision = nsIContentPolicy::REJECT_REQUEST;
+      }
       LogMixedContentMessage(classification, aContentLocation, rootDoc, eBlocked);
       // See if the pref will change here. If it will, only then do we need to call OnSecurityChange() to update the UI.
       if (rootDoc->GetHasMixedActiveContentBlocked()) {
         return NS_OK;
       }
       rootDoc->SetHasMixedActiveContentBlocked(true);
 
       // The user has not overriden the pref, so make sure they still have an option by calling eventSink
       // which will invoke the doorhanger
       if (NS_SUCCEEDED(stateRV)) {
          eventSink->OnSecurityChange(aRequestingContext, (state | nsIWebProgressListener::STATE_BLOCKED_MIXED_ACTIVE_CONTENT));
       }
       return NS_OK;
     }
-
   } else {
     // The content is not blocked by the mixed content prefs.
 
     // Log a message that we are loading mixed content.
     LogMixedContentMessage(classification, aContentLocation, rootDoc, eUserOverride);
 
     // Fire the event from a script runner as it is unsafe to run script
     // from within ShouldLoad
@@ -968,51 +1036,150 @@ nsMixedContentBlocker::ShouldProcess(uin
 
 enum MixedContentHSTSState {
   MCB_HSTS_PASSIVE_NO_HSTS   = 0,
   MCB_HSTS_PASSIVE_WITH_HSTS = 1,
   MCB_HSTS_ACTIVE_NO_HSTS    = 2,
   MCB_HSTS_ACTIVE_WITH_HSTS  = 3
 };
 
+// Similar to the existing mixed-content HSTS, except MCB_HSTS_*_NO_HSTS is
+// broken into two distinct states, indicating whether we plan to send a priming
+// request or not. If we decided not go send a priming request, it could be
+// because it is a type we do not support, or because we cached a previous
+// negative response.
+enum MixedContentHSTSPrimingState {
+  eMCB_HSTS_PASSIVE_WITH_HSTS  = 0,
+  eMCB_HSTS_ACTIVE_WITH_HSTS   = 1,
+  eMCB_HSTS_PASSIVE_NO_PRIMING = 2,
+  eMCB_HSTS_PASSIVE_DO_PRIMING = 3,
+  eMCB_HSTS_ACTIVE_NO_PRIMING  = 4,
+  eMCB_HSTS_ACTIVE_DO_PRIMING  = 5
+};
+
 // Record information on when HSTS would have made mixed content not mixed
 // content (regardless of whether it was actually blocked)
 void
-nsMixedContentBlocker::AccumulateMixedContentHSTS(nsIURI* aURI, bool aActive)
+nsMixedContentBlocker::AccumulateMixedContentHSTS(nsIURI* aURI, bool aActive, bool aHasHSTSPriming)
 {
   // This method must only be called in the parent, because
   // nsSiteSecurityService is only available in the parent
   if (!XRE_IsParentProcess()) {
     MOZ_ASSERT(false);
     return;
   }
 
   bool hsts;
   nsresult rv;
   nsCOMPtr<nsISiteSecurityService> sss = do_GetService(NS_SSSERVICE_CONTRACTID, &rv);
   if (NS_FAILED(rv)) {
     return;
   }
-  rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, aURI, 0, &hsts);
+  rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, aURI, 0, nullptr, &hsts);
   if (NS_FAILED(rv)) {
     return;
   }
 
+  // states: would upgrade, would prime, hsts info cached
+  // active, passive
+  //
   if (!aActive) {
     if (!hsts) {
       Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS,
                             MCB_HSTS_PASSIVE_NO_HSTS);
+      if (aHasHSTSPriming) {
+        Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING,
+                              eMCB_HSTS_PASSIVE_DO_PRIMING);
+      } else {
+        Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING,
+                              eMCB_HSTS_PASSIVE_NO_PRIMING);
+      }
     }
     else {
       Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS,
                             MCB_HSTS_PASSIVE_WITH_HSTS);
+      Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING,
+                            eMCB_HSTS_PASSIVE_WITH_HSTS);
     }
   } else {
     if (!hsts) {
       Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS,
                             MCB_HSTS_ACTIVE_NO_HSTS);
+      if (aHasHSTSPriming) {
+        Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING,
+                              eMCB_HSTS_ACTIVE_DO_PRIMING);
+      } else {
+        Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING,
+                              eMCB_HSTS_ACTIVE_NO_PRIMING);
+      }
     }
     else {
       Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS,
                             MCB_HSTS_ACTIVE_WITH_HSTS);
+      Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING,
+                            eMCB_HSTS_ACTIVE_WITH_HSTS);
     }
   }
 }
+
+//static
+nsresult
+nsMixedContentBlocker::MarkLoadInfoForPriming(nsIURI* aURI,
+                                              nsISupports* aRequestingContext,
+                                              nsILoadInfo* aLoadInfo)
+{
+  nsresult rv;
+  bool sendPriming = false;
+  bool mixedContentWouldBlock = false;
+  rv = GetHSTSPrimingFromRequestingContext(aURI,
+                                           aRequestingContext,
+                                           &sendPriming,
+                                           &mixedContentWouldBlock);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (sendPriming) {
+    aLoadInfo->SetHSTSPriming(mixedContentWouldBlock);
+  }
+
+  return NS_OK;
+}
+
+//static
+nsresult
+nsMixedContentBlocker::GetHSTSPrimingFromRequestingContext(nsIURI* aURI,
+    nsISupports* aRequestingContext,
+    bool* aSendPrimingRequest,
+    bool* aMixedContentWouldBlock)
+{
+  *aSendPrimingRequest = false;
+  *aMixedContentWouldBlock = false;
+  // If we marked for priming, we used the innermost URI, so get that
+  nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(aURI);
+  if (!innerURI) {
+    NS_ERROR("Can't get innerURI from aContentLocation");
+    return NS_ERROR_CONTENT_BLOCKED;
+  }
+
+  bool isHttp = false;
+  innerURI->SchemeIs("http", &isHttp);
+  if (!isHttp) {
+    // there is nothign to do
+    return NS_OK;
+  }
+
+  // If the DocShell was marked for HSTS priming, propagate that to the LoadInfo
+  nsCOMPtr<nsIDocShell> docShell = NS_CP_GetDocShellFromContext(aRequestingContext);
+  if (!docShell) {
+    return NS_OK;
+  }
+  nsCOMPtr<nsIDocument> document = docShell->GetDocument();
+  if (!document) {
+    return NS_OK;
+  }
+
+  HSTSPrimingState status = document->GetHSTSPrimingStateForLocation(innerURI);
+  if (status != HSTSPrimingState::eNO_HSTS_PRIMING) {
+    *aSendPrimingRequest = (status != HSTSPrimingState::eNO_HSTS_PRIMING);
+    *aMixedContentWouldBlock = (status == HSTSPrimingState::eHSTS_PRIMING_BLOCK);
+  }
+
+  return NS_OK;
+}
--- a/dom/security/nsMixedContentBlocker.h
+++ b/dom/security/nsMixedContentBlocker.h
@@ -23,16 +23,18 @@ enum MixedContentTypes {
   eMixedDisplay
 };
 
 #include "nsIContentPolicy.h"
 #include "nsIChannel.h"
 #include "nsIChannelEventSink.h"
 #include "imgRequest.h"
 
+class nsILoadInfo; // forward declaration
+
 class nsMixedContentBlocker : public nsIContentPolicy,
                               public nsIChannelEventSink
 {
 private:
   virtual ~nsMixedContentBlocker();
 
 public:
   NS_DECL_ISUPPORTS
@@ -54,14 +56,46 @@ public:
                              uint32_t aContentType,
                              nsIURI* aContentLocation,
                              nsIURI* aRequestingLocation,
                              nsISupports* aRequestingContext,
                              const nsACString& aMimeGuess,
                              nsISupports* aExtra,
                              nsIPrincipal* aRequestPrincipal,
                              int16_t* aDecision);
-  static void AccumulateMixedContentHSTS(nsIURI* aURI, bool aActive);
+  static void AccumulateMixedContentHSTS(nsIURI* aURI,
+                                         bool aActive,
+                                         bool aHasHSTSPriming);
+  /* If the document associated with aRequestingContext requires priming for
+   * aURI, propagate that to the LoadInfo so the HttpChannel will find out about
+   * it.
+   *
+   * @param aURI The URI associated with the load
+   * @param aRequestingContext the requesting context passed to ShouldLoad
+   * @param aLoadInfo the LoadInfo for the load
+   */
+  static nsresult MarkLoadInfoForPriming(nsIURI* aURI,
+                                         nsISupports* aRequestingContext,
+                                         nsILoadInfo* aLoadInfo);
+
+  /* Given a context, return whether HSTS was marked on the document associated
+   * with the load for the given URI. This is used by MarkLoadInfoForPriming and
+   * directly by the image loader to determine whether to allow a load to occur
+   * from the cache.
+   *
+   * @param aURI The URI associated with the load
+   * @param aRequestingContext the requesting context passed to ShouldLoad
+   * @param aSendPrimingRequest out true if priming is required on the channel
+   * @param aMixedContentWouldBlock out true if mixed content would block
+   */
+  static nsresult GetHSTSPrimingFromRequestingContext(nsIURI* aURI,
+                                                      nsISupports* aRequestingContext,
+                                                      bool* aSendPrimingRequest,
+                                                      bool* aMixedContentWouldBlock);
+
+
   static bool sBlockMixedScript;
   static bool sBlockMixedDisplay;
+  static bool sUseHSTS;
+  static bool sSendHSTSPriming;
 };
 
 #endif /* nsMixedContentBlocker_h___ */
new file mode 100644
--- /dev/null
+++ b/dom/security/test/hsts/browser.ini
@@ -0,0 +1,9 @@
+[DEFAULT]
+support-files =
+  file_priming-top.html
+  file_testserver.sjs
+  file_1x1.png
+  file_priming.js
+  file_stylesheet.css
+
+[browser_hsts-priming_main.js]
new file mode 100644
--- /dev/null
+++ b/dom/security/test/hsts/browser_hsts-priming_main.js
@@ -0,0 +1,296 @@
+/*
+ * Description of the test:
+ *   Check that HSTS priming occurs correctly with mixed content
+ *
+ *   This test uses three hostnames, each of which treats an HSTS priming
+ *   request differently.
+ *   * no-ssl never returns an ssl response
+ *   * reject-upgrade returns an ssl response, but with no STS header
+ *   * prime-hsts returns an ssl response with the appropriate STS header
+ *
+ *   For each server, test that it response appropriately when the we allow
+ *   or block active or display content, as well as when we send an hsts priming
+ *   request, but do not change the order of mixed-content and HSTS.
+ *
+ *   This test uses http-on-examine-response, so must be run in browser context.
+ */
+'use strict';
+
+var TOP_URI = "https://example.com/browser/dom/security/test/hsts/file_priming-top.html";
+
+var test_servers = {
+  // a test server that does not support TLS
+  'no-ssl': {
+    host: 'example.co.jp',
+    response: false,
+    id: 'no-ssl',
+  },
+  // a test server which does not support STS upgrade
+  'reject-upgrade': {
+    host: 'example.org',
+    response: true,
+    id: 'reject-upgrade',
+  },
+  // a test server when sends an STS header when priming
+  'prime-hsts': {
+    host: 'test1.example.com',
+    response: true,
+    id: 'prime-hsts'
+  },
+};
+// The number of priming responses we expect to see
+var priming_count = 2;
+
+var test_settings = {
+  // mixed active content is allowed, priming will upgrade
+  allow_active: {
+    block_active: false,
+    block_display: false,
+    use_hsts: true,
+    send_hsts_priming: true,
+    type: 'script',
+    result: {
+      'no-ssl': 'insecure',
+      'reject-upgrade': 'insecure',
+      'prime-hsts': 'secure',
+    },
+  },
+  // mixed active content is blocked, priming will upgrade
+  block_active: {
+    block_active: true,
+    block_display: false,
+    use_hsts: true,
+    send_hsts_priming: true,
+    type: 'script',
+    result: {
+      'no-ssl': 'blocked',
+      'reject-upgrade': 'blocked',
+      'prime-hsts': 'secure',
+    },
+  },
+  // keep the original order of mixed-content and HSTS, but send
+  // priming requests
+  hsts_after_mixed: {
+    block_active: true,
+    block_display: false,
+    use_hsts: false,
+    send_hsts_priming: true,
+    type: 'script',
+    result: {
+      'no-ssl': 'blocked',
+      'reject-upgrade': 'blocked',
+      'prime-hsts': 'blocked',
+    },
+  },
+  // mixed display content is allowed, priming will upgrade
+  allow_display: {
+    block_active: true,
+    block_display: false,
+    use_hsts: true,
+    send_hsts_priming: true,
+    type: 'img',
+    result: {
+      'no-ssl': 'insecure',
+      'reject-upgrade': 'insecure',
+      'prime-hsts': 'secure',
+    },
+  },
+  // mixed display content is blocked, priming will upgrade
+  block_display: {
+    block_active: true,
+    block_display: true,
+    use_hsts: true,
+    send_hsts_priming: true,
+    type: 'img',
+    result: {
+      'no-ssl': 'blocked',
+      'reject-upgrade': 'blocked',
+      'prime-hsts': 'secure',
+    },
+  },
+  // mixed active content is blocked, priming will upgrade (css)
+  block_active_css: {
+    block_active: true,
+    block_display: false,
+    use_hsts: true,
+    send_hsts_priming: true,
+    type: 'css',
+    result: {
+      'no-ssl': 'blocked',
+      'reject-upgrade': 'blocked',
+      'prime-hsts': 'secure',
+    },
+  },
+  // mixed active content is blocked, priming will upgrade
+  // redirect to the same host
+  block_active_with_redir_same: {
+    block_active: true,
+    block_display: false,
+    use_hsts: true,
+    send_hsts_priming: true,
+    type: 'script',
+    redir: 'same',
+    result: {
+      'no-ssl': 'blocked',
+      'reject-upgrade': 'blocked',
+      'prime-hsts': 'secure',
+    },
+  },
+}
+// track which test we are on
+var which_test = "";
+
+const Observer = {
+  observe: function (subject, topic, data) {
+    switch (topic) {
+      case 'console-api-log-event':
+        return Observer.console_api_log_event(subject, topic, data);
+      case 'http-on-examine-response':
+        return Observer.http_on_examine_response(subject, topic, data);
+    }
+    throw "Can't handle topic "+topic;
+  },
+  // When a load is blocked which results in an error event within a page, the
+  // test logs to the console.
+  console_api_log_event: function (subject, topic, data) {
+    var message = subject.wrappedJSObject.arguments[0];
+    // when we are blocked, this will match the message we sent to the console,
+    // ignore everything else.
+    var re = RegExp(/^HSTS_PRIMING: Blocked ([-\w]+).*$/);
+    if (!re.test(message)) {
+      return;
+    }
+
+    let id = message.replace(re, '$1');
+    let curTest =test_servers[id];
+
+    if (!curTest) {
+      ok(false, "HSTS priming got a console message blocked, "+
+          "but doesn't match expectations "+id+" (msg="+message);
+      return;
+    }
+
+    is("blocked", test_settings[which_test].result[curTest.id], "HSTS priming "+
+        which_test+":"+curTest.id+" expected "+
+        test_settings[which_test].result[curTest.id]+", got blocked");
+    test_settings[which_test].finished[curTest.id] = "blocked";
+  },
+  // When we see a response come back, peek at the response and test it is secure
+  // or insecure as needed. Addtionally, watch the response for priming requests.
+  http_on_examine_response: function (subject, topic, data) {
+    let curTest = null;
+    let channel = subject.QueryInterface(Ci.nsIHttpChannel);
+    for (let item in test_servers) {
+      let re = RegExp('https?://'+test_servers[item].host);
+      if (re.test(channel.URI.asciiSpec)) {
+        curTest = test_servers[item];
+        break;
+      }
+    }
+
+    if (!curTest) {
+      return;
+    }
+
+    let result = (channel.URI.asciiSpec.startsWith('https:')) ? "secure" : "insecure";
+
+    // This is a priming request, go ahead and validate we were supposed to see
+    // a response from the server
+    if (channel.requestMethod == 'HEAD') {
+      is(true, curTest.response, "HSTS priming response found " + curTest.id);
+      test_settings[which_test].priming[curTest.id] = true;
+      return;
+    }
+
+    // This is the response to our query, make sure it matches
+    is(result, test_settings[which_test].result[curTest.id],
+        "HSTS priming result " + which_test + ":" + curTest.id);
+    test_settings[which_test].finished[curTest.id] = result;
+  },
+};
+
+// opens `uri' in a new tab and focuses it.
+// returns the newly opened tab
+function openTab(uri) {
+  let tab = gBrowser.addTab(uri);
+
+  // select tab and make sure its browser is focused
+  gBrowser.selectedTab = tab;
+  tab.ownerDocument.defaultView.focus();
+
+  return tab;
+}
+
+function clear_sts_data() {
+  for (let test in test_servers) {
+    SpecialPowers.cleanUpSTSData('http://'+test_servers[test].host);
+  }
+}
+
+function do_cleanup() {
+  clear_sts_data();
+
+  Services.obs.removeObserver(Observer, "console-api-log-event");
+  Services.obs.removeObserver(Observer, "http-on-examine-response");
+}
+
+function SetupPrefTestEnvironment(which) {
+  which_test = which;
+  clear_sts_data();
+
+  var settings = test_settings[which];
+  // priming counts how many priming requests we saw
+  settings.priming = {};
+  // priming counts how many tests were finished
+  settings.finished= {};
+
+  SpecialPowers.pushPrefEnv({'set': [["security.mixed_content.block_active_content",
+                                      settings.block_active],
+                                     ["security.mixed_content.block_display_content",
+                                      settings.block_display],
+                                     ["security.mixed_content.use_hsts",
+                                      settings.use_hsts],
+                                     ["security.mixed_content.send_hsts_priming",
+                                      settings.send_hsts_priming]]});
+}
+
+// make the top-level test uri
+function build_test_uri(base_uri, host, test_id, type) {
+  return base_uri +
+          "?host=" + escape(host) +
+          "&id=" + escape(test_id) +
+          "&type=" + escape(type);
+}
+
+// open a new tab, load the test, and wait for it to finish
+function execute_test(test, mimetype) {
+  var src = build_test_uri(TOP_URI, test_servers[test].host,
+      test, test_settings[which_test].type);
+
+  let tab = openTab(src);
+  test_servers[test]['tab'] = tab;
+
+  let browser = gBrowser.getBrowserForTab(tab);
+  yield BrowserTestUtils.browserLoaded(browser);
+
+  yield BrowserTestUtils.removeTab(tab);
+}
+
+//jscs:disable
+add_task(function*() {
+  //jscs:enable
+  Services.obs.addObserver(Observer, "console-api-log-event", false);
+  Services.obs.addObserver(Observer, "http-on-examine-response", false);
+  registerCleanupFunction(do_cleanup);
+  requestLongerTimeout(4);
+
+  for (let which of Object.keys(test_settings)) {
+    SetupPrefTestEnvironment(which);
+
+    for (let server of Object.keys(test_servers)) {
+      yield execute_test(server, test_settings[which].mimetype);
+    }
+
+    SpecialPowers.popPrefEnv();
+  }
+});
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..1ba31ba1a62313908f41f844a9fb2e74663a4cd2
GIT binary patch
literal 17811
zc%1E<cT`i^x5p2?D5xkXWh4d_P*NxfNrWH}qzI@q6GR~-7l@Jsk_e%rG72K%fQq7G
zd4pg<aR57D0THEIU_d|_3quhEM7sQf=!k-|T<iVbKX2W$*3He`=Y01kcb~ma&dpkh
zTkB}8Ag?A508p^Cp*V@oV&jLbwCGh+xhPI_k_)h*@d21Icl;0osYe$8Ab*QxX}NZ-
zA6LNT`*8yhww9KN03O$e<qrWMtj{{=>*8Fuuz0M$>Al73*y#OLE1V?7Wn&zHRI8WP
z;{7XQ^%rOUsT?R-vVFI#n_#x&?MuL5cdXR26VzK|^M&$l1nO+%1O3~qg}pVyJvE_Y
zd7ak_^J<C19sI5og_U8qQtXN|rC08ypyL(0eYKY^$%(UD31sGB#a0v`*0;@9Ru%);
z?vKOaZPJq9#RXH(Wy*KQ@TqrlJ;Z>L6gYFo!(0*Y<N-_B2I2vGMQ}R)wG$DzNdtSD
zx03;IlL2LW0yz@Er;YGj0-V;vlK@Z$i0XlGnl$Ls0@=Nisyv`J7icW8_)RieMHY~^
z+ZW7^b^{=YNSyI2$bAXC4h(V0s!_f6t|2{xs>>D@&H!MaxYac${V3g$LT&5whX{8i
zf#2ajs{DYL7~*N&jJvLSw#Sr-w~>o(N%}6cc)66iPko=sZHq{+0m<cmyc}722y>)i
zc~Y;pHuW;r{WWGz<?gkE8Qm^N530{Iyxn@rt4XrO_uf+bfHa-R8p#%)l9)9Y<kq$t
zSmiF`U)Q5<Ejijy>M#EB6|JZ2&#Kayac52L<laIYK*u>}r92pQO>+u-!i)@zwW}<T
zU-$OFLvPnm6*IlfkF@RjA`r8;BgB!>3V|<XcD|5A&z485*?^P*vQ(Xo%jQDN2X&E7
zX9x-9$+8+9e^%^u<bXKa(vEJ=k!CRjh|ac2uIKnhpO6O>!kNG{@h7$*_qsMNM-dFk
zu56dtW^vH?LNfsGR*G%)+KyhZU6;Del(0ixC^mmPVkUBtN|F~@7BQE%QaYYaJipCC
zXJ=OS+y`_sjqRHW(%L8FD(*;K6*qo?Z`f6GA|_I(VEImN)f~x1NI}fDzmcnAiQe%9
zEsY`>ISb@Izr}L4<_5FnPOUts)xOXBcS&7qMI`S2#rvwS#CZy>77LO2_Ypk{2X}kx
zC_tyAGo?rhuTMn!5!B)<EDfuy{K)RnoK+92>~a;^6wC?Zs^Ltfhy+DO^g4YNpFMR9
z4LuNFlypg8zcZ_7cec}crE?|j*@_+L(?v~Qo&k~?5+(QQUfv8id6jS!T#>z^bVY9c
z-gEbGf1#T@{|XJXys9ZAZPMh%_ZRiPiyN3Z;vK%IV4ml%W(kFR3KP3ZqL8y?^|mj$
zeQdU!4U#$M;2iv%4@zpX>=?9#CjF4j;x;9ATvL$^^0<z+ic;|0_SlxV;Mmck%Eik{
zV_(_JFI|$n`1icuogS@oTHdPIx`eg=yg6PwEJ>@>qQ|L6rH9U&F{pCwxQSi$?^LIo
zx|jCrJy5e#O;6(;?uqHyW|yA2)-*Hc46<$WX4O7I-?F@vp<hp1<(!nNdh=*rZh5X&
zF23HfUWRmxW8U&8xs^BM6RI$Z99137QL`*JPsmv6vLW5=WKCv}_1i2Gt<j@#O+-Ox
zLFkzw+rABLT%-GHDr#bC;Ry%Sl+}EVLXCFfR8t*Oe@i8%9x+PXyy>w<6>>ApPl_d%
zC!edx+W4f6;Wg8F6|lTTEg_U$E&JP7>Ci#@g@*C8GLw59Ohwif)|%SAwVO_DJQ?A*
zq4W+_MQ`Eividuu+igA($Gow{T)X=H3C?X94TlX*8M^K|mwej!_s1vro-OCl8;os?
zHv}KyBzHQS9!*PV^3N|09e$5)&p-U&XdBKIo9EXScP8=ux(M}Azfovt*L!Z;EcFFy
zF$-)Ig48{XgpB(Juhe{0JF)&a4I}RbgA3yJ1T8pb99mrCGkmGI!{Eb`5Bn|^1ZG~k
zGjJj>qdPM$-S%jyd$M$L#_4&^ds)|86(0vZUSz11bJ*u$`R4M4hm*^RZw|k?QMRJY
zT2Bw9q=%;((PmQHs6BeUs4b}D)atD8EESrodt=U0D*t>=R=@l6%jRqkwgG#YKd0{0
zWzJ>fv;4~&gM=$)t%%#0zA@3Xys460Sx{3@yY2Pt=Iol2rOj`Ra#*F0FFUj!`f#`U
z>ADW#pWT;|4>})u?4F^L(O-eD5O0xh2}27~yi#1z<>;={&m7krDc}xCtl!ygU#oE8
z?S<!!4UJ4rA7@9WPp58YSVI`W-f^Dq+?KO4O?RI&4wTRmF=?DMUgg}hsF1oaDD0O$
zwL^5G7W-baYDCl0rtq#EO?H}>*X*to(@ej33G@8QqkdxT{^hUS0zHRl!_eT<!Ti49
z_bG$NKWNImn@OEhr4lQ(Q&L4LNSY;8EOkJ}bOv6oLiU2fLis=in#v=U-Ln*CHLBF#
zHGCl_#N9lJ?ax-Gr`%yR>o>n{uGI1uRS?Rs`Y_$_N@cjg!|;PlH|NEa9^@5ka>5^b
z+~@T@^S{%PS*M)iNm*fqN$iZJ7wN?k)p~Vn%z{>Tyd<0>Sb2oq3>|40NFDes=2lFS
zB=lEqk#d}I()%QeZFy<YtAST%Uo{iW`9T$}$J<F)0yzC94?|YJ*?8olv8TycgLt3R
zh?KVTJ6$<lYv+P)t-%8$%`ThT(w;0bkM>G=k+*Mo!y*m)UdQFOVTU`9ZAv2VYd&_#
zl!1M0oMY6RptI5i9h#z?nrF6U@d|@sn-7Nw<_-HSAJ~=>wW^bttI;SUkE=|2$`dzt
z9anUZM=rm5pPq-?f%sPy57`F|Upoyt{CdDU|CDj1_C;h@?_UGO+9`hM-}J6sd*ai2
zFCEjnqc@7SbHh?<O+kZjF!go*@)0);kGS*p8|w-l2wZQU{rGgzxpTE^YnT@YBeGuB
zC*^6@E;+wN7pp_vSapTWj9{O1KY2decSr5cQM2YqrO_wXs-c&i31+)vtZL(biC-OG
z$X4>d{xRoqC1jS!c6{D&t(g6=AS)&-DXZ~W_!ZX_zk{z*-m{Ni%MX+Zv##EoxMF_C
zO*c}$U<~~LU5tik6k0DQr*T7Y)bdKbLeFN;>TK(%%6q0-VZVlTb-e66_nezuC>*Pm
zI3+Plai@m=`P@uBQwm{!@ZZJF19yKZ42V2)=Fz!j7ltA!JPPfXjS*+=<)40&`LP<6
z=a+RVD4WA+e9PEYlG^^ZeHK1jZFl0`z~b)pg{MdABakDMVcUw*A?G(4ACCGQMQ^64
zrQj8D5MK94a_WtWm}4w%_h#bgk-J>$3bo@|gp3Hz>gDNqC$@Hke;jPavz{H9p*nx4
zetxc5!69LHQ+izr3A4+yAau#VgJQ+Py}j%6FQ(<c33*$#wW9B|I>%Uzd%ekI^hSGE
z#)?LgI``W1%>{1*Z=`QowZ8qA=PTxSN;l>65`(+$jnevWTz`38^L^Rdb>!LP1wx(3
zfzEYJ|5IIZ?)Z!2^Gu%#-G}VQXs<PEqKtGJ_qT*fgmKA<As?@<sp=onyf!va<N0p;
z$5Zdqs~@I{wQS4TaeK!V&+?F`W9~zt?;V<tr$%*#Z0Y;(#IvE0qqsz|uP`}ZJ*=>G
zN9Qgr&5S62&An=oA9PzD|7}z4+gSP3li*1zSQ;Kdygs1by`e6+y$fY_C(1<ip{jav
z4-^i-EdU(W$Oxk(qthgBT~%1QPQB|s0MUE;L!-MDd!C2fw3YzT>MKnFg(v3m3^?+{
z%ASG0*<hVVG!=aUjXvArnS7-4FOyCyw)m}-y$KOE0O2kX-leLp&Q?<&-Xs-mD(*LI
z)Ko48jM{+Weu8uirNMoyrERR^{+ML<YdM*-gfv?KNNAP|RX}yHCo#BeG@Z%yhR{NG
z02ps3X2JkEV+$lectgG{jw!P1Mj;ZxVwxh?;~cOK0hW*-%O;cuIfpvBFhaL5h)kpz
zS>8lQ5;<T)0y;v-_UG_PLQ~`?za-K7aWe*q_%uYY#T02i-hrSxtVLLIc@P4J#-kYe
z`uYfCB3d6uFf_pFAq=qkI1HAE(Z`~&`XqfU$pDM^+>m5>(W?oM=|gg&ton=-U6~^N
z1cCq(1``|{j1D$Lb9ufPeIk*F!5Ux;3{avGD1Hb>Ko_Doe60y5U;I!YK7+>!5U{u$
z#P~Jco4ZwDibTpICbiG^#SZuf#NmIAohXVJAw2-2kH%ttAaZc{cNv>Kt&%UW3KIOg
zlJ61{0AZXUK6fjR0a*n>9D&w!u9%E}`~+;}`G1NJlYxQ!A+|`u7x~hk_9Fy*dE}Y?
zWBk+o!@C!-e0-+Yj`#df1_@a|kd60D$jD!HCZZsdEO`)Jz~#Acx&GvdEc^PvCVE~Q
z&nkqD1D(O*j2m@LFyD)&^9NBx2U(=Wqp(IOEY3wAPr~9!1~?Sfn1sbnaxyXUJ0A|B
z>}S#i^q=wa&Dih#aG5Nhke~AMb>z1`92`it9KL|gVL-MNvdDmDv6v)?Kp@hYI6BG*
zkHw>m4C&q|f;SzD^7i&-;vlTP4}r<}<oH|p6xS3kW9xV-iTWl=pUGv2-2amxB)m6W
zU*FgWhcXnEJ4*DHh$1rSL=gxFG4+`^L&%W+1vJUo6wn$TOSCf4{U?nXzZa$m=7YuI
zaX!WdC??UHi83-WGDLafm_{g^D6DiWUW7C-{DPRq%@mXki!Vxz5b`(q`Dx8?h62B}
ze(CUMeOf~T=sZ3&UYe%JFSUuBA{f_vDsd8>F~0hc8K2UKX@dF2`ePYu>U92G2&Tw?
z%n0^_IRBP%lScj637_jD2&VHOb6-)Uf37yC6HbXZW+LSO2UEeP2mS9R;^*q>|H(vr
z1u*>R9AAh@#(WWeG5p-zO<2FJt}hikwHi&B43ZC*$EFL&EH>R2!US-9O)y`LlSY1B
zhb+1NT%Kr=gvg>on9e+{%GT0y?ZoeWWD1KP;7<>kES}UmNj?4rH@?n@B1Zp|K>8+_
zDYoy&Q^%9kf9t0{V*{+HiMB!5@d=*N{k@d-?^4>oNWXTua##Yg!Dq>L=1HpYZ{dlU
zIg2)o5E+ld8RASZ-<!WztYrxye=3C~T5<WGBQmXhS{>~NI@%Q7L>f)n*iGyjF`u`M
zq8%&dpDpWj$^74MFa(Cc5EueOU<eF>Aut4nzz`S$LtqFDfgvyihQJUQ0z+U341pmq
z1cty67y?6J2n>NCFa(Cc5EueOU<eF>Aut4nzz`S$Ltw~%86yAnPoNNoX^IR+nvwhS
zcIf~B5DXh92LL$Q0PrsYF_|0yLb3o@qXfj{AONs`05Ip?20l$+G<L2n#oR?$?>#U!
hW+oxJ9vhPYvLoUjAE$4nkN?%w*2<A`!NP0%e*nP=bK(F1
new file mode 100644
--- /dev/null
+++ b/dom/security/test/hsts/file_priming-top.html
@@ -0,0 +1,84 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Bug 1246540</title>
+  <meta http-equiv='content-type' content="text/html;charset=utf-8" />
+</head>
+<body>
+  <p id="display"></p>
+  <div id="content" style="visibility: hidden">
+  </div>
+
+<script type="text/javascript">
+/*
+ * Description of the test:
+ * Attempt to load an insecure resource. If the resource responds to HSTS
+ * priming with an STS header, the load should continue securely.
+ * If it does not, the load should continue be blocked or continue insecurely.
+ */
+
+function parse_query_string() {
+  var q = {};
+  document.location.search.substr(1).
+    split('&').forEach(function (item, idx, ar) {
+      let [k, v] = item.split('=');
+      q[k] = unescape(v);
+    });
+  return q;
+}
+
+var args = parse_query_string();
+
+var subresources = {
+  css: { mimetype: 'text/css', file: 'file_stylesheet.css' },
+  img: { mimetype: 'image/png', file: 'file_1x1.png' },
+  script: { mimetype: 'text/javascript', file: 'file_priming.js' },
+};
+
+function handler(ev) {
+  console.log("HSTS_PRIMING: Blocked "+args.id);
+}
+
+function loadCss(src) {
+  let head = document.getElementsByTagName("head")[0];
+  let link = document.createElement("link");
+  link.setAttribute("rel", "stylesheet");
+  link.setAttribute("type", subresources[args.type].mimetype);
+  link.setAttribute("href", src);
+  head.appendChild(link);
+}
+
+function loadResource(src) {
+  let content = document.getElementById("content");
+  let testElem = document.createElement(args.type);
+  testElem.setAttribute("id", args.id);
+  testElem.setAttribute("charset", "UTF-8");
+  testElem.onerror = handler;
+  content.appendChild(testElem);
+  testElem.src = src;
+}
+
+function loadTest() {
+  let subresource = subresources[args.type];
+
+  let src = "http://"
+    + args.host
+    + "/browser/dom/security/test/hsts/file_testserver.sjs"
+    + "?file=" +escape("browser/dom/security/test/hsts/" + subresource.file)
+    + "&primer=" + escape(args.id)
+    + "&mimetype=" + escape(subresource.mimetype)
+    ;
+  if (args.type == 'css') {
+    loadCss(src);
+    return;
+  }
+
+  loadResource(src);
+}
+
+// start running the tests
+loadTest();
+
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/security/test/hsts/file_priming.js
@@ -0,0 +1,4 @@
+function completed() {
+  return;
+}
+completed();
new file mode 100644
new file mode 100644
--- /dev/null
+++ b/dom/security/test/hsts/file_testserver.sjs
@@ -0,0 +1,66 @@
+// SJS file for HSTS mochitests
+
+Components.utils.import("resource://gre/modules/NetUtil.jsm");
+Components.utils.importGlobalProperties(["URLSearchParams"]);
+
+function loadFromFile(path) {
+  // Load the HTML to return in the response from file.
+  // Since it's relative to the cwd of the test runner, we start there and
+  // append to get to the actual path of the file.
+  var testFile =
+    Components.classes["@mozilla.org/file/directory_service;1"].
+    getService(Components.interfaces.nsIProperties).
+    get("CurWorkD", Components.interfaces.nsILocalFile);
+  var dirs = path.split("/");
+  for (var i = 0; i < dirs.length; i++) {
+    testFile.append(dirs[i]);
+  }
+  var testFileStream =
+    Components.classes["@mozilla.org/network/file-input-stream;1"].
+    createInstance(Components.interfaces.nsIFileInputStream);
+  testFileStream.init(testFile, -1, 0, 0);
+  var test = NetUtil.readInputStreamToString(testFileStream, testFileStream.available());
+  return test;
+}
+
+function handleRequest(request, response)
+{
+  const query = new URLSearchParams(request.queryString);
+
+  redir = query.get('redir');
+  if (redir == 'same') {
+    query.delete("redir");
+    response.setStatus(302);
+    let newURI = request.uri;
+    newURI.queryString = query.serialize();
+    response.setHeader("Location", newURI.spec)
+  }
+
+  // avoid confusing cache behaviors
+  response.setHeader("Cache-Control", "no-cache", false);
+
+  // if we have a priming header, check for required behavior
+  // and set header appropriately
+  if (request.hasHeader('Upgrade-Insecure-Requests')) {
+    var expected = query.get('primer');
+    if (expected == 'prime-hsts') {
+      // set it for 5 minutes
+      response.setHeader("Strict-Transport-Security", "max-age="+(60*5), false);
+    } else if (expected == 'reject-upgrade') {
+      response.setHeader("Strict-Transport-Security", "max-age=0", false);
+    }
+    response.write('');
+    return;
+  }
+
+  var file = query.get('file');
+  if (file) {
+    var mimetype = unescape(query.get('mimetype'));
+    response.setHeader("Content-Type", mimetype, false);
+    response.write(loadFromFile(unescape(file)));
+    return;
+  }
+
+  response.setHeader("Content-Type", "application/json", false);
+  response.write('{}');
+}
--- a/dom/security/test/mixedcontentblocker/test_main.html
+++ b/dom/security/test/mixedcontentblocker/test_main.html
@@ -157,16 +157,19 @@ https://bugzilla.mozilla.org/show_bug.cg
         testsToRun["imageLeavePicture"] = true;
         break;
 
     }
     checkTestsCompleted();
   }
 
   function startTest() {
+    // Set prefs to use mixed-content before HSTS
+    SpecialPowers.pushPrefEnv({'set': [["security.mixed_content.use_hsts", false],
+                                       ["security.mixed_content.send_hsts_priming", false]]});
     //Set the first set of mixed content settings and increment the counter.
     //Enable <picture> and <img srcset> for the test.
     changePrefs([[ "dom.image.srcset.enabled", true ], [ "dom.image.picture.enabled", true ]],
       function() {
         //listen for a messages from the mixed content test harness
         window.addEventListener("message", receiveMessage, false);
 
         //Kick off test
--- a/dom/security/test/moz.build
+++ b/dom/security/test/moz.build
@@ -22,9 +22,10 @@ MOCHITEST_MANIFESTS += [
 
 MOCHITEST_CHROME_MANIFESTS += [
     'csp/chrome.ini',
 ]
 
 BROWSER_CHROME_MANIFESTS += [
     'contentverifier/browser.ini',
     'csp/browser.ini',
+    'hsts/browser.ini',
 ]
--- a/image/imgLoader.cpp
+++ b/image/imgLoader.cpp
@@ -602,16 +602,29 @@ ShouldLoadCachedImage(imgRequest* aImgRe
                                              aLoadingPrincipal,
                                              &decision);
       if (NS_FAILED(rv) || !NS_CP_ACCEPTED(decision)) {
         return false;
       }
     }
   }
 
+  bool sendPriming = false;
+  bool mixedContentWouldBlock = false;
+  rv = nsMixedContentBlocker::GetHSTSPrimingFromRequestingContext(contentLocation,
+      aLoadingContext, &sendPriming, &mixedContentWouldBlock);
+  if (NS_FAILED(rv)) {
+    return false;
+  }
+  if (sendPriming && mixedContentWouldBlock) {
+    // if either of the securty checks above would cause a priming request, we
+    // can't load this image from the cache, so go ahead and return false here
+    return false;
+  }
+
   return true;
 }
 
 // Returns true if this request is compatible with the given CORS mode on the
 // given loading principal, and false if the request may not be reused due
 // to CORS.  Also checks the Referrer Policy, since requests with different
 // referrers/policies may generate different responses.
 static bool
--- a/ipc/glue/BackgroundUtils.cpp
+++ b/ipc/glue/BackgroundUtils.cpp
@@ -261,17 +261,19 @@ LoadInfoToLoadInfoArgs(nsILoadInfo *aLoa
       aLoadInfo->GetEnforceSecurity(),
       aLoadInfo->GetInitialSecurityCheckDone(),
       aLoadInfo->GetIsInThirdPartyContext(),
       aLoadInfo->GetOriginAttributes(),
       redirectChainIncludingInternalRedirects,
       redirectChain,
       aLoadInfo->CorsUnsafeHeaders(),
       aLoadInfo->GetForcePreflight(),
-      aLoadInfo->GetIsPreflight());
+      aLoadInfo->GetIsPreflight(),
+      aLoadInfo->GetForceHSTSPriming(),
+      aLoadInfo->GetMixedContentWouldBlock());
 
   return NS_OK;
 }
 
 nsresult
 LoadInfoArgsToLoadInfo(const OptionalLoadInfoArgs& aOptionalLoadInfoArgs,
                        nsILoadInfo** outLoadInfo)
 {
@@ -333,16 +335,19 @@ LoadInfoArgsToLoadInfo(const OptionalLoa
                           loadInfoArgs.enforceSecurity(),
                           loadInfoArgs.initialSecurityCheckDone(),
                           loadInfoArgs.isInThirdPartyContext(),
                           loadInfoArgs.originAttributes(),
                           redirectChainIncludingInternalRedirects,
                           redirectChain,
                           loadInfoArgs.corsUnsafeHeaders(),
                           loadInfoArgs.forcePreflight(),
-                          loadInfoArgs.isPreflight());
+                          loadInfoArgs.isPreflight(),
+                          loadInfoArgs.forceHSTSPriming(),
+                          loadInfoArgs.mixedContentWouldBlock()
+                          );
 
    loadInfo.forget(outLoadInfo);
    return NS_OK;
 }
 
 } // namespace ipc
 } // namespace mozilla
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -5551,8 +5551,22 @@ pref("dom.webkitBlink.dirPicker.enabled"
 pref("dom.webkitBlink.filesystem.enabled", true);
 #endif
 
 pref("media.block-autoplay-until-in-foreground", true);
 #ifdef MOZ_STYLO
 // Is the Servo-backed style system enabled?
 pref("layout.css.servo.enabled", true);
 #endif
+
+// HSTS Priming
+// If a request is mixed-content, send an HSTS priming request to attempt to
+// see if it is available over HTTPS.
+pref("security.mixed_content.send_hsts_priming", true);
+#ifdef RELEASE_BUILD
+// Don't change the order of evaluation of mixed-content and HSTS upgrades in
+// order to be most compatible with current standards
+pref("security.mixed_content.use_hsts", false);
+#else
+// Change the order of evaluation so HSTS upgrades happen before
+// mixed-content blocking
+pref("security.mixed_content.use_hsts", true);
+#endif
--- a/netwerk/base/LoadInfo.cpp
+++ b/netwerk/base/LoadInfo.cpp
@@ -57,16 +57,18 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadin
   , mOuterWindowID(0)
   , mParentOuterWindowID(0)
   , mFrameOuterWindowID(0)
   , mEnforceSecurity(false)
   , mInitialSecurityCheckDone(false)
   , mIsThirdPartyContext(false)
   , mForcePreflight(false)
   , mIsPreflight(false)
+  , mForceHSTSPriming(false)
+  , mMixedContentWouldBlock(false)
 {
   MOZ_ASSERT(mLoadingPrincipal);
   MOZ_ASSERT(mTriggeringPrincipal);
   MOZ_ASSERT(mPrincipalToInherit);
 
 #ifdef DEBUG
   // TYPE_DOCUMENT loads initiated by javascript tests will go through
   // nsIOService and use the wrong constructor.  Don't enforce the
@@ -228,16 +230,18 @@ LoadInfo::LoadInfo(nsPIDOMWindowOuter* a
   , mOuterWindowID(0)
   , mParentOuterWindowID(0)
   , mFrameOuterWindowID(0)
   , mEnforceSecurity(false)
   , mInitialSecurityCheckDone(false)
   , mIsThirdPartyContext(false) // NB: TYPE_DOCUMENT implies not third-party.
   , mForcePreflight(false)
   , mIsPreflight(false)
+  , mForceHSTSPriming(false)
+  , mMixedContentWouldBlock(false)
 {
   // Top-level loads are never third-party
   // Grab the information we can out of the window.
   MOZ_ASSERT(aOuterWindow);
   MOZ_ASSERT(mTriggeringPrincipal);
   MOZ_ASSERT(mPrincipalToInherit);
 
   // if the load is sandboxed, we can not also inherit the principal
@@ -292,16 +296,18 @@ LoadInfo::LoadInfo(const LoadInfo& rhs)
   , mIsThirdPartyContext(rhs.mIsThirdPartyContext)
   , mOriginAttributes(rhs.mOriginAttributes)
   , mRedirectChainIncludingInternalRedirects(
       rhs.mRedirectChainIncludingInternalRedirects)
   , mRedirectChain(rhs.mRedirectChain)
   , mCorsUnsafeHeaders(rhs.mCorsUnsafeHeaders)
   , mForcePreflight(rhs.mForcePreflight)
   , mIsPreflight(rhs.mIsPreflight)
+  , mForceHSTSPriming(rhs.mForceHSTSPriming)
+  , mMixedContentWouldBlock(rhs.mMixedContentWouldBlock)
 {
 }
 
 LoadInfo::LoadInfo(nsIPrincipal* aLoadingPrincipal,
                    nsIPrincipal* aTriggeringPrincipal,
                    nsIPrincipal* aPrincipalToInherit,
                    nsSecurityFlags aSecurityFlags,
                    nsContentPolicyType aContentPolicyType,
@@ -317,17 +323,19 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadin
                    bool aEnforceSecurity,
                    bool aInitialSecurityCheckDone,
                    bool aIsThirdPartyContext,
                    const NeckoOriginAttributes& aOriginAttributes,
                    nsTArray<nsCOMPtr<nsIPrincipal>>& aRedirectChainIncludingInternalRedirects,
                    nsTArray<nsCOMPtr<nsIPrincipal>>& aRedirectChain,
                    const nsTArray<nsCString>& aCorsUnsafeHeaders,
                    bool aForcePreflight,
-                   bool aIsPreflight)
+                   bool aIsPreflight,
+                   bool aForceHSTSPriming,
+                   bool aMixedContentWouldBlock)
   : mLoadingPrincipal(aLoadingPrincipal)
   , mTriggeringPrincipal(aTriggeringPrincipal)
   , mPrincipalToInherit(aPrincipalToInherit)
   , mSecurityFlags(aSecurityFlags)
   , mInternalContentPolicyType(aContentPolicyType)
   , mTainting(aTainting)
   , mUpgradeInsecureRequests(aUpgradeInsecureRequests)
   , mVerifySignedContent(aVerifySignedContent)
@@ -339,16 +347,18 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadin
   , mFrameOuterWindowID(aFrameOuterWindowID)
   , mEnforceSecurity(aEnforceSecurity)
   , mInitialSecurityCheckDone(aInitialSecurityCheckDone)
   , mIsThirdPartyContext(aIsThirdPartyContext)
   , mOriginAttributes(aOriginAttributes)
   , mCorsUnsafeHeaders(aCorsUnsafeHeaders)
   , mForcePreflight(aForcePreflight)
   , mIsPreflight(aIsPreflight)
+  , mForceHSTSPriming (aForceHSTSPriming)
+  , mMixedContentWouldBlock(aMixedContentWouldBlock)
 {
   // Only top level TYPE_DOCUMENT loads can have a null loadingPrincipal
   MOZ_ASSERT(mLoadingPrincipal || aContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT);
   MOZ_ASSERT(mTriggeringPrincipal);
   MOZ_ASSERT(mPrincipalToInherit);
 
   mRedirectChainIncludingInternalRedirects.SwapElements(
     aRedirectChainIncludingInternalRedirects);
@@ -829,16 +839,44 @@ LoadInfo::SetIsPreflight()
 NS_IMETHODIMP
 LoadInfo::GetIsPreflight(bool* aIsPreflight)
 {
   *aIsPreflight = mIsPreflight;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+LoadInfo::GetForceHSTSPriming(bool* aForceHSTSPriming)
+{
+  *aForceHSTSPriming = mForceHSTSPriming;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+LoadInfo::GetMixedContentWouldBlock(bool *aMixedContentWouldBlock)
+{
+  *aMixedContentWouldBlock = mMixedContentWouldBlock;
+  return NS_OK;
+}
+
+void
+LoadInfo::SetHSTSPriming(bool aMixedContentWouldBlock)
+{
+  mForceHSTSPriming = true;
+  mMixedContentWouldBlock = aMixedContentWouldBlock;
+}
+
+void
+LoadInfo::ClearHSTSPriming()
+{
+  mForceHSTSPriming = false;
+  mMixedContentWouldBlock = false;
+}
+
+NS_IMETHODIMP
 LoadInfo::GetTainting(uint32_t* aTaintingOut)
 {
   MOZ_ASSERT(aTaintingOut);
   *aTaintingOut = static_cast<uint32_t>(mTainting);
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/netwerk/base/LoadInfo.h
+++ b/netwerk/base/LoadInfo.h
@@ -101,17 +101,19 @@ private:
            bool aEnforceSecurity,
            bool aInitialSecurityCheckDone,
            bool aIsThirdPartyRequest,
            const NeckoOriginAttributes& aOriginAttributes,
            nsTArray<nsCOMPtr<nsIPrincipal>>& aRedirectChainIncludingInternalRedirects,
            nsTArray<nsCOMPtr<nsIPrincipal>>& aRedirectChain,
            const nsTArray<nsCString>& aUnsafeHeaders,
            bool aForcePreflight,
-           bool aIsPreflight);
+           bool aIsPreflight,
+           bool aForceHSTSPriming,
+           bool aMixedContentWouldBlock);
   LoadInfo(const LoadInfo& rhs);
 
   friend nsresult
   mozilla::ipc::LoadInfoArgsToLoadInfo(
     const mozilla::net::OptionalLoadInfoArgs& aLoadInfoArgs,
     nsILoadInfo** outLoadInfo);
 
   ~LoadInfo();
@@ -144,15 +146,18 @@ private:
   bool                             mInitialSecurityCheckDone;
   bool                             mIsThirdPartyContext;
   NeckoOriginAttributes            mOriginAttributes;
   nsTArray<nsCOMPtr<nsIPrincipal>> mRedirectChainIncludingInternalRedirects;
   nsTArray<nsCOMPtr<nsIPrincipal>> mRedirectChain;
   nsTArray<nsCString>              mCorsUnsafeHeaders;
   bool                             mForcePreflight;
   bool                             mIsPreflight;
+
+  bool                             mForceHSTSPriming : 1;
+  bool                             mMixedContentWouldBlock : 1;
 };
 
 } // namespace net
 } // namespace mozilla
 
 #endif // mozilla_LoadInfo_h
 
--- a/netwerk/base/nsILoadInfo.idl
+++ b/netwerk/base/nsILoadInfo.idl
@@ -597,16 +597,42 @@ interface nsILoadInfo : nsISupports
   [infallible] readonly attribute boolean forcePreflight;
 
   /**
    * A C++ friendly getter for the forcePreflight flag.
    */
   [infallible] readonly attribute boolean isPreflight;
 
   /**
+   * When this request would be mixed-content and we do not have an
+   * entry in the HSTS cache, we send an HSTS priming request to
+   * determine if it is ok to upgrade the request to HTTPS.
+   */
+  /**
+   * True if this is a mixed-content load and HSTS priming request will be sent.
+   */
+  [noscript, infallible] readonly attribute boolean forceHSTSPriming;
+  /**
+   * Carry the decision whether this load would be blocked by mixed content so
+   * that if HSTS priming fails, the correct decision can be made.
+   */
+  [noscript, infallible] readonly attribute boolean mixedContentWouldBlock;
+
+  /**
+   * Mark this LoadInfo as needing HSTS Priming
+   *
+   * @param wouldBlock Carry the decision of Mixed Content Blocking to be
+   * applied when HSTS priming is complete.
+   */
+  [noscript, notxpcom, nostdcall]
+  void setHSTSPriming(in boolean mixeContentWouldBlock);
+  [noscript, notxpcom, nostdcall]
+  void clearHSTSPriming();
+
+  /**
   * Constants reflecting the channel tainting.  These are mainly defined here
   * for script.  Internal C++ code should use the enum defined in LoadTainting.h.
   * See LoadTainting.h for documentation.
   */
   const unsigned long TAINTING_BASIC = 0;
   const unsigned long TAINTING_CORS = 1;
   const unsigned long TAINTING_OPAQUE = 2;
 
--- a/netwerk/base/nsNetUtil.cpp
+++ b/netwerk/base/nsNetUtil.cpp
@@ -2306,17 +2306,17 @@ NS_ShouldSecureUpgrade(nsIURI* aURI,
 
     // enforce Strict-Transport-Security
     nsISiteSecurityService* sss = gHttpHandler->GetSSService();
     NS_ENSURE_TRUE(sss, NS_ERROR_OUT_OF_MEMORY);
 
     bool isStsHost = false;
     uint32_t flags = aPrivateBrowsing ? nsISocketProvider::NO_PERMANENT_STORAGE : 0;
     rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, aURI, flags,
-                          &isStsHost);
+                          nullptr, &isStsHost);
 
     // if the SSS check fails, it's likely because this load is on a
     // malformed URI or something else in the setup is wrong, so any error
     // should be reported.
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (isStsHost) {
       LOG(("nsHttpChannel::Connect() STS permissions found\n"));
--- a/netwerk/base/security-prefs.js
+++ b/netwerk/base/security-prefs.js
@@ -93,8 +93,20 @@ pref("security.webauth.u2f_enable_usbtok
 pref("security.ssl.errorReporting.enabled", true);
 pref("security.ssl.errorReporting.url", "https://incoming.telemetry.mozilla.org/submit/sslreports/");
 pref("security.ssl.errorReporting.automatic", false);
 
 // Impose a maximum age on HPKP headers, to avoid sites getting permanently
 // blacking themselves out by setting a bad pin.  (60 days by default)
 // https://tools.ietf.org/html/rfc7469#section-4.1
 pref("security.cert_pinning.max_max_age_seconds", 5184000);
+
+// If a request is mixed-content, send an HSTS priming request to attempt to
+// see if it is available over HTTPS.
+pref("security.mixed_content.send_hsts_priming", true);
+#ifdef RELEASE_BUILD
+// Don't change the order of evaluation of mixed-content and HSTS upgrades
+pref("security.mixed_content.use_hsts", false);
+#else
+// Change the order of evaluation so HSTS upgrades happen before
+// mixed-content blocking
+pref("security.mixed_content.use_hsts", true);
+#endif
--- a/netwerk/ipc/NeckoChannelParams.ipdlh
+++ b/netwerk/ipc/NeckoChannelParams.ipdlh
@@ -46,16 +46,18 @@ struct LoadInfoArgs
   bool                  initialSecurityCheckDone;
   bool                  isInThirdPartyContext;
   NeckoOriginAttributes originAttributes;
   PrincipalInfo[]       redirectChainIncludingInternalRedirects;
   PrincipalInfo[]       redirectChain;
   nsCString[]           corsUnsafeHeaders;
   bool                  forcePreflight;
   bool                  isPreflight;
+  bool                  forceHSTSPriming;
+  bool                  mixedContentWouldBlock;
 };
 
 /**
  * Not every channel necessarily has a loadInfo attached.
  */
 union OptionalLoadInfoArgs
 {
   void_t;
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/HSTSPrimerListener.cpp
@@ -0,0 +1,258 @@
+/* -*- 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 "nsHttp.h"
+
+#include "HSTSPrimerListener.h"
+#include "nsIHstsPrimingCallback.h"
+#include "nsIPrincipal.h"
+#include "nsSecurityHeaderParser.h"
+#include "nsISiteSecurityService.h"
+#include "nsISocketProvider.h"
+#include "nsISSLStatus.h"
+#include "nsISSLStatusProvider.h"
+#include "nsStreamUtils.h"
+#include "nsHttpChannel.h"
+#include "LoadInfo.h"
+
+namespace mozilla {
+namespace net {
+
+using namespace mozilla;
+
+NS_IMPL_ISUPPORTS(HSTSPrimingListener, nsIStreamListener,
+                  nsIRequestObserver, nsIInterfaceRequestor)
+
+NS_IMETHODIMP
+HSTSPrimingListener::GetInterface(const nsIID & aIID, void **aResult)
+{
+  return QueryInterface(aIID, aResult);
+}
+
+NS_IMETHODIMP
+HSTSPrimingListener::OnStartRequest(nsIRequest *aRequest,
+                                    nsISupports *aContext)
+{
+  nsresult rv = CheckHSTSPrimingRequestStatus(aRequest);
+  nsCOMPtr<nsIHstsPrimingCallback> callback(mCallback);
+  mCallback = nullptr;
+
+  if (NS_FAILED(rv)) {
+    LOG(("HSTS Priming Failed (request was not approved)"));
+    return callback->OnHSTSPrimingFailed(rv, false);
+  }
+
+  LOG(("HSTS Priming Succeeded (request was approved)"));
+  return callback->OnHSTSPrimingSucceeded(false);
+}
+
+NS_IMETHODIMP
+HSTSPrimingListener::OnStopRequest(nsIRequest *aRequest,
+                                   nsISupports *aContext,
+                                   nsresult aStatus)
+{
+  return NS_OK;
+}
+
+nsresult
+HSTSPrimingListener::CheckHSTSPrimingRequestStatus(nsIRequest* aRequest)
+{
+  nsresult status;
+  nsresult rv = aRequest->GetStatus(&status);
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (NS_FAILED(status)) {
+    return NS_ERROR_CONTENT_BLOCKED;
+  }
+
+  // Test that things worked on a HTTP level
+  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
+  NS_ENSURE_STATE(httpChannel);
+  nsCOMPtr<nsIHttpChannelInternal> internal = do_QueryInterface(aRequest);
+  NS_ENSURE_STATE(internal);
+
+  bool succeedded;
+  rv = httpChannel->GetRequestSucceeded(&succeedded);
+  if (NS_FAILED(rv) || !succeedded) {
+    // If the request did not return a 2XX response, don't process it
+    return NS_ERROR_CONTENT_BLOCKED;
+  }
+
+  bool synthesized = false;
+  nsHttpChannel* rawHttpChannel = static_cast<nsHttpChannel*>(httpChannel.get());
+  rv = rawHttpChannel->GetResponseSynthesized(&synthesized);
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (synthesized) {
+    // Don't consider synthesized responses
+    return NS_ERROR_CONTENT_BLOCKED;
+  }
+
+  // check to see if the HSTS cache was updated
+  nsCOMPtr<nsISiteSecurityService> sss = do_GetService(NS_SSSERVICE_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIURI> uri;
+  rv = httpChannel->GetURI(getter_AddRefs(uri));
+  NS_ENSURE_SUCCESS(rv, rv);
+  NS_ENSURE_TRUE(uri, NS_ERROR_CONTENT_BLOCKED);
+
+  bool hsts;
+  rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, uri, 0, nullptr, &hsts);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (hsts) {
+    // An HSTS upgrade was found
+    return NS_OK;
+  }
+
+  // There is no HSTS upgrade available
+  return NS_ERROR_CONTENT_BLOCKED;
+}
+
+/** nsIStreamListener methods **/
+
+NS_IMETHODIMP
+HSTSPrimingListener::OnDataAvailable(nsIRequest *aRequest,
+                                     nsISupports *ctxt,
+                                     nsIInputStream *inStr,
+                                     uint64_t sourceOffset,
+                                     uint32_t count)
+{
+  uint32_t totalRead;
+  return inStr->ReadSegments(NS_DiscardSegment, nullptr, count, &totalRead);
+}
+
+// static
+nsresult
+HSTSPrimingListener::StartHSTSPriming(nsIChannel* aRequestChannel,
+                                      nsIHstsPrimingCallback* aCallback)
+{
+
+  nsCOMPtr<nsIURI> finalChannelURI;
+  nsresult rv = NS_GetFinalChannelURI(aRequestChannel, getter_AddRefs(finalChannelURI));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIURI> uri;
+  rv = NS_GetSecureUpgradedURI(finalChannelURI, getter_AddRefs(uri));
+  NS_ENSURE_SUCCESS(rv,rv);
+
+  // check the HSTS cache
+  bool hsts;
+  bool cached;
+  nsCOMPtr<nsISiteSecurityService> sss = do_GetService(NS_SSSERVICE_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, uri, 0, &cached, &hsts);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (hsts) {
+    // already saw this host and will upgrade if allowed by preferences
+    return aCallback->OnHSTSPrimingSucceeded(true);
+  }
+
+  if (cached) {
+    // there is a non-expired entry in the cache that doesn't allow us to
+    // upgrade, so go ahead and fail early.
+    return aCallback->OnHSTSPrimingFailed(NS_ERROR_CONTENT_BLOCKED, true);
+  }
+
+  // Either it wasn't cached or the cached result has expired. Build a
+  // channel for the HEAD request.
+
+  nsCOMPtr<nsILoadInfo> originalLoadInfo = aRequestChannel->GetLoadInfo();
+  MOZ_ASSERT(originalLoadInfo, "can not perform HSTS priming without a loadInfo");
+  if (!originalLoadInfo) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr<nsILoadInfo> loadInfo = static_cast<mozilla::LoadInfo*>
+    (originalLoadInfo.get())->CloneForNewRequest();
+
+  // the LoadInfo must have a security flag set in order to pass through priming
+  // if none of these security flags are set, go ahead and fail now instead of
+  // crashing in nsContentSecurityManager::ValidateSecurityFlags
+  nsSecurityFlags securityMode = loadInfo->GetSecurityMode();
+  if (securityMode != nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS &&
+      securityMode != nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED &&
+      securityMode != nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS &&
+      securityMode != nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL &&
+      securityMode != nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS) {
+    return aCallback->OnHSTSPrimingFailed(NS_ERROR_CONTENT_BLOCKED, true);
+  }
+
+  nsCOMPtr<nsILoadGroup> loadGroup;
+  rv = aRequestChannel->GetLoadGroup(getter_AddRefs(loadGroup));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsLoadFlags loadFlags;
+  rv = aRequestChannel->GetLoadFlags(&loadFlags);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  loadFlags &= HttpBaseChannel::INHIBIT_CACHING |
+               HttpBaseChannel::INHIBIT_PERSISTENT_CACHING |
+               HttpBaseChannel::LOAD_BYPASS_CACHE |
+               HttpBaseChannel::LOAD_FROM_CACHE |
+               HttpBaseChannel::VALIDATE_ALWAYS;
+  // Priming requests should never be intercepted by service workers and
+  // are always anonymous.
+  loadFlags |= nsIChannel::LOAD_BYPASS_SERVICE_WORKER |
+               nsIRequest::LOAD_ANONYMOUS;
+
+  // Create a new channel to send the priming request
+  nsCOMPtr<nsIChannel> primingChannel;
+  rv = NS_NewChannelInternal(getter_AddRefs(primingChannel),
+                             uri,
+                             loadInfo,
+                             loadGroup,
+                             nullptr,   // aCallbacks are set later
+                             loadFlags);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Set method and headers
+  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(primingChannel);
+  if (!httpChannel) {
+    NS_ERROR("HSTSPrimingListener: Failed to QI to nsIHttpChannel!");
+    return NS_ERROR_FAILURE;
+  }
+
+  // Currently using HEAD per the draft, but under discussion to change to GET
+  // with credentials so if the upgrade is approved the result is already cached.
+  rv = httpChannel->SetRequestMethod(NS_LITERAL_CSTRING("HEAD"));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = httpChannel->
+    SetRequestHeader(NS_LITERAL_CSTRING("Upgrade-Insecure-Requests"),
+                     NS_LITERAL_CSTRING("1"), false);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // attempt to set the class of service flags on the new channel
+  nsCOMPtr<nsIClassOfService> requestClass = do_QueryInterface(aRequestChannel);
+  if (!requestClass) {
+    NS_ERROR("HSTSPrimingListener: aRequestChannel is not an nsIClassOfService");
+    return NS_ERROR_FAILURE;
+  }
+  nsCOMPtr<nsIClassOfService> primingClass = do_QueryInterface(httpChannel);
+  if (!primingClass) {
+    NS_ERROR("HSTSPrimingListener: aRequestChannel is not an nsIClassOfService");
+    return NS_ERROR_FAILURE;
+  }
+
+  uint32_t classFlags = 0;
+  rv = requestClass ->GetClassFlags(&classFlags);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = primingClass->SetClassFlags(classFlags);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Set up listener which will start the original channel
+  nsCOMPtr<nsIStreamListener> primingListener(new HSTSPrimingListener(aCallback));
+
+  // Start priming
+  rv = primingChannel->AsyncOpen2(primingListener);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+} // namespace net
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/HSTSPrimerListener.h
@@ -0,0 +1,108 @@
+/* -*- 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 HSTSPrimingListener_h__
+#define HSTSPrimingListener_h__
+
+#include "nsCOMPtr.h"
+#include "nsIChannelEventSink.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIStreamListener.h"
+#include "nsIThreadRetargetableStreamListener.h"
+
+#include "mozilla/Attributes.h"
+
+class nsIPrincipal;
+class nsINetworkInterceptController;
+class nsIHstsPrimingCallback;
+
+namespace mozilla {
+namespace net {
+
+class HttpChannelParent;
+class nsHttpChannel;
+
+/*
+ * How often do we get back an HSTS priming result which upgrades the connection to HTTPS?
+ */
+enum HSTSPrimingResult {
+  // This site has been seen before and won't be upgraded
+  eHSTS_PRIMING_CACHED_NO_UPGRADE = 0,
+  // This site has been seen before and will be upgraded
+  eHSTS_PRIMING_CACHED_DO_UPGRADE = 1,
+  // This site has been seen before and will be blocked
+  eHSTS_PRIMING_CACHED_BLOCK      = 2,
+  // The request was already upgraded, probably through
+  // upgrade-insecure-requests
+  eHSTS_PRIMING_ALREADY_UPGRADED  = 3,
+  // HSTS priming is successful and the connection will be upgraded to HTTPS
+  eHSTS_PRIMING_SUCCEEDED         = 4,
+  // When priming succeeds, but preferences require preservation of the order
+  // of mixed-content and hsts, and mixed-content blocks the load
+  eHSTS_PRIMING_SUCCEEDED_BLOCK   = 5,
+  // When priming succeeds, but preferences require preservation of the order
+  // of mixed-content and hsts, and mixed-content allows the load over http
+  eHSTS_PRIMING_SUCCEEDED_HTTP    = 6,
+  // HSTS priming failed, and the load is blocked by mixed-content
+  eHSTS_PRIMING_FAILED_BLOCK      = 7,
+  // HSTS priming failed, and the load is allowed by mixed-content
+  eHSTS_PRIMING_FAILED_ACCEPT     = 8
+};
+
+//////////////////////////////////////////////////////////////////////////
+// Class used as streamlistener and notification callback when
+// doing the HEAD request for an HSTS Priming check. Needs to be an
+// nsIStreamListener in order to receive events from AsyncOpen2
+class HSTSPrimingListener final : public nsIStreamListener,
+                                  public nsIInterfaceRequestor
+{
+public:
+  explicit HSTSPrimingListener(nsIHstsPrimingCallback* aCallback)
+   : mCallback(aCallback)
+  {
+  }
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSISTREAMLISTENER
+  NS_DECL_NSIREQUESTOBSERVER
+  NS_DECL_NSIINTERFACEREQUESTOR
+
+private:
+  ~HSTSPrimingListener() {}
+
+  // Only nsHttpChannel can invoke HSTS priming
+  friend class mozilla::net::nsHttpChannel;
+
+  /**
+   * Start the HSTS priming request. This will send an anonymous HEAD request to
+   * the URI aRequestChannel is attempting to load. On success, the new HSTS
+   * priming channel is allocated in aHSTSPrimingChannel.
+   *
+   * @param aRequestChannel the reference channel used to initialze the HSTS
+   *        priming channel
+   * @param aCallback the callback stored to handle the results of HSTS priming.
+   * @param aHSTSPrimingChannel if the new HSTS priming channel is allocated
+   *        successfully, it will be placed here.
+   */
+  static nsresult StartHSTSPriming(nsIChannel* aRequestChannel,
+                                   nsIHstsPrimingCallback* aCallback);
+
+  /**
+   * Given a request, return NS_OK if it has resulted in a cached HSTS update.
+   * We don't need to check for the header as that has already been done for us.
+   */
+  nsresult CheckHSTSPrimingRequestStatus(nsIRequest* aRequest);
+
+  /**
+   * the nsIHttpChannel to notify with the result of HSTS priming.
+   */
+  nsCOMPtr<nsIHstsPrimingCallback> mCallback;
+};
+
+
+}} // mozilla::net
+
+#endif // HSTSPrimingListener_h__
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -1587,34 +1587,44 @@ public:
     return NS_OK;
   }
 };
 
 NS_IMETHODIMP
 HttpChannelChild::OnRedirectVerifyCallback(nsresult result)
 {
   LOG(("HttpChannelChild::OnRedirectVerifyCallback [this=%p]\n", this));
+  nsresult rv;
   OptionalURIParams redirectURI;
   nsCOMPtr<nsIHttpChannel> newHttpChannel =
       do_QueryInterface(mRedirectChannelChild);
 
   if (NS_SUCCEEDED(result) && !mRedirectChannelChild) {
     // mRedirectChannelChild doesn't exist means we're redirecting to a protocol
     // that doesn't implement nsIChildChannel. The redirect result should be set
     // as failed by veto listeners and shouldn't enter this condition. As the
     // last resort, we synthesize the error result as NS_ERROR_DOM_BAD_URI here
     // to let nsHttpChannel::ContinueProcessResponse2 know it's redirecting to
     // another protocol and throw an error.
     LOG(("  redirecting to a protocol that doesn't implement nsIChildChannel"));
     result = NS_ERROR_DOM_BAD_URI;
   }
 
+  bool forceHSTSPriming = false;
+  bool mixedContentWouldBlock = false;
   if (newHttpChannel) {
     // Must not be called until after redirect observers called.
     newHttpChannel->SetOriginalURI(mOriginalURI);
+
+    nsCOMPtr<nsILoadInfo> newLoadInfo;
+    rv = newHttpChannel->GetLoadInfo(getter_AddRefs(newLoadInfo));
+    if (NS_SUCCEEDED(rv) && newLoadInfo) {
+      forceHSTSPriming = newLoadInfo->GetForceHSTSPriming();
+      mixedContentWouldBlock = newLoadInfo->GetMixedContentWouldBlock();
+    }
   }
 
   if (mRedirectingForSubsequentSynthesizedResponse) {
     nsCOMPtr<nsIHttpChannelChild> httpChannelChild = do_QueryInterface(mRedirectChannelChild);
     MOZ_ASSERT(httpChannelChild);
     RefPtr<HttpChannelChild> redirectedChannel =
         static_cast<HttpChannelChild*>(httpChannelChild.get());
 
@@ -1668,17 +1678,18 @@ HttpChannelChild::OnRedirectVerifyCallba
     nsCOMPtr<nsIRequest> request = do_QueryInterface(mRedirectChannelChild);
     if (request) {
       request->GetLoadFlags(&loadFlags);
     }
   }
 
   if (mIPCOpen)
     SendRedirect2Verify(result, *headerTuples, loadFlags, redirectURI,
-                        corsPreflightArgs);
+                        corsPreflightArgs, forceHSTSPriming,
+                        mixedContentWouldBlock);
 
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // HttpChannelChild::nsIRequest
 //-----------------------------------------------------------------------------
 
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -626,20 +626,23 @@ HttpChannelParent::RecvUpdateAssociatedC
   return true;
 }
 
 bool
 HttpChannelParent::RecvRedirect2Verify(const nsresult& result,
                                        const RequestHeaderTuples& changedHeaders,
                                        const uint32_t& loadFlags,
                                        const OptionalURIParams& aAPIRedirectURI,
-                                       const OptionalCorsPreflightArgs& aCorsPreflightArgs)
+                                       const OptionalCorsPreflightArgs& aCorsPreflightArgs,
+                                       const bool& aForceHSTSPriming,
+                                       const bool& aMixedContentWouldBlock)
 {
   LOG(("HttpChannelParent::RecvRedirect2Verify [this=%p result=%x]\n",
        this, result));
+  nsresult rv;
   if (NS_SUCCEEDED(result)) {
     nsCOMPtr<nsIHttpChannel> newHttpChannel =
         do_QueryInterface(mRedirectChannel);
 
     if (newHttpChannel) {
       nsCOMPtr<nsIURI> apiRedirectUri = DeserializeURI(aAPIRedirectURI);
 
       if (apiRedirectUri)
@@ -663,16 +666,24 @@ HttpChannelParent::RecvRedirect2Verify(c
 
       if (aCorsPreflightArgs.type() == OptionalCorsPreflightArgs::TCorsPreflightArgs) {
         nsCOMPtr<nsIHttpChannelInternal> newInternalChannel =
           do_QueryInterface(newHttpChannel);
         MOZ_RELEASE_ASSERT(newInternalChannel);
         const CorsPreflightArgs& args = aCorsPreflightArgs.get_CorsPreflightArgs();
         newInternalChannel->SetCorsPreflightParameters(args.unsafeHeaders());
       }
+
+      if (aForceHSTSPriming) {
+        nsCOMPtr<nsILoadInfo> newLoadInfo;
+        rv = newHttpChannel->GetLoadInfo(getter_AddRefs(newLoadInfo));
+        if (NS_SUCCEEDED(rv) && newLoadInfo) {
+          newLoadInfo->SetHSTSPriming(aMixedContentWouldBlock);
+        }
+      }
     }
   }
 
   if (!mRedirectCallback) {
     // This should according the logic never happen, log the situation.
     if (mReceivedRedirect2Verify)
       LOG(("RecvRedirect2Verify[%p]: Duplicate fire", this));
     if (mSentRedirect1BeginFailed)
--- a/netwerk/protocol/http/HttpChannelParent.h
+++ b/netwerk/protocol/http/HttpChannelParent.h
@@ -150,17 +150,19 @@ protected:
   virtual bool RecvSetCacheTokenCachedCharset(const nsCString& charset) override;
   virtual bool RecvSuspend() override;
   virtual bool RecvResume() override;
   virtual bool RecvCancel(const nsresult& status) override;
   virtual bool RecvRedirect2Verify(const nsresult& result,
                                    const RequestHeaderTuples& changedHeaders,
                                    const uint32_t& loadFlags,
                                    const OptionalURIParams& apiRedirectUri,
-                                   const OptionalCorsPreflightArgs& aCorsPreflightArgs) override;
+                                   const OptionalCorsPreflightArgs& aCorsPreflightArgs,
+                                   const bool& aForceHSTSPriming,
+                                   const bool& aMixedContentWouldBlock) override;
   virtual bool RecvUpdateAssociatedContentSecurity(const int32_t& broken,
                                                    const int32_t& no) override;
   virtual bool RecvDocumentChannelCleanup() override;
   virtual bool RecvMarkOfflineCacheEntryAsForeign() override;
   virtual bool RecvDivertOnDataAvailable(const nsCString& data,
                                          const uint64_t& offset,
                                          const uint32_t& count) override;
   virtual bool RecvDivertOnStopRequest(const nsresult& statusCode) override;
--- a/netwerk/protocol/http/PHttpChannel.ipdl
+++ b/netwerk/protocol/http/PHttpChannel.ipdl
@@ -41,17 +41,18 @@ parent:
   async Suspend();
   async Resume();
 
   async Cancel(nsresult status);
 
   // Reports approval/veto of redirect by child process redirect observers
   async Redirect2Verify(nsresult result, RequestHeaderTuples changedHeaders,
                         uint32_t loadFlags, OptionalURIParams apiRedirectTo,
-                        OptionalCorsPreflightArgs corsPreflightArgs);
+                        OptionalCorsPreflightArgs corsPreflightArgs,
+                        bool forceHSTSPriming, bool mixedContentWouldBlock);
 
   // For document loads we keep this protocol open after child's
   // OnStopRequest, and send this msg (instead of __delete__) to allow
   // partial cleanup on parent.
   async DocumentChannelCleanup();
 
   // This might have to be sync. If this fails we must fail the document load
   // to avoid endless loop.
--- a/netwerk/protocol/http/moz.build
+++ b/netwerk/protocol/http/moz.build
@@ -1,15 +1,16 @@
 # -*- 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/.
 
 XPIDL_SOURCES += [
+    'nsIHstsPrimingCallback.idl',
     'nsIHttpActivityObserver.idl',
     'nsIHttpAuthenticableChannel.idl',
     'nsIHttpAuthenticator.idl',
     'nsIHttpAuthManager.idl',
     'nsIHttpChannel.idl',
     'nsIHttpChannelAuthProvider.idl',
     'nsIHttpChannelChild.idl',
     'nsIHttpChannelInternal.idl',
@@ -54,16 +55,17 @@ SOURCES += [
     'nsHttpChannelAuthProvider.cpp', # redefines GetAuthType
 ]
 
 UNIFIED_SOURCES += [
     'AltDataOutputStreamChild.cpp',
     'AltDataOutputStreamParent.cpp',
     'CacheControlParser.cpp',
     'ConnectionDiagnostics.cpp',
+    'HSTSPrimerListener.cpp',
     'Http2Compression.cpp',
     'Http2Push.cpp',
     'Http2Session.cpp',
     'Http2Stream.cpp',
     'HttpBaseChannel.cpp',
     'HttpChannelChild.cpp',
     'HttpChannelParent.cpp',
     'HttpChannelParentListener.cpp',
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -95,16 +95,18 @@
 #include "nsIDeprecationWarner.h"
 #include "nsIDocument.h"
 #include "nsIDOMDocument.h"
 #include "nsICompressConvStats.h"
 #include "nsCORSListenerProxy.h"
 #include "nsISocketProvider.h"
 #include "mozilla/net/Predictor.h"
 #include "CacheControlParser.h"
+#include "nsMixedContentBlocker.h"
+#include "HSTSPrimerListener.h"
 
 namespace mozilla { namespace net {
 
 namespace {
 
 // Monotonically increasing ID for generating unique cache entries per
 // intercepted channel.
 static uint64_t gNumIntercepted = 0;
@@ -402,22 +404,60 @@ nsHttpChannel::Connect()
             if (!mFallbackChannel && !mFallbackKey.IsEmpty()) {
                 return AsyncCall(&nsHttpChannel::HandleAsyncFallback);
             }
             return NS_ERROR_DOCUMENT_NOT_CACHED;
         }
         // otherwise, let's just proceed without using the cache.
     }
 
+    return TryHSTSPriming();
+}
+
+nsresult
+nsHttpChannel::TryHSTSPriming()
+{
+    if (mLoadInfo) {
+        // HSTS priming requires the LoadInfo provided with AsyncOpen2
+        bool requireHSTSPriming =
+            mLoadInfo->GetForceHSTSPriming();
+
+        if (requireHSTSPriming &&
+                nsMixedContentBlocker::sSendHSTSPriming &&
+                mInterceptCache == DO_NOT_INTERCEPT) {
+            bool isHttpsScheme;
+            nsresult rv = mURI->SchemeIs("https", &isHttpsScheme);
+            NS_ENSURE_SUCCESS(rv, rv);
+            if (!isHttpsScheme) {
+                rv = HSTSPrimingListener::StartHSTSPriming(this, this);
+
+                if (NS_FAILED(rv)) {
+                    CloseCacheEntry(false);
+                    return rv;
+                }
+
+                return NS_OK;
+            }
+
+            // The request was already upgraded, for example by
+            // upgrade-insecure-requests or a prior successful priming request
+            Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_RESULT,
+                    HSTSPrimingResult::eHSTS_PRIMING_ALREADY_UPGRADED);
+            mLoadInfo->ClearHSTSPriming();
+        }
+    }
+
     return ContinueConnect();
 }
 
 nsresult
 nsHttpChannel::ContinueConnect()
 {
+    // If we have had HSTS priming, we need to reevaluate whether we need
+    // a CORS preflight. Bug: 1272440
     // If we need to start a CORS preflight, do it now!
     // Note that it is important to do this before the early returns below.
     if (!mIsCorsPreflightDone && mRequireCORSPreflight &&
         mInterceptCache != INTERCEPTED) {
         MOZ_ASSERT(!mPreflightChannel);
         nsresult rv =
             nsCORSListenerProxy::StartCORSPreflight(this, this,
                                                     mUnsafeHeaders,
@@ -4133,17 +4173,17 @@ nsHttpChannel::OnCacheEntryAvailableInte
         return rv;
     }
 
     // We may be waiting for more callbacks...
     if (AwaitingCacheCallbacks()) {
         return NS_OK;
     }
 
-    return ContinueConnect();
+    return TryHSTSPriming();
 }
 
 nsresult
 nsHttpChannel::OnNormalCacheEntryAvailable(nsICacheEntry *aEntry,
                                            bool aNew,
                                            nsresult aEntryStatus)
 {
     mCacheEntriesToWaitFor &= ~WAIT_FOR_CACHE_ENTRY;
@@ -5494,16 +5534,17 @@ NS_INTERFACE_MAP_BEGIN(nsHttpChannel)
     NS_INTERFACE_MAP_ENTRY(nsIApplicationCacheContainer)
     NS_INTERFACE_MAP_ENTRY(nsIApplicationCacheChannel)
     NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback)
     NS_INTERFACE_MAP_ENTRY(nsIThreadRetargetableRequest)
     NS_INTERFACE_MAP_ENTRY(nsIThreadRetargetableStreamListener)
     NS_INTERFACE_MAP_ENTRY(nsIDNSListener)
     NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
     NS_INTERFACE_MAP_ENTRY(nsICorsPreflightCallback)
+    NS_INTERFACE_MAP_ENTRY(nsIHstsPrimingCallback)
     NS_INTERFACE_MAP_ENTRY(nsIChannelWithDivertableParentListener)
     // we have no macro that covers this case.
     if (aIID.Equals(NS_GET_IID(nsHttpChannel)) ) {
         AddRef();
         *aInstancePtr = this;
         return NS_OK;
     } else
 NS_INTERFACE_MAP_END_INHERITING(HttpBaseChannel)
@@ -7882,16 +7923,115 @@ nsHttpChannel::OnPreflightFailed(nsresul
     mPreflightChannel = nullptr;
 
     CloseCacheEntry(false);
     AsyncAbort(aError);
     return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
+// nsIHstsPrimingCallback functions
+//-----------------------------------------------------------------------------
+
+/*
+ * May be invoked synchronously if HSTS priming has already been performed
+ * for the host.
+ */
+nsresult
+nsHttpChannel::OnHSTSPrimingSucceeded(bool aCached)
+{
+    if (nsMixedContentBlocker::sUseHSTS) {
+        // redirect the channel to HTTPS if the pref
+        // "security.mixed_content.use_hsts" is true
+        LOG(("HSTS Priming succeeded, redirecting to HTTPS [this=%p]", this));
+        Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_RESULT,
+                (aCached) ? HSTSPrimingResult::eHSTS_PRIMING_CACHED_DO_UPGRADE :
+                            HSTSPrimingResult::eHSTS_PRIMING_SUCCEEDED);
+        return AsyncCall(&nsHttpChannel::HandleAsyncRedirectChannelToHttps);
+    }
+
+    // If "security.mixed_content.use_hsts" is false, record the result of
+    // HSTS priming and block or proceed with the load as required by
+    // mixed-content blocking
+    bool wouldBlock = mLoadInfo->GetMixedContentWouldBlock();
+
+    // preserve the mixed-content-before-hsts order and block if required
+    if (wouldBlock) {
+        LOG(("HSTS Priming succeeded, blocking for mixed-content [this=%p]",
+                    this));
+        Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_RESULT,
+                              HSTSPrimingResult::eHSTS_PRIMING_SUCCEEDED_BLOCK);
+        CloseCacheEntry(false);
+        return AsyncAbort(NS_ERROR_CONTENT_BLOCKED);
+    }
+
+    LOG(("HSTS Priming succeeded, loading insecure: [this=%p]", this));
+    Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_RESULT,
+                          HSTSPrimingResult::eHSTS_PRIMING_SUCCEEDED_HTTP);
+
+    nsresult rv = ContinueConnect();
+    if (NS_FAILED(rv)) {
+        CloseCacheEntry(false);
+        return AsyncAbort(rv);
+    }
+
+    return NS_OK;
+}
+
+/*
+ * May be invoked synchronously if HSTS priming has already been performed
+ * for the host.
+ */
+nsresult
+nsHttpChannel::OnHSTSPrimingFailed(nsresult aError, bool aCached)
+{
+    bool wouldBlock = mLoadInfo->GetMixedContentWouldBlock();
+
+    LOG(("HSTS Priming Failed [this=%p], %s the load", this,
+                (wouldBlock) ? "blocking" : "allowing"));
+    if (aCached) {
+        // Between the time we marked for priming and started the priming request,
+        // the host was found to not allow the upgrade, probably from another
+        // priming request.
+        Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_RESULT,
+                (wouldBlock) ?  HSTSPrimingResult::eHSTS_PRIMING_CACHED_BLOCK :
+                                HSTSPrimingResult::eHSTS_PRIMING_CACHED_NO_UPGRADE);
+    } else {
+        // A priming request was sent, and no HSTS header was found that allows
+        // the upgrade.
+        Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_RESULT,
+                (wouldBlock) ?  HSTSPrimingResult::eHSTS_PRIMING_FAILED_BLOCK :
+                                HSTSPrimingResult::eHSTS_PRIMING_FAILED_ACCEPT);
+    }
+
+    // Don't visit again for at least one day
+    nsISiteSecurityService* sss = gHttpHandler->GetSSService();
+    NS_ENSURE_TRUE(sss, NS_ERROR_OUT_OF_MEMORY);
+    nsresult rv = sss->CacheNegativeHSTSResult(mURI, 24 * 60 * 60);
+    if (NS_FAILED(rv)) {
+        NS_ERROR("nsISiteSecurityService::CacheNegativeHSTSResult failed");
+    }
+
+    // If we would block, go ahead and abort with the error provided
+    if (wouldBlock) {
+        CloseCacheEntry(false);
+        return AsyncAbort(aError);
+    }
+
+    // we can continue the load and the UI has been updated as mixed content
+    rv = ContinueConnect();
+    if (NS_FAILED(rv)) {
+        CloseCacheEntry(false);
+        return AsyncAbort(rv);
+    }
+
+    return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
 // AChannelHasDivertableParentChannelAsListener internal functions
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 nsHttpChannel::MessageDiversionStarted(ADivertableParentChannel *aParentChannel)
 {
   LOG(("nsHttpChannel::MessageDiversionStarted [this=%p]", this));
   MOZ_ASSERT(!mParentChannel);
--- a/netwerk/protocol/http/nsHttpChannel.h
+++ b/netwerk/protocol/http/nsHttpChannel.h
@@ -23,16 +23,17 @@
 #include "nsWeakReference.h"
 #include "TimingStruct.h"
 #include "ADivertableParentChannel.h"
 #include "AutoClose.h"
 #include "nsIStreamListener.h"
 #include "nsISupportsPrimitives.h"
 #include "nsICorsPreflightCallback.h"
 #include "AlternateServices.h"
+#include "nsIHstsPrimingCallback.h"
 
 class nsDNSPrefetch;
 class nsICancelable;
 class nsIHttpChannelAuthProvider;
 class nsInputStreamPump;
 class nsISSLStatus;
 
 namespace mozilla { namespace net {
@@ -70,31 +71,33 @@ class nsHttpChannel final : public HttpB
                           , public nsIApplicationCacheChannel
                           , public nsIAsyncVerifyRedirectCallback
                           , public nsIThreadRetargetableRequest
                           , public nsIThreadRetargetableStreamListener
                           , public nsIDNSListener
                           , public nsSupportsWeakReference
                           , public nsICorsPreflightCallback
                           , public nsIChannelWithDivertableParentListener
+                          , public nsIHstsPrimingCallback
 {
 public:
     NS_DECL_ISUPPORTS_INHERITED
     NS_DECL_NSIREQUESTOBSERVER
     NS_DECL_NSISTREAMLISTENER
     NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
     NS_DECL_NSICACHEINFOCHANNEL
     NS_DECL_NSICACHINGCHANNEL
     NS_DECL_NSICACHEENTRYOPENCALLBACK
     NS_DECL_NSITRANSPORTEVENTSINK
     NS_DECL_NSIPROTOCOLPROXYCALLBACK
     NS_DECL_NSIPROXIEDCHANNEL
     NS_DECL_NSIAPPLICATIONCACHECONTAINER
     NS_DECL_NSIAPPLICATIONCACHECHANNEL
     NS_DECL_NSIASYNCVERIFYREDIRECTCALLBACK
+    NS_DECL_NSIHSTSPRIMINGCALLBACK
     NS_DECL_NSITHREADRETARGETABLEREQUEST
     NS_DECL_NSIDNSLISTENER
     NS_DECL_NSICHANNELWITHDIVERTABLEPARENTLISTENER
     NS_DECLARE_STATIC_IID_ACCESSOR(NS_HTTPCHANNEL_IID)
 
     // nsIHttpAuthenticableChannel. We can't use
     // NS_DECL_NSIHTTPAUTHENTICABLECHANNEL because it duplicates cancel() and
     // others.
@@ -199,16 +202,19 @@ public: /* internal necko use only */
     uint32_t GetRequestTime() const
     {
         return mRequestTime;
     }
 
     nsresult OpenCacheEntry(bool usingSSL);
     nsresult ContinueConnect();
 
+    // If the load is mixed-content, build and send an HSTS priming request.
+    nsresult TryHSTSPriming();
+
     nsresult StartRedirectChannelToURI(nsIURI *, uint32_t);
 
     // This allows cache entry to be marked as foreign even after channel itself
     // is gone.  Needed for e10s (see HttpChannelParent::RecvDocumentChannelCleanup)
     class OfflineCacheEntryAsForeignMarker {
         nsCOMPtr<nsIApplicationCache> mApplicationCache;
         nsCOMPtr<nsIURI> mCacheURI;
     public:
--- a/netwerk/protocol/http/nsHttpHandler.cpp
+++ b/netwerk/protocol/http/nsHttpHandler.cpp
@@ -2235,17 +2235,18 @@ nsHttpHandler::SpeculativeConnectInterna
         return NS_OK;
 
     nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(aCallbacks);
     uint32_t flags = 0;
     if (loadContext && loadContext->UsePrivateBrowsing())
         flags |= nsISocketProvider::NO_PERMANENT_STORAGE;
     nsCOMPtr<nsIURI> clone;
     if (NS_SUCCEEDED(sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS,
-                                      aURI, flags, &isStsHost)) && isStsHost) {
+                                      aURI, flags, nullptr, &isStsHost)) &&
+                                      isStsHost) {
         if (NS_SUCCEEDED(NS_GetSecureUpgradedURI(aURI,
                                                  getter_AddRefs(clone)))) {
             aURI = clone.get();
             // (NOTE: We better make sure |clone| stays alive until the end
             // of the function now, since our aURI arg now points to it!)
         }
     }
 
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/nsIHstsPrimingCallback.idl
@@ -0,0 +1,50 @@
+/* -*- 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 "nsISupports.idl"
+
+/**
+ * HSTS priming attempts to prevent mixed-content by looking for the
+ * Strict-Transport-Security header as a signal from the server that it is
+ * safe to upgrade HTTP to HTTPS.
+ *
+ * Since mixed-content blocking happens very early in the process in AsyncOpen2,
+ * the status of mixed-content blocking is stored in the LoadInfo and then used
+ * to determine whether to send a priming request or not.
+ *
+ * This interface is implemented by nsHttpChannel so that it can receive the
+ * result of HSTS priming.
+ */
+[builtinclass, uuid(eca6daca-3f2a-4a2a-b3bf-9f24f79bc999)]
+interface nsIHstsPrimingCallback : nsISupports
+{
+  /**
+   * HSTS priming has succeeded with an STS header, and the site asserts it is
+   * safe to upgrade the request from HTTP to HTTPS. The request may still be
+   * blocked based on the user's preferences.
+   *
+   * May be invoked synchronously if HSTS priming has already been performed
+   * for the host.
+   *
+   * @param aCached whether the result was already in the HSTS cache
+   */
+  [noscript, nostdcall]
+  void onHSTSPrimingSucceeded(in bool aCached);
+  /**
+   * HSTS priming has seen no STS header, the request itself has failed,
+   * or some other failure which does not constitute a positive signal that the
+   * site can be upgraded safely to HTTPS. The request may still be allowed
+   * based on the user's preferences.
+   *
+   * May be invoked synchronously if HSTS priming has already been performed
+   * for the host.
+   *
+   * @param aError The error which caused this failure, or NS_ERROR_CONTENT_BLOCKED
+   * @param aCached whether the result was already in the HSTS cache
+   */
+  [noscript, nostdcall]
+  void onHSTSPrimingFailed(in nsresult aError, in bool aCached);
+};
--- a/security/manager/ssl/SSLServerCertVerification.cpp
+++ b/security/manager/ssl/SSLServerCertVerification.cpp
@@ -504,26 +504,28 @@ CertErrorRunnable::CheckCertOverrides()
            ("[%p][%p] couldn't get nsISiteSecurityService to check for HSTS/HPKP\n",
             mFdForLogging, this));
     return new SSLServerCertVerificationResult(mInfoObject,
                                                mDefaultErrorCodeToReport);
   }
   nsresult nsrv = sss->IsSecureHost(nsISiteSecurityService::HEADER_HSTS,
                                     mInfoObject->GetHostNameRaw(),
                                     mProviderFlags,
+                                    nullptr,
                                     &strictTransportSecurityEnabled);
   if (NS_FAILED(nsrv)) {
     MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
            ("[%p][%p] checking for HSTS failed\n", mFdForLogging, this));
     return new SSLServerCertVerificationResult(mInfoObject,
                                                mDefaultErrorCodeToReport);
   }
   nsrv = sss->IsSecureHost(nsISiteSecurityService::HEADER_HPKP,
                            mInfoObject->GetHostNameRaw(),
                            mProviderFlags,
+                           nullptr,
                            &hasPinningInformation);
   if (NS_FAILED(nsrv)) {
     MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
            ("[%p][%p] checking for HPKP failed\n", mFdForLogging, this));
     return new SSLServerCertVerificationResult(mInfoObject,
                                                mDefaultErrorCodeToReport);
   }
 
--- a/security/manager/ssl/nsISiteSecurityService.idl
+++ b/security/manager/ssl/nsISiteSecurityService.idl
@@ -110,36 +110,42 @@ interface nsISiteSecurityService : nsISu
 
     /**
      * See isSecureURI
      *
      * @param aType the type of security state in question.
      * @param aHost the hostname (punycode) to query for state.
      * @param aFlags  options for this request as defined in nsISocketProvider:
      *                  NO_PERMANENT_STORAGE
+     * @param aCached true if we have cached information regarding whether or not
+     *                  the host is HSTS, false otherwise.
      */
     boolean isSecureHost(in uint32_t aType,
                          in string aHost,
-                         in uint32_t aFlags);
+                         in uint32_t aFlags,
+                         [optional] out boolean aCached);
 
     /**
      * Checks whether or not the URI's hostname has a given security state set.
      * For example, for HSTS:
      * The URI is an HSTS URI if either the host has the HSTS state set, or one
      * of its super-domains has the HSTS "includeSubdomains" flag set.
      * NOTE: this function makes decisions based only on the
      * host contained in the URI, and disregards other portions of the URI
      * such as path and port.
      *
      * @param aType the type of security state in question.
      * @param aURI the URI to query for STS state.
      * @param aFlags  options for this request as defined in nsISocketProvider:
      *                  NO_PERMANENT_STORAGE
+     * @param aCached true if we have cached information regarding whether or not
+     *                  the host is HSTS, false otherwise.
      */
-    boolean isSecureURI(in uint32_t aType, in nsIURI aURI, in uint32_t aFlags);
+    boolean isSecureURI(in uint32_t aType, in nsIURI aURI, in uint32_t aFlags,
+                        [optional] out boolean aCached);
 
     /**
      * Removes all security state by resetting to factory-original settings.
      */
     void clearAll();
 
     /**
      * Returns an array of sha256-hashed key pins for the given domain, if any.
@@ -169,13 +175,21 @@ interface nsISiteSecurityService : nsISu
      * @param aMaxAge lifetime (in seconds) of this pin set
      * @param aPinCount number of keys being pinnned
      * @param aSha256Pins array of hashed key fingerprints (SHA-256, base64)
      */
      boolean setKeyPins(in string aHost, in boolean aIncludeSubdomains,
                         in unsigned long aMaxAge, in unsigned long aPinCount,
                         [array, size_is(aPinCount)] in string aSha256Pins);
 
+    /**
+     * Mark a host as declining to provide a given security state so that features
+     * such as HSTS priming will not flood a server with requests.
+     *
+     * @param aURI the nsIURI that this applies to
+     * @param aMaxAge lifetime (in seconds) of this negative cache
+     */
+    [noscript] void cacheNegativeHSTSResult(in nsIURI aURI, in unsigned long long aMaxAge);
 };
 
 %{C++
 #define NS_SSSERVICE_CONTRACTID "@mozilla.org/ssservice;1"
 %}
--- a/security/manager/ssl/nsSiteSecurityService.cpp
+++ b/security/manager/ssl/nsSiteSecurityService.cpp
@@ -309,26 +309,31 @@ ExpireTimeFromMaxAge(uint64_t maxAge)
   return (PR_Now() / PR_USEC_PER_MSEC) + ((int64_t)maxAge * PR_MSEC_PER_SEC);
 }
 
 nsresult
 nsSiteSecurityService::SetHSTSState(uint32_t aType,
                                     nsIURI* aSourceURI,
                                     int64_t maxage,
                                     bool includeSubdomains,
-                                    uint32_t flags)
+                                    uint32_t flags,
+                                    SecurityPropertyState aHSTSState)
 {
   // If max-age is zero, that's an indication to immediately remove the
   // security state, so here's a shortcut.
   if (!maxage) {
     return RemoveState(aType, aSourceURI, flags);
   }
 
+  MOZ_ASSERT((aHSTSState == SecurityPropertySet ||
+              aHSTSState == SecurityPropertyNegative),
+      "HSTS State must be SecurityPropertySet or SecurityPropertyNegative");
+
   int64_t expiretime = ExpireTimeFromMaxAge(maxage);
-  SiteHSTSState siteState(expiretime, SecurityPropertySet, includeSubdomains);
+  SiteHSTSState siteState(expiretime, aHSTSState, includeSubdomains);
   nsAutoCString stateString;
   siteState.ToString(stateString);
   nsAutoCString hostname;
   nsresult rv = GetHost(aSourceURI, hostname);
   NS_ENSURE_SUCCESS(rv, rv);
   SSSLOG(("SSS: setting state for %s", hostname.get()));
   bool isPrivate = flags & nsISocketProvider::NO_PERMANENT_STORAGE;
   mozilla::DataStorageType storageType = isPrivate
@@ -338,16 +343,24 @@ nsSiteSecurityService::SetHSTSState(uint
   SetStorageKey(storageKey, hostname, aType);
   rv = mSiteStateStorage->Put(storageKey, stateString, storageType);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsSiteSecurityService::CacheNegativeHSTSResult(nsIURI* aSourceURI,
+                                               uint64_t aMaxAge)
+{
+  return SetHSTSState(nsISiteSecurityService::HEADER_HSTS, aSourceURI,
+                      aMaxAge, false, 0, SecurityPropertyNegative);
+}
+
+NS_IMETHODIMP
 nsSiteSecurityService::RemoveState(uint32_t aType, nsIURI* aURI,
                                    uint32_t aFlags)
 {
    // Child processes are not allowed direct access to this.
    if (!XRE_IsParentProcess()) {
      MOZ_CRASH("Child process: no direct access to nsISiteSecurityService::RemoveState");
    }
 
@@ -859,17 +872,17 @@ nsSiteSecurityService::ProcessSTSHeader(
     if (aFailureResult) {
       *aFailureResult = nsISiteSecurityService::ERROR_NO_MAX_AGE;
     }
     return NS_ERROR_FAILURE;
   }
 
   // record the successfully parsed header data.
   nsresult rv = SetHSTSState(aType, aSourceURI, maxAge, foundIncludeSubdomains,
-                             aFlags);
+                             aFlags, SecurityPropertySet);
   if (NS_FAILED(rv)) {
     SSSLOG(("SSS: failed to set STS state"));
     if (aFailureResult) {
       *aFailureResult = nsISiteSecurityService::ERROR_COULD_NOT_SAVE_STATE;
     }
     return rv;
   }
 
@@ -883,17 +896,18 @@ nsSiteSecurityService::ProcessSTSHeader(
 
   return foundUnrecognizedDirective
            ? NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA
            : NS_OK;
 }
 
 NS_IMETHODIMP
 nsSiteSecurityService::IsSecureURI(uint32_t aType, nsIURI* aURI,
-                                   uint32_t aFlags, bool* aResult)
+                                   uint32_t aFlags, bool* aCached,
+                                   bool* aResult)
 {
    // Child processes are not allowed direct access to this.
    if (!XRE_IsParentProcess() && aType != nsISiteSecurityService::HEADER_HSTS) {
      MOZ_CRASH("Child process: no direct access to nsISiteSecurityService::IsSecureURI for non-HSTS entries");
    }
 
   NS_ENSURE_ARG(aURI);
   NS_ENSURE_ARG(aResult);
@@ -907,17 +921,17 @@ nsSiteSecurityService::IsSecureURI(uint3
   nsresult rv = GetHost(aURI, hostname);
   NS_ENSURE_SUCCESS(rv, rv);
   /* An IP address never qualifies as a secure URI. */
   if (HostIsIPAddress(hostname.get())) {
     *aResult = false;
     return NS_OK;
   }
 
-  return IsSecureHost(aType, hostname.get(), aFlags, aResult);
+  return IsSecureHost(aType, hostname.get(), aFlags, aCached, aResult);
 }
 
 int STSPreloadCompare(const void *key, const void *entry)
 {
   const char *keyStr = (const char *)key;
   const nsSTSPreload *preloadEntry = (const nsSTSPreload *)entry;
   return strcmp(keyStr, &kSTSHostTable[preloadEntry->mHostIndex]);
 }
@@ -937,33 +951,37 @@ nsSiteSecurityService::GetPreloadListEnt
                                           STSPreloadCompare);
   }
 
   return nullptr;
 }
 
 NS_IMETHODIMP
 nsSiteSecurityService::IsSecureHost(uint32_t aType, const char* aHost,
-                                    uint32_t aFlags, bool* aResult)
+                                    uint32_t aFlags, bool* aCached,
+                                    bool* aResult)
 {
    // Child processes are not allowed direct access to this.
    if (!XRE_IsParentProcess() && aType != nsISiteSecurityService::HEADER_HSTS) {
      MOZ_CRASH("Child process: no direct access to nsISiteSecurityService::IsSecureHost for non-HSTS entries");
    }
 
   NS_ENSURE_ARG(aHost);
   NS_ENSURE_ARG(aResult);
 
   // Only HSTS and HPKP are supported at the moment.
   NS_ENSURE_TRUE(aType == nsISiteSecurityService::HEADER_HSTS ||
                  aType == nsISiteSecurityService::HEADER_HPKP,
                  NS_ERROR_NOT_IMPLEMENTED);
 
   // set default in case if we can't find any STS information
   *aResult = false;
+  if (aCached) {
+    *aCached = false;
+  }
 
   /* An IP address never qualifies as a secure URI. */
   if (HostIsIPAddress(aHost)) {
     return NS_OK;
   }
 
   if (aType == nsISiteSecurityService::HEADER_HPKP) {
     RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
@@ -979,16 +997,19 @@ nsSiteSecurityService::IsSecureHost(uint
     return PublicKeyPinningService::HostHasPins(aHost, mozilla::pkix::Now(),
                                                 enforceTestMode, *aResult);
   }
 
   // Holepunch chart.apis.google.com and subdomains.
   nsAutoCString host(PublicKeyPinningService::CanonicalizeHostname(aHost));
   if (host.EqualsLiteral("chart.apis.google.com") ||
       StringEndsWith(host, NS_LITERAL_CSTRING(".chart.apis.google.com"))) {
+    if (aCached) {
+      *aCached = true;
+    }
     return NS_OK;
   }
 
   const nsSTSPreload *preload = nullptr;
 
   // First check the exact host. This involves first checking for an entry in
   // site security storage. If that entry exists, we don't want to check
   // in the preload list. We only want to use the stored value if it is not a
@@ -1002,31 +1023,42 @@ nsSiteSecurityService::IsSecureHost(uint
                                          : mozilla::DataStorage_Persistent;
   nsAutoCString storageKey;
   SetStorageKey(storageKey, host, aType);
   nsCString value = mSiteStateStorage->Get(storageKey, storageType);
   SiteHSTSState siteState(value);
   if (siteState.mHSTSState != SecurityPropertyUnset) {
     SSSLOG(("Found entry for %s", host.get()));
     bool expired = siteState.IsExpired(aType);
-    if (!expired && siteState.mHSTSState == SecurityPropertySet) {
-      *aResult = true;
-      return NS_OK;
+    if (!expired) {
+      if (aCached) {
+        *aCached = true;
+      }
+      if (siteState.mHSTSState == SecurityPropertySet) {
+        *aResult = true;
+        return NS_OK;
+      } else if (siteState.mHSTSState == SecurityPropertyNegative) {
+        *aResult = false;
+        return NS_OK;
+      }
     }
 
     // If the entry is expired and not in the preload list, we can remove it.
     if (expired && !GetPreloadListEntry(host.get())) {
       mSiteStateStorage->Remove(storageKey, storageType);
     }
   }
   // Finally look in the preloaded list. This is the exact host,
   // so if an entry exists at all, this host is HSTS.
   else if (GetPreloadListEntry(host.get())) {
     SSSLOG(("%s is a preloaded STS host", host.get()));
     *aResult = true;
+    if (aCached) {
+      *aCached = true;
+    }
     return NS_OK;
   }
 
   SSSLOG(("no HSTS data for %s found, walking up domain", host.get()));
   const char *subdomain;
 
   uint32_t offset = 0;
   for (offset = host.FindChar('.', offset) + 1;
@@ -1049,32 +1081,43 @@ nsSiteSecurityService::IsSecureHost(uint
     nsCString subdomainString(subdomain);
     nsAutoCString storageKey;
     SetStorageKey(storageKey, subdomainString, aType);
     value = mSiteStateStorage->Get(storageKey, storageType);
     SiteHSTSState siteState(value);
     if (siteState.mHSTSState != SecurityPropertyUnset) {
       SSSLOG(("Found entry for %s", subdomain));
       bool expired = siteState.IsExpired(aType);
-      if (!expired && siteState.mHSTSState == SecurityPropertySet) {
-        *aResult = siteState.mHSTSIncludeSubdomains;
-        break;
+      if (!expired) {
+        if (aCached) {
+          *aCached = true;
+        }
+        if (siteState.mHSTSState == SecurityPropertySet) {
+          *aResult = siteState.mHSTSIncludeSubdomains;
+          break;
+        } else if (siteState.mHSTSState == SecurityPropertyNegative) {
+          *aResult = false;
+          break;
+        }
       }
 
       // If the entry is expired and not in the preload list, we can remove it.
       if (expired && !GetPreloadListEntry(subdomain)) {
         mSiteStateStorage->Remove(storageKey, storageType);
       }
     }
     // This is an ancestor, so if we get a match, we have to check if the
     // preloaded entry includes subdomains.
     else if ((preload = GetPreloadListEntry(subdomain)) != nullptr) {
       if (preload->mIncludeSubdomains) {
         SSSLOG(("%s is a preloaded STS host", subdomain));
         *aResult = true;
+        if (aCached) {
+          *aCached = true;
+        }
         break;
       }
     }
 
     SSSLOG(("no HSTS data for %s found, walking up domain", subdomain));
   }
 
   // Use whatever we ended up with, which defaults to false.
--- a/security/manager/ssl/nsSiteSecurityService.h
+++ b/security/manager/ssl/nsSiteSecurityService.h
@@ -29,17 +29,18 @@ class nsISSLStatus;
  * a site has or does not have the security property in question, respectively.
  * SecurityPropertyKnockout indicates a value on a preloaded list is being
  * overridden, and the associated site does not have the security property
  * in question.
  */
 enum SecurityPropertyState {
   SecurityPropertyUnset = 0,
   SecurityPropertySet = 1,
-  SecurityPropertyKnockout = 2
+  SecurityPropertyKnockout = 2,
+  SecurityPropertyNegative = 3,
 };
 
 /**
  * SiteHPKPState: A utility class that encodes/decodes a string describing
  * the public key pins of a site.
  * HPKP state consists of:
  *  - Expiry time (PRTime (aka int64_t) in milliseconds)
  *  - A state flag (SecurityPropertyState, default SecurityPropertyUnset)
@@ -123,17 +124,18 @@ public:
   nsresult Init();
 
 protected:
   virtual ~nsSiteSecurityService();
 
 private:
   nsresult GetHost(nsIURI *aURI, nsACString &aResult);
   nsresult SetHSTSState(uint32_t aType, nsIURI* aSourceURI, int64_t maxage,
-                        bool includeSubdomains, uint32_t flags);
+                        bool includeSubdomains, uint32_t flags,
+                        SecurityPropertyState aHSTSState);
   nsresult ProcessHeaderInternal(uint32_t aType, nsIURI* aSourceURI,
                                  const char* aHeader, nsISSLStatus* aSSLStatus,
                                  uint32_t aFlags, uint64_t* aMaxAge,
                                  bool* aIncludeSubdomains,
                                  uint32_t* aFailureResult);
   nsresult ProcessSTSHeader(nsIURI* aSourceURI, const char* aHeader,
                             uint32_t flags, uint64_t* aMaxAge,
                             bool* aIncludeSubdomains, uint32_t* aFailureResult);
--- a/testing/specialpowers/content/SpecialPowersObserverAPI.js
+++ b/testing/specialpowers/content/SpecialPowersObserverAPI.js
@@ -319,20 +319,20 @@ SpecialPowersObserverAPI.prototype = {
           throw new SpecialPowersError("Invalid operation for SPPrefService");
         }
 
         // Now we make the call
         switch(prefType) {
           case "BOOL":
             if (aMessage.json.op == "get")
               return(prefs.getBoolPref(prefName));
-            else 
+            else
               return(prefs.setBoolPref(prefName, prefValue));
           case "INT":
-            if (aMessage.json.op == "get") 
+            if (aMessage.json.op == "get")
               return(prefs.getIntPref(prefName));
             else
               return(prefs.setIntPref(prefName, prefValue));
           case "CHAR":
             if (aMessage.json.op == "get")
               return(prefs.getCharPref(prefName));
             else
               return(prefs.setCharPref(prefName, prefValue));
@@ -558,16 +558,17 @@ SpecialPowersObserverAPI.prototype = {
 
       case "SPCleanUpSTSData": {
         let origin = aMessage.data.origin;
         let flags = aMessage.data.flags;
         let uri = Services.io.newURI(origin, null, null);
         let sss = Cc["@mozilla.org/ssservice;1"].
                   getService(Ci.nsISiteSecurityService);
         sss.removeState(Ci.nsISiteSecurityService.HEADER_HSTS, uri, flags);
+        return undefined;
       }
 
       case "SPLoadExtension": {
         let {Extension} = Components.utils.import("resource://gre/modules/Extension.jsm", {});
 
         let id = aMessage.data.id;
         let ext = aMessage.data.ext;
         let extension = Extension.generate(ext);
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/blockable/no-opt-in/cross-origin-http/fetch-request/top-level/keep-scheme-redirect/no-opt-in-blocks.https.html.ini
@@ -0,0 +1,3 @@
+[no-opt-in-blocks.https.html]
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/blockable/no-opt-in/cross-origin-http/fetch-request/top-level/no-redirect/no-opt-in-blocks.https.html.ini
@@ -0,0 +1,3 @@
+[no-opt-in-blocks.https.html]
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/blockable/no-opt-in/cross-origin-http/fetch-request/top-level/swap-scheme-redirect/no-opt-in-blocks.https.html.ini
@@ -0,0 +1,3 @@
+[no-opt-in-blocks.https.html]
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/blockable/no-opt-in/cross-origin-http/link-css-tag/top-level/keep-scheme-redirect/no-opt-in-blocks.https.html.ini
@@ -0,0 +1,3 @@
+[no-opt-in-blocks.https.html]
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/blockable/no-opt-in/cross-origin-http/link-css-tag/top-level/no-redirect/no-opt-in-blocks.https.html.ini
@@ -0,0 +1,3 @@
+[no-opt-in-blocks.https.html]
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/blockable/no-opt-in/cross-origin-http/link-css-tag/top-level/swap-scheme-redirect/no-opt-in-blocks.https.html.ini
@@ -0,0 +1,3 @@
+[no-opt-in-blocks.https.html]
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/blockable/no-opt-in/cross-origin-http/object-tag/top-level/keep-scheme-redirect/no-opt-in-blocks.https.html.ini
@@ -0,0 +1,3 @@
+[no-opt-in-blocks.https.html]
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/blockable/no-opt-in/cross-origin-http/object-tag/top-level/no-redirect/no-opt-in-blocks.https.html.ini
@@ -0,0 +1,3 @@
+[no-opt-in-blocks.https.html]
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/blockable/no-opt-in/cross-origin-http/object-tag/top-level/swap-scheme-redirect/no-opt-in-blocks.https.html.ini
@@ -0,0 +1,3 @@
+[no-opt-in-blocks.https.html]
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/blockable/no-opt-in/cross-origin-http/picture-tag/top-level/keep-scheme-redirect/no-opt-in-blocks.https.html.ini
@@ -0,0 +1,3 @@
+[no-opt-in-blocks.https.html]
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/blockable/no-opt-in/cross-origin-http/picture-tag/top-level/no-redirect/no-opt-in-blocks.https.html.ini
@@ -0,0 +1,3 @@
+[no-opt-in-blocks-https.html]
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/blockable/no-opt-in/cross-origin-http/picture-tag/top-level/swap-scheme-redirect/no-opt-in-blocks.https.html.ini
@@ -0,0 +1,3 @@
+[no-opt-in-blocks.https.html]
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/blockable/no-opt-in/cross-origin-http/script-tag/top-level/keep-scheme-redirect/no-opt-in-blocks.https.html.ini
@@ -0,0 +1,3 @@
+[no-opt-in-blocks.https.html]
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/blockable/no-opt-in/cross-origin-http/script-tag/top-level/no-redirect/no-opt-in-blocks.https.html.ini
@@ -0,0 +1,3 @@
+[no-opt-in-blocks.https.html]
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/blockable/no-opt-in/cross-origin-http/script-tag/top-level/swap-scheme-redirect/no-opt-in-blocks.https.html.ini
@@ -0,0 +1,3 @@
+[no-opt-in-blocks.https.html]
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/blockable/no-opt-in/cross-origin-http/xhr-request/top-level/keep-scheme-redirect/no-opt-in-blocks.https.html.ini
@@ -0,0 +1,3 @@
+[no-opt-in-blocks.https.html]
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/blockable/no-opt-in/cross-origin-http/xhr-request/top-level/no-redirect/no-opt-in-blocks.https.html.ini
@@ -0,0 +1,3 @@
+[no-opt-in-blocks.https.html]
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/blockable/no-opt-in/cross-origin-http/xhr-request/top-level/swap-scheme-redirect/no-opt-in-blocks.https.html.ini
@@ -0,0 +1,3 @@
+[no-opt-in-blocks.https.html]
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/blockable/no-opt-in/same-host-http/fetch-request/top-level/keep-scheme-redirect/no-opt-in-blocks.https.html.ini
@@ -0,0 +1,3 @@
+[no-opt-in-blocks.https.html]
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/blockable/no-opt-in/same-host-http/fetch-request/top-level/no-redirect/no-opt-in-blocks.https.html.ini
@@ -0,0 +1,3 @@
+[no-opt-in-blocks.https.html]
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/blockable/no-opt-in/same-host-http/fetch-request/top-level/swap-scheme-redirect/no-opt-in-blocks.https.html.ini
@@ -0,0 +1,3 @@
+[no-opt-in-blocks.https.html]
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/blockable/no-opt-in/same-host-http/link-css-tag/top-level/keep-scheme-redirect/no-opt-in-blocks.https.html.ini
@@ -0,0 +1,3 @@
+[no-opt-in-blocks.https.html]
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/blockable/no-opt-in/same-host-http/link-css-tag/top-level/no-redirect/no-opt-in-blocks.https.html.ini
@@ -0,0 +1,3 @@
+[no-opt-in-blocks.https.html]
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/blockable/no-opt-in/same-host-http/link-css-tag/top-level/swap-scheme-redirect/no-opt-in-blocks.https.html.ini
@@ -0,0 +1,3 @@
+[no-opt-in-blocks.https.html]
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/blockable/no-opt-in/same-host-http/object-tag/top-level/keep-scheme-redirect/no-opt-in-blocks.https.html.ini
@@ -0,0 +1,3 @@
+[no-opt-in-blocks.https.html]
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/blockable/no-opt-in/same-host-http/object-tag/top-level/no-redirect/no-opt-in-blocks.https.html.ini
@@ -0,0 +1,3 @@
+[no-opt-in-blocks.https.html]
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/blockable/no-opt-in/same-host-http/object-tag/top-level/swap-scheme-redirect/no-opt-in-blocks.https.html.ini
@@ -0,0 +1,3 @@
+[no-opt-in-blocks.https.html]
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/blockable/no-opt-in/same-host-http/picture-tag/top-level/keep-scheme-redirect/no-opt-in-blocks.https.html.ini
@@ -0,0 +1,3 @@
+[no-opt-in-blocks.https.html]
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/blockable/no-opt-in/same-host-http/picture-tag/top-level/no-redirect/no-opt-in-blocks.https.html.ini
@@ -0,0 +1,3 @@
+[no-opt-in-blocks.https.html]
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/blockable/no-opt-in/same-host-http/picture-tag/top-level/swap-scheme-redirect/no-opt-in-blocks.https.html.ini
@@ -0,0 +1,3 @@
+[no-opt-in-blocks.https.html]
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/blockable/no-opt-in/same-host-http/script-tag/top-level/keep-scheme-redirect/no-opt-in-blocks.https.html.ini
@@ -0,0 +1,3 @@
+[no-opt-in-blocks.https.html]
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/blockable/no-opt-in/same-host-http/script-tag/top-level/no-redirect/no-opt-in-blocks.https.html.ini
@@ -0,0 +1,3 @@
+[no-opt-in-blocks.https.html]
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/blockable/no-opt-in/same-host-http/script-tag/top-level/swap-scheme-redirect/no-opt-in-blocks.https.html.ini
@@ -0,0 +1,3 @@
+[no-opt-in-blocks.https.html]
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/blockable/no-opt-in/same-host-http/xhr-request/top-level/keep-scheme-redirect/no-opt-in-blocks.https.html.ini
@@ -0,0 +1,3 @@
+[no-opt-in-blocks.https.html]
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/blockable/no-opt-in/same-host-http/xhr-request/top-level/no-redirect/no-opt-in-blocks.https.html.ini
@@ -0,0 +1,3 @@
+[no-opt-in-blocks.https.html]
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/blockable/no-opt-in/same-host-http/xhr-request/top-level/swap-scheme-redirect/no-opt-in-blocks.https.html.ini
@@ -0,0 +1,3 @@
+[no-opt-in-blocks.https.html]
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/audio-tag/top-level/keep-scheme-redirect/no-opt-in-allows.https.html.ini
@@ -0,0 +1,3 @@
+[no-opt-in-allows.https.html]
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/audio-tag/top-level/no-redirect/no-opt-in-allows.https.html.ini
@@ -0,0 +1,3 @@
+[no-opt-in-allows.https.html]
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/audio-tag/top-level/swap-scheme-redirect/no-opt-in-allows.https.html.ini
@@ -0,0 +1,3 @@
+[no-opt-in-allows.https.html]
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/img-tag/top-level/keep-scheme-redirect/no-opt-in-allows.https.html.ini
@@ -0,0 +1,3 @@
+[no-opt-in-allows.https.html]
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/img-tag/top-level/no-redirect/no-opt-in-allows.https.html.ini
@@ -0,0 +1,3 @@
+[no-opt-in-allows.https.html]
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/img-tag/top-level/swap-scheme-redirect/no-opt-in-allows.https.html.ini
@@ -0,0 +1,3 @@
+[no-opt-in-allows.https.html]
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
--- a/testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/link-prefetch-tag/top-level/no-redirect/no-opt-in-allows.https.html.ini
+++ b/testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/link-prefetch-tag/top-level/no-redirect/no-opt-in-allows.https.html.ini
@@ -1,6 +1,8 @@
 [no-opt-in-allows.https.html]
   type: testharness
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
   [opt_in_method: no-opt-in\n                                 origin: cross-origin-http\n                                 source_scheme: https\n                                 context_nesting: top-level\n                                 redirection: no-redirect\n                                 subresource: link-prefetch-tag\n                                 expectation: allowed]
     expected: FAIL
     bug: haven't implement prefetch link as an optionally blockable item
 
--- a/testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/link-prefetch-tag/top-level/swap-scheme-redirect/no-opt-in-allows.https.html.ini
+++ b/testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/link-prefetch-tag/top-level/swap-scheme-redirect/no-opt-in-allows.https.html.ini
@@ -1,6 +1,8 @@
 [no-opt-in-allows.https.html]
   type: testharness
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
   [opt_in_method: no-opt-in\n                                 origin: cross-origin-http\n                                 source_scheme: https\n                                 context_nesting: top-level\n                                 redirection: swap-scheme-redirect\n                                 subresource: link-prefetch-tag\n                                 expectation: allowed]
     expected: FAIL
     bug: haven't implement prefetch link as an optionally blockable item
 
--- a/testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/video-tag/top-level/keep-scheme-redirect/no-opt-in-allows.https.html.ini
+++ b/testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/video-tag/top-level/keep-scheme-redirect/no-opt-in-allows.https.html.ini
@@ -1,7 +1,9 @@
 [no-opt-in-allows.https.html]
   type: testharness
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
   [opt_in_method: no-opt-in\n                                 origin: cross-origin-http\n                                 source_scheme: https\n                                 context_nesting: top-level\n                                 redirection: keep-scheme-redirect\n                                 subresource: video-tag\n                                 expectation: allowed]
     expected:
       if (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): FAIL
     bug: The video this test is using doesn't seem to want to play on WinXP.
 
--- a/testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/video-tag/top-level/no-redirect/no-opt-in-allows.https.html.ini
+++ b/testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/video-tag/top-level/no-redirect/no-opt-in-allows.https.html.ini
@@ -1,7 +1,9 @@
 [no-opt-in-allows.https.html]
   type: testharness
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
   [opt_in_method: no-opt-in\n                                 origin: cross-origin-http\n                                 source_scheme: https\n                                 context_nesting: top-level\n                                 redirection: no-redirect\n                                 subresource: video-tag\n                                 expectation: allowed]
     expected:
       if (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): FAIL
     bug: The video this test is using doesn't seem to want to play on WinXP.
 
--- a/testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/video-tag/top-level/swap-scheme-redirect/no-opt-in-allows.https.html.ini
+++ b/testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/video-tag/top-level/swap-scheme-redirect/no-opt-in-allows.https.html.ini
@@ -1,7 +1,9 @@
 [no-opt-in-allows.https.html]
   type: testharness
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
   [opt_in_method: no-opt-in\n                                 origin: cross-origin-http\n                                 source_scheme: https\n                                 context_nesting: top-level\n                                 redirection: swap-scheme-redirect\n                                 subresource: video-tag\n                                 expectation: allowed]
     expected:
       if (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): FAIL
     bug: The video this test is using doesn't seem to want to play on WinXP.
 
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/same-host-http/audio-tag/top-level/keep-scheme-redirect/no-opt-in-allows.https.html.ini
@@ -0,0 +1,3 @@
+[no-opt-in-allows.https.html]
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/same-host-http/audio-tag/top-level/no-redirect/no-opt-in-allows.https.html.ini
@@ -0,0 +1,3 @@
+[no-opt-in-allows.https.html]
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/same-host-http/audio-tag/top-level/swap-scheme-redirect/no-opt-in-allows.https.html.ini
@@ -0,0 +1,3 @@
+[no-opt-in-allows.https.html]
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/same-host-http/img-tag/top-level/keep-scheme-redirect/no-opt-in-allows.https.html.ini
@@ -0,0 +1,3 @@
+[no-opt-in-allows.https.html]
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/same-host-http/img-tag/top-level/no-redirect/no-opt-in-allows.https.html.ini
@@ -0,0 +1,3 @@
+[no-opt-in-allows.https.html]
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/same-host-http/img-tag/top-level/swap-scheme-redirect/no-opt-in-allows.https.html.ini
@@ -0,0 +1,3 @@
+[no-opt-in-allows.https.html]
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
--- a/testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/same-host-http/link-prefetch-tag/top-level/keep-scheme-redirect/no-opt-in-allows.https.html.ini
+++ b/testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/same-host-http/link-prefetch-tag/top-level/keep-scheme-redirect/no-opt-in-allows.https.html.ini
@@ -1,6 +1,8 @@
 [no-opt-in-allows.https.html]
   type: testharness
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
   [opt_in_method: no-opt-in\n                                 origin: same-host-http\n                                 source_scheme: https\n                                 context_nesting: top-level\n                                 redirection: keep-scheme-redirect\n                                 subresource: link-prefetch-tag\n                                 expectation: allowed]
     expected: FAIL
     bug: haven't implement prefetch link as an optionally blockable item
 
--- a/testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/same-host-http/link-prefetch-tag/top-level/no-redirect/no-opt-in-allows.https.html.ini
+++ b/testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/same-host-http/link-prefetch-tag/top-level/no-redirect/no-opt-in-allows.https.html.ini
@@ -1,6 +1,8 @@
 [no-opt-in-allows.https.html]
   type: testharness
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
   [opt_in_method: no-opt-in\n                                 origin: same-host-http\n                                 source_scheme: https\n                                 context_nesting: top-level\n                                 redirection: no-redirect\n                                 subresource: link-prefetch-tag\n                                 expectation: allowed]
     expected: FAIL
     bug: haven't implement prefetch link as an optionally blockable item
 
--- a/testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/same-host-http/link-prefetch-tag/top-level/swap-scheme-redirect/no-opt-in-allows.https.html.ini
+++ b/testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/same-host-http/link-prefetch-tag/top-level/swap-scheme-redirect/no-opt-in-allows.https.html.ini
@@ -1,6 +1,8 @@
 [no-opt-in-allows.https.html]
   type: testharness
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
   [opt_in_method: no-opt-in\n                                 origin: same-host-http\n                                 source_scheme: https\n                                 context_nesting: top-level\n                                 redirection: swap-scheme-redirect\n                                 subresource: link-prefetch-tag\n                                 expectation: allowed]
     expected: FAIL
     bug: haven't implement prefetch link as an optionally blockable item
 
--- a/testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/same-host-http/video-tag/top-level/keep-scheme-redirect/no-opt-in-allows.https.html.ini
+++ b/testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/same-host-http/video-tag/top-level/keep-scheme-redirect/no-opt-in-allows.https.html.ini
@@ -1,7 +1,9 @@
 [no-opt-in-allows.https.html]
   type: testharness
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
   [opt_in_method: no-opt-in\n                                 origin: same-host-http\n                                 source_scheme: https\n                                 context_nesting: top-level\n                                 redirection: keep-scheme-redirect\n                                 subresource: video-tag\n                                 expectation: allowed]
     expected:
       if (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): FAIL
     bug: The video this test is using doesn't seem to want to play on WinXP.
 
--- a/testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/same-host-http/video-tag/top-level/no-redirect/no-opt-in-allows.https.html.ini
+++ b/testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/same-host-http/video-tag/top-level/no-redirect/no-opt-in-allows.https.html.ini
@@ -1,7 +1,9 @@
 [no-opt-in-allows.https.html]
   type: testharness
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
   [opt_in_method: no-opt-in\n                                 origin: same-host-http\n                                 source_scheme: https\n                                 context_nesting: top-level\n                                 redirection: no-redirect\n                                 subresource: video-tag\n                                 expectation: allowed]
     expected:
       if (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): FAIL
     bug: The video this test is using doesn't seem to want to play on WinXP.
 
--- a/testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/same-host-http/video-tag/top-level/swap-scheme-redirect/no-opt-in-allows.https.html.ini
+++ b/testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/same-host-http/video-tag/top-level/swap-scheme-redirect/no-opt-in-allows.https.html.ini
@@ -1,7 +1,9 @@
 [no-opt-in-allows.https.html]
   type: testharness
+  prefs: [security.mixed_content.send_hsts_priming:false,
+          security.mixed_content.use_hsts:false]
   [opt_in_method: no-opt-in\n                                 origin: same-host-http\n                                 source_scheme: https\n                                 context_nesting: top-level\n                                 redirection: swap-scheme-redirect\n                                 subresource: video-tag\n                                 expectation: allowed]
     expected:
       if (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): FAIL
     bug: The video this test is using doesn't seem to want to play on WinXP.
 
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -7692,16 +7692,32 @@
   },
   "MIXED_CONTENT_HSTS": {
     "alert_emails": ["seceng@mozilla.org"],
     "expires_in_version": "never",
     "kind": "enumerated",
     "n_values": 10,
     "description": "How often would blocked mixed content be allowed if HSTS upgrades were allowed? 0=display/no-HSTS, 1=display/HSTS, 2=active/no-HSTS, 3=active/HSTS"
   },
+  "MIXED_CONTENT_HSTS_PRIMING": {
+    "alert_emails": ["seceng@mozilla.org"],
+    "bug_numbers": [1246540],
+    "expires_in_version": "60",
+    "kind": "enumerated",
+    "n_values": 16,
+    "description": "How often would blocked mixed content be allowed if HSTS upgrades were allowed, including how often would we send an HSTS priming request? 0=display/no-HSTS, 1=display/HSTS, 2=active/no-HSTS, 3=active/HSTS, 4=display/no-HSTS-priming, 5=display/do-HSTS-priming, 6=active/no-HSTS-priming, 7=active/do-HSTS-priming"
+  },
+  "MIXED_CONTENT_HSTS_PRIMING_RESULT": {
+    "alert_emails": ["seceng@mozilla.org"],
+    "bug_numbers": [1246540],
+    "expires_in_version": "60",
+    "kind": "enumerated",
+    "n_values": 16,
+    "description": "How often do we get back an HSTS priming result which upgrades the connection to HTTPS? 0=cached (no upgrade), 1=cached (do upgrade), 2=cached (blocked), 3=already upgraded, 4=priming succeeded, 5=priming succeeded (block due to pref), 6=priming succeeded (no upgrade due to pref), 7=priming failed (block), 8=priming failed (accept)"
+  },
   "MIXED_CONTENT_OBJECT_SUBREQUEST": {
     "alert_emails": ["seceng@mozilla.org"],
     "bug_numbers": [1244116],
     "expires_in_version": "55",
     "kind": "enumerated",
     "n_values": 10,
     "description": "How often objects load insecure content on secure pages (counting pages, not objects). 0=pages with no mixed object subrequests, 1=pages with mixed object subrequests"
   },