Backed out 5 changesets (bug 1427726) for failing linux asan at modules/libjar/test/unit/test_bug407303.js on a CLOSED TREE
authorAndreea Pavel <apavel@mozilla.com>
Wed, 11 Apr 2018 12:46:20 +0300
changeset 412740 f0ae3ef35bcbfd63de832d660e8e5236606575aa
parent 412739 b0ccfd1abb6b24e8d0d92b31c3355c5e75db4261
child 412741 b55d8ad0bf359c2fc9091be40400ba6038bf967f
push id62487
push userapavel@mozilla.com
push dateWed, 11 Apr 2018 09:46:41 +0000
treeherderautoland@f0ae3ef35bcb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1427726, 407303
milestone61.0a1
backs outee9abd6f1ba529d7b5b61c290c87b926ab6b4b2a
b1b76f9dff73d5bd66370691aa3eb20fb56fa6fa
f41cf7811770bb9de1ac59a906c2e246fdce8bcf
cb35e7b10235e00ff9ca7a0fb86e3e7abf5b8241
753ece6c9f1bf415676b97ebc89208909814f2a5
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out 5 changesets (bug 1427726) for failing linux asan at modules/libjar/test/unit/test_bug407303.js on a CLOSED TREE Backed out changeset ee9abd6f1ba5 (bug 1427726) Backed out changeset b1b76f9dff73 (bug 1427726) Backed out changeset f41cf7811770 (bug 1427726) Backed out changeset cb35e7b10235 (bug 1427726) Backed out changeset 753ece6c9f1b (bug 1427726)
browser/base/content/test/performance/browser_preferences_usage.js
docshell/base/nsDocShell.cpp
docshell/base/nsIDocShell.idl
docshell/test/mochitest.ini
dom/base/nsGlobalWindowOuter.cpp
dom/base/test/mochitest.ini
dom/html/test/mochitest.ini
dom/security/test/cors/mochitest.ini
dom/security/test/cors/test_CrossSiteXHR_origin.html
dom/tests/mochitest/whatwg/mochitest.ini
dom/tests/mochitest/whatwg/postMessage.jar
dom/tests/mochitest/whatwg/postMessage.jar^headers^
dom/tests/mochitest/whatwg/test_postMessage_jar.html
dom/workers/test/browser.ini
dom/workers/test/bug1063538.sjs
dom/workers/test/bug1063538_worker.js
dom/workers/test/mochitest.ini
dom/workers/test/test_bug1063538.html
modules/libjar/nsIJARChannel.idl
modules/libjar/nsJARChannel.cpp
modules/libjar/nsJARChannel.h
modules/libjar/test/mochitest/bug1173171.zip
modules/libjar/test/mochitest/bug1173171.zip^headers^
modules/libjar/test/mochitest/bug403331.zip
modules/libjar/test/mochitest/bug403331.zip^headers^
modules/libjar/test/mochitest/mochitest.ini
modules/libjar/test/mochitest/openredirect.sjs
modules/libjar/test/mochitest/test_bug1034143_mapped.html
modules/libjar/test/mochitest/test_bug1173171.html
modules/libjar/test/mochitest/test_bug403331.html
modules/libpref/init/all.js
--- a/browser/base/content/test/performance/browser_preferences_usage.js
+++ b/browser/base/content/test/performance/browser_preferences_usage.js
@@ -92,16 +92,20 @@ add_task(async function startup() {
     "extensions.getAddons.cache.enabled": {
       min: 10,
       max: 55,
     },
     "dom.max_chrome_script_run_time": {
       min: 20,
       max: 55,
     },
+    // This seems to get called frequently only on infra.
+    "network.jar.block-remote-files": {
+      max: 500,
+    },
   };
 
   let startupRecorder = Cc["@mozilla.org/test/startuprecorder;1"].getService().wrappedJSObject;
   await startupRecorder.done;
 
   ok(startupRecorder.data.prefStats, "startupRecorder has prefStats");
 
   checkPrefGetters(startupRecorder.data.prefStats, max, whitelist);
@@ -152,16 +156,18 @@ add_task(async function open_10_tabs() {
     },
     "dom.max_chrome_script_run_time": {
       max: 20,
     },
     "toolkit.cosmeticAnimations.enabled": {
       min: 5,
       max: 20,
     },
+    // This seems to get called frequently only on infra.
+    "network.jar.block-remote-files": { },
   };
 
   Services.prefs.resetStats();
 
   let tabs = [];
   while (tabs.length < 10) {
     tabs.push(await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com", true, true));
   }
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -1605,16 +1605,34 @@ nsDocShell::GetParentCharset(const Encod
                              nsIPrincipal** aPrincipal)
 {
   aCharset = mParentCharset;
   *aCharsetSource = mParentCharsetSource;
   NS_IF_ADDREF(*aPrincipal = mParentCharsetPrincipal);
 }
 
 NS_IMETHODIMP
+nsDocShell::GetChannelIsUnsafe(bool* aUnsafe)
+{
+  *aUnsafe = false;
+
+  nsIChannel* channel = GetCurrentDocChannel();
+  if (!channel) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(channel);
+  if (!jarChannel) {
+    return NS_OK;
+  }
+
+  return jarChannel->GetIsUnsafe(aUnsafe);
+}
+
+NS_IMETHODIMP
 nsDocShell::GetHasMixedActiveContentLoaded(bool* aHasMixedActiveContentLoaded)
 {
   nsCOMPtr<nsIDocument> doc(GetDocument());
   *aHasMixedActiveContentLoaded = doc && doc->GetHasMixedActiveContentLoaded();
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -1662,16 +1680,22 @@ nsDocShell::GetHasTrackingContentLoaded(
 }
 
 NS_IMETHODIMP
 nsDocShell::GetAllowPlugins(bool* aAllowPlugins)
 {
   NS_ENSURE_ARG_POINTER(aAllowPlugins);
 
   *aAllowPlugins = mAllowPlugins;
+  if (!mAllowPlugins) {
+    return NS_OK;
+  }
+
+  bool unsafe;
+  *aAllowPlugins = NS_SUCCEEDED(GetChannelIsUnsafe(&unsafe)) && !unsafe;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocShell::SetAllowPlugins(bool aAllowPlugins)
 {
   mAllowPlugins = aAllowPlugins;
   // XXX should enable or disable a plugin host
@@ -1875,16 +1899,22 @@ nsDocShell::NotifyReflowObservers(bool a
 }
 
 NS_IMETHODIMP
 nsDocShell::GetAllowMetaRedirects(bool* aReturn)
 {
   NS_ENSURE_ARG_POINTER(aReturn);
 
   *aReturn = mAllowMetaRedirects;
+  if (!mAllowMetaRedirects) {
+    return NS_OK;
+  }
+
+  bool unsafe;
+  *aReturn = NS_SUCCEEDED(GetChannelIsUnsafe(&unsafe)) && !unsafe;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocShell::SetAllowMetaRedirects(bool aValue)
 {
   mAllowMetaRedirects = aValue;
   return NS_OK;
@@ -9546,16 +9576,44 @@ nsDocShell::InternalLoad(nsIURI* aURI,
         (aFlags & INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL) &&
          NS_SUCCEEDED(nsContentUtils::URIInheritsSecurityContext(aURI,
                                                                  &inherits)) &&
          inherits) {
       principalToInherit = GetInheritedPrincipal(true);
     }
   }
 
+  // Don't allow loads that would inherit our security context
+  // if this document came from an unsafe channel.
+  {
+    bool willInherit;
+    // This condition needs to match the one in
+    // nsContentUtils::ChannelShouldInheritPrincipal.
+    // Except we reverse the rv check to be safe in case
+    // nsContentUtils::URIInheritsSecurityContext fails here and
+    // succeeds there.
+    rv = nsContentUtils::URIInheritsSecurityContext(aURI, &willInherit);
+    if (NS_FAILED(rv) || willInherit || NS_IsAboutBlank(aURI)) {
+      nsCOMPtr<nsIDocShellTreeItem> treeItem = this;
+      do {
+        nsCOMPtr<nsIDocShell> itemDocShell = do_QueryInterface(treeItem);
+        bool isUnsafe;
+        if (itemDocShell &&
+            NS_SUCCEEDED(itemDocShell->GetChannelIsUnsafe(&isUnsafe)) &&
+            isUnsafe) {
+          return NS_ERROR_DOM_SECURITY_ERR;
+        }
+
+        nsCOMPtr<nsIDocShellTreeItem> parent;
+        treeItem->GetSameTypeParent(getter_AddRefs(parent));
+        parent.swap(treeItem);
+      } while (treeItem);
+    }
+  }
+
   nsIDocument* doc = mContentViewer ? mContentViewer->GetDocument()
                                     : nullptr;
 
   const bool isDocumentAuxSandboxed = doc &&
     (doc->GetSandboxFlags() & SANDBOXED_AUXILIARY_NAVIGATION);
 
   if (aURI && mLoadURIDelegate &&
       (!targetDocShell || targetDocShell == static_cast<nsIDocShell*>(this))) {
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -578,16 +578,23 @@ interface nsIDocShell : nsIDocShellTreeI
 
   /**
    * Find out whether the docshell is currently in the middle of a page
    * transition. This is set just before the pagehide/unload events fire.
    */
   readonly attribute boolean isInUnload;
 
   /**
+   * Find out if the currently loaded document came from a suspicious channel
+   * (such as a JAR channel where the server-returned content type isn't a
+   * known JAR type).
+   */
+  readonly attribute boolean channelIsUnsafe;
+
+  /**
    * This attribute determines whether Mixed Active Content is loaded on the
    * document. When it is true, mixed active content was not blocked and has
    * loaded (or is about to load) on the page. When it is false, mixed active content
    * has not loaded on the page, either because there was no mixed active content
    * requests on the page or such requests were blocked by nsMixedContentBlocker.
    * This boolean is set to true in nsMixedContentBlocker if Mixed Active Content
    * is allowed (either explicitly on the page by the user or when the about:config
    * setting security.mixed_content.block_active_content is set to false).
--- a/docshell/test/mochitest.ini
+++ b/docshell/test/mochitest.ini
@@ -1,24 +1,27 @@
 [DEFAULT]
 support-files =
   bug123696-subframe.html
+  bug369814.jar
+  bug369814.zip
   bug404548-subframe.html
   bug404548-subframe_window.html
   bug413310-post.sjs
   bug413310-subframe.html
   bug529119-window.html
   bug570341_recordevents.html
   bug668513_redirect.html
   bug668513_redirect.html^headers^
   bug691547_frame.html
   dummy_page.html
   file_anchor_scroll_after_document_open.html
   file_bfcache_plus_hash_1.html
   file_bfcache_plus_hash_2.html
+  file_bug369814.html
   file_bug385434_1.html
   file_bug385434_2.html
   file_bug385434_3.html
   file_bug475636.sjs
   file_bug509055.html
   file_bug540462.html
   file_bug580069_1.html
   file_bug580069_2.sjs
@@ -49,16 +52,17 @@ support-files =
   historyframes.html
   start_historyframe.html
   url1_historyframe.html
   url2_historyframe.html
 
 [test_anchor_scroll_after_document_open.html]
 [test_bfcache_plus_hash.html]
 [test_bug123696.html]
+[test_bug369814.html]
 [test_bug384014.html]
 [test_bug385434.html]
 [test_bug387979.html]
 [test_bug402210.html]
 [test_bug404548.html]
 [test_bug413310.html]
 skip-if = true
 # Disabled for too many intermittent failures (bug 719186)
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -2000,16 +2000,24 @@ nsGlobalWindowOuter::SetNewDocument(nsID
       JS::Rooted<JSObject*> obj(cx, newInnerGlobal);
       rv = kungFuDeathGrip->InitClasses(obj);
       NS_ENSURE_SUCCESS(rv, rv);
 
       rv = newInnerWindow->ExecutionReady();
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
+    // If the document comes from a JAR, check if the channel was determined
+    // to be unsafe. If so, permanently disable script on the compartment by
+    // calling Block() and throwing away the key.
+    nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(aDocument->GetChannel());
+    if (jarChannel && jarChannel->GetIsUnsafe()) {
+      xpc::Scriptability::Get(newInnerGlobal).Block();
+    }
+
     if (mArguments) {
       newInnerWindow->DefineArgumentsProperty(mArguments);
       mArguments = nullptr;
     }
 
     // Give the new inner window our chrome event handler (since it
     // doesn't have one).
     newInnerWindow->mChromeEventHandler = mChromeEventHandler;
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -116,16 +116,17 @@ support-files =
   file_bug707142_bom.json
   file_bug707142_utf-16.json
   file_bug708620-2.html
   file_bug708620.html
   file_bug753278.html
   file_bug769117.html
   file_bug782342.txt
   file_bug787778.sjs
+  file_bug804395.jar
   file_bug869432.eventsource
   file_bug869432.eventsource^headers^
   file_bug907892.html
   file_bug945152.jar
   file_bug1274806.html
   file_domwindowutils_animation.html
   file_general_document.html
   file_history_document_open.html
@@ -539,16 +540,17 @@ skip-if = toolkit == 'android' #bug 6870
 [test_bug750096.html]
 [test_bug753278.html]
 [test_bug761120.html]
 [test_bug769117.html]
 [test_bug782342.html]
 [test_bug787778.html]
 [test_bug789315.html]
 [test_bug789856.html]
+[test_bug804395.html]
 [test_bug809003.html]
 [test_bug810494.html]
 [test_bug811701.html]
 [test_bug811701.xhtml]
 [test_bug813919.html]
 [test_bug814576.html]
 [test_bug819051.html]
 [test_bug820909.html]
--- a/dom/html/test/mochitest.ini
+++ b/dom/html/test/mochitest.ini
@@ -14,16 +14,18 @@ support-files =
   bug242709_load.html
   bug277724_iframe1.html
   bug277724_iframe2.xhtml
   bug277890_iframe.html
   bug277890_load.html
   bug340800_iframe.txt
   bug369370-popup.png
   bug372098-link-target.html
+  bug392567.jar
+  bug392567.jar^headers^
   bug441930_iframe.html
   bug445004-inner.html
   bug445004-inner.js
   bug445004-outer-abs.html
   bug445004-outer-rel.html
   bug445004-outer-write.html
   bug446483-iframe.html
   bug448564-echo.sjs
@@ -244,16 +246,17 @@ skip-if = toolkit == 'android' #TIMED_OU
 [test_bug386728.html]
 [test_bug386996.html]
 [test_bug388558.html]
 [test_bug388746.html]
 [test_bug388794.html]
 [test_bug389797.html]
 [test_bug390975.html]
 [test_bug391994.html]
+[test_bug392567.html]
 [test_bug394700.html]
 [test_bug395107.html]
 [test_bug401160.xhtml]
 [test_bug405242.html]
 [test_bug406596.html]
 [test_bug417760.html]
 [test_bug421640.html]
 [test_bug424698.html]
--- a/dom/security/test/cors/mochitest.ini
+++ b/dom/security/test/cors/mochitest.ini
@@ -1,10 +1,11 @@
 [DEFAULT]
 support-files =
   file_CrossSiteXHR_cache_server.sjs
   file_CrossSiteXHR_inner.html
+  file_CrossSiteXHR_inner.jar
   file_CrossSiteXHR_inner_data.sjs
   file_CrossSiteXHR_server.sjs
 
 [test_CrossSiteXHR.html]
 [test_CrossSiteXHR_cache.html]
 [test_CrossSiteXHR_origin.html]
--- a/dom/security/test/cors/test_CrossSiteXHR_origin.html
+++ b/dom/security/test/cors/test_CrossSiteXHR_origin.html
@@ -34,16 +34,19 @@ var origins =
    },
    { server: 'http://ex\xe4mple.test',
      origin: 'http://xn--exmple-cua.test'
    },
    { server: 'http://xn--exmple-cua.test' },
    { server: 'http://\u03c0\u03b1\u03c1\u03ac\u03b4\u03b5\u03b9\u03b3\u03bc\u03b1.\u03b4\u03bf\u03ba\u03b9\u03bc\u03ae',
      origin: 'http://xn--hxajbheg2az3al.xn--jxalpdlp'
    },
+   { origin: 'http://example.org',
+     file: 'jar:http://example.org/tests/dom/security/test/cors/file_CrossSiteXHR_inner.jar!/file_CrossSiteXHR_inner.html'
+   },
    { origin: 'null',
      file: 'http://example.org/tests/dom/security/test/cors/file_CrossSiteXHR_inner_data.sjs'
    },
    ];
 
    //['https://example.com:443'],
    //['https://sub1.test1.example.com:443'],
 
@@ -154,14 +157,16 @@ function* runTest() {
          "wrong events in test for " + allowOrigin);
     }
   }
 
   SimpleTest.finish();
 }
 
 addLoadEvent(function() {
-  gen.next();
+  SpecialPowers.pushPrefEnv({"set": [["network.jar.block-remote-files", false]]}, function() {
+    gen.next();
+  });
 });
 </script>
 </pre>
 </body>
 </html>
--- a/dom/tests/mochitest/whatwg/mochitest.ini
+++ b/dom/tests/mochitest/whatwg/mochitest.ini
@@ -1,16 +1,18 @@
 [DEFAULT]
 support-files =
   browserFu.js
   postMessage_chrome_helper.html
   postMessage_closed_helper.html
   postMessage_hash.html
   postMessage_helper.html
   postMessage_idn_helper.html
+  postMessage.jar
+  postMessage.jar^headers^
   postMessage_joined_helper2.html
   postMessage_joined_helper.html
   postMessage_onOther.html
   postMessage_origin_helper.xhtml
   postMessage_override_helper.html
   postMessage_structured_clone_helper.html
   postMessage_structured_clone_helper.js
   postMessage_throw_helper.html
@@ -22,16 +24,17 @@ support-files =
 [test_MessageEvent_dispatchToOther.html]
 [test_MessageEvent.html]
 [test_postMessage_basehref.html]
 [test_postMessage_closed.html]
 skip-if = toolkit == 'android' #bug 894914 - wrong data - got FAIL, expected message
 [test_postMessage_hash.html]
 [test_postMessage.html]
 [test_postMessage_idn.xhtml]
+[test_postMessage_jar.html]
 [test_postMessage_joined.html]
 [test_postMessage_onOther.html]
 [test_postMessage_origin.xhtml]
 [test_postMessage_override.html]
 [test_postMessage_special.xhtml]
 [test_postMessage_structured_clone.html]
 [test_postMessage_throw.html]
 [test_postMessage_transfer.html]
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..d4ee4abc3d19283fd89574bc21bd8a9b7ba571c4
GIT binary patch
literal 616
zc$^FHW@Zs#U|`^2IJME!X8jU<-&KqZ3^SM+7(^Ik7z*->OMFv{ixbmR<FgWr^fF3v
zb3#Kn8JN3Oio9EZxU_<sfsy3}P&t@r4YuvSZNPK)ueO9Q-=xjew-ujPyfAfctmBQf
zS|uj@I>Ec?hy`!e-}g(E)X#18cztx*&wG!RUBfn)&!4}q?)Rp~X}{K;TlJZH=_=!f
zE|by-Yv~utZ3^Wa=P#{qFc7e?3rlr!$;sGL#!^zUL+sK{Kl4djOrLI+N#W|Uy8X^q
z_<z%JwyzC~^ZJ<2Zl1r!*yH@h2WvPvvIU}ZOETUkd3~H==#a0ti2L<dKj|53C9eqW
z42}G@EwfZ`;cE7}BXb@atkjj@WY0hQPH$o{BlnI=Q4KSSCuz1%QVDKzxD;Ge7;Sn;
zf0p{Y_^yNRYMa&1Hk+N2<5IVk+NC>7V!ex%WQMW9>%Cs@{ylujRlTw7th}tz|EcpI
zy}I;y^Se)F%jGx6N1ADQEcJUOU!q|0qWGe^;>JIZZ%yAYHP#``zgcj?H@*37jney_
z9|Z<%yZQa1l)Sm@vGd!HM}#`L@Ea_;d6U0tQ`=?twDijwV!Gm~Dg7H?g}!mC<FtIu
z85j59;o17Emuo^|PMm!9WWM+UALnzwgO{<c+815g^SXpTYVCQm*Z$v<GLBvN?IAHS
zHR{0q0B=SnIc8ijFTnr;KrX|QMi2{oI$(vQ1GF>{;LXYg(ap#Z1*Cs~j9~x(<;eS+
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/whatwg/postMessage.jar^headers^
@@ -0,0 +1,1 @@
+Content-Type: application/java-archive
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/whatwg/test_postMessage_jar.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=430251
+-->
+<head>
+  <title>postMessage's interaction with pages at jar: URIs</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>        
+  <script type="text/javascript" src="browserFu.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <base href="http://mochi.test:8888/" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=430251">Mozilla Bug 430251</a>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+
+<pre id="test">
+<script class="testbody" type="application/javascript">
+/** Test for Bug 430251 **/
+
+SimpleTest.waitForExplicitFinish();
+
+function receiveMessage(evt)
+{
+  is(evt.origin, "http://mochi.test:8888", "wrong sender");
+  ok(evt.source === window.frames.kid, "wrong source");
+
+  is(evt.data, "finish-test", "wrong data");
+  is(evt.lastEventId, "", "wrong lastEventId");
+
+  SimpleTest.finish();
+}
+
+window.addEventListener("message", receiveMessage);
+
+addLoadEvent(function() {
+  SpecialPowers.pushPrefEnv({"set": [["network.jar.block-remote-files", false]]}, function() {
+    var iframe = document.createElement('iframe');
+    iframe.setAttribute('src', 'jar:http://mochi.test:8888/tests/dom/tests/mochitest/whatwg/postMessage.jar!/postMessage_jar.html');
+    iframe.setAttribute('name', 'kid');
+    document.getElementById("content").appendChild(iframe);
+
+    iframe.onload = function() {
+      window.frames.kid.postMessage("start-test", "http://mochi.test:8888");
+    }
+  });
+});
+</script>
+</pre>
+</body>
+</html>
--- a/dom/workers/test/browser.ini
+++ b/dom/workers/test/browser.ini
@@ -1,11 +1,12 @@
 [DEFAULT]
 support-files =
   bug1047663_tab.html
   bug1047663_worker.sjs
   frame_script.js
   head.js
   !/dom/base/test/file_empty.html
+  !/dom/base/test/file_bug945152.jar
 
 [browser_bug1047663.js]
 [browser_bug1104623.js]
 run-if = buildapp == 'browser'
deleted file mode 100644
--- a/dom/workers/test/bug1063538.sjs
+++ /dev/null
@@ -1,6 +0,0 @@
-function handleRequest(request, response) {
-  response.processAsync();
-  response.write("Hello");
-  setTimeout(function() { response.finish(); }, 100000); // wait 100 seconds.
-}
-
--- a/dom/workers/test/bug1063538_worker.js
+++ b/dom/workers/test/bug1063538_worker.js
@@ -1,25 +1,25 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
-var gURL = "http://example.org/tests/dom/workers/test/bug1063538.sjs";
+var gJar = "jar:http://example.org/tests/dom/base/test/file_bug945152.jar!/data_big.txt";
 var xhr = new XMLHttpRequest({mozAnon: true, mozSystem: true});
 var progressFired = false;
 
 xhr.onloadend = function(e) {
   postMessage({type: 'finish', progressFired: progressFired });
   self.close();
 };
 
 xhr.onprogress = function(e) {
   if (e.loaded > 0) {
     progressFired = true;
     xhr.abort();
   }
 };
 
 onmessage = function(e) {
-  xhr.open("GET", gURL, true);
+  xhr.open("GET", gJar, true);
   xhr.send();
 }
--- a/dom/workers/test/mochitest.ini
+++ b/dom/workers/test/mochitest.ini
@@ -89,16 +89,17 @@ support-files =
   worker_referrer.js
   importScripts_3rdParty_worker.js
   worker_bug1278777.js
   worker_setTimeoutWith0.js
   worker_bug1301094.js
   script_createFile.js
   worker_suspended.js
   window_suspended.html
+  !/dom/base/test/file_bug945152.jar
   !/dom/notification/test/mochitest/MockServices.js
   !/dom/notification/test/mochitest/NotificationTest.js
   !/dom/xhr/tests/relativeLoad_import.js
   !/dom/xhr/tests/relativeLoad_worker.js
   !/dom/xhr/tests/relativeLoad_worker2.js
   !/dom/xhr/tests/subdir/relativeLoad_sub_worker.js
   !/dom/xhr/tests/subdir/relativeLoad_sub_worker2.js
   !/dom/xhr/tests/subdir/relativeLoad_sub_import.js
--- a/dom/workers/test/test_bug1063538.html
+++ b/dom/workers/test/test_bug1063538.html
@@ -33,15 +33,17 @@ function runTest() {
   };
 
   worker.postMessage(true);
 }
 
 SimpleTest.waitForExplicitFinish();
 
 addLoadEvent(function() {
-  SpecialPowers.pushPermissions([{'type': 'systemXHR', 'allow': true, 'context': document}], runTest);
+  SpecialPowers.pushPrefEnv({"set": [["network.jar.block-remote-files", false]]}, function() {
+    SpecialPowers.pushPermissions([{'type': 'systemXHR', 'allow': true, 'context': document}], runTest);
+  });
 });
 
 </script>
 </pre>
 </body>
 </html>
--- a/modules/libjar/nsIJARChannel.idl
+++ b/modules/libjar/nsIJARChannel.idl
@@ -7,16 +7,24 @@
 
 interface nsIFile;
 interface nsIZipEntry;
 
 [scriptable, builtinclass, uuid(e72b179b-d5df-4d87-b5de-fd73a65c60f6)]
 interface nsIJARChannel : nsIChannel
 {
     /**
+     * Returns TRUE if the JAR file is not safe (if the content type reported
+     * by the server for a remote JAR is not of an expected type).  Scripting,
+     * redirects, and plugins should be disabled when loading from this
+     * channel.
+     */
+    [infallible] readonly attribute boolean isUnsafe;
+
+    /**
      * Returns the JAR file.  May be null if the jar is remote.
      * Setting the JAR file is optional and overrides the JAR
      * file used for local file JARs. Setting the JAR file after
      * the channel has been opened is not permitted.
      */
     attribute nsIFile jarFile;
 
     /**
--- a/modules/libjar/nsJARChannel.cpp
+++ b/modules/libjar/nsJARChannel.cpp
@@ -189,24 +189,29 @@ nsJARInputThunk::IsNonBlocking(bool *non
 
 //-----------------------------------------------------------------------------
 // nsJARChannel
 //-----------------------------------------------------------------------------
 
 
 nsJARChannel::nsJARChannel()
     : mOpened(false)
+    , mContentDisposition(0)
     , mContentLength(-1)
     , mLoadFlags(LOAD_NORMAL)
     , mStatus(NS_OK)
     , mIsPending(false)
     , mEnableOMT(true)
     , mPendingEvent()
+    , mIsUnsafe(true)
+    , mBlockRemoteFiles(false)
 {
     LOG(("nsJARChannel::nsJARChannel [this=%p]\n", this));
+    mBlockRemoteFiles = Preferences::GetBool("network.jar.block-remote-files", false);
+
     // hold an owning reference to the jar handler
     mJarHandler = gJarHandler;
 }
 
 nsJARChannel::~nsJARChannel()
 {
     LOG(("nsJARChannel::~nsJARChannel [this=%p]\n", this));
     NS_ReleaseOnMainThreadSystemGroup("nsJARChannel::mLoadInfo",
@@ -258,44 +263,50 @@ nsJARChannel::Init(nsIURI *uri)
     return rv;
 }
 
 nsresult
 nsJARChannel::CreateJarInput(nsIZipReaderCache *jarCache, nsJARInputThunk **resultInput)
 {
     LOG(("nsJARChannel::CreateJarInput [this=%p]\n", this));
     MOZ_ASSERT(resultInput);
-    MOZ_ASSERT(mJarFile);
+    MOZ_ASSERT(mJarFile || mTempMem);
 
     // important to pass a clone of the file since the nsIFile impl is not
     // necessarily MT-safe
     nsCOMPtr<nsIFile> clonedFile;
     nsresult rv = NS_OK;
     if (mJarFile) {
         rv = mJarFile->Clone(getter_AddRefs(clonedFile));
         if (NS_FAILED(rv))
             return rv;
     }
 
     nsCOMPtr<nsIZipReader> reader;
     if (mPreCachedJarReader) {
         reader = mPreCachedJarReader;
     } else if (jarCache) {
+        MOZ_ASSERT(mJarFile);
         if (mInnerJarEntry.IsEmpty())
             rv = jarCache->GetZip(clonedFile, getter_AddRefs(reader));
         else
             rv = jarCache->GetInnerZip(clonedFile, mInnerJarEntry,
                                        getter_AddRefs(reader));
     } else {
         // create an uncached jar reader
         nsCOMPtr<nsIZipReader> outerReader = do_CreateInstance(kZipReaderCID, &rv);
         if (NS_FAILED(rv))
             return rv;
 
-        rv = outerReader->Open(clonedFile);
+        if (mJarFile) {
+            rv = outerReader->Open(clonedFile);
+        } else {
+            rv = outerReader->OpenMemory(mTempMem->Elements(),
+                                         mTempMem->Length());
+        }
         if (NS_FAILED(rv))
             return rv;
 
         if (mInnerJarEntry.IsEmpty())
             reader = outerReader;
         else {
             reader = do_CreateInstance(kZipReaderCID, &rv);
             if (NS_FAILED(rv))
@@ -428,16 +439,19 @@ nsJARChannel::OpenLocalFile()
     LOG(("nsJARChannel::OpenLocalFile [this=%p]\n", this));
 
     MOZ_ASSERT(NS_IsMainThread());
 
     MOZ_ASSERT(mWorker);
     MOZ_ASSERT(mIsPending);
     MOZ_ASSERT(mJarFile);
 
+    // Local files are always considered safe.
+    mIsUnsafe = false;
+
     nsresult rv;
 
     // Set mLoadGroup and mOpened before AsyncOpen return, and set back if
     // if failed when callback.
     if (mLoadGroup) {
         mLoadGroup->AddRequest(this, nullptr);
     }
     mOpened = true;
@@ -880,17 +894,21 @@ nsJARChannel::SetContentCharset(const ns
 {
     mContentCharset = aContentCharset;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsJARChannel::GetContentDisposition(uint32_t *aContentDisposition)
 {
-    return NS_ERROR_NOT_AVAILABLE;
+    if (mContentDispositionHeader.IsEmpty())
+        return NS_ERROR_NOT_AVAILABLE;
+
+    *aContentDisposition = mContentDisposition;
+    return NS_OK;
 }
 
 NS_IMETHODIMP
 nsJARChannel::SetContentDisposition(uint32_t aContentDisposition)
 {
     return NS_ERROR_NOT_AVAILABLE;
 }
 
@@ -904,17 +922,21 @@ NS_IMETHODIMP
 nsJARChannel::SetContentDispositionFilename(const nsAString &aContentDispositionFilename)
 {
     return NS_ERROR_NOT_AVAILABLE;
 }
 
 NS_IMETHODIMP
 nsJARChannel::GetContentDispositionHeader(nsACString &aContentDispositionHeader)
 {
-    return NS_ERROR_NOT_AVAILABLE;
+    if (mContentDispositionHeader.IsEmpty())
+        return NS_ERROR_NOT_AVAILABLE;
+
+    aContentDispositionHeader = mContentDispositionHeader;
+    return NS_OK;
 }
 
 NS_IMETHODIMP
 nsJARChannel::GetContentLength(int64_t *result)
 {
     *result = mContentLength;
     return NS_OK;
 }
@@ -931,34 +953,37 @@ NS_IMETHODIMP
 nsJARChannel::Open(nsIInputStream **stream)
 {
     LOG(("nsJARChannel::Open [this=%p]\n", this));
 
     NS_ENSURE_TRUE(!mOpened, NS_ERROR_IN_PROGRESS);
     NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
 
     mJarFile = nullptr;
+    mIsUnsafe = true;
 
     nsresult rv = LookupFile();
     if (NS_FAILED(rv))
         return rv;
 
-    // If mJarFile was not set by LookupFile, we can't open a channel.
+    // If mJarInput was not set by LookupFile, the JAR is a remote jar.
     if (!mJarFile) {
-        NS_NOTREACHED("only file-backed jars are supported");
+        NS_NOTREACHED("need sync downloader");
         return NS_ERROR_NOT_IMPLEMENTED;
     }
 
     RefPtr<nsJARInputThunk> input;
     rv = CreateJarInput(gJarHandler->JarCache(), getter_AddRefs(input));
     if (NS_FAILED(rv))
         return rv;
 
     input.forget(stream);
     mOpened = true;
+    // local files are always considered safe
+    mIsUnsafe = false;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsJARChannel::Open2(nsIInputStream** aStream)
 {
     LOG(("nsJARChannel::Open2 [this=%p]\n", this));
     nsCOMPtr<nsIStreamListener> listener;
@@ -973,75 +998,138 @@ nsJARChannel::AsyncOpen(nsIStreamListene
     LOG(("nsJARChannel::AsyncOpen [this=%p]\n", this));
     MOZ_ASSERT(!mLoadInfo ||
                mLoadInfo->GetSecurityMode() == 0 ||
                mLoadInfo->GetInitialSecurityCheckDone() ||
                (mLoadInfo->GetSecurityMode() == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL &&
                 nsContentUtils::IsSystemPrincipal(mLoadInfo->LoadingPrincipal())),
                "security flags in loadInfo but asyncOpen2() not called");
 
+    LOG(("nsJARChannel::AsyncOpen [this=%p]\n", this));
+
     NS_ENSURE_ARG_POINTER(listener);
     NS_ENSURE_TRUE(!mOpened, NS_ERROR_IN_PROGRESS);
     NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
 
     mJarFile = nullptr;
+    mIsUnsafe = true;
 
     // Initialize mProgressSink
     NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup, mProgressSink);
 
     mListener = listener;
     mListenerContext = ctx;
     mIsPending = true;
 
     nsresult rv = LookupFile();
-    if (NS_FAILED(rv) || !mJarFile) {
-        // Not a local file...
-        mIsPending = false;
-        mListenerContext = nullptr;
-        mListener = nullptr;
-        mCallbacks = nullptr;
-        mProgressSink = nullptr;
-        return mJarFile ? rv : NS_ERROR_UNSAFE_CONTENT_TYPE;
-    }
-
-    rv = OpenLocalFile();
     if (NS_FAILED(rv)) {
         mIsPending = false;
         mListenerContext = nullptr;
         mListener = nullptr;
         mCallbacks = nullptr;
         mProgressSink = nullptr;
         return rv;
     }
 
-    return NS_OK;
-}
+    nsCOMPtr<nsIChannel> channel;
+
+    if (!mJarFile) {
+        // Not a local file...
+
+        // Check preferences to see if all remote jar support should be disabled
+        if (mBlockRemoteFiles) {
+            mIsUnsafe = true;
+            mIsPending = false;
+            mListenerContext = nullptr;
+            mListener = nullptr;
+            mCallbacks = nullptr;
+            mProgressSink = nullptr;
+            return NS_ERROR_UNSAFE_CONTENT_TYPE;
+        }
 
-NS_IMETHODIMP
-nsJARChannel::AsyncOpen2(nsIStreamListener *aListener)
-{
-    LOG(("nsJARChannel::AsyncOpen2 [this=%p]\n", this));
-    nsCOMPtr<nsIStreamListener> listener = aListener;
-    nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
+        // kick off an async download of the base URI...
+        nsCOMPtr<nsIStreamListener> downloader = new MemoryDownloader(this);
+        uint32_t loadFlags =
+            mLoadFlags & ~(LOAD_DOCUMENT_URI | LOAD_CALL_CONTENT_SNIFFERS);
+        rv = NS_NewChannelInternal(getter_AddRefs(channel),
+                                   mJarBaseURI,
+                                   mLoadInfo,
+                                   nullptr, // PerformanceStorage
+                                   mLoadGroup,
+                                   mCallbacks,
+                                   loadFlags);
+        if (NS_FAILED(rv)) {
+            mIsPending = false;
+            mListenerContext = nullptr;
+            mListener = nullptr;
+            mCallbacks = nullptr;
+            mProgressSink = nullptr;
+            return rv;
+        }
+        if (mLoadInfo && mLoadInfo->GetEnforceSecurity()) {
+            rv = channel->AsyncOpen2(downloader);
+        }
+        else {
+            rv = channel->AsyncOpen(downloader, nullptr);
+        }
+
+    }
+    else {
+        rv = OpenLocalFile();
+        if (NS_SUCCEEDED(rv)) {
+            return NS_OK;
+        }
+    }
+
     if (NS_FAILED(rv)) {
         mIsPending = false;
         mListenerContext = nullptr;
         mListener = nullptr;
         mCallbacks = nullptr;
         mProgressSink = nullptr;
         return rv;
     }
 
-    return AsyncOpen(listener, nullptr);
+    if (mLoadGroup)
+        mLoadGroup->AddRequest(this, nullptr);
+
+    mOpened = true;
+    LOG(("nsJARChannel::AsyncOpen [this=%p] 8\n", this));
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsJARChannel::AsyncOpen2(nsIStreamListener *aListener)
+{
+    LOG(("nsJARChannel::AsyncOpen2 [this=%p]\n", this));
+  nsCOMPtr<nsIStreamListener> listener = aListener;
+  nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
+  if (NS_FAILED(rv)) {
+      mIsPending = false;
+      mListenerContext = nullptr;
+      mListener = nullptr;
+      mCallbacks = nullptr;
+      mProgressSink = nullptr;
+      return rv;
+  }
+
+  return AsyncOpen(listener, nullptr);
 }
 
 //-----------------------------------------------------------------------------
 // nsIJARChannel
 //-----------------------------------------------------------------------------
 NS_IMETHODIMP
+nsJARChannel::GetIsUnsafe(bool *isUnsafe)
+{
+    *isUnsafe = mIsUnsafe;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
 nsJARChannel::GetJarFile(nsIFile **aFile)
 {
     NS_IF_ADDREF(*aFile = mJarFile);
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsJARChannel::SetJarFile(nsIFile *aFile)
@@ -1118,16 +1206,126 @@ nsJARChannel::GetZipEntry(nsIZipEntry **
     rv = gJarHandler->JarCache()->GetZip(mJarFile, getter_AddRefs(reader));
     if (NS_FAILED(rv))
         return rv;
 
     return reader->GetEntry(mJarEntry, aZipEntry);
 }
 
 //-----------------------------------------------------------------------------
+// mozilla::net::MemoryDownloader::IObserver
+//-----------------------------------------------------------------------------
+
+void
+nsJARChannel::OnDownloadComplete(MemoryDownloader* aDownloader,
+                                 nsIRequest    *request,
+                                 nsISupports   *context,
+                                 nsresult       status,
+                                 MemoryDownloader::Data aData)
+{
+    nsresult rv;
+
+    nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
+    if (channel) {
+        uint32_t loadFlags;
+        channel->GetLoadFlags(&loadFlags);
+        if (loadFlags & LOAD_REPLACE) {
+            // Update our URI to reflect any redirects that happen during
+            // the HTTP request.
+            if (!mOriginalURI) {
+                SetOriginalURI(mJarURI);
+            }
+
+            nsCOMPtr<nsIURI> innerURI;
+            rv = channel->GetURI(getter_AddRefs(innerURI));
+            if (NS_SUCCEEDED(rv)) {
+                nsCOMPtr<nsIJARURI> newURI;
+                rv = mJarURI->CloneWithJARFile(innerURI,
+                                               getter_AddRefs(newURI));
+                if (NS_SUCCEEDED(rv)) {
+                    mJarURI = newURI;
+                }
+            }
+            if (NS_SUCCEEDED(status)) {
+                status = rv;
+            }
+        }
+    }
+
+    if (NS_SUCCEEDED(status) && channel) {
+        // In case the load info object has changed during a redirect,
+        // grab it from the target channel.
+        channel->GetLoadInfo(getter_AddRefs(mLoadInfo));
+        // Grab the security info from our base channel
+        channel->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
+
+        nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
+        if (httpChannel) {
+            // We only want to run scripts if the server really intended to
+            // send us a JAR file.  Check the server-supplied content type for
+            // a JAR type.
+            nsAutoCString header;
+            Unused << httpChannel->GetResponseHeader(
+              NS_LITERAL_CSTRING("Content-Type"), header);
+            nsAutoCString contentType;
+            nsAutoCString charset;
+            NS_ParseResponseContentType(header, contentType, charset);
+            nsAutoCString channelContentType;
+            channel->GetContentType(channelContentType);
+            mIsUnsafe = !(contentType.Equals(channelContentType) &&
+                          (contentType.EqualsLiteral("application/java-archive") ||
+                           contentType.EqualsLiteral("application/x-jar")));
+        } else {
+            nsCOMPtr<nsIJARChannel> innerJARChannel(do_QueryInterface(channel));
+            if (innerJARChannel) {
+                mIsUnsafe = innerJARChannel->GetIsUnsafe();
+            }
+        }
+
+        channel->GetContentDispositionHeader(mContentDispositionHeader);
+        mContentDisposition = NS_GetContentDispositionFromHeader(mContentDispositionHeader, this);
+    }
+
+    // This is a defense-in-depth check for the preferences to see if all remote jar
+    // support should be disabled. This check may not be needed.
+    MOZ_RELEASE_ASSERT(!mBlockRemoteFiles);
+
+    if (NS_SUCCEEDED(status) && mIsUnsafe &&
+        !Preferences::GetBool("network.jar.open-unsafe-types", false)) {
+        status = NS_ERROR_UNSAFE_CONTENT_TYPE;
+    }
+
+    if (NS_SUCCEEDED(status)) {
+        // Refuse to unpack view-source: jars even if open-unsafe-types is set.
+        nsCOMPtr<nsIViewSourceChannel> viewSource = do_QueryInterface(channel);
+        if (viewSource) {
+            status = NS_ERROR_UNSAFE_CONTENT_TYPE;
+        }
+    }
+
+    if (NS_SUCCEEDED(status)) {
+        mTempMem = Move(aData);
+
+        RefPtr<nsJARInputThunk> input;
+        rv = CreateJarInput(nullptr, getter_AddRefs(input));
+        if (NS_SUCCEEDED(rv)) {
+            // create input stream pump
+            rv = NS_NewInputStreamPump(getter_AddRefs(mPump), input.forget());
+            if (NS_SUCCEEDED(rv))
+                rv = mPump->AsyncRead(this, nullptr);
+        }
+        status = rv;
+    }
+
+    if (NS_FAILED(status)) {
+        NotifyError(status);
+    }
+}
+
+//-----------------------------------------------------------------------------
 // nsIStreamListener
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 nsJARChannel::OnStartRequest(nsIRequest *req, nsISupports *ctx)
 {
     LOG(("nsJARChannel::OnStartRequest [this=%p %s]\n", this, mSpec.get()));
 
--- a/modules/libjar/nsJARChannel.h
+++ b/modules/libjar/nsJARChannel.h
@@ -28,16 +28,17 @@
 
 class nsJARInputThunk;
 class nsJARProtocolHandler;
 class nsInputStreamPump;
 
 //-----------------------------------------------------------------------------
 
 class nsJARChannel final : public nsIJARChannel
+                         , public mozilla::net::MemoryDownloader::IObserver
                          , public nsIStreamListener
                          , public nsIThreadRetargetableRequest
                          , public nsIThreadRetargetableStreamListener
                          , public nsHashPropertyBag
 {
 public:
     NS_DECL_ISUPPORTS_INHERITED
     NS_DECL_NSIREQUEST
@@ -60,16 +61,22 @@ private:
     nsresult CreateJarInput(nsIZipReaderCache *, nsJARInputThunk **);
     nsresult LookupFile();
     nsresult OpenLocalFile();
     nsresult ContinueOpenLocalFile(nsJARInputThunk* aInput, bool aIsSyncCall);
     nsresult OnOpenLocalFileComplete(nsresult aResult, bool aIsSyncCall);
     nsresult CheckPendingEvents();
     void NotifyError(nsresult aError);
     void FireOnProgress(uint64_t aProgress);
+    virtual void OnDownloadComplete(mozilla::net::MemoryDownloader* aDownloader,
+                                    nsIRequest* aRequest,
+                                    nsISupports* aCtxt,
+                                    nsresult aStatus,
+                                    mozilla::net::MemoryDownloader::Data aData)
+        override;
 
     nsCString                       mSpec;
 
     bool                            mOpened;
 
     RefPtr<nsJARProtocolHandler>    mJarHandler;
     nsCOMPtr<nsIJARURI>             mJarURI;
     nsCOMPtr<nsIURI>                mOriginalURI;
@@ -78,36 +85,46 @@ private:
     nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
     nsCOMPtr<nsISupports>           mSecurityInfo;
     nsCOMPtr<nsIProgressEventSink>  mProgressSink;
     nsCOMPtr<nsILoadGroup>          mLoadGroup;
     nsCOMPtr<nsIStreamListener>     mListener;
     nsCOMPtr<nsISupports>           mListenerContext;
     nsCString                       mContentType;
     nsCString                       mContentCharset;
+    nsCString                       mContentDispositionHeader;
+    /* mContentDisposition is uninitialized if mContentDispositionHeader is
+     * empty */
+    uint32_t                        mContentDisposition;
     int64_t                         mContentLength;
     uint32_t                        mLoadFlags;
     nsresult                        mStatus;
     bool                            mIsPending; // the AsyncOpen is in progress.
 
     bool                            mEnableOMT;
     // |Cancel()|, |Suspend()|, and |Resume()| might be called during AsyncOpen.
     struct {
         bool isCanceled;
         uint32_t suspendCount;
     }                               mPendingEvent;
 
+    bool                            mIsUnsafe;
+
+    mozilla::net::MemoryDownloader::Data mTempMem;
     nsCOMPtr<nsIInputStreamPump>    mPump;
     // mRequest is only non-null during OnStartRequest, so we'll have a pointer
     // to the request if we get called back via RetargetDeliveryTo.
     nsCOMPtr<nsIRequest>            mRequest;
     nsCOMPtr<nsIFile>               mJarFile;
     nsCOMPtr<nsIFile>               mJarFileOverride;
     nsCOMPtr<nsIZipReader>          mPreCachedJarReader;
     nsCOMPtr<nsIURI>                mJarBaseURI;
     nsCString                       mJarEntry;
     nsCString                       mInnerJarEntry;
 
     // use StreamTransportService as background thread
     nsCOMPtr<nsIEventTarget>        mWorker;
+
+    // True if this channel should not download any remote files.
+    bool                            mBlockRemoteFiles;
 };
 
 #endif // nsJARChannel_h__
rename from modules/libjar/test/mochitest/bug1173171.zip
rename to modules/libjar/test/mochitest/bug403331.zip
rename from modules/libjar/test/mochitest/bug1173171.zip^headers^
rename to modules/libjar/test/mochitest/bug403331.zip^headers^
--- a/modules/libjar/test/mochitest/mochitest.ini
+++ b/modules/libjar/test/mochitest/mochitest.ini
@@ -1,5 +1,11 @@
-
-[test_bug1173171.html]
+[DEFAULT]
 support-files =
-  bug1173171.zip
-  bug1173171.zip^headers^
\ No newline at end of file
+  bug403331.zip
+  bug403331.zip^headers^
+  openredirect.sjs
+  !/dom/base/test/file_bug945152.jar
+
+[test_bug403331.html]
+[test_bug1034143_mapped.html]
+run-if = os == 'linux'
+[test_bug1173171.html]
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/modules/libjar/test/mochitest/openredirect.sjs
@@ -0,0 +1,5 @@
+function handleRequest(request, response)
+{
+  response.setStatusLine(request.httpVersion, 301, "Moved Permanently");
+  response.setHeader("Location", request.queryString, false);
+}
new file mode 100644
--- /dev/null
+++ b/modules/libjar/test/mochitest/test_bug1034143_mapped.html
@@ -0,0 +1,53 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1034143
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 945152</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1034143">Mozilla Bug 1034143</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+// Ensure that XMLHttpRequest's memory-mapping code can handle a case
+// where the nsIJARChannel's jarFile property is null, but which is
+// otherwise eligible for bug 945152's memory-mapping optimization.
+
+function runTest() {
+  const jarURL = "jar:http://example.org/tests/dom/base/test/file_bug945152.jar!/data_1.txt";
+  let xhr = new XMLHttpRequest({ mozAnon: true, mozSystem: true });
+  xhr.open("GET", jarURL);
+  xhr.onerror = function onerror(e) {
+    ok(false, "JAR XHR failed: " + e.status);
+    SimpleTest.finish();
+  };
+  xhr.onload = function onload(e) {
+    ok(xhr.status == 200, "Status is 200");
+    let ct = xhr.getResponseHeader("Content-Type");
+    ok(!ct.includes("mem-mapped"), "Data is not memory-mapped");
+    SimpleTest.finish();
+  };
+  xhr.responseType = 'arraybuffer';
+  xhr.send();
+}
+
+SimpleTest.waitForExplicitFinish();
+
+addLoadEvent(function() {
+  SpecialPowers.pushPrefEnv({"set": [["dom.mapped_arraybuffer.enabled", true], 
+                                     ["network.jar.block-remote-files", false]]}, function() {
+    SpecialPowers.pushPermissions([{'type': 'systemXHR', 'allow': true, 'context': document}], runTest);
+  });
+});
+</script>
+</pre>
+</body>
+</html>
--- a/modules/libjar/test/mochitest/test_bug1173171.html
+++ b/modules/libjar/test/mochitest/test_bug1173171.html
@@ -13,16 +13,25 @@ https://bugzilla.mozilla.org/show_bug.cg
 
 <iframe id="testFrame"></iframe>
 
 <pre id="test">
 <script class="testbody" type="application/javascript">
 
 /** Test for Bug 1173171 **/
 
+// __setPref(key, value)__.
+// Set a pref value asynchronously, returning a prmoise that resolves
+// when it succeeds.
+let pushPref = function (key, value) {
+  return new Promise(function(resolve, reject) {
+    SpecialPowers.pushPrefEnv({"set": [[key, value]]}, resolve);
+  });
+};
+
 // __xhr(method, url, responseType__.
 // A simple async XMLHttpRequest call.
 // Returns a promise with the response.
 let xhr = function (method, url, responseType) {
   return new Promise(function (resolve, reject) {
     let xhr = new XMLHttpRequest();
     xhr.open(method, url, true);
     xhr.onload = function () {
@@ -35,18 +44,21 @@ let xhr = function (method, url, respons
     xhr.send();
   });
 };
 
 let jarURL = "jar:http://mochi.test:8888/tests/modules/libjar/test/mochitest/bug403331.zip!/test.html";
 
 // Test behavior when blocking is deactivated and activated.
 add_task(async function() {
-  let shouldBlock = true;
-  let response = await xhr("GET", jarURL, "document");
-  is(response, null, "Remote jars should be blocked.");
+  for (let shouldBlock of [false, true]) {
+    await pushPref("network.jar.block-remote-files", shouldBlock);
+    let response = await xhr("GET", jarURL, "document");
+    ok(shouldBlock === (response === null),
+       "Remote jars should be blocked if and only if the 'network.jar.block-remote-files' pref is active.");
+  }
 });
 
 </script>
 </pre>
 
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/modules/libjar/test/mochitest/test_bug403331.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=403331
+-->
+<head>
+  <title>Test for Bug 403331</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>        
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<iframe id="testFrame"></iframe>
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 403331 **/
+
+SimpleTest.waitForExplicitFinish();
+
+function runTest() {
+  var testFrame = document.getElementById('testFrame');
+
+  // Try a redirected load from another domain to this one.
+
+  testFrame.onload = function() {
+    // If properly redirected, we'll be able to access elements in the loaded
+    // document.
+    var item = testFrame.contentDocument.getElementById('testitem');
+    is(item.textContent, "testcontents", "Should be able to access the child document");
+
+    SimpleTest.finish();
+  }
+
+  testFrame.src = "jar:http://example.org:80/tests/modules/libjar/test/mochitest/openredirect.sjs?http://mochi.test:8888/tests/modules/libjar/test/mochitest/bug403331.zip!/test.html";
+}
+
+addLoadEvent(function() {
+  SpecialPowers.pushPrefEnv({"set": [["network.jar.block-remote-files", false]]}, runTest);
+});
+
+</script>
+</pre>
+
+</body>
+</html>
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1887,16 +1887,23 @@ pref("network.websocket.allowInsecureFro
 pref("network.websocket.delay-failed-reconnects", true);
 
 // </ws>
 
 // Server-Sent Events
 // Equal to the DEFAULT_RECONNECTION_TIME_VALUE value in nsEventSource.cpp
 pref("dom.server-events.default-reconnection-time", 5000); // in milliseconds
 
+// If false, remote JAR files that are served with a content type other than
+// application/java-archive or application/x-jar will not be opened
+// by the jar channel.
+pref("network.jar.open-unsafe-types", false);
+// If true, loading remote JAR files using the jar: protocol will be prevented.
+pref("network.jar.block-remote-files", true);
+
 // This preference, if true, causes all UTF-8 domain names to be normalized to
 // punycode.  The intention is to allow UTF-8 domain names as input, but never
 // generate them from punycode.
 pref("network.IDN_show_punycode", false);
 
 // If "network.IDN.use_whitelist" is set to true, TLDs with
 // "network.IDN.whitelist.tld" explicitly set to true are treated as
 // IDN-safe. Otherwise, they're treated as unsafe and punycode will be used