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.CSS_BACKGROUND_INITIAL;
18 import static org.htmlunit.BrowserVersionFeatures.CSS_BACKGROUND_RGBA;
19 import static org.htmlunit.css.CssStyleSheet.FIXED;
20 import static org.htmlunit.css.CssStyleSheet.INITIAL;
21 import static org.htmlunit.css.CssStyleSheet.NONE;
22 import static org.htmlunit.css.CssStyleSheet.REPEAT;
23 import static org.htmlunit.css.CssStyleSheet.SCROLL;
24
25 import java.io.Serializable;
26 import java.net.URL;
27 import java.util.LinkedHashMap;
28 import java.util.Map;
29 import java.util.regex.Matcher;
30 import java.util.regex.Pattern;
31
32 import org.htmlunit.BrowserVersion;
33 import org.htmlunit.BrowserVersionFeatures;
34 import org.htmlunit.css.StyleAttributes.Definition;
35 import org.htmlunit.cssparser.dom.AbstractCSSRuleImpl;
36 import org.htmlunit.html.DomElement;
37 import org.htmlunit.html.HtmlPage;
38 import org.htmlunit.html.impl.Color;
39 import org.htmlunit.util.StringUtils;
40 import org.htmlunit.util.UrlUtils;
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57 public abstract class AbstractCssStyleDeclaration implements Serializable {
58
59
60
61 private static final Pattern POSITION_PATTERN =
62 Pattern.compile("(?<!\\d)(\\d++\\s*+(%|px|cm|mm|in|pt|pc|em|ex))\\s*+"
63 + "(\\d++\\s*+(%|px|cm|mm|in|pt|pc|em|ex)|top|bottom|center)");
64 private static final Pattern POSITION_PATTERN2 =
65 Pattern.compile("(left|right|center)\\s*(\\d+\\s*(%|px|cm|mm|in|pt|pc|em|ex)|top|bottom|center)");
66 private static final Pattern POSITION_PATTERN3 =
67 Pattern.compile("(top|bottom|center)\\s*(\\d+\\s*(%|px|cm|mm|in|pt|pc|em|ex)|left|right|center)");
68
69
70
71
72
73
74
75 public abstract String getStylePriority(String name);
76
77
78
79
80
81 public abstract String getCssText();
82
83
84
85
86
87
88 public abstract String getStyleAttribute(String name);
89
90
91
92
93
94
95
96
97
98 public abstract String getStyleAttribute(Definition definition, boolean getDefaultValueIfEmpty);
99
100
101
102
103
104
105 public abstract boolean hasFeature(BrowserVersionFeatures property);
106
107
108
109
110
111
112 public abstract BrowserVersion getBrowserVersion();
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133 public String getStyleAttribute(final Definition definition1, final Definition definition2) {
134 final StyleElement element1 = getStyleElement(definition1.getAttributeName());
135 final StyleElement element2 = getStyleElement(definition2.getAttributeName());
136
137 if (element2 == null) {
138 if (element1 == null) {
139 return "";
140 }
141 return element1.getValue();
142 }
143 if (element1 != null) {
144 if (StyleElement.compareToByImportanceAndSpecificity(element1, element2) > 0) {
145 return element1.getValue();
146 }
147 }
148
149 final String[] values = StringUtils.splitAtJavaWhitespace(element2.getValue());
150 if (definition1.name().contains("TOP")) {
151 if (values.length > 0) {
152 return values[0];
153 }
154 return "";
155 }
156 else if (definition1.name().contains("RIGHT")) {
157 if (values.length > 1) {
158 return values[1];
159 }
160 else if (values.length > 0) {
161 return values[0];
162 }
163 return "";
164 }
165 else if (definition1.name().contains("BOTTOM")) {
166 if (values.length > 2) {
167 return values[2];
168 }
169 else if (values.length > 0) {
170 return values[0];
171 }
172 return "";
173 }
174 else if (definition1.name().contains("LEFT")) {
175 if (values.length > 3) {
176 return values[3];
177 }
178 else if (values.length > 1) {
179 return values[1];
180 }
181 else if (values.length > 0) {
182 return values[0];
183 }
184 else {
185 return "";
186 }
187 }
188 else {
189 throw new IllegalStateException("Unsupported definition: " + definition1);
190 }
191 }
192
193
194
195
196
197 public abstract void setCssText(String value);
198
199
200
201
202
203
204
205 public abstract void setStyleAttribute(String name, String newValue, String important);
206
207
208
209
210
211
212 public abstract String removeStyleAttribute(String name);
213
214
215
216
217
218 public abstract int getLength();
219
220
221
222
223
224
225
226 public abstract String item(int index);
227
228
229
230
231
232
233
234 public abstract AbstractCSSRuleImpl getParentRule();
235
236
237
238
239
240
241
242 public abstract StyleElement getStyleElement(String name);
243
244
245
246
247
248
249
250
251 public abstract StyleElement getStyleElementCaseInSensitive(String name);
252
253
254
255
256
257
258
259 public abstract Map<String, StyleElement> getStyleMap();
260
261
262
263
264
265
266 public boolean isComputed() {
267 return false;
268 }
269
270
271
272
273
274 public String getBackgroundAttachment() {
275 String value = getStyleAttribute(Definition.BACKGROUND_ATTACHMENT, false);
276 if (StringUtils.isBlank(value)) {
277 final String bg = getStyleAttribute(Definition.BACKGROUND, true);
278 if (StringUtils.isNotBlank(bg)) {
279 value = findAttachment(bg);
280 if (value == null) {
281 if (hasFeature(CSS_BACKGROUND_INITIAL) && !isComputed()) {
282 return INITIAL;
283 }
284 return SCROLL;
285 }
286 return value;
287 }
288 return "";
289 }
290
291 return value;
292 }
293
294
295
296
297
298 public String getBackgroundColor() {
299 String value = getStyleAttribute(Definition.BACKGROUND_COLOR, false);
300 if (StringUtils.isBlank(value)) {
301 final String bg = getStyleAttribute(Definition.BACKGROUND, false);
302 if (StringUtils.isBlank(bg)) {
303 return "";
304 }
305 value = findColor(bg);
306 if (value == null) {
307 if (hasFeature(CSS_BACKGROUND_INITIAL)) {
308 if (!isComputed()) {
309 return INITIAL;
310 }
311 return "rgba(0, 0, 0, 0)";
312 }
313 if (hasFeature(CSS_BACKGROUND_RGBA)) {
314 return "rgba(0, 0, 0, 0)";
315 }
316 return "transparent";
317 }
318 return value;
319 }
320 if (StringUtils.isBlank(value)) {
321 return "";
322 }
323 return value;
324 }
325
326
327
328
329
330 public String getBackgroundImage() {
331 String value = getStyleAttribute(Definition.BACKGROUND_IMAGE, false);
332 if (StringUtils.isBlank(value)) {
333 final String bg = getStyleAttribute(Definition.BACKGROUND, false);
334 if (StringUtils.isNotBlank(bg)) {
335 value = findImageUrl(bg);
336 final boolean backgroundInitial = hasFeature(CSS_BACKGROUND_INITIAL);
337 if (value == null) {
338 return backgroundInitial && !isComputed() ? INITIAL : NONE;
339 }
340 if (isComputed()) {
341 try {
342 value = value.substring(5, value.length() - 2);
343 final DomElement domElement = ((ComputedCssStyleDeclaration) this).getDomElement();
344 final HtmlPage page = domElement.getHtmlPageOrNull();
345
346 final URL url = UrlUtils.encodeUrl(page.getFullyQualifiedUrl(value), page.getCharset());
347 return "url(\"" + url + "\")";
348 }
349 catch (final Exception ignored) {
350
351 }
352 }
353 return value;
354 }
355 return "";
356 }
357
358 return value;
359 }
360
361
362
363
364
365 public String getBackgroundPosition() {
366 String value = getStyleAttribute(Definition.BACKGROUND_POSITION, false);
367 if (value == null) {
368 return null;
369 }
370 if (StringUtils.isBlank(value)) {
371 final String bg = getStyleAttribute(Definition.BACKGROUND, false);
372 if (bg == null) {
373 return null;
374 }
375 if (StringUtils.isNotBlank(bg)) {
376 value = findPosition(bg);
377 final boolean isInitial = hasFeature(CSS_BACKGROUND_INITIAL);
378 if (value == null) {
379 if (isInitial) {
380 return isComputed() ? "" : INITIAL;
381 }
382 return "0% 0%";
383 }
384 if (isComputed()) {
385 final String[] values = StringUtils.splitAtBlank(value);
386 switch (values[0]) {
387 case "left":
388 values[0] = "0%";
389 break;
390
391 case "center":
392 values[0] = "50%";
393 break;
394
395 case "right":
396 values[0] = "100%";
397 break;
398
399 default:
400 }
401 switch (values[1]) {
402 case "top":
403 values[1] = "0%";
404 break;
405
406 case "center":
407 values[1] = "50%";
408 break;
409
410 case "bottom":
411 values[1] = "100%";
412 break;
413
414 default:
415 }
416 value = values[0] + ' ' + values[1];
417 }
418 return value;
419 }
420 return "";
421 }
422
423 return value;
424 }
425
426
427
428
429
430 public String getBackgroundRepeat() {
431 String value = getStyleAttribute(Definition.BACKGROUND_REPEAT, false);
432 if (StringUtils.isBlank(value)) {
433 final String bg = getStyleAttribute(Definition.BACKGROUND, false);
434 if (StringUtils.isNotBlank(bg)) {
435 value = findRepeat(bg);
436 if (value == null) {
437 if (hasFeature(CSS_BACKGROUND_INITIAL) && !isComputed()) {
438 return INITIAL;
439 }
440 return REPEAT;
441 }
442 return value;
443 }
444 return "";
445 }
446
447 return value;
448 }
449
450
451
452
453
454 public String getBlockSize() {
455 return getStyleAttribute(Definition.BLOCK_SIZE, true);
456 }
457
458
459
460
461
462 public String getBorderBottomColor() {
463 String value = getStyleAttribute(Definition.BORDER_BOTTOM_COLOR, false);
464 if (value.isEmpty()) {
465 value = findColor(getStyleAttribute(Definition.BORDER_BOTTOM, false));
466 if (value == null) {
467 value = findColor(getStyleAttribute(Definition.BORDER, false));
468 }
469 if (value == null) {
470 value = "";
471 }
472 }
473 return value;
474 }
475
476
477
478
479
480 public String getBorderBottomStyle() {
481 String value = getStyleAttribute(Definition.BORDER_BOTTOM_STYLE, false);
482 if (value.isEmpty()) {
483 value = findBorderStyle(getStyleAttribute(Definition.BORDER_BOTTOM, false));
484 if (value == null) {
485 value = findBorderStyle(getStyleAttribute(Definition.BORDER, false));
486 }
487 if (value == null) {
488 value = "";
489 }
490 }
491 return value;
492 }
493
494
495
496
497
498 public String getBorderBottomWidth() {
499 return getBorderWidth(Definition.BORDER_BOTTOM_WIDTH, Definition.BORDER_BOTTOM);
500 }
501
502
503
504
505
506 public String getBorderLeftColor() {
507 String value = getStyleAttribute(Definition.BORDER_LEFT_COLOR, false);
508 if (value.isEmpty()) {
509 value = findColor(getStyleAttribute(Definition.BORDER_LEFT, false));
510 if (value == null) {
511 value = findColor(getStyleAttribute(Definition.BORDER, false));
512 }
513 if (value == null) {
514 value = "";
515 }
516 }
517 return value;
518 }
519
520
521
522
523
524 public String getBorderLeftStyle() {
525 String value = getStyleAttribute(Definition.BORDER_LEFT_STYLE, false);
526 if (value.isEmpty()) {
527 value = findBorderStyle(getStyleAttribute(Definition.BORDER_LEFT, false));
528 if (value == null) {
529 value = findBorderStyle(getStyleAttribute(Definition.BORDER, false));
530 }
531 if (value == null) {
532 value = "";
533 }
534 }
535 return value;
536 }
537
538
539
540
541
542 public String getBorderLeftWidth() {
543 return getBorderWidth(Definition.BORDER_LEFT_WIDTH, Definition.BORDER_LEFT);
544 }
545
546
547
548
549
550
551
552 private String getBorderWidth(final Definition borderSideWidth, final Definition borderSide) {
553 String value = getStyleAttribute(borderSideWidth, false);
554 if (value.isEmpty()) {
555 value = findBorderWidth(getStyleAttribute(borderSide, false));
556 if (value == null) {
557 final String borderWidth = getStyleAttribute(Definition.BORDER_WIDTH, false);
558 if (!StringUtils.isEmptyOrNull(borderWidth)) {
559 final String[] values = StringUtils.splitAtJavaWhitespace(borderWidth);
560 int index = values.length;
561 if (borderSideWidth.name().contains("TOP")) {
562 index = 0;
563 }
564 else if (borderSideWidth.name().contains("RIGHT")) {
565 index = 1;
566 }
567 else if (borderSideWidth.name().contains("BOTTOM")) {
568 index = 2;
569 }
570 else if (borderSideWidth.name().contains("LEFT")) {
571 index = 3;
572 }
573 if (index < values.length) {
574 value = values[index];
575 }
576 }
577 }
578 if (value == null) {
579 value = findBorderWidth(getStyleAttribute(Definition.BORDER, false));
580 }
581 if (value == null) {
582 value = "";
583 }
584 }
585 return value;
586 }
587
588
589
590
591
592 public String getBorderRightColor() {
593 String value = getStyleAttribute(Definition.BORDER_RIGHT_COLOR, false);
594 if (value.isEmpty()) {
595 value = findColor(getStyleAttribute(Definition.BORDER_RIGHT, false));
596 if (value == null) {
597 value = findColor(getStyleAttribute(Definition.BORDER, false));
598 }
599 if (value == null) {
600 value = "";
601 }
602 }
603 return value;
604 }
605
606
607
608
609
610 public String getBorderRightStyle() {
611 String value = getStyleAttribute(Definition.BORDER_RIGHT_STYLE, false);
612 if (value.isEmpty()) {
613 value = findBorderStyle(getStyleAttribute(Definition.BORDER_RIGHT, false));
614 if (value == null) {
615 value = findBorderStyle(getStyleAttribute(Definition.BORDER, false));
616 }
617 if (value == null) {
618 value = "";
619 }
620 }
621 return value;
622 }
623
624
625
626
627
628 public String getBorderRightWidth() {
629 return getBorderWidth(Definition.BORDER_RIGHT_WIDTH, Definition.BORDER_RIGHT);
630 }
631
632
633
634
635
636 public String getBorderTop() {
637 return getStyleAttribute(Definition.BORDER_TOP, true);
638 }
639
640
641
642
643
644 public String getBorderTopColor() {
645 String value = getStyleAttribute(Definition.BORDER_TOP_COLOR, false);
646 if (value.isEmpty()) {
647 value = findColor(getStyleAttribute(Definition.BORDER_TOP, false));
648 if (value == null) {
649 value = findColor(getStyleAttribute(Definition.BORDER, false));
650 }
651 if (value == null) {
652 value = "";
653 }
654 }
655 return value;
656 }
657
658
659
660
661
662 public String getBorderTopStyle() {
663 String value = getStyleAttribute(Definition.BORDER_TOP_STYLE, false);
664 if (value.isEmpty()) {
665 value = findBorderStyle(getStyleAttribute(Definition.BORDER_TOP, false));
666 if (value == null) {
667 value = findBorderStyle(getStyleAttribute(Definition.BORDER, false));
668 }
669 if (value == null) {
670 value = "";
671 }
672 }
673 return value;
674 }
675
676
677
678
679
680 public String getBorderTopWidth() {
681 return getBorderWidth(Definition.BORDER_TOP_WIDTH, Definition.BORDER_TOP);
682 }
683
684
685
686
687
688 public String getBottom() {
689 return getStyleAttribute(Definition.BOTTOM, true);
690 }
691
692
693
694
695
696 public String getColor() {
697 return getStyleAttribute(Definition.COLOR, true);
698 }
699
700
701
702
703
704 public String getCssFloat() {
705 return getStyleAttribute(Definition.FLOAT, true);
706 }
707
708
709
710
711
712 public String getDisplay() {
713 return getStyleAttribute(Definition.DISPLAY, true);
714 }
715
716
717
718
719
720 public String getFont() {
721 return getStyleAttribute(Definition.FONT, true);
722 }
723
724
725
726
727
728 public String getFontFamily() {
729 return getStyleAttribute(Definition.FONT_FAMILY, true);
730 }
731
732
733
734
735
736 public String getFontSize() {
737 return getStyleAttribute(Definition.FONT_SIZE, true);
738 }
739
740
741
742
743
744 public String getHeight() {
745 return getStyleAttribute(Definition.HEIGHT, true);
746 }
747
748
749
750
751
752
753 public String getLeft() {
754 return getStyleAttribute(Definition.LEFT, true);
755 }
756
757
758
759
760
761
762 public String getLetterSpacing() {
763 return getStyleAttribute(Definition.LETTER_SPACING, true);
764 }
765
766
767
768
769
770
771 public String getLineHeight() {
772 return getStyleAttribute(Definition.LINE_HEIGHT, true);
773 }
774
775
776
777
778
779
780 public String getMargin() {
781 return getStyleAttribute(Definition.MARGIN, true);
782 }
783
784
785
786
787
788
789 public String getMarginBottom() {
790 return getStyleAttribute(Definition.MARGIN_BOTTOM, Definition.MARGIN);
791 }
792
793
794
795
796
797
798 public String getMarginLeft() {
799 return getStyleAttribute(Definition.MARGIN_LEFT, Definition.MARGIN);
800 }
801
802
803
804
805
806
807 public String getMarginRight() {
808 return getStyleAttribute(Definition.MARGIN_RIGHT, Definition.MARGIN);
809 }
810
811
812
813
814
815
816 public String getMarginTop() {
817 return getStyleAttribute(Definition.MARGIN_TOP, Definition.MARGIN);
818 }
819
820
821
822
823
824
825 public String getMaxHeight() {
826 return getStyleAttribute(Definition.MAX_HEIGHT, true);
827 }
828
829
830
831
832
833
834 public String getMaxWidth() {
835 return getStyleAttribute(Definition.MAX_WIDTH, true);
836 }
837
838
839
840
841
842
843 public String getMinHeight() {
844 return getStyleAttribute(Definition.MIN_HEIGHT, true);
845 }
846
847
848
849
850
851
852 public String getMinWidth() {
853 return getStyleAttribute(Definition.MIN_WIDTH, true);
854 }
855
856
857
858
859
860
861 public String getOpacity() {
862 final String opacity = getStyleAttribute(Definition.OPACITY, false);
863 if (opacity == null || opacity.isEmpty()) {
864 return "";
865 }
866
867 final String trimmedOpacity = opacity.trim();
868 try {
869 final double value = Double.parseDouble(trimmedOpacity);
870 if (value % 1 == 0) {
871 return Integer.toString((int) value);
872 }
873 return Double.toString(value);
874 }
875 catch (final NumberFormatException ignored) {
876
877 }
878 return "";
879 }
880
881
882
883
884
885
886 public String getOrphans() {
887 return getStyleAttribute(Definition.ORPHANS, true);
888 }
889
890
891
892
893
894
895 public String getOutline() {
896 return getStyleAttribute(Definition.OUTLINE, true);
897 }
898
899
900
901
902
903
904 public String getOutlineWidth() {
905 return getStyleAttribute(Definition.OUTLINE_WIDTH, true);
906 }
907
908
909
910
911
912
913 public String getPadding() {
914 return getStyleAttribute(Definition.PADDING, true);
915 }
916
917
918
919
920
921
922 public String getPaddingBottom() {
923 return getStyleAttribute(Definition.PADDING_BOTTOM, Definition.PADDING);
924 }
925
926
927
928
929
930
931 public String getPaddingLeft() {
932 return getStyleAttribute(Definition.PADDING_LEFT, Definition.PADDING);
933 }
934
935
936
937
938
939
940 public String getPaddingRight() {
941 return getStyleAttribute(Definition.PADDING_RIGHT, Definition.PADDING);
942 }
943
944
945
946
947
948
949 public String getPaddingTop() {
950 return getStyleAttribute(Definition.PADDING_TOP, Definition.PADDING);
951 }
952
953
954
955
956
957
958 public String getPosition() {
959 return getStyleAttribute(Definition.POSITION, true);
960 }
961
962
963
964
965
966
967 public String getRight() {
968 return getStyleAttribute(Definition.RIGHT, true);
969 }
970
971
972
973
974
975
976 public String getRubyAlign() {
977 return getStyleAttribute(Definition.RUBY_ALIGN, true);
978 }
979
980
981
982
983
984
985 public String getSize() {
986 return getStyleAttribute(Definition.SIZE, true);
987 }
988
989
990
991
992
993
994 public String getTextIndent() {
995 return getStyleAttribute(Definition.TEXT_INDENT, true);
996 }
997
998
999
1000
1001
1002
1003 public String getTop() {
1004 return getStyleAttribute(Definition.TOP, true);
1005 }
1006
1007
1008
1009
1010
1011
1012 public String getVerticalAlign() {
1013 return getStyleAttribute(Definition.VERTICAL_ALIGN, true);
1014 }
1015
1016
1017
1018
1019
1020
1021 public String getWidows() {
1022 return getStyleAttribute(Definition.WIDOWS, true);
1023 }
1024
1025
1026
1027
1028
1029
1030 public String getWidth() {
1031 return getStyleAttribute(Definition.WIDTH, true);
1032 }
1033
1034
1035
1036
1037
1038
1039 public String getWordSpacing() {
1040 return getStyleAttribute(Definition.WORD_SPACING, true);
1041 }
1042
1043
1044
1045
1046
1047
1048 public String getZIndex() {
1049 final String value = getStyleAttribute(Definition.Z_INDEX_, true);
1050 try {
1051 Integer.parseInt(value);
1052 return value;
1053 }
1054 catch (final NumberFormatException e) {
1055 return "";
1056 }
1057 }
1058
1059
1060
1061
1062
1063
1064
1065 private static String findAttachment(final String text) {
1066 if (text.contains(SCROLL)) {
1067 return SCROLL;
1068 }
1069 if (text.contains(FIXED)) {
1070 return FIXED;
1071 }
1072 return null;
1073 }
1074
1075
1076
1077
1078
1079
1080
1081 private static String findColor(final String text) {
1082 Color tmpColor = StringUtils.findColorRGB(text);
1083 if (tmpColor != null) {
1084 return StringUtils.formatColor(tmpColor);
1085 }
1086
1087 final String[] tokens = StringUtils.splitAtBlank(text);
1088 for (final String token : tokens) {
1089 if (CssColors.isColorKeyword(token)) {
1090 return token;
1091 }
1092
1093 tmpColor = StringUtils.asColorHexadecimal(token);
1094 if (tmpColor != null) {
1095 return StringUtils.formatColor(tmpColor);
1096 }
1097 }
1098 return null;
1099 }
1100
1101
1102
1103
1104
1105
1106
1107 private static String findImageUrl(final String text) {
1108
1109
1110
1111
1112
1113
1114 final int length = text.length();
1115
1116
1117
1118
1119 int closing = -1;
1120 int urlEnd = -1;
1121 int newline = -1;
1122
1123 int start = text.indexOf("url(");
1124 while (start != -1) {
1125 int urlStart = start + 4;
1126 while (urlStart < length && isCssWhitespace(text.charAt(urlStart))) {
1127 urlStart++;
1128 }
1129
1130 if (urlStart < length && isQuote(text.charAt(urlStart))) {
1131
1132 final char quote = text.charAt(urlStart);
1133 urlStart++;
1134 int end = urlStart;
1135 while (end < length && text.charAt(end) != quote && !isCssNewline(text.charAt(end))) {
1136 if (text.charAt(end) == '\\') {
1137
1138 end++;
1139 }
1140 end++;
1141 }
1142 if (end >= length) {
1143 return "url(\"" + text.substring(urlStart, length) + "\")";
1144 }
1145 if (text.charAt(end) == quote) {
1146 int pos = end + 1;
1147 while (pos < length && isCssWhitespace(text.charAt(pos))) {
1148 pos++;
1149 }
1150 if (pos == length || text.charAt(pos) == ')') {
1151 return "url(\"" + text.substring(urlStart, end) + "\")";
1152 }
1153 }
1154
1155
1156
1157 start = text.indexOf("url(", end);
1158 continue;
1159 }
1160
1161 if (closing < urlStart) {
1162 closing = text.indexOf(')', urlStart);
1163 if (closing == -1) {
1164
1165 closing = length;
1166 }
1167
1168 urlEnd = closing;
1169 while (urlEnd > urlStart && isCssWhitespace(text.charAt(urlEnd - 1))) {
1170 urlEnd--;
1171 }
1172 if (urlEnd > urlStart && isQuote(text.charAt(urlEnd - 1))) {
1173 urlEnd--;
1174 }
1175
1176 newline = urlEnd - 1;
1177 while (newline >= urlStart && !isCssNewline(text.charAt(newline))) {
1178 newline--;
1179 }
1180 }
1181
1182
1183 if (newline < urlStart) {
1184 return "url(\"" + text.substring(urlStart, Math.max(urlEnd, urlStart)) + "\")";
1185 }
1186 start = text.indexOf("url(", start + 1);
1187 }
1188 return null;
1189 }
1190
1191
1192
1193
1194
1195
1196
1197
1198 private static boolean isCssWhitespace(final char c) {
1199 return c == ' ' || c == '\t' || isCssNewline(c);
1200 }
1201
1202
1203
1204
1205
1206
1207
1208 private static boolean isQuote(final char c) {
1209 return c == '"' || c == '\'';
1210 }
1211
1212
1213
1214
1215
1216
1217
1218
1219 private static boolean isCssNewline(final char c) {
1220 return c == '\n' || c == '\r' || c == '\f';
1221 }
1222
1223
1224
1225
1226
1227
1228
1229 private static String findPosition(final String text) {
1230 Matcher m = POSITION_PATTERN.matcher(text);
1231 if (m.find()) {
1232 return m.group(1) + " " + m.group(3);
1233 }
1234 m = POSITION_PATTERN2.matcher(text);
1235 if (m.find()) {
1236 return m.group(1) + " " + m.group(2);
1237 }
1238 m = POSITION_PATTERN3.matcher(text);
1239 if (m.find()) {
1240 return m.group(2) + " " + m.group(1);
1241 }
1242 return null;
1243 }
1244
1245
1246
1247
1248
1249
1250
1251 private static String findRepeat(final String text) {
1252 if (text.contains("repeat-x")) {
1253 return "repeat-x";
1254 }
1255 if (text.contains("repeat-y")) {
1256 return "repeat-y";
1257 }
1258 if (text.contains("no-repeat")) {
1259 return "no-repeat";
1260 }
1261 if (text.contains(REPEAT)) {
1262 return REPEAT;
1263 }
1264 return null;
1265 }
1266
1267
1268
1269
1270
1271
1272
1273 private static String findBorderStyle(final String text) {
1274 for (final String token : StringUtils.splitAtBlank(text)) {
1275 if (isBorderStyle(token)) {
1276 return token;
1277 }
1278 }
1279 return null;
1280 }
1281
1282
1283
1284
1285
1286
1287
1288 private static boolean isBorderStyle(final String token) {
1289 return NONE.equalsIgnoreCase(token) || "hidden".equalsIgnoreCase(token)
1290 || "dotted".equalsIgnoreCase(token) || "dashed".equalsIgnoreCase(token)
1291 || "solid".equalsIgnoreCase(token) || "double".equalsIgnoreCase(token)
1292 || "groove".equalsIgnoreCase(token) || "ridge".equalsIgnoreCase(token)
1293 || "inset".equalsIgnoreCase(token) || "outset".equalsIgnoreCase(token);
1294 }
1295
1296
1297
1298
1299
1300
1301
1302 private static String findBorderWidth(final String text) {
1303 for (final String token : StringUtils.splitAtBlank(text)) {
1304 if (isBorderWidth(token)) {
1305 return token;
1306 }
1307 }
1308 return null;
1309 }
1310
1311
1312
1313
1314
1315
1316
1317 private static boolean isBorderWidth(final String token) {
1318 return "thin".equalsIgnoreCase(token) || "medium".equalsIgnoreCase(token)
1319 || "thick".equalsIgnoreCase(token) || isLength(token);
1320 }
1321
1322
1323
1324
1325
1326
1327
1328 static boolean isLength(final String token) {
1329 if (token.endsWith("%")) {
1330 try {
1331 Double.parseDouble(token.substring(0, token.length() - 1));
1332 return true;
1333 }
1334 catch (final NumberFormatException ignored) {
1335
1336 }
1337 return false;
1338 }
1339
1340 if (token.endsWith("em") || token.endsWith("ex") || token.endsWith("px") || token.endsWith("in")
1341 || token.endsWith("cm") || token.endsWith("mm") || token.endsWith("pt") || token.endsWith("pc")) {
1342
1343 try {
1344 Double.parseDouble(token.substring(0, token.length() - 2));
1345 return true;
1346 }
1347 catch (final NumberFormatException ignored) {
1348
1349 }
1350 return false;
1351 }
1352
1353 return false;
1354 }
1355 }