diff --git a/src/main/java/org/htmlunit/WebClient.java b/src/main/java/org/htmlunit/WebClient.java
index 1dce1190e33..6c23e5f5ac5 100644
--- a/src/main/java/org/htmlunit/WebClient.java
+++ b/src/main/java/org/htmlunit/WebClient.java
@@ -27,6 +27,7 @@
import static org.htmlunit.BrowserVersionFeatures.WINDOW_EXECUTE_EVENTS;
import java.io.BufferedInputStream;
+import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@@ -71,12 +72,13 @@
import org.htmlunit.corejs.javascript.ScriptableObject;
import org.htmlunit.css.ComputedCssStyleDeclaration;
import org.htmlunit.cssparser.parser.CSSErrorHandler;
+import org.htmlunit.cssparser.parser.javacc.CSS3Parser;
import org.htmlunit.html.BaseFrameElement;
import org.htmlunit.html.DomElement;
import org.htmlunit.html.DomNode;
import org.htmlunit.html.FrameWindow;
-import org.htmlunit.html.HtmlElement;
import org.htmlunit.html.FrameWindow.PageDenied;
+import org.htmlunit.html.HtmlElement;
import org.htmlunit.html.HtmlInlineFrame;
import org.htmlunit.html.HtmlPage;
import org.htmlunit.html.XHtmlPage;
@@ -204,6 +206,10 @@ public class WebClient implements Serializable, AutoCloseable {
private OnbeforeunloadHandler onbeforeunloadHandler_;
private Cache cache_ = new Cache();
+ // mini pool to save resource when parsing css
+ private transient PooledCSS3Parser firstPooledCSS3Parser_ = new PooledCSS3Parser();
+ private transient PooledCSS3Parser secondPooledCSS3Parser_ = new PooledCSS3Parser();
+
/** target "_blank". */
public static final String TARGET_BLANK = "_blank";
@@ -2944,4 +2950,50 @@ public XHtmlPage loadXHtmlCodeIntoCurrentWindow(final String xhtmlCode) throws I
htmlParser.parse(webResponse, page, true, false);
return page;
}
+
+ /**
+ * INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.
+ *
+ * @return CSS3Parser from pool
+ */
+ public PooledCSS3Parser getCSS3ParserFromPool() {
+ if (!firstPooledCSS3Parser_.inUse_) {
+ if (firstPooledCSS3Parser_.open()) {
+ return firstPooledCSS3Parser_;
+ }
+ }
+ if (!secondPooledCSS3Parser_.inUse_) {
+ if (secondPooledCSS3Parser_.open()) {
+ return secondPooledCSS3Parser_;
+ }
+ }
+
+ return new PooledCSS3Parser();
+ }
+
+ public static class PooledCSS3Parser implements Closeable {
+ private final CSS3Parser css3Parser_;
+ private boolean inUse_;
+
+ public PooledCSS3Parser() {
+ css3Parser_ = new CSS3Parser();
+ }
+
+ public CSS3Parser css3Parser() {
+ return css3Parser_;
+ }
+
+ public synchronized boolean open() {
+ if (inUse_) {
+ return false;
+ }
+ inUse_ = true;
+ return true;
+ }
+
+ @Override
+ public void close() throws IOException {
+ inUse_ = false;
+ }
+ }
}
diff --git a/src/main/java/org/htmlunit/css/CssStyleSheet.java b/src/main/java/org/htmlunit/css/CssStyleSheet.java
index aa90bc88c19..a2051b7732b 100644
--- a/src/main/java/org/htmlunit/css/CssStyleSheet.java
+++ b/src/main/java/org/htmlunit/css/CssStyleSheet.java
@@ -56,6 +56,7 @@
import org.htmlunit.Page;
import org.htmlunit.SgmlPage;
import org.htmlunit.WebClient;
+import org.htmlunit.WebClient.PooledCSS3Parser;
import org.htmlunit.WebRequest;
import org.htmlunit.WebResponse;
import org.htmlunit.WebWindow;
@@ -1011,9 +1012,9 @@ private static boolean getNthElement(final String nth, final int index) {
*/
private static CSSStyleSheetImpl parseCSS(final InputSource source, final WebClient client) {
CSSStyleSheetImpl ss;
- try {
+ try (PooledCSS3Parser pooledParser = client.getCSS3ParserFromPool()) {
final CSSErrorHandler errorHandler = client.getCssErrorHandler();
- final CSSOMParser parser = new CSSOMParser(new CSS3Parser());
+ final CSSOMParser parser = new CSSOMParser(pooledParser.css3Parser());
parser.setErrorHandler(errorHandler);
ss = parser.parseStyleSheet(source, null);
}
@@ -1026,6 +1027,39 @@ private static CSSStyleSheetImpl parseCSS(final InputSource source, final WebCli
return ss;
}
+ /**
+ * Parses the given media string. If anything at all goes wrong, this
+ * method returns an empty MediaList list.
+ *
+ * @param mediaString the source from which to retrieve the media to be parsed
+ * @param webClient the {@link WebClient} to be used
+ * @return the media parsed from the specified input source
+ */
+ public static MediaListImpl parseMedia(final String mediaString, final WebClient webClient) {
+ MediaListImpl media = media_.get(mediaString);
+ if (media != null) {
+ return media;
+ }
+
+ try (PooledCSS3Parser pooledParser = webClient.getCSS3ParserFromPool()) {
+ final CSSOMParser parser = new CSSOMParser(pooledParser.css3Parser());
+ parser.setErrorHandler(webClient.getCssErrorHandler());
+
+ media = new MediaListImpl(parser.parseMedia(mediaString));
+ media_.put(mediaString, media);
+ return media;
+ }
+ catch (final Exception e) {
+ if (LOG.isErrorEnabled()) {
+ LOG.error("Error parsing CSS media from '" + mediaString + "': " + e.getMessage(), e);
+ }
+ }
+
+ media = new MediaListImpl(null);
+ media_.put(mediaString, media);
+ return media;
+ }
+
/**
* Parses the given media string. If anything at all goes wrong, this
* method returns an empty MediaList list.
@@ -1033,7 +1067,10 @@ private static CSSStyleSheetImpl parseCSS(final InputSource source, final WebCli
* @param mediaString the source from which to retrieve the media to be parsed
* @param errorHandler the {@link CSSErrorHandler} to be used
* @return the media parsed from the specified input source
+ *
+ * @deprecated as of version 3.8.0; use {@link #parseMedia(String, WebClient)} instead
*/
+ @Deprecated
public static MediaListImpl parseMedia(final CSSErrorHandler errorHandler, final String mediaString) {
MediaListImpl media = media_.get(mediaString);
if (media != null) {
@@ -1241,7 +1278,7 @@ else if (owner_ instanceof HtmlLink) {
}
final WebWindow webWindow = owner_.getPage().getEnclosingWindow();
- final MediaListImpl mediaList = parseMedia(webWindow.getWebClient().getCssErrorHandler(), media);
+ final MediaListImpl mediaList = parseMedia(media, webWindow.getWebClient());
return isActive(mediaList, webWindow);
}
diff --git a/src/main/java/org/htmlunit/html/DomElement.java b/src/main/java/org/htmlunit/html/DomElement.java
index 9437e80e541..94c4d59ba3f 100644
--- a/src/main/java/org/htmlunit/html/DomElement.java
+++ b/src/main/java/org/htmlunit/html/DomElement.java
@@ -1593,12 +1593,12 @@ public boolean isMouseOver() {
*/
public boolean matches(final String selectorString) {
try {
- final BrowserVersion browserVersion = getPage().getWebClient().getBrowserVersion();
- final SelectorList selectorList = getSelectorList(selectorString, browserVersion);
+ final WebClient webClient = getPage().getWebClient();
+ final SelectorList selectorList = getSelectorList(selectorString, webClient);
if (selectorList != null) {
for (final Selector selector : selectorList) {
- if (CssStyleSheet.selects(browserVersion, selector, this, null, true, true)) {
+ if (CssStyleSheet.selects(webClient.getBrowserVersion(), selector, this, null, true, true)) {
return true;
}
}
diff --git a/src/main/java/org/htmlunit/html/DomNode.java b/src/main/java/org/htmlunit/html/DomNode.java
index 57f579f0651..d8ab0afafd3 100644
--- a/src/main/java/org/htmlunit/html/DomNode.java
+++ b/src/main/java/org/htmlunit/html/DomNode.java
@@ -32,13 +32,13 @@
import java.util.Map;
import java.util.NoSuchElementException;
-import org.htmlunit.BrowserVersion;
import org.htmlunit.BrowserVersionFeatures;
import org.htmlunit.IncorrectnessListener;
import org.htmlunit.Page;
import org.htmlunit.SgmlPage;
import org.htmlunit.WebAssert;
import org.htmlunit.WebClient;
+import org.htmlunit.WebClient.PooledCSS3Parser;
import org.htmlunit.WebWindow;
import org.htmlunit.corejs.javascript.Context;
import org.htmlunit.corejs.javascript.ScriptableObject;
@@ -49,7 +49,6 @@
import org.htmlunit.cssparser.parser.CSSException;
import org.htmlunit.cssparser.parser.CSSOMParser;
import org.htmlunit.cssparser.parser.CSSParseException;
-import org.htmlunit.cssparser.parser.javacc.CSS3Parser;
import org.htmlunit.cssparser.parser.selector.Selector;
import org.htmlunit.cssparser.parser.selector.SelectorList;
import org.htmlunit.html.HtmlElement.DisplayStyle;
@@ -1835,14 +1834,14 @@ private List safeGetCharacterDataListeners() {
*/
public DomNodeList querySelectorAll(final String selectors) {
try {
- final BrowserVersion browserVersion = getPage().getWebClient().getBrowserVersion();
- final SelectorList selectorList = getSelectorList(selectors, browserVersion);
+ final WebClient webClient = getPage().getWebClient();
+ final SelectorList selectorList = getSelectorList(selectors, webClient);
final List elements = new ArrayList<>();
if (selectorList != null) {
for (final DomElement child : getDomElementDescendants()) {
for (final Selector selector : selectorList) {
- if (CssStyleSheet.selects(browserVersion, selector, child, null, true, true)) {
+ if (CssStyleSheet.selects(webClient.getBrowserVersion(), selector, child, null, true, true)) {
elements.add(child);
break;
}
@@ -1859,34 +1858,36 @@ public DomNodeList querySelectorAll(final String selectors) {
/**
* Returns the {@link SelectorList}.
* @param selectors the selectors
- * @param browserVersion the {@link BrowserVersion}
+ * @param webClient the {@link WebClient}
* @return the {@link SelectorList}
* @throws IOException if an error occurs
*/
- protected SelectorList getSelectorList(final String selectors, final BrowserVersion browserVersion)
+ protected SelectorList getSelectorList(final String selectors, final WebClient webClient)
throws IOException {
- final CSSOMParser parser = new CSSOMParser(new CSS3Parser());
- final CheckErrorHandler errorHandler = new CheckErrorHandler();
- parser.setErrorHandler(errorHandler);
-
- final SelectorList selectorList = parser.parseSelectors(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();
- }
+ try (PooledCSS3Parser pooledParser = webClient.getCSS3ParserFromPool()) {
+ final CSSOMParser parser = new CSSOMParser(pooledParser.css3Parser());
+ final CheckErrorHandler errorHandler = new CheckErrorHandler();
+ parser.setErrorHandler(errorHandler);
+
+ final SelectorList selectorList = parser.parseSelectors(selectors);
+ // in case of error parseSelectors returns null
+ if (errorHandler.errorDetected()) {
+ throw new CSSException("Invalid selectors: '" + selectors + "'");
}
- CssStyleSheet.validateSelectors(selectorList, documentMode, this);
+ if (selectorList != null) {
+ int documentMode = 9;
+ if (webClient.getBrowserVersion().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;
}
- return selectorList;
}
/**
@@ -2009,15 +2010,15 @@ public DomElement getNextElementSibling() {
*/
public DomElement closest(final String selectorString) {
try {
- final BrowserVersion browserVersion = getPage().getWebClient().getBrowserVersion();
- final SelectorList selectorList = getSelectorList(selectorString, browserVersion);
+ final WebClient webClient = getPage().getWebClient();
+ final SelectorList selectorList = getSelectorList(selectorString, webClient);
DomNode current = this;
if (selectorList != null) {
do {
for (final Selector selector : selectorList) {
final DomElement elem = (DomElement) current;
- if (CssStyleSheet.selects(browserVersion, selector, elem, null, true, true)) {
+ if (CssStyleSheet.selects(webClient.getBrowserVersion(), selector, elem, null, true, true)) {
return elem;
}
}
diff --git a/src/main/java/org/htmlunit/html/HtmlLink.java b/src/main/java/org/htmlunit/html/HtmlLink.java
index 8d9ad540250..d98aac7a99f 100644
--- a/src/main/java/org/htmlunit/html/HtmlLink.java
+++ b/src/main/java/org/htmlunit/html/HtmlLink.java
@@ -372,7 +372,7 @@ public boolean isActiveStyleSheetLink() {
}
final MediaListImpl mediaList =
- CssStyleSheet.parseMedia(getPage().getWebClient().getCssErrorHandler(), media);
+ CssStyleSheet.parseMedia(media, getPage().getWebClient());
return CssStyleSheet.isActive(mediaList, getPage().getEnclosingWindow());
}
return false;
diff --git a/src/main/java/org/htmlunit/javascript/host/css/MediaQueryList.java b/src/main/java/org/htmlunit/javascript/host/css/MediaQueryList.java
index 43d3cd73ff8..abfd0768ae6 100644
--- a/src/main/java/org/htmlunit/javascript/host/css/MediaQueryList.java
+++ b/src/main/java/org/htmlunit/javascript/host/css/MediaQueryList.java
@@ -22,7 +22,6 @@
import org.htmlunit.WebWindow;
import org.htmlunit.css.CssStyleSheet;
import org.htmlunit.cssparser.dom.MediaListImpl;
-import org.htmlunit.cssparser.parser.CSSErrorHandler;
import org.htmlunit.javascript.configuration.JsxClass;
import org.htmlunit.javascript.configuration.JsxConstructor;
import org.htmlunit.javascript.configuration.JsxFunction;
@@ -72,8 +71,7 @@ public String getMedia() {
@JsxGetter
public boolean isMatches() {
final WebWindow webWindow = getWindow().getWebWindow();
- final CSSErrorHandler errorHandler = webWindow.getWebClient().getCssErrorHandler();
- final MediaListImpl mediaList = CssStyleSheet.parseMedia(errorHandler, media_);
+ final MediaListImpl mediaList = CssStyleSheet.parseMedia(media_, webWindow.getWebClient());
return CssStyleSheet.isActive(mediaList, webWindow);
}
diff --git a/src/main/java/org/htmlunit/javascript/host/css/StyleMedia.java b/src/main/java/org/htmlunit/javascript/host/css/StyleMedia.java
index b13b5d12b1e..1170ad6a6ec 100644
--- a/src/main/java/org/htmlunit/javascript/host/css/StyleMedia.java
+++ b/src/main/java/org/htmlunit/javascript/host/css/StyleMedia.java
@@ -21,7 +21,6 @@
import org.htmlunit.WebWindow;
import org.htmlunit.css.CssStyleSheet;
import org.htmlunit.cssparser.dom.MediaListImpl;
-import org.htmlunit.cssparser.parser.CSSErrorHandler;
import org.htmlunit.javascript.HtmlUnitScriptable;
import org.htmlunit.javascript.configuration.JsxClass;
import org.htmlunit.javascript.configuration.JsxFunction;
@@ -60,8 +59,7 @@ public String getType() {
@JsxFunction
public boolean matchMedium(final String media) {
final WebWindow webWindow = getWindow().getWebWindow();
- final CSSErrorHandler errorHandler = webWindow.getWebClient().getCssErrorHandler();
- final MediaListImpl mediaList = CssStyleSheet.parseMedia(errorHandler, media);
+ final MediaListImpl mediaList = CssStyleSheet.parseMedia(media, webWindow.getWebClient());
return CssStyleSheet.isActive(mediaList, webWindow);
}