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