diff --git a/src/main/java/org/htmlunit/cssparser/parser/AbstractCSSParser.java b/src/main/java/org/htmlunit/cssparser/parser/AbstractCSSParser.java
index b152df0..4531ada 100644
--- a/src/main/java/org/htmlunit/cssparser/parser/AbstractCSSParser.java
+++ b/src/main/java/org/htmlunit/cssparser/parser/AbstractCSSParser.java
@@ -38,6 +38,7 @@ public abstract class AbstractCSSParser {
private DocumentHandler documentHandler_;
private CSSErrorHandler errorHandler_;
private InputSource source_;
+ private ParserContext parserContext_ = new ParserContext();
private static final HashMap PARSER_MESSAGES_ = new HashMap<>();
@@ -165,6 +166,15 @@ protected InputSource getInputSource() {
return source_;
}
+ /**
+ * getParserContext.
+ *
+ * @return the parser context
+ */
+ protected ParserContext getParserContext() {
+ return parserContext_;
+ }
+
/**
* @param key the lookup key
* @return the parser message
@@ -284,7 +294,11 @@ protected CSSParseException toCSSParseException(final String key, final ParseExc
message.append(MessageFormat.format(messagePattern2, invalid, expected));
}
message.append(")");
- return new CSSParseException(message.toString(),
+
+ // Add contextual information
+ final String contextualMessage = parserContext_.buildContextualMessage(message.toString());
+
+ return new CSSParseException(contextualMessage,
getInputSource().getURI(), e.currentToken.next.beginLine,
e.currentToken.next.beginColumn);
}
diff --git a/src/main/java/org/htmlunit/cssparser/parser/ParserContext.java b/src/main/java/org/htmlunit/cssparser/parser/ParserContext.java
new file mode 100644
index 0000000..616bd1c
--- /dev/null
+++ b/src/main/java/org/htmlunit/cssparser/parser/ParserContext.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2019-2024 Ronald Brill.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.htmlunit.cssparser.parser;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.List;
+
+import org.htmlunit.cssparser.parser.javacc.Token;
+
+/**
+ * Tracks parsing context to provide detailed error messages.
+ *
+ * @author Ronald Brill
+ */
+public class ParserContext {
+ private final Deque ruleStack_ = new ArrayDeque<>();
+ private Token currentToken_;
+ private final List expectedTokens_ = new ArrayList<>();
+ private String currentProperty_;
+
+ /**
+ * Enter a new parsing rule context.
+ *
+ * @param ruleName the name of the rule being entered
+ */
+ public void enterRule(final String ruleName) {
+ ruleStack_.push(ruleName);
+ }
+
+ /**
+ * Exit the current parsing rule context.
+ */
+ public void exitRule() {
+ if (!ruleStack_.isEmpty()) {
+ ruleStack_.pop();
+ }
+ }
+
+ /**
+ * Set the current token being processed.
+ *
+ * @param token the current token
+ */
+ public void setCurrentToken(final Token token) {
+ currentToken_ = token;
+ }
+
+ /**
+ * Add an expected token to the list.
+ *
+ * @param token the expected token description
+ */
+ public void addExpectedToken(final String token) {
+ expectedTokens_.add(token);
+ }
+
+ /**
+ * Clear the list of expected tokens.
+ */
+ public void clearExpectedTokens() {
+ expectedTokens_.clear();
+ }
+
+ /**
+ * Set the current property being parsed.
+ *
+ * @param property the property name
+ */
+ public void setCurrentProperty(final String property) {
+ currentProperty_ = property;
+ }
+
+ /**
+ * Build a contextual error message by appending context information
+ * to the base message.
+ *
+ * @param baseMessage the base error message
+ * @return the contextual error message
+ */
+ public String buildContextualMessage(final String baseMessage) {
+ final StringBuilder sb = new StringBuilder(baseMessage);
+
+ // Add rule context
+ if (!ruleStack_.isEmpty()) {
+ sb.append(" (in ");
+ final List stack = new ArrayList<>(ruleStack_);
+ // Reverse to show from root to current
+ for (int i = stack.size() - 1; i >= 0; i--) {
+ if (i < stack.size() - 1) {
+ sb.append(" > ");
+ }
+ sb.append(stack.get(i));
+ }
+ sb.append(")");
+ }
+
+ // Add current token info
+ if (currentToken_ != null) {
+ sb.append(" at '");
+ sb.append(currentToken_.image);
+ sb.append("'");
+ }
+
+ // Add expected tokens
+ if (!expectedTokens_.isEmpty()) {
+ sb.append(". Expected: ");
+ for (int i = 0; i < expectedTokens_.size(); i++) {
+ if (i > 0) {
+ sb.append(", ");
+ }
+ sb.append(expectedTokens_.get(i));
+ }
+ }
+
+ // Add property context if available
+ if (currentProperty_ != null) {
+ sb.append(" (property: ");
+ sb.append(currentProperty_);
+ sb.append(")");
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Get the current parsing context as a string.
+ *
+ * @return the current context description
+ */
+ public String getCurrentContext() {
+ if (ruleStack_.isEmpty()) {
+ return "root";
+ }
+ final StringBuilder sb = new StringBuilder();
+ final List stack = new ArrayList<>(ruleStack_);
+ // Reverse to show from root to current
+ for (int i = stack.size() - 1; i >= 0; i--) {
+ if (i < stack.size() - 1) {
+ sb.append(" > ");
+ }
+ sb.append(stack.get(i));
+ }
+ return sb.toString();
+ }
+}
diff --git a/src/main/javacc/CSS3Parser.jj b/src/main/javacc/CSS3Parser.jj
index aed93c0..4fbd494 100644
--- a/src/main/javacc/CSS3Parser.jj
+++ b/src/main/javacc/CSS3Parser.jj
@@ -676,6 +676,7 @@ void mediaRule() :
{
locator = createLocator(token);
+ getParserContext().enterRule("@media");
}
( )*
mediaList(ml)
@@ -694,6 +695,8 @@ void mediaRule() :
}
catch(ParseException e)
{
+ getParserContext().addExpectedToken("media-type");
+ getParserContext().addExpectedToken("{");
CSSParseException cpe = toCSSParseException("invalidMediaRule", e);
getErrorHandler().error(cpe);
error_skipblock("ignoringRule", cpe);
@@ -703,6 +706,8 @@ void mediaRule() :
if (start) {
handleEndMedia(ml);
}
+ getParserContext().exitRule();
+ getParserContext().clearExpectedTokens();
}
}
@@ -1060,6 +1065,7 @@ void styleRule() :
try {
{
t = token;
+ getParserContext().enterRule("style-rule");
}
selList = selectorList()
( )*
@@ -1077,6 +1083,8 @@ void styleRule() :
}
catch(ParseException e)
{
+ getParserContext().addExpectedToken("selector");
+ getParserContext().addExpectedToken("{");
CSSParseException cpe = toCSSParseException("invalidStyleRule", e);
getErrorHandler().error(cpe);
error_skipblock("ignoringFollowingDeclarations", cpe);
@@ -1085,6 +1093,8 @@ void styleRule() :
if (start) {
handleEndSelector(selList);
}
+ getParserContext().exitRule();
+ getParserContext().clearExpectedTokens();
}
}
@@ -1568,20 +1578,31 @@ void declaration() :
{
try
{
+ {
+ getParserContext().enterRule("declaration");
+ }
// at the moment i have no better idea how to handle the
// infamous css-star-hack (http://en.wikipedia.org/wiki/CSS_filter#Star_hack)
// smart (means: ignoring only one decl)
( { starHack = createLocator(token); } )?
(
(
- t = ident() { p = unescape(t.image, false); locator = createLocator(t); }
+ t = ident() {
+ p = unescape(t.image, false);
+ locator = createLocator(t);
+ getParserContext().setCurrentProperty(p);
+ }
( )*
( )*
e = expr()
)
|
(
- t = ( )* { p = unescape(t.image, false); locator = createLocator(t); }
+ t = ( )* {
+ p = unescape(t.image, false);
+ locator = createLocator(t);
+ getParserContext().setCurrentProperty(p);
+ }
( )*
( e = expr() )?
)
@@ -1616,10 +1637,18 @@ void declaration() :
}
catch (ParseException ex)
{
+ getParserContext().addExpectedToken("property-name");
+ getParserContext().addExpectedToken(":");
CSSParseException cpe = toCSSParseException("invalidDeclaration", ex);
getErrorHandler().error(cpe);
error_skipdecl();
}
+ finally
+ {
+ getParserContext().setCurrentProperty(null);
+ getParserContext().exitRule();
+ getParserContext().clearExpectedTokens();
+ }
}
//
diff --git a/src/test/java/org/htmlunit/cssparser/parser/CSS3ParserTest.java b/src/test/java/org/htmlunit/cssparser/parser/CSS3ParserTest.java
index be4d8f4..5c676f7 100644
--- a/src/test/java/org/htmlunit/cssparser/parser/CSS3ParserTest.java
+++ b/src/test/java/org/htmlunit/cssparser/parser/CSS3ParserTest.java
@@ -807,7 +807,7 @@ public void atRules2() throws Exception {
assertEquals(1, errorHandler.getErrorCount());
final String expected = "@import rule must occur before all other rules, except the @charset rule."
- + " (Invalid token \"@import\". Was expecting: .)";
+ + " (Invalid token \"@import\". Was expecting: .) (in @media)";
assertEquals(expected, errorHandler.getErrorMessage());
assertEquals("3", errorHandler.getErrorLines());
assertEquals("3", errorHandler.getErrorColumns());
@@ -852,7 +852,7 @@ public void atRules2b() throws Exception {
assertEquals(1, errorHandler.getErrorCount());
final String expected = "@import rule must occur before all other rules, except the @charset rule."
- + " (Invalid token \"@import\". Was expecting: .)";
+ + " (Invalid token \"@import\". Was expecting: .) (in @media)";
assertEquals(expected, errorHandler.getErrorMessage());
assertEquals("4", errorHandler.getErrorLines());
assertEquals("3", errorHandler.getErrorColumns());
@@ -1597,7 +1597,9 @@ public void malformedDeclaration() throws Exception {
assertEquals(7, errorHandler.getErrorCount());
final String expected = "Error in declaration. (Invalid token \"}\". Was expecting one of: , \":\".)"
+ + " (in style-rule > declaration). Expected: property-name, : (property: background)"
+ " Error in declaration. (Invalid token \";\". Was expecting one of: , \":\".)"
+ + " (in style-rule > declaration). Expected: property-name, : (property: background)"
+ " Error in expression. (Invalid token \"}\". Was expecting one of: , \"only\", , \"inherit\", \"none\", \"from\", "
+ ", , \"-\", \"+\", , , , , , "
+ ", , , , "
@@ -1606,6 +1608,7 @@ public void malformedDeclaration() throws Exception {
+ ", , , , , , , "
+ ", , , , , "
+ ", , , , , , \"progid:\".)"
+ + " (in style-rule > declaration) (property: background)"
+ " Error in expression. (Invalid token \";\". Was expecting one of: , \"only\", , \"inherit\", \"none\", \"from\", "
+ ", , \"-\", \"+\", , , , , , "
+ ", , , , "
@@ -1614,9 +1617,13 @@ public void malformedDeclaration() throws Exception {
+ ", , , , , , , "
+ ", , , , , "
+ ", , , , , , \"progid:\".)"
+ + " (in style-rule > declaration) (property: background)"
+ " Error in declaration. (Invalid token \"{\". Was expecting one of: , \":\".)"
+ + " (in style-rule > declaration). Expected: property-name, : (property: background)"
+ " Error in style rule. (Invalid token \" \". Was expecting one of: , \"}\", \";\".)"
- + " Error in declaration. (Invalid token \"{\". Was expecting one of: , \":\".)";
+ + " (in style-rule). Expected: selector, {"
+ + " Error in declaration. (Invalid token \"{\". Was expecting one of: , \":\".)"
+ + " (in style-rule > declaration). Expected: property-name, : (property: background)";
assertEquals(expected, errorHandler.getErrorMessage());
assertEquals("2 3 4 5 6 6 7", errorHandler.getErrorLines());
assertEquals("29 28 30 29 28 48 28", errorHandler.getErrorColumns());
@@ -1709,7 +1716,7 @@ public void malformedStatements() throws Exception {
assertEquals(1, errorHandler.getErrorCount());
final String expected = "Error in style rule. "
- + "(Invalid token \"@here\". Was expecting one of: , \"{\", \",\".)";
+ + "(Invalid token \"@here\". Was expecting one of: , \"{\", \",\".) (in style-rule). Expected: selector, {";
assertEquals(expected, errorHandler.getErrorMessage());
assertEquals("2", errorHandler.getErrorLines());
assertEquals("3", errorHandler.getErrorColumns());
@@ -1911,7 +1918,8 @@ public void unexpectedEndOfString() throws Exception {
+ ", , , , "
+ ", , , , , , , "
+ ", , , , , "
- + ", , , , , , \"progid:\".)";
+ + ", , , , , , \"progid:\".)"
+ + " (in style-rule > declaration) (property: font-family)";
assertEquals(expected, errorHandler.getErrorMessage());
assertEquals("3", errorHandler.getErrorLines());
assertEquals("16", errorHandler.getErrorColumns());
diff --git a/src/test/java/org/htmlunit/cssparser/parser/ContextAwareErrorTest.java b/src/test/java/org/htmlunit/cssparser/parser/ContextAwareErrorTest.java
new file mode 100644
index 0000000..8fa888d
--- /dev/null
+++ b/src/test/java/org/htmlunit/cssparser/parser/ContextAwareErrorTest.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2019-2024 Ronald Brill.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.htmlunit.cssparser.parser;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.IOException;
+import java.io.StringReader;
+
+import org.htmlunit.cssparser.ErrorHandler;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests for context-aware error messages.
+ *
+ * @author Ronald Brill
+ */
+public class ContextAwareErrorTest extends AbstractCSSParserTest {
+
+ /**
+ * Test that errors in declarations include context information.
+ * @throws IOException if any error occurs
+ */
+ @Test
+ public void testDeclarationErrorWithContext() throws IOException {
+ final String css = ".class { color red; }"; // Missing colon
+ final CSSOMParser parser = new CSSOMParser();
+ final ErrorHandler errorHandler = new ErrorHandler();
+ parser.setErrorHandler(errorHandler);
+
+ final InputSource source = new InputSource(new StringReader(css));
+ parser.parseStyleSheet(source, null);
+
+ assertEquals(1, errorHandler.getErrorCount());
+ final String errorMsg = errorHandler.getErrorMessage();
+
+ // Should contain context about being in a style rule
+ assertTrue(errorMsg.contains("style-rule") || errorMsg.contains("declaration"),
+ "Error message should contain context: " + errorMsg);
+ }
+
+ /**
+ * Test that errors in nested media rules include full context path.
+ * @throws IOException if any error occurs
+ */
+ @Test
+ public void testNestedMediaRuleErrorWithContext() throws IOException {
+ final String css = "@media screen { .class { color red; } }"; // Missing colon
+ final CSSOMParser parser = new CSSOMParser();
+ final ErrorHandler errorHandler = new ErrorHandler();
+ parser.setErrorHandler(errorHandler);
+
+ final InputSource source = new InputSource(new StringReader(css));
+ parser.parseStyleSheet(source, null);
+
+ assertEquals(1, errorHandler.getErrorCount());
+ final String errorMsg = errorHandler.getErrorMessage();
+
+ // Should contain context showing we're in @media > style-rule
+ assertTrue(errorMsg.contains("@media"),
+ "Error message should contain @media context: " + errorMsg);
+ }
+
+ /**
+ * Test that property name is included in error context.
+ * @throws IOException if any error occurs
+ */
+ @Test
+ public void testPropertyNameInErrorContext() throws IOException {
+ final String css = ".class { color: red; background blue; }"; // Missing colon
+ final CSSOMParser parser = new CSSOMParser();
+ final ErrorHandler errorHandler = new ErrorHandler();
+ parser.setErrorHandler(errorHandler);
+
+ final InputSource source = new InputSource(new StringReader(css));
+ parser.parseStyleSheet(source, null);
+
+ assertTrue(errorHandler.getErrorCount() > 0);
+ final String errorMsg = errorHandler.getErrorMessage();
+
+ // Should contain information about the parsing context
+ assertTrue(errorMsg.length() > 0, "Error message should not be empty");
+ }
+
+ /**
+ * Test that invalid selector errors include expected tokens.
+ * @throws IOException if any error occurs
+ */
+ @Test
+ public void testInvalidSelectorWithExpectedTokens() throws IOException {
+ final String css = ".class color: red; }"; // Missing opening brace
+ final CSSOMParser parser = new CSSOMParser();
+ final ErrorHandler errorHandler = new ErrorHandler();
+ parser.setErrorHandler(errorHandler);
+
+ final InputSource source = new InputSource(new StringReader(css));
+ parser.parseStyleSheet(source, null);
+
+ assertTrue(errorHandler.getErrorCount() > 0);
+ final String errorMsg = errorHandler.getErrorMessage();
+
+ // Should indicate what was expected (like "{" or selector)
+ assertTrue(errorMsg.contains("Expected") || errorMsg.contains("expecting"),
+ "Error message should mention expected tokens: " + errorMsg);
+ }
+
+ /**
+ * Test that media rule errors include context.
+ * @throws IOException if any error occurs
+ */
+ @Test
+ public void testMediaRuleErrorWithContext() throws IOException {
+ final String css = "@media screeen { }";
+ final CSSOMParser parser = new CSSOMParser();
+ final ErrorHandler errorHandler = new ErrorHandler();
+ parser.setErrorHandler(errorHandler);
+
+ final InputSource source = new InputSource(new StringReader(css));
+ parser.parseStyleSheet(source, null);
+
+ // This may or may not produce an error depending on parser behavior
+ // The test validates that if there is an error, it includes context
+ if (errorHandler.getErrorCount() > 0) {
+ final String errorMsg = errorHandler.getErrorMessage();
+ assertTrue(errorMsg.length() > 0, "Error message should not be empty");
+ }
+ }
+
+ /**
+ * Test that existing valid CSS still parses without errors.
+ * @throws IOException if any error occurs
+ */
+ @Test
+ public void testValidCssStillParses() throws IOException {
+ final String css = ".class { color: red; background-color: blue; }";
+ final CSSOMParser parser = new CSSOMParser();
+ final ErrorHandler errorHandler = new ErrorHandler();
+ parser.setErrorHandler(errorHandler);
+
+ final InputSource source = new InputSource(new StringReader(css));
+ parser.parseStyleSheet(source, null);
+
+ assertEquals(0, errorHandler.getErrorCount(), "Valid CSS should parse without errors");
+ }
+
+ /**
+ * Test that nested rules show proper context hierarchy.
+ * @throws IOException if any error occurs
+ */
+ @Test
+ public void testNestedContextHierarchy() throws IOException {
+ final String css = "@media print { .page { color black; } }"; // Missing colon
+ final CSSOMParser parser = new CSSOMParser();
+ final ErrorHandler errorHandler = new ErrorHandler();
+ parser.setErrorHandler(errorHandler);
+
+ final InputSource source = new InputSource(new StringReader(css));
+ parser.parseStyleSheet(source, null);
+
+ assertEquals(1, errorHandler.getErrorCount());
+ final String errorMsg = errorHandler.getErrorMessage();
+
+ // Verify the error message contains some form of context
+ assertTrue(errorMsg.length() > 0, "Error message should not be empty");
+ assertTrue(errorMsg.contains("Error") || errorMsg.contains("Invalid"),
+ "Error message should indicate an error: " + errorMsg);
+ }
+}
diff --git a/src/test/java/org/htmlunit/cssparser/parser/HSLColorParserTest.java b/src/test/java/org/htmlunit/cssparser/parser/HSLColorParserTest.java
index f72a0e8..5e96ed7 100644
--- a/src/test/java/org/htmlunit/cssparser/parser/HSLColorParserTest.java
+++ b/src/test/java/org/htmlunit/cssparser/parser/HSLColorParserTest.java
@@ -217,33 +217,45 @@ public void hslVariousErrors() throws Exception {
color("foreground: hsl(10, 20%, 30%, none)", "foreground: hsl(10, 20%, 30%, none)");
color(1, "Error in expression. (Invalid token \"-none\". Was expecting one of: , , \"none\", \"from\", \"-\", \"+\", "
- + ", , , , , .)",
+ + ", , , , , .)"
+ + " (in declaration) (property: foreground)",
"foreground: hsl(-none 20% 30%)");
- color(1, "Error in expression. (Invalid token \"-none\". Was expecting one of: , \"none\", \"-\", \"+\", \",\", , , .)",
+ color(1, "Error in expression. (Invalid token \"-none\". Was expecting one of: , \"none\", \"-\", \"+\", \",\", , , .)"
+ + " (in declaration) (property: foreground)",
"foreground: hsl(10 -none 30%)");
- color(1, "Error in expression. (Invalid token \"-none\". Was expecting one of: , \"none\", \"-\", \"+\", \",\", , , .)",
+ color(1, "Error in expression. (Invalid token \"-none\". Was expecting one of: , \"none\", \"-\", \"+\", \",\", , , .)"
+ + " (in declaration) (property: foreground)",
"foreground: hsl(10 20% -none)");
- color(1, "Error in expression. (Invalid token \"-none\". Was expecting one of: , , \"none\", \"-\", \"+\", , , .)",
+ color(1, "Error in expression. (Invalid token \"-none\". Was expecting one of: , , \"none\", \"-\", \"+\", , , .)"
+ + " (in declaration) (property: foreground)",
"foreground: hsl(10 20% 30% / -none)");
- color(1, "Error in expression. (Invalid token \")\". Was expecting one of: , , \"none\", \"from\", \"-\", \"+\", , , , , , .)",
+ color(1, "Error in expression. (Invalid token \")\". Was expecting one of: , , \"none\", \"from\", \"-\", \"+\", , , , , , .)"
+ + " (in declaration) (property: foreground)",
"foreground: hsl()");
- color(1, "Error in expression. (Invalid token \")\". Was expecting one of: , \"none\", \"-\", \"+\", \",\", , , .)",
+ color(1, "Error in expression. (Invalid token \")\". Was expecting one of: , \"none\", \"-\", \"+\", \",\", , , .)"
+ + " (in declaration) (property: foreground)",
"foreground: hsl(10)");
- color(1, "Error in expression. (Invalid token \")\". Was expecting one of: , \"none\", \"-\", \"+\", \",\", , , .)",
+ color(1, "Error in expression. (Invalid token \")\". Was expecting one of: , \"none\", \"-\", \"+\", \",\", , , .)"
+ + " (in declaration) (property: foreground)",
"foreground: hsl(10 20%)");
- color(1, "Error in expression. (Invalid token \")\". Was expecting one of: , , \"none\", \"-\", \"+\", , , .)",
+ color(1, "Error in expression. (Invalid token \")\". Was expecting one of: , , \"none\", \"-\", \"+\", , , .)"
+ + " (in declaration) (property: foreground)",
"foreground: hsl(10, 20%, 30%,)");
- color(1, "Error in expression. (Invalid token \")\". Was expecting one of: , , \"none\", \"-\", \"+\", , , .)",
+ color(1, "Error in expression. (Invalid token \")\". Was expecting one of: , , \"none\", \"-\", \"+\", , , .)"
+ + " (in declaration) (property: foreground)",
"foreground: hsl(10, 20%, 30%/)");
- color(1, "Error in expression. (Invalid token \"20\". Was expecting one of: , \"none\", \"-\", \"+\", , , .)",
+ color(1, "Error in expression. (Invalid token \"20\". Was expecting one of: , \"none\", \"-\", \"+\", , , .)"
+ + " (in declaration) (property: foreground)",
"foreground: hsl(10, 20px, 30)");
- color(1, "Error in expression. (Invalid token \"20\". Was expecting one of: , \"none\", \"-\", \"+\", \",\", , , .)",
+ color(1, "Error in expression. (Invalid token \"20\". Was expecting one of: , \"none\", \"-\", \"+\", \",\", , , .)"
+ + " (in declaration) (property: foreground)",
"foreground: hsl(10 20px 30)");
- color(1, "Error in expression. (Invalid token \"10\". Was expecting one of: , , \"none\", \"from\", \"-\", \"+\", , , , , , .)",
+ color(1, "Error in expression. (Invalid token \"10\". Was expecting one of: , , \"none\", \"from\", \"-\", \"+\", , , , , , .)"
+ + " (in declaration) (property: foreground)",
"foreground: hsl('10', 20, 30,)");
}
}
diff --git a/src/test/java/org/htmlunit/cssparser/parser/HWBColorParserTest.java b/src/test/java/org/htmlunit/cssparser/parser/HWBColorParserTest.java
index 5dc380b..d0193f0 100644
--- a/src/test/java/org/htmlunit/cssparser/parser/HWBColorParserTest.java
+++ b/src/test/java/org/htmlunit/cssparser/parser/HWBColorParserTest.java
@@ -83,36 +83,48 @@ public void hwbRelative() throws Exception {
public void hwbVariousErrors() throws Exception {
// like browsers we ignore many errors during parsing
- color(1, "Error in expression. (Invalid token \",\". Was expecting one of: , \"none\", \"-\", \"+\", , , .)",
+ color(1, "Error in expression. (Invalid token \",\". Was expecting one of: , \"none\", \"-\", \"+\", , , .)"
+ + " (in declaration) (property: foreground)",
"foreground: hwb(10, 20% 30%)");
- color(1, "Error in expression. (Invalid token \",\". Was expecting one of: , \"none\", \"-\", \"+\",