From: <asa...@us...> - 2017-06-01 07:28:58
|
Revision: 14520 http://sourceforge.net/p/htmlunit/code/14520 Author: asashour Date: 2017-06-01 07:28:54 +0000 (Thu, 01 Jun 2017) Log Message: ----------- JavaScript: implement Element.matches() Modified Paths: -------------- trunk/htmlunit/src/changes/changes.xml trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/DomElement.java trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/DomNode.java trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/configuration/ClassConfiguration.java trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/Element.java trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/html/HTMLTextAreaElement.java trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/host/ElementTest.java Modified: trunk/htmlunit/src/changes/changes.xml =================================================================== --- trunk/htmlunit/src/changes/changes.xml 2017-05-31 21:12:01 UTC (rev 14519) +++ trunk/htmlunit/src/changes/changes.xml 2017-06-01 07:28:54 UTC (rev 14520) @@ -9,6 +9,9 @@ <body> <release version="2.27" date="???" description="GAE broken, FF52, Bugfixes"> <action type="add" dev="asashour"> + JavaScript: implement Element.matches(). + </action> + <action type="add" dev="asashour"> JavaScript: implement .before(), .after() and .replaceWith(). </action> <action type="fix" dev="rbri" issue="1827"> Modified: trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/DomElement.java =================================================================== --- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/DomElement.java 2017-05-31 21:12:01 UTC (rev 14519) +++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/DomElement.java 2017-06-01 07:28:54 UTC (rev 14520) @@ -36,6 +36,9 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.w3c.css.sac.CSSException; +import org.w3c.css.sac.Selector; +import org.w3c.css.sac.SelectorList; import org.w3c.dom.Attr; import org.w3c.dom.DOMException; import org.w3c.dom.Element; @@ -43,6 +46,7 @@ import org.w3c.dom.Node; import org.w3c.dom.TypeInfo; +import com.gargoylesoftware.htmlunit.BrowserVersion; import com.gargoylesoftware.htmlunit.Page; import com.gargoylesoftware.htmlunit.ScriptResult; import com.gargoylesoftware.htmlunit.SgmlPage; @@ -54,6 +58,7 @@ import com.gargoylesoftware.htmlunit.javascript.NashornJavaScriptEngine; import com.gargoylesoftware.htmlunit.javascript.SimpleScriptObject; import com.gargoylesoftware.htmlunit.javascript.SimpleScriptable; +import com.gargoylesoftware.htmlunit.javascript.host.css.CSSStyleSheet; import com.gargoylesoftware.htmlunit.javascript.host.event.Event; import com.gargoylesoftware.htmlunit.javascript.host.event.Event2; import com.gargoylesoftware.htmlunit.javascript.host.event.EventTarget; @@ -1599,6 +1604,30 @@ return false; } + /** + * Returns true if the element would be selected by the specified selector string; otherwise, returns false. + * @param selectorString the selector to test + * @return true if the element would be selected by the specified selector string; otherwise, returns false. + */ + public boolean matches(final String selectorString) { + try { + final BrowserVersion browserVersion = getPage().getWebClient().getBrowserVersion(); + final SelectorList selectorList = getSelectorList(selectorString, browserVersion); + + if (selectorList != null) { + for (int i = 0; i < selectorList.getLength(); i++) { + final Selector selector = selectorList.item(i); + if (CSSStyleSheet.selects(browserVersion, selector, this, null, true)) { + return true; + } + } + } + return false; + } + catch (final IOException e) { + throw new CSSException("Error parsing CSS selectors from '" + selectorString + "': " + e.getMessage()); + } + } } /** @@ -1830,4 +1859,5 @@ public Collection<DomAttr> values() { return map_.values(); } + } Modified: trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/DomNode.java =================================================================== --- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/DomNode.java 2017-05-31 21:12:01 UTC (rev 14519) +++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/DomNode.java 2017-06-01 07:28:54 UTC (rev 14520) @@ -1898,29 +1898,11 @@ */ public DomNodeList<DomNode> querySelectorAll(final String selectors) { try { - final WebClient webClient = getPage().getWebClient(); - final CSSOMParser parser = new CSSOMParser(new SACParserCSS3()); - final CheckErrorHandler errorHandler = new CheckErrorHandler(); - parser.setErrorHandler(errorHandler); + final BrowserVersion browserVersion = getPage().getWebClient().getBrowserVersion(); + final SelectorList selectorList = getSelectorList(selectors, browserVersion); - final SelectorList selectorList = parser.parseSelectors(new InputSource(new StringReader(selectors))); - // in case of error parseSelectors returns null - if (errorHandler.errorDetected()) { - throw new CSSException("Invalid selectors: " + selectors); - } - final List<DomNode> elements = new ArrayList<>(); if (selectorList != null) { - final BrowserVersion browserVersion = webClient.getBrowserVersion(); - int documentMode = 9; - if (browserVersion.hasFeature(QUERYSELECTORALL_NOT_IN_QUIRKS)) { - final Object sobj = getPage().getScriptableObject(); - if (sobj instanceof HTMLDocument) { - documentMode = ((HTMLDocument) sobj).getDocumentMode(); - } - } - CSSStyleSheet.validateSelectors(selectorList, documentMode, this); - for (final DomElement child : getDomElementDescendants()) { for (int i = 0; i < selectorList.getLength(); i++) { final Selector selector = selectorList.item(i); @@ -1939,6 +1921,39 @@ } /** + * Returns the {@link SelectorList}. + * @param selectors the selectors + * @param browserVersion the {@link BrowserVersion} + * @return the {@link SelectorList} + * @throws IOException if an error occurs + */ + protected SelectorList getSelectorList(final String selectors, final BrowserVersion browserVersion) + throws IOException { + final CSSOMParser parser = new CSSOMParser(new SACParserCSS3()); + final CheckErrorHandler errorHandler = new CheckErrorHandler(); + parser.setErrorHandler(errorHandler); + + final SelectorList selectorList = parser.parseSelectors(new InputSource(new StringReader(selectors))); + // in case of error parseSelectors returns null + if (errorHandler.errorDetected()) { + throw new CSSException("Invalid selectors: " + selectors); + } + + if (selectorList != null) { + int documentMode = 9; + if (browserVersion.hasFeature(QUERYSELECTORALL_NOT_IN_QUIRKS)) { + final Object sobj = getPage().getScriptableObject(); + if (sobj instanceof HTMLDocument) { + documentMode = ((HTMLDocument) sobj).getDocumentMode(); + } + } + CSSStyleSheet.validateSelectors(selectorList, documentMode, this); + + } + return selectorList; + } + + /** * Returns the first element within the document that matches the specified group of selectors. * @param selectors one or more CSS selectors separated by commas * @param <N> the node type Modified: trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/configuration/ClassConfiguration.java =================================================================== --- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/configuration/ClassConfiguration.java 2017-05-31 21:12:01 UTC (rev 14519) +++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/configuration/ClassConfiguration.java 2017-06-01 07:28:54 UTC (rev 14520) @@ -61,6 +61,7 @@ * @param domClasses the DOM classes that this object supports * @param jsObject boolean flag for if this object is a JavaScript object * @param className the class name, can be null + * @param extendedClassName the extended class name */ public ClassConfiguration(final Class<? extends HtmlUnitScriptable> hostClass, final Class<?>[] domClasses, final boolean jsObject, final String className, final String extendedClassName) { Modified: trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/Element.java =================================================================== --- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/Element.java 2017-05-31 21:12:01 UTC (rev 14519) +++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/Element.java 2017-06-01 07:28:54 UTC (rev 14520) @@ -1995,4 +1995,44 @@ final Function function) { Node.replaceWith(context, thisObj, args, function); } + + /** + * Returns true if the element would be selected by the specified selector string; otherwise, returns false. + * @param selectorString the selector to test + * @return the value + */ + @JsxFunction({CHROME, FF}) + public boolean matches(final String selectorString) { + return getDomNodeOrDie().matches(selectorString); + } + + /** + * Returns true if the element would be selected by the specified selector string; otherwise, returns false. + * @param selectorString the selector to test + * @return the value + */ + @JsxFunction(FF) + public boolean mozMatchesSelector(final String selectorString) { + return matches(selectorString); + } + + /** + * Returns true if the element would be selected by the specified selector string; otherwise, returns false. + * @param selectorString the selector to test + * @return the value + */ + @JsxFunction({CHROME, FF}) + public boolean webkitMatchesSelector(final String selectorString) { + return matches(selectorString); + } + + /** + * Returns true if the element would be selected by the specified selector string; otherwise, returns false. + * @param selectorString the selector to test + * @return the value + */ + @JsxFunction(IE) + public boolean msMatchesSelector(final String selectorString) { + return matches(selectorString); + } } Modified: trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/html/HTMLTextAreaElement.java =================================================================== --- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/html/HTMLTextAreaElement.java 2017-05-31 21:12:01 UTC (rev 14519) +++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/html/HTMLTextAreaElement.java 2017-06-01 07:28:54 UTC (rev 14520) @@ -341,7 +341,7 @@ try { final int i = Integer.parseInt(minLength); - if (i < 0 ) { + if (i < 0) { throw Context.throwAsScriptRuntimeEx( new NumberFormatException("New value for minLength '" + minLength + "' is smaller than zero.")); } Modified: trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/host/ElementTest.java =================================================================== --- trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/host/ElementTest.java 2017-05-31 21:12:01 UTC (rev 14519) +++ trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/host/ElementTest.java 2017-06-01 07:28:54 UTC (rev 14520) @@ -1265,4 +1265,39 @@ loadPageWithAlerts2(html); } + /** + * @throws Exception if an error occurs + */ + @Test + @Alerts(DEFAULT = "Philippine eagle", + IE = "not found") + public void matches() throws Exception { + final String html = HtmlPageTest.STANDARDS_MODE_PREFIX_ + + "<html><head>\n" + + "<script>\n" + + " function test() {\n" + + " var birds = document.getElementsByTagName('li');\n" + + " var found = false;" + + " for (var i = 0; i < birds.length; i++) {\n" + + " if (birds[i].matches && birds[i].matches('.endangered')) {\n" + + " alert(birds[i].textContent);\n" + + " found = true;" + + " }\n" + + " }\n" + + " if (!found) {\n" + + " alert('not found');\n" + + " }\n" + + " }\n" + + "</script>\n" + + "</head>\n" + + "<body onload='test()'>\n" + + " <ul id='birds'>\n" + + " <li>Orange-winged parrot</li>\n" + + " <li class='endangered'>Philippine eagle</li>\n" + + " <li>Great white pelican</li>\n" + + " </ul>\n" + + "</body></html>"; + + loadPageWithAlerts2(html); + } } |