requestHeaders_ = Collections.synchronizedMap(new HashMap<>(89));
@@ -332,6 +338,8 @@ public WebClient(final BrowserVersion browserVersion, final boolean javaScriptEn
}
loadQueue_ = new ArrayList<>();
+ webSocketAdapterFactory_ = new JettyWebSocketAdapterFactory();
+
// The window must be constructed AFTER the script engine.
currentWindowTracker_ = new CurrentWindowTracker(this, true);
currentWindow_ = new TopLevelWindow("", this);
@@ -507,7 +515,8 @@ P getPage(final WebWindow webWindow, final WebRequest webReques
/**
* Convenient method to build a URL and load it into the current WebWindow as it would be done
* by {@link #getPage(WebWindow, WebRequest)}.
- * @param url the URL of the new content
+ * @param url the URL of the new content; in contrast to real browsers plain file url's are not supported.
+ * You have to use the 'file', 'data', 'blob', 'http' or 'https' protocol.
* @param
the page type
* @return the new page
* @throws FailingHttpStatusCodeException if the server returns a failing status code AND the property
@@ -523,7 +532,8 @@ public
P getPage(final String url) throws IOException, FailingH
/**
* Convenient method to load a URL into the current top WebWindow as it would be done
* by {@link #getPage(WebWindow, WebRequest)}.
- * @param url the URL of the new content
+ * @param url the URL of the new content; in contrast to real browsers plain file url's are not supported.
+ * You have to use the 'file', 'data', 'blob', 'http' or 'https' protocol.
* @param
the page type
* @return the new page
* @throws FailingHttpStatusCodeException if the server returns a failing status code AND the property
@@ -534,7 +544,6 @@ public
P getPage(final URL url) throws IOException, FailingHttp
final WebRequest request = new WebRequest(url, getBrowserVersion().getHtmlAcceptHeader(),
getBrowserVersion().getAcceptEncodingHeader());
request.setCharset(UTF_8);
-
return getPage(getCurrentWindow().getTopWindow(), request);
}
@@ -1539,7 +1548,8 @@ private WebResponse makeWebResponseForJavaScriptUrl(final WebWindow webWindow, f
* @return the WebResponse
*/
public WebResponse loadWebResponse(final WebRequest webRequest) throws IOException {
- switch (webRequest.getUrl().getProtocol()) {
+ final String protocol = webRequest.getUrl().getProtocol();
+ switch (protocol) {
case UrlUtils.ABOUT:
return makeWebResponseForAboutUrl(webRequest);
@@ -1552,8 +1562,12 @@ public WebResponse loadWebResponse(final WebRequest webRequest) throws IOExcepti
case "blob":
return makeWebResponseForBlobUrl(webRequest);
- default:
+ case "http":
+ case "https":
return loadWebResponseFromWebConnection(webRequest, ALLOWED_REDIRECTIONS_SAME_URL);
+
+ default:
+ throw new IOException("Unsupported protocol '" + protocol + "'");
}
}
@@ -2540,6 +2554,7 @@ private void readObject(final ObjectInputStream in) throws IOException, ClassNot
scriptEngine_ = new JavaScriptEngine(this);
jobManagers_ = Collections.synchronizedList(new ArrayList<>());
loadQueue_ = new ArrayList<>();
+ css3ParserPool_ = new CSS3ParserPool();
}
private static class LoadJob {
@@ -2589,13 +2604,12 @@ public boolean isOutdated() {
* @param target the name of the target window
* @param request the request to perform
* @param checkHash if true check for hashChenage
- * @param forceLoad if true always load the request even if there is already the same in the queue
* @param forceAttachmentWithFilename if not {@code null} the AttachmentHandler isAttachment() method is not called,
* the response has to be handled as attachment in any case
* @param description information about the origin of the request. Useful for debugging.
*/
public void download(final WebWindow requestingWindow, final String target,
- final WebRequest request, final boolean checkHash, final boolean forceLoad,
+ final WebRequest request, final boolean checkHash,
final String forceAttachmentWithFilename, final String description) {
final WebWindow targetWindow = resolveWindow(requestingWindow, target);
@@ -2632,9 +2646,7 @@ public void download(final WebWindow requestingWindow, final String target,
final WebRequest otherRequest = otherLoadJob.request_;
final URL otherUrl = otherRequest.getUrl();
- // TODO: investigate but it seems that IE considers query string too but not FF
- if (!forceLoad
- && url.getPath().equals(otherUrl.getPath()) // fail fast
+ if (url.getPath().equals(otherUrl.getPath()) // fail fast
&& url.toString().equals(otherUrl.toString())
&& request.getRequestParameters().equals(otherRequest.getRequestParameters())
&& Objects.equals(request.getRequestBody(), otherRequest.getRequestBody())) {
@@ -2863,7 +2875,7 @@ public HtmlPage loadHtmlCodeIntoCurrentWindow(final String htmlCode) throws IOEx
final HtmlPage page = new HtmlPage(webResponse, webWindow);
webWindow.setEnclosedPage(page);
- htmlParser.parse(webResponse, page, false, false);
+ htmlParser.parse(this, webResponse, page, false, false);
return page;
}
@@ -2884,10 +2896,29 @@ public XHtmlPage loadXHtmlCodeIntoCurrentWindow(final String xhtmlCode) throws I
final XHtmlPage page = new XHtmlPage(webResponse, webWindow);
webWindow.setEnclosedPage(page);
- htmlParser.parse(webResponse, page, true, false);
+ htmlParser.parse(this, webResponse, page, true, false);
return page;
}
+ /**
+ * Creates a new {@link WebSocketAdapter}.
+ *
+ * @param webSocketListener the {@link WebSocketListener}
+ * @return a new {@link WebSocketAdapter}
+ */
+ public WebSocketAdapter buildWebSocketAdapter(final WebSocketListener webSocketListener) {
+ return webSocketAdapterFactory_.buildWebSocketAdapter(this, webSocketListener);
+ }
+
+ /**
+ * Defines a new factory method to create a new WebSocketAdapter.
+ *
+ * @param factory a {@link WebSocketAdapterFactory}
+ */
+ public void setWebSocketAdapter(final WebSocketAdapterFactory factory) {
+ webSocketAdapterFactory_ = factory;
+ }
+
/**
* INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.
*
@@ -2919,7 +2950,7 @@ static class CSS3ParserPool {
* Our pool. We only hold data when it is available. In addition, synchronization against
* this deque is cheap.
*/
- private ConcurrentLinkedDeque parsers_ = new ConcurrentLinkedDeque<>();
+ private final ConcurrentLinkedDeque parsers_ = new ConcurrentLinkedDeque<>();
/**
* Fetch a new or recycled CSS3parser. Make sure you use the try-with-resource concept
diff --git a/src/main/java/org/htmlunit/WebClientOptions.java b/src/main/java/org/htmlunit/WebClientOptions.java
index a1a1237243d..2e672d3ff81 100644
--- a/src/main/java/org/htmlunit/WebClientOptions.java
+++ b/src/main/java/org/htmlunit/WebClientOptions.java
@@ -37,6 +37,7 @@
* @author Madis Pärn
* @author Ronald Brill
*/
+@SuppressWarnings("PMD.TooManyFields")
public class WebClientOptions implements Serializable {
/** 1920. */
@@ -82,6 +83,8 @@ public class WebClientOptions implements Serializable {
private boolean geolocationEnabled_;
private Geolocation geolocation_;
+ private int nekoReaderBufferSize_ = -1;
+
private boolean webSocketEnabled_ = true;
private int webSocketMaxTextMessageSize_ = -1;
private int webSocketMaxTextMessageBufferSize_ = -1;
@@ -725,6 +728,23 @@ public int getScreenHeight() {
return screenHeight_;
}
+ /**
+ * @return the Neko Html parser reader buffer size
+ */
+ public int getNekoReaderBufferSize() {
+ return nekoReaderBufferSize_;
+ }
+
+ /**
+ * Sets the Neko Html parser reader buffer size.
+ *
+ * @param nekoReaderBufferSize the new value
+ */
+ public void setNekoReaderBufferSize(final int nekoReaderBufferSize) {
+ nekoReaderBufferSize_ = nekoReaderBufferSize;
+ }
+
+
/**
* Enables/disables WebSocket support. By default, this property is enabled.
*
diff --git a/src/main/java/org/htmlunit/WebRequest.java b/src/main/java/org/htmlunit/WebRequest.java
index e07f4fd775b..137225f8c17 100644
--- a/src/main/java/org/htmlunit/WebRequest.java
+++ b/src/main/java/org/htmlunit/WebRequest.java
@@ -54,6 +54,7 @@
* @author Lai Quang Duong
* @author Kristof Neirynck
*/
+@SuppressWarnings("PMD.TooManyFields")
public class WebRequest implements Serializable {
/**
@@ -559,17 +560,6 @@ public void setRefererHeader(final URL url) {
}
}
- /**
- * Sets the referer HTTP header - only if the provided url is valid.
- * @param url the url for the referer HTTP header
- *
- * @deprecated as of version 4.5.0; use {@link #setRefererHeader(URL)} instead
- */
- @Deprecated
- public void setRefererlHeader(final URL url) {
- setRefererHeader(url);
- }
-
/**
* Sets the specified name/value pair in the additional HTTP headers.
* @param name the name of the additional HTTP header
diff --git a/src/main/java/org/htmlunit/WebResponse.java b/src/main/java/org/htmlunit/WebResponse.java
index 80ce54b66d5..4661bde6bb1 100644
--- a/src/main/java/org/htmlunit/WebResponse.java
+++ b/src/main/java/org/htmlunit/WebResponse.java
@@ -240,7 +240,7 @@ public Charset getContentCharset() {
* @return {@code true} if the charset of the previous call to {@link #getContentCharset()} was
* "tentative".
* @see
- * https://html.spec.whatwg.org/multipage/parsing.html#concept-encoding-confidence
+ * https://html.spec.whatwg.org/multipage/parsing.html#concept-encoding-confidence
*/
public boolean wasContentCharsetTentative() {
return wasContentCharsetTentative_;
diff --git a/src/main/java/org/htmlunit/WebWindowListener.java b/src/main/java/org/htmlunit/WebWindowListener.java
index 0cb8868d4f1..e111fc50c70 100644
--- a/src/main/java/org/htmlunit/WebWindowListener.java
+++ b/src/main/java/org/htmlunit/WebWindowListener.java
@@ -25,6 +25,10 @@
* process. This implies, the initial window is already open at the time you attach this listener.
* Therefore you will receive no open event for this.
*
+ *
+ * Caution: Frames and also iFrames are handled as separate windows. Therefore the listener is also
+ * called for each and every containing (i)Frame when e.g. closing a {@link TopLevelWindow}.
+ *
*
* @author Mike Bowler
* @author Ronald Brill
diff --git a/src/main/java/org/htmlunit/css/AbstractCssStyleDeclaration.java b/src/main/java/org/htmlunit/css/AbstractCssStyleDeclaration.java
index 4e650546d79..c0baff12da7 100644
--- a/src/main/java/org/htmlunit/css/AbstractCssStyleDeclaration.java
+++ b/src/main/java/org/htmlunit/css/AbstractCssStyleDeclaration.java
@@ -437,6 +437,14 @@ public String getBackgroundRepeat() {
return value;
}
+ /**
+ * Gets the {@code blockSize} style attribute.
+ * @return the style attribute
+ */
+ public String getBlockSize() {
+ return getStyleAttribute(Definition.BLOCK_SIZE, true);
+ }
+
/**
* Gets the {@code borderBottomColor} style attribute.
* @return the style attribute
diff --git a/src/main/java/org/htmlunit/css/ComputedCssStyleDeclaration.java b/src/main/java/org/htmlunit/css/ComputedCssStyleDeclaration.java
index ae132d1273c..702b704a269 100644
--- a/src/main/java/org/htmlunit/css/ComputedCssStyleDeclaration.java
+++ b/src/main/java/org/htmlunit/css/ComputedCssStyleDeclaration.java
@@ -18,6 +18,9 @@
import static org.htmlunit.BrowserVersionFeatures.JS_CLIENTHEIGHT_INPUT_18;
import static org.htmlunit.BrowserVersionFeatures.JS_CLIENTHEIGHT_RADIO_CHECKBOX_10;
import static org.htmlunit.BrowserVersionFeatures.JS_CLIENTHEIGHT_RADIO_CHECKBOX_14;
+import static org.htmlunit.BrowserVersionFeatures.JS_CLIENTHEIGHT_RB_17;
+import static org.htmlunit.BrowserVersionFeatures.JS_CLIENTHEIGHT_RT_9;
+import static org.htmlunit.BrowserVersionFeatures.JS_CLIENTHEIGHT_RUBY_17;
import static org.htmlunit.BrowserVersionFeatures.JS_CLIENTWIDTH_INPUT_TEXT_154;
import static org.htmlunit.BrowserVersionFeatures.JS_CLIENTWIDTH_INPUT_TEXT_173;
import static org.htmlunit.BrowserVersionFeatures.JS_CLIENTWIDTH_RADIO_CHECKBOX_10;
@@ -526,6 +529,10 @@ public String getBackgroundAttachment() {
*/
@Override
public String getBackgroundColor() {
+ if (!getDomElement().isAttachedToPage()) {
+ return EMPTY_FINAL;
+ }
+
final String value = super.getBackgroundColor();
if (StringUtils.isEmpty(value)) {
return Definition.BACKGROUND_COLOR.getDefaultComputedValue(getBrowserVersion());
@@ -558,6 +565,38 @@ public String getBackgroundRepeat() {
return defaultIfEmpty(super.getBackgroundRepeat(), Definition.BACKGROUND_REPEAT);
}
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getBlockSize() {
+ if (NONE.equals(getDisplay())) {
+ return defaultIfEmpty(super.getBlockSize(), Definition.BLOCK_SIZE);
+ }
+
+ final DomElement domElem = getDomElement();
+ if (!domElem.isAttachedToPage()) {
+ return defaultIfEmpty(super.getBlockSize(), Definition.BLOCK_SIZE);
+ }
+
+ return CssPixelValueConverter.pixelString(domElem, new CssPixelValueConverter.CssValue(0, 0) {
+ @Override
+ public String get(final ComputedCssStyleDeclaration style) {
+ final String value = style.getStyleAttribute(Definition.HEIGHT, true);
+ if (StringUtils.isEmpty(value)) {
+ final String content = domElem.getVisibleText();
+ // do this only for small content
+ // at least for empty div's this is more correct
+ if (null == content) {
+ return getDefaultValue() + "px";
+ }
+ return getEmptyHeight(domElem) + "px";
+ }
+ return value;
+ }
+ });
+ }
+
/**
* {@inheritDoc}
*/
@@ -1177,6 +1216,10 @@ public String getWordSpacing() {
*/
@Override
public String getZIndex() {
+ if (!getDomElement().isAttachedToPage()) {
+ return EMPTY_FINAL;
+ }
+
final String response = super.getZIndex();
if (response.isEmpty()) {
return AUTO;
@@ -1663,9 +1706,9 @@ else if (child instanceof DomText) {
* elements
*/
private int getEmptyHeight(final DomElement element) {
- final Integer cachedHeight2 = getCachedEmptyHeight();
- if (cachedHeight2 != null) {
- return cachedHeight2.intValue();
+ final Integer cachedEmptyHeight = getCachedEmptyHeight();
+ if (cachedEmptyHeight != null) {
+ return cachedEmptyHeight.intValue();
}
if (!element.mayBeDisplayed()) {
@@ -1726,10 +1769,7 @@ private int getEmptyHeight(final DomElement element) {
|| element instanceof HtmlNoLayer
|| element instanceof HtmlNoScript
|| element instanceof HtmlPlainText
- || element instanceof HtmlRuby
- || element instanceof HtmlRb
|| element instanceof HtmlRp
- || element instanceof HtmlRt
|| element instanceof HtmlRtc
|| element instanceof HtmlS
|| element instanceof HtmlSample
@@ -1797,6 +1837,33 @@ else if (element instanceof HtmlInlineFrame) {
defaultHeight = 0;
}
}
+ else if (element instanceof HtmlRb) {
+ final BrowserVersion browser = webWindow.getWebClient().getBrowserVersion();
+ if (browser.hasFeature(JS_CLIENTHEIGHT_RB_17)) {
+ defaultHeight = 17;
+ }
+ else {
+ defaultHeight = 0;
+ }
+ }
+ else if (element instanceof HtmlRt) {
+ final BrowserVersion browser = webWindow.getWebClient().getBrowserVersion();
+ if (browser.hasFeature(JS_CLIENTHEIGHT_RT_9)) {
+ defaultHeight = 9;
+ }
+ else {
+ defaultHeight = 0;
+ }
+ }
+ else if (element instanceof HtmlRuby) {
+ final BrowserVersion browser = webWindow.getWebClient().getBrowserVersion();
+ if (browser.hasFeature(JS_CLIENTHEIGHT_RUBY_17)) {
+ defaultHeight = 17;
+ }
+ else {
+ defaultHeight = 0;
+ }
+ }
else {
final String fontSize;
@@ -1902,6 +1969,13 @@ else if (element instanceof HtmlHeading6) {
defaultHeight *= StringUtils.countMatches(content, '\n') + 1;
}
}
+
+ final String styleHeight = getStyleAttribute(Definition.HEIGHT, true);
+ if (styleHeight.endsWith("%")) {
+ if (page instanceof HtmlPage && !((HtmlPage) page).isQuirksMode()) {
+ return defaultHeight;
+ }
+ }
}
}
diff --git a/src/main/java/org/htmlunit/css/CssColors.java b/src/main/java/org/htmlunit/css/CssColors.java
index 85c0a8aa086..1e5ae5094c6 100644
--- a/src/main/java/org/htmlunit/css/CssColors.java
+++ b/src/main/java/org/htmlunit/css/CssColors.java
@@ -14,9 +14,9 @@
*/
package org.htmlunit.css;
-import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
/**
* Helper to work with colors.
@@ -25,7 +25,7 @@
*/
public final class CssColors {
- private static final Map CSS_COLORS = new HashMap<>();
+ private static final Map CSS_COLORS = new ConcurrentHashMap<>();
static {
CSS_COLORS.put("aqua", "rgb(0, 255, 255)");
diff --git a/src/main/java/org/htmlunit/css/CssStyleSheet.java b/src/main/java/org/htmlunit/css/CssStyleSheet.java
index afcbe5339de..9afb0bc3feb 100644
--- a/src/main/java/org/htmlunit/css/CssStyleSheet.java
+++ b/src/main/java/org/htmlunit/css/CssStyleSheet.java
@@ -1044,25 +1044,6 @@ private static String toString(final InputSource source) {
}
}
- /**
- * Validates the list of selectors.
- * @param selectorList the selectors
- * @param documentMode see {@link Document#getDocumentMode()}
- * @param domNode the dom node the query should work on
- * @throws CSSException if a selector is invalid
- *
- * @deprecated as of version 4.5.0; use {@link #validateSelectors(SelectorList, DomNode)} instead
- */
- @Deprecated
- public static void validateSelectors(final SelectorList selectorList, final int documentMode,
- final DomNode domNode) throws CSSException {
- for (final Selector selector : selectorList) {
- if (!isValidSelector(selector, domNode)) {
- throw new CSSException("Invalid selector: " + selector, null);
- }
- }
- }
-
/**
* Validates the list of selectors.
* @param selectorList the selectors
@@ -1224,7 +1205,8 @@ public static boolean isActive(final MediaListImpl mediaList, final WebWindow we
return true;
}
- for (int i = 0; i < mediaList.getLength(); i++) {
+ final int length = mediaList.getLength();
+ for (int i = 0; i < length; i++) {
final MediaQuery mediaQuery = mediaList.mediaQuery(i);
boolean isActive = isActive(mediaQuery, webWindow);
if (mediaQuery.isNot()) {
diff --git a/src/main/java/org/htmlunit/css/StyleAttributes.java b/src/main/java/org/htmlunit/css/StyleAttributes.java
index 137da28391a..a41f839323e 100644
--- a/src/main/java/org/htmlunit/css/StyleAttributes.java
+++ b/src/main/java/org/htmlunit/css/StyleAttributes.java
@@ -32,9 +32,9 @@
import java.io.Serializable;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
import org.htmlunit.BrowserVersion;
@@ -48,7 +48,7 @@
*/
@SuppressWarnings("PMD.AvoidDuplicateLiterals")
public final class StyleAttributes implements Serializable {
- private static final Map STYLES = new HashMap<>();
+ private static final Map STYLES = new ConcurrentHashMap<>();
static {
for (final Definition definition : Definition.values()) {
@@ -338,10 +338,10 @@ public enum Definition {
BASELINE_SOURCE_("baseline-source", "baseline-source", ff("auto")),
/** The style property {@code blockSize}. */
- BLOCK_SIZE("blockSize", "block-size", chromeAndEdge("324px"), ff("328px")),
+ BLOCK_SIZE("blockSize", "block-size", chromeAndEdgeAndFirefox("auto")),
/** The style property {@code block-size}. */
- BLOCK_SIZE_("block-size", "block-size", ff("328px")),
+ BLOCK_SIZE_("block-size", "block-size", ff("auto")),
/** The style property {@code border}. */
BORDER("border", "border", chromeAndEdge("0px none rgb(0, 0, 0)"), ff("0px rgb(0, 0, 0)")),
@@ -1426,7 +1426,10 @@ public enum Definition {
HYPHENATE_CHARACTER_("hyphenate-character", "hyphenate-character", ff("auto")),
/** The style property {@code hyphenateLimitChars}. */
- HYPHENATE_LIMIT_CHAR("hyphenateLimitChars", "hyphenate-limit-char", chromeAndEdgeAuto()),
+ HYPHENATE_LIMIT_CHAR("hyphenateLimitChars", "hyphenate-limit-chars", chromeAndEdgeAuto(), ffLatest("auto")),
+
+ /** The style property {@code hyphenate-limit-chars}. */
+ HYPHENATE_LIMIT_CHAR_("hyphenate-limit-chars", "hyphenate-limit-chars", ffLatest("auto")),
/** The style property {@code hyphens}. */
HYPHENS("hyphens", "hyphens", ff("manual"), chromeAndEdge("manual")),
@@ -1505,6 +1508,9 @@ public enum Definition {
/** The style property {@code inset-inline-start}. */
INSET_INLINE_START_("inset-inline-start", "inset-inline-start", ff("auto")),
+ /** The style property {@code interactivity}. */
+ INTERACTIVITY("interactivity", "interpolate-size", chromeAndEdge("auto")),
+
/** The style property {@code interpolateSize}. */
INTERPOLATE_SIZE("interpolateSize", "interpolate-size", chromeAndEdge("numeric-only")),
@@ -2084,10 +2090,10 @@ public enum Definition {
MOZ_PERSPECTIVE__("-moz-perspective", "-moz-perspective", ffLatest("none")),
/** The style property {@code MozPerspectiveOrigin}. */
- MOZ_PERSPECTIVE_ORIGIN("MozPerspectiveOrigin", "-moz-perspective-origin", ffLatest("620px 164px")),
+ MOZ_PERSPECTIVE_ORIGIN("MozPerspectiveOrigin", "-moz-perspective-origin", ffLatest("620px 9px")),
/** The style property {@code -moz-perspective-origin}. */
- MOZ_PERSPECTIVE_ORIGIN__("-moz-perspective-origin", "-moz-perspective-origin", ffLatest("620px 164px")),
+ MOZ_PERSPECTIVE_ORIGIN__("-moz-perspective-origin", "-moz-perspective-origin", ffLatest("620px 9px")),
/** The style property {@code MozTabSize}. */
MOZ_TAB_SIZE("MozTabSize", "-moz-tab-size", ff("8")),
@@ -2119,11 +2125,11 @@ public enum Definition {
/** The style property {@code MozTransformOrigin}. */
MOZ_TRANSFORM_ORIGIN("MozTransformOrigin", "-moz-transform-origin",
- ffEsr("622px 164px"), ffLatest("620px 164px")),
+ ffEsr("622px 9px"), ffLatest("620px 9px")),
/** The style property {@code -moz-transform-origin}. */
MOZ_TRANSFORM_ORIGIN__("-moz-transform-origin", "-moz-transform-origin",
- ffEsr("622px 164px"), ffLatest("620px 164px")),
+ ffEsr("622px 9px"), ffLatest("620px 9px")),
/** The style property {@code MozTransformStyle}. */
MOZ_TRANSFORM_STYLE("MozTransformStyle", "-moz-transform-style", ffLatest("flat")),
@@ -2257,7 +2263,7 @@ public enum Definition {
OVERFLOW_ANCHOR_("overflow-anchor", "overflow-anchor", ff("auto")),
/** The style property {@code overflowBlock}. */
- OVERFLOW_BLOCK("overflowBlock", "overflow-block", ff("visible")),
+ OVERFLOW_BLOCK("overflowBlock", "overflow-block", chromeAndEdgeAndFirefox("visible")),
/** The style property {@code overflow-block}. */
OVERFLOW_BLOCK_("overflow-block", "overflow-block", ff("visible")),
@@ -2269,7 +2275,7 @@ public enum Definition {
OVERFLOW_CLIP_MARGIN_("overflow-clip-margin", "overflow-clip-margin", ff("0px")),
/** The style property {@code overflowInline}. */
- OVERFLOW_INLINE("overflowInline", "overflow-inline", ff("visible")),
+ OVERFLOW_INLINE("overflowInline", "overflow-inline", chromeAndEdgeAndFirefox("visible")),
/** The style property {@code overflow-inline}. */
OVERFLOW_INLINE_("overflow-inline", "overflow-inline", ff("visible")),
@@ -2433,10 +2439,11 @@ public enum Definition {
/** The style property {@code perspectiveOrigin}. */
PERSPECTIVE_ORIGIN("perspectiveOrigin", "perspective-origin",
- ffEsr("622px 164px"), ffLatest("620px 164px"), chrome("620px 162px"), edge("616px 162px")),
+ ffEsr("622px 9px"), ffLatest("620px 9px"), chrome("620px 9px"), edge("616px 9px")),
/** The style property {@code perspective-origin}. */
- PERSPECTIVE_ORIGIN_("perspective-origin", "perspective-origin", ffEsr("622px 164px"), ffLatest("620px 164px")),
+ PERSPECTIVE_ORIGIN_("perspective-origin", "perspective-origin",
+ ffEsr("622px 9px"), ffLatest("620px 9px")),
/** The style property {@code placeContent}. */
PLACE_CONTENT("placeContent", "place-content", chromeAndEdgeNormal(), ffNormal()),
@@ -2619,6 +2626,9 @@ public enum Definition {
/** The style property {@code scroll-margin-top}. */
SCROLL_MARGIN_TOP_("scroll-margin-top", "scroll-margin-top", ff("0px")),
+ /** The style property {@code scrollMarkerGroup}. */
+ SCROLL_MARKER_GROUP("scrollMarkerGroup", "scroll-marker-group", chromeAndEdgeNone()),
+
/** The style property {@code scrollPadding}. */
SCROLL_PADDING("scrollPadding", "scroll-padding", chromeAndEdgeAuto(), ff("auto")),
@@ -3062,10 +3072,10 @@ public enum Definition {
/** The style property {@code transformOrigin}. */
TRANSFORM_ORIGIN("transformOrigin", "transform-origin",
- ffEsr("622px 164px"), ffLatest("620px 164px"), chrome("620px 162px"), edge("616px 162px")),
+ ffEsr("622px 9px"), ffLatest("620px 9px"), chrome("620px 9px"), edge("616px 9px")),
/** The style property {@code transform-origin}. */
- TRANSFORM_ORIGIN_("transform-origin", "transform-origin", ffEsr("622px 164px"), ffLatest("620px 164px")),
+ TRANSFORM_ORIGIN_("transform-origin", "transform-origin", ffEsr("622px 9px"), ffLatest("620px 9px")),
/** The style property {@code transformStyle}. */
TRANSFORM_STYLE("transformStyle", "transform-style", ff("flat"), chromeAndEdge("flat")),
@@ -3706,7 +3716,7 @@ public enum Definition {
WEBKIT_LOCALE("webkitLocale", "webkit-locale", chromeAndEdgeAuto()),
/** The style property {@code webkitLogicalHeight}. */
- WEBKIT_LOGICAL_HEIGHT("webkitLogicalHeight", "webkit-logical-height", chromeAndEdge("324px")),
+ WEBKIT_LOGICAL_HEIGHT("webkitLogicalHeight", "webkit-logical-height", chromeAndEdge("18px")),
/** The style property {@code webkitLogicalWidth}. */
WEBKIT_LOGICAL_WIDTH("webkitLogicalWidth", "webkit-logical-width", chrome("1240px"), edge("1232px")),
@@ -3879,15 +3889,15 @@ public enum Definition {
/** The style property {@code webkitPerspectiveOrigin}. */
WEBKIT_PERSPECTIVE_ORIGIN("webkitPerspectiveOrigin", "webkit-perspective-origin",
- ffEsr("622px 164px"), ffLatest("620px 164px"), chrome("620px 162px"), edge("616px 162px")),
+ ffEsr("622px 9px"), ffLatest("620px 9px"), chrome("620px 9px"), edge("616px 9px")),
/** The style property {@code WebkitPerspectiveOrigin}. */
WEBKIT_PERSPECTIVE_ORIGIN_("WebkitPerspectiveOrigin", "webkit-perspective-origin",
- ffEsr("622px 164px"), ffLatest("620px 164px")),
+ ffEsr("622px 9px"), ffLatest("620px 9px")),
/** The style property {@code -webkit-perspective-origin}. */
WEBKIT_PERSPECTIVE_ORIGIN__("-webkit-perspective-origin", "webkit-perspective-origin",
- ffEsr("622px 164px"), ffLatest("620px 164px")),
+ ffEsr("622px 9px"), ffLatest("620px 9px")),
/** The style property {@code webkitPerspectiveOriginX}. */
WEBKIT_PERSPECTIVE_ORIGIN_X("webkitPerspectiveOriginX", "webkit-perspective-origin-x", chromeAndEdgeEmpty()),
@@ -4013,15 +4023,15 @@ public enum Definition {
/** The style property {@code webkitTransformOrigin}. */
WEBKIT_TRANSFORM_ORIGIN("webkitTransformOrigin", "webkit-transform-origin",
- ffEsr("622px 164px"), ffLatest("620px 164px"), chrome("620px 162px"), edge("616px 162px")),
+ ffEsr("622px 9px"), ffLatest("620px 9px"), chrome("620px 9px"), edge("616px 9px")),
/** The style property {@code WebkitTransformOrigin}. */
WEBKIT_TRANSFORM_ORIGIN_("WebkitTransformOrigin", "webkit-transform-origin",
- ffEsr("622px 164px"), ffLatest("620px 164px")),
+ ffEsr("622px 9px"), ffLatest("620px 9px")),
/** The style property {@code -webkit-transform-origin}. */
WEBKIT_TRANSFORM_ORIGIN__("-webkit-transform-origin", "webkit-transform-origin",
- ffEsr("622px 164px"), ffLatest("620px 164px")),
+ ffEsr("622px 9px"), ffLatest("620px 9px")),
/** The style property {@code webkitTransformOriginX}. */
WEBKIT_TRANSFORM_ORIGIN_X("webkitTransformOriginX", "webkit-transform-origin-x", chromeAndEdgeEmpty()),
diff --git a/src/main/java/org/htmlunit/html/CharacterDataChangeListener.java b/src/main/java/org/htmlunit/html/CharacterDataChangeListener.java
index fab5572b72b..d37c4ce833f 100644
--- a/src/main/java/org/htmlunit/html/CharacterDataChangeListener.java
+++ b/src/main/java/org/htmlunit/html/CharacterDataChangeListener.java
@@ -20,8 +20,10 @@
* Implementations of this interface receive notifications of changes to the Character Data.
*
* @author Ahmed Ashour
+ * @author Ronald Brill
* @see CharacterDataChangeEvent
*/
+@FunctionalInterface
public interface CharacterDataChangeListener extends Serializable {
/**
diff --git a/src/main/java/org/htmlunit/html/DomNode.java b/src/main/java/org/htmlunit/html/DomNode.java
index 019fee9bbbb..bf6b80f8724 100644
--- a/src/main/java/org/htmlunit/html/DomNode.java
+++ b/src/main/java/org/htmlunit/html/DomNode.java
@@ -1120,7 +1120,8 @@ public void removeAllChildren() {
* @throws SAXException in case of error
*/
public void parseHtmlSnippet(final String source) throws SAXException, IOException {
- getPage().getWebClient().getPageCreator().getHtmlParser().parseFragment(this, source);
+ final WebClient webClient = getPage().getWebClient();
+ webClient.getPageCreator().getHtmlParser().parseFragment(webClient, this, this, source, false);
}
/**
@@ -1173,7 +1174,7 @@ else if (previousSibling_ != null && previousSibling_.nextSibling_ == this) {
private void fireRemoval(final DomNode exParent) {
final SgmlPage page = getPage();
- if (page != null && page instanceof HtmlPage) {
+ if (page instanceof HtmlPage) {
// some actions executed on removal need an intact parent relationship (e.g. for the
// DocumentPositionComparator) so we have to restore it temporarily
parent_ = exParent;
@@ -2296,9 +2297,6 @@ public DomElement closest(final String selectorString) {
* An unmodifiable empty {@link NamedNodeMap} implementation.
*/
private static final class ReadOnlyEmptyNamedNodeMapImpl implements NamedNodeMap, Serializable {
- private ReadOnlyEmptyNamedNodeMapImpl() {
- super();
- }
/**
* {@inheritDoc}
diff --git a/src/main/java/org/htmlunit/html/DomNodeIterator.java b/src/main/java/org/htmlunit/html/DomNodeIterator.java
index 9424ff28139..2bedfb345c0 100644
--- a/src/main/java/org/htmlunit/html/DomNodeIterator.java
+++ b/src/main/java/org/htmlunit/html/DomNodeIterator.java
@@ -143,15 +143,7 @@ private DomNode traverse(final boolean next) {
}
else {
if (beforeNode) {
- final DomNode left = getSibling(node, true);
- if (left == null) {
- final Node parent = node.getParentNode();
- if (parent == null) {
- node = null;
- }
- }
-
- DomNode follow = left;
+ DomNode follow = getSibling(node, true);
if (follow != null) {
while (follow.hasChildNodes()) {
final DomNode toFollow = getChild(follow, false);
diff --git a/src/main/java/org/htmlunit/html/Html.java b/src/main/java/org/htmlunit/html/Html.java
index 9a00ceca549..d12ed2e2454 100644
--- a/src/main/java/org/htmlunit/html/Html.java
+++ b/src/main/java/org/htmlunit/html/Html.java
@@ -21,6 +21,9 @@
*/
public final class Html {
+ /** The html5 doctype. */
+ public static final String DOCTYPE_HTML = "";
+
/** XHTML namespace. */
public static final String XHTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
diff --git a/src/main/java/org/htmlunit/html/HtmlAnchor.java b/src/main/java/org/htmlunit/html/HtmlAnchor.java
index 09a55e7ac38..ba42f1b9857 100644
--- a/src/main/java/org/htmlunit/html/HtmlAnchor.java
+++ b/src/main/java/org/htmlunit/html/HtmlAnchor.java
@@ -195,7 +195,7 @@ protected void doClickStateUpdate(final boolean shiftKey, final boolean ctrlKey,
target = page.getResolvedTarget(getTargetAttribute());
}
page.getWebClient().download(page.getEnclosingWindow(), target, webRequest,
- true, false, (ATTRIBUTE_NOT_DEFINED != downloadAttribute) ? downloadAttribute : null, "Link click");
+ true, (ATTRIBUTE_NOT_DEFINED != downloadAttribute) ? downloadAttribute : null, "Link click");
}
private boolean relContainsNoreferrer() {
diff --git a/src/main/java/org/htmlunit/html/HtmlDomTreeWalker.java b/src/main/java/org/htmlunit/html/HtmlDomTreeWalker.java
index e2db02a4411..0f6a9a7625e 100644
--- a/src/main/java/org/htmlunit/html/HtmlDomTreeWalker.java
+++ b/src/main/java/org/htmlunit/html/HtmlDomTreeWalker.java
@@ -26,7 +26,7 @@
* therefore we have this impl as backend.
*
* @see
- * DOM-Level-2-Traversal-Range
+ * DOM-Level-2-Traversal-Range
* @author Mike Dirolf
* @author Frank Danek
* @author Ahmed Ashour
diff --git a/src/main/java/org/htmlunit/html/HtmlElement.java b/src/main/java/org/htmlunit/html/HtmlElement.java
index d028fc6fa93..68034b78210 100644
--- a/src/main/java/org/htmlunit/html/HtmlElement.java
+++ b/src/main/java/org/htmlunit/html/HtmlElement.java
@@ -16,6 +16,9 @@
import static org.htmlunit.BrowserVersionFeatures.HTMLELEMENT_REMOVE_ACTIVE_TRIGGERS_BLUR_EVENT;
import static org.htmlunit.BrowserVersionFeatures.KEYBOARD_EVENT_SPECIAL_KEYPRESS;
+import static org.htmlunit.css.CssStyleSheet.ABSOLUTE;
+import static org.htmlunit.css.CssStyleSheet.FIXED;
+import static org.htmlunit.css.CssStyleSheet.STATIC;
import java.io.IOException;
import java.util.ArrayList;
@@ -31,6 +34,8 @@
import org.htmlunit.SgmlPage;
import org.htmlunit.WebAssert;
import org.htmlunit.WebClient;
+import org.htmlunit.WebWindow;
+import org.htmlunit.css.ComputedCssStyleDeclaration;
import org.htmlunit.html.impl.SelectableTextInput;
import org.htmlunit.javascript.HtmlUnitScriptable;
import org.htmlunit.javascript.host.dom.Document;
@@ -191,17 +196,17 @@ protected void setAttributeNS(final String namespaceURI, final String qualifiedN
final String attributeValue, final boolean notifyAttributeChangeListeners,
final boolean notifyMutationObservers) {
+ final HtmlPage htmlPage = getHtmlPageOrNull();
+
// TODO: Clean up; this is a hack for HtmlElement living within an XmlPage.
- if (null == getHtmlPageOrNull()) {
+ if (null == htmlPage) {
super.setAttributeNS(namespaceURI, qualifiedName, attributeValue, notifyAttributeChangeListeners,
notifyMutationObservers);
return;
}
final String oldAttributeValue = getAttribute(qualifiedName);
- final HtmlPage htmlPage = (HtmlPage) getPage();
final boolean mappedElement = isAttachedToPage()
- && htmlPage != null
&& (DomElement.NAME_ATTRIBUTE.equals(qualifiedName) || DomElement.ID_ATTRIBUTE.equals(qualifiedName));
if (mappedElement) {
// cast is save here because isMappedElement checks for HtmlPage
@@ -287,12 +292,19 @@ private void fireAttributeChangeImpl(final HtmlAttributeChangeEvent event,
*/
@Override
public Attr setAttributeNode(final Attr attribute) {
+ final HtmlPage htmlPage = getHtmlPageOrNull();
+
+ // TODO: Clean up; this is a hack for HtmlElement living within an XmlPage.
+ if (null == htmlPage) {
+ return super.setAttributeNode(attribute);
+ }
+
final String qualifiedName = attribute.getName();
final String oldAttributeValue = getAttribute(qualifiedName);
- final HtmlPage htmlPage = (HtmlPage) getPage();
+
final boolean mappedElement = isAttachedToPage()
- && htmlPage != null
- && (DomElement.NAME_ATTRIBUTE.equals(qualifiedName) || DomElement.ID_ATTRIBUTE.equals(qualifiedName));
+ && (DomElement.NAME_ATTRIBUTE.equals(qualifiedName)
+ || DomElement.ID_ATTRIBUTE.equals(qualifiedName));
if (mappedElement) {
htmlPage.removeMappedElement(this, false, false);
}
@@ -325,23 +337,28 @@ public void removeAttribute(final String attributeName) {
}
final HtmlPage htmlPage = getHtmlPageOrNull();
- final boolean mapped = htmlPage != null
- && (DomElement.NAME_ATTRIBUTE.equals(attributeName) || DomElement.ID_ATTRIBUTE.equals(attributeName));
+
+ // TODO: Clean up; this is a hack for HtmlElement living within an XmlPage.
+ if (null == htmlPage) {
+ super.removeAttribute(attributeName);
+ return;
+ }
+
+ final boolean mapped = DomElement.NAME_ATTRIBUTE.equals(attributeName)
+ || DomElement.ID_ATTRIBUTE.equals(attributeName);
if (mapped) {
htmlPage.removeMappedElement(this, false, false);
}
super.removeAttribute(attributeName);
- if (htmlPage != null) {
- if (mapped) {
- htmlPage.addMappedElement(this, false);
- }
-
- final HtmlAttributeChangeEvent event = new HtmlAttributeChangeEvent(this, attributeName, value);
- fireHtmlAttributeRemoved(event);
- htmlPage.fireHtmlAttributeRemoved(event);
+ if (mapped) {
+ htmlPage.addMappedElement(this, false);
}
+
+ final HtmlAttributeChangeEvent event = new HtmlAttributeChangeEvent(this, attributeName, value);
+ fireHtmlAttributeRemoved(event);
+ htmlPage.fireHtmlAttributeRemoved(event);
}
/**
@@ -662,7 +679,8 @@ public Page type(final Keyboard keyboard) throws IOException {
}
}
- for (int i = 0; i < keys.size(); i++) {
+ final int size = keys.size();
+ for (int i = 0; i < size; i++) {
final Object[] entry = keys.get(i);
if (entry.length == 1) {
type((char) entry[0], i == keys.size() - 1);
@@ -1423,6 +1441,186 @@ public void setRequired(final boolean required) {
}
}
+ /**
+ * INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.
+ *
+ * @param returnNullIfFixed if position is 'fixed' return null
+ * @return the offset parent {@link HtmlElement}
+ */
+ public HtmlElement getOffsetParentInternal(final boolean returnNullIfFixed) {
+ if (getParentNode() == null) {
+ return null;
+ }
+
+ final WebWindow webWindow = getPage().getEnclosingWindow();
+ final ComputedCssStyleDeclaration style = webWindow.getComputedStyle(this, null);
+ final String position = style.getPositionWithInheritance();
+
+ if (returnNullIfFixed && FIXED.equals(position)) {
+ return null;
+ }
+
+ final boolean staticPos = STATIC.equals(position);
+
+ DomNode currentElement = this;
+ while (currentElement != null) {
+
+ final DomNode parentNode = currentElement.getParentNode();
+ if (parentNode instanceof HtmlBody
+ || (staticPos && parentNode instanceof HtmlTableDataCell)
+ || (staticPos && parentNode instanceof HtmlTable)) {
+ return (HtmlElement) parentNode;
+ }
+
+ if (parentNode instanceof HtmlElement) {
+ final ComputedCssStyleDeclaration parentStyle =
+ webWindow.getComputedStyle((HtmlElement) parentNode, null);
+ final String parentPosition = parentStyle.getPositionWithInheritance();
+ if (!STATIC.equals(parentPosition)) {
+ return (HtmlElement) parentNode;
+ }
+ }
+
+ currentElement = currentElement.getParentNode();
+ }
+
+ return null;
+ }
+
+ /**
+ * @return this element's top offset, which is the calculated left position of this
+ * element relative to the offsetParent
.
+ */
+ public int getOffsetTop() {
+ if (this instanceof HtmlBody) {
+ return 0;
+ }
+
+ int top = 0;
+
+ // Add the offset for this node.
+ final WebWindow webWindow = getPage().getEnclosingWindow();
+ ComputedCssStyleDeclaration style = webWindow.getComputedStyle(this, null);
+ top += style.getTop(true, false, false);
+
+ // If this node is absolutely positioned, we're done.
+ final String position = style.getPositionWithInheritance();
+ if (ABSOLUTE.equals(position) || FIXED.equals(position)) {
+ return top;
+ }
+
+ final HtmlElement offsetParent = getOffsetParentInternal(false);
+
+ // Add the offset for the ancestor nodes.
+ DomNode parentNode = getParentNode();
+ while (parentNode != null && parentNode != offsetParent) {
+ if (parentNode instanceof HtmlElement) {
+ style = webWindow.getComputedStyle((HtmlElement) parentNode, null);
+ top += style.getTop(false, true, true);
+ }
+ parentNode = parentNode.getParentNode();
+ }
+
+ if (offsetParent != null) {
+ style = webWindow.getComputedStyle(this, null);
+ final boolean thisElementHasTopMargin = style.getMarginTopValue() != 0;
+
+ style = webWindow.getComputedStyle(offsetParent, null);
+ if (!thisElementHasTopMargin) {
+ top += style.getMarginTopValue();
+ }
+ top += style.getPaddingTopValue();
+ }
+
+ return top;
+ }
+
+ /**
+ * @return this element's left offset, which is the calculated left position of this
+ * element relative to the offsetParent
.
+ */
+ public int getOffsetLeft() {
+ if (this instanceof HtmlBody) {
+ return 0;
+ }
+
+ int left = 0;
+
+ // Add the offset for this node.
+ final WebWindow webWindow = getPage().getEnclosingWindow();
+ ComputedCssStyleDeclaration style = webWindow.getComputedStyle(this, null);
+ left += style.getLeft(true, false, false);
+
+ // If this node is absolutely positioned, we're done.
+ final String position = style.getPositionWithInheritance();
+ if (ABSOLUTE.equals(position) || FIXED.equals(position)) {
+ return left;
+ }
+
+ final HtmlElement offsetParent = getOffsetParentInternal(false);
+
+ DomNode parentNode = getParentNode();
+ while (parentNode != null && parentNode != offsetParent) {
+ if (parentNode instanceof HtmlElement) {
+ style = webWindow.getComputedStyle((HtmlElement) parentNode, null);
+ left += style.getLeft(true, true, true);
+ }
+ parentNode = parentNode.getParentNode();
+ }
+
+ if (offsetParent != null) {
+ style = webWindow.getComputedStyle(offsetParent, null);
+ left += style.getMarginLeftValue();
+ left += style.getPaddingLeftValue();
+ }
+
+ return left;
+ }
+
+ /**
+ * Returns this element's X position.
+ * @return this element's X position
+ */
+ public int getPosX() {
+ int cumulativeOffset = 0;
+ final WebWindow webWindow = getPage().getEnclosingWindow();
+
+ HtmlElement element = this;
+ while (element != null) {
+ cumulativeOffset += element.getOffsetLeft();
+ if (element != this) {
+ final ComputedCssStyleDeclaration style =
+ webWindow.getComputedStyle(element, null);
+ cumulativeOffset += style.getBorderLeftValue();
+ }
+ element = element.getOffsetParentInternal(false);
+ }
+
+ return cumulativeOffset;
+ }
+
+ /**
+ * Returns this element's Y position.
+ * @return this element's Y position
+ */
+ public int getPosY() {
+ int cumulativeOffset = 0;
+ final WebWindow webWindow = getPage().getEnclosingWindow();
+
+ HtmlElement element = this;
+ while (element != null) {
+ cumulativeOffset += element.getOffsetTop();
+ if (element != this) {
+ final ComputedCssStyleDeclaration style =
+ webWindow.getComputedStyle(element, null);
+ cumulativeOffset += style.getBorderTopValue();
+ }
+ element = element.getOffsetParentInternal(false);
+ }
+
+ return cumulativeOffset;
+ }
+
/**
* {@inheritDoc}
*/
diff --git a/src/main/java/org/htmlunit/html/HtmlForm.java b/src/main/java/org/htmlunit/html/HtmlForm.java
index fdb07d5c743..dd7797388f3 100644
--- a/src/main/java/org/htmlunit/html/HtmlForm.java
+++ b/src/main/java/org/htmlunit/html/HtmlForm.java
@@ -191,7 +191,7 @@ && getAttributeDirect(ATTRIBUTE_NOVALIDATE) != ATTRIBUTE_NOT_DEFINED) {
final WebWindow webWindow = htmlPage.getEnclosingWindow();
// Calling form.submit() twice forces double download.
- webClient.download(webWindow, target, request, false, false, null, "JS form.submit()");
+ webClient.download(webWindow, target, request, false, null, "JS form.submit()");
}
/**
@@ -541,28 +541,9 @@ private List getFormElementsByAttribute(
final String attributeName,
final String attributeValue) {
- final List list = new ArrayList<>();
- final String lowerCaseTagName = elementName.toLowerCase(Locale.ROOT);
-
- for (final HtmlElement element : getElements()) {
- if (element.getTagName().equals(lowerCaseTagName)) {
- final String attValue = element.getAttribute(attributeName);
- if (attValue.equals(attributeValue)) {
- list.add((E) element);
- }
- }
- }
- return list;
- }
-
- /**
- * @return returns a list of all form controls contained in the <form> element or referenced by formId
- * but ignoring elements that are contained in a nested form
- * @deprecated as of version 4.4.0; use {@link #getFormElements()}, {@link #getElementsJS()} instead
- */
- @Deprecated
- public List getElements() {
- return getElements(htmlElement -> SUBMITTABLE_TAG_NAMES.contains(htmlElement.getTagName()));
+ return (List) getElements(htmlElement ->
+ htmlElement.getTagName().equals(elementName)
+ && htmlElement.getAttribute(attributeName).equals(attributeValue));
}
/**
diff --git a/src/main/java/org/htmlunit/html/HtmlImage.java b/src/main/java/org/htmlunit/html/HtmlImage.java
index 0695114fa4b..ec90e4aa4f4 100644
--- a/src/main/java/org/htmlunit/html/HtmlImage.java
+++ b/src/main/java/org/htmlunit/html/HtmlImage.java
@@ -47,7 +47,6 @@
import org.htmlunit.javascript.host.dom.Document;
import org.htmlunit.javascript.host.event.Event;
import org.htmlunit.javascript.host.event.MouseEvent;
-import org.htmlunit.javascript.host.html.HTMLElement;
import org.htmlunit.platform.Platform;
import org.htmlunit.platform.geom.IntDimension2D;
import org.htmlunit.platform.image.ImageData;
@@ -876,12 +875,11 @@ public String getLocalName() {
public ScriptResult fireEvent(final Event event) {
if (event instanceof MouseEvent) {
final MouseEvent mouseEvent = (MouseEvent) event;
- final HTMLElement scriptableObject = getScriptableObject();
if (lastClickX_ >= 0) {
- mouseEvent.setClientX(scriptableObject.getPosX() + lastClickX_);
+ mouseEvent.setClientX(getPosX() + lastClickX_);
}
if (lastClickY_ >= 0) {
- mouseEvent.setClientY(scriptableObject.getPosX() + lastClickY_);
+ mouseEvent.setClientY(getPosX() + lastClickY_);
}
}
diff --git a/src/main/java/org/htmlunit/html/HtmlOption.java b/src/main/java/org/htmlunit/html/HtmlOption.java
index 12438877c9e..c0dda178ab6 100644
--- a/src/main/java/org/htmlunit/html/HtmlOption.java
+++ b/src/main/java/org/htmlunit/html/HtmlOption.java
@@ -224,7 +224,7 @@ public final void setLabelAttribute(final String newLabel) {
* HTML 4.01
* documentation for details on the use of this attribute.
* @see
- * initial value if value attribute is not set
+ * initial value if value attribute is not set
* @return the value of the attribute {@code value}
*/
public final String getValueAttribute() {
diff --git a/src/main/java/org/htmlunit/html/HtmlPage.java b/src/main/java/org/htmlunit/html/HtmlPage.java
index a52a241cb47..a3a8547a386 100644
--- a/src/main/java/org/htmlunit/html/HtmlPage.java
+++ b/src/main/java/org/htmlunit/html/HtmlPage.java
@@ -139,6 +139,7 @@
* @author Lai Quang Duong
* @author Sven Strickroth
*/
+@SuppressWarnings("PMD.TooManyFields")
public class HtmlPage extends SgmlPage {
private static final Log LOG = LogFactory.getLog(HtmlPage.class);
@@ -923,7 +924,7 @@ public ScriptResult executeJavaScript(final String sourceCode) {
* Please note: Although this method is public, it is not intended for
* general execution of JavaScript. Users of HtmlUnit should interact with the pages
* as a user would by clicking on buttons or links and having the JavaScript event
- * handlers execute as needed..
+ * handlers execute as needed.
*
*
* @param sourceCode the JavaScript code to execute
@@ -2874,7 +2875,7 @@ private void readObject(final ObjectInputStream in) throws IOException, ClassNot
}
private static final class MappedElementIndexEntry implements Serializable {
- private ArrayList elements_;
+ private final ArrayList elements_;
private boolean sorted_;
MappedElementIndexEntry() {
diff --git a/src/main/java/org/htmlunit/html/HtmlTable.java b/src/main/java/org/htmlunit/html/HtmlTable.java
index a656ed38ebb..9d62e33c804 100644
--- a/src/main/java/org/htmlunit/html/HtmlTable.java
+++ b/src/main/java/org/htmlunit/html/HtmlTable.java
@@ -91,9 +91,11 @@ public final HtmlTableCell getCellAt(final int rowIndex, final int columnIndex)
return cell;
}
}
- if (cell.getRowSpan() > 1 || cell.getColumnSpan() > 1) {
- for (int i = 0; i < cell.getRowSpan(); i++) {
- for (int j = 0; j < cell.getColumnSpan(); j++) {
+ final int rowSpan = cell.getRowSpan();
+ final int columnSpan = cell.getColumnSpan();
+ if (rowSpan > 1 || columnSpan > 1) {
+ for (int i = 0; i < rowSpan; i++) {
+ for (int j = 0; j < columnSpan; j++) {
occupied.add(new Position(row + i, col + j));
}
}
diff --git a/src/main/java/org/htmlunit/html/HtmlTableCell.java b/src/main/java/org/htmlunit/html/HtmlTableCell.java
index 7225fda0abd..591a495729e 100644
--- a/src/main/java/org/htmlunit/html/HtmlTableCell.java
+++ b/src/main/java/org/htmlunit/html/HtmlTableCell.java
@@ -14,8 +14,6 @@
*/
package org.htmlunit.html;
-import static org.htmlunit.BrowserVersionFeatures.JS_TABLE_SPAN_SET_ZERO_IF_INVALID;
-
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
@@ -80,18 +78,11 @@ public int getRowSpan() {
}
try {
final int span = (int) Double.parseDouble(spanString);
- if (getPage().getWebClient().getBrowserVersion().hasFeature(JS_TABLE_SPAN_SET_ZERO_IF_INVALID)) {
- if (span < 0) {
- return 1;
- }
- if (span < 1) {
- return 0;
- }
+ if (span < 0) {
+ return 1;
}
- else {
- if (span < 1) {
- return 1;
- }
+ if (span < 1) {
+ return 0;
}
if (span > 65_534) {
diff --git a/src/main/java/org/htmlunit/html/parser/HTMLParser.java b/src/main/java/org/htmlunit/html/parser/HTMLParser.java
index adeafefec45..4c56579a337 100644
--- a/src/main/java/org/htmlunit/html/parser/HTMLParser.java
+++ b/src/main/java/org/htmlunit/html/parser/HTMLParser.java
@@ -17,6 +17,7 @@
import java.io.IOException;
import org.htmlunit.SgmlPage;
+import org.htmlunit.WebClient;
import org.htmlunit.WebResponse;
import org.htmlunit.html.DomNode;
import org.htmlunit.html.ElementFactory;
@@ -75,12 +76,19 @@ ElementFactory getElementFactory(SgmlPage page, String namespaceURI,
* @param source the (X)HTML to be parsed
* @throws SAXException if a SAX error occurs
* @throws IOException if an IO error occurs
+ *
+ * @deprecated as of version 4.12.0; use
+ * {@link #parseFragment(WebClient, DomNode, DomNode, String, boolean)} instead.
*/
- void parseFragment(DomNode parent, String source) throws SAXException, IOException;
+ @Deprecated
+ default void parseFragment(DomNode parent, String source) throws SAXException, IOException {
+ parseFragment(null, parent, parent, source, false);
+ }
/**
* Parses the HTML content from the given string into an object tree representation.
*
+ * @param webClient the {@link WebClient}
* @param parent where the new parsed nodes will be added to
* @param context the context to build the fragment context stack
* @param source the (X)HTML to be parsed
@@ -88,9 +96,41 @@ ElementFactory getElementFactory(SgmlPage page, String namespaceURI,
* @throws SAXException if a SAX error occurs
* @throws IOException if an IO error occurs
*/
- void parseFragment(DomNode parent, DomNode context, String source,
+ void parseFragment(WebClient webClient, DomNode parent, DomNode context, String source,
boolean createdByJavascript) throws SAXException, IOException;
+ /**
+ * Parses the HTML content from the given string into an object tree representation.
+ *
+ * @param parent where the new parsed nodes will be added to
+ * @param context the context to build the fragment context stack
+ * @param source the (X)HTML to be parsed
+ * @param createdByJavascript if true the (script) tag was created by javascript
+ * @throws SAXException if a SAX error occurs
+ * @throws IOException if an IO error occurs
+ *
+ * @deprecated as of version 4.12.0; use
+ * {@link #parseFragment(WebClient, DomNode, DomNode, String, boolean)} instead.
+ */
+ @Deprecated
+ default void parseFragment(DomNode parent, DomNode context, String source,
+ boolean createdByJavascript) throws SAXException, IOException {
+ parseFragment(null, parent, context, source, createdByJavascript);
+ }
+
+ /**
+ * Parses the WebResponse into an object tree representation.
+ *
+ * @param webClient the {@link WebClient}
+ * @param webResponse the response data
+ * @param page the HtmlPage to add the nodes
+ * @param xhtml if true use the XHtml parser
+ * @param createdByJavascript if true the (script) tag was created by javascript
+ * @throws IOException if there is an IO error
+ */
+ void parse(WebClient webClient, WebResponse webResponse, HtmlPage page,
+ boolean xhtml, boolean createdByJavascript) throws IOException;
+
/**
* Parses the WebResponse into an object tree representation.
*
@@ -99,6 +139,12 @@ void parseFragment(DomNode parent, DomNode context, String source,
* @param xhtml if true use the XHtml parser
* @param createdByJavascript if true the (script) tag was created by javascript
* @throws IOException if there is an IO error
+ *
+ * @deprecated as of version 4.12.0; use
+ * {@link #parse(WebClient, WebResponse, HtmlPage, boolean, boolean)} instead.
*/
- void parse(WebResponse webResponse, HtmlPage page, boolean xhtml, boolean createdByJavascript) throws IOException;
+ @Deprecated
+ default void parse(WebResponse webResponse, HtmlPage page, boolean xhtml, boolean createdByJavascript) throws IOException {
+ parse(null, webResponse, page, xhtml, createdByJavascript);
+ }
}
diff --git a/src/main/java/org/htmlunit/html/parser/neko/HtmlUnitNekoHtmlParser.java b/src/main/java/org/htmlunit/html/parser/neko/HtmlUnitNekoHtmlParser.java
index 6c40cb36074..7a601eff1f2 100644
--- a/src/main/java/org/htmlunit/html/parser/neko/HtmlUnitNekoHtmlParser.java
+++ b/src/main/java/org/htmlunit/html/parser/neko/HtmlUnitNekoHtmlParser.java
@@ -21,14 +21,15 @@
import java.net.URL;
import java.nio.charset.Charset;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
import org.htmlunit.ObjectInstantiationException;
import org.htmlunit.Page;
import org.htmlunit.SgmlPage;
import org.htmlunit.WebAssert;
+import org.htmlunit.WebClient;
import org.htmlunit.WebResponse;
import org.htmlunit.cyberneko.HTMLScanner;
import org.htmlunit.cyberneko.HTMLTagBalancer;
@@ -73,7 +74,7 @@ public final class HtmlUnitNekoHtmlParser implements HTMLParser {
*/
public static final SvgElementFactory SVG_FACTORY = new SvgElementFactory();
- private static final Map ELEMENT_FACTORIES = new HashMap<>();
+ private static final Map ELEMENT_FACTORIES = new ConcurrentHashMap<>();
static {
final DefaultElementFactory defaultElementFactory = new DefaultElementFactory();
@@ -83,30 +84,10 @@ public final class HtmlUnitNekoHtmlParser implements HTMLParser {
}
/**
- * Parses the HTML content from the given string into an object tree representation.
- *
- * @param parent the parent for the new nodes
- * @param source the (X)HTML to be parsed
- * @throws SAXException if a SAX error occurs
- * @throws IOException if an IO error occurs
+ *{@inheritDoc}
*/
@Override
- public void parseFragment(final DomNode parent, final String source) throws SAXException, IOException {
- parseFragment(parent, parent, source, false);
- }
-
- /**
- * Parses the HTML content from the given string into an object tree representation.
- *
- * @param parent where the new parsed nodes will be added to
- * @param context the context to build the fragment context stack
- * @param source the (X)HTML to be parsed
- * @param createdByJavascript if true the (script) tag was created by javascript
- * @throws SAXException if a SAX error occurs
- * @throws IOException if an IO error occurs
- */
- @Override
- public void parseFragment(final DomNode parent, final DomNode context, final String source,
+ public void parseFragment(final WebClient webClient, final DomNode parent, final DomNode context, final String source,
final boolean createdByJavascript)
throws SAXException, IOException {
final Page page = parent.getPage();
@@ -153,16 +134,10 @@ else if (ancestors.size() == 1
}
/**
- * Parses the WebResponse into an object tree representation.
- *
- * @param webResponse the response data
- * @param page the HtmlPage to add the nodes
- * @param xhtml if true use the XHtml parser
- * @param createdByJavascript if true the (script) tag was created by javascript
- * @throws IOException if there is an IO error
+ * {@inheritDoc}
*/
@Override
- public void parse(final WebResponse webResponse, final HtmlPage page,
+ public void parse(final WebClient webClient, final WebResponse webResponse, final HtmlPage page,
final boolean xhtml, final boolean createdByJavascript) throws IOException {
final URL url = webResponse.getWebRequest().getUrl();
final HtmlUnitNekoDOMBuilder domBuilder =
@@ -182,6 +157,13 @@ public void parse(final WebResponse webResponse, final HtmlPage page,
domBuilder.setFeature(HTMLScanner.STYLE_STRIP_CDATA_DELIMS, true);
domBuilder.setFeature(HTMLScanner.CDATA_EARLY_CLOSING, false);
}
+
+ if (webClient != null) {
+ final int bufferSize = webClient.getOptions().getNekoReaderBufferSize();
+ if (bufferSize > 0) {
+ domBuilder.setProperty(HTMLScanner.READER_BUFFER_SIZE, bufferSize);
+ }
+ }
}
catch (final Exception e) {
throw new ObjectInstantiationException("Error setting HTML parser feature", e);
diff --git a/src/main/java/org/htmlunit/html/serializer/HtmlSerializerVisibleText.java b/src/main/java/org/htmlunit/html/serializer/HtmlSerializerVisibleText.java
index b57f2f485a5..435a3d22201 100644
--- a/src/main/java/org/htmlunit/html/serializer/HtmlSerializerVisibleText.java
+++ b/src/main/java/org/htmlunit/html/serializer/HtmlSerializerVisibleText.java
@@ -512,9 +512,7 @@ protected void appendSelect(final HtmlSerializerTextBuilder builder,
*/
protected void appendOption(final HtmlSerializerTextBuilder builder,
final HtmlOption htmlOption, final Mode mode) {
- builder.ignoreHtmlBreaks();
appendChildren(builder, htmlOption, mode);
- builder.processHtmlBreaks();
}
/**
@@ -787,7 +785,6 @@ private enum State {
private final StringBuilder builder_;
private int trimRightPos_;
private boolean contentAdded_;
- private boolean ignoreHtmlBreaks_;
/**
* Ctor.
@@ -985,10 +982,6 @@ public void appendBlockSeparator() {
* @param mode the {@link Mode}
*/
public void appendBreak(final Mode mode) {
- if (ignoreHtmlBreaks_) {
- return;
- }
-
builder_.setLength(trimRightPos_);
builder_.append('\n');
@@ -1046,20 +1039,6 @@ public void resetContentAdded() {
contentAdded_ = false;
}
- /**
- * Ignore the following html breaks in the content to be added.
- */
- public void ignoreHtmlBreaks() {
- ignoreHtmlBreaks_ = true;
- }
-
- /**
- * Prozess the following html breaks in the content to be added.
- */
- public void processHtmlBreaks() {
- ignoreHtmlBreaks_ = false;
- }
-
/**
* @return the constructed text.
*/
diff --git a/src/main/java/org/htmlunit/html/xpath/XPathAdapter.java b/src/main/java/org/htmlunit/html/xpath/XPathAdapter.java
index ade9c47c8b8..203c0cc72da 100644
--- a/src/main/java/org/htmlunit/html/xpath/XPathAdapter.java
+++ b/src/main/java/org/htmlunit/html/xpath/XPathAdapter.java
@@ -45,14 +45,6 @@ private enum STATE {
}
private final Expression mainExp_;
- private FunctionTable funcTable_;
-
- /**
- * Initiates the function table.
- */
- private void initFunctionTable() {
- funcTable_ = new FunctionTable();
- }
/**
* Constructor.
@@ -64,11 +56,9 @@ private void initFunctionTable() {
public XPathAdapter(final String exprString, final PrefixResolver prefixResolver, final boolean caseSensitive)
throws TransformerException {
- initFunctionTable();
-
final ErrorListener errorHandler = new DefaultErrorHandler();
final XPathParser parser = new XPathParser(errorHandler);
- final Compiler compiler = new Compiler(errorHandler, funcTable_);
+ final Compiler compiler = new Compiler(errorHandler, new FunctionTable());
final String expression = preProcessXPath(exprString, caseSensitive);
parser.initXPath(compiler, expression, prefixResolver);
diff --git a/src/main/java/org/htmlunit/html/xpath/XPathHelper.java b/src/main/java/org/htmlunit/html/xpath/XPathHelper.java
index e7e675ff736..881058edc74 100644
--- a/src/main/java/org/htmlunit/html/xpath/XPathHelper.java
+++ b/src/main/java/org/htmlunit/html/xpath/XPathHelper.java
@@ -112,7 +112,8 @@ public static List getByXPath(final Node node, final XPathAdapter xpath,
if (result instanceof XNodeSet) {
final NodeList nodelist = result.nodelist();
- for (int i = 0; i < nodelist.getLength(); i++) {
+ final int length = nodelist.getLength();
+ for (int i = 0; i < length; i++) {
list.add((T) nodelist.item(i));
}
}
diff --git a/src/main/java/org/htmlunit/httpclient/HtmlUnitBrowserCompatCookieSpec.java b/src/main/java/org/htmlunit/httpclient/HtmlUnitBrowserCompatCookieSpec.java
index 893ec8bf758..fce1220057f 100644
--- a/src/main/java/org/htmlunit/httpclient/HtmlUnitBrowserCompatCookieSpec.java
+++ b/src/main/java/org/htmlunit/httpclient/HtmlUnitBrowserCompatCookieSpec.java
@@ -223,7 +223,8 @@ public List formatCookies(final List cookies) {
final CharArrayBuffer buffer = new CharArrayBuffer(20 * cookies.size());
buffer.append(SM.COOKIE);
buffer.append(": ");
- for (int i = 0; i < cookies.size(); i++) {
+ final int size = cookies.size();
+ for (int i = 0; i < size; i++) {
final Cookie cookie = cookies.get(i);
if (i > 0) {
buffer.append("; ");
diff --git a/src/main/java/org/htmlunit/javascript/HtmlUnitScriptable.java b/src/main/java/org/htmlunit/javascript/HtmlUnitScriptable.java
index 1ac52788f88..608f982b3e0 100644
--- a/src/main/java/org/htmlunit/javascript/HtmlUnitScriptable.java
+++ b/src/main/java/org/htmlunit/javascript/HtmlUnitScriptable.java
@@ -117,22 +117,17 @@ public void put(final String name, final Scriptable start, final Object value) {
/**
* Gets a named property from the object.
* Normally HtmlUnit objects don't need to overwrite this method as properties are defined
- * on the prototypes from the XML configuration. In some cases where "content" of object
+ * on the prototypes. In some cases where "content" of object
* has priority compared to the properties consider using utility {@link #getWithPreemption(String)}.
+ *
* {@inheritDoc}
*/
@Override
public Object get(final String name, final Scriptable start) {
// Try to get property configured on object itself.
- Object response = super.get(name, start);
- if (response != NOT_FOUND) {
- return response;
- }
- if (this == start) {
- response = getWithPreemption(name);
- }
- if (response == NOT_FOUND && start instanceof Window) {
- response = ((Window) start).getWithFallback(name);
+ final Object response = super.get(name, start);
+ if (response == NOT_FOUND && this == start) {
+ return getWithPreemption(name);
}
return response;
}
diff --git a/src/main/java/org/htmlunit/javascript/JavaScriptEngine.java b/src/main/java/org/htmlunit/javascript/JavaScriptEngine.java
index 9b93756300f..7d299d2301c 100644
--- a/src/main/java/org/htmlunit/javascript/JavaScriptEngine.java
+++ b/src/main/java/org/htmlunit/javascript/JavaScriptEngine.java
@@ -103,7 +103,7 @@
* @author Sven Strickroth
*
* @see
- * Rhino and Java Browser
+ * Rhino and Java Browser
*/
public class JavaScriptEngine implements AbstractJavaScriptEngine\n"
+ + "