diff --git a/layout/style/CSSStyleSheet.cpp b/layout/style/CSSStyleSheet.cpp index 05bb84018f..f2f89b9127 100644 --- a/layout/style/CSSStyleSheet.cpp +++ b/layout/style/CSSStyleSheet.cpp @@ -283,6 +283,10 @@ nsMediaExpression::Matches(nsPresContext *aPresContext, return cmp != 1; case nsMediaExpression::eEqual: return cmp == 0; + case nsMediaExpression::eMinExclusive: + return cmp == 1; + case nsMediaExpression::eMaxExclusive: + return cmp == -1; } NS_NOTREACHED("unexpected mRange"); return false; @@ -487,7 +491,13 @@ nsMediaQuery::AppendToString(nsAString& aString) const aString.Append(nsDependentAtomString(*feature->mName)); if (expr.mValue.GetUnit() != eCSSUnit_Null) { - aString.AppendLiteral(": "); + if (expr.mRange == nsMediaExpression::eMinExclusive) { + aString.AppendLiteral(" > "); + } else if (expr.mRange == nsMediaExpression::eMaxExclusive) { + aString.AppendLiteral(" < "); + } else { + aString.AppendLiteral(": "); + } switch (feature->mValueType) { case nsMediaFeature::eLength: NS_ASSERTION(expr.mValue.IsLengthUnit(), "bad unit"); diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp index 59872b0f67..afb9f195a8 100644 --- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -3845,9 +3845,7 @@ CSSParserImpl::ParseMediaQueryExpression(nsMediaQuery* aQuery) break; } } - if (!feature->mName || - (expr->mRange != nsMediaExpression::eEqual && - feature->mRangeType != nsMediaFeature::eMinMaxAllowed)) { + if (!feature->mName) { REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureName); SkipUntil(')'); return false; @@ -3865,13 +3863,52 @@ CSSParserImpl::ParseMediaQueryExpression(nsMediaQuery* aQuery) return true; } - if (!mToken.IsSymbol(':')) { + bool usesRangeSyntax = false; + if (mToken.IsSymbol('<') || mToken.IsSymbol('>') || mToken.IsSymbol('=')) { + if (expr->mRange != nsMediaExpression::eEqual) { + REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureNameEnd); + UngetToken(); + SkipUntil(')'); + return false; + } + + usesRangeSyntax = true; + char16_t rangeSymbol = mToken.mSymbol; + bool inclusive = rangeSymbol == '='; + + if (rangeSymbol != '=') { + if (!GetToken(true)) { + REPORT_UNEXPECTED_EOF(PEMQExpressionEOF); + return false; + } + if (mToken.IsSymbol('=')) { + inclusive = true; + } else { + UngetToken(); + } + } + + if (rangeSymbol == '>') { + expr->mRange = inclusive ? nsMediaExpression::eMin + : nsMediaExpression::eMinExclusive; + } else if (rangeSymbol == '<') { + expr->mRange = inclusive ? nsMediaExpression::eMax + : nsMediaExpression::eMaxExclusive; + } + } else if (!mToken.IsSymbol(':')) { REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureNameEnd); UngetToken(); SkipUntil(')'); return false; } + if ((expr->mRange != nsMediaExpression::eEqual || usesRangeSyntax) && + feature->mRangeType != nsMediaFeature::eMinMaxAllowed) { + REPORT_UNEXPECTED_TOKEN(PEMQExpectedFeatureName); + SkipUntil(')'); + return false; + } + bool rv = false; switch (feature->mValueType) { case nsMediaFeature::eLength: diff --git a/layout/style/nsIMediaList.h b/layout/style/nsIMediaList.h index 8f8c692cd6..52244b48f4 100644 --- a/layout/style/nsIMediaList.h +++ b/layout/style/nsIMediaList.h @@ -33,7 +33,7 @@ class DocumentRule; } // namespace mozilla struct nsMediaExpression { - enum Range { eMin, eMax, eEqual }; + enum Range { eMin, eMax, eEqual, eMinExclusive, eMaxExclusive }; const nsMediaFeature *mFeature; Range mRange; diff --git a/layout/style/test/test_media_queries.html b/layout/style/test/test_media_queries.html index a962215d34..90b55a2ac5 100644 --- a/layout/style/test/test_media_queries.html +++ b/layout/style/test/test_media_queries.html @@ -290,6 +290,12 @@ function run() { should_apply("all and (max-" + feature + ": " + value + "px)"); should_apply("all and (max-" + feature + ": " + (value + 1) + "px)"); should_not_apply("all and (max-" + feature + ": " + (value - 1) + "px)"); + should_apply("all and (" + feature + " >= " + value + "px)"); + should_not_apply("all and (" + feature + " > " + value + "px)"); + should_apply("all and (" + feature + " > " + (value - 1) + "px)"); + should_apply("all and (" + feature + " <= " + value + "px)"); + should_not_apply("all and (" + feature + " < " + value + "px)"); + should_apply("all and (" + feature + " < " + (value + 1) + "px)"); should_not_apply("all and (min-" + feature + ": " + (Math.ceil(value/em_size) + 1) + "em)"); should_apply("all and (min-" + feature + ": " + @@ -349,6 +355,9 @@ function run() { should_apply("(orientation: landscape)"); should_not_apply("(orientation: portrait)"); should_apply("not all and (orientation: portrait)"); + expression_should_not_be_parseable("orientation > landscape"); + expression_should_not_be_parseable("min-width > 1px"); + expression_should_not_be_parseable("width >"); // ratio that reduces to 59/80 change_state(function() { iframe_style.height = "320px";