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