/* * Copyright (c) 2002-2008 Gargoyle Software Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.gargoylesoftware.htmlunit.javascript.host; import static org.apache.commons.lang.StringUtils.defaultIfEmpty; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.SortedMap; import java.util.TreeMap; import org.apache.commons.lang.math.NumberUtils; import org.mozilla.javascript.Context; import com.gargoylesoftware.htmlunit.html.DomNode; import com.gargoylesoftware.htmlunit.html.HtmlElement; import com.gargoylesoftware.htmlunit.html.HtmlHead; /** * A JavaScript object for a ComputedCSSStyleDeclaration. * * @version $Revision$ * @author Ahmed Ashour * @author Marc Guillemot */ public class ComputedCSSStyleDeclaration extends CSSStyleDeclaration { private static final long serialVersionUID = -1883166331827717255L; /** * Local modifications maintained here rather than in the element. We use a sorted * map so that results are deterministic and thus easily testable. */ private SortedMap localModifications_ = new TreeMap(); private static final Map DEFAULT_DISPLAY; static { final Map map = new HashMap(); map.put("A", "inline"); map.put("CODE", "inline"); map.put("LI", "list-item"); map.put("SPAN", "inline"); map.put("TABLE", "table"); map.put("TBODY", "table-row-group"); map.put("TD", "table-cell"); map.put("TH", "table-cell"); map.put("THEAD", "table-header-group"); map.put("TR", "table-row"); DEFAULT_DISPLAY = Collections.unmodifiableMap(map); } /** * Create an instance. JavaScript objects must have a default constructor. */ public ComputedCSSStyleDeclaration() { // Empty. } /** * @param style the original Style */ ComputedCSSStyleDeclaration(final CSSStyleDeclaration style) { super(style.getHTMLElement()); getHTMLElement().setDefaults(this); } /** * {@inheritDoc} * * This method does nothing as the object is read-only. */ @Override protected void setStyleAttribute(final String name, final String newValue) { // Empty. } /** * Makes a local, "computed", modification to this CSS style. * * @param name the name of the style attribute to set * @param newValue the value of the style attribute to set */ void setLocalStyleAttribute(final String name, final String newValue) { final StyleElement element = new StyleElement(name, newValue, getCurrentElementIndex()); localModifications_.put(name, element); } /** * Makes a local, "computed", modification to this CSS style that won't override other * style attributes of the same name. This method should be used to set default values * for style attributes. * * @param name the name of the style attribute to set * @param newValue the value of the style attribute to set */ void setDefaultLocalStyleAttribute(final String name, final String newValue) { final StyleElement element = new StyleElement(name, newValue); localModifications_.put(name, element); } /** * {@inheritDoc} */ @Override protected Map getStyleMap(final boolean camelCase) { final Map styleMap = super.getStyleMap(camelCase); if (localModifications_ != null) { for (final StyleElement e : localModifications_.values()) { String key = e.getName(); if (camelCase) { key = camelize(key); } final StyleElement existent = styleMap.get(key); final StyleElement element = new StyleElement(key, e.getValue(), e.getIndex()); if (existent == null || !element.isDefault()) { styleMap.put(key, element); } } } return styleMap; } /** * {@inheritDoc} */ @Override public String jsxGet_backgroundAttachment() { return defaultIfEmpty(super.jsxGet_backgroundAttachment(), "scroll"); } /** * {@inheritDoc} */ @Override public String jsxGet_backgroundColor() { return defaultIfEmpty(super.jsxGet_backgroundColor(), "transparent"); } /** * {@inheritDoc} */ @Override public String jsxGet_backgroundImage() { return defaultIfEmpty(super.jsxGet_backgroundImage(), "none"); } /** * {@inheritDoc} */ @Override public String jsxGet_backgroundRepeat() { return defaultIfEmpty(super.jsxGet_backgroundRepeat(), "repeat"); } /** * {@inheritDoc} */ @Override public String jsxGet_borderBottomColor() { return defaultIfEmpty(super.jsxGet_borderBottomColor(), "rgb(0, 0, 0)"); } /** * {@inheritDoc} */ @Override public String jsxGet_borderBottomStyle() { return defaultIfEmpty(super.jsxGet_borderBottomStyle(), "none"); } /** * {@inheritDoc} */ @Override public String jsxGet_borderBottomWidth() { return defaultIfEmpty(super.jsxGet_borderBottomWidth(), "0px"); } /** * {@inheritDoc} */ @Override public String jsxGet_borderCollapse() { return defaultIfEmpty(super.jsxGet_borderCollapse(), "separate"); } /** * {@inheritDoc} */ @Override public String jsxGet_borderLeftColor() { return defaultIfEmpty(super.jsxGet_borderLeftColor(), "rgb(0, 0, 0)"); } /** * {@inheritDoc} */ @Override public String jsxGet_borderLeftStyle() { return defaultIfEmpty(super.jsxGet_borderLeftStyle(), "none"); } /** * {@inheritDoc} */ @Override public String jsxGet_borderLeftWidth() { return defaultIfEmpty(super.jsxGet_borderLeftWidth(), "0px"); } /** * {@inheritDoc} */ @Override public String jsxGet_borderRightColor() { return defaultIfEmpty(super.jsxGet_borderRightColor(), "rgb(0, 0, 0)"); } /** * {@inheritDoc} */ @Override public String jsxGet_borderRightStyle() { return defaultIfEmpty(super.jsxGet_borderRightStyle(), "none"); } /** * {@inheritDoc} */ @Override public String jsxGet_borderRightWidth() { return defaultIfEmpty(super.jsxGet_borderRightWidth(), "0px"); } /** * {@inheritDoc} */ @Override public String jsxGet_borderSpacing() { return defaultIfEmpty(super.jsxGet_borderSpacing(), "0px 0px"); } /** * {@inheritDoc} */ @Override public String jsxGet_borderTopColor() { return defaultIfEmpty(super.jsxGet_borderTopColor(), "rgb(0, 0, 0)"); } /** * {@inheritDoc} */ @Override public String jsxGet_borderTopStyle() { return defaultIfEmpty(super.jsxGet_borderTopStyle(), "none"); } /** * {@inheritDoc} */ @Override public String jsxGet_borderTopWidth() { return defaultIfEmpty(super.jsxGet_borderTopWidth(), "0px"); } /** * {@inheritDoc} */ @Override public String jsxGet_bottom() { return defaultIfEmpty(super.jsxGet_bottom(), "auto"); } /** * {@inheritDoc} */ @Override public String jsxGet_captionSide() { return defaultIfEmpty(super.jsxGet_captionSide(), "top"); } /** * {@inheritDoc} */ @Override public String jsxGet_clear() { return defaultIfEmpty(super.jsxGet_clear(), "none"); } /** * {@inheritDoc} */ @Override public String jsxGet_clip() { return defaultIfEmpty(super.jsxGet_clip(), "auto"); } /** * {@inheritDoc} */ @Override public String jsxGet_color() { return defaultIfEmpty(super.jsxGet_color(), "rgb(0, 0, 0)"); } /** * {@inheritDoc} */ @Override public String jsxGet_counterIncrement() { return defaultIfEmpty(super.jsxGet_counterIncrement(), "none"); } /** * {@inheritDoc} */ @Override public String jsxGet_counterReset() { return defaultIfEmpty(super.jsxGet_counterReset(), "none"); } /** * {@inheritDoc} */ @Override public String jsxGet_cssFloat() { return defaultIfEmpty(super.jsxGet_cssFloat(), "none"); } /** * {@inheritDoc} */ @Override public String jsxGet_cursor() { return defaultIfEmpty(super.jsxGet_cursor(), "auto"); } /** * {@inheritDoc} */ @Override public String jsxGet_direction() { return defaultIfEmpty(super.jsxGet_direction(), "ltr"); } /** * {@inheritDoc} */ @Override public String jsxGet_display() { return defaultIfEmpty(super.jsxGet_display(), getDefaultStyleDisplay()); } private String getDefaultStyleDisplay() { final String defaultValue = DEFAULT_DISPLAY.get(getHTMLElement().jsxGet_tagName()); if (defaultValue == null) { return "block"; } return defaultValue; } /** * {@inheritDoc} */ @Override public String jsxGet_emptyCells() { return defaultIfEmpty(super.jsxGet_emptyCells(), "-moz-show-background"); } /** * {@inheritDoc} */ @Override public String jsxGet_fontFamily() { return defaultIfEmpty(super.jsxGet_fontFamily(), "serif"); } /** * {@inheritDoc} */ @Override public String jsxGet_fontSize() { return defaultIfEmpty(super.jsxGet_fontSize(), "16px"); } /** * {@inheritDoc} */ @Override public String jsxGet_fontSizeAdjust() { return defaultIfEmpty(super.jsxGet_fontSizeAdjust(), "none"); } /** * {@inheritDoc} */ @Override public String jsxGet_fontStyle() { return defaultIfEmpty(super.jsxGet_fontStyle(), "normal"); } /** * {@inheritDoc} */ @Override public String jsxGet_fontVariant() { return defaultIfEmpty(super.jsxGet_fontVariant(), "normal"); } /** * {@inheritDoc} */ @Override public String jsxGet_fontWeight() { return defaultIfEmpty(super.jsxGet_fontWeight(), "400"); } /** * {@inheritDoc} */ @Override public String jsxGet_height() { return defaultIfEmpty(super.jsxGet_height(), "363px"); } /** * {@inheritDoc} */ @Override public String jsxGet_left() { return defaultIfEmpty(super.jsxGet_left(), "auto"); } /** * {@inheritDoc} */ @Override public String jsxGet_letterSpacing() { return defaultIfEmpty(super.jsxGet_letterSpacing(), "normal"); } /** * {@inheritDoc} */ @Override public String jsxGet_lineHeight() { return defaultIfEmpty(super.jsxGet_lineHeight(), "normal"); } /** * {@inheritDoc} */ @Override public String jsxGet_listStyleImage() { return defaultIfEmpty(super.jsxGet_listStyleImage(), "none"); } /** * {@inheritDoc} */ @Override public String jsxGet_listStylePosition() { return defaultIfEmpty(super.jsxGet_listStylePosition(), "outside"); } /** * {@inheritDoc} */ @Override public String jsxGet_listStyleType() { return defaultIfEmpty(super.jsxGet_listStyleType(), "disc"); } /** * {@inheritDoc} */ @Override public String jsxGet_marginBottom() { return defaultIfEmpty(super.jsxGet_marginBottom(), "0px"); } /** * {@inheritDoc} */ @Override public String jsxGet_marginLeft() { return defaultIfEmpty(super.jsxGet_marginLeft(), "0px"); } /** * {@inheritDoc} */ @Override public String jsxGet_marginRight() { return defaultIfEmpty(super.jsxGet_marginRight(), "0px"); } /** * {@inheritDoc} */ @Override public String jsxGet_marginTop() { return defaultIfEmpty(super.jsxGet_marginTop(), "0px"); } /** * {@inheritDoc} */ @Override public String jsxGet_markerOffset() { return defaultIfEmpty(super.jsxGet_markerOffset(), "none"); } /** * {@inheritDoc} */ @Override public String jsxGet_maxHeight() { return defaultIfEmpty(super.jsxGet_maxHeight(), "none"); } /** * {@inheritDoc} */ @Override public String jsxGet_maxWidth() { return defaultIfEmpty(super.jsxGet_maxWidth(), "none"); } /** * {@inheritDoc} */ @Override public String jsxGet_minHeight() { return defaultIfEmpty(super.jsxGet_minHeight(), "0px"); } /** * {@inheritDoc} */ @Override public String jsxGet_minWidth() { return defaultIfEmpty(super.jsxGet_minWidth(), "0px"); } /** * {@inheritDoc} */ @Override public String jsxGet_MozAppearance() { return defaultIfEmpty(super.jsxGet_MozAppearance(), "none"); } /** * {@inheritDoc} */ @Override public String jsxGet_MozBackgroundClip() { return defaultIfEmpty(super.jsxGet_MozBackgroundClip(), "border"); } /** * {@inheritDoc} */ @Override public String jsxGet_MozBackgroundInlinePolicy() { return defaultIfEmpty(super.jsxGet_MozBackgroundInlinePolicy(), "continuous"); } /** * {@inheritDoc} */ @Override public String jsxGet_MozBackgroundOrigin() { return defaultIfEmpty(super.jsxGet_MozBackgroundOrigin(), "padding"); } /** * {@inheritDoc} */ @Override public String jsxGet_MozBinding() { return defaultIfEmpty(super.jsxGet_MozBinding(), "none"); } /** * {@inheritDoc} */ @Override public String jsxGet_MozBorderBottomColors() { return defaultIfEmpty(super.jsxGet_MozBorderBottomColors(), "none"); } /** * {@inheritDoc} */ @Override public String jsxGet_MozBorderLeftColors() { return defaultIfEmpty(super.jsxGet_MozBorderLeftColors(), "none"); } /** * {@inheritDoc} */ @Override public String jsxGet_MozBorderRadiusBottomleft() { return defaultIfEmpty(super.jsxGet_MozBorderRadiusBottomleft(), "0px"); } /** * {@inheritDoc} */ @Override public String jsxGet_MozBorderRadiusBottomright() { return defaultIfEmpty(super.jsxGet_MozBorderRadiusBottomright(), "0px"); } /** * {@inheritDoc} */ @Override public String jsxGet_MozBorderRadiusTopleft() { return defaultIfEmpty(super.jsxGet_MozBorderRadiusTopleft(), "0px"); } /** * {@inheritDoc} */ @Override public String jsxGet_MozBorderRadiusTopright() { return defaultIfEmpty(super.jsxGet_MozBorderRadiusTopright(), "0px"); } /** * {@inheritDoc} */ @Override public String jsxGet_MozBorderRightColors() { return defaultIfEmpty(super.jsxGet_MozBorderRightColors(), "none"); } /** * {@inheritDoc} */ @Override public String jsxGet_MozBorderTopColors() { return defaultIfEmpty(super.jsxGet_MozBorderTopColors(), "none"); } /** * {@inheritDoc} */ @Override public String jsxGet_MozBoxAlign() { return defaultIfEmpty(super.jsxGet_MozBoxAlign(), "stretch"); } /** * {@inheritDoc} */ @Override public String jsxGet_MozBoxDirection() { return defaultIfEmpty(super.jsxGet_MozBoxDirection(), "normal"); } /** * {@inheritDoc} */ @Override public String jsxGet_MozBoxFlex() { return defaultIfEmpty(super.jsxGet_MozBoxFlex(), "0"); } /** * {@inheritDoc} */ @Override public String jsxGet_MozBoxOrdinalGroup() { return defaultIfEmpty(super.jsxGet_MozBoxOrdinalGroup(), "1"); } /** * {@inheritDoc} */ @Override public String jsxGet_MozBoxOrient() { return defaultIfEmpty(super.jsxGet_MozBoxOrient(), "horizontal"); } /** * {@inheritDoc} */ @Override public String jsxGet_MozBoxPack() { return defaultIfEmpty(super.jsxGet_MozBoxPack(), "start"); } /** * {@inheritDoc} */ @Override public String jsxGet_MozBoxSizing() { return defaultIfEmpty(super.jsxGet_MozBoxSizing(), "content-box"); } /** * {@inheritDoc} */ @Override public String jsxGet_MozColumnCount() { return defaultIfEmpty(super.jsxGet_MozColumnCount(), "auto"); } /** * {@inheritDoc} */ @Override public String jsxGet_MozColumnGap() { return defaultIfEmpty(super.jsxGet_MozColumnGap(), "0px"); } /** * {@inheritDoc} */ @Override public String jsxGet_MozColumnWidth() { return defaultIfEmpty(super.jsxGet_MozColumnWidth(), "auto"); } /** * {@inheritDoc} */ @Override public String jsxGet_MozFloatEdge() { return defaultIfEmpty(super.jsxGet_MozFloatEdge(), "content-box"); } /** * {@inheritDoc} */ @Override public String jsxGet_MozImageRegion() { return defaultIfEmpty(super.jsxGet_MozImageRegion(), "auto"); } /** * {@inheritDoc} */ @Override public String jsxGet_MozOpacity() { return defaultIfEmpty(super.jsxGet_MozOpacity(), "1"); } /** * {@inheritDoc} */ @Override public String jsxGet_MozOutlineColor() { return defaultIfEmpty(super.jsxGet_MozOutlineColor(), "rgb(0, 0, 0)"); } /** * {@inheritDoc} */ @Override public String jsxGet_MozOutlineOffset() { return defaultIfEmpty(super.jsxGet_MozOutlineOffset(), "0px"); } /** * {@inheritDoc} */ @Override public String jsxGet_MozOutlineRadiusBottomleft() { return defaultIfEmpty(super.jsxGet_MozOutlineRadiusBottomleft(), "0px"); } /** * {@inheritDoc} */ @Override public String jsxGet_MozOutlineRadiusBottomright() { return defaultIfEmpty(super.jsxGet_MozOutlineRadiusBottomright(), "0px"); } /** * {@inheritDoc} */ @Override public String jsxGet_MozOutlineRadiusTopleft() { return defaultIfEmpty(super.jsxGet_MozOutlineRadiusTopleft(), "0px"); } /** * {@inheritDoc} */ @Override public String jsxGet_MozOutlineRadiusTopright() { return defaultIfEmpty(super.jsxGet_MozOutlineRadiusTopright(), "0px"); } /** * {@inheritDoc} */ @Override public String jsxGet_MozOutlineStyle() { return defaultIfEmpty(super.jsxGet_MozOutlineStyle(), "none"); } /** * {@inheritDoc} */ @Override public String jsxGet_MozOutlineWidth() { return defaultIfEmpty(super.jsxGet_MozOutlineWidth(), "0px"); } /** * {@inheritDoc} */ @Override public String jsxGet_MozUserFocus() { return defaultIfEmpty(super.jsxGet_MozUserFocus(), "none"); } /** * {@inheritDoc} */ @Override public String jsxGet_MozUserInput() { return defaultIfEmpty(super.jsxGet_MozUserInput(), "auto"); } /** * {@inheritDoc} */ @Override public String jsxGet_MozUserModify() { return defaultIfEmpty(super.jsxGet_MozUserModify(), "read-only"); } /** * {@inheritDoc} */ @Override public String jsxGet_MozUserSelect() { return defaultIfEmpty(super.jsxGet_MozUserSelect(), "auto"); } /** * {@inheritDoc} */ @Override public String jsxGet_opacity() { return defaultIfEmpty(super.jsxGet_opacity(), "1"); } /** * {@inheritDoc} */ @Override public String jsxGet_outlineColor() { return defaultIfEmpty(super.jsxGet_outlineColor(), "rgb(0, 0, 0)"); } /** * {@inheritDoc} */ @Override public String jsxGet_outlineOffset() { return defaultIfEmpty(super.jsxGet_outlineOffset(), "0px"); } /** * {@inheritDoc} */ @Override public String jsxGet_outlineStyle() { return defaultIfEmpty(super.jsxGet_outlineStyle(), "none"); } /** * {@inheritDoc} */ @Override public String jsxGet_outlineWidth() { return defaultIfEmpty(super.jsxGet_outlineWidth(), "0px"); } /** * {@inheritDoc} */ @Override public String jsxGet_overflow() { return defaultIfEmpty(super.jsxGet_overflow(), "visible"); } /** * {@inheritDoc} */ @Override public String jsxGet_overflowX() { return defaultIfEmpty(super.jsxGet_overflowX(), "visible"); } /** * {@inheritDoc} */ @Override public String jsxGet_overflowY() { return defaultIfEmpty(super.jsxGet_overflowY(), "visible"); } /** * {@inheritDoc} */ @Override public String jsxGet_paddingBottom() { return defaultIfEmpty(super.jsxGet_paddingBottom(), "0px"); } /** * {@inheritDoc} */ @Override public String jsxGet_paddingLeft() { return defaultIfEmpty(super.jsxGet_paddingLeft(), "0px"); } /** * {@inheritDoc} */ @Override public String jsxGet_paddingRight() { return defaultIfEmpty(super.jsxGet_paddingRight(), "0px"); } /** * {@inheritDoc} */ @Override public String jsxGet_paddingTop() { return defaultIfEmpty(super.jsxGet_paddingTop(), "0px"); } /** * {@inheritDoc} */ @Override public String jsxGet_position() { return defaultIfEmpty(super.jsxGet_position(), "static"); } /** * {@inheritDoc} */ @Override public String jsxGet_right() { return defaultIfEmpty(super.jsxGet_right(), "auto"); } /** * {@inheritDoc} */ @Override public String jsxGet_tableLayout() { return defaultIfEmpty(super.jsxGet_tableLayout(), "auto"); } /** * {@inheritDoc} */ @Override public String jsxGet_textAlign() { return defaultIfEmpty(super.jsxGet_textAlign(), "start"); } /** * {@inheritDoc} */ @Override public String jsxGet_textDecoration() { return defaultIfEmpty(super.jsxGet_textDecoration(), "none"); } /** * {@inheritDoc} */ @Override public String jsxGet_textIndent() { return defaultIfEmpty(super.jsxGet_textIndent(), "0px"); } /** * {@inheritDoc} */ @Override public String jsxGet_textTransform() { return defaultIfEmpty(super.jsxGet_textTransform(), "none"); } /** * {@inheritDoc} */ @Override public String jsxGet_top() { return defaultIfEmpty(super.jsxGet_top(), "auto"); } /** * {@inheritDoc} */ @Override public String jsxGet_unicodeBidi() { return defaultIfEmpty(super.jsxGet_unicodeBidi(), "normal"); } /** * {@inheritDoc} */ @Override public String jsxGet_verticalAlign() { return defaultIfEmpty(super.jsxGet_verticalAlign(), "baseline"); } /** * {@inheritDoc} */ @Override public String jsxGet_visibility() { return defaultIfEmpty(super.jsxGet_visibility(), "visible"); } /** * {@inheritDoc} */ @Override public String jsxGet_whiteSpace() { return defaultIfEmpty(super.jsxGet_whiteSpace(), "normal"); } /** * {@inheritDoc} */ @Override public String jsxGet_width() { final String defaultWidth; if (jsxGet_display().equals("none")) { defaultWidth = "auto"; } else { defaultWidth = "1256px"; } return defaultIfEmpty(super.jsxGet_width(), defaultWidth); } /** * Returns the element's width, possibly including its padding and border. * @param includeBorder whether or not to include the border width in the returned value * @param includePadding whether or not to include the padding width in the returned value * @return the element's width, possibly including its padding and border */ int getCalculatedWidth(final boolean includeBorder, final boolean includePadding) { int width = intValue(super.jsxGet_width()); if (includeBorder) { final int borderLeft = intValue(jsxGet_borderLeftWidth()); final int borderRight = intValue(jsxGet_borderRightWidth()); width += borderLeft + borderRight; } if (includePadding) { final int paddingLeft = intValue(jsxGet_paddingLeft()); final int paddingRight = intValue(jsxGet_paddingRight()); width += paddingLeft + paddingRight; } return width; } /** * Returns the element's height, possibly including its padding and border. * @param includeBorder whether or not to include the border height in the returned value * @param includePadding whether or not to include the padding height in the returned value * @return the element's height, possibly including its padding and border */ int getCalculatedHeight(final boolean includeBorder, final boolean includePadding) { int height = intValue(super.jsxGet_height()); if (includeBorder) { final int borderTop = intValue(jsxGet_borderTopWidth()); final int borderBottom = intValue(jsxGet_borderBottomWidth()); height += borderTop + borderBottom; } if (includePadding) { final int paddingTop = intValue(jsxGet_paddingTop()); final int paddingBottom = intValue(jsxGet_paddingBottom()); height += paddingTop + paddingBottom; } return height; } /** * Returns the computed top (Y coordinate), relative to the node's parent's top edge. * @param includeMargin whether or not to take the margin into account in the calculation * @param includeBorder whether or not to take the border into account in the calculation * @param includePadding whether or not to take the padding into account in the calculation * @return the computed top (Y coordinate), relative to the node's parent's top edge */ int getTop(final boolean includeMargin, final boolean includeBorder, final boolean includePadding) { final String p = jsxGet_position(); final String t = jsxGet_top(); final String b = jsxGet_bottom(); int top; if ("absolute".equals(p) && !"auto".equals(t)) { // No need to calculate displacement caused by sibling nodes. top = intValue(t); } else if ("absolute".equals(p) && !"auto".equals(b)) { // Estimate the vertical displacement caused by *all* siblings. // This is very rough, and doesn't even take position or display types into account (hence the // need for the explicit check for HtmlHead elements, which are display:none in regular UAs). // It also doesn't take into account the fact that the parent's height may be hardcoded in CSS. top = 0; DomNode child = this.getHTMLElement().getDomNodeOrDie().getParentNode().getFirstChild(); while (child != null) { if (child instanceof HtmlElement && !(child instanceof HtmlHead)) { top += 20; } child = child.getPreviousSibling(); } top -= intValue(b); } else { // Estimate the vertical displacement caused by *previous* siblings. // This is very rough, and doesn't even take position or display types into account (hence the // need for the explicit check for HtmlHead elements, which are display:none in regular UAs). top = 0; DomNode prev = this.getHTMLElement().getDomNodeOrDie().getPreviousSibling(); while (prev != null) { if (prev instanceof HtmlElement && !(prev instanceof HtmlHead)) { top += 20; } prev = prev.getPreviousSibling(); } } if (includeMargin) { final int margin = intValue(jsxGet_marginTop()); top += margin; } if (includeBorder) { final int border = intValue(jsxGet_borderTopWidth()); top += border; } if (includePadding) { final int padding = intValue(jsxGet_paddingTop()); top += padding; } return top; } /** * Returns the computed left (X coordinate), relative to the node's parent's left edge. * @param includeMargin whether or not to take the margin into account in the calculation * @param includeBorder whether or not to take the border into account in the calculation * @param includePadding whether or not to take the padding into account in the calculation * @return the computed left (X coordinate), relative to the node's parent's left edge */ int getLeft(final boolean includeMargin, final boolean includeBorder, final boolean includePadding) { final String p = jsxGet_position(); final String l = jsxGet_left(); final String r = jsxGet_right(); int left; if ("absolute".equals(p) && !"auto".equals(l)) { // No need to calculate displacement caused by sibling nodes. left = intValue(l); } else if ("absolute".equals(p) && !"auto".equals(r)) { // We *should* calculate the horizontal displacement caused by *all* siblings. // However, that would require us to retrieve computed styles for all siblings, // and that sounds like a lot of work. We'll use a bogus parent width until a // scenario arises that requires a more exact calculation. left = 200 - intValue(r); } else { // We *should* calculate the horizontal displacement caused by *previous* siblings. // However, that would require us to retrieve computed styles for these siblings, // and that also sounds like a lot of work. We'll just use 0, which is actually correct // for block elements. left = 0; } if (includeMargin) { final int margin = intValue(jsxGet_marginLeft()); left += margin; } if (includeBorder) { final int border = intValue(jsxGet_borderLeftWidth()); left += border; } if (includePadding) { final int padding = intValue(jsxGet_paddingLeft()); left += padding; } return left; } /** * Converts the specified length string value into an integer number of pixels. * @param value the length string value to convert to an integer number of pixels * @return the integer number of pixels corresponding to the specified length string value */ private int intValue(final String value) { return NumberUtils.toInt(value.replaceAll("(\\d+).*", "$1"), 0); } /** * {@inheritDoc} */ @Override public String jsxGet_wordSpacing() { return defaultIfEmpty(super.jsxGet_wordSpacing(), "normal"); } /** * {@inheritDoc} */ @Override public Object jsxGet_zIndex() { final Object response = super.jsxGet_zIndex(); if (response.toString().length() == 0) { return "auto"; } return response; } /** * {@inheritDoc} */ @Override public String jsxFunction_getPropertyValue(final String name) { // need to invoke the getter to take care of the default value final String response = Context.toString(getProperty(this, camelize(name))); if (response == NOT_FOUND) { return super.jsxFunction_getPropertyValue(name); } return response; } }