Merge inbound to central, a=merge
authorWes Kocher <wkocher@mozilla.com>
Fri, 24 Jun 2016 13:37:12 -0700
changeset 302570 0e073f5ca38a002d43e92016ee40d686da4a0534
parent 302542 9dac1358aaadcea90aca05b170fc8bc59e097d52 (current diff)
parent 302569 a277f3e065d2a4026a6c10d2dae02699ef30a504 (diff)
child 302583 0cfc0fa7df034a592eb0ef580e0041a5c037bfa3
child 302631 80884ad1ee27b41ef871d5357e95a1d812176702
push id30365
push userkwierso@gmail.com
push dateFri, 24 Jun 2016 20:37:17 +0000
treeherdermozilla-central@0e073f5ca38a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone50.0a1
first release with
nightly linux32
0e073f5ca38a / 50.0a1 / 20160625030241 / files
nightly linux64
0e073f5ca38a / 50.0a1 / 20160625030241 / files
nightly mac
0e073f5ca38a / 50.0a1 / 20160625030241 / files
nightly win32
0e073f5ca38a / 50.0a1 / 20160625030241 / files
nightly win64
0e073f5ca38a / 50.0a1 / 20160625030241 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to central, a=merge
--- a/dom/base/Link.cpp
+++ b/dom/base/Link.cpp
@@ -100,17 +100,17 @@ Link::TryDNSPrefetchPreconnectOrPrefetch
       (linkTypes & nsStyleLinkElement::eNEXT)){
     nsCOMPtr<nsIPrefetchService> prefetchService(do_GetService(NS_PREFETCHSERVICE_CONTRACTID));
     if (prefetchService) {
       nsCOMPtr<nsIURI> uri(GetURI());
       if (uri) {
         nsCOMPtr<nsIDOMNode> domNode = GetAsDOMNode(mElement);
         prefetchService->PrefetchURI(uri,
                                      mElement->OwnerDoc()->GetDocumentURI(),
-                                     domNode, true);
+                                     domNode, linkTypes & nsStyleLinkElement::ePREFETCH);
         return;
       }
     }
   }
 
   if (linkTypes & nsStyleLinkElement::ePRECONNECT) {
     nsCOMPtr<nsIURI> uri(GetURI());
     if (uri && mElement->OwnerDoc()) {
--- a/dom/base/nsContentPolicy.cpp
+++ b/dom/base/nsContentPolicy.cpp
@@ -155,17 +155,19 @@ nsContentPolicy::CheckPolicy(CPMethod   
         window = node->OwnerDoc()->GetWindow();
     } else {
         window = do_QueryInterface(requestingContext);
     }
 
     if (window) {
         nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
         nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell);
-        loadContext->GetTopFrameElement(getter_AddRefs(topFrameElement));
+        if (loadContext) {
+          loadContext->GetTopFrameElement(getter_AddRefs(topFrameElement));
+        }
 
         MOZ_ASSERT(window->IsOuterWindow());
 
         if (topFrameElement) {
             nsCOMPtr<nsPIDOMWindowOuter> topWindow = window->GetScriptableTop();
             isTopLevel = topWindow == window;
         } else {
             // If we don't have a top frame element, then requestingContext is
--- a/dom/base/nsStyleLinkElement.cpp
+++ b/dom/base/nsStyleLinkElement.cpp
@@ -12,16 +12,17 @@
 
 #include "nsStyleLinkElement.h"
 
 #include "mozilla/StyleSheetHandle.h"
 #include "mozilla/StyleSheetHandleInlines.h"
 #include "mozilla/css/Loader.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/FragmentOrElement.h"
+#include "mozilla/dom/HTMLLinkElement.h"
 #include "mozilla/dom/ShadowRoot.h"
 #include "mozilla/dom/SRILogHelper.h"
 #include "mozilla/Preferences.h"
 #include "nsIContent.h"
 #include "nsIDocument.h"
 #include "nsIDOMComment.h"
 #include "nsIDOMNode.h"
 #include "nsIDOMStyleSheet.h"
@@ -430,23 +431,32 @@ nsStyleLinkElement::DoUpdateStyleSheet(n
     nsAutoString integrity;
     thisContent->GetAttr(kNameSpaceID_None, nsGkAtoms::integrity, integrity);
     if (!integrity.IsEmpty()) {
       MOZ_LOG(SRILogHelper::GetSriLog(), mozilla::LogLevel::Debug,
               ("nsStyleLinkElement::DoUpdateStyleSheet, integrity=%s",
                NS_ConvertUTF16toUTF8(integrity).get()));
     }
 
+    // if referrer attributes are enabled in preferences, load the link's referrer
+    // attribute. If the link does not provide a referrer attribute, ignore this
+    // and use the document's referrer policy
+
+    net::ReferrerPolicy referrerPolicy = GetLinkReferrerPolicy();
+    if (referrerPolicy == net::RP_Unset) {
+      referrerPolicy = doc->GetReferrerPolicy();
+    }
+
     // XXXbz clone the URI here to work around content policies modifying URIs.
     nsCOMPtr<nsIURI> clonedURI;
     uri->Clone(getter_AddRefs(clonedURI));
     NS_ENSURE_TRUE(clonedURI, NS_ERROR_OUT_OF_MEMORY);
     rv = doc->CSSLoader()->
       LoadStyleLink(thisContent, clonedURI, title, media, isAlternate,
-                    GetCORSMode(), doc->GetReferrerPolicy(), integrity,
+                    GetCORSMode(), referrerPolicy, integrity,
                     aObserver, &isAlternate);
     if (NS_FAILED(rv)) {
       // Don't propagate LoadStyleLink() errors further than this, since some
       // consumers (e.g. nsXMLContentSink) will completely abort on innocuous
       // things like a stylesheet load being blocked by the security system.
       doneLoading = true;
       isAlternate = false;
       rv = NS_OK;
--- a/dom/base/nsStyleLinkElement.h
+++ b/dom/base/nsStyleLinkElement.h
@@ -11,16 +11,17 @@
  */
 
 #ifndef nsStyleLinkElement_h___
 #define nsStyleLinkElement_h___
 
 #include "mozilla/Attributes.h"
 #include "mozilla/CORSMode.h"
 #include "mozilla/CSSStyleSheet.h"
+#include "mozilla/net/ReferrerPolicy.h"
 #include "nsCOMPtr.h"
 #include "nsIStyleSheetLinkingElement.h"
 #include "nsTArray.h"
 
 class nsIDocument;
 class nsIURI;
 
 namespace mozilla {
@@ -108,16 +109,21 @@ protected:
                                  bool* aIsAlternate) = 0;
 
   virtual mozilla::CORSMode GetCORSMode() const
   {
     // Default to no CORS
     return mozilla::CORS_NONE;
   }
 
+  virtual mozilla::net::ReferrerPolicy GetLinkReferrerPolicy()
+  {
+    return mozilla::net::RP_Unset;
+  }
+
   // CC methods
   void Unlink();
   void Traverse(nsCycleCollectionTraversalCallback &cb);
 
 private:
   /**
    * @param aOldDocument should be non-null only if we're updating because we
    *                     removed the node from the document.
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -730,16 +730,19 @@ skip-if = e10s || os != 'linux' || build
 [test_iframe_referrer.html]
 [test_iframe_referrer_changing.html]
 [test_iframe_referrer_invalid.html]
 [test_Image_constructor.html]
 [test_img_referrer.html]
 [test_innersize_scrollport.html]
 [test_integer_attr_with_leading_zero.html]
 [test_ipc_messagemanager_blob.html]
+[test_link_prefetch.html]
+skip-if = !e10s # Track Bug 1281415
+[test_link_stylesheet.html]
 [test_messagemanager_targetchain.html]
 [test_meta_viewport0.html]
 skip-if = (os != 'b2g' && os != 'android')    # meta-viewport tag support is mobile-only
 [test_meta_viewport1.html]
 skip-if = (os != 'b2g' && os != 'android')    # meta-viewport tag support is mobile-only
 [test_meta_viewport2.html]
 skip-if = (os != 'b2g' && os != 'android')    # meta-viewport tag support is mobile-only
 [test_meta_viewport3.html]
--- a/dom/base/test/referrer_testserver.sjs
+++ b/dom/base/test/referrer_testserver.sjs
@@ -147,16 +147,71 @@ function createRedirectImgTestCase(aPara
             window.addEventListener("load", function() {
               parent.postMessage("childLoadComplete", "http://mochi.test:8888");
             }.bind(window), false);
           </script>
           </body>
           </html>`;
 }
 
+// test page using link referrer attribute
+function createLinkPageUsingRefferer(aMetaPolicy, aAttributePolicy, aNewAttributePolicy, aName, aRel, aStringBuilder, aSchemeFrom, aSchemeTo, aTestType) {
+  var metaString = "";
+  if (aMetaPolicy) {
+    metaString = `<meta name="referrer" content="${aMetaPolicy}">`;
+  }
+  var relString = "";
+  if (aRel) {
+    relString = `rel=${aRel}`;
+  }
+
+  var changeString = "";
+  var policy = aAttributePolicy ? aAttributePolicy : aMetaPolicy;
+  var elementString = aStringBuilder(policy, aName, relString, aSchemeFrom, aSchemeTo, aTestType);
+
+  if (aTestType === "setAttribute") {
+    changeString = `var link = document.getElementById("test_link");
+                    link.setAttribute("referrerpolicy", "${aNewAttributePolicy}");
+                    link.href = "${createTestUrl(policy, "test", aName, "link_element", aSchemeFrom, aSchemeTo)}";`;
+  } else if (aTestType === "property") {
+    changeString = `var link = document.getElementById("test_link");
+                    link.referrerPolicy = "${aNewAttributePolicy}";
+                    link.href = "${createTestUrl(policy, "test", aName, "link_element", aSchemeFrom, aSchemeTo)}";`;
+  }
+
+  return `<!DOCTYPE HTML>
+           <html>
+             <head>
+               ${metaString}
+               ${elementString}
+             </head>
+             <body>
+                <script>
+                  ${changeString}
+                </script>
+             </body>
+           </html>`;
+}
+
+function buildLinkString(aPolicy, aName, aRelString, aSchemeFrom, aSchemeTo, aTestType) {
+  var result;
+  var href = '';
+  var onChildComplete = `window.parent.postMessage("childLoadComplete", "http://mochi.test:8888");`
+  if (!aTestType) {
+    href = `href=${createTestUrl(aPolicy, "test", aName, "link_element", aSchemeFrom, aSchemeTo)}`;
+  }
+  if (!aPolicy) {
+    result = `<link ${aRelString} ${href} id="test_link" onload='${onChildComplete}' onerror='${onChildComplete}'>`;
+  } else {
+    result = `<link ${aRelString} ${href} referrerpolicy=${aPolicy} id="test_link" onload='${onChildComplete}' onerror='${onChildComplete}'>`;
+  }
+
+  return result;
+}
+
 function handleRequest(request, response) {
   var params = new URLSearchParams(request.queryString);
   var action = params.get("ACTION");
   var schemeFrom = params.get("SCHEME_FROM") || "http";
   var schemeTo = params.get("SCHEME_TO") || "http";
 
   if (action === "resetState") {
     setSharedState(SHARED_KEY, "{}");
@@ -309,11 +364,28 @@ function handleRequest(request, response
     return;
   }
   if (action === "generate-iframe-redirect-policy-test") {
     response.write(createIframeTestPageUsingRefferer(metaPolicy, attributePolicy, newAttributePolicy, name, params,
                                                      schemeFrom, schemeTo));
     return;
   }
 
+  var _getPage = createLinkPageUsingRefferer.bind(null, metaPolicy, attributePolicy, newAttributePolicy, name, rel);
+  var _getLinkPage = _getPage.bind(null, buildLinkString, schemeFrom, schemeTo);
+
+  // link
+  if (action === "generate-link-policy-test") {
+    response.write(_getLinkPage());
+    return;
+  }
+  if (action === "generate-link-policy-test-set-attribute") {
+    response.write(_getLinkPage("setAttribute"));
+    return;
+  }
+  if (action === "generate-link-policy-test-property") {
+    response.write(_getLinkPage("property"));
+    return;
+  }
+
   response.write("I don't know action " + action);
   return;
 }
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_link_prefetch.html
@@ -0,0 +1,176 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test link policy attribute for Bug 1264165</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+  <!--
+  Testing that link referrer attributes are honoured correctly
+  https://bugzilla.mozilla.org/show_bug.cgi?id=1264165
+  -->
+
+  <script type="application/javascript;version=1.8">
+
+  const SJS = "://example.com/tests/dom/base/test/referrer_testserver.sjs?";
+  const PARAMS = ["ATTRIBUTE_POLICY", "NEW_ATTRIBUTE_POLICY", "META_POLICY", "REL", "SCHEME_FROM", "SCHEME_TO"];
+
+  const testCases = [
+    {ACTION: ["generate-link-policy-test"],
+      TESTS: [
+        {ATTRIBUTE_POLICY: 'unsafe-url',
+         NAME: 'prefetch-unsafe-url-with-origin-in-meta',
+         META_POLICY: 'origin',
+         REL: 'prefetch',
+         DESC: "prefetch-unsafe-url with origin in meta",
+         RESULT: 'full'},
+        {ATTRIBUTE_POLICY: 'origin',
+         NAME: 'prefetch-origin-with-unsafe-url-in-meta',
+         META_POLICY: 'unsafe-url',
+         REL: 'prefetch',
+         DESC: "prefetch-origin with unsafe-url in meta",
+         RESULT: 'origin'},
+        {ATTRIBUTE_POLICY: 'no-referrer',
+         NAME: 'prefetch-no-referrer-with-origin-in-meta',
+         META_POLICY: 'origin',
+         REL: 'prefetch',
+         DESC: "prefetch-no-referrer with origin in meta",
+         RESULT: 'none'},
+        {NAME: 'prefetch-no-referrer-in-meta',
+         META_POLICY: 'no-referrer',
+         REL: 'prefetch',
+         DESC: "prefetch-no-referrer in meta",
+         RESULT: 'none'},
+
+         // Downgrade.
+        {ATTRIBUTE_POLICY: 'no-referrer-when-downgrade',
+         NAME: 'prefetch-origin-in-meta-downgrade-in-attr',
+         META_POLICY: 'origin',
+         DESC: 'prefetch-origin in meta downgrade in attr',
+         REL: 'prefetch',
+         SCHEME_FROM: 'https',
+         SCHEME_TO: 'http',
+         RESULT: 'none'},
+
+         // No downgrade.
+        {ATTRIBUTE_POLICY: 'no-referrer-when-downgrade',
+         NAME: 'prefetch-origin-in-meta-downgrade-in-attr',
+         META_POLICY: 'origin',
+         DESC: 'prefetch-origin in meta downgrade in attr',
+         REL: 'prefetch',
+         SCHEME_FROM: 'https',
+         SCHEME_TO: 'https',
+         RESULT: 'full'},
+
+        {ATTRIBUTE_POLICY: 'origin',
+         NAME: 'prefetch-origin-with-no-meta',
+         META_POLICY: '',
+         REL: 'prefetch',
+         DESC: "prefetch-origin with no meta",
+         RESULT: 'origin'},
+
+        // Cross origin
+        {ATTRIBUTE_POLICY: 'origin-when-cross-origin',
+         NAME: 'prefetch-origin-when-cross-origin-with-no-meta',
+         META_POLICY: '',
+         SCHEME_FROM: 'https',
+         SCHEME_TO: 'http',
+         REL: 'prefetch',
+         DESC: "prefetch-origin-when-cross-origin with no meta",
+         RESULT: 'origin'},
+        {ATTRIBUTE_POLICY: 'origin-when-cross-origin',
+         NAME: 'prefetch-origin-when-cross-origin-with-no-referrer-in-meta',
+         META_POLICY: 'no-referrer',
+         SCHEME_FROM: 'https',
+         SCHEME_TO: 'http',
+         REL: 'prefetch',
+         DESC: "prefetch-origin-when-cross-origin with no-referrer in meta",
+         RESULT: 'origin'},
+        {ATTRIBUTE_POLICY: 'origin-when-cross-origin',
+         NAME: 'prefetch-origin-when-cross-origin-with-unsafe-url-in-meta',
+         META_POLICY: 'unsafe-url',
+         SCHEME_FROM: 'https',
+         SCHEME_TO: 'http',
+         REL: 'prefetch',
+         DESC: "prefetch-origin-when-cross-origin with unsafe-url in meta",
+         RESULT: 'origin'},
+        {ATTRIBUTE_POLICY: 'origin-when-cross-origin',
+         NAME: 'prefetch-origin-when-cross-origin-with-origin-in-meta',
+         META_POLICY: 'origin',
+         SCHEME_FROM: 'https',
+         SCHEME_TO: 'http',
+         REL: 'prefetch',
+         DESC: "prefetch-origin-when-cross-origin with origin in meta",
+         RESULT: 'origin'},
+
+        // Invalid
+        {ATTRIBUTE_POLICY: 'default',
+         NAME: 'prefetch-default-with-no-meta',
+         META_POLICY: '',
+         REL: 'prefetch',
+         DESC: "prefetch-default with no meta",
+         RESULT: 'full'},
+        {ATTRIBUTE_POLICY: 'something',
+         NAME: 'prefetch-something-with-no-meta',
+         META_POLICY: '',
+         REL: 'prefetch',
+         DESC: "prefetch-something with no meta",
+         RESULT: 'full'},
+      ]},
+
+    {ACTION: ["generate-link-policy-test-set-attribute"],
+      TESTS: [
+        {ATTRIBUTE_POLICY: 'unsafe-url',
+         NEW_ATTRIBUTE_POLICY: 'no-referrer',
+         NAME: 'prefetch-no-referrer-unsafe-url-set-attribute-with-origin-in-meta',
+         META_POLICY: 'origin',
+         REL: 'prefetch',
+         DESC: "prefetch-no-referrer-set-attribute (orginally unsafe-url) with origin in meta",
+         RESULT: 'none'},
+        {ATTRIBUTE_POLICY: 'origin',
+         NEW_ATTRIBUTE_POLICY: 'unsafe-url',
+         NAME: 'prefetch-unsafe-url-origin-set-attribute-with-no-referrer-in-meta',
+         META_POLICY: 'no-referrer',
+         REL: 'prefetch',
+         DESC: "prefetch-unsafe-url-set-attribute(orginally origin) with no-referrer in meta",
+         RESULT: 'full'},
+      ]},
+
+    {ACTION: ["generate-link-policy-test-property"],
+      TESTS: [
+        {ATTRIBUTE_POLICY: 'no-referrer',
+         NEW_ATTRIBUTE_POLICY: 'unsafe-url',
+         NAME: 'prefetch-unsafe-url-no-referrer-property-with-origin-in-meta',
+         META_POLICY: 'origin',
+         REL: 'prefetch',
+         DESC: "prefetch-unsafe-url-property (orginally no-referrer) with origin in meta",
+         RESULT: 'full'},
+        {ATTRIBUTE_POLICY: 'origin',
+         NEW_ATTRIBUTE_POLICY: 'unsafe-url',
+         NAME: 'prefetch-unsafe-url-origin-property-with-no-referrer-in-meta',
+         META_POLICY: 'no-referrer',
+         REL: 'prefetch',
+         DESC: "prefetch-unsafe-url-property (orginally origin) with no-referrer in meta",
+         RESULT: 'full'},
+      ]},
+  ];
+
+  </script>
+  <script type="application/javascript;version=1.7" src="/tests/dom/base/test/referrer_helper.js"></script>
+</head>
+<body onload="tests.next();">
+  <script type="application/javascript;version=1.7">
+    /**
+     * Listen for notifications that pretching finishes.
+     * XXX Bug 1268962 - Fire load/error events on <link rel="prefetch">
+     * Because there's no onload/onerror event fired, we catch prefetch-load-completed
+     * to test
+     * Simply remove this after bug 1268962 is fixed
+     */
+    netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+    SpecialPowers.Services.obs.addObserver(function() { tests.next(); }, "prefetch-load-completed", false);
+  </script>
+  <iframe id="testframe"></iframe>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_link_stylesheet.html
@@ -0,0 +1,166 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test link policy attribute for Bug 1264165</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+  <!--
+  Testing that link referrer attributes are honoured correctly
+  https://bugzilla.mozilla.org/show_bug.cgi?id=1264165
+  -->
+
+  <script type="application/javascript;version=1.8">
+
+  const SJS = "://example.com/tests/dom/base/test/referrer_testserver.sjs?";
+  const PARAMS = ["ATTRIBUTE_POLICY", "NEW_ATTRIBUTE_POLICY", "META_POLICY", "REL", "SCHEME_FROM", "SCHEME_TO"];
+
+  const testCases = [
+    {ACTION: ["generate-link-policy-test"],
+      TESTS: [
+        {ATTRIBUTE_POLICY: 'unsafe-url',
+         NAME: 'stylesheet-unsafe-url-with-origin-in-meta',
+         META_POLICY: 'origin',
+         REL: 'stylesheet',
+         DESC: "stylesheet-unsafe-url with origin in meta",
+         RESULT: 'full'},
+        {ATTRIBUTE_POLICY: 'origin',
+         NAME: 'stylesheet-origin-with-unsafe-url-in-meta',
+         META_POLICY: 'unsafe-url',
+         REL: 'stylesheet',
+         DESC: "stylesheet-origin with unsafe-url in meta",
+         RESULT: 'origin'},
+        {ATTRIBUTE_POLICY: 'no-referrer',
+         NAME: 'stylesheet-no-referrer-with-origin-in-meta',
+         META_POLICY: 'origin',
+         REL: 'stylesheet',
+         DESC: "stylesheet-no-referrer with origin in meta",
+         RESULT: 'none'},
+        {NAME: 'stylesheet-no-referrer-in-meta',
+         META_POLICY: 'no-referrer',
+         REL: 'stylesheet',
+         DESC: "stylesheet-no-referrer in meta",
+         RESULT: 'none'},
+
+         // Downgrade.
+        {ATTRIBUTE_POLICY: 'no-referrer-when-downgrade',
+         NAME: 'stylesheet-origin-in-meta-downgrade-in-attr',
+         META_POLICY: 'origin',
+         DESC: 'stylesheet-origin in meta downgrade in attr',
+         REL: 'stylesheet',
+         SCHEME_FROM: 'https',
+         SCHEME_TO: 'http',
+         RESULT: 'none'},
+
+          // No downgrade.
+        {ATTRIBUTE_POLICY: 'no-referrer-when-downgrade',
+         NAME: 'stylesheet-origin-in-meta-downgrade-in-attr',
+         META_POLICY: 'origin',
+         DESC: 'stylesheet-origin in meta downgrade in attr',
+         REL: 'stylesheet',
+         SCHEME_FROM: 'https',
+         SCHEME_TO: 'https',
+         RESULT: 'full'},
+
+        {ATTRIBUTE_POLICY: 'origin',
+         NAME: 'stylesheet-origin-with-no-meta',
+         META_POLICY: '',
+         REL: 'stylesheet',
+         DESC: "stylesheet-origin with no meta",
+         RESULT: 'origin'},
+
+        // Cross origin
+        {ATTRIBUTE_POLICY: 'origin-when-cross-origin',
+         NAME: 'stylesheet-origin-when-cross-origin-with-no-meta',
+         META_POLICY: '',
+         SCHEME_FROM: 'https',
+         SCHEME_TO: 'http',
+         REL: 'stylesheet',
+         DESC: "stylesheet-origin-when-cross-origin with no meta",
+         RESULT: 'origin'},
+        {ATTRIBUTE_POLICY: 'origin-when-cross-origin',
+         NAME: 'stylesheet-origin-when-cross-origin-with-no-referrer-in-meta',
+         META_POLICY: 'no-referrer',
+         SCHEME_FROM: 'https',
+         SCHEME_TO: 'http',
+         REL: 'stylesheet',
+         DESC: "stylesheet-origin-when-cross-origin with no-referrer in meta",
+         RESULT: 'origin'},
+        {ATTRIBUTE_POLICY: 'origin-when-cross-origin',
+         NAME: 'stylesheet-origin-when-cross-origin-with-unsafe-url-in-meta',
+         META_POLICY: 'unsafe-url',
+         SCHEME_FROM: 'https',
+         SCHEME_TO: 'http',
+         REL: 'stylesheet',
+         DESC: "stylesheet-origin-when-cross-origin with unsafe-url in meta",
+         RESULT: 'origin'},
+        {ATTRIBUTE_POLICY: 'origin-when-cross-origin',
+         NAME: 'stylesheet-origin-when-cross-origin-with-origin-in-meta',
+         META_POLICY: 'origin',
+         SCHEME_FROM: 'https',
+         SCHEME_TO: 'http',
+         REL: 'stylesheet',
+         DESC: "stylesheet-origin-when-cross-origin with origin in meta",
+         RESULT: 'origin'},
+
+        // Invalid
+        {ATTRIBUTE_POLICY: 'default',
+         NAME: 'stylesheet-default-with-no-meta',
+         META_POLICY: '',
+         REL: 'stylesheet',
+         DESC: "stylesheet-default with no meta",
+         RESULT: 'full'},
+        {ATTRIBUTE_POLICY: 'something',
+         NAME: 'stylesheet-something-with-no-meta',
+         META_POLICY: '',
+         REL: 'stylesheet',
+         DESC: "stylesheet-something with no meta",
+         RESULT: 'full'},
+      ]},
+
+    {ACTION: ["generate-link-policy-test-set-attribute"],
+      TESTS: [
+        {ATTRIBUTE_POLICY: 'unsafe-url',
+         NEW_ATTRIBUTE_POLICY: 'no-referrer',
+         NAME: 'stylesheet-no-referrer-unsafe-url-set-attribute-with-origin-in-meta',
+         META_POLICY: 'origin',
+         REL: 'stylesheet',
+         DESC: "stylesheet-no-referrer-set-attribute (orginally unsafe-url) with origin in meta",
+         RESULT: 'none'},
+        {ATTRIBUTE_POLICY: 'origin',
+         NEW_ATTRIBUTE_POLICY: 'unsafe-url',
+         NAME: 'stylesheet-unsafe-url-origin-set-attribute-with-no-referrer-in-meta',
+         META_POLICY: 'no-referrer',
+         REL: 'stylesheet',
+         DESC: "stylesheet-unsafe-url-set-attribute (orginally origin) with no-referrer in meta",
+         RESULT: 'full'},
+      ]},
+
+    {ACTION: ["generate-link-policy-test-property"],
+      TESTS: [
+        {ATTRIBUTE_POLICY: 'no-referrer',
+         NEW_ATTRIBUTE_POLICY: 'unsafe-url',
+         NAME: 'stylesheet-unsafe-url-no-referrer-property-with-origin-in-meta',
+         META_POLICY: 'origin',
+         REL: 'stylesheet',
+         DESC: "stylesheet-unsafe-url-property (orginally no-referrer) with origin in meta",
+         RESULT: 'full'},
+        {ATTRIBUTE_POLICY: 'origin',
+         NEW_ATTRIBUTE_POLICY: 'unsafe-url',
+         NAME: 'stylesheet-unsafe-url-origin-property-with-no-referrer-in-meta',
+         META_POLICY: 'no-referrer',
+         REL: 'stylesheet',
+         DESC: "stylesheet-unsafe-url-property (orginally origin) with no-referrer in meta",
+         RESULT: 'full'},
+      ]},
+  ];
+  </script>
+  <script type="application/javascript;version=1.7" src="/tests/dom/base/test/referrer_helper.js"></script>
+</head>
+<body onload="tests.next();">
+  <iframe id="testframe"></iframe>
+</body>
+</html>
+
+
--- a/dom/html/HTMLLinkElement.h
+++ b/dom/html/HTMLLinkElement.h
@@ -145,16 +145,28 @@ public:
   void GetIntegrity(nsAString& aIntegrity) const
   {
     GetHTMLAttr(nsGkAtoms::integrity, aIntegrity);
   }
   void SetIntegrity(const nsAString& aIntegrity, ErrorResult& aRv)
   {
     SetHTMLAttr(nsGkAtoms::integrity, aIntegrity, aRv);
   }
+  void SetReferrerPolicy(const nsAString& aReferrer, ErrorResult& aError)
+  {
+    SetHTMLAttr(nsGkAtoms::referrerpolicy, aReferrer, aError);
+  }
+  void GetReferrerPolicy(nsAString& aReferrer)
+  {
+    GetEnumAttr(nsGkAtoms::referrerpolicy, EmptyCString().get(), aReferrer);
+  }
+  mozilla::net::ReferrerPolicy GetLinkReferrerPolicy() override
+  {
+    return GetReferrerPolicyAsEnum();
+  }
 
   already_AddRefed<nsIDocument> GetImport();
   already_AddRefed<ImportLoader> GetImportLoader()
   {
     return RefPtr<ImportLoader>(mImportLoader).forget();
   }
 
   virtual CORSMode GetCORSMode() const override;
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -509,17 +509,16 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_IN
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mVideoTrackList)
 #ifdef MOZ_EME
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mMediaKeys)
 #endif
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(HTMLMediaElement)
   NS_INTERFACE_MAP_ENTRY(nsIDOMHTMLMediaElement)
-  NS_INTERFACE_MAP_ENTRY(nsIObserver)
   NS_INTERFACE_MAP_ENTRY(nsIAudioChannelAgentCallback)
 NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLElement)
 
 // nsIDOMHTMLMediaElement
 NS_IMPL_URI_ATTR(HTMLMediaElement, Src, src)
 NS_IMPL_BOOL_ATTR(HTMLMediaElement, Controls, controls)
 NS_IMPL_BOOL_ATTR(HTMLMediaElement, Autoplay, autoplay)
 NS_IMPL_BOOL_ATTR(HTMLMediaElement, Loop, loop)
@@ -2245,20 +2244,58 @@ HTMLMediaElement::LookupMediaElementURIT
       if (resource->CanClone()) {
         return elem;
       }
     }
   }
   return nullptr;
 }
 
+class HTMLMediaElement::ShutdownObserver : public nsIObserver {
+public:
+  NS_DECL_ISUPPORTS
+
+  NS_IMETHOD Observe(nsISupports*, const char* aTopic, const char16_t*) override {
+    MOZ_DIAGNOSTIC_ASSERT(mWeak);
+    if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
+      mWeak->NotifyShutdownEvent();
+    }
+    return NS_OK;
+  }
+  void Subscribe(HTMLMediaElement* aPtr) {
+    MOZ_DIAGNOSTIC_ASSERT(!mWeak);
+    mWeak = aPtr;
+    nsContentUtils::RegisterShutdownObserver(this);
+  }
+  void Unsubscribe() {
+    MOZ_DIAGNOSTIC_ASSERT(mWeak);
+    mWeak = nullptr;
+    nsContentUtils::UnregisterShutdownObserver(this);
+  }
+  void AddRefMediaElement() {
+    mWeak->AddRef();
+  }
+  void ReleaseMediaElement() {
+    mWeak->Release();
+  }
+private:
+  virtual ~ShutdownObserver() {
+    MOZ_DIAGNOSTIC_ASSERT(!mWeak);
+  }
+  // Guaranteed to be valid by HTMLMediaElement.
+  HTMLMediaElement* mWeak = nullptr;
+};
+
+NS_IMPL_ISUPPORTS(HTMLMediaElement::ShutdownObserver, nsIObserver)
+
 HTMLMediaElement::HTMLMediaElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
   : nsGenericHTMLElement(aNodeInfo),
     mWatchManager(this, AbstractThread::MainThread()),
     mSrcStreamPausedCurrentTime(-1),
+    mShutdownObserver(new ShutdownObserver),
     mCurrentLoadID(0),
     mNetworkState(nsIDOMHTMLMediaElement::NETWORK_EMPTY),
     mReadyState(nsIDOMHTMLMediaElement::HAVE_NOTHING, "HTMLMediaElement::mReadyState"),
     mLoadWaitStatus(NOT_WAITING),
     mVolume(1.0),
     mPreloadAction(PRELOAD_UNDEFINED),
     mLastCurrentTime(0.0),
     mFragmentStart(-1.0),
@@ -2325,23 +2362,27 @@ HTMLMediaElement::HTMLMediaElement(alrea
   RegisterActivityObserver();
   NotifyOwnerDocumentActivityChangedInternal();
 
   MOZ_ASSERT(NS_IsMainThread());
   mWatchManager.Watch(mDownloadSuspendedByCache, &HTMLMediaElement::UpdateReadyStateInternal);
   // Paradoxically, there is a self-edge whereby UpdateReadyStateInternal refuses
   // to run until mReadyState reaches at least HAVE_METADATA by some other means.
   mWatchManager.Watch(mReadyState, &HTMLMediaElement::UpdateReadyStateInternal);
+
+  mShutdownObserver->Subscribe(this);
 }
 
 HTMLMediaElement::~HTMLMediaElement()
 {
   NS_ASSERTION(!mHasSelfReference,
                "How can we be destroyed if we're still holding a self reference?");
 
+  mShutdownObserver->Unsubscribe();
+
   if (mVideoFrameContainer) {
     mVideoFrameContainer->ForgetElement();
   }
   UnregisterActivityObserver();
   if (mDecoder) {
     ShutdownDecoder();
   }
   if (mProgressTimer) {
@@ -4243,16 +4284,20 @@ bool HTMLMediaElement::IsHidden() const
     return true;
   }
   nsIDocument* ownerDoc = OwnerDoc();
   return !ownerDoc || ownerDoc->Hidden();
 }
 
 VideoFrameContainer* HTMLMediaElement::GetVideoFrameContainer()
 {
+  if (mShuttingDown) {
+    return nullptr;
+  }
+
   if (mVideoFrameContainer)
     return mVideoFrameContainer;
 
   // Only video frames need an image container.
   if (!IsVideo()) {
     return nullptr;
   }
 
@@ -4600,47 +4645,42 @@ void HTMLMediaElement::AddRemoveSelfRefe
      (mDecoder && mDecoder->IsSeeking()) ||
      CanActivateAutoplay() ||
      (mMediaSource ? mProgressTimer :
       mNetworkState == nsIDOMHTMLMediaElement::NETWORK_LOADING));
 
   if (needSelfReference != mHasSelfReference) {
     mHasSelfReference = needSelfReference;
     if (needSelfReference) {
-      // The observer service will hold a strong reference to us. This
+      // The shutdown observer will hold a strong reference to us. This
       // will do to keep us alive. We need to know about shutdown so that
       // we can release our self-reference.
-      nsContentUtils::RegisterShutdownObserver(this);
+      mShutdownObserver->AddRefMediaElement();
     } else {
       // Dispatch Release asynchronously so that we don't destroy this object
       // inside a call stack of method calls on this object
       nsCOMPtr<nsIRunnable> event =
         NewRunnableMethod(this, &HTMLMediaElement::DoRemoveSelfReference);
       NS_DispatchToMainThread(event);
     }
   }
 
   UpdateAudioChannelPlayingState();
 }
 
 void HTMLMediaElement::DoRemoveSelfReference()
 {
-  // We don't need the shutdown observer anymore. Unregistering releases
-  // its reference to us, which we were using as our self-reference.
-  nsContentUtils::UnregisterShutdownObserver(this);
-}
-
-nsresult HTMLMediaElement::Observe(nsISupports* aSubject,
-                                   const char* aTopic, const char16_t* aData)
-{
-  if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
-    mShuttingDown = true;
-    AddRemoveSelfReference();
-  }
-  return NS_OK;
+  mShutdownObserver->ReleaseMediaElement();
+}
+
+void HTMLMediaElement::NotifyShutdownEvent()
+{
+  mShuttingDown = true;
+  ResetState();
+  AddRemoveSelfReference();
 }
 
 bool
 HTMLMediaElement::IsNodeOfType(uint32_t aFlags) const
 {
   return !(aFlags & ~(eCONTENT | eMEDIA));
 }
 
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -74,17 +74,16 @@ namespace dom {
 class MediaError;
 class MediaSource;
 class TextTrackList;
 class AudioTrackList;
 class VideoTrackList;
 
 class HTMLMediaElement : public nsGenericHTMLElement,
                          public nsIDOMHTMLMediaElement,
-                         public nsIObserver,
                          public MediaDecoderOwner,
                          public nsIAudioChannelAgentCallback,
                          public PrincipalChangeObserver<DOMMediaStream>
 {
   friend AutoNotifyAudioChannelAgent;
 
 public:
   typedef mozilla::TimeStamp TimeStamp;
@@ -109,18 +108,16 @@ public:
    * @param aListener returns a stream listener that should receive
    * notifications for the stream
    */
   nsresult LoadWithChannel(nsIChannel *aChannel, nsIStreamListener **aListener);
 
   // nsIDOMHTMLMediaElement
   NS_DECL_NSIDOMHTMLMEDIAELEMENT
 
-  NS_DECL_NSIOBSERVER
-
   NS_DECL_NSIAUDIOCHANNELAGENTCALLBACK
 
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLMediaElement,
                                            nsGenericHTMLElement)
 
   virtual bool ParseAttribute(int32_t aNamespaceID,
@@ -735,16 +732,17 @@ public:
 protected:
   virtual ~HTMLMediaElement();
 
   class MediaLoadListener;
   class MediaStreamTracksAvailableCallback;
   class MediaStreamTrackListener;
   class StreamListener;
   class StreamSizeListener;
+  class ShutdownObserver;
 
   MediaDecoderOwner::NextFrameStatus NextFrameStatus();
   void SetDecoder(MediaDecoder* aDecoder) {
     MOZ_ASSERT(aDecoder); // Use ShutdownDecoder() to clear.
     mDecoder = aDecoder;
   }
 
   class WakeLockBoolWrapper {
@@ -983,16 +981,21 @@ protected:
   void AddRemoveSelfReference();
 
   /**
    * Called asynchronously to release a self-reference to this element.
    */
   void DoRemoveSelfReference();
 
   /**
+   * Called when "xpcom-shutdown" event is received.
+   */
+  void NotifyShutdownEvent();
+
+  /**
    * Possible values of the 'preload' attribute.
    */
   enum PreloadAttrValue {
     PRELOAD_ATTR_EMPTY,    // set to ""
     PRELOAD_ATTR_NONE,     // set to "none"
     PRELOAD_ATTR_METADATA, // set to "metadata"
     PRELOAD_ATTR_AUTO      // set to "auto"
   };
@@ -1235,16 +1238,18 @@ protected:
 
   // Holds a reference to the MediaStreamListener attached to mSrcStream's
   // playback stream.
   RefPtr<StreamListener> mMediaStreamListener;
   // Holds a reference to the size-getting MediaStreamListener attached to
   // mSrcStream.
   RefPtr<StreamSizeListener> mMediaStreamSizeListener;
 
+  const RefPtr<ShutdownObserver> mShutdownObserver;
+
   // Holds a reference to the MediaSource, if any, referenced by the src
   // attribute on the media element.
   RefPtr<MediaSource> mSrcMediaSource;
 
   // Holds a reference to the MediaSource supplying data for playback.  This
   // may either match mSrcMediaSource or come from Source element children.
   // This is set when and only when mLoadingSrc corresponds to an object url
   // that resolved to a MediaSource.
--- a/dom/html/test/test_bug1260664.html
+++ b/dom/html/test/test_bug1260664.html
@@ -3,34 +3,33 @@
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=1260664
 -->
 <head>
   <meta charset="utf-8">
   <title>Test for Bug 1260664</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="reflect.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=1260664">Mozilla Bug 1260664</a>
 <p id="display"></p>
 <div id="content" style="display: none">
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 /** Test for Bug 1260664 **/
 SpecialPowers.setBoolPref("network.http.enablePerElementReferrer", true);
 
 SimpleTest.waitForExplicitFinish();
 SimpleTest.waitForFocus(runTests);
 
 function runTests() {
-  var elements = [ "iframe", "img", "a", "area" ];
+  var elements = [ "iframe", "img", "a", "area", "link" ];
 
   for (var i = 0; i < elements.length; ++i) {
     reflectLimitedEnumerated({
       element: document.createElement(elements[i]),
       attribute: { content: "referrerpolicy", idl: "referrerPolicy" },
       validValues: [ "no-referrer",
                      "origin",
                      /** These 2 below values are still invalid, please see
--- a/dom/locales/en-US/chrome/security/caps.properties
+++ b/dom/locales/en-US/chrome/security/caps.properties
@@ -1,12 +1,11 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
-CheckMessage = Remember this decision
 CheckLoadURIError = Security Error: Content at %S may not load or link to %S.
 CheckSameOriginError = Security Error: Content at %S may not load data from %S.
 ExternalDataError = Security Error: Content at %S attempted to load %S, but may not load external data when being used as an image. 
 
 # LOCALIZATION NOTE (GetPropertyDeniedOrigins):
 # %1$S is the origin of the script which was denied access.
 # %2$S is the type of object it was.
 # %3$S is the property of that object that access was denied for.
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -648,16 +648,17 @@ MediaDecoder::Shutdown()
       ->Then(AbstractThread::MainThread(), __func__, this,
              &MediaDecoder::FinishShutdown,
              &MediaDecoder::FinishShutdown);
   } else {
     // Ensure we always unregister asynchronously in order not to disrupt
     // the hashtable iterating in MediaShutdownManager::Shutdown().
     RefPtr<MediaDecoder> self = this;
     nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self] () {
+      self->mVideoFrameContainer = nullptr;
       MediaShutdownManager::Instance().Unregister(self);
     });
     AbstractThread::MainThread()->Dispatch(r.forget());
   }
 
   // Force any outstanding seek and byterange requests to complete
   // to prevent shutdown from deadlocking.
   if (mResource) {
@@ -701,16 +702,17 @@ MediaDecoder::OnPlaybackEvent(MediaEvent
 }
 
 void
 MediaDecoder::FinishShutdown()
 {
   MOZ_ASSERT(NS_IsMainThread());
   mDecoderStateMachine->BreakCycles();
   SetStateMachine(nullptr);
+  mVideoFrameContainer = nullptr;
   MediaShutdownManager::Instance().Unregister(this);
 }
 
 MediaResourceCallback*
 MediaDecoder::GetResourceCallback() const
 {
   return mResourceCallback;
 }
--- a/dom/media/MediaDecoder.h
+++ b/dom/media/MediaDecoder.h
@@ -661,17 +661,17 @@ protected:
   // This should only ever be accessed from the main thread.
   // It is set in Init and cleared in Shutdown when the element goes away.
   // The decoder does not add a reference the element.
   MediaDecoderOwner* const mOwner;
 
   // Counters related to decode and presentation of frames.
   const RefPtr<FrameStatistics> mFrameStats;
 
-  const RefPtr<VideoFrameContainer> mVideoFrameContainer;
+  RefPtr<VideoFrameContainer> mVideoFrameContainer;
 
   // Data needed to estimate playback data rate. The timeline used for
   // this estimate is "decode time" (where the "current time" is the
   // time of the last decoded video frame).
   RefPtr<MediaChannelStatistics> mPlaybackStatistics;
 
   // True when our media stream has been pinned. We pin the stream
   // while seeking.
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -131,16 +131,17 @@ MediaFormatReader::Shutdown()
     mVideo.mTaskQueue->BeginShutdown();
     mVideo.mTaskQueue->AwaitShutdownAndIdle();
     mVideo.mTaskQueue = nullptr;
   }
   MOZ_ASSERT(!mVideo.HasPromise());
 
   mDemuxer = nullptr;
   mPlatform = nullptr;
+  mVideoFrameContainer = nullptr;
 
   return MediaDecoderReader::Shutdown();
 }
 
 void
 MediaFormatReader::InitLayersBackendType()
 {
   // Extract the layer manager backend type so that platform decoders
--- a/dom/webidl/HTMLLinkElement.webidl
+++ b/dom/webidl/HTMLLinkElement.webidl
@@ -24,16 +24,18 @@ interface HTMLLinkElement : HTMLElement 
   [PutForwards=value]
   readonly attribute DOMTokenList relList;
   [SetterThrows, Pure]
            attribute DOMString media;
   [SetterThrows, Pure]
            attribute DOMString hreflang;
   [SetterThrows, Pure]
            attribute DOMString type;
+  [SetterThrows, Pure, Pref="network.http.enablePerElementReferrer"]
+           attribute DOMString referrerPolicy;
   [PutForwards=value] readonly attribute DOMTokenList sizes;
 };
 HTMLLinkElement implements LinkStyle;
 
 // http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis
 partial interface HTMLLinkElement {
   [SetterThrows, Pure]
            attribute DOMString charset;
--- a/js/src/asmjs/WasmBaselineCompile.cpp
+++ b/js/src/asmjs/WasmBaselineCompile.cpp
@@ -403,16 +403,18 @@ class BaseCompiler
 
 #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)
     RegI32 specific_eax;
     RegI32 specific_ecx;
     RegI32 specific_edx;
 #endif
 
     // The join registers are used to carry values out of blocks.
+    // JoinRegI32 and joinRegI64 must overlap: emitBrIf and
+    // emitBrTable assume that.
 
     RegI32 joinRegI32;
     RegI64 joinRegI64;
     RegF32 joinRegF32;
     RegF64 joinRegF64;
 
     // More members: see the stk_ and ctl_ vectors, defined below.
 
@@ -1478,16 +1480,17 @@ class BaseCompiler
           default:
             MOZ_CRASH("Compiler bug: unexpected value on stack");
         }
     }
 
     void pushJoinReg(AnyReg r) {
         switch (r.tag) {
           case AnyReg::NONE:
+            pushVoid();
             break;
           case AnyReg::I32:
             pushI32(r.i32());
             break;
           case AnyReg::I64:
             pushI64(r.i64());
             break;
           case AnyReg::F64:
@@ -1718,22 +1721,16 @@ class BaseCompiler
                 storeToFrameI32(scratch, i+4);
 #else
             MOZ_CRASH("BaseCompiler platform hook: init frame");
 #endif
         }
     }
 
     bool endFunction() {
-        // A frame greater than 256KB is implausible, probably an attack,
-        // so bail out.
-
-        if (maxFramePushed_ > 256 * 1024)
-            return false;
-
         // Out-of-line prologue.  Assumes that the in-line prologue has
         // been executed and that a frame of size = localSize_ + sizeof(AsmJSFrame)
         // has been allocated.
 
         masm.bind(&outOfLinePrologue_);
 
         MOZ_ASSERT(maxFramePushed_ >= localSize_);
 
@@ -1765,16 +1762,22 @@ class BaseCompiler
         //gen->perfSpewer().noteEndInlineCode(masm);
 #endif
 
         if (!generateOutOfLineCode())
             return false;
 
         compileResults_.offsets().end = masm.currentOffset();
 
+        // A frame greater than 256KB is implausible, probably an attack,
+        // so fail the compilation.
+
+        if (maxFramePushed_ > 256 * 1024)
+            return false;
+
         return true;
     }
 
     //////////////////////////////////////////////////////////////////////
     //
     // Calls.
 
     struct FunctionCall
@@ -2428,17 +2431,17 @@ class BaseCompiler
         masm.lshift32(rhs.reg, srcDest.reg);
 #else
         MOZ_CRASH("BaseCompiler platform hook: lshiftI32");
 #endif
     }
 
     void lshiftI32(int32_t count, RegI32 srcDest) {
 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
-        masm.shll(Imm32(count), srcDest.reg);
+        masm.shll(Imm32(count & 31), srcDest.reg);
 #else
         MOZ_CRASH("BaseCompiler platform hook: lshiftI32");
 #endif
     }
 
     void lshiftI64(RegI64 rhs, RegI64 srcDest) {
 #if defined(JS_CODEGEN_X64)
         MOZ_ASSERT(rhs.reg.reg == rcx);
@@ -2454,17 +2457,17 @@ class BaseCompiler
         masm.rshift32Arithmetic(rhs.reg, srcDest.reg);
 #else
         MOZ_CRASH("BaseCompiler platform hook: rshiftI32");
 #endif
     }
 
     void rshiftI32(int32_t count, RegI32 srcDest) {
 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
-        masm.sarl(Imm32(count), srcDest.reg);
+        masm.sarl(Imm32(count & 31), srcDest.reg);
 #else
         MOZ_CRASH("BaseCompiler platform hook: rshiftI32");
 #endif
     }
 
     void rshiftI64(RegI64 rhs, RegI64 srcDest) {
 #if defined(JS_CODEGEN_X64)
         MOZ_ASSERT(rhs.reg.reg == rcx);
@@ -2480,17 +2483,17 @@ class BaseCompiler
         masm.rshift32(rhs.reg, srcDest.reg);
 #else
         MOZ_CRASH("BaseCompiler platform hook: rshiftU32");
 #endif
     }
 
     void rshiftU32(int32_t count, RegI32 srcDest) {
 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
-        masm.shrl(Imm32(count), srcDest.reg);
+        masm.shrl(Imm32(count & 31), srcDest.reg);
 #else
         MOZ_CRASH("BaseCompiler platform hook: rshiftU32");
 #endif
     }
 
     void rshiftU64(RegI64 rhs, RegI64 srcDest) {
 #if defined(JS_CODEGEN_X64)
         MOZ_ASSERT(rhs.reg.reg == rcx);
@@ -4612,29 +4615,30 @@ BaseCompiler::emitBrIf()
     //
     // TODO / OPTIMIZE: We can generate better code if no cleanup code
     // need be executed along the taken edge.
     //
     // TODO / OPTIMIZE: Optimize boolean evaluation for control by
     // allowing a conditional expression to be left on the stack and
     // reified here as part of the branch instruction.
 
-    // Don't use it for rc
-    if (type == ExprType::I32)
+    // We'll need the joinreg later, so don't use it for rc.
+    // We assume joinRegI32 and joinRegI64 overlap.
+    if (type == ExprType::I32 || type == ExprType::I64)
         needI32(joinRegI32);
 
     // Condition value is on top, always I32.
     RegI32 rc = popI32();
 
     // There may or may not be a value underneath, to be carried along
     // the taken edge.
     if (IsVoid(type))
         pushVoid();
 
-    if (type == ExprType::I32)
+    if (type == ExprType::I32 || type == ExprType::I64)
         freeI32(joinRegI32);
 
     // Save any value in the designated join register, where the
     // normal block exit code will also leave it.
     AnyReg r = popJoinReg();
 
     masm.branch32(Assembler::Equal, rc.reg, Imm32(0), &notTaken);
 
@@ -4675,29 +4679,30 @@ BaseCompiler::emitBrTable()
             return false;
         depths.infallibleAppend(depth);
     }
 
     uint32_t defaultDepth;
     if (!iter_.readBrTableEntry(type, &defaultDepth))
         return false;
 
-    // We'll need this, so don't use it for rc
-    if (type == ExprType::I32)
+    // We'll need the joinreg later, so don't use it for rc.
+    // We assume joinRegI32 and joinRegI64 overlap.
+    if (type == ExprType::I32 || type == ExprType::I64)
         needI32(joinRegI32);
 
     // Table switch value always on top.
     RegI32 rc = popI32();
 
     // There may or may not be a value underneath, to be carried along
     // the taken edge.
     if (IsVoid(type))
         pushVoid();
 
-    if (type == ExprType::I32)
+    if (type == ExprType::I32 || type == ExprType::I64)
         freeI32(joinRegI32);
 
     AnyReg r = popJoinReg();
 
     Label dispatchCode;
     masm.branch32(Assembler::Below, rc.reg, Imm32(tableLength), &dispatchCode);
 
     // This is the out-of-range stub.  rc is dead here but we don't need it.
@@ -4809,19 +4814,20 @@ BaseCompiler::emitCallArgs(const ValType
 
     return true;
 }
 
 void
 BaseCompiler::pushReturned(ExprType type)
 {
     switch (type) {
-      case ExprType::Void:
+      case ExprType::Void: {
         pushVoid();
         break;
+      }
       case ExprType::I32: {
         RegI32 rv = needI32();
         captureReturnedI32(rv);
         pushI32(rv);
         break;
       }
       case ExprType::I64: {
         RegI64 rv = needI64();
@@ -5370,16 +5376,17 @@ BaseCompiler::emitSelect()
     Nothing unused_condition;
     if (!iter_.readSelect(&type, &unused_trueValue, &unused_falseValue, &unused_condition))
         return false;
 
     // I32 condition on top, then false, then true.
 
     RegI32 rc = popI32();
     switch (type) {
+      case AnyType:
       case ExprType::Void: {
         popValueStackBy(2);
         pushVoid();
         break;
       }
       case ExprType::I32: {
         Label done;
         RegI32 r0, r1;
@@ -5654,25 +5661,31 @@ BaseCompiler::emitBody()
 #define NEXT()        continue
 #define CHECK_NEXT(E) if (!(E)) goto done; continue
 
         // TODO / EVALUATE: Not obvious that this attempt at reducing
         // overhead is really paying off relative to making the check
         // every iteration.
 
         if (overhead == 0) {
+            // Check every 50 expressions -- a happy medium between
+            // memory usage and checking overhead.
+            overhead = 50;
+
             // Checking every 50 expressions should be safe, as the
             // baseline JIT does very little allocation per expression.
             CHECK(alloc_.ensureBallast());
-            CHECK(stk_.reserve(stk_.length() + 64));
-            overhead = 50;
-        } else {
-            overhead -= 1;
+
+            // The pushiest opcode is LOOP, which pushes two values
+            // per instance.
+            CHECK(stk_.reserve(stk_.length() + overhead * 2));
         }
 
+        overhead--;
+
         if (done())
             return true;
 
         uint32_t exprOffset = iter_.currentOffset();
 
         Expr expr;
         CHECK(iter_.readExpr(&expr));
 
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -179,17 +179,18 @@ js::CheckTracedThing(JSTracer* trc, T* t
 {
 #ifdef DEBUG
     MOZ_ASSERT(trc);
     MOZ_ASSERT(thing);
 
     if (!trc->checkEdges())
         return;
 
-    thing = MaybeForwarded(thing);
+    if (IsForwarded(thing))
+        thing = Forwarded(thing);
 
     /* This function uses data that's not available in the nursery. */
     if (IsInsideNursery(thing))
         return;
 
     MOZ_ASSERT_IF(!IsMovingTracer(trc) && !trc->isTenuringTracer(), !IsForwarded(thing));
 
     /*
@@ -230,18 +231,23 @@ js::CheckTracedThing(JSTracer* trc, T* t
         MOZ_ASSERT(!(zone->isGCSweeping() || zone->isGCFinished() || zone->isGCCompacting()));
     }
 
     /*
      * Try to assert that the thing is allocated.  This is complicated by the
      * fact that allocated things may still contain the poison pattern if that
      * part has not been overwritten.  Also, background sweeping may be running
      * and concurrently modifiying the free list.
+     *
+     * Tracing is done off main thread while compacting and reading the contents
+     * of the thing in IsThingPoisoned is racy so this check is skipped there.
      */
-    MOZ_ASSERT_IF(IsThingPoisoned(thing) && rt->isHeapBusy() && !rt->gc.isBackgroundSweeping(),
+    MOZ_ASSERT_IF(rt->isHeapBusy() && !zone->isGCCompacting() &&
+                  !rt->gc.isBackgroundSweeping() &&
+                  IsThingPoisoned(thing),
                   !InFreeList(thing->asTenured().arena(), thing));
 #endif
 }
 
 template <typename S>
 struct CheckTracedFunctor : public VoidDefaultAdaptor<S> {
     template <typename T> void operator()(T* t, JSTracer* trc) { CheckTracedThing(trc, t); }
 };
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/regress/reserve-enough.js
@@ -0,0 +1,16 @@
+// |jit-test| test-also-wasm-baseline
+load(libdir + "wasm.js");
+
+// Bug 1281131 - be sure to reserve enough stack space
+
+wasmEvalText(
+`(module
+  (func $func0
+   ${loopy(100)}
+   (nop)))`);
+
+function loopy(n) {
+    if (n == 0)
+	return "(nop)";
+    return `(loop $out${n} $in${n} ${loopy(n-1)})`;
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/regress/reserve-joinreg.js
@@ -0,0 +1,20 @@
+// |jit-test| test-also-wasm-baseline
+load(libdir + "wasm.js");
+
+if (!hasI64())
+    quit(0);
+
+// Bug 1280933, excerpted from binary test case provided there.
+
+wasmEvalText(
+`(module
+  (func $func0 (param $var0 i32) (result i32)
+	(i32.add
+	 (block
+	  (loop $label1 $label0
+		(block $label2
+		       (br_table $label0 $label1 $label2 (i64.const 0) (get_local $var0)))
+		(set_local $var0 (i32.mul (i32.const 2) (get_local $var0))))
+	  (set_local $var0 (i32.add (i32.const 4) (get_local $var0))))
+	 (i32.const 1)))
+  (export "" 0))`);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/regress/select-any.js
@@ -0,0 +1,32 @@
+// |jit-test| test-also-wasm-baseline
+load(libdir + "wasm.js");
+
+// Bug 1280921
+
+var m1 = wasmEvalText(
+`(module
+  (type $type0 (func))
+  (func $func0
+   (select (unreachable) (return (nop)) (loop (i32.const 1))))
+  (export "" 0))`);
+
+try {
+    m1();
+} catch (e) {
+    if (!(e instanceof Error && e.message.match(/unreachable executed/)))
+	throw e;
+}
+
+var m2 = wasmEvalText(
+`(module
+  (type $type0 (func))
+  (func $func0
+   (select (i32.const 26) (unreachable) (i32.const 3)))
+  (export "" 0))`);
+
+try {
+    m2();
+} catch (e) {
+    if (!(e instanceof Error && e.message.match(/unreachable executed/)))
+	throw e;
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/regress/shift-counts.js
@@ -0,0 +1,11 @@
+// |jit-test| test-also-wasm-baseline
+load(libdir + "wasm.js");
+
+// Bug 1280926, extracted from binary
+
+wasmEvalText(
+`(module
+  (type $type0 (func (result i32)))
+  (export "" $func0)
+  (func $func0 (result i32)
+   (i32.shr_s (i32.const -40) (i32.const 34))))`);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/regress/too-large-frame.js
@@ -0,0 +1,30 @@
+// |jit-test| test-also-wasm-baseline
+load(libdir + "wasm.js");
+
+// Bug 1280934, equivalent test case.
+
+try {
+
+wasmEvalText(
+`(module
+  (func $func0 (result i32) ${locals()}
+   (i32.const 0))
+  (export "" 0))`);
+
+} catch (e) {
+    // The wasm baseline compiler throws OOM on too-large frames, so
+    // handle that.
+    if (!String(e).match(/out of memory/))
+	throw e;
+}
+
+// The wasm baseline compiler cuts off frames at 256KB at the moment;
+// the test case for bug 1280934 constructed a frame around 512KB so
+// duplicate that here.
+
+function locals() {
+    var s = "";
+    for ( var i=0 ; i < 64000 ; i++ )
+	s += "(local f64)\n";
+    return s;
+}
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -2465,21 +2465,30 @@ GCRuntime::updateCellPointers(MovingTrac
     {
         AutoLockHelperThreadState lock;
 
         for (size_t i = 0; i < tasksStarted; i++)
             joinTask(*bgTasks[i], gcstats::PHASE_COMPACT_UPDATE_CELLS);
     }
 }
 
+// Pointer updates run in three phases because of depdendencies between the
+// different types of GC thing. The most important consideration is the
+// dependency:
+//
+//    object ---> shape ---> base shape
+
+static const AllocKinds UpdatePhaseBaseShapes {
+    AllocKind::BASE_SHAPE
+};
+
 static const AllocKinds UpdatePhaseMisc {
     AllocKind::SCRIPT,
     AllocKind::LAZY_SCRIPT,
     AllocKind::SHAPE,
-    AllocKind::BASE_SHAPE,
     AllocKind::ACCESSOR_SHAPE,
     AllocKind::OBJECT_GROUP,
     AllocKind::STRING,
     AllocKind::JITCODE
 };
 
 static const AllocKinds UpdatePhaseObjects {
     AllocKind::FUNCTION,
@@ -2500,16 +2509,18 @@ static const AllocKinds UpdatePhaseObjec
 
 void
 GCRuntime::updateAllCellPointers(MovingTracer* trc, Zone* zone)
 {
     AutoDisableProxyCheck noProxyCheck(rt); // These checks assert when run in parallel.
 
     size_t bgTaskCount = CellUpdateBackgroundTaskCount();
 
+    updateCellPointers(trc, zone, UpdatePhaseBaseShapes, bgTaskCount);
+
     updateCellPointers(trc, zone, UpdatePhaseMisc, bgTaskCount);
 
     // Update TypeDescrs before all other objects as typed objects access these
     // objects when we trace them.
     updateTypeDescrObjects(trc, zone);
 
     updateCellPointers(trc, zone, UpdatePhaseObjects, bgTaskCount);
 }
--- a/media/mtransport/nr_timer.cpp
+++ b/media/mtransport/nr_timer.cpp
@@ -114,17 +114,17 @@ class nrappkitTimerCallback : public nra
   virtual ~nrappkitTimerCallback() {}
 };
 
 NS_IMPL_ISUPPORTS(nrappkitTimerCallback, nsITimerCallback)
 
 NS_IMETHODIMP nrappkitTimerCallback::Notify(nsITimer *timer) {
   r_log(LOG_GENERIC, LOG_DEBUG, "Timer callback fired (set in %s:%d)",
         function_.c_str(), line_);
-  MOZ_ASSERT(timer == timer_);
+  MOZ_RELEASE_ASSERT(timer == timer_);
   cb_(0, 0, cb_arg_);
 
   // Allow the timer to go away.
   timer_ = nullptr;
   return NS_OK;
 }
 
 class nrappkitScheduledCallback : public nrappkitCallback {
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
@@ -200,28 +200,29 @@ PeerConnectionImpl* PeerConnectionImpl::
 }
 
 NS_IMETHODIMP PeerConnectionMedia::ProtocolProxyQueryHandler::
 OnProxyAvailable(nsICancelable *request,
                  nsIChannel *aChannel,
                  nsIProxyInfo *proxyinfo,
                  nsresult result) {
 
-  if (result == NS_ERROR_ABORT) {
-    // NS_ERROR_ABORT means that the PeerConnectionMedia is no longer waiting
+  if (!pcm_->mProxyRequest) {
+    // PeerConnectionMedia is no longer waiting
     return NS_OK;
   }
 
   CSFLogInfo(logTag, "%s: Proxy Available: %d", __FUNCTION__, (int)result);
 
   if (NS_SUCCEEDED(result) && proxyinfo) {
     SetProxyOnPcm(*proxyinfo);
   }
 
   pcm_->mProxyResolveCompleted = true;
+  pcm_->mProxyRequest = nullptr;
   pcm_->FlushIceCtxOperationQueueIfReady();
 
   return NS_OK;
 }
 
 void
 PeerConnectionMedia::ProtocolProxyQueryHandler::SetProxyOnPcm(
     nsIProxyInfo& proxyinfo)
@@ -990,16 +991,17 @@ PeerConnectionMedia::SelfDestruct()
   }
 
   for (uint32_t i=0; i < mRemoteSourceStreams.Length(); ++i) {
     mRemoteSourceStreams[i]->DetachMedia_m();
   }
 
   if (mProxyRequest) {
     mProxyRequest->Cancel(NS_ERROR_ABORT);
+    mProxyRequest = nullptr;
   }
 
   // Shutdown the transport (async)
   RUN_ON_THREAD(mSTSThread, WrapRunnable(
       this, &PeerConnectionMedia::ShutdownMediaTransport_s),
                 NS_DISPATCH_NORMAL);
 
   CSFLogDebug(logTag, "%s: Media shut down", __FUNCTION__);
--- a/mobile/android/components/NSSDialogService.js
+++ b/mobile/android/components/NSSDialogService.js
@@ -32,17 +32,17 @@ NSSDialogs.prototype = {
     }
     return this.bundle.GetStringFromName(aName);
   },
 
   formatString: function(aName, argList) {
     if (!this.bundle) {
       this.bundle = Services.strings.createBundle("chrome://browser/locale/pippki.properties");
     }
-    return this.bundle.formatStringFromName(aName, argList, 1);
+    return this.bundle.formatStringFromName(aName, argList, argList.length);
   },
 
   getPrompt: function(aTitle, aText, aButtons) {
     return new Prompt({
       title: aTitle,
       text: aText,
       buttons: aButtons,
     });
@@ -138,72 +138,111 @@ NSSDialogs.prototype = {
                           ["certmgr.begins", aCert.validity.notBeforeLocalDay,
                            "certmgr.expires", aCert.validity.notAfterLocalDay])})
      .addLabel({ label: this.certInfoSection("certmgr.fingerprints.label",
                           ["certmgr.certdetail.sha256fingerprint", aCert.sha256Fingerprint,
                            "certmgr.certdetail.sha1fingerprint", aCert.sha1Fingerprint], false) });
     this.showPrompt(p);
   },
 
+  /**
+   * Returns a list of details of the given cert relevant for TLS client
+   * authentication.
+   *
+   * @param {nsIX509Cert} cert Cert to get the details of.
+   * @returns {String} <br/> delimited list of details.
+   */
+  getCertDetails: function(cert) {
+    let detailLines = [
+      this.formatString("clientAuthAsk.issuedTo", [cert.subjectName]),
+      this.formatString("clientAuthAsk.serial", [cert.serialNumber]),
+      this.formatString("clientAuthAsk.validityPeriod",
+                        [cert.validity.notBeforeLocalTime,
+                         cert.validity.notAfterLocalTime]),
+    ];
+    let keyUsages = cert.keyUsages;
+    if (keyUsages) {
+      detailLines.push(this.formatString("clientAuthAsk.keyUsages",
+                                         [keyUsages]));
+    }
+    let emailAddresses = cert.getEmailAddresses({});
+    if (emailAddresses.length > 0) {
+      let joinedAddresses = emailAddresses.join(", ");
+      detailLines.push(this.formatString("clientAuthAsk.emailAddresses",
+                                         [joinedAddresses]));
+    }
+    detailLines.push(this.formatString("clientAuthAsk.issuedBy",
+                                       [cert.issuerName]));
+    detailLines.push(this.formatString("clientAuthAsk.storedOn",
+                                       [cert.tokenName]));
+
+    return detailLines.join("<br/>");
+  },
+
   viewCertDetails: function(details) {
     let p = this.getPrompt(this.getString("clientAuthAsk.message3"),
                     '',
                     [ this.getString("nssdialogs.ok.label") ]);
     p.addLabel({ label: details });
     this.showPrompt(p);
   },
 
-  ChooseCertificate: function(aCtx, cn, organization, issuer, certNickList, certDetailsList, count, selectedIndex, canceled) {
-    let rememberSetting = true;
-    var pref = Cc['@mozilla.org/preferences-service;1']
-               .getService(Components.interfaces.nsIPrefService);
-    if (pref) {
-      pref = pref.getBranch(null);
-      try {
-        rememberSetting = pref.getBoolPref("security.remember_cert_checkbox_default_setting");
-      } catch (e) {
-        // pref is missing
-      }
+  chooseCertificate: function(ctx, cnAndPort, organization, issuerOrg, certList,
+                              selectedIndex) {
+    let rememberSetting =
+      Services.prefs.getBoolPref("security.remember_cert_checkbox_default_setting");
+
+    let serverRequestedDetails = [
+      cnAndPort,
+      this.formatString("clientAuthAsk.organization", [organization]),
+      this.formatString("clientAuthAsk.issuer", [issuerOrg]),
+    ].join("<br/>");
+
+    let certNickList = [];
+    let certDetailsList = [];
+    for (let i = 0; i < certList.length; i++) {
+      let cert = certList.queryElementAt(i, Ci.nsIX509Cert);
+      certNickList.push(this.formatString("clientAuthAsk.nickAndSerial",
+                                          [cert.nickname, cert.serialNumber]));
+      certDetailsList.push(this.getCertDetails(cert));
     }
 
-    let organizationString = this.formatString("clientAuthAsk.organization",
-                                               [organization]);
-    let issuerString = this.formatString("clientAuthAsk.issuer",
-                                         [issuer]);
-    let serverRequestedDetails = cn + '<br/>' + organizationString + '<br/>' + issuerString;
-
-    selectedIndex = 0;
+    selectedIndex.value = 0;
     while (true) {
+      let buttons = [
+        this.getString("nssdialogs.ok.label"),
+        this.getString("clientAuthAsk.viewCert.label"),
+        this.getString("nssdialogs.cancel.label"),
+      ];
       let prompt = this.getPrompt(this.getString("clientAuthAsk.title"),
-                                     this.getString("clientAuthAsk.message1"),
-                                     [ this.getString("nssdialogs.ok.label"),
-                                       this.getString("clientAuthAsk.viewCert.label"),
-                                       this.getString("nssdialogs.cancel.label")
-                                     ])
+                                  this.getString("clientAuthAsk.message1"),
+                                  buttons)
       .addLabel({ id: "requestedDetails", label: serverRequestedDetails } )
       .addMenulist({
         id: "nicknames",
         label: this.getString("clientAuthAsk.message2"),
-        values: certNickList, selected: selectedIndex
+        values: certNickList,
+        selected: selectedIndex.value,
       }).addCheckbox({
         id: "rememberBox",
         label: this.getString("clientAuthAsk.remember.label"),
         checked: rememberSetting
       });
       let response = this.showPrompt(prompt);
-      selectedIndex = response.nicknames;
-      if (response.button == 1) {
-        this.viewCertDetails(certDetailsList[selectedIndex]);
+      selectedIndex.value = response.nicknames;
+      if (response.button == 1 /* buttons[1] */) {
+        this.viewCertDetails(certDetailsList[selectedIndex.value]);
         continue;
-      } else if (response.button == 0) {
-        canceled.value = false;
+      } else if (response.button == 0 /* buttons[0] */) {
         if (response.rememberBox == true) {
-          aCtx.QueryInterface(Ci.nsIClientAuthUserDecision).rememberClientAuthCertificate = true;
+          let caud = ctx.QueryInterface(Ci.nsIClientAuthUserDecision);
+          if (caud) {
+            caud.rememberClientAuthCertificate = true;
+          }
         }
         return true;
       }
-      canceled.value = true;
       return false;
     }
   }
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NSSDialogs]);
--- a/mobile/android/locales/en-US/chrome/pippki.properties
+++ b/mobile/android/locales/en-US/chrome/pippki.properties
@@ -14,18 +14,50 @@ downloadCert.trustEmail=Trust to identif
 downloadCert.trustObjSign=Trust to identify software developers.
 pkcs12.getpassword.title=Password Entry Dialog
 pkcs12.getpassword.message=Please enter the password that was used to encrypt this certificate backup.
 clientAuthAsk.title=User Identification Request
 clientAuthAsk.message1=This site has requested that you identify yourself with a certificate:
 clientAuthAsk.message2=Choose a certificate to present as identification:
 clientAuthAsk.message3=Details of selected certificate:
 clientAuthAsk.remember.label=Remember this decision
+# LOCALIZATION NOTE(clientAuthAsk.nickAndSerial): Represents a single cert when
+# the user is choosing from a list of certificates.
+# %1$S is the nickname of the cert.
+# %2$S is the serial number of the cert in AA:BB:CC hex format.
+clientAuthAsk.nickAndSerial=%1$S [%2$S]
+# LOCALIZATION NOTE(clientAuthAsk.organization): %S is the Organization of the
+# server cert.
 clientAuthAsk.organization=Organization: "%S"
+# LOCALIZATION NOTE(clientAuthAsk.issuer): %S is the Organization of the
+# issuer cert of the server cert.
 clientAuthAsk.issuer=Issued Under: "%S"
+# LOCALIZATION NOTE(clientAuthAsk.issuedTo): %1$S is the Distinguished Name of
+# the currently selected client cert, such as "CN=John Doe,OU=Example" (without
+# quotes).
+clientAuthAsk.issuedTo=Issued to: %1$S
+# LOCALIZATION NOTE(clientAuthAsk.serial): %1$S is the serial number of the
+# selected cert in AA:BB:CC hex format.
+clientAuthAsk.serial=Serial number: %1$S
+# LOCALIZATION NOTE(clientAuthAsk.validityPeriod):
+# %1$S is the already localized notBefore date of the selected cert.
+# %2$S is the already localized notAfter date of the selected cert.
+clientAuthAsk.validityPeriod=Valid from %1$S to %2$S
+# LOCALIZATION NOTE(clientAuthAsk.keyUsages): %1$S is a comma separated list of
+# already localized key usages the selected cert is valid for.
+clientAuthAsk.keyUsages=Key Usages: %1$S
+# LOCALIZATION NOTE(clientAuthAsk.emailAddresses): %1$S is a comma separated
+# list of e-mail addresses the selected cert is valid for.
+clientAuthAsk.emailAddresses=Email addresses: %1$S
+# LOCALIZATION NOTE(clientAuthAsk.issuedBy): %1$S is the Distinguished Name of
+# the cert which issued the selected cert.
+clientAuthAsk.issuedBy=Issued by: %1$S
+# LOCALIZATION NOTE(clientAuthAsk.storedOn): %1$S is the name of the PKCS #11
+# token the selected cert is stored on.
+clientAuthAsk.storedOn=Stored on: %1$S
 clientAuthAsk.viewCert.label=View
 
 certmgr.title=Certificate Details
 # These strings are stolen from security/manager/locales/en-US/chrome/pippki/certManager.dtd
 certmgr.subjectinfo.label=Issued To
 certmgr.issuerinfo.label=Issued By
 certmgr.periodofvalidity.label=Period of Validity
 certmgr.fingerprints.label=Fingerprints
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1407,17 +1407,17 @@ pref("network.http.referer.spoofSource",
 pref("network.http.referer.trimmingPolicy", 0);
 // 0=always send, 1=send iff base domains match, 2=send iff hosts match
 pref("network.http.referer.XOriginPolicy", 0);
 
 // Controls whether we send HTTPS referres to other HTTPS sites.
 // By default this is enabled for compatibility (see bug 141641)
 pref("network.http.sendSecureXSiteReferrer", true);
 
-// Controls whether referrer attributes in <a>, <img>, <area>, and <iframe> are honoured
+// Controls whether referrer attributes in <a>, <img>, <area>, <iframe>, and <link> are honoured
 pref("network.http.enablePerElementReferrer", true);
 
 // Maximum number of consecutive redirects before aborting.
 pref("network.http.redirection-limit", 20);
 
 // Enable http compression: comment this out in case of problems with 1.1
 // NOTE: support for "compress" has been disabled per bug 196406.
 // NOTE: separate values with comma+space (", "): see bug 576033
--- a/netwerk/base/LoadInfo.cpp
+++ b/netwerk/base/LoadInfo.cpp
@@ -337,16 +337,24 @@ NS_IMPL_ISUPPORTS(LoadInfo, nsILoadInfo)
 already_AddRefed<nsILoadInfo>
 LoadInfo::Clone() const
 {
   RefPtr<LoadInfo> copy(new LoadInfo(*this));
   return copy.forget();
 }
 
 already_AddRefed<nsILoadInfo>
+LoadInfo::CloneWithNewSecFlags(nsSecurityFlags aSecurityFlags) const
+{
+  RefPtr<LoadInfo> copy(new LoadInfo(*this));
+  copy->mSecurityFlags = aSecurityFlags;
+  return copy.forget();
+}
+
+already_AddRefed<nsILoadInfo>
 LoadInfo::CloneForNewRequest() const
 {
   RefPtr<LoadInfo> copy(new LoadInfo(*this));
   copy->mEnforceSecurity = false;
   copy->mInitialSecurityCheckDone = false;
   copy->mRedirectChainIncludingInternalRedirects.Clear();
   copy->mRedirectChain.Clear();
   return copy.forget();
--- a/netwerk/base/LoadInfo.h
+++ b/netwerk/base/LoadInfo.h
@@ -59,16 +59,21 @@ public:
   // Constructor used for TYPE_DOCUMENT loads which have no reasonable
   // loadingNode or loadingPrincipal
   LoadInfo(nsPIDOMWindowOuter* aOuterWindow,
            nsIPrincipal* aTriggeringPrincipal,
            nsSecurityFlags aSecurityFlags);
 
   // create an exact copy of the loadinfo
   already_AddRefed<nsILoadInfo> Clone() const;
+  // hands off!!! don't use CloneWithNewSecFlags unless you know
+  // exactly what you are doing - it should only be used within
+  // nsBaseChannel::Redirect()
+  already_AddRefed<nsILoadInfo>
+  CloneWithNewSecFlags(nsSecurityFlags aSecurityFlags) const;
   // creates a copy of the loadinfo which is appropriate to use for a
   // separate request. I.e. not for a redirect or an inner channel, but
   // when a separate request is made with the same security properties.
   already_AddRefed<nsILoadInfo> CloneForNewRequest() const;
 
   void SetIsPreflight();
 
 private:
--- a/netwerk/base/nsBaseChannel.cpp
+++ b/netwerk/base/nsBaseChannel.cpp
@@ -79,18 +79,20 @@ nsBaseChannel::Redirect(nsIChannel *newC
 
   newChannel->SetLoadGroup(mLoadGroup);
   newChannel->SetNotificationCallbacks(mCallbacks);
   newChannel->SetLoadFlags(mLoadFlags | LOAD_REPLACE);
 
   // make a copy of the loadinfo, append to the redirectchain
   // and set it on the new channel
   if (mLoadInfo) {
+    nsSecurityFlags secFlags = mLoadInfo->GetSecurityFlags() ^
+                               nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
     nsCOMPtr<nsILoadInfo> newLoadInfo =
-      static_cast<mozilla::LoadInfo*>(mLoadInfo.get())->Clone();
+      static_cast<mozilla::LoadInfo*>(mLoadInfo.get())->CloneWithNewSecFlags(secFlags);
 
     nsCOMPtr<nsIPrincipal> uriPrincipal;
     nsIScriptSecurityManager *sm = nsContentUtils::GetSecurityManager();
     sm->GetChannelURIPrincipal(this, getter_AddRefs(uriPrincipal));
     bool isInternalRedirect =
       (redirectFlags & (nsIChannelEventSink::REDIRECT_INTERNAL |
                         nsIChannelEventSink::REDIRECT_STS_UPGRADE));
     newLoadInfo->AppendRedirectedPrincipal(uriPrincipal, isInternalRedirect);
--- a/security/manager/locales/en-US/chrome/pippki/pippki.properties
+++ b/security/manager/locales/en-US/chrome/pippki/pippki.properties
@@ -47,18 +47,51 @@ certNotVerified_CertExpired=Could not ve
 certNotVerified_CertNotTrusted=Could not verify this certificate because it is not trusted.
 certNotVerified_IssuerNotTrusted=Could not verify this certificate because the issuer is not trusted.
 certNotVerified_IssuerUnknown=Could not verify this certificate because the issuer is unknown.
 certNotVerified_CAInvalid=Could not verify this certificate because the CA certificate is invalid.
 certNotVerified_AlgorithmDisabled=Could not verify this certificate because it was signed using a signature algorithm that was disabled because that algorithm is not secure.
 certNotVerified_Unknown=Could not verify this certificate for unknown reasons.
 
 #Client auth
+clientAuthRemember=Remember this decision
+# LOCALIZATION NOTE(clientAuthNickAndSerial): Represents a single cert when the
+# user is choosing from a list of certificates.
+# %1$S is the nickname of the cert.
+# %2$S is the serial number of the cert in AA:BB:CC hex format.
+clientAuthNickAndSerial=%1$S [%2$S]
+# LOCALIZATION NOTE(clientAuthMessage1): %S is the Organization of the server
+# cert.
 clientAuthMessage1=Organization: ā€œ%Sā€
+# LOCALIZATION NOTE(clientAuthMessage2): %S is the Organization of the issuer
+# cert of the server cert.
 clientAuthMessage2=Issued Under: ā€œ%Sā€
+# LOCALIZATION NOTE(clientAuthIssuedTo): %1$S is the Distinguished Name of the
+# currently selected client cert, such as "CN=John Doe,OU=Example" (without
+# quotes).
+clientAuthIssuedTo=Issued to: %1$S
+# LOCALIZATION NOTE(clientAuthSerial): %1$S is the serial number of the selected
+# cert in AA:BB:CC hex format.
+clientAuthSerial=Serial number: %1$S
+# LOCALIZATION NOTE(clientAuthValidityPeriod):
+# %1$S is the already localized notBefore date of the selected cert.
+# %2$S is the already localized notAfter date of the selected cert.
+clientAuthValidityPeriod=Valid from %1$S to %2$S
+# LOCALIZATION NOTE(clientAuthKeyUsages): %1$S is a comma separated list of
+# already localized key usages the selected cert is valid for.
+clientAuthKeyUsages=Key Usages: %1$S
+# LOCALIZATION NOTE(clientAuthEmailAddresses): %1$S is a comma separated list of
+# e-mail addresses the selected cert is valid for.
+clientAuthEmailAddresses=Email addresses: %1$S
+# LOCALIZATION NOTE(clientAuthIssuedBy): %1$S is the Distinguished Name of the
+# cert which issued the selected cert.
+clientAuthIssuedBy=Issued by: %1$S
+# LOCALIZATION NOTE(clientAuthStoredOn): %1$S is the name of the PKCS #11 token
+# the selected cert is stored on.
+clientAuthStoredOn=Stored on: %1$S
 
 #Page Info
 pageInfo_NoEncryption=Connection Not Encrypted
 pageInfo_Privacy_None1=The website %S does not support encryption for the page you are viewing.
 pageInfo_Privacy_None2=Information sent over the Internet without encryption can be seen by other people while it is in transit. 
 pageInfo_Privacy_None4=The page you are viewing was not encrypted before being transmitted over the Internet.
 # LOCALIZATION NOTE (pageInfo_EncryptionWithBitsAndProtocol and pageInfo_BrokenEncryption):
 # %1$S is the name of the encryption standard,
--- a/security/manager/pki/nsNSSDialogs.cpp
+++ b/security/manager/pki/nsNSSDialogs.cpp
@@ -3,40 +3,39 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /*
  * Dialog services for PIP.
  */
 #include "mozIDOMWindow.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Casting.h"
 #include "nsArray.h"
-#include "nsCOMPtr.h"
 #include "nsDateTimeFormatCID.h"
 #include "nsEmbedCID.h"
 #include "nsIComponentManager.h"
 #include "nsIDateTimeFormat.h"
 #include "nsIDialogParamBlock.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIKeygenThread.h"
 #include "nsIPromptService.h"
 #include "nsIProtectedAuthThread.h"
 #include "nsIServiceManager.h"
-#include "nsIStringBundle.h"
 #include "nsIWindowWatcher.h"
 #include "nsIX509CertDB.h"
 #include "nsIX509Cert.h"
 #include "nsIX509CertValidity.h"
 #include "nsNSSDialogHelper.h"
 #include "nsNSSDialogs.h"
 #include "nsPromiseFlatString.h"
 #include "nsReadableUtils.h"
 #include "nsString.h"
-#include "nsXPIDLString.h"
 
 #define PIPSTRING_BUNDLE_URL "chrome://pippki/locale/pippki.properties"
 
 /* ==== */
 
 nsNSSDialogs::nsNSSDialogs()
 {
 }
@@ -153,83 +152,107 @@ nsNSSDialogs::ConfirmDownloadCACert(nsII
   *_trust |= (objsign) ? nsIX509CertDB::TRUSTED_OBJSIGN : 0;
 
   *_retval = (status != 0);
 
   return rv;
 }
 
 NS_IMETHODIMP
-nsNSSDialogs::ChooseCertificate(nsIInterfaceRequestor* ctx, const char16_t* cn,
-                                const char16_t* organization,
-                                const char16_t* issuer,
-                                const char16_t** certNickList,
-                                const char16_t** certDetailsList, uint32_t count,
-                                int32_t* selectedIndex, bool* canceled)
+nsNSSDialogs::ChooseCertificate(nsIInterfaceRequestor* ctx,
+                                const nsAString& cnAndPort,
+                                const nsAString& organization,
+                                const nsAString& issuerOrg,
+                                nsIArray* certList,
+                        /*out*/ uint32_t* selectedIndex,
+                        /*out*/ bool* certificateChosen)
 {
-  nsresult rv;
-  uint32_t i;
+  NS_ENSURE_ARG_POINTER(ctx);
+  NS_ENSURE_ARG_POINTER(certList);
+  NS_ENSURE_ARG_POINTER(selectedIndex);
+  NS_ENSURE_ARG_POINTER(certificateChosen);
 
-  *canceled = false;
-
-  // Get the parent window for the dialog
-  nsCOMPtr<nsIDOMWindow> parent = do_GetInterface(ctx);
+  *certificateChosen = false;
 
   nsCOMPtr<nsIDialogParamBlock> block =
-           do_CreateInstance(NS_DIALOGPARAMBLOCK_CONTRACTID);
-  if (!block) return NS_ERROR_FAILURE;
-
-  block->SetNumberStrings(4+count*2);
-
-  rv = block->SetString(0, cn);
-  if (NS_FAILED(rv)) return rv;
+    do_CreateInstance(NS_DIALOGPARAMBLOCK_CONTRACTID);
+  if (!block) {
+    return NS_ERROR_FAILURE;
+  }
 
-  rv = block->SetString(1, organization);
-  if (NS_FAILED(rv)) return rv;
-
-  rv = block->SetString(2, issuer);
-  if (NS_FAILED(rv)) return rv;
-
-  for (i = 0; i < count; i++) {
-    rv = block->SetString(i+3, certNickList[i]);
-    if (NS_FAILED(rv)) return rv;
+  nsCOMPtr<nsIMutableArray> paramBlockArray = nsArrayBase::Create();
+  if (!paramBlockArray) {
+    return NS_ERROR_FAILURE;
+  }
+  nsresult rv = paramBlockArray->AppendElement(certList, false);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  rv = block->SetObjects(paramBlockArray);
+  if (NS_FAILED(rv)) {
+    return rv;
   }
 
-  for (i = 0; i < count; i++) {
-    rv = block->SetString(i+count+3, certDetailsList[i]);
-    if (NS_FAILED(rv)) return rv;
+  rv = block->SetNumberStrings(3);
+  if (NS_FAILED(rv)) {
+    return rv;
   }
 
-  rv = block->SetInt(0, count);
-  if (NS_FAILED(rv)) return rv;
+  rv = block->SetString(0, PromiseFlatString(cnAndPort).get());
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  rv = block->SetString(1, PromiseFlatString(organization).get());
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  rv = block->SetString(2, PromiseFlatString(issuerOrg).get());
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
 
   rv = nsNSSDialogHelper::openDialog(nullptr,
-                                "chrome://pippki/content/clientauthask.xul",
-                                block);
-  if (NS_FAILED(rv)) return rv;
+                                     "chrome://pippki/content/clientauthask.xul",
+                                     block);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
 
   int32_t status;
   rv = block->GetInt(0, &status);
-  if (NS_FAILED(rv)) return rv;
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
 
   nsCOMPtr<nsIClientAuthUserDecision> extraResult = do_QueryInterface(ctx);
   if (extraResult) {
     int32_t rememberSelection;
     rv = block->GetInt(2, &rememberSelection);
     if (NS_SUCCEEDED(rv)) {
       extraResult->SetRememberClientAuthCertificate(rememberSelection!=0);
     }
   }
 
-  *canceled = (status == 0)?true:false;
-  if (!*canceled) {
-    // retrieve the nickname
-    rv = block->GetInt(1, selectedIndex);
+  *certificateChosen = (status != 0);
+  if (*certificateChosen) {
+    int32_t index = 0;
+    rv = block->GetInt(1, &index);
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+
+    if (index < 0) {
+      MOZ_ASSERT_UNREACHABLE("Selected index should never be negative");
+      return NS_ERROR_FAILURE;
+    }
+
+    *selectedIndex = mozilla::AssertedCast<uint32_t>(index);
   }
-  return rv;
+
+  return NS_OK;
 }
 
 
 NS_IMETHODIMP
 nsNSSDialogs::PickCertificate(nsIInterfaceRequestor *ctx, 
                               const char16_t **certNickList, 
                               const char16_t **certDetailsList, 
                               uint32_t count, 
--- a/security/manager/pki/resources/content/clientauthask.js
+++ b/security/manager/pki/resources/content/clientauthask.js
@@ -1,104 +1,129 @@
 /* -*- tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2 -*-
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 /* import-globals-from pippki.js */
 "use strict";
 
-const nsIDialogParamBlock = Components.interfaces.nsIDialogParamBlock;
+const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
+
+const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
 
+/**
+ * The pippki <stringbundle> element.
+ * @type <stringbundle>
+ */
+var bundle;
+/**
+ * The array of certs the user can choose from.
+ * @type nsIArray<nsIX509Cert>
+ */
+var certArray;
+/**
+ * The param block to get params from and set results on.
+ * @type nsIDialogParamBlock
+ */
 var dialogParams;
-var itemCount = 0;
+/**
+ * The checkbox storing whether the user wants to remember the selected cert.
+ * @type nsIDOMXULCheckboxElement
+ */
 var rememberBox;
 
-function onLoad()
-{
-    var cn;
-    var org;
-    var issuer;
-
-    dialogParams = window.arguments[0].QueryInterface(nsIDialogParamBlock);
-    cn = dialogParams.GetString(0);
-    org = dialogParams.GetString(1);
-    issuer = dialogParams.GetString(2);
-
-    // added with bug 431819. reuse string from caps in order to avoid string changes
-    var capsBundle = document.getElementById("caps_bundle");
-    var rememberString = capsBundle.getString("CheckMessage");
-    var rememberSetting = true;
+function onLoad() {
+  dialogParams = window.arguments[0].QueryInterface(Ci.nsIDialogParamBlock);
 
-    var pref = Components.classes['@mozilla.org/preferences-service;1']
-                         .getService(Components.interfaces.nsIPrefService);
-    if (pref) {
-      pref = pref.getBranch(null);
-      try {
-	rememberSetting =
-	  pref.getBoolPref("security.remember_cert_checkbox_default_setting");
-      } catch (e) {
-	// pref is missing
-      }
-    }
+  bundle = document.getElementById("pippki_bundle");
+  let rememberSetting =
+    Services.prefs.getBoolPref("security.remember_cert_checkbox_default_setting");
 
-    rememberBox = document.getElementById("rememberBox");
-    rememberBox.label = rememberString;
-    rememberBox.checked = rememberSetting;
+  rememberBox = document.getElementById("rememberBox");
+  rememberBox.label = bundle.getString("clientAuthRemember");
+  rememberBox.checked = rememberSetting;
 
-    var bundle = document.getElementById("pippki_bundle");
-    var message1 = bundle.getFormattedString("clientAuthMessage1", [org]);
-    var message2 = bundle.getFormattedString("clientAuthMessage2", [issuer]);
-    setText("hostname", cn);
-    setText("organization", message1);
-    setText("issuer", message2);
+  let cnAndPort = dialogParams.GetString(0);
+  let org = dialogParams.GetString(1);
+  let issuerOrg = dialogParams.GetString(2);
+  let formattedOrg = bundle.getFormattedString("clientAuthMessage1", [org]);
+  let formattedIssuerOrg = bundle.getFormattedString("clientAuthMessage2",
+                                                     [issuerOrg]);
+  setText("hostname", cnAndPort);
+  setText("organization", formattedOrg);
+  setText("issuer", formattedIssuerOrg);
 
-    var selectElement = document.getElementById("nicknames");
-    itemCount = dialogParams.GetInt(0);
-    for (let i = 0; i < itemCount; i++) {
-        var menuItemNode = document.createElement("menuitem");
-        let nick = dialogParams.GetString(i + 3);
-        menuItemNode.setAttribute("value", i);
-        menuItemNode.setAttribute("label", nick); // this is displayed
-        selectElement.firstChild.appendChild(menuItemNode);
-        if (i == 0) {
-            selectElement.selectedItem = menuItemNode;
-        }
+  let selectElement = document.getElementById("nicknames");
+  certArray = dialogParams.objects.queryElementAt(0, Ci.nsIArray);
+  for (let i = 0; i < certArray.length; i++) {
+    let menuItemNode = document.createElement("menuitem");
+    let cert = certArray.queryElementAt(i, Ci.nsIX509Cert);
+    let nickAndSerial =
+      bundle.getFormattedString("clientAuthNickAndSerial",
+                                [cert.nickname, cert.serialNumber]);
+    menuItemNode.setAttribute("value", i);
+    menuItemNode.setAttribute("label", nickAndSerial); // This is displayed.
+    selectElement.firstChild.appendChild(menuItemNode);
+    if (i == 0) {
+      selectElement.selectedItem = menuItemNode;
     }
+  }
 
-    setDetails();
-}
-
-function setDetails()
-{
-  let index = parseInt(document.getElementById("nicknames").value);
-  let details = dialogParams.GetString(index + itemCount + 3);
-  document.getElementById("details").value = details;
-}
-
-function onCertSelected()
-{
   setDetails();
 }
 
-function doOK()
-{
+/**
+ * Populates the details section with information concerning the selected cert.
+ */
+function setDetails() {
+  let index = parseInt(document.getElementById("nicknames").value);
+  let cert = certArray.queryElementAt(index, Ci.nsIX509Cert);
+
+  let detailLines = [
+    bundle.getFormattedString("clientAuthIssuedTo", [cert.subjectName]),
+    bundle.getFormattedString("clientAuthSerial", [cert.serialNumber]),
+    bundle.getFormattedString("clientAuthValidityPeriod",
+                              [cert.validity.notBeforeLocalTime,
+                               cert.validity.notAfterLocalTime]),
+  ];
+  let keyUsages = cert.keyUsages;
+  if (keyUsages) {
+    detailLines.push(bundle.getFormattedString("clientAuthKeyUsages",
+                                               [keyUsages]));
+  }
+  let emailAddresses = cert.getEmailAddresses({});
+  if (emailAddresses.length > 0) {
+    let joinedAddresses = emailAddresses.join(", ");
+    detailLines.push(bundle.getFormattedString("clientAuthEmailAddresses",
+                                               [joinedAddresses]));
+  }
+  detailLines.push(bundle.getFormattedString("clientAuthIssuedBy",
+                                             [cert.issuerName]));
+  detailLines.push(bundle.getFormattedString("clientAuthStoredOn",
+                                             [cert.tokenName]));
+
+  document.getElementById("details").value = detailLines.join("\n");
+}
+
+function onCertSelected() {
+  setDetails();
+}
+
+function doOK() {
   // Signal that the user accepted.
   dialogParams.SetInt(0, 1);
   let index = parseInt(document.getElementById("nicknames").value);
   // Signal the index of the selected cert in the list of cert nicknames
   // provided.
   dialogParams.SetInt(1, index);
   // Signal whether the user wanted to remember the selection.
   dialogParams.SetInt(2, rememberBox.checked);
   return true;
 }
 
-function doCancel()
-{
+function doCancel() {
   // Signal that the user cancelled.
   dialogParams.SetInt(0, 0);
-  // Signal some invalid index value since a cert hasn't actually been chosen.
-  dialogParams.SetInt(1, -1); // invalid value
   // Signal whether the user wanted to remember the "selection".
   dialogParams.SetInt(2, rememberBox.checked);
   return true;
 }
--- a/security/manager/pki/resources/content/clientauthask.xul
+++ b/security/manager/pki/resources/content/clientauthask.xul
@@ -15,17 +15,16 @@
   xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"      
   buttons="accept,cancel"
   ondialogaccept="return doOK();"
   ondialogcancel="return doCancel();"
   onload="onLoad();">
 
 <stringbundleset id="stringbundleset">
   <stringbundle id="pippki_bundle" src="chrome://pippki/locale/pippki.properties"/>
-  <stringbundle id="caps_bundle" src="chrome://global/locale/security/caps.properties"/>
 </stringbundleset>
 
 <script type="application/javascript" src="chrome://pippki/content/pippki.js"/>
 <script type="application/javascript" src="chrome://pippki/content/clientauthask.js"/>
 
   <groupbox>
     <description style="font-weight: bold;">&clientAuthAsk.message1;</description>
     <description id="hostname"/>
--- a/security/manager/ssl/nsIClientAuthDialogs.idl
+++ b/security/manager/ssl/nsIClientAuthDialogs.idl
@@ -1,36 +1,42 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
+interface nsIArray;
 interface nsIInterfaceRequestor;
 
 /**
- * nsIClientAuthDialog
  * Provides UI for SSL client-auth dialogs.
  */
 [scriptable, uuid(fa4c7520-1433-11d5-ba24-00108303b117)]
 interface nsIClientAuthDialogs : nsISupports
 {
   /**
-   * display
-   *   UI shown when a user is asked to do SSL client auth.
+   * Called when a user is asked to choose a certificate for client auth.
+   *
+   * @param ctx Context that allows at least nsIClientAuthUserDecision to be
+   *            queried.
+   * @param cnAndPort Common Name of the server cert and the port of the server.
+   * @param organization Organization field of the server cert.
+   * @param issuerOrg Organization field of the issuer cert of the server cert.
+   * @param certList List of certificates the user can choose from.
+   * @param selectedIndex Index of the cert in |certList| that the user chose.
+   *                      Ignored if the return value is false.
+   * @return true if a certificate was chosen. false if the user canceled.
    */
-  void ChooseCertificate(in nsIInterfaceRequestor ctx, 
-                        in wstring cn,
-                        in wstring organization,
-                        in wstring issuer,
-                        [array, size_is(count)] in wstring certNickList,
-                        [array, size_is(count)] in wstring certDetailsList,
-                        in unsigned long count,
-                        out long selectedIndex,
-                        out boolean canceled);
+  boolean chooseCertificate(in nsIInterfaceRequestor ctx,
+                            in AString cnAndPort,
+                            in AString organization,
+                            in AString issuerOrg,
+                            in nsIArray certList,
+                            out unsigned long selectedIndex);
 };
 
 [scriptable, uuid(95c4373e-bdd4-4a63-b431-f5b000367721)]
 interface nsIClientAuthUserDecision : nsISupports
 {
   attribute boolean rememberClientAuthCertificate;
 };
 
--- a/security/manager/ssl/nsIX509Cert.idl
+++ b/security/manager/ssl/nsIX509Cert.idl
@@ -178,34 +178,16 @@ interface nsIX509Cert : nsISupports {
   const unsigned long CERT_NOT_TRUSTED     = 1 << 3;
   const unsigned long ISSUER_NOT_TRUSTED   = 1 << 4;
   const unsigned long ISSUER_UNKNOWN       = 1 << 5;
   const unsigned long INVALID_CA           = 1 << 6;
   const unsigned long USAGE_NOT_ALLOWED    = 1 << 7;
   const unsigned long SIGNATURE_ALGORITHM_DISABLED = 1 << 8;
 
   /**
-   *  Constants that describe the certified usages of a certificate.
-   *
-   *  Deprecated and unused
-   */
-  const unsigned long CERT_USAGE_SSLClient = 0;
-  const unsigned long CERT_USAGE_SSLServer = 1;
-  const unsigned long CERT_USAGE_SSLServerWithStepUp = 2;
-  const unsigned long CERT_USAGE_SSLCA = 3;
-  const unsigned long CERT_USAGE_EmailSigner = 4;
-  const unsigned long CERT_USAGE_EmailRecipient = 5;
-  const unsigned long CERT_USAGE_ObjectSigner = 6;
-  const unsigned long CERT_USAGE_UserCertImport = 7;
-  const unsigned long CERT_USAGE_VerifyCA = 8;
-  const unsigned long CERT_USAGE_ProtectedObjectSigner = 9;
-  const unsigned long CERT_USAGE_StatusResponder = 10;
-  const unsigned long CERT_USAGE_AnyCA = 11;
-
-  /**
    *  Constants for specifying the chain mode when exporting a certificate
    */
   const unsigned long CMS_CHAIN_MODE_CertOnly = 1;
   const unsigned long CMS_CHAIN_MODE_CertChain = 2;
   const unsigned long CMS_CHAIN_MODE_CertChainWithRoot = 3;
 
   /**
    *  Obtain a list of certificates that contains this certificate
@@ -246,16 +228,23 @@ interface nsIX509Cert : nsISupports {
    *  @param localOnly Do not hit the network, even if revocation information
    *                   downloading is currently activated.
    *  @param verified The certificate verification result, see constants.
    *  @param purposes The string listing the usages.
    */
   void getUsagesString(in boolean localOnly, out uint32_t verified, out AString usages);
 
   /**
+   * A comma separated list of localized strings representing the contents of
+   * the certificate's key usage extension, if present. The empty string if the
+   * certificate doesn't have the key usage extension, or has an empty extension.
+   */
+  readonly attribute AString keyUsages;
+
+  /**
    *  This is the attribute which describes the ASN1 layout
    *  of the certificate.  This can be used when doing a
    *  "pretty print" of the certificate's ASN1 structure.
    */
   readonly attribute nsIASN1Object ASN1Structure;
 
   /**
    *  Obtain a raw binary encoding of this certificate
--- a/security/manager/ssl/nsNSSCertificate.cpp
+++ b/security/manager/ssl/nsNSSCertificate.cpp
@@ -6,16 +6,17 @@
 #include "nsNSSCertificate.h"
 
 #include "CertVerifier.h"
 #include "ExtendedValidation.h"
 #include "NSSCertDBTrustDomain.h"
 #include "certdb.h"
 #include "mozilla/Base64.h"
 #include "mozilla/Casting.h"
+#include "mozilla/NotNull.h"
 #include "mozilla/unused.h"
 #include "nsArray.h"
 #include "nsCOMPtr.h"
 #include "nsCRT.h"
 #include "nsCertVerificationThread.h"
 #include "nsICertificateDialogs.h"
 #include "nsIClassInfoImpl.h"
 #include "nsIObjectInputStream.h"
@@ -65,16 +66,18 @@ extern LazyLogModule gPIPNSSLog;
 // in the list to mean not yet initialized.
 #define CERT_TYPE_NOT_YET_INITIALIZED (1 << 30)
 
 NS_IMPL_ISUPPORTS(nsNSSCertificate,
                   nsIX509Cert,
                   nsISerializable,
                   nsIClassInfo)
 
+static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
+
 /*static*/ nsNSSCertificate*
 nsNSSCertificate::Create(CERTCertificate* cert, SECOidTag* evOidPolicy)
 {
   if (GeckoProcessType_Default != XRE_GetProcessType()) {
     NS_ERROR("Trying to initialize nsNSSCertificate in a non-chrome process!");
     return nullptr;
   }
   if (cert)
@@ -251,110 +254,102 @@ nsNSSCertificate::MarkForPermDeletion()
       return NS_ERROR_FAILURE;
     }
   }
 
   mPermDelete = true;
   return NS_OK;
 }
 
-nsresult
-GetKeyUsagesString(CERTCertificate* cert, nsINSSComponent* nssComponent,
-                   nsString& text)
+/**
+ * Appends a pipnss bundle string to the given string.
+ *
+ * @param nssComponent For accessing the string bundle.
+ * @param bundleKey Key for the string to append.
+ * @param currentText The text to append to, using commas as separators.
+ */
+template<size_t N>
+void
+AppendBundleString(const NotNull<nsCOMPtr<nsINSSComponent>>& nssComponent,
+                   const char (&bundleKey)[N],
+        /*in/out*/ nsAString& currentText)
+{
+  nsAutoString bundleString;
+  nsresult rv = nssComponent->GetPIPNSSBundleString(bundleKey, bundleString);
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
+  if (!currentText.IsEmpty()) {
+    currentText.Append(',');
+  }
+  currentText.Append(bundleString);
+}
+
+NS_IMETHODIMP
+nsNSSCertificate::GetKeyUsages(nsAString& text)
 {
   text.Truncate();
 
-  SECItem keyUsageItem;
-  keyUsageItem.data = nullptr;
-  keyUsageItem.len = 0;
+  nsCOMPtr<nsINSSComponent> nssComponent = do_GetService(kNSSComponentCID);
+  if (!nssComponent) {
+    return NS_ERROR_FAILURE;
+  }
 
-  SECStatus srv;
+  if (!mCert) {
+    return NS_ERROR_FAILURE;
+  }
 
-  // There is no extension, v1 or v2 certificate
-  if (!cert->extensions)
+  if (!mCert->extensions) {
     return NS_OK;
-
+  }
 
-  srv = CERT_FindKeyUsageExtension(cert, &keyUsageItem);
-  if (srv == SECFailure) {
-    if (PORT_GetError () == SEC_ERROR_EXTENSION_NOT_FOUND)
-      return NS_OK;
-    else
-      return NS_ERROR_FAILURE;
+  ScopedAutoSECItem keyUsageItem;
+  if (CERT_FindKeyUsageExtension(mCert.get(), &keyUsageItem) != SECSuccess) {
+    return PORT_GetError() == SEC_ERROR_EXTENSION_NOT_FOUND ? NS_OK
+                                                            : NS_ERROR_FAILURE;
   }
+
   unsigned char keyUsage = 0;
   if (keyUsageItem.len) {
     keyUsage = keyUsageItem.data[0];
   }
 
-  nsAutoString local;
-  nsresult rv;
-  const char16_t comma = ',';
-
+  NotNull<nsCOMPtr<nsINSSComponent>> wrappedNSSComponent =
+    WrapNotNull(nssComponent);
   if (keyUsage & KU_DIGITAL_SIGNATURE) {
-    rv = nssComponent->GetPIPNSSBundleString("CertDumpKUSign", local);
-    if (NS_SUCCEEDED(rv)) {
-      if (!text.IsEmpty()) text.Append(comma);
-      text.Append(local.get());
-    }
+    AppendBundleString(wrappedNSSComponent, "CertDumpKUSign", text);
   }
   if (keyUsage & KU_NON_REPUDIATION) {
-    rv = nssComponent->GetPIPNSSBundleString("CertDumpKUNonRep", local);
-    if (NS_SUCCEEDED(rv)) {
-      if (!text.IsEmpty()) text.Append(comma);
-      text.Append(local.get());
-    }
+    AppendBundleString(wrappedNSSComponent, "CertDumpKUNonRep", text);
   }
   if (keyUsage & KU_KEY_ENCIPHERMENT) {
-    rv = nssComponent->GetPIPNSSBundleString("CertDumpKUEnc", local);
-    if (NS_SUCCEEDED(rv)) {
-      if (!text.IsEmpty()) text.Append(comma);
-      text.Append(local.get());
-    }
+    AppendBundleString(wrappedNSSComponent, "CertDumpKUEnc", text);
   }
   if (keyUsage & KU_DATA_ENCIPHERMENT) {
-    rv = nssComponent->GetPIPNSSBundleString("CertDumpKUDEnc", local);
-    if (NS_SUCCEEDED(rv)) {
-      if (!text.IsEmpty()) text.Append(comma);
-      text.Append(local.get());
-    }
+    AppendBundleString(wrappedNSSComponent, "CertDumpKUDEnc", text);
   }
   if (keyUsage & KU_KEY_AGREEMENT) {
-    rv = nssComponent->GetPIPNSSBundleString("CertDumpKUKA", local);
-    if (NS_SUCCEEDED(rv)) {
-      if (!text.IsEmpty()) text.Append(comma);
-      text.Append(local.get());
-    }
+    AppendBundleString(wrappedNSSComponent, "CertDumpKUKA", text);
   }
   if (keyUsage & KU_KEY_CERT_SIGN) {
-    rv = nssComponent->GetPIPNSSBundleString("CertDumpKUCertSign", local);
-    if (NS_SUCCEEDED(rv)) {
-      if (!text.IsEmpty()) text.Append(comma);
-      text.Append(local.get());
-    }
+    AppendBundleString(wrappedNSSComponent, "CertDumpKUCertSign", text);
   }
   if (keyUsage & KU_CRL_SIGN) {
-    rv = nssComponent->GetPIPNSSBundleString("CertDumpKUCRLSign", local);
-    if (NS_SUCCEEDED(rv)) {
-      if (!text.IsEmpty()) text.Append(comma);
-      text.Append(local.get());
-    }
+    AppendBundleString(wrappedNSSComponent, "CertDumpKUCRLSign", text);
   }
 
-  PORT_Free (keyUsageItem.data);
   return NS_OK;
 }
 
 nsresult
 nsNSSCertificate::FormatUIStrings(const nsAutoString& nickname,
                                   nsAutoString& nickWithSerial,
                                   nsAutoString& details)
 {
-  static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
-
   if (!NS_IsMainThread()) {
     NS_ERROR("nsNSSCertificate::FormatUIStrings called off the main thread");
     return NS_ERROR_NOT_SAME_THREAD;
   }
 
   nsresult rv = NS_OK;
 
   nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(kNSSComponentCID, &rv));
@@ -416,18 +411,17 @@ nsNSSCertificate::FormatUIStrings(const 
         details.Append(char16_t(' '));
       }
       details.Append(temp1);
     }
 
     details.Append(char16_t('\n'));
   }
 
-  if (NS_SUCCEEDED(GetKeyUsagesString(mCert.get(), nssComponent, temp1)) &&
-      !temp1.IsEmpty()) {
+  if (NS_SUCCEEDED(GetKeyUsages(temp1)) && !temp1.IsEmpty()) {
     details.AppendLiteral("  ");
     if (NS_SUCCEEDED(nssComponent->GetPIPNSSBundleString("CertDumpKeyUsage", info))) {
       details.Append(info);
       details.AppendLiteral(": ");
     }
     details.Append(temp1);
     details.Append(char16_t('\n'));
   }
@@ -580,18 +574,16 @@ nsNSSCertificate::GetWindowTitle(nsAStri
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNSSCertificate::GetNickname(nsAString& aNickname)
 {
-  static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
-
   nsNSSShutDownPreventionLock locker;
   if (isAlreadyShutDown())
     return NS_ERROR_NOT_AVAILABLE;
 
   if (mCert->nickname) {
     CopyUTF8toUTF16(mCert->nickname, aNickname);
   } else {
     nsresult rv;
@@ -602,18 +594,16 @@ nsNSSCertificate::GetNickname(nsAString&
     nssComponent->GetPIPNSSBundleString("CertNoNickname", aNickname);
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNSSCertificate::GetEmailAddress(nsAString& aEmailAddress)
 {
-  static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
-
   nsNSSShutDownPreventionLock locker;
   if (isAlreadyShutDown())
     return NS_ERROR_NOT_AVAILABLE;
 
   if (mCert->emailAddr) {
     CopyUTF8toUTF16(mCert->emailAddr, aEmailAddress);
   } else {
     nsresult rv;
@@ -1049,18 +1039,16 @@ NS_IMETHODIMP
 nsNSSCertificate::GetSha1Fingerprint(nsAString& _sha1Fingerprint)
 {
   return GetCertificateHash(_sha1Fingerprint, SEC_OID_SHA1);
 }
 
 NS_IMETHODIMP
 nsNSSCertificate::GetTokenName(nsAString& aTokenName)
 {
-  static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
-
   nsNSSShutDownPreventionLock locker;
   if (isAlreadyShutDown())
     return NS_ERROR_NOT_AVAILABLE;
 
   aTokenName.Truncate();
   if (mCert) {
     // HACK alert
     // When the trust of a builtin cert is modified, NSS copies it into the
--- a/security/manager/ssl/nsNSSCertificateFakeTransport.cpp
+++ b/security/manager/ssl/nsNSSCertificateFakeTransport.cpp
@@ -1,22 +1,22 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsNSSCertificateFakeTransport.h"
 
+#include "mozilla/Assertions.h"
 #include "nsIClassInfoImpl.h"
 #include "nsIObjectInputStream.h"
 #include "nsIObjectOutputStream.h"
 #include "nsISupportsPrimitives.h"
 #include "nsNSSCertificate.h"
 #include "nsString.h"
-#include "nsXPIDLString.h"
 
 NS_IMPL_ISUPPORTS(nsNSSCertificateFakeTransport,
                   nsIX509Cert,
                   nsISerializable,
                   nsIClassInfo)
 
 nsNSSCertificateFakeTransport::nsNSSCertificateFakeTransport()
   : mCertSerialization(nullptr)
@@ -195,16 +195,23 @@ nsNSSCertificateFakeTransport::GetUsages
 NS_IMETHODIMP
 nsNSSCertificateFakeTransport::GetUsagesString(bool, uint32_t*, nsAString&)
 {
   NS_NOTREACHED("Unimplemented on content process");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
+nsNSSCertificateFakeTransport::GetKeyUsages(nsAString&)
+{
+  MOZ_ASSERT_UNREACHABLE("Unimplemented on content process");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
 nsNSSCertificateFakeTransport::GetASN1Structure(nsIASN1Object**)
 {
   NS_NOTREACHED("Unimplemented on content process");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsNSSCertificateFakeTransport::Equals(nsIX509Cert*, bool*)
--- a/security/manager/ssl/nsNSSIOLayer.cpp
+++ b/security/manager/ssl/nsNSSIOLayer.cpp
@@ -16,16 +16,17 @@
 #include "SharedSSLState.h"
 #include "keyhi.h"
 #include "mozilla/Casting.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/Logging.h"
 #include "mozilla/Move.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Telemetry.h"
+#include "nsArrayUtils.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsClientAuthRemember.h"
 #include "nsContentUtils.h"
 #include "nsIClientAuthDialogs.h"
 #include "nsIConsoleService.h"
 #include "nsIPrefService.h"
 #include "nsISocketProvider.h"
 #include "nsIWebProgressListener.h"
@@ -2094,21 +2095,16 @@ ClientAuthDataRunnable::RunOnTargetThrea
   // We check the value of a pref in this runnable, so this runnable should only
   // be run on the main thread.
   MOZ_ASSERT(NS_IsMainThread());
 
   UniquePLArenaPool arena;
   char** caNameStrings;
   UniqueCERTCertificate cert;
   UniqueSECKEYPrivateKey privKey;
-  UniqueCERTCertList certList;
-  CERTCertListNode* node;
-  UniqueCERTCertNicknames nicknames;
-  int keyError = 0; // used for private key retrieval error
-  int32_t NumberOfCerts = 0;
   void* wincx = mSocketInfo;
   nsresult rv;
 
   nsCOMPtr<nsIX509Cert> socketClientCert;
   mSocketInfo->GetClientCert(getter_AddRefs(socketClientCert));
 
   // If a client cert preference was set on the socket info, use that and skip
   // the client cert UI and/or search of the user's past cert decisions.
@@ -2147,72 +2143,70 @@ ClientAuthDataRunnable::RunOnTargetThrea
     goto loser;
   }
 
   // find valid user cert and key pair
   if (nsGetUserCertChoice() == UserCertChoice::Auto) {
     // automatically find the right cert
 
     // find all user certs that are valid and for SSL
-    certList.reset(CERT_FindUserCertsByUsage(CERT_GetDefaultCertDB(),
-                                             certUsageSSLClient, false, true,
-                                             wincx));
+    UniqueCERTCertList certList(
+      CERT_FindUserCertsByUsage(CERT_GetDefaultCertDB(), certUsageSSLClient,
+                                false, true, wincx));
     if (!certList) {
-      goto noCert;
+      goto loser;
     }
 
     // filter the list to those issued by CAs supported by the server
     mRV = CERT_FilterCertListByCANames(certList.get(), mCANames->nnames,
                                        caNameStrings, certUsageSSLClient);
     if (mRV != SECSuccess) {
-      goto noCert;
+      goto loser;
     }
 
     // make sure the list is not empty
-    node = CERT_LIST_HEAD(certList);
-    if (CERT_LIST_END(node, certList)) {
-      goto noCert;
+    if (CERT_LIST_END(CERT_LIST_HEAD(certList), certList)) {
+      goto loser;
     }
 
     UniqueCERTCertificate lowPrioNonrepCert;
 
     // loop through the list until we find a cert with a key
-    while (!CERT_LIST_END(node, certList)) {
+    for (CERTCertListNode* node = CERT_LIST_HEAD(certList);
+         !CERT_LIST_END(node, certList);
+         node = CERT_LIST_NEXT(node)) {
       // if the certificate has restriction and we do not satisfy it we do not
       // use it
       privKey.reset(PK11_FindKeyByAnyCert(node->cert, wincx));
       if (privKey) {
         if (hasExplicitKeyUsageNonRepudiation(node->cert)) {
           privKey = nullptr;
           // Not a preferred cert
           if (!lowPrioNonrepCert) { // did not yet find a low prio cert
             lowPrioNonrepCert.reset(CERT_DupCertificate(node->cert));
           }
         } else {
           // this is a good cert to present
           cert.reset(CERT_DupCertificate(node->cert));
           break;
         }
       }
-      keyError = PR_GetError();
-      if (keyError == SEC_ERROR_BAD_PASSWORD) {
+      if (PR_GetError() == SEC_ERROR_BAD_PASSWORD) {
         // problem with password: bail
         goto loser;
       }
-
-      node = CERT_LIST_NEXT(node);
     }
 
     if (!cert && lowPrioNonrepCert) {
       cert = Move(lowPrioNonrepCert);
       privKey.reset(PK11_FindKeyByAnyCert(cert.get(), wincx));
     }
 
     if (!cert) {
-      goto noCert;
+      goto loser;
     }
   } else { // Not Auto => ask
     // Get the SSL Certificate
 
     nsXPIDLCString hostname;
     mSocketInfo->GetHostName(getter_Copies(hostname));
 
     RefPtr<nsClientAuthRememberService> cars =
@@ -2224,102 +2218,73 @@ ClientAuthDataRunnable::RunOnTargetThrea
       bool found;
       rv = cars->HasRememberedDecision(hostname, mServerCert,
         rememberedDBKey, &found);
       if (NS_SUCCEEDED(rv) && found) {
         hasRemembered = true;
       }
     }
 
-    bool canceled = false;
+    if (hasRemembered && !rememberedDBKey.IsEmpty()) {
+      nsCOMPtr<nsIX509CertDB> certdb = do_GetService(NS_X509CERTDB_CONTRACTID);
+      if (certdb) {
+        nsCOMPtr<nsIX509Cert> foundCert;
+        rv = certdb->FindCertByDBKey(rememberedDBKey.get(),
+                                     getter_AddRefs(foundCert));
+        if (NS_SUCCEEDED(rv) && foundCert) {
+          nsNSSCertificate* objCert =
+            BitwiseCast<nsNSSCertificate*, nsIX509Cert*>(foundCert.get());
+          if (objCert) {
+            cert.reset(objCert->GetCert());
+          }
+        }
 
-    if (hasRemembered) {
-      if (rememberedDBKey.IsEmpty()) {
-        canceled = true;
-      } else {
-        nsCOMPtr<nsIX509CertDB> certdb;
-        certdb = do_GetService(NS_X509CERTDB_CONTRACTID);
-        if (certdb) {
-          nsCOMPtr<nsIX509Cert> found_cert;
-          nsresult find_rv =
-            certdb->FindCertByDBKey(rememberedDBKey.get(),
-            getter_AddRefs(found_cert));
-          if (NS_SUCCEEDED(find_rv) && found_cert) {
-            nsNSSCertificate* obj_cert =
-              BitwiseCast<nsNSSCertificate*, nsIX509Cert*>(found_cert.get());
-            if (obj_cert) {
-              cert.reset(obj_cert->GetCert());
-            }
-          }
-
-          if (!cert) {
-            hasRemembered = false;
-          }
+        if (!cert) {
+          hasRemembered = false;
         }
       }
     }
 
     if (!hasRemembered) {
       // user selects a cert to present
       nsCOMPtr<nsIClientAuthDialogs> dialogs;
-      int32_t selectedIndex = -1;
-      char16_t** certNicknameList = nullptr;
-      char16_t** certDetailsList = nullptr;
 
       // find all user certs that are for SSL
       // note that we are allowing expired certs in this list
-      certList.reset(CERT_FindUserCertsByUsage(CERT_GetDefaultCertDB(),
-                                               certUsageSSLClient, false,
-                                               false, wincx));
+      UniqueCERTCertList certList(
+        CERT_FindUserCertsByUsage(CERT_GetDefaultCertDB(), certUsageSSLClient,
+                                  false, false, wincx));
       if (!certList) {
-        goto noCert;
+        goto loser;
       }
 
       if (mCANames->nnames != 0) {
         // filter the list to those issued by CAs supported by the server
         mRV = CERT_FilterCertListByCANames(certList.get(),
                                            mCANames->nnames,
                                            caNameStrings,
                                            certUsageSSLClient);
         if (mRV != SECSuccess) {
           goto loser;
         }
       }
 
       if (CERT_LIST_END(CERT_LIST_HEAD(certList), certList)) {
         // list is empty - no matching certs
-        goto noCert;
-      }
-
-      // filter it further for hostname restriction
-      node = CERT_LIST_HEAD(certList.get());
-      while (!CERT_LIST_END(node, certList.get())) {
-        ++NumberOfCerts;
-        node = CERT_LIST_NEXT(node);
-      }
-      if (CERT_LIST_END(CERT_LIST_HEAD(certList.get()), certList.get())) {
-        goto noCert;
-      }
-
-      nicknames.reset(getNSSCertNicknamesFromCertList(certList));
-
-      if (!nicknames) {
         goto loser;
       }
 
-      NS_ASSERTION(nicknames->numnicknames == NumberOfCerts, "nicknames->numnicknames != NumberOfCerts");
-
       // Get CN and O of the subject and O of the issuer
       UniquePORTString ccn(CERT_GetCommonName(&mServerCert->subject));
       NS_ConvertUTF8toUTF16 cn(ccn.get());
 
       int32_t port;
       mSocketInfo->GetPort(&port);
 
-      nsString cn_host_port;
+      nsAutoString cn_host_port;
       if (ccn && strcmp(ccn.get(), hostname) == 0) {
         cn_host_port.Append(cn);
         cn_host_port.Append(':');
         cn_host_port.AppendInt(port);
       } else {
         cn_host_port.Append(cn);
         cn_host_port.AppendLiteral(" (");
         cn_host_port.Append(':');
@@ -2328,120 +2293,83 @@ ClientAuthDataRunnable::RunOnTargetThrea
       }
 
       UniquePORTString corg(CERT_GetOrgName(&mServerCert->subject));
       NS_ConvertUTF8toUTF16 org(corg.get());
 
       UniquePORTString cissuer(CERT_GetOrgName(&mServerCert->issuer));
       NS_ConvertUTF8toUTF16 issuer(cissuer.get());
 
-      certNicknameList =
-        (char16_t**)moz_xmalloc(sizeof(char16_t*)* nicknames->numnicknames);
-      if (!certNicknameList)
-        goto loser;
-      certDetailsList =
-        (char16_t**)moz_xmalloc(sizeof(char16_t*)* nicknames->numnicknames);
-      if (!certDetailsList) {
-        free(certNicknameList);
+      nsCOMPtr<nsIMutableArray> certArray = nsArrayBase::Create();
+      if (!certArray) {
         goto loser;
       }
 
-      int32_t CertsToUse;
-      for (CertsToUse = 0, node = CERT_LIST_HEAD(certList);
-        !CERT_LIST_END(node, certList) && CertsToUse < nicknames->numnicknames;
-        node = CERT_LIST_NEXT(node)
-        ) {
-        RefPtr<nsNSSCertificate> tempCert(nsNSSCertificate::Create(node->cert));
-
-        if (!tempCert)
-          continue;
-
-        NS_ConvertUTF8toUTF16 i_nickname(nicknames->nicknames[CertsToUse]);
-        nsAutoString nickWithSerial, details;
-
-        if (NS_FAILED(tempCert->FormatUIStrings(i_nickname, nickWithSerial, details)))
-          continue;
-
-        certNicknameList[CertsToUse] = ToNewUnicode(nickWithSerial);
-        if (!certNicknameList[CertsToUse])
-          continue;
-        certDetailsList[CertsToUse] = ToNewUnicode(details);
-        if (!certDetailsList[CertsToUse]) {
-          free(certNicknameList[CertsToUse]);
-          continue;
+      for (CERTCertListNode* node = CERT_LIST_HEAD(certList);
+           !CERT_LIST_END(node, certList);
+           node = CERT_LIST_NEXT(node)) {
+        nsCOMPtr<nsIX509Cert> tempCert = nsNSSCertificate::Create(node->cert);
+        if (!tempCert) {
+          goto loser;
         }
 
-        ++CertsToUse;
+        rv = certArray->AppendElement(tempCert, false);
+        if (NS_FAILED(rv)) {
+          goto loser;
+        }
       }
 
       // Throw up the client auth dialog and get back the index of the selected cert
-      nsresult rv = getNSSDialogs(getter_AddRefs(dialogs),
-                                  NS_GET_IID(nsIClientAuthDialogs),
-                                  NS_CLIENTAUTHDIALOGS_CONTRACTID);
+      rv = getNSSDialogs(getter_AddRefs(dialogs),
+                         NS_GET_IID(nsIClientAuthDialogs),
+                         NS_CLIENTAUTHDIALOGS_CONTRACTID);
 
       if (NS_FAILED(rv)) {
-        NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(CertsToUse, certNicknameList);
-        NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(CertsToUse, certDetailsList);
         goto loser;
       }
 
-      rv = dialogs->ChooseCertificate(mSocketInfo, cn_host_port.get(),
-                                      org.get(), issuer.get(),
-                                      (const char16_t**)certNicknameList,
-                                      (const char16_t**)certDetailsList,
-                                      CertsToUse, &selectedIndex, &canceled);
-
-      NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(CertsToUse, certNicknameList);
-      NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(CertsToUse, certDetailsList);
-
-      if (NS_FAILED(rv)) goto loser;
+      uint32_t selectedIndex = 0;
+      bool certChosen = false;
+      rv = dialogs->ChooseCertificate(mSocketInfo, cn_host_port, org, issuer,
+                                      certArray, &selectedIndex, &certChosen);
+      if (NS_FAILED(rv)) {
+        goto loser;
+      }
 
       // even if the user has canceled, we want to remember that, to avoid repeating prompts
       bool wantRemember = false;
       mSocketInfo->GetRememberClientAuthCertificate(&wantRemember);
 
-      int i;
-      if (!canceled)
-      for (i = 0, node = CERT_LIST_HEAD(certList);
-        !CERT_LIST_END(node, certList);
-        ++i, node = CERT_LIST_NEXT(node)) {
-
-        if (i == selectedIndex) {
-          cert.reset(CERT_DupCertificate(node->cert));
-          break;
+      if (certChosen) {
+        nsCOMPtr<nsIX509Cert> selectedCert = do_QueryElementAt(certArray,
+                                                               selectedIndex);
+        if (!selectedCert) {
+          goto loser;
         }
+        cert.reset(selectedCert->GetCert());
       }
 
       if (cars && wantRemember) {
         cars->RememberDecision(hostname, mServerCert,
-          canceled ? nullptr : cert.get());
+                               certChosen ? cert.get() : nullptr);
       }
     }
 
-    if (canceled) { rv = NS_ERROR_NOT_AVAILABLE; goto loser; }
-
     if (!cert) {
       goto loser;
     }
 
     // go get the private key
     privKey.reset(PK11_FindKeyByAnyCert(cert.get(), wincx));
     if (!privKey) {
-      keyError = PR_GetError();
-      if (keyError == SEC_ERROR_BAD_PASSWORD) {
-        // problem with password: bail
-        goto loser;
-      } else {
-        goto noCert;
-      }
+      goto loser;
     }
   }
   goto done;
 
-noCert:
 loser:
   if (mRV == SECSuccess) {
     mRV = SECFailure;
   }
 done:
   int error = PR_GetError();
 
   *mPRetCert = cert.release();
--- a/testing/marionette/driver.js
+++ b/testing/marionette/driver.js
@@ -1812,17 +1812,18 @@ GeckoDriver.prototype.getElementProperty
   switch (this.context) {
     case Context.CHROME:
       let win = this.getCurrentWindow();
       let el = this.curBrowser.seenEls.get(id, {frame: win});
       resp.body.value = el[name];
       break;
 
     case Context.CONTENT:
-      return this.listener.getElementProperty(id, name);
+      resp.body.value = yield this.listener.getElementProperty(id, name);
+      break;
   }
 };
 
 /**
  * Get the text of an element, if any.  Includes the text of all child
  * elements.
  *
  * @param {string} id
--- a/testing/marionette/harness/marionette/tests/unit/test_element_state.py
+++ b/testing/marionette/harness/marionette/tests/unit/test_element_state.py
@@ -132,17 +132,17 @@ class TestGetElementAttribute(Marionette
 class TestGetElementProperty(MarionetteTestCase):
     def test_get(self):
         self.marionette.navigate(disabled)
         el = self.marionette.find_element(By.TAG_NAME, "input")
         prop = el.get_property("disabled")
         self.assertIsInstance(prop, bool)
         self.assertTrue(prop)
 
-    def test_missing_property_returns_false(self):
+    def test_missing_property_returns_default(self):
         self.marionette.navigate(input)
         el = self.marionette.find_element(By.TAG_NAME, "input")
         prop = el.get_property("checked")
         self.assertIsInstance(prop, bool)
         self.assertFalse(prop)
 
     def test_attribute_not_returned(self):
         self.marionette.navigate(attribute)
--- a/testing/marionette/listener.js
+++ b/testing/marionette/listener.js
@@ -1096,17 +1096,17 @@ function getElementAttribute(id, name) {
     }
   } else {
     return el.getAttribute(name);
   }
 }
 
 function getElementProperty(id, name) {
   let el = seenEls.get(id, curContainer);
-  return el[name];
+  return typeof el[name] != "undefined" ? el[name] : null;
 }
 
 /**
  * Get the text of this element. This includes text from child elements.
  *
  * @param {WebElement} id
  *     Reference to web element.
  *
--- a/testing/talos/talos/output.py
+++ b/testing/talos/talos/output.py
@@ -6,17 +6,17 @@
 """output formats for Talos"""
 
 import filter
 import json
 import utils
 
 from mozlog import get_proxy_logger
 
-# NOTE: we have a circular dependecy with output.py when we import results
+# NOTE: we have a circular dependency with output.py when we import results
 import results as TalosResults
 
 LOG = get_proxy_logger()
 
 
 def filesizeformat(bytes):
     """
     Format the value like a 'human-readable' file size (i.e. 13 KB, 4.1 MB, 102
@@ -40,125 +40,16 @@ class Output(object):
 
     def __init__(self, results):
         """
         - results : TalosResults instance
         """
         self.results = results
 
     def __call__(self):
-        """return list of results strings"""
-        raise NotImplementedError("Abstract base class")
-
-    def output(self, results, results_url, tbpl_output):
-        """output to the results_url
-        - results_url : http:// or file:// URL
-        - results : list of results
-        """
-
-        # parse the results url
-        results_url_split = utils.urlsplit(results_url)
-        results_scheme, results_server, results_path, _, _ = results_url_split
-
-        if results_scheme in ('http', 'https'):
-            self.post(results, results_server, results_path, results_scheme,
-                      tbpl_output)
-        elif results_scheme == 'file':
-            with open(results_path, 'w') as f:
-                for result in results:
-                    f.write("%s\n" % result)
-        else:
-            raise NotImplementedError(
-                "%s: %s - only http://, https://, and file:// supported"
-                % (self.__class__.__name__, results_url)
-            )
-
-    def post(self, results, server, path, scheme, tbpl_output):
-        raise NotImplementedError("Abstract base class")
-
-    @classmethod
-    def shortName(cls, name):
-        """short name for counters"""
-        names = {"Working Set": "memset",
-                 "% Processor Time": "%cpu",
-                 "Private Bytes": "pbytes",
-                 "RSS": "rss",
-                 "XRes": "xres",
-                 "Modified Page List Bytes": "modlistbytes",
-                 "Main_RSS": "main_rss"}
-        return names.get(name, name)
-
-    @classmethod
-    def isMemoryMetric(cls, resultName):
-        """returns if the result is a memory metric"""
-        memory_metric = ['memset', 'rss', 'pbytes', 'xres', 'modlistbytes',
-                         'main_rss', 'content_rss']  # measured in bytes
-        return bool([i for i in memory_metric if i in resultName])
-
-    @classmethod
-    def v8_Metric(cls, val_list):
-        results = [i for i, j in val_list]
-        score = 100 * filter.geometric_mean(results)
-        return score
-
-    @classmethod
-    def JS_Metric(cls, val_list):
-        """v8 benchmark score"""
-        results = [i for i, j in val_list]
-        LOG.info("javascript benchmark")
-        return sum(results)
-
-    @classmethod
-    def CanvasMark_Metric(cls, val_list):
-        """CanvasMark benchmark score (NOTE: this is identical to JS_Metric)"""
-        results = [i for i, j in val_list]
-        LOG.info("CanvasMark benchmark")
-        return sum(results)
-
-
-class PerfherderOutput(Output):
-    def __init__(self, results):
-        Output.__init__(self, results)
-
-    def output(self, results, results_url, tbpl_output):
-        """output to the a file if results_url starts with file://
-        - results : json instance
-        - results_url : file:// URL
-        """
-
-        # parse the results url
-        results_url_split = utils.urlsplit(results_url)
-        results_scheme, results_server, results_path, _, _ = results_url_split
-
-        # This is the output that treeherder expects to find when parsing the
-        # log file
-        LOG.info("PERFHERDER_DATA: %s" % json.dumps(results))
-        if results_scheme in ('file'):
-            json.dump(results, file(results_path, 'w'), indent=2,
-                      sort_keys=True)
-
-    def post(self, results, server, path, scheme, tbpl_output):
-        """conform to current code- not needed for perfherder"""
-        pass
-
-    def construct_results(self, vals, testname):
-        if 'responsiveness' in testname:
-            return filter.responsiveness_Metric([val for (val, page) in vals])
-        elif testname.startswith('v8_7'):
-            return self.v8_Metric(vals)
-        elif testname.startswith('kraken'):
-            return self.JS_Metric(vals)
-        elif testname.startswith('tcanvasmark'):
-            return self.CanvasMark_Metric(vals)
-        elif len(vals) > 1:
-            return filter.geometric_mean([i for i, j in vals])
-        else:
-            return filter.mean([i for i, j in vals])
-
-    def __call__(self):
         suites = []
         test_results = {
             'framework': {
                 'name': self.results.results[0].framework,
             },
             'suites': suites,
         }
 
@@ -273,10 +164,93 @@ class PerfherderOutput(Output):
                         if len(vals) > 0:
                             varray = [float(v) for v in vals]
                             subtest['value'] = filter.mean(varray)
             if counter_subtests:
                 suites.append({'name': test.name(),
                                'subtests': counter_subtests})
         return test_results
 
-# available output formats
-formats = {'output_urls': PerfherderOutput}
+    def output(self, results, results_url, tbpl_output):
+        """output to the a file if results_url starts with file://
+        - results : json instance
+        - results_url : file:// URL
+        """
+
+        # parse the results url
+        results_url_split = utils.urlsplit(results_url)
+        results_scheme, results_server, results_path, _, _ = results_url_split
+
+        if results_scheme in ('http', 'https'):
+            self.post(results, results_server, results_path, results_scheme,
+                      tbpl_output)
+        elif results_scheme == 'file':
+            with open(results_path, 'w') as f:
+                for result in results:
+                    f.write("%s\n" % result)
+        else:
+            raise NotImplementedError(
+                "%s: %s - only http://, https://, and file:// supported"
+                % (self.__class__.__name__, results_url)
+            )
+
+        # This is the output that treeherder expects to find when parsing the
+        # log file
+        LOG.info("PERFHERDER_DATA: %s" % json.dumps(results))
+        if results_scheme in ('file'):
+            json.dump(results, file(results_path, 'w'), indent=2,
+                      sort_keys=True)
+
+    def post(self, results, server, path, scheme, tbpl_output):
+        raise NotImplementedError("Abstract base class")
+
+    @classmethod
+    def shortName(cls, name):
+        """short name for counters"""
+        names = {"Working Set": "memset",
+                 "% Processor Time": "%cpu",
+                 "Private Bytes": "pbytes",
+                 "RSS": "rss",
+                 "XRes": "xres",
+                 "Modified Page List Bytes": "modlistbytes",
+                 "Main_RSS": "main_rss"}
+        return names.get(name, name)
+
+    @classmethod
+    def isMemoryMetric(cls, resultName):
+        """returns if the result is a memory metric"""
+        memory_metric = ['memset', 'rss', 'pbytes', 'xres', 'modlistbytes',
+                         'main_rss', 'content_rss']  # measured in bytes
+        return bool([i for i in memory_metric if i in resultName])
+
+    @classmethod
+    def v8_Metric(cls, val_list):
+        results = [i for i, j in val_list]
+        score = 100 * filter.geometric_mean(results)
+        return score
+
+    @classmethod
+    def JS_Metric(cls, val_list):
+        """v8 benchmark score"""
+        results = [i for i, j in val_list]
+        LOG.info("javascript benchmark")
+        return sum(results)
+
+    @classmethod
+    def CanvasMark_Metric(cls, val_list):
+        """CanvasMark benchmark score (NOTE: this is identical to JS_Metric)"""
+        results = [i for i, j in val_list]
+        LOG.info("CanvasMark benchmark")
+        return sum(results)
+
+    def construct_results(self, vals, testname):
+        if 'responsiveness' in testname:
+            return filter.responsiveness_Metric([val for (val, page) in vals])
+        elif testname.startswith('v8_7'):
+            return self.v8_Metric(vals)
+        elif testname.startswith('kraken'):
+            return self.JS_Metric(vals)
+        elif testname.startswith('tcanvasmark'):
+            return self.CanvasMark_Metric(vals)
+        elif len(vals) > 1:
+            return filter.geometric_mean([i for i, j in vals])
+        else:
+            return filter.mean([i for i, j in vals])
--- a/testing/talos/talos/results.py
+++ b/testing/talos/talos/results.py
@@ -24,50 +24,27 @@ class TalosResults(object):
         self.extra_options = []
 
     def add(self, test_results):
         self.results.append(test_results)
 
     def add_extra_option(self, extra_option):
         self.extra_options.append(extra_option)
 
-    def check_output_formats(self, output_formats):
-        """check output formats"""
-
-        # ensure formats are available
-        formats = output_formats.keys()
-        missing = self.check_formats_exist(formats)
-        if missing:
-            raise utils.TalosError("Output format(s) unknown: %s"
-                                   % ','.join(missing))
-
-        # perform per-format check
-        for format, urls in output_formats.items():
-            cls = output.formats[format]
-            cls.check(urls)
-
-    @classmethod
-    def check_formats_exist(cls, formats):
-        """
-        ensure that all formats are registered
-        return missing formats
-        """
-        return [i for i in formats if i not in output.formats]
-
     def output(self, output_formats):
         """
         output all results to appropriate URLs
         - output_formats: a dict mapping formats to a list of URLs
         """
 
         tbpl_output = {}
         try:
 
             for key, urls in output_formats.items():
-                _output = output.formats[key](self)
+                _output = output.Output(self)
                 results = _output()
                 for url in urls:
                     _output.output(results, url, tbpl_output)
 
         except utils.TalosError, e:
             # print to results.out
             try:
                 _output = output.GraphserverOutput(self)
--- a/testing/talos/talos/run_tests.py
+++ b/testing/talos/talos/run_tests.py
@@ -173,17 +173,16 @@ def run_tests(config, browser_config):
         results_urls = dict(
             # another hack; datazilla stands for Perfherder
             # and do not require url, but a non empty dict is required...
             output_urls=['local.json'],
         )
     else:
         # local mode, output to files
         results_urls = dict(output_urls=[os.path.abspath('local.json')])
-    talos_results.check_output_formats(results_urls)
 
     httpd = setup_webserver(browser_config['webserver'])
     httpd.start()
 
     # if e10s add as extra results option
     if config['e10s']:
         talos_results.add_extra_option('e10s')
 
--- a/testing/web-platform/tests/referrer-policy/generic/subresource-test/attr-referrer-invalid-value.html
+++ b/testing/web-platform/tests/referrer-policy/generic/subresource-test/attr-referrer-invalid-value.html
@@ -6,17 +6,17 @@
     <script src="/resources/testharnessreport.js"></script>
   </head>
   <body>
     <h1>Invalid referrerPolicy attribute value</h1>
     <pre>Running...</pre>
 
     <script>
       test(function () {
-          var elements = ["iframe", "img", "a", "area"];
+          var elements = ["iframe", "img", "a", "area", "link"];
           for (var i = 0; i < elements.length; i++) {
               var elem = document.createElement(elements[i]);
               elem.referrerPolicy = "unsafe-url";
               assert_equals(elem.referrerPolicy, "unsafe-url");
               elem.referrerPolicy = "not-valid-value";
               assert_equals(elem.referrerPolicy, "");
           }
       }, "Invalid referrerpolicy values not reflected");
--- a/toolkit/components/prompts/test/prompt_common.js
+++ b/toolkit/components/prompts/test/prompt_common.js
@@ -68,19 +68,23 @@ function checkPromptState(promptState, e
     }
 
     // For prompts with a time-delay button.
     if (expectedState.butt0Disabled) {
         is(promptState.butt0Disabled, true,  "Checking accept-button is disabled");
         is(promptState.butt1Disabled, false, "Checking cancel-button isn't disabled");
     }
 
-    is(promptState.defButton0, expectedState.defButton == "button0", "checking button0 default");
-    is(promptState.defButton1, expectedState.defButton == "button1", "checking button1 default");
-    is(promptState.defButton2, expectedState.defButton == "button2", "checking button2 default");
+    if (isLinux && !promptState.focused) {
+      todo(false, "Checking button default fails if focus is not correct."); // bug 1278418
+    } else {
+      is(promptState.defButton0, expectedState.defButton == "button0", "checking button0 default");
+      is(promptState.defButton1, expectedState.defButton == "button1", "checking button1 default");
+      is(promptState.defButton2, expectedState.defButton == "button2", "checking button2 default");
+    }
 
     if (isLinux && (!promptState.focused || isE10S)) {
         todo(false, "Focus seems missing or wrong on Linux"); // bug 1265077
     } else if (isOSX && expectedState.focused && expectedState.focused.startsWith("button")) {
         is(promptState.focused, "infoBody", "buttons don't focus on OS X, but infoBody does instead");
     } else {
         is(promptState.focused, expectedState.focused, "Checking focused element");
     }
--- a/uriloader/prefetch/nsPrefetchService.cpp
+++ b/uriloader/prefetch/nsPrefetchService.cpp
@@ -104,19 +104,27 @@ nsPrefetchNode::OpenChannel()
     if (!source) {
         // Don't attempt to prefetch if we don't have a source node
         // (which should never happen).
 
         return NS_ERROR_FAILURE;
     }
     nsCOMPtr<nsILoadGroup> loadGroup = source->OwnerDoc()->GetDocumentLoadGroup();
     CORSMode corsMode = CORS_NONE;
+    net::ReferrerPolicy referrerPolicy = net::RP_Unset;
     if (source->IsHTMLElement(nsGkAtoms::link)) {
-      corsMode = static_cast<dom::HTMLLinkElement*>(source.get())->GetCORSMode();
+      dom::HTMLLinkElement* link = static_cast<dom::HTMLLinkElement*>(source.get());
+      corsMode = link->GetCORSMode();
+      referrerPolicy = link->GetLinkReferrerPolicy();
     }
+
+    if (referrerPolicy == net::RP_Unset) {
+      referrerPolicy = source->OwnerDoc()->GetReferrerPolicy();
+    }
+
     uint32_t securityFlags;
     if (corsMode == CORS_NONE) {
       securityFlags = nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS;
     } else {
       securityFlags = nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS;
       if (corsMode == CORS_USE_CREDENTIALS) {
         securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE;
       }
@@ -134,17 +142,17 @@ nsPrefetchNode::OpenChannel()
                                         nsICachingChannel::LOAD_ONLY_IF_MODIFIED);
 
     NS_ENSURE_SUCCESS(rv, rv);
 
     // configure HTTP specific stuff
     nsCOMPtr<nsIHttpChannel> httpChannel =
         do_QueryInterface(mChannel);
     if (httpChannel) {
-        httpChannel->SetReferrer(mReferrerURI);
+        httpChannel->SetReferrerWithPolicy(mReferrerURI, referrerPolicy);
         httpChannel->SetRequestHeader(
             NS_LITERAL_CSTRING("X-Moz"),
             NS_LITERAL_CSTRING("prefetch"),
             false);
     }
 
     return mChannel->AsyncOpen2(this);
 }