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