author | Simon Sapin <simon.sapin@exyr.org> |
Mon, 31 Mar 2014 22:49:45 -0700 | |
changeset 194967 | f3476de5a68039ddb09ee0886478c9ff99d8902a |
parent 194966 | 9051a06f0268ba529d567e3022a5440fe71b1c60 |
child 194968 | f5c5742ad0041f32448da8ebcf658051f7d29a1b |
push id | 3624 |
push user | asasaki@mozilla.com |
push date | Mon, 09 Jun 2014 21:49:01 +0000 |
treeherder | mozilla-beta@b1a5da15899a [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | dholbert |
bugs | 978478 |
milestone | 31.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -58,16 +58,22 @@ nsCSSProps::kParserVariantTable[eCSSProp parsevariant_, #include "nsCSSPropList.h" #undef CSS_PROP }; // Length of the "var-" prefix of custom property names. #define VAR_PREFIX_LENGTH 4 +// Maximum number of repetitions for the repeat() function +// in the grid-template-columns and grid-template-rows properties, +// to limit high memory usage from small stylesheets. +// Must be a positive integer. Should be large-ish. +#define GRID_TEMPLATE_MAX_REPETITIONS 10000 + // End-of-array marker for mask arguments to ParseBitmaskValues #define MASK_END_VALUE (-1) MOZ_BEGIN_ENUM_CLASS(CSSParseResult, int32_t) // Parsed something successfully: Ok, // Did not find what we were looking for, but did not consume any token: NotFound, @@ -652,16 +658,17 @@ protected: // or set it to a eCSSUnit_List of eCSSUnit_Ident. // // To parse an optional <line-names> (ie. if not finding an open paren // is considered the same as an empty list), // treat CSSParseResult::NotFound the same as CSSParseResult::Ok. // // If aValue is already a eCSSUnit_List, append to that list. CSSParseResult ParseGridLineNames(nsCSSValue& aValue); + bool ParseGridLineNameListRepeat(nsCSSValueList** aTailPtr); bool ParseOptionalLineNameListAfterSubgrid(nsCSSValue& aValue); bool ParseGridTrackBreadth(nsCSSValue& aValue); CSSParseResult ParseGridTrackSize(nsCSSValue& aValue); bool ParseGridAutoColumnsRows(nsCSSProperty aPropID); // Assuming a [ <line-names>? ] has already been parsed, // parse the rest of a <track-list>. // @@ -7001,36 +7008,103 @@ CSSParserImpl::ParseGridLineNames(nsCSSV if (!GetToken(true) || mToken.IsSymbol(')')) { return CSSParseResult::Ok; } item->mNext = new nsCSSValueList; item = item->mNext; } } +// Assuming the 'repeat(' function token has already been consumed, +// parse the rest of repeat(<positive-integer>, <line-names>+) +// Append to the linked list whose end is given by |aTailPtr|, +// and updated |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(')'); + return false; + } + int32_t repetitions = std::min(mToken.mInteger, + GRID_TEMPLATE_MAX_REPETITIONS); + if (!ExpectSymbol(',', true)) { + SkipUntil(')'); + return false; + } + + // Parse at least one <line-names> + nsCSSValueList* tail = *aTailPtr; + do { + tail->mNext = new nsCSSValueList; + tail = tail->mNext; + if (ParseGridLineNames(tail->mValue) != CSSParseResult::Ok) { + SkipUntil(')'); + return false; + } + } while (!ExpectSymbol(')', true)); + nsCSSValueList* firstRepeatedItem = (*aTailPtr)->mNext; + nsCSSValueList* lastRepeatedItem = tail; + + // Our repeated items are already in the target list once, + // so they need to be repeated |repetitions - 1| more times. + MOZ_ASSERT(repetitions > 0, "Should have only accepted positive integers"); + while (--repetitions) { + nsCSSValueList* repeatedItem = firstRepeatedItem; + for (;;) { + tail->mNext = new nsCSSValueList; + tail = tail->mNext; + tail->mValue = repeatedItem->mValue; + if (repeatedItem == lastRepeatedItem) { + break; + } + repeatedItem = repeatedItem->mNext; + } + } + *aTailPtr = tail; + return true; +} + // Assuming a 'subgrid' keyword was already consumed, parse <line-name-list>? bool CSSParserImpl::ParseOptionalLineNameListAfterSubgrid(nsCSSValue& aValue) { nsCSSValueList* item = aValue.SetListValue(); // This marker distinguishes the value from a <track-list>. item->mValue.SetIntValue(NS_STYLE_GRID_TEMPLATE_SUBGRID, eCSSUnit_Enumerated); for (;;) { - nsCSSValue lineNames; - CSSParseResult result = ParseGridLineNames(lineNames); - if (result == CSSParseResult::NotFound) { + // First try to parse repeat(<positive-integer>, <line-names>+) + if (!GetToken(true)) { return true; } - if (result == CSSParseResult::Error) { - return false; - } - item->mNext = new nsCSSValueList; - item = item->mNext; - item->mValue = lineNames; + if (mToken.mType == eCSSToken_Function && + mToken.mIdent.LowerCaseEqualsLiteral("repeat")) { + if (!ParseGridLineNameListRepeat(&item)) { + return false; + } + } else { + UngetToken(); + + // This was not a repeat() function. Try to parse <line-names>. + nsCSSValue lineNames; + CSSParseResult result = ParseGridLineNames(lineNames); + if (result == CSSParseResult::NotFound) { + return true; + } + if (result == CSSParseResult::Error) { + return false; + } + item->mNext = new nsCSSValueList; + item = item->mNext; + item->mValue = lineNames; + } } } // Parse a <track-breadth> bool CSSParserImpl::ParseGridTrackBreadth(nsCSSValue& aValue) { if (ParseNonNegativeVariant(aValue,
--- a/layout/style/test/property_database.js +++ b/layout/style/test/property_database.js @@ -4878,16 +4878,18 @@ if (SpecialPowers.getBoolPref("layout.cs "() 40px (-foo) 2em (bar baz This\ is\ one\ ident)", // TODO bug 978478: "(a) repeat(3, (b) 20px (c) 40px (d)) (e)", // See https://bugzilla.mozilla.org/show_bug.cgi?id=981300 "(none auto subgrid min-content max-content foo) 40px", "subgrid", "subgrid () (foo bar)", + "subgrid repeat(1, ())", + "subgrid Repeat(4, (a) (b c) () (d))", ], invalid_values: [ "", "normal", "40ms", "-40px", "-12%", "-2fr", @@ -4909,16 +4911,26 @@ if (SpecialPowers.getBoolPref("layout.cs "mİnmax(20px, 100px)", "minmax(20px, 100px, 200px)", "maxmin(100px, 20px)", "minmax(min-content, auto)", "minmax(min-content, minmax(30px, max-content))", "subgrid (foo) 40px", "subgrid (foo 40px)", "(foo) subgrid", + "subgrid rêpeat(1, ())", + "subgrid repeat(0, ())", + "subgrid repeat(-3, ())", + "subgrid repeat(2.0, ())", + "subgrid repeat(2.5, ())", + "subgrid repeat(3px, ())", + "subgrid repeat(1)", + "subgrid repeat(1, )", + "subgrid repeat(2, (40px))", + "subgrid repeat(2, foo)", ] }; gCSSProperties["grid-template-rows"] = { domProp: "gridTemplateRows", inherited: false, type: CSS_TYPE_LONGHAND, initial_values: gCSSProperties["grid-template-columns"].initial_values, other_values: gCSSProperties["grid-template-columns"].other_values, @@ -4964,17 +4976,17 @@ if (SpecialPowers.getBoolPref("layout.cs // <'grid-template-columns'> / <'grid-template-rows'> "40px / 100px", "(foo) 40px (bar) / (baz) 100px (fizz)", " none/100px", "40px/none", "subgrid/40px 20px", "subgrid (foo) () (bar baz) / 40px 20px", "40px 20px/subgrid", - "40px 20px/subgrid (foo) () (bar baz)", + "40px 20px/subgrid (foo) () repeat(3, (a) (b)) (bar baz)", "subgrid/subgrid", "subgrid (foo) () (bar baz)/subgrid (foo) () (bar baz)", // [ <track-list> / ]? [ <line-names>? <string> <track-size>? <line-names>? ]+ "'fizz'", "(bar) 'fizz'", "(foo) 40px / 'fizz'", "(foo) 40px / (bar) 'fizz'", "(foo) 40px / 'fizz' 100px",
--- a/layout/style/test/test_grid_container_shorthands.html +++ b/layout/style/test/test_grid_container_shorthands.html @@ -93,16 +93,30 @@ var grid_template_test_cases = [ gridTemplateRows: "subgrid", }, { specified: "subgrid / subgrid (foo)", gridTemplateColumns: "subgrid", gridTemplateRows: "subgrid (foo)", }, { + specified: "subgrid / subgrid (foo) repeat(3, () (a b) (c))", + gridTemplateColumns: "subgrid", + gridTemplateRows: "subgrid (foo) () (a b) (c) () (a b) (c) () (a b) (c)", + }, + { + // https://bugzilla.mozilla.org/show_bug.cgi?id=978478#c1 + // The number of repetitions is clamped to + // #define GRID_TEMPLATE_MAX_REPETITIONS 10000 + specified: "subgrid / subgrid (foo) repeat(999999999, (a))", + gridTemplateColumns: "subgrid", + // Array(n + 1).join(s) is a hack for the non-standard s.repeat(n) + gridTemplateRows: "subgrid (foo)" + Array(10000 + 1).join(" (a)"), + }, + { specified: "subgrid () (foo)/ subgrid (bar", gridTemplateColumns: "subgrid () (foo)", gridTemplateRows: "subgrid (bar)", }, ]; grid_test_cases = grid_template_test_cases.concat([ {