diff --git a/ph-css/src/main/java/com/helger/css/handler/CSSNodeToDomainObject.java b/ph-css/src/main/java/com/helger/css/handler/CSSNodeToDomainObject.java index d2d64485..4bbb33f5 100644 --- a/ph-css/src/main/java/com/helger/css/handler/CSSNodeToDomainObject.java +++ b/ph-css/src/main/java/com/helger/css/handler/CSSNodeToDomainObject.java @@ -644,6 +644,12 @@ private CSSDeclaration _createDeclaration (@Nonnull final CSSNode aNode) } final String sProperty = aNode.jjtGetChild (0).getText (); + if (sProperty == null) + { + // Syntax error with deprecated property name (see #84) + return null; + } + final CSSExpression aExpression = _createExpression (aNode.jjtGetChild (1)); boolean bImportant = false; if (nChildCount == 3) diff --git a/ph-css/src/main/java/com/helger/css/reader/errorhandler/CSSParseError.java b/ph-css/src/main/java/com/helger/css/reader/errorhandler/CSSParseError.java index 69e167fd..5f5ab404 100644 --- a/ph-css/src/main/java/com/helger/css/reader/errorhandler/CSSParseError.java +++ b/ph-css/src/main/java/com/helger/css/reader/errorhandler/CSSParseError.java @@ -149,6 +149,12 @@ public static CSSParseError createUnexpectedRule (@Nonnull final Token aCurrentT return new CSSParseError (LoggingCSSParseErrorHandler.createLoggingStringUnexpectedRule (aCurrentToken, sRule, sMsg)); } + @Nonnull + public static CSSParseError createDeprecatedProperty (@Nonnull final Token aPrefixToken, @Nonnull final Token aIdentifierToken) + { + return new CSSParseError (LoggingCSSParseErrorHandler.createLoggingStringDeprecatedProperty (aPrefixToken, aIdentifierToken)); + } + @Nonnull public static CSSParseError createBrowserCompliantSkip (@Nullable final ParseException ex, @Nonnull final Token aFromToken, diff --git a/ph-css/src/main/java/com/helger/css/reader/errorhandler/CollectingCSSParseErrorHandler.java b/ph-css/src/main/java/com/helger/css/reader/errorhandler/CollectingCSSParseErrorHandler.java index 61a14877..11250c23 100644 --- a/ph-css/src/main/java/com/helger/css/reader/errorhandler/CollectingCSSParseErrorHandler.java +++ b/ph-css/src/main/java/com/helger/css/reader/errorhandler/CollectingCSSParseErrorHandler.java @@ -62,20 +62,25 @@ public void onCSSUnexpectedRule (@Nonnull final Token aCurrentToken, @Nonnull @Nonempty final String sRule, @Nonnull @Nonempty final String sMsg) throws ParseException { - m_aRWLock.writeLockedBoolean ( () -> m_aErrors.add (CSSParseError.createUnexpectedRule (aCurrentToken, sRule, sMsg))); + m_aRWLock.writeLocked ( () -> m_aErrors.add (CSSParseError.createUnexpectedRule (aCurrentToken, sRule, sMsg))); + } + + public void onCSSDeprecatedProperty (@Nonnull final Token aPrefixToken, @Nonnull final Token aIdentifierToken) + { + m_aRWLock.writeLocked ( () -> m_aErrors.add (CSSParseError.createDeprecatedProperty (aPrefixToken, aIdentifierToken))); } public void onCSSBrowserCompliantSkip (@Nullable final ParseException ex, @Nonnull final Token aFromToken, @Nonnull final Token aToToken) throws ParseException { - m_aRWLock.writeLockedBoolean ( () -> m_aErrors.add (CSSParseError.createBrowserCompliantSkip (ex, aFromToken, aToToken))); + m_aRWLock.writeLocked ( () -> m_aErrors.add (CSSParseError.createBrowserCompliantSkip (ex, aFromToken, aToToken))); } @Override public void onIllegalCharacter (final char cIllegalChar) { - m_aRWLock.writeLockedBoolean ( () -> m_aErrors.add (CSSParseError.createIllegalCharacter (cIllegalChar))); + m_aRWLock.writeLocked ( () -> m_aErrors.add (CSSParseError.createIllegalCharacter (cIllegalChar))); } /** diff --git a/ph-css/src/main/java/com/helger/css/reader/errorhandler/DoNothingCSSParseErrorHandler.java b/ph-css/src/main/java/com/helger/css/reader/errorhandler/DoNothingCSSParseErrorHandler.java index fe192143..519a2222 100644 --- a/ph-css/src/main/java/com/helger/css/reader/errorhandler/DoNothingCSSParseErrorHandler.java +++ b/ph-css/src/main/java/com/helger/css/reader/errorhandler/DoNothingCSSParseErrorHandler.java @@ -35,21 +35,24 @@ public class DoNothingCSSParseErrorHandler implements ICSSParseErrorHandler public DoNothingCSSParseErrorHandler () {} - public void onCSSParseError (@Nonnull final ParseException aParseEx, @Nullable final Token aLastSkippedToken) throws ParseException + public void onCSSParseError (@Nonnull final ParseException aParseEx, @Nullable final Token aLastSkippedToken) { /* really do nothing :) */ } public void onCSSUnexpectedRule (@Nonnull final Token aCurrentToken, @Nonnull @Nonempty final String sRule, - @Nonnull @Nonempty final String sMsg) throws ParseException + @Nonnull @Nonempty final String sMsg) { /* really do nothing :) */ } - public void onCSSBrowserCompliantSkip (@Nullable final ParseException ex, - @Nonnull final Token aFromToken, - @Nonnull final Token aToToken) throws ParseException + public void onCSSDeprecatedProperty (@Nonnull final Token aPrefixToken, @Nonnull final Token aIdentifierToken) + { + /* really do nothing :) */ + } + + public void onCSSBrowserCompliantSkip (@Nullable final ParseException ex, @Nonnull final Token aFromToken, @Nonnull final Token aToToken) { /* really do nothing :) */ } diff --git a/ph-css/src/main/java/com/helger/css/reader/errorhandler/ICSSParseErrorHandler.java b/ph-css/src/main/java/com/helger/css/reader/errorhandler/ICSSParseErrorHandler.java index 496088e9..ce69e919 100644 --- a/ph-css/src/main/java/com/helger/css/reader/errorhandler/ICSSParseErrorHandler.java +++ b/ph-css/src/main/java/com/helger/css/reader/errorhandler/ICSSParseErrorHandler.java @@ -64,6 +64,19 @@ void onCSSUnexpectedRule (@Nonnull Token aCurrentToken, @Nonnull @Nonempty String sRule, @Nonnull @Nonempty String sMsg) throws ParseException; + /** + * To be called, if a deprecated old IE 6/7 property is found. + * + * @param aPrefixToken + * The prefix token found (like '$' or '*'). Never null. + * @param aIdentifierToken + * The identifier token found. Never null. + * @throws ParseException + * In case the error is fatal and should be propagated. + * @since 6.4.5 + */ + void onCSSDeprecatedProperty (@Nonnull Token aPrefixToken, @Nonnull Token aIdentifierToken) throws ParseException; + /** * This method is only called in browser compliant mode if a certain part of * the CSS is skipped. @@ -75,7 +88,7 @@ void onCSSUnexpectedRule (@Nonnull Token aCurrentToken, * Original token that caused the error and was skipped (inclusive). * Never null. * @param aToToken - * The end token until which was skipped(exclusive). Never + * The end token until which was skipped (exclusive). Never * null. * @throws ParseException * In case the error is fatal and should be propagated. @@ -124,6 +137,12 @@ public void onCSSUnexpectedRule (@Nonnull final Token aCurrentToken, aOther.onCSSUnexpectedRule (aCurrentToken, sRule, sMsg); } + public void onCSSDeprecatedProperty (@Nonnull final Token aPrefixToken, @Nonnull final Token aIdentifierToken) throws ParseException + { + aThis.onCSSDeprecatedProperty (aPrefixToken, aIdentifierToken); + aOther.onCSSDeprecatedProperty (aPrefixToken, aIdentifierToken); + } + public void onCSSBrowserCompliantSkip (@Nullable final ParseException ex, @Nonnull final Token aFromToken, @Nonnull final Token aToToken) throws ParseException diff --git a/ph-css/src/main/java/com/helger/css/reader/errorhandler/LoggingCSSParseErrorHandler.java b/ph-css/src/main/java/com/helger/css/reader/errorhandler/LoggingCSSParseErrorHandler.java index cbdd491b..c3f2d015 100644 --- a/ph-css/src/main/java/com/helger/css/reader/errorhandler/LoggingCSSParseErrorHandler.java +++ b/ph-css/src/main/java/com/helger/css/reader/errorhandler/LoggingCSSParseErrorHandler.java @@ -150,6 +150,38 @@ public void onCSSUnexpectedRule (@Nonnull final Token aCurrentToken, LOGGER.warn (createLoggingStringUnexpectedRule (aCurrentToken, sRule, sMsg)); } + /** + * Create a common string to be used for deprecated properties. To be called, + * if a deprecated old IE 6/7 property is found. + * + * @param aPrefixToken + * The prefix token found (like '$' or '*'). Never null. + * @param aIdentifierToken + * The identifier token found. Never null. + * @throws ParseException + * In case the error is fatal and should be propagated. + * @return The concatenated string with source location, etc. May neither be + * null nor empty. + */ + @Nonnull + @Nonempty + public static String createLoggingStringDeprecatedProperty (@Nonnull final Token aPrefixToken, @Nonnull final Token aIdentifierToken) + { + return "[" + + aPrefixToken.beginLine + + ":" + + aPrefixToken.beginColumn + + "] Deprecated property name '" + + aPrefixToken.image + + aIdentifierToken.image + + "'"; + } + + public void onCSSDeprecatedProperty (@Nonnull final Token aPrefixToken, @Nonnull final Token aIdentifierToken) + { + LOGGER.warn (createLoggingStringDeprecatedProperty (aPrefixToken, aIdentifierToken)); + } + @Nonnull @Nonempty public static String createLoggingStringBrowserCompliantSkip (@Nullable final ParseException ex, diff --git a/ph-css/src/main/java/com/helger/css/reader/errorhandler/ThrowingCSSParseErrorHandler.java b/ph-css/src/main/java/com/helger/css/reader/errorhandler/ThrowingCSSParseErrorHandler.java index 5fee6db4..9e49bbd6 100644 --- a/ph-css/src/main/java/com/helger/css/reader/errorhandler/ThrowingCSSParseErrorHandler.java +++ b/ph-css/src/main/java/com/helger/css/reader/errorhandler/ThrowingCSSParseErrorHandler.java @@ -51,6 +51,11 @@ public void onCSSUnexpectedRule (@Nonnull final Token aCurrentToken, throw new ParseException (LoggingCSSParseErrorHandler.createLoggingStringUnexpectedRule (aCurrentToken, sRule, sMsg)); } + public void onCSSDeprecatedProperty (@Nonnull final Token aPrefixToken, @Nonnull final Token aIdentifierToken) throws ParseException + { + throw new ParseException (LoggingCSSParseErrorHandler.createLoggingStringDeprecatedProperty (aPrefixToken, aIdentifierToken)); + } + public void onCSSBrowserCompliantSkip (@Nullable final ParseException ex, @Nonnull final Token aFromToken, @Nonnull final Token aToToken) throws ParseException diff --git a/ph-css/src/main/jjtree/ParserCSS30.jjt b/ph-css/src/main/jjtree/ParserCSS30.jjt index f2502e3b..c1c70f8e 100644 --- a/ph-css/src/main/jjtree/ParserCSS30.jjt +++ b/ph-css/src/main/jjtree/ParserCSS30.jjt @@ -18,9 +18,7 @@ options { // JavaCC options // LOOKAHEAD = 2; CHOICE_AMBIGUITY_CHECK = 5; - // DEBUG_PARSER = true; - // DEBUG_LOOKAHEAD = true; - // DEBUG_TOKEN_MANAGER = true; + // DEBUG_PARSER = true; DEBUG_LOOKAHEAD = true; DEBUG_TOKEN_MANAGER = true; // ERROR_REPORTING = false; JAVA_UNICODE_ESCAPE = true; UNICODE_INPUT = true; @@ -346,9 +344,24 @@ SPECIAL_TOKEN : } JAVACODE -private void errorDeprecated(final Token tok, final int... aKinds) throws ParseException #void { - int expectedSeq[][] = { aKinds }; - throw new ParseException(tok, expectedSeq, tokenImage); +private void errorDeprecatedProperty(final Token aPrefixToken) throws ParseException #void +{ + if (m_bBrowserCompliantMode) + { + if (m_aCustomErrorHandler != null) + m_aCustomErrorHandler.onCSSDeprecatedProperty (aPrefixToken, token); + else + LOGGER.warn("[" + aPrefixToken.beginLine + ":" + aPrefixToken.beginColumn + "] Deprecated property name '" + aPrefixToken.image + token.image + "'"); + } + else + { + final ParseException ex = new ParseException (aPrefixToken, new int[][] { new int[] { IDENT } }, + tokenImage, token_source == null ? null : ParserCSS30TokenManager.lexStateNames[token_source.curLexState]); + // javaSkipToClosingBraceOrSemicolon (1); + // LOGGER.warn ("Skipped now '"+s+"'"); + // if (m_aCustomErrorHandler != null) m_aCustomErrorHandler.onCSSParseError (ex, token); + throw ex; + } } JAVACODE @@ -510,7 +523,7 @@ JAVACODE * @param ex The source ParseException. May not be null * @throws ParseEOFException If EOF occurs while skipping */ -private void browserCompliantSkip(final ParseException ex) throws ParseEOFException #void { +private void browserCompliantSkipInRule(final ParseException ex) throws ParseEOFException #void { javaSkipToClosingBrace (1); // push back last token (char count!!) token_source.backup(1); @@ -524,7 +537,7 @@ JAVACODE * @param ex The source ParseException. May not be null * @throws ParseEOFException If EOF occurs while skipping */ -private void browserCompliantSkipSelector(final ParseException ex) throws ParseEOFException #void { +private void browserCompliantSkipInSelector(final ParseException ex) throws ParseEOFException #void { javaSkipToClosingBrace (0); // push back last token (char count!!) token_source.backup(1); @@ -1134,15 +1147,17 @@ void selector() : {} void property() : { - Token save; + Token aPrefixToken; } { ( { jjtThis.setText (token.image); } ( )* - ) - | LOOKAHEAD(2, ( | ) ) - { save = token; } - ( | ) { errorDeprecated(save, IDENT); } + | LOOKAHEAD(2, ( | ) ) + ( | ) { aPrefixToken = token; } + { errorDeprecatedProperty (aPrefixToken); } + /* leaving jjtThis.text null is handled inside the code */ + ( )* + ) } void important() : {} @@ -1190,25 +1205,34 @@ void styleDeclarationOrRule() #void : {} CSSNode styleDeclarationList() : {} { +try { ( )* ( styleDeclarationOrRule() )? ( ( )* ( styleDeclarationOrRule() )? )* +} catch (/*final*/ ParseException ex) { + if (m_bBrowserCompliantMode) + browserCompliantSkipDecl (ex); + else { + errorSkipTo (ex, RBRACE); + token_source.backup(1); + } +} { return jjtThis; } } void styleDeclarationBlock() #void : {} { -try{ +try { styleDeclarationList() } catch (/*final*/ ParseException ex) { if (m_bBrowserCompliantMode) browserCompliantSkipDecl (ex); - else + else errorSkipTo (ex, RBRACE); } } @@ -1226,7 +1250,7 @@ try{ styleDeclarationBlock() } catch (/*final*/ ParseException ex) { if (m_bBrowserCompliantMode) - browserCompliantSkipSelector (ex); + browserCompliantSkipInSelector (ex); else throw ex; } @@ -1327,7 +1351,7 @@ try{ } catch (/*final*/ ParseException ex) { if (m_bBrowserCompliantMode) - browserCompliantSkip (ex); + browserCompliantSkipInRule (ex); else errorSkipTo (ex, RBRACE); } @@ -1412,7 +1436,7 @@ try{ } catch (/*final*/ ParseException ex) { if (m_bBrowserCompliantMode) - browserCompliantSkip (ex); + browserCompliantSkipInRule (ex); else errorSkipTo (ex, RBRACE); } @@ -1488,7 +1512,7 @@ try{ } catch (/*final*/ ParseException ex) { if (m_bBrowserCompliantMode) - browserCompliantSkip (ex); + browserCompliantSkipInRule (ex); else errorSkipTo (ex, RBRACE); } @@ -1575,7 +1599,7 @@ try{ } catch (/*final*/ ParseException ex) { if (m_bBrowserCompliantMode) - browserCompliantSkip (ex); + browserCompliantSkipInRule (ex); else errorSkipTo (ex, RBRACE); } diff --git a/ph-css/src/test/java/com/helger/css/supplementary/issues/Issue84Test.java b/ph-css/src/test/java/com/helger/css/supplementary/issues/Issue84Test.java new file mode 100644 index 00000000..9f6dca38 --- /dev/null +++ b/ph-css/src/test/java/com/helger/css/supplementary/issues/Issue84Test.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2014-2022 Philip Helger (www.helger.com) + * philip[at]helger[dot]com + * + * 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 + * + * http://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 com.helger.css.supplementary.issues; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import org.junit.Test; + +import com.helger.css.ECSSVersion; +import com.helger.css.decl.CSSStyleRule; +import com.helger.css.decl.CascadingStyleSheet; +import com.helger.css.reader.CSSReader; +import com.helger.css.reader.CSSReaderSettings; +import com.helger.css.writer.CSSWriter; +import com.helger.css.writer.CSSWriterSettings; + +/** + * Test for https://github.com/phax/ph-css/issues/84 + * + * @author Philip Helger + */ +public final class Issue84Test +{ + @Test + public void testBasicStarBrowserCompliant () + { + final String sCSS = "div {\n" + "a: 100px;\n" + "*b: 1;\n" + "c:d;\n" + "}"; + final CascadingStyleSheet aCSS = CSSReader.readFromStringReader (sCSS, + new CSSReaderSettings ().setCSSVersion (ECSSVersion.LATEST) + .setBrowserCompliantMode (true)); + assertNotNull (aCSS); + assertEquals (1, aCSS.getStyleRuleCount ()); + + final CSSStyleRule aSR = aCSS.getStyleRuleAtIndex (0); + assertEquals (2, aSR.getDeclarationCount ()); + + assertEquals ("div{a:100px;c:d}", + new CSSWriter (new CSSWriterSettings ().setOptimizedOutput (true)).setWriteHeaderText (false).getCSSAsString (aCSS)); + } + + @Test + public void testBasicStarNotBrowserCompliant () + { + final String sCSS = "div {\n" + "a: 100px;\n" + "*b: 1;\n" + "c:d;\n" + "}"; + final CascadingStyleSheet aCSS = CSSReader.readFromStringReader (sCSS, + new CSSReaderSettings ().setCSSVersion (ECSSVersion.LATEST) + .setBrowserCompliantMode (false)); + assertNotNull (aCSS); + assertEquals (1, aCSS.getStyleRuleCount ()); + + final CSSStyleRule aSR = aCSS.getStyleRuleAtIndex (0); + assertEquals (1, aSR.getDeclarationCount ()); + + assertEquals ("div{a:100px}", + new CSSWriter (new CSSWriterSettings ().setOptimizedOutput (true)).setWriteHeaderText (false).getCSSAsString (aCSS)); + } +} diff --git a/ph-css/src/test/resources/testfiles/css30/good/issue84.css b/ph-css/src/test/resources/testfiles/css30/good/issue84.css new file mode 100644 index 00000000..f30119d2 --- /dev/null +++ b/ph-css/src/test/resources/testfiles/css30/good/issue84.css @@ -0,0 +1,11 @@ +div { + max-width: 100px; + *zoom: 1; + position: relative; +} + +div { + max-width: 100px; + $zoom: 1; + position: relative; +}