Merge inbound to m-c. a=merge
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 = ®alloc->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 ©)
+ : 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, ¤tAge);
+
+ 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 ¤tName =
+ parsedAltSvc.mValues[index].mValues[pairIndex].mName;
+ nsDependentCSubstring ¤tValue =
+ 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());