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