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