Merge inbound to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Sat, 11 Oct 2014 16:16:00 -0400
changeset 209977 44168a7af20de3db73f0a7ed63abd923ad3a3732
parent 209947 2783f7f7d67a81370fa55299e2aa89c7424a50ba (current diff)
parent 209976 16a8a5c8b96a2fe14420f3a689b099e4410ddf32 (diff)
child 209978 0990e633fd0dcdefdc171f0d0cf49c0db18e84e0
child 209984 bdba65a0ead7241c34a19fd6fb44d518755cb8f9
child 210008 fcb718391722309929f3639c2eead11984c44704
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersmerge
milestone35.0a1
Merge inbound to m-c. a=merge
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -215,20 +215,18 @@ pref("app.update.showInstalledUI", false
 //     to newer versions of installed addons that resolve them.
 // 1 = suppress prompting for incompatibilities only if there are VersionInfo
 //     updates available to installed addons that resolve them, not newer
 //     versions.
 pref("app.update.incompatible.mode", 0);
 
 // Whether or not to attempt using the service for updates.
 #ifdef MOZ_MAINTENANCE_SERVICE
-#ifndef HAVE_64BIT_BUILD
 pref("app.update.service.enabled", true);
 #endif
-#endif
 
 // Symmetric (can be overridden by individual extensions) update preferences.
 // e.g.
 //  extensions.{GUID}.update.enabled
 //  extensions.{GUID}.update.url
 //  .. etc ..
 //
 pref("extensions.update.enabled", true);
--- a/browser/components/translation/translation-infobar.xml
+++ b/browser/components/translation/translation-infobar.xml
@@ -224,17 +224,37 @@
               this.removeEventListener("transitionend", onShown);
 
               // These strings are hardcoded because they need to reach beta
               // without riding the trains.
               let localizedStrings = {
                 en: ["Hey look! It's something new!",
                      "Now the Web is even more accessible with our new in-page translation feature. Click the translate button to try it!",
                      "Learn more.",
-                     "Thanks"]
+                     "Thanks"],
+                "es-AR": ["\xA1Mir\xE1! \xA1Hay algo nuevo!",
+                          "Ahora la web es a\xFAn m\xE1s accesible con nuestra nueva funcionalidad de traducci\xF3n integrada. \xA1Hac\xE9 clic en el bot\xF3n traducir para probarla!",
+                          "Conoc\xE9 m\xE1s.",
+                          "Gracias"],
+                "es-ES": ["\xA1Mira! \xA1Hay algo nuevo!",
+                          "Con la nueva funcionalidad de traducci\xF3n integrada, ahora la Web es a\xFAn m\xE1s accesible. \xA1Pulsa el bot\xF3n Traducir y pru\xE9bala!",
+                          "M\xE1s informaci\xF3n.",
+                          "Gracias"],
+                pl: ["Sp\xF3jrz tutaj! To co\u015B nowego!",
+                     "Sie\u0107 sta\u0142a si\u0119 w\u0142a\u015Bnie jeszcze bardziej dost\u0119pna dzi\u0119ki opcji bezpo\u015Bredniego t\u0142umaczenia stron. Kliknij przycisk t\u0142umaczenia, aby spr\xF3bowa\u0107!",
+                     "Dowiedz si\u0119 wi\u0119cej",
+                     "Dzi\u0119kuj\u0119"],
+                tr: ["Bak\u0131n, burada yeni bir \u015Fey var!",
+                     "Yeni sayfa i\xE7i \xE7eviri \xF6zelli\u011Fimiz sayesinde Web art\u0131k \xE7ok daha anla\u015F\u0131l\u0131r olacak. Denemek i\xE7in \xC7evir d\xFC\u011Fmesine t\u0131klay\u0131n!",
+                     "Daha fazla bilgi al\u0131n.",
+                     "Te\u015Fekk\xFCrler"],
+                vi: ["Nh\xECn n\xE0y! \u0110\u1ED3 m\u1EDBi!",
+                     "Gi\u1EDD \u0111\xE2y ch\xFAng ta c\xF3 th\u1EC3 ti\u1EBFp c\u1EADn web d\u1EC5 d\xE0ng h\u01A1n n\u1EEFa v\u1EDBi t\xEDnh n\u0103ng d\u1ECBch ngay trong trang.  Hay nh\u1EA5n n\xFAt d\u1ECBch \u0111\u1EC3 th\u1EED!",
+                     "T\xECm hi\u1EC3u th\xEAm.",
+                     "C\u1EA3m \u01A1n"]
               };
 
               let locale = Cc["@mozilla.org/chrome/chrome-registry;1"]
                              .getService(Ci.nsIXULChromeRegistry)
                              .getSelectedLocale("browser");
               if (!(locale in localizedStrings))
                 locale = "en";
               let strings = localizedStrings[locale];
--- a/browser/confvars.sh
+++ b/browser/confvars.sh
@@ -5,18 +5,18 @@
 
 MOZ_APP_BASENAME=Firefox
 MOZ_APP_VENDOR=Mozilla
 MOZ_UPDATER=1
 MOZ_PHOENIX=1
 
 if test "$OS_ARCH" = "WINNT"; then
   MOZ_MAINTENANCE_SERVICE=1
+  MOZ_VERIFY_MAR_SIGNATURE=1
   if ! test "$HAVE_64BIT_BUILD"; then
-    MOZ_VERIFY_MAR_SIGNATURE=1
     if test "$MOZ_UPDATE_CHANNEL" = "nightly" -o \
             "$MOZ_UPDATE_CHANNEL" = "aurora" -o \
             "$MOZ_UPDATE_CHANNEL" = "beta" -o \
             "$MOZ_UPDATE_CHANNEL" = "release"; then
       if ! test "$MOZ_DEBUG"; then
         MOZ_STUB_INSTALLER=1
       fi
     fi
--- a/configure.in
+++ b/configure.in
@@ -1512,17 +1512,16 @@ if test "$GNU_CXX"; then
     # https://gcc.gnu.org/onlinedocs/gcc-4.4.0/gcc/Warning-Options.html
     #
     # -Wall - turn on a lot of warnings
     # -Wempty-body - catches bugs, e.g. "if (c); foo();", few false positives
     # -Wendif-labels - catches `#else FOO` and `#endif FOO` not in comment
     # -Wint-to-pointer-cast - catches cast to pointer from integer of different size
     # -Wmissing-braces - catches aggregate initializers missing nested braces
     # -Woverloaded-virtual - function declaration hides virtual function from base class
-    # -Wparentheses - catches `if (a=b)` and operator precedence bugs
     # -Wpointer-arith - catches pointer arithmetic using NULL or sizeof(void)
     # -Wreturn-type - catches missing returns, zero false positives
     # -Wsequence-point - catches undefined order behavior like `a = a++`
     # -Wsign-compare - catches comparison of signed and unsigned types
     # -Wtrigraphs - catches unlikely use of trigraphs
     # -Wtype-limits - catches overflow bugs, few false positives
     # -Wunused-label - catches unused goto labels
     # -Wwrite-strings - catches non-const char* pointers to string literals
@@ -1532,17 +1531,16 @@ if test "$GNU_CXX"; then
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Woverloaded-virtual"
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wsign-compare"
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wwrite-strings"
 
     # Treat some warnings as errors:
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=endif-labels"
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=int-to-pointer-cast"
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=missing-braces"
-    _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=parentheses"
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=pointer-arith"
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=return-type"
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=sequence-point"
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=unused-label"
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=trigraphs"
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=type-limits"
 
     # Turn off the following warnings that -Wall turns on:
--- a/content/media/gtest/TestWebMBuffered.cpp
+++ b/content/media/gtest/TestWebMBuffered.cpp
@@ -1,181 +1,20 @@
 /* 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 "gtest/gtest.h"
-#include "mozilla/ArrayUtils.h"
+#include <stdio.h>
+#include "nsTArray.h"
 #include "WebMBufferedParser.h"
 
 using namespace mozilla;
 
-// This is a manually constructed WebM file containing several VP9 and Opus
-// frames.  It is stored parallel to this test as test.webm.
-// XXX: This can be substituted for simple file access once bug 1054809 is
-// addressed.
-static const unsigned char gWebMData[] = {
-  0x1a, 0x45, 0xdf, 0xa3, 0x9f, 0x42, 0x86, 0x81, 0x01, 0x42, 0xf7, 0x81, 0x01,
-  0x42, 0xf2, 0x81, 0x04, 0x42, 0xf3, 0x81, 0x08, 0x42, 0x82, 0x84, 0x77, 0x65,
-  0x62, 0x6d, 0x42, 0x87, 0x81, 0x04, 0x42, 0x85, 0x81, 0x02, 0x18, 0x53, 0x80,
-  0x67, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x29, 0x7b, 0x11, 0x4d, 0x9b, 0x74,
-  0x9e, 0x4d, 0xbb, 0x8c, 0x53, 0xab, 0x84, 0x15, 0x49, 0xa9, 0x66, 0x53, 0xac,
-  0x82, 0x10, 0x03, 0x4d, 0xbb, 0x8c, 0x53, 0xab, 0x84, 0x16, 0x54, 0xae, 0x6b,
-  0x53, 0xac, 0x82, 0x10, 0x91, 0x15, 0x49, 0xa9, 0x66, 0x40, 0x88, 0x2a, 0xd7,
-  0xb1, 0x83, 0x07, 0xa1, 0x20, 0x4d, 0x80, 0xa3, 0x6c, 0x69, 0x62, 0x65, 0x62,
-  0x6d, 0x6c, 0x20, 0x76, 0x31, 0x2e, 0x33, 0x2e, 0x30, 0x20, 0x2b, 0x20, 0x6c,
-  0x69, 0x62, 0x6d, 0x61, 0x74, 0x72, 0x6f, 0x73, 0x6b, 0x61, 0x20, 0x76, 0x31,
-  0x2e, 0x34, 0x2e, 0x31, 0x57, 0x41, 0xc6, 0x6d, 0x6b, 0x76, 0x6d, 0x65, 0x72,
-  0x67, 0x65, 0x20, 0x76, 0x37, 0x2e, 0x30, 0x2e, 0x30, 0x20, 0x28, 0x27, 0x57,
-  0x68, 0x65, 0x72, 0x65, 0x20, 0x57, 0x65, 0x20, 0x47, 0x6f, 0x69, 0x6e, 0x67,
-  0x27, 0x29, 0x20, 0x36, 0x34, 0x62, 0x69, 0x74, 0x20, 0x62, 0x75, 0x69, 0x6c,
-  0x74, 0x20, 0x6f, 0x6e, 0x20, 0x4a, 0x75, 0x6e, 0x20, 0x31, 0x38, 0x20, 0x32,
-  0x30, 0x31, 0x34, 0x20, 0x32, 0x32, 0x3a, 0x35, 0x39, 0x3a, 0x33, 0x33, 0x44,
-  0x89, 0x84, 0x46, 0x78, 0xf0, 0x00, 0x44, 0x61, 0x88, 0x06, 0x03, 0x72, 0xf2,
-  0x43, 0xa1, 0x5c, 0x00, 0x16, 0x54, 0xae, 0x6b, 0x40, 0x82, 0xae, 0xb0, 0xd7,
-  0x81, 0x01, 0x73, 0xc5, 0x84, 0x73, 0x13, 0x25, 0xf2, 0x83, 0x81, 0x01, 0x6d,
-  0xe7, 0x81, 0x01, 0x86, 0x85, 0x56, 0x5f, 0x56, 0x50, 0x39, 0xe0, 0x96, 0xb0,
-  0x82, 0x01, 0x68, 0xba, 0x82, 0x02, 0x80, 0x54, 0xb0, 0x84, 0x00, 0x00, 0x01,
-  0x68, 0x54, 0xba, 0x84, 0x00, 0x00, 0x02, 0x80, 0xae, 0xce, 0xd7, 0x81, 0x02,
-  0x73, 0xc5, 0x88, 0x1b, 0x43, 0x37, 0xbe, 0x2e, 0x33, 0x75, 0xb7, 0x83, 0x81,
-  0x02, 0x86, 0x86, 0x41, 0x5f, 0x4f, 0x50, 0x55, 0x53, 0x56, 0xbb, 0x84, 0x04,
-  0xc4, 0xb4, 0x00, 0x63, 0xa2, 0x93, 0x4f, 0x70, 0x75, 0x73, 0x48, 0x65, 0x61,
-  0x64, 0x01, 0x02, 0x38, 0x01, 0x80, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22,
-  0xb5, 0x9c, 0x83, 0x75, 0x6e, 0x64, 0x56, 0xaa, 0x83, 0x63, 0x2e, 0xa0, 0xe1,
-  0x89, 0xb5, 0x84, 0x47, 0x3b, 0x80, 0x00, 0x9f, 0x81, 0x02, 0x1f, 0x43, 0xb6,
-  0x75, 0x20, 0x7b, 0x16, 0xe7, 0x81, 0x00, 0xa3, 0x40, 0x7f, 0x81, 0x00, 0x84,
-  0x00, 0x86, 0x00, 0x40, 0x96, 0x18, 0x6c, 0x5a, 0xa0, 0x00, 0x07, 0x70, 0xc9,
-  0x9f, 0x90, 0x50, 0xc6, 0x00, 0x68, 0x2b, 0x93, 0x3a, 0x84, 0x29, 0x51, 0x23,
-  0x14, 0x96, 0xf8, 0xbc, 0xeb, 0x12, 0xde, 0x2c, 0x10, 0x52, 0x96, 0x3f, 0x7f,
-  0xad, 0xb7, 0xea, 0x69, 0x08, 0x20, 0x9b, 0x70, 0x2a, 0x94, 0x85, 0x21, 0x90,
-  0x66, 0x93, 0x7d, 0x4f, 0xff, 0x20, 0x02, 0xbd, 0x5f, 0xa2, 0xb1, 0xd3, 0x4b,
-  0xae, 0x35, 0xdc, 0x7a, 0x2a, 0xcb, 0x2e, 0xdf, 0x0c, 0x9b, 0x57, 0xff, 0x2e,
-  0xe7, 0x4c, 0x61, 0xd0, 0x8b, 0xf6, 0x89, 0x3d, 0x79, 0x60, 0x8a, 0x61, 0xb9,
-  0xbf, 0x2c, 0x6c, 0x6f, 0x56, 0xe2, 0xb7, 0xf7, 0xcd, 0xc7, 0xf6, 0xe1, 0xd7,
-  0xc8, 0x84, 0x17, 0x4c, 0x0b, 0xb2, 0x41, 0x1d, 0x22, 0xb1, 0x3e, 0x01, 0x32,
-  0xa5, 0xc8, 0xde, 0x75, 0xd5, 0x96, 0x7c, 0xa3, 0x41, 0x0c, 0x82, 0x01, 0x40,
-  0x86, 0x07, 0xa1, 0xbc, 0xc1, 0xbe, 0xc0, 0xbf, 0xc0, 0x68, 0x08, 0xae, 0xc4,
-  0x07, 0x86, 0x80, 0x78, 0xe6, 0x60, 0x0c, 0x96, 0xf1, 0x0e, 0xc0, 0xdb, 0xce,
-  0x8c, 0xea, 0xbe, 0xf1, 0x38, 0x10, 0x6e, 0x55, 0x76, 0x86, 0xe0, 0x0c, 0x8b,
-  0x6e, 0x62, 0x1e, 0x68, 0x08, 0xae, 0xfa, 0x05, 0x7e, 0x38, 0x25, 0x6e, 0x8a,
-  0xe0, 0x68, 0xe4, 0x34, 0x91, 0x47, 0x1a, 0xb2, 0x9e, 0x8f, 0xb1, 0xc6, 0x2e,
-  0x6f, 0xd6, 0xda, 0x3b, 0x7f, 0x4b, 0x98, 0x68, 0x08, 0xae, 0xe6, 0xeb, 0xbf,
-  0xde, 0x84, 0x2e, 0xd9, 0xe1, 0x38, 0x90, 0xaa, 0x86, 0x3b, 0xd1, 0xa7, 0xd3,
-  0x8f, 0x77, 0x02, 0xe0, 0xd5, 0xf8, 0xa0, 0x28, 0x0f, 0xd2, 0x60, 0xe8, 0xe9,
-  0x68, 0x08, 0xad, 0xdc, 0x8b, 0x4b, 0x58, 0x92, 0x9c, 0xe3, 0x64, 0x98, 0x70,
-  0xdf, 0x04, 0x06, 0x0d, 0x47, 0x39, 0xbc, 0xcf, 0x08, 0xdc, 0x8e, 0x95, 0x0b,
-  0x1c, 0x30, 0x6a, 0x2f, 0xe7, 0x68, 0x08, 0xae, 0xca, 0x2c, 0xa3, 0x01, 0x61,
-  0xff, 0x31, 0x44, 0x65, 0x8f, 0x5f, 0xbe, 0x49, 0xdf, 0x3d, 0xa3, 0x76, 0xc6,
-  0x78, 0x0e, 0x31, 0xb3, 0xda, 0x67, 0x81, 0x7f, 0xa3, 0x69, 0x97, 0x68, 0x08,
-  0xae, 0xaf, 0xa5, 0xe2, 0x11, 0xca, 0x99, 0x40, 0xc3, 0x1f, 0xc1, 0x01, 0xd0,
-  0x45, 0x46, 0x10, 0xfc, 0x1f, 0xad, 0x18, 0xb7, 0xa2, 0x0b, 0x42, 0x86, 0x16,
-  0x8c, 0xf3, 0x00, 0x29, 0x68, 0x08, 0xae, 0xcb, 0x01, 0x40, 0x52, 0x77, 0xc4,
-  0xcb, 0xd3, 0x47, 0x22, 0x80, 0xde, 0x81, 0xed, 0x19, 0x6d, 0xf2, 0xd8, 0x02,
-  0xb4, 0xb9, 0x27, 0x18, 0xe1, 0xd1, 0x53, 0xc0, 0x5a, 0x7c, 0x9e, 0x68, 0x08,
-  0xc2, 0x87, 0xa4, 0xfc, 0x0b, 0x8c, 0xa3, 0xa9, 0xe0, 0x1d, 0xc0, 0x7a, 0x16,
-  0x0a, 0x0b, 0x1f, 0xd5, 0xc6, 0xa9, 0x41, 0xc4, 0x58, 0xf0, 0x30, 0x84, 0xf3,
-  0x0b, 0x3e, 0x55, 0xb5, 0xe0, 0xa3, 0x40, 0x9b, 0x81, 0x00, 0xc8, 0x00, 0x86,
-  0x00, 0x40, 0x96, 0x18, 0x34, 0x5a, 0xa0, 0x00, 0x05, 0x60, 0xf4, 0x40, 0x00,
-  0x00, 0x60, 0xfb, 0x4e, 0x88, 0x14, 0xf1, 0xff, 0x24, 0x86, 0xd8, 0x07, 0xd4,
-  0x3f, 0xf0, 0x2b, 0xcb, 0xd6, 0xcd, 0xe4, 0xea, 0x4d, 0xde, 0x1a, 0xc7, 0xf8,
-  0x9e, 0xab, 0x62, 0xd5, 0x2b, 0xf9, 0x21, 0x7f, 0xe1, 0x81, 0xef, 0xbd, 0xd6,
-  0x08, 0x8d, 0xba, 0x2e, 0xd3, 0x1d, 0xc4, 0x20, 0x45, 0xda, 0xde, 0x64, 0xa1,
-  0x90, 0x51, 0x15, 0x3e, 0xdf, 0x64, 0xf1, 0x97, 0x5f, 0xb6, 0x5d, 0x1e, 0x3e,
-  0xe6, 0x0c, 0x46, 0x04, 0x28, 0x5d, 0x72, 0x2f, 0x26, 0xec, 0x65, 0x09, 0x3c,
-  0xfa, 0xf7, 0x28, 0x73, 0xb4, 0x59, 0x37, 0x21, 0xf6, 0xc3, 0xd2, 0xfa, 0x26,
-  0xa5, 0xcc, 0x3c, 0x77, 0x12, 0x3e, 0x69, 0xd6, 0xf8, 0xf0, 0x7a, 0x32, 0xfe,
-  0x70, 0xe6, 0x61, 0x67, 0x39, 0xb1, 0x8f, 0xe1, 0x6f, 0x37, 0xf1, 0x43, 0xbc,
-  0xa1, 0xa0, 0xe9, 0x76, 0xe1, 0x48, 0x6d, 0xac, 0xf3, 0x60, 0x58, 0x2c, 0x19,
-  0xbe, 0x52, 0xa0, 0x15, 0xbc, 0x32, 0x80, 0xa3, 0x40, 0x98, 0x81, 0x01, 0x0a,
-  0x00, 0x86, 0x00, 0x40, 0x96, 0x28, 0x44, 0x5a, 0xa0, 0x00, 0x03, 0x60, 0x00,
-  0x00, 0x5c, 0xf9, 0xef, 0x22, 0x1b, 0x32, 0x7f, 0xb5, 0xc0, 0x29, 0x8d, 0x80,
-  0x5f, 0x15, 0x79, 0xbb, 0xa1, 0xa7, 0xde, 0x8f, 0xba, 0x67, 0x83, 0x88, 0xbc,
-  0x6a, 0xb0, 0x71, 0xa6, 0x14, 0xae, 0x47, 0x6f, 0xb6, 0x03, 0x5b, 0xe8, 0x50,
-  0x07, 0xa0, 0xbb, 0xcd, 0x0d, 0xc2, 0x28, 0xc1, 0x33, 0x7a, 0x6b, 0x4d, 0x5c,
-  0x00, 0xc8, 0x3c, 0x57, 0x92, 0x38, 0x78, 0xd8, 0x91, 0x45, 0x69, 0xb9, 0x78,
-  0xf7, 0x0c, 0xf9, 0x43, 0xc4, 0x94, 0x22, 0xd1, 0x61, 0x97, 0x32, 0xa9, 0x0b,
-  0x78, 0x3a, 0xcd, 0xcc, 0x12, 0xe3, 0x66, 0xef, 0x37, 0xb2, 0xbe, 0x8f, 0x2d,
-  0xea, 0xa0, 0x11, 0x9b, 0x03, 0xb1, 0x0d, 0x44, 0xe3, 0xd5, 0x64, 0x74, 0x3a,
-  0xc8, 0x63, 0x46, 0xef, 0x83, 0xb2, 0x9b, 0xac, 0xa4, 0xd5, 0x6d, 0xef, 0x0c,
-  0x5a, 0xfe, 0x1b, 0x01, 0xc5, 0xb3, 0x33, 0xe3, 0xca, 0xc4, 0x5a, 0xd6, 0xde,
-  0x67, 0x9c, 0x92, 0x9e, 0x2a, 0x44, 0xa3, 0x40, 0x9c, 0x81, 0x01, 0x4c, 0x00,
-  0x86, 0x00, 0x40, 0x96, 0x18, 0x5c, 0x5a, 0xa0, 0x00, 0x03, 0x60, 0x00, 0x00,
-  0x58, 0xec, 0xd2, 0x58, 0x0d, 0x75, 0x16, 0x57, 0x84, 0x79, 0x36, 0x68, 0xc0,
-  0x88, 0x22, 0x7f, 0xde, 0x47, 0x68, 0xfe, 0x16, 0x98, 0x0c, 0xcc, 0x7e, 0xa2,
-  0x8f, 0xd3, 0xe9, 0x9f, 0xf1, 0x63, 0x61, 0xc1, 0xf4, 0x2a, 0xbe, 0x84, 0xdb,
-  0x96, 0x8a, 0x72, 0x24, 0xd0, 0xda, 0x2d, 0x15, 0x43, 0xd2, 0xb5, 0x02, 0x27,
-  0xf0, 0x1c, 0x93, 0x3c, 0x0c, 0xa5, 0xda, 0x50, 0x94, 0xe4, 0xec, 0x92, 0x3a,
-  0x1a, 0xbc, 0xac, 0xbb, 0x79, 0x58, 0x8e, 0xaf, 0x8a, 0x6c, 0xe3, 0xb3, 0x23,
-  0x82, 0x48, 0x42, 0x7d, 0x1f, 0x67, 0x00, 0x95, 0xbe, 0xa4, 0x30, 0xe5, 0xc7,
-  0x88, 0xb3, 0xb0, 0x14, 0x03, 0x24, 0xba, 0x11, 0x95, 0x4a, 0xd9, 0xf4, 0xa2,
-  0x1f, 0xae, 0xaf, 0x96, 0x16, 0x41, 0x17, 0x76, 0x9e, 0x86, 0x33, 0xac, 0x34,
-  0x68, 0x0f, 0x77, 0xa9, 0x8c, 0xe6, 0xcb, 0xd9, 0x35, 0x21, 0x28, 0x01, 0x2f,
-  0x21, 0xce, 0x52, 0xff, 0xc6, 0x81, 0x87, 0x99, 0x80, 0xa3, 0x40, 0x85, 0x81,
-  0x01, 0x90, 0x00, 0x86, 0x00, 0x40, 0x96, 0x18, 0x7c, 0x5a, 0xc0, 0x00, 0x03,
-  0x60, 0x00, 0x00, 0x55, 0x18, 0xaa, 0xef, 0xc0, 0x94, 0x12, 0xd3, 0xb7, 0xc5,
-  0x38, 0xfa, 0xb1, 0xd8, 0xf1, 0x9e, 0x4f, 0x2d, 0xb0, 0xef, 0x92, 0xcd, 0xda,
-  0x79, 0x59, 0x95, 0x52, 0x11, 0xb3, 0x20, 0xae, 0x61, 0x0d, 0x90, 0xe3, 0x9a,
-  0x6c, 0x99, 0xae, 0x59, 0xd6, 0xbc, 0xa7, 0x16, 0x03, 0xc0, 0x2e, 0x39, 0xb6,
-  0x74, 0xa7, 0x4e, 0x23, 0xd9, 0x58, 0x8d, 0xc1, 0x30, 0x54, 0x84, 0x39, 0x91,
-  0xb2, 0x11, 0x7e, 0x0f, 0x4b, 0x6e, 0x41, 0x1c, 0x6c, 0x82, 0xaa, 0x86, 0xbb,
-  0x4a, 0x57, 0x76, 0x9b, 0xa1, 0x6e, 0x13, 0x72, 0x0c, 0x41, 0x64, 0xb4, 0xe9,
-  0xc1, 0x4d, 0x47, 0xa1, 0x24, 0x12, 0xdb, 0x78, 0xc4, 0xc4, 0x8a, 0xa8, 0x8b,
-  0x8d, 0xf7, 0x7a, 0x51, 0x2f, 0xf2, 0x75, 0xef, 0xd1, 0x7f, 0x46, 0x1a, 0xe4,
-  0x58, 0x00, 0xa3, 0x40, 0xa0, 0x81, 0x01, 0xd2, 0x00, 0x86, 0x00, 0x40, 0x96,
-  0x61, 0x11, 0x6b, 0x80, 0x00, 0x14, 0x60, 0xa5, 0x30, 0x00, 0x00, 0x53, 0x52,
-  0x29, 0x3a, 0xd0, 0xb9, 0xe3, 0x8b, 0x66, 0x7c, 0xc6, 0x2d, 0xae, 0x58, 0x27,
-  0x26, 0xd6, 0xb4, 0x06, 0xa2, 0xed, 0x73, 0xff, 0x69, 0x27, 0xfb, 0xe7, 0xe4,
-  0xc9, 0x6e, 0x55, 0x06, 0x79, 0xe3, 0x3b, 0x90, 0x18, 0x5b, 0x36, 0x7a, 0x6a,
-  0x80, 0x5e, 0xcf, 0x59, 0x1f, 0x83, 0x4b, 0xe2, 0x90, 0x6a, 0xfc, 0xd6, 0x7b,
-  0xb7, 0x97, 0x78, 0x7d, 0x13, 0x64, 0xc4, 0x3f, 0xf1, 0xd3, 0x25, 0x36, 0xa5,
-  0x1d, 0xe0, 0xdb, 0x4f, 0x7b, 0x48, 0xde, 0x2c, 0xde, 0x96, 0x7c, 0xc3, 0xd5,
-  0x72, 0x9b, 0xcb, 0x7a, 0xb6, 0x35, 0x18, 0x8f, 0x7e, 0xf8, 0xdb, 0x7c, 0x52,
-  0x6b, 0x5b, 0x17, 0xcf, 0xe3, 0xcc, 0x56, 0x5b, 0x0c, 0x7e, 0x3d, 0xf6, 0xf2,
-  0x0d, 0xc2, 0xfa, 0x52, 0xc8, 0x8e, 0x7d, 0x6e, 0x7c, 0x1a, 0x6b, 0xfb, 0xed,
-  0xe4, 0x95, 0xdf, 0x56, 0xed, 0x57, 0xc4, 0x14, 0x9c, 0xbf, 0xfa, 0xa0, 0xad,
-  0x98, 0x95, 0x13, 0xdd, 0x87, 0x73, 0xb9, 0x29, 0x00, 0xa3, 0x41, 0xd5, 0x82,
-  0x02, 0x80, 0x86, 0x07, 0xa9, 0xc7, 0xc2, 0xc4, 0xc8, 0xbb, 0xc4, 0x68, 0x09,
-  0xbc, 0x34, 0x69, 0x89, 0xaa, 0x8f, 0xe4, 0xff, 0xf4, 0x1d, 0x34, 0x67, 0x43,
-  0x2e, 0x1d, 0x26, 0x93, 0x14, 0x6e, 0x8f, 0xfa, 0x8e, 0xb0, 0xe2, 0x25, 0x7e,
-  0x1a, 0xce, 0x3a, 0x0e, 0xad, 0x50, 0x8c, 0xe9, 0xec, 0xed, 0x36, 0x4b, 0xb0,
-  0x68, 0x86, 0x19, 0xe2, 0xd9, 0xa5, 0xbc, 0x56, 0x9b, 0x2e, 0x19, 0x7c, 0x5b,
-  0x04, 0x2a, 0x84, 0x35, 0x80, 0x5a, 0xbe, 0x10, 0xb6, 0xff, 0x1c, 0x6d, 0x83,
-  0xa7, 0xf9, 0x20, 0x2e, 0x84, 0xe3, 0x83, 0x95, 0xe2, 0xf3, 0xa3, 0xc2, 0xe7,
-  0x0d, 0x05, 0x2b, 0x74, 0x25, 0x3a, 0x51, 0xbd, 0x93, 0x9f, 0x68, 0x80, 0x0a,
-  0x2d, 0x53, 0xde, 0xda, 0xb5, 0xc4, 0x32, 0x04, 0xec, 0xaf, 0xd7, 0xb0, 0x5b,
-  0x7d, 0x93, 0xb5, 0x44, 0xe7, 0x92, 0xb5, 0xba, 0x08, 0xbe, 0x8e, 0x23, 0x59,
-  0xef, 0xb1, 0x2b, 0x6b, 0xe2, 0x89, 0x59, 0xa7, 0x9c, 0x62, 0x9f, 0x97, 0x46,
-  0xd3, 0xe1, 0x33, 0x47, 0xf1, 0x1a, 0x69, 0x6b, 0xc1, 0xe8, 0x68, 0x80, 0x22,
-  0x95, 0x26, 0x69, 0x77, 0xdd, 0x3d, 0x66, 0x9d, 0xaa, 0xbf, 0xab, 0x9e, 0x18,
-  0x0a, 0x78, 0x17, 0xac, 0x58, 0x1e, 0x06, 0x2c, 0x5c, 0x66, 0x3c, 0x07, 0xb5,
-  0x5f, 0xd2, 0x55, 0xb7, 0xd3, 0x1c, 0xaf, 0x0f, 0xf3, 0xa6, 0x9d, 0x18, 0x3d,
-  0x65, 0x8c, 0x38, 0xd6, 0xdf, 0xa0, 0x21, 0x20, 0x72, 0x0f, 0x75, 0xa7, 0x0b,
-  0xde, 0x01, 0x68, 0x80, 0x45, 0x8f, 0x82, 0x6e, 0x30, 0x0a, 0x59, 0x93, 0x00,
-  0xab, 0x17, 0x2b, 0xfc, 0xfd, 0x5b, 0x42, 0x4d, 0x05, 0xf3, 0xb7, 0x2b, 0xcb,
-  0x88, 0x08, 0xf1, 0x93, 0xa0, 0xf4, 0x29, 0x53, 0x80, 0xef, 0xce, 0xe2, 0x18,
-  0x7f, 0xf7, 0x63, 0x91, 0x4d, 0x46, 0x32, 0x74, 0xa9, 0x1e, 0xf5, 0x2d, 0x04,
-  0x53, 0x3f, 0x44, 0x9c, 0x33, 0xb3, 0x91, 0x22, 0x0a, 0x51, 0x80, 0x73, 0xde,
-  0x27, 0x9e, 0x9b, 0x68, 0x80, 0x55, 0xd0, 0xd5, 0x84, 0x5e, 0x9d, 0xef, 0x6f,
-  0xc9, 0x22, 0x35, 0x5f, 0x44, 0x0d, 0xfe, 0xa2, 0x76, 0x77, 0x50, 0x00, 0x36,
-  0x81, 0xc7, 0x24, 0x1a, 0x8e, 0x90, 0x50, 0x2b, 0xf8, 0x19, 0x44, 0xbe, 0x86,
-  0x05, 0x28, 0x67, 0x7a, 0xbe, 0xad, 0x13, 0x94, 0x18, 0x23, 0x41, 0x4b, 0x2c,
-  0x60, 0x20, 0x4e, 0x02, 0x08, 0x99, 0xa8, 0x77, 0xb7, 0xcb, 0xef, 0x51, 0xa0,
-  0x68, 0x80, 0x76, 0x9b, 0x54, 0x21, 0xaa, 0x23, 0xb6, 0x7b, 0x17, 0xeb, 0xf3,
-  0x2b, 0xb5, 0xb6, 0xf0, 0x31, 0xb6, 0x0f, 0x52, 0x43, 0xd9, 0xb1, 0x1a, 0xba,
-  0x32, 0x43, 0xf1, 0x38, 0x32, 0xe7, 0x64, 0xea, 0xaf, 0x2c, 0xf9, 0x52, 0x86,
-  0x0e, 0xfa, 0xba, 0x1d, 0x29, 0x80, 0xc2, 0xa5, 0x40, 0x7c, 0x8b, 0xf8, 0x85,
-  0xfe, 0x28, 0x6a, 0x91, 0x76, 0x78, 0x22, 0xc0, 0xda, 0xfd, 0x8d, 0x92, 0xb1,
-  0x90, 0xa9, 0x68, 0x87, 0x4c, 0xac, 0xda, 0x9e, 0x36, 0x56, 0x5d, 0x7a, 0x79,
-  0xea, 0xe1, 0x0a, 0xb3, 0x67, 0x3b, 0x55, 0x2b, 0x4e, 0xde, 0x7f, 0x25, 0x4f,
-  0x34, 0xdb, 0x53, 0x53, 0x1e, 0x4d, 0x31, 0x51, 0x8c, 0x7b, 0xc8, 0x06, 0x73,
-  0x97, 0x7f, 0x47, 0xbe, 0xa7, 0xb7, 0x39, 0xbe, 0x89, 0xfb, 0x53, 0x6e, 0xd9,
-  0xf2, 0xfb, 0x80, 0xaf, 0x11, 0x28, 0xa3, 0x95, 0x3b, 0x88, 0xa3, 0x30, 0x00
-};
-
-// gWebMData contains 8 SimpleBlocks in a single Cluster with the following attributes
+// "test.webm" contains 8 SimpleBlocks in a single Cluster with the following attributes
 static const uint64_t gTimecodes[] = { 66000000, 160000000, 100000000, 133000000,
                                       166000000, 200000000, 233000000, 320000000 };
 static const int64_t gEndOffsets[] = { 501, 772, 930, 1085, 1244, 1380, 1543, 2015 };
 
 TEST(WebMBuffered, BasicTests)
 {
   ReentrantMonitor dummy("dummy");
   WebMBufferedParser parser(0);
@@ -188,60 +27,89 @@ TEST(WebMBuffered, BasicTests)
 
   unsigned char buf[] = { 0x1a, 0x45, 0xdf, 0xa3 };
   parser.Append(buf, ArrayLength(buf), mapping, dummy);
   EXPECT_TRUE(mapping.IsEmpty());
   EXPECT_EQ(parser.mStartOffset, 0);
   EXPECT_EQ(parser.mCurrentOffset, 4);
 }
 
+static void
+ReadFile(const char* aPath, nsTArray<uint8_t>& aBuffer)
+{
+  FILE* f = fopen(aPath, "rb");
+  ASSERT_NE(f, (FILE *) nullptr);
+
+  int r = fseek(f, 0, SEEK_END);
+  ASSERT_EQ(r, 0);
+
+  long size = ftell(f);
+  ASSERT_NE(size, -1);
+  aBuffer.SetLength(size);
+
+  r = fseek(f, 0, SEEK_SET);
+  ASSERT_EQ(r, 0);
+
+  size_t got = fread(aBuffer.Elements(), 1, size, f);
+  ASSERT_EQ(got, size_t(size));
+
+  r = fclose(f);
+  ASSERT_EQ(r, 0);
+}
+
 TEST(WebMBuffered, RealData)
 {
   ReentrantMonitor dummy("dummy");
   WebMBufferedParser parser(0);
 
+  nsTArray<uint8_t> webmData;
+  ReadFile("test.webm", webmData);
+
   nsTArray<WebMTimeDataOffset> mapping;
-  parser.Append(gWebMData, ArrayLength(gWebMData), mapping, dummy);
+  parser.Append(webmData.Elements(), webmData.Length(), mapping, dummy);
   EXPECT_EQ(mapping.Length(), 8u);
   EXPECT_EQ(parser.mStartOffset, 0);
-  EXPECT_EQ(parser.mCurrentOffset, int64_t(ArrayLength(gWebMData)));
+  EXPECT_EQ(parser.mCurrentOffset, int64_t(webmData.Length()));
   EXPECT_EQ(parser.GetTimecodeScale(), 500000u);
 
   for (uint32_t i = 0; i < mapping.Length(); ++i) {
     EXPECT_EQ(mapping[i].mEndOffset, gEndOffsets[i]);
     EXPECT_EQ(mapping[i].mSyncOffset, 361);
     EXPECT_EQ(mapping[i].mTimecode, gTimecodes[i]);
   }
 }
 
 TEST(WebMBuffered, RealDataAppend)
 {
   ReentrantMonitor dummy("dummy");
   WebMBufferedParser parser(0);
   nsTArray<WebMTimeDataOffset> mapping;
 
+  nsTArray<uint8_t> webmData;
+  ReadFile("test.webm", webmData);
+
   uint32_t arrayEntries = mapping.Length();
   size_t offset = 0;
-  while (offset < ArrayLength(gWebMData)) {
-    parser.Append(gWebMData + offset, 1, mapping, dummy);
+  while (offset < webmData.Length()) {
+    parser.Append(webmData.Elements() + offset, 1, mapping, dummy);
     offset += 1;
     EXPECT_EQ(parser.mCurrentOffset, int64_t(offset));
     if (mapping.Length() != arrayEntries) {
       arrayEntries = mapping.Length();
       ASSERT_LE(arrayEntries, 8u);
       uint32_t i = arrayEntries - 1;
       EXPECT_EQ(mapping[i].mEndOffset, gEndOffsets[i]);
       EXPECT_EQ(mapping[i].mSyncOffset, 361);
       EXPECT_EQ(mapping[i].mTimecode, gTimecodes[i]);
       EXPECT_EQ(parser.GetTimecodeScale(), 500000u);
     }
   }
   EXPECT_EQ(mapping.Length(), 8u);
   EXPECT_EQ(parser.mStartOffset, 0);
-  EXPECT_EQ(parser.mCurrentOffset, int64_t(ArrayLength(gWebMData)));
+  EXPECT_EQ(parser.mCurrentOffset, int64_t(webmData.Length()));
   EXPECT_EQ(parser.GetTimecodeScale(), 500000u);
 
   for (uint32_t i = 0; i < mapping.Length(); ++i) {
     EXPECT_EQ(mapping[i].mEndOffset, gEndOffsets[i]);
     EXPECT_EQ(mapping[i].mSyncOffset, 361);
     EXPECT_EQ(mapping[i].mTimecode, gTimecodes[i]);
   }
 }
--- a/content/media/gtest/moz.build
+++ b/content/media/gtest/moz.build
@@ -6,16 +6,20 @@
 
 UNIFIED_SOURCES += [
     'TestAudioCompactor.cpp',
     'TestTrackEncoder.cpp',
     'TestVideoSegment.cpp',
     'TestWebMBuffered.cpp'
 ]
 
+TEST_HARNESS_FILES.gtest += [
+    'test.webm'
+]
+
 if CONFIG['MOZ_WEBM_ENCODER']:
     UNIFIED_SOURCES += ['TestVideoTrackEncoder.cpp',
                         'TestVorbisTrackEncoder.cpp',
                         'TestWebMWriter.cpp',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
--- a/dom/camera/CameraCommon.h
+++ b/dom/camera/CameraCommon.h
@@ -10,20 +10,16 @@
 #ifndef __func__
 #ifdef __FUNCTION__
 #define __func__ __FUNCTION__
 #else
 #define __func__ __FILE__
 #endif
 #endif
 
-#ifndef NAN
-#define NAN std::numeric_limits<double>::quiet_NaN()
-#endif
-
 #include "prlog.h"
 
 #ifdef PR_LOGGING
 extern PRLogModuleInfo* GetCameraLog();
 #define DOM_CAMERA_LOG( type, ... ) PR_LOG(GetCameraLog(), (PRLogModuleLevel)type, ( __VA_ARGS__ ))
 #else
 #define DOM_CAMERA_LOG( type, ... )
 #endif
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -1176,17 +1176,16 @@ if test "$GNU_CC"; then
     # -Wendif-labels - catches `#else FOO` and `#endif FOO` not in comment
     # -Wenum-compare - catches comparison of different enum types
     # -Wignored-qualifiers - catches returns types with qualifiers like const
     # -Wimplicit-function-declaration - catches missing C function prototypes
     # -Wint-to-pointer-cast - catches cast to pointer from integer of different size
     # -Wmissing-braces - catches aggregate initializers missing nested braces
     # -Wmultichar - catches multicharacter integer constants like 'THIS'
     # -Wnonnull - catches NULL used with functions arguments marked as non-null
-    # -Wparentheses - catches `if (a=b)` and operator precedence bugs
     # -Wpointer-arith - catches pointer arithmetic using NULL or sizeof(void)
     # -Wpointer-sign - catches mixing pointers to signed and unsigned types
     # -Wpointer-to-int-cast - catches casts from pointer to different sized int
     # -Wreturn-type - catches missing returns, zero false positives
     # -Wsequence-point - catches undefined order behavior like `a = a++`
     # -Wsign-compare - catches comparison of signed and unsigned types
     # -Wswitch - catches switches without all enum cases or default case
     # -Wtrigraphs - catches unlikely use of trigraphs
@@ -1207,17 +1206,16 @@ if test "$GNU_CC"; then
     _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Werror=endif-labels"
     _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Werror=enum-compare"
     _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Werror=ignored-qualifiers"
     _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Werror=implicit-function-declaration"
     _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Werror=int-to-pointer-cast"
     _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Werror=missing-braces"
     _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Werror=multichar"
     _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Werror=nonnull"
-    _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Werror=parentheses"
     _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Werror=pointer-arith"
     _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Werror=pointer-sign"
     _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Werror=pointer-to-int-cast"
     _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Werror=return-type"
     _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Werror=sequence-point"
     _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Werror=switch"
     _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Werror=trigraphs"
     _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Werror=unknown-pragmas"
@@ -1276,17 +1274,16 @@ if test "$GNU_CXX"; then
     # -Wcomment - catches nested comments
     # -Wconversion-null - catches conversions between NULL and non-pointer types
     # -Wempty-body - catches bugs, e.g. "if (c); foo();", few false positives
     # -Wendif-labels - catches `#else FOO` and `#endif FOO` not in comment
     # -Wignored-qualifiers - catches returns types with qualifiers like const
     # -Wint-to-pointer-cast - catches cast to pointer from integer of different size
     # -Wmissing-braces - catches aggregate initializers missing nested braces
     # -Woverloaded-virtual - function declaration hides virtual function from base class
-    # -Wparentheses - catches `if (a=b)` and operator precedence bugs
     # -Wpointer-arith - catches pointer arithmetic using NULL or sizeof(void)
     # -Wpointer-to-int-cast - catches casts from pointer to different sized int
     # -Wreorder - catches ctor initializer list not matching class definition order
     # -Wreturn-type - catches missing returns, zero false positives
     # -Wsequence-point - catches undefined order behavior like `a = a++`
     # -Wsign-compare - catches comparison of signed and unsigned types
     # -Wswitch - catches switches without all enum cases or default case
     # -Wtrigraphs - catches unlikely use of trigraphs
@@ -1304,17 +1301,16 @@ if test "$GNU_CXX"; then
     # Treat some warnings as errors:
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=char-subscripts"
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=comment"
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=empty-body"
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=endif-labels"
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=int-to-pointer-cast"
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=missing-braces"
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=overloaded-virtual"
-    _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=parentheses"
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=pointer-arith"
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=reorder"
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=return-type"
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=sequence-point"
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=switch"
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=trigraphs"
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=unknown-pragmas"
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=unused-label"
--- a/js/src/irregexp/RegExpEngine.cpp
+++ b/js/src/irregexp/RegExpEngine.cpp
@@ -996,17 +996,17 @@ ChoiceNode::FilterASCII(int depth, bool 
         RegExpNode* replacement =
             alternatives()[i].node()->FilterASCII(depth - 1, ignore_case);
         if (replacement != nullptr) {
             alternatives()[i].set_node(replacement);
             new_alternatives.append(alternatives()[i]);
         }
     }
 
-    alternatives_.appendAll(new_alternatives);
+    alternatives_ = Move(new_alternatives);
     return this;
 }
 
 // -------------------------------------------------------------------
 // NegativeLookaheadChoiceNode
 
 bool
 NegativeLookaheadChoiceNode::FillInBMInfo(int offset,
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug1081175.js
@@ -0,0 +1,20 @@
+var input = "webkit-search-cancel-button-aaaaaaa-bbbbb-ccccccc-dddddddd,"
+var bad_regex = '([a-u-]|\\u0080|\\u0100)*[d]';
+
+function forceUnicode(s) {
+    return ('\uffff' + s).replace(/^\uffff/, '');
+}
+function testRegex(input) {
+    for (var i = 0; i < input.length; i++) {
+        var sub = input.substring(0, i + 1);
+	var res = sub.match(bad_regex);
+	if (i >= 50) {
+	    assertEq(res.length, 2);
+	    assertEq(res[1], sub.substr(-2, 1));
+	} else {
+	    assertEq(res, null);
+	}
+    }
+}
+testRegex(input);
+testRegex(forceUnicode(input));
--- a/js/src/jit/BacktrackingAllocator.cpp
+++ b/js/src/jit/BacktrackingAllocator.cpp
@@ -244,31 +244,31 @@ BacktrackingAllocator::tryGroupReusedReg
     // instruction. Handle this splitting eagerly.
 
     if (usedReg.numIntervals() != 1 ||
         (usedReg.def()->isFixed() && !usedReg.def()->output()->isRegister())) {
         reg.setMustCopyInput();
         return true;
     }
     LiveInterval *interval = usedReg.getInterval(0);
-    LBlock *block = insData[reg.ins()].block();
+    LBlock *block = reg.ins()->block();
 
     // The input's lifetime must end within the same block as the definition,
     // otherwise it could live on in phis elsewhere.
     if (interval->end() > exitOf(block)) {
         reg.setMustCopyInput();
         return true;
     }
 
     for (UsePositionIterator iter = interval->usesBegin(); iter != interval->usesEnd(); iter++) {
         if (iter->pos <= inputOf(reg.ins()))
             continue;
 
         LUse *use = iter->use;
-        if (FindReusingDefinition(insData[iter->pos].ins(), use)) {
+        if (FindReusingDefinition(insData[iter->pos], use)) {
             reg.setMustCopyInput();
             return true;
         }
         if (use->policy() != LUse::ANY && use->policy() != LUse::KEEPALIVE) {
             reg.setMustCopyInput();
             return true;
         }
     }
@@ -953,26 +953,26 @@ BacktrackingAllocator::resolveControlFlo
                     skip = true;
                     break;
                 }
             }
             if (skip)
                 continue;
 
             CodePosition start = interval->start();
-            InstructionData *data = &insData[start];
-            if (interval->start() > entryOf(data->block())) {
-                MOZ_ASSERT(start == inputOf(data->ins()) || start == outputOf(data->ins()));
+            LNode *ins = insData[start];
+            if (interval->start() > entryOf(ins->block())) {
+                MOZ_ASSERT(start == inputOf(ins) || start == outputOf(ins));
 
                 LiveInterval *prevInterval = reg->intervalFor(start.previous());
                 if (start.subpos() == CodePosition::INPUT) {
-                    if (!moveInput(inputOf(data->ins()), prevInterval, interval, reg->type()))
+                    if (!moveInput(ins->toInstruction(), prevInterval, interval, reg->type()))
                         return false;
                 } else {
-                    if (!moveAfter(outputOf(data->ins()), prevInterval, interval, reg->type()))
+                    if (!moveAfter(ins->toInstruction(), prevInterval, interval, reg->type()))
                         return false;
                 }
             }
         }
     }
 
     JitSpew(JitSpew_RegAlloc, "Resolving control flow (block loop)");
 
@@ -1037,25 +1037,25 @@ BacktrackingAllocator::resolveControlFlo
             }
         }
     }
 
     return true;
 }
 
 bool
-BacktrackingAllocator::isReusedInput(LUse *use, LInstruction *ins, bool considerCopy)
+BacktrackingAllocator::isReusedInput(LUse *use, LNode *ins, bool considerCopy)
 {
     if (LDefinition *def = FindReusingDefinition(ins, use))
         return considerCopy || !vregs[def->virtualRegister()].mustCopyInput();
     return false;
 }
 
 bool
-BacktrackingAllocator::isRegisterUse(LUse *use, LInstruction *ins, bool considerCopy)
+BacktrackingAllocator::isRegisterUse(LUse *use, LNode *ins, bool considerCopy)
 {
     switch (use->policy()) {
       case LUse::ANY:
         return isReusedInput(use, ins, considerCopy);
 
       case LUse::REGISTER:
       case LUse::FIXED:
         return true;
@@ -1096,17 +1096,17 @@ BacktrackingAllocator::reifyAllocations(
 
         for (size_t j = 0; j < reg->numIntervals(); j++) {
             LiveInterval *interval = reg->getInterval(j);
             MOZ_ASSERT(interval->index() == j);
 
             if (interval->index() == 0) {
                 reg->def()->setOutput(*interval->getAllocation());
                 if (reg->ins()->recoversInput()) {
-                    LSnapshot *snapshot = reg->ins()->snapshot();
+                    LSnapshot *snapshot = reg->ins()->toInstruction()->snapshot();
                     for (size_t i = 0; i < snapshot->numEntries(); i++) {
                         LAllocation *entry = snapshot->getEntry(i);
                         if (entry->isUse() && entry->toUse()->policy() == LUse::RECOVERED_INPUT)
                             *entry = *reg->def()->output();
                     }
                 }
             }
 
@@ -1114,25 +1114,25 @@ BacktrackingAllocator::reifyAllocations(
                  iter != interval->usesEnd();
                  iter++)
             {
                 LAllocation *alloc = iter->use;
                 *alloc = *interval->getAllocation();
 
                 // For any uses which feed into MUST_REUSE_INPUT definitions,
                 // add copies if the use and def have different allocations.
-                LInstruction *ins = insData[iter->pos].ins();
+                LNode *ins = insData[iter->pos];
                 if (LDefinition *def = FindReusingDefinition(ins, alloc)) {
                     LiveInterval *outputInterval =
                         vregs[def->virtualRegister()].intervalFor(outputOf(ins));
                     LAllocation *res = outputInterval->getAllocation();
                     LAllocation *sourceAlloc = interval->getAllocation();
 
                     if (*res != *alloc) {
-                        LMoveGroup *group = getInputMoveGroup(inputOf(ins));
+                        LMoveGroup *group = getInputMoveGroup(ins->toInstruction());
                         if (!group->addAfter(sourceAlloc, res, reg->type()))
                             return false;
                         *alloc = *res;
                     }
                 }
             }
 
             addLiveRegistersForInterval(reg, interval);
@@ -1358,25 +1358,25 @@ BacktrackingAllocator::computePriority(c
     for (size_t j = 0; j < group->registers.length(); j++) {
         uint32_t vreg = group->registers[j];
         priority += computePriority(vregs[vreg].getInterval(0));
     }
     return priority;
 }
 
 bool
-BacktrackingAllocator::minimalDef(const LiveInterval *interval, LInstruction *ins)
+BacktrackingAllocator::minimalDef(const LiveInterval *interval, LNode *ins)
 {
     // Whether interval is a minimal interval capturing a definition at ins.
     return (interval->end() <= minimalDefEnd(ins).next()) &&
         ((!ins->isPhi() && interval->start() == inputOf(ins)) || interval->start() == outputOf(ins));
 }
 
 bool
-BacktrackingAllocator::minimalUse(const LiveInterval *interval, LInstruction *ins)
+BacktrackingAllocator::minimalUse(const LiveInterval *interval, LNode *ins)
 {
     // Whether interval is a minimal interval capturing a use at ins.
     return (interval->start() == inputOf(ins)) &&
         (interval->end() == outputOf(ins) || interval->end() == outputOf(ins).next());
 }
 
 bool
 BacktrackingAllocator::minimalInterval(const LiveInterval *interval, bool *pfixed)
@@ -1398,22 +1398,22 @@ BacktrackingAllocator::minimalInterval(c
     for (UsePositionIterator iter = interval->usesBegin(); iter != interval->usesEnd(); iter++) {
         LUse *use = iter->use;
 
         switch (use->policy()) {
           case LUse::FIXED:
             if (fixed)
                 return false;
             fixed = true;
-            if (minimalUse(interval, insData[iter->pos].ins()))
+            if (minimalUse(interval, insData[iter->pos]))
                 minimal = true;
             break;
 
           case LUse::REGISTER:
-            if (minimalUse(interval, insData[iter->pos].ins()))
+            if (minimalUse(interval, insData[iter->pos]))
                 minimal = true;
             break;
 
           default:
             break;
         }
     }
 
@@ -1535,29 +1535,29 @@ BacktrackingAllocator::trySplitAfterLast
     // split it after the last use which does require a register. If conflict
     // is specified, only consider register uses before the conflict starts.
 
     CodePosition lastRegisterFrom, lastRegisterTo, lastUse;
 
     // If the definition of the interval is in a register, consider that a
     // register use too for our purposes here.
     if (isRegisterDefinition(interval)) {
-        CodePosition spillStart = minimalDefEnd(insData[interval->start()].ins()).next();
+        CodePosition spillStart = minimalDefEnd(insData[interval->start()]).next();
         if (!conflict || spillStart < conflict->start()) {
             lastUse = lastRegisterFrom = interval->start();
             lastRegisterTo = spillStart;
         }
     }
 
     for (UsePositionIterator iter(interval->usesBegin());
          iter != interval->usesEnd();
          iter++)
     {
         LUse *use = iter->use;
-        LInstruction *ins = insData[iter->pos].ins();
+        LNode *ins = insData[iter->pos];
 
         // Uses in the interval should be sorted.
         MOZ_ASSERT(iter->pos >= lastUse);
         lastUse = inputOf(ins);
 
         if (!conflict || outputOf(ins) < conflict->start()) {
             if (isRegisterUse(use, ins, /* considerCopy = */ true)) {
                 lastRegisterFrom = inputOf(ins);
@@ -1604,17 +1604,17 @@ BacktrackingAllocator::trySplitBeforeFir
 
     CodePosition firstRegisterFrom;
 
     for (UsePositionIterator iter(interval->usesBegin());
          iter != interval->usesEnd();
          iter++)
     {
         LUse *use = iter->use;
-        LInstruction *ins = insData[iter->pos].ins();
+        LNode *ins = insData[iter->pos];
 
         if (!conflict || outputOf(ins) >= conflict->end()) {
             if (isRegisterUse(use, ins, /* considerCopy = */ true)) {
                 firstRegisterFrom = inputOf(ins);
                 break;
             }
         }
     }
@@ -1656,17 +1656,17 @@ BacktrackingAllocator::splitAtAllRegiste
         spillIntervalIsNew = true;
     }
 
     CodePosition spillStart = interval->start();
     if (isRegisterDefinition(interval)) {
         // Treat the definition of the interval as a register use so that it
         // can be split and spilled ASAP.
         CodePosition from = interval->start();
-        CodePosition to = minimalDefEnd(insData[from].ins()).next();
+        CodePosition to = minimalDefEnd(insData[from]).next();
         if (!addLiveInterval(newIntervals, vreg, spillInterval, from, to))
             return false;
         spillStart = to;
     }
 
     if (spillIntervalIsNew) {
         for (size_t i = 0; i < interval->numRanges(); i++) {
             const LiveInterval::Range *range = interval->getRange(i);
@@ -1675,17 +1675,17 @@ BacktrackingAllocator::splitAtAllRegiste
                 return false;
         }
     }
 
     for (UsePositionIterator iter(interval->usesBegin());
          iter != interval->usesEnd();
          iter++)
     {
-        LInstruction *ins = insData[iter->pos].ins();
+        LNode *ins = insData[iter->pos];
         if (iter->pos < spillStart) {
             newIntervals.back()->addUseAtEnd(new(alloc()) UsePosition(iter->use, iter->pos));
         } else if (isRegisterUse(iter->use, ins)) {
             // For register uses which are not useRegisterAtStart, pick an
             // interval that covers both the instruction's input and output, so
             // that the register is not reused for an output.
             CodePosition from = inputOf(ins);
             CodePosition to = iter->pos.next();
@@ -1743,17 +1743,17 @@ BacktrackingAllocator::splitAt(LiveInter
     // splitPositions should be non-empty and sorted.
     MOZ_ASSERT(!splitPositions.empty());
     for (size_t i = 1; i < splitPositions.length(); ++i)
         MOZ_ASSERT(splitPositions[i-1] < splitPositions[i]);
 
     // Don't spill the interval until after the end of its definition.
     CodePosition spillStart = interval->start();
     if (isRegisterDefinition(interval))
-        spillStart = minimalDefEnd(insData[interval->start()].ins()).next();
+        spillStart = minimalDefEnd(insData[interval->start()]).next();
 
     uint32_t vreg = interval->vreg();
 
     // If this LiveInterval is the result of an earlier split which created a
     // spill interval, that spill interval covers the whole range, so we don't
     // need to create a new one.
     bool spillIntervalIsNew = false;
     LiveInterval *spillInterval = interval->spillInterval();
@@ -1777,17 +1777,17 @@ BacktrackingAllocator::splitAt(LiveInter
         newInterval->setSpillInterval(spillInterval);
         if (!newIntervals.append(newInterval))
             return false;
         lastRegisterUse = interval->start();
     }
 
     size_t activeSplitPosition = NextSplitPosition(0, splitPositions, interval->start());
     for (UsePositionIterator iter(interval->usesBegin()); iter != interval->usesEnd(); iter++) {
-        LInstruction *ins = insData[iter->pos].ins();
+        LNode *ins = insData[iter->pos];
         if (iter->pos < spillStart) {
             newIntervals.back()->addUseAtEnd(new(alloc()) UsePosition(iter->use, iter->pos));
             activeSplitPosition = NextSplitPosition(activeSplitPosition, splitPositions, iter->pos);
         } else if (isRegisterUse(iter->use, ins)) {
             if (lastRegisterUse.bits() == 0 ||
                 SplitHere(activeSplitPosition, splitPositions, iter->pos))
             {
                 // Place this register use into a different interval from the
@@ -1815,17 +1815,17 @@ BacktrackingAllocator::splitAt(LiveInter
         CodePosition start, end;
         if (i == 0 && spillStart != interval->start()) {
             start = interval->start();
             if (newInterval->usesEmpty())
                 end = spillStart;
             else
                 end = newInterval->usesBack()->pos.next();
         } else {
-            start = inputOf(insData[newInterval->usesBegin()->pos].ins());
+            start = inputOf(insData[newInterval->usesBegin()->pos]);
             end = newInterval->usesBack()->pos.next();
         }
         for (; activeRange > 0; --activeRange) {
             const LiveInterval::Range *range = interval->getRange(activeRange - 1);
             if (range->to <= start)
                 continue;
             if (range->from >= end)
                 break;
--- a/js/src/jit/BacktrackingAllocator.h
+++ b/js/src/jit/BacktrackingAllocator.h
@@ -202,35 +202,35 @@ class BacktrackingAllocator
     bool tryAllocateGroupRegister(PhysicalRegister &r, VirtualRegisterGroup *group,
                                   bool *psuccess, bool *pfixed, LiveInterval **pconflicting);
     bool evictInterval(LiveInterval *interval);
     void distributeUses(LiveInterval *interval, const LiveIntervalVector &newIntervals);
     bool split(LiveInterval *interval, const LiveIntervalVector &newIntervals);
     bool requeueIntervals(const LiveIntervalVector &newIntervals);
     void spill(LiveInterval *interval);
 
-    bool isReusedInput(LUse *use, LInstruction *ins, bool considerCopy);
-    bool isRegisterUse(LUse *use, LInstruction *ins, bool considerCopy = false);
+    bool isReusedInput(LUse *use, LNode *ins, bool considerCopy);
+    bool isRegisterUse(LUse *use, LNode *ins, bool considerCopy = false);
     bool isRegisterDefinition(LiveInterval *interval);
     bool addLiveInterval(LiveIntervalVector &intervals, uint32_t vreg,
                          LiveInterval *spillInterval,
                          CodePosition from, CodePosition to);
 
     bool resolveControlFlow();
     bool reifyAllocations();
     bool populateSafepoints();
 
     void dumpRegisterGroups();
     void dumpFixedRanges();
     void dumpAllocations();
 
     struct PrintLiveIntervalRange;
 
-    bool minimalDef(const LiveInterval *interval, LInstruction *ins);
-    bool minimalUse(const LiveInterval *interval, LInstruction *ins);
+    bool minimalDef(const LiveInterval *interval, LNode *ins);
+    bool minimalUse(const LiveInterval *interval, LNode *ins);
     bool minimalInterval(const LiveInterval *interval, bool *pfixed = nullptr);
 
     // Heuristic methods.
 
     size_t computePriority(const LiveInterval *interval);
     size_t computeSpillWeight(const LiveInterval *interval);
 
     size_t computePriority(const VirtualRegisterGroup *group);
--- a/js/src/jit/C1Spewer.cpp
+++ b/js/src/jit/C1Spewer.cpp
@@ -96,26 +96,26 @@ DumpDefinition(FILE *fp, MDefinition *de
     fprintf(fp, "%u %u ", def->id(), unsigned(def->useCount()));
     def->printName(fp);
     fprintf(fp, " ");
     def->printOpcode(fp);
     fprintf(fp, " <|@\n");
 }
 
 static void
-DumpLIR(FILE *fp, LInstruction *ins)
+DumpLIR(FILE *fp, LNode *ins)
 {
     fprintf(fp, "      ");
     fprintf(fp, "%d ", ins->id());
     ins->dump(fp);
     fprintf(fp, " <|@\n");
 }
 
 void
-C1Spewer::spewIntervals(FILE *fp, LinearScanAllocator *regalloc, LInstruction *ins, size_t &nextId)
+C1Spewer::spewIntervals(FILE *fp, LinearScanAllocator *regalloc, LNode *ins, size_t &nextId)
 {
     for (size_t k = 0; k < ins->numDefs(); k++) {
         uint32_t id = ins->getDef(k)->virtualRegister();
         VirtualRegister *vreg = &regalloc->vregs[id];
 
         for (size_t i = 0; i < vreg->numIntervals(); i++) {
             LiveInterval *live = vreg->getInterval(i);
             if (live->numRanges()) {
--- a/js/src/jit/C1Spewer.h
+++ b/js/src/jit/C1Spewer.h
@@ -16,17 +16,17 @@
 namespace js {
 namespace jit {
 
 class MDefinition;
 class MInstruction;
 class MBasicBlock;
 class MIRGraph;
 class LinearScanAllocator;
-class LInstruction;
+class LNode;
 
 class C1Spewer
 {
     MIRGraph *graph;
     FILE *spewout_;
 
   public:
     C1Spewer()
@@ -37,17 +37,17 @@ class C1Spewer
     void beginFunction(MIRGraph *graph, HandleScript script);
     void spewPass(const char *pass);
     void spewIntervals(const char *pass, LinearScanAllocator *regalloc);
     void endFunction();
     void finish();
 
   private:
     void spewPass(FILE *fp, MBasicBlock *block);
-    void spewIntervals(FILE *fp, LinearScanAllocator *regalloc, LInstruction *ins, size_t &nextId);
+    void spewIntervals(FILE *fp, LinearScanAllocator *regalloc, LNode *ins, size_t &nextId);
     void spewIntervals(FILE *fp, MBasicBlock *block, LinearScanAllocator *regalloc, size_t &nextId);
 };
 
 } // namespace jit
 } // namespace js
 
 #endif /* DEBUG */
 
--- a/js/src/jit/FixedList.h
+++ b/js/src/jit/FixedList.h
@@ -38,16 +38,20 @@ class FixedList
             return true;
 
         if (length & mozilla::tl::MulOverflowMask<sizeof(T)>::value)
             return false;
         list_ = (T *)alloc.allocate(length * sizeof(T));
         return list_ != nullptr;
     }
 
+    size_t empty() const {
+        return length_ == 0;
+    }
+
     size_t length() const {
         return length_;
     }
 
     void shrink(size_t num) {
         MOZ_ASSERT(num < length_);
         length_ -= num;
     }
--- a/js/src/jit/IonCaches.h
+++ b/js/src/jit/IonCaches.h
@@ -17,16 +17,18 @@
 #include "vm/TypedArrayCommon.h"
 
 namespace js {
 
 class LockedJSContext;
 
 namespace jit {
 
+class LInstruction;
+
 #define IONCACHE_KIND_LIST(_)                                   \
     _(GetProperty)                                              \
     _(SetProperty)                                              \
     _(GetElement)                                               \
     _(SetElement)                                               \
     _(BindName)                                                 \
     _(Name)                                                     \
     _(CallsiteClone)                                            \
--- a/js/src/jit/JSONSpewer.cpp
+++ b/js/src/jit/JSONSpewer.cpp
@@ -330,17 +330,17 @@ JSONSpewer::spewMIR(MIRGraph *mir)
         endObject();
     }
 
     endList();
     endObject();
 }
 
 void
-JSONSpewer::spewLIns(LInstruction *ins)
+JSONSpewer::spewLIns(LNode *ins)
 {
     if (!fp_)
         return;
 
     beginObject();
 
     integerProperty("id", ins->id());
 
--- a/js/src/jit/JSONSpewer.h
+++ b/js/src/jit/JSONSpewer.h
@@ -17,17 +17,17 @@ namespace js {
 namespace jit {
 
 class MDefinition;
 class MInstruction;
 class MBasicBlock;
 class MIRGraph;
 class MResumePoint;
 class LinearScanAllocator;
-class LInstruction;
+class LNode;
 
 class JSONSpewer
 {
   private:
     // Set by beginFunction(); unset by endFunction().
     // Used to correctly format output in case of abort during compilation.
     bool inFunction_;
 
@@ -58,17 +58,17 @@ class JSONSpewer
     ~JSONSpewer();
 
     bool init(const char *path);
     void beginFunction(JSScript *script);
     void beginPass(const char * pass);
     void spewMDef(MDefinition *def);
     void spewMResumePoint(MResumePoint *rp);
     void spewMIR(MIRGraph *mir);
-    void spewLIns(LInstruction *ins);
+    void spewLIns(LNode *ins);
     void spewLIR(MIRGraph *mir);
     void spewIntervals(LinearScanAllocator *regalloc);
     void endPass();
     void endFunction();
     void finish();
 };
 
 } // namespace jit
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -6060,82 +6060,16 @@ class LGuardClass : public LInstructionH
     const MGuardClass *mir() const {
         return mir_->toGuardClass();
     }
     const LDefinition *tempInt() {
         return getTemp(0);
     }
 };
 
-class MPhi;
-
-// Phi is a pseudo-instruction that emits no code, and is an annotation for the
-// register allocator. Like its equivalent in MIR, phis are collected at the
-// top of blocks and are meant to be executed in parallel, choosing the input
-// corresponding to the predecessor taken in the control flow graph.
-class LPhi MOZ_FINAL : public LInstruction
-{
-    LAllocation *const inputs_;
-    LDefinition def_;
-
-  public:
-    LIR_HEADER(Phi)
-
-    LPhi(MPhi *ins, LAllocation *inputs)
-        : inputs_(inputs)
-    {
-        setMir(ins);
-    }
-
-    size_t numDefs() const {
-        return 1;
-    }
-    LDefinition *getDef(size_t index) {
-        MOZ_ASSERT(index == 0);
-        return &def_;
-    }
-    void setDef(size_t index, const LDefinition &def) {
-        MOZ_ASSERT(index == 0);
-        def_ = def;
-    }
-    size_t numOperands() const {
-        return mir_->toPhi()->numOperands();
-    }
-    LAllocation *getOperand(size_t index) {
-        MOZ_ASSERT(index < numOperands());
-        return &inputs_[index];
-    }
-    void setOperand(size_t index, const LAllocation &a) {
-        MOZ_ASSERT(index < numOperands());
-        inputs_[index] = a;
-    }
-    size_t numTemps() const {
-        return 0;
-    }
-    LDefinition *getTemp(size_t index) {
-        MOZ_CRASH("no temps");
-    }
-    void setTemp(size_t index, const LDefinition &temp) {
-        MOZ_CRASH("no temps");
-    }
-    size_t numSuccessors() const {
-        return 0;
-    }
-    MBasicBlock *getSuccessor(size_t i) const {
-        MOZ_CRASH("no successors");
-    }
-    void setSuccessor(size_t i, MBasicBlock *) {
-        MOZ_CRASH("no successors");
-    }
-
-    virtual void printInfo(FILE *fp) {
-        printOperands(fp);
-    }
-};
-
 class LIn : public LCallInstructionHelper<1, BOX_PIECES+1, 0>
 {
   public:
     LIR_HEADER(In)
     explicit LIn(const LAllocation &rhs) {
         setOperand(RHS, rhs);
     }
 
--- a/js/src/jit/LIR.cpp
+++ b/js/src/jit/LIR.cpp
@@ -52,93 +52,85 @@ LIRGraph::noteNeedsSafepoint(LInstructio
     // Instructions with safepoints must be in linear order.
     MOZ_ASSERT_IF(!safepoints_.empty(), safepoints_.back()->id() < ins->id());
     if (!ins->isCall() && !nonCallSafepoints_.append(ins))
         return false;
     return safepoints_.append(ins);
 }
 
 void
-LIRGraph::dump(FILE *fp) const
+LIRGraph::dump(FILE *fp)
 {
     for (size_t i = 0; i < numBlocks(); i++) {
         getBlock(i)->dump(fp);
         fprintf(fp, "\n");
     }
 }
 
 void
-LIRGraph::dump() const
+LIRGraph::dump()
 {
     dump(stderr);
 }
 
-LBlock *
-LBlock::New(TempAllocator &alloc, MBasicBlock *from)
+LBlock::LBlock(MBasicBlock *from)
+  : block_(from),
+    phis_(),
+    entryMoveGroup_(nullptr),
+    exitMoveGroup_(nullptr)
 {
-    LBlock *block = new(alloc) LBlock(from);
-    if (!block)
-        return nullptr;
+    from->assignLir(this);
+}
 
+bool
+LBlock::init(TempAllocator &alloc)
+{
     // Count the number of LPhis we'll need.
     size_t numLPhis = 0;
-    for (MPhiIterator i(from->phisBegin()), e(from->phisEnd()); i != e; ++i) {
+    for (MPhiIterator i(block_->phisBegin()), e(block_->phisEnd()); i != e; ++i) {
         MPhi *phi = *i;
         numLPhis += (phi->type() == MIRType_Value) ? BOX_PIECES : 1;
     }
 
     // Allocate space for the LPhis.
-    if (!block->phis_.init(alloc, numLPhis))
+    if (!phis_.init(alloc, numLPhis))
         return nullptr;
 
     // For each MIR phi, set up LIR phis as appropriate. We'll fill in their
     // operands on each incoming edge, and set their definitions at the start of
     // their defining block.
     size_t phiIndex = 0;
-    size_t numPreds = from->numPredecessors();
-    for (MPhiIterator i(from->phisBegin()), e(from->phisEnd()); i != e; ++i) {
+    size_t numPreds = block_->numPredecessors();
+    for (MPhiIterator i(block_->phisBegin()), e(block_->phisEnd()); i != e; ++i) {
         MPhi *phi = *i;
         MOZ_ASSERT(phi->numOperands() == numPreds);
 
         int numPhis = (phi->type() == MIRType_Value) ? BOX_PIECES : 1;
         for (int i = 0; i < numPhis; i++) {
             void *array = alloc.allocateArray<sizeof(LAllocation)>(numPreds);
             LAllocation *inputs = static_cast<LAllocation *>(array);
             if (!inputs)
-                return nullptr;
+                return false;
 
-            new (&block->phis_[phiIndex++]) LPhi(phi, inputs);
+            LPhi *lphi = new (&phis_[phiIndex++]) LPhi(phi, inputs);
+            lphi->setBlock(this);
         }
     }
-    return block;
+    return true;
 }
 
-uint32_t
-LBlock::firstId() const
+const LInstruction *
+LBlock::firstInstructionWithId() const
 {
-    if (phis_.length()) {
-        return phis_[0].id();
-    } else {
-        for (LInstructionIterator i(instructions_.begin()); i != instructions_.end(); i++) {
-            if (i->id())
-                return i->id();
-        }
+    for (LInstructionIterator i(instructions_.begin()); i != instructions_.end(); ++i) {
+        if (i->id())
+            return *i;
     }
     return 0;
 }
-uint32_t
-LBlock::lastId() const
-{
-    LInstruction *last = *instructions_.rbegin();
-    MOZ_ASSERT(last->id());
-    // The last instruction is a control flow instruction which does not have
-    // any output.
-    MOZ_ASSERT(last->numDefs() == 0);
-    return last->id();
-}
 
 LMoveGroup *
 LBlock::getEntryMoveGroup(TempAllocator &alloc)
 {
     if (entryMoveGroup_)
         return entryMoveGroup_;
     entryMoveGroup_ = LMoveGroup::New(alloc);
     if (begin()->isLabel())
@@ -306,32 +298,32 @@ LSnapshot::rewriteRecoveredInput(LUse in
     // equal to the instruction's result.
     for (size_t i = 0; i < numEntries(); i++) {
         if (getEntry(i)->isUse() && getEntry(i)->toUse()->virtualRegister() == input.virtualRegister())
             setEntry(i, LUse(input.virtualRegister(), LUse::RECOVERED_INPUT));
     }
 }
 
 void
-LInstruction::printName(FILE *fp, Opcode op)
+LNode::printName(FILE *fp, Opcode op)
 {
     static const char * const names[] =
     {
 #define LIROP(x) #x,
         LIR_OPCODE_LIST(LIROP)
 #undef LIROP
     };
     const char *name = names[op];
     size_t len = strlen(name);
     for (size_t i = 0; i < len; i++)
         fprintf(fp, "%c", tolower(name[i]));
 }
 
 void
-LInstruction::printName(FILE *fp)
+LNode::printName(FILE *fp)
 {
     printName(fp, op());
 }
 
 bool
 LAllocation::aliases(const LAllocation &other) const
 {
     if (isFloatReg() && other.isFloatReg())
@@ -456,17 +448,17 @@ LAllocation::dump() const
 
 void
 LDefinition::dump() const
 {
     fprintf(stderr, "%s\n", toString());
 }
 
 void
-LInstruction::printOperands(FILE *fp)
+LNode::printOperands(FILE *fp)
 {
     for (size_t i = 0, e = numOperands(); i < e; i++) {
         fprintf(fp, " (%s)", getOperand(i)->toString());
         if (i != numOperands() - 1)
             fprintf(fp, ",");
     }
 }
 
@@ -483,30 +475,30 @@ LInstruction::assignSnapshot(LSnapshot *
                 (void *)snapshot, (void *)this);
         printName(JitSpewFile);
         fprintf(JitSpewFile, ")\n");
     }
 #endif
 }
 
 void
-LInstruction::dump(FILE *fp)
+LNode::dump(FILE *fp)
 {
     if (numDefs() != 0) {
         fprintf(fp, "{");
         for (size_t i = 0; i < numDefs(); i++) {
             fprintf(fp, "%s", getDef(i)->toString());
             if (i != numDefs() - 1)
                 fprintf(fp, ", ");
         }
         fprintf(fp, "} <- ");
     }
 
     printName(fp);
-    printInfo(fp);
+    printOperands(fp);
 
     if (numTemps()) {
         fprintf(fp, " t=(");
         for (size_t i = 0; i < numTemps(); i++) {
             fprintf(fp, "%s", getTemp(i)->toString());
             if (i != numTemps() - 1)
                 fprintf(fp, ", ");
         }
@@ -520,17 +512,17 @@ LInstruction::dump(FILE *fp)
             if (i != numSuccessors() - 1)
                 fprintf(fp, ", ");
         }
         fprintf(fp, ")");
     }
 }
 
 void
-LInstruction::dump()
+LNode::dump()
 {
     dump(stderr);
     fprintf(stderr, "\n");
 }
 
 void
 LInstruction::initSafepoint(TempAllocator &alloc)
 {
--- a/js/src/jit/LIR.h
+++ b/js/src/jit/LIR.h
@@ -590,44 +590,35 @@ class LDefinition
 
 // Forward declarations of LIR types.
 #define LIROP(op) class L##op;
     LIR_OPCODE_LIST(LIROP)
 #undef LIROP
 
 class LSnapshot;
 class LSafepoint;
-class LInstructionVisitor;
+class LInstruction;
+class LElementVisitor;
 
-class LInstruction
-  : public TempObject,
-    public InlineListNode<LInstruction>
+// The common base class for LPhi and LInstruction.
+class LNode
 {
     uint32_t id_;
-
-    // This snapshot could be set after a ResumePoint.  It is used to restart
-    // from the resume point pc.
-    LSnapshot *snapshot_;
-
-    // Structure capturing the set of stack slots and registers which are known
-    // to hold either gcthings or Values.
-    LSafepoint *safepoint_;
+    LBlock *block_;
 
   protected:
     MDefinition *mir_;
 
-    LInstruction()
+  public:
+    LNode()
       : id_(0),
-        snapshot_(nullptr),
-        safepoint_(nullptr),
+        block_(nullptr),
         mir_(nullptr)
     { }
 
-  public:
-    class InputIterator;
     enum Opcode {
 #   define LIROP(name) LOp_##name,
         LIR_OPCODE_LIST(LIROP)
 #   undef LIROP
         LOp_Invalid
     };
 
     const char *opName() {
@@ -642,19 +633,24 @@ class LInstruction
     }
 
     // Hook for opcodes to add extra high level detail about what code will be
     // emitted for the op.
     virtual const char *extraName() const {
         return nullptr;
     }
 
-  public:
     virtual Opcode op() const = 0;
 
+    bool isInstruction() const {
+        return op() != LOp_Phi;
+    }
+    inline LInstruction *toInstruction();
+    inline const LInstruction *toInstruction() const;
+
     // Returns the number of outputs of this instruction. If an output is
     // unallocated, it is an LDefinition, defining a virtual register.
     virtual size_t numDefs() const = 0;
     virtual LDefinition *getDef(size_t index) = 0;
     virtual void setDef(size_t index, const LDefinition &def) = 0;
 
     // Returns information about operands.
     virtual size_t numOperands() const = 0;
@@ -679,124 +675,254 @@ class LInstruction
     uint32_t id() const {
         return id_;
     }
     void setId(uint32_t id) {
         MOZ_ASSERT(!id_);
         MOZ_ASSERT(id);
         id_ = id;
     }
-    LSnapshot *snapshot() const {
-        return snapshot_;
-    }
-    LSafepoint *safepoint() const {
-        return safepoint_;
-    }
     void setMir(MDefinition *mir) {
         mir_ = mir;
     }
     MDefinition *mirRaw() const {
         /* Untyped MIR for this op. Prefer mir() methods in subclasses. */
         return mir_;
     }
-    void assignSnapshot(LSnapshot *snapshot);
-    void initSafepoint(TempAllocator &alloc);
+    LBlock *block() const {
+        return block_;
+    }
+    void setBlock(LBlock *block) {
+        block_ = block;
+    }
 
     // For an instruction which has a MUST_REUSE_INPUT output, whether that
     // output register will be restored to its original value when bailing out.
     virtual bool recoversInput() const {
         return false;
     }
 
     virtual void dump(FILE *fp);
     void dump();
     static void printName(FILE *fp, Opcode op);
     virtual void printName(FILE *fp);
     virtual void printOperands(FILE *fp);
-    virtual void printInfo(FILE *fp) { }
 
   public:
     // Opcode testing and casts.
 #   define LIROP(name)                                                      \
     bool is##name() const {                                                 \
         return op() == LOp_##name;                                          \
     }                                                                       \
-    inline L##name *to##name();
+    inline L##name *to##name();                                             \
+    inline const L##name *to##name() const;
     LIR_OPCODE_LIST(LIROP)
 #   undef LIROP
 
-    virtual bool accept(LInstructionVisitor *visitor) = 0;
+    virtual bool accept(LElementVisitor *visitor) = 0;
+
+#define LIR_HEADER(opcode)                                                  \
+    Opcode op() const {                                                     \
+        return LInstruction::LOp_##opcode;                                  \
+    }                                                                       \
+    bool accept(LElementVisitor *visitor) {                                 \
+        visitor->setElement(this);                                          \
+        return visitor->visit##opcode(this);                                \
+    }
 };
 
-class LInstructionVisitor
+class LInstruction
+  : public LNode
+  , public TempObject
+  , public InlineListNode<LInstruction>
 {
-    LInstruction *ins_;
+    // This snapshot could be set after a ResumePoint.  It is used to restart
+    // from the resume point pc.
+    LSnapshot *snapshot_;
+
+    // Structure capturing the set of stack slots and registers which are known
+    // to hold either gcthings or Values.
+    LSafepoint *safepoint_;
+
+    LMoveGroup *inputMoves_;
+    LMoveGroup *movesAfter_;
+
+  protected:
+    LInstruction()
+      : snapshot_(nullptr),
+        safepoint_(nullptr),
+        inputMoves_(nullptr),
+        movesAfter_(nullptr)
+    { }
+
+  public:
+    LSnapshot *snapshot() const {
+        return snapshot_;
+    }
+    LSafepoint *safepoint() const {
+        return safepoint_;
+    }
+    LMoveGroup *inputMoves() const {
+        return inputMoves_;
+    }
+    void setInputMoves(LMoveGroup *moves) {
+        inputMoves_ = moves;
+    }
+    LMoveGroup *movesAfter() const {
+        return movesAfter_;
+    }
+    void setMovesAfter(LMoveGroup *moves) {
+        movesAfter_ = moves;
+    }
+    void assignSnapshot(LSnapshot *snapshot);
+    void initSafepoint(TempAllocator &alloc);
+
+    class InputIterator;
+};
+
+LInstruction *
+LNode::toInstruction()
+{
+    MOZ_ASSERT(isInstruction());
+    return static_cast<LInstruction *>(this);
+}
+
+const LInstruction *
+LNode::toInstruction() const
+{
+    MOZ_ASSERT(isInstruction());
+    return static_cast<const LInstruction *>(this);
+}
+
+class LElementVisitor
+{
+    LNode *ins_;
 
   protected:
     jsbytecode *lastPC_;
     jsbytecode *lastNotInlinedPC_;
 
-    LInstruction *instruction() {
+    LNode *instruction() {
         return ins_;
     }
 
   public:
-    void setInstruction(LInstruction *ins) {
+    void setElement(LNode *ins) {
         ins_ = ins;
         if (ins->mirRaw()) {
             lastPC_ = ins->mirRaw()->trackedPc();
             if (ins->mirRaw()->trackedTree())
                 lastNotInlinedPC_ = ins->mirRaw()->profilerLeavePc();
         }
     }
 
-    LInstructionVisitor()
+    LElementVisitor()
       : ins_(nullptr),
         lastPC_(nullptr),
         lastNotInlinedPC_(nullptr)
     {}
 
   public:
 #define VISIT_INS(op) virtual bool visit##op(L##op *) { MOZ_CRASH("NYI: " #op); }
     LIR_OPCODE_LIST(VISIT_INS)
 #undef VISIT_INS
 };
 
 typedef InlineList<LInstruction>::iterator LInstructionIterator;
 typedef InlineList<LInstruction>::reverse_iterator LInstructionReverseIterator;
 
-class LPhi;
+class MPhi;
+
+// Phi is a pseudo-instruction that emits no code, and is an annotation for the
+// register allocator. Like its equivalent in MIR, phis are collected at the
+// top of blocks and are meant to be executed in parallel, choosing the input
+// corresponding to the predecessor taken in the control flow graph.
+class LPhi MOZ_FINAL : public LNode
+{
+    LAllocation *const inputs_;
+    LDefinition def_;
+
+  public:
+    LIR_HEADER(Phi)
+
+    LPhi(MPhi *ins, LAllocation *inputs)
+        : inputs_(inputs)
+    {
+        setMir(ins);
+    }
+
+    size_t numDefs() const {
+        return 1;
+    }
+    LDefinition *getDef(size_t index) {
+        MOZ_ASSERT(index == 0);
+        return &def_;
+    }
+    void setDef(size_t index, const LDefinition &def) {
+        MOZ_ASSERT(index == 0);
+        def_ = def;
+    }
+    size_t numOperands() const {
+        return mir_->toPhi()->numOperands();
+    }
+    LAllocation *getOperand(size_t index) {
+        MOZ_ASSERT(index < numOperands());
+        return &inputs_[index];
+    }
+    void setOperand(size_t index, const LAllocation &a) {
+        MOZ_ASSERT(index < numOperands());
+        inputs_[index] = a;
+    }
+    size_t numTemps() const {
+        return 0;
+    }
+    LDefinition *getTemp(size_t index) {
+        MOZ_CRASH("no temps");
+    }
+    void setTemp(size_t index, const LDefinition &temp) {
+        MOZ_CRASH("no temps");
+    }
+    size_t numSuccessors() const {
+        return 0;
+    }
+    MBasicBlock *getSuccessor(size_t i) const {
+        MOZ_CRASH("no successors");
+    }
+    void setSuccessor(size_t i, MBasicBlock *) {
+        MOZ_CRASH("no successors");
+    }
+};
+
 class LMoveGroup;
-class LBlock : public TempObject
+class LBlock
 {
     MBasicBlock *block_;
     FixedList<LPhi> phis_;
     InlineList<LInstruction> instructions_;
     LMoveGroup *entryMoveGroup_;
     LMoveGroup *exitMoveGroup_;
     Label label_;
 
-    explicit LBlock(MBasicBlock *block)
-      : block_(block),
-        phis_(),
-        entryMoveGroup_(nullptr),
-        exitMoveGroup_(nullptr)
-    { }
+  public:
+    explicit LBlock(MBasicBlock *block);
+    bool init(TempAllocator &alloc);
 
-  public:
-    static LBlock *New(TempAllocator &alloc, MBasicBlock *from);
     void add(LInstruction *ins) {
+        ins->setBlock(this);
         instructions_.pushBack(ins);
     }
     size_t numPhis() const {
         return phis_.length();
     }
     LPhi *getPhi(size_t index) {
         return &phis_[index];
     }
+    const LPhi *getPhi(size_t index) const {
+        return &phis_[index];
+    }
     MBasicBlock *mir() const {
         return block_;
     }
     LInstructionIterator begin() {
         return instructions_.begin();
     }
     LInstructionIterator begin(LInstruction *at) {
         return instructions_.begin(at);
@@ -818,18 +944,36 @@ class LBlock : public TempObject
     }
     void insertAfter(LInstruction *at, LInstruction *ins) {
         instructions_.insertAfter(at, ins);
     }
     void insertBefore(LInstruction *at, LInstruction *ins) {
         MOZ_ASSERT(!at->isLabel());
         instructions_.insertBefore(at, ins);
     }
-    uint32_t firstId() const;
-    uint32_t lastId() const;
+    const LNode *firstElementWithId() const {
+        return !phis_.empty()
+               ? static_cast<const LNode *>(getPhi(0))
+               : firstInstructionWithId();
+    }
+    uint32_t firstId() const {
+        return firstElementWithId()->id();
+    }
+    uint32_t lastId() const {
+        return lastInstructionWithId()->id();
+    }
+    const LInstruction *firstInstructionWithId() const;
+    const LInstruction *lastInstructionWithId() const {
+        const LInstruction *last = *instructions_.rbegin();
+        MOZ_ASSERT(last->id());
+        // The last instruction is a control flow instruction which does not have
+        // any output.
+        MOZ_ASSERT(last->numDefs() == 0);
+        return last;
+    }
 
     // Return the label to branch to when branching to this block.
     Label *label() {
         MOZ_ASSERT(!isTrivial());
         return &label_;
     }
 
     LMoveGroup *getEntryMoveGroup(TempAllocator &alloc);
@@ -900,20 +1044,16 @@ class LInstructionHelper : public LInstr
     const LAllocation *input() {
         MOZ_ASSERT(numOperands() == 1);
         return getOperand(0);
     }
     const LDefinition *output() {
         MOZ_ASSERT(numDefs() == 1);
         return getDef(0);
     }
-
-    virtual void printInfo(FILE *fp) {
-        printOperands(fp);
-    }
 };
 
 template <size_t Defs, size_t Operands, size_t Temps>
 class LCallInstructionHelper : public LInstructionHelper<Defs, Operands, Temps>
 {
   public:
     virtual bool isCall() const {
         return true;
@@ -1517,17 +1657,17 @@ class LIRGraph
         static HashNumber hash(const Value &v) {
             return HashNumber(v.asRawBits());
         }
         static bool match(const Value &lhs, const Value &rhs) {
             return lhs == rhs;
         }
     };
 
-    FixedList<LBlock *> blocks_;
+    FixedList<LBlock> blocks_;
     Vector<Value, 0, IonAllocPolicy> constantPool_;
     typedef HashMap<Value, uint32_t, ValueHasher, IonAllocPolicy> ConstantPoolMap;
     ConstantPoolMap constantPoolMap_;
     Vector<LInstruction *, 0, IonAllocPolicy> safepoints_;
     Vector<LInstruction *, 0, IonAllocPolicy> nonCallSafepoints_;
     uint32_t numVirtualRegisters_;
     uint32_t numInstructions_;
 
@@ -1548,24 +1688,25 @@ class LIRGraph
         return constantPoolMap_.init() && blocks_.init(mir_.alloc(), mir_.numBlocks());
     }
     MIRGraph &mir() const {
         return mir_;
     }
     size_t numBlocks() const {
         return blocks_.length();
     }
-    LBlock *getBlock(size_t i) const {
-        return blocks_[i];
+    LBlock *getBlock(size_t i) {
+        return &blocks_[i];
     }
     uint32_t numBlockIds() const {
         return mir_.numBlockIds();
     }
-    void setBlock(size_t index, LBlock *block) {
-        blocks_[index] = block;
+    bool initBlock(MBasicBlock *mir) {
+        LBlock *lir = new (&blocks_[mir->id()]) LBlock(mir);
+        return lir->init(mir_.alloc());
     }
     uint32_t getVirtualRegister() {
         numVirtualRegisters_ += VREG_INCREMENT;
         return numVirtualRegisters_;
     }
     uint32_t numVirtualRegisters() const {
         // Virtual registers are 1-based, not 0-based, so add one as a
         // convenience for 0-based arrays.
@@ -1634,18 +1775,18 @@ class LIRGraph
     }
     size_t numSafepoints() const {
         return safepoints_.length();
     }
     LInstruction *getSafepoint(size_t i) const {
         return safepoints_[i];
     }
 
-    void dump(FILE *fp) const;
-    void dump() const;
+    void dump(FILE *fp);
+    void dump();
 };
 
 LAllocation::LAllocation(AnyRegister reg)
 {
     if (reg.isFloat())
         *this = LFloatReg(reg.fpu());
     else
         *this = LGeneralReg(reg.gpr());
@@ -1658,25 +1799,16 @@ LAllocation::toRegister() const
     if (isFloatReg())
         return AnyRegister(toFloatReg()->reg());
     return AnyRegister(toGeneralReg()->reg());
 }
 
 } // namespace jit
 } // namespace js
 
-#define LIR_HEADER(opcode)                                                  \
-    Opcode op() const {                                                     \
-        return LInstruction::LOp_##opcode;                                  \
-    }                                                                       \
-    bool accept(LInstructionVisitor *visitor) {                             \
-        visitor->setInstruction(this);                                      \
-        return visitor->visit##opcode(this);                                \
-    }
-
 #include "jit/LIR-Common.h"
 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
 # if defined(JS_CODEGEN_X86)
 #  include "jit/x86/LIR-x86.h"
 # elif defined(JS_CODEGEN_X64)
 #  include "jit/x64/LIR-x64.h"
 # endif
 # include "jit/shared/LIR-x86-shared.h"
@@ -1691,20 +1823,25 @@ LAllocation::toRegister() const
 #endif
 
 #undef LIR_HEADER
 
 namespace js {
 namespace jit {
 
 #define LIROP(name)                                                         \
-    L##name *LInstruction::to##name()                                       \
+    L##name *LNode::to##name()                                              \
     {                                                                       \
         MOZ_ASSERT(is##name());                                             \
         return static_cast<L##name *>(this);                                \
+    }                                                                       \
+    const L##name *LNode::to##name() const                                  \
+    {                                                                       \
+        MOZ_ASSERT(is##name());                                             \
+        return static_cast<const L##name *>(this);                          \
     }
     LIR_OPCODE_LIST(LIROP)
 #undef LIROP
 
 #define LALLOC_CAST(type)                                                   \
     L##type *LAllocation::to##type() {                                      \
         MOZ_ASSERT(is##type());                                             \
         return static_cast<L##type *>(this);                                \
--- a/js/src/jit/LinearScan.cpp
+++ b/js/src/jit/LinearScan.cpp
@@ -290,22 +290,22 @@ LinearScanAllocator::resolveControlFlow(
             }
         }
     }
 
     return true;
 }
 
 bool
-LinearScanAllocator::moveInputAlloc(CodePosition pos, LAllocation *from, LAllocation *to,
+LinearScanAllocator::moveInputAlloc(LInstruction *ins, LAllocation *from, LAllocation *to,
                                     LDefinition::Type type)
 {
     if (*from == *to)
         return true;
-    LMoveGroup *moves = getInputMoveGroup(pos);
+    LMoveGroup *moves = getInputMoveGroup(ins);
     return moves->add(from, to, type);
 }
 
 static inline void
 SetOsiPointUses(LiveInterval *interval, CodePosition defEnd, const LAllocation &allocation)
 {
     // Moves are inserted after OsiPoint instructions. This function sets
     // any OsiPoint uses of this interval to the allocation of the value
@@ -344,17 +344,17 @@ LinearScanAllocator::reifyAllocations()
             continue;
 
         UsePositionIterator usePos(interval->usesBegin());
         for (; usePos != interval->usesEnd(); usePos++) {
             if (usePos->use->isFixedRegister()) {
                 LiveInterval *to = fixedIntervals[GetFixedRegister(reg->def(), usePos->use).code()];
 
                 *static_cast<LAllocation *>(usePos->use) = *to->getAllocation();
-                if (!moveInput(usePos->pos, interval, to, reg->type()))
+                if (!moveInput(insData[usePos->pos]->toInstruction(), interval, to, reg->type()))
                     return false;
             } else {
                 MOZ_ASSERT(UseCompatibleWith(usePos->use, *interval->getAllocation()));
                 *static_cast<LAllocation *>(usePos->use) = *interval->getAllocation();
             }
         }
 
         // Erase the def of this interval if it's the first one
@@ -370,39 +370,39 @@ LinearScanAllocator::reifyAllocations()
             if (def->policy() == LDefinition::FIXED && def->output()->isRegister()) {
                 AnyRegister fixedReg = def->output()->toRegister();
                 LiveInterval *from = fixedIntervals[fixedReg.code()];
 
                 // If we insert the move after an OsiPoint that uses this vreg,
                 // it should use the fixed register instead.
                 SetOsiPointUses(interval, defEnd, LAllocation(fixedReg));
 
-                if (!moveAfter(defEnd, from, interval, def->type()))
+                if (!moveAfter(insData[defEnd]->toInstruction(), from, interval, def->type()))
                     return false;
                 spillFrom = from->getAllocation();
             } else {
                 if (def->policy() == LDefinition::MUST_REUSE_INPUT) {
                     LAllocation *inputAlloc = reg->ins()->getOperand(def->getReusedInput());
                     LAllocation *origAlloc = LAllocation::New(alloc(), *inputAlloc);
 
                     MOZ_ASSERT(!inputAlloc->isUse());
 
                     *inputAlloc = *interval->getAllocation();
-                    if (!moveInputAlloc(inputOf(reg->ins()), origAlloc, inputAlloc, def->type()))
+                    if (!moveInputAlloc(reg->ins()->toInstruction(), origAlloc, inputAlloc, def->type()))
                         return false;
                 }
 
                 MOZ_ASSERT(DefinitionCompatibleWith(reg->ins(), def, *interval->getAllocation()));
                 def->setOutput(*interval->getAllocation());
 
                 spillFrom = interval->getAllocation();
             }
 
             if (reg->ins()->recoversInput()) {
-                LSnapshot *snapshot = reg->ins()->snapshot();
+                LSnapshot *snapshot = reg->ins()->toInstruction()->snapshot();
                 for (size_t i = 0; i < snapshot->numEntries(); i++) {
                     LAllocation *entry = snapshot->getEntry(i);
                     if (entry->isUse() && entry->toUse()->policy() == LUse::RECOVERED_INPUT)
                         *entry = *def->output();
                 }
             }
 
             if (reg->mustSpillAtDefinition() && !reg->ins()->isPhi() &&
@@ -410,47 +410,47 @@ LinearScanAllocator::reifyAllocations()
             {
                 // If we move the spill after an OsiPoint, the OsiPoint should
                 // use the original location instead.
                 SetOsiPointUses(interval, defEnd, *spillFrom);
 
                 // Insert a spill after this instruction (or after any OsiPoint
                 // or Nop instructions). Note that we explicitly ignore phis,
                 // which should have been handled in resolveControlFlow().
-                LMoveGroup *moves = getMoveGroupAfter(defEnd);
+                LMoveGroup *moves = getMoveGroupAfter(insData[defEnd]->toInstruction());
                 if (!moves->add(spillFrom, reg->canonicalSpill(), def->type()))
                     return false;
             }
         }
-        else if (interval->start() > entryOf(insData[interval->start()].block()) &&
+        else if (interval->start() > entryOf(insData[interval->start()]->block()) &&
                  (!reg->canonicalSpill() ||
                   (reg->canonicalSpill() == interval->getAllocation() &&
                    !reg->mustSpillAtDefinition()) ||
                   *reg->canonicalSpill() != *interval->getAllocation()))
         {
             // If this virtual register has no canonical spill location, this
             // is the first spill to that location, or this is a move to somewhere
             // completely different, we have to emit a move for this interval.
             // Don't do this if the interval starts at the first instruction of the
             // block; this case should have been handled by resolveControlFlow().
             //
             // If the interval starts at the output half of an instruction, we have to
             // emit the move *after* this instruction, to prevent clobbering an input
             // register.
             LiveInterval *prevInterval = reg->getInterval(interval->index() - 1);
             CodePosition start = interval->start();
-            InstructionData *data = &insData[start];
+            LInstruction *ins = insData[start]->toInstruction();
 
-            MOZ_ASSERT(start == inputOf(data->ins()) || start == outputOf(data->ins()));
+            MOZ_ASSERT(start == inputOf(ins) || start == outputOf(ins));
 
             if (start.subpos() == CodePosition::INPUT) {
-                if (!moveInput(inputOf(data->ins()), prevInterval, interval, reg->type()))
+                if (!moveInput(ins, prevInterval, interval, reg->type()))
                     return false;
             } else {
-                if (!moveAfter(outputOf(data->ins()), prevInterval, interval, reg->type()))
+                if (!moveAfter(ins, prevInterval, interval, reg->type()))
                     return false;
             }
 
             // Mark this interval's spill position, if needed.
             if (reg->canonicalSpill() == interval->getAllocation() &&
                 !reg->mustSpillAtDefinition())
             {
                 reg->setSpillPosition(interval->start());
@@ -784,17 +784,17 @@ LinearScanAllocator::assign(LAllocation 
             // This interval is spilled more than once, so just always spill
             // it at its definition.
             reg->setSpillAtDefinition(outputOf(reg->ins()));
         } else {
             reg->setCanonicalSpill(current->getAllocation());
 
             // If this spill is inside a loop, and the definition is outside
             // the loop, instead move the spill to outside the loop.
-            InstructionData *other = &insData[current->start()];
+            LNode *other = insData[current->start()];
             uint32_t loopDepthAtDef = reg->block()->mir()->loopDepth();
             uint32_t loopDepthAtSpill = other->block()->mir()->loopDepth();
             if (loopDepthAtSpill > loopDepthAtDef)
                 reg->setSpillAtDefinition(outputOf(reg->ins()));
         }
     }
 
     active.pushBack(current);
--- a/js/src/jit/LinearScan.h
+++ b/js/src/jit/LinearScan.h
@@ -106,17 +106,17 @@ class LinearScanAllocator
     bool splitBlockingIntervals(AnyRegister allocatedReg);
     bool assign(LAllocation allocation);
     bool spill();
     void freeAllocation(LiveInterval *interval, LAllocation *alloc);
     void finishInterval(LiveInterval *interval);
     AnyRegister::Code findBestFreeRegister(CodePosition *freeUntil);
     AnyRegister::Code findBestBlockedRegister(CodePosition *nextUsed);
     bool canCoexist(LiveInterval *a, LiveInterval *b);
-    bool moveInputAlloc(CodePosition pos, LAllocation *from, LAllocation *to, LDefinition::Type type);
+    bool moveInputAlloc(LInstruction *ins, LAllocation *from, LAllocation *to, LDefinition::Type type);
     void setIntervalRequirement(LiveInterval *interval);
     bool isSpilledAt(LiveInterval *interval, CodePosition pos);
 
 #ifdef DEBUG
     void validateIntervals();
     void validateAllocations();
 #else
     inline void validateIntervals() { }
--- a/js/src/jit/LiveRangeAllocator.cpp
+++ b/js/src/jit/LiveRangeAllocator.cpp
@@ -522,32 +522,32 @@ LiveRangeAllocator<VREG, forLSRA>::init(
             return false;
 
         LBlock *block = graph.getBlock(i);
         for (LInstructionIterator ins = block->begin(); ins != block->end(); ins++) {
             for (size_t j = 0; j < ins->numDefs(); j++) {
                 LDefinition *def = ins->getDef(j);
                 if (def->isBogusTemp())
                     continue;
-                if (!vregs[def].init(alloc(), block, *ins, def, /* isTemp */ false))
+                if (!vregs[def].init(alloc(), *ins, def, /* isTemp */ false))
                     return false;
             }
 
             for (size_t j = 0; j < ins->numTemps(); j++) {
                 LDefinition *def = ins->getTemp(j);
                 if (def->isBogusTemp())
                     continue;
-                if (!vregs[def].init(alloc(), block, *ins, def, /* isTemp */ true))
+                if (!vregs[def].init(alloc(), *ins, def, /* isTemp */ true))
                     return false;
             }
         }
         for (size_t j = 0; j < block->numPhis(); j++) {
             LPhi *phi = block->getPhi(j);
             LDefinition *def = phi->getDef(0);
-            if (!vregs[def].init(alloc(), block, phi, def, /* isTemp */ false))
+            if (!vregs[def].init(alloc(), phi, def, /* isTemp */ false))
                 return false;
         }
     }
 
     return true;
 }
 
 static void
@@ -770,17 +770,17 @@ LiveRangeAllocator<VREG, forLSRA>::build
             DebugOnly<bool> hasUseRegister = false;
             DebugOnly<bool> hasUseRegisterAtStart = false;
 
             for (LInstruction::InputIterator inputAlloc(**ins); inputAlloc.more(); inputAlloc.next()) {
                 if (inputAlloc->isUse()) {
                     LUse *use = inputAlloc->toUse();
 
                     // The first instruction, LLabel, has no uses.
-                    MOZ_ASSERT_IF(forLSRA, inputOf(*ins) > outputOf(block->firstId()));
+                    MOZ_ASSERT_IF(forLSRA, inputOf(*ins) > outputOf(block->firstElementWithId()));
 
                     // Call uses should always be at-start or fixed, since the fixed intervals
                     // use all registers.
                     MOZ_ASSERT_IF(ins->isCall() && !inputAlloc.isSnapshotInput(),
                                   use->isFixedRegister() || use->usedAtStart());
 
 #ifdef DEBUG
                     // Don't allow at-start call uses if there are temps of the same kind,
@@ -862,21 +862,19 @@ LiveRangeAllocator<VREG, forLSRA>::build
         // contain any phi outputs.
         for (unsigned int i = 0; i < block->numPhis(); i++) {
             LDefinition *def = block->getPhi(i)->getDef(0);
             if (live->contains(def->virtualRegister())) {
                 live->remove(def->virtualRegister());
             } else {
                 // This is a dead phi, so add a dummy range over all phis. This
                 // can go away if we have an earlier dead code elimination pass.
-                if (!vregs[def].getInterval(0)->addRangeAtHead(entryOf(block),
-                                                               outputOf(block->firstId())))
-                {
+                CodePosition entryPos = entryOf(block);
+                if (!vregs[def].getInterval(0)->addRangeAtHead(entryPos, entryPos.next()))
                     return false;
-                }
             }
         }
 
         if (mblock->isLoopHeader()) {
             // A divergence from the published algorithm is required here, as
             // our block order does not guarantee that blocks of a loop are
             // contiguous. As a result, a single live interval spanning the
             // loop is not possible. Additionally, we require liveIn in a later
--- a/js/src/jit/LiveRangeAllocator.h
+++ b/js/src/jit/LiveRangeAllocator.h
@@ -161,17 +161,17 @@ UseCompatibleWith(const LUse *use, LAllo
       default:
         MOZ_CRASH("Unknown use policy");
     }
 }
 
 #ifdef DEBUG
 
 static inline bool
-DefinitionCompatibleWith(LInstruction *ins, const LDefinition *def, LAllocation alloc)
+DefinitionCompatibleWith(LNode *ins, const LDefinition *def, LAllocation alloc)
 {
     if (ins->isPhi()) {
         if (def->isFloatReg())
             return alloc.isFloatReg() || alloc.isStackSlot();
         return alloc.isGeneralReg() || alloc.isStackSlot();
     }
 
     switch (def->policy()) {
@@ -188,17 +188,17 @@ DefinitionCompatibleWith(LInstruction *i
       default:
         MOZ_CRASH("Unknown definition policy");
     }
 }
 
 #endif // DEBUG
 
 static inline LDefinition *
-FindReusingDefinition(LInstruction *ins, LAllocation *alloc)
+FindReusingDefinition(LNode *ins, LAllocation *alloc)
 {
     for (size_t i = 0; i < ins->numDefs(); i++) {
         LDefinition *def = ins->getDef(i);
         if (def->policy() == LDefinition::MUST_REUSE_INPUT &&
             ins->getOperand(def->getReusedInput()) == alloc)
             return def;
     }
     for (size_t i = 0; i < ins->numTemps(); i++) {
@@ -421,50 +421,48 @@ class LiveInterval
 
 /*
  * Represents all of the register allocation state associated with a virtual
  * register, including all associated intervals and pointers to relevant LIR
  * structures.
  */
 class VirtualRegister
 {
-    LBlock *block_;
-    LInstruction *ins_;
+    LNode *ins_;
     LDefinition *def_;
     Vector<LiveInterval *, 1, IonAllocPolicy> intervals_;
 
     // Whether def_ is a temp or an output.
     bool isTemp_ : 1;
 
     void operator=(const VirtualRegister &) MOZ_DELETE;
     VirtualRegister(const VirtualRegister &) MOZ_DELETE;
 
   protected:
     explicit VirtualRegister(TempAllocator &alloc)
       : intervals_(alloc)
     {}
 
   public:
-    bool init(TempAllocator &alloc, LBlock *block, LInstruction *ins, LDefinition *def,
+    bool init(TempAllocator &alloc, LNode *ins, LDefinition *def,
               bool isTemp)
     {
-        MOZ_ASSERT(block && !block_);
-        block_ = block;
+        MOZ_ASSERT(ins && !ins_);
         ins_ = ins;
         def_ = def;
         isTemp_ = isTemp;
         LiveInterval *initial = LiveInterval::New(alloc, def->virtualRegister(), 0);
         if (!initial)
             return false;
         return intervals_.append(initial);
     }
     LBlock *block() {
-        return block_;
+        return ins_->block();
     }
-    LInstruction *ins() {
+    LNode *ins() {
         return ins_;
     }
     LDefinition *def() const {
         return def_;
     }
     LDefinition::Type type() const {
         return def()->type();
     }
@@ -670,27 +668,27 @@ class LiveRangeAllocator : protected Reg
     }
 #endif
 
     bool addMove(LMoveGroup *moves, LiveInterval *from, LiveInterval *to, LDefinition::Type type) {
         MOZ_ASSERT(*from->getAllocation() != *to->getAllocation());
         return moves->add(from->getAllocation(), to->getAllocation(), type);
     }
 
-    bool moveInput(CodePosition pos, LiveInterval *from, LiveInterval *to, LDefinition::Type type) {
+    bool moveInput(LInstruction *ins, LiveInterval *from, LiveInterval *to, LDefinition::Type type) {
         if (*from->getAllocation() == *to->getAllocation())
             return true;
-        LMoveGroup *moves = getInputMoveGroup(pos);
+        LMoveGroup *moves = getInputMoveGroup(ins);
         return addMove(moves, from, to, type);
     }
 
-    bool moveAfter(CodePosition pos, LiveInterval *from, LiveInterval *to, LDefinition::Type type) {
+    bool moveAfter(LInstruction *ins, LiveInterval *from, LiveInterval *to, LDefinition::Type type) {
         if (*from->getAllocation() == *to->getAllocation())
             return true;
-        LMoveGroup *moves = getMoveGroupAfter(pos);
+        LMoveGroup *moves = getMoveGroupAfter(ins);
         return addMove(moves, from, to, type);
     }
 
     bool moveAtExit(LBlock *block, LiveInterval *from, LiveInterval *to, LDefinition::Type type) {
         if (*from->getAllocation() == *to->getAllocation())
             return true;
         LMoveGroup *moves = block->getExitMoveGroup(alloc());
         return addMove(moves, from, to, type);
@@ -723,18 +721,20 @@ class LiveRangeAllocator : protected Reg
 
         // Don't add output registers to the safepoint.
         CodePosition start = interval->start();
         if (interval->index() == 0 && !reg->isTemp()) {
 #ifdef CHECK_OSIPOINT_REGISTERS
             // We don't add the output register to the safepoint,
             // but it still might get added as one of the inputs.
             // So eagerly add this reg to the safepoint clobbered registers.
-            if (LSafepoint *safepoint = reg->ins()->safepoint())
-                safepoint->addClobberedRegister(a->toRegister());
+            if (reg->ins()->isInstruction()) {
+                if (LSafepoint *safepoint = reg->ins()->toInstruction()->safepoint())
+                    safepoint->addClobberedRegister(a->toRegister());
+            }
 #endif
             start = start.next();
         }
 
         size_t i = findFirstNonCallSafepoint(start);
         for (; i < graph.numNonCallSafepoints(); i++) {
             LInstruction *ins = graph.getNonCallSafepoint(i);
             CodePosition pos = inputOf(ins);
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -4076,21 +4076,18 @@ LIRGenerator::visitBlock(MBasicBlock *bl
 bool
 LIRGenerator::generate()
 {
     // Create all blocks and prep all phis beforehand.
     for (ReversePostorderIterator block(graph.rpoBegin()); block != graph.rpoEnd(); block++) {
         if (gen->shouldCancel("Lowering (preparation loop)"))
             return false;
 
-        current = LBlock::New(alloc(), *block);
-        if (!current)
+        if (!lirGraph_.initBlock(*block))
             return false;
-        lirGraph_.setBlock(block->id(), current);
-        block->assignLir(current);
     }
 
     for (ReversePostorderIterator block(graph.rpoBegin()); block != graph.rpoEnd(); block++) {
         if (gen->shouldCancel("Lowering (main loop)"))
             return false;
 
         if (!visitBlock(*block))
             return false;
--- a/js/src/jit/RegisterAllocator.cpp
+++ b/js/src/jit/RegisterAllocator.cpp
@@ -469,59 +469,54 @@ bool
 RegisterAllocator::init()
 {
     if (!insData.init(mir, graph.numInstructions()))
         return false;
 
     for (size_t i = 0; i < graph.numBlocks(); i++) {
         LBlock *block = graph.getBlock(i);
         for (LInstructionIterator ins = block->begin(); ins != block->end(); ins++)
-            insData[*ins].init(*ins, block);
+            insData[ins->id()] = *ins;
         for (size_t j = 0; j < block->numPhis(); j++) {
             LPhi *phi = block->getPhi(j);
-            insData[phi].init(phi, block);
+            insData[phi->id()] = phi;
         }
     }
 
     return true;
 }
 
 LMoveGroup *
-RegisterAllocator::getInputMoveGroup(uint32_t ins)
+RegisterAllocator::getInputMoveGroup(LInstruction *ins)
 {
-    InstructionData *data = &insData[ins];
-    MOZ_ASSERT(!data->ins()->isPhi());
-    MOZ_ASSERT(!data->ins()->isLabel());
+    MOZ_ASSERT(!ins->isLabel());
 
-    if (data->inputMoves())
-        return data->inputMoves();
+    if (ins->inputMoves())
+        return ins->inputMoves();
 
     LMoveGroup *moves = LMoveGroup::New(alloc());
-    data->setInputMoves(moves);
-    data->block()->insertBefore(data->ins(), moves);
+    ins->setInputMoves(moves);
+    ins->block()->insertBefore(ins, moves);
 
     return moves;
 }
 
 LMoveGroup *
-RegisterAllocator::getMoveGroupAfter(uint32_t ins)
+RegisterAllocator::getMoveGroupAfter(LInstruction *ins)
 {
-    InstructionData *data = &insData[ins];
-    MOZ_ASSERT(!data->ins()->isPhi());
-
-    if (data->movesAfter())
-        return data->movesAfter();
+    if (ins->movesAfter())
+        return ins->movesAfter();
 
     LMoveGroup *moves = LMoveGroup::New(alloc());
-    data->setMovesAfter(moves);
+    ins->setMovesAfter(moves);
 
-    if (data->ins()->isLabel())
-        data->block()->insertAfter(data->block()->getEntryMoveGroup(alloc()), moves);
+    if (ins->isLabel())
+        ins->block()->insertAfter(ins->block()->getEntryMoveGroup(alloc()), moves);
     else
-        data->block()->insertAfter(data->ins(), moves);
+        ins->block()->insertAfter(ins, moves);
     return moves;
 }
 
 void
 RegisterAllocator::dumpInstructions()
 {
 #ifdef DEBUG
     fprintf(stderr, "Instructions:\n");
--- a/js/src/jit/RegisterAllocator.h
+++ b/js/src/jit/RegisterAllocator.h
@@ -26,33 +26,33 @@ class LIRGenerator;
 //
 // - Check the integrity of the allocation, i.e. that the reads and writes of
 //   physical values preserve the semantics of the original virtual registers.
 //
 // - Populate safepoints with live registers, GC thing and value data, to
 //   streamline the process of prototyping new allocators.
 struct AllocationIntegrityState
 {
-    explicit AllocationIntegrityState(const LIRGraph &graph)
+    explicit AllocationIntegrityState(LIRGraph &graph)
       : graph(graph)
     {}
 
     // Record all virtual registers in the graph. This must be called before
     // register allocation, to pick up the original LUses.
     bool record();
 
     // Perform the liveness analysis on the graph, and assert on an invalid
     // allocation. This must be called after register allocation, to pick up
     // all assigned physical values. If populateSafepoints is specified,
     // safepoints will be filled in with liveness information.
     bool check(bool populateSafepoints);
 
   private:
 
-    const LIRGraph &graph;
+    LIRGraph &graph;
 
     // For all instructions and phis in the graph, keep track of the virtual
     // registers for all inputs and outputs of the nodes. These are overwritten
     // in place during register allocation. This information is kept on the
     // side rather than in the instructions and phis themselves to avoid
     // debug-builds-only bloat in the size of the involved structures.
 
     struct InstructionInfo {
@@ -212,84 +212,43 @@ class CodePosition
         return CodePosition(bits_ - 1);
     }
     CodePosition next() const {
         MOZ_ASSERT(*this != MAX);
         return CodePosition(bits_ + 1);
     }
 };
 
-// Structure to track moves inserted before or after an instruction.
-class InstructionData
-{
-    LInstruction *ins_;
-    LBlock *block_;
-    LMoveGroup *inputMoves_;
-    LMoveGroup *movesAfter_;
-
-  public:
-    void init(LInstruction *ins, LBlock *block) {
-        MOZ_ASSERT(!ins_);
-        MOZ_ASSERT(!block_);
-        ins_ = ins;
-        block_ = block;
-    }
-    LInstruction *ins() const {
-        return ins_;
-    }
-    LBlock *block() const {
-        return block_;
-    }
-    void setInputMoves(LMoveGroup *moves) {
-        inputMoves_ = moves;
-    }
-    LMoveGroup *inputMoves() const {
-        return inputMoves_;
-    }
-    void setMovesAfter(LMoveGroup *moves) {
-        movesAfter_ = moves;
-    }
-    LMoveGroup *movesAfter() const {
-        return movesAfter_;
-    }
-};
-
 // Structure to track all moves inserted next to instructions in a graph.
 class InstructionDataMap
 {
-    FixedList<InstructionData> insData_;
+    FixedList<LNode *> insData_;
 
   public:
     InstructionDataMap()
       : insData_()
     { }
 
     bool init(MIRGenerator *gen, uint32_t numInstructions) {
         if (!insData_.init(gen->alloc(), numInstructions))
             return false;
-        memset(&insData_[0], 0, sizeof(InstructionData) * numInstructions);
+        memset(&insData_[0], 0, sizeof(LNode *) * numInstructions);
         return true;
     }
 
-    InstructionData &operator[](CodePosition pos) {
-        return operator[](pos.ins());
-    }
-    const InstructionData &operator[](CodePosition pos) const {
+    LNode *&operator[](CodePosition pos) {
         return operator[](pos.ins());
     }
-    InstructionData &operator[](LInstruction *ins) {
-        return operator[](ins->id());
+    LNode *const &operator[](CodePosition pos) const {
+        return operator[](pos.ins());
     }
-    const InstructionData &operator[](LInstruction *ins) const {
-        return operator[](ins->id());
-    }
-    InstructionData &operator[](uint32_t ins) {
+    LNode *&operator[](uint32_t ins) {
         return insData_[ins];
     }
-    const InstructionData &operator[](uint32_t ins) const {
+    LNode *const &operator[](uint32_t ins) const {
         return insData_[ins];
     }
 };
 
 // Common superclass for register allocators.
 class RegisterAllocator
 {
     void operator=(const RegisterAllocator &) MOZ_DELETE;
@@ -327,66 +286,64 @@ class RegisterAllocator
     }
 
     bool init();
 
     TempAllocator &alloc() const {
         return mir->alloc();
     }
 
-    CodePosition outputOf(uint32_t pos) const {
+    CodePosition outputOf(const LNode *ins) const {
+        return ins->isPhi()
+               ? outputOf(ins->toPhi())
+               : outputOf(ins->toInstruction());
+    }
+    CodePosition outputOf(const LPhi *ins) const {
         // All phis in a block write their outputs after all of them have
         // read their inputs. Consequently, it doesn't make sense to talk
         // about code positions in the middle of a series of phis.
-        if (insData[pos].ins()->isPhi()) {
-            while (insData[pos + 1].ins()->isPhi())
-                ++pos;
-        }
-        return CodePosition(pos, CodePosition::OUTPUT);
+        LBlock *block = ins->block();
+        return CodePosition(block->getPhi(block->numPhis() - 1)->id(), CodePosition::OUTPUT);
     }
     CodePosition outputOf(const LInstruction *ins) const {
-        return outputOf(ins->id());
+        return CodePosition(ins->id(), CodePosition::OUTPUT);
     }
-    CodePosition inputOf(uint32_t pos) const {
+    CodePosition inputOf(const LNode *ins) const {
+        return ins->isPhi()
+               ? inputOf(ins->toPhi())
+               : inputOf(ins->toInstruction());
+    }
+    CodePosition inputOf(const LPhi *ins) const {
         // All phis in a block read their inputs before any of them write their
         // outputs. Consequently, it doesn't make sense to talk about code
         // positions in the middle of a series of phis.
-        if (insData[pos].ins()->isPhi()) {
-            while (pos > 0 && insData[pos - 1].ins()->isPhi())
-                --pos;
-        }
-        return CodePosition(pos, CodePosition::INPUT);
+        return CodePosition(ins->block()->getPhi(0)->id(), CodePosition::INPUT);
     }
     CodePosition inputOf(const LInstruction *ins) const {
-        return inputOf(ins->id());
+        return CodePosition(ins->id(), CodePosition::INPUT);
     }
     CodePosition entryOf(const LBlock *block) {
-        return inputOf(block->firstId());
+        return block->numPhis() != 0
+               ? CodePosition(block->getPhi(0)->id(), CodePosition::INPUT)
+               : inputOf(block->firstInstructionWithId());
     }
     CodePosition exitOf(const LBlock *block) {
-        return outputOf(block->lastId());
+        return outputOf(block->lastInstructionWithId());
     }
 
-    LMoveGroup *getInputMoveGroup(uint32_t ins);
-    LMoveGroup *getMoveGroupAfter(uint32_t ins);
+    LMoveGroup *getInputMoveGroup(LInstruction *ins);
+    LMoveGroup *getMoveGroupAfter(LInstruction *ins);
 
-    LMoveGroup *getInputMoveGroup(CodePosition pos) {
-        return getInputMoveGroup(pos.ins());
-    }
-    LMoveGroup *getMoveGroupAfter(CodePosition pos) {
-        return getMoveGroupAfter(pos.ins());
-    }
-
-    CodePosition minimalDefEnd(LInstruction *ins) {
+    CodePosition minimalDefEnd(LNode *ins) {
         // Compute the shortest interval that captures vregs defined by ins.
         // Watch for instructions that are followed by an OSI point and/or Nop.
         // If moves are introduced between the instruction and the OSI point then
         // safepoint information for the instruction may be incorrect.
         while (true) {
-            LInstruction *next = insData[outputOf(ins).next()].ins();
+            LNode *next = insData[ins->id() + 1];
             if (!next->isNop() && !next->isOsiPoint())
                 break;
             ins = next;
         }
 
         return outputOf(ins);
     }
 
--- a/js/src/jit/StupidAllocator.cpp
+++ b/js/src/jit/StupidAllocator.cpp
@@ -177,17 +177,17 @@ StupidAllocator::allocateRegister(LInstr
     evictAliasedRegister(ins, best);
     return best;
 }
 
 void
 StupidAllocator::syncRegister(LInstruction *ins, RegisterIndex index)
 {
     if (registers[index].dirty) {
-        LMoveGroup *input = getInputMoveGroup(ins->id());
+        LMoveGroup *input = getInputMoveGroup(ins);
         LAllocation *source = new(alloc()) LAllocation(registers[index].reg);
 
         uint32_t existing = registers[index].vreg;
         LAllocation *dest = stackLocation(existing);
         input->addAfter(source, dest, registers[index].type);
 
         registers[index].dirty = false;
     }
@@ -209,17 +209,17 @@ StupidAllocator::evictAliasedRegister(LI
         registers[aindex].set(MISSING_ALLOCATION);
     }
 }
 
 void
 StupidAllocator::loadRegister(LInstruction *ins, uint32_t vreg, RegisterIndex index, LDefinition::Type type)
 {
     // Load a vreg from its stack location to a register.
-    LMoveGroup *input = getInputMoveGroup(ins->id());
+    LMoveGroup *input = getInputMoveGroup(ins);
     LAllocation *source = stackLocation(vreg);
     LAllocation *dest = new(alloc()) LAllocation(registers[index].reg);
     input->addAfter(source, dest, type);
     registers[index].set(vreg, ins);
     registers[index].type = type;
 }
 
 StupidAllocator::RegisterIndex
@@ -304,17 +304,17 @@ StupidAllocator::syncForBlockEnd(LBlock 
                 continue;
 
             LAllocation *source = stackLocation(sourcevreg);
             LAllocation *dest = stackLocation(destvreg);
 
             if (!group) {
                 // The moves we insert here need to happen simultaneously with
                 // each other, yet after any existing moves before the instruction.
-                LMoveGroup *input = getInputMoveGroup(ins->id());
+                LMoveGroup *input = getInputMoveGroup(ins);
                 if (input->numMoves() == 0) {
                     group = input;
                 } else {
                     group = LMoveGroup::New(alloc());
                     block->insertAfter(input, group);
                 }
             }
 
--- a/js/src/jit/shared/CodeGenerator-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-shared.cpp
@@ -441,17 +441,17 @@ CodeGeneratorShared::encode(LSnapshot *s
 
 #ifdef TRACK_SNAPSHOTS
     uint32_t pcOpcode = 0;
     uint32_t lirOpcode = 0;
     uint32_t lirId = 0;
     uint32_t mirOpcode = 0;
     uint32_t mirId = 0;
 
-    if (LInstruction *ins = instruction()) {
+    if (LNode *ins = instruction()) {
         lirOpcode = ins->op();
         lirId = ins->id();
         if (ins->mirRaw()) {
             mirOpcode = ins->mirRaw()->op();
             mirId = ins->mirRaw()->id();
             if (ins->mirRaw()->trackedPc())
                 pcOpcode = *ins->mirRaw()->trackedPc();
         }
--- a/js/src/jit/shared/CodeGenerator-shared.h
+++ b/js/src/jit/shared/CodeGenerator-shared.h
@@ -43,17 +43,17 @@ struct PatchableBackedgeInfo
     {}
 };
 
 struct ReciprocalMulConstants {
     int32_t multiplier;
     int32_t shiftAmount;
 };
 
-class CodeGeneratorShared : public LInstructionVisitor
+class CodeGeneratorShared : public LElementVisitor
 {
     js::Vector<OutOfLineCode *, 0, SystemAllocPolicy> outOfLineCode_;
     OutOfLineCode *oolIns;
 
     MacroAssembler &ensureMasm(MacroAssembler *masm);
     mozilla::Maybe<MacroAssembler> maybeMasm_;
 
   public:
--- a/layout/svg/SVGTextFrame.cpp
+++ b/layout/svg/SVGTextFrame.cpp
@@ -2704,17 +2704,16 @@ public:
    * @param aShouldPaintSVGGlyphs Whether SVG glyphs should be painted.
    */
   SVGTextDrawPathCallbacks(nsRenderingContext* aContext,
                            nsTextFrame* aFrame,
                            const gfxMatrix& aCanvasTM,
                            bool aShouldPaintSVGGlyphs)
     : DrawPathCallbacks(aShouldPaintSVGGlyphs),
       gfx(aContext->ThebesContext()),
-      mRenderMode(SVGAutoRenderState::GetRenderMode(aContext->GetDrawTarget())),
       mFrame(aFrame),
       mCanvasTM(aCanvasTM)
   {
   }
 
   void NotifyBeforeText(nscolor aColor) MOZ_OVERRIDE;
   void NotifyGlyphPathEmitted() MOZ_OVERRIDE;
   void NotifyBeforeSVGGlyphPainted() MOZ_OVERRIDE;
@@ -2725,16 +2724,23 @@ public:
   void NotifyBeforeDecorationLine(nscolor aColor) MOZ_OVERRIDE;
   void NotifyDecorationLinePathEmitted() MOZ_OVERRIDE;
   void NotifyBeforeSelectionDecorationLine(nscolor aColor) MOZ_OVERRIDE;
   void NotifySelectionDecorationLinePathEmitted() MOZ_OVERRIDE;
 
 private:
   void SetupContext();
 
+  bool IsClipPathChild() const {
+    // parent is the CSS text frame, grand parent must be
+    // an SVG frame of some kind
+    return mFrame->GetParent()->GetParent()->GetStateBits() &
+             NS_STATE_SVG_CLIPPATH_CHILD;
+  }
+
   /**
    * Paints a piece of text geometry.  This is called when glyphs
    * or text decorations have been emitted to the gfxContext.
    */
   void HandleTextGeometry();
 
   /**
    * Sets the gfxContext paint to the appropriate color or pattern
@@ -2754,17 +2760,16 @@ private:
   void FillGeometry();
 
   /**
    * Strokes a piece of text geometry.
    */
   void StrokeGeometry();
 
   gfxContext* gfx;
-  uint16_t mRenderMode;
   nsTextFrame* mFrame;
   const gfxMatrix& mCanvasTM;
 
   /**
    * The color that we were last told from one of the path callback functions.
    * This color can be the special NS_SAME_AS_FOREGROUND_COLOR,
    * NS_40PERCENT_FOREGROUND_COLOR and NS_TRANSPARENT colors when we are
    * painting selections or IME decorations.
@@ -2804,29 +2809,29 @@ void
 SVGTextDrawPathCallbacks::NotifyAfterText()
 {
   gfx->Restore();
 }
 
 void
 SVGTextDrawPathCallbacks::NotifyBeforeSelectionBackground(nscolor aColor)
 {
-  if (mRenderMode != SVGAutoRenderState::NORMAL) {
+  if (IsClipPathChild()) {
     // Don't paint selection backgrounds when in a clip path.
     return;
   }
 
   mColor = aColor;
   gfx->Save();
 }
 
 void
 SVGTextDrawPathCallbacks::NotifySelectionBackgroundPathEmitted()
 {
-  if (mRenderMode != SVGAutoRenderState::NORMAL) {
+  if (IsClipPathChild()) {
     // Don't paint selection backgrounds when in a clip path.
     return;
   }
 
   GeneralPattern fillPattern;
   MakeFillPattern(&fillPattern);
   if (fillPattern.GetPattern()) {
     gfx->SetFillRule(nsSVGUtils::ToFillRule(mFrame->StyleSVG()->mFillRule));
@@ -2849,29 +2854,29 @@ SVGTextDrawPathCallbacks::NotifyDecorati
   HandleTextGeometry();
   gfx->NewPath();
   gfx->Restore();
 }
 
 void
 SVGTextDrawPathCallbacks::NotifyBeforeSelectionDecorationLine(nscolor aColor)
 {
-  if (mRenderMode != SVGAutoRenderState::NORMAL) {
+  if (IsClipPathChild()) {
     // Don't paint selection decorations when in a clip path.
     return;
   }
 
   mColor = aColor;
   gfx->Save();
 }
 
 void
 SVGTextDrawPathCallbacks::NotifySelectionDecorationLinePathEmitted()
 {
-  if (mRenderMode != SVGAutoRenderState::NORMAL) {
+  if (IsClipPathChild()) {
     // Don't paint selection decorations when in a clip path.
     return;
   }
 
   FillAndStrokeGeometry();
   gfx->Restore();
 }
 
@@ -2891,17 +2896,17 @@ SVGTextDrawPathCallbacks::SetupContext()
     gfx->SetAntialiasMode(AntialiasMode::SUBPIXEL);
     break;
   }
 }
 
 void
 SVGTextDrawPathCallbacks::HandleTextGeometry()
 {
-  if (mRenderMode == SVGAutoRenderState::CLIP_MASK) {
+  if (IsClipPathChild()) {
     gfx->SetColor(gfxRGBA(1.0f, 1.0f, 1.0f, 1.0f));
     gfx->Fill();
   } else {
     // Normal painting.
     gfxContextMatrixAutoSaveRestore saveMatrix(gfx);
     gfx->SetMatrix(mCanvasTM);
 
     FillAndStrokeGeometry();
@@ -2960,17 +2965,20 @@ SVGTextDrawPathCallbacks::FillAndStrokeG
 }
 
 void
 SVGTextDrawPathCallbacks::FillGeometry()
 {
   GeneralPattern fillPattern;
   MakeFillPattern(&fillPattern);
   if (fillPattern.GetPattern()) {
-    gfx->SetFillRule(nsSVGUtils::ToFillRule(mFrame->StyleSVG()->mFillRule));
+    gfx->SetFillRule(
+      nsSVGUtils::ToFillRule(
+        IsClipPathChild() ?
+          mFrame->StyleSVG()->mClipRule : mFrame->StyleSVG()->mFillRule));
     gfx->Fill(fillPattern);
   }
 }
 
 void
 SVGTextDrawPathCallbacks::StrokeGeometry()
 {
   // We don't paint the stroke when we are filling with a selection color.
@@ -5112,18 +5120,17 @@ SVGTextFrame::DoGlyphPositioning()
 }
 
 bool
 SVGTextFrame::ShouldRenderAsPath(nsRenderingContext* aContext,
                                  nsTextFrame* aFrame,
                                  bool& aShouldPaintSVGGlyphs)
 {
   // Rendering to a clip path.
-  if (SVGAutoRenderState::GetRenderMode(aContext->GetDrawTarget()) !=
-        SVGAutoRenderState::NORMAL) {
+  if (aFrame->GetParent()->GetParent()->GetStateBits() & NS_STATE_SVG_CLIPPATH_CHILD) {
     aShouldPaintSVGGlyphs = false;
     return true;
   }
 
   aShouldPaintSVGGlyphs = true;
 
   const nsStyleSVG* style = aFrame->StyleSVG();
 
--- a/layout/svg/nsSVGClipPathFrame.cpp
+++ b/layout/svg/nsSVGClipPathFrame.cpp
@@ -84,21 +84,20 @@ nsSVGClipPathFrame::ApplyClipOrPaintClip
       // clipping path otherwise can't be resolved:
       gfx->Rectangle(gfxRect());
     }
     gfx->Clip();
     gfx->NewPath();
     return NS_OK;
   }
 
-  // Seems like this is a non-trivial clipPath, so we need to use a clip mask.
-
-  // Notify our children that they're painting into a clip mask:
-  SVGAutoRenderState mode(aContext->GetDrawTarget(),
-                          SVGAutoRenderState::CLIP_MASK);
+  // This is a non-trivial clipPath, so we need to paint its contents into a
+  // temporary surface and use that to mask the clipped content.  Note that
+  // nsSVGPathGeometryFrame::Render checks for the NS_STATE_SVG_CLIPPATH_CHILD
+  // state bit and paints into our mask surface using opaque black in that case.
 
   // Check if this clipPath is itself clipped by another clipPath:
   nsSVGClipPathFrame *clipPathFrame =
     nsSVGEffects::GetEffectProperties(this).GetClipPathFrame(nullptr);
   bool referencedClipIsTrivial;
   if (clipPathFrame) {
     referencedClipIsTrivial = clipPathFrame->IsTrivial();
     gfx->Save();
--- a/layout/svg/nsSVGOuterSVGFrame.cpp
+++ b/layout/svg/nsSVGOuterSVGFrame.cpp
@@ -583,20 +583,18 @@ nsDisplayOuterSVG::HitTest(nsDisplayList
 void
 nsDisplayOuterSVG::Paint(nsDisplayListBuilder* aBuilder,
                          nsRenderingContext* aContext)
 {
 #if defined(DEBUG) && defined(SVG_DEBUG_PAINT_TIMING)
   PRTime start = PR_Now();
 #endif
 
-  // Create an SVGAutoRenderState so we can call SetPaintingToWindow on
-  // it, but do so without changing the render mode:
-  SVGAutoRenderState state(aContext->GetDrawTarget(),
-    SVGAutoRenderState::GetRenderMode(aContext->GetDrawTarget()));
+  // Create an SVGAutoRenderState so we can call SetPaintingToWindow on it.
+  SVGAutoRenderState state(aContext->GetDrawTarget());
 
   if (aBuilder->IsPaintingToWindow()) {
     state.SetPaintingToWindow(true);
   }
 
   nsRect viewportRect =
     mFrame->GetContentRectRelativeToSelf() + ToReferenceFrame();
 
--- a/layout/svg/nsSVGPathGeometryFrame.cpp
+++ b/layout/svg/nsSVGPathGeometryFrame.cpp
@@ -283,32 +283,30 @@ nsSVGPathGeometryFrame::PaintSVG(nsRende
 
 nsIFrame*
 nsSVGPathGeometryFrame::GetFrameForPoint(const gfxPoint& aPoint)
 {
   FillRule fillRule;
   uint16_t hitTestFlags;
   if (GetStateBits() & NS_STATE_SVG_CLIPPATH_CHILD) {
     hitTestFlags = SVG_HIT_TEST_FILL;
-    fillRule = StyleSVG()->mClipRule == NS_STYLE_FILL_RULE_NONZERO
-                 ? FillRule::FILL_WINDING : FillRule::FILL_EVEN_ODD;
+    fillRule = nsSVGUtils::ToFillRule(StyleSVG()->mClipRule);
   } else {
     hitTestFlags = GetHitTestFlags();
     if (!hitTestFlags) {
       return nullptr;
     }
     if (hitTestFlags & SVG_HIT_TEST_CHECK_MRECT) {
       gfxRect rect =
         nsLayoutUtils::RectToGfxRect(mRect, PresContext()->AppUnitsPerCSSPixel());
       if (!rect.Contains(aPoint)) {
         return nullptr;
       }
     }
-    fillRule = StyleSVG()->mFillRule == NS_STYLE_FILL_RULE_NONZERO
-                 ? FillRule::FILL_WINDING : FillRule::FILL_EVEN_ODD;
+    fillRule = nsSVGUtils::ToFillRule(StyleSVG()->mFillRule);
   }
 
   bool isHit = false;
 
   nsSVGPathGeometryElement* content =
     static_cast<nsSVGPathGeometryElement*>(mContent);
 
   // Using ScreenReferenceDrawTarget() opens us to Moz2D backend specific hit-
@@ -474,18 +472,17 @@ nsSVGPathGeometryFrame::GetBBoxContribut
   nsRefPtr<gfxASurface> refSurf =
     gfxPlatform::GetPlatform()->ScreenReferenceSurface();
   tmpDT = gfxPlatform::GetPlatform()->
     CreateDrawTargetForSurface(refSurf, IntSize(1, 1));
 #else
   tmpDT = gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
 #endif
 
-  FillRule fillRule = StyleSVG()->mFillRule == NS_STYLE_FILL_RULE_NONZERO
-                        ? FillRule::FILL_WINDING : FillRule::FILL_EVEN_ODD;
+  FillRule fillRule = nsSVGUtils::ToFillRule(StyleSVG()->mFillRule);
   RefPtr<Path> pathInUserSpace = element->GetOrBuildPath(*tmpDT, fillRule);
   if (!pathInUserSpace) {
     return bbox;
   }
   RefPtr<Path> pathInBBoxSpace;
   if (aToBBoxUserspace.IsIdentity()) {
     pathInBBoxSpace = pathInUserSpace;
   } else {
@@ -675,24 +672,19 @@ void
 nsSVGPathGeometryFrame::Render(gfxContext* aContext,
                                uint32_t aRenderComponents,
                                const gfxMatrix& aNewTransform)
 {
   MOZ_ASSERT(!aNewTransform.IsSingular());
 
   DrawTarget* drawTarget = aContext->GetDrawTarget();
 
-  uint16_t renderMode = SVGAutoRenderState::GetRenderMode(drawTarget);
-  MOZ_ASSERT(renderMode == SVGAutoRenderState::NORMAL ||
-             renderMode == SVGAutoRenderState::CLIP_MASK,
-             "Unknown render mode");
-
   FillRule fillRule =
-    nsSVGUtils::ToFillRule(renderMode == SVGAutoRenderState::NORMAL ?
-                             StyleSVG()->mFillRule : StyleSVG()->mClipRule);
+    nsSVGUtils::ToFillRule((GetStateBits() & NS_STATE_SVG_CLIPPATH_CHILD) ?
+                             StyleSVG()->mClipRule : StyleSVG()->mFillRule);
 
   nsSVGPathGeometryElement* element =
     static_cast<nsSVGPathGeometryElement*>(mContent);
 
   RefPtr<Path> path = element->GetOrBuildPath(*drawTarget, fillRule);
   if (!path) {
     return;
   }
@@ -703,17 +695,17 @@ nsSVGPathGeometryFrame::Render(gfxContex
     AntialiasMode::NONE : AntialiasMode::SUBPIXEL;
 
   // We wait as late as possible before setting the transform so that we don't
   // set it unnecessarily if we return early (it's an expensive operation for
   // some backends).
   gfxContextMatrixAutoSaveRestore autoRestoreTransform(aContext);
   aContext->SetMatrix(aNewTransform);
 
-  if (renderMode == SVGAutoRenderState::CLIP_MASK) {
+  if (GetStateBits() & NS_STATE_SVG_CLIPPATH_CHILD) {
     drawTarget->Fill(path, ColorPattern(Color(1.0f, 1.0f, 1.0f, 1.0f)),
                      DrawOptions(1.0f, CompositionOp::OP_OVER, aaMode));
     return;
   }
 
   gfxTextContextPaint *contextPaint =
     (gfxTextContextPaint*)drawTarget->
       GetUserData(&gfxTextContextPaint::sUserDataKey);
--- a/layout/svg/nsSVGUtils.cpp
+++ b/layout/svg/nsSVGUtils.cpp
@@ -85,22 +85,20 @@ NS_SVGNewGetBBoxEnabled()
 {
   return sSVGNewGetBBoxEnabled;
 }
 
 
 // we only take the address of this:
 static mozilla::gfx::UserDataKey sSVGAutoRenderStateKey;
 
-SVGAutoRenderState::SVGAutoRenderState(DrawTarget* aDrawTarget,
-                                       RenderMode aMode
+SVGAutoRenderState::SVGAutoRenderState(DrawTarget* aDrawTarget
                                        MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
   : mDrawTarget(aDrawTarget)
   , mOriginalRenderState(nullptr)
-  , mMode(aMode)
   , mPaintingToWindow(false)
 {
   MOZ_GUARD_OBJECT_NOTIFIER_INIT;
   mOriginalRenderState =
     aDrawTarget->RemoveUserData(&sSVGAutoRenderStateKey);
   // We always remove ourselves from aContext before it dies, so
   // passing nullptr as the destroy function is okay.
   aDrawTarget->AddUserData(&sSVGAutoRenderStateKey, this, nullptr);
@@ -116,26 +114,16 @@ SVGAutoRenderState::~SVGAutoRenderState(
 }
 
 void
 SVGAutoRenderState::SetPaintingToWindow(bool aPaintingToWindow)
 {
   mPaintingToWindow = aPaintingToWindow;
 }
 
-/* static */ SVGAutoRenderState::RenderMode
-SVGAutoRenderState::GetRenderMode(DrawTarget* aDrawTarget)
-{
-  void *state = aDrawTarget->GetUserData(&sSVGAutoRenderStateKey);
-  if (state) {
-    return static_cast<SVGAutoRenderState*>(state)->mMode;
-  }
-  return NORMAL;
-}
-
 /* static */ bool
 SVGAutoRenderState::IsPaintingToWindow(DrawTarget* aDrawTarget)
 {
   void *state = aDrawTarget->GetUserData(&sSVGAutoRenderStateKey);
   if (state) {
     return static_cast<SVGAutoRenderState*>(state)->mPaintingToWindow;
   }
   return false;
--- a/layout/svg/nsSVGUtils.h
+++ b/layout/svg/nsSVGUtils.h
@@ -145,43 +145,27 @@ private:
 // GRRR WINDOWS HATE HATE HATE
 #undef CLIP_MASK
 
 class MOZ_STACK_CLASS SVGAutoRenderState
 {
   typedef mozilla::gfx::DrawTarget DrawTarget;
 
 public:
-  enum RenderMode {
-    /**
-     * Used to inform SVG frames that they should paint as normal.
-     */
-    NORMAL, 
-    /**
-     * Used to inform SVG frames when they are painting as the child of a
-     * complex clipPath that requires the use of a clip mask. In this case they
-     * should only draw their basic geometry as a path and then fill it using
-     * fully opaque white. They should not stroke, or paint anything else.
-     */
-    CLIP_MASK 
-  };
-
-  SVGAutoRenderState(DrawTarget* aDrawTarget, RenderMode aMode
-                     MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
+  explicit SVGAutoRenderState(DrawTarget* aDrawTarget
+                              MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
   ~SVGAutoRenderState();
 
   void SetPaintingToWindow(bool aPaintingToWindow);
 
-  static RenderMode GetRenderMode(DrawTarget* aDrawTarget);
   static bool IsPaintingToWindow(DrawTarget* aDrawTarget);
 
 private:
   DrawTarget* mDrawTarget;
   void* mOriginalRenderState;
-  RenderMode mMode;
   bool mPaintingToWindow;
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 
 #define NS_ISVGFILTERREFERENCE_IID \
 { 0x9744ee20, 0x1bcf, 0x4c62, \
  { 0x86, 0x7d, 0xd3, 0x7a, 0x91, 0x60, 0x3e, 0xef } }
--- a/mfbt/Attributes.h
+++ b/mfbt/Attributes.h
@@ -60,16 +60,25 @@
 #      error Please do not try to use clang-cl with MSVC10 or below emulation!
 #    endif
      /* MSVC <= 10 used to spell "final" as "sealed". */
 #    define MOZ_HAVE_CXX11_FINAL         sealed
 #  endif
 #  define MOZ_HAVE_CXX11_OVERRIDE
 #  define MOZ_HAVE_NEVER_INLINE          __declspec(noinline)
 #  define MOZ_HAVE_NORETURN              __declspec(noreturn)
+#  ifdef __clang__
+     /* clang-cl probably supports constexpr and explicit conversions. */
+#    if __has_extension(cxx_constexpr)
+#      define MOZ_HAVE_CXX11_CONSTEXPR
+#    endif
+#    if __has_extension(cxx_explicit_conversions)
+#      define MOZ_HAVE_EXPLICIT_CONVERSION
+#    endif
+#  endif
 #elif defined(__clang__)
    /*
     * Per Clang documentation, "Note that marketing version numbers should not
     * be used to check for language features, as different vendors use different
     * numbering schemes. Instead, use the feature checking macros."
     */
 #  ifndef __has_extension
 #    define __has_extension __has_feature /* compatibility, for older versions of clang */
--- a/mfbt/MemoryChecking.h
+++ b/mfbt/MemoryChecking.h
@@ -31,23 +31,31 @@
 #define MOZ_HAVE_MEM_CHECKS 1
 #endif
 
 #if defined(MOZ_ASAN)
 #include <stddef.h>
 
 #include "mozilla/Types.h"
 
+#ifdef _MSC_VER
+// In clang-cl based ASAN, we link against the memory poisoning functions
+// statically.
+#define MOZ_ASAN_VISIBILITY
+#else
+#define MOZ_ASAN_VISIBILITY MOZ_EXPORT
+#endif
+
 extern "C" {
 /* These definitions are usually provided through the
  * sanitizer/asan_interface.h header installed by ASan.
  */
-void MOZ_EXPORT
+void MOZ_ASAN_VISIBILITY
 __asan_poison_memory_region(void const volatile *addr, size_t size);
-void MOZ_EXPORT
+void MOZ_ASAN_VISIBILITY
 __asan_unpoison_memory_region(void const volatile *addr, size_t size);
 
 #define MOZ_MAKE_MEM_NOACCESS(addr, size) \
   __asan_poison_memory_region((addr), (size))
 
 #define MOZ_MAKE_MEM_UNDEFINED(addr, size) \
   __asan_unpoison_memory_region((addr), (size))
 
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1243,16 +1243,26 @@ pref("network.http.spdy.timeout", 180);
 pref("network.http.spdy.coalesce-hostnames", true);
 pref("network.http.spdy.persistent-settings", false);
 pref("network.http.spdy.ping-threshold", 58);
 pref("network.http.spdy.ping-timeout", 8);
 pref("network.http.spdy.send-buffer-size", 131072);
 pref("network.http.spdy.allow-push", true);
 pref("network.http.spdy.push-allowance", 131072);
 
+// alt-svc allows separation of transport routing from
+// the origin host without using a proxy.
+#ifdef RELEASE_BUILD
+pref("network.http.altsvc.enabled", false);
+pref("network.http.altsvc.oe", false);
+#else
+pref("network.http.altsvc.enabled", true);
+pref("network.http.altsvc.oe", true);
+#endif
+
 pref("network.http.diagnostics", false);
 
 pref("network.http.pacing.requests.enabled", true);
 pref("network.http.pacing.requests.min-parallelism", 6);
 pref("network.http.pacing.requests.hz", 100);
 pref("network.http.pacing.requests.burst", 32);
 
 // TCP Keepalive config for HTTP connections.
--- a/netwerk/base/public/nsISpeculativeConnect.idl
+++ b/netwerk/base/public/nsISpeculativeConnect.idl
@@ -30,17 +30,17 @@ interface nsISpeculativeConnect : nsISup
 
 };
 
 /**
  * This is used to override the default values for various values (documented
  * inline) to determine whether or not to actually make a speculative
  * connection.
  */
-[builtinclass, uuid(a9cdd875-2ef8-4d53-95d6-e4e18f65e0db)]
+[builtinclass, uuid(f6a0d1e5-369f-4abc-81ae-d370d36e4006)]
 interface nsISpeculativeConnectionOverrider : nsISupports
 {
     /**
      * Used to determine the maximum number of unused speculative connections
      * we will have open for a host at any one time
      */
     [infallible] readonly attribute unsigned long parallelSpeculativeConnectLimit;
 
@@ -58,9 +58,14 @@ interface nsISpeculativeConnectionOverri
      */
     [infallible] readonly attribute boolean ignoreIdle;
 
     /*
      * Used by the Predictor to gather telemetry data on speculative connection
      * usage.
      */
     [infallible] readonly attribute boolean isFromPredictor;
+
+    /**
+     * by default speculative connections are not made to RFC 1918 addresses
+     */
+    [infallible] readonly attribute boolean allow1918;
 };
--- a/netwerk/base/src/Predictor.cpp
+++ b/netwerk/base/src/Predictor.cpp
@@ -390,16 +390,23 @@ Predictor::GetParallelSpeculativeConnect
 
 NS_IMETHODIMP
 Predictor::GetIsFromPredictor(bool *isFromPredictor)
 {
   *isFromPredictor = true;
   return NS_OK;
 }
 
+NS_IMETHODIMP
+Predictor::GetAllow1918(bool *allow1918)
+{
+  *allow1918 = false;
+  return NS_OK;
+}
+
 // Predictor::nsIInterfaceRequestor
 
 NS_IMETHODIMP
 Predictor::GetInterface(const nsIID &iid, void **result)
 {
   return QueryInterface(iid, result);
 }
 
--- a/netwerk/base/src/nsSocketTransport2.cpp
+++ b/netwerk/base/src/nsSocketTransport2.cpp
@@ -1230,17 +1230,19 @@ nsSocketTransport::InitiateSocket()
                 netAddrCString = NS_LITERAL_CSTRING("<IP-to-string failed>");
             SOCKET_LOG(("nsSocketTransport::InitiateSocket skipping "
                         "speculative connection for host [%s:%d] proxy "
                         "[%s:%d] with Local IP address [%s]",
                         mHost.get(), mPort, mProxyHost.get(), mProxyPort,
                         netAddrCString.get()));
         }
 #endif
-        return NS_ERROR_CONNECTION_REFUSED;
+        mCondition = NS_ERROR_CONNECTION_REFUSED;
+        OnSocketDetached(nullptr);
+        return mCondition;
     }
 
     //
     // find out if it is going to be ok to attach another socket to the STS.
     // if not then we have to wait for the STS to tell us that it is ok.
     // the notification is asynchronous, which means that when we could be
     // in a race to call AttachSocket once notified.  for this reason, when
     // we get notified, we just re-enter this function.  as a result, we are
--- a/netwerk/dns/nsHostResolver.cpp
+++ b/netwerk/dns/nsHostResolver.cpp
@@ -661,17 +661,17 @@ nsHostResolver::DnsExperimentChanged(con
     }
 
     auto self = static_cast<nsHostResolver*>(aClosure);
     MOZ_ASSERT(self);
 
     // We can't set a pref in the context of a pref change callback, so
     // dispatch DnsExperimentChangedInternal for async getting/setting.
     DebugOnly<nsresult> rv = NS_DispatchToMainThread(
-        NS_NewRunnableMethod(self, &DnsExperimentChangedInternal));
+        NS_NewRunnableMethod(self, &nsHostResolver::DnsExperimentChangedInternal));
     NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
                      "Could not dispatch DnsExperimentChanged event.");
 }
 
 void
 nsHostResolver::InitCRandom()
 {
     MOZ_ASSERT(NS_IsMainThread(), "Should be seeding rand() on main thread!");
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/AlternateServices.cpp
@@ -0,0 +1,452 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "HttpLog.h"
+
+#include "AlternateServices.h"
+#include "nsHttpConnectionInfo.h"
+#include "nsHttpHandler.h"
+#include "nsThreadUtils.h"
+#include "NullHttpTransaction.h"
+#include "nsISSLStatusProvider.h"
+#include "nsISSLStatus.h"
+#include "nsISSLSocketControl.h"
+
+namespace mozilla {
+namespace net {
+
+AltSvcMapping::AltSvcMapping(const nsACString &originScheme,
+                             const nsACString &originHost,
+                             int32_t originPort,
+                             const nsACString &username,
+                             bool privateBrowsing,
+                             uint32_t expiresAt,
+                             const nsACString &alternateHost,
+                             int32_t alternatePort,
+                             const nsACString &npnToken)
+  : mAlternateHost(alternateHost)
+  , mAlternatePort(alternatePort)
+  , mOriginHost(originHost)
+  , mOriginPort(originPort)
+  , mUsername(username)
+  , mPrivate(privateBrowsing)
+  , mExpiresAt(expiresAt)
+  , mValidated(false)
+  , mRunning(false)
+  , mNPNToken(npnToken)
+{
+  mHttps = originScheme.Equals("https");
+
+  if (mAlternatePort == -1) {
+    mAlternatePort = mHttps ? NS_HTTPS_DEFAULT_PORT : NS_HTTP_DEFAULT_PORT;
+  }
+  if (mOriginPort == -1) {
+    mOriginPort = mHttps ? NS_HTTPS_DEFAULT_PORT : NS_HTTP_DEFAULT_PORT;
+  }
+
+  LOG(("AltSvcMapping ctor %p %s://%s:%d to %s:%d\n", this,
+       nsCString(originScheme).get(), mOriginHost.get(), mOriginPort,
+       mAlternateHost.get(), mAlternatePort));
+
+  if (mAlternateHost.IsEmpty()) {
+    mAlternateHost = mOriginHost;
+  }
+  MakeHashKey(mHashKey, originScheme, mOriginHost, mOriginPort, mPrivate);
+}
+
+void
+AltSvcMapping::MakeHashKey(nsCString &outKey,
+                           const nsACString &originScheme,
+                           const nsACString &originHost,
+                           int32_t originPort,
+                           bool privateBrowsing)
+{
+  if (originPort == -1) {
+    bool isHttps = originScheme.Equals("https");
+    originPort = isHttps ? NS_HTTPS_DEFAULT_PORT : NS_HTTP_DEFAULT_PORT;
+  }
+
+  outKey.Append(originScheme);
+  outKey.Append(':');
+  outKey.Append(originHost);
+  outKey.Append(':');
+  outKey.AppendInt(originPort);
+  outKey.Append(':');
+  outKey.Append(privateBrowsing ? 'P' : '.');
+}
+
+int32_t
+AltSvcMapping::TTL()
+{
+  return mExpiresAt - NowInSeconds();
+}
+
+void
+AltSvcMapping::SetExpired()
+{
+  mExpiresAt = NowInSeconds() - 1;
+}
+
+bool
+AltSvcMapping::RouteEquals(AltSvcMapping *map)
+{
+  MOZ_ASSERT(map->mHashKey.Equals(mHashKey));
+  return mAlternateHost.Equals(map->mAlternateHost) &&
+    (mAlternatePort == map->mAlternatePort) &&
+    mNPNToken.Equals(map->mNPNToken);
+
+  return false;
+}
+
+void
+AltSvcMapping::GetConnectionInfo(nsHttpConnectionInfo **outCI,
+                                 nsProxyInfo *pi)
+{
+  nsRefPtr<nsHttpConnectionInfo> ci =
+    new nsHttpConnectionInfo(mAlternateHost, mAlternatePort, mNPNToken,
+                             mUsername, pi, mOriginHost, mOriginPort);
+  if (!mHttps) {
+    ci->SetRelaxed(true);
+  }
+  ci->SetPrivate(mPrivate);
+  ci.forget(outCI);
+}
+
+// This is the asynchronous null transaction used to validate
+// an alt-svc advertisement
+class AltSvcTransaction MOZ_FINAL : public NullHttpTransaction
+{
+public:
+    AltSvcTransaction(AltSvcMapping *map,
+                      nsHttpConnectionInfo *ci,
+                      nsIInterfaceRequestor *callbacks,
+                      uint32_t caps)
+    : NullHttpTransaction(ci, callbacks, caps)
+    , mMapping(map)
+    , mRunning(false)
+    , mTriedToValidate(false)
+    , mTriedToWrite(false)
+  {
+    MOZ_ASSERT(mMapping);
+    LOG(("AltSvcTransaction ctor %p map %p [%s -> %s]",
+         this, map, map->OriginHost().get(), map->AlternateHost().get()));
+  }
+
+  ~AltSvcTransaction()
+  {
+    LOG(("AltSvcTransaction dtor %p map %p running %d",
+         this, mMapping.get(), mRunning));
+
+    if (mRunning) {
+      MOZ_ASSERT(mMapping->IsRunning());
+      MaybeValidate(NS_OK);
+    }
+    if (!mMapping->Validated()) {
+      // try again later
+      mMapping->SetExpiresAt(NowInSeconds() + 2);
+    }
+    LOG(("AltSvcTransaction dtor %p map %p validated %d [%s]",
+         this, mMapping.get(), mMapping->Validated(),
+         mMapping->HashKey().get()));
+    mMapping->SetRunning(false);
+  }
+
+  void StartTransaction()
+  {
+    LOG(("AltSvcTransaction::StartTransaction() %p", this));
+
+    MOZ_ASSERT(!mRunning);
+    MOZ_ASSERT(!mMapping->IsRunning());
+    mCaps &= ~NS_HTTP_ALLOW_KEEPALIVE;
+    mRunning = true;
+    mMapping->SetRunning(true);
+  }
+
+  void MaybeValidate(nsresult reason)
+  {
+    if (mTriedToValidate) {
+      return;
+    }
+    mTriedToValidate = true;
+
+    LOG(("AltSvcTransaction::MaybeValidate() %p reason=%x running=%d conn=%p write=%d",
+         this, reason, mRunning, mConnection.get(), mTriedToWrite));
+
+    if (mTriedToWrite && reason == NS_BASE_STREAM_CLOSED) {
+      // The normal course of events is to cause the transaction to fail with CLOSED
+      // on a write - so that's a success that means the HTTP/2 session is setup.
+      reason = NS_OK;
+    }
+
+    if (NS_FAILED(reason) || !mRunning || !mConnection) {
+      LOG(("AltSvcTransaction::MaybeValidate %p Failed due to precondition", this));
+      return;
+    }
+
+    // insist on spdy/3* or >= http/2
+    uint32_t version = mConnection->Version();
+    LOG(("AltSvcTransaction::MaybeValidate() %p version %d\n", this, version));
+    if ((version < HTTP_VERSION_2) &&
+        (version != SPDY_VERSION_31) && (version != SPDY_VERSION_3)) {
+      LOG(("AltSvcTransaction::MaybeValidate %p Failed due to protocol version", this));
+      return;
+    }
+
+    nsCOMPtr<nsISupports> secInfo;
+    mConnection->GetSecurityInfo(getter_AddRefs(secInfo));
+    nsCOMPtr<nsISSLSocketControl> socketControl = do_QueryInterface(secInfo);
+    bool bypassAuth = false;
+
+    if (!socketControl ||
+        NS_FAILED(socketControl->GetBypassAuthentication(&bypassAuth))) {
+      bypassAuth = false;
+    }
+
+    LOG(("AltSvcTransaction::MaybeValidate() %p socketControl=%p bypass=%d",
+         this, socketControl.get(), bypassAuth));
+
+    if (bypassAuth) {
+      LOG(("AltSvcTransaction::MaybeValidate() %p "
+           "validating alternate service because relaxed", this));
+      mMapping->SetValidated(true);
+      return;
+    }
+
+    if (socketControl->GetFailedVerification()) {
+      LOG(("AltSvcTransaction::MaybeValidate() %p "
+           "not validated due to auth error", this));
+      return;
+    }
+
+    LOG(("AltSvcTransaction::MaybeValidate() %p "
+         "validating alternate service with auth check", this));
+    mMapping->SetValidated(true);
+  }
+
+  void Close(nsresult reason) MOZ_OVERRIDE
+  {
+    LOG(("AltSvcTransaction::Close() %p reason=%x running %d",
+         this, reason, mRunning));
+
+    MaybeValidate(reason);
+    if (!mMapping->Validated() && mConnection) {
+      mConnection->DontReuse();
+    }
+    NullHttpTransaction::Close(reason);
+  }
+
+  nsresult ReadSegments(nsAHttpSegmentReader *reader,
+                        uint32_t count, uint32_t *countRead) MOZ_OVERRIDE
+  {
+    LOG(("AltSvcTransaction::ReadSegements() %p\n"));
+    mTriedToWrite = true;
+    return NullHttpTransaction::ReadSegments(reader, count, countRead);
+  }
+
+private:
+  nsRefPtr<AltSvcMapping> mMapping;
+  uint32_t                mRunning : 1;
+  uint32_t                mTriedToValidate : 1;
+  uint32_t                mTriedToWrite : 1;
+};
+
+void
+AltSvcCache::UpdateAltServiceMapping(AltSvcMapping *map, nsProxyInfo *pi,
+                                     nsIInterfaceRequestor *aCallbacks,
+                                     uint32_t caps)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  AltSvcMapping *existing = mHash.GetWeak(map->mHashKey);
+  LOG(("AltSvcCache::UpdateAltServiceMapping %p map %p existing %p %s",
+       this, map, existing, map->AlternateHost().get()));
+
+  if (existing && (existing->TTL() <= 0)) {
+    LOG(("AltSvcCache::UpdateAltServiceMapping %p map %p is expired",
+         this, map));
+    existing = nullptr;
+    mHash.Remove(map->mHashKey);
+  }
+
+  if (existing && existing->mValidated) {
+    if (existing->RouteEquals(map)) {
+      // update expires
+      LOG(("AltSvcCache::UpdateAltServiceMapping %p map %p updates ttl of %p\n",
+           this, map, existing));
+      existing->SetExpiresAt(map->GetExpiresAt());
+      return;
+    }
+
+    LOG(("AltSvcCache::UpdateAltServiceMapping %p map %p overwrites %p\n",
+         this, map, existing));
+    existing = nullptr;
+    mHash.Remove(map->mHashKey);
+  }
+
+  if (existing) {
+    LOG(("AltSvcCache::UpdateAltServiceMapping %p map %p ignored because %p "
+         "still in progress\n", this, map, existing));
+    return;
+  }
+
+  mHash.Put(map->mHashKey, map);
+
+  nsRefPtr<nsHttpConnectionInfo> ci;
+  map->GetConnectionInfo(getter_AddRefs(ci), pi);
+  caps |= ci->GetAnonymous() ? NS_HTTP_LOAD_ANONYMOUS : 0;
+
+  nsCOMPtr<nsIInterfaceRequestor> callbacks = new AltSvcOverride(aCallbacks);
+
+  nsRefPtr<AltSvcTransaction> nullTransaction =
+    new AltSvcTransaction(map, ci, aCallbacks, caps);
+  nullTransaction->StartTransaction();
+  gHttpHandler->ConnMgr()->SpeculativeConnect(ci, callbacks, caps, nullTransaction);
+}
+
+AltSvcMapping *
+AltSvcCache::GetAltServiceMapping(const nsACString &scheme, const nsACString &host,
+                                  int32_t port, bool privateBrowsing)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  if (!gHttpHandler->AllowAltSvc()) {
+    return nullptr;
+  }
+  if (!gHttpHandler->AllowAltSvcOE() && scheme.Equals(NS_LITERAL_CSTRING("http"))) {
+    return nullptr;
+  }
+
+  nsAutoCString key;
+  AltSvcMapping::MakeHashKey(key, scheme, host, port, privateBrowsing);
+  AltSvcMapping *existing = mHash.GetWeak(key);
+  LOG(("AltSvcCache::GetAltServiceMapping %p key=%s "
+       "existing=%p validated=%d running=%d ttl=%d",
+       this, key.get(), existing, existing ? existing->mValidated : 0,
+       existing ? existing->mRunning : 0,
+       existing ? existing->TTL() : 0));
+  if (existing && (existing->TTL() <= 0)) {
+    LOG(("AltSvcCache::GetAltServiceMapping %p map %p is expired", this, existing));
+    mHash.Remove(existing->mHashKey);
+    existing = nullptr;
+  }
+  if (existing && existing->mValidated)
+    return existing;
+  return nullptr;
+}
+
+class ProxyClearHostMapping : public nsRunnable {
+public:
+  explicit ProxyClearHostMapping(const nsACString &host, int32_t port)
+    : mHost(host)
+    , mPort(port)
+    {}
+
+    NS_IMETHOD Run()
+    {
+      MOZ_ASSERT(NS_IsMainThread());
+      gHttpHandler->ConnMgr()->ClearHostMapping(mHost, mPort);
+      return NS_OK;
+    }
+private:
+    nsCString mHost;
+    int32_t mPort;
+};
+
+void
+AltSvcCache::ClearHostMapping(const nsACString &host, int32_t port)
+{
+  if (!NS_IsMainThread()) {
+    nsCOMPtr<nsIRunnable> event = new ProxyClearHostMapping(host, port);
+    if (event) {
+      NS_DispatchToMainThread(event);
+    }
+    return;
+  }
+
+  nsAutoCString key;
+
+  AltSvcMapping::MakeHashKey(key, NS_LITERAL_CSTRING("http"), host, port, true);
+  AltSvcMapping *existing = mHash.GetWeak(key);
+  if (existing) {
+    existing->SetExpired();
+  }
+
+  AltSvcMapping::MakeHashKey(key, NS_LITERAL_CSTRING("https"), host, port, true);
+  existing = mHash.GetWeak(key);
+  if (existing) {
+    existing->SetExpired();
+  }
+
+  AltSvcMapping::MakeHashKey(key, NS_LITERAL_CSTRING("http"), host, port, false);
+  existing = mHash.GetWeak(key);
+  if (existing) {
+    existing->SetExpired();
+  }
+
+  AltSvcMapping::MakeHashKey(key, NS_LITERAL_CSTRING("https"), host, port, false);
+  existing = mHash.GetWeak(key);
+  if (existing) {
+    existing->SetExpired();
+  }
+}
+
+void
+AltSvcCache::ClearAltServiceMappings()
+{
+    MOZ_ASSERT(NS_IsMainThread());
+    mHash.Clear();
+}
+
+NS_IMETHODIMP
+AltSvcOverride::GetInterface(const nsIID &iid, void **result)
+{
+  if (NS_SUCCEEDED(QueryInterface(iid, result)) && *result) {
+    return NS_OK;
+  }
+  return mCallbacks->GetInterface(iid, result);
+}
+
+NS_IMETHODIMP
+AltSvcOverride::GetIgnoreIdle(bool *ignoreIdle)
+{
+  *ignoreIdle = true;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+AltSvcOverride::GetIgnorePossibleSpdyConnections(bool *ignorePossibleSpdyConnections)
+{
+  *ignorePossibleSpdyConnections = true;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+AltSvcOverride::GetParallelSpeculativeConnectLimit(
+  uint32_t *parallelSpeculativeConnectLimit)
+{
+  *parallelSpeculativeConnectLimit = 32;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+AltSvcOverride::GetIsFromPredictor(bool *isFromPredictor)
+{
+  *isFromPredictor = false;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+AltSvcOverride::GetAllow1918(bool *allow)
+{
+  // normally we don't do speculative connects to 1918.. and we use
+  // speculative connects for the mapping validation, so override
+  // that default here for alt-svc
+  *allow = true;
+  return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(AltSvcOverride, nsIInterfaceRequestor, nsISpeculativeConnectionOverrider)
+
+} // namespace mozilla::net
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/AlternateServices.h
@@ -0,0 +1,129 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+Alt-Svc allows separation of transport routing from the origin host without
+using a proxy. See https://httpwg.github.io/http-extensions/alt-svc.html and
+https://tools.ietf.org/html/draft-ietf-httpbis-alt-svc-03
+
+ Nice To Have Future Enhancements::
+ * flush on network change event when we have an indicator
+ * use established https channel for http instead separate of conninfo hash
+ * pin via http-tls header
+ * clear based on origin when a random fail happens not just 421
+ * upon establishment of channel, cancel and retry trans that have not yet written anything
+ * persistent storage (including private browsing filter)
+ * memory reporter for cache, but this is rather tiny
+*/
+
+#ifndef mozilla_net_AlternateServices_h
+#define mozilla_net_AlternateServices_h
+
+#include "nsRefPtrHashtable.h"
+#include "nsString.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsISpeculativeConnect.h"
+
+class nsProxyInfo;
+
+namespace mozilla { namespace net {
+
+class nsHttpConnectionInfo;
+
+class AltSvcMapping
+{
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AltSvcMapping)
+  friend class AltSvcCache;
+
+public:
+  AltSvcMapping(const nsACString &originScheme,
+                const nsACString &originHost,
+                int32_t originPort,
+                const nsACString &username,
+                bool privateBrowsing,
+                uint32_t expiresAt,
+                const nsACString &alternateHost,
+                int32_t alternatePort,
+                const nsACString &npnToken);
+
+  const nsCString &AlternateHost() const { return mAlternateHost; }
+  const nsCString &OriginHost() const { return mOriginHost; }
+  const nsCString &HashKey() const { return mHashKey; }
+  uint32_t AlternatePort() const { return mAlternatePort; }
+  bool Validated() { return mValidated; }
+  void SetValidated(bool val) { mValidated = val; }
+  bool IsRunning() { return mRunning; }
+  void SetRunning(bool val) { mRunning = val; }
+  int32_t GetExpiresAt() { return mExpiresAt; }
+  void SetExpiresAt(int32_t val) { mExpiresAt = val; }
+  void SetExpired();
+  bool RouteEquals(AltSvcMapping *map);
+
+  void GetConnectionInfo(nsHttpConnectionInfo **outCI, nsProxyInfo *pi);
+  int32_t TTL();
+
+private:
+  virtual ~AltSvcMapping() {};
+  static void MakeHashKey(nsCString &outKey,
+                          const nsACString &originScheme,
+                          const nsACString &originHost,
+                          int32_t originPort,
+                          bool privateBrowsing);
+
+  nsCString mHashKey;
+
+  nsCString mAlternateHost;
+  int32_t mAlternatePort;
+
+  nsCString mOriginHost;
+  int32_t mOriginPort;
+
+  nsCString mUsername;
+  bool mPrivate;
+
+  uint32_t mExpiresAt;
+
+  bool mValidated;
+  bool mRunning;
+  bool mHttps;
+
+  nsCString mNPNToken;
+};
+
+class AltSvcOverride : public nsIInterfaceRequestor
+                     , public nsISpeculativeConnectionOverrider
+{
+public:
+    NS_DECL_THREADSAFE_ISUPPORTS
+    NS_DECL_NSISPECULATIVECONNECTIONOVERRIDER
+    NS_DECL_NSIINTERFACEREQUESTOR
+
+    AltSvcOverride(nsIInterfaceRequestor *aRequestor)
+      : mCallbacks(aRequestor) {}
+
+private:
+    virtual ~AltSvcOverride() {}
+    nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
+};
+
+class AltSvcCache
+{
+public:
+  void UpdateAltServiceMapping(AltSvcMapping *map, nsProxyInfo *pi,
+                               nsIInterfaceRequestor *, uint32_t caps); // main thread
+  AltSvcMapping *GetAltServiceMapping(const nsACString &scheme,
+                                      const nsACString &host,
+                                      int32_t port, bool pb);
+  void ClearAltServiceMappings();
+  void ClearHostMapping(const nsACString &host, int32_t port);
+
+private:
+  nsRefPtrHashtable<nsCStringHashKey, AltSvcMapping> mHash;
+};
+
+}} // namespace mozilla::net
+
+#endif // include guard
--- a/netwerk/protocol/http/Http2Session.cpp
+++ b/netwerk/protocol/http/Http2Session.cpp
@@ -253,17 +253,18 @@ static Http2ControlFx sControlFunctions[
   Http2Session::RecvHeaders,
   Http2Session::RecvPriority,
   Http2Session::RecvRstStream,
   Http2Session::RecvSettings,
   Http2Session::RecvPushPromise,
   Http2Session::RecvPing,
   Http2Session::RecvGoAway,
   Http2Session::RecvWindowUpdate,
-  Http2Session::RecvContinuation
+  Http2Session::RecvContinuation,
+  Http2Session::RecvAltSvc // extension for type 0x0A
 };
 
 bool
 Http2Session::RoomForMoreConcurrent()
 {
   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   return (mConcurrent < mMaxConcurrent);
 }
@@ -437,17 +438,18 @@ Http2Session::AddStream(nsAHttpTransacti
     LOG3(("Http2Session::AddStream %p stream %p activated immediately.",
           this, stream));
     ActivateStream(stream);
   } else {
     LOG3(("Http2Session::AddStream %p stream %p queued.", this, stream));
     mQueuedStreams.Push(stream);
   }
 
-  if (!(aHttpTransaction->Caps() & NS_HTTP_ALLOW_KEEPALIVE)) {
+  if (!(aHttpTransaction->Caps() & NS_HTTP_ALLOW_KEEPALIVE) &&
+      !aHttpTransaction->IsNullTransaction()) {
     LOG3(("Http2Session::AddStream %p transaction %p forces keep-alive off.\n",
           this, aHttpTransaction));
     DontReuse();
   }
 
   return true;
 }
 
@@ -1853,16 +1855,218 @@ Http2Session::RecvContinuation(Http2Sess
   // continued push promise
   if (self->mInputFrameFlags & kFlag_END_HEADERS) {
     self->mInputFrameFlags &= ~kFlag_END_HEADERS;
     self->mInputFrameFlags |= kFlag_END_PUSH_PROMISE;
   }
   return RecvPushPromise(self);
 }
 
+class UpdateAltSvcEvent : public nsRunnable
+{
+public:
+  UpdateAltSvcEvent(const nsCString &host, const uint16_t port,
+                    const nsCString &npnToken, const uint32_t expires,
+                    const nsCString &aOrigin,
+                    nsHttpConnectionInfo *aCI,
+                    nsIInterfaceRequestor *callbacks)
+    : mHost(host)
+    , mPort(port)
+    , mNPNToken(npnToken)
+    , mExpires(expires)
+    , mOrigin(aOrigin)
+    , mCI(aCI)
+    , mCallbacks(callbacks)
+  {
+  }
+
+  NS_IMETHOD Run() MOZ_OVERRIDE
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    nsCString originScheme;
+    nsCString originHost;
+    int32_t originPort = -1;
+
+    nsCOMPtr<nsIURI> uri;
+    if (NS_FAILED(NS_NewURI(getter_AddRefs(uri), mOrigin))) {
+      LOG(("UpdateAltSvcEvent origin does not parse %s\n",
+           mOrigin.get()));
+      return NS_OK;
+    }
+    uri->GetScheme(originScheme);
+    uri->GetHost(originHost);
+    uri->GetPort(&originPort);
+
+    const char *username = mCI->Username();
+    const bool privateBrowsing = mCI->GetPrivate();
+
+    LOG(("UpdateAltSvcEvent location=%s:%u protocol=%s expires=%u "
+         "origin=%s://%s:%u user=%s private=%d", mHost.get(), mPort,
+         mNPNToken.get(), mExpires, originScheme.get(), originHost.get(),
+         originPort, username, privateBrowsing));
+    nsRefPtr<AltSvcMapping> mapping = new AltSvcMapping(
+      nsDependentCString(originScheme.get()),
+      nsDependentCString(originHost.get()),
+      originPort, nsDependentCString(username), privateBrowsing, mExpires,
+      mHost, mPort, mNPNToken);
+
+    nsProxyInfo *proxyInfo = mCI->ProxyInfo();
+    gHttpHandler->UpdateAltServiceMapping(mapping, proxyInfo, mCallbacks, 0);
+    return NS_OK;
+  }
+
+private:
+  nsCString mHost;
+  uint16_t mPort;
+  nsCString mNPNToken;
+  uint32_t mExpires;
+  nsCString mOrigin;
+  nsRefPtr<nsHttpConnectionInfo> mCI;
+  nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
+};
+
+// defined as an http2 extension - alt-svc
+// defines receipt of frame type 0x0A.. See AlternateSevices.h
+nsresult
+Http2Session::RecvAltSvc(Http2Session *self)
+{
+  MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_ALTSVC);
+  LOG3(("Http2Session::RecvAltSvc %p Flags 0x%X id 0x%X\n", self,
+        self->mInputFrameFlags, self->mInputFrameID));
+
+  if (self->mInputFrameDataSize < 8) {
+    LOG3(("Http2Session::RecvAltSvc %p frame too small", self));
+    RETURN_SESSION_ERROR(self, FRAME_SIZE_ERROR);
+  }
+
+  uint32_t maxAge =
+    PR_ntohl(*reinterpret_cast<uint32_t *>(self->mInputFrameBuffer.get() + kFrameHeaderBytes));
+  uint16_t portRoute =
+    PR_ntohs(*reinterpret_cast<uint16_t *>(self->mInputFrameBuffer.get() + kFrameHeaderBytes + 4));
+  uint8_t protoLen = self->mInputFrameBuffer.get()[kFrameHeaderBytes + 6];
+  LOG3(("Http2Session::RecvAltSvc %p maxAge=%d port=%d protoLen=%d", self,
+        maxAge, portRoute, protoLen));
+
+  if (self->mInputFrameDataSize < (8U + protoLen)) {
+    LOG3(("Http2Session::RecvAltSvc %p frame too small for protocol", self));
+    RETURN_SESSION_ERROR(self, FRAME_SIZE_ERROR);
+  }
+  nsAutoCString protocol;
+  protocol.Assign(self->mInputFrameBuffer.get() + kFrameHeaderBytes + 7, protoLen);
+
+  uint32_t spdyIndex;
+  SpdyInformation *spdyInfo = gHttpHandler->SpdyInfo();
+  if (!(NS_SUCCEEDED(spdyInfo->GetNPNIndex(protocol, &spdyIndex)) &&
+        spdyInfo->ProtocolEnabled(spdyIndex))) {
+    LOG3(("Http2Session::RecvAltSvc %p unknown protocol %s, ignoring", self,
+          protocol.BeginReading()));
+    self->ResetDownstreamState();
+    return NS_OK;
+  }
+
+  uint8_t hostLen = self->mInputFrameBuffer.get()[kFrameHeaderBytes + 7 + protoLen];
+  if (self->mInputFrameDataSize < (8U + protoLen + hostLen)) {
+    LOG3(("Http2Session::RecvAltSvc %p frame too small for host", self));
+    RETURN_SESSION_ERROR(self, FRAME_SIZE_ERROR);
+  }
+
+  nsRefPtr<nsHttpConnectionInfo> ci(self->ConnectionInfo());
+  if (!self->mConnection || !ci) {
+    LOG3(("Http2Session::RecvAltSvc %p no connection or conninfo for %d", self,
+          self->mInputFrameID));
+    self->ResetDownstreamState();
+    return NS_OK;
+  }
+
+  nsAutoCString hostRoute;
+  hostRoute.Assign(self->mInputFrameBuffer.get() + kFrameHeaderBytes + 8 + protoLen, hostLen);
+
+  uint32_t originLen = self->mInputFrameDataSize - 8 - protoLen - hostLen;
+  nsAutoCString specifiedOrigin;
+  if (originLen) {
+    if (self->mInputFrameID) {
+      LOG3(("Http2Session::RecvAltSvc %p got frame w/origin on non zero stream", self));
+      self->ResetDownstreamState();
+      return NS_OK;
+    }
+    specifiedOrigin.Assign(
+      self->mInputFrameBuffer.get() + kFrameHeaderBytes + 8 + protoLen + hostLen,
+      originLen);
+
+    bool okToReroute = true;
+    nsCOMPtr<nsISupports> securityInfo;
+    self->mConnection->GetSecurityInfo(getter_AddRefs(securityInfo));
+    nsCOMPtr<nsISSLSocketControl> ssl = do_QueryInterface(securityInfo);
+    if (!ssl) {
+      okToReroute = false;
+    }
+
+    // a little off main thread origin parser. This is a non critical function because
+    // any alternate route created has to be verified anyhow
+    nsAutoCString specifiedOriginHost;
+    if (specifiedOrigin.EqualsIgnoreCase("https://", 8)) {
+      specifiedOriginHost.Assign(specifiedOrigin.get() + 8,
+                                 specifiedOrigin.Length() - 8);
+      if (ci->GetRelaxed()) {
+        // technically this is ok because it will still be confirmed before being used
+        // but let's not support it.
+        okToReroute = false;
+      }
+    } else if (specifiedOrigin.EqualsIgnoreCase("http://", 7)) {
+      specifiedOriginHost.Assign(specifiedOrigin.get() + 7,
+                                 specifiedOrigin.Length() - 7);
+    }
+
+    int32_t colonOffset = specifiedOriginHost.FindCharInSet(":", 0);
+    if (colonOffset != kNotFound) {
+      specifiedOriginHost.Truncate(colonOffset);
+    }
+
+    if (okToReroute) {
+      ssl->IsAcceptableForHost(specifiedOriginHost, &okToReroute);
+    }
+    if (!okToReroute) {
+      LOG3(("Http2Session::RecvAltSvc %p can't reroute non-authoritative origin %s",
+            self, specifiedOrigin.BeginReading()));
+      self->ResetDownstreamState();
+      return NS_OK;
+    }
+  } else {
+    // no origin specified in frame. We need to have an active pull stream to match
+    // this up to as if it were a response header.
+    if (!(self->mInputFrameID & 0x1) ||
+        NS_FAILED(self->SetInputFrameDataStream(self->mInputFrameID)) ||
+        !self->mInputFrameDataStream->Transaction() ||
+        !self->mInputFrameDataStream->Transaction()->RequestHead()) {
+      LOG3(("Http2Session::RecvAltSvc %p got frame w/o origin on invalid stream", self));
+      self->ResetDownstreamState();
+      return NS_OK;
+    }
+
+    specifiedOrigin.Assign(
+      self->mInputFrameDataStream->Transaction()->RequestHead()->Origin());
+  }
+
+  nsCOMPtr<nsISupports> callbacks;
+  self->mConnection->GetSecurityInfo(getter_AddRefs(callbacks));
+  nsCOMPtr<nsIInterfaceRequestor> irCallbacks = do_QueryInterface(callbacks);
+
+  nsRefPtr<UpdateAltSvcEvent> event = new UpdateAltSvcEvent(
+    hostRoute, portRoute, protocol, NowInSeconds() + maxAge,
+    specifiedOrigin, ci, irCallbacks);
+  NS_DispatchToMainThread(event);
+
+  LOG3(("Http2Session::RecvAltSvc %p processed location=%s:%u protocol=%s "
+        "maxAge=%u origin=%s", self, hostRoute.get(), portRoute,
+        protocol.get(), maxAge, specifiedOrigin.get()));
+  self->ResetDownstreamState();
+  return NS_OK;
+}
+
 //-----------------------------------------------------------------------------
 // nsAHttpTransaction. It is expected that nsHttpConnection is the caller
 // of these methods
 //-----------------------------------------------------------------------------
 
 void
 Http2Session::OnTransportStatus(nsITransport* aTransport,
                                 nsresult aStatus, uint64_t aProgress)
@@ -1978,20 +2182,27 @@ Http2Session::ReadSegments(nsAHttpSegmen
     } else {
       rv = NS_BASE_STREAM_WOULD_BLOCK;
     }
     SetWriteCallbacks();
     return rv;
   }
 
   if (NS_FAILED(rv)) {
-    LOG3(("Http2Session::ReadSegments %p returning FAIL code %X",
+    LOG3(("Http2Session::ReadSegments %p may return FAIL code %X",
           this, rv));
-    if (rv != NS_BASE_STREAM_WOULD_BLOCK)
-      CleanupStream(stream, rv, CANCEL_ERROR);
+    if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
+      return rv;
+    }
+
+    CleanupStream(stream, rv, CANCEL_ERROR);
+    if (SoftStreamError(rv)) {
+      LOG3(("Http2Session::ReadSegments %p soft error override\n", this));
+      rv = NS_OK;
+    }
     return rv;
   }
 
   if (*countRead > 0) {
     LOG3(("Http2Session::ReadSegments %p stream=%p countread=%d",
           this, stream, *countRead));
     mReadyForWrite.Push(stream);
     SetWriteCallbacks();
--- a/netwerk/protocol/http/Http2Session.h
+++ b/netwerk/protocol/http/Http2Session.h
@@ -70,27 +70,28 @@ public:
   +-+-------------+---------------+-------------------------------+
   |R|                 Stream Identifier (31)                      |
   +-+-------------------------------------------------------------+
   |                     Frame Data (0...)                       ...
   +---------------------------------------------------------------+
 */
 
   enum frameType {
-    FRAME_TYPE_DATA = 0,
-    FRAME_TYPE_HEADERS = 1,
-    FRAME_TYPE_PRIORITY = 2,
-    FRAME_TYPE_RST_STREAM = 3,
-    FRAME_TYPE_SETTINGS = 4,
-    FRAME_TYPE_PUSH_PROMISE = 5,
-    FRAME_TYPE_PING = 6,
-    FRAME_TYPE_GOAWAY = 7,
-    FRAME_TYPE_WINDOW_UPDATE = 8,
-    FRAME_TYPE_CONTINUATION = 9,
-    FRAME_TYPE_LAST = 10
+    FRAME_TYPE_DATA          = 0x0,
+    FRAME_TYPE_HEADERS       = 0x1,
+    FRAME_TYPE_PRIORITY      = 0x2,
+    FRAME_TYPE_RST_STREAM    = 0x3,
+    FRAME_TYPE_SETTINGS      = 0x4,
+    FRAME_TYPE_PUSH_PROMISE  = 0x5,
+    FRAME_TYPE_PING          = 0x6,
+    FRAME_TYPE_GOAWAY        = 0x7,
+    FRAME_TYPE_WINDOW_UPDATE = 0x8,
+    FRAME_TYPE_CONTINUATION  = 0x9,
+    FRAME_TYPE_ALTSVC        = 0xA,
+    FRAME_TYPE_LAST          = 0xB
   };
 
   // NO_ERROR is a macro defined on windows, so we'll name the HTTP2 goaway
   // code NO_ERROR to be NO_HTTP_ERROR
   enum errorType {
     NO_HTTP_ERROR = 0,
     PROTOCOL_ERROR = 1,
     INTERNAL_ERROR = 2,
@@ -163,16 +164,17 @@ public:
   static nsresult RecvPriority(Http2Session *);
   static nsresult RecvRstStream(Http2Session *);
   static nsresult RecvSettings(Http2Session *);
   static nsresult RecvPushPromise(Http2Session *);
   static nsresult RecvPing(Http2Session *);
   static nsresult RecvGoAway(Http2Session *);
   static nsresult RecvWindowUpdate(Http2Session *);
   static nsresult RecvContinuation(Http2Session *);
+  static nsresult RecvAltSvc(Http2Session *);
 
   char       *EnsureOutputBuffer(uint32_t needed);
 
   template<typename charType>
   void CreateFrameHeader(charType dest, uint16_t frameLength,
                          uint8_t frameType, uint8_t frameFlags,
                          uint32_t streamID);
 
--- a/netwerk/protocol/http/NullHttpTransaction.cpp
+++ b/netwerk/protocol/http/NullHttpTransaction.cpp
@@ -18,20 +18,20 @@ namespace net {
 NS_IMPL_ISUPPORTS(NullHttpTransaction, NullHttpTransaction, nsISupportsWeakReference)
 
 NullHttpTransaction::NullHttpTransaction(nsHttpConnectionInfo *ci,
                                          nsIInterfaceRequestor *callbacks,
                                          uint32_t caps)
   : mStatus(NS_OK)
   , mCaps(caps | NS_HTTP_ALLOW_KEEPALIVE)
   , mCapsToClear(0)
+  , mRequestHead(nullptr)
+  , mIsDone(false)
   , mCallbacks(callbacks)
   , mConnectionInfo(ci)
-  , mRequestHead(nullptr)
-  , mIsDone(false)
 {
 }
 
 NullHttpTransaction::~NullHttpTransaction()
 {
   mCallbacks = nullptr;
   delete mRequestHead;
 }
--- a/netwerk/protocol/http/NullHttpTransaction.h
+++ b/netwerk/protocol/http/NullHttpTransaction.h
@@ -44,27 +44,31 @@ public:
     return PR_SecondsToInterval(15);
   }
 
 protected:
   virtual ~NullHttpTransaction();
 
 private:
   nsresult mStatus;
+protected:
   uint32_t mCaps;
+private:
   // mCapsToClear holds flags that should be cleared in mCaps, e.g. unset
   // NS_HTTP_REFRESH_DNS when DNS refresh request has completed to avoid
   // redundant requests on the network. To deal with raciness, only unsetting
   // bitfields should be allowed: 'lost races' will thus err on the
   // conservative side, e.g. by going ahead with a 2nd DNS refresh.
   uint32_t mCapsToClear;
+  nsHttpRequestHead *mRequestHead;
+  bool mIsDone;
+
+protected:
   nsRefPtr<nsAHttpConnection> mConnection;
   nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
   nsRefPtr<nsHttpConnectionInfo> mConnectionInfo;
-  nsHttpRequestHead *mRequestHead;
-  bool mIsDone;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(NullHttpTransaction, NS_NULLHTTPTRANSACTION_IID)
 
 }} // namespace mozilla::net
 
 #endif // mozilla_net_NullHttpTransaction_h
--- a/netwerk/protocol/http/SpdySession3.cpp
+++ b/netwerk/protocol/http/SpdySession3.cpp
@@ -381,37 +381,41 @@ SpdySession3::AddStream(nsAHttpTransacti
           this, stream));
     ActivateStream(stream);
   }
   else {
     LOG3(("SpdySession3::AddStream %p stream %p queued.", this, stream));
     mQueuedStreams.Push(stream);
   }
 
-  if (!(aHttpTransaction->Caps() & NS_HTTP_ALLOW_KEEPALIVE)) {
+  if (!(aHttpTransaction->Caps() & NS_HTTP_ALLOW_KEEPALIVE) &&
+      !aHttpTransaction->IsNullTransaction()) {
     LOG3(("SpdySession3::AddStream %p transaction %p forces keep-alive off.\n",
           this, aHttpTransaction));
     DontReuse();
   }
   return true;
 }
 
 void
 SpdySession3::ActivateStream(SpdyStream3 *stream)
 {
   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   MOZ_ASSERT(!stream->StreamID() || (stream->StreamID() & 1),
              "Do not activate pushed streams");
 
-  ++mConcurrent;
-  if (mConcurrent > mConcurrentHighWater)
-    mConcurrentHighWater = mConcurrent;
-  LOG3(("SpdySession3::AddStream %p activating stream %p Currently %d "
-        "streams in session, high water mark is %d",
-        this, stream, mConcurrent, mConcurrentHighWater));
+  nsAHttpTransaction *trans = stream->Transaction();
+  if (!trans || !trans->IsNullTransaction() || trans->QuerySpdyConnectTransaction()) {
+    ++mConcurrent;
+    if (mConcurrent > mConcurrentHighWater)
+      mConcurrentHighWater = mConcurrent;
+    LOG3(("SpdySession3::AddStream %p activating stream %p Currently %d "
+          "streams in session, high water mark is %d",
+          this, stream, mConcurrent, mConcurrentHighWater));
+  }
 
   mReadyForWrite.Push(stream);
   SetWriteCallbacks();
 
   // Kick off the SYN transmit without waiting for the poll loop
   // This won't work for stream id=1 because there is no segment reader
   // yet.
   if (mSegmentReader) {
@@ -1756,20 +1760,27 @@ SpdySession3::ReadSegments(nsAHttpSegmen
       rv = NS_OK;
     else
       rv = NS_BASE_STREAM_WOULD_BLOCK;
     SetWriteCallbacks();
     return rv;
   }
 
   if (NS_FAILED(rv)) {
-    LOG3(("SpdySession3::ReadSegments %p returning FAIL code %X",
+    LOG3(("SpdySession3::ReadSegments %p may return FAIL code %X",
           this, rv));
-    if (rv != NS_BASE_STREAM_WOULD_BLOCK)
-      CleanupStream(stream, rv, RST_CANCEL);
+    if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
+      return rv;
+    }
+
+    CleanupStream(stream, rv, RST_CANCEL);
+    if (SoftStreamError(rv)) {
+      LOG3(("SpdySession3::ReadSegments %p soft error override\n", this));
+      rv = NS_OK;
+    }
     return rv;
   }
 
   if (*countRead > 0) {
     LOG3(("SpdySession3::ReadSegments %p stream=%p countread=%d",
           this, stream, *countRead));
     mReadyForWrite.Push(stream);
     SetWriteCallbacks();
--- a/netwerk/protocol/http/SpdySession31.cpp
+++ b/netwerk/protocol/http/SpdySession31.cpp
@@ -384,38 +384,43 @@ SpdySession31::AddStream(nsAHttpTransact
           this, stream));
     ActivateStream(stream);
   }
   else {
     LOG3(("SpdySession31::AddStream %p stream %p queued.", this, stream));
     mQueuedStreams.Push(stream);
   }
 
-  if (!(aHttpTransaction->Caps() & NS_HTTP_ALLOW_KEEPALIVE)) {
+  if (!(aHttpTransaction->Caps() & NS_HTTP_ALLOW_KEEPALIVE) &&
+      !aHttpTransaction->IsNullTransaction()) {
     LOG3(("SpdySession31::AddStream %p transaction %p forces keep-alive off.\n",
           this, aHttpTransaction));
     DontReuse();
   }
 
   return true;
 }
 
 void
 SpdySession31::ActivateStream(SpdyStream31 *stream)
 {
   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   MOZ_ASSERT(!stream->StreamID() || (stream->StreamID() & 1),
              "Do not activate pushed streams");
 
-  ++mConcurrent;
-  if (mConcurrent > mConcurrentHighWater)
-    mConcurrentHighWater = mConcurrent;
-  LOG3(("SpdySession31::AddStream %p activating stream %p Currently %d "
-        "streams in session, high water mark is %d",
-        this, stream, mConcurrent, mConcurrentHighWater));
+  nsAHttpTransaction *trans = stream->Transaction();
+  if (!trans || !trans->IsNullTransaction() || trans->QuerySpdyConnectTransaction()) {
+    ++mConcurrent;
+    if (mConcurrent > mConcurrentHighWater) {
+      mConcurrentHighWater = mConcurrent;
+    }
+    LOG3(("SpdySession31::AddStream %p activating stream %p Currently %d "
+          "streams in session, high water mark is %d",
+          this, stream, mConcurrent, mConcurrentHighWater));
+  }
 
   mReadyForWrite.Push(stream);
   SetWriteCallbacks();
 
   // Kick off the SYN transmit without waiting for the poll loop
   // This won't work for stream id=1 because there is no segment reader
   // yet.
   if (mSegmentReader) {
@@ -1822,20 +1827,27 @@ SpdySession31::ReadSegments(nsAHttpSegme
       rv = NS_OK;
     else
       rv = NS_BASE_STREAM_WOULD_BLOCK;
     SetWriteCallbacks();
     return rv;
   }
 
   if (NS_FAILED(rv)) {
-    LOG3(("SpdySession31::ReadSegments %p returning FAIL code %X",
+    LOG3(("SpdySession31::ReadSegments %p may return FAIL code %X",
           this, rv));
-    if (rv != NS_BASE_STREAM_WOULD_BLOCK)
-      CleanupStream(stream, rv, RST_CANCEL);
+    if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
+      return rv;
+    }
+
+    CleanupStream(stream, rv, RST_CANCEL);
+    if (SoftStreamError(rv)) {
+      LOG3(("SpdySession31::ReadSegments %p soft error override\n", this));
+      rv = NS_OK;
+    }
     return rv;
   }
 
   if (*countRead > 0) {
     LOG3(("SpdySession31::ReadSegments %p stream=%p countread=%d",
           this, stream, *countRead));
     mReadyForWrite.Push(stream);
     SetWriteCallbacks();
--- a/netwerk/protocol/http/moz.build
+++ b/netwerk/protocol/http/moz.build
@@ -34,16 +34,17 @@ EXPORTS.mozilla.net += [
     'HttpInfo.h',
     'PHttpChannelParams.h',
     'PSpdyPush.h',
 ]
 
 # ASpdySession.cpp and nsHttpAuthCache cannot be built in unified mode because
 # they use plarena.h.
 SOURCES += [
+    'AlternateServices.cpp',
     'ASpdySession.cpp',
     'nsHttpAuthCache.cpp',
     'nsHttpChannelAuthProvider.cpp', # redefines GetAuthType
 ]
 
 UNIFIED_SOURCES += [
     'ConnectionDiagnostics.cpp',
     'Http2Compression.cpp',
--- a/netwerk/protocol/http/nsAHttpConnection.h
+++ b/netwerk/protocol/http/nsAHttpConnection.h
@@ -135,16 +135,19 @@ public:
 
     // The number of transaction bytes written out on this HTTP Connection, does
     // not count CONNECT tunnel setup
     virtual int64_t BytesWritten() = 0;
 
     // Update the callbacks used to provide security info. May be called on
     // any thread.
     virtual void SetSecurityCallbacks(nsIInterfaceRequestor* aCallbacks) = 0;
+
+    // nsHttp.h version
+    virtual uint32_t Version() = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsAHttpConnection, NS_AHTTPCONNECTION_IID)
 
 #define NS_DECL_NSAHTTPCONNECTION(fwdObject)                    \
     nsresult OnHeadersAvailable(nsAHttpTransaction *, nsHttpRequestHead *, nsHttpResponseHead *, bool *reset); \
     void CloseTransaction(nsAHttpTransaction *, nsresult); \
     nsresult TakeTransport(nsISocketTransport **,    \
@@ -202,16 +205,22 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsAHttpCon
         return (fwdObject)->ForceRecv();   \
     }                                      \
     nsISocketTransport *Transport()        \
     {                                      \
         if (!(fwdObject))                  \
             return nullptr;                 \
         return (fwdObject)->Transport();   \
     }                                      \
+    uint32_t Version()                     \
+    {                                      \
+        return (fwdObject) ?               \
+            (fwdObject)->Version() :       \
+            NS_HTTP_VERSION_UNKNOWN;       \
+    }                                      \
     bool IsProxyConnectInProgress()                         \
     {                                                       \
         return (fwdObject)->IsProxyConnectInProgress();     \
     }                                                       \
     bool LastTransactionExpectedNoContent()                 \
     {                                                       \
         return (fwdObject)->LastTransactionExpectedNoContent(); \
     }                                                       \
--- a/netwerk/protocol/http/nsAHttpTransaction.h
+++ b/netwerk/protocol/http/nsAHttpTransaction.h
@@ -198,17 +198,17 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsAHttpTra
                            nsresult status, uint64_t progress); \
     bool     IsDone(); \
     nsresult Status(); \
     uint32_t Caps();   \
     void     SetDNSWasRefreshed(); \
     uint64_t Available(); \
     virtual nsresult ReadSegments(nsAHttpSegmentReader *, uint32_t, uint32_t *); \
     virtual nsresult WriteSegments(nsAHttpSegmentWriter *, uint32_t, uint32_t *); \
-    void     Close(nsresult reason);                                    \
+    virtual void Close(nsresult reason);                                \
     nsHttpConnectionInfo *ConnectionInfo();                             \
     void     SetProxyConnectFailed();                                   \
     virtual nsHttpRequestHead *RequestHead();                                   \
     uint32_t Http1xTransactionCount();                                  \
     nsresult TakeSubTransactions(nsTArray<nsRefPtr<nsAHttpTransaction> > &outTransactions); \
     nsresult AddTransaction(nsAHttpTransaction *);                      \
     uint32_t PipelineDepth();                                           \
     nsresult SetPipelinePosition(int32_t);                              \
--- a/netwerk/protocol/http/nsHttp.cpp
+++ b/netwerk/protocol/http/nsHttp.cpp
@@ -341,11 +341,133 @@ void EnsureBuffer(nsAutoArrayPtr<char> &
     localEnsureBuffer<char> (buf, newSize, preserve, objSize);
 }
 
 void EnsureBuffer(nsAutoArrayPtr<uint8_t> &buf, uint32_t newSize,
                   uint32_t preserve, uint32_t &objSize)
 {
     localEnsureBuffer<uint8_t> (buf, newSize, preserve, objSize);
 }
+///
+
+void
+ParsedHeaderValueList::Tokenize(char *input, uint32_t inputLen, char **token,
+                                uint32_t *tokenLen, bool *foundEquals, char **next)
+{
+    if (foundEquals) {
+        *foundEquals = false;
+    }
+    if (next) {
+        *next = nullptr;
+    }
+    if (inputLen < 1 || !input || !token) {
+        return;
+    }
+
+    bool foundFirst = false;
+    bool inQuote = false;
+    bool foundToken = false;
+    *token = input;
+    *tokenLen = inputLen;
+
+    for (uint32_t index = 0; !foundToken && index < inputLen; ++index) {
+        // strip leading cruft
+        if (!foundFirst &&
+            (input[index] == ' ' || input[index] == '"' || input[index] == '\t')) {
+            (*token)++;
+        } else {
+            foundFirst = true;
+        }
+
+        if (input[index] == '"') {
+            inQuote = !inQuote;
+            continue;
+        }
+
+        if (inQuote) {
+            continue;
+        }
+
+        if (input[index] == '=' || input[index] == ';') {
+            *tokenLen = (input + index) - *token;
+            if (next && ((index + 1) < inputLen)) {
+                *next = input + index + 1;
+            }
+            foundToken = true;
+            if (foundEquals && input[index] == '=') {
+                *foundEquals = true;
+            }
+            break;
+        }
+    }
+
+    if (!foundToken) {
+        *tokenLen = (input + inputLen) - *token;
+    }
+
+    // strip trailing cruft
+    for (char *index = *token + *tokenLen - 1; index >= *token; --index) {
+        if (*index != ' ' && *index != '\t' && *index != '"') {
+            break;
+        }
+        --(*tokenLen);
+        if (*index == '"') {
+            break;
+        }
+    }
+}
+
+ParsedHeaderValueList::ParsedHeaderValueList(char *t, uint32_t len)
+{
+    char *name = nullptr;
+    uint32_t nameLen = 0;
+    char *value = nullptr;
+    uint32_t valueLen = 0;
+    char *next = nullptr;
+    bool foundEquals;
+
+    while (t) {
+        Tokenize(t, len, &name, &nameLen, &foundEquals, &next);
+        if (next) {
+            len -= next - t;
+        }
+        t = next;
+        if (foundEquals && t) {
+            Tokenize(t, len, &value, &valueLen, nullptr, &next);
+            if (next) {
+                len -= next - t;
+            }
+            t = next;
+        }
+        mValues.AppendElement(ParsedHeaderPair(name, nameLen, value, valueLen));
+        value = name = nullptr;
+        valueLen = nameLen = 0;
+        next = nullptr;
+    }
+}
+
+ParsedHeaderValueListList::ParsedHeaderValueListList(const nsCString &fullHeader)
+    : mFull(fullHeader)
+{
+    char *t = mFull.BeginWriting();
+    uint32_t len = mFull.Length();
+    char *last = t;
+    bool inQuote = false;
+    for (uint32_t index = 0; index < len; ++index) {
+        if (t[index] == '"') {
+            inQuote = !inQuote;
+            continue;
+        }
+        if (inQuote) {
+            continue;
+        }
+        if (t[index] == ',') {
+            mValues.AppendElement(ParsedHeaderValueList(last, (t + index) - last));
+            last = t + index + 1;
+        }
+    }
+    if (!inQuote) {
+        mValues.AppendElement(ParsedHeaderValueList(last, (t + len) - last));
+    }
+}
 
 } // namespace mozilla::net
 } // namespace mozilla
--- a/netwerk/protocol/http/nsHttp.h
+++ b/netwerk/protocol/http/nsHttp.h
@@ -7,16 +7,17 @@
 #ifndef nsHttp_h__
 #define nsHttp_h__
 
 #include <stdint.h>
 #include "prtime.h"
 #include "nsAutoPtr.h"
 #include "nsString.h"
 #include "nsError.h"
+#include "nsTArray.h"
 
 // http version codes
 #define NS_HTTP_VERSION_UNKNOWN  0
 #define NS_HTTP_VERSION_0_9      9
 #define NS_HTTP_VERSION_1_0     10
 #define NS_HTTP_VERSION_1_1     11
 #define NS_HTTP_VERSION_2_0     20
 
@@ -198,12 +199,62 @@ PRTimeToSeconds(PRTime t_usec)
 #define HTTP_LWS " \t"
 #define HTTP_HEADER_VALUE_SEPS HTTP_LWS ","
 
 void EnsureBuffer(nsAutoArrayPtr<char> &buf, uint32_t newSize,
                   uint32_t preserve, uint32_t &objSize);
 void EnsureBuffer(nsAutoArrayPtr<uint8_t> &buf, uint32_t newSize,
                   uint32_t preserve, uint32_t &objSize);
 
+// h2=":443"; ma=60; single
+// results in 3 mValues = {{h2, :443}, {ma, 60}, {single}}
+
+class ParsedHeaderPair
+{
+public:
+    ParsedHeaderPair(const char *name, int32_t nameLen,
+                     const char *val, int32_t valLen)
+    {
+        if (nameLen > 0) {
+            mName.Rebind(name, name + nameLen);
+        }
+        if (valLen > 0) {
+            mValue.Rebind(val, val + valLen);
+        }
+    }
+
+    ParsedHeaderPair(ParsedHeaderPair const &copy)
+        : mName(copy.mName)
+        , mValue(copy.mValue)
+    {
+    }
+
+    nsDependentCSubstring mName;
+    nsDependentCSubstring mValue;
+};
+
+class ParsedHeaderValueList
+{
+public:
+    ParsedHeaderValueList(char *t, uint32_t len);
+    nsTArray<ParsedHeaderPair> mValues;
+
+private:
+    void ParsePair(char *t, uint32_t len);
+    void Tokenize(char *input, uint32_t inputLen, char **token,
+                  uint32_t *tokenLen, bool *foundEquals, char **next);
+};
+
+class ParsedHeaderValueListList
+{
+public:
+    explicit ParsedHeaderValueListList(const nsCString &txt);
+    nsTArray<ParsedHeaderValueList> mValues;
+
+private:
+    nsCString mFull;
+};
+
+
 } // namespace mozilla::net
 } // namespace mozilla
 
 #endif // nsHttp_h__
--- a/netwerk/protocol/http/nsHttpAtomList.h
+++ b/netwerk/protocol/http/nsHttpAtomList.h
@@ -18,16 +18,18 @@
  ******/
 
 HTTP_ATOM(Accept,                    "Accept")
 HTTP_ATOM(Accept_Encoding,           "Accept-Encoding")
 HTTP_ATOM(Accept_Language,           "Accept-Language")
 HTTP_ATOM(Accept_Ranges,             "Accept-Ranges")
 HTTP_ATOM(Age,                       "Age")
 HTTP_ATOM(Allow,                     "Allow")
+HTTP_ATOM(Alternate_Service,         "Alt-Svc")
+HTTP_ATOM(Alternate_Service_Used,    "Alt-Svc-Used")
 HTTP_ATOM(Assoc_Req,                 "Assoc-Req")
 HTTP_ATOM(Authentication,            "Authentication")
 HTTP_ATOM(Authorization,             "Authorization")
 HTTP_ATOM(Cache_Control,             "Cache-Control")
 HTTP_ATOM(Connection,                "Connection")
 HTTP_ATOM(Content_Disposition,       "Content-Disposition")
 HTTP_ATOM(Content_Encoding,          "Content-Encoding")
 HTTP_ATOM(Content_Language,          "Content-Language")
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -59,16 +59,17 @@
 #include "nsURLHelper.h"
 #include "nsISocketTransport.h"
 #include "nsIStreamConverterService.h"
 #include "nsISiteSecurityService.h"
 #include "nsCRT.h"
 #include "nsPerformance.h"
 #include "CacheObserver.h"
 #include "mozilla/Telemetry.h"
+#include "AlternateServices.h"
 
 namespace mozilla { namespace net {
 
 namespace {
 
 // True if the local cache should be bypassed when processing a request.
 #define BYPASS_LOCAL_CACHE(loadFlags) \
         (loadFlags & (nsIRequest::LOAD_BYPASS_CACHE | \
@@ -272,21 +273,21 @@ nsHttpChannel::Connect()
     nsresult rv;
 
     LOG(("nsHttpChannel::Connect [this=%p]\n", this));
 
     // Even if we're in private browsing mode, we still enforce existing STS
     // data (it is read-only).
     // if the connection is not using SSL and either the exact host matches or
     // a superdomain wants to force HTTPS, do it.
-    bool usingSSL = false;
-    rv = mURI->SchemeIs("https", &usingSSL);
+    bool isHttps = false;
+    rv = mURI->SchemeIs("https", &isHttps);
     NS_ENSURE_SUCCESS(rv,rv);
 
-    if (mAllowSTS && !usingSSL) {
+    if (mAllowSTS && !isHttps) {
         // enforce Strict-Transport-Security
         nsISiteSecurityService* sss = gHttpHandler->GetSSService();
         NS_ENSURE_TRUE(sss, NS_ERROR_OUT_OF_MEMORY);
 
         bool isStsHost = false;
         uint32_t flags = mPrivateBrowsing ? nsISocketProvider::NO_PERMANENT_STORAGE : 0;
         rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, mURI, flags,
                               &isStsHost);
@@ -320,17 +321,17 @@ nsHttpChannel::Connect()
         return NS_ERROR_DOCUMENT_NOT_CACHED;
     }
 
     if (!gHttpHandler->UseCache()) {
         return ContinueConnect();
     }
 
     // open a cache entry for this channel...
-    rv = OpenCacheEntry(usingSSL);
+    rv = OpenCacheEntry(isHttps);
 
     // do not continue if asyncOpenCacheEntry is in progress
     if (mCacheEntriesToWaitFor) {
         MOZ_ASSERT(NS_SUCCEEDED(rv), "Unexpected state");
         return NS_OK;
     }
 
     if (NS_FAILED(rv)) {
@@ -1224,16 +1225,137 @@ nsHttpChannel::ProcessSSLInformation()
                                   expireTime);
         LOG(("nsHttpChannel::ProcessSSLInformation [this=%p] "
              "falsestart-rsa permission granted for this host\n", this));
     } else {
         permMgr->RemoveFromPrincipal(principal, "falsestart-rsa");
     }
 }
 
+void
+nsHttpChannel::ProcessAltService()
+{
+    // e.g. Alt-Svc: h2=":443"; ma=60
+    // e.g. Alt-Svc: h2="otherhost:443"
+    // Alt-Svc       = 1#( alternative *( OWS ";" OWS parameter ) )
+    // alternative   = protocol-id "=" alt-authority
+    // protocol-id   = token ; percent-encoded ALPN protocol identifier
+    // alt-authority = quoted-string ;  containing [ uri-host ] ":" port
+
+    if (!gHttpHandler->AllowAltSvc()) {
+        return;
+    }
+
+    nsAutoCString scheme;
+    mURI->GetScheme(scheme);
+    bool isHttp = scheme.Equals(NS_LITERAL_CSTRING("http"));
+    if (!isHttp && !scheme.Equals(NS_LITERAL_CSTRING("https"))) {
+        return;
+    }
+
+    if (isHttp && !gHttpHandler->AllowAltSvcOE()) {
+        return;
+    }
+
+    const char *altSvc;
+    if (!(altSvc = mResponseHead->PeekHeader(nsHttp::Alternate_Service))) {
+        return;
+    }
+
+    LOG(("nsHttpChannel %p Alt-Svc Response Header %s\n", this, altSvc));
+
+    nsCString buf(altSvc);
+    if (!nsHttp::IsReasonableHeaderValue(buf)) {
+        LOG(("Alt-Svc Response Header seems unreasonable - skipping\n"));
+        return;
+    }
+
+    ParsedHeaderValueListList parsedAltSvc(buf);
+    nsRefPtr<AltSvcMapping> mapping;
+
+    nsAutoCString originHost;
+    int32_t originPort = 80;
+    mURI->GetPort(&originPort);
+    if (NS_FAILED(mURI->GetHost(originHost))) {
+        return;
+    }
+    uint32_t now = NowInSeconds(), currentAge = 0;
+    mResponseHead->ComputeCurrentAge(now, mRequestTime, &currentAge);
+
+    for (uint32_t index = 0; index < parsedAltSvc.mValues.Length(); ++index) {
+        uint32_t maxage = 86400; // default
+        nsAutoCString hostname; // Always empty in the header form
+        nsAutoCString npnToken;
+        int32_t portno = originPort;
+
+        for (uint32_t pairIndex = 0;
+             pairIndex < parsedAltSvc.mValues[index].mValues.Length();
+             ++pairIndex) {
+            nsDependentCSubstring &currentName =
+                parsedAltSvc.mValues[index].mValues[pairIndex].mName;
+            nsDependentCSubstring &currentValue =
+                parsedAltSvc.mValues[index].mValues[pairIndex].mValue;
+
+            if (!pairIndex) {
+                // h2=:443
+                npnToken = currentName;
+                int32_t colonIndex = currentValue.FindChar(':');
+                if (colonIndex >= 0) {
+                    portno =
+                        atoi(PromiseFlatCString(currentValue).get() + colonIndex + 1);
+                } else {
+                    colonIndex = 0;
+                }
+                hostname.Assign(currentValue.BeginReading(), colonIndex);
+            } else if (currentName.Equals(NS_LITERAL_CSTRING("ma"))) {
+                maxage = atoi(PromiseFlatCString(currentValue).get());
+                break;
+            }
+        }
+
+        // unescape modifies a c string in place, so afterwards
+        // update nsCString length
+        nsUnescape(npnToken.BeginWriting());
+        npnToken.SetLength(strlen(npnToken.BeginReading()));
+
+        uint32_t spdyIndex;
+        SpdyInformation *spdyInfo = gHttpHandler->SpdyInfo();
+        if (!(NS_SUCCEEDED(spdyInfo->GetNPNIndex(npnToken, &spdyIndex)) &&
+              spdyInfo->ProtocolEnabled(spdyIndex))) {
+            LOG(("Alt Svc %p unknown protocol %s, ignoring", this, npnToken.get()));
+            continue;
+        }
+
+        mapping = new AltSvcMapping(scheme,
+                                    originHost, originPort,
+                                    mUsername, mPrivateBrowsing,
+                                    NowInSeconds() + maxage,
+                                    hostname, portno, npnToken);
+        if (!mapping) {
+            continue;
+        }
+
+        nsCOMPtr<nsIInterfaceRequestor> callbacks;
+        NS_NewNotificationCallbacksAggregation(mCallbacks, mLoadGroup,
+                                               getter_AddRefs(callbacks));
+        if (!callbacks) {
+            return;
+        }
+
+        nsCOMPtr<nsProxyInfo> proxyInfo;
+        if (mProxyInfo) {
+            proxyInfo = do_QueryInterface(mProxyInfo);
+        }
+
+        gHttpHandler->
+            UpdateAltServiceMapping(mapping, proxyInfo, callbacks,
+                                    mCaps & (NS_HTTP_ALLOW_RSA_FALSESTART | NS_HTTP_DISALLOW_SPDY));
+    }
+}
+
 nsresult
 nsHttpChannel::ProcessResponse()
 {
     nsresult rv;
     uint32_t httpStatus = mResponseHead->Status();
 
     // Gather data on whether the transaction and page (if this is
     // the initial page load) is being loaded with SSL.
@@ -1278,16 +1400,20 @@ nsHttpChannel::ProcessResponse()
 
         // reset the authentication's current continuation state because our
         // last authentication attempt has been completed successfully
         mAuthProvider->Disconnect(NS_ERROR_ABORT);
         mAuthProvider = nullptr;
         LOG(("  continuation state has been reset"));
     }
 
+    if (httpStatus < 500) {
+        ProcessAltService();
+    }
+
     bool successfulReval = false;
 
     // handle different server response categories.  Note that we handle
     // caching or not caching of error pages in
     // nsHttpResponseHead::MustValidate; if you change this switch, update that
     // one
     switch (httpStatus) {
     case 200:
@@ -2507,17 +2633,17 @@ IsSubRangeRequest(nsHttpRequestHead &aRe
     if (!aRequestHead.PeekHeader(nsHttp::Range))
         return false;
     nsAutoCString byteRange;
     aRequestHead.GetHeader(nsHttp::Range, byteRange);
     return !byteRange.EqualsLiteral("bytes=0-");
 }
 
 nsresult
-nsHttpChannel::OpenCacheEntry(bool usingSSL)
+nsHttpChannel::OpenCacheEntry(bool isHttps)
 {
     MOZ_EVENT_TRACER_EXEC(this, "net::http::OpenCacheEntry");
 
     // Handle correctly mCacheEntriesToWaitFor
     AutoCacheWaitFlags waitFlags(this);
 
     // Drop this flag here
     mConcurentCacheAccess = 0;
@@ -2842,18 +2968,18 @@ nsHttpChannel::OnCacheEntryCheck(nsICach
                 // also set.
                 MOZ_ASSERT(mLoadFlags & LOAD_ONLY_IF_MODIFIED);
             } else {
                 return rv;
             }
         }
     }
 
-    bool usingSSL = false;
-    rv = mURI->SchemeIs("https", &usingSSL);
+    bool isHttps = false;
+    rv = mURI->SchemeIs("https", &isHttps);
     NS_ENSURE_SUCCESS(rv,rv);
 
     bool doValidation = false;
     bool canAddImsHeader = true;
 
     bool isForcedValid = false;
     entry->GetIsForcedValid(&isForcedValid);
 
@@ -2882,17 +3008,17 @@ nsHttpChannel::OnCacheEntryCheck(nsICach
     }
     // Even if the VALIDATE_NEVER flag is set, there are still some cases in
     // which we must validate the cached response with the server.
     else if (mLoadFlags & nsIRequest::VALIDATE_NEVER) {
         LOG(("VALIDATE_NEVER set\n"));
         // if no-store or if no-cache and ssl, validate cached response (see
         // bug 112564 for an explanation of this logic)
         if (mCachedResponseHead->NoStore() ||
-           (mCachedResponseHead->NoCache() && usingSSL)) {
+           (mCachedResponseHead->NoCache() && isHttps)) {
             LOG(("Validating based on (no-store || (no-cache && ssl)) logic\n"));
             doValidation = true;
         }
         else {
             LOG(("NOT validating based on VALIDATE_NEVER load flag\n"));
             doValidation = false;
         }
     }
@@ -3437,21 +3563,21 @@ nsHttpChannel::ShouldUpdateOfflineCacheE
 }
 
 nsresult
 nsHttpChannel::OpenCacheInputStream(nsICacheEntry* cacheEntry, bool startBuffering,
                                     bool checkingAppCacheEntry)
 {
     nsresult rv;
 
-    bool usingSSL = false;
-    rv = mURI->SchemeIs("https", &usingSSL);
+    bool isHttps = false;
+    rv = mURI->SchemeIs("https", &isHttps);
     NS_ENSURE_SUCCESS(rv,rv);
 
-    if (usingSSL) {
+    if (isHttps) {
         rv = cacheEntry->GetSecurityInfo(
                                       getter_AddRefs(mCachedSecurityInfo));
         if (NS_FAILED(rv)) {
             LOG(("failed to parse security-info [channel=%p, entry=%p]",
                  this, cacheEntry));
             NS_WARNING("failed to parse security-info");
             return rv;
         }
@@ -3795,19 +3921,21 @@ void
 nsHttpChannel::UpdateInhibitPersistentCachingFlag()
 {
     // The no-store directive within the 'Cache-Control:' header indicates
     // that we must not store the response in a persistent cache.
     if (mResponseHead->NoStore())
         mLoadFlags |= INHIBIT_PERSISTENT_CACHING;
 
     // Only cache SSL content on disk if the pref is set
+    bool isHttps;
     if (!gHttpHandler->IsPersistentHttpsCachingEnabled() &&
-        mConnectionInfo->EndToEndSSL())
+        NS_SUCCEEDED(mURI->SchemeIs("https", &isHttps)) && isHttps) {
         mLoadFlags |= INHIBIT_PERSISTENT_CACHING;
+    }
 }
 
 nsresult
 nsHttpChannel::InitOfflineCacheEntry()
 {
     // This function can be called even when we fail to connect (bug 551990)
 
     if (!mOfflineCacheEntry) {
@@ -4568,44 +4696,85 @@ nsHttpChannel::AsyncOpen(nsIStreamListen
 nsresult
 nsHttpChannel::BeginConnect()
 {
     LOG(("nsHttpChannel::BeginConnect [this=%p]\n", this));
     nsresult rv;
 
     // Construct connection info object
     nsAutoCString host;
+    nsAutoCString scheme;
     int32_t port = -1;
-    nsAutoCString username;
-    bool usingSSL = false;
-
-    rv = mURI->SchemeIs("https", &usingSSL);
+    bool isHttps = false;
+
+    rv = mURI->GetScheme(scheme);
+    if (NS_SUCCEEDED(rv))
+        rv = mURI->SchemeIs("https", &isHttps);
     if (NS_SUCCEEDED(rv))
         rv = mURI->GetAsciiHost(host);
     if (NS_SUCCEEDED(rv))
         rv = mURI->GetPort(&port);
     if (NS_SUCCEEDED(rv))
-        mURI->GetUsername(username);
+        mURI->GetUsername(mUsername);
     if (NS_SUCCEEDED(rv))
         rv = mURI->GetAsciiSpec(mSpec);
     if (NS_FAILED(rv))
         return rv;
 
     // Reject the URL if it doesn't specify a host
     if (host.IsEmpty())
         return NS_ERROR_MALFORMED_URI;
     LOG(("host=%s port=%d\n", host.get(), port));
     LOG(("uri=%s\n", mSpec.get()));
 
     nsCOMPtr<nsProxyInfo> proxyInfo;
     if (mProxyInfo)
         proxyInfo = do_QueryInterface(mProxyInfo);
 
-    mConnectionInfo = new nsHttpConnectionInfo(host, port, username, proxyInfo, usingSSL);
-    mRequestHead.SetHTTPS(usingSSL);
+    mRequestHead.SetHTTPS(isHttps);
+    mRequestHead.SetOrigin(scheme, host, port);
+
+    nsRefPtr<AltSvcMapping> mapping;
+    if ((scheme.Equals(NS_LITERAL_CSTRING("http")) ||
+         scheme.Equals(NS_LITERAL_CSTRING("https"))) &&
+        (mapping = gHttpHandler->GetAltServiceMapping(scheme,
+                                                      host, port,
+                                                      mPrivateBrowsing))) {
+        LOG(("nsHttpChannel %p Alt Service Mapping Found %s://%s:%d\n", this,
+             scheme.get(), mapping->AlternateHost().get(),
+             mapping->AlternatePort()));
+        mRequestHead.SetHeader(nsHttp::Alternate_Service_Used, NS_LITERAL_CSTRING("1"));
+
+        nsCOMPtr<nsIConsoleService> consoleService =
+            do_GetService(NS_CONSOLESERVICE_CONTRACTID);
+        if (consoleService) {
+            nsAutoString message(NS_LITERAL_STRING("Alternate Service Mapping found: "));
+            AppendASCIItoUTF16(scheme.get(), message);
+            message.Append(NS_LITERAL_STRING("://"));
+            AppendASCIItoUTF16(host.get(), message);
+            message.Append(NS_LITERAL_STRING(":"));
+            message.AppendInt(port);
+            message.Append(NS_LITERAL_STRING(" to "));
+            AppendASCIItoUTF16(scheme.get(), message);
+            message.Append(NS_LITERAL_STRING("://"));
+            AppendASCIItoUTF16(mapping->AlternateHost().get(), message);
+            message.Append(NS_LITERAL_STRING(":"));
+            message.AppendInt(mapping->AlternatePort());
+            consoleService->LogStringMessage(message.get());
+        }
+
+        LOG(("nsHttpChannel %p Using connection info from altsvc mapping", this));
+        mapping->GetConnectionInfo(getter_AddRefs(mConnectionInfo), proxyInfo);
+        Telemetry::Accumulate(Telemetry::HTTP_TRANSACTION_USE_ALTSVC, true);
+        Telemetry::Accumulate(Telemetry::HTTP_TRANSACTION_USE_ALTSVC_OE, !isHttps);
+    } else {
+        LOG(("nsHttpChannel %p Using default connection info", this));
+        mConnectionInfo = new nsHttpConnectionInfo(host, port, EmptyCString(), mUsername, proxyInfo, isHttps);
+        Telemetry::Accumulate(Telemetry::HTTP_TRANSACTION_USE_ALTSVC, false);
+    }
 
     mAuthProvider =
         do_CreateInstance("@mozilla.org/network/http-channel-auth-provider;1",
                           &rv);
     if (NS_SUCCEEDED(rv))
         rv = mAuthProvider->Init(this);
     if (NS_FAILED(rv))
         return rv;
@@ -4870,18 +5039,20 @@ nsHttpChannel::GetResponseEnd(TimeStamp*
 
 //-----------------------------------------------------------------------------
 // nsHttpChannel::nsIHttpAuthenticableChannel
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 nsHttpChannel::GetIsSSL(bool *aIsSSL)
 {
-    *aIsSSL = mConnectionInfo->EndToEndSSL();
-    return NS_OK;
+    // this attribute is really misnamed - it wants to know if
+    // https:// is being used. SSL might be used to cover http://
+    // in some circumstances (proxies, http/2, etc..)
+    return mURI->SchemeIs("https", aIsSSL);
 }
 
 NS_IMETHODIMP
 nsHttpChannel::GetProxyMethodIsConnect(bool *aProxyMethodIsConnect)
 {
     *aProxyMethodIsConnect = mConnectionInfo->UsingConnect();
     return NS_OK;
 }
--- a/netwerk/protocol/http/nsHttpChannel.h
+++ b/netwerk/protocol/http/nsHttpChannel.h
@@ -199,16 +199,17 @@ private:
     void     SpeculativeConnect();
     nsresult SetupTransaction();
     void     SetupTransactionLoadGroupInfo();
     nsresult CallOnStartRequest();
     nsresult ProcessResponse();
     nsresult ContinueProcessResponse(nsresult);
     nsresult ProcessNormal();
     nsresult ContinueProcessNormal(nsresult);
+    void     ProcessAltService();
     nsresult ProcessNotModified();
     nsresult AsyncProcessRedirection(uint32_t httpStatus);
     nsresult ContinueProcessRedirection(nsresult);
     nsresult ContinueProcessRedirectionAfterFallback(nsresult);
     nsresult ProcessFailedProxyConnect(uint32_t httpStatus);
     nsresult ProcessFallback(bool *waitingForRedirectCallback);
     nsresult ContinueProcessFallback(nsresult);
     void     HandleAsyncAbort();
@@ -421,16 +422,18 @@ private:
 
     // Needed for accurate DNS timing
     nsRefPtr<nsDNSPrefetch>           mDNSPrefetch;
 
     nsresult WaitForRedirectCallback();
     void PushRedirectAsyncFunc(nsContinueRedirectionFunc func);
     void PopRedirectAsyncFunc(nsContinueRedirectionFunc func);
 
+    nsCString mUsername;
+
 protected:
     virtual void DoNotifyListenerCleanup();
 
 private: // cache telemetry
     bool mDidReval;
 };
 
 } } // namespace mozilla::net
--- a/netwerk/protocol/http/nsHttpConnection.cpp
+++ b/netwerk/protocol/http/nsHttpConnection.cpp
@@ -478,35 +478,58 @@ nsHttpConnection::SetupSSL()
 // offer list for both NPN and ALPN. ALPN validation callbacks are made
 // now before the handshake is complete, and NPN validation callbacks
 // are made during the handshake.
 nsresult
 nsHttpConnection::SetupNPNList(nsISSLSocketControl *ssl, uint32_t caps)
 {
     nsTArray<nsCString> protocolArray;
 
-    // The first protocol is used as the fallback if none of the
-    // protocols supported overlap with the server's list.
-    // When using ALPN the advertised preferences are protocolArray indicies
-    // {1, .., N, 0} in decreasing order.
-    // For NPN, In the case of overlap, matching priority is driven by
-    // the order of the server's advertisement - with index 0 used when
-    // there is no match.
-    protocolArray.AppendElement(NS_LITERAL_CSTRING("http/1.1"));
+    nsCString npnToken = mConnInfo->GetNPNToken();
+    if (npnToken.IsEmpty()) {
+        // The first protocol is used as the fallback if none of the
+        // protocols supported overlap with the server's list.
+        // When using ALPN the advertised preferences are protocolArray indicies
+        // {1, .., N, 0} in decreasing order.
+        // For NPN, In the case of overlap, matching priority is driven by
+        // the order of the server's advertisement - with index 0 used when
+        // there is no match.
+        protocolArray.AppendElement(NS_LITERAL_CSTRING("http/1.1"));
 
-    if (gHttpHandler->IsSpdyEnabled() &&
-        !(caps & NS_HTTP_DISALLOW_SPDY)) {
-        LOG(("nsHttpConnection::SetupSSL Allow SPDY NPN selection"));
-        const SpdyInformation *info = gHttpHandler->SpdyInfo();
-        for (uint32_t index = SpdyInformation::kCount; index > 0; --index) {
-            if (info->ProtocolEnabled(index - 1) &&
-                info->ALPNCallbacks[index - 1](ssl)) {
-                protocolArray.AppendElement(info->VersionString[index - 1]);
+        if (gHttpHandler->IsSpdyEnabled() &&
+            !(caps & NS_HTTP_DISALLOW_SPDY)) {
+            LOG(("nsHttpConnection::SetupSSL Allow SPDY NPN selection"));
+            const SpdyInformation *info = gHttpHandler->SpdyInfo();
+            for (uint32_t index = SpdyInformation::kCount; index > 0; --index) {
+                if (info->ProtocolEnabled(index - 1) &&
+                    info->ALPNCallbacks[index - 1](ssl)) {
+                    protocolArray.AppendElement(info->VersionString[index - 1]);
+                }
             }
         }
+    } else {
+        LOG(("nsHttpConnection::SetupSSL limiting NPN selection to %s",
+             npnToken.get()));
+        protocolArray.AppendElement(npnToken);
+    }
+
+    nsCString authHost = mConnInfo->GetAuthenticationHost();
+    int32_t   authPort = mConnInfo->GetAuthenticationPort();
+
+    if (!authHost.IsEmpty()) {
+        ssl->SetAuthenticationName(authHost);
+        ssl->SetAuthenticationPort(authPort);
+    }
+
+    if (mConnInfo->GetRelaxed()) { // http:// over tls
+        if (authHost.IsEmpty() || authHost.Equals(mConnInfo->GetHost())) {
+            LOG(("nsHttpConnection::SetupSSL %p TLS-Relaxed "
+                 "with Same Host Auth Bypass", this));
+            ssl->SetBypassAuthentication(true);
+        }
     }
 
     nsresult rv = ssl->SetNPNList(protocolArray);
     LOG(("nsHttpConnection::SetupNPNList %p %x\n",this, rv));
     return rv;
 }
 
 nsresult
@@ -526,16 +549,24 @@ nsHttpConnection::AddTransaction(nsAHttp
     bool needTunnel = transCI->UsingHttpsProxy();
     needTunnel = needTunnel && !mTLSFilter;
     needTunnel = needTunnel && transCI->UsingConnect();
     needTunnel = needTunnel && httpTransaction->QueryHttpTransaction();
 
     LOG(("nsHttpConnection::AddTransaction for SPDY%s",
          needTunnel ? " over tunnel" : ""));
 
+    // do a runtime check here just for defense in depth
+    if (transCI->GetRelaxed() &&
+        httpTransaction->RequestHead() && httpTransaction->RequestHead()->IsHTTPS()) {
+        LOG(("This Cannot happen - https on relaxed tls stream\n"));
+        MOZ_ASSERT(false, "https:// on tls relaxed");
+        return NS_ERROR_FAILURE;
+    }
+
     if (!mSpdySession->AddStream(httpTransaction, priority,
                                  needTunnel, mCallbacks)) {
         MOZ_ASSERT(false); // this cannot happen!
         httpTransaction->Close(NS_ERROR_ABORT);
         return NS_ERROR_FAILURE;
     }
 
     ResumeSend();
@@ -1402,16 +1433,22 @@ nsHttpConnection::EndIdleMonitoring()
     if (mIdleMonitoring) {
         LOG(("Leaving Idle Monitoring Mode [this=%p]", this));
         mIdleMonitoring = false;
         if (mSocketIn)
             mSocketIn->AsyncWait(nullptr, 0, 0, nullptr);
     }
 }
 
+uint32_t
+nsHttpConnection::Version()
+{
+    return mUsingSpdyVersion  ? mUsingSpdyVersion : mLastHttpResponseVersion;
+}
+
 //-----------------------------------------------------------------------------
 // nsHttpConnection <private>
 //-----------------------------------------------------------------------------
 
 void
 nsHttpConnection::CloseTransaction(nsAHttpTransaction *trans, nsresult reason)
 {
     LOG(("nsHttpConnection::CloseTransaction[this=%p trans=%p reason=%x]\n",
--- a/netwerk/protocol/http/nsHttpConnection.h
+++ b/netwerk/protocol/http/nsHttpConnection.h
@@ -206,16 +206,18 @@ public:
     void CheckForTraffic(bool check);
 
     // NoTraffic() returns true if there's been no traffic on the (non-spdy)
     // connection since CheckForTraffic() was called.
     bool NoTraffic() {
         return mTrafficStamp &&
             (mTrafficCount == (mTotalBytesWritten + mTotalBytesRead));
     }
+    // override of nsAHttpConnection
+    virtual uint32_t Version();
 
 private:
     // Value (set in mTCPKeepaliveConfig) indicates which set of prefs to use.
     enum TCPKeepaliveConfig {
       kTCPKeepaliveDisabled = 0,
       kTCPKeepaliveShortLivedConfig,
       kTCPKeepaliveLongLivedConfig
     };
--- a/netwerk/protocol/http/nsHttpConnectionInfo.cpp
+++ b/netwerk/protocol/http/nsHttpConnectionInfo.cpp
@@ -15,26 +15,59 @@
 
 #include "nsHttpConnectionInfo.h"
 #include "mozilla/net/DNS.h"
 #include "prnetdb.h"
 
 namespace mozilla {
 namespace net {
 
-nsHttpConnectionInfo::nsHttpConnectionInfo(const nsACString &host, int32_t port,
+nsHttpConnectionInfo::nsHttpConnectionInfo(const nsACString &physicalHost,
+                                           int32_t physicalPort,
+                                           const nsACString &npnToken,
                                            const nsACString &username,
-                                           nsProxyInfo* proxyInfo,
+                                           nsProxyInfo *proxyInfo,
                                            bool endToEndSSL)
-    : mUsername(username)
-    , mProxyInfo(proxyInfo)
-    , mEndToEndSSL(endToEndSSL)
-    , mUsingConnect(false)
+    : mAuthenticationPort(443)
+{
+    Init(physicalHost, physicalPort, npnToken, username, proxyInfo, endToEndSSL);
+}
+
+nsHttpConnectionInfo::nsHttpConnectionInfo(const nsACString &physicalHost,
+                                           int32_t physicalPort,
+                                           const nsACString &npnToken,
+                                           const nsACString &username,
+                                           nsProxyInfo *proxyInfo,
+                                           const nsACString &logicalHost,
+                                           int32_t logicalPort)
+
 {
-    LOG(("Creating nsHttpConnectionInfo @%x\n", this));
+    mEndToEndSSL = true; // so DefaultPort() works
+    mAuthenticationPort = logicalPort == -1 ? DefaultPort() : logicalPort;
+
+    if (!physicalHost.Equals(logicalHost) || (physicalPort != logicalPort)) {
+        mAuthenticationHost = logicalHost;
+    }
+    Init(physicalHost, physicalPort, npnToken, username, proxyInfo, true);
+}
+
+void
+nsHttpConnectionInfo::Init(const nsACString &host, int32_t port,
+                           const nsACString &npnToken,
+                           const nsACString &username,
+                           nsProxyInfo* proxyInfo,
+                           bool e2eSSL)
+{
+    LOG(("Init nsHttpConnectionInfo @%p\n", this));
+
+    mUsername = username;
+    mProxyInfo = proxyInfo;
+    mEndToEndSSL = e2eSSL;
+    mUsingConnect = false;
+    mNPNToken = npnToken;
 
     mUsingHttpsProxy = (proxyInfo && proxyInfo->IsHTTPS());
     mUsingHttpProxy = mUsingHttpsProxy || (proxyInfo && proxyInfo->IsHTTP());
 
     if (mUsingHttpProxy) {
         mUsingConnect = mEndToEndSSL;  // SSL always uses CONNECT
         uint32_t resolveFlags = 0;
         if (NS_SUCCEEDED(mProxyInfo->GetResolveFlags(&resolveFlags)) &&
@@ -73,18 +106,19 @@ nsHttpConnectionInfo::SetOriginServer(co
         keyPort = Port();
     }
 
     // The hashkey has 4 fields followed by host connection info
     // byte 0 is P/T/. {P,T} for Plaintext/TLS Proxy over HTTP
     // byte 1 is S/. S is for end to end ssl such as https:// uris
     // byte 2 is A/. A is for an anonymous channel (no cookies, etc..)
     // byte 3 is P/. P is for a private browising channel
-    mHashKey.AssignLiteral("....");
+    // byte 4 is R/. R is for 'relaxed' unauthed TLS for http:// uris
 
+    mHashKey.AssignLiteral(".....");
     mHashKey.Append(keyHost);
     mHashKey.Append(':');
     mHashKey.AppendInt(keyPort);
     if (!mUsername.IsEmpty()) {
         mHashKey.Append('[');
         mHashKey.Append(mUsername);
         mHashKey.Append(']');
     }
@@ -113,44 +147,86 @@ nsHttpConnectionInfo::SetOriginServer(co
         mHashKey.AppendLiteral(" (");
         mHashKey.Append(ProxyType());
         mHashKey.Append(':');
         mHashKey.Append(ProxyHost());
         mHashKey.Append(':');
         mHashKey.AppendInt(ProxyPort());
         mHashKey.Append(')');
     }
+
+    if(!mAuthenticationHost.IsEmpty()) {
+        mHashKey.AppendLiteral(" <TLS-LOGIC ");
+        mHashKey.Append(mAuthenticationHost);
+        mHashKey.Append(':');
+        mHashKey.AppendInt(mAuthenticationPort);
+        mHashKey.Append('>');
+    }
+
+    if (!mNPNToken.IsEmpty()) {
+        mHashKey.AppendLiteral(" {NPN-TOKEN ");
+        mHashKey.Append(mNPNToken);
+        mHashKey.AppendLiteral("}");
+    }
 }
 
 nsHttpConnectionInfo*
 nsHttpConnectionInfo::Clone() const
 {
-    nsHttpConnectionInfo* clone = new nsHttpConnectionInfo(mHost, mPort, mUsername, mProxyInfo, mEndToEndSSL);
+    nsHttpConnectionInfo *clone;
+    if (mAuthenticationHost.IsEmpty()) {
+        clone = new nsHttpConnectionInfo(mHost, mPort, mNPNToken, mUsername, mProxyInfo, mEndToEndSSL);
+    } else {
+        MOZ_ASSERT(mEndToEndSSL);
+        clone = new nsHttpConnectionInfo(mHost, mPort, mNPNToken, mUsername, mProxyInfo,
+                                         mAuthenticationHost,
+                                         mAuthenticationPort);
+    }
 
-    // Make sure the anonymous and private flags are transferred!
+    // Make sure the anonymous, relaxed, and private flags are transferred
     clone->SetAnonymous(GetAnonymous());
     clone->SetPrivate(GetPrivate());
+    clone->SetRelaxed(GetRelaxed());
     MOZ_ASSERT(clone->Equals(this));
+
     return clone;
 }
 
+void
+nsHttpConnectionInfo::CloneAsDirectRoute(nsHttpConnectionInfo **outCI)
+{
+    if (mAuthenticationHost.IsEmpty()) {
+        *outCI = Clone();
+        return;
+    }
+
+    nsRefPtr<nsHttpConnectionInfo> clone =
+        new nsHttpConnectionInfo(mAuthenticationHost, mAuthenticationPort,
+                                 EmptyCString(), mUsername, mProxyInfo, mEndToEndSSL);
+    // Make sure the anonymous, relaxed, and private flags are transferred
+    clone->SetAnonymous(GetAnonymous());
+    clone->SetPrivate(GetPrivate());
+    clone->SetRelaxed(GetRelaxed());
+    clone.forget(outCI);
+}
+
 nsresult
 nsHttpConnectionInfo::CreateWildCard(nsHttpConnectionInfo **outParam)
 {
     // T???mozilla.org:443 (https:proxy.ducksong.com:3128) [specifc form]
     // TS??*:0 (https:proxy.ducksong.com:3128)   [wildcard form]
 
     if (!mUsingHttpsProxy) {
         MOZ_ASSERT(false);
         return NS_ERROR_NOT_IMPLEMENTED;
     }
 
     nsRefPtr<nsHttpConnectionInfo> clone;
     clone = new nsHttpConnectionInfo(NS_LITERAL_CSTRING("*"), 0,
-                                     mUsername, mProxyInfo, true);
+                                     mNPNToken, mUsername, mProxyInfo, true);
     // Make sure the anonymous and private flags are transferred!
     clone->SetAnonymous(GetAnonymous());
     clone->SetPrivate(GetPrivate());
     clone.forget(outParam);
     return NS_OK;
 }
 
 bool
--- a/netwerk/protocol/http/nsHttpConnectionInfo.h
+++ b/netwerk/protocol/http/nsHttpConnectionInfo.h
@@ -27,39 +27,48 @@ extern PRLogModuleInfo *gHttpLog;
 // and multiplex non tunneled transactions at the same time, so they have a
 // special wildcard CI that accepts all origins through that proxy.
 
 namespace mozilla { namespace net {
 
 class nsHttpConnectionInfo
 {
 public:
-    nsHttpConnectionInfo(const nsACString &host, int32_t port,
+    nsHttpConnectionInfo(const nsACString &physicalHost,
+                         int32_t physicalPort,
+                         const nsACString &npnToken,
                          const nsACString &username,
-                         nsProxyInfo* proxyInfo,
+                         nsProxyInfo *proxyInfo,
                          bool endToEndSSL = false);
 
+    // this version must use TLS and you may supply the domain
+    // information to be validated
+    nsHttpConnectionInfo(const nsACString &physicalHost,
+                         int32_t physicalPort,
+                         const nsACString &npnToken,
+                         const nsACString &username,
+                         nsProxyInfo *proxyInfo,
+                         const nsACString &logicalHost,
+                         int32_t logicalPort);
+
 private:
     virtual ~nsHttpConnectionInfo()
     {
         PR_LOG(gHttpLog, 4, ("Destroying nsHttpConnectionInfo @%x\n", this));
     }
 
 public:
     const nsAFlatCString &HashKey() const { return mHashKey; }
 
-    void SetOriginServer(const nsACString &host, int32_t port);
-
-    void SetOriginServer(const char *host, int32_t port)
-    {
-        SetOriginServer(nsDependentCString(host), port);
-    }
+    const nsCString &GetAuthenticationHost() const { return mAuthenticationHost; }
+    int32_t GetAuthenticationPort() const { return mAuthenticationPort; }
 
     // OK to treat these as an infalible allocation
     nsHttpConnectionInfo* Clone() const;
+    void CloneAsDirectRoute(nsHttpConnectionInfo **outParam);
     nsresult CreateWildCard(nsHttpConnectionInfo **outParam);
 
     const char *ProxyHost() const { return mProxyInfo ? mProxyInfo->Host().get() : nullptr; }
     int32_t     ProxyPort() const { return mProxyInfo ? mProxyInfo->Port() : -1; }
     const char *ProxyType() const { return mProxyInfo ? mProxyInfo->Type() : nullptr; }
 
     // Compare this connection info to another...
     // Two connections are 'equal' if they end up talking the same
@@ -78,18 +87,22 @@ public:
     const char   *Username() const       { return mUsername.get(); }
     nsProxyInfo  *ProxyInfo()            { return mProxyInfo; }
     int32_t       DefaultPort() const    { return mEndToEndSSL ? NS_HTTPS_DEFAULT_PORT : NS_HTTP_DEFAULT_PORT; }
     void          SetAnonymous(bool anon)
                                          { mHashKey.SetCharAt(anon ? 'A' : '.', 2); }
     bool          GetAnonymous() const   { return mHashKey.CharAt(2) == 'A'; }
     void          SetPrivate(bool priv)  { mHashKey.SetCharAt(priv ? 'P' : '.', 3); }
     bool          GetPrivate() const     { return mHashKey.CharAt(3) == 'P'; }
+    void          SetRelaxed(bool relaxed)
+                                       { mHashKey.SetCharAt(relaxed ? 'R' : '.', 4); }
+    bool          GetRelaxed() const   { return mHashKey.CharAt(4) == 'R'; }
 
     const nsCString &GetHost() { return mHost; }
+    const nsCString &GetNPNToken() { return mNPNToken; }
 
     // Returns true for any kind of proxy (http, socks, https, etc..)
     bool UsingProxy();
 
     // Returns true when proxying over HTTP or HTTPS
     bool UsingHttpProxy() const { return mUsingHttpProxy || mUsingHttpsProxy; }
 
     // Returns true when proxying over HTTPS
@@ -103,25 +116,36 @@ public:
 
     // Returns true when CONNECT is used to tunnel through the proxy (e.g. https:// or ws://)
     bool UsingConnect() const { return mUsingConnect; }
 
     // Returns true when mHost is an RFC1918 literal.
     bool HostIsLocalIPLiteral() const;
 
 private:
+    void Init(const nsACString &host,
+              int32_t port,
+              const nsACString &npnToken,
+              const nsACString &username,
+              nsProxyInfo* proxyInfo,
+              bool EndToEndSSL);
+    void SetOriginServer(const nsACString &host, int32_t port);
+
     nsCString              mHashKey;
     nsCString              mHost;
     int32_t                mPort;
     nsCString              mUsername;
+    nsCString              mAuthenticationHost;
+    int32_t                mAuthenticationPort;
     nsCOMPtr<nsProxyInfo>  mProxyInfo;
     bool                   mUsingHttpProxy;
     bool                   mUsingHttpsProxy;
     bool                   mEndToEndSSL;
     bool                   mUsingConnect;  // if will use CONNECT with http proxy
+    nsCString              mNPNToken;
 
 // for nsRefPtr
     NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsHttpConnectionInfo)
 };
 
 }} // namespace mozilla::net
 
 #endif // nsHttpConnectionInfo_h__
--- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp
@@ -377,65 +377,75 @@ public:
 public: // intentional!
     nsRefPtr<NullHttpTransaction> mTrans;
 
     bool mOverridesOK;
     uint32_t mParallelSpeculativeConnectLimit;
     bool mIgnoreIdle;
     bool mIgnorePossibleSpdyConnections;
     bool mIsFromPredictor;
+    bool mAllow1918;
 
     // As above, added manually so we can use nsRefPtr without inheriting from
     // nsISupports
 protected:
     ThreadSafeAutoRefCnt mRefCnt;
     NS_DECL_OWNINGTHREAD
 };
 
 NS_IMPL_ADDREF(SpeculativeConnectArgs)
 NS_IMPL_RELEASE(SpeculativeConnectArgs)
 
 nsresult
 nsHttpConnectionMgr::SpeculativeConnect(nsHttpConnectionInfo *ci,
                                         nsIInterfaceRequestor *callbacks,
-                                        uint32_t caps)
+                                        uint32_t caps,
+                                        NullHttpTransaction *nullTransaction)
 {
     MOZ_ASSERT(NS_IsMainThread(), "nsHttpConnectionMgr::SpeculativeConnect called off main thread!");
 
     LOG(("nsHttpConnectionMgr::SpeculativeConnect [ci=%s]\n",
          ci->HashKey().get()));
 
+    nsCOMPtr<nsISpeculativeConnectionOverrider> overrider =
+        do_GetInterface(callbacks);
+
+    bool allow1918 = false;
+    if (overrider) {
+        overrider->GetAllow1918(&allow1918);
+    }
+
     // Hosts that are Local IP Literals should not be speculatively
     // connected - Bug 853423.
-    if (ci && ci->HostIsLocalIPLiteral()) {
+    if ((!allow1918) && ci && ci->HostIsLocalIPLiteral()) {
         LOG(("nsHttpConnectionMgr::SpeculativeConnect skipping RFC1918 "
              "address [%s]", ci->Host()));
         return NS_OK;
     }
 
     nsRefPtr<SpeculativeConnectArgs> args = new SpeculativeConnectArgs();
 
     // Wrap up the callbacks and the target to ensure they're released on the target
     // thread properly.
     nsCOMPtr<nsIInterfaceRequestor> wrappedCallbacks;
     NS_NewInterfaceRequestorAggregation(callbacks, nullptr, getter_AddRefs(wrappedCallbacks));
 
     caps |= ci->GetAnonymous() ? NS_HTTP_LOAD_ANONYMOUS : 0;
-    args->mTrans = new NullHttpTransaction(ci, wrappedCallbacks, caps);
-
-    nsCOMPtr<nsISpeculativeConnectionOverrider> overrider =
-        do_GetInterface(callbacks);
+    args->mTrans =
+        nullTransaction ? nullTransaction : new NullHttpTransaction(ci, wrappedCallbacks, caps);
+
     if (overrider) {
         args->mOverridesOK = true;
         overrider->GetParallelSpeculativeConnectLimit(
             &args->mParallelSpeculativeConnectLimit);
         overrider->GetIgnoreIdle(&args->mIgnoreIdle);
         overrider->GetIgnorePossibleSpdyConnections(
             &args->mIgnorePossibleSpdyConnections);
         overrider->GetIsFromPredictor(&args->mIsFromPredictor);
+        overrider->GetAllow1918(&args->mAllow1918);
     }
 
     nsresult rv =
         PostEvent(&nsHttpConnectionMgr::OnMsgSpeculativeConnect, 0, args);
     if (NS_SUCCEEDED(rv))
         unused << args.forget();
     return rv;
 }
@@ -1292,17 +1302,17 @@ nsHttpConnectionMgr::ReportFailedToProce
     if (NS_SUCCEEDED(rv))
         uri->GetUsername(username);
     if (NS_FAILED(rv) || !isHttp || host.IsEmpty())
         return;
 
     // report the event for all the permutations of anonymous and
     // private versions of this host
     nsRefPtr<nsHttpConnectionInfo> ci =
-        new nsHttpConnectionInfo(host, port, username, nullptr, usingSSL);
+        new nsHttpConnectionInfo(host, port, EmptyCString(), username, nullptr, usingSSL);
     ci->SetAnonymous(false);
     ci->SetPrivate(false);
     PipelineFeedbackInfo(ci, RedCorruptedContent, nullptr, 0);
 
     ci = ci->Clone();
     ci->SetAnonymous(false);
     ci->SetPrivate(true);
     PipelineFeedbackInfo(ci, RedCorruptedContent, nullptr, 0);
@@ -1462,17 +1472,25 @@ nsHttpConnectionMgr::MakeNewConnection(n
             // We've found a speculative connection in the half
             // open list. Remove the speculative bit from it and that
             // connection can later be used for this transaction
             // (or another one in the pending queue) - we don't
             // need to open a new connection here.
             LOG(("nsHttpConnectionMgr::MakeNewConnection [ci = %s]\n"
                  "Found a speculative half open connection\n",
                  ent->mConnInfo->HashKey().get()));
+
+            uint32_t flags;
             ent->mHalfOpens[i]->SetSpeculative(false);
+            nsISocketTransport *transport = ent->mHalfOpens[i]->SocketTransport();
+            if (transport && NS_SUCCEEDED(transport->GetConnectionFlags(&flags))) {
+                flags &= ~nsISocketTransport::DISABLE_RFC1918;
+                transport->SetConnectionFlags(flags);
+            }
+
             Telemetry::AutoCounter<Telemetry::HTTPCONNMGR_USED_SPECULATIVE_CONN> usedSpeculativeConn;
             ++usedSpeculativeConn;
 
             if (ent->mHalfOpens[i]->IsFromPredictor()) {
               Telemetry::AutoCounter<Telemetry::PREDICTOR_TOTAL_PRECONNECTS_USED> totalPreconnectsUsed;
               ++totalPreconnectsUsed;
             }
 
@@ -1505,17 +1523,17 @@ nsHttpConnectionMgr::MakeNewConnection(n
 
     if ((mNumIdleConns + mNumActiveConns + 1 >= mMaxConns) &&
         mNumActiveConns && gHttpHandler->IsSpdyEnabled())
         mCT.Enumerate(PurgeExcessSpdyConnectionsCB, this);
 
     if (AtActiveConnectionLimit(ent, trans->Caps()))
         return NS_ERROR_NOT_AVAILABLE;
 
-    nsresult rv = CreateTransport(ent, trans, trans->Caps(), false);
+    nsresult rv = CreateTransport(ent, trans, trans->Caps(), false, false, true);
     if (NS_FAILED(rv)) {
         /* hard failure */
         LOG(("nsHttpConnectionMgr::MakeNewConnection [ci = %s trans = %p] "
              "CreateTransport() hard failure.\n",
              ent->mConnInfo->HashKey().get(), trans));
         trans->Close(rv);
         if (rv == NS_ERROR_NOT_AVAILABLE)
             rv = NS_ERROR_FAILURE;
@@ -2132,23 +2150,25 @@ nsHttpConnectionMgr::RecvdConnect()
     ConditionallyStopTimeoutTick();
 }
 
 nsresult
 nsHttpConnectionMgr::CreateTransport(nsConnectionEntry *ent,
                                      nsAHttpTransaction *trans,
                                      uint32_t caps,
                                      bool speculative,
-                                     bool isFromPredictor)
+                                     bool isFromPredictor,
+                                     bool allow1918)
 {
     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
 
     nsRefPtr<nsHalfOpenSocket> sock = new nsHalfOpenSocket(ent, trans, caps);
     if (speculative) {
         sock->SetSpeculative(true);
+        sock->SetAllow1918(allow1918);
         Telemetry::AutoCounter<Telemetry::HTTPCONNMGR_TOTAL_SPECULATIVE_CONN> totalSpeculativeConn;
         ++totalSpeculativeConn;
 
         if (isFromPredictor) {
           sock->SetIsFromPredictor(true);
           Telemetry::AutoCounter<Telemetry::PREDICTOR_TOTAL_PRECONNECTS_CREATED> totalPreconnectsCreated;
           ++totalPreconnectsCreated;
         }
@@ -2923,30 +2943,33 @@ nsHttpConnectionMgr::OnMsgSpeculativeCon
     if (preferredEntry)
         ent = preferredEntry;
 
     uint32_t parallelSpeculativeConnectLimit =
         gHttpHandler->ParallelSpeculativeConnectLimit();
     bool ignorePossibleSpdyConnections = false;
     bool ignoreIdle = false;
     bool isFromPredictor = false;
+    bool allow1918 = false;
 
     if (args->mOverridesOK) {
         parallelSpeculativeConnectLimit = args->mParallelSpeculativeConnectLimit;
         ignorePossibleSpdyConnections = args->mIgnorePossibleSpdyConnections;
         ignoreIdle = args->mIgnoreIdle;
         isFromPredictor = args->mIsFromPredictor;
+        allow1918 = args->mAllow1918;
     }
 
+    bool keepAlive = args->mTrans->Caps() & NS_HTTP_ALLOW_KEEPALIVE;
     if (mNumHalfOpenConns < parallelSpeculativeConnectLimit &&
         ((ignoreIdle && (ent->mIdleConns.Length() < parallelSpeculativeConnectLimit)) ||
          !ent->mIdleConns.Length()) &&
-        !RestrictConnections(ent, ignorePossibleSpdyConnections) &&
+        !(keepAlive && RestrictConnections(ent, ignorePossibleSpdyConnections)) &&
         !AtActiveConnectionLimit(ent, args->mTrans->Caps())) {
-        CreateTransport(ent, args->mTrans, args->mTrans->Caps(), true, isFromPredictor);
+        CreateTransport(ent, args->mTrans, args->mTrans->Caps(), true, isFromPredictor, allow1918);
     }
     else {
         LOG(("  Transport not created due to existing connection count\n"));
     }
 }
 
 bool
 nsHttpConnectionMgr::nsConnectionHandle::IsPersistent()
@@ -2970,32 +2993,32 @@ nsresult
 nsHttpConnectionMgr::nsConnectionHandle::PushBack(const char *buf, uint32_t bufLen)
 {
     return mConn->PushBack(buf, bufLen);
 }
 
 
 //////////////////////// nsHalfOpenSocket
 
-
 NS_IMPL_ISUPPORTS(nsHttpConnectionMgr::nsHalfOpenSocket,
                   nsIOutputStreamCallback,
                   nsITransportEventSink,
                   nsIInterfaceRequestor,
                   nsITimerCallback)
 
 nsHttpConnectionMgr::
 nsHalfOpenSocket::nsHalfOpenSocket(nsConnectionEntry *ent,
                                    nsAHttpTransaction *trans,
                                    uint32_t caps)
     : mEnt(ent)
     , mTransaction(trans)
     , mCaps(caps)
     , mSpeculative(false)
     , mIsFromPredictor(false)
+    , mAllow1918(true)
     , mHasConnected(false)
     , mPrimaryConnectedOK(false)
     , mBackupConnectedOK(false)
 {
     MOZ_ASSERT(ent && trans, "constructor with null arguments");
     LOG(("Creating nsHalfOpenSocket [this=%p trans=%p ent=%s key=%s]\n",
          this, trans, ent->mConnInfo->Host(), ent->mConnInfo->HashKey().get()));
 }
@@ -3061,17 +3084,17 @@ nsHalfOpenSocket::SetupStreams(nsISocket
     if (mEnt->mPreferIPv6) {
         tmpFlags |= nsISocketTransport::DISABLE_IPV4;
     }
     else if (mEnt->mPreferIPv4 ||
              (isBackup && gHttpHandler->FastFallbackToIPv4())) {
         tmpFlags |= nsISocketTransport::DISABLE_IPV6;
     }
 
-    if (IsSpeculative()) {
+    if (!Allow1918()) {
         tmpFlags |= nsISocketTransport::DISABLE_RFC1918;
     }
 
     socketTransport->SetConnectionFlags(tmpFlags);
 
     socketTransport->SetQoSBits(gHttpHandler->GetQoSBits());
 
     rv = socketTransport->SetEventSink(this, nullptr);
@@ -3125,16 +3148,18 @@ nsHttpConnectionMgr::nsHalfOpenSocket::S
         mSocketTransport = nullptr;
     }
     return rv;
 }
 
 nsresult
 nsHttpConnectionMgr::nsHalfOpenSocket::SetupBackupStreams()
 {
+    MOZ_ASSERT(mTransaction && !mTransaction->IsNullTransaction());
+
     mBackupSynStarted = TimeStamp::Now();
     nsresult rv = SetupStreams(getter_AddRefs(mBackupTransport),
                                getter_AddRefs(mBackupStreamIn),
                                getter_AddRefs(mBackupStreamOut),
                                true);
     LOG(("nsHalfOpenSocket::SetupBackupStream [this=%p ent=%s rv=%x]",
          this, mEnt->mConnInfo->Host(), rv));
     if (NS_FAILED(rv)) {
@@ -3147,18 +3172,18 @@ nsHttpConnectionMgr::nsHalfOpenSocket::S
     return rv;
 }
 
 void
 nsHttpConnectionMgr::nsHalfOpenSocket::SetupBackupTimer()
 {
     uint16_t timeout = gHttpHandler->GetIdleSynTimeout();
     MOZ_ASSERT(!mSynTimer, "timer already initd");
-
-    if (timeout && !mTransaction->IsDone()) {
+    if (timeout && !mTransaction->IsDone() &&
+        !mTransaction->IsNullTransaction()) {
         // Setup the timer that will establish a backup socket
         // if we do not get a writable event on the main one.
         // We do this because a lost SYN takes a very long time
         // to repair at the TCP level.
         //
         // Failure to setup the timer is something we can live with,
         // so don't return an error in that case.
         nsresult rv;
@@ -3334,18 +3359,17 @@ nsHalfOpenSocket::OnOutputStreamReady(ns
     index = mEnt->mPendingQ.IndexOf(mTransaction);
     if (index != -1) {
         MOZ_ASSERT(!mSpeculative,
                    "Speculative Half Open found mTransaction");
         nsRefPtr<nsHttpTransaction> temp = dont_AddRef(mEnt->mPendingQ[index]);
         mEnt->mPendingQ.RemoveElementAt(index);
         gHttpHandler->ConnMgr()->AddActiveConn(conn, mEnt);
         rv = gHttpHandler->ConnMgr()->DispatchTransaction(mEnt, temp, conn);
-    }
-    else {
+    } else {
         // this transaction was dispatched off the pending q before all the
         // sockets established themselves.
 
         // After about 1 second allow for the possibility of restarting a
         // transaction due to server close. Keep at sub 1 second as that is the
         // minimum granularity we can expect a server to be timing out with.
         conn->SetIsReusedAfter(950);
 
@@ -3353,27 +3377,32 @@ nsHalfOpenSocket::OnOutputStreamReady(ns
         // then form a null transaction to drive the SSL handshake to
         // completion. Afterwards the connection will be 100% ready for the next
         // transaction to use it. Make an exception for SSL tunneled HTTP proxy as the
         // NullHttpTransaction does not know how to drive Connect
         if (mEnt->mConnInfo->FirstHopSSL() && !mEnt->mPendingQ.Length() &&
             !mEnt->mConnInfo->UsingConnect()) {
             LOG(("nsHalfOpenSocket::OnOutputStreamReady null transaction will "
                  "be used to finish SSL handshake on conn %p\n", conn.get()));
-            nsRefPtr<NullHttpTransaction>  trans =
-                new NullHttpTransaction(mEnt->mConnInfo,
-                                        callbacks,
-                                        mCaps & ~NS_HTTP_ALLOW_PIPELINING);
+            nsRefPtr<nsAHttpTransaction> trans;
+            if (mTransaction->IsNullTransaction()) {
+                // null transactions cannot be put in the entry queue, so that
+                // explains why it is not present.
+                trans = mTransaction;
+            } else {
+                trans = new NullHttpTransaction(mEnt->mConnInfo,
+                                                callbacks,
+                                                mCaps & ~NS_HTTP_ALLOW_PIPELINING);
+            }
 
             gHttpHandler->ConnMgr()->AddActiveConn(conn, mEnt);
             conn->Classify(nsAHttpTransaction::CLASS_SOLO);
             rv = gHttpHandler->ConnMgr()->
                 DispatchAbstractTransaction(mEnt, trans, mCaps, conn, 0);
-        }
-        else {
+        } else {
             // otherwise just put this in the persistent connection pool
             LOG(("nsHalfOpenSocket::OnOutputStreamReady no transaction match "
                  "returning conn %p to pool\n", conn.get()));
             nsRefPtr<nsHttpConnection> copy(conn);
             // forget() to effectively addref because onmsg*() will drop a ref
             gHttpHandler->ConnMgr()->OnMsgReclaimConnection(
                 0, conn.forget().take());
         }
--- a/netwerk/protocol/http/nsHttpConnectionMgr.h
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.h
@@ -11,30 +11,33 @@
 #include "nsTArray.h"
 #include "nsThreadUtils.h"
 #include "nsClassHashtable.h"
 #include "nsDataHashtable.h"
 #include "nsAutoPtr.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Attributes.h"
+#include "AlternateServices.h"
 
 #include "nsIObserver.h"
 #include "nsITimer.h"
 
 class nsIHttpUpgradeListener;
 
 namespace mozilla {
 namespace net {
 class EventTokenBucket;
+class NullHttpTransaction;
 struct HttpRetParams;
 
 //-----------------------------------------------------------------------------
 
 class nsHttpConnectionMgr : public nsIObserver
+                          , public AltSvcCache
 {
 public:
     NS_DECL_THREADSAFE_ISUPPORTS
     NS_DECL_NSIOBSERVER
 
     // parameter names
     enum nsParamName {
         MAX_CONNECTIONS,
@@ -110,17 +113,18 @@ public:
     // called to indicate a transaction for the connectionInfo is likely coming
     // soon. The connection manager may use this information to start a TCP
     // and/or SSL level handshake for that resource immediately so that it is
     // ready when the transaction is submitted. No obligation is taken on by the
     // connection manager, nor is the submitter obligated to actually submit a
     // real transaction for this connectionInfo.
     nsresult SpeculativeConnect(nsHttpConnectionInfo *,
                                 nsIInterfaceRequestor *,
-                                uint32_t caps = 0);
+                                uint32_t caps = 0,
+                                NullHttpTransaction * = nullptr);
 
     // called when a connection is done processing a transaction.  if the
     // connection can be reused then it will be added to the idle list, else
     // it will be closed.
     nsresult ReclaimConnection(nsHttpConnection *conn);
 
     // called by the main thread to execute the taketransport() logic on the
     // socket thread after a 101 response has been received and the socket
@@ -460,16 +464,19 @@ private:
         nsAHttpTransaction *Transaction() { return mTransaction; }
 
         bool IsSpeculative() { return mSpeculative; }
         void SetSpeculative(bool val) { mSpeculative = val; }
 
         bool IsFromPredictor() { return mIsFromPredictor; }
         void SetIsFromPredictor(bool val) { mIsFromPredictor = val; }
 
+        bool Allow1918() { return mAllow1918; }
+        void SetAllow1918(bool val) { mAllow1918 = val; }
+
         bool HasConnected() { return mHasConnected; }
 
         void PrintDiagnostics(nsCString &log);
     private:
         nsConnectionEntry              *mEnt;
         nsRefPtr<nsAHttpTransaction>   mTransaction;
         nsCOMPtr<nsISocketTransport>   mSocketTransport;
         nsCOMPtr<nsIAsyncOutputStream> mStreamOut;
@@ -485,16 +492,18 @@ private:
         // more connections that are needed.)
         bool                           mSpeculative;
 
         // mIsFromPredictor is set if the socket originated from the network
         // Predictor. It is used to gather telemetry data on used speculative
         // connections from the predictor.
         bool                           mIsFromPredictor;
 
+        bool                           mAllow1918;
+
         TimeStamp             mPrimarySynStarted;
         TimeStamp             mBackupSynStarted;
 
         // for syn retry
         nsCOMPtr<nsITimer>             mSynTimer;
         nsCOMPtr<nsISocketTransport>   mBackupTransport;
         nsCOMPtr<nsIAsyncOutputStream> mBackupStreamOut;
         nsCOMPtr<nsIAsyncInputStream>  mBackupStreamIn;
@@ -557,17 +566,17 @@ private:
                            nsAHttpTransaction *,
                            nsHttpPipeline **);
     bool     RestrictConnections(nsConnectionEntry *, bool = false);
     nsresult ProcessNewTransaction(nsHttpTransaction *);
     nsresult EnsureSocketThreadTarget();
     void     ClosePersistentConnections(nsConnectionEntry *ent);
     void     ReportProxyTelemetry(nsConnectionEntry *ent);
     nsresult CreateTransport(nsConnectionEntry *, nsAHttpTransaction *,
-                             uint32_t, bool, bool = false);
+                             uint32_t, bool, bool, bool);
     void     AddActiveConn(nsHttpConnection *, nsConnectionEntry *);
     void     DecrementActiveConnCount(nsHttpConnection *);
     void     StartedConnect();
     void     RecvdConnect();
 
     nsConnectionEntry *GetOrCreateConnectionEntry(nsHttpConnectionInfo *,
                                                   bool allowWildCard);
 
--- a/netwerk/protocol/http/nsHttpHandler.cpp
+++ b/netwerk/protocol/http/nsHttpHandler.cpp
@@ -185,16 +185,18 @@ nsHttpHandler::nsHttpHandler()
     , mSpdyV3(true)
     , mSpdyV31(true)
     , mHttp2DraftEnabled(true)
     , mHttp2Enabled(true)
     , mEnforceHttp2TlsProfile(true)
     , mCoalesceSpdy(true)
     , mSpdyPersistentSettings(false)
     , mAllowPush(true)
+    , mEnableAltSvc(true)
+    , mEnableAltSvcOE(true)
     , mSpdySendingChunkSize(ASpdySession::kSendingChunkSize)
     , mSpdySendBufferSize(ASpdySession::kTCPSendBufferSize)
     , mSpdyPushAllowance(32768)
     , mSpdyPingThreshold(PR_SecondsToInterval(58))
     , mSpdyPingTimeout(PR_SecondsToInterval(8))
     , mConnectTimeout(90000)
     , mParallelSpeculativeConnectLimit(6)
     , mRequestTokenBucketEnabled(true)
@@ -1225,16 +1227,31 @@ nsHttpHandler::PrefsChanged(nsIPrefBranc
 
     if (PREF_CHANGED(HTTP_PREF("spdy.allow-push"))) {
         rv = prefs->GetBoolPref(HTTP_PREF("spdy.allow-push"),
                                 &cVar);
         if (NS_SUCCEEDED(rv))
             mAllowPush = cVar;
     }
 
+    if (PREF_CHANGED(HTTP_PREF("altsvc.enabled"))) {
+        rv = prefs->GetBoolPref(HTTP_PREF("atsvc.enabled"),
+                                &cVar);
+        if (NS_SUCCEEDED(rv))
+            mEnableAltSvc = cVar;
+    }
+
+
+    if (PREF_CHANGED(HTTP_PREF("altsvc.oe"))) {
+        rv = prefs->GetBoolPref(HTTP_PREF("atsvc.oe"),
+                                &cVar);
+        if (NS_SUCCEEDED(rv))
+            mEnableAltSvcOE = cVar;
+    }
+
     if (PREF_CHANGED(HTTP_PREF("spdy.push-allowance"))) {
         rv = prefs->GetIntPref(HTTP_PREF("spdy.push-allowance"), &val);
         if (NS_SUCCEEDED(rv)) {
             mSpdyPushAllowance =
                 static_cast<uint32_t>
                 (clamped(val, 1024, static_cast<int32_t>(ASpdySession::kInitialRwin)));
         }
     }
@@ -1829,21 +1846,28 @@ nsHttpHandler::Observe(nsISupports *subj
         }
     } else if (!strcmp(topic, "net:failed-to-process-uri-content")) {
         nsCOMPtr<nsIURI> uri = do_QueryInterface(subject);
         if (uri && mConnMgr) {
             mConnMgr->ReportFailedToProcess(uri);
         }
     } else if (!strcmp(topic, "last-pb-context-exited")) {
         mPrivateAuthCache.ClearAll();
+        if (mConnMgr) {
+            mConnMgr->ClearAltServiceMappings();
+        }
     } else if (!strcmp(topic, "browser:purge-session-history")) {
-        if (mConnMgr && gSocketTransportService) {
-            nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(mConnMgr,
-                &nsHttpConnectionMgr::ClearConnectionHistory);
-            gSocketTransportService->Dispatch(event, NS_DISPATCH_NORMAL);
+        if (mConnMgr) {
+            if (gSocketTransportService) {
+                nsCOMPtr<nsIRunnable> event =
+                    NS_NewRunnableMethod(mConnMgr,
+                                         &nsHttpConnectionMgr::ClearConnectionHistory);
+                gSocketTransportService->Dispatch(event, NS_DISPATCH_NORMAL);
+            }
+            mConnMgr->ClearAltServiceMappings();
         }
     } else if (!strcmp(topic, NS_NETWORK_LINK_TOPIC)) {
         nsAutoCString converted = NS_ConvertUTF16toUTF8(data);
         if (!strcmp(converted.get(), NS_NETWORK_LINK_DATA_CHANGED)) {
             if (mConnMgr) {
                 mConnMgr->PruneDeadConnections();
                 mConnMgr->VerifyTraffic();
             }
@@ -1912,17 +1936,17 @@ nsHttpHandler::SpeculativeConnect(nsIURI
     rv = aURI->GetPort(&port);
     if (NS_FAILED(rv))
         return rv;
 
     nsAutoCString username;
     aURI->GetUsername(username);
 
     nsHttpConnectionInfo *ci =
-        new nsHttpConnectionInfo(host, port, username, nullptr, usingSSL);
+        new nsHttpConnectionInfo(host, port, EmptyCString(), username, nullptr, usingSSL);
 
     return SpeculativeConnect(ci, aCallbacks);
 }
 
 void
 nsHttpHandler::TickleWifi(nsIInterfaceRequestor *cb)
 {
     if (!cb || !mWifiTickler)
--- a/netwerk/protocol/http/nsHttpHandler.h
+++ b/netwerk/protocol/http/nsHttpHandler.h
@@ -32,16 +32,17 @@ class nsITimer;
 namespace mozilla {
 namespace net {
 class ATokenBucketEvent;
 class EventTokenBucket;
 class Tickler;
 class nsHttpConnection;
 class nsHttpConnectionInfo;
 class nsHttpTransaction;
+class AltSvcMapping;
 
 //-----------------------------------------------------------------------------
 // nsHttpHandler - protocol handler for HTTP and HTTPS
 //-----------------------------------------------------------------------------
 
 class nsHttpHandler MOZ_FINAL : public nsIHttpProtocolHandler
                               , public nsIObserver
                               , public nsSupportsWeakReference
@@ -103,16 +104,18 @@ public:
     bool           CoalesceSpdy() { return mCoalesceSpdy; }
     bool           UseSpdyPersistentSettings() { return mSpdyPersistentSettings; }
     uint32_t       SpdySendingChunkSize() { return mSpdySendingChunkSize; }
     uint32_t       SpdySendBufferSize()      { return mSpdySendBufferSize; }
     uint32_t       SpdyPushAllowance()       { return mSpdyPushAllowance; }
     PRIntervalTime SpdyPingThreshold() { return mSpdyPingThreshold; }
     PRIntervalTime SpdyPingTimeout() { return mSpdyPingTimeout; }
     bool           AllowPush()   { return mAllowPush; }
+    bool           AllowAltSvc() { return mEnableAltSvc; }
+    bool           AllowAltSvcOE() { return mEnableAltSvcOE; }
     uint32_t       ConnectTimeout()  { return mConnectTimeout; }
     uint32_t       ParallelSpeculativeConnectLimit() { return mParallelSpeculativeConnectLimit; }
     bool           CriticalRequestPrioritization() { return mCriticalRequestPrioritization; }
 
     uint32_t       MaxConnectionsPerOrigin() { return mMaxPersistentConnectionsPerServer; }
     bool           UseRequestTokenBucket() { return mRequestTokenBucketEnabled; }
     uint16_t       RequestTokenBucketMinParallelism() { return mRequestTokenBucketMinParallelism; }
     uint32_t       RequestTokenBucketHz() { return mRequestTokenBucketHz; }
@@ -214,16 +217,32 @@ public:
     nsresult SpeculativeConnect(nsHttpConnectionInfo *ci,
                                 nsIInterfaceRequestor *callbacks,
                                 uint32_t caps = 0)
     {
         TickleWifi(callbacks);
         return mConnMgr->SpeculativeConnect(ci, callbacks, caps);
     }
 
+    // Alternate Services Maps are main thread only
+    void UpdateAltServiceMapping(AltSvcMapping *map,
+                                 nsProxyInfo *proxyInfo,
+                                 nsIInterfaceRequestor *callbacks,
+                                 uint32_t caps)
+    {
+        mConnMgr->UpdateAltServiceMapping(map, proxyInfo, callbacks, caps);
+    }
+
+    AltSvcMapping *GetAltServiceMapping(const nsACString &scheme,
+                                        const nsACString &host,
+                                        int32_t port, bool pb)
+    {
+        return mConnMgr->GetAltServiceMapping(scheme, host, port, pb);
+    }
+
     //
     // The HTTP handler caches pointers to specific XPCOM services, and
     // provides the following helper routines for accessing those services:
     //
     nsresult GetStreamConverterService(nsIStreamConverterService **);
     nsresult GetIOService(nsIIOService** service);
     nsICookieService * GetCookieService(); // not addrefed
     nsISiteSecurityService * GetSSService();
@@ -449,16 +468,18 @@ private:
     uint32_t           mSpdyV3 : 1;
     uint32_t           mSpdyV31 : 1;
     uint32_t           mHttp2DraftEnabled : 1;
     uint32_t           mHttp2Enabled : 1;
     uint32_t           mEnforceHttp2TlsProfile : 1;
     uint32_t           mCoalesceSpdy : 1;
     uint32_t           mSpdyPersistentSettings : 1;
     uint32_t           mAllowPush : 1;
+    uint32_t           mEnableAltSvc : 1;
+    uint32_t           mEnableAltSvcOE : 1;
 
     // Try to use SPDY features instead of HTTP/1.1 over SSL
     SpdyInformation    mSpdyInfo;
 
     uint32_t       mSpdySendingChunkSize;
     uint32_t       mSpdySendBufferSize;
     uint32_t       mSpdyPushAllowance;
     PRIntervalTime mSpdyPingThreshold;
--- a/netwerk/protocol/http/nsHttpRequestHead.cpp
+++ b/netwerk/protocol/http/nsHttpRequestHead.cpp
@@ -46,16 +46,28 @@ nsHttpRequestHead::SetMethod(const nsACS
         mParsedMethod = kMethod_Head;
     } else if (!strcmp(mMethod.get(), "PUT")) {
         mParsedMethod = kMethod_Put;
     } else if (!strcmp(mMethod.get(), "TRACE")) {
         mParsedMethod = kMethod_Trace;
     }
 }
 
+void
+nsHttpRequestHead::SetOrigin(const nsACString &scheme, const nsACString &host, int32_t port)
+{
+    mOrigin.Assign(scheme);
+    mOrigin.Append(NS_LITERAL_CSTRING("://"));
+    mOrigin.Append(host);
+    if (port >= 0) {
+        mOrigin.Append(NS_LITERAL_CSTRING(":"));
+        mOrigin.AppendInt(port);
+    }
+}
+
 bool
 nsHttpRequestHead::IsSafeMethod() const
 {
   // This code will need to be extended for new safe methods, otherwise
   // they'll default to "not safe".
     if (IsGet() || IsHead() || IsOptions() || IsTrace()) {
         return true;
     }
--- a/netwerk/protocol/http/nsHttpRequestHead.h
+++ b/netwerk/protocol/http/nsHttpRequestHead.h
@@ -31,16 +31,19 @@ public:
     nsHttpHeaderArray & Headers()          { return mHeaders; }
     const nsCString &Method()        const { return mMethod; }
     nsHttpVersion       Version()    const { return mVersion; }
     const nsCSubstring &RequestURI() const { return mRequestURI; }
 
     void SetHTTPS(bool val) { mHTTPS = val; }
     bool IsHTTPS() const { return mHTTPS; }
 
+    void SetOrigin(const nsACString &scheme, const nsACString &host, int32_t port);
+    const nsCString &Origin() const { return mOrigin; }
+
     const char *PeekHeader(nsHttpAtom h) const
     {
         return mHeaders.PeekHeader(h);
     }
     nsresult SetHeader(nsHttpAtom h, const nsACString &v, bool m=false) { return mHeaders.SetHeader(h, v, m); }
     nsresult GetHeader(nsHttpAtom h, nsACString &v) const
     {
         return mHeaders.GetHeader(h, v);
@@ -92,15 +95,16 @@ public:
     bool IsTrace() const { return EqualsMethod(kMethod_Trace); }
 
 private:
     // All members must be copy-constructable and assignable
     nsHttpHeaderArray mHeaders;
     nsCString         mMethod;
     nsHttpVersion     mVersion;
     nsCString         mRequestURI;
+    nsCString         mOrigin;
     ParsedMethodType  mParsedMethod;
     bool              mHTTPS;
 };
 
 }} // namespace mozilla::net
 
 #endif // nsHttpRequestHead_h__
--- a/netwerk/protocol/http/nsHttpTransaction.cpp
+++ b/netwerk/protocol/http/nsHttpTransaction.cpp
@@ -86,17 +86,16 @@ LogHeaders(const char *lineStart)
 //-----------------------------------------------------------------------------
 // nsHttpTransaction <public>
 //-----------------------------------------------------------------------------
 
 nsHttpTransaction::nsHttpTransaction()
     : mLock("transaction lock")
     , mRequestSize(0)
     , mConnection(nullptr)
-    , mConnInfo(nullptr)
     , mRequestHead(nullptr)
     , mResponseHead(nullptr)
     , mContentLength(-1)
     , mContentRead(0)
     , mInvalidResponseBytesRead(0)
     , mChunkedDecoder(nullptr)
     , mStatus(NS_OK)
     , mPriority(0)
@@ -119,16 +118,17 @@ nsHttpTransaction::nsHttpTransaction()
     , mStatusEventPending(false)
     , mHasRequestBody(false)
     , mProxyConnectFailed(false)
     , mHttpResponseMatched(false)
     , mPreserveStream(false)
     , mDispatchedAsBlocking(false)
     , mResponseTimeoutEnabled(true)
     , mDontRouteViaWildCard(false)
+    , mForceRestart(false)
     , mReportedStart(false)
     , mReportedResponseHeader(false)
     , mForTakeResponseHead(nullptr)
     , mResponseHeadTaken(false)
     , mSubmittedRatePacing(false)
     , mPassedRatePacing(false)
     , mSynchronousRatePaceRequest(false)
     , mCountRecv(0)
@@ -843,16 +843,21 @@ nsHttpTransaction::Close(nsresult reason
     //
     // NOTE: because of the way SSL proxy CONNECT is implemented, it is
     // possible that the transaction may have received data without having
     // sent any data.  for this reason, mSendData == FALSE does not imply
     // mReceivedData == FALSE.  (see bug 203057 for more info.)
     //
     if (reason == NS_ERROR_NET_RESET || reason == NS_OK) {
 
+        if (mForceRestart && NS_SUCCEEDED(Restart())) {
+            LOG(("transaction force restarted\n"));
+            return;
+        }
+
         // reallySentData is meant to separate the instances where data has
         // been sent by this transaction but buffered at a higher level while
         // a TLS session (perhaps via a tunnel) is setup.
         bool reallySentData =
             mSentData && (!mConnection || mConnection->BytesWritten());
 
         if (!mReceivedData &&
             (!reallySentData || connReused || mPipelinePosition)) {
@@ -1105,16 +1110,27 @@ nsHttpTransaction::Restart()
     }
 
     // disable pipelining for the next attempt in case pipelining caused the
     // reset.  this is being overly cautious since we don't know if pipelining
     // was the problem here.
     mCaps &= ~NS_HTTP_ALLOW_PIPELINING;
     SetPipelinePosition(0);
 
+    if (!mConnInfo->GetAuthenticationHost().IsEmpty()) {
+        MutexAutoLock lock(*nsHttp::GetLock());
+        nsRefPtr<nsHttpConnectionInfo> ci;
+         mConnInfo->CloneAsDirectRoute(getter_AddRefs(ci));
+         mConnInfo = ci;
+        if (mRequestHead) {
+            mRequestHead->SetHeader(nsHttp::Alternate_Service_Used, NS_LITERAL_CSTRING("0"));
+        }
+    }
+    mForceRestart = false;
+
     return gHttpHandler->InitiateTransaction(this, mPriority);
 }
 
 char *
 nsHttpTransaction::LocateHttpStart(char *buf, uint32_t len,
                                    bool aAllowPartialMatch)
 {
     MOZ_ASSERT(!aAllowPartialMatch || mLineBuf.IsEmpty());
@@ -1376,21 +1392,21 @@ nsHttpTransaction::ParseHead(char *buf,
             return NS_OK;
         rv = ParseLineSegment(buf, len);
         if (NS_FAILED(rv))
             return rv;
     }
     return NS_OK;
 }
 
-// called on the socket thread
 nsresult
 nsHttpTransaction::HandleContentStart()
 {
     LOG(("nsHttpTransaction::HandleContentStart [this=%p]\n", this));
+    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
 
     if (mResponseHead) {
 #if defined(PR_LOGGING)
         if (LOG3_ENABLED()) {
             LOG3(("http response [\n"));
             nsAutoCString headers;
             mResponseHead->Flatten(headers, false);
             LogHeaders(headers.get());
@@ -1424,16 +1440,24 @@ nsHttpTransaction::HandleContentStart()
         case 101:
             mPreserveStream = true;    // fall through to other no content
         case 204:
         case 205:
         case 304:
             mNoContent = true;
             LOG(("this response should not contain a body.\n"));
             break;
+        case 421:
+            if (!mConnInfo->GetAuthenticationHost().IsEmpty()) {
+                LOG(("Not Authoritative.\n"));
+                gHttpHandler->ConnMgr()->
+                    ClearHostMapping(mConnInfo->GetHost(), mConnInfo->Port());
+                mForceRestart = true;
+            }
+            break;
         }
 
         if (mResponseHead->Status() == 200 &&
             mConnection->IsProxyConnectInProgress()) {
             // successful CONNECTs do not have response bodies
             mNoContent = true;
         }
         mConnection->SetLastTransactionExpectedNoContent(mNoContent);
--- a/netwerk/protocol/http/nsHttpTransaction.h
+++ b/netwerk/protocol/http/nsHttpTransaction.h
@@ -259,16 +259,17 @@ private:
     bool                            mStatusEventPending;
     bool                            mHasRequestBody;
     bool                            mProxyConnectFailed;
     bool                            mHttpResponseMatched;
     bool                            mPreserveStream;
     bool                            mDispatchedAsBlocking;
     bool                            mResponseTimeoutEnabled;
     bool                            mDontRouteViaWildCard;
+    bool                            mForceRestart;
 
     // mClosed           := transaction has been explicitly closed
     // mTransactionDone  := transaction ran to completion or was interrupted
     // mResponseComplete := transaction ran to completion
 
     // For Restart-In-Progress Functionality
     bool                            mReportedStart;
     bool                            mReportedResponseHeader;
--- a/netwerk/socket/nsISSLSocketControl.idl
+++ b/netwerk/socket/nsISSLSocketControl.idl
@@ -10,17 +10,17 @@ interface nsIInterfaceRequestor;
 interface nsIX509Cert;
 
 %{C++
 template<class T> class nsTArray;
 class nsCString;
 %}
 [ref] native nsCStringTArrayRef(nsTArray<nsCString>);
 
-[scriptable, builtinclass, uuid(89b819dc-31b0-4d09-915a-66f8a3703483)]
+[scriptable, builtinclass, uuid(f160ec31-01f3-47f2-b542-0e12a647b07f)]
 interface nsISSLSocketControl : nsISupports {
     attribute nsIInterfaceRequestor     notificationCallbacks;
 
     void proxyStartSSL();
     void StartTLS();
 
     /* NPN (Next Protocol Negotiation) is a mechanism for
        negotiating the protocol to be spoken inside the SSL
@@ -48,16 +48,21 @@ interface nsISSLSocketControl : nsISuppo
      * a desired NPN negotiated protocol of npnProtocol can use the socket
      * associated with this object instead of making a new one.
      */
     boolean joinConnection(
       in ACString npnProtocol, /* e.g. "spdy/2" */
       in ACString hostname,
       in long port);
 
+    /* Determine if existing connection should be trusted to convey information about
+     * a hostname.
+     */
+    boolean isAcceptableForHost(in ACString hostname);
+
     /* The Key Exchange Algorithm is used when determining whether or
        not to do false start and whether or not HTTP/2 can be used.
 
        After a handshake is complete it can be read from KEAUsed,
        before a handshake is started it may be set through KEAExpected.
        The values correspond to the SSLKEAType enum in NSS or the
        KEY_EXCHANGE_UNKNOWN constant defined below.
 
@@ -98,10 +103,31 @@ interface nsISSLSocketControl : nsISuppo
     [infallible] readonly attribute short MACAlgorithmUsed;
 
     /**
      * If set before the server requests a client cert (assuming it does so at
      * all), then this cert will be presented to the server, instead of asking
      * the user or searching the set of rememebered user cert decisions.
      */
     attribute nsIX509Cert clientCert;
+
+    /**
+     * If you wish to verify the host certificate using a different name than
+     * was used for the tcp connection, but without using proxy semantics, you
+     * can set authenticationName and authenticationPort
+     */
+    attribute ACString authenticationName;
+    [infallible] attribute long authenticationPort;
+
+    /**
+     * set bypassAuthentication to true if the server certificate checks should
+     * not be enforced. This is to enable non-secure transport over TLS.
+     */
+    [infallible] attribute boolean bypassAuthentication;
+
+    /*
+     * failedVerification is true if any enforced certificate checks have failed.
+     * Connections that have not yet tried to verify, have verifications bypassed,
+     * or are using acceptable exceptions will all return false.
+     */
+    [infallible] readonly attribute boolean failedVerification;
 };
 
--- a/netwerk/test/unit/test_http2.js
+++ b/netwerk/test/unit/test_http2.js
@@ -330,27 +330,75 @@ function test_http2_post() {
 
 // Make sure we can do a POST that covers more than 2 frames
 function test_http2_post_big() {
   var chan = makeChan("https://localhost:6944/post");
   var listener = new Http2PostListener(md5s[1]);
   do_post(posts[1], chan, listener);
 }
 
+Cu.import("resource://testing-common/httpd.js");
+var httpserv = null;
+var ios = Components.classes["@mozilla.org/network/io-service;1"]
+                    .getService(Components.interfaces.nsIIOService);
+
+var altsvcClientListener = {
+  onStartRequest: function test_onStartR(request, ctx) {
+    do_check_eq(request.status, Components.results.NS_OK);
+  },
+
+  onDataAvailable: function test_ODA(request, cx, stream, offset, cnt) {
+   read_stream(stream, cnt);
+  },
+
+  onStopRequest: function test_onStopR(request, ctx, status) {
+    var isHttp2Connection = checkIsHttp2(request);
+    if (!isHttp2Connection) {
+	// not over tls yet - retry. It's all async and transparent to client
+	var chan = ios.newChannel("http://localhost:" + httpserv.identity.primaryPort + "/altsvc1",
+				  null, null).QueryInterface(Components.interfaces.nsIHttpChannel);
+	chan.asyncOpen(altsvcClientListener, null);
+    } else {
+        do_check_true(isHttp2Connection);
+	httpserv.stop(do_test_finished);
+	run_next_test();
+    }
+  }
+};
+
+function altsvcHttp1Server(metadata, response) {
+  response.setStatusLine(metadata.httpVersion, 200, "OK");
+  response.setHeader("Content-Type", "text/plain", false);
+  response.setHeader("Alt-Svc", 'h2=":6944"; ma=3200, h2-14=":6944"', false);
+  var body = "this is where a cool kid would write something neat.\n";
+  response.bodyOutputStream.write(body, body.length);
+}
+
+function test_http2_altsvc() {
+  httpserv = new HttpServer();
+  httpserv.registerPathHandler("/altsvc1", altsvcHttp1Server);
+  httpserv.start(-1);
+
+  var chan = ios.newChannel("http://localhost:" + httpserv.identity.primaryPort + "/altsvc1",
+			    null, null).QueryInterface(Components.interfaces.nsIHttpChannel);
+  chan.asyncOpen(altsvcClientListener, null);
+}
+
 // hack - the header test resets the multiplex object on the server,
 // so make sure header is always run before the multiplex test.
 //
 // make sure post_big runs first to test race condition in restarting
 // a stalled stream when a SETTINGS frame arrives
 var tests = [ test_http2_post_big
             , test_http2_basic
             , test_http2_push1
             , test_http2_push2
             , test_http2_push3
             , test_http2_push4
+	    , test_http2_altsvc
             , test_http2_doubleheader
             , test_http2_xhr
             , test_http2_header
             , test_http2_cookie_crumbling
             , test_http2_multiplex
             , test_http2_big
             , test_http2_post
             ];
@@ -427,16 +475,18 @@ var tlspref;
 var loadGroup;
 
 function resetPrefs() {
   prefs.setBoolPref("network.http.spdy.enabled", spdypref);
   prefs.setBoolPref("network.http.spdy.enabled.v3", spdy3pref);
   prefs.setBoolPref("network.http.spdy.allow-push", spdypush);
   prefs.setBoolPref("network.http.spdy.enabled.http2draft", http2pref);
   prefs.setBoolPref("network.http.spdy.enforce-tls-profile", tlspref);
+  prefs.setBoolPref("network.http.altsvc.enabled", altsvcpref1);
+  prefs.setBoolPref("network.http.altsvc.oe", altsvcpref2);
 }
 
 function run_test() {
   // Set to allow the cert presented by our SPDY server
   do_get_profile();
   var prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
   var oldPref = prefs.getIntPref("network.http.speculative-parallel-limit");
   prefs.setIntPref("network.http.speculative-parallel-limit", 0);
@@ -449,19 +499,24 @@ function run_test() {
   prefs.setIntPref("network.http.speculative-parallel-limit", oldPref);
 
   // Enable all versions of spdy to see that we auto negotiate http/2
   spdypref = prefs.getBoolPref("network.http.spdy.enabled");
   spdy3pref = prefs.getBoolPref("network.http.spdy.enabled.v3");
   spdypush = prefs.getBoolPref("network.http.spdy.allow-push");
   http2pref = prefs.getBoolPref("network.http.spdy.enabled.http2draft");
   tlspref = prefs.getBoolPref("network.http.spdy.enforce-tls-profile");
+  altsvcpref1 = prefs.getBoolPref("network.http.altsvc.enabled");
+  altsvcpref2 = prefs.getBoolPref("network.http.altsvc.oe", true);
+
   prefs.setBoolPref("network.http.spdy.enabled", true);
   prefs.setBoolPref("network.http.spdy.enabled.v3", true);
   prefs.setBoolPref("network.http.spdy.allow-push", true);
   prefs.setBoolPref("network.http.spdy.enabled.http2draft", true);
   prefs.setBoolPref("network.http.spdy.enforce-tls-profile", false);
+  prefs.setBoolPref("network.http.altsvc.enabled", true);
+  prefs.setBoolPref("network.http.altsvc.oe", true);
 
   loadGroup = Cc["@mozilla.org/network/load-group;1"].createInstance(Ci.nsILoadGroup);
 
   // And make go!
   run_next_test();
 }
--- a/parser/htmlparser/nsParser.cpp
+++ b/parser/htmlparser/nsParser.cpp
@@ -638,17 +638,17 @@ VerifyPublicIDs()
 }
 #endif
 
 namespace {
 
 struct PublicIdComparator
 {
   const nsAutoCString& mPublicId;
-  PublicIdComparator(const nsAutoCString& aPublicId)
+  explicit PublicIdComparator(const nsAutoCString& aPublicId)
     : mPublicId(aPublicId) {}
   int operator()(const PubIDInfo& aInfo) const {
     return nsCRT::strcmp(mPublicId.get(), aInfo.name);
   }
 };
 
 } // namespace
 
--- a/security/manager/ssl/src/SSLServerCertVerification.cpp
+++ b/security/manager/ssl/src/SSLServerCertVerification.cpp
@@ -395,16 +395,26 @@ CertErrorRunnable::CheckCertOverrides()
   unused << mFdForLogging;
 
   if (!NS_IsMainThread()) {
     NS_ERROR("CertErrorRunnable::CheckCertOverrides called off main thread");
     return new SSLServerCertVerificationResult(mInfoObject,
                                                mDefaultErrorCodeToReport);
   }
 
+  nsCOMPtr<nsISSLSocketControl> sslSocketControl = do_QueryInterface(
+    NS_ISUPPORTS_CAST(nsITransportSecurityInfo*, mInfoObject));
+  if (sslSocketControl &&
+      sslSocketControl->GetBypassAuthentication()) {
+    PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
+           ("[%p][%p] Bypass Auth in CheckCertOverrides\n",
+            mFdForLogging, this));
+    return new SSLServerCertVerificationResult(mInfoObject, 0);
+  }
+
   int32_t port;
   mInfoObject->GetPort(&port);
 
   nsAutoCString hostWithPortString(mInfoObject->GetHostName());
   hostWithPortString.Append(':');
   hostWithPortString.AppendInt(port);
 
   uint32_t remaining_display_errors = mCollectedErrors;
@@ -484,18 +494,16 @@ CertErrorRunnable::CheckCertOverrides()
   PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
          ("[%p][%p] Certificate error was not overridden\n",
          mFdForLogging, this));
 
   // Ok, this is a full stop.
   // First, deliver the technical details of the broken SSL status.
 
   // Try to get a nsIBadCertListener2 implementation from the socket consumer.
-  nsCOMPtr<nsISSLSocketControl> sslSocketControl = do_QueryInterface(
-    NS_ISUPPORTS_CAST(nsITransportSecurityInfo*, mInfoObject));
   if (sslSocketControl) {
     nsCOMPtr<nsIInterfaceRequestor> cb;
     sslSocketControl->GetNotificationCallbacks(getter_AddRefs(cb));
     if (cb) {
       nsCOMPtr<nsIBadCertListener2> bcl = do_GetInterface(cb);
       if (bcl) {
         nsIInterfaceRequestor* csi
           = static_cast<nsIInterfaceRequestor*>(mInfoObject);
--- a/security/manager/ssl/src/nsNSSIOLayer.cpp
+++ b/security/manager/ssl/src/nsNSSIOLayer.cpp
@@ -127,21 +127,23 @@ nsNSSSocketInfo::nsNSSSocketInfo(SharedS
     mNPNCompleted(false),
     mFalseStartCallbackCalled(false),
     mFalseStarted(false),
     mIsFullHandshake(false),
     mHandshakeCompleted(false),
     mJoined(false),
     mSentClientCert(false),
     mNotedTimeUntilReady(false),
+    mFailedVerification(false),
     mKEAUsed(nsISSLSocketControl::KEY_EXCHANGE_UNKNOWN),
     mKEAExpected(nsISSLSocketControl::KEY_EXCHANGE_UNKNOWN),
     mKEAKeyBits(0),
     mSSLVersionUsed(nsISSLSocketControl::SSL_VERSION_UNKNOWN),
     mMACAlgorithmUsed(nsISSLSocketControl::SSL_MAC_UNKNOWN),
+    mBypassAuthentication(false),
     mProviderFlags(providerFlags),
     mSocketCreationTimestamp(TimeStamp::Now()),
     mPlaintextBytesRead(0),
     mClientCert(nullptr)
 {
   mTLSVersionRange.min = 0;
   mTLSVersionRange.max = 0;
 }
@@ -222,16 +224,62 @@ nsNSSSocketInfo::GetClientCert(nsIX509Ce
 NS_IMETHODIMP
 nsNSSSocketInfo::SetClientCert(nsIX509Cert* aClientCert)
 {
   mClientCert = aClientCert;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsNSSSocketInfo::GetBypassAuthentication(bool* arg)
+{
+  *arg = mBypassAuthentication;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNSSSocketInfo::SetBypassAuthentication(bool arg)
+{
+  mBypassAuthentication = arg;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNSSSocketInfo::GetFailedVerification(bool* arg)
+{
+  *arg = mFailedVerification;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNSSSocketInfo::GetAuthenticationName(nsACString& aAuthenticationName)
+{
+  aAuthenticationName = GetHostName();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNSSSocketInfo::SetAuthenticationName(const nsACString& aAuthenticationName)
+{
+  return SetHostName(PromiseFlatCString(aAuthenticationName).get());
+}
+
+NS_IMETHODIMP
+nsNSSSocketInfo::GetAuthenticationPort(int32_t* aAuthenticationPort)
+{
+  return GetPort(aAuthenticationPort);
+}
+
+NS_IMETHODIMP
+nsNSSSocketInfo::SetAuthenticationPort(int32_t aAuthenticationPort)
+{
+  return SetPort(aAuthenticationPort);
+}
+
+NS_IMETHODIMP
 nsNSSSocketInfo::GetRememberClientAuthCertificate(bool* aRemember)
 {
   NS_ENSURE_ARG_POINTER(aRemember);
   *aRemember = mRememberClientAuthCertificate;
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -373,31 +421,18 @@ nsNSSSocketInfo::GetNegotiatedNPN(nsACSt
   if (!mNPNCompleted)
     return NS_ERROR_NOT_CONNECTED;
 
   aNegotiatedNPN = mNegotiatedNPN;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsNSSSocketInfo::JoinConnection(const nsACString& npnProtocol,
-                                const nsACString& hostname,
-                                int32_t port,
-                                bool* _retval)
+nsNSSSocketInfo::IsAcceptableForHost(const nsACString& hostname, bool* _retval)
 {
-  *_retval = false;
-
-  // Different ports may not be joined together
-  if (port != GetPort())
-    return NS_OK;
-
-  // Make sure NPN has been completed and matches requested npnProtocol
-  if (!mNPNCompleted || !mNegotiatedNPN.Equals(npnProtocol))
-    return NS_OK;
-
   // If this is the same hostname then the certicate status does not
   // need to be considered. They are joinable.
   if (hostname.Equals(GetHostName())) {
     *_retval = true;
     return NS_OK;
   }
 
   // Before checking the server certificate we need to make sure the
@@ -457,22 +492,46 @@ nsNSSSocketInfo::JoinConnection(const ns
                                                    mozilla::pkix::Now(),
                                                    nullptr, hostnameFlat.get(),
                                                    false, flags, nullptr,
                                                    nullptr);
   if (rv != SECSuccess) {
     return NS_OK;
   }
 
-  // All tests pass - this is joinable
-  mJoined = true;
+  // All tests pass
   *_retval = true;
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsNSSSocketInfo::JoinConnection(const nsACString& npnProtocol,
+                                const nsACString& hostname,
+                                int32_t port,
+                                bool* _retval)
+{
+  *_retval = false;
+
+  // Different ports may not be joined together
+  if (port != GetPort())
+    return NS_OK;
+
+  // Make sure NPN has been completed and matches requested npnProtocol
+  if (!mNPNCompleted || !mNegotiatedNPN.Equals(npnProtocol))
+    return NS_OK;
+
+  IsAcceptableForHost(hostname, _retval);
+
+  if (*_retval) {
+    // All tests pass - this is joinable
+    mJoined = true;
+  }
+  return NS_OK;
+}
+
 bool
 nsNSSSocketInfo::GetForSTARTTLS()
 {
   return mForSTARTTLS;
 }
 
 void
 nsNSSSocketInfo::SetForSTARTTLS(bool aForSTARTTLS)
@@ -627,16 +686,17 @@ nsNSSSocketInfo::SetCertVerificationResu
       if (errorCode == 0) {
         NS_ERROR("SSL_AuthCertificateComplete didn't set error code");
         errorCode = PR_INVALID_STATE_ERROR;
       }
     }
   }
 
   if (errorCode) {
+    mFailedVerification = true;
     SetCanceled(errorCode, errorMessageType);
   }
 
   if (mPlaintextBytesRead && !errorCode) {
     Telemetry::Accumulate(Telemetry::SSL_BYTES_BEFORE_CERT_CALLBACK,
                           AssertedCast<uint32_t>(mPlaintextBytesRead));
   }
 
--- a/security/manager/ssl/src/nsNSSIOLayer.h
+++ b/security/manager/ssl/src/nsNSSIOLayer.h
@@ -108,16 +108,32 @@ public:
 
   void SetSSLVersionUsed(int16_t version)
   {
     mSSLVersionUsed = version;
   }
 
   void SetMACAlgorithmUsed(int16_t mac) { mMACAlgorithmUsed = mac; }
 
+  inline bool GetBypassAuthentication()
+  {
+    bool result = false;
+    mozilla::DebugOnly<nsresult> rv = GetBypassAuthentication(&result);
+    MOZ_ASSERT(NS_SUCCEEDED(rv));
+    return result;
+  }
+
+  inline int32_t GetAuthenticationPort()
+  {
+    int32_t result = -1;
+    mozilla::DebugOnly<nsresult> rv = GetAuthenticationPort(&result);
+    MOZ_ASSERT(NS_SUCCEEDED(rv));
+    return result;
+  }
+
 protected:
   virtual ~nsNSSSocketInfo();
 
 private:
   PRFileDesc* mFd;
 
   CertVerificationState mCertVerificationState;
 
@@ -134,24 +150,26 @@ private:
   bool      mNPNCompleted;
   bool      mFalseStartCallbackCalled;
   bool      mFalseStarted;
   bool      mIsFullHandshake;
   bool      mHandshakeCompleted;
   bool      mJoined;
   bool      mSentClientCert;
   bool      mNotedTimeUntilReady;
+  bool      mFailedVerification;
 
   // mKEA* are used in false start and http/2 detetermination
   // Values are from nsISSLSocketControl
   int16_t mKEAUsed;
   int16_t mKEAExpected;
   uint32_t mKEAKeyBits;
   int16_t mSSLVersionUsed;
   int16_t mMACAlgorithmUsed;
+  bool    mBypassAuthentication;
 
   uint32_t mProviderFlags;
   mozilla::TimeStamp mSocketCreationTimestamp;
   uint64_t mPlaintextBytesRead;
 
   nsCOMPtr<nsIX509Cert> mClientCert;
 };
 
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -1188,16 +1188,26 @@
     "kind": "boolean",
     "description": "Whether a HTTP transaction was over SSL or not."
   },
   "HTTP_PAGELOAD_IS_SSL": {
     "expires_in_version": "never",
     "kind": "boolean",
     "description": "Whether a HTTP base page load was over SSL or not."
   },
+  "HTTP_TRANSACTION_USE_ALTSVC": {
+    "expires_in_version": "never",
+    "kind": "boolean",
+    "description": "Whether a HTTP transaction was routed via Alt-Svc or not."
+  },
+  "HTTP_TRANSACTION_USE_ALTSVC_OE": {
+    "expires_in_version": "never",
+    "kind": "boolean",
+    "description": "Whether a HTTP transaction routed via Alt-Svc was scheme=http"
+  },
   "SSL_HANDSHAKE_VERSION": {
     "expires_in_version": "never",
     "kind": "enumerated",
     "n_values": 16,
     "description": "SSL Version (0=ssl3, 1=tls1, 2=tls1.1, 3=tls1.2)"
   },
   "SSL_TIME_UNTIL_READY": {
     "expires_in_version": "never",
--- a/toolkit/modules/SelectContentHelper.jsm
+++ b/toolkit/modules/SelectContentHelper.jsm
@@ -87,18 +87,18 @@ this.SelectContentHelper.prototype = {
 }
 
 function buildOptionListForChildren(node) {
   let result = [];
   for (let child = node.firstChild; child; child = child.nextSibling) {
     if (child.tagName == 'OPTION' || child.tagName == 'OPTGROUP') {
       let info = {
         tagName: child.tagName,
-        textContent: child.tagName == 'OPTGROUP' ? child.getAttribute("label")
-                                                 : child.textContent,
+        textContent: child.tagName == 'OPTGROUP' ? child.getAttribute("label").trim()
+                                                 : child.textContent.trim(),
         // XXX this uses a highlight color when this is the selected element.
         // We need to suppress such highlighting in the content process to get
         // the option's correct unhighlighted color here.
         // We also need to detect default color vs. custom so that a standard
         // color does not override color: menutext in the parent.
         // backgroundColor: computedStyle.backgroundColor,
         // color: computedStyle.color,
         children: child.tagName == 'OPTGROUP' ? buildOptionListForChildren(child) : []
--- a/toolkit/mozapps/update/tests/Makefile.in
+++ b/toolkit/mozapps/update/tests/Makefile.in
@@ -18,22 +18,20 @@ xpcshell-data_FILES   := $(wildcard $(sr
 # Android doesn't use the Mozilla updater or the toolkit update UI
 ifneq (android,$(MOZ_WIDGET_TOOLKIT))
 INSTALL_TARGETS           += base-updater-head
 base-updater-head_TARGET  := libs
 base-updater-head_DEST    := $(XPCSHELLTESTROOT)/unit_base_updater
 base-updater-head_FILES   := $(XPCSHELLTESTROOT)/unit_aus_update/head_update.js
 
 ifdef MOZ_MAINTENANCE_SERVICE
-ifndef HAVE_64BIT_BUILD
 INSTALL_TARGETS              += service-updater-head
 service-updater-head_TARGET  := libs
 service-updater-head_DEST    := $(XPCSHELLTESTROOT)/unit_service_updater
 service-updater-head_FILES   := $(XPCSHELLTESTROOT)/unit_aus_update/head_update.js
-endif # HAVE_64BIT_BUILD
 endif # MOZ_MAINTENANCE_SERVICE
 
 ifndef MOZ_PROFILE_GENERATE
 ifdef COMPILE_ENVIRONMENT
 INSTALL_TARGETS              += xpcshell-test-helper
 xpcshell-test-helper_TARGET  := libs
 xpcshell-test-helper_DEST    := $(XPCSHELLTESTROOT)/data
 xpcshell-test-helper_FILES   := $(DIST)/bin/TestAUSHelper$(BIN_SUFFIX)
--- a/toolkit/mozapps/update/tests/moz.build
+++ b/toolkit/mozapps/update/tests/moz.build
@@ -5,17 +5,17 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 XPCSHELL_TESTS_MANIFESTS += ['unit_aus_update/xpcshell.ini']
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android':
     MOCHITEST_CHROME_MANIFESTS += ['chrome/chrome.ini']
     XPCSHELL_TESTS_MANIFESTS += ['unit_base_updater/xpcshell.ini']
 
-    if CONFIG['MOZ_MAINTENANCE_SERVICE'] and not CONFIG['HAVE_64BIT_BUILD']:
+    if CONFIG['MOZ_MAINTENANCE_SERVICE']:
         XPCSHELL_TESTS_MANIFESTS += ['unit_service_updater/xpcshell.ini']
 
     SimplePrograms([
         'TestAUSHelper',
         'TestAUSReadStrings',
     ])
 
     LOCAL_INCLUDES += [
@@ -39,17 +39,17 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'andr
         ]
 
 for var in ('MOZ_APP_NAME', 'MOZ_APP_BASENAME', 'MOZ_APP_DISPLAYNAME',
             'MOZ_APP_VENDOR', 'BIN_SUFFIX', 'MOZ_DEBUG'):
     DEFINES[var] = CONFIG[var]
 
 DEFINES['NS_NO_XPCOM'] = True
 
-if CONFIG['MOZ_MAINTENANCE_SERVICE'] and not CONFIG['HAVE_64BIT_BUILD']:
+if CONFIG['MOZ_MAINTENANCE_SERVICE']:
     DEFINES['MOZ_MAINTENANCE_SERVICE'] = CONFIG['MOZ_MAINTENANCE_SERVICE']
 
 # For debugging purposes only
 #DEFINES['DISABLE_UPDATER_AUTHENTICODE_CHECK'] = True
 
 if CONFIG['OS_ARCH'] == 'WINNT':
     DEFINES['UNICODE'] = True
     DEFINES['_UNICODE'] = True
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -2848,17 +2848,17 @@ static void LogRegistryEvent(const wchar
   }
 }
 #else
 
 #define LOGREGISTRY(msg)
 
 #endif
 
-static DWORD InitDwriteBG(LPVOID lpdwThreadParam)
+static DWORD WINAPI InitDwriteBG(LPVOID lpdwThreadParam)
 {
   SetThreadPriority(GetCurrentThread(), THREAD_MODE_BACKGROUND_BEGIN);
   LOGREGISTRY(L"loading dwrite.dll");
   HMODULE dwdll = LoadLibraryW(L"dwrite.dll");
   if (dwdll) {
     decltype(DWriteCreateFactory)* createDWriteFactory = (decltype(DWriteCreateFactory)*)
       GetProcAddress(dwdll, "DWriteCreateFactory");
     if (createDWriteFactory) {
@@ -3038,18 +3038,17 @@ XREMain::XRE_mainInit(bool* aExitFlag)
     // calls cause the IDWriteFactory object to communicate with the FntCache
     // service with a timeout; if there's no response after the timeout, the
     // DirectWrite client library will assume the service isn't around and do
     // manual font file I/O on _all_ system fonts.  To avoid this, load the
     // dwrite library and create a factory as early as possible so that the
     // FntCache service is ready by the time it's needed.
 
     if (IsVistaOrLater()) {
-      CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE)&InitDwriteBG,
-                   nullptr, 0, nullptr);
+      CreateThread(nullptr, 0, &InitDwriteBG, nullptr, 0, nullptr);
     }
   }
 #endif
 
 #ifdef XP_UNIX
   const char *home = PR_GetEnv("HOME");
   if (!home || !*home) {
     struct passwd *pw = getpwuid(geteuid());