Bug 1435733 - Upgrade mixed display content pref. r?mayhemer,baku,francois,ckerschb draft
authorJonathan Kingston <jkt@mozilla.com>
Mon, 05 Feb 2018 15:37:27 +0000
changeset 757897 b895c0fa750bd89b85ab2583d7e23aab2f5ee8ab
parent 757019 f60cfdc81e946cf052cc29b8beae7c50613a3039
push id99871
push userbmo:jkt@mozilla.com
push dateWed, 21 Feb 2018 14:02:49 +0000
reviewersmayhemer, baku, francois, ckerschb
bugs1435733
milestone60.0a1
Bug 1435733 - Upgrade mixed display content pref. r?mayhemer,baku,francois,ckerschb MozReview-Commit-ID: ETIgVF3zhRu
browser/base/content/test/siteIdentity/browser_bug822367.js
browser/base/content/test/siteIdentity/browser_mcb_redirect.js
browser/base/content/test/siteIdentity/browser_mixedContentFramesOnHttp.js
browser/base/content/test/siteIdentity/browser_mixedContentFromOnunload.js
browser/base/content/test/siteIdentity/browser_mixed_passive_content_indicator.js
browser/base/content/test/siteIdentity/browser_mixedcontent_securityflags.js
browser/base/content/test/siteIdentity/browser_no_mcb_for_loopback.js
devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_allow_mixedcontent_securityerrors.js
devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_block_mixedcontent_securityerrors.js
devtools/client/webconsole/test/browser_webconsole_allow_mixedcontent_securityerrors.js
devtools/client/webconsole/test/browser_webconsole_block_mixedcontent_securityerrors.js
dom/base/nsContentUtils.cpp
dom/base/nsContentUtils.h
dom/ipc/ContentPrefs.cpp
dom/locales/en-US/chrome/security/security.properties
dom/security/nsMixedContentBlocker.cpp
dom/security/test/csp/mochitest.ini
dom/security/test/mixedcontentblocker/file_main.html
dom/security/test/mixedcontentblocker/file_server.sjs
dom/security/test/mixedcontentblocker/mochitest.ini
dom/security/test/mixedcontentblocker/test_main.html
image/imgRequest.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/ipc/NeckoChannelParams.ipdlh
netwerk/protocol/http/nsCORSListenerProxy.cpp
security/manager/ssl/tests/mochitest/mixedcontent/mochitest.ini
security/manager/ssl/tests/mochitest/mixedcontent/test_bug455367.html
testing/firefox-ui/tests/functional/security/test_mixed_content_page.py
testing/web-platform/meta/content-security-policy/prefetch-src/prefetch-allowed.html.ini
testing/web-platform/meta/mixed-content/audio-tag/http-csp/same-host-http/top-level/keep-scheme-redirect/optionally-blockable/opt-in-blocks.https.html.ini
testing/web-platform/meta/mixed-content/audio-tag/no-opt-in/cross-origin-http/top-level/keep-scheme-redirect/optionally-blockable/no-opt-in-allows.https.html.ini
testing/web-platform/meta/mixed-content/audio-tag/no-opt-in/cross-origin-http/top-level/no-redirect/optionally-blockable/no-opt-in-allows.https.html.ini
testing/web-platform/meta/mixed-content/audio-tag/no-opt-in/cross-origin-http/top-level/swap-scheme-redirect/optionally-blockable/no-opt-in-allows.https.html.ini
testing/web-platform/meta/mixed-content/audio-tag/no-opt-in/same-host-http/top-level/keep-scheme-redirect/optionally-blockable/no-opt-in-allows.https.html.ini
testing/web-platform/meta/mixed-content/audio-tag/no-opt-in/same-host-http/top-level/no-redirect/optionally-blockable/no-opt-in-allows.https.html.ini
testing/web-platform/meta/mixed-content/audio-tag/no-opt-in/same-host-http/top-level/swap-scheme-redirect/optionally-blockable/no-opt-in-allows.https.html.ini
testing/web-platform/meta/mixed-content/imageset.https.sub.html.ini
testing/web-platform/meta/mixed-content/img-tag/no-opt-in/cross-origin-http/top-level/keep-scheme-redirect/optionally-blockable/no-opt-in-allows.https.html.ini
testing/web-platform/meta/mixed-content/img-tag/no-opt-in/cross-origin-http/top-level/no-redirect/optionally-blockable/no-opt-in-allows.https.html.ini
testing/web-platform/meta/mixed-content/img-tag/no-opt-in/cross-origin-http/top-level/swap-scheme-redirect/optionally-blockable/no-opt-in-allows.https.html.ini
testing/web-platform/meta/mixed-content/img-tag/no-opt-in/same-host-http/top-level/keep-scheme-redirect/optionally-blockable/no-opt-in-allows.https.html.ini
testing/web-platform/meta/mixed-content/img-tag/no-opt-in/same-host-http/top-level/no-redirect/optionally-blockable/no-opt-in-allows.https.html.ini
testing/web-platform/meta/mixed-content/img-tag/no-opt-in/same-host-http/top-level/swap-scheme-redirect/optionally-blockable/no-opt-in-allows.https.html.ini
testing/web-platform/meta/mixed-content/video-tag/no-opt-in/cross-origin-http/top-level/keep-scheme-redirect/optionally-blockable/no-opt-in-allows.https.html.ini
testing/web-platform/meta/mixed-content/video-tag/no-opt-in/cross-origin-http/top-level/no-redirect/optionally-blockable/no-opt-in-allows.https.html.ini
testing/web-platform/meta/mixed-content/video-tag/no-opt-in/cross-origin-http/top-level/swap-scheme-redirect/optionally-blockable/no-opt-in-allows.https.html.ini
testing/web-platform/meta/mixed-content/video-tag/no-opt-in/same-host-http/top-level/keep-scheme-redirect/optionally-blockable/no-opt-in-allows.https.html.ini
testing/web-platform/meta/mixed-content/video-tag/no-opt-in/same-host-http/top-level/no-redirect/optionally-blockable/no-opt-in-allows.https.html.ini
testing/web-platform/meta/mixed-content/video-tag/no-opt-in/same-host-http/top-level/swap-scheme-redirect/optionally-blockable/no-opt-in-allows.https.html.ini
toolkit/components/extensions/test/mochitest/chrome.ini
toolkit/components/extensions/test/mochitest/mochitest-common.ini
toolkit/components/telemetry/Histograms.json
--- a/browser/base/content/test/siteIdentity/browser_bug822367.js
+++ b/browser/base/content/test/siteIdentity/browser_bug822367.js
@@ -1,24 +1,26 @@
 /*
  * User Override Mixed Content Block - Tests for Bug 822367
  */
 
 
 const PREF_DISPLAY = "security.mixed_content.block_display_content";
+const PREF_DISPLAY_UPGRADE = "security.mixed_content.upgrade_display_content";
 const PREF_ACTIVE = "security.mixed_content.block_active_content";
 
 // We alternate for even and odd test cases to simulate different hosts
 const HTTPS_TEST_ROOT = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "https://example.com");
 const HTTPS_TEST_ROOT_2 = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "https://test1.example.com");
 
 var gTestBrowser = null;
 
 add_task(async function test() {
   await SpecialPowers.pushPrefEnv({ set: [[ PREF_DISPLAY, true ],
+                                          [ PREF_DISPLAY_UPGRADE, false ],
                                           [ PREF_ACTIVE, true  ]] });
 
   var newTab = BrowserTestUtils.addTab(gBrowser);
   gBrowser.selectedTab = newTab;
   gTestBrowser = gBrowser.selectedBrowser;
   newTab.linkedBrowser.stop();
 
   // Mixed Script Test
--- a/browser/base/content/test/siteIdentity/browser_mcb_redirect.js
+++ b/browser/base/content/test/siteIdentity/browser_mcb_redirect.js
@@ -50,31 +50,34 @@
  *    https inside an https page
  *    - the image would have gone through two redirects: HTTPS->HTTP->HTTPS,
  *      but instead we try to use the cached image.
  *    - the image should not load
  */
 
 const PREF_ACTIVE = "security.mixed_content.block_active_content";
 const PREF_DISPLAY = "security.mixed_content.block_display_content";
+const PREF_DISPLAY_UPGRADE = "security.mixed_content.upgrade_display_content";
 const HTTPS_TEST_ROOT = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "https://example.com");
 const HTTP_TEST_ROOT = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "http://example.com");
 const PREF_INSECURE_ICON = "security.insecure_connection_icon.enabled";
 
 var origBlockActive;
 var origBlockDisplay;
+var origUpgradeDisplay;
 var origInsecurePref;
 var gTestBrowser = null;
 
 // ------------------------ Helper Functions ---------------------
 
 registerCleanupFunction(function() {
   // Set preferences back to their original values
   Services.prefs.setBoolPref(PREF_ACTIVE, origBlockActive);
   Services.prefs.setBoolPref(PREF_DISPLAY, origBlockDisplay);
+  Services.prefs.setBoolPref(PREF_DISPLAY_UPGRADE, origUpgradeDisplay);
   Services.prefs.setBoolPref(PREF_INSECURE_ICON, origInsecurePref);
 
   // Make sure we are online again
   Services.io.offline = false;
 });
 
 function cleanUpAfterTests() {
   gBrowser.removeCurrentTab();
@@ -293,19 +296,21 @@ function checkLoadEventForTest9() {
 
 function test() {
   // Performing async calls, e.g. 'onload', we have to wait till all of them finished
   waitForExplicitFinish();
 
   // Store original preferences so we can restore settings after testing
   origBlockActive = Services.prefs.getBoolPref(PREF_ACTIVE);
   origBlockDisplay = Services.prefs.getBoolPref(PREF_DISPLAY);
+  origUpgradeDisplay = Services.prefs.getBoolPref(PREF_DISPLAY_UPGRADE);
   origInsecurePref = Services.prefs.getBoolPref(PREF_INSECURE_ICON);
   Services.prefs.setBoolPref(PREF_ACTIVE, true);
   Services.prefs.setBoolPref(PREF_DISPLAY, true);
+  Services.prefs.setBoolPref(PREF_DISPLAY_UPGRADE, false);
 
   var newTab = BrowserTestUtils.addTab(gBrowser);
   gBrowser.selectedTab = newTab;
   gTestBrowser = gBrowser.selectedBrowser;
   newTab.linkedBrowser.stop();
 
   executeSoon(test1);
 }
--- a/browser/base/content/test/siteIdentity/browser_mixedContentFramesOnHttp.js
+++ b/browser/base/content/test/siteIdentity/browser_mixedContentFramesOnHttp.js
@@ -11,17 +11,18 @@
  */
 
 const TEST_URL = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "http://example.com") + "file_mixedContentFramesOnHttp.html";
 
 add_task(async function() {
   await SpecialPowers.pushPrefEnv({
     "set": [
       ["security.mixed_content.block_active_content", true],
-      ["security.mixed_content.block_display_content", false]
+      ["security.mixed_content.block_display_content", false],
+      ["security.mixed_content.upgrade_display_content", false]
     ]});
 
   await BrowserTestUtils.withNewTab(TEST_URL, async function(browser) {
     isSecurityState(browser, "insecure");
     await assertMixedContentBlockingState(browser, {activeLoaded: false, activeBlocked: false, passiveLoaded: true});
   });
 });
 
--- a/browser/base/content/test/siteIdentity/browser_mixedContentFromOnunload.js
+++ b/browser/base/content/test/siteIdentity/browser_mixedContentFromOnunload.js
@@ -14,17 +14,18 @@ const HTTP_TEST_ROOT_2 = getRootDirector
 const HTTPS_TEST_ROOT_2 = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "https://test2.example.com");
 
 add_task(async function() {
   let url = HTTP_TEST_ROOT_1 + "file_mixedContentFromOnunload.html";
   await BrowserTestUtils.withNewTab(url, async function(browser) {
     await SpecialPowers.pushPrefEnv({
       "set": [
         ["security.mixed_content.block_active_content", true],
-        ["security.mixed_content.block_display_content", false]
+        ["security.mixed_content.block_display_content", false],
+        ["security.mixed_content.upgrade_display_content", false]
       ]
     });
     // Navigation from an http page to a https page with no mixed content
     // The http page loads an http image on unload
     url = HTTPS_TEST_ROOT_1 + "file_mixedContentFromOnunload_test1.html";
     await BrowserTestUtils.loadURI(browser, url);
     await BrowserTestUtils.browserLoaded(browser);
     // check security state.  Since current url is https and doesn't have any
--- a/browser/base/content/test/siteIdentity/browser_mixed_passive_content_indicator.js
+++ b/browser/base/content/test/siteIdentity/browser_mixed_passive_content_indicator.js
@@ -1,9 +1,14 @@
 const TEST_URL = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "https://example.com") + "simple_mixed_passive.html";
 
 add_task(async function test_mixed_passive_content_indicator() {
+  await SpecialPowers.pushPrefEnv({
+    "set": [
+      ["security.mixed_content.upgrade_display_content", false]
+    ]
+  });
   await BrowserTestUtils.withNewTab(TEST_URL, function() {
     is(document.getElementById("identity-box").className,
        "unknownIdentity mixedDisplayContent",
        "identity box has class name for mixed content");
   });
 });
--- a/browser/base/content/test/siteIdentity/browser_mixedcontent_securityflags.js
+++ b/browser/base/content/test/siteIdentity/browser_mixedcontent_securityflags.js
@@ -7,29 +7,32 @@
 //   loaded) we load the page and check the flags.
 // * We change the about:config prefs (mixed active blocked, mixed display
 //   blocked), reload the page, and check the flags again.
 // * We override protection so all mixed content can load and check the
 //   flags again.
 
 const TEST_URI = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "https://example.com") + "test-mixedcontent-securityerrors.html";
 const PREF_DISPLAY = "security.mixed_content.block_display_content";
+const PREF_DISPLAY_UPGRADE = "security.mixed_content.upgrade_display_content";
 const PREF_ACTIVE = "security.mixed_content.block_active_content";
 var gTestBrowser = null;
 
 registerCleanupFunction(function() {
   // Set preferences back to their original values
   Services.prefs.clearUserPref(PREF_DISPLAY);
+  Services.prefs.clearUserPref(PREF_DISPLAY_UPGRADE);
   Services.prefs.clearUserPref(PREF_ACTIVE);
   gBrowser.removeCurrentTab();
 });
 
 add_task(async function blockMixedActiveContentTest() {
   // Turn on mixed active blocking and mixed display loading and load the page.
   Services.prefs.setBoolPref(PREF_DISPLAY, false);
+  Services.prefs.setBoolPref(PREF_DISPLAY_UPGRADE, false);
   Services.prefs.setBoolPref(PREF_ACTIVE, true);
 
   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URI);
   gTestBrowser = gBrowser.getBrowserForTab(tab);
 
   await ContentTask.spawn(gTestBrowser, null, function() {
     is(docShell.hasMixedDisplayContentBlocked, false, "hasMixedDisplayContentBlocked flag has been set");
     is(docShell.hasMixedActiveContentBlocked, true, "hasMixedActiveContentBlocked flag has been set");
--- a/browser/base/content/test/siteIdentity/browser_no_mcb_for_loopback.js
+++ b/browser/base/content/test/siteIdentity/browser_no_mcb_for_loopback.js
@@ -16,26 +16,29 @@ if (!gMultiProcessBrowser) {
   PromiseTestUtils.expectUncaughtRejection(/NetworkError/);
 }
 
 const TEST_URL = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "https://example.com") + "test_no_mcb_for_loopback.html";
 
 const LOOPBACK_PNG_URL = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "http://127.0.0.1:8888") + "moz.png";
 
 const PREF_BLOCK_DISPLAY = "security.mixed_content.block_display_content";
+const PREF_UPGRADE_DISPLAY = "security.mixed_content.upgrade_display_content";
 const PREF_BLOCK_ACTIVE = "security.mixed_content.block_active_content";
 
 registerCleanupFunction(function() {
   Services.prefs.clearUserPref(PREF_BLOCK_DISPLAY);
+  Services.prefs.clearUserPref(PREF_UPGRADE_DISPLAY);
   Services.prefs.clearUserPref(PREF_BLOCK_ACTIVE);
   gBrowser.removeCurrentTab();
 });
 
 add_task(async function allowLoopbackMixedContent() {
   Services.prefs.setBoolPref(PREF_BLOCK_DISPLAY, true);
+  Services.prefs.setBoolPref(PREF_UPGRADE_DISPLAY, false);
   Services.prefs.setBoolPref(PREF_BLOCK_ACTIVE, true);
 
   const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
   const browser = gBrowser.getBrowserForTab(tab);
 
   await ContentTask.spawn(browser, null, function() {
     is(docShell.hasMixedDisplayContentBlocked, false, "hasMixedDisplayContentBlocked not set");
     is(docShell.hasMixedActiveContentBlocked, false, "hasMixedActiveContentBlocked not set");
--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_allow_mixedcontent_securityerrors.js
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_allow_mixedcontent_securityerrors.js
@@ -17,16 +17,17 @@ const TEST_URI = "https://example.com/br
   "new-console-output/test/mochitest/test-mixedcontent-securityerrors.html";
 const LEARN_MORE_URI =
   "https://developer.mozilla.org/docs/Web/Security/Mixed_content" + DOCS_GA_PARAMS;
 
 add_task(async function () {
   await Promise.all([
     pushPref("security.mixed_content.block_active_content", false),
     pushPref("security.mixed_content.block_display_content", false),
+    pushPref("security.mixed_content.upgrade_display_content", false),
   ]);
 
   const hud = await openNewTabAndConsole(TEST_URI);
 
   const activeContentText = "Loading mixed (insecure) active content " +
     "\u201chttp://example.com/\u201d on a secure page";
   const displayContentText = "Loading mixed (insecure) display content " +
     "\u201chttp://example.com/tests/image/test/mochitest/blue.png\u201d on a secure page";
--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_block_mixedcontent_securityerrors.js
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_block_mixedcontent_securityerrors.js
@@ -77,12 +77,13 @@ add_task(async function() {
   response = await simulateLinkClick(learnMoreLink);
   is(response.link, LEARN_MORE_URI, `Clicking the provided link opens ${response.link}`);
 });
 
 function pushPrefEnv() {
   const prefs = [
     ["security.mixed_content.block_active_content", true],
     ["security.mixed_content.block_display_content", true],
+    ["security.mixed_content.upgrade_display_content", false],
   ];
 
   return Promise.all(prefs.map(([pref, value]) => pushPref(pref, value)));
 }
--- a/devtools/client/webconsole/test/browser_webconsole_allow_mixedcontent_securityerrors.js
+++ b/devtools/client/webconsole/test/browser_webconsole_allow_mixedcontent_securityerrors.js
@@ -50,16 +50,17 @@ add_task(function* () {
 
   yield testClickOpenNewTab(hud, results);
 });
 
 function pushPrefEnv() {
   let deferred = defer();
   let options = {"set":
       [["security.mixed_content.block_active_content", false],
+       ["security.mixed_content.upgrade_display_content", false],
        ["security.mixed_content.block_display_content", false]
   ]};
   SpecialPowers.pushPrefEnv(options, deferred.resolve);
   return deferred.promise;
 }
 
 function testClickOpenNewTab(hud, results) {
   let warningNode = results[0].clickableElements[0];
--- a/devtools/client/webconsole/test/browser_webconsole_block_mixedcontent_securityerrors.js
+++ b/devtools/client/webconsole/test/browser_webconsole_block_mixedcontent_securityerrors.js
@@ -56,16 +56,17 @@ add_task(function* () {
 });
 
 function pushPrefEnv() {
   let deferred = defer();
   let options = {
     "set": [
       ["security.mixed_content.block_active_content", true],
       ["security.mixed_content.block_display_content", true],
+      ["security.mixed_content.upgrade_display_content", false],
     ]
   };
   SpecialPowers.pushPrefEnv(options, deferred.resolve);
   return deferred.promise;
 }
 
 function mixedContentOverrideTest2(hud, browser) {
   let deferred = defer();
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -285,16 +285,17 @@ nsString* nsContentUtils::sOSText = null
 nsString* nsContentUtils::sAltText = nullptr;
 nsString* nsContentUtils::sModifierSeparator = nullptr;
 
 bool nsContentUtils::sInitialized = false;
 bool nsContentUtils::sIsFullScreenApiEnabled = false;
 bool nsContentUtils::sIsUnprefixedFullscreenApiEnabled = false;
 bool nsContentUtils::sTrustedFullScreenOnly = true;
 bool nsContentUtils::sIsCutCopyAllowed = true;
+bool nsContentUtils::sIsUpgradableDisplayContentPrefEnabled = false;
 bool nsContentUtils::sIsFrameTimingPrefEnabled = false;
 bool nsContentUtils::sIsPerformanceTimingEnabled = false;
 bool nsContentUtils::sIsResourceTimingEnabled = false;
 bool nsContentUtils::sIsPerformanceNavigationTimingEnabled = false;
 bool nsContentUtils::sIsFormAutofillAutocompleteEnabled = false;
 bool nsContentUtils::sIsShadowDOMEnabled = false;
 bool nsContentUtils::sIsCustomElementsEnabled = false;
 bool nsContentUtils::sSendPerformanceTimingNotifications = false;
@@ -644,16 +645,19 @@ nsContentUtils::Init()
                                "dom.enable_performance", true);
 
   Preferences::AddBoolVarCache(&sIsResourceTimingEnabled,
                                "dom.enable_resource_timing", true);
 
   Preferences::AddBoolVarCache(&sIsPerformanceNavigationTimingEnabled,
                                "dom.enable_performance_navigation_timing", true);
 
+  Preferences::AddBoolVarCache(&sIsUpgradableDisplayContentPrefEnabled,
+                               "security.mixed_content.upgrade_display_content", false);
+
   Preferences::AddBoolVarCache(&sIsFrameTimingPrefEnabled,
                                "dom.enable_frame_timing", false);
 
   Preferences::AddBoolVarCache(&sIsFormAutofillAutocompleteEnabled,
                                "dom.forms.autocomplete.formautofill", false);
 
   Preferences::AddBoolVarCache(&sIsShadowDOMEnabled,
                                "dom.webcomponents.shadowdom.enabled", false);
@@ -8818,16 +8822,26 @@ nsContentUtils::GetWindowRoot(nsIDocumen
 bool
 nsContentUtils::IsPreloadType(nsContentPolicyType aType)
 {
   return (aType == nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD ||
           aType == nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD ||
           aType == nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD);
 }
 
+/* static */
+bool
+nsContentUtils::IsUpgradableDisplayType(nsContentPolicyType aType)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  return sIsUpgradableDisplayContentPrefEnabled &&
+         (aType == nsIContentPolicy::TYPE_IMAGE ||
+          aType == nsIContentPolicy::TYPE_MEDIA);
+}
+
 nsresult
 nsContentUtils::SetFetchReferrerURIWithPolicy(nsIPrincipal* aPrincipal,
                                               nsIDocument* aDoc,
                                               nsIHttpChannel* aChannel,
                                               mozilla::net::ReferrerPolicy aReferrerPolicy)
 {
   NS_ENSURE_ARG_POINTER(aPrincipal);
   NS_ENSURE_ARG_POINTER(aChannel);
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -1301,16 +1301,24 @@ public:
    * Returns true if the content policy type is any of:
    *   * TYPE_INTERNAL_SCRIPT_PRELOAD
    *   * TYPE_INTERNAL_IMAGE_PRELOAD
    *   * TYPE_INTERNAL_STYLESHEET_PRELOAD
    */
   static bool IsPreloadType(nsContentPolicyType aType);
 
   /**
+   * Returns true if the pref "security.mixed_content.upgrade_display_content" is true
+   * and the content policy type is any of:
+   *   * TYPE_IMAGE
+   *   * TYPE_MEDIA
+   */
+  static bool IsUpgradableDisplayType(nsContentPolicyType aType);
+
+  /**
    * Quick helper to determine whether there are any mutation listeners
    * of a given type that apply to this content or any of its ancestors.
    * The method has the side effect to call document's MayDispatchMutationEvent
    * using aTargetForSubtreeModified as the parameter.
    *
    * @param aNode  The node to search for listeners
    * @param aType  The type of listener (NS_EVENT_BITS_MUTATION_*)
    * @param aTargetForSubtreeModified The node which is the target of the
@@ -3440,16 +3448,17 @@ private:
   static bool sIsFullScreenApiEnabled;
   static bool sIsUnprefixedFullscreenApiEnabled;
   static bool sTrustedFullScreenOnly;
   static bool sIsCutCopyAllowed;
   static uint32_t sHandlingInputTimeout;
   static bool sIsPerformanceTimingEnabled;
   static bool sIsResourceTimingEnabled;
   static bool sIsPerformanceNavigationTimingEnabled;
+  static bool sIsUpgradableDisplayContentPrefEnabled;
   static bool sIsFrameTimingPrefEnabled;
   static bool sIsFormAutofillAutocompleteEnabled;
   static bool sIsShadowDOMEnabled;
   static bool sIsCustomElementsEnabled;
   static bool sSendPerformanceTimingNotifications;
   static bool sUseActivityCursor;
   static bool sAnimationsAPICoreEnabled;
   static bool sAnimationsAPIElementAnimateEnabled;
--- a/dom/ipc/ContentPrefs.cpp
+++ b/dom/ipc/ContentPrefs.cpp
@@ -299,16 +299,17 @@ const char* mozilla::dom::ContentPrefs::
   "security.data_uri.block_toplevel_data_uri_navigations",
   "security.data_uri.unique_opaque_origin",
   "security.fileuri.strict_origin_policy",
   "security.mixed_content.block_active_content",
   "security.mixed_content.block_display_content",
   "security.mixed_content.block_object_subrequest",
   "security.mixed_content.hsts_priming_cache_timeout",
   "security.mixed_content.send_hsts_priming",
+  "security.mixed_content.upgrade_display_content",
   "security.mixed_content.use_hsts",
   "security.sandbox.content.level",
   "security.sandbox.content.tempDirSuffix",
   "security.sandbox.logging.enabled",
   "security.sandbox.mac.track.violations",
   "security.sandbox.windows.log.stackTraceDepth",
   "svg.disabled",
   "svg.display-lists.hit-testing.enabled",
--- a/dom/locales/en-US/chrome/security/security.properties
+++ b/dom/locales/en-US/chrome/security/security.properties
@@ -81,8 +81,11 @@ MimeTypeMismatch=The resource from “%1$S” was blocked due to MIME type mismatch (X-Content-Type-Options: nosniff).
 XCTOHeaderValueMissing=X-Content-Type-Options header warning: value was “%1$S”; did you mean to send “nosniff”?
 
 BlockScriptWithWrongMimeType=Script from “%1$S” was blocked because of a disallowed MIME type.
 
 # LOCALIZATION NOTE: Do not translate "data: URI".
 BlockTopLevelDataURINavigation=Navigation to toplevel data: URI not allowed (Blocked loading of: “%1$S”)
 BlockSubresourceRedirectToData=Redirecting to insecure data: URI not allowed (Blocked loading of: “%1$S”)
 
+# LOCALIZATION NOTE (browserUpgradeInsecureDisplayRequest):
+# %1$S is the browser name "brandShortName"; %2$S is the URL of the upgraded request; %1$S is the upgraded scheme.
+BrowserUpgradeInsecureDisplayRequest = %1$S is upgrading an insecure display request ‘%2$S’ to use ‘%3$S’
--- a/dom/security/nsMixedContentBlocker.cpp
+++ b/dom/security/nsMixedContentBlocker.cpp
@@ -738,16 +738,27 @@ nsMixedContentBlocker::ShouldLoad(bool a
   // is present on the page.
   nsIDocument* document = docShell->GetDocument();
   MOZ_ASSERT(document, "Expected a document");
   if (isHttpScheme && document->GetUpgradeInsecureRequests(isPreload)) {
     *aDecision = ACCEPT;
     return NS_OK;
   }
 
+
+  // Allow http: mixed content if we are choosing to upgrade them when the
+  // pref "security.mixed_content.upgrade_display_content" is true.
+  // This behaves like GetUpgradeInsecureRequests above in that the channel will
+  // be upgraded to https before fetching any data from the netwerk.
+  bool isUpgradableDisplayType = nsContentUtils::IsUpgradableDisplayType(aContentType);
+  if (isHttpScheme && isUpgradableDisplayType) {
+    *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 (document->GetBlockAllMixedContent(isPreload)) {
     // log a message to the console before returning.
--- a/dom/security/test/csp/mochitest.ini
+++ b/dom/security/test/csp/mochitest.ini
@@ -228,16 +228,18 @@ support-files =
   file_ro_ignore_xfo.html^headers^
   file_data_csp_inheritance.html
   file_data_csp_merge.html
   file_data_doc_ignore_meta_csp.html
   file_report_font_cache-1.html
   file_report_font_cache-2.html
   file_report_font_cache-2.html^headers^
   Ahem.ttf
+prefs =
+  security.mixed_content.upgrade_display_content=false
 
 [test_base-uri.html]
 [test_blob_data_schemes.html]
 [test_connect-src.html]
 [test_CSP.html]
 [test_allow_https_schemes.html]
 [test_bug663567.html]
 [test_bug802872.html]
--- a/dom/security/test/mixedcontentblocker/file_main.html
+++ b/dom/security/test/mixedcontentblocker/file_main.html
@@ -33,229 +33,269 @@ https://bugzilla.mozilla.org/show_bug.cg
     our ping implementation is off by default and does not comply with the current spec (bug 786347)
   case nsIContentPolicy::TYPE_BEACON:
 
   }
      */
 -->
 
 <script>
-  var baseUrl = "http://example.com/tests/dom/security/test/mixedcontentblocker/file_server.sjs";
+  async function init() {
+    var baseUrl = "http://example.com/tests/dom/security/test/mixedcontentblocker/file_server.sjs";
+    var checkLastRequestUrl = "https://example.com/tests/dom/security/test/mixedcontentblocker/file_server.sjs?lastRequest=true";
 
-  //For tests that require setTimeout, set the maximum polling time to 100 x 100ms = 10 seconds.
-  var MAX_COUNT = 100;
-  var TIMEOUT_INTERVAL = 100;
-
-  var testContent = document.getElementById("testContent");
+    //For tests that require setTimeout, set the maximum polling time to 100 x 100ms = 10 seconds.
+    var MAX_COUNT = 100;
+    var TIMEOUT_INTERVAL = 100;
 
-  /* Part 1: Mixed Script tests */
+    var testContent = document.getElementById("testContent");
 
-  // Test 1a: insecure object
-  var object = document.createElement("object");
-  object.data = baseUrl + "?type=object";
-  object.type = "application/x-test";
-  object.width = "200";
-  object.height = "200";
+    async function checkLastRequest() {
+      const response = await fetch(checkLastRequestUrl);
+      return response.json();
+    }
+
+    /* Part 1: Mixed Script tests */
 
-  testContent.appendChild(object);
-
-  var objectCount = 0;
+    // Test 1a: insecure object
+    var object = document.createElement("object");
+    object.data = baseUrl + "?type=object";
+    object.type = "application/x-test";
+    object.width = "200";
+    object.height = "200";
 
-  function objectStatus(object) {
-    // Expose our privileged bits on the object
-    object = SpecialPowers.wrap(object);
+    testContent.appendChild(object);
 
-    if (object.displayedType != SpecialPowers.Ci.nsIObjectLoadingContent.TYPE_NULL) {
-      //object loaded
-      parent.postMessage({"test": "object", "msg": "insecure object loaded"}, "http://mochi.test:8888");
-    }
-    else {
-      if(objectCount < MAX_COUNT) {
-        objectCount++;
-        setTimeout(objectStatus, TIMEOUT_INTERVAL, object);
+    var objectCount = 0;
+
+    function objectStatus(object) {
+      // Expose our privileged bits on the object
+      object = SpecialPowers.wrap(object);
+
+      if (object.displayedType != SpecialPowers.Ci.nsIObjectLoadingContent.TYPE_NULL) {
+        //object loaded
+        parent.postMessage({"test": "object", "msg": "insecure object loaded"}, "http://mochi.test:8888");
       }
       else {
-        //After we have called setTimeout the maximum number of times, assume object is blocked
-        parent.postMessage({"test": "object", "msg": "insecure object blocked"}, "http://mochi.test:8888");
+        if(objectCount < MAX_COUNT) {
+          objectCount++;
+          setTimeout(objectStatus, TIMEOUT_INTERVAL, object);
+        }
+        else {
+          //After we have called setTimeout the maximum number of times, assume object is blocked
+          parent.postMessage({"test": "object", "msg": "insecure object blocked"}, "http://mochi.test:8888");
+        }
+      }
+    }
+
+    // object does not have onload and onerror events. Hence we need a setTimeout to check the object's status
+    setTimeout(objectStatus, TIMEOUT_INTERVAL, object);
+
+    // Test 1b: insecure script
+    var script = document.createElement("script");
+    var scriptLoad = false;
+    var scriptCount = 0;
+    script.src = baseUrl + "?type=script";
+    script.onload = function(e) {
+      parent.postMessage({"test": "script", "msg": "insecure script loaded"}, "http://mochi.test:8888");
+      scriptLoad = true;
+    }
+    testContent.appendChild(script);
+
+    function scriptStatus(script)
+    {
+      if(scriptLoad) {
+        return;
+      }
+      else {
+        if(scriptCount < MAX_COUNT) {
+          scriptCount++;
+          setTimeout(scriptStatus, TIMEOUT_INTERVAL, script);
+        }
+        else {
+          //After we have called setTimeout the maximum number of times, assume script is blocked
+          parent.postMessage({"test": "script", "msg": "insecure script blocked"}, "http://mochi.test:8888");
+        }
+      }
+    }
+
+    // scripts blocked by Content Policy's do not have onerror events (see bug 789856).  Hence we need a setTimeout to check the script's status
+    setTimeout(scriptStatus, TIMEOUT_INTERVAL, script);
+
+
+    // Test 1c: insecure stylesheet
+    var cssStyleSheet = document.createElement("link");
+    cssStyleSheet.rel = "stylesheet";
+    cssStyleSheet.href = baseUrl + "?type=stylesheet";
+    cssStyleSheet.type = "text/css";
+    testContent.appendChild(cssStyleSheet);
+
+    var styleCount = 0;
+
+    function styleStatus(cssStyleSheet) {
+      if( cssStyleSheet.sheet || cssStyleSheet.styleSheet || cssStyleSheet.innerHTML ) {
+        parent.postMessage({"test": "stylesheet", "msg": "insecure stylesheet loaded"}, "http://mochi.test:8888");
+      }
+      else {
+        if(styleCount < MAX_COUNT) {
+          styleCount++;
+          setTimeout(styleStatus, TIMEOUT_INTERVAL, cssStyleSheet);
+        }
+        else {
+          //After we have called setTimeout the maximum number of times, assume stylesheet is blocked
+          parent.postMessage({"test": "stylesheet", "msg": "insecure stylesheet blocked"}, "http://mochi.test:8888");
+        }
       }
     }
-  }
 
-  // object does not have onload and onerror events. Hence we need a setTimeout to check the object's status
-  setTimeout(objectStatus, TIMEOUT_INTERVAL, object);
-
-  // Test 1b: insecure script
-  var script = document.createElement("script");
-  var scriptLoad = false;
-  var scriptCount = 0;
-  script.src = baseUrl + "?type=script";
-  script.onload = function() {
-    parent.postMessage({"test": "script", "msg": "insecure script loaded"}, "http://mochi.test:8888");
-    scriptLoad = true;
-  }
-  testContent.appendChild(script);
+    // link does not have onload and onerror events. Hence we need a setTimeout to check the link's status
+    window.setTimeout(styleStatus, TIMEOUT_INTERVAL, cssStyleSheet);
 
-  function scriptStatus(script)
-  {
-    if(scriptLoad) {
-      return;
+    // Test 1d: insecure iframe
+    var iframe = document.createElement("iframe");
+    iframe.src = baseUrl + "?type=iframe";
+    iframe.onload = function() {
+      parent.postMessage({"test": "iframe", "msg": "insecure iframe loaded"}, "http://mochi.test:8888");
     }
-    else {
-      if(scriptCount < MAX_COUNT) {
-        scriptCount++;
-        setTimeout(scriptStatus, TIMEOUT_INTERVAL, script);
-      }
-      else {
-        //After we have called setTimeout the maximum number of times, assume script is blocked
-        parent.postMessage({"test": "script", "msg": "insecure script blocked"}, "http://mochi.test:8888");
-      }
-    }
-  }
-
-  // scripts blocked by Content Policy's do not have onerror events (see bug 789856).  Hence we need a setTimeout to check the script's status
-  setTimeout(scriptStatus, TIMEOUT_INTERVAL, script);
+    iframe.onerror = function() {
+      parent.postMessage({"test": "iframe", "msg": "insecure iframe blocked"}, "http://mochi.test:8888");
+    };
+    testContent.appendChild(iframe);
 
 
-  // Test 1c: insecure stylesheet
-  var cssStyleSheet = document.createElement("link");
-  cssStyleSheet.rel = "stylesheet";
-  cssStyleSheet.href = baseUrl + "?type=stylesheet";
-  cssStyleSheet.type = "text/css";
-  testContent.appendChild(cssStyleSheet);
+    // Test 1e: insecure xhr
+    await new Promise((resolve) => {
+      var xhr = new XMLHttpRequest;
+      try {
+        xhr.open("GET", baseUrl + "?type=xhr", true);
+        xhr.send();
+        xhr.onloadend = function (oEvent) {
+          if (xhr.status == 200) {
+            parent.postMessage({"test": "xhr", "msg": "insecure xhr loaded"}, "http://mochi.test:8888");
+            resolve();
+          }
+          else {
+            parent.postMessage({"test": "xhr", "msg": "insecure xhr blocked"}, "http://mochi.test:8888");
+            resolve();
+          }
+        }
+      } catch(ex) {
+         parent.postMessage({"test": "xhr", "msg": "insecure xhr blocked"}, "http://mochi.test:8888");
+        resolve();
+      }
+    });
+
+    /* Part 2: Mixed Display tests */
 
-  var styleCount = 0;
+    // Shorthand for all image test variants
+    async function imgHandlers(img, test) {
+      await new Promise((resolve) => {
+        img.onload = async () => {
+          const lastRequest = await checkLastRequest();
+          let message = "insecure image loaded";
+          if (lastRequest.scheme == "https") {
+            message = "secure image loaded after upgrade";
+          }
+          parent.postMessage({"test": test, "msg": message}, "http://mochi.test:8888");
+          resolve();
+        }
+        img.onerror = async () => {
+          let message = "insecure image blocked";
+          parent.postMessage({"test": test, "msg": message}, "http://mochi.test:8888");
+          resolve();
+        }
+      });
+    }
+
+    // Test 2a: insecure image
+    var img = document.createElement("img");
+    img.src = baseUrl + "?type=img";
+    await imgHandlers(img, "image");
+    // We don't need to append the image to the document. Doing so causes the image test to run twice.
 
-  function styleStatus(cssStyleSheet) {
-    if( cssStyleSheet.sheet || cssStyleSheet.styleSheet || cssStyleSheet.innerHTML ) {
-      parent.postMessage({"test": "stylesheet", "msg": "insecure stylesheet loaded"}, "http://mochi.test:8888");
-    } 
-    else {
-      if(styleCount < MAX_COUNT) {
-        styleCount++;
-        setTimeout(styleStatus, TIMEOUT_INTERVAL, cssStyleSheet);
+    // Test 2b: insecure media
+    var media = document.createElement("video");
+    media.src = baseUrl + "?type=media&" + Math.floor((Math.random()*1000)+1);
+    media.width = "320";
+    media.height = "200";
+    media.type = "video/ogg";
+    await new Promise(res => {
+      media.onloadeddata = async () => {
+        const lastRequest = await checkLastRequest();
+        let message = "insecure media loaded";
+        if (lastRequest.scheme == "https") {
+          message = "secure media loaded after upgrade";
+        }
+        parent.postMessage({"test": "media", "msg": message}, "http://mochi.test:8888");
+        res();
+      }
+      media.onerror = function() {
+        parent.postMessage({"test": "media", "msg": "insecure media blocked"}, "http://mochi.test:8888");
+        res();
       }
-      else {
-        //After we have called setTimeout the maximum number of times, assume stylesheet is blocked
-        parent.postMessage({"test": "stylesheet", "msg": "insecure stylesheet blocked"}, "http://mochi.test:8888");
+    });
+    // We don't need to append the video to the document. Doing so causes the image test to run twice.
+
+    /* Part 3: Mixed Active Tests for Image srcset */
+
+    // Test 3a: image with srcset
+    var imgA = document.createElement("img");
+    imgA.srcset = baseUrl + "?type=img";
+    await imgHandlers(imgA, "imageSrcset");
+
+    // Test 3b: image with srcset, using fallback from src, should still use imageset policy
+    var imgB = document.createElement("img");
+    imgB.srcset = " ";
+    imgB.src = baseUrl + "?type=img";
+    await imgHandlers(imgB, "imageSrcsetFallback");
+
+    // Test 3c: image in <picture>
+    var imgC = document.createElement("img");
+    var pictureC = document.createElement("picture");
+    var sourceC = document.createElement("source");
+    sourceC.srcset = baseUrl + "?type=img";
+    pictureC.appendChild(sourceC);
+    pictureC.appendChild(imgC);
+    await imgHandlers(imgC, "imagePicture");
+
+    // Test 3d: Loaded basic image switching to a <picture>, loading
+    //          same source, should still redo the request with new
+    //          policy.
+    var imgD = document.createElement("img");
+    imgD.src = baseUrl + "?type=img";
+    await new Promise(res => {
+      imgD.onload = imgD.onerror = function() {
+        // Whether or not it loads, we want to now append it to a picture and observe
+        var pictureD = document.createElement("picture");
+        var sourceD = document.createElement("source");
+        sourceD.srcset = baseUrl + "?type=img";
+        pictureD.appendChild(sourceD);
+        pictureD.appendChild(imgD);
+        imgHandlers(imgD, "imageJoinPicture");
+        res();
       }
-    }
+    });
+
+    // Test 3e: img load from <picture> source reverts to img.src as it
+    //          is removed -- the new request should revert to mixed
+    //          display policy
+    var imgE = document.createElement("img");
+    var pictureE = document.createElement("picture");
+    var sourceE = document.createElement("source");
+    sourceE.srcset = baseUrl + "?type=img";
+    pictureE.appendChild(sourceE);
+    pictureE.appendChild(imgE);
+    imgE.src = baseUrl + "?type=img";
+    await new Promise(res => {
+      imgE.onload = imgE.onerror = function() {
+        // Whether or not it loads, remove it from the picture and observe
+        pictureE.removeChild(imgE)
+        imgHandlers(imgE, "imageLeavePicture");
+        res();
+      }
+    });
   }
 
-  // link does not have onload and onerror events. Hence we need a setTimeout to check the link's status
-  window.setTimeout(styleStatus, TIMEOUT_INTERVAL, cssStyleSheet);
-
-  // Test 1d: insecure iframe
-  var iframe = document.createElement("iframe");
-  iframe.src = baseUrl + "?type=iframe";
-  iframe.onload = function() {
-    parent.postMessage({"test": "iframe", "msg": "insecure iframe loaded"}, "http://mochi.test:8888");
-  }
-  iframe.onerror = function() {
-    parent.postMessage({"test": "iframe", "msg": "insecure iframe blocked"}, "http://mochi.test:8888");
-  };
-  testContent.appendChild(iframe);
-
-
-  // Test 1e: insecure xhr
-  var xhr = new XMLHttpRequest;
-  try {
-    xhr.open("GET", baseUrl + "?type=xhr", true);
-    xhr.send();
-    xhr.onloadend = function (oEvent) {
-      if (xhr.status == 200) {
-        parent.postMessage({"test": "xhr", "msg": "insecure xhr loaded"}, "http://mochi.test:8888");
-      }
-      else {
-        parent.postMessage({"test": "xhr", "msg": "insecure xhr blocked"}, "http://mochi.test:8888");
-      }
-    }
-  } catch(ex) {
-     parent.postMessage({"test": "xhr", "msg": "insecure xhr blocked"}, "http://mochi.test:8888");
-  }
-
-  /* Part 2: Mixed Display tests */
-
-  // Shorthand for all image test variants
-  function imgHandlers(img, test) {
-    img.onload = function () {
-      parent.postMessage({"test": test, "msg": "insecure image loaded"}, "http://mochi.test:8888");
-    }
-    img.onerror = function() {
-      parent.postMessage({"test": test, "msg": "insecure image blocked"}, "http://mochi.test:8888");
-    }
-  }
-
-  // Test 2a: insecure image
-  var img = document.createElement("img");
-  img.src = "http://mochi.test:8888/tests/image/test/mochitest/blue.png";
-  imgHandlers(img, "image");
-  // We don't need to append the image to the document. Doing so causes the image test to run twice.
-
-  // Test 2b: insecure media
-  var media = document.createElement("video");
-  media.src = "http://mochi.test:8888/tests/dom/media/test/320x240.ogv?" + Math.floor((Math.random()*1000)+1);
-  media.width = "320";
-  media.height = "200";
-  media.type = "video/ogg";
-  media.onloadeddata = function() {
-    parent.postMessage({"test": "media", "msg": "insecure media loaded"}, "http://mochi.test:8888");
-  }
-  media.onerror = function() {
-    parent.postMessage({"test": "media", "msg": "insecure media blocked"}, "http://mochi.test:8888");
-  }
-  // We don't need to append the video to the document. Doing so causes the image test to run twice.
-
-  /* Part 3: Mixed Active Tests for Image srcset */
-
-  // Test 3a: image with srcset
-  var imgA = document.createElement("img");
-  imgA.srcset = "http://mochi.test:8888/tests/image/test/mochitest/blue.png";
-  imgHandlers(imgA, "imageSrcset");
-
-  // Test 3b: image with srcset, using fallback from src, should still use imageset policy
-  var imgB = document.createElement("img");
-  imgB.srcset = " ";
-  imgB.src = "http://mochi.test:8888/tests/image/test/mochitest/blue.png";
-  imgHandlers(imgB, "imageSrcsetFallback");
-
-  // Test 3c: image in <picture>
-  var imgC = document.createElement("img");
-  var pictureC = document.createElement("picture");
-  var sourceC = document.createElement("source");
-  sourceC.srcset = "http://mochi.test:8888/tests/image/test/mochitest/blue.png";
-  pictureC.appendChild(sourceC);
-  pictureC.appendChild(imgC);
-  imgHandlers(imgC, "imagePicture");
-
-  // Test 3d: Loaded basic image switching to a <picture>, loading
-  //          same source, should still redo the request with new
-  //          policy.
-  var imgD = document.createElement("img");
-  imgD.src = "http://mochi.test:8888/tests/image/test/mochitest/blue.png";
-  imgD.onload = imgD.onerror = function() {
-    // Whether or not it loads, we want to now append it to a picture and observe
-    var pictureD = document.createElement("picture");
-    var sourceD = document.createElement("source");
-    sourceD.srcset = "http://mochi.test:8888/tests/image/test/mochitest/blue.png";
-    pictureD.appendChild(sourceD);
-    pictureD.appendChild(imgD);
-    imgHandlers(imgD, "imageJoinPicture");
-  }
-
-  // Test 3e: img load from <picture> source reverts to img.src as it
-  //          is removed -- the new request should revert to mixed
-  //          display policy
-  var imgE = document.createElement("img");
-  var pictureE = document.createElement("picture");
-  var sourceE = document.createElement("source");
-  sourceE.srcset = "http://mochi.test:8888/tests/image/test/mochitest/blue.png";
-  pictureE.appendChild(sourceE);
-  pictureE.appendChild(imgE);
-  imgE.src = "http://mochi.test:8888/tests/image/test/mochitest/blue.png";
-  imgE.onload = imgE.onerror = function() {
-    // Whether or not it loads, remove it from the picture and observe
-    pictureE.removeChild(imgE)
-    imgHandlers(imgE, "imageLeavePicture");
-  }
+  init();
 
 </script>
 </body>
 </html>
--- a/dom/security/test/mixedcontentblocker/file_server.sjs
+++ b/dom/security/test/mixedcontentblocker/file_server.sjs
@@ -1,24 +1,73 @@
+Components.utils.import("resource://gre/modules/NetUtil.jsm");
+
+function loadContentFromFile(path) {
+  // Load the content 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 testContentFile =
+    Components.classes["@mozilla.org/file/directory_service;1"].
+    getService(Components.interfaces.nsIProperties).
+    get("CurWorkD", Components.interfaces.nsIFile);
+  var dirs = path.split("/");
+  for (var i = 0; i < dirs.length; i++) {
+    testContentFile.append(dirs[i]);
+  }
+  var testContentFileStream =
+    Components.classes["@mozilla.org/network/file-input-stream;1"].
+    createInstance(Components.interfaces.nsIFileInputStream);
+  testContentFileStream.init(testContentFile, -1, 0, 0);
+  var testContent = NetUtil.readInputStreamToString(testContentFileStream, testContentFileStream.available());
+  return testContent;
+}
 
 function handleRequest(request, response)
 {
+  const { scheme, host, path } = request;
   // get the Content-Type to serve from the query string
   var contentType = null;
-  request.queryString.split('&').forEach( function (val) {
+  var showLastRequest = false;
+  request.queryString.split('&').forEach(function (val) {
      var [name, value] = val.split('=');
        if (name == "type") {
          contentType = unescape(value);
        }
+       if (name == "lastRequest") {
+         showLastRequest = true;
+       }
   });
 
   // avoid confusing cache behaviors
   response.setHeader("Cache-Control", "no-cache", false);
 
+  if (showLastRequest) {
+    response.setHeader("Content-Type", "text/html", false);
+    response.write(getState("lastRequest"));
+    return;
+  }
+
+  setState("lastRequest", JSON.stringify({
+    scheme,
+    host,
+    path,
+    contentType: contentType || "other"
+  }));
+
   switch (contentType) {
+    case "img":
+      response.setHeader("Content-Type", "image/png", false);
+      response.write(loadContentFromFile("tests/image/test/mochitest/blue.png"));
+      break;
+
+    case "media":
+      response.setHeader("Content-Type", "video/ogg", false);
+      response.write(loadContentFromFile("tests/dom/media/test/320x240.ogv"));
+      break;
+
     case "iframe":
       response.setHeader("Content-Type", "text/html", false);
       response.write("frame content");
       break;
 
     case "script":
       response.setHeader("Content-Type", "application/javascript", false);
       break;
--- a/dom/security/test/mixedcontentblocker/mochitest.ini
+++ b/dom/security/test/mixedcontentblocker/mochitest.ini
@@ -1,10 +1,12 @@
 [DEFAULT]
 tags = mcb
+prefs =
+  security.mixed_content.upgrade_display_content=false
 support-files =
   file_bug803225_test_mailto.html
   file_frameNavigation.html
   file_frameNavigation_blankTarget.html
   file_frameNavigation_grandchild.html
   file_frameNavigation_innermost.html
   file_frameNavigation_secure.html
   file_frameNavigation_secure_grandchild.html
--- a/dom/security/test/mixedcontentblocker/test_main.html
+++ b/dom/security/test/mixedcontentblocker/test_main.html
@@ -8,37 +8,50 @@ https://bugzilla.mozilla.org/show_bug.cg
   <meta charset="utf-8">
   <title>Tests for Bug 62178</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script>
   SpecialPowers.setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
 
-  var counter = 0;
-  var settings = [ [true, true], [true, false], [false, true], [false, false] ];
+  let counter = 0;
+  // blockDisplay blockActive upgradeDisplay
+  const settings = [
+    [true, true, true],
+    [true, false, true],
+    [false, true, true],
+    [false, false, true],
+    [true, true, false],
+    [true, false, false],
+    [false, true, false],
+    [false, false, false],
+  ];
 
-  var blockActive;
-  var blockDisplay;
+  let blockActive;
+  let blockDisplay;
+  let upgradeDisplay;
 
-  //Cycle through 4 different preference settings.
+  //Cycle through 8 different preference settings.
   function changePrefs(otherPrefs, callback) {
     let basePrefs = [["security.mixed_content.block_display_content", settings[counter][0]],
-                     ["security.mixed_content.block_active_content", settings[counter][1]]];
+                     ["security.mixed_content.block_active_content", settings[counter][1]],
+                     ["security.mixed_content.upgrade_display_content", settings[counter][2]]];
     let newPrefs = basePrefs.concat(otherPrefs);
 
     SpecialPowers.pushPrefEnv({"set": newPrefs}, function () {
       blockDisplay = SpecialPowers.getBoolPref("security.mixed_content.block_display_content");
       blockActive = SpecialPowers.getBoolPref("security.mixed_content.block_active_content");
+      upgradeDisplay = SpecialPowers.getBoolPref("security.mixed_content.upgrade_display_content");
       counter++;
       callback();
     });
   }
 
-  var testsToRun = {
+  let testsToRun = {
     iframe: false,
     image: false,
     imageSrcset: false,
     imageSrcsetFallback: false,
     imagePicture: false,
     imageJoinPicture: false,
     imageLeavePicture: false,
     script: false,
@@ -58,36 +71,36 @@ https://bugzilla.mozilla.org/show_bug.cg
 
   function checkTestsCompleted() {
     for (var prop in testsToRun) {
       // some test hasn't run yet so we're not done
       if (!testsToRun[prop])
         return;
     }
     //if the testsToRun are all completed, chnage the pref and run the tests again until we have cycled through all the prefs.
-    if(counter < 4) {
+    if(counter < 8) {
        for (var prop in testsToRun) {
          testsToRun[prop] = false;
        }
       //call to change the preferences
       changePrefs([], function() {
-        log("\nblockDisplay set to "+blockDisplay+", blockActive set to "+blockActive+".");
+        log(`\nblockDisplay set to ${blockDisplay}, blockActive set to ${blockActive}, upgradeDisplay set to ${upgradeDisplay}`);
         reloadFrame();
       });
     }
     else {
       SimpleTest.finish();
     }
   }
 
   var firstTest = true;
 
   function receiveMessage(event) {
     if(firstTest) {
-      log("blockActive set to "+blockActive+", blockDisplay set to "+blockDisplay+".");
+      log(`blockActive set to ${blockActive}, blockDisplay set to ${blockDisplay}, upgradeDisplay set to ${upgradeDisplay}.`);
       firstTest = false;
     }
 
     log("test: "+event.data.test+", msg: "+event.data.msg + " logging message.");
     // test that the load type matches the pref for this type of content
     // (i.e. active vs. display)
 
     switch(event.data.test) {
@@ -116,49 +129,79 @@ https://bugzilla.mozilla.org/show_bug.cg
       case "xhr":
         ok(blockActive == (event.data.msg == "insecure xhr blocked"), "xhr did not follow block_active_content pref");
         testsToRun["xhr"] = true;
         break;
 
       /* Mixed Display tests */
       case "image":
         //test that the image load matches the pref for display content
-        ok(blockDisplay == (event.data.msg == "insecure image blocked"), "image did not follow block_display_content pref");
+        if (upgradeDisplay) {
+          ok(event.data.msg == "secure image loaded after upgrade", "image did not follow upgrade_display_content pref");
+        } else {
+          ok(blockDisplay == (event.data.msg == "insecure image blocked"), "image did not follow block_display_content pref");
+        }
         testsToRun["image"] = true;
         break;
 
       case "media":
-        ok(blockDisplay == (event.data.msg == "insecure media blocked"), "media did not follow block_display_content pref");
+        if (upgradeDisplay) {
+          ok(event.data.msg == "secure media loaded after upgrade", "media did not follow upgrade_display_content pref");
+        } else {
+          ok(blockDisplay == (event.data.msg == "insecure media blocked"), "media did not follow block_display_content pref");
+        }
         testsToRun["media"] = true;
         break;
 
       /* Images using the "imageset" policy, from <img srcset> and <picture>, do not get the mixed display exception */
       case "imageSrcset":
-        ok(blockActive == (event.data.msg == "insecure image blocked"), "imageSrcset did not follow block_active_content pref");
+        // When blockDisplay && blockActive && upgradeDisplay are all true the request is blocked
+        // This appears to be a side effect of blockDisplay taking precedence here.
+        if (event.data.msg == "secure image loaded after upgrade") {
+          ok(upgradeDisplay, "imageSrcset did not follow upgrade_display_content pref");
+        } else {
+          ok(blockActive == (event.data.msg == "insecure image blocked"), "imageSrcset did not follow block_active_content pref");
+        }
         testsToRun["imageSrcset"] = true;
         break;
 
       case "imageSrcsetFallback":
-        ok(blockActive == (event.data.msg == "insecure image blocked"), "imageSrcsetFallback did not follow block_active_content pref");
+        if (event.data.msg == "secure image loaded after upgrade") {
+          ok(upgradeDisplay, "imageSrcsetFallback did not follow upgrade_display_content pref");
+        } else {
+          ok(blockActive == (event.data.msg == "insecure image blocked"), "imageSrcsetFallback did not follow block_active_content pref");
+        }
         testsToRun["imageSrcsetFallback"] = true;
         break;
 
       case "imagePicture":
-        ok(blockActive == (event.data.msg == "insecure image blocked"), "imagePicture did not follow block_active_content pref");
+        if (event.data.msg == "secure image loaded after upgrade") {
+          ok(upgradeDisplay, "imagePicture did not follow upgrade_display_content pref");
+        } else {
+          ok(blockActive == (event.data.msg == "insecure image blocked"), "imagePicture did not follow block_active_content pref");
+        }
         testsToRun["imagePicture"] = true;
         break;
 
       case "imageJoinPicture":
-        ok(blockActive == (event.data.msg == "insecure image blocked"), "imageJoinPicture did not follow block_active_content pref");
+        if (event.data.msg == "secure image loaded after upgrade") {
+          ok(upgradeDisplay, "imageJoinPicture did not follow upgrade_display_content pref");
+        } else {
+          ok(blockActive == (event.data.msg == "insecure image blocked"), "imageJoinPicture did not follow block_active_content pref");
+        }
         testsToRun["imageJoinPicture"] = true;
         break;
 
       // Should return to mixed display mode
       case "imageLeavePicture":
-        ok(blockDisplay == (event.data.msg == "insecure image blocked"), "imageLeavePicture did not follow block_display_content pref");
+        if (event.data.msg == "secure image loaded after upgrade") {
+          ok(upgradeDisplay, "imageLeavePicture did not follow upgrade_display_content pref");
+        } else {
+          ok(blockDisplay == (event.data.msg == "insecure image blocked"), "imageLeavePicture did not follow block_display_content pref");
+        }
         testsToRun["imageLeavePicture"] = true;
         break;
 
     }
     checkTestsCompleted();
   }
 
   function startTest() {
--- a/image/imgRequest.cpp
+++ b/image/imgRequest.cpp
@@ -1345,17 +1345,19 @@ imgRequest::OnRedirectVerifyCallback(nsr
       (!isHttps && !isChrome && !schemeLocal)) {
     MutexAutoLock lock(mMutex);
 
     // The csp directive upgrade-insecure-requests performs an internal redirect
     // to upgrade all requests from http to https before any data is fetched from
     // the network. Do not pollute mHadInsecureRedirect in case of such an internal
     // redirect.
     nsCOMPtr<nsILoadInfo> loadInfo = mChannel->GetLoadInfo();
-    bool upgradeInsecureRequests = loadInfo ? loadInfo->GetUpgradeInsecureRequests()
+    bool upgradeInsecureRequests = loadInfo ?
+                                   loadInfo->GetUpgradeInsecureRequests() ||
+                                   loadInfo->GetBrowserUpgradeInsecureRequests()
                                             : false;
     if (!upgradeInsecureRequests) {
       mHadInsecureRedirect = true;
     }
   }
 
   // Update the final URI.
   mChannel->GetURI(getter_AddRefs(mFinalURI));
--- a/ipc/glue/BackgroundUtils.cpp
+++ b/ipc/glue/BackgroundUtils.cpp
@@ -398,16 +398,17 @@ LoadInfoToLoadInfoArgs(nsILoadInfo *aLoa
       triggeringPrincipalInfo,
       principalToInheritInfo,
       sandboxedLoadingPrincipalInfo,
       optionalResultPrincipalURI,
       aLoadInfo->GetSecurityFlags(),
       aLoadInfo->InternalContentPolicyType(),
       static_cast<uint32_t>(aLoadInfo->GetTainting()),
       aLoadInfo->GetUpgradeInsecureRequests(),
+      aLoadInfo->GetBrowserUpgradeInsecureRequests(),
       aLoadInfo->GetVerifySignedContent(),
       aLoadInfo->GetEnforceSRI(),
       aLoadInfo->GetAllowDocumentToBeAgnosticToCSP(),
       aLoadInfo->GetForceAllowDataURI(),
       aLoadInfo->GetForceInheritPrincipalDropped(),
       aLoadInfo->GetInnerWindowID(),
       aLoadInfo->GetOuterWindowID(),
       aLoadInfo->GetParentOuterWindowID(),
@@ -543,16 +544,17 @@ LoadInfoArgsToLoadInfo(const OptionalLoa
                           clientInfo,
                           reservedClientInfo,
                           initialClientInfo,
                           controller,
                           loadInfoArgs.securityFlags(),
                           loadInfoArgs.contentPolicyType(),
                           static_cast<LoadTainting>(loadInfoArgs.tainting()),
                           loadInfoArgs.upgradeInsecureRequests(),
+                          loadInfoArgs.browserUpgradeInsecureRequests(),
                           loadInfoArgs.verifySignedContent(),
                           loadInfoArgs.enforceSRI(),
                           loadInfoArgs.allowDocumentToBeAgnosticToCSP(),
                           loadInfoArgs.forceAllowDataURI(),
                           loadInfoArgs.forceInheritPrincipalDropped(),
                           loadInfoArgs.innerWindowID(),
                           loadInfoArgs.outerWindowID(),
                           loadInfoArgs.parentOuterWindowID(),
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -2620,16 +2620,23 @@ pref("security.csp.enable_violation_even
 
 // Default Content Security Policy to apply to signed contents.
 pref("security.signed_content.CSP.default", "script-src 'self'; style-src 'self'");
 
 // Mixed content blocking
 pref("security.mixed_content.block_active_content", false);
 pref("security.mixed_content.block_display_content", false);
 
+// Upgrade mixed display content before it's blocked
+#ifdef NIGHTLY_BUILD
+pref("security.mixed_content.upgrade_display_content", true);
+#else
+pref("security.mixed_content.upgrade_display_content", false);
+#endif
+
 // Block sub requests that happen within an object
 #ifdef EARLY_BETA_OR_EARLIER
 pref("security.mixed_content.block_object_subrequest", true);
 #else
 pref("security.mixed_content.block_object_subrequest", false);
 #endif
 
 // Sub-resource integrity
--- a/netwerk/base/LoadInfo.cpp
+++ b/netwerk/base/LoadInfo.cpp
@@ -58,16 +58,17 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadin
   , mClientInfo(aLoadingClientInfo)
   , mController(aController)
   , mLoadingContext(do_GetWeakReference(aLoadingContext))
   , mContextForTopLevelLoad(nullptr)
   , mSecurityFlags(aSecurityFlags)
   , mInternalContentPolicyType(aContentPolicyType)
   , mTainting(LoadTainting::Basic)
   , mUpgradeInsecureRequests(false)
+  , mBrowserUpgradeInsecureRequests(false)
   , mVerifySignedContent(false)
   , mEnforceSRI(false)
   , mAllowDocumentToBeAgnosticToCSP(false)
   , mForceAllowDataURI(false)
   , mOriginalFrameSrcLoad(false)
   , mForceInheritPrincipalDropped(false)
   , mInnerWindowID(0)
   , mOuterWindowID(0)
@@ -177,16 +178,30 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadin
     // if the document forces all requests to be upgraded from http to https, then
     // we should do that for all requests. If it only forces preloads to be upgraded
     // then we should enforce upgrade insecure requests only for preloads.
     mUpgradeInsecureRequests =
       aLoadingContext->OwnerDoc()->GetUpgradeInsecureRequests(false) ||
       (nsContentUtils::IsPreloadType(mInternalContentPolicyType) &&
        aLoadingContext->OwnerDoc()->GetUpgradeInsecureRequests(true));
 
+    uint32_t externalType =
+      nsContentUtils::InternalContentPolicyTypeToExternal(mInternalContentPolicyType);
+    if (nsContentUtils::IsUpgradableDisplayType(externalType)) {
+      nsCOMPtr<nsIURI> uri;
+      mLoadingPrincipal->GetURI(getter_AddRefs(uri));
+      if (uri) {
+        // Checking https not secure context as http://localhost can't be upgraded
+        bool isHttpsScheme;
+        nsresult rv = uri->SchemeIs("https", &isHttpsScheme);
+        if (NS_SUCCEEDED(rv) && isHttpsScheme) {
+          mBrowserUpgradeInsecureRequests = true;
+        }
+      }
+    }
     // if owner doc has content signature, we enforce SRI
     nsCOMPtr<nsIChannel> channel = aLoadingContext->OwnerDoc()->GetChannel();
     if (channel) {
       nsCOMPtr<nsILoadInfo> loadInfo = channel->GetLoadInfo();
       if (loadInfo) {
         mEnforceSRI = loadInfo->GetVerifySignedContent();
       }
     }
@@ -263,16 +278,17 @@ LoadInfo::LoadInfo(nsPIDOMWindowOuter* a
   : mLoadingPrincipal(nullptr)
   , mTriggeringPrincipal(aTriggeringPrincipal)
   , mPrincipalToInherit(nullptr)
   , mContextForTopLevelLoad(do_GetWeakReference(aContextForTopLevelLoad))
   , mSecurityFlags(aSecurityFlags)
   , mInternalContentPolicyType(nsIContentPolicy::TYPE_DOCUMENT)
   , mTainting(LoadTainting::Basic)
   , mUpgradeInsecureRequests(false)
+  , mBrowserUpgradeInsecureRequests(false)
   , mVerifySignedContent(false)
   , mEnforceSRI(false)
   , mAllowDocumentToBeAgnosticToCSP(false)
   , mForceAllowDataURI(false)
   , mOriginalFrameSrcLoad(false)
   , mForceInheritPrincipalDropped(false)
   , mInnerWindowID(0)
   , mOuterWindowID(0)
@@ -338,16 +354,17 @@ LoadInfo::LoadInfo(const LoadInfo& rhs)
   , mController(rhs.mController)
   , mPerformanceStorage(rhs.mPerformanceStorage)
   , mLoadingContext(rhs.mLoadingContext)
   , mContextForTopLevelLoad(rhs.mContextForTopLevelLoad)
   , mSecurityFlags(rhs.mSecurityFlags)
   , mInternalContentPolicyType(rhs.mInternalContentPolicyType)
   , mTainting(rhs.mTainting)
   , mUpgradeInsecureRequests(rhs.mUpgradeInsecureRequests)
+  , mBrowserUpgradeInsecureRequests(rhs.mBrowserUpgradeInsecureRequests)
   , mVerifySignedContent(rhs.mVerifySignedContent)
   , mEnforceSRI(rhs.mEnforceSRI)
   , mAllowDocumentToBeAgnosticToCSP(rhs.mAllowDocumentToBeAgnosticToCSP)
   , mForceAllowDataURI(rhs.mForceAllowDataURI)
   , mOriginalFrameSrcLoad(rhs.mOriginalFrameSrcLoad)
   , mForceInheritPrincipalDropped(rhs.mForceInheritPrincipalDropped)
   , mInnerWindowID(rhs.mInnerWindowID)
   , mOuterWindowID(rhs.mOuterWindowID)
@@ -380,16 +397,17 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadin
                    const Maybe<ClientInfo>& aClientInfo,
                    const Maybe<ClientInfo>& aReservedClientInfo,
                    const Maybe<ClientInfo>& aInitialClientInfo,
                    const Maybe<ServiceWorkerDescriptor>& aController,
                    nsSecurityFlags aSecurityFlags,
                    nsContentPolicyType aContentPolicyType,
                    LoadTainting aTainting,
                    bool aUpgradeInsecureRequests,
+                   bool aBrowserUpgradeInsecureRequests,
                    bool aVerifySignedContent,
                    bool aEnforceSRI,
                    bool aAllowDocumentToBeAgnosticToCSP,
                    bool aForceAllowDataURI,
                    bool aForceInheritPrincipalDropped,
                    uint64_t aInnerWindowID,
                    uint64_t aOuterWindowID,
                    uint64_t aParentOuterWindowID,
@@ -416,16 +434,17 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadin
   , mClientInfo(aClientInfo)
   , mReservedClientInfo(aReservedClientInfo)
   , mInitialClientInfo(aInitialClientInfo)
   , mController(aController)
   , mSecurityFlags(aSecurityFlags)
   , mInternalContentPolicyType(aContentPolicyType)
   , mTainting(aTainting)
   , mUpgradeInsecureRequests(aUpgradeInsecureRequests)
+  , mBrowserUpgradeInsecureRequests(aBrowserUpgradeInsecureRequests)
   , mVerifySignedContent(aVerifySignedContent)
   , mEnforceSRI(aEnforceSRI)
   , mAllowDocumentToBeAgnosticToCSP(aAllowDocumentToBeAgnosticToCSP)
   , mForceAllowDataURI(aForceAllowDataURI)
   , mOriginalFrameSrcLoad(false)
   , mForceInheritPrincipalDropped(aForceInheritPrincipalDropped)
   , mInnerWindowID(aInnerWindowID)
   , mOuterWindowID(aOuterWindowID)
@@ -773,16 +792,23 @@ LoadInfo::InternalContentPolicyType()
 NS_IMETHODIMP
 LoadInfo::GetUpgradeInsecureRequests(bool* aResult)
 {
   *aResult = mUpgradeInsecureRequests;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+LoadInfo::GetBrowserUpgradeInsecureRequests(bool* aResult)
+{
+  *aResult = mBrowserUpgradeInsecureRequests;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 LoadInfo::SetVerifySignedContent(bool aVerifySignedContent)
 {
   MOZ_ASSERT(mInternalContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT,
             "can only verify content for TYPE_DOCUMENT");
   mVerifySignedContent = aVerifySignedContent;
   return NS_OK;
 }
 
@@ -1102,16 +1128,22 @@ LoadInfo::SetIsPreflight()
 }
 
 void
 LoadInfo::SetUpgradeInsecureRequests()
 {
   mUpgradeInsecureRequests = true;
 }
 
+void
+LoadInfo::SetBrowserUpgradeInsecureRequests()
+{
+  mBrowserUpgradeInsecureRequests = true;
+}
+
 NS_IMETHODIMP
 LoadInfo::GetIsPreflight(bool* aIsPreflight)
 {
   *aIsPreflight = mIsPreflight;
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/netwerk/base/LoadInfo.h
+++ b/netwerk/base/LoadInfo.h
@@ -88,16 +88,17 @@ public:
   // This method allows us to override the tainting level in that case.
   //
   // NOTE: This should not be used outside of service worker code! Use
   //       nsILoadInfo::MaybeIncreaseTainting() instead.
   void SynthesizeServiceWorkerTainting(LoadTainting aTainting);
 
   void SetIsPreflight();
   void SetUpgradeInsecureRequests();
+  void SetBrowserUpgradeInsecureRequests();
 
 private:
   // private constructor that is only allowed to be called from within
   // HttpChannelParent and FTPChannelParent declared as friends undeneath.
   // In e10s we can not serialize nsINode, hence we store the innerWindowID.
   // Please note that aRedirectChain uses swapElements.
   LoadInfo(nsIPrincipal* aLoadingPrincipal,
            nsIPrincipal* aTriggeringPrincipal,
@@ -107,16 +108,17 @@ private:
            const Maybe<mozilla::dom::ClientInfo>& aClientInfo,
            const Maybe<mozilla::dom::ClientInfo>& aReservedClientInfo,
            const Maybe<mozilla::dom::ClientInfo>& aInitialClientInfo,
            const Maybe<mozilla::dom::ServiceWorkerDescriptor>& aController,
            nsSecurityFlags aSecurityFlags,
            nsContentPolicyType aContentPolicyType,
            LoadTainting aTainting,
            bool aUpgradeInsecureRequests,
+           bool aBrowserUpgradeInsecureRequests,
            bool aVerifySignedContent,
            bool aEnforceSRI,
            bool aAllowDocumentToBeAgnosticToCSP,
            bool aForceAllowDataURI,
            bool aForceInheritPrincipalDropped,
            uint64_t aInnerWindowID,
            uint64_t aOuterWindowID,
            uint64_t aParentOuterWindowID,
@@ -171,16 +173,17 @@ private:
   RefPtr<mozilla::dom::PerformanceStorage>      mPerformanceStorage;
 
   nsWeakPtr                        mLoadingContext;
   nsWeakPtr                        mContextForTopLevelLoad;
   nsSecurityFlags                  mSecurityFlags;
   nsContentPolicyType              mInternalContentPolicyType;
   LoadTainting                     mTainting;
   bool                             mUpgradeInsecureRequests;
+  bool                             mBrowserUpgradeInsecureRequests;
   bool                             mVerifySignedContent;
   bool                             mEnforceSRI;
   bool                             mAllowDocumentToBeAgnosticToCSP;
   bool                             mForceAllowDataURI;
   bool                             mOriginalFrameSrcLoad;
   bool                             mForceInheritPrincipalDropped;
   uint64_t                         mInnerWindowID;
   uint64_t                         mOuterWindowID;
--- a/netwerk/base/nsILoadInfo.idl
+++ b/netwerk/base/nsILoadInfo.idl
@@ -504,16 +504,23 @@ interface nsILoadInfo : nsISupports
    * requests in e10s where the loadingDocument is not available.
    *
    * Warning: If the loadingDocument is null, then the
    * upgradeInsecureRequests is false.
    */
   [infallible] readonly attribute boolean upgradeInsecureRequests;
 
   /**
+   * Returns true if the the page is https and the content is upgradable from http
+   * requires 'security.mixed_content.upgrade_display_content' pref to be true.
+   * Currently this only upgrades display content but might be expanded to other loads.
+   * This is very similar in implementation to upgradeInsecureRequests but browser set.
+   */
+  [infallible] readonly attribute boolean browserUpgradeInsecureRequests;
+  /**
    * If true, the content of the channel is queued up and checked
    * if it matches a content signature. Note, setting this flag
    * to true will negatively impact performance since the preloader
    * can not start until all of the content is fetched from the
    * netwerk.
    *
    * Only use that in combination with TYPE_DOCUMENT.
    */
--- a/netwerk/base/nsNetUtil.cpp
+++ b/netwerk/base/nsNetUtil.cpp
@@ -2976,37 +2976,61 @@ NS_ShouldSecureUpgrade(nsIURI* aURI,
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (!isHttps) {
     if (aLoadInfo) {
       // If any of the documents up the chain to the root document makes use of
       // the CSP directive 'upgrade-insecure-requests', then it's time to fulfill
       // the promise to CSP and mixed content blocking to upgrade the channel
       // from http to https.
-      if (aLoadInfo->GetUpgradeInsecureRequests()) {
+      if (aLoadInfo->GetUpgradeInsecureRequests() ||
+          aLoadInfo->GetBrowserUpgradeInsecureRequests()) {
         // let's log a message to the console that we are upgrading a request
         nsAutoCString scheme;
         aURI->GetScheme(scheme);
         // append the additional 's' for security to the scheme :-)
         scheme.AppendASCII("s");
         NS_ConvertUTF8toUTF16 reportSpec(aURI->GetSpecOrDefault());
         NS_ConvertUTF8toUTF16 reportScheme(scheme);
 
-        const char16_t* params[] = { reportSpec.get(), reportScheme.get() };
-        uint32_t innerWindowId = aLoadInfo->GetInnerWindowID();
-        CSP_LogLocalizedStr("upgradeInsecureRequest",
-                            params, ArrayLength(params),
-                            EmptyString(), // aSourceFile
-                            EmptyString(), // aScriptSample
-                            0, // aLineNumber
-                            0, // aColumnNumber
-                            nsIScriptError::warningFlag, "CSP",
-                            innerWindowId);
-
-        Telemetry::Accumulate(Telemetry::HTTP_SCHEME_UPGRADE, 4);
+        if (aLoadInfo->GetUpgradeInsecureRequests()) {
+          const char16_t* params[] = { reportSpec.get(), reportScheme.get() };
+          uint32_t innerWindowId = aLoadInfo->GetInnerWindowID();
+          CSP_LogLocalizedStr("upgradeInsecureRequest",
+                              params, ArrayLength(params),
+                              EmptyString(), // aSourceFile
+                              EmptyString(), // aScriptSample
+                              0, // aLineNumber
+                              0, // aColumnNumber
+                              nsIScriptError::warningFlag, "CSP",
+                              innerWindowId);
+          Telemetry::AccumulateCategorical(Telemetry::LABELS_HTTP_SCHEME_UPGRADE_TYPE::CSP);
+        } else {
+          nsCOMPtr<nsIDocument> doc;
+          nsINode* node = aLoadInfo->LoadingNode();
+          if (node) {
+            doc = node->OwnerDoc();
+          }
+
+          nsAutoString brandName;
+          nsresult rv =
+            nsContentUtils::GetLocalizedString(nsContentUtils::eBRAND_PROPERTIES,
+                                               "brandShortName", brandName);
+          if (NS_SUCCEEDED(rv)) {
+            const char16_t* params[] = { brandName.get(), reportSpec.get(), reportScheme.get() };
+            nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
+                                            NS_LITERAL_CSTRING("DATA_URI_BLOCKED"),
+                                            doc,
+                                            nsContentUtils::eSECURITY_PROPERTIES,
+                                            "BrowserUpgradeInsecureDisplayRequest",
+                                            params, ArrayLength(params));
+          }
+          Telemetry::AccumulateCategorical(Telemetry::LABELS_HTTP_SCHEME_UPGRADE_TYPE::BrowserDisplay);
+        }
+
         aShouldUpgrade = true;
         return NS_OK;
       }
     }
 
     // enforce Strict-Transport-Security
     nsISiteSecurityService* sss = gHttpHandler->GetSSService();
     NS_ENSURE_TRUE(sss, NS_ERROR_OUT_OF_MEMORY);
@@ -3020,40 +3044,40 @@ NS_ShouldSecureUpgrade(nsIURI* aURI,
     // 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"));
       if (aAllowSTS) {
-        Telemetry::Accumulate(Telemetry::HTTP_SCHEME_UPGRADE, 3);
+        Telemetry::AccumulateCategorical(Telemetry::LABELS_HTTP_SCHEME_UPGRADE_TYPE::STS);
         aShouldUpgrade = true;
         switch (hstsSource) {
           case nsISiteSecurityService::SOURCE_PRELOAD_LIST:
               Telemetry::Accumulate(Telemetry::HSTS_UPGRADE_SOURCE, 0);
               break;
           case nsISiteSecurityService::SOURCE_ORGANIC_REQUEST:
               Telemetry::Accumulate(Telemetry::HSTS_UPGRADE_SOURCE, 1);
               break;
           case nsISiteSecurityService::SOURCE_UNKNOWN:
           default:
               // record this as an organic request
               Telemetry::Accumulate(Telemetry::HSTS_UPGRADE_SOURCE, 1);
               break;
         }
         return NS_OK;
       } else {
-        Telemetry::Accumulate(Telemetry::HTTP_SCHEME_UPGRADE, 2);
+        Telemetry::AccumulateCategorical(Telemetry::LABELS_HTTP_SCHEME_UPGRADE_TYPE::PrefBlockedSTS);
       }
     } else {
-      Telemetry::Accumulate(Telemetry::HTTP_SCHEME_UPGRADE, 1);
+      Telemetry::AccumulateCategorical(Telemetry::LABELS_HTTP_SCHEME_UPGRADE_TYPE::NoReasonToUpgrade);
     }
   } else {
-    Telemetry::Accumulate(Telemetry::HTTP_SCHEME_UPGRADE, 0);
+    Telemetry::AccumulateCategorical(Telemetry::LABELS_HTTP_SCHEME_UPGRADE_TYPE::AlreadyHTTPS);
   }
   aShouldUpgrade = false;
   return NS_OK;
 }
 
 nsresult
 NS_GetSecureUpgradedURI(nsIURI* aURI, nsIURI** aUpgradedURI)
 {
--- a/netwerk/ipc/NeckoChannelParams.ipdlh
+++ b/netwerk/ipc/NeckoChannelParams.ipdlh
@@ -42,16 +42,17 @@ struct LoadInfoArgs
   PrincipalInfo               triggeringPrincipalInfo;
   OptionalPrincipalInfo       principalToInheritInfo;
   OptionalPrincipalInfo       sandboxedLoadingPrincipalInfo;
   OptionalURIParams           resultPrincipalURI;
   uint32_t                    securityFlags;
   uint32_t                    contentPolicyType;
   uint32_t                    tainting;
   bool                        upgradeInsecureRequests;
+  bool                        browserUpgradeInsecureRequests;
   bool                        verifySignedContent;
   bool                        enforceSRI;
   bool                        allowDocumentToBeAgnosticToCSP;
   bool                        forceAllowDataURI;
   bool                        forceInheritPrincipalDropped;
   uint64_t                    innerWindowID;
   uint64_t                    outerWindowID;
   uint64_t                    parentOuterWindowID;
--- a/netwerk/protocol/http/nsCORSListenerProxy.cpp
+++ b/netwerk/protocol/http/nsCORSListenerProxy.cpp
@@ -873,17 +873,18 @@ CheckUpgradeInsecureRequestsPreventsCORS
   NS_ENSURE_SUCCESS(rv, false);
 
   if (!loadInfo) {
     return false;
   }
 
   // lets see if the loadInfo indicates that the request will
   // be upgraded before fetching any data from the netwerk.
-  return loadInfo->GetUpgradeInsecureRequests();
+  return loadInfo->GetUpgradeInsecureRequests() ||
+         loadInfo->GetBrowserUpgradeInsecureRequests();
 }
 
 
 nsresult
 nsCORSListenerProxy::UpdateChannel(nsIChannel* aChannel,
                                    DataURIHandling aAllowDataURI,
                                    UpdateType aUpdateType)
 {
--- a/security/manager/ssl/tests/mochitest/mixedcontent/mochitest.ini
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/mochitest.ini
@@ -1,10 +1,12 @@
 [DEFAULT]
 skip-if = toolkit == 'android' #TIMED_OUT
+prefs =
+  security.mixed_content.upgrade_display_content=false
 support-files =
   alloworigin.sjs
   backward.html
   bug329869.js
   bug383369step2.html
   bug383369step3.html
   download.auto
   download.auto^headers^
--- a/security/manager/ssl/tests/mochitest/mixedcontent/test_bug455367.html
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_bug455367.html
@@ -11,16 +11,19 @@
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 
   <script class="testbody" type="text/javascript">
   /* import-globals-from mixedContentTest.js */
   "use strict";
 
   function runTest()
   {
+    SpecialPowers.pushPrefEnv(
+      {"set": [["security.mixed_content.upgrade_display_content", false]]},
+      null);
     isSecurityState("broken", "broken");
     finish();
   }
 
   function afterNavigationTest()
   {
     isSecurityState("broken", "broken after navigation");
     finish();
--- a/testing/firefox-ui/tests/functional/security/test_mixed_content_page.py
+++ b/testing/firefox-ui/tests/functional/security/test_mixed_content_page.py
@@ -5,16 +5,17 @@
 from firefox_puppeteer import PuppeteerMixin
 from marionette_harness import MarionetteTestCase
 
 
 class TestMixedContentPage(PuppeteerMixin, MarionetteTestCase):
     def setUp(self):
         super(TestMixedContentPage, self).setUp()
 
+        self.marionette.set_pref('security.mixed_content.upgrade_display_content', False)
         self.locationbar = self.browser.navbar.locationbar
         self.identity_popup = self.locationbar.identity_popup
 
         self.url = 'https://mozqa.com/data/firefox/security/mixedcontent.html'
 
     def tearDown(self):
         try:
             self.identity_popup.close(force=True)
--- a/testing/web-platform/meta/content-security-policy/prefetch-src/prefetch-allowed.html.ini
+++ b/testing/web-platform/meta/content-security-policy/prefetch-src/prefetch-allowed.html.ini
@@ -1,4 +1,5 @@
 [prefetch-allowed.html]
+  prefs: [security.mixed_content.upgrade_display_content:false]
   [Prefetch succeeds when allowed by prefetch-src]
     expected: FAIL
 
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/audio-tag/http-csp/same-host-http/top-level/keep-scheme-redirect/optionally-blockable/opt-in-blocks.https.html.ini
@@ -0,0 +1,2 @@
+[opt-in-blocks.https.html]
+  prefs: [security.mixed_content.upgrade_display_content:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/audio-tag/no-opt-in/cross-origin-http/top-level/keep-scheme-redirect/optionally-blockable/no-opt-in-allows.https.html.ini
@@ -0,0 +1,2 @@
+[no-opt-in-allows.https.html]
+  prefs: [security.mixed_content.upgrade_display_content:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/audio-tag/no-opt-in/cross-origin-http/top-level/no-redirect/optionally-blockable/no-opt-in-allows.https.html.ini
@@ -0,0 +1,2 @@
+[no-opt-in-allows.https.html]
+  prefs: [security.mixed_content.upgrade_display_content:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/audio-tag/no-opt-in/cross-origin-http/top-level/swap-scheme-redirect/optionally-blockable/no-opt-in-allows.https.html.ini
@@ -0,0 +1,2 @@
+[no-opt-in-allows.https.html]
+  prefs: [security.mixed_content.upgrade_display_content:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/audio-tag/no-opt-in/same-host-http/top-level/keep-scheme-redirect/optionally-blockable/no-opt-in-allows.https.html.ini
@@ -0,0 +1,2 @@
+[no-opt-in-allows.https.html]
+  prefs: [security.mixed_content.upgrade_display_content:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/audio-tag/no-opt-in/same-host-http/top-level/no-redirect/optionally-blockable/no-opt-in-allows.https.html.ini
@@ -0,0 +1,2 @@
+[no-opt-in-allows.https.html]
+  prefs: [security.mixed_content.upgrade_display_content:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/audio-tag/no-opt-in/same-host-http/top-level/swap-scheme-redirect/optionally-blockable/no-opt-in-allows.https.html.ini
@@ -0,0 +1,2 @@
+[no-opt-in-allows.https.html]
+  prefs: [security.mixed_content.upgrade_display_content:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/imageset.https.sub.html.ini
@@ -0,0 +1,2 @@
+[imageset.https.sub.html]
+  prefs: [security.mixed_content.upgrade_display_content:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/img-tag/no-opt-in/cross-origin-http/top-level/keep-scheme-redirect/optionally-blockable/no-opt-in-allows.https.html.ini
@@ -0,0 +1,2 @@
+[no-opt-in-allows.https.html]
+  prefs: [security.mixed_content.upgrade_display_content:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/img-tag/no-opt-in/cross-origin-http/top-level/no-redirect/optionally-blockable/no-opt-in-allows.https.html.ini
@@ -0,0 +1,2 @@
+[no-opt-in-allows.https.html]
+  prefs: [security.mixed_content.upgrade_display_content:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/img-tag/no-opt-in/cross-origin-http/top-level/swap-scheme-redirect/optionally-blockable/no-opt-in-allows.https.html.ini
@@ -0,0 +1,2 @@
+[no-opt-in-allows.https.html]
+  prefs: [security.mixed_content.upgrade_display_content:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/img-tag/no-opt-in/same-host-http/top-level/keep-scheme-redirect/optionally-blockable/no-opt-in-allows.https.html.ini
@@ -0,0 +1,2 @@
+[no-opt-in-allows.https.html]
+  prefs: [security.mixed_content.upgrade_display_content:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/img-tag/no-opt-in/same-host-http/top-level/no-redirect/optionally-blockable/no-opt-in-allows.https.html.ini
@@ -0,0 +1,2 @@
+[no-opt-in-allows.https.html]
+  prefs: [security.mixed_content.upgrade_display_content:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/img-tag/no-opt-in/same-host-http/top-level/swap-scheme-redirect/optionally-blockable/no-opt-in-allows.https.html.ini
@@ -0,0 +1,2 @@
+[no-opt-in-allows.https.html]
+  prefs: [security.mixed_content.upgrade_display_content:false]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/mixed-content/video-tag/no-opt-in/cross-origin-http/top-level/keep-scheme-redirect/optionally-blockable/no-opt-in-allows.https.html.ini
@@ -0,0 +1,2 @@
+[no-opt-in-allows.https.html]
+  prefs: [security.mixed_content.upgrade_display_content:false]
--- a/testing/web-platform/meta/mixed-content/video-tag/no-opt-in/cross-origin-http/top-level/no-redirect/optionally-blockable/no-opt-in-allows.https.html.ini
+++ b/testing/web-platform/meta/mixed-content/video-tag/no-opt-in/cross-origin-http/top-level/no-redirect/optionally-blockable/no-opt-in-allows.https.html.ini
@@ -1,6 +1,7 @@
 [no-opt-in-allows.https.html]
+  prefs: [security.mixed_content.upgrade_display_content: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/video-tag/no-opt-in/cross-origin-http/top-level/swap-scheme-redirect/optionally-blockable/no-opt-in-allows.https.html.ini
+++ b/testing/web-platform/meta/mixed-content/video-tag/no-opt-in/cross-origin-http/top-level/swap-scheme-redirect/optionally-blockable/no-opt-in-allows.https.html.ini
@@ -1,6 +1,7 @@
 [no-opt-in-allows.https.html]
+  prefs: [security.mixed_content.upgrade_display_content: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.
 
--- a/testing/web-platform/meta/mixed-content/video-tag/no-opt-in/same-host-http/top-level/keep-scheme-redirect/optionally-blockable/no-opt-in-allows.https.html.ini
+++ b/testing/web-platform/meta/mixed-content/video-tag/no-opt-in/same-host-http/top-level/keep-scheme-redirect/optionally-blockable/no-opt-in-allows.https.html.ini
@@ -1,6 +1,7 @@
 [no-opt-in-allows.https.html]
+  prefs: [security.mixed_content.upgrade_display_content: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/video-tag/no-opt-in/same-host-http/top-level/no-redirect/optionally-blockable/no-opt-in-allows.https.html.ini
+++ b/testing/web-platform/meta/mixed-content/video-tag/no-opt-in/same-host-http/top-level/no-redirect/optionally-blockable/no-opt-in-allows.https.html.ini
@@ -1,6 +1,7 @@
 [no-opt-in-allows.https.html]
+  prefs: [security.mixed_content.upgrade_display_content: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/video-tag/no-opt-in/same-host-http/top-level/swap-scheme-redirect/optionally-blockable/no-opt-in-allows.https.html.ini
+++ b/testing/web-platform/meta/mixed-content/video-tag/no-opt-in/same-host-http/top-level/swap-scheme-redirect/optionally-blockable/no-opt-in-allows.https.html.ini
@@ -1,6 +1,7 @@
 [no-opt-in-allows.https.html]
+  prefs: [security.mixed_content.upgrade_display_content: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/extensions/test/mochitest/chrome.ini
+++ b/toolkit/components/extensions/test/mochitest/chrome.ini
@@ -4,16 +4,18 @@ support-files =
   chrome_cleanup_script.js
   head.js
   head_cookies.js
   file_image_great.png
   file_sample.html
   file_with_images.html
   webrequest_chromeworker.js
   webrequest_test.jsm
+prefs =
+  security.mixed_content.upgrade_display_content=false
 tags = webextensions in-process-webextensions
 
 # NO NEW TESTS.  mochitest-chrome does not run under e10s, avoid adding new
 # tests here unless absolutely necessary.
 
 [test_chrome_ext_contentscript_data_uri.html]
 [test_chrome_ext_contentscript_telemetry.html]
 [test_chrome_ext_contentscript_unrecognizedprop_warning.html]
--- a/toolkit/components/extensions/test/mochitest/mochitest-common.ini
+++ b/toolkit/components/extensions/test/mochitest/mochitest-common.ini
@@ -58,16 +58,18 @@ support-files =
   oauth.html
   redirect_auto.sjs
   redirection.sjs
   return_headers.sjs
   slow_response.sjs
   webrequest_worker.js
   !/dom/tests/mochitest/geolocation/network_geolocation.sjs
   !/toolkit/components/passwordmgr/test/authenticate.sjs
+prefs =
+  security.mixed_content.upgrade_display_content=false
 
 [test_ext_background_api_injection.html]
 [test_ext_background_generated_url.html]
 [test_ext_background_canvas.html]
 [test_ext_background_page.html]
 skip-if = (toolkit == 'android') # android doesn't have devtools
 [test_ext_background_teardown.html]
 [test_ext_clipboard.html]
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -2118,25 +2118,25 @@
     "description": "Whether a HTTP transaction was routed via Alt-Svc or not."
   },
   "HTTP_TRANSACTION_USE_ALTSVC_OE": {
     "record_in_processes": ["main", "content"],
     "expires_in_version": "never",
     "kind": "boolean",
     "description": "Whether a HTTP transaction routed via Alt-Svc was scheme=http"
   },
-  "HTTP_SCHEME_UPGRADE": {
-    "record_in_processes": ["main", "content"],
-    "alert_emails": ["seceng-telemetry@mozilla.com"],
-    "bug_numbers": [1340021],
-    "releaseChannelCollection": "opt-out",
-    "expires_in_version": "never",
-    "kind": "enumerated",
-    "n_values": 10,
-    "description": "Was the URL upgraded to HTTPS?  (0=already HTTPS, 1=no reason to upgrade, 2=STS upgrade blocked by pref, 3=upgraded with STS, 4=upgraded with CSP)"
+  "HTTP_SCHEME_UPGRADE_TYPE": {
+    "record_in_processes": ["main", "content"],
+    "alert_emails": ["seceng-telemetry@mozilla.com", "jkt@mozilla.com"],
+    "bug_numbers": [1340021, 1435733],
+    "releaseChannelCollection": "opt-out",
+    "expires_in_version": "never",
+    "kind": "categorical",
+    "labels": ["AlreadyHTTPS", "NoReasonToUpgrade", "PrefBlockedSTS", "STS", "CSP", "BrowserDisplay"],
+    "description": "Was the URL upgraded to HTTPS?"
   },
   "HTTP_RESPONSE_STATUS_CODE": {
     "record_in_processes": ["main", "content"],
     "alert_emails": ["ckerschbaumer@mozilla.com"],
     "bug_numbers": [1272345, 1296287],
     "expires_in_version": "56",
     "kind": "enumerated",
     "n_values": 12,