From: <mgu...@us...> - 2012-06-11 09:21:49
|
Revision: 6942 http://htmlunit.svn.sourceforge.net/htmlunit/?rev=6942&view=rev Author: mguillem Date: 2012-06-11 09:21:38 +0000 (Mon, 11 Jun 2012) Log Message: ----------- JavaScript: querySelector(All) throws for invalid selectors Modified Paths: -------------- trunk/htmlunit/src/changes/changes.xml trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/DomNode.java trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/css/CSSStyleSheet.java trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/html/HTMLDocument.java trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/html/HTMLElement.java trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/host/html/HTMLDocumentTest.java trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/host/html/HTMLElement2Test.java Modified: trunk/htmlunit/src/changes/changes.xml =================================================================== --- trunk/htmlunit/src/changes/changes.xml 2012-06-09 18:29:10 UTC (rev 6941) +++ trunk/htmlunit/src/changes/changes.xml 2012-06-11 09:21:38 UTC (rev 6942) @@ -7,6 +7,9 @@ <body> <release version="2.10" date="???" description="Bugfixes, SSL client certificate"> <action type="fix" dev="mguillem"> + JavaScript: querySelector(All) throws for invalid selectors. + </action> + <action type="fix" dev="mguillem"> Java 7: fix SSLPeerUnverifiedException for valid certificates when WebClient.setUseInsecureSSL(true) is used. </action> <action type="update" dev="rbri"> Modified: trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/DomNode.java =================================================================== --- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/DomNode.java 2012-06-09 18:29:10 UTC (rev 6941) +++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/DomNode.java 2012-06-11 09:21:38 UTC (rev 6942) @@ -14,6 +14,7 @@ */ package com.gargoylesoftware.htmlunit.html; +import java.io.IOException; import java.io.PrintWriter; import java.io.Serializable; import java.io.StringReader; @@ -29,6 +30,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.w3c.css.sac.CSSException; import org.w3c.css.sac.ErrorHandler; import org.w3c.css.sac.InputSource; import org.w3c.css.sac.Selector; @@ -1524,6 +1526,8 @@ final SelectorList selectorList = parser.parseSelectors(new InputSource(new StringReader(selectors))); // in case of error parseSelectors returns null if (null != selectorList) { + CSSStyleSheet.validateSelectors(selectorList); + final BrowserVersion browserVersion = webClient.getBrowserVersion(); for (final HtmlElement child : getHtmlElementDescendants()) { for (int i = 0; i < selectorList.getLength(); i++) { @@ -1535,8 +1539,8 @@ } } } - catch (final Exception e) { - LOG.error("Error parsing CSS selectors from '" + selectors + "': " + e.getMessage(), e); + catch (final IOException e) { + throw new CSSException("Error parsing CSS selectors from '" + selectors + "': " + e.getMessage()); } return new StaticDomNodeList(elements); } 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 2012-06-09 18:29:10 UTC (rev 6941) +++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/css/CSSStyleSheet.java 2012-06-11 09:21:38 UTC (rev 6942) @@ -21,6 +21,8 @@ import java.io.StringReader; import java.net.MalformedURLException; import java.net.URL; +import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -33,6 +35,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.w3c.css.sac.AttributeCondition; +import org.w3c.css.sac.CSSException; import org.w3c.css.sac.CombinatorCondition; import org.w3c.css.sac.Condition; import org.w3c.css.sac.ConditionalSelector; @@ -81,6 +84,8 @@ import com.steadystate.css.parser.CSSOMParser; import com.steadystate.css.parser.SACParserCSS21; import com.steadystate.css.parser.SelectorListImpl; +import com.steadystate.css.parser.selectors.ChildSelectorImpl; +import com.steadystate.css.parser.selectors.PseudoClassConditionImpl; /** * A JavaScript object for a Stylesheet. @@ -110,6 +115,9 @@ /** This stylesheet's URI (used to resolved contained @import rules). */ private String uri_; + private static final Collection<String> PSEUDO_CLASSES = Arrays.asList("link", "visited", "hover", "active", "focus", + "target", "lang", "disabled", "checked", "indeterminated", "root", "nth-child()"); + /** * Creates a new empty stylesheet. */ @@ -776,4 +784,52 @@ return false; } + /** + * Validates the list of selectors. + * @param selectorList the selectors + * @throws CSSException if a selector is invalid + */ + public static void validateSelectors(final SelectorList selectorList) throws CSSException { + for (int i = 0; i < selectorList.getLength(); ++i) { + final Selector item = selectorList.item(i); + if (!isValidSelector(item)) { + throw new CSSException("Invalid selector: " + item); + } + } + } + + private static boolean isValidSelector(final Selector selector) { + switch (selector.getSelectorType()) { + case Selector.SAC_ELEMENT_NODE_SELECTOR: + return true; + case Selector.SAC_CONDITIONAL_SELECTOR: + final ConditionalSelector conditional = (ConditionalSelector) selector; + return isValidSelector(conditional.getSimpleSelector()) && isValidSelector(conditional.getCondition()); + case Selector.SAC_DESCENDANT_SELECTOR: + case Selector.SAC_CHILD_SELECTOR: + final DescendantSelector ds = (DescendantSelector) selector; + return isValidSelector(ds.getAncestorSelector()) && isValidSelector(ds.getSimpleSelector()); + default: + LOG.warn("Unhandled CSS selector type '" + selector.getSelectorType() + "'. Accepting it silently."); + return true; // at least in a first time to break less stuff + } + } + + private static boolean isValidSelector(final Condition condition) { + switch (condition.getConditionType()) { + case Condition.SAC_AND_CONDITION: + final CombinatorCondition cc1 = (CombinatorCondition) condition; + return isValidSelector(cc1.getFirstCondition()) && isValidSelector(cc1.getSecondCondition()); + case Condition.SAC_ATTRIBUTE_CONDITION: + case Condition.SAC_ID_CONDITION: + case Condition.SAC_CLASS_CONDITION: + return true; + case Condition.SAC_PSEUDO_CLASS_CONDITION: + final PseudoClassConditionImpl pcc = (PseudoClassConditionImpl) condition; + return PSEUDO_CLASSES.contains(pcc.getValue()); + default: + LOG.warn("Unhandled CSS condition type '" + condition.getConditionType() + "'. Accepting it silently."); + return true; + } + } } Modified: trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/html/HTMLDocument.java =================================================================== --- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/html/HTMLDocument.java 2012-06-09 18:29:10 UTC (rev 6941) +++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/html/HTMLDocument.java 2012-06-11 09:21:38 UTC (rev 6942) @@ -44,6 +44,7 @@ import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.w3c.css.sac.CSSException; import org.w3c.dom.DOMException; import com.gargoylesoftware.htmlunit.BrowserVersion; @@ -1654,11 +1655,16 @@ * @return the static node list */ public StaticNodeList jsxFunction_querySelectorAll(final String selectors) { - final List<Node> nodes = new ArrayList<Node>(); - for (final DomNode domNode : getHtmlPage().querySelectorAll(selectors)) { - nodes.add((Node) domNode.getScriptObject()); + try { + final List<Node> nodes = new ArrayList<Node>(); + for (final DomNode domNode : getHtmlPage().querySelectorAll(selectors)) { + nodes.add((Node) domNode.getScriptObject()); + } + return new StaticNodeList(nodes, this); } - return new StaticNodeList(nodes, this); + catch (final CSSException e) { + throw Context.reportRuntimeError("An invalid or illegal string was specified"); + } } /** @@ -1667,11 +1673,16 @@ * @return null if no matches are found; otherwise, it returns the first matching element */ public Node jsxFunction_querySelector(final String selectors) { - final DomNode node = getHtmlPage().querySelector(selectors); - if (node != null) { - return (Node) node.getScriptObject(); + try { + final DomNode node = getHtmlPage().querySelector(selectors); + if (node != null) { + return (Node) node.getScriptObject(); + } + return null; } - return null; + catch (final CSSException e) { + throw Context.reportRuntimeError("An invalid or illegal string was specified"); + } } /** Modified: trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/html/HTMLElement.java =================================================================== --- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/html/HTMLElement.java 2012-06-09 18:29:10 UTC (rev 6941) +++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/html/HTMLElement.java 2012-06-11 09:21:38 UTC (rev 6942) @@ -38,6 +38,7 @@ import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.w3c.css.sac.CSSException; import org.xml.sax.SAXException; import org.xml.sax.helpers.AttributesImpl; @@ -1788,11 +1789,16 @@ * @return the static node list */ public StaticNodeList jsxFunction_querySelectorAll(final String selectors) { - final List<Node> nodes = new ArrayList<Node>(); - for (final DomNode domNode : getDomNodeOrDie().querySelectorAll(selectors)) { - nodes.add((Node) domNode.getScriptObject()); + try { + final List<Node> nodes = new ArrayList<Node>(); + for (final DomNode domNode : getDomNodeOrDie().querySelectorAll(selectors)) { + nodes.add((Node) domNode.getScriptObject()); + } + return new StaticNodeList(nodes, this); } - return new StaticNodeList(nodes, this); + catch (final CSSException e) { + throw Context.reportRuntimeError("An invalid or illegal string was specified"); + } } /** @@ -1801,11 +1807,16 @@ * @return null if no matches are found; otherwise, it returns the first matching element */ public Node jsxFunction_querySelector(final String selectors) { - final DomNode node = getDomNodeOrDie().querySelector(selectors); - if (node != null) { - return (Node) node.getScriptObject(); + try { + final DomNode node = getDomNodeOrDie().querySelector(selectors); + if (node != null) { + return (Node) node.getScriptObject(); + } + return null; } - return null; + catch (final CSSException e) { + throw Context.reportRuntimeError("An invalid or illegal string was specified"); + } } /** Modified: trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/host/html/HTMLDocumentTest.java =================================================================== --- trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/host/html/HTMLDocumentTest.java 2012-06-09 18:29:10 UTC (rev 6941) +++ trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/host/html/HTMLDocumentTest.java 2012-06-11 09:21:38 UTC (rev 6942) @@ -60,6 +60,12 @@ */ @RunWith(BrowserRunner.class) public class HTMLDocumentTest extends WebDriverTestCase { + /** jQuery selectors that aren't CSS selectors. */ + public static final String[] JQUERY_CUSTOM_SELECTORS = {"div.submenu-last:last", + "*#__sizzle__ div.submenu-last:last", "div:animated", "div:animated", "*:button", "*:checkbox", "div:even", + "*:file", "div:first", "td:gt(4)", "div:has(p)", ":header", ":hidden", ":image", ":input", "td:lt(4)", + ":odd", ":password", ":radio", ":reset", ":selected", ":submit", ":text", ":visible" + }; /** * @throws Exception if the test fails @@ -1084,6 +1090,50 @@ * @throws Exception if the test fails */ @Test + @Alerts("exception") + public void querySelectorAll_badSelector() throws Exception { + for (final String selector : JQUERY_CUSTOM_SELECTORS) { + doTestQuerySelectorAll_badSelector(selector); + } + } + + private void doTestQuerySelectorAll_badSelector(final String selector) throws Exception { + final String html = "<html><body><script>\n" + + "try {\n" + + " document.querySelectorAll('" + selector + "');\n" + + " alert('working: ' + selector);\n" + + "} catch(e) { alert('exception'); }\n" + + "</script></body></html>"; + + loadPageWithAlerts2(html); + } + + /** + * @throws Exception if the test fails + */ + @Test + @Alerts("exception") + public void querySelector_badSelector() throws Exception { + for (final String selector : JQUERY_CUSTOM_SELECTORS) { + doTestQuerySelector_badSelector(selector); + } + } + + private void doTestQuerySelector_badSelector(final String selector) throws Exception { + final String html = "<html><body><script>\n" + + "try {\n" + + " document.querySelector('" + selector + "');\n" + + " alert('working: ' + selector);\n" + + "} catch(e) { alert('exception'); }\n" + + "</script></body></html>"; + + loadPageWithAlerts2(html); + } + + /** + * @throws Exception if the test fails + */ + @Test @Alerts(FF3 = "undefined", FF = { "3", "div1" }, IE = "undefined") Modified: trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/host/html/HTMLElement2Test.java =================================================================== --- trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/host/html/HTMLElement2Test.java 2012-06-09 18:29:10 UTC (rev 6941) +++ trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/host/html/HTMLElement2Test.java 2012-06-11 09:21:38 UTC (rev 6942) @@ -925,4 +925,48 @@ loadPageWithAlerts2(html); } + + /** + * @throws Exception if the test fails + */ + @Test + @Alerts("exception") + public void querySelectorAll_badSelector() throws Exception { + for (final String selector : HTMLDocumentTest.JQUERY_CUSTOM_SELECTORS) { + doTestQuerySelectorAll_badSelector(selector); + } + } + + private void doTestQuerySelectorAll_badSelector(final String selector) throws Exception { + final String html = "<html><body><div id='it'></div><script>\n" + + "try {\n" + + " document.getElementById('it').querySelectorAll('" + selector + "');\n" + + " alert('working: ' + selector);\n" + + "} catch(e) { alert('exception'); }\n" + + "</script></body></html>"; + + loadPageWithAlerts2(html); + } + + /** + * @throws Exception if the test fails + */ + @Test + @Alerts("exception") + public void querySelector_badSelector() throws Exception { + for (final String selector : HTMLDocumentTest.JQUERY_CUSTOM_SELECTORS) { + doTestQuerySelector_badSelector(selector); + } + } + + private void doTestQuerySelector_badSelector(final String selector) throws Exception { + final String html = "<html><body><div id='it'></div><script>\n" + + "try {\n" + + " document.getElementById('it').querySelector('" + selector + "');\n" + + " alert('working: ' + selector);\n" + + "} catch(e) { alert('exception'); }\n" + + "</script></body></html>"; + + loadPageWithAlerts2(html); + } } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |