diff --git a/layout/reftests/css-grid/grid-repeat-auto-fill-fit-001-ref.html b/layout/reftests/css-grid/grid-repeat-auto-fill-fit-001-ref.html
new file mode 100644
index 0000000000..f2123e2bd4
--- /dev/null
+++ b/layout/reftests/css-grid/grid-repeat-auto-fill-fit-001-ref.html
@@ -0,0 +1,193 @@
+
+
+
+
+
Reference: repeat(auto-fill/auto-fit)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/layout/reftests/css-grid/grid-repeat-auto-fill-fit-001.html b/layout/reftests/css-grid/grid-repeat-auto-fill-fit-001.html
new file mode 100644
index 0000000000..35f338c78d
--- /dev/null
+++ b/layout/reftests/css-grid/grid-repeat-auto-fill-fit-001.html
@@ -0,0 +1,179 @@
+
+
+
+
+
CSS Grid Test: repeat(auto-fill/auto-fit)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/layout/reftests/css-grid/grid-repeat-auto-fill-fit-002-ref.html b/layout/reftests/css-grid/grid-repeat-auto-fill-fit-002-ref.html
new file mode 100644
index 0000000000..f2cad12312
--- /dev/null
+++ b/layout/reftests/css-grid/grid-repeat-auto-fill-fit-002-ref.html
@@ -0,0 +1,190 @@
+
+
+
+
+
Reference: repeat(auto-fill/auto-fit)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/layout/reftests/css-grid/grid-repeat-auto-fill-fit-002.html b/layout/reftests/css-grid/grid-repeat-auto-fill-fit-002.html
new file mode 100644
index 0000000000..114b7a120d
--- /dev/null
+++ b/layout/reftests/css-grid/grid-repeat-auto-fill-fit-002.html
@@ -0,0 +1,182 @@
+
+
+
+
+
CSS Grid Test: repeat(auto-fill/auto-fit)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/layout/reftests/css-grid/grid-repeat-auto-fill-fit-003-ref.html b/layout/reftests/css-grid/grid-repeat-auto-fill-fit-003-ref.html
new file mode 100644
index 0000000000..9f83cf9b15
--- /dev/null
+++ b/layout/reftests/css-grid/grid-repeat-auto-fill-fit-003-ref.html
@@ -0,0 +1,189 @@
+
+
+
+
+
Reference: repeat(auto-fill/auto-fit)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/layout/reftests/css-grid/grid-repeat-auto-fill-fit-003.html b/layout/reftests/css-grid/grid-repeat-auto-fill-fit-003.html
new file mode 100644
index 0000000000..07ba155d92
--- /dev/null
+++ b/layout/reftests/css-grid/grid-repeat-auto-fill-fit-003.html
@@ -0,0 +1,181 @@
+
+
+
+
+
CSS Grid Test: repeat(auto-fill/auto-fit)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/layout/reftests/css-grid/grid-repeat-auto-fill-fit-004-ref.html b/layout/reftests/css-grid/grid-repeat-auto-fill-fit-004-ref.html
new file mode 100644
index 0000000000..d61a9dcaab
--- /dev/null
+++ b/layout/reftests/css-grid/grid-repeat-auto-fill-fit-004-ref.html
@@ -0,0 +1,191 @@
+
+
+
+
+
Reference: repeat(auto-fill/auto-fit)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/layout/reftests/css-grid/grid-repeat-auto-fill-fit-004.html b/layout/reftests/css-grid/grid-repeat-auto-fill-fit-004.html
new file mode 100644
index 0000000000..dd9af776aa
--- /dev/null
+++ b/layout/reftests/css-grid/grid-repeat-auto-fill-fit-004.html
@@ -0,0 +1,183 @@
+
+
+
+
+
CSS Grid Test: repeat(auto-fill/auto-fit)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/layout/reftests/css-grid/grid-repeat-auto-fill-fit-005-ref.html b/layout/reftests/css-grid/grid-repeat-auto-fill-fit-005-ref.html
new file mode 100644
index 0000000000..7c29879df3
--- /dev/null
+++ b/layout/reftests/css-grid/grid-repeat-auto-fill-fit-005-ref.html
@@ -0,0 +1,381 @@
+
+
+
+
+
Reference: repeat(auto-fill/auto-fit)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/layout/reftests/css-grid/grid-repeat-auto-fill-fit-005.html b/layout/reftests/css-grid/grid-repeat-auto-fill-fit-005.html
new file mode 100644
index 0000000000..9549734620
--- /dev/null
+++ b/layout/reftests/css-grid/grid-repeat-auto-fill-fit-005.html
@@ -0,0 +1,377 @@
+
+
+
+
+
CSS Grid Test: repeat(auto-fill/auto-fit)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/layout/reftests/css-grid/grid-repeat-auto-fill-fit-006-ref.html b/layout/reftests/css-grid/grid-repeat-auto-fill-fit-006-ref.html
new file mode 100644
index 0000000000..79ac94548e
--- /dev/null
+++ b/layout/reftests/css-grid/grid-repeat-auto-fill-fit-006-ref.html
@@ -0,0 +1,184 @@
+
+
+
+
+
Reference: repeat(auto-fit) with grid-aligned abs.pos.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/layout/reftests/css-grid/grid-repeat-auto-fill-fit-006.html b/layout/reftests/css-grid/grid-repeat-auto-fill-fit-006.html
new file mode 100644
index 0000000000..246b292151
--- /dev/null
+++ b/layout/reftests/css-grid/grid-repeat-auto-fill-fit-006.html
@@ -0,0 +1,213 @@
+
+
+
+
+
CSS Grid Test: repeat(auto-fit) with grid-aligned abs.pos.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/layout/reftests/css-grid/reftest.list b/layout/reftests/css-grid/reftest.list
index ca368dc8fb..3cad9b04d8 100644
--- a/layout/reftests/css-grid/reftest.list
+++ b/layout/reftests/css-grid/reftest.list
@@ -87,3 +87,12 @@ fuzzy-if(winWidget,1,36) == grid-auto-min-sizing-definite-001.html grid-auto-min
== grid-item-margin-right-auto-002.html grid-item-margin-right-auto-002-ref.html
== grid-item-margin-right-auto-003.html grid-item-margin-right-auto-003-ref.html
== grid-item-margin-right-auto-004.html grid-item-margin-right-auto-004-ref.html
+== grid-container-min-max-width-height-001.html grid-container-min-max-width-height-001-ref.html
+== grid-clamping-001.html grid-clamping-001-ref.html
+== grid-clamping-002.html grid-clamping-002-ref.html
+== grid-repeat-auto-fill-fit-001.html grid-repeat-auto-fill-fit-001-ref.html
+== grid-repeat-auto-fill-fit-002.html grid-repeat-auto-fill-fit-002-ref.html
+== grid-repeat-auto-fill-fit-003.html grid-repeat-auto-fill-fit-003-ref.html
+== grid-repeat-auto-fill-fit-004.html grid-repeat-auto-fill-fit-004-ref.html
+== grid-repeat-auto-fill-fit-005.html grid-repeat-auto-fill-fit-005-ref.html
+== grid-repeat-auto-fill-fit-006.html grid-repeat-auto-fill-fit-006-ref.html
diff --git a/layout/style/nsCSSKeywordList.h b/layout/style/nsCSSKeywordList.h
index e2e574c616..07051c8cf4 100644
--- a/layout/style/nsCSSKeywordList.h
+++ b/layout/style/nsCSSKeywordList.h
@@ -143,6 +143,8 @@ CSS_KEY(always, always)
CSS_KEY(annotation, annotation)
CSS_KEY(appworkspace, appworkspace)
CSS_KEY(auto, auto)
+CSS_KEY(auto-fill, auto_fill)
+CSS_KEY(auto-fit, auto_fit)
CSS_KEY(avoid, avoid)
CSS_KEY(background, background)
CSS_KEY(backwards, backwards)
diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp
index d5d38c7fec..c7f7ec792e 100644
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -10,6 +10,7 @@
#include "mozilla/DebugOnly.h"
#include "mozilla/Move.h"
#include "mozilla/MathAlgorithms.h"
+#include "mozilla/TypedEnumBits.h"
#include
// for std::stable_sort
@@ -97,6 +98,12 @@ enum class CSSParseResult : int32_t {
Error
};
+enum class GridTrackSizeFlags {
+ eDefaultTrackSize = 0x0,
+ eFixedTrackSize = 0x1, // parse a instead of
+};
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(GridTrackSizeFlags)
+
namespace {
// Rule processing function
@@ -927,10 +934,19 @@ protected:
CSSParseResult ParseGridLineNames(nsCSSValue& aValue);
bool ParseGridLineNameListRepeat(nsCSSValueList** aTailPtr);
bool ParseOptionalLineNameListAfterSubgrid(nsCSSValue& aValue);
- CSSParseResult ParseGridTrackBreadth(nsCSSValue& aValue);
- CSSParseResult ParseGridTrackSize(nsCSSValue& aValue);
+
+ // eFixedTrackSize in aFlags makes it parse a .
+ CSSParseResult ParseGridTrackBreadth(nsCSSValue& aValue,
+ GridTrackSizeFlags aFlags = GridTrackSizeFlags::eDefaultTrackSize);
+ // eFixedTrackSize in aFlags makes it parse a .
+ CSSParseResult ParseGridTrackSize(nsCSSValue& aValue,
+ GridTrackSizeFlags aFlags = GridTrackSizeFlags::eDefaultTrackSize);
+
bool ParseGridAutoColumnsRows(nsCSSProperty aPropID);
bool ParseGridTrackListRepeat(nsCSSValueList** aTailPtr);
+ bool ParseGridTrackRepeatIntro(bool aForSubgrid,
+ int32_t* aRepetitions,
+ Maybe* aRepeatAutoEnum);
// Assuming a [ ? ] has already been parsed,
// parse the rest of a .
@@ -8386,24 +8402,38 @@ CSSParserImpl::ParseGridLineNames(nsCSSValue& aValue)
}
// Assuming the 'repeat(' function token has already been consumed,
-// parse the rest of repeat(, +)
+// parse the rest of repeat( | auto-fill, +)
// Append to the linked list whose end is given by |aTailPtr|,
-// and updated |aTailPtr| to point to the new end of the list.
+// and update |aTailPtr| to point to the new end of the list.
bool
CSSParserImpl::ParseGridLineNameListRepeat(nsCSSValueList** aTailPtr)
{
- if (!(GetToken(true) &&
- mToken.mType == eCSSToken_Number &&
- mToken.mIntegerValid &&
- mToken.mInteger > 0)) {
- SkipUntil(')');
+ int32_t repetitions;
+ Maybe repeatAutoEnum;
+ if (!ParseGridTrackRepeatIntro(true, &repetitions, &repeatAutoEnum)) {
return false;
}
- int32_t repetitions = std::min(mToken.mInteger,
- GRID_TEMPLATE_MAX_REPETITIONS);
- if (!ExpectSymbol(',', true)) {
- SkipUntil(')');
- return false;
+ if (repeatAutoEnum.isSome()) {
+ // Parse exactly one .
+ nsCSSValue listValue;
+ nsCSSValueList* list = listValue.SetListValue();
+ if (ParseGridLineNames(list->mValue) != CSSParseResult::Ok) {
+ return false;
+ }
+ if (!ExpectSymbol(')', true)) {
+ return false;
+ }
+ // Instead of hooking up this list into the flat name list as usual,
+ // we create a pair(Int, List) where the first value is the auto-fill
+ // keyword and the second is the name list to repeat.
+ nsCSSValue kwd;
+ kwd.SetIntValue(repeatAutoEnum.value(), eCSSUnit_Enumerated);
+ *aTailPtr = (*aTailPtr)->mNext = new nsCSSValueList;
+ (*aTailPtr)->mValue.SetPairValue(kwd, listValue);
+ // Append an empty list since the caller expects that to represent the names
+ // that follows the repeat() function.
+ *aTailPtr = (*aTailPtr)->mNext = new nsCSSValueList;
+ return true;
}
// Parse at least one
@@ -8412,7 +8442,6 @@ CSSParserImpl::ParseGridLineNameListRepeat(nsCSSValueList** aTailPtr)
tail->mNext = new nsCSSValueList;
tail = tail->mNext;
if (ParseGridLineNames(tail->mValue) != CSSParseResult::Ok) {
- SkipUntil(')');
return false;
}
} while (!ExpectSymbol(')', true));
@@ -8446,16 +8475,27 @@ CSSParserImpl::ParseOptionalLineNameListAfterSubgrid(nsCSSValue& aValue)
// This marker distinguishes the value from a .
item->mValue.SetIntValue(NS_STYLE_GRID_TEMPLATE_SUBGRID,
eCSSUnit_Enumerated);
+ bool haveRepeatAuto = false;
for (;;) {
- // First try to parse repeat(, +)
+ // First try to parse , i.e.
+ // repeat( | auto-fill, +)
if (!GetToken(true)) {
return true;
}
if (mToken.mType == eCSSToken_Function &&
mToken.mIdent.LowerCaseEqualsLiteral("repeat")) {
+ nsCSSValueList* startOfRepeat = item;
if (!ParseGridLineNameListRepeat(&item)) {
+ SkipUntil(')');
return false;
}
+ if (startOfRepeat->mNext->mValue.GetUnit() == eCSSUnit_Pair) {
+ if (haveRepeatAuto) {
+ REPORT_UNEXPECTED(PEMoreThanOneGridRepeatAutoFillInNameList);
+ return false;
+ }
+ haveRepeatAuto = true;
+ }
} else {
UngetToken();
@@ -8475,16 +8515,20 @@ CSSParserImpl::ParseOptionalLineNameListAfterSubgrid(nsCSSValue& aValue)
}
}
-// Parse a .
+// Parse a , or when aFlags has eFixedTrackSize.
CSSParseResult
-CSSParserImpl::ParseGridTrackBreadth(nsCSSValue& aValue)
+CSSParserImpl::ParseGridTrackBreadth(nsCSSValue& aValue,
+ GridTrackSizeFlags aFlags)
{
- CSSParseResult result =
+ CSSParseResult result = (aFlags & GridTrackSizeFlags::eFixedTrackSize) ?
+ ParseNonNegativeVariant(aValue, VARIANT_LPCALC, nullptr) :
ParseNonNegativeVariant(aValue,
VARIANT_AUTO | VARIANT_LPCALC | VARIANT_KEYWORD,
nsCSSProps::kGridTrackBreadthKTable);
+
if (result == CSSParseResult::Ok ||
- result == CSSParseResult::Error) {
+ result == CSSParseResult::Error ||
+ (aFlags & GridTrackSizeFlags::eFixedTrackSize)) {
return result;
}
@@ -8502,12 +8546,13 @@ CSSParserImpl::ParseGridTrackBreadth(nsCSSValue& aValue)
return CSSParseResult::Ok;
}
-// Parse a .
+// Parse a , or when aFlags has eFixedTrackSize.
CSSParseResult
-CSSParserImpl::ParseGridTrackSize(nsCSSValue& aValue)
+CSSParserImpl::ParseGridTrackSize(nsCSSValue& aValue,
+ GridTrackSizeFlags aFlags)
{
// Attempt to parse a single .
- CSSParseResult result = ParseGridTrackBreadth(aValue);
+ CSSParseResult result = ParseGridTrackBreadth(aValue, aFlags);
if (result == CSSParseResult::Ok ||
result == CSSParseResult::Error) {
return result;
@@ -8523,9 +8568,9 @@ CSSParserImpl::ParseGridTrackSize(nsCSSValue& aValue)
return CSSParseResult::NotFound;
}
nsCSSValue::Array* func = aValue.InitFunction(eCSSKeyword_minmax, 2);
- if (ParseGridTrackBreadth(func->Item(1)) == CSSParseResult::Ok &&
+ if (ParseGridTrackBreadth(func->Item(1), aFlags) == CSSParseResult::Ok &&
ExpectSymbol(',', true) &&
- ParseGridTrackBreadth(func->Item(2)) == CSSParseResult::Ok &&
+ ParseGridTrackBreadth(func->Item(2), aFlags) == CSSParseResult::Ok &&
ExpectSymbol(')', true)) {
return CSSParseResult::Ok;
}
@@ -8564,6 +8609,7 @@ CSSParserImpl::ParseGridTrackListWithFirstLineNames(nsCSSValue& aValue,
// case after ParseGridTrackSize(); i.e. we'll greedily parse
// repeat()/ until we can't find one.
nsCSSValueList* item = firstLineNamesItem;
+ bool haveRepeatAuto = false;
for (;;) {
// First try to parse repeat()
if (!GetToken(true)) {
@@ -8571,9 +8617,18 @@ CSSParserImpl::ParseGridTrackListWithFirstLineNames(nsCSSValue& aValue,
}
if (mToken.mType == eCSSToken_Function &&
mToken.mIdent.LowerCaseEqualsLiteral("repeat")) {
+ nsCSSValueList* startOfRepeat = item;
if (!ParseGridTrackListRepeat(&item)) {
+ SkipUntil(')');
return false;
}
+ if (startOfRepeat->mNext->mValue.GetUnit() == eCSSUnit_Pair) {
+ if (haveRepeatAuto) {
+ REPORT_UNEXPECTED(PEMoreThanOneGridRepeatAutoFillFitInTrackList);
+ return false;
+ }
+ haveRepeatAuto = true;
+ }
} else {
UngetToken();
@@ -8644,26 +8699,60 @@ ConcatLineNames(nsCSSValue& aFirst, nsCSSValue& aSecond)
source->mNext = nullptr;
}
+// Assuming the 'repeat(' function token has already been consumed,
+// parse "repeat( | auto-fill | auto-fit ,"
+// (or "repeat( | auto-fill ," when aForSubgrid is true)
+// and stop after the comma. Return true when parsing succeeds,
+// with aRepetitions set to the number of repetitions and aRepeatAutoEnum set
+// to an enum value for auto-fill | auto-fit (it's not set at all when
+// was parsed).
+bool
+CSSParserImpl::ParseGridTrackRepeatIntro(bool aForSubgrid,
+ int32_t* aRepetitions,
+ Maybe* aRepeatAutoEnum)
+{
+ if (!GetToken(true)) {
+ return false;
+ }
+ if (mToken.mType == eCSSToken_Ident) {
+ if (mToken.mIdent.LowerCaseEqualsLiteral("auto-fill")) {
+ aRepeatAutoEnum->emplace(NS_STYLE_GRID_REPEAT_AUTO_FILL);
+ } else if (!aForSubgrid &&
+ mToken.mIdent.LowerCaseEqualsLiteral("auto-fit")) {
+ aRepeatAutoEnum->emplace(NS_STYLE_GRID_REPEAT_AUTO_FIT);
+ } else {
+ return false;
+ }
+ *aRepetitions = 1;
+ } else if (mToken.mType == eCSSToken_Number) {
+ if (!(mToken.mIntegerValid &&
+ mToken.mInteger > 0)) {
+ return false;
+ }
+ *aRepetitions = std::min(mToken.mInteger, GRID_TEMPLATE_MAX_REPETITIONS);
+ } else {
+ return false;
+ }
+
+ if (!ExpectSymbol(',', true)) {
+ return false;
+ }
+ return true;
+}
+
// Assuming the 'repeat(' function token has already been consumed,
// parse the rest of
-// repeat( ,
+// repeat( | auto-fill | auto-fit ,
// [ ? ]+ ? )
// Append to the linked list whose end is given by |aTailPtr|,
-// and updated |aTailPtr| to point to the new end of the list.
+// and update |aTailPtr| to point to the new end of the list.
+// Note: only one is allowed for auto-fill/fit
bool
CSSParserImpl::ParseGridTrackListRepeat(nsCSSValueList** aTailPtr)
{
- if (!(GetToken(true) &&
- mToken.mType == eCSSToken_Number &&
- mToken.mIntegerValid &&
- mToken.mInteger > 0)) {
- SkipUntil(')');
- return false;
- }
- int32_t repetitions = std::min(mToken.mInteger,
- GRID_TEMPLATE_MAX_REPETITIONS);
- if (!ExpectSymbol(',', true)) {
- SkipUntil(')');
+ int32_t repetitions;
+ Maybe repeatAutoEnum;
+ if (!ParseGridTrackRepeatIntro(false, &repetitions, &repeatAutoEnum)) {
return false;
}
@@ -8676,12 +8765,13 @@ CSSParserImpl::ParseGridTrackListRepeat(nsCSSValueList** aTailPtr)
nsCSSValue lastLineNames;
// Optional
if (ParseGridLineNames(firstLineNames) == CSSParseResult::Error) {
- SkipUntil(')');
return false;
}
// Required
- if (ParseGridTrackSize(trackSize) != CSSParseResult::Ok) {
- SkipUntil(')');
+ GridTrackSizeFlags flags = repeatAutoEnum.isSome()
+ ? GridTrackSizeFlags::eFixedTrackSize
+ : GridTrackSizeFlags::eDefaultTrackSize;
+ if (ParseGridTrackSize(trackSize, flags) != CSSParseResult::Ok) {
return false;
}
// Use nsAutoPtr to free the list in case of early return.
@@ -8692,7 +8782,6 @@ CSSParserImpl::ParseGridTrackListRepeat(nsCSSValueList** aTailPtr)
for (;;) {
// Optional
if (ParseGridLineNames(lastLineNames) == CSSParseResult::Error) {
- SkipUntil(')');
return false;
}
@@ -8700,9 +8789,15 @@ CSSParserImpl::ParseGridTrackListRepeat(nsCSSValueList** aTailPtr)
break;
}
+ // only accepts a single track size:
+ // ? ?
+ if (repeatAutoEnum.isSome()) {
+ REPORT_UNEXPECTED(PEMoreThanOneGridRepeatTrackSize);
+ return false;
+ }
+
// Required
if (ParseGridTrackSize(trackSize) != CSSParseResult::Ok) {
- SkipUntil(')');
return false;
}
@@ -8725,6 +8820,30 @@ CSSParserImpl::ParseGridTrackListRepeat(nsCSSValueList** aTailPtr)
// with the sublists in between
// * lastLineNames: the last
+ if (repeatAutoEnum.isSome()) {
+ // Instead of hooking up this list into the flat track/name list as usual,
+ // we create a pair(Int, List) where the first value is the auto-fill/fit
+ // keyword and the second is the list to repeat. There are three items
+ // in this list, the first is the list of line names before the track size,
+ // the second item is the track size, and the last item is the list of line
+ // names after the track size. Note that the line names are NOT merged
+ // with any line names before/after the repeat() itself.
+ nsCSSValue listValue;
+ nsCSSValueList* list = listValue.SetListValue();
+ list->mValue = firstLineNames;
+ list = list->mNext = new nsCSSValueList;
+ list->mValue = trackSize;
+ list = list->mNext = new nsCSSValueList;
+ list->mValue = lastLineNames;
+ nsCSSValue kwd;
+ kwd.SetIntValue(repeatAutoEnum.value(), eCSSUnit_Enumerated);
+ *aTailPtr = (*aTailPtr)->mNext = new nsCSSValueList;
+ (*aTailPtr)->mValue.SetPairValue(kwd, listValue);
+ // Append an empty list since the caller expects that to represent the names
+ // that follows the repeat() function.
+ *aTailPtr = (*aTailPtr)->mNext = new nsCSSValueList;
+ return true;
+ }
// Join the last and first (in that order.)
// For example, repeat(3, (a) 100px (b) 200px (c)) results in
diff --git a/layout/style/nsCSSValue.cpp b/layout/style/nsCSSValue.cpp
index 47f50e8f36..b40e6e5f3f 100644
--- a/layout/style/nsCSSValue.cpp
+++ b/layout/style/nsCSSValue.cpp
@@ -2011,9 +2011,48 @@ AppendGridTemplateToString(const nsCSSValueList* val,
if (unit == eCSSUnit_Enumerated &&
val->mValue.GetIntValue() == NS_STYLE_GRID_TEMPLATE_SUBGRID) {
+ MOZ_ASSERT(!isSubgrid, "saw subgrid once already");
isSubgrid = true;
aResult.AppendLiteral("subgrid");
+ } else if (unit == eCSSUnit_Pair) {
+ // This is a repeat 'auto-fill' / 'auto-fit'.
+ const nsCSSValuePair& pair = val->mValue.GetPairValue();
+ switch (pair.mXValue.GetIntValue()) {
+ case NS_STYLE_GRID_REPEAT_AUTO_FILL:
+ aResult.AppendLiteral("repeat(auto-fill, ");
+ break;
+ case NS_STYLE_GRID_REPEAT_AUTO_FIT:
+ aResult.AppendLiteral("repeat(auto-fit, ");
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("unexpected enum value");
+ }
+ const nsCSSValueList* repeatList = pair.mYValue.GetListValue();
+ if (repeatList->mValue.GetUnit() != eCSSUnit_Null) {
+ aResult.Append('[');
+ AppendValueListToString(repeatList->mValue.GetListValue(), aProperty,
+ aResult, aSerialization);
+ aResult.Append(']');
+ if (!isSubgrid) {
+ aResult.Append(' ');
+ }
+ } else if (isSubgrid) {
+ aResult.AppendLiteral("[]");
+ }
+ if (!isSubgrid) {
+ repeatList = repeatList->mNext;
+ repeatList->mValue.AppendToString(aProperty, aResult, aSerialization);
+ repeatList = repeatList->mNext;
+ if (repeatList->mValue.GetUnit() != eCSSUnit_Null) {
+ aResult.AppendLiteral(" [");
+ AppendValueListToString(repeatList->mValue.GetListValue(), aProperty,
+ aResult, aSerialization);
+ aResult.Append(']');
+ }
+ }
+ aResult.Append(')');
+
} else if (unit == eCSSUnit_Null) {
// Empty or omitted .
if (isSubgrid) {
diff --git a/layout/style/nsComputedDOMStyle.cpp b/layout/style/nsComputedDOMStyle.cpp
index dc79426817..ac4e66946f 100644
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -2362,26 +2362,60 @@ nsComputedDOMStyle::DoGetGridTemplateAreas()
return valueList;
}
-// aLineNames must not be empty
-CSSValue*
-nsComputedDOMStyle::GetGridLineNames(const nsTArray& aLineNames)
+void
+nsComputedDOMStyle::AppendGridLineNames(nsString& aResult,
+ const nsTArray& aLineNames)
{
- nsROCSSPrimitiveValue *val = new nsROCSSPrimitiveValue;
+ MOZ_ASSERT(!aLineNames.IsEmpty(), "expected some line names");
+ uint32_t numLines = aLineNames.Length();
+ for (uint32_t i = 0;;) {
+ nsStyleUtil::AppendEscapedCSSIdent(aLineNames[i], aResult);
+ if (++i == numLines) {
+ break;
+ }
+ aResult.Append(' ');
+ }
+}
+
+void
+nsComputedDOMStyle::AppendGridLineNames(nsDOMCSSValueList* aValueList,
+ const nsTArray& aLineNames)
+{
+ if (aLineNames.IsEmpty()) {
+ return;
+ }
+ nsROCSSPrimitiveValue* val = new nsROCSSPrimitiveValue;
nsAutoString lineNamesString;
- uint32_t i_end = aLineNames.Length();
lineNamesString.Assign('[');
- if (i_end > 0) {
- for (uint32_t i = 0;;) {
- nsStyleUtil::AppendEscapedCSSIdent(aLineNames[i], lineNamesString);
- if (++i == i_end) {
- break;
- }
+ AppendGridLineNames(lineNamesString, aLineNames);
+ lineNamesString.Append(']');
+ val->SetString(lineNamesString);
+ aValueList->AppendCSSValue(val);
+}
+
+void
+nsComputedDOMStyle::AppendGridLineNames(nsDOMCSSValueList* aValueList,
+ const nsTArray& aLineNames1,
+ const nsTArray& aLineNames2)
+{
+ if (aLineNames1.IsEmpty() && aLineNames2.IsEmpty()) {
+ return;
+ }
+ nsROCSSPrimitiveValue* val = new nsROCSSPrimitiveValue;
+ nsAutoString lineNamesString;
+ lineNamesString.Assign('[');
+ if (!aLineNames1.IsEmpty()) {
+ AppendGridLineNames(lineNamesString, aLineNames1);
+ }
+ if (!aLineNames2.IsEmpty()) {
+ if (!aLineNames1.IsEmpty()) {
lineNamesString.Append(' ');
}
+ AppendGridLineNames(lineNamesString, aLineNames2);
}
lineNamesString.Append(']');
val->SetString(lineNamesString);
- return val;
+ aValueList->AppendCSSValue(val);
}
CSSValue*
@@ -2421,6 +2455,7 @@ nsComputedDOMStyle::GetGridTemplateColumnsRows(const nsStyleGridTemplate& aTrack
const nsTArray* aTrackSizes)
{
if (aTrackList.mIsSubgrid) {
+ // XXX TODO: add support for repeat(auto-fill) for 'subgrid' (bug 1234311)
NS_ASSERTION(aTrackList.mMinTrackSizingFunctions.IsEmpty() &&
aTrackList.mMaxTrackSizingFunctions.IsEmpty(),
"Unexpected sizing functions with subgrid");
@@ -2431,7 +2466,21 @@ nsComputedDOMStyle::GetGridTemplateColumnsRows(const nsStyleGridTemplate& aTrack
valueList->AppendCSSValue(subgridKeyword);
for (uint32_t i = 0; i < aTrackList.mLineNameLists.Length(); i++) {
- valueList->AppendCSSValue(GetGridLineNames(aTrackList.mLineNameLists[i]));
+ if (MOZ_UNLIKELY(aTrackList.IsRepeatAutoIndex(i))) {
+ MOZ_ASSERT(aTrackList.mIsAutoFill, "subgrid can only have 'auto-fill'");
+ MOZ_ASSERT(!aTrackList.mRepeatAutoLineNameListBefore.IsEmpty(),
+ "The syntax is + so this shouldn't be empty");
+ MOZ_ASSERT(aTrackList.mRepeatAutoLineNameListAfter.IsEmpty(),
+ "mRepeatAutoLineNameListAfter isn't used for subgrid");
+ nsROCSSPrimitiveValue* start = new nsROCSSPrimitiveValue;
+ start->SetString(NS_LITERAL_STRING("repeat(auto-fill,"));
+ valueList->AppendCSSValue(start);
+ AppendGridLineNames(valueList, aTrackList.mRepeatAutoLineNameListBefore);
+ nsROCSSPrimitiveValue* end = new nsROCSSPrimitiveValue;
+ end->SetString(NS_LITERAL_STRING(")"));
+ valueList->AppendCSSValue(end);
+ }
+ AppendGridLineNames(valueList, aTrackList.mLineNameLists[i]);
}
return valueList;
}
@@ -2452,29 +2501,98 @@ nsComputedDOMStyle::GetGridTemplateColumnsRows(const nsStyleGridTemplate& aTrack
MOZ_ASSERT(aTrackList.mLineNameLists.Length() == numSizes + 1,
"Unexpected number of line name lists");
if (aTrackSizes) {
- for (uint32_t i = 0;; i++) {
- const nsTArray& lineNames = aTrackList.mLineNameLists[i];
- if (!lineNames.IsEmpty()) {
- valueList->AppendCSSValue(GetGridLineNames(lineNames));
+ // We've done layout on the grid and have resolved the sizes of its tracks,
+ // so we'll return those sizes here. The grid spec says we MAY use
+ // repeat(, Npx) here for consecutive tracks with the same
+ // size, but that doesn't seem worth doing since even for repeat(auto-*)
+ // the resolved size might differ for the repeated tracks.
+ const uint32_t numTracks = aTrackSizes->Length();
+ MOZ_ASSERT(numTracks > 0 ||
+ (aTrackList.HasRepeatAuto() && !aTrackList.mIsAutoFill),
+ "only 'auto-fit' can result in zero tracks");
+ if (numTracks == 0) {
+ nsROCSSPrimitiveValue* val = new nsROCSSPrimitiveValue;
+ val->SetIdent(eCSSKeyword_none);
+ return val;
+ }
+ int32_t endOfRepeat = 0; // first index after any repeat() tracks
+ int32_t offsetToLastRepeat = 0;
+ if (aTrackList.HasRepeatAuto()) {
+ // offsetToLastRepeat is -1 if all repeat(auto-fit) tracks are empty
+ offsetToLastRepeat = numTracks + 1 - aTrackList.mLineNameLists.Length();
+ endOfRepeat = aTrackList.mRepeatAutoIndex + offsetToLastRepeat + 1;
+ }
+ MOZ_ASSERT(numTracks > 0);
+ for (int32_t i = 0;; i++) {
+ if (aTrackList.HasRepeatAuto()) {
+ if (i == aTrackList.mRepeatAutoIndex) {
+ const nsTArray& lineNames = aTrackList.mLineNameLists[i];
+ if (i == endOfRepeat) {
+ // all auto-fit tracks are empty, so "[a] repeat(...) [b]"
+ // becomes "[a b]"
+ AppendGridLineNames(valueList, lineNames,
+ aTrackList.mLineNameLists[i + 1]);
+ } else {
+ AppendGridLineNames(valueList, lineNames,
+ aTrackList.mRepeatAutoLineNameListBefore);
+ }
+ } else if (i == endOfRepeat) {
+ const nsTArray& lineNames =
+ aTrackList.mLineNameLists[aTrackList.mRepeatAutoIndex + 1];
+ AppendGridLineNames(valueList,
+ aTrackList.mRepeatAutoLineNameListAfter,
+ lineNames);
+ } else if (i > aTrackList.mRepeatAutoIndex && i < endOfRepeat) {
+ AppendGridLineNames(valueList,
+ aTrackList.mRepeatAutoLineNameListAfter,
+ aTrackList.mRepeatAutoLineNameListBefore);
+ } else {
+ uint32_t j = i > endOfRepeat ? i - offsetToLastRepeat : i;
+ const nsTArray& lineNames = aTrackList.mLineNameLists[j];
+ AppendGridLineNames(valueList, lineNames);
+ }
+ } else {
+ const nsTArray& lineNames = aTrackList.mLineNameLists[i];
+ AppendGridLineNames(valueList, lineNames);
}
- if (i == numSizes) {
+ if (uint32_t(i) == numTracks) {
break;
}
nsROCSSPrimitiveValue* val = new nsROCSSPrimitiveValue;
- val->SetAppUnits(aTrackSizes->ElementAt(i));
+ val->SetAppUnits((*aTrackSizes)[i]);
valueList->AppendCSSValue(val);
}
} else {
+ // We don't have a frame. So, we'll just return a serialization of
+ // the tracks from the style (without resolved sizes).
for (uint32_t i = 0;; i++) {
const nsTArray& lineNames = aTrackList.mLineNameLists[i];
if (!lineNames.IsEmpty()) {
- valueList->AppendCSSValue(GetGridLineNames(lineNames));
+ AppendGridLineNames(valueList, lineNames);
}
if (i == numSizes) {
break;
}
- valueList->AppendCSSValue(GetGridTrackSize(aTrackList.mMinTrackSizingFunctions[i],
- aTrackList.mMaxTrackSizingFunctions[i]));
+ if (MOZ_UNLIKELY(aTrackList.IsRepeatAutoIndex(i))) {
+ nsROCSSPrimitiveValue* start = new nsROCSSPrimitiveValue;
+ start->SetString(aTrackList.mIsAutoFill ? NS_LITERAL_STRING("repeat(auto-fill,")
+ : NS_LITERAL_STRING("repeat(auto-fit,"));
+ valueList->AppendCSSValue(start);
+ if (!aTrackList.mRepeatAutoLineNameListBefore.IsEmpty()) {
+ AppendGridLineNames(valueList, aTrackList.mRepeatAutoLineNameListBefore);
+ }
+ valueList->AppendCSSValue(GetGridTrackSize(aTrackList.mMinTrackSizingFunctions[i],
+ aTrackList.mMaxTrackSizingFunctions[i]));
+ if (!aTrackList.mRepeatAutoLineNameListAfter.IsEmpty()) {
+ AppendGridLineNames(valueList, aTrackList.mRepeatAutoLineNameListAfter);
+ }
+ nsROCSSPrimitiveValue* end = new nsROCSSPrimitiveValue;
+ end->SetString(NS_LITERAL_STRING(")"));
+ valueList->AppendCSSValue(end);
+ } else {
+ valueList->AppendCSSValue(GetGridTrackSize(aTrackList.mMinTrackSizingFunctions[i],
+ aTrackList.mMaxTrackSizingFunctions[i]));
+ }
}
}
diff --git a/layout/style/nsComputedDOMStyle.h b/layout/style/nsComputedDOMStyle.h
index ce7a6d02b4..c5b04d1f27 100644
--- a/layout/style/nsComputedDOMStyle.h
+++ b/layout/style/nsComputedDOMStyle.h
@@ -176,7 +176,16 @@ private:
mozilla::dom::CSSValue* GetSVGPaintFor(bool aFill);
- mozilla::dom::CSSValue* GetGridLineNames(const nsTArray& aLineNames);
+ // Appends all aLineNames (must be non-empty) space-separated to aResult.
+ void AppendGridLineNames(nsString& aResult,
+ const nsTArray& aLineNames);
+ // Appends aLineNames (if non-empty) as a CSSValue* to aValueList.
+ void AppendGridLineNames(nsDOMCSSValueList* aValueList,
+ const nsTArray& aLineNames);
+ // Appends aLineNames1/2 (if non-empty) as a CSSValue* to aValueList.
+ void AppendGridLineNames(nsDOMCSSValueList* aValueList,
+ const nsTArray& aLineNames1,
+ const nsTArray& aLineNames2);
mozilla::dom::CSSValue* GetGridTrackSize(const nsStyleCoord& aMinSize,
const nsStyleCoord& aMaxSize);
mozilla::dom::CSSValue* GetGridTemplateColumnsRows(const nsStyleGridTemplate& aTrackList,
diff --git a/layout/style/nsRuleNode.cpp b/layout/style/nsRuleNode.cpp
index 66981d7783..f10f273cfb 100644
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -7690,9 +7690,11 @@ SetGridTrackBreadth(const nsCSSValue& aValue,
MOZ_ASSERT(unit != eCSSUnit_Inherit && unit != eCSSUnit_Unset,
"Unexpected value that would use dummyParentCoord");
const nsStyleCoord dummyParentCoord;
- SetCoord(aValue, aResult, dummyParentCoord,
- SETCOORD_LPE | SETCOORD_STORE_CALC,
- aStyleContext, aPresContext, aConditions);
+ DebugOnly stored =
+ SetCoord(aValue, aResult, dummyParentCoord,
+ SETCOORD_LPE | SETCOORD_STORE_CALC,
+ aStyleContext, aPresContext, aConditions);
+ MOZ_ASSERT(stored, "invalid value");
}
}
@@ -7760,15 +7762,14 @@ SetGridAutoColumnsRows(const nsCSSValue& aValue,
static void
AppendGridLineNames(const nsCSSValue& aValue,
- nsStyleGridTemplate& aResult)
+ nsTArray& aNameList)
{
// Compute a value
- nsTArray* nameList = aResult.mLineNameLists.AppendElement();
// Null unit means empty list, nothing more to do.
if (aValue.GetUnit() != eCSSUnit_Null) {
const nsCSSValueList* item = aValue.GetListValue();
do {
- nsString* name = nameList->AppendElement();
+ nsString* name = aNameList.AppendElement();
item->mValue.GetStringValue(*name);
item = item->mNext;
} while (item);
@@ -7794,6 +7795,10 @@ SetGridTrackList(const nsCSSValue& aValue,
aResult.mLineNameLists = aParentValue.mLineNameLists;
aResult.mMinTrackSizingFunctions = aParentValue.mMinTrackSizingFunctions;
aResult.mMaxTrackSizingFunctions = aParentValue.mMaxTrackSizingFunctions;
+ aResult.mRepeatAutoLineNameListBefore = aParentValue.mRepeatAutoLineNameListBefore;
+ aResult.mRepeatAutoLineNameListAfter = aParentValue.mRepeatAutoLineNameListAfter;
+ aResult.mRepeatAutoIndex = aParentValue.mRepeatAutoIndex;
+ aResult.mIsAutoFill = aParentValue.mIsAutoFill;
break;
case eCSSUnit_Initial:
@@ -7803,20 +7808,42 @@ SetGridTrackList(const nsCSSValue& aValue,
aResult.mLineNameLists.Clear();
aResult.mMinTrackSizingFunctions.Clear();
aResult.mMaxTrackSizingFunctions.Clear();
+ aResult.mRepeatAutoLineNameListBefore.Clear();
+ aResult.mRepeatAutoLineNameListAfter.Clear();
+ aResult.mRepeatAutoIndex = -1;
+ aResult.mIsAutoFill = false;
break;
default:
aResult.mLineNameLists.Clear();
aResult.mMinTrackSizingFunctions.Clear();
aResult.mMaxTrackSizingFunctions.Clear();
+ aResult.mRepeatAutoLineNameListBefore.Clear();
+ aResult.mRepeatAutoLineNameListAfter.Clear();
+ aResult.mRepeatAutoIndex = -1;
+ aResult.mIsAutoFill = false;
const nsCSSValueList* item = aValue.GetListValue();
if (item->mValue.GetUnit() == eCSSUnit_Enumerated &&
item->mValue.GetIntValue() == NS_STYLE_GRID_TEMPLATE_SUBGRID) {
// subgrid ?
aResult.mIsSubgrid = true;
item = item->mNext;
- while (item) {
- AppendGridLineNames(item->mValue, aResult);
+ for (int32_t i = 0; item && i < nsStyleGridLine::kMaxLine; ++i) {
+ if (item->mValue.GetUnit() == eCSSUnit_Pair) {
+ // This is a 'auto-fill' expression.
+ const nsCSSValuePair& pair = item->mValue.GetPairValue();
+ MOZ_ASSERT(aResult.mRepeatAutoIndex == -1,
+ "can only have one with auto-fill");
+ aResult.mRepeatAutoIndex = i;
+ aResult.mIsAutoFill = true;
+ MOZ_ASSERT(pair.mXValue.GetIntValue() == NS_STYLE_GRID_REPEAT_AUTO_FILL,
+ "unexpected repeat() enum value for subgrid");
+ const nsCSSValueList* list = pair.mYValue.GetListValue();
+ AppendGridLineNames(list->mValue, aResult.mRepeatAutoLineNameListBefore);
+ } else {
+ AppendGridLineNames(item->mValue,
+ *aResult.mLineNameLists.AppendElement());
+ }
item = item->mNext;
}
} else {
@@ -7826,17 +7853,45 @@ SetGridTrackList(const nsCSSValue& aValue,
// and alternating between that and .
aResult.mIsSubgrid = false;
for (int32_t line = 1; ; ++line) {
- AppendGridLineNames(item->mValue, aResult);
+ AppendGridLineNames(item->mValue,
+ *aResult.mLineNameLists.AppendElement());
item = item->mNext;
if (!item || line == nsStyleGridLine::kMaxLine) {
break;
}
- nsStyleCoord& min = *aResult.mMinTrackSizingFunctions.AppendElement();
- nsStyleCoord& max = *aResult.mMaxTrackSizingFunctions.AppendElement();
- SetGridTrackSize(item->mValue, min, max,
- aStyleContext, aPresContext, aConditions);
+ if (item->mValue.GetUnit() == eCSSUnit_Pair) {
+ // This is a 'auto-fill' / 'auto-fit' expression.
+ const nsCSSValuePair& pair = item->mValue.GetPairValue();
+ MOZ_ASSERT(aResult.mRepeatAutoIndex == -1,
+ "can only have one ");
+ aResult.mRepeatAutoIndex = line - 1;
+ switch (pair.mXValue.GetIntValue()) {
+ case NS_STYLE_GRID_REPEAT_AUTO_FILL:
+ aResult.mIsAutoFill = true;
+ break;
+ case NS_STYLE_GRID_REPEAT_AUTO_FIT:
+ aResult.mIsAutoFill = false;
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("unexpected repeat() enum value");
+ }
+ const nsCSSValueList* list = pair.mYValue.GetListValue();
+ AppendGridLineNames(list->mValue, aResult.mRepeatAutoLineNameListBefore);
+ list = list->mNext;
+ nsStyleCoord& min = *aResult.mMinTrackSizingFunctions.AppendElement();
+ nsStyleCoord& max = *aResult.mMaxTrackSizingFunctions.AppendElement();
+ SetGridTrackSize(list->mValue, min, max,
+ aStyleContext, aPresContext, aConditions);
+ list = list->mNext;
+ AppendGridLineNames(list->mValue, aResult.mRepeatAutoLineNameListAfter);
+ } else {
+ nsStyleCoord& min = *aResult.mMinTrackSizingFunctions.AppendElement();
+ nsStyleCoord& max = *aResult.mMaxTrackSizingFunctions.AppendElement();
+ SetGridTrackSize(item->mValue, min, max,
+ aStyleContext, aPresContext, aConditions);
+ }
item = item->mNext;
MOZ_ASSERT(item, "Expected a eCSSUnit_List of odd length");
diff --git a/layout/style/nsStyleConsts.h b/layout/style/nsStyleConsts.h
index 9f14a50275..bf5eda55f4 100644
--- a/layout/style/nsStyleConsts.h
+++ b/layout/style/nsStyleConsts.h
@@ -640,6 +640,10 @@ enum class FillMode : uint32_t;
#define NS_STYLE_GRID_TRACK_BREADTH_MAX_CONTENT 1
#define NS_STYLE_GRID_TRACK_BREADTH_MIN_CONTENT 2
+// CSS Grid keywords for
+#define NS_STYLE_GRID_REPEAT_AUTO_FILL 0
+#define NS_STYLE_GRID_REPEAT_AUTO_FIT 1
+
// defaults per MathML spec
#define NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER 0.71f
#define NS_MATHML_DEFAULT_SCRIPT_MIN_SIZE_PT 8
diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h
index 66d757bb47..952a8c2bfa 100644
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -1262,55 +1262,6 @@ public:
nsRect mImageRegion; // [inherited] the rect to use within an image
};
-// Computed value of the grid-template-columns or grid-template-rows property
-// (but *not* grid-template-areas.)
-// http://dev.w3.org/csswg/css-grid/#track-sizing
-//
-// This represents either:
-// * none:
-// mIsSubgrid is false, all three arrays are empty
-// * :
-// mIsSubgrid is false,
-// mMinTrackSizingFunctions and mMaxTrackSizingFunctions
-// are of identical non-zero size,
-// and mLineNameLists is one element longer than that.
-// (Delimiting N columns requires N+1 lines:
-// one before each track, plus one at the very end.)
-//
-// An omitted is still represented in mLineNameLists,
-// as an empty sub-array.
-//
-// A specified as a single is represented
-// as identical min and max sizing functions.
-//
-// The units for nsStyleCoord are:
-// * eStyleUnit_Percent represents a
-// * eStyleUnit_FlexFraction represents a flexible fraction
-// * eStyleUnit_Coord represents a
-// * eStyleUnit_Enumerated represents min-content or max-content
-// * subgrid ?:
-// mIsSubgrid is true,
-// mLineNameLists may or may not be empty,
-// mMinTrackSizingFunctions and mMaxTrackSizingFunctions are empty.
-struct nsStyleGridTemplate {
- bool mIsSubgrid;
- nsTArray> mLineNameLists;
- nsTArray mMinTrackSizingFunctions;
- nsTArray mMaxTrackSizingFunctions;
-
- nsStyleGridTemplate()
- : mIsSubgrid(false)
- {
- }
-
- inline bool operator!=(const nsStyleGridTemplate& aOther) const {
- return mIsSubgrid != aOther.mIsSubgrid ||
- mLineNameLists != aOther.mLineNameLists ||
- mMinTrackSizingFunctions != aOther.mMinTrackSizingFunctions ||
- mMaxTrackSizingFunctions != aOther.mMaxTrackSizingFunctions;
- }
-};
-
struct nsStyleGridLine {
// http://dev.w3.org/csswg/css-grid/#typedef-grid-line
// XXXmats we could optimize memory size here
@@ -2294,6 +2245,83 @@ struct nsStyleTable {
int32_t mSpan; // [reset] the number of columns spanned by a colgroup or col
};
+// Computed value of the grid-template-columns or grid-template-rows property
+// (but *not* grid-template-areas.)
+// http://dev.w3.org/csswg/css-grid/#track-sizing
+//
+// This represents either:
+// * none:
+// mIsSubgrid is false, all three arrays are empty
+// * :
+// mIsSubgrid is false,
+// mMinTrackSizingFunctions and mMaxTrackSizingFunctions
+// are of identical non-zero size,
+// and mLineNameLists is one element longer than that.
+// (Delimiting N columns requires N+1 lines:
+// one before each track, plus one at the very end.)
+//
+// An omitted is still represented in mLineNameLists,
+// as an empty sub-array.
+//
+// A specified as a single is represented
+// as identical min and max sizing functions.
+//
+// The units for nsStyleCoord are:
+// * eStyleUnit_Percent represents a
+// * eStyleUnit_FlexFraction represents a flexible fraction
+// * eStyleUnit_Coord represents a
+// * eStyleUnit_Enumerated represents min-content or max-content
+// * subgrid ?:
+// mIsSubgrid is true,
+// mLineNameLists may or may not be empty,
+// mMinTrackSizingFunctions and mMaxTrackSizingFunctions are empty.
+//
+// If mRepeatAutoIndex != -1 then that index is an and
+// mIsAutoFill == true means it's an 'auto-fill', otherwise 'auto-fit'.
+// mRepeatAutoLineNameListBefore is the list of line names before the track
+// size, mRepeatAutoLineNameListAfter the names after. (They are empty
+// when there is no track, i.e. when mRepeatAutoIndex == -1).
+// When mIsSubgrid is true, mRepeatAutoLineNameListBefore contains the line
+// names and mRepeatAutoLineNameListAfter is empty.
+struct nsStyleGridTemplate {
+ nsTArray> mLineNameLists;
+ nsTArray mMinTrackSizingFunctions;
+ nsTArray mMaxTrackSizingFunctions;
+ nsTArray mRepeatAutoLineNameListBefore;
+ nsTArray mRepeatAutoLineNameListAfter;
+ int16_t mRepeatAutoIndex; // -1 or the track index for an auto-fill/fit track
+ bool mIsAutoFill : 1;
+ bool mIsSubgrid : 1;
+
+ nsStyleGridTemplate()
+ : mRepeatAutoIndex(-1)
+ , mIsAutoFill(false)
+ , mIsSubgrid(false)
+ {
+ }
+
+ inline bool operator!=(const nsStyleGridTemplate& aOther) const {
+ return
+ mIsSubgrid != aOther.mIsSubgrid ||
+ mLineNameLists != aOther.mLineNameLists ||
+ mMinTrackSizingFunctions != aOther.mMinTrackSizingFunctions ||
+ mMaxTrackSizingFunctions != aOther.mMaxTrackSizingFunctions ||
+ mIsAutoFill != aOther.mIsAutoFill ||
+ mRepeatAutoIndex != aOther.mRepeatAutoIndex ||
+ mRepeatAutoLineNameListBefore != aOther.mRepeatAutoLineNameListBefore ||
+ mRepeatAutoLineNameListAfter != aOther.mRepeatAutoLineNameListAfter;
+ }
+
+ bool HasRepeatAuto() const {
+ return mRepeatAutoIndex != -1;
+ }
+
+ bool IsRepeatAutoIndex(uint32_t aIndex) const {
+ MOZ_ASSERT(aIndex < uint32_t(2*nsStyleGridLine::kMaxLine));
+ return int32_t(aIndex) == mRepeatAutoIndex;
+ }
+};
+
struct nsStylePosition {
nsStylePosition(void);
nsStylePosition(const nsStylePosition& aOther);
diff --git a/layout/style/test/property_database.js b/layout/style/test/property_database.js
index 734cd0c0da..b5f80c564c 100644
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -5955,7 +5955,15 @@ if (IsCSSPropertyPrefEnabled("layout.css.grid.enabled")) {
"[a] 2.5fr [z] Repeat(4, [a] 20px [] auto [b c]) [d]",
"[a] 2.5fr [z] Repeat(4, [a] 20px [] auto) [d]",
"[a] 2.5fr [z] Repeat(4, 20px [b c] auto [b c]) [d]",
- "[a] 2.5fr [z] Repeat(4, 20px auto) [d]"
+ "[a] 2.5fr [z] Repeat(4, 20px auto) [d]",
+ "repeat(auto-fill, 0)",
+ "[a] repeat( Auto-fill,1%)",
+ "[a] repeat(Auto-fit, 0)",
+ "repeat(Auto-fit,[] 1%)",
+ "repeat(auto-fit, [a] 1em) auto",
+ "[a] repeat( auto-fit,[a b] minmax(0,0) )",
+ "[a] 40px repeat(auto-fit,[a b] minmax(1px, 0) [])",
+ "[a] auto [b] repeat(auto-fit,[a b] minmax(1mm, 1%) [c]) [c] auto",
],
invalid_values: [
"",
@@ -5992,7 +6000,22 @@ if (IsCSSPropertyPrefEnabled("layout.css.grid.enabled")) {
"repeat(2.5, 20px)",
"repeat(2, (foo))",
"repeat(2, foo)",
- "40px calc(0px + rubbish)"
+ "40px calc(0px + rubbish)",
+ "repeat(1, repeat(1, 20px))",
+ "repeat(auto-fill, auto)",
+ "repeat(auto-fit,auto)",
+ "repeat(auto-fit,[])",
+ "repeat(auto-fill, 0) repeat(auto-fit, 0) ",
+ "repeat(auto-fit, 0) repeat(auto-fill, 0) ",
+ "[a] repeat(auto-fit, 0) repeat(auto-fit, 0) ",
+ "[a] repeat(auto-fill, 0) [a] repeat(auto-fill, 0) ",
+ "repeat(auto-fill, 0 0)",
+ "repeat(auto-fill, 0 [] 0)",
+ "repeat(auto-fill, min-content)",
+ "repeat(auto-fit,max-content)",
+ "repeat(auto-fit,minmax(auto,auto))",
+ "repeat(auto-fit,[] minmax(1px, min-content))",
+ "repeat(auto-fit,[a] minmax(1%, auto) [])",
],
unbalanced_values: [
"(foo] 40px",
@@ -6006,7 +6029,11 @@ if (IsCSSPropertyPrefEnabled("layout.css.grid.enabled")) {
"subgrid",
"subgrid [] [foo bar]",
"subgrid repeat(1, [])",
- "subgrid Repeat(4, [a] [b c] [] [d])"
+ "subgrid Repeat(4, [a] [b c] [] [d])",
+ "repeat(auto-fill, [])",
+ "[] repeat(Auto-fill, [a] [b c] [] [d])",
+ "[x] repeat( Auto-fill, [a b c]) []",
+ "[x] repeat(auto-fill, []) [y z]"
);
gCSSProperties["grid-template-columns"].invalid_values.push(
"subgrid (foo) 40px",
@@ -6021,7 +6048,14 @@ if (IsCSSPropertyPrefEnabled("layout.css.grid.enabled")) {
"subgrid repeat(1)",
"subgrid repeat(1, )",
"subgrid repeat(2, (40px))",
- "subgrid repeat(2, foo)"
+ "subgrid repeat(2, foo)",
+ "subgrid repeat(1, repeat(1, []))",
+ "subgrid repeat(auto-fit,[])",
+ "subgrid [] repeat(auto-fit,[])",
+ "subgrid [a] repeat(auto-fit,[])",
+ "subgrid repeat(auto-fill, 1px)",
+ "subgrid repeat(auto-fill, 1px [])",
+ "subgrid repeat(auto-fill, []) repeat(auto-fill, [])"
);
}
gCSSProperties["grid-template-rows"] = {
diff --git a/media/webrtc/signaling/src/media-conduit/MediaConduitInterface.h b/media/webrtc/signaling/src/media-conduit/MediaConduitInterface.h
index f7ce554fd1..364261ed2f 100644
--- a/media/webrtc/signaling/src/media-conduit/MediaConduitInterface.h
+++ b/media/webrtc/signaling/src/media-conduit/MediaConduitInterface.h
@@ -238,7 +238,7 @@ class CodecPluginID
public:
virtual ~CodecPluginID() {}
- virtual const uint64_t PluginID() = 0;
+ virtual uint64_t PluginID() const = 0;
};
class VideoEncoder : public CodecPluginID
diff --git a/media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.h b/media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.h
index 597d4c3f1b..b81acebf7b 100644
--- a/media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.h
+++ b/media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.h
@@ -133,7 +133,7 @@ public:
// Implement VideoEncoder interface, sort of.
// (We cannot use |Release|, since that's needed for nsRefPtr)
- virtual const uint64_t PluginID()
+ virtual uint64_t PluginID() const
{
return mCachedPluginId;
}
@@ -300,7 +300,7 @@ class WebrtcVideoEncoderProxy : public WebrtcVideoEncoder
RegisterEncodeCompleteCallback(nullptr);
}
- const uint64_t PluginID() override
+ uint64_t PluginID() const override
{
return mEncoderImpl->PluginID();
}
@@ -359,7 +359,7 @@ public:
// Implement VideoEncoder interface, sort of.
// (We cannot use |Release|, since that's needed for nsRefPtr)
- virtual const uint64_t PluginID()
+ virtual uint64_t PluginID() const
{
return mCachedPluginId;
}
@@ -477,7 +477,7 @@ class WebrtcVideoDecoderProxy : public WebrtcVideoDecoder
RegisterDecodeCompleteCallback(nullptr);
}
- const uint64_t PluginID() override
+ uint64_t PluginID() const override
{
return mDecoderImpl->PluginID();
}
diff --git a/media/webrtc/signaling/src/media-conduit/WebrtcMediaCodecVP8VideoCodec.h b/media/webrtc/signaling/src/media-conduit/WebrtcMediaCodecVP8VideoCodec.h
index 990208406a..c8e57c0587 100644
--- a/media/webrtc/signaling/src/media-conduit/WebrtcMediaCodecVP8VideoCodec.h
+++ b/media/webrtc/signaling/src/media-conduit/WebrtcMediaCodecVP8VideoCodec.h
@@ -31,7 +31,7 @@ public:
virtual ~WebrtcMediaCodecVP8VideoEncoder() override;
// Implement VideoEncoder interface.
- virtual const uint64_t PluginID() override { return 0; }
+ virtual uint64_t PluginID() const override { return 0; }
virtual int32_t InitEncode(const webrtc::VideoCodec* codecSettings,
int32_t numberOfCores,
@@ -74,7 +74,7 @@ public:
virtual ~WebrtcMediaCodecVP8VideoDecoder() override;
// Implement VideoDecoder interface.
- virtual const uint64_t PluginID() override { return 0; }
+ virtual uint64_t PluginID() const override { return 0; }
virtual int32_t InitDecode(const webrtc::VideoCodec* codecSettings,
int32_t numberOfCores) override;
diff --git a/media/webrtc/signaling/src/media-conduit/WebrtcOMXH264VideoCodec.h b/media/webrtc/signaling/src/media-conduit/WebrtcOMXH264VideoCodec.h
index 55022ff7aa..940c6270ad 100644
--- a/media/webrtc/signaling/src/media-conduit/WebrtcOMXH264VideoCodec.h
+++ b/media/webrtc/signaling/src/media-conduit/WebrtcOMXH264VideoCodec.h
@@ -36,7 +36,7 @@ public:
virtual ~WebrtcOMXH264VideoEncoder();
// Implement VideoEncoder interface.
- virtual const uint64_t PluginID() override { return 0; }
+ virtual uint64_t PluginID() const override { return 0; }
virtual int32_t InitEncode(const webrtc::VideoCodec* aCodecSettings,
int32_t aNumOfCores,
@@ -82,7 +82,7 @@ public:
virtual ~WebrtcOMXH264VideoDecoder();
// Implement VideoDecoder interface.
- virtual const uint64_t PluginID() override { return 0; }
+ virtual uint64_t PluginID() const override { return 0; }
virtual int32_t InitDecode(const webrtc::VideoCodec* aCodecSettings,
int32_t aNumOfCores) override;
diff --git a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h
index e7f8cb4b1d..a69eb4a0bc 100644
--- a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h
+++ b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h
@@ -403,7 +403,7 @@ public:
// Index used to refer to this before we know the TrackID
// Note: unlike MediaPipeline::trackid(), this is threadsafe
// Not set until first media is received
- virtual TrackID const trackid_locked() { return listener_->trackid(); }
+ virtual TrackID trackid_locked() const { return listener_->trackid(); }
// written and used from MainThread
virtual bool IsVideo() const override { return is_video_; }
diff --git a/media/webrtc/signaling/src/sdp/Sdp.h b/media/webrtc/signaling/src/sdp/Sdp.h
index 3b00e5ab1b..8eeb89e2f8 100644
--- a/media/webrtc/signaling/src/sdp/Sdp.h
+++ b/media/webrtc/signaling/src/sdp/Sdp.h
@@ -156,7 +156,7 @@ public:
return mSessionVersion;
}
- const sdp::AddrType
+ sdp::AddrType
GetAddrType() const
{
return mAddrType;
diff --git a/media/webrtc/signaling/test/FakeMediaStreams.h b/media/webrtc/signaling/test/FakeMediaStreams.h
index e1cda6f50e..bfc693d4e4 100644
--- a/media/webrtc/signaling/test/FakeMediaStreams.h
+++ b/media/webrtc/signaling/test/FakeMediaStreams.h
@@ -313,7 +313,7 @@ public:
{
return mIsVideo? nullptr : this;
}
- const uint32_t typeSize () const
+ uint32_t typeSize () const
{
return sizeof(Fake_MediaStreamTrack);
}
diff --git a/modules/libjar/InterceptedJARChannel.cpp b/modules/libjar/InterceptedJARChannel.cpp
index c52e373b46..659ba98d63 100644
--- a/modules/libjar/InterceptedJARChannel.cpp
+++ b/modules/libjar/InterceptedJARChannel.cpp
@@ -45,6 +45,12 @@ InterceptedJARChannel::GetChannel(nsIChannel** aChannel)
return NS_OK;
}
+NS_IMETHODIMP
+InterceptedJARChannel::GetSecureUpgradedChannelURI(nsIURI** aURI)
+{
+ return mChannel->GetURI(aURI);
+}
+
NS_IMETHODIMP
InterceptedJARChannel::ResetInterception()
{
diff --git a/mozglue/linker/Mappable.h b/mozglue/linker/Mappable.h
index 56f5dccd5f..7ab7a66555 100644
--- a/mozglue/linker/Mappable.h
+++ b/mozglue/linker/Mappable.h
@@ -239,18 +239,18 @@ private:
}
/* Returns offset + length */
- const off_t endOffset() const {
+ off_t endOffset() const {
return offset + length;
}
/* Returns the offset corresponding to the given address */
- const off_t offsetOf(const void *ptr) const {
+ off_t offsetOf(const void *ptr) const {
return reinterpret_cast(ptr)
- reinterpret_cast(addr) + offset;
}
/* Returns whether the given address is in the LazyMap range */
- const bool Contains(const void *ptr) const {
+ bool Contains(const void *ptr) const {
return (ptr >= addr) && (ptr < end());
}
};
diff --git a/mozglue/linker/SeekableZStream.h b/mozglue/linker/SeekableZStream.h
index 83e6196f46..3505c681e8 100644
--- a/mozglue/linker/SeekableZStream.h
+++ b/mozglue/linker/SeekableZStream.h
@@ -77,18 +77,18 @@ public:
bool DecompressChunk(void *where, size_t chunk, size_t length = 0);
/* Returns the uncompressed size of the complete zstream */
- const size_t GetUncompressedSize() const
+ size_t GetUncompressedSize() const
{
return (offsetTable.numElements() - 1) * chunkSize + lastChunkSize;
}
/* Returns the chunk size of the given chunk */
- const size_t GetChunkSize(size_t chunk = 0) const {
+ size_t GetChunkSize(size_t chunk = 0) const {
return (chunk == offsetTable.numElements() - 1) ? lastChunkSize : chunkSize;
}
/* Returns the number of chunks */
- const size_t GetChunksNum() const {
+ size_t GetChunksNum() const {
return offsetTable.numElements();
}
diff --git a/netwerk/base/PrivateBrowsingChannel.h b/netwerk/base/PrivateBrowsingChannel.h
index 165bea03c4..0fe96bd1a2 100644
--- a/netwerk/base/PrivateBrowsingChannel.h
+++ b/netwerk/base/PrivateBrowsingChannel.h
@@ -48,7 +48,7 @@ public:
NS_IMETHOD GetIsChannelPrivate(bool *aResult)
{
NS_ENSURE_ARG_POINTER(aResult);
- *aResult = NS_UsePrivateBrowsing(static_cast(this));
+ *aResult = mPrivateBrowsing;
return NS_OK;
}
@@ -63,6 +63,21 @@ public:
return NS_OK;
}
+ // Must be called every time the channel's callbacks or loadGroup is updated
+ void UpdatePrivateBrowsing()
+ {
+ // once marked as private we never go un-private
+ if (mPrivateBrowsing) {
+ return;
+ }
+
+ nsCOMPtr loadContext;
+ NS_QueryNotificationCallbacks(static_cast(this), loadContext);
+ if (loadContext) {
+ mPrivateBrowsing = loadContext->UsePrivateBrowsing();
+ }
+ }
+
bool CanSetCallbacks(nsIInterfaceRequestor* aCallbacks) const
{
// Make sure that the private bit override flag is not set.
diff --git a/netwerk/base/nsBaseChannel.cpp b/netwerk/base/nsBaseChannel.cpp
index 80d2bc5afc..27ae10478d 100644
--- a/netwerk/base/nsBaseChannel.cpp
+++ b/netwerk/base/nsBaseChannel.cpp
@@ -101,7 +101,7 @@ nsBaseChannel::Redirect(nsIChannel *newChannel, uint32_t redirectFlags,
newChannel->SetLoadInfo(nullptr);
}
- // Try to preserve the privacy bit if it has been overridden
+ // Preserve the privacy bit if it has been overridden
if (mPrivateBrowsingOverriden) {
nsCOMPtr newPBChannel =
do_QueryInterface(newChannel);
@@ -424,6 +424,7 @@ nsBaseChannel::SetLoadGroup(nsILoadGroup *aLoadGroup)
mLoadGroup = aLoadGroup;
CallbacksChanged();
+ UpdatePrivateBrowsing();
return NS_OK;
}
@@ -497,6 +498,7 @@ nsBaseChannel::SetNotificationCallbacks(nsIInterfaceRequestor *aCallbacks)
mCallbacks = aCallbacks;
CallbacksChanged();
+ UpdatePrivateBrowsing();
return NS_OK;
}
diff --git a/netwerk/base/nsINetworkInterceptController.idl b/netwerk/base/nsINetworkInterceptController.idl
index 7e59c6cae5..bbac746b27 100644
--- a/netwerk/base/nsINetworkInterceptController.idl
+++ b/netwerk/base/nsINetworkInterceptController.idl
@@ -29,7 +29,7 @@ class ChannelInfo;
* which do not implement nsIChannel.
*/
-[scriptable, uuid(64439e24-eda5-4f39-9a7e-162c4b5e0150)]
+[scriptable, uuid(f4b82975-6a86-4cc4-87fe-9a1fd430c86d)]
interface nsIInterceptedChannel : nsISupports
{
/**
@@ -76,6 +76,12 @@ interface nsIInterceptedChannel : nsISupports
*/
readonly attribute nsIChannel channel;
+ /**
+ * The URL of the underlying channel object, corrected for a potential
+ * secure upgrade.
+ */
+ readonly attribute nsIURI secureUpgradedChannelURI;
+
/**
* This method allows to override the channel info for the channel.
*/
diff --git a/netwerk/base/nsNetUtil.cpp b/netwerk/base/nsNetUtil.cpp
index a1a7025b80..bec7ebaf94 100644
--- a/netwerk/base/nsNetUtil.cpp
+++ b/netwerk/base/nsNetUtil.cpp
@@ -4,6 +4,9 @@
* 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/. */
+// HttpLog.h should generally be included first
+#include "HttpLog.h"
+
#include "mozilla/LoadContext.h"
#include "mozilla/LoadInfo.h"
#include "mozilla/BasePrincipal.h"
@@ -58,6 +61,10 @@
#include "nsInterfaceRequestorAgg.h"
#include "plstr.h"
#include "nsINestedURI.h"
+#include "mozilla/dom/nsCSPUtils.h"
+#include "nsIScriptError.h"
+#include "nsISiteSecurityService.h"
+#include "nsHttpHandler.h"
#ifdef MOZ_WIDGET_GONK
#include "nsINetworkManager.h"
@@ -1220,13 +1227,12 @@ bool
NS_UsePrivateBrowsing(nsIChannel *channel)
{
bool isPrivate = false;
- bool isOverriden = false;
nsCOMPtr pbChannel = do_QueryInterface(channel);
- if (pbChannel &&
- NS_SUCCEEDED(pbChannel->IsPrivateModeOverriden(&isPrivate, &isOverriden)) &&
- isOverriden) {
+ if (pbChannel && NS_SUCCEEDED(pbChannel->GetIsChannelPrivate(&isPrivate))) {
return isPrivate;
}
+
+ // Some channels may not implement nsIPrivateBrowsingChannel
nsCOMPtr loadContext;
NS_QueryNotificationCallbacks(channel, loadContext);
return loadContext && loadContext->UsePrivateBrowsing();
@@ -2226,6 +2232,100 @@ NS_IsSrcdocChannel(nsIChannel *aChannel)
return false;
}
+nsresult
+NS_ShouldSecureUpgrade(nsIURI* aURI,
+ nsILoadInfo* aLoadInfo,
+ nsIPrincipal* aChannelResultPrincipal,
+ bool aPrivateBrowsing,
+ bool aAllowSTS,
+ bool& aShouldUpgrade)
+{
+ // 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 isHttps = false;
+ nsresult rv = aURI->SchemeIs("https", &isHttps);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!isHttps) {
+ // If any of the documents up the chain to the root doucment makes use of
+ // the CSP directive 'upgrade-insecure-requests', then it's time to fulfill
+ // the promise to CSP and mixed content blocking to upgrade the channel
+ // from http to https.
+ if (aLoadInfo) {
+ bool isPreload = nsContentUtils::IsPreloadType(aLoadInfo->InternalContentPolicyType());
+ bool upgradeRequests =
+ ((isPreload && aLoadInfo->GetUpgradeInsecurePreloads()) ||
+ (aLoadInfo->GetUpgradeInsecureRequests()));
+
+ // Please note that cross origin top level navigations are not subject
+ // to upgrade-insecure-requests, see:
+ // http://www.w3.org/TR/upgrade-insecure-requests/#examples
+ bool crossOriginNavigation =
+ (aLoadInfo->GetExternalContentPolicyType() == nsIContentPolicy::TYPE_DOCUMENT) &&
+ (!aChannelResultPrincipal->Equals(aLoadInfo->LoadingPrincipal()));
+
+ if (upgradeRequests && !crossOriginNavigation) {
+ // let's log a message to the console that we are upgrading a request
+ nsAutoCString spec, scheme;
+ aURI->GetSpec(spec);
+ aURI->GetScheme(scheme);
+ // append the additional 's' for security to the scheme :-)
+ scheme.AppendASCII("s");
+ NS_ConvertUTF8toUTF16 reportSpec(spec);
+ NS_ConvertUTF8toUTF16 reportScheme(scheme);
+
+ const char16_t* params[] = { reportSpec.get(), reportScheme.get() };
+ uint32_t innerWindowId = aLoadInfo->GetInnerWindowID();
+ CSP_LogLocalizedStr(MOZ_UTF16("upgradeInsecureRequest"),
+ params, ArrayLength(params),
+ EmptyString(), // aSourceFile
+ EmptyString(), // aScriptSample
+ 0, // aLineNumber
+ 0, // aColumnNumber
+ nsIScriptError::warningFlag, "CSP",
+ innerWindowId);
+
+ //Telemetry::Accumulate(Telemetry::HTTP_SCHEME_UPGRADE, 4);
+ aShouldUpgrade = true;
+ return NS_OK;
+ }
+ }
+
+ // enforce Strict-Transport-Security
+ nsISiteSecurityService* sss = gHttpHandler->GetSSService();
+ NS_ENSURE_TRUE(sss, NS_ERROR_OUT_OF_MEMORY);
+
+ bool isStsHost = false;
+ uint32_t flags = aPrivateBrowsing ? nsISocketProvider::NO_PERMANENT_STORAGE : 0;
+ rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, aURI, flags,
+ &isStsHost);
+
+ // if the SSS check fails, it's likely because this load is on a
+ // malformed URI or something else in the setup is wrong, so any error
+ // should be reported.
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (isStsHost) {
+ LOG(("nsHttpChannel::Connect() STS permissions found\n"));
+ if (aAllowSTS) {
+ //Telemetry::Accumulate(Telemetry::HTTP_SCHEME_UPGRADE, 3);
+ aShouldUpgrade = true;
+ return NS_OK;
+ } else {
+ //Telemetry::Accumulate(Telemetry::HTTP_SCHEME_UPGRADE, 2);
+ }
+ } else {
+ //Telemetry::Accumulate(Telemetry::HTTP_SCHEME_UPGRADE, 1);
+ }
+ } else {
+ //Telemetry::Accumulate(Telemetry::HTTP_SCHEME_UPGRADE, 0);
+ }
+ aShouldUpgrade = false;
+ return NS_OK;
+}
+
namespace mozilla {
namespace net {
diff --git a/netwerk/base/nsNetUtil.h b/netwerk/base/nsNetUtil.h
index d512ce2f4b..fc1c715a6f 100644
--- a/netwerk/base/nsNetUtil.h
+++ b/netwerk/base/nsNetUtil.h
@@ -979,6 +979,16 @@ bool NS_IsReasonableHTTPHeaderValue(const nsACString &aValue);
*/
bool NS_IsValidHTTPToken(const nsACString &aToken);
+/**
+ * Return true if the given request must be upgraded to HTTPS.
+ */
+nsresult NS_ShouldSecureUpgrade(nsIURI* aURI,
+ nsILoadInfo* aLoadInfo,
+ nsIPrincipal* aChannelResultPrincipal,
+ bool aPrivateBrowsing,
+ bool aAllowSTS,
+ bool& aShouldUpgrade);
+
namespace mozilla {
namespace net {
diff --git a/netwerk/cache2/CacheObserver.cpp b/netwerk/cache2/CacheObserver.cpp
index 5c37b69854..39acf15e92 100644
--- a/netwerk/cache2/CacheObserver.cpp
+++ b/netwerk/cache2/CacheObserver.cpp
@@ -234,7 +234,7 @@ CacheObserver::AttachToPreferences()
}
// static
-uint32_t const CacheObserver::MemoryCacheCapacity()
+uint32_t CacheObserver::MemoryCacheCapacity()
{
if (sMemoryCacheCapacity >= 0)
return sMemoryCacheCapacity << 10;
@@ -272,7 +272,7 @@ uint32_t const CacheObserver::MemoryCacheCapacity()
}
// static
-bool const CacheObserver::UseNewCache()
+bool CacheObserver::UseNewCache()
{
uint32_t useNewCache = sUseNewCache;
@@ -383,7 +383,7 @@ nsresult Run(NeckoOriginAttributes const &aOa)
} // anon
// static
-bool const CacheObserver::EntryIsTooBig(int64_t aSize, bool aUsingDisk)
+bool CacheObserver::EntryIsTooBig(int64_t aSize, bool aUsingDisk)
{
// If custom limit is set, check it.
int64_t preferredLimit = aUsingDisk ? sMaxDiskEntrySize : sMaxMemoryEntrySize;
diff --git a/netwerk/cache2/CacheObserver.h b/netwerk/cache2/CacheObserver.h
index 8dd20db1bf..174d5a2c38 100644
--- a/netwerk/cache2/CacheObserver.h
+++ b/netwerk/cache2/CacheObserver.h
@@ -27,43 +27,43 @@ class CacheObserver : public nsIObserver
static CacheObserver* Self() { return sSelf; }
// Access to preferences
- static bool const UseNewCache();
- static bool const UseDiskCache()
+ static bool UseNewCache();
+ static bool UseDiskCache()
{ return sUseDiskCache; }
- static bool const UseMemoryCache()
+ static bool UseMemoryCache()
{ return sUseMemoryCache; }
- static uint32_t const MetadataMemoryLimit() // result in bytes.
+ static uint32_t MetadataMemoryLimit() // result in bytes.
{ return sMetadataMemoryLimit << 10; }
- static uint32_t const MemoryCacheCapacity(); // result in bytes.
- static uint32_t const DiskCacheCapacity() // result in bytes.
+ static uint32_t MemoryCacheCapacity(); // result in bytes.
+ static uint32_t DiskCacheCapacity() // result in bytes.
{ return sDiskCacheCapacity << 10; }
static void SetDiskCacheCapacity(uint32_t); // parameter in bytes.
- static uint32_t const DiskFreeSpaceSoftLimit() // result in bytes.
+ static uint32_t DiskFreeSpaceSoftLimit() // result in bytes.
{ return sDiskFreeSpaceSoftLimit << 10; }
- static uint32_t const DiskFreeSpaceHardLimit() // result in bytes.
+ static uint32_t DiskFreeSpaceHardLimit() // result in bytes.
{ return sDiskFreeSpaceHardLimit << 10; }
- static bool const SmartCacheSizeEnabled()
+ static bool SmartCacheSizeEnabled()
{ return sSmartCacheSizeEnabled; }
- static uint32_t const PreloadChunkCount()
+ static uint32_t PreloadChunkCount()
{ return sPreloadChunkCount; }
- static uint32_t const MaxMemoryEntrySize() // result in bytes.
+ static uint32_t MaxMemoryEntrySize() // result in bytes.
{ return sMaxMemoryEntrySize << 10; }
- static uint32_t const MaxDiskEntrySize() // result in bytes.
+ static uint32_t MaxDiskEntrySize() // result in bytes.
{ return sMaxDiskEntrySize << 10; }
- static uint32_t const MaxDiskChunksMemoryUsage(bool aPriority) // result in bytes.
+ static uint32_t MaxDiskChunksMemoryUsage(bool aPriority) // result in bytes.
{ return aPriority ? sMaxDiskPriorityChunksMemoryUsage << 10
: sMaxDiskChunksMemoryUsage << 10; }
- static uint32_t const CompressionLevel()
+ static uint32_t CompressionLevel()
{ return sCompressionLevel; }
- static uint32_t const HalfLifeSeconds()
+ static uint32_t HalfLifeSeconds()
{ return sHalfLifeHours * 60.0F * 60.0F; }
- static int32_t const HalfLifeExperiment()
+ static int32_t HalfLifeExperiment()
{ return sHalfLifeExperiment; }
- static bool const ClearCacheOnShutdown()
+ static bool ClearCacheOnShutdown()
{ return sSanitizeOnShutdown && sClearCacheOnShutdown; }
static void ParentDirOverride(nsIFile ** aDir);
- static bool const EntryIsTooBig(int64_t aSize, bool aUsingDisk);
+ static bool EntryIsTooBig(int64_t aSize, bool aUsingDisk);
private:
static CacheObserver* sSelf;
diff --git a/netwerk/cache2/CacheStorageService.cpp b/netwerk/cache2/CacheStorageService.cpp
index 1885ce9f82..0cf532a9d9 100644
--- a/netwerk/cache2/CacheStorageService.cpp
+++ b/netwerk/cache2/CacheStorageService.cpp
@@ -88,7 +88,7 @@ CacheStorageService::MemoryPool::~MemoryPool()
}
}
-uint32_t const
+uint32_t
CacheStorageService::MemoryPool::Limit() const
{
switch (mType) {
diff --git a/netwerk/cache2/CacheStorageService.h b/netwerk/cache2/CacheStorageService.h
index 9c7c20acc3..91d200492c 100644
--- a/netwerk/cache2/CacheStorageService.h
+++ b/netwerk/cache2/CacheStorageService.h
@@ -318,7 +318,7 @@ private:
void PurgeAll(uint32_t aWhat);
private:
- uint32_t const Limit() const;
+ uint32_t Limit() const;
MemoryPool() = delete;
};
diff --git a/netwerk/ipc/NeckoChannelParams.ipdlh b/netwerk/ipc/NeckoChannelParams.ipdlh
index 25c2b6f3e9..6a8e9583f7 100644
--- a/netwerk/ipc/NeckoChannelParams.ipdlh
+++ b/netwerk/ipc/NeckoChannelParams.ipdlh
@@ -116,6 +116,7 @@ struct HttpChannelOpenArgs
nsCString schedulingContextID;
OptionalCorsPreflightArgs preflightArgs;
uint32_t initialRwin;
+ bool suspendAfterSynthesizeResponse;
bool allowStaleCacheContent;
bool isFromProcessingFrameAttributes;
};
diff --git a/netwerk/protocol/http/HttpBaseChannel.cpp b/netwerk/protocol/http/HttpBaseChannel.cpp
index 089eea6031..2d9b493465 100644
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -37,6 +37,7 @@
#include "nsIObserverService.h"
#include "nsProxyRelease.h"
#include "nsPIDOMWindow.h"
+#include "nsIDocShell.h"
#include "nsPerformance.h"
#include "nsINetworkInterceptController.h"
#include "mozIThirdPartyUtil.h"
@@ -254,14 +255,14 @@ NS_IMETHODIMP
HttpBaseChannel::SetLoadGroup(nsILoadGroup *aLoadGroup)
{
MOZ_ASSERT(NS_IsMainThread(), "Should only be called on the main thread.");
-
+
if (!CanSetLoadGroup(aLoadGroup)) {
return NS_ERROR_FAILURE;
}
mLoadGroup = aLoadGroup;
mProgressSink = nullptr;
- mPrivateBrowsing = NS_UsePrivateBrowsing(this);
+ UpdatePrivateBrowsing();
return NS_OK;
}
@@ -296,6 +297,47 @@ HttpBaseChannel::SetLoadFlags(nsLoadFlags aLoadFlags)
return NS_OK;
}
+NS_IMETHODIMP
+HttpBaseChannel::SetDocshellUserAgentOverride()
+{
+ // This sets the docshell specific user agent override, it will be overwritten
+ // by UserAgentOverrides.jsm if site-specific user agent overrides are set.
+ nsresult rv;
+ nsCOMPtr loadContext;
+ NS_QueryNotificationCallbacks(this, loadContext);
+ if (!loadContext) {
+ return NS_OK;
+ }
+
+ nsCOMPtr domWindow;
+ loadContext->GetAssociatedWindow(getter_AddRefs(domWindow));
+ if (!domWindow) {
+ return NS_OK;
+ }
+
+ nsCOMPtr pDomWindow = do_QueryInterface(domWindow);
+ if (!pDomWindow) {
+ return NS_OK;
+ }
+
+ nsIDocShell* docshell = pDomWindow->GetDocShell();
+ if (!docshell) {
+ return NS_OK;
+ }
+
+ nsString customUserAgent;
+ docshell->GetCustomUserAgent(customUserAgent);
+ if (customUserAgent.IsEmpty()) {
+ return NS_OK;
+ }
+
+ NS_ConvertUTF16toUTF8 utf8CustomUserAgent(customUserAgent);
+ rv = SetRequestHeader(NS_LITERAL_CSTRING("User-Agent"), utf8CustomUserAgent, false);
+ if (NS_FAILED(rv)) return rv;
+
+ return NS_OK;
+}
+
//-----------------------------------------------------------------------------
// HttpBaseChannel::nsIChannel
//-----------------------------------------------------------------------------
@@ -370,7 +412,7 @@ NS_IMETHODIMP
HttpBaseChannel::SetNotificationCallbacks(nsIInterfaceRequestor *aCallbacks)
{
MOZ_ASSERT(NS_IsMainThread(), "Should only be called on the main thread.");
-
+
if (!CanSetCallbacks(aCallbacks)) {
return NS_ERROR_FAILURE;
}
@@ -378,7 +420,7 @@ HttpBaseChannel::SetNotificationCallbacks(nsIInterfaceRequestor *aCallbacks)
mCallbacks = aCallbacks;
mProgressSink = nullptr;
- mPrivateBrowsing = NS_UsePrivateBrowsing(this);
+ UpdatePrivateBrowsing();
return NS_OK;
}
@@ -1378,7 +1420,7 @@ HttpBaseChannel::SetReferrerWithPolicy(nsIURI *referrer,
if (eTLDService) {
rv = eTLDService->GetBaseDomain(mURI, extraDomains, currentDomain);
if (NS_FAILED(rv)) return rv;
- rv = eTLDService->GetBaseDomain(clone, extraDomains, referrerDomain);
+ rv = eTLDService->GetBaseDomain(clone, extraDomains, referrerDomain);
if (NS_FAILED(rv)) return rv;
}
@@ -2403,13 +2445,13 @@ HttpBaseChannel::BypassServiceWorker() const
}
bool
-HttpBaseChannel::ShouldIntercept()
+HttpBaseChannel::ShouldIntercept(nsIURI* aURI)
{
nsCOMPtr controller;
GetCallback(controller);
bool shouldIntercept = false;
if (controller && !BypassServiceWorker() && mLoadInfo) {
- nsresult rv = controller->ShouldPrepareForIntercept(mURI,
+ nsresult rv = controller->ShouldPrepareForIntercept(aURI ? aURI : mURI.get(),
nsContentUtils::IsNonSubresourceRequest(this),
&shouldIntercept);
if (NS_FAILED(rv)) {
@@ -2446,7 +2488,7 @@ void
HttpBaseChannel::ReleaseListeners()
{
MOZ_ASSERT(NS_IsMainThread(), "Should only be called on the main thread.");
-
+
mListener = nullptr;
mListenerContext = nullptr;
mCallbacks = nullptr;
@@ -3119,6 +3161,34 @@ HttpBaseChannel::SetCorsPreflightParameters(const nsTArray& aUnsafeHe
mUnsafeHeaders = aUnsafeHeaders;
}
+// static
+nsresult
+HttpBaseChannel::GetSecureUpgradedURI(nsIURI* aURI, nsIURI** aUpgradedURI)
+{
+ nsCOMPtr upgradedURI;
+
+ nsresult rv = aURI->Clone(getter_AddRefs(upgradedURI));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ upgradedURI->SetScheme(NS_LITERAL_CSTRING("https"));
+
+ int32_t oldPort = -1;
+ rv = aURI->GetPort(&oldPort);
+ if (NS_FAILED(rv)) return rv;
+
+ // Keep any nonstandard ports so only the scheme is changed.
+ // For example:
+ // http://foo.com:80 -> https://foo.com:443
+ // http://foo.com:81 -> https://foo.com:81
+
+ if (oldPort == 80 || oldPort == -1)
+ upgradedURI->SetPort(-1);
+ else
+ upgradedURI->SetPort(oldPort);
+
+ upgradedURI.forget(aUpgradedURI);
+ return NS_OK;
+}
+
} // namespace net
} // namespace mozilla
-
diff --git a/netwerk/protocol/http/HttpBaseChannel.h b/netwerk/protocol/http/HttpBaseChannel.h
index 58b348c6aa..9466d8492b 100644
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -100,6 +100,7 @@ public:
NS_IMETHOD SetLoadGroup(nsILoadGroup *aLoadGroup) override;
NS_IMETHOD GetLoadFlags(nsLoadFlags *aLoadFlags) override;
NS_IMETHOD SetLoadFlags(nsLoadFlags aLoadFlags) override;
+ NS_IMETHOD SetDocshellUserAgentOverride();
// nsIChannel
NS_IMETHOD GetOriginalURI(nsIURI **aOriginalURI) override;
@@ -298,6 +299,10 @@ public: /* Necko internal use only... */
// the new mUploadStream.
void EnsureUploadStreamIsCloneableComplete(nsresult aStatus);
+ // Returns an https URI for channels that need to go through secure
+ // upgrades.
+ static nsresult GetSecureUpgradedURI(nsIURI* aURI, nsIURI** aUpgradedURI);
+
protected:
nsCOMArray mSecurityConsoleMessages;
@@ -342,7 +347,7 @@ protected:
// Returns true if this channel should intercept the network request and prepare
// for a possible synthesized response instead.
- bool ShouldIntercept();
+ bool ShouldIntercept(nsIURI* aURI = nullptr);
friend class PrivateBrowsingChannel;
friend class InterceptFailedOnStop;
diff --git a/netwerk/protocol/http/HttpChannelChild.cpp b/netwerk/protocol/http/HttpChannelChild.cpp
index 562b9187a5..39027d78da 100644
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -154,7 +154,7 @@ InterceptStreamListener::OnStopRequest(nsIRequest* aRequest, nsISupports* aConte
{
if (mOwner) {
mOwner->DoPreOnStopRequest(aStatusCode);
- mOwner->DoOnStopRequest(mOwner, mContext);
+ mOwner->DoOnStopRequest(mOwner, aStatusCode, mContext);
}
Cleanup();
return NS_OK;
@@ -187,7 +187,10 @@ HttpChannelChild::HttpChannelChild()
, mSynthesizedResponse(false)
, mShouldInterceptSubsequentRedirect(false)
, mRedirectingForSubsequentSynthesizedResponse(false)
+ , mPostRedirectChannelShouldIntercept(false)
+ , mPostRedirectChannelShouldUpgrade(false)
, mShouldParentIntercept(false)
+ , mSuspendParentAfterSynthesizeResponse(false)
{
LOG(("Creating HttpChannelChild @%x\n", this));
@@ -457,6 +460,8 @@ HttpChannelChild::OnStartRequest(const nsresult& channelStatus,
mCacheEntryAvailable = cacheEntryAvailable;
mCacheExpirationTime = cacheExpirationTime;
mCachedCharset = cachedCharset;
+ mSelfAddr = selfAddr;
+ mPeerAddr = peerAddr;
AutoEventEnqueuer ensureSerialDispatch(mEventQ);
@@ -486,11 +491,63 @@ HttpChannelChild::OnStartRequest(const nsresult& channelStatus,
mTracingEnabled = false;
DoOnStartRequest(this, mListenerContext);
-
- mSelfAddr = selfAddr;
- mPeerAddr = peerAddr;
}
+namespace {
+
+class SyntheticDiversionListener final : public nsIStreamListener
+{
+ RefPtr mChannel;
+
+ ~SyntheticDiversionListener()
+ {
+ }
+
+public:
+ explicit SyntheticDiversionListener(HttpChannelChild* aChannel)
+ : mChannel(aChannel)
+ {
+ MOZ_ASSERT(mChannel);
+ }
+
+ NS_IMETHOD
+ OnStartRequest(nsIRequest* aRequest, nsISupports* aContext) override
+ {
+ MOZ_ASSERT_UNREACHABLE("SyntheticDiversionListener should never see OnStartRequest");
+ return NS_OK;
+ }
+
+ NS_IMETHOD
+ OnStopRequest(nsIRequest* aRequest, nsISupports* aContext,
+ nsresult aStatus) override
+ {
+ mChannel->SendDivertOnStopRequest(aStatus);
+ return NS_OK;
+ }
+
+ NS_IMETHOD
+ OnDataAvailable(nsIRequest* aRequest, nsISupports* aContext,
+ nsIInputStream* aInputStream, uint64_t aOffset,
+ uint32_t aCount) override
+ {
+ nsAutoCString data;
+ nsresult rv = NS_ConsumeStream(aInputStream, aCount, data);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ aRequest->Cancel(rv);
+ return rv;
+ }
+
+ mChannel->SendDivertOnDataAvailable(data, aOffset, aCount);
+ return NS_OK;
+ }
+
+ NS_DECL_ISUPPORTS
+};
+
+NS_IMPL_ISUPPORTS(SyntheticDiversionListener, nsIStreamListener);
+
+} // anonymous namespace
+
void
HttpChannelChild::DoOnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
{
@@ -508,6 +565,15 @@ HttpChannelChild::DoOnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
if (mLoadGroup) {
mLoadGroup->RemoveRequest(this, nullptr, mStatus);
}
+
+ // If the response has been synthesized in the child, then we are going
+ // be getting OnDataAvailable and OnStopRequest from the synthetic
+ // stream pump. We need to forward these back to the parent diversion
+ // listener.
+ if (mSynthesizedResponse) {
+ mListener = new SyntheticDiversionListener(this);
+ }
+
return;
}
@@ -874,7 +940,7 @@ HttpChannelChild::OnStopRequest(const nsresult& channelStatus,
// so make sure this goes out of scope before then.
AutoEventEnqueuer ensureSerialDispatch(mEventQ);
- DoOnStopRequest(this, mListenerContext);
+ DoOnStopRequest(this, channelStatus, mListenerContext);
}
ReleaseListeners();
@@ -903,12 +969,15 @@ HttpChannelChild::DoPreOnStopRequest(nsresult aStatus)
}
void
-HttpChannelChild::DoOnStopRequest(nsIRequest* aRequest, nsISupports* aContext)
+HttpChannelChild::DoOnStopRequest(nsIRequest* aRequest, nsresult aChannelStatus, nsISupports* aContext)
{
LOG(("HttpChannelChild::DoOnStopRequest [this=%p]\n", this));
MOZ_ASSERT(!mIsPending);
- if (mStatus == NS_ERROR_TRACKING_URI) {
+ // NB: We use aChannelStatus here instead of mStatus because if there was an
+ // nsCORSListenerProxy on this request, it will override the tracking
+ // protection's return value.
+ if (aChannelStatus == NS_ERROR_TRACKING_URI) {
nsChannelClassifier::SetBlockedTrackingContent(this);
}
@@ -1203,11 +1272,24 @@ HttpChannelChild::SetupRedirect(nsIURI* uri,
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr httpChannelChild = do_QueryInterface(newChannel);
- if (mShouldInterceptSubsequentRedirect && httpChannelChild) {
- // In the case where there was a synthesized response that caused a redirection,
- // we must force the new channel to intercept the request in the parent before a
- // network transaction is initiated.
- httpChannelChild->ForceIntercepted();
+ if (httpChannelChild) {
+ bool shouldUpgrade = false;
+ if (mShouldInterceptSubsequentRedirect) {
+ // In the case where there was a synthesized response that caused a redirection,
+ // we must force the new channel to intercept the request in the parent before a
+ // network transaction is initiated.
+ httpChannelChild->ForceIntercepted(false, false);
+ } else if (mRedirectMode == nsIHttpChannelInternal::REDIRECT_MODE_MANUAL &&
+ ((redirectFlags & (nsIChannelEventSink::REDIRECT_TEMPORARY |
+ nsIChannelEventSink::REDIRECT_PERMANENT)) != 0) &&
+ ShouldInterceptURI(newChannel, uri, shouldUpgrade)) {
+ // In the case where the redirect mode is manual, we need to check whether
+ // the post-redirect channel needs to be intercepted. If that is the
+ // case, force the new channel to intercept the request in the parent
+ // similar to the case above, but also remember that ShouldInterceptURI()
+ // returned true to avoid calling it a second time.
+ httpChannelChild->ForceIntercepted(true, shouldUpgrade);
+ }
}
mRedirectChannelChild = do_QueryInterface(newChannel);
@@ -1743,7 +1825,14 @@ HttpChannelChild::AsyncOpen(nsIStreamListener *listener, nsISupports *aContext)
return NS_OK;
}
- if (ShouldIntercept()) {
+ // Set user agent override
+ HttpBaseChannel::SetDocshellUserAgentOverride();
+
+ MOZ_ASSERT_IF(mPostRedirectChannelShouldUpgrade,
+ mPostRedirectChannelShouldIntercept);
+ bool shouldUpgrade = mPostRedirectChannelShouldUpgrade;
+ if (mPostRedirectChannelShouldIntercept ||
+ ShouldInterceptURI(this, mURI, shouldUpgrade)) {
mResponseCouldBeSynthesized = true;
nsCOMPtr controller;
@@ -1752,7 +1841,8 @@ HttpChannelChild::AsyncOpen(nsIStreamListener *listener, nsISupports *aContext)
mInterceptListener = new InterceptStreamListener(this, mListenerContext);
RefPtr intercepted =
- new InterceptedChannelContent(this, controller, mInterceptListener);
+ new InterceptedChannelContent(this, controller,
+ mInterceptListener, shouldUpgrade);
intercepted->NotifyController();
return NS_OK;
}
@@ -1820,8 +1910,11 @@ HttpChannelChild::ContinueAsyncOpen()
if (mResponseHead) {
openArgs.synthesizedResponseHead() = *mResponseHead;
+ openArgs.suspendAfterSynthesizeResponse() =
+ mSuspendParentAfterSynthesizeResponse;
} else {
openArgs.synthesizedResponseHead() = mozilla::void_t();
+ openArgs.suspendAfterSynthesizeResponse() = false;
}
nsCOMPtr secInfoSer = do_QueryInterface(mSecurityInfo);
@@ -1989,36 +2082,6 @@ HttpChannelChild::SetupFallbackChannel(const char *aFallbackKey)
DROP_DEAD();
}
-// The next four _should_ be implemented, but we need to figure out how
-// to transfer the data from the chrome process first.
-
-NS_IMETHODIMP
-HttpChannelChild::GetRemoteAddress(nsACString & _result)
-{
- return NS_ERROR_NOT_AVAILABLE;
-}
-
-NS_IMETHODIMP
-HttpChannelChild::GetRemotePort(int32_t * _result)
-{
- NS_ENSURE_ARG_POINTER(_result);
- return NS_ERROR_NOT_AVAILABLE;
-}
-
-NS_IMETHODIMP
-HttpChannelChild::GetLocalAddress(nsACString & _result)
-{
- return NS_ERROR_NOT_AVAILABLE;
-}
-
-NS_IMETHODIMP
-HttpChannelChild::GetLocalPort(int32_t * _result)
-{
- NS_ENSURE_ARG_POINTER(_result);
- return NS_ERROR_NOT_AVAILABLE;
-}
-
-
//-----------------------------------------------------------------------------
// HttpChannelChild::nsICacheInfoChannel
//-----------------------------------------------------------------------------
@@ -2395,20 +2458,31 @@ HttpChannelChild::DivertToParent(ChannelDiverterChild **aChild)
MOZ_RELEASE_ASSERT(gNeckoChild);
MOZ_RELEASE_ASSERT(!mDivertingToParent);
+ nsresult rv = NS_OK;
+
+ // If the channel was intercepted, then we likely do not have an IPC actor
+ // yet. We need one, though, in order to have a parent side to divert to.
+ // Therefore, create the actor just in time for us to suspend and divert it.
+ if (mSynthesizedResponse && !RemoteChannelExists()) {
+ mSuspendParentAfterSynthesizeResponse = true;
+ rv = ContinueAsyncOpen();
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
// We must fail DivertToParent() if there's no parent end of the channel (and
// won't be!) due to early failure.
if (NS_FAILED(mStatus) && !RemoteChannelExists()) {
return mStatus;
}
- nsresult rv = Suspend();
+ // Once this is set, it should not be unset before the child is taken down.
+ mDivertingToParent = true;
+
+ rv = Suspend();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
- // Once this is set, it should not be unset before the child is taken down.
- mDivertingToParent = true;
-
HttpChannelDiverterArgs args;
args.mChannelChild() = this;
args.mApplyConversion() = mApplyConversion;
@@ -2541,9 +2615,12 @@ HttpChannelChild::OverrideWithSynthesizedResponse(nsAutoPtr&
}
NS_IMETHODIMP
-HttpChannelChild::ForceIntercepted()
+HttpChannelChild::ForceIntercepted(bool aPostRedirectChannelShouldIntercept,
+ bool aPostRedirectChannelShouldUpgrade)
{
mShouldParentIntercept = true;
+ mPostRedirectChannelShouldIntercept = aPostRedirectChannelShouldIntercept;
+ mPostRedirectChannelShouldUpgrade = aPostRedirectChannelShouldUpgrade;
return NS_OK;
}
@@ -2573,5 +2650,35 @@ HttpChannelChild::RecvIssueDeprecationWarning(const uint32_t& warning,
return true;
}
+bool
+HttpChannelChild::ShouldInterceptURI(nsIChannel* aChannel,
+ nsIURI* aURI,
+ bool& aShouldUpgrade)
+{
+ bool isHttps = false;
+ nsresult rv = aURI->SchemeIs("https", &isHttps);
+ NS_ENSURE_SUCCESS(rv, false);
+ nsCOMPtr resultPrincipal;
+ if (!isHttps && mLoadInfo) {
+ nsContentUtils::GetSecurityManager()->
+ GetChannelResultPrincipal(aChannel, getter_AddRefs(resultPrincipal));
+ }
+ rv = NS_ShouldSecureUpgrade(aURI,
+ mLoadInfo,
+ resultPrincipal,
+ mPrivateBrowsing,
+ mAllowSTS,
+ aShouldUpgrade);
+ NS_ENSURE_SUCCESS(rv, false);
+
+ nsCOMPtr upgradedURI;
+ if (aShouldUpgrade) {
+ rv = GetSecureUpgradedURI(aURI, getter_AddRefs(upgradedURI));
+ NS_ENSURE_SUCCESS(rv, false);
+ }
+
+ return ShouldIntercept(upgradedURI ? upgradedURI.get() : aURI);
+}
+
} // namespace net
} // namespace mozilla
diff --git a/netwerk/protocol/http/HttpChannelChild.h b/netwerk/protocol/http/HttpChannelChild.h
index 438c296af5..825f0837d1 100644
--- a/netwerk/protocol/http/HttpChannelChild.h
+++ b/netwerk/protocol/http/HttpChannelChild.h
@@ -86,10 +86,6 @@ public:
NS_IMETHOD GetProtocolVersion(nsACString& aProtocolVersion) override;
// nsIHttpChannelInternal
NS_IMETHOD SetupFallbackChannel(const char *aFallbackKey) override;
- NS_IMETHOD GetLocalAddress(nsACString& addr) override;
- NS_IMETHOD GetLocalPort(int32_t* port) override;
- NS_IMETHOD GetRemoteAddress(nsACString& addr) override;
- NS_IMETHOD GetRemotePort(int32_t* port) override;
NS_IMETHOD ForceIntercepted(uint64_t aInterceptionID) override;
// nsISupportsPriority
NS_IMETHOD SetPriority(int32_t value) override;
@@ -168,7 +164,9 @@ private:
void DoOnDataAvailable(nsIRequest* aRequest, nsISupports* aContext, nsIInputStream* aStream,
uint64_t offset, uint32_t count);
void DoPreOnStopRequest(nsresult aStatus);
- void DoOnStopRequest(nsIRequest* aRequest, nsISupports* aContext);
+ void DoOnStopRequest(nsIRequest* aRequest, nsresult aChannelStatus, nsISupports* aContext);
+
+ bool ShouldInterceptURI(nsIChannel* aChannel, nsIURI* aURI, bool& aShouldUpgrade);
// Discard the prior interception and continue with the original network request.
void ResetInterception();
@@ -230,10 +228,22 @@ private:
// response to a channel using a different principal than the current one.
bool mRedirectingForSubsequentSynthesizedResponse;
+ // Set if a manual redirect mode channel needs to be intercepted in the
+ // parent.
+ bool mPostRedirectChannelShouldIntercept;
+ // Set if a manual redirect mode channel needs to be upgraded to a secure URI
+ // when it's being considered for interception. Can only be true if
+ // mPostRedirectChannelShouldIntercept is true.
+ bool mPostRedirectChannelShouldUpgrade;
+
// Set if the corresponding parent channel should force an interception to occur
// before the network transaction is initiated.
bool mShouldParentIntercept;
+ // Set if the corresponding parent channel should suspend after a response
+ // is synthesized.
+ bool mSuspendParentAfterSynthesizeResponse;
+
// true after successful AsyncOpen until OnStopRequest completes.
bool RemoteChannelExists() { return mIPCOpen && !mKeptAlive; }
diff --git a/netwerk/protocol/http/HttpChannelParent.cpp b/netwerk/protocol/http/HttpChannelParent.cpp
index ab7de80594..38f8d27a6d 100644
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -32,7 +32,6 @@
#include "nsIOService.h"
#include "nsICachingChannel.h"
#include "mozilla/LoadInfo.h"
-#include "nsIHttpHeaderVisitor.h"
#include "nsQueryObject.h"
#include "mozilla/BasePrincipal.h"
#include "nsCORSListenerProxy.h"
@@ -60,11 +59,12 @@ HttpChannelParent::HttpChannelParent(const PBrowserOrId& iframeEmbedding,
, mPBOverride(aOverrideStatus)
, mLoadContext(aLoadContext)
, mStatus(NS_OK)
+ , mPendingDiversion(false)
, mDivertingFromChild(false)
, mDivertedOnStartRequest(false)
, mSuspendedForDiversion(false)
- , mShouldIntercept(false)
- , mShouldSuspendIntercept(false)
+ , mSuspendAfterSynthesizeResponse(false)
+ , mWillSynthesizeResponse(false)
, mNestedFrameId(0)
{
LOG(("Creating HttpChannelParent [this=%p]\n", this));
@@ -105,9 +105,8 @@ HttpChannelParent::ActorDestroy(ActorDestroyReason why)
// If this is an intercepted channel, we need to make sure that any resources are
// cleaned up to avoid leaks.
- if (mInterceptedChannel) {
- mInterceptedChannel->Cancel(NS_ERROR_INTERCEPTION_FAILED);
- mInterceptedChannel = nullptr;
+ if (mParentListener) {
+ mParentListener->ClearInterceptedChannel();
}
}
@@ -131,7 +130,9 @@ HttpChannelParent::Init(const HttpChannelCreationArgs& aArgs)
a.loadInfo(), a.synthesizedResponseHead(),
a.synthesizedSecurityInfoSerialization(),
a.cacheKey(), a.schedulingContextID(), a.preflightArgs(),
- a.initialRwin(), a.allowStaleCacheContent());
+ a.initialRwin(),
+ a.suspendAfterSynthesizeResponse(),
+ a.allowStaleCacheContent());
}
case HttpChannelCreationArgs::THttpChannelConnectArgs:
{
@@ -148,127 +149,23 @@ HttpChannelParent::Init(const HttpChannelCreationArgs& aArgs)
// HttpChannelParent::nsISupports
//-----------------------------------------------------------------------------
-NS_IMPL_ISUPPORTS(HttpChannelParent,
- nsIInterfaceRequestor,
- nsIProgressEventSink,
- nsIRequestObserver,
- nsIStreamListener,
- nsIPackagedAppChannelListener,
- nsIParentChannel,
- nsIAuthPromptProvider,
- nsIParentRedirectingChannel,
- nsINetworkInterceptController,
- nsIDeprecationWarner)
-
-NS_IMETHODIMP
-HttpChannelParent::ShouldPrepareForIntercept(nsIURI* aURI,
- bool aIsNonSubresourceRequest,
- bool* aShouldIntercept)
-{
- *aShouldIntercept = mShouldIntercept;
- return NS_OK;
-}
-
-class HeaderVisitor final : public nsIHttpHeaderVisitor
-{
- nsCOMPtr mChannel;
- ~HeaderVisitor()
- {
- }
-public:
- explicit HeaderVisitor(nsIInterceptedChannel* aChannel) : mChannel(aChannel)
- {
- }
-
- NS_DECL_ISUPPORTS
-
- NS_IMETHOD VisitHeader(const nsACString& aHeader, const nsACString& aValue) override
- {
- mChannel->SynthesizeHeader(aHeader, aValue);
- return NS_OK;
- }
-};
-
-NS_IMPL_ISUPPORTS(HeaderVisitor, nsIHttpHeaderVisitor)
-
-class FinishSynthesizedResponse : public nsRunnable
-{
- nsCOMPtr mChannel;
-public:
- explicit FinishSynthesizedResponse(nsIInterceptedChannel* aChannel)
- : mChannel(aChannel)
- {
- }
-
- NS_IMETHOD Run()
- {
- // The URL passed as an argument here doesn't matter, since the child will
- // receive a redirection notification as a result of this synthesized response.
- mChannel->FinishSynthesizedResponse(EmptyCString());
- return NS_OK;
- }
-};
-
-class ResponseSynthesizer final : public nsIFetchEventDispatcher
-{
-public:
- ResponseSynthesizer(nsIInterceptedChannel* aChannel,
- HttpChannelParent* aParentChannel)
- : mChannel(aChannel)
- , mParentChannel(aParentChannel)
- {
- }
-
- NS_DECL_ISUPPORTS
- NS_DECL_NSIFETCHEVENTDISPATCHER
-
-private:
- ~ResponseSynthesizer()
- {
- }
-
- nsCOMPtr mChannel;
- RefPtr mParentChannel;
-};
-
-NS_IMPL_ISUPPORTS(ResponseSynthesizer, nsIFetchEventDispatcher)
-
-NS_IMETHODIMP
-ResponseSynthesizer::Dispatch()
-{
- mParentChannel->SynthesizeResponse(mChannel);
-
- return NS_OK;
-}
-
-NS_IMETHODIMP
-HttpChannelParent::ChannelIntercepted(nsIInterceptedChannel* aChannel,
- nsIFetchEventDispatcher** aDispatcher)
-{
- RefPtr dispatcher =
- new ResponseSynthesizer(aChannel, this);
- dispatcher.forget(aDispatcher);
- return NS_OK;
-}
-
-void
-HttpChannelParent::SynthesizeResponse(nsIInterceptedChannel* aChannel)
-{
- if (mShouldSuspendIntercept) {
- mInterceptedChannel = aChannel;
- return;
- }
-
- aChannel->SynthesizeStatus(mSynthesizedResponseHead->Status(),
- mSynthesizedResponseHead->StatusText());
- nsCOMPtr visitor = new HeaderVisitor(aChannel);
- mSynthesizedResponseHead->Headers().VisitHeaders(visitor);
-
- nsCOMPtr event = new FinishSynthesizedResponse(aChannel);
- NS_DispatchToCurrentThread(event);
-
- mSynthesizedResponseHead = nullptr;
-}
+NS_IMPL_ADDREF(HttpChannelParent)
+NS_IMPL_RELEASE(HttpChannelParent)
+NS_INTERFACE_MAP_BEGIN(HttpChannelParent)
+ NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
+ NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink)
+ NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
+ NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
+ NS_INTERFACE_MAP_ENTRY(nsIPackagedAppChannelListener)
+ NS_INTERFACE_MAP_ENTRY(nsIParentChannel)
+ NS_INTERFACE_MAP_ENTRY(nsIAuthPromptProvider)
+ NS_INTERFACE_MAP_ENTRY(nsIParentRedirectingChannel)
+ NS_INTERFACE_MAP_ENTRY(nsIDeprecationWarner)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIParentRedirectingChannel)
+ if (aIID.Equals(NS_GET_IID(HttpChannelParent))) {
+ foundInterface = static_cast(this);
+ } else
+NS_INTERFACE_MAP_END
//-----------------------------------------------------------------------------
// HttpChannelParent::nsIInterfaceRequestor
@@ -368,6 +265,7 @@ HttpChannelParent::DoAsyncOpen( const URIParams& aURI,
const nsCString& aSchedulingContextID,
const OptionalCorsPreflightArgs& aCorsPreflightArgs,
const uint32_t& aInitialRwin,
+ const bool& aSuspendAfterSynthesizeResponse,
const bool& aAllowStaleCacheContent)
{
nsCOMPtr uri = DeserializeURI(aURI);
@@ -489,8 +387,8 @@ HttpChannelParent::DoAsyncOpen( const URIParams& aURI,
mChannel->SetAllowStaleCacheContent(aAllowStaleCacheContent);
if (aSynthesizedResponseHead.type() == OptionalHttpResponseHead::TnsHttpResponseHead) {
- mSynthesizedResponseHead = new nsHttpResponseHead(aSynthesizedResponseHead.get_nsHttpResponseHead());
- mShouldIntercept = true;
+ mParentListener->SetupInterception(aSynthesizedResponseHead.get_nsHttpResponseHead());
+ mWillSynthesizeResponse = true;
mChannel->SetCouldBeSynthesized();
if (!aSecurityInfoSerialization.IsEmpty()) {
@@ -583,6 +481,8 @@ HttpChannelParent::DoAsyncOpen( const URIParams& aURI,
schedulingContextID.Parse(aSchedulingContextID.BeginReading());
mChannel->SetSchedulingContextID(schedulingContextID);
+ mSuspendAfterSynthesizeResponse = aSuspendAfterSynthesizeResponse;
+
if (loadInfo && loadInfo->GetEnforceSecurity()) {
rv = mChannel->AsyncOpen2(mParentListener);
}
@@ -607,12 +507,11 @@ HttpChannelParent::ConnectChannel(const uint32_t& channelId, const bool& shouldI
mChannel = static_cast(channel.get());
LOG((" found channel %p, rv=%08x", mChannel.get(), rv));
- mShouldIntercept = shouldIntercept;
- if (mShouldIntercept) {
- // When an interception occurs, this channel should suspend all further activity.
- // It will be torn down and recreated if necessary.
- mShouldSuspendIntercept = true;
- }
+ nsCOMPtr controller;
+ NS_QueryNotificationCallbacks(channel, controller);
+ RefPtr parentListener = do_QueryObject(controller);
+ MOZ_ASSERT(parentListener);
+ parentListener->SetupInterceptionAfterRedirect(shouldIntercept);
if (mPBOverride != kPBOverride_Unset) {
// redirected-to channel may not support PB
@@ -1042,6 +941,43 @@ HttpChannelParent::DivertComplete()
mParentListener = nullptr;
}
+void
+HttpChannelParent::MaybeFlushPendingDiversion()
+{
+ if (!mPendingDiversion) {
+ return;
+ }
+
+ mPendingDiversion = false;
+
+ nsresult rv = SuspendForDiversion();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return;
+ }
+
+ if (mDivertListener) {
+ DivertTo(mDivertListener);
+ }
+
+ return;
+}
+
+void
+HttpChannelParent::ResponseSynthesized()
+{
+ // Suspend now even though the FinishSynthesizeResponse runnable has
+ // not executed. We want to suspend after we get far enough to trigger
+ // the synthesis, but not actually allow the nsHttpChannel to trigger
+ // any OnStartRequests().
+ if (mSuspendAfterSynthesizeResponse) {
+ mChannel->Suspend();
+ }
+
+ mWillSynthesizeResponse = false;
+
+ MaybeFlushPendingDiversion();
+}
+
bool
HttpChannelParent::RecvRemoveCorsPreflightCacheEntry(const URIParams& uri,
const mozilla::ipc::PrincipalInfo& requestingPrincipal)
@@ -1385,16 +1321,35 @@ HttpChannelParent::SuspendForDiversion()
LOG(("HttpChannelParent::SuspendForDiversion [this=%p]\n", this));
MOZ_ASSERT(mChannel);
MOZ_ASSERT(mParentListener);
+
+ // If we're in the process of opening a synthesized response, we must wait
+ // to perform the diversion. Some of our diversion listeners clear callbacks
+ // which breaks the synthesis process.
+ if (mWillSynthesizeResponse) {
+ mPendingDiversion = true;
+ return NS_OK;
+ }
+
if (NS_WARN_IF(mDivertingFromChild)) {
MOZ_ASSERT(!mDivertingFromChild, "Already suspended for diversion!");
return NS_ERROR_UNEXPECTED;
}
+ nsresult rv = NS_OK;
+
// Try suspending the channel. Allow it to fail, since OnStopRequest may have
- // been called and thus the channel may not be pending.
- nsresult rv = mChannel->Suspend();
- MOZ_ASSERT(NS_SUCCEEDED(rv) || rv == NS_ERROR_NOT_AVAILABLE);
- mSuspendedForDiversion = NS_SUCCEEDED(rv);
+ // been called and thus the channel may not be pending. If we've already
+ // automatically suspended after synthesizing the response, then we don't
+ // need to suspend again here.
+ if (!mSuspendAfterSynthesizeResponse) {
+ rv = mChannel->Suspend();
+ MOZ_ASSERT(NS_SUCCEEDED(rv) || rv == NS_ERROR_NOT_AVAILABLE);
+ mSuspendedForDiversion = NS_SUCCEEDED(rv);
+ } else {
+ // Take ownership of the automatic suspend that occurred after synthesizing
+ // the response.
+ mSuspendedForDiversion = true;
+ }
rv = mParentListener->SuspendForDiversion();
MOZ_ASSERT(NS_SUCCEEDED(rv));
@@ -1441,6 +1396,18 @@ HttpChannelParent::DivertTo(nsIStreamListener *aListener)
LOG(("HttpChannelParent::DivertTo [this=%p aListener=%p]\n",
this, aListener));
MOZ_ASSERT(mParentListener);
+
+ // If we're in the process of opening a synthesized response, we must wait
+ // to perform the diversion. Some of our diversion listeners clear callbacks
+ // which breaks the synthesis process.
+ if (mWillSynthesizeResponse) {
+ // We should already have started pending the diversion when
+ // SuspendForDiversion() was called.
+ MOZ_ASSERT(mPendingDiversion);
+ mDivertListener = aListener;
+ return;
+ }
+
if (NS_WARN_IF(!mDivertingFromChild)) {
MOZ_ASSERT(mDivertingFromChild,
"Cannot DivertTo new listener if diverting is not set!");
diff --git a/netwerk/protocol/http/HttpChannelParent.h b/netwerk/protocol/http/HttpChannelParent.h
index a66ff67b42..662046a3fd 100644
--- a/netwerk/protocol/http/HttpChannelParent.h
+++ b/netwerk/protocol/http/HttpChannelParent.h
@@ -20,13 +20,16 @@
#include "nsHttpChannel.h"
#include "nsIAuthPromptProvider.h"
#include "mozilla/dom/ipc/IdType.h"
-#include "nsINetworkInterceptController.h"
#include "nsIDeprecationWarner.h"
#include "nsIPackagedAppChannelListener.h"
class nsICacheEntry;
class nsIAssociatedContentSecurity;
+#define HTTP_CHANNEL_PARENT_IID \
+ { 0x982b2372, 0x7aa5, 0x4e8a, \
+ { 0xbd, 0x9f, 0x89, 0x74, 0xd7, 0xf0, 0x58, 0xeb } }
+
namespace mozilla {
namespace dom{
@@ -38,13 +41,16 @@ namespace net {
class HttpChannelParentListener;
-class HttpChannelParent final : public PHttpChannelParent
+// Note: nsIInterfaceRequestor must be the first base so that do_QueryObject()
+// works correctly on this object, as it's needed to compute a void* pointing to
+// the beginning of this object.
+
+class HttpChannelParent final : public nsIInterfaceRequestor
+ , public PHttpChannelParent
, public nsIParentRedirectingChannel
, public nsIProgressEventSink
- , public nsIInterfaceRequestor
, public ADivertableParentChannel
, public nsIAuthPromptProvider
- , public nsINetworkInterceptController
, public nsIDeprecationWarner
, public DisconnectableParent
, public nsIPackagedAppChannelListener
@@ -62,9 +68,10 @@ public:
NS_DECL_NSIPROGRESSEVENTSINK
NS_DECL_NSIINTERFACEREQUESTOR
NS_DECL_NSIAUTHPROMPTPROVIDER
- NS_DECL_NSINETWORKINTERCEPTCONTROLLER
NS_DECL_NSIDEPRECATIONWARNER
+ NS_DECLARE_STATIC_IID_ACCESSOR(HTTP_CHANNEL_PARENT_IID)
+
HttpChannelParent(const dom::PBrowserOrId& iframeEmbedding,
nsILoadContext* aLoadContext,
PBOverrideStatus aStatus);
@@ -129,6 +136,7 @@ protected:
const nsCString& aSchedulingContextID,
const OptionalCorsPreflightArgs& aCorsPreflightArgs,
const uint32_t& aInitialRwin,
+ const bool& aSuspendAfterSynthesizeResponse,
const bool& aAllowStaleCacheContent);
virtual bool RecvSetPriority(const uint16_t& priority) override;
@@ -178,13 +186,12 @@ private:
const uint32_t& count);
void DivertOnStopRequest(const nsresult& statusCode);
void DivertComplete();
-
- void SynthesizeResponse(nsIInterceptedChannel* aChannel);
+ void MaybeFlushPendingDiversion();
+ void ResponseSynthesized();
friend class DivertDataAvailableEvent;
friend class DivertStopRequestEvent;
friend class DivertCompleteEvent;
- friend class ResponseSynthesizer;
RefPtr mChannel;
nsCOMPtr mCacheEntry;
@@ -213,13 +220,15 @@ private:
nsCOMPtr mLoadContext;
RefPtr mHttpHandler;
- nsAutoPtr mSynthesizedResponseHead;
-
RefPtr mParentListener;
- // This is listener we are diverting to.
+ // The listener we are diverting to or will divert to if mPendingDiversion
+ // is set.
nsCOMPtr mDivertListener;
// Set to the canceled status value if the main channel was canceled.
nsresult mStatus;
+ // Indicates that diversion has been requested, but we could not start it
+ // yet because the channel is still being opened with a synthesized response.
+ bool mPendingDiversion;
// Once set, no OnStart/OnData/OnStop calls should be accepted; conversely, it
// must be set when RecvDivertOnData/~DivertOnStop/~DivertComplete are
// received from the child channel.
@@ -230,19 +239,19 @@ private:
bool mSuspendedForDiversion;
- // Set if this channel should be intercepted before it sets up the HTTP transaction.
- bool mShouldIntercept : 1;
- // Set if this channel should suspend on interception.
- bool mShouldSuspendIntercept : 1;
+ // Set if this channel should be suspended after synthesizing a response.
+ bool mSuspendAfterSynthesizeResponse;
+ // Set if this channel will synthesize its response.
+ bool mWillSynthesizeResponse;
dom::TabId mNestedFrameId;
- // Handle to the channel wrapper if this channel has been intercepted.
- nsCOMPtr mInterceptedChannel;
-
RefPtr mEventQ;
};
+NS_DEFINE_STATIC_IID_ACCESSOR(HttpChannelParent,
+ HTTP_CHANNEL_PARENT_IID)
+
} // namespace net
} // namespace mozilla
diff --git a/netwerk/protocol/http/HttpChannelParentListener.cpp b/netwerk/protocol/http/HttpChannelParentListener.cpp
index e924c4b921..b25298c0fb 100644
--- a/netwerk/protocol/http/HttpChannelParentListener.cpp
+++ b/netwerk/protocol/http/HttpChannelParentListener.cpp
@@ -13,6 +13,7 @@
#include "nsIRedirectChannelRegistrar.h"
#include "nsIHttpEventSink.h"
#include "nsIPackagedAppChannelListener.h"
+#include "nsIHttpHeaderVisitor.h"
using mozilla::Unused;
@@ -23,6 +24,8 @@ HttpChannelParentListener::HttpChannelParentListener(HttpChannelParent* aInitial
: mNextListener(aInitialChannel)
, mRedirectChannelId(0)
, mSuspendedForDiversion(false)
+ , mShouldIntercept(false)
+ , mShouldSuspendIntercept(false)
{
}
@@ -34,13 +37,21 @@ HttpChannelParentListener::~HttpChannelParentListener()
// HttpChannelParentListener::nsISupports
//-----------------------------------------------------------------------------
-NS_IMPL_ISUPPORTS(HttpChannelParentListener,
- nsIInterfaceRequestor,
- nsIStreamListener,
- nsIRequestObserver,
- nsIChannelEventSink,
- nsIPackagedAppChannelListener,
- nsIRedirectResultListener)
+NS_IMPL_ADDREF(HttpChannelParentListener)
+NS_IMPL_RELEASE(HttpChannelParentListener)
+NS_INTERFACE_MAP_BEGIN(HttpChannelParentListener)
+ NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
+ NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
+ NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
+ NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
+ NS_INTERFACE_MAP_ENTRY(nsIPackagedAppChannelListener)
+ NS_INTERFACE_MAP_ENTRY(nsIRedirectResultListener)
+ NS_INTERFACE_MAP_ENTRY(nsINetworkInterceptController)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInterfaceRequestor)
+ if (aIID.Equals(NS_GET_IID(HttpChannelParentListener))) {
+ foundInterface = static_cast(this);
+ } else
+NS_INTERFACE_MAP_END
//-----------------------------------------------------------------------------
// HttpChannelParentListener::nsIRequestObserver
@@ -124,6 +135,7 @@ HttpChannelParentListener::GetInterface(const nsIID& aIID, void **result)
{
if (aIID.Equals(NS_GET_IID(nsIChannelEventSink)) ||
aIID.Equals(NS_GET_IID(nsIHttpEventSink)) ||
+ aIID.Equals(NS_GET_IID(nsINetworkInterceptController)) ||
aIID.Equals(NS_GET_IID(nsIRedirectResultListener)))
{
return QueryInterface(aIID, result);
@@ -246,6 +258,125 @@ HttpChannelParentListener::OnRedirectResult(bool succeeded)
return NS_OK;
}
+//-----------------------------------------------------------------------------
+// HttpChannelParentListener::nsINetworkInterceptController
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+HttpChannelParentListener::ShouldPrepareForIntercept(nsIURI* aURI,
+ bool aIsNonSubresourceRequest,
+ bool* aShouldIntercept)
+{
+ *aShouldIntercept = mShouldIntercept;
+ return NS_OK;
+}
+
+class HeaderVisitor final : public nsIHttpHeaderVisitor
+{
+ nsCOMPtr mChannel;
+ ~HeaderVisitor()
+ {
+ }
+public:
+ explicit HeaderVisitor(nsIInterceptedChannel* aChannel) : mChannel(aChannel)
+ {
+ }
+
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHOD VisitHeader(const nsACString& aHeader, const nsACString& aValue) override
+ {
+ mChannel->SynthesizeHeader(aHeader, aValue);
+ return NS_OK;
+ }
+};
+
+NS_IMPL_ISUPPORTS(HeaderVisitor, nsIHttpHeaderVisitor)
+
+class FinishSynthesizedResponse : public nsRunnable
+{
+ nsCOMPtr mChannel;
+public:
+ explicit FinishSynthesizedResponse(nsIInterceptedChannel* aChannel)
+ : mChannel(aChannel)
+ {
+ }
+
+ NS_IMETHOD Run()
+ {
+ // The URL passed as an argument here doesn't matter, since the child will
+ // receive a redirection notification as a result of this synthesized response.
+ mChannel->FinishSynthesizedResponse(EmptyCString());
+ return NS_OK;
+ }
+};
+
+class ResponseSynthesizer final : public nsIFetchEventDispatcher
+{
+public:
+ ResponseSynthesizer(nsIInterceptedChannel* aChannel,
+ HttpChannelParentListener* aParentListener)
+ : mChannel(aChannel)
+ , mParentListener(aParentListener)
+ {
+ }
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIFETCHEVENTDISPATCHER
+
+private:
+ ~ResponseSynthesizer()
+ {
+ }
+
+ nsCOMPtr mChannel;
+ RefPtr mParentListener;
+};
+
+NS_IMPL_ISUPPORTS(ResponseSynthesizer, nsIFetchEventDispatcher)
+
+NS_IMETHODIMP
+ResponseSynthesizer::Dispatch()
+{
+ mParentListener->SynthesizeResponse(mChannel);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+HttpChannelParentListener::ChannelIntercepted(nsIInterceptedChannel* aChannel,
+ nsIFetchEventDispatcher** aDispatcher)
+{
+ RefPtr dispatcher =
+ new ResponseSynthesizer(aChannel, this);
+ dispatcher.forget(aDispatcher);
+ return NS_OK;
+}
+
+void
+HttpChannelParentListener::SynthesizeResponse(nsIInterceptedChannel* aChannel)
+{
+ if (mShouldSuspendIntercept) {
+ mInterceptedChannel = aChannel;
+ return;
+ }
+
+ aChannel->SynthesizeStatus(mSynthesizedResponseHead->Status(),
+ mSynthesizedResponseHead->StatusText());
+ nsCOMPtr visitor = new HeaderVisitor(aChannel);
+ mSynthesizedResponseHead->Headers().VisitHeaders(visitor);
+
+ nsCOMPtr event = new FinishSynthesizedResponse(aChannel);
+ NS_DispatchToCurrentThread(event);
+
+ mSynthesizedResponseHead = nullptr;
+
+ MOZ_ASSERT(mNextListener);
+ RefPtr