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