1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.htmlunit.css;
16
17 import static org.htmlunit.BrowserVersionFeatures.JS_CLIENTHEIGHT_INPUT_17;
18 import static org.htmlunit.BrowserVersionFeatures.JS_CLIENTHEIGHT_INPUT_18;
19 import static org.htmlunit.BrowserVersionFeatures.JS_CLIENTHEIGHT_RADIO_CHECKBOX_10;
20 import static org.htmlunit.BrowserVersionFeatures.JS_CLIENTHEIGHT_RADIO_CHECKBOX_14;
21 import static org.htmlunit.BrowserVersionFeatures.JS_CLIENTHEIGHT_RB_17;
22 import static org.htmlunit.BrowserVersionFeatures.JS_CLIENTHEIGHT_RT_9;
23 import static org.htmlunit.BrowserVersionFeatures.JS_CLIENTHEIGHT_RUBY_17;
24 import static org.htmlunit.BrowserVersionFeatures.JS_CLIENTWIDTH_INPUT_TEXT_154;
25 import static org.htmlunit.BrowserVersionFeatures.JS_CLIENTWIDTH_INPUT_TEXT_173;
26 import static org.htmlunit.BrowserVersionFeatures.JS_CLIENTWIDTH_RADIO_CHECKBOX_10;
27 import static org.htmlunit.BrowserVersionFeatures.JS_CLIENTWIDTH_RADIO_CHECKBOX_14;
28 import static org.htmlunit.css.CssStyleSheet.ABSOLUTE;
29 import static org.htmlunit.css.CssStyleSheet.AUTO;
30 import static org.htmlunit.css.CssStyleSheet.BLOCK;
31 import static org.htmlunit.css.CssStyleSheet.FIXED;
32 import static org.htmlunit.css.CssStyleSheet.INHERIT;
33 import static org.htmlunit.css.CssStyleSheet.INLINE;
34 import static org.htmlunit.css.CssStyleSheet.NONE;
35 import static org.htmlunit.css.CssStyleSheet.RELATIVE;
36 import static org.htmlunit.css.CssStyleSheet.SCROLL;
37 import static org.htmlunit.css.CssStyleSheet.STATIC;
38
39 import java.util.EnumSet;
40 import java.util.HashSet;
41 import java.util.Map;
42 import java.util.Set;
43 import java.util.SortedMap;
44 import java.util.TreeMap;
45
46 import org.htmlunit.BrowserVersion;
47 import org.htmlunit.BrowserVersionFeatures;
48 import org.htmlunit.Page;
49 import org.htmlunit.SgmlPage;
50 import org.htmlunit.WebWindow;
51 import org.htmlunit.css.CssPixelValueConverter.CssValue;
52 import org.htmlunit.css.StyleAttributes.Definition;
53 import org.htmlunit.cssparser.dom.AbstractCSSRuleImpl;
54 import org.htmlunit.cssparser.dom.CSSStyleDeclarationImpl;
55 import org.htmlunit.cssparser.dom.Property;
56 import org.htmlunit.cssparser.parser.selector.Selector;
57 import org.htmlunit.cssparser.parser.selector.SelectorSpecificity;
58 import org.htmlunit.html.BaseFrameElement;
59 import org.htmlunit.html.DomElement;
60 import org.htmlunit.html.DomNode;
61 import org.htmlunit.html.DomText;
62 import org.htmlunit.html.HtmlAbbreviated;
63 import org.htmlunit.html.HtmlAcronym;
64 import org.htmlunit.html.HtmlAddress;
65 import org.htmlunit.html.HtmlArticle;
66 import org.htmlunit.html.HtmlAside;
67 import org.htmlunit.html.HtmlBaseFont;
68 import org.htmlunit.html.HtmlBidirectionalIsolation;
69 import org.htmlunit.html.HtmlBidirectionalOverride;
70 import org.htmlunit.html.HtmlBig;
71 import org.htmlunit.html.HtmlBody;
72 import org.htmlunit.html.HtmlBold;
73 import org.htmlunit.html.HtmlButton;
74 import org.htmlunit.html.HtmlButtonInput;
75 import org.htmlunit.html.HtmlCanvas;
76 import org.htmlunit.html.HtmlCenter;
77 import org.htmlunit.html.HtmlCheckBoxInput;
78 import org.htmlunit.html.HtmlCitation;
79 import org.htmlunit.html.HtmlCode;
80 import org.htmlunit.html.HtmlData;
81 import org.htmlunit.html.HtmlDefinition;
82 import org.htmlunit.html.HtmlDefinitionDescription;
83 import org.htmlunit.html.HtmlDefinitionTerm;
84 import org.htmlunit.html.HtmlDivision;
85 import org.htmlunit.html.HtmlElement;
86 import org.htmlunit.html.HtmlElement.DisplayStyle;
87 import org.htmlunit.html.HtmlEmphasis;
88 import org.htmlunit.html.HtmlFigure;
89 import org.htmlunit.html.HtmlFigureCaption;
90 import org.htmlunit.html.HtmlFileInput;
91 import org.htmlunit.html.HtmlFooter;
92 import org.htmlunit.html.HtmlHeader;
93 import org.htmlunit.html.HtmlHeading1;
94 import org.htmlunit.html.HtmlHeading2;
95 import org.htmlunit.html.HtmlHeading3;
96 import org.htmlunit.html.HtmlHeading4;
97 import org.htmlunit.html.HtmlHeading5;
98 import org.htmlunit.html.HtmlHeading6;
99 import org.htmlunit.html.HtmlHiddenInput;
100 import org.htmlunit.html.HtmlImage;
101 import org.htmlunit.html.HtmlInlineFrame;
102 import org.htmlunit.html.HtmlInput;
103 import org.htmlunit.html.HtmlItalic;
104 import org.htmlunit.html.HtmlKeyboard;
105 import org.htmlunit.html.HtmlLayer;
106 import org.htmlunit.html.HtmlLegend;
107 import org.htmlunit.html.HtmlMain;
108 import org.htmlunit.html.HtmlMark;
109 import org.htmlunit.html.HtmlNav;
110 import org.htmlunit.html.HtmlNoBreak;
111 import org.htmlunit.html.HtmlNoEmbed;
112 import org.htmlunit.html.HtmlNoFrames;
113 import org.htmlunit.html.HtmlNoLayer;
114 import org.htmlunit.html.HtmlNoScript;
115 import org.htmlunit.html.HtmlOutput;
116 import org.htmlunit.html.HtmlPage;
117 import org.htmlunit.html.HtmlPasswordInput;
118 import org.htmlunit.html.HtmlPlainText;
119 import org.htmlunit.html.HtmlRadioButtonInput;
120 import org.htmlunit.html.HtmlRb;
121 import org.htmlunit.html.HtmlResetInput;
122 import org.htmlunit.html.HtmlRp;
123 import org.htmlunit.html.HtmlRt;
124 import org.htmlunit.html.HtmlRtc;
125 import org.htmlunit.html.HtmlRuby;
126 import org.htmlunit.html.HtmlS;
127 import org.htmlunit.html.HtmlSample;
128 import org.htmlunit.html.HtmlSection;
129 import org.htmlunit.html.HtmlSelect;
130 import org.htmlunit.html.HtmlSlot;
131 import org.htmlunit.html.HtmlSmall;
132 import org.htmlunit.html.HtmlSpan;
133 import org.htmlunit.html.HtmlStrike;
134 import org.htmlunit.html.HtmlStrong;
135 import org.htmlunit.html.HtmlSubmitInput;
136 import org.htmlunit.html.HtmlSubscript;
137 import org.htmlunit.html.HtmlSummary;
138 import org.htmlunit.html.HtmlSuperscript;
139 import org.htmlunit.html.HtmlTableCell;
140 import org.htmlunit.html.HtmlTableRow;
141 import org.htmlunit.html.HtmlTeletype;
142 import org.htmlunit.html.HtmlTextArea;
143 import org.htmlunit.html.HtmlTextInput;
144 import org.htmlunit.html.HtmlTime;
145 import org.htmlunit.html.HtmlUnderlined;
146 import org.htmlunit.html.HtmlUnknownElement;
147 import org.htmlunit.html.HtmlVariable;
148 import org.htmlunit.html.HtmlWordBreak;
149 import org.htmlunit.platform.Platform;
150 import org.htmlunit.util.StringUtils;
151
152
153
154
155
156
157
158
159
160
161
162
163
164 @SuppressWarnings("PMD.AvoidDuplicateLiterals")
165 public class ComputedCssStyleDeclaration extends AbstractCssStyleDeclaration {
166
167
168 private static final Set<Definition> INHERITABLE_DEFINITIONS = EnumSet.of(
169 Definition.BORDER_COLLAPSE,
170 Definition.BORDER_SPACING,
171 Definition.CAPTION_SIDE,
172 Definition.COLOR,
173 Definition.CURSOR,
174 Definition.DIRECTION,
175 Definition.EMPTY_CELLS,
176 Definition.FONT_FAMILY,
177 Definition.FONT_SIZE,
178 Definition.FONT_STYLE,
179 Definition.FONT_VARIANT,
180 Definition.FONT_WEIGHT,
181 Definition.FONT,
182 Definition.LETTER_SPACING,
183 Definition.LINE_HEIGHT,
184 Definition.LIST_STYLE_IMAGE,
185 Definition.LIST_STYLE_POSITION,
186 Definition.LIST_STYLE_TYPE,
187 Definition.LIST_STYLE,
188 Definition.ORPHANS,
189 Definition.QUOTES,
190 Definition.SPEAK,
191 Definition.TEXT_ALIGN,
192 Definition.TEXT_INDENT,
193 Definition.TEXT_TRANSFORM,
194 Definition.VISIBILITY,
195 Definition.WHITE_SPACE,
196 Definition.WIDOWS,
197 Definition.WORD_SPACING);
198
199
200 public static final String EMPTY_FINAL = new String("");
201
202
203 private Integer width_;
204
205
206
207
208
209 private Integer height_;
210
211
212
213
214
215 private Integer emptyHeight_;
216
217
218 private Integer paddingHorizontal_;
219
220
221 private Integer paddingVertical_;
222
223
224 private Integer borderHorizontal_;
225
226
227 private Integer borderVertical_;
228
229
230 private Integer top_;
231
232
233
234
235
236 private final SortedMap<String, StyleElement> localModifications_ = new TreeMap<>();
237
238
239 private final ElementCssStyleDeclaration elementStyleDeclaration_;
240
241
242
243
244
245 public ComputedCssStyleDeclaration(final ElementCssStyleDeclaration styleDeclaration) {
246 super();
247 elementStyleDeclaration_ = styleDeclaration;
248 elementStyleDeclaration_.getDomElement().setDefaults(this);
249 }
250
251
252
253
254 @Override
255 public String getStylePriority(final String name) {
256 return elementStyleDeclaration_.getStylePriority(name);
257 }
258
259
260
261
262 @Override
263 public String getCssText() {
264 return elementStyleDeclaration_.getCssText();
265 }
266
267
268
269
270 @Override
271 public String getStyleAttribute(final String name) {
272 final StyleElement element = getStyleElement(name);
273 if (element != null && element.getValue() != null) {
274 final String value = element.getValue();
275 if (!"content".equals(name)
276 && !value.contains("url")) {
277 return StringUtils.toRootLowerCase(value);
278 }
279 return value;
280 }
281 return "";
282 }
283
284
285
286
287 @Override
288 public String getStyleAttribute(final Definition definition, final boolean getDefaultValueIfEmpty) {
289 final BrowserVersion browserVersion = getDomElement().getPage().getWebClient().getBrowserVersion();
290 final boolean isDefInheritable = INHERITABLE_DEFINITIONS.contains(definition);
291
292
293 final ComputedCssStyleDeclaration[] queue = {this};
294 String value = null;
295 while (queue[0] != null) {
296 value = getStyleAttributeWorker(definition, getDefaultValueIfEmpty,
297 browserVersion, true, isDefInheritable, queue);
298 }
299
300 return value;
301 }
302
303 private static String getStyleAttributeWorker(final Definition definition,
304 final boolean getDefaultValueIfEmpty, final BrowserVersion browserVersion,
305 final boolean feature, final boolean isDefInheritable,
306 final ComputedCssStyleDeclaration[] queue) {
307 final ComputedCssStyleDeclaration decl = queue[0];
308 queue[0] = null;
309
310 final DomElement domElem = decl.getDomElement();
311 if (!domElem.isAttachedToPage() && feature) {
312 return EMPTY_FINAL;
313 }
314
315 String value = decl.getStyleAttribute(definition.getAttributeName());
316 if (value.isEmpty()) {
317 final DomNode parent = domElem.getParentNode();
318 if (isDefInheritable && parent instanceof DomElement) {
319 final WebWindow window = domElem.getPage().getEnclosingWindow();
320
321 queue[0] = window.getComputedStyle((DomElement) parent, null);
322 }
323 else if (getDefaultValueIfEmpty) {
324 value = definition.getDefaultComputedValue(browserVersion);
325 }
326 }
327
328 return value;
329 }
330
331
332
333
334
335
336 private String getStyleAttribute(final Definition definition, final String toReturnIfEmptyOrDefault,
337 final String defaultValue) {
338 final DomElement domElement = getDomElement();
339
340 if (!domElement.isAttachedToPage()) {
341 return EMPTY_FINAL;
342 }
343
344 final boolean isDefInheritable = INHERITABLE_DEFINITIONS.contains(definition);
345
346
347 final BrowserVersion browserVersion = domElement.getPage().getWebClient().getBrowserVersion();
348 final ComputedCssStyleDeclaration[] queue = {this};
349 String value = null;
350 while (queue[0] != null) {
351 value = getStyleAttributeWorker(definition, false,
352 browserVersion, true, isDefInheritable, queue);
353 }
354
355 if (value == null || value.isEmpty() || value.equals(defaultValue)) {
356 return toReturnIfEmptyOrDefault;
357 }
358
359 return value;
360 }
361
362
363
364
365 @Override
366 public void setCssText(final String value) {
367
368 }
369
370
371
372
373 @Override
374 public void setStyleAttribute(final String name, final String newValue, final String important) {
375
376 }
377
378
379
380
381 @Override
382 public String removeStyleAttribute(final String name) {
383
384 return null;
385 }
386
387
388
389
390 @Override
391 public int getLength() {
392 return elementStyleDeclaration_.getLength();
393 }
394
395
396
397
398 @Override
399 public String getWidth() {
400 if (NONE.equals(getDisplay())) {
401 return AUTO;
402 }
403
404 final DomElement domElem = getDomElement();
405 if (!domElem.isAttachedToPage()) {
406 return "";
407 }
408
409 final int windowWidth = domElem.getPage().getEnclosingWindow().getInnerWidth();
410 return CssPixelValueConverter.pixelString(domElem, new CssPixelValueConverter.CssValue(0, windowWidth) {
411 @Override
412 public String get(final ComputedCssStyleDeclaration style) {
413 final String value = style.getStyleAttribute(Definition.WIDTH, true);
414 if (StringUtils.isEmptyOrNull(value)) {
415 final String position = getStyleAttribute(Definition.POSITION, true);
416 if (ABSOLUTE.equals(position) || FIXED.equals(position)) {
417 final String content = domElem.getVisibleText();
418
419
420 if (null != content && content.length() < 13) {
421 return (content.length() * 7) + "px";
422 }
423 }
424
425 int windowDefaultValue = getWindowDefaultValue();
426 if (domElem instanceof HtmlBody) {
427 windowDefaultValue -= 16;
428 }
429 return windowDefaultValue + "px";
430 }
431 else if (AUTO.equals(value)) {
432 int windowDefaultValue = getWindowDefaultValue();
433 if (domElem instanceof HtmlBody) {
434 windowDefaultValue -= 16;
435 }
436 return windowDefaultValue + "px";
437 }
438
439 return value;
440 }
441 });
442 }
443
444
445
446
447 @Override
448 public String item(final int index) {
449 return elementStyleDeclaration_.item(index);
450 }
451
452
453
454
455 @Override
456 public AbstractCSSRuleImpl getParentRule() {
457 return elementStyleDeclaration_.getParentRule();
458 }
459
460
461
462
463 @Override
464 public StyleElement getStyleElement(final String name) {
465 final StyleElement existent = elementStyleDeclaration_.getStyleElement(name);
466
467 final StyleElement localStyleMod = localModifications_.get(name);
468 if (localStyleMod == null) {
469 return existent;
470 }
471
472 if (existent == null) {
473
474
475
476 return localStyleMod;
477 }
478
479
480 if (StyleElement.PRIORITY_IMPORTANT.equals(localStyleMod.getPriority())) {
481 if (existent.isImportant()) {
482 if (existent.getSpecificity().compareTo(localStyleMod.getSpecificity()) < 0) {
483 return localStyleMod;
484 }
485 }
486 else {
487 return localStyleMod;
488 }
489 }
490 return existent;
491 }
492
493
494
495
496 @Override
497 public StyleElement getStyleElementCaseInSensitive(final String name) {
498 return elementStyleDeclaration_.getStyleElementCaseInSensitive(name);
499 }
500
501
502
503
504 @Override
505 public Map<String, StyleElement> getStyleMap() {
506 return elementStyleDeclaration_.getStyleMap();
507 }
508
509
510
511
512
513 public DomElement getDomElement() {
514 return elementStyleDeclaration_.getDomElement();
515 }
516
517
518
519
520 @Override
521 public String getBackgroundAttachment() {
522 return defaultIfEmpty(super.getBackgroundAttachment(), Definition.BACKGROUND_ATTACHMENT);
523 }
524
525
526
527
528 @Override
529 public String getBackgroundColor() {
530 if (!getDomElement().isAttachedToPage()) {
531 return EMPTY_FINAL;
532 }
533
534 final String value = super.getBackgroundColor();
535 if (StringUtils.isEmptyOrNull(value)) {
536 return Definition.BACKGROUND_COLOR.getDefaultComputedValue(getBrowserVersion());
537 }
538 return CssColors.toRGBColor(value);
539 }
540
541
542
543
544 @Override
545 public String getBackgroundImage() {
546 return defaultIfEmpty(super.getBackgroundImage(), Definition.BACKGROUND_IMAGE);
547 }
548
549
550
551
552
553 @Override
554 public String getBackgroundPosition() {
555 return defaultIfEmpty(super.getBackgroundPosition(), Definition.BACKGROUND_POSITION);
556 }
557
558
559
560
561 @Override
562 public String getBackgroundRepeat() {
563 return defaultIfEmpty(super.getBackgroundRepeat(), Definition.BACKGROUND_REPEAT);
564 }
565
566
567
568
569 @Override
570 public String getBlockSize() {
571 if (NONE.equals(getDisplay())) {
572 return defaultIfEmpty(super.getBlockSize(), Definition.BLOCK_SIZE);
573 }
574
575 final DomElement domElem = getDomElement();
576 if (!domElem.isAttachedToPage()) {
577 return defaultIfEmpty(super.getBlockSize(), Definition.BLOCK_SIZE);
578 }
579
580 return CssPixelValueConverter.pixelString(domElem, new CssPixelValueConverter.CssValue(0, 0) {
581 @Override
582 public String get(final ComputedCssStyleDeclaration style) {
583 final String value = style.getStyleAttribute(Definition.HEIGHT, true);
584 if (StringUtils.isEmptyOrNull(value)) {
585 final String content = domElem.getVisibleText();
586
587
588 if (null == content) {
589 return getDefaultValue() + "px";
590 }
591 return getEmptyHeight(domElem) + "px";
592 }
593 return value;
594 }
595 });
596 }
597
598
599
600
601 @Override
602 public String getBorderBottomColor() {
603 return defaultIfEmpty(super.getBorderBottomColor(), Definition.BORDER_BOTTOM_COLOR);
604 }
605
606
607
608
609 @Override
610 public String getBorderBottomStyle() {
611 return defaultIfEmpty(super.getBorderBottomStyle(), Definition.BORDER_BOTTOM_STYLE);
612 }
613
614
615
616
617 @Override
618 public String getBorderBottomWidth() {
619 return pixelString(defaultIfEmpty(super.getBorderBottomWidth(), Definition.BORDER_BOTTOM_WIDTH));
620 }
621
622
623
624
625 @Override
626 public String getBorderLeftColor() {
627 return defaultIfEmpty(super.getBorderLeftColor(), Definition.BORDER_LEFT_COLOR);
628 }
629
630
631
632
633 @Override
634 public String getBorderLeftStyle() {
635 return defaultIfEmpty(super.getBorderLeftStyle(), Definition.BORDER_LEFT_STYLE);
636 }
637
638
639
640
641 @Override
642 public String getBorderLeftWidth() {
643 return pixelString(defaultIfEmpty(super.getBorderLeftWidth(), "0px", null));
644 }
645
646
647
648
649 @Override
650 public String getBorderRightColor() {
651 return defaultIfEmpty(super.getBorderRightColor(), "rgb(0, 0, 0)", null);
652 }
653
654
655
656
657 @Override
658 public String getBorderRightStyle() {
659 return defaultIfEmpty(super.getBorderRightStyle(), NONE, null);
660 }
661
662
663
664
665 @Override
666 public String getBorderRightWidth() {
667 return pixelString(defaultIfEmpty(super.getBorderRightWidth(), "0px", null));
668 }
669
670
671
672
673 @Override
674 public String getBorderTopColor() {
675 return defaultIfEmpty(super.getBorderTopColor(), "rgb(0, 0, 0)", null);
676 }
677
678
679
680
681 @Override
682 public String getBorderTopStyle() {
683 return defaultIfEmpty(super.getBorderTopStyle(), NONE, null);
684 }
685
686
687
688
689 @Override
690 public String getBorderTopWidth() {
691 return pixelString(defaultIfEmpty(super.getBorderTopWidth(), "0px", null));
692 }
693
694
695
696
697 @Override
698 public String getBottom() {
699 return getStyleAttribute(Definition.BOTTOM, AUTO, null);
700 }
701
702
703
704
705 @Override
706 public String getColor() {
707 final String value = getStyleAttribute(Definition.COLOR, "rgb(0, 0, 0)", null);
708 return CssColors.toRGBColor(value);
709 }
710
711
712
713
714 @Override
715 public String getCssFloat() {
716 return defaultIfEmpty(super.getCssFloat(), Definition.CSS_FLOAT);
717 }
718
719
720
721
722 @Override
723 public String getDisplay() {
724 final DomElement domElem = getDomElement();
725 if (!domElem.isAttachedToPage()) {
726 return "";
727 }
728
729 if (domElem instanceof HtmlElement) {
730 if (((HtmlElement) domElem).isHidden()) {
731 return DisplayStyle.NONE.value();
732 }
733 }
734
735
736
737 final String value = getStyleAttribute(Definition.DISPLAY.getAttributeName());
738 if (StringUtils.isEmptyOrNull(value)) {
739 if (domElem instanceof HtmlElement) {
740 return ((HtmlElement) domElem).getDefaultStyleDisplay().value();
741 }
742 return "";
743 }
744 return value;
745 }
746
747
748
749
750 @Override
751 public String getFont() {
752 final DomElement domElem = getDomElement();
753 if (domElem.isAttachedToPage()) {
754 return getStyleAttribute(Definition.FONT, true);
755 }
756 return "";
757 }
758
759
760
761
762 @Override
763 public String getFontFamily() {
764 return getStyleAttribute(Definition.FONT_FAMILY, true);
765 }
766
767
768
769
770 @Override
771 public String getFontSize() {
772 return getStyleAttribute(Definition.FONT_SIZE, true);
773 }
774
775
776
777
778 @Override
779 public String getLineHeight() {
780 return defaultIfEmpty(super.getLineHeight(), Definition.LINE_HEIGHT);
781 }
782
783
784
785
786 @Override
787 public String getHeight() {
788 if (NONE.equals(getDisplay())) {
789 return AUTO;
790 }
791
792 final DomElement elem = getDomElement();
793 if (!elem.isAttachedToPage()) {
794 return "";
795 }
796
797 final ComputedCssStyleDeclaration style = elem.getPage().getEnclosingWindow().getComputedStyle(elem, null);
798 final String styleValue = style.getStyleAttribute(Definition.HEIGHT, true);
799
800 if (styleValue == null || styleValue.isEmpty() || AUTO.equals(styleValue) || styleValue.endsWith("%")) {
801 final String calculatedHeight = style.getCalculatedHeight(false, false) + "px";
802 return calculatedHeight;
803 }
804
805 if (styleValue.endsWith("px")) {
806 return styleValue;
807 }
808
809 return CssPixelValueConverter.pixelValue(styleValue) + "px";
810 }
811
812
813
814
815 @Override
816 public String getLeft() {
817 if (NONE.equals(getDisplay())) {
818 return AUTO;
819 }
820
821 final DomElement elem = getDomElement();
822 if (!elem.isAttachedToPage()) {
823 return "";
824 }
825
826 final String superLeft = super.getLeft();
827 if (!superLeft.endsWith("%")) {
828 return defaultIfEmpty(superLeft, AUTO, null);
829 }
830
831 return CssPixelValueConverter.pixelString(elem, new CssPixelValueConverter.CssValue(0, 0) {
832 @Override
833 public String get(final ComputedCssStyleDeclaration style) {
834 if (style.getDomElement() == elem) {
835 return style.getStyleAttribute(Definition.LEFT, true);
836 }
837 return style.getStyleAttribute(Definition.WIDTH, true);
838 }
839 });
840 }
841
842
843
844
845 @Override
846 public String getLetterSpacing() {
847 return defaultIfEmpty(super.getLetterSpacing(), "normal", null);
848 }
849
850
851
852
853 @Override
854 public String getMargin() {
855 return defaultIfEmpty(super.getMargin(), Definition.MARGIN, true);
856 }
857
858
859
860
861 @Override
862 public String getMarginBottom() {
863 return pixelString(defaultIfEmpty(super.getMarginBottom(), "0px", null));
864 }
865
866
867
868
869 @Override
870 public String getMarginLeft() {
871 return getMarginX(super.getMarginLeft(), Definition.MARGIN_LEFT);
872 }
873
874
875
876
877 @Override
878 public String getMarginRight() {
879 return getMarginX(super.getMarginRight(), Definition.MARGIN_RIGHT);
880 }
881
882 private String getMarginX(final String superMarginX, final Definition definition) {
883 if (!superMarginX.endsWith("%")) {
884 return pixelString(defaultIfEmpty(superMarginX, "0px", null));
885 }
886 final DomElement element = getDomElement();
887 if (!element.isAttachedToPage()) {
888 return "";
889 }
890
891 final int windowWidth = element.getPage().getEnclosingWindow().getInnerWidth();
892 return CssPixelValueConverter
893 .pixelString(element, new CssPixelValueConverter.CssValue(0, windowWidth) {
894 @Override
895 public String get(final ComputedCssStyleDeclaration style) {
896 if (style.getDomElement() == element) {
897 return style.getStyleAttribute(definition, true);
898 }
899 return style.getStyleAttribute(Definition.WIDTH, true);
900 }
901 });
902 }
903
904
905
906
907 @Override
908 public String getMarginTop() {
909 return pixelString(defaultIfEmpty(super.getMarginTop(), "0px", null));
910 }
911
912
913
914
915 @Override
916 public String getMaxHeight() {
917 return defaultIfEmpty(super.getMaxHeight(), NONE, null);
918 }
919
920
921
922
923 @Override
924 public String getMaxWidth() {
925 return defaultIfEmpty(super.getMaxWidth(), NONE, null);
926 }
927
928
929
930
931 @Override
932 public String getMinHeight() {
933 return defaultIfEmpty(super.getMinHeight(), "0px", null);
934 }
935
936
937
938
939 @Override
940 public String getMinWidth() {
941 return defaultIfEmpty(super.getMinWidth(), "0px", null);
942 }
943
944
945
946
947 @Override
948 public String getOpacity() {
949 return defaultIfEmpty(super.getOpacity(), "1", null);
950 }
951
952
953
954
955 @Override
956 public String getOrphans() {
957 return defaultIfEmpty(super.getOrphans(), Definition.ORPHANS);
958 }
959
960
961
962
963 @Override
964 public String getOutlineWidth() {
965 return defaultIfEmpty(super.getOutlineWidth(), "0px", null);
966 }
967
968
969
970
971 @Override
972 public String getPadding() {
973 return defaultIfEmpty(super.getPadding(), Definition.PADDING, true);
974 }
975
976
977
978
979 @Override
980 public String getPaddingBottom() {
981 return pixelString(defaultIfEmpty(super.getPaddingBottom(), "0px", null));
982 }
983
984
985
986
987 @Override
988 public String getPaddingLeft() {
989 return pixelString(defaultIfEmpty(super.getPaddingLeft(), "0px", null));
990 }
991
992
993
994
995 @Override
996 public String getPaddingRight() {
997 return pixelString(defaultIfEmpty(super.getPaddingRight(), "0px", null));
998 }
999
1000
1001
1002
1003 @Override
1004 public String getPaddingTop() {
1005 return pixelString(defaultIfEmpty(super.getPaddingTop(), "0px", null));
1006 }
1007
1008
1009
1010
1011 @Override
1012 public String getRight() {
1013 return defaultIfEmpty(super.getRight(), AUTO, null);
1014 }
1015
1016
1017
1018
1019 @Override
1020 public String getTextIndent() {
1021 return defaultIfEmpty(super.getTextIndent(), "0px", null);
1022 }
1023
1024
1025
1026
1027 @Override
1028 public String getTop() {
1029 if (NONE.equals(getDisplay())) {
1030 return AUTO;
1031 }
1032
1033 final DomElement elem = getDomElement();
1034 if (!elem.isAttachedToPage()) {
1035 return "";
1036 }
1037
1038 final String superTop = super.getTop();
1039 if (!superTop.endsWith("%")) {
1040 return defaultIfEmpty(superTop, Definition.TOP);
1041 }
1042
1043 return CssPixelValueConverter.pixelString(elem, new CssPixelValueConverter.CssValue(0, 0) {
1044 @Override
1045 public String get(final ComputedCssStyleDeclaration style) {
1046 if (style.getDomElement() == elem) {
1047 return style.getStyleAttribute(Definition.TOP, true);
1048 }
1049 return style.getStyleAttribute(Definition.HEIGHT, true);
1050 }
1051 });
1052 }
1053
1054
1055
1056
1057
1058
1059
1060
1061 public int getTop(final boolean includeMargin, final boolean includeBorder, final boolean includePadding) {
1062 Integer cachedTop = getCachedTop();
1063
1064 int top = 0;
1065 if (null == cachedTop) {
1066 final String position = getPositionWithInheritance();
1067 if (ABSOLUTE.equals(position) || FIXED.equals(position)) {
1068 top = getTopForAbsolutePositionWithInheritance();
1069 }
1070 else if (getDomElement() instanceof HtmlTableCell) {
1071 top = 0;
1072 }
1073 else {
1074
1075 DomNode prev = getDomElement().getPreviousSibling();
1076 boolean prevHadComputedTop = false;
1077 while (prev != null && !prevHadComputedTop) {
1078 if (prev instanceof HtmlElement) {
1079 final ComputedCssStyleDeclaration style =
1080 prev.getPage().getEnclosingWindow().getComputedStyle((DomElement) prev, null);
1081
1082
1083 final String display = style.getDisplay();
1084 if (isBlock(display)) {
1085 int prevTop = 0;
1086 final Integer eCachedTop = style.getCachedTop();
1087 if (eCachedTop == null) {
1088 final String prevPosition = style.getPositionWithInheritance();
1089 if (ABSOLUTE.equals(prevPosition) || FIXED.equals(prevPosition)) {
1090 prevTop += style.getTopForAbsolutePositionWithInheritance();
1091 }
1092 else {
1093 if (RELATIVE.equals(prevPosition)) {
1094 final String t = style.getTopWithInheritance();
1095 prevTop += CssPixelValueConverter.pixelValue(t);
1096 }
1097 }
1098 }
1099 else {
1100 prevHadComputedTop = true;
1101 prevTop += eCachedTop.intValue();
1102 }
1103 prevTop += style.getCalculatedHeight(true, true);
1104 final int margin = CssPixelValueConverter.pixelValue(style.getMarginTop());
1105 prevTop += margin;
1106 top += prevTop;
1107 }
1108 }
1109 prev = prev.getPreviousSibling();
1110 }
1111
1112 if (RELATIVE.equals(position)) {
1113 final String t = getTopWithInheritance();
1114 top += CssPixelValueConverter.pixelValue(t);
1115 }
1116 }
1117 cachedTop = Integer.valueOf(top);
1118 setCachedTop(cachedTop);
1119 }
1120 else {
1121 top = cachedTop.intValue();
1122 }
1123
1124 if (includeMargin) {
1125 final int margin = CssPixelValueConverter.pixelValue(getMarginTop());
1126 top += margin;
1127 }
1128
1129 if (includeBorder) {
1130 final int border = CssPixelValueConverter.pixelValue(getBorderTopWidth());
1131 top += border;
1132 }
1133
1134 if (includePadding) {
1135 final int padding = getPaddingTopValue();
1136 top += padding;
1137 }
1138
1139 return top;
1140 }
1141
1142 private static boolean isBlock(final String display) {
1143 return display != null
1144 && !INLINE.equals(display)
1145 && !NONE.equals(display);
1146 }
1147
1148
1149
1150
1151
1152 public String getTopWithInheritance() {
1153 String top = getTop();
1154 if (INHERIT.equals(top)) {
1155 final HtmlElement parent = (HtmlElement) getDomElement().getParentNode();
1156 if (parent == null) {
1157 top = AUTO;
1158 }
1159 else {
1160 final ComputedCssStyleDeclaration style =
1161 parent.getPage().getEnclosingWindow().getComputedStyle(parent, null);
1162 top = style.getTopWithInheritance();
1163 }
1164 }
1165 return top;
1166 }
1167
1168
1169
1170
1171
1172 public String getBottomWithInheritance() {
1173 String bottom = getBottom();
1174 if (INHERIT.equals(bottom)) {
1175 final DomNode parent = getDomElement().getParentNode();
1176 if (parent == null) {
1177 bottom = AUTO;
1178 }
1179 else {
1180 final ComputedCssStyleDeclaration style =
1181 parent.getPage().getEnclosingWindow().getComputedStyle((DomElement) parent, null);
1182 bottom = style.getBottomWithInheritance();
1183 }
1184 }
1185 return bottom;
1186 }
1187
1188
1189
1190
1191 @Override
1192 public String getVerticalAlign() {
1193 return defaultIfEmpty(super.getVerticalAlign(), "baseline", null);
1194 }
1195
1196
1197
1198
1199 @Override
1200 public String getWidows() {
1201 return defaultIfEmpty(super.getWidows(), Definition.WIDOWS);
1202 }
1203
1204
1205
1206
1207 @Override
1208 public String getWordSpacing() {
1209 return defaultIfEmpty(super.getWordSpacing(), Definition.WORD_SPACING);
1210 }
1211
1212
1213
1214
1215 @Override
1216 public String getZIndex() {
1217 if (!getDomElement().isAttachedToPage()) {
1218 return EMPTY_FINAL;
1219 }
1220
1221 final String response = super.getZIndex();
1222 if (response.isEmpty()) {
1223 return AUTO;
1224 }
1225 return response;
1226 }
1227
1228
1229
1230
1231
1232 public int getMarginLeftValue() {
1233 return CssPixelValueConverter.pixelValue(getMarginLeft());
1234 }
1235
1236
1237
1238
1239
1240 public int getMarginRightValue() {
1241 return CssPixelValueConverter.pixelValue(getMarginRight());
1242 }
1243
1244
1245
1246
1247
1248 public int getMarginTopValue() {
1249 return CssPixelValueConverter.pixelValue(getMarginTop());
1250 }
1251
1252
1253
1254
1255
1256 public int getMarginBottomValue() {
1257 return CssPixelValueConverter.pixelValue(getMarginBottom());
1258 }
1259
1260
1261
1262
1263
1264
1265
1266
1267 public int getLeft(final boolean includeMargin, final boolean includeBorder, final boolean includePadding) {
1268 final String p = getPositionWithInheritance();
1269 final String l = getLeftWithInheritance();
1270 final String r = getRightWithInheritance();
1271
1272 int left;
1273 if ((ABSOLUTE.equals(p) || FIXED.equals(p)) && !AUTO.equals(l)) {
1274
1275 left = CssPixelValueConverter.pixelValue(l);
1276 }
1277 else if ((ABSOLUTE.equals(p) || FIXED.equals(p)) && !AUTO.equals(r)) {
1278
1279 final DomNode parent = getDomElement().getParentNode();
1280 final int parentWidth;
1281 if (parent == null) {
1282 parentWidth = getDomElement().getPage().getEnclosingWindow().getInnerWidth();
1283 }
1284 else if (parent instanceof Page) {
1285 parentWidth = ((Page) parent).getEnclosingWindow().getInnerWidth();
1286 }
1287 else {
1288 final ComputedCssStyleDeclaration parentStyle =
1289 parent.getPage().getEnclosingWindow().getComputedStyle((DomElement) parent, null);
1290 parentWidth = parentStyle.getCalculatedWidth(false, false);
1291 }
1292 left = parentWidth - CssPixelValueConverter.pixelValue(r);
1293 }
1294 else if (FIXED.equals(p) && !AUTO.equals(r)) {
1295 final DomElement e = getDomElement();
1296 final WebWindow win = e.getPage().getEnclosingWindow();
1297 final ComputedCssStyleDeclaration style = win.getComputedStyle(e, null);
1298
1299 final DomNode parent = e.getParentNode();
1300 final int parentWidth;
1301 if (parent == null) {
1302 parentWidth = win.getInnerWidth();
1303 }
1304 else {
1305 final ComputedCssStyleDeclaration parentStyle = win.getComputedStyle((DomElement) parent, null);
1306 parentWidth = CssPixelValueConverter.pixelValue(parentStyle.getWidth())
1307 - CssPixelValueConverter.pixelValue(style.getWidth());
1308 }
1309 left = parentWidth - CssPixelValueConverter.pixelValue(r);
1310 }
1311 else if (FIXED.equals(p) && AUTO.equals(l)) {
1312
1313 final DomNode parent = getDomElement().getParentNode();
1314 if (parent == null || parent instanceof Page) {
1315 left = 0;
1316 }
1317 else {
1318 final ComputedCssStyleDeclaration style =
1319 parent.getPage().getEnclosingWindow().getComputedStyle((DomElement) parent, null);
1320 left = CssPixelValueConverter.pixelValue(style.getLeftWithInheritance());
1321 }
1322 }
1323 else if (STATIC.equals(p)) {
1324
1325 left = 0;
1326 DomNode prev = getDomElement().getPreviousSibling();
1327 while (prev != null) {
1328 if (prev instanceof HtmlElement) {
1329 final ComputedCssStyleDeclaration style =
1330 prev.getPage().getEnclosingWindow().getComputedStyle((DomElement) prev, null);
1331 final String d = style.getDisplay();
1332 if (isBlock(d)) {
1333 break;
1334 }
1335 else if (!NONE.equals(d)) {
1336 left += style.getCalculatedWidth(true, true);
1337 }
1338 }
1339 else if (prev instanceof DomText) {
1340 final String content = prev.getVisibleText();
1341 if (content != null) {
1342 left += content.trim().length()
1343 * getDomElement().getPage().getWebClient().getBrowserVersion().getPixesPerChar();
1344 }
1345 }
1346 prev = prev.getPreviousSibling();
1347 }
1348 }
1349 else {
1350
1351 left = CssPixelValueConverter.pixelValue(l);
1352 }
1353
1354 if (includeMargin) {
1355 final int margin = getMarginLeftValue();
1356 left += margin;
1357 }
1358
1359 if (includeBorder) {
1360 final int border = CssPixelValueConverter.pixelValue(getBorderLeftWidth());
1361 left += border;
1362 }
1363
1364 if (includePadding) {
1365 final int padding = getPaddingLeftValue();
1366 left += padding;
1367 }
1368
1369 return left;
1370 }
1371
1372
1373
1374
1375 @Override
1376 public String getPosition() {
1377 return defaultIfEmpty(super.getPosition(), Definition.POSITION);
1378 }
1379
1380
1381
1382
1383
1384 public String getPositionWithInheritance() {
1385 String p = getStyleAttribute(Definition.POSITION, true);
1386 if (INHERIT.equals(p)) {
1387 final DomNode parent = getDomElement().getParentNode();
1388 if (parent == null) {
1389 p = STATIC;
1390 }
1391 else {
1392 final ComputedCssStyleDeclaration style =
1393 parent.getPage().getEnclosingWindow().getComputedStyle((DomElement) parent, null);
1394 p = style.getPositionWithInheritance();
1395 }
1396 }
1397 return p;
1398 }
1399
1400
1401
1402
1403
1404 public String getLeftWithInheritance() {
1405 String left = getLeft();
1406 if (INHERIT.equals(left)) {
1407 final DomNode parent = getDomElement().getParentNode();
1408 if (parent == null) {
1409 left = AUTO;
1410 }
1411 else {
1412 final ComputedCssStyleDeclaration style =
1413 parent.getPage().getEnclosingWindow().getComputedStyle((DomElement) parent, null);
1414 left = style.getLeftWithInheritance();
1415 }
1416 }
1417 return left;
1418 }
1419
1420
1421
1422
1423
1424 public String getRightWithInheritance() {
1425 String right = getRight();
1426 if (INHERIT.equals(right)) {
1427 final DomNode parent = getDomElement().getParentNode();
1428 if (parent == null) {
1429 right = AUTO;
1430 }
1431 else {
1432 final ComputedCssStyleDeclaration style =
1433 parent.getPage().getEnclosingWindow().getComputedStyle((DomElement) parent, null);
1434 right = style.getRightWithInheritance();
1435 }
1436 }
1437 return right;
1438 }
1439
1440 private int getTopForAbsolutePositionWithInheritance() {
1441 final String t = getTopWithInheritance();
1442
1443 if (!AUTO.equals(t)) {
1444
1445 return CssPixelValueConverter.pixelValue(t);
1446 }
1447
1448 final String b = getBottomWithInheritance();
1449 if (!AUTO.equals(b)) {
1450
1451
1452
1453 int top = 0;
1454 DomNode child = getDomElement().getParentNode().getFirstChild();
1455 while (child != null) {
1456 if (child instanceof HtmlElement && child.mayBeDisplayed()) {
1457 top += 20;
1458 }
1459 child = child.getNextSibling();
1460 }
1461 top -= CssPixelValueConverter.pixelValue(b);
1462 return top;
1463 }
1464
1465 return 0;
1466 }
1467
1468
1469
1470
1471
1472
1473
1474 public int getCalculatedHeight(final boolean includeBorder, final boolean includePadding) {
1475 final DomElement element = getDomElement();
1476
1477 if (!element.isAttachedToPage()) {
1478 return 0;
1479 }
1480 int height = getCalculatedHeight(element);
1481 if (!"border-box".equals(getStyleAttribute(Definition.BOX_SIZING, true))) {
1482 if (includeBorder) {
1483 height += getBorderVertical();
1484 }
1485 else if (isScrollable(element, true, true) && !(element instanceof HtmlBody)) {
1486 height -= 17;
1487 }
1488
1489 if (includePadding) {
1490 height += getPaddingVertical();
1491 }
1492 }
1493 return height;
1494 }
1495
1496
1497
1498
1499
1500 private int getCalculatedHeight(final DomElement element) {
1501 final Integer cachedHeight = getCachedHeight();
1502 if (cachedHeight != null) {
1503 return cachedHeight.intValue();
1504 }
1505
1506 if (element instanceof HtmlImage) {
1507 return updateCachedHeight(((HtmlImage) element).getHeightOrDefault());
1508 }
1509
1510 final boolean isInline = INLINE.equals(getDisplay()) && !(element instanceof HtmlInlineFrame);
1511
1512 if (isInline || super.getHeight().isEmpty()) {
1513 final int contentHeight = getContentHeight();
1514 if (contentHeight > 0) {
1515 return updateCachedHeight(contentHeight);
1516 }
1517 }
1518
1519 return updateCachedHeight(getEmptyHeight(element));
1520 }
1521
1522
1523
1524
1525
1526
1527
1528 public int getCalculatedWidth(final boolean includeBorder, final boolean includePadding) {
1529 final DomElement element = getDomElement();
1530
1531 if (!element.isAttachedToPage()) {
1532 return 0;
1533 }
1534 int width = getCalculatedWidth();
1535 if (!"border-box".equals(getStyleAttribute(Definition.BOX_SIZING, true))) {
1536 if (includeBorder) {
1537 width += getBorderHorizontal();
1538 }
1539 else if (isScrollable(element, false, true) && !(element instanceof HtmlBody)) {
1540 width -= 17;
1541 }
1542
1543 if (includePadding) {
1544 width += getPaddingHorizontal();
1545 }
1546 }
1547 return width;
1548 }
1549
1550 private int getCalculatedWidth() {
1551 final Integer cachedWidth = getCachedWidth();
1552 if (cachedWidth != null) {
1553 return cachedWidth.intValue();
1554 }
1555
1556 final DomElement element = getDomElement();
1557 if (!element.mayBeDisplayed()) {
1558 return updateCachedWidth(0);
1559 }
1560
1561 final String display = getDisplay();
1562 if (NONE.equals(display)) {
1563 return updateCachedWidth(0);
1564 }
1565
1566 final int width;
1567 final String styleWidth = getStyleAttribute(Definition.WIDTH, true);
1568 final DomNode parent = element.getParentNode();
1569
1570
1571 if ((INLINE.equals(display) || StringUtils.isEmptyOrNull(styleWidth))
1572 && parent instanceof HtmlElement) {
1573
1574 if (element instanceof HtmlCanvas) {
1575 return 300;
1576 }
1577
1578
1579 final String cssFloat = getCssFloat();
1580 final String position = getStyleAttribute(Definition.POSITION, true);
1581 if ("right".equals(cssFloat) || "left".equals(cssFloat)
1582 || ABSOLUTE.equals(position) || FIXED.equals(position)) {
1583 final BrowserVersion browserVersion = getDomElement().getPage().getWebClient().getBrowserVersion();
1584
1585 width = element.getVisibleText().length() * browserVersion.getPixesPerChar();
1586 }
1587 else if (BLOCK.equals(display)) {
1588 final int windowWidth = element.getPage().getEnclosingWindow().getInnerWidth();
1589 if (element instanceof HtmlBody) {
1590 width = windowWidth - 16;
1591 }
1592 else {
1593
1594 width = CssPixelValueConverter.pixelValue((DomElement) parent,
1595 new CssPixelValueConverter.CssValue(0, windowWidth) {
1596 @Override public String get(final ComputedCssStyleDeclaration style) {
1597 return style.getWidth();
1598 }
1599 }) - (getBorderHorizontal() + getPaddingHorizontal());
1600 }
1601 }
1602 else if (element instanceof HtmlSubmitInput
1603 || element instanceof HtmlResetInput
1604 || element instanceof HtmlButtonInput
1605 || element instanceof HtmlButton
1606 || element instanceof HtmlFileInput) {
1607
1608
1609 final String text = element.asNormalizedText();
1610 final BrowserVersion browserVersion = getDomElement().getPage().getWebClient().getBrowserVersion();
1611
1612 width = 10 + (int) (text.length() * browserVersion.getPixesPerChar() * 0.9);
1613 }
1614 else if (element instanceof HtmlTextInput || element instanceof HtmlPasswordInput) {
1615 final BrowserVersion browserVersion = getDomElement().getPage().getWebClient().getBrowserVersion();
1616 if (browserVersion.hasFeature(JS_CLIENTWIDTH_INPUT_TEXT_173)) {
1617 return 173;
1618 }
1619 if (browserVersion.hasFeature(JS_CLIENTWIDTH_INPUT_TEXT_154)) {
1620 return 154;
1621 }
1622 width = 161;
1623 }
1624 else if (element instanceof HtmlRadioButtonInput || element instanceof HtmlCheckBoxInput) {
1625 final BrowserVersion browserVersion = getDomElement().getPage().getWebClient().getBrowserVersion();
1626 if (browserVersion.hasFeature(JS_CLIENTWIDTH_RADIO_CHECKBOX_10)) {
1627 width = 10;
1628 }
1629 else if (browserVersion.hasFeature(JS_CLIENTWIDTH_RADIO_CHECKBOX_14)) {
1630 width = 14;
1631 }
1632 else {
1633 width = 13;
1634 }
1635 }
1636 else if (element instanceof HtmlTextArea) {
1637 width = 100;
1638 }
1639 else if (element instanceof HtmlImage) {
1640 width = ((HtmlImage) element).getWidthOrDefault();
1641 }
1642 else {
1643
1644 width = getContentWidth();
1645 }
1646 }
1647 else if (AUTO.equals(styleWidth)) {
1648 width = element.getPage().getEnclosingWindow().getInnerWidth();
1649 }
1650 else {
1651
1652 width = CssPixelValueConverter.pixelValue(element,
1653 new CssPixelValueConverter.CssValue(0, element.getPage().getEnclosingWindow().getInnerWidth()) {
1654 @Override public String get(final ComputedCssStyleDeclaration style) {
1655 return style.getStyleAttribute(Definition.WIDTH, true);
1656 }
1657 });
1658 }
1659
1660 return updateCachedWidth(width);
1661 }
1662
1663
1664
1665
1666
1667 public int getContentWidth() {
1668 int width = 0;
1669 final DomElement element = getDomElement();
1670 Iterable<DomNode> children = element.getChildren();
1671 if (element instanceof BaseFrameElement) {
1672 final Page enclosedPage = ((BaseFrameElement) element).getEnclosedPage();
1673 if (enclosedPage != null && enclosedPage.isHtmlPage()) {
1674 children = ((DomNode) enclosedPage).getChildren();
1675 }
1676 }
1677 final WebWindow webWindow = element.getPage().getEnclosingWindow();
1678 for (final DomNode child : children) {
1679 if (child instanceof HtmlElement) {
1680 final HtmlElement e = (HtmlElement) child;
1681 final ComputedCssStyleDeclaration style = webWindow.getComputedStyle(e, null);
1682 final int w = style.getCalculatedWidth(true, true);
1683 width += w;
1684 }
1685 else if (child instanceof DomText) {
1686 final BrowserVersion browserVersion = getDomElement().getPage().getWebClient().getBrowserVersion();
1687
1688 final DomNode parent = child.getParentNode();
1689 if (parent instanceof HtmlElement) {
1690 final ComputedCssStyleDeclaration style = webWindow.getComputedStyle((DomElement) parent, null);
1691 final int height = browserVersion.getFontHeight(
1692 style.getStyleAttribute(Definition.FONT_SIZE, true));
1693 width += child.getVisibleText().length() * (int) (height / 1.8f);
1694 }
1695 else {
1696 width += child.getVisibleText().length() * browserVersion.getPixesPerChar();
1697 }
1698 }
1699 }
1700 return width;
1701 }
1702
1703
1704
1705
1706
1707 private int getEmptyHeight(final DomElement element) {
1708 final Integer cachedEmptyHeight = getCachedEmptyHeight();
1709 if (cachedEmptyHeight != null) {
1710 return cachedEmptyHeight.intValue();
1711 }
1712
1713 if (!element.mayBeDisplayed()) {
1714 return updateCachedEmptyHeight(0);
1715 }
1716
1717 final String display = getDisplay();
1718 if (NONE.equals(display)) {
1719 return updateCachedEmptyHeight(0);
1720 }
1721
1722 final SgmlPage page = element.getPage();
1723 final WebWindow webWindow = page.getEnclosingWindow();
1724 final int windowHeight = webWindow.getInnerHeight();
1725
1726 if (element instanceof HtmlBody) {
1727 if (page instanceof HtmlPage && ((HtmlPage) page).isQuirksMode()) {
1728 return updateCachedEmptyHeight(windowHeight);
1729 }
1730
1731 return updateCachedEmptyHeight(0);
1732 }
1733
1734 final boolean isInline = INLINE.equals(display) && !(element instanceof HtmlInlineFrame);
1735
1736 final boolean explicitHeightSpecified = !isInline && !super.getHeight().isEmpty();
1737
1738 int defaultHeight;
1739 if ((element instanceof HtmlAbbreviated
1740 || element instanceof HtmlAcronym
1741 || element instanceof HtmlAddress
1742 || element instanceof HtmlArticle
1743 || element instanceof HtmlAside
1744 || element instanceof HtmlBaseFont
1745 || element instanceof HtmlBidirectionalIsolation
1746 || element instanceof HtmlBidirectionalOverride
1747 || element instanceof HtmlBig
1748 || element instanceof HtmlBold
1749 || element instanceof HtmlCenter
1750 || element instanceof HtmlCitation
1751 || element instanceof HtmlCode
1752 || element instanceof HtmlDefinition
1753 || element instanceof HtmlDefinitionDescription
1754 || element instanceof HtmlDefinitionTerm
1755 || element instanceof HtmlEmphasis
1756 || element instanceof HtmlFigure
1757 || element instanceof HtmlFigureCaption
1758 || element instanceof HtmlFooter
1759 || element instanceof HtmlHeader
1760 || element instanceof HtmlItalic
1761 || element instanceof HtmlKeyboard
1762 || element instanceof HtmlLayer
1763 || element instanceof HtmlMark
1764 || element instanceof HtmlNav
1765 || element instanceof HtmlNoBreak
1766 || element instanceof HtmlNoEmbed
1767 || element instanceof HtmlNoFrames
1768 || element instanceof HtmlNoLayer
1769 || element instanceof HtmlNoScript
1770 || element instanceof HtmlPlainText
1771 || element instanceof HtmlRp
1772 || element instanceof HtmlRtc
1773 || element instanceof HtmlS
1774 || element instanceof HtmlSample
1775 || element instanceof HtmlSection
1776 || element instanceof HtmlSmall
1777 || element instanceof HtmlStrike
1778 || element instanceof HtmlStrong
1779 || element instanceof HtmlSubscript
1780 || element instanceof HtmlSummary
1781 || element instanceof HtmlSuperscript
1782 || element instanceof HtmlTeletype
1783 || element instanceof HtmlUnderlined
1784 || element instanceof HtmlUnknownElement
1785 || element instanceof HtmlWordBreak
1786 || element instanceof HtmlMain
1787 || element instanceof HtmlVariable
1788
1789 || element instanceof HtmlDivision
1790 || element instanceof HtmlData
1791 || element instanceof HtmlTime
1792 || element instanceof HtmlOutput
1793 || element instanceof HtmlSlot
1794 || element instanceof HtmlLegend)
1795 && StringUtils.isBlank(element.getTextContent())) {
1796 defaultHeight = 0;
1797 }
1798 else if (element.getFirstChild() == null) {
1799 if (element instanceof HtmlRadioButtonInput || element instanceof HtmlCheckBoxInput) {
1800 final BrowserVersion browser = webWindow.getWebClient().getBrowserVersion();
1801 if (browser.hasFeature(JS_CLIENTHEIGHT_RADIO_CHECKBOX_10)) {
1802 defaultHeight = 10;
1803 }
1804 else if (browser.hasFeature(JS_CLIENTHEIGHT_RADIO_CHECKBOX_14)) {
1805 defaultHeight = 14;
1806 }
1807 else {
1808 defaultHeight = 13;
1809 }
1810 }
1811 else if (element instanceof HtmlButton) {
1812 defaultHeight = 20;
1813 }
1814 else if (element instanceof HtmlInput && !(element instanceof HtmlHiddenInput)) {
1815 final BrowserVersion browser = webWindow.getWebClient().getBrowserVersion();
1816 if (browser.hasFeature(JS_CLIENTHEIGHT_INPUT_17)) {
1817 defaultHeight = 17;
1818 }
1819 else if (browser.hasFeature(JS_CLIENTHEIGHT_INPUT_18)) {
1820 defaultHeight = 18;
1821 }
1822 else {
1823 defaultHeight = 20;
1824 }
1825 }
1826 else if (element instanceof HtmlSelect) {
1827 defaultHeight = 20;
1828 }
1829 else if (element instanceof HtmlTextArea) {
1830 defaultHeight = 49;
1831 }
1832 else if (element instanceof HtmlInlineFrame) {
1833 defaultHeight = 154;
1834 }
1835 else {
1836 defaultHeight = 0;
1837 }
1838 }
1839 else if (element instanceof HtmlRb) {
1840 final BrowserVersion browser = webWindow.getWebClient().getBrowserVersion();
1841 if (browser.hasFeature(JS_CLIENTHEIGHT_RB_17)) {
1842 defaultHeight = 17;
1843 }
1844 else {
1845 defaultHeight = 0;
1846 }
1847 }
1848 else if (element instanceof HtmlRt) {
1849 final BrowserVersion browser = webWindow.getWebClient().getBrowserVersion();
1850 if (browser.hasFeature(JS_CLIENTHEIGHT_RT_9)) {
1851 defaultHeight = 9;
1852 }
1853 else {
1854 defaultHeight = 0;
1855 }
1856 }
1857 else if (element instanceof HtmlRuby) {
1858 final BrowserVersion browser = webWindow.getWebClient().getBrowserVersion();
1859 if (browser.hasFeature(JS_CLIENTHEIGHT_RUBY_17)) {
1860 defaultHeight = 17;
1861 }
1862 else {
1863 defaultHeight = 0;
1864 }
1865 }
1866 else {
1867 final String fontSize;
1868
1869 boolean isHeading = false;
1870 if (element instanceof HtmlHeading1) {
1871 isHeading = true;
1872 final String value = getStyleAttribute(Definition.FONT_SIZE, false);
1873 if (value.isEmpty()) {
1874 fontSize = "32px";
1875 }
1876 else {
1877 fontSize = getStyleAttribute(Definition.FONT_SIZE, true);
1878 }
1879 }
1880 else if (element instanceof HtmlHeading2) {
1881 isHeading = true;
1882 final String value = getStyleAttribute(Definition.FONT_SIZE, false);
1883 if (value.isEmpty()) {
1884 fontSize = "24px";
1885 }
1886 else {
1887 fontSize = getStyleAttribute(Definition.FONT_SIZE, true);
1888 }
1889 }
1890 else if (element instanceof HtmlHeading3) {
1891 isHeading = true;
1892 final String value = getStyleAttribute(Definition.FONT_SIZE, false);
1893 if (value.isEmpty()) {
1894 fontSize = "19px";
1895 }
1896 else {
1897 fontSize = getStyleAttribute(Definition.FONT_SIZE, true);
1898 }
1899 }
1900 else if (element instanceof HtmlHeading4) {
1901 isHeading = true;
1902 final String value = getStyleAttribute(Definition.FONT_SIZE, false);
1903 if (value.isEmpty()) {
1904 fontSize = "16px";
1905 }
1906 else {
1907 fontSize = getStyleAttribute(Definition.FONT_SIZE, true);
1908 }
1909 }
1910 else if (element instanceof HtmlHeading5) {
1911 isHeading = true;
1912 final String value = getStyleAttribute(Definition.FONT_SIZE, false);
1913 if (value.isEmpty()) {
1914 fontSize = "13px";
1915 }
1916 else {
1917 fontSize = getStyleAttribute(Definition.FONT_SIZE, true);
1918 }
1919 }
1920 else if (element instanceof HtmlHeading6) {
1921 isHeading = true;
1922 final String value = getStyleAttribute(Definition.FONT_SIZE, false);
1923 if (value.isEmpty()) {
1924 fontSize = "11px";
1925 }
1926 else {
1927 fontSize = getStyleAttribute(Definition.FONT_SIZE, true);
1928 }
1929 }
1930 else {
1931 fontSize = getStyleAttribute(Definition.FONT_SIZE, true);
1932 }
1933
1934 defaultHeight = webWindow.getWebClient().getBrowserVersion().getFontHeight(fontSize);
1935
1936 if (isHeading
1937 || element instanceof HtmlDivision
1938 || element instanceof HtmlSpan) {
1939 String width = getStyleAttribute(Definition.WIDTH, false);
1940
1941
1942 DomNode parent = getDomElement().getParentNode();
1943 final WebWindow win = parent.getPage().getEnclosingWindow();
1944 while (width.isEmpty() && parent != null) {
1945 if (parent instanceof DomElement) {
1946 final ComputedCssStyleDeclaration computedCss = win.getComputedStyle((DomElement) parent, null);
1947 width = computedCss.getStyleAttribute(Definition.WIDTH, false);
1948 }
1949 parent = parent.getParentNode();
1950 if (parent instanceof Page) {
1951 break;
1952 }
1953 }
1954 final int pixelWidth = CssPixelValueConverter.pixelValue(width);
1955 final String content = element.getVisibleText();
1956
1957 if (pixelWidth > 0
1958 && !width.isEmpty()
1959 && StringUtils.isNotBlank(content)) {
1960 final int lineCount = Platform.getFontUtil().countLines(content, pixelWidth, fontSize);
1961 defaultHeight *= lineCount;
1962 }
1963 else {
1964 if (element instanceof HtmlSpan && StringUtils.isEmptyOrNull(content)) {
1965 defaultHeight = 0;
1966 }
1967 else {
1968 defaultHeight *= org.apache.commons.lang3.StringUtils.countMatches(content, '\n') + 1;
1969 }
1970 }
1971
1972 final String styleHeight = getStyleAttribute(Definition.HEIGHT, true);
1973 if (styleHeight.endsWith("%")) {
1974 if (page instanceof HtmlPage && !((HtmlPage) page).isQuirksMode()) {
1975 return defaultHeight;
1976 }
1977 }
1978 }
1979 }
1980
1981 final int defaultWindowHeight = element instanceof HtmlCanvas ? 150 : windowHeight;
1982
1983 int height = CssPixelValueConverter.pixelValue(element,
1984 new CssPixelValueConverter.CssValue(defaultHeight, defaultWindowHeight) {
1985 @Override public String get(final ComputedCssStyleDeclaration style) {
1986 final DomElement elem = style.getDomElement();
1987 if (elem instanceof HtmlBody) {
1988 return String.valueOf(elem.getPage().getEnclosingWindow().getInnerHeight());
1989 }
1990
1991 if (isInline) {
1992 return "";
1993 }
1994 return style.getStyleAttribute(Definition.HEIGHT, true);
1995 }
1996 });
1997
1998 if (height == 0 && !explicitHeightSpecified) {
1999 height = defaultHeight;
2000 }
2001
2002 return updateCachedEmptyHeight(height);
2003 }
2004
2005
2006
2007
2008
2009 public int getContentHeight() {
2010
2011
2012
2013
2014 final DomNode node = getDomElement();
2015 if (!node.mayBeDisplayed()) {
2016 return 0;
2017 }
2018
2019 ComputedCssStyleDeclaration lastFlowing = null;
2020 final Set<ComputedCssStyleDeclaration> styles = new HashSet<>();
2021
2022 if (node instanceof HtmlTableRow) {
2023 final HtmlTableRow row = (HtmlTableRow) node;
2024 for (final HtmlTableCell cell : row.getCellIterator()) {
2025 if (cell.mayBeDisplayed()) {
2026 final ComputedCssStyleDeclaration style =
2027 cell.getPage().getEnclosingWindow().getComputedStyle(cell, null);
2028 styles.add(style);
2029 }
2030 }
2031 }
2032 else {
2033 for (final DomNode child : node.getChildren()) {
2034 if (child.mayBeDisplayed()) {
2035 if (child instanceof HtmlElement) {
2036 final HtmlElement e = (HtmlElement) child;
2037 final ComputedCssStyleDeclaration style =
2038 e.getPage().getEnclosingWindow().getComputedStyle(e, null);
2039 final String position = style.getPositionWithInheritance();
2040 if (STATIC.equals(position) || RELATIVE.equals(position)) {
2041 lastFlowing = style;
2042 }
2043 else if (ABSOLUTE.equals(position) || FIXED.equals(position)) {
2044 styles.add(style);
2045 }
2046 }
2047 }
2048 }
2049
2050 if (lastFlowing != null) {
2051 styles.add(lastFlowing);
2052 }
2053 }
2054
2055 int max = 0;
2056 for (final ComputedCssStyleDeclaration style : styles) {
2057 final int h = style.getTop(true, false, false) + style.getCalculatedHeight(true, true);
2058 if (h > max) {
2059 max = h;
2060 }
2061 }
2062 return max;
2063 }
2064
2065
2066
2067
2068
2069
2070
2071 public boolean isScrollable(final boolean horizontal) {
2072 return isScrollable(getDomElement(), horizontal, false);
2073 }
2074
2075
2076
2077
2078 private boolean isScrollable(final DomElement element, final boolean horizontal, final boolean ignoreSize) {
2079 final boolean scrollable;
2080
2081 String overflow;
2082 if (horizontal) {
2083 overflow = getStyleAttribute(Definition.OVERFLOW_X_, false);
2084 if (StringUtils.isEmptyOrNull(overflow)) {
2085 overflow = getStyleAttribute(Definition.OVERFLOW_X, false);
2086 }
2087
2088 if (StringUtils.isEmptyOrNull(overflow)) {
2089 overflow = getStyleAttribute(Definition.OVERFLOW, true);
2090 }
2091 scrollable = (element instanceof HtmlBody || SCROLL.equals(overflow) || AUTO.equals(overflow))
2092 && (ignoreSize || getContentWidth() > getCalculatedWidth());
2093 }
2094 else {
2095 overflow = getStyleAttribute(Definition.OVERFLOW_Y_, false);
2096 if (StringUtils.isEmptyOrNull(overflow)) {
2097 overflow = getStyleAttribute(Definition.OVERFLOW_Y, false);
2098 }
2099
2100 if (StringUtils.isEmptyOrNull(overflow)) {
2101 overflow = getStyleAttribute(Definition.OVERFLOW, true);
2102 }
2103
2104 scrollable = (element instanceof HtmlBody || SCROLL.equals(overflow) || AUTO.equals(overflow))
2105 && (ignoreSize || getContentHeight() > getEmptyHeight(element));
2106 }
2107 return scrollable;
2108 }
2109
2110 private int getBorderHorizontal() {
2111 final Integer borderHorizontal = getCachedBorderHorizontal();
2112 if (borderHorizontal != null) {
2113 return borderHorizontal.intValue();
2114 }
2115
2116 final int border = NONE.equals(getDisplay()) ? 0 : getBorderLeftValue() + getBorderRightValue();
2117 return updateCachedBorderHorizontal(border);
2118 }
2119
2120 private int getBorderVertical() {
2121 final Integer borderVertical = getCachedBorderVertical();
2122 if (borderVertical != null) {
2123 return borderVertical.intValue();
2124 }
2125
2126 final int border = NONE.equals(getDisplay()) ? 0 : getBorderTopValue() + getBorderBottomValue();
2127 return updateCachedBorderVertical(border);
2128 }
2129
2130
2131
2132
2133
2134 public int getBorderLeftValue() {
2135 return CssPixelValueConverter.pixelValue(getBorderLeftWidth());
2136 }
2137
2138
2139
2140
2141
2142 public int getBorderRightValue() {
2143 return CssPixelValueConverter.pixelValue(getBorderRightWidth());
2144 }
2145
2146
2147
2148
2149
2150 public int getBorderTopValue() {
2151 return CssPixelValueConverter.pixelValue(getBorderTopWidth());
2152 }
2153
2154
2155
2156
2157
2158 public int getBorderBottomValue() {
2159 return CssPixelValueConverter.pixelValue(getBorderBottomWidth());
2160 }
2161
2162 private int getPaddingHorizontal() {
2163 final Integer paddingHorizontal = getCachedPaddingHorizontal();
2164 if (paddingHorizontal != null) {
2165 return paddingHorizontal.intValue();
2166 }
2167
2168 final int padding = NONE.equals(getDisplay()) ? 0 : getPaddingLeftValue() + getPaddingRightValue();
2169 return updateCachedPaddingHorizontal(padding);
2170 }
2171
2172 private int getPaddingVertical() {
2173 final Integer paddingVertical = getCachedPaddingVertical();
2174 if (paddingVertical != null) {
2175 return paddingVertical.intValue();
2176 }
2177
2178 final int padding = NONE.equals(getDisplay()) ? 0 : getPaddingTopValue() + getPaddingBottomValue();
2179 return updateCachedPaddingVertical(padding);
2180 }
2181
2182
2183
2184
2185
2186 public int getPaddingLeftValue() {
2187 return CssPixelValueConverter.pixelValue(getPaddingLeft());
2188 }
2189
2190
2191
2192
2193
2194 public int getPaddingRightValue() {
2195 return CssPixelValueConverter.pixelValue(getPaddingRight());
2196 }
2197
2198
2199
2200
2201
2202 public int getPaddingTopValue() {
2203 return CssPixelValueConverter.pixelValue(getPaddingTop());
2204 }
2205
2206
2207
2208
2209
2210 public int getPaddingBottomValue() {
2211 return CssPixelValueConverter.pixelValue(getPaddingBottom());
2212 }
2213
2214
2215
2216
2217
2218 public Integer getCachedWidth() {
2219 return width_;
2220 }
2221
2222
2223
2224
2225
2226
2227 public int updateCachedWidth(final int width) {
2228 width_ = Integer.valueOf(width);
2229 return width;
2230 }
2231
2232
2233
2234
2235
2236 public Integer getCachedHeight() {
2237 return height_;
2238 }
2239
2240
2241
2242
2243
2244
2245 public int updateCachedHeight(final int height) {
2246 height_ = Integer.valueOf(height);
2247 return height;
2248 }
2249
2250
2251
2252
2253
2254 public Integer getCachedEmptyHeight() {
2255 return emptyHeight_;
2256 }
2257
2258
2259
2260
2261
2262
2263 public int updateCachedEmptyHeight(final int emptyHeight) {
2264 emptyHeight_ = Integer.valueOf(emptyHeight);
2265 return emptyHeight;
2266 }
2267
2268
2269
2270
2271
2272 public Integer getCachedTop() {
2273 return top_;
2274 }
2275
2276
2277
2278
2279
2280 public void setCachedTop(final Integer top) {
2281 top_ = top;
2282 }
2283
2284
2285
2286
2287
2288 public Integer getCachedPaddingHorizontal() {
2289 return paddingHorizontal_;
2290 }
2291
2292
2293
2294
2295
2296
2297 public int updateCachedPaddingHorizontal(final int paddingHorizontal) {
2298 paddingHorizontal_ = Integer.valueOf(paddingHorizontal);
2299 return paddingHorizontal;
2300 }
2301
2302
2303
2304
2305
2306 public Integer getCachedPaddingVertical() {
2307 return paddingVertical_;
2308 }
2309
2310
2311
2312
2313
2314
2315 public int updateCachedPaddingVertical(final int paddingVertical) {
2316 paddingVertical_ = Integer.valueOf(paddingVertical);
2317 return paddingVertical;
2318 }
2319
2320
2321
2322
2323
2324 public Integer getCachedBorderHorizontal() {
2325 return borderHorizontal_;
2326 }
2327
2328
2329
2330
2331
2332
2333 public int updateCachedBorderHorizontal(final int borderHorizontal) {
2334 borderHorizontal_ = Integer.valueOf(borderHorizontal);
2335 return borderHorizontal;
2336 }
2337
2338
2339
2340
2341
2342 public Integer getCachedBorderVertical() {
2343 return borderVertical_;
2344 }
2345
2346
2347
2348
2349
2350
2351 public int updateCachedBorderVertical(final int borderVertical) {
2352 borderVertical_ = Integer.valueOf(borderVertical);
2353 return borderVertical;
2354 }
2355
2356
2357
2358
2359
2360
2361
2362 public void applyStyleFromSelector(final CSSStyleDeclarationImpl declaration, final Selector selector) {
2363 final SelectorSpecificity specificity = selector.getSelectorSpecificity();
2364 for (final Property prop : declaration.getProperties()) {
2365 final String name = prop.getName();
2366 final String value = declaration.getPropertyValue(name);
2367 final String priority = declaration.getPropertyPriority(name);
2368 applyLocalStyleAttribute(name, value, priority, specificity);
2369 }
2370 }
2371
2372 private void applyLocalStyleAttribute(final String name, final String newValue, final String priority,
2373 final SelectorSpecificity specificity) {
2374 if (!StyleElement.PRIORITY_IMPORTANT.equals(priority)) {
2375 final StyleElement existingElement = localModifications_.get(name);
2376 if (existingElement != null) {
2377 if (existingElement.isImportant()) {
2378 return;
2379 }
2380 else if (specificity.compareTo(existingElement.getSpecificity()) < 0) {
2381 return;
2382 }
2383 }
2384 }
2385 final StyleElement element = new StyleElement(name, newValue, priority, specificity);
2386 localModifications_.put(name, element);
2387 }
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397 public void setDefaultLocalStyleAttribute(final String name, final String newValue) {
2398 final StyleElement element = new StyleElement(name, newValue, "", SelectorSpecificity.DEFAULT_STYLE_ATTRIBUTE);
2399 localModifications_.put(name, element);
2400 }
2401
2402
2403
2404
2405 @Override
2406 public boolean hasFeature(final BrowserVersionFeatures property) {
2407 return getDomElement().hasFeature(property);
2408 }
2409
2410
2411
2412
2413 @Override
2414 public BrowserVersion getBrowserVersion() {
2415 return getDomElement().getPage().getWebClient().getBrowserVersion();
2416 }
2417
2418
2419
2420
2421 @Override
2422 public boolean isComputed() {
2423 return true;
2424 }
2425
2426
2427
2428
2429 @Override
2430 public String toString() {
2431 return "ComputedCssStyleDeclaration for '" + getDomElement() + "'";
2432 }
2433
2434 private String defaultIfEmpty(final String str, final StyleAttributes.Definition definition) {
2435 return defaultIfEmpty(str, definition, false);
2436 }
2437
2438 private String defaultIfEmpty(final String str, final StyleAttributes.Definition definition,
2439 final boolean isPixel) {
2440 if (!getDomElement().isAttachedToPage()) {
2441 return EMPTY_FINAL;
2442 }
2443 if (str == null || str.isEmpty()) {
2444 return definition.getDefaultComputedValue(getBrowserVersion());
2445 }
2446 if (isPixel) {
2447 return pixelString(str);
2448 }
2449 return str;
2450 }
2451
2452
2453
2454
2455
2456
2457 private String defaultIfEmpty(final String str, final String toReturnIfEmptyOrDefault, final String defaultValue) {
2458 if (!getDomElement().isAttachedToPage()) {
2459 return EMPTY_FINAL;
2460 }
2461 if (str == null || str.isEmpty() || str.equals(defaultValue)) {
2462 return toReturnIfEmptyOrDefault;
2463 }
2464 return str;
2465 }
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475 private static String pixelString(final String value) {
2476 if (EMPTY_FINAL == value || value.endsWith("px")) {
2477 return value;
2478 }
2479 return CssPixelValueConverter.pixelValue(value) + "px";
2480 }
2481 }