com.gargoylesoftware.htmlunit.javascript.host.css.CSSStyleDeclaration Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of htmlunit Show documentation
Show all versions of htmlunit Show documentation
A headless browser intended for use in testing web-based applications.
/*
* Copyright (c) 2002-2020 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.css;
import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.CSS_BACKGROUND_INITIAL;
import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.CSS_BACKGROUND_RGBA;
import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.CSS_LENGTH_INITIAL;
import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.CSS_OUTLINE_WIDTH_UNIT_NOT_REQUIRED;
import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.CSS_SET_NULL_THROWS;
import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.CSS_SUPPORTS_BEHAVIOR_PROPERTY;
import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.CSS_VERTICAL_ALIGN_SUPPORTS_AUTO;
import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.CSS_ZINDEX_TYPE_INTEGER;
import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.JS_STYLE_SET_PROPERTY_IMPORTANT_IGNORES_CASE;
import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.JS_STYLE_UNSUPPORTED_PROPERTY_GETTER;
import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.JS_STYLE_WORD_SPACING_ACCEPTS_PERCENT;
import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.JS_STYLE_WRONG_INDEX_RETURNS_UNDEFINED;
import static com.gargoylesoftware.htmlunit.javascript.configuration.SupportedBrowser.CHROME;
import static com.gargoylesoftware.htmlunit.javascript.configuration.SupportedBrowser.FF;
import static com.gargoylesoftware.htmlunit.javascript.configuration.SupportedBrowser.FF60;
import static com.gargoylesoftware.htmlunit.javascript.configuration.SupportedBrowser.FF68;
import static com.gargoylesoftware.htmlunit.javascript.configuration.SupportedBrowser.IE;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.ACCELERATOR;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.BACKGROUND;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.BACKGROUND_ATTACHMENT;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.BACKGROUND_COLOR;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.BACKGROUND_IMAGE;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.BACKGROUND_POSITION;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.BACKGROUND_REPEAT;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.BEHAVIOR;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.BORDER;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.BORDER_BOTTOM;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.BORDER_BOTTOM_COLOR;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.BORDER_BOTTOM_STYLE;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.BORDER_BOTTOM_WIDTH;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.BORDER_LEFT;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.BORDER_LEFT_COLOR;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.BORDER_LEFT_STYLE;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.BORDER_LEFT_WIDTH;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.BORDER_RIGHT;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.BORDER_RIGHT_COLOR;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.BORDER_RIGHT_STYLE;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.BORDER_RIGHT_WIDTH;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.BORDER_TOP;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.BORDER_TOP_COLOR;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.BORDER_TOP_STYLE;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.BORDER_TOP_WIDTH;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.BORDER_WIDTH;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.BOTTOM;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.COLOR;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.DISPLAY;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.FLOAT;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.FONT;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.FONT_FAMILY;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.FONT_SIZE;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.HEIGHT;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.LEFT;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.LETTER_SPACING;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.LINE_HEIGHT;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.MARGIN;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.MARGIN_BOTTOM;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.MARGIN_LEFT;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.MARGIN_RIGHT;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.MARGIN_TOP;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.MAX_HEIGHT;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.MAX_WIDTH;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.MIN_HEIGHT;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.MIN_WIDTH;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.OPACITY;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.ORPHANS;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.OUTLINE;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.OUTLINE_WIDTH;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.PADDING;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.PADDING_BOTTOM;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.PADDING_LEFT;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.PADDING_RIGHT;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.PADDING_TOP;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.PAGE;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.POSITION;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.RIGHT;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.RUBY_ALIGN;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.SIZE;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.TEXT_INDENT;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.TOP;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.VERTICAL_ALIGN;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.WIDOWS;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.WIDTH;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.WORD_SPACING;
import static com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition.Z_INDEX_;
import static org.apache.commons.lang3.StringUtils.defaultIfEmpty;
import java.awt.Color;
import java.io.IOException;
import java.text.MessageFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.gargoylesoftware.css.dom.CSSStyleDeclarationImpl;
import com.gargoylesoftware.css.dom.CSSValueImpl;
import com.gargoylesoftware.css.parser.CSSErrorHandler;
import com.gargoylesoftware.css.parser.CSSOMParser;
import com.gargoylesoftware.css.parser.javacc.CSS3Parser;
import com.gargoylesoftware.htmlunit.BrowserVersion;
import com.gargoylesoftware.htmlunit.WebAssert;
import com.gargoylesoftware.htmlunit.css.StyleElement;
import com.gargoylesoftware.htmlunit.html.HtmlElement;
import com.gargoylesoftware.htmlunit.javascript.SimpleScriptable;
import com.gargoylesoftware.htmlunit.javascript.configuration.JsxClass;
import com.gargoylesoftware.htmlunit.javascript.configuration.JsxConstructor;
import com.gargoylesoftware.htmlunit.javascript.configuration.JsxFunction;
import com.gargoylesoftware.htmlunit.javascript.configuration.JsxGetter;
import com.gargoylesoftware.htmlunit.javascript.configuration.JsxSetter;
import com.gargoylesoftware.htmlunit.javascript.host.Element;
import com.gargoylesoftware.htmlunit.javascript.host.css.StyleAttributes.Definition;
import com.gargoylesoftware.htmlunit.javascript.host.html.HTMLCanvasElement;
import com.gargoylesoftware.htmlunit.javascript.host.html.HTMLElement;
import com.gargoylesoftware.htmlunit.javascript.host.html.HTMLHtmlElement;
import net.sourceforge.htmlunit.corejs.javascript.Context;
import net.sourceforge.htmlunit.corejs.javascript.ScriptRuntime;
import net.sourceforge.htmlunit.corejs.javascript.Scriptable;
import net.sourceforge.htmlunit.corejs.javascript.Undefined;
/**
* A JavaScript object for {@code CSSStyleDeclaration}.
*
* @author Mike Bowler
* @author Christian Sell
* @author Daniel Gredler
* @author Chris Erskine
* @author Ahmed Ashour
* @author Rodney Gitzel
* @author Sudhan Moghe
* @author Ronald Brill
* @author Frank Danek
*/
@JsxClass
public class CSSStyleDeclaration extends SimpleScriptable {
private static final Pattern TO_INT_PATTERN = Pattern.compile("(\\d+).*");
private static final Pattern URL_PATTERN =
Pattern.compile("url\\(\\s*[\"']?(.*?)[\"']?\\s*\\)");
private static final Pattern POSITION_PATTERN =
Pattern.compile("(\\d+\\s*(%|px|cm|mm|in|pt|pc|em|ex))\\s*"
+ "(\\d+\\s*(%|px|cm|mm|in|pt|pc|em|ex)|top|bottom|center)");
private static final Pattern POSITION_PATTERN2 =
Pattern.compile("(left|right|center)\\s*(\\d+\\s*(%|px|cm|mm|in|pt|pc|em|ex)|top|bottom|center)");
private static final Pattern POSITION_PATTERN3 =
Pattern.compile("(top|bottom|center)\\s*(\\d+\\s*(%|px|cm|mm|in|pt|pc|em|ex)|left|right|center)");
private static final Set LENGTH_PROPERTIES_FFFF = new HashSet<>(Arrays.asList(
BORDER_TOP_WIDTH.getAttributeName(),
BORDER_LEFT_WIDTH.getAttributeName(),
BORDER_BOTTOM_WIDTH.getAttributeName(),
BORDER_RIGHT_WIDTH.getAttributeName(),
LETTER_SPACING.getAttributeName()));
private static final Set LENGTH_PROPERTIES_TTFF = new HashSet<>(Arrays.asList(
HEIGHT.getAttributeName(),
WIDTH.getAttributeName(),
TOP.getAttributeName(),
LEFT.getAttributeName(),
BOTTOM.getAttributeName(),
RIGHT.getAttributeName(),
MARGIN_TOP.getAttributeName(),
MARGIN_LEFT.getAttributeName(),
MARGIN_BOTTOM.getAttributeName(),
MARGIN_RIGHT.getAttributeName(),
MIN_HEIGHT.getAttributeName(),
MIN_WIDTH.getAttributeName()
));
private static final Set LENGTH_PROPERTIES_FTFF = new HashSet<>(Arrays.asList(
FONT_SIZE.getAttributeName(),
TEXT_INDENT.getAttributeName(),
PADDING_TOP.getAttributeName(),
PADDING_LEFT.getAttributeName(),
PADDING_BOTTOM.getAttributeName(),
PADDING_RIGHT.getAttributeName(),
MAX_HEIGHT.getAttributeName(),
MAX_WIDTH.getAttributeName()
));
static final String NONE = "none";
static final String AUTO = "auto";
static final String STATIC = "static";
static final String INHERIT = "inherit";
private static final String INITIAL = "initial";
static final String RELATIVE = "relative";
static final String FIXED = "fixed";
static final String ABSOLUTE = "absolute";
private static final String REPEAT = "repeat";
static final String BLOCK = "block";
static final String INLINE = "inline";
private static final Log LOG = LogFactory.getLog(CSSStyleDeclaration.class);
private static final Map CSSColors_ = new HashMap<>();
private static final Map CamelizeCache_
= Collections.synchronizedMap(new HashMap());
/** Used to parse URLs. */
private static final MessageFormat URL_FORMAT = new MessageFormat("url({0})");
/** The element to which this style belongs. */
private Element jsElement_;
/** The wrapped CSSStyleDeclaration (if created from CSSStyleRule). */
private CSSStyleDeclarationImpl styleDeclaration_;
static {
CSSColors_.put("aqua", "rgb(0, 255, 255)");
CSSColors_.put("black", "rgb(0, 0, 0)");
CSSColors_.put("blue", "rgb(0, 0, 255)");
CSSColors_.put("fuchsia", "rgb(255, 0, 255)");
CSSColors_.put("gray", "rgb(128, 128, 128)");
CSSColors_.put("green", "rgb(0, 128, 0)");
CSSColors_.put("lime", "rgb(0, 255, 0)");
CSSColors_.put("maroon", "rgb(128, 0, 0)");
CSSColors_.put("navy", "rgb(0, 0, 128)");
CSSColors_.put("olive", "rgb(128, 128, 0)");
CSSColors_.put("purple", "rgb(128, 0, 128)");
CSSColors_.put("red", "rgb(255, 0, 0)");
CSSColors_.put("silver", "rgb(192, 192, 192)");
CSSColors_.put("teal", "rgb(0, 128, 128)");
CSSColors_.put("white", "rgb(255, 255, 255)");
CSSColors_.put("yellow", "rgb(255, 255, 0)");
}
/**
* Creates an instance.
*/
@JsxConstructor({CHROME, FF, FF68, FF60})
public CSSStyleDeclaration() {
}
/**
* Creates an instance and sets its parent scope to the one of the provided element.
* @param element the element to which this style is bound
*/
public CSSStyleDeclaration(final Element element) {
setParentScope(element.getParentScope());
setPrototype(getPrototype(getClass()));
initialize(element);
}
/**
* Creates an instance which wraps the specified style declaration.
* @param parentScope the parent scope to use
* @param styleDeclaration the style declaration to wrap
*/
CSSStyleDeclaration(final Scriptable parentScope, final CSSStyleDeclarationImpl styleDeclaration) {
setParentScope(parentScope);
setPrototype(getPrototype(getClass()));
styleDeclaration_ = styleDeclaration;
}
/**
* Initializes the object.
* @param htmlElement the element that this style describes
*/
private void initialize(final Element element) {
// Initialize.
WebAssert.notNull("htmlElement", element);
jsElement_ = element;
setDomNode(element.getDomNodeOrNull(), false);
// If an IE behavior was specified in the style, apply the behavior.
if (getBrowserVersion().hasFeature(CSS_SUPPORTS_BEHAVIOR_PROPERTY)
&& element instanceof HTMLElement) {
final HTMLElement htmlElement = (HTMLElement) element;
final String behavior = getStyleAttribute(BEHAVIOR);
if (StringUtils.isNotBlank(behavior)) {
try {
final Object[] url = URL_FORMAT.parse(behavior);
if (url.length > 0) {
htmlElement.addBehavior((String) url[0]);
}
}
catch (final ParseException e) {
if (LOG.isWarnEnabled()) {
LOG.warn("Invalid behavior: '" + behavior + "'.");
}
}
}
}
}
/**
* IE makes unknown style properties accessible.
* @param name the name of the requested property
* @return the object value, {@link #NOT_FOUND} if nothing is found
*/
@Override
protected Object getWithPreemption(final String name) {
if (getBrowserVersion().hasFeature(JS_STYLE_UNSUPPORTED_PROPERTY_GETTER) && null != jsElement_) {
final StyleElement element = getStyleElement(name);
if (element != null && element.getValue() != null) {
return element.getValue();
}
}
return NOT_FOUND;
}
/**
* Returns the element to which this style belongs.
* @return the element to which this style belongs
*/
protected Element getElement() {
return jsElement_;
}
/**
* Returns the priority of the named style attribute, or an empty string if it is not found.
*
* @param name the name of the style attribute whose value is to be retrieved
* @return the named style attribute value, or an empty string if it is not found
*/
protected String getStylePriority(final String name) {
if (styleDeclaration_ != null) {
return styleDeclaration_.getPropertyPriority(name);
}
final StyleElement element = getStyleElement(name);
if (element != null && element.getValue() != null) {
return element.getPriority();
}
return "";
}
/**
* Determines the StyleElement for the given name.
*
* @param name the name of the requested StyleElement
* @return the StyleElement or null if not found
*/
protected StyleElement getStyleElement(final String name) {
if (jsElement_ == null) {
return null;
}
return jsElement_.getDomNodeOrDie().getStyleElement(name);
}
/**
* Determines the StyleElement for the given name.
* This ignores the case of the name.
*
* @param name the name of the requested StyleElement
* @return the StyleElement or null if not found
*/
private StyleElement getStyleElementCaseInSensitive(final String name) {
if (jsElement_ == null) {
return null;
}
return jsElement_.getDomNodeOrDie().getStyleElementCaseInSensitive(name);
}
/**
* Returns the value of one of the two named style attributes. If both attributes exist,
* the value of the attribute that was declared last is returned. If only one of the
* attributes exists, its value is returned. If neither attribute exists, an empty string
* is returned.
*
* The second named attribute may be shorthand for a the actual desired property.
* The following formats are possible:
*
* - top right bottom left: All values are explicit.
* - top right bottom: Left is implicitly the same as right.
* - top right: Left is implicitly the same as right, bottom is implicitly the same as top.
* - top: Left, bottom and right are implicitly the same as top.
*
*
* @param name1 the name of the first style attribute
* @param name2 the name of the second style attribute
* @return the value of one of the two named style attributes
*/
private String getStyleAttribute(final Definition name1, final Definition name2) {
final String value;
if (styleDeclaration_ == null) {
final StyleElement element1 = getStyleElement(name1.getAttributeName());
final StyleElement element2 = getStyleElement(name2.getAttributeName());
if (element2 == null) {
if (element1 == null) {
return "";
}
return element1.getValue();
}
if (element1 == null) {
value = element2.getValue();
}
else {
if (element1.compareTo(element2) > 0) {
return element1.getValue();
}
value = element2.getValue();
}
}
else {
final String value1 = styleDeclaration_.getPropertyValue(name1.getAttributeName());
final String value2 = styleDeclaration_.getPropertyValue(name2.getAttributeName());
if ("".equals(value1) && "".equals(value2)) {
return "";
}
if (!"".equals(value1) && "".equals(value2)) {
return value1;
}
value = value2;
}
final String[] values = StringUtils.split(value);
if (name1.name().contains("TOP")) {
return values[0];
}
else if (name1.name().contains("RIGHT")) {
if (values.length > 1) {
return values[1];
}
return values[0];
}
else if (name1.name().contains("BOTTOM")) {
if (values.length > 2) {
return values[2];
}
return values[0];
}
else if (name1.name().contains("LEFT")) {
if (values.length > 3) {
return values[3];
}
else if (values.length > 1) {
return values[1];
}
else {
return values[0];
}
}
else {
throw new IllegalStateException("Unsupported definitino: " + name1);
}
}
/**
* Sets the specified style attribute.
* @param name the attribute name (camel-cased)
* @param newValue the attribute value
*/
protected void setStyleAttribute(final String name, final String newValue) {
setStyleAttribute(name, newValue, "");
}
/**
* Sets the specified style attribute.
* @param name the attribute name (camel-cased)
* @param newValue the attribute value
* @param important important value
*/
protected void setStyleAttribute(final String name, String newValue, final String important) {
if (null == newValue || "null".equals(newValue)) {
newValue = "";
}
if (styleDeclaration_ != null) {
styleDeclaration_.setProperty(name, newValue, important);
return;
}
jsElement_.getDomNodeOrDie().replaceStyleAttribute(name, newValue, important);
}
/**
* Removes the specified style attribute, returning the value of the removed attribute.
* @param name the attribute name (delimiter-separated, not camel-cased)
*/
private String removeStyleAttribute(final String name) {
if (null != styleDeclaration_) {
return styleDeclaration_.removeProperty(name);
}
return jsElement_.getDomNodeOrDie().removeStyleAttribute(name);
}
/**
* Returns a sorted map containing style elements, keyed on style element name. We use a
* {@link LinkedHashMap} map so that results are deterministic and are thus testable.
*
* @return a sorted map containing style elements, keyed on style element name
*/
private Map getStyleMap() {
if (jsElement_ == null) {
return Collections.emptyMap();
}
return jsElement_.getDomNodeOrDie().getStyleMap();
}
/**
* Transforms the specified string from delimiter-separated (e.g. font-size)
* to camel-cased (e.g. fontSize).
* @param string the string to camelize
* @return the transformed string
* @see com.gargoylesoftware.htmlunit.javascript.host.dom.DOMStringMap#decamelize(String)
*/
protected static final String camelize(final String string) {
if (string == null) {
return null;
}
String result = CamelizeCache_.get(string);
if (null != result) {
return result;
}
// not found in CamelizeCache_; convert and store in cache
final int pos = string.indexOf('-');
if (pos == -1 || pos == string.length() - 1) {
// cache also this strings for performance
CamelizeCache_.put(string, string);
return string;
}
final StringBuilder builder = new StringBuilder(string);
builder.deleteCharAt(pos);
builder.setCharAt(pos, Character.toUpperCase(builder.charAt(pos)));
int i = pos + 1;
while (i < builder.length() - 1) {
if (builder.charAt(i) == '-') {
builder.deleteCharAt(i);
builder.setCharAt(i, Character.toUpperCase(builder.charAt(i)));
}
i++;
}
result = builder.toString();
CamelizeCache_.put(string, result);
return result;
}
/**
* Gets the {@code accelerator} style attribute.
* @return the style attribute
*/
@JsxGetter(IE)
public String getAccelerator() {
return defaultIfEmpty(getStyleAttribute(ACCELERATOR), "false");
}
/**
* Sets the {@code accelerator} style attribute.
* @param accelerator the new attribute
*/
@JsxSetter(IE)
public void setAccelerator(final String accelerator) {
setStyleAttribute(ACCELERATOR.getAttributeName(), accelerator);
}
/**
* Gets the {@code backgroundAttachment} style attribute.
* @return the style attribute
*/
@JsxGetter
public String getBackgroundAttachment() {
String value = getStyleAttribute(BACKGROUND_ATTACHMENT, false);
if (StringUtils.isBlank(value)) {
final String bg = getStyleAttribute(BACKGROUND);
if (StringUtils.isNotBlank(bg)) {
value = findAttachment(bg);
if (value == null) {
if (getBrowserVersion().hasFeature(CSS_BACKGROUND_INITIAL)
&& getClass() == CSSStyleDeclaration.class) {
return INITIAL;
}
return "scroll"; // default if shorthand is used
}
return value;
}
return "";
}
return value;
}
/**
* Sets the {@code backgroundAttachment} style attribute.
* @param backgroundAttachment the new attribute
*/
@JsxSetter
public void setBackgroundAttachment(final String backgroundAttachment) {
setStyleAttribute(BACKGROUND_ATTACHMENT.getAttributeName(), backgroundAttachment);
}
/**
* Gets the {@code backgroundColor} style attribute.
* @return the style attribute
*/
@JsxGetter
public String getBackgroundColor() {
String value = getStyleAttribute(BACKGROUND_COLOR, false);
if (StringUtils.isBlank(value)) {
final String bg = getStyleAttribute(BACKGROUND, false);
if (StringUtils.isBlank(bg)) {
return "";
}
value = findColor(bg);
if (value == null) {
if (getBrowserVersion().hasFeature(CSS_BACKGROUND_INITIAL)) {
if (getClass() == CSSStyleDeclaration.class) {
return INITIAL;
}
return "rgba(0, 0, 0, 0)";
}
if (getBrowserVersion().hasFeature(CSS_BACKGROUND_RGBA)) {
return "rgba(0, 0, 0, 0)";
}
return "transparent"; // default if shorthand is used
}
return value;
}
if (StringUtils.isBlank(value)) {
return "";
}
return value;
}
/**
* Sets the {@code backgroundColor} style attribute.
* @param backgroundColor the new attribute
*/
@JsxSetter
public void setBackgroundColor(final String backgroundColor) {
setStyleAttribute(BACKGROUND_COLOR.getAttributeName(), backgroundColor);
}
/**
* Gets the {@code backgroundImage} style attribute.
* @return the style attribute
*/
@JsxGetter
public String getBackgroundImage() {
String value = getStyleAttribute(BACKGROUND_IMAGE, false);
if (StringUtils.isBlank(value)) {
final String bg = getStyleAttribute(BACKGROUND, false);
if (StringUtils.isNotBlank(bg)) {
value = findImageUrl(bg);
final boolean isComputed = getClass() != CSSStyleDeclaration.class;
final boolean backgroundInitial = getBrowserVersion().hasFeature(CSS_BACKGROUND_INITIAL);
if (value == null) {
return backgroundInitial && !isComputed ? INITIAL : NONE;
}
if (isComputed) {
try {
value = value.substring(5, value.length() - 2);
return "url(\"" + ((HtmlElement) jsElement_.getDomNodeOrDie()).getHtmlPageOrNull()
.getFullyQualifiedUrl(value) + "\")";
}
catch (final Exception e) {
// ignore
}
}
return value;
}
return "";
}
return value;
}
/**
* Sets the {@code backgroundImage} style attribute.
* @param backgroundImage the new attribute
*/
@JsxSetter
public void setBackgroundImage(final String backgroundImage) {
setStyleAttribute(BACKGROUND_IMAGE.getAttributeName(), backgroundImage);
}
/**
* Gets the {@code backgroundPosition} style attribute.
* @return the style attribute
*/
@JsxGetter
public String getBackgroundPosition() {
String value = getStyleAttribute(BACKGROUND_POSITION, false);
if (value == null) {
return null;
}
if (StringUtils.isBlank(value)) {
final String bg = getStyleAttribute(BACKGROUND, false);
if (bg == null) {
return null;
}
if (StringUtils.isNotBlank(bg)) {
value = findPosition(bg);
final boolean isInitial = getBrowserVersion().hasFeature(CSS_BACKGROUND_INITIAL);
final boolean isComputed = getClass() != CSSStyleDeclaration.class;
if (value == null) {
if (isInitial) {
return isComputed ? "" : INITIAL;
}
return "0% 0%";
}
if (getBrowserVersion().hasFeature(CSS_ZINDEX_TYPE_INTEGER)) {
final String[] values = value.split(" ");
if ("center".equals(values[0])) {
values[0] = "";
}
if ("center".equals(values[1])) {
values[1] = "";
}
if (!isComputed || value.contains("top")) {
return (values[0] + ' ' + values[1]).trim();
}
}
if (isComputed) {
final String[] values = value.split(" ");
switch (values[0]) {
case "left":
values[0] = "0%";
break;
case "center":
values[0] = "50%";
break;
case "right":
values[0] = "100%";
break;
default:
}
switch (values[1]) {
case "top":
values[1] = "0%";
break;
case "center":
values[1] = "50%";
break;
case "bottom":
values[1] = "100%";
break;
default:
}
value = values[0] + ' ' + values[1];
}
return value;
}
return "";
}
return value;
}
/**
* Sets the {@code backgroundPosition} style attribute.
* @param backgroundPosition the new attribute
*/
@JsxSetter
public void setBackgroundPosition(final String backgroundPosition) {
setStyleAttribute(BACKGROUND_POSITION.getAttributeName(), backgroundPosition);
}
/**
* Gets the {@code backgroundRepeat} style attribute.
* @return the style attribute
*/
@JsxGetter
public String getBackgroundRepeat() {
String value = getStyleAttribute(BACKGROUND_REPEAT, false);
if (StringUtils.isBlank(value)) {
final String bg = getStyleAttribute(BACKGROUND, false);
if (StringUtils.isNotBlank(bg)) {
value = findRepeat(bg);
if (value == null) {
if (getBrowserVersion().hasFeature(CSS_BACKGROUND_INITIAL)
&& getClass() == CSSStyleDeclaration.class) {
return INITIAL;
}
return REPEAT; // default if shorthand is used
}
return value;
}
return "";
}
return value;
}
/**
* Sets the {@code backgroundRepeat} style attribute.
* @param backgroundRepeat the new attribute
*/
@JsxSetter
public void setBackgroundRepeat(final String backgroundRepeat) {
setStyleAttribute(BACKGROUND_REPEAT.getAttributeName(), backgroundRepeat);
}
/**
* Gets the {@code borderBottomColor} style attribute.
* @return the style attribute
*/
@JsxGetter
public String getBorderBottomColor() {
String value = getStyleAttribute(BORDER_BOTTOM_COLOR, false);
if (value.isEmpty()) {
value = findColor(getStyleAttribute(BORDER_BOTTOM, false));
if (value == null) {
value = findColor(getStyleAttribute(BORDER, false));
}
if (value == null) {
value = "";
}
}
return value;
}
/**
* Sets the {@code borderBottomColor} style attribute.
* @param borderBottomColor the new attribute
*/
@JsxSetter
public void setBorderBottomColor(final String borderBottomColor) {
setStyleAttribute(BORDER_BOTTOM_COLOR.getAttributeName(), borderBottomColor);
}
/**
* Gets the {@code borderBottomStyle} style attribute.
* @return the style attribute
*/
@JsxGetter
public String getBorderBottomStyle() {
String value = getStyleAttribute(BORDER_BOTTOM_STYLE, false);
if (value.isEmpty()) {
value = findBorderStyle(getStyleAttribute(BORDER_BOTTOM, false));
if (value == null) {
value = findBorderStyle(getStyleAttribute(BORDER, false));
}
if (value == null) {
value = "";
}
}
return value;
}
/**
* Sets the {@code borderBottomStyle} style attribute.
* @param borderBottomStyle the new attribute
*/
@JsxSetter
public void setBorderBottomStyle(final String borderBottomStyle) {
setStyleAttribute(BORDER_BOTTOM_STYLE.getAttributeName(), borderBottomStyle);
}
/**
* Gets the {@code borderBottomWidth} style attribute.
* @return the style attribute
*/
@JsxGetter
public String getBorderBottomWidth() {
return getBorderWidth(BORDER_BOTTOM_WIDTH, BORDER_BOTTOM);
}
/**
* Sets the {@code borderBottomWidth} style attribute.
* @param borderBottomWidth the new attribute
*/
@JsxSetter
public void setBorderBottomWidth(final Object borderBottomWidth) {
setStyleLengthAttribute(BORDER_BOTTOM_WIDTH.getAttributeName(), borderBottomWidth, "",
false, false, false, false);
}
/**
* Gets the {@code borderLeftColor} style attribute.
* @return the style attribute
*/
@JsxGetter
public String getBorderLeftColor() {
String value = getStyleAttribute(BORDER_LEFT_COLOR, false);
if (value.isEmpty()) {
value = findColor(getStyleAttribute(BORDER_LEFT, false));
if (value == null) {
value = findColor(getStyleAttribute(BORDER, false));
}
if (value == null) {
value = "";
}
}
return value;
}
/**
* Sets the {@code borderLeftColor} style attribute.
* @param borderLeftColor the new attribute
*/
@JsxSetter
public void setBorderLeftColor(final String borderLeftColor) {
setStyleAttribute(BORDER_LEFT_COLOR.getAttributeName(), borderLeftColor);
}
/**
* Gets the {@code borderLeftStyle} style attribute.
* @return the style attribute
*/
@JsxGetter
public String getBorderLeftStyle() {
String value = getStyleAttribute(BORDER_LEFT_STYLE, false);
if (value.isEmpty()) {
value = findBorderStyle(getStyleAttribute(BORDER_LEFT, false));
if (value == null) {
value = findBorderStyle(getStyleAttribute(BORDER, false));
}
if (value == null) {
value = "";
}
}
return value;
}
/**
* Sets the {@code borderLeftStyle} style attribute.
* @param borderLeftStyle the new attribute
*/
@JsxSetter
public void setBorderLeftStyle(final String borderLeftStyle) {
setStyleAttribute(BORDER_LEFT_STYLE.getAttributeName(), borderLeftStyle);
}
/**
* Gets the {@code borderLeftWidth} style attribute.
* @return the style attribute
*/
@JsxGetter
public String getBorderLeftWidth() {
return getBorderWidth(BORDER_LEFT_WIDTH, BORDER_LEFT);
}
/**
* Gets the border width for the specified side
* @param borderSideWidth the border side width Definition
* @param borderside the border side Definition
* @return the width, "" if not defined
*/
private String getBorderWidth(final Definition borderSideWidth, final Definition borderSide) {
String value = getStyleAttribute(borderSideWidth, false);
if (value.isEmpty()) {
value = findBorderWidth(getStyleAttribute(borderSide, false));
if (value == null) {
final String borderWidth = getStyleAttribute(BORDER_WIDTH, false);
if (!StringUtils.isEmpty(borderWidth)) {
final String[] values = StringUtils.split(borderWidth);
int index = values.length;
if (borderSideWidth.name().contains("TOP")) {
index = 0;
}
else if (borderSideWidth.name().contains("RIGHT")) {
index = 1;
}
else if (borderSideWidth.name().contains("BOTTOM")) {
index = 2;
}
else if (borderSideWidth.name().contains("LEFT")) {
index = 3;
}
if (index < values.length) {
value = values[index];
}
}
}
if (value == null) {
value = findBorderWidth(getStyleAttribute(BORDER, false));
}
if (value == null) {
value = "";
}
}
return value;
}
/**
* Sets the {@code borderLeftWidth} style attribute.
* @param borderLeftWidth the new attribute
*/
@JsxSetter
public void setBorderLeftWidth(final Object borderLeftWidth) {
setStyleLengthAttribute(BORDER_LEFT_WIDTH.getAttributeName(), borderLeftWidth, "",
false, false, false, false);
}
/**
* Gets the {@code borderRightColor} style attribute.
* @return the style attribute
*/
@JsxGetter
public String getBorderRightColor() {
String value = getStyleAttribute(BORDER_RIGHT_COLOR, false);
if (value.isEmpty()) {
value = findColor(getStyleAttribute(BORDER_RIGHT, false));
if (value == null) {
value = findColor(getStyleAttribute(BORDER, false));
}
if (value == null) {
value = "";
}
}
return value;
}
/**
* Sets the {@code borderRightColor} style attribute.
* @param borderRightColor the new attribute
*/
@JsxSetter
public void setBorderRightColor(final String borderRightColor) {
setStyleAttribute(BORDER_RIGHT_COLOR.getAttributeName(), borderRightColor);
}
/**
* Gets the {@code borderRightStyle} style attribute.
* @return the style attribute
*/
@JsxGetter
public String getBorderRightStyle() {
String value = getStyleAttribute(BORDER_RIGHT_STYLE, false);
if (value.isEmpty()) {
value = findBorderStyle(getStyleAttribute(BORDER_RIGHT, false));
if (value == null) {
value = findBorderStyle(getStyleAttribute(BORDER, false));
}
if (value == null) {
value = "";
}
}
return value;
}
/**
* Sets the {@code borderRightStyle} style attribute.
* @param borderRightStyle the new attribute
*/
@JsxSetter
public void setBorderRightStyle(final String borderRightStyle) {
setStyleAttribute(BORDER_RIGHT_STYLE.getAttributeName(), borderRightStyle);
}
/**
* Gets the {@code borderRightWidth} style attribute.
* @return the style attribute
*/
@JsxGetter
public String getBorderRightWidth() {
return getBorderWidth(BORDER_RIGHT_WIDTH, BORDER_RIGHT);
}
/**
* Sets the {@code borderRightWidth} style attribute.
* @param borderRightWidth the new attribute
*/
@JsxSetter
public void setBorderRightWidth(final Object borderRightWidth) {
setStyleLengthAttribute(BORDER_RIGHT_WIDTH.getAttributeName(), borderRightWidth, "",
false, false, false, false);
}
/**
* Sets the {@code borderTop} style attribute.
* @param borderTop the new attribute
*/
@JsxSetter
public void setBorderTop(final String borderTop) {
setStyleAttribute(BORDER_TOP.getAttributeName(), borderTop);
}
/**
* Gets the {@code borderTopColor} style attribute.
* @return the style attribute
*/
@JsxGetter
public String getBorderTopColor() {
String value = getStyleAttribute(BORDER_TOP_COLOR, false);
if (value.isEmpty()) {
value = findColor(getStyleAttribute(BORDER_TOP, false));
if (value == null) {
value = findColor(getStyleAttribute(BORDER, false));
}
if (value == null) {
value = "";
}
}
return value;
}
/**
* Sets the {@code borderTopColor} style attribute.
* @param borderTopColor the new attribute
*/
@JsxSetter
public void setBorderTopColor(final String borderTopColor) {
setStyleAttribute(BORDER_TOP_COLOR.getAttributeName(), borderTopColor);
}
/**
* Gets the {@code borderTopStyle} style attribute.
* @return the style attribute
*/
@JsxGetter
public String getBorderTopStyle() {
String value = getStyleAttribute(BORDER_TOP_STYLE, false);
if (value.isEmpty()) {
value = findBorderStyle(getStyleAttribute(BORDER_TOP, false));
if (value == null) {
value = findBorderStyle(getStyleAttribute(BORDER, false));
}
if (value == null) {
value = "";
}
}
return value;
}
/**
* Sets the {@code borderTopStyle} style attribute.
* @param borderTopStyle the new attribute
*/
@JsxSetter
public void setBorderTopStyle(final String borderTopStyle) {
setStyleAttribute(BORDER_TOP_STYLE.getAttributeName(), borderTopStyle);
}
/**
* Gets the {@code borderTopWidth} style attribute.
* @return the style attribute
*/
@JsxGetter
public String getBorderTopWidth() {
return getBorderWidth(BORDER_TOP_WIDTH, BORDER_TOP);
}
/**
* Sets the {@code borderTopWidth} style attribute.
* @param borderTopWidth the new attribute
*/
@JsxSetter
public void setBorderTopWidth(final Object borderTopWidth) {
setStyleLengthAttribute(BORDER_TOP_WIDTH.getAttributeName(), borderTopWidth, "",
false, false, false, false);
}
/**
* Gets the {@code bottom} style attribute.
* @return the style attribute
*/
@JsxGetter
public String getBottom() {
return getStyleAttribute(BOTTOM);
}
/**
* Sets the {@code bottom} style attribute.
* @param bottom the new attribute
*/
@JsxSetter
public void setBottom(final Object bottom) {
setStyleLengthAttribute(BOTTOM.getAttributeName(), bottom, "", true, true, false, false);
}
/**
* Gets the {@code color} style attribute.
* @return the style attribute
*/
@JsxGetter
public String getColor() {
return getStyleAttribute(COLOR);
}
/**
* Sets the {@code color} style attribute.
* @param color the new attribute
*/
@JsxSetter
public void setColor(final String color) {
setStyleAttribute(COLOR.getAttributeName(), color);
}
/**
* Gets the {@code cssFloat} style attribute.
* @return the style attribute
*/
@JsxGetter
public String getCssFloat() {
return getStyleAttribute(FLOAT);
}
/**
* Sets the {@code cssFloat} style attribute.
* @param value the new attribute
*/
@JsxSetter
public void setCssFloat(final String value) {
setStyleAttribute(FLOAT.getAttributeName(), value);
}
/**
* Returns the actual text of the style.
* @return the actual text of the style
*/
@JsxGetter
public String getCssText() {
if (styleDeclaration_ != null) {
final String text = styleDeclaration_.getCssText();
if (styleDeclaration_.getLength() > 0) {
return text + ";";
}
return text;
}
return jsElement_.getDomNodeOrDie().getAttributeDirect("style");
}
/**
* Sets the actual text of the style.
* @param value the new text
*/
@JsxSetter
public void setCssText(final String value) {
if (styleDeclaration_ != null) {
styleDeclaration_.setCssText(value);
return;
}
jsElement_.getDomNodeOrDie().setAttribute("style", value);
}
/**
* Gets the {@code display} style attribute.
* @return the style attribute
*/
@JsxGetter
public String getDisplay() {
return getStyleAttribute(DISPLAY);
}
/**
* Sets the {@code display} style attribute.
* @param display the new attribute
*/
@JsxSetter
public void setDisplay(final String display) {
setStyleAttribute(DISPLAY.getAttributeName(), display);
}
/**
* Gets the {@code fontSize} style attribute.
* @return the style attribute
*/
@JsxGetter
public String getFontSize() {
return getStyleAttribute(FONT_SIZE);
}
/**
* Sets the {@code fontSize} style attribute.
* @param fontSize the new attribute
*/
@JsxSetter
public void setFontSize(final Object fontSize) {
setStyleLengthAttribute(FONT_SIZE.getAttributeName(), fontSize, "", false, true, false, false);
updateFont(getFont(), false);
}
/**
* Gets the {@code lineHeight} style attribute.
* @return the style attribute
*/
@JsxGetter
public String getLineHeight() {
return getStyleAttribute(LINE_HEIGHT);
}
/**
* Sets the {@code lineHeight} style attribute.
* @param lineHeight the new attribute
*/
@JsxSetter
public void setLineHeight(final String lineHeight) {
setStyleAttribute(LINE_HEIGHT.getAttributeName(), lineHeight);
updateFont(getFont(), false);
}
/**
* Gets the {@code fontFamily} style attribute.
* @return the style attribute
*/
@JsxGetter
public String getFontFamily() {
return getStyleAttribute(FONT_FAMILY);
}
/**
* Sets the {@code fontFamily} style attribute.
* @param fontFamily the new attribute
*/
@JsxSetter
public void setFontFamily(final String fontFamily) {
setStyleAttribute(FONT_FAMILY.getAttributeName(), fontFamily);
updateFont(getFont(), false);
}
private void updateFont(final String font, final boolean force) {
final BrowserVersion browserVersion = getBrowserVersion();
final String[] details = ComputedFont.getDetails(font, !browserVersion.hasFeature(CSS_SET_NULL_THROWS));
if (details != null || force) {
final StringBuilder newFont = new StringBuilder();
newFont.append(getFontSize());
String lineHeight = getLineHeight();
final String defaultLineHeight = LINE_HEIGHT.getDefaultComputedValue(browserVersion);
if (lineHeight.isEmpty()) {
lineHeight = defaultLineHeight;
}
if (browserVersion.hasFeature(CSS_ZINDEX_TYPE_INTEGER) || !lineHeight.equals(defaultLineHeight)) {
newFont.append('/');
if (lineHeight.equals(defaultLineHeight)) {
newFont.append(LINE_HEIGHT.getDefaultComputedValue(browserVersion));
}
else {
newFont.append(lineHeight);
}
}
newFont.append(' ').append(getFontFamily());
setStyleAttribute(FONT.getAttributeName(), newFont.toString());
}
}
/**
* Gets the {@code font} style attribute.
* @return the style attribute
*/
@JsxGetter
public String getFont() {
return getStyleAttribute(FONT);
}
/**
* Sets the {@code font} style attribute.
* @param font the new attribute
*/
@JsxSetter
public void setFont(final String font) {
final String[] details = ComputedFont.getDetails(font, !getBrowserVersion().hasFeature(CSS_SET_NULL_THROWS));
if (details != null) {
setStyleAttribute(FONT_FAMILY.getAttributeName(), details[ComputedFont.FONT_FAMILY_INDEX]);
final String fontSize = details[ComputedFont.FONT_SIZE_INDEX];
if (details[ComputedFont.LINE_HEIGHT_INDEX] != null) {
setStyleAttribute(LINE_HEIGHT.getAttributeName(), details[ComputedFont.LINE_HEIGHT_INDEX]);
}
setStyleAttribute(FONT_SIZE.getAttributeName(), fontSize);
updateFont(font, true);
}
}
/**
* Gets the {@code height} style attribute.
* @return the style attribute
*/
@JsxGetter
public String getHeight() {
return getStyleAttribute(HEIGHT);
}
/**
* Sets the {@code height} style attribute.
* @param height the new attribute
*/
@JsxSetter
public void setHeight(final Object height) {
setStyleLengthAttribute(HEIGHT.getAttributeName(), height, "", true, true, false, false);
}
/**
* Gets the {@code left} style attribute.
* @return the style attribute
*/
@JsxGetter
public String getLeft() {
return getStyleAttribute(LEFT);
}
/**
* Sets the {@code left} style attribute.
* @param left the new attribute
*/
@JsxSetter
public void setLeft(final Object left) {
setStyleLengthAttribute(LEFT.getAttributeName(), left, "", true, true, false, false);
}
/**
* Returns the {@code length} property.
* @return the {@code length} property
*/
@JsxGetter
public int getLength() {
return getStyleMap().size();
}
/**
* Gets the {@code letterSpacing} style attribute.
* @return the style attribute
*/
@JsxGetter
public String getLetterSpacing() {
return getStyleAttribute(LETTER_SPACING);
}
/**
* Sets the {@code letterSpacing} style attribute.
* @param letterSpacing the new attribute
*/
@JsxSetter
public void setLetterSpacing(final Object letterSpacing) {
setStyleLengthAttribute(LETTER_SPACING.getAttributeName(), letterSpacing, "",
false, false, false, false);
}
/**
* Gets the {@code margin} style attribute.
* @return the style attribute
*/
@JsxGetter
public String getMargin() {
return getStyleAttribute(MARGIN);
}
/**
* Sets the {@code margin} style attribute.
* @param margin the new attribute
*/
@JsxSetter
public void setMargin(final String margin) {
setStyleAttribute(MARGIN.getAttributeName(), margin);
}
/**
* Gets the {@code marginBottom} style attribute.
* @return the style attribute
*/
@JsxGetter
public String getMarginBottom() {
return getStyleAttribute(MARGIN_BOTTOM, MARGIN);
}
/**
* Sets the {@code marginBottom} style attribute.
* @param marginBottom the new attribute
*/
@JsxSetter
public void setMarginBottom(final Object marginBottom) {
setStyleLengthAttribute(MARGIN_BOTTOM.getAttributeName(), marginBottom, "", true, true, false, false);
}
/**
* Gets the {@code marginLeft} style attribute.
* @return the style attribute
*/
@JsxGetter
public String getMarginLeft() {
return getStyleAttribute(MARGIN_LEFT, MARGIN);
}
/**
* Sets the {@code marginLeft} style attribute.
* @param marginLeft the new attribute
*/
@JsxSetter
public void setMarginLeft(final Object marginLeft) {
setStyleLengthAttribute(MARGIN_LEFT.getAttributeName(), marginLeft, "", true, true, false, false);
}
/**
* Gets the {@code marginRight} style attribute.
* @return the style attribute
*/
@JsxGetter
public String getMarginRight() {
return getStyleAttribute(MARGIN_RIGHT, MARGIN);
}
/**
* Sets the {@code marginRight} style attribute.
* @param marginRight the new attribute
*/
@JsxSetter
public void setMarginRight(final Object marginRight) {
setStyleLengthAttribute(MARGIN_RIGHT.getAttributeName(), marginRight, "", true, true, false, false);
}
/**
* Gets the {@code marginTop} style attribute.
* @return the style attribute
*/
@JsxGetter
public String getMarginTop() {
return getStyleAttribute(MARGIN_TOP, MARGIN);
}
/**
* Sets the {@code marginTop} style attribute.
* @param marginTop the new attribute
*/
@JsxSetter
public void setMarginTop(final Object marginTop) {
setStyleLengthAttribute(MARGIN_TOP.getAttributeName(), marginTop, "", true, true, false, false);
}
/**
* Gets the {@code maxHeight} style attribute.
* @return the style attribute
*/
@JsxGetter
public String getMaxHeight() {
return getStyleAttribute(MAX_HEIGHT);
}
/**
* Sets the {@code maxHeight} style attribute.
* @param maxHeight the new attribute
*/
@JsxSetter
public void setMaxHeight(final Object maxHeight) {
setStyleLengthAttribute(MAX_HEIGHT.getAttributeName(), maxHeight, "", false, true, false, false);
}
/**
* Gets the {@code maxWidth} style attribute.
* @return the style attribute
*/
@JsxGetter
public String getMaxWidth() {
return getStyleAttribute(MAX_WIDTH);
}
/**
* Sets the {@code maxWidth} style attribute.
* @param maxWidth the new attribute
*/
@JsxSetter
public void setMaxWidth(final Object maxWidth) {
setStyleLengthAttribute(MAX_WIDTH.getAttributeName(), maxWidth, "", false, true, false, false);
}
/**
* Gets the {@code minHeight} style attribute.
* @return the style attribute
*/
@JsxGetter
public String getMinHeight() {
return getStyleAttribute(MIN_HEIGHT);
}
/**
* Sets the {@code minHeight} style attribute.
* @param minHeight the new attribute
*/
@JsxSetter
public void setMinHeight(final Object minHeight) {
setStyleLengthAttribute(MIN_HEIGHT.getAttributeName(), minHeight, "", true, true, false, false);
}
/**
* Gets the {@code minWidth} style attribute.
* @return the style attribute
*/
@JsxGetter
public String getMinWidth() {
return getStyleAttribute(MIN_WIDTH);
}
/**
* Sets the {@code minWidth} style attribute.
* @param minWidth the new attribute
*/
@JsxSetter
public void setMinWidth(final Object minWidth) {
setStyleLengthAttribute(MIN_WIDTH.getAttributeName(), minWidth, "", true, true, false, false);
}
/**
* {@inheritDoc}
*/
@Override
public Object get(final String name, final Scriptable start) {
if (this != start) {
return super.get(name, start);
}
Scriptable prototype = getPrototype();
while (prototype != null) {
final Object value = prototype.get(name, start);
if (value != Scriptable.NOT_FOUND) {
return value;
}
prototype = prototype.getPrototype();
}
final Definition style = StyleAttributes.getDefinition(name, getBrowserVersion());
if (style != null) {
return getStyleAttribute(style);
}
return super.get(name, start);
}
@Override
public Object get(final int index, final Scriptable start) {
if (index < 0) {
return Undefined.instance;
}
final Map style = getStyleMap();
final int size = style.size();
if (index >= size) {
if (getBrowserVersion().hasFeature(JS_STYLE_WRONG_INDEX_RETURNS_UNDEFINED)) {
return Undefined.instance;
}
return "";
}
return style.keySet().toArray(new String[size])[index];
}
/**
* Get the value for the style attribute.
* @param definition the definition
* @return the value
*/
public final String getStyleAttribute(final Definition definition) {
return getStyleAttribute(definition, true);
}
/**
* Get the value for the style attribute.
* @param definition the definition
* @param getDefaultValueIfEmpty whether to get the default value if empty or not
* @return the value
*/
public String getStyleAttribute(final Definition definition, final boolean getDefaultValueIfEmpty) {
return getStyleAttributeImpl(definition.getAttributeName());
}
private String getStyleAttributeImpl(final String string) {
if (styleDeclaration_ != null) {
return styleDeclaration_.getPropertyValue(string);
}
final StyleElement element = getStyleElement(string);
if (element != null && element.getValue() != null) {
final String value = element.getValue();
if (!value.contains("url")) {
return value.toLowerCase(Locale.ROOT);
}
return value;
}
return "";
}
@Override
public void put(final String name, final Scriptable start, final Object value) {
if (this != start) {
super.put(name, start, value);
return;
}
final Scriptable prototype = getPrototype();
if (prototype != null && !"constructor".equals(name) && prototype.get(name, start) != Scriptable.NOT_FOUND) {
prototype.put(name, start, value);
return;
}
if (getDomNodeOrNull() != null) { // check if prototype or not
final Definition style = StyleAttributes.getDefinition(name, getBrowserVersion());
if (style != null) {
final String stringValue = Context.toString(value);
setStyleAttribute(style.getAttributeName(), stringValue);
return;
}
}
super.put(name, start, value);
}
@Override
public boolean has(final String name, final Scriptable start) {
if (this != start) {
return super.has(name, start);
}
final Definition style = StyleAttributes.getDefinition(name, getBrowserVersion());
if (style != null) {
return true;
}
return super.has(name, start);
}
@Override
public Object[] getIds() {
final List