Merge inbound to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Sat, 11 Oct 2014 16:16:00 -0400
changeset 233192 44168a7af20de3db73f0a7ed63abd923ad3a3732
parent 233162 2783f7f7d67a81370fa55299e2aa89c7424a50ba (current diff)
parent 233191 16a8a5c8b96a2fe14420f3a689b099e4410ddf32 (diff)
child 233193 0990e633fd0dcdefdc171f0d0cf49c0db18e84e0
child 233199 bdba65a0ead7241c34a19fd6fb44d518755cb8f9
child 233223 fcb718391722309929f3639c2eead11984c44704
push id4187
push userbhearsum@mozilla.com
push dateFri, 28 Nov 2014 15:29:12 +0000
treeherdermozilla-beta@f23cc6a30c11 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone35.0a1
first release with
nightly linux32
44168a7af20d / 35.0a1 / 20141012030203 / files
nightly linux64
44168a7af20d / 35.0a1 / 20141012030203 / files
nightly mac
44168a7af20d / 35.0a1 / 20141012030203 / files
nightly win32
44168a7af20d / 35.0a1 / 20141012030203 / files
nightly win64
44168a7af20d / 35.0a1 / 20141012030203 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to 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());