From a954493159a3a49f55dfd6d4bf35d46c78abf132 Mon Sep 17 00:00:00 2001 From: Mike Wiedenbauer Date: Thu, 13 Jan 2022 17:27:04 +0100 Subject: [PATCH] #80 introduce :host-context pseudo class function - add testfile for issue --- .../decl/CSSSelectorMemberHostContext.java | 111 ++++++++++++++++++ .../css/handler/CSSNodeToDomainObject.java | 12 ++ .../com/helger/css/handler/ECSSNodeType.java | 1 + ph-css/src/main/jjtree/ParserCSS30.jjt | 12 ++ .../testfiles/css30/good/issue80.css | 23 ++++ 5 files changed, 159 insertions(+) create mode 100644 ph-css/src/main/java/com/helger/css/decl/CSSSelectorMemberHostContext.java create mode 100644 ph-css/src/test/resources/testfiles/css30/good/issue80.css diff --git a/ph-css/src/main/java/com/helger/css/decl/CSSSelectorMemberHostContext.java b/ph-css/src/main/java/com/helger/css/decl/CSSSelectorMemberHostContext.java new file mode 100644 index 00000000..fc0df538 --- /dev/null +++ b/ph-css/src/main/java/com/helger/css/decl/CSSSelectorMemberHostContext.java @@ -0,0 +1,111 @@ +/* + * 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.decl; + +import javax.annotation.Nonnegative; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.concurrent.NotThreadSafe; + +import com.helger.commons.ValueEnforcer; +import com.helger.commons.annotation.Nonempty; +import com.helger.commons.hashcode.HashCodeGenerator; +import com.helger.commons.string.ToStringGenerator; +import com.helger.css.CSSSourceLocation; +import com.helger.css.ECSSVersion; +import com.helger.css.ICSSSourceLocationAware; +import com.helger.css.ICSSVersionAware; +import com.helger.css.ICSSWriterSettings; + +/** + * Represents a single, simple CSS selector as used for the ":host-context()" CSS pseudo + * class function.
+ * Note: this class was completely redesigned for version 3.7.4 + * + * @author Philip Helger + */ +@NotThreadSafe +public class CSSSelectorMemberHostContext implements ICSSSelectorMember, ICSSVersionAware, ICSSSourceLocationAware +{ + private final CSSSelector m_aSelector; + private CSSSourceLocation m_aSourceLocation; + + public CSSSelectorMemberHostContext (@Nonnull final CSSSelector aSimpleSelector) + { + ValueEnforcer.notNull (aSimpleSelector, "SimpleSelector"); + m_aSelector = aSimpleSelector; + } + + @Nonnull + public final CSSSelector getSelector () + { + return m_aSelector; + } + + @Nonnull + @Nonempty + public String getAsCSSString (@Nonnull final ICSSWriterSettings aSettings, @Nonnegative final int nIndentLevel) + { + aSettings.checkVersionRequirements (this); + + final StringBuilder aSB = new StringBuilder (":host-context("); + aSB.append (m_aSelector.getAsCSSString (aSettings, 0)); + return aSB.append (')').toString (); + } + + @Nonnull + public ECSSVersion getMinimumCSSVersion () + { + return ECSSVersion.CSS30; + } + + @Nullable + public final CSSSourceLocation getSourceLocation () + { + return m_aSourceLocation; + } + + public final void setSourceLocation (@Nullable final CSSSourceLocation aSourceLocation) + { + m_aSourceLocation = aSourceLocation; + } + + @Override + public boolean equals (final Object o) + { + if (o == this) + return true; + if (o == null || !getClass ().equals (o.getClass ())) + return false; + final CSSSelectorMemberHostContext rhs = (CSSSelectorMemberHostContext) o; + return m_aSelector.equals (rhs.m_aSelector); + } + + @Override + public int hashCode () + { + return new HashCodeGenerator (this).append (m_aSelector).getHashCode (); + } + + @Override + public String toString () + { + return new ToStringGenerator (null).append ("Selector", m_aSelector) + .appendIfNotNull ("SourceLocation", m_aSourceLocation) + .getToString (); + } +} 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 f7450df1..d2d64485 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 @@ -285,6 +285,18 @@ private ICSSSelectorMember _createSelectorMember (final CSSNode aNode) return ret; } + if (ECSSNodeType.HOSTCONTEXT.isNode (aChildNode, m_eVersion)) + { + final CSSSelector aSelector = new CSSSelector (); + final int nChildChildCount = aChildNode.jjtGetNumChildren (); + for (int j = 0; j < nChildChildCount; ++j) + aSelector.addMember (_createSelectorMember (aChildNode.jjtGetChild (j))); + final CSSSelectorMemberHostContext ret = new CSSSelectorMemberHostContext (aSelector); + if (m_bUseSourceLocation) + ret.setSourceLocation (aNode.getSourceLocation ()); + return ret; + } + if (ECSSNodeType.SLOTTED.isNode (aChildNode, m_eVersion)) { final CSSSelector aSelector = new CSSSelector (); diff --git a/ph-css/src/main/java/com/helger/css/handler/ECSSNodeType.java b/ph-css/src/main/java/com/helger/css/handler/ECSSNodeType.java index c6518dd6..1daf19c8 100644 --- a/ph-css/src/main/java/com/helger/css/handler/ECSSNodeType.java +++ b/ph-css/src/main/java/com/helger/css/handler/ECSSNodeType.java @@ -58,6 +58,7 @@ public enum ECSSNodeType CLASS (ParserCSS30TreeConstants.JJTCLASS), PSEUDO (ParserCSS30TreeConstants.JJTPSEUDO), HOST (ParserCSS30TreeConstants.JJTHOST), + HOSTCONTEXT (ParserCSS30TreeConstants.JJTHOSTCONTEXT), SLOTTED (ParserCSS30TreeConstants.JJTSLOTTED), NEGATION (ParserCSS30TreeConstants.JJTNEGATION), ATTRIB (ParserCSS30TreeConstants.JJTATTRIB), diff --git a/ph-css/src/main/jjtree/ParserCSS30.jjt b/ph-css/src/main/jjtree/ParserCSS30.jjt index 9e308004..ab6e9ea0 100644 --- a/ph-css/src/main/jjtree/ParserCSS30.jjt +++ b/ph-css/src/main/jjtree/ParserCSS30.jjt @@ -302,6 +302,7 @@ TOKEN : | "-" "-calc(" > | < FUNCTION_NOT: ":not(" > | < FUNCTION_HOST: "host(" > +| < FUNCTION_HOSTCONTEXT: "host-context(" > | < FUNCTION_SLOTTED: "slotted(" > | < FUNCTION_NTH: "nth-child(" | "nth-last-child(" @@ -1020,6 +1021,14 @@ void pseudoHost () #host : {} )* } +void pseudoHostContext () #hostcontext : {} +{ + ( )* + ( simpleSelectorSequence () + ( )* + )* +} + void pseudoSlotted () #slotted : {} { ( )* @@ -1039,6 +1048,9 @@ void pseudo() : {} | { jjtThis.appendText (token.image); } pseudoHost() // do not append because of expression! + | { jjtThis.appendText (token.image); } + pseudoHostContext() + // do not append because of expression! | { jjtThis.appendText (token.image); } pseudoSlotted() // do not append because of expression! diff --git a/ph-css/src/test/resources/testfiles/css30/good/issue80.css b/ph-css/src/test/resources/testfiles/css30/good/issue80.css new file mode 100644 index 00000000..bbfbe3e5 --- /dev/null +++ b/ph-css/src/test/resources/testfiles/css30/good/issue80.css @@ -0,0 +1,23 @@ +/* Selects a shadow root host, only if it is + a descendant of the selector argument given */ +:host-context(h1) { + font-weight: bold; +} + +:host-context(.main .article) { + font-weight: bold; +} + +/* Changes paragraph text color from black to white when + a .dark-theme class is applied to the document body */ +p { + color: #000; +} + +:host-context(body.dark-theme) p { + color: #fff; +} + +:host-context([dir="rtl"]) { + float: right !important; +}