From: <rb...@us...> - 2018-03-04 09:34:00
|
Revision: 15147 http://sourceforge.net/p/htmlunit/code/15147 Author: rbri Date: 2018-03-04 09:33:53 +0000 (Sun, 04 Mar 2018) Log Message: ----------- index based css handling Modified Paths: -------------- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/css/StyleElement.java trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/DomElement.java trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/css/CSSStyleSheet.java trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/css/ComputedCSSStyleDeclaration.java trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/host/css/CSSImportRuleTest.java Removed Paths: ------------- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/css/SelectorSpecificity.java trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/host/css/SelectorSpecificityTest.java Deleted: trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/css/SelectorSpecificity.java =================================================================== --- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/css/SelectorSpecificity.java 2018-03-04 09:31:50 UTC (rev 15146) +++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/css/SelectorSpecificity.java 2018-03-04 09:33:53 UTC (rev 15147) @@ -1,256 +0,0 @@ -/* - * Copyright (c) 2002-2018 Gargoyle Software Inc. - * - * 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.gargoylesoftware.htmlunit.css; - -import java.io.Serializable; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import com.gargoylesoftware.css.parser.condition.AttributeCondition; -import com.gargoylesoftware.css.parser.condition.BeginHyphenAttributeCondition; -import com.gargoylesoftware.css.parser.condition.Condition; -import com.gargoylesoftware.css.parser.condition.OneOfAttributeCondition; -import com.gargoylesoftware.css.parser.condition.PrefixAttributeCondition; -import com.gargoylesoftware.css.parser.condition.SubstringAttributeCondition; -import com.gargoylesoftware.css.parser.condition.SuffixAttributeCondition; -import com.gargoylesoftware.css.parser.selector.ChildSelector; -import com.gargoylesoftware.css.parser.selector.DescendantSelector; -import com.gargoylesoftware.css.parser.selector.DirectAdjacentSelector; -import com.gargoylesoftware.css.parser.selector.ElementSelector; -import com.gargoylesoftware.css.parser.selector.GeneralAdjacentSelector; -import com.gargoylesoftware.css.parser.selector.PseudoElementSelector; -import com.gargoylesoftware.css.parser.selector.Selector; - -/** - * <span style="color:red">INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.</span><br> - * - * Calculates a selector's specificity. - * @see <a href="http://www.w3.org/TR/CSS21/cascade.html#specificity">W3C CSS21</a> - * - * @author Marc Guillemot - * @author Ronald Brill - */ -public class SelectorSpecificity implements Comparable<SelectorSpecificity>, Serializable { - private static final Log LOG = LogFactory.getLog(SelectorSpecificity.class); - - /** - * The specificity for declarations made in the style attributes of an element. - */ - public static final SelectorSpecificity FROM_STYLE_ATTRIBUTE = new SelectorSpecificity(1, 0, 0, 0); - /** - * The specificity for browser defaults. - */ - public static final SelectorSpecificity DEFAULT_STYLE_ATTRIBUTE = new SelectorSpecificity(0, 0, 0, 0); - - private int fieldA_; - private int fieldB_; - private int fieldC_; - private int fieldD_; - - /** - * Ctor. - * @param selector the selector to read from - */ - public SelectorSpecificity(final Selector selector) { - readSelectorSpecificity(selector); - } - - private SelectorSpecificity(final int a, final int b, final int c, final int d) { - fieldA_ = a; - fieldB_ = b; - fieldC_ = c; - fieldD_ = d; - } - - private void readSelectorSpecificity(final Selector selector) { - switch (selector.getSelectorType()) { - case DESCENDANT_SELECTOR: - final DescendantSelector ds = (DescendantSelector) selector; - readSelectorSpecificity(ds.getAncestorSelector()); - readSelectorSpecificity(ds.getSimpleSelector()); - return; - case CHILD_SELECTOR: - final ChildSelector cs = (ChildSelector) selector; - readSelectorSpecificity(cs.getAncestorSelector()); - readSelectorSpecificity(cs.getSimpleSelector()); - return; - case ELEMENT_NODE_SELECTOR: - final ElementSelector es = (ElementSelector) selector; - if (es.getLocalName() != null) { - fieldD_++; - } - if (es.getConditions() != null) { - for (Condition condition : es.getConditions()) { - readSelectorSpecificity(condition); - } - } - return; - case PSEUDO_ELEMENT_SELECTOR: - final PseudoElementSelector pes = (PseudoElementSelector) selector; - final String pesName = pes.getLocalName(); - if (pesName != null) { - fieldD_++; - } - return; - case DIRECT_ADJACENT_SELECTOR: - final DirectAdjacentSelector das = (DirectAdjacentSelector) selector; - readSelectorSpecificity(das.getSelector()); - readSelectorSpecificity(das.getSimpleSelector()); - return; - case GENERAL_ADJACENT_SELECTOR: - final GeneralAdjacentSelector gas = (GeneralAdjacentSelector) selector; - readSelectorSpecificity(gas.getSelector()); - readSelectorSpecificity(gas.getSimpleSelector()); - return; - default: - LOG.warn("Unhandled CSS selector type for specificity computation: '" - + selector.getSelectorType() + "'."); - return; - } - } - - private void readSelectorSpecificity(final Condition condition) { - switch (condition.getConditionType()) { - case ID_CONDITION: - fieldB_++; - return; - case CLASS_CONDITION: - fieldC_++; - return; - case ATTRIBUTE_CONDITION: - if ("id".equalsIgnoreCase(((AttributeCondition) condition).getLocalName())) { - fieldB_++; - } - else { - fieldC_++; - } - return; - case SUBSTRING_ATTRIBUTE_CONDITION: - if ("id".equalsIgnoreCase(((SubstringAttributeCondition) condition).getLocalName())) { - fieldB_++; - } - else { - fieldC_++; - } - return; - case SUFFIX_ATTRIBUTE_CONDITION: - if ("id".equalsIgnoreCase(((SuffixAttributeCondition) condition).getLocalName())) { - fieldB_++; - } - else { - fieldC_++; - } - return; - case PREFIX_ATTRIBUTE_CONDITION: - if ("id".equalsIgnoreCase(((PrefixAttributeCondition) condition).getLocalName())) { - fieldB_++; - } - else { - fieldC_++; - } - return; - case BEGIN_HYPHEN_ATTRIBUTE_CONDITION: - if ("id".equalsIgnoreCase(((BeginHyphenAttributeCondition) condition).getLocalName())) { - fieldB_++; - } - else { - fieldC_++; - } - return; - case ONE_OF_ATTRIBUTE_CONDITION: - if ("id".equalsIgnoreCase(((OneOfAttributeCondition) condition).getLocalName())) { - fieldB_++; - } - else { - fieldC_++; - } - return; - case PSEUDO_CLASS_CONDITION: - fieldD_++; - return; - default: - LOG.warn("Unhandled CSS condition type for specifity computation: '" - + condition.getConditionType() + "'."); - return; - } - } - - /** - * {@inheritDoc} - */ - @Override - public String toString() { - return fieldA_ + "," + fieldB_ + "," + fieldC_ + "," + fieldD_; - } - - /** - * {@inheritDoc} - */ - @Override - public int compareTo(final SelectorSpecificity other) { - if (fieldA_ != other.fieldA_) { - return fieldA_ - other.fieldA_; - } - else if (fieldB_ != other.fieldB_) { - return fieldB_ - other.fieldB_; - } - else if (fieldC_ != other.fieldC_) { - return fieldC_ - other.fieldC_; - } - else if (fieldD_ != other.fieldD_) { - return fieldD_ - other.fieldD_; - } - return 0; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + fieldA_; - result = prime * result + fieldB_; - result = prime * result + fieldC_; - result = prime * result + fieldD_; - return result; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final SelectorSpecificity other = (SelectorSpecificity) obj; - if (fieldA_ != other.fieldA_) { - return false; - } - if (fieldB_ != other.fieldB_) { - return false; - } - if (fieldC_ != other.fieldC_) { - return false; - } - if (fieldD_ != other.fieldD_) { - return false; - } - return true; - } -} Modified: trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/css/StyleElement.java =================================================================== --- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/css/StyleElement.java 2018-03-04 09:31:50 UTC (rev 15146) +++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/css/StyleElement.java 2018-03-04 09:33:53 UTC (rev 15147) @@ -16,6 +16,8 @@ import java.io.Serializable; +import com.gargoylesoftware.css.parser.selector.SelectorSpecificity; + /** * <span style="color:red">INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.</span><br> * Modified: trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/DomElement.java =================================================================== --- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/DomElement.java 2018-03-04 09:31:50 UTC (rev 15146) +++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/DomElement.java 2018-03-04 09:33:53 UTC (rev 15147) @@ -46,12 +46,12 @@ import com.gargoylesoftware.css.parser.CSSException; import com.gargoylesoftware.css.parser.selector.Selector; import com.gargoylesoftware.css.parser.selector.SelectorList; +import com.gargoylesoftware.css.parser.selector.SelectorSpecificity; import com.gargoylesoftware.htmlunit.BrowserVersion; import com.gargoylesoftware.htmlunit.Page; import com.gargoylesoftware.htmlunit.ScriptResult; import com.gargoylesoftware.htmlunit.SgmlPage; import com.gargoylesoftware.htmlunit.WebClient; -import com.gargoylesoftware.htmlunit.css.SelectorSpecificity; import com.gargoylesoftware.htmlunit.css.StyleElement; import com.gargoylesoftware.htmlunit.javascript.AbstractJavaScriptEngine; import com.gargoylesoftware.htmlunit.javascript.JavaScriptEngine; Modified: trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/css/CSSStyleSheet.java =================================================================== --- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/css/CSSStyleSheet.java 2018-03-04 09:31:50 UTC (rev 15146) +++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/css/CSSStyleSheet.java 2018-03-04 09:33:53 UTC (rev 15147) @@ -238,22 +238,29 @@ */ public void modifyIfNecessary(final ComputedCSSStyleDeclaration style, final Element element, final String pseudoElement) { - final CSSRuleList rules = getWrappedSheet().getCssRules(); - modifyIfNecessary(style, element, pseudoElement, rules, new HashSet<String>()); +// final CSSRuleList ruleList = getWrappedSheet().getCssRules(); +// modifyIfNecessary(style, element, pseudoElement, ruleList, new HashSet<String>()); + + final BrowserVersion browser = getBrowserVersion(); + final DomElement e = element.getDomNodeOrDie(); + final List<CSSStyleSheetImpl.SelectorEntry> matchingRules = + selects(getRuleIndex(), this, browser, e, pseudoElement, false); + for (CSSStyleSheetImpl.SelectorEntry entry : matchingRules) { + final org.w3c.dom.css.CSSStyleDeclaration dec = entry.getRule().getStyle(); + style.applyStyleFromSelector(dec, entry.getSelector()); + } } private void modifyIfNecessary(final ComputedCSSStyleDeclaration style, final Element element, - final String pseudoElement, final CSSRuleList rules, final Set<String> alreadyProcessing) { - if (rules == null) { + final String pseudoElement, final CSSRuleList ruleList, final Set<String> alreadyProcessing) { + if (ruleList == null) { return; } final BrowserVersion browser = getBrowserVersion(); final DomElement e = element.getDomNodeOrDie(); - final int rulesLength = rules.getLength(); - for (int i = 0; i < rulesLength; i++) { - final CSSRule rule = rules.item(i); - + final List<org.w3c.dom.css.CSSRule> rules = ((CSSRuleListImpl) ruleList).getRules(); + for (CSSRule rule : rules) { final short ruleType = rule.getType(); if (CSSRule.STYLE_RULE == ruleType) { final CSSStyleRuleImpl styleRule = (CSSStyleRuleImpl) rule; @@ -281,9 +288,9 @@ } if (!alreadyProcessing.contains(sheet.getUri())) { - final CSSRuleList sheetRules = sheet.getWrappedSheet().getCssRules(); + final CSSRuleList sheetRuleList = sheet.getWrappedSheet().getCssRules(); alreadyProcessing.add(getUri()); - sheet.modifyIfNecessary(style, element, pseudoElement, sheetRules, alreadyProcessing); + sheet.modifyIfNecessary(style, element, pseudoElement, sheetRuleList, alreadyProcessing); } } } @@ -308,7 +315,7 @@ public static CSSStyleSheet loadStylesheet(final HTMLElement element, final HtmlLink link, final String url) { CSSStyleSheet sheet; final HtmlPage page = (HtmlPage) element.getDomNodeOrDie().getPage(); - String uri = page.getUrl().toExternalForm(); // fallback uri for exceptions + String uri = page.getUrl().toExternalForm(); try { // Retrieve the associated content and respect client settings regarding failing HTTP status codes. final WebRequest request; @@ -335,8 +342,7 @@ // Use href. final String accept = client.getBrowserVersion().getCssAcceptHeader(); request = new WebRequest(new URL(url), accept); - final String referer = page.getUrl().toExternalForm(); - request.setAdditionalHeader(HttpHeader.REFERER, referer); + request.setAdditionalHeader(HttpHeader.REFERER, uri); // our cache is a bit strange; // loadWebResponse check the cache for the web response @@ -1113,11 +1119,10 @@ cssRules_.clearRules(); cssRulesIndexFix_.clear(); - final CSSRuleList ruleList = getWrappedSheet().getCssRules(); - final List<org.w3c.dom.css.CSSRule> rules = ((CSSRuleListImpl) ruleList).getRules(); + final CSSRuleListImpl ruleList = (CSSRuleListImpl) getWrappedSheet().getCssRules(); + final List<org.w3c.dom.css.CSSRule> rules = ruleList.getRules(); int pos = 0; - for (Iterator<CSSRule> it = rules.iterator(); it.hasNext();) { - final org.w3c.dom.css.CSSRule rule = it.next(); + for (CSSRule rule : rules) { if (rule instanceof org.w3c.dom.css.CSSCharsetRule) { cssRulesIndexFix_.add(pos); continue; @@ -1133,6 +1138,9 @@ } pos++; } + + // reset our index also + ((CSSStyleSheetImpl) getWrappedSheet()).resetRuleIndex(); } private int fixIndex(int index) { @@ -1551,4 +1559,104 @@ refreshCssRules(); } } + + private CSSStyleSheetImpl.CSSStyleSheetRuleIndex getRuleIndex() { + final CSSStyleSheetImpl styleSheet = (CSSStyleSheetImpl) getWrappedSheet(); + CSSStyleSheetImpl.CSSStyleSheetRuleIndex index = styleSheet.getRuleIndex(); + + if (index == null) { + index = new CSSStyleSheetImpl.CSSStyleSheetRuleIndex(); + final CSSRuleListImpl ruleList = (CSSRuleListImpl) styleSheet.getCssRules(); + index(index, ruleList, new HashSet<String>()); + + styleSheet.setRuleIndex(index); + } + return index; + } + + private void index(final CSSStyleSheetImpl.CSSStyleSheetRuleIndex index, final CSSRuleListImpl ruleList, + final Set<String> alreadyProcessing) { + + for (CSSRule rule : ruleList.getRules()) { + final short ruleType = rule.getType(); + if (CSSRule.STYLE_RULE == ruleType) { + final CSSStyleRuleImpl styleRule = (CSSStyleRuleImpl) rule; + final SelectorList selectors = styleRule.getSelectors(); + for (Selector selector : selectors) { + final SimpleSelector simpleSel = selector.getSimpleSelector(); + if (SelectorType.ELEMENT_NODE_SELECTOR == simpleSel.getSelectorType()) { + final ElementSelector es = (ElementSelector) simpleSel; + index.addElementSelector(es.getElementName(), selector, styleRule); + } + else { + index.addOtherSelector(selector, styleRule); + } + } + } + else if (CSSRule.IMPORT_RULE == ruleType) { + final CSSImportRuleImpl importRule = (CSSImportRuleImpl) rule; + final MediaList mediaList = importRule.getMedia(); + + CSSStyleSheet sheet = imports_.get(importRule); + if (sheet == null) { + final String href = importRule.getHref(); + final String url = UrlUtils.resolveUrl(getUri(), href); + sheet = loadStylesheet(ownerNode_, null, url); + imports_.put(importRule, sheet); + } + + if (!alreadyProcessing.contains(sheet.getUri())) { + final CSSRuleList sheetRuleList = sheet.getWrappedSheet().getCssRules(); + alreadyProcessing.add(sheet.getUri()); + + if (mediaList.getLength() == 0 && index.getMediaList().getLength() == 0) { + index(index, (CSSRuleListImpl) sheetRuleList, alreadyProcessing); + } + else { + index(index.addMedia(mediaList), (CSSRuleListImpl) sheetRuleList, alreadyProcessing); + } + } + } + else if (CSSRule.MEDIA_RULE == ruleType) { + final CSSMediaRuleImpl mediaRule = (CSSMediaRuleImpl) rule; + final MediaList mediaList = mediaRule.getMedia(); + if (mediaList.getLength() == 0 && index.getMediaList().getLength() == 0) { + index(index, (CSSRuleListImpl) mediaRule.getCssRules(), alreadyProcessing); + } + else { + index(index.addMedia(mediaList), (CSSRuleListImpl) mediaRule.getCssRules(), alreadyProcessing); + } + } + } + } + + private List<CSSStyleSheetImpl.SelectorEntry> selects( + final CSSStyleSheetImpl.CSSStyleSheetRuleIndex index, + final SimpleScriptable scriptable, + final BrowserVersion browserVersion, final DomElement element, + final String pseudoElement, final boolean fromQuerySelectorAll) { + + final List<CSSStyleSheetImpl.SelectorEntry> matchingRules = new ArrayList<>(); + + if (CSSStyleSheet.isActive(scriptable, index.getMediaList())) { + final String elementName = element.getLocalName(); + final Iterator<CSSStyleSheetImpl.SelectorEntry> iter = index.getSelectorEntriesIteratorFor(elementName); + + CSSStyleSheetImpl.SelectorEntry entry = iter.next(); + while (null != entry) { + if (CSSStyleSheet.selects(browserVersion, entry.getSelector(), + element, pseudoElement, fromQuerySelectorAll)) { + matchingRules.add(entry); + } + entry = iter.next(); + } + + for (CSSStyleSheetImpl.CSSStyleSheetRuleIndex child : index.getChildren()) { + matchingRules.addAll(selects(child, scriptable, browserVersion, + element, pseudoElement, fromQuerySelectorAll)); + } + } + + return matchingRules; + } } Modified: trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/css/ComputedCSSStyleDeclaration.java =================================================================== --- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/css/ComputedCSSStyleDeclaration.java 2018-03-04 09:31:50 UTC (rev 15146) +++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/css/ComputedCSSStyleDeclaration.java 2018-03-04 09:33:53 UTC (rev 15147) @@ -95,9 +95,9 @@ import org.apache.commons.lang3.StringUtils; import com.gargoylesoftware.css.parser.selector.Selector; +import com.gargoylesoftware.css.parser.selector.SelectorSpecificity; import com.gargoylesoftware.htmlunit.BrowserVersion; import com.gargoylesoftware.htmlunit.Page; -import com.gargoylesoftware.htmlunit.css.SelectorSpecificity; import com.gargoylesoftware.htmlunit.css.StyleElement; import com.gargoylesoftware.htmlunit.html.BaseFrameElement; import com.gargoylesoftware.htmlunit.html.DomElement; @@ -261,7 +261,7 @@ */ public void applyStyleFromSelector(final org.w3c.dom.css.CSSStyleDeclaration declaration, final Selector selector) { final BrowserVersion browserVersion = getBrowserVersion(); - final SelectorSpecificity specificity = new SelectorSpecificity(selector); + final SelectorSpecificity specificity = selector.getSelectorSpecificity(); for (int i = 0; i < declaration.getLength(); i++) { final String name = declaration.item(i); if (!browserVersion.hasFeature(CSS_COMPUTED_NO_Z_INDEX) || !"z-index".equals(name)) { Modified: trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/host/css/CSSImportRuleTest.java =================================================================== --- trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/host/css/CSSImportRuleTest.java 2018-03-04 09:31:50 UTC (rev 15146) +++ trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/host/css/CSSImportRuleTest.java 2018-03-04 09:33:53 UTC (rev 15147) @@ -254,6 +254,6 @@ getMockWebConnection().setResponse(URL_SECOND, screenCss, "text/css"); getMockWebConnection().setResponse(URL_THIRD, printCss, "text/css"); - loadPageWithAlerts2(html); + loadPageWithAlerts2(html, 70000); } } Deleted: trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/host/css/SelectorSpecificityTest.java =================================================================== --- trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/host/css/SelectorSpecificityTest.java 2018-03-04 09:31:50 UTC (rev 15146) +++ trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/host/css/SelectorSpecificityTest.java 2018-03-04 09:33:53 UTC (rev 15147) @@ -1,110 +0,0 @@ -/* - * Copyright (c) 2002-2018 Gargoyle Software Inc. - * - * 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.gargoylesoftware.htmlunit.javascript.host.css; - -import java.io.StringReader; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import com.gargoylesoftware.css.parser.InputSource; -import com.gargoylesoftware.css.parser.selector.Selector; -import com.gargoylesoftware.htmlunit.BrowserRunner; -import com.gargoylesoftware.htmlunit.SimpleWebTestCase; -import com.gargoylesoftware.htmlunit.css.SelectorSpecificity; -import com.gargoylesoftware.htmlunit.html.HtmlPage; -import com.gargoylesoftware.htmlunit.html.HtmlStyle; -import com.gargoylesoftware.htmlunit.javascript.host.html.HTMLStyleElement; - -/** - * Tests for {@link SelectorSpecificity}. - * - * @author Marc Guillemot - */ -@RunWith(BrowserRunner.class) -public class SelectorSpecificityTest extends SimpleWebTestCase { - - /** - * @throws Exception if the test fails - */ - @Test - public void selectorSpecifity() throws Exception { - final SelectorSpecificity specificy0 = selectorSpecifity("*", "0,0,0,0"); - final SelectorSpecificity specificy1 = selectorSpecifity("li", "0,0,0,1"); - final SelectorSpecificity specificy2a = selectorSpecifity("li:first-line", "0,0,0,2"); - final SelectorSpecificity specificy2b = selectorSpecifity("ul li", "0,0,0,2"); - final SelectorSpecificity specificy2c = selectorSpecifity("body > p", "0,0,0,2"); - final SelectorSpecificity specificy3 = selectorSpecifity("ul ol+li", "0,0,0,3"); - final SelectorSpecificity specificy11 = selectorSpecifity("h1 + *[rel=up]", "0,0,1,1"); - final SelectorSpecificity specificy13 = selectorSpecifity("ul ol li.red", "0,0,1,3"); - final SelectorSpecificity specificy21 = selectorSpecifity("li.red.level", "0,0,2,1"); - final SelectorSpecificity specificy100 = selectorSpecifity("#x34y", "0,1,0,0"); - - selectorSpecifity("test#x34y", "0,1,0,1"); - - assertEquals(0, specificy0.compareTo(specificy0)); - assertTrue(specificy0.compareTo(specificy1) < 0); - assertTrue(specificy0.compareTo(specificy2a) < 0); - assertTrue(specificy0.compareTo(specificy13) < 0); - - assertEquals(0, specificy1.compareTo(specificy1)); - assertTrue(specificy1.compareTo(specificy0) > 0); - assertTrue(specificy1.compareTo(specificy2a) < 0); - assertTrue(specificy1.compareTo(specificy13) < 0); - - assertEquals(0, specificy2a.compareTo(specificy2b)); - assertEquals(0, specificy2a.compareTo(specificy2c)); - assertTrue(specificy2a.compareTo(specificy0) > 0); - assertTrue(specificy2a.compareTo(specificy3) < 0); - assertTrue(specificy2a.compareTo(specificy11) < 0); - assertTrue(specificy2a.compareTo(specificy13) < 0); - assertTrue(specificy2a.compareTo(specificy100) < 0); - - assertEquals(0, specificy11.compareTo(specificy11)); - assertTrue(specificy11.compareTo(specificy0) > 0); - assertTrue(specificy11.compareTo(specificy13) < 0); - assertTrue(specificy11.compareTo(specificy21) < 0); - assertTrue(specificy11.compareTo(specificy100) < 0); - } - - /** - * @throws Exception if the test fails - */ - @Test - public void selectorSpecifitySiblingCombinator() throws Exception { - selectorSpecifity(".cls ~ p", "0,0,1,1"); - } - - private SelectorSpecificity selectorSpecifity(final String selector, final String expectedSpecificity) - throws Exception { - final String html = - "<html><body id='b'><style></style>\n" - + "<div id='d' class='foo bar'><span>x</span><span id='s'>a</span>b</div>\n" - + "</body></html>"; - final HtmlPage page = loadPage(html); - final HtmlStyle node = (HtmlStyle) page.getElementsByTagName("style").item(0); - final HTMLStyleElement host = (HTMLStyleElement) node.getScriptableObject(); - final CSSStyleSheet sheet = host.getSheet(); - - final Selector selectorObject = parseSelector(sheet, selector); - final SelectorSpecificity specificity = new SelectorSpecificity(selectorObject); - assertEquals(expectedSpecificity, specificity.toString()); - return specificity; - } - - private static Selector parseSelector(final CSSStyleSheet sheet, final String rule) { - return sheet.parseSelectors(new InputSource(new StringReader(rule))).get(0); - } -} |