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