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