Skip to content

Commit

Permalink
try css3Parser mini pool
Browse files Browse the repository at this point in the history
  • Loading branch information
rbri committed Oct 31, 2023
1 parent ab8e36c commit eca9946
Show file tree
Hide file tree
Showing 7 changed files with 129 additions and 43 deletions.
54 changes: 53 additions & 1 deletion src/main/java/org/htmlunit/WebClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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";

Expand Down Expand Up @@ -2944,4 +2950,50 @@ public XHtmlPage loadXHtmlCodeIntoCurrentWindow(final String xhtmlCode) throws I
htmlParser.parse(webResponse, page, true, false);
return page;
}

/**
* <span style="color:red">INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.</span><br>
*
* @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;

This comment has been minimized.

Copy link
@rschwietzke

rschwietzke Nov 15, 2023

Contributor

Technically, that does not work because you have to read and write under the same constraint aka synchronization. See my JUG session ;) But I have time today and I will suggest a slightly different approach.

}
}
}
43 changes: 40 additions & 3 deletions src/main/java/org/htmlunit/css/CssStyleSheet.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
Expand All @@ -1026,14 +1027,50 @@ 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.
*
* @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) {
Expand Down Expand Up @@ -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);
}

Expand Down
6 changes: 3 additions & 3 deletions src/main/java/org/htmlunit/html/DomElement.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Expand Down
59 changes: 30 additions & 29 deletions src/main/java/org/htmlunit/html/DomNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -1835,14 +1834,14 @@ private List<CharacterDataChangeListener> safeGetCharacterDataListeners() {
*/
public DomNodeList<DomNode> 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<DomNode> 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;
}
Expand All @@ -1859,34 +1858,36 @@ public DomNodeList<DomNode> 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;
}

/**
Expand Down Expand Up @@ -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;
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/htmlunit/html/HtmlLink.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}

Expand Down

0 comments on commit eca9946

Please sign in to comment.