1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.htmlunit.javascript.regexp;
16
17 import static java.nio.charset.StandardCharsets.ISO_8859_1;
18
19 import org.apache.commons.io.IOUtils;
20 import org.htmlunit.WebDriverTestCase;
21 import org.htmlunit.junit.annotation.Alerts;
22 import org.junit.jupiter.api.Test;
23 import org.junit.jupiter.api.Timeout;
24
25
26
27
28
29
30
31
32
33
34
35
36
37 public class RegExp3Test extends WebDriverTestCase {
38
39 private static final String SCRIPT_TEST_MATCH = "function arrayToString(_arr) {\n"
40 + " if (_arr == null) return null;\n"
41 + " var s = '[';\n"
42 + " for (var i = 0; i < _arr.length; i++)\n"
43 + " {\n"
44 + " if (i != 0) s += ', ';\n"
45 + " s += _arr[i];\n"
46 + " }\n"
47 + " s += ']';\n"
48 + " return s;\n"
49 + "}\n"
50
51 + "function assertArrEquals(actual, expected) {\n"
52 + " if (expected == null) {\n"
53 + " if (actual != null)\n"
54 + " throw 'Expected >null< got >' + actual + '<';\n"
55 + " else return;\n"
56 + " }\n"
57 + " var expectedStr = arrayToString(expected);\n"
58 + " var actualStr = arrayToString(actual);\n"
59 + " if (expectedStr != actualStr)\n"
60 + " throw 'Expected >' + expectedStr + '< got >' + actualStr + '<';\n"
61 + "}\n"
62
63 + "assertArrEquals('ab'.match(), ['']);\n"
64 + "assertArrEquals('ab'.match('foo'), null);\n"
65 + "assertArrEquals('ab'.match('a'), ['a']);\n"
66 + "assertArrEquals('abab'.match('a'), ['a']);\n"
67 + "assertArrEquals('abab'.match('.a'), ['ba']);\n"
68 + "assertArrEquals('abab'.match(/.a/), ['ba']);\n"
69 + "assertArrEquals('li'.match(/^([^a-z0-9_-])?([a-z0-9_-]+)(.*)/i), ['li', undefined, 'li', '']);\n"
70 + "assertArrEquals('abab'.match(new RegExp('.a')), ['ba']);\n"
71 + "var s = '<script>var a = 1;</' + 'script>';\n"
72 + "var re = '(?:<script.*?>)((\\n|\\r|.)*?)(?:<\\/script>)';\n"
73 + "assertArrEquals(s.match(re), [s, 'var a = 1;', ';']);\n";
74
75
76
77
78 @Test
79 @Alerts("ab21")
80 public void replaceNormalStringReplace() throws Exception {
81 testEvaluate("'121'.replace('1', 'ab')");
82 }
83
84
85
86
87 @Test
88 @Alerts("xyz121")
89 public void replaceNormalStringEmptySearch() throws Exception {
90 testEvaluate("'121'.replace('', 'xyz')");
91 }
92
93
94
95
96 @Test
97 @Alerts("1")
98 public void replaceNormalStringEmptyReplace() throws Exception {
99 testEvaluate("'121'.replace('21', '')");
100 }
101
102
103
104
105 @Test
106 @Alerts("xyz")
107 public void replaceNormalStringEmptyStringEmptySearch() throws Exception {
108 testEvaluate("''.replace('', 'xyz')");
109 }
110
111
112
113
114 @Test
115 @Alerts("a$c21")
116 public void replaceNormalStringReplaceSingleDollar() throws Exception {
117 testEvaluate("'121'.replace('1', 'a$c')");
118 }
119
120
121
122
123 @Test
124 @Alerts("a$c21")
125 public void replaceNormalStringReplaceDoubleDollar() throws Exception {
126 testEvaluate("'121'.replace('1', 'a$$c')");
127 }
128
129
130
131
132 @Test
133 @Alerts("a121")
134 public void replaceNormalStringReplaceSubstring() throws Exception {
135 testEvaluate("'121'.replace('1', 'a$&')");
136 }
137
138
139
140
141 @Test
142 @Alerts("abaabe")
143 public void replaceNormalStringReplacePreceeding() throws Exception {
144 testEvaluate("'abcde'.replace('cd', 'a$`')");
145 }
146
147
148
149
150 @Test
151 @Alerts("a21")
152 public void replaceNormalStringReplacePreceedingEmpty() throws Exception {
153 testEvaluate("'121'.replace('1', 'a$`')");
154 }
155
156
157
158
159 @Test
160 @Alerts("abaee")
161 public void replaceNormalStringReplaceFollowing() throws Exception {
162 testEvaluate("'abcde'.replace('cd', \"a$'\")");
163 }
164
165
166
167
168 @Test
169 @Alerts("aba")
170 public void replaceNormalStringReplaceFollowingEmpty() throws Exception {
171 testEvaluate("'abcd'.replace('cd', \"a$'\")");
172 }
173
174
175
176
177 @Test
178 @Alerts("aba$0")
179 public void replaceNormalStringReplaceGroupZero() throws Exception {
180 testEvaluate("'abcd'.replace('cd', 'a$0')");
181 }
182
183
184
185
186 @Test
187 @Alerts("aba$1")
188 public void replaceNormalStringReplaceGroupOne() throws Exception {
189 testEvaluate("'abcd'.replace('cd', 'a$1')");
190 }
191
192
193
194
195
196 @Test
197 @Alerts("123456")
198 public void replaceNormalStringWithRegexpChars() throws Exception {
199 testEvaluate("'123456'.replace('/{\\d+}', '')");
200 }
201
202
203
204
205 @Test
206 @Alerts("123456")
207 public void replaceWithUndefinedPattern() throws Exception {
208 final String html = "<html><head><script>\n"
209 + LOG_TITLE_FUNCTION
210 + " function test() {\n"
211 + " var pattern;\n"
212 + " log('123456'.replace(pattern, ''));\n"
213 + " }\n"
214 + "</script></head><body onload='test()'>\n"
215 + "</body></html>";
216
217 loadPageVerifyTitle2(html);
218 }
219
220
221
222
223 @Test
224 @Alerts("123456")
225 public void replace() throws Exception {
226 final String html = "<html><head><script>\n"
227 + LOG_TITLE_FUNCTION
228 + " function test() {\n"
229 + " var pattern = /{\\d+}/g;\n"
230 + " log('123456'.replace(pattern, ''));\n"
231 + " }\n"
232 + "</script></head><body onload='test()'>\n"
233 + "</body></html>";
234
235 loadPageVerifyTitle2(html);
236 }
237
238
239
240
241 @Test
242 public void match() throws Exception {
243 final String html = "<html><head><script>\n"
244 + LOG_TITLE_FUNCTION
245 + SCRIPT_TEST_MATCH
246 + "</script></head><body>\n"
247 + "</body></html>";
248
249 loadPageVerifyTitle2(html);
250 }
251
252
253
254
255 @Test
256 @Alerts("0")
257 public void index() throws Exception {
258 final String html = "<html><head><script>\n"
259 + LOG_TITLE_FUNCTION
260 + " function test() {\n"
261 + " var match = '#{tests} tests'.match(/(^|.|\\r|\\n)(#\\{(.*?)\\})/);\n"
262 + " log(match.index);\n"
263 + " }\n"
264 + "</script></head><body onload='test()'>\n"
265 + "</body></html>";
266
267 loadPageVerifyTitle2(html);
268 }
269
270
271
272
273 @Test
274 @Alerts("ab,a")
275 public void match_NotFirstCharacter() throws Exception {
276 testEvaluate("\"ab\".match(/^(.)[^\\1]$/)");
277 }
278
279
280
281
282 @Test
283 @Alerts("boo();")
284 public void regExp_exec() throws Exception {
285 final String html = "<html><head><script>\n"
286 + LOG_TITLE_FUNCTION
287 + " function test() {\n"
288 + " var re = new RegExp('(?:<s' + 'cript.*?>)(.*)<\\/script>');\n"
289 + " var t = 'foo <scr' + 'ipt>boo();</' + 'script>bar';\n"
290 + " var r = re.exec(t);\n"
291 + " log(r[1]);\n"
292 + " }\n"
293 + "</script></head><body onload='test()'>\n"
294 + "</body></html>";
295
296 loadPageVerifyTitle2(html);
297 }
298
299
300
301
302 @Test
303 @Alerts("<script>boo();</script>")
304 public void flag_global() throws Exception {
305 final String html = "<html><head><script>\n"
306 + LOG_TITLE_FUNCTION
307 + " function test() {\n"
308 + " var str = 'foo <script>boo();<'+'/script>bar';\n"
309 + " var regExp = new RegExp('<script[^>]*>([\\\\S\\\\s]*?)<\\/script>', 'img');\n"
310 + " log(str.match(regExp));\n"
311 + " }\n"
312 + "</script></head><body onload='test()'>\n"
313 + "</body></html>";
314
315 loadPageVerifyTitle2(html);
316 }
317
318
319
320
321
322 @Test
323 @Alerts({"true", "false", "true"})
324 public void prototype() throws Exception {
325 final String html = "<html><head><script>\n"
326 + LOG_TITLE_FUNCTION
327 + " function test() {\n"
328 + " var regexp = /^(?:(?:(?:(?:(?:1[6-9]|[2-9]\\d)?(?:0[48]|[2468][048]|[13579][26])|"
329 + "(?:(?:16|[2468][048]|[3579][26])00)))(\\/|-|\\.)(?:0?2\\1(?:29)))|(?:(?:(?:1[6-9]|[2-9]\\d)?\\d{2})"
330 + "(\\/|-|\\.)(?:(?:(?:0?[13578]|1[02])\\2(?:31))|(?:(?:0?[1,3-9]|1[0-2])\\2(29|30))|(?:(?:0?[1-9])|"
331 + "(?:1[0-2]))\\2(?:0?[1-9]|1\\d|2[0-8]))))$/;\n"
332 + " var str = '2001-06-16';\n"
333 + " log(regexp.test(str));\n"
334 + " log(regexp.test('hello'));\n"
335 + " log(regexp.exec(str) != null);\n"
336 + " }\n"
337 + "</script></head><body onload='test()'>\n"
338 + "</body></html>";
339
340 loadPageVerifyTitle2(html);
341 }
342
343
344
345
346 @Test
347 public void test() throws Exception {
348 test("/foo/", "hello foo", true, true, false);
349 test("/foo/gi", "hello foo", true, false, false);
350 test("/foo/gi", "hello Foo", true, false, false);
351 }
352
353 private void test(final String regexp, final String testString, final boolean... expectedResults) throws Exception {
354 final String html = "<html><head><script>\n"
355 + LOG_TITLE_FUNCTION
356 + " function test() {\n"
357 + " var regexp = " + regexp + ";\n"
358 + " var str = '" + testString + "';\n"
359 + " log(regexp.test(str));\n"
360 + " log(regexp.exec(str) != null);\n"
361 + " log(regexp.test('blabla'));\n"
362 + " }\n"
363 + "</script></head><body onload='test()'>\n"
364 + "</body></html>";
365
366 setExpectedAlerts(String.valueOf(expectedResults[0]), String.valueOf(expectedResults[1]),
367 String.valueOf(expectedResults[2]));
368
369 loadPageVerifyTitle2(html);
370 }
371
372
373
374
375
376 @Test
377 @Alerts("true")
378 public void test_minimal() throws Exception {
379 final String html = "<html><head><script>\n"
380 + LOG_TITLE_FUNCTION
381 + " function test() {\n"
382 + " var regexp = /((?:2001)-)/;\n"
383 + " var str = '2001-';\n"
384 + " log(regexp.test(str));\n"
385 + " }\n"
386 + "</script></head><body onload='test()'>\n"
387 + "</body></html>";
388
389 loadPageVerifyTitle2(html);
390 }
391
392
393
394
395
396 @Test
397 @Alerts("{#abcd},{,abcd,}")
398 public void regexWithNonEscapedCurlyBraces() throws Exception {
399 final String html = "<html><head><script>\n"
400 + LOG_TITLE_FUNCTION
401 + " function test() {\n"
402 + " var regexp = /(^|{)#([^}]+)(}|$)/;\n"
403 + " var str = '|{#abcd}|';\n"
404 + " log(str.match(regexp));\n"
405 + " }\n"
406 + "</script></head><body onload='test()'>\n"
407 + "</body></html>";
408
409 loadPageVerifyTitle2(html);
410 }
411
412
413
414
415
416 @Test
417 @Alerts("aa-b-b-")
418 public void backSpace() throws Exception {
419 final String html = "<html><head><script>\n"
420 + LOG_TITLE_FUNCTION
421 + " function test() {\n"
422 + " var regexp = /[p\\bz]/g;\n"
423 + " var str = 'aapbzb' + String.fromCharCode(8);\n"
424 + " log(str.replace(regexp, '-'));\n"
425 + " }\n"
426 + "</script></head><body onload='test()'>\n"
427 + "</body></html>";
428
429 loadPageVerifyTitle2(html);
430 }
431
432
433
434
435 @Test
436 @Alerts("")
437 public void dollarSignAndCurlyBracket() throws Exception {
438 testEvaluate("''.replace(/\\${/g, '')");
439 }
440
441
442
443
444 @Test
445 @Alerts({"null", "["})
446 public void openingSquareBracketInCharacterClass() throws Exception {
447 final String html = "<html><head><script>\n"
448 + LOG_TITLE_FUNCTION
449 + "function test() {\n"
450 + " var re = /[[]/;\n"
451 + " log('div'.match(re));\n"
452 + " log('['.match(re));\n"
453 + " }\n"
454 + "</script></head><body onload='test()'>\n"
455 + "</body></html>";
456
457 loadPageVerifyTitle2(html);
458 }
459
460
461
462
463
464 @Test
465 @Alerts("div")
466 public void jquerySizzleChunker() throws Exception {
467 final String html = "<html><head><script>\n"
468 + LOG_TITLE_FUNCTION
469 + " var re = /((?:\\((?:\\([^()]+\\)|[^()]+)+\\)|\\[(?:\\[[^[\\]]*\\]|['\"][^'\"]+['\"]|[^[\\]'\"]+)+\\]"
470 + "|\\\\.|[^ >+~,(\\[]+)+|[>+~])(\\s*,\\s*)?/g\n"
471 + " function test() {\n"
472 + " log('div'.match(re));\n"
473 + " }\n"
474 + "</script></head><body onload='test()'>\n"
475 + "</body></html>";
476
477 loadPageVerifyTitle2(html);
478 }
479
480
481
482
483
484 @Test
485 @Alerts({":toto,toto,,", "null"})
486 public void jqueryPseudo() throws Exception {
487 final String html = "<html><head><script>\n"
488 + LOG_TITLE_FUNCTION
489 + " var re = /:((?:[\\w\\u00c0-\\uFFFF_-]|\\\\.)+)(?:\\((['\"]*)((?:\\([^\\)]+\\)"
490 + "|[^\\2\\(\\)]*)+)\\2\\))?/;\n"
491 + " function test() {\n"
492 + " log(':toto'.match(re));\n"
493 + " log('foo'.match(re));\n"
494 + " }\n"
495 + "</script></head><body onload='test()'>\n"
496 + "</body></html>";
497
498 loadPageVerifyTitle2(html);
499 }
500
501
502
503
504
505 @Test
506 @Alerts({ "[floating=true],floating,=,,true",
507 "[floating=\"true\"],floating,=,\",true",
508 "[floating=\"true'],floating,=,,\"true'",
509 "[floating=\"true],floating,=,,\"true",
510 "[floating=true\"],floating,=,,true\""})
511 public void extJs() throws Exception {
512 final String html = "<html><head><script>\n"
513 + LOG_TITLE_FUNCTION
514 + " var re = /^(?:\\[((?:[@?$])?[\\w\\-]*)\\s*(?:([\\^$*~%!\\/]?=)\\s*(['\\\"])?((?:\\\\\\]|.)*?)\\3)?(?!\\\\)\\])/;\n"
515 + " function test() {\n"
516 + " log('[floating=true]'.match(re));\n"
517 + " log('[floating=\"true\"]'.match(re));\n"
518 + " log('[floating=\"true\\']'.match(re));\n"
519 + " log('[floating=\"true]'.match(re));\n"
520 + " log('[floating=true\"]'.match(re));\n"
521 + " }\n"
522 + "</script></head><body onload='test()'>\n"
523 + "</body></html>";
524
525 loadPageVerifyTitle2(html);
526 }
527
528
529
530
531
532 @Test
533 @Alerts({"axxxxa,a", "xxxx,", "xxxx,", "xxxx,"})
534 public void backReferenceToOptionalGroup() throws Exception {
535 final String html = "<html><head><script>\n"
536 + LOG_TITLE_FUNCTION
537 + "function test() {\n"
538 + " var re = /(a)?x+\\1/;\n"
539 + " log('axxxxa'.match(re));\n"
540 + " log('axxxx'.match(re));\n"
541 + " log('xxxxa'.match(re));\n"
542 + " log('xxxx'.match(re));\n"
543 + "}\n"
544 + "</script></head><body onload='test()'>\n"
545 + "</body></html>";
546
547 loadPageVerifyTitle2(html);
548 }
549
550
551
552
553
554 @Test
555 @Alerts({"abcx\u0004,b,x", "null", "null", "null"})
556 public void backReferenceToNotDefinedGroupsAreHandledAsOctal() throws Exception {
557 final String html = "<html><head>\n<script>\n"
558 + LOG_TEXTAREA_FUNCTION
559 + "function test() {\n"
560 + " var re = /(?:a)(b)c(x)\\4/;\n"
561 + " log('abcx\\04'.match(re));\n"
562 + " log('abcx'.match(re));\n"
563 + " log('abb'.match(re));\n"
564 + " log('abbxx'.match(re));\n"
565 + "}\n"
566 + "</script></head><body onload='test()'>\n"
567 + LOG_TEXTAREA
568 + "</body></html>";
569
570 loadPageVerifyTextArea2(html);
571 }
572
573
574
575
576
577 @Test
578 @Alerts({"abcx,b,x", "abcx,b,x", "null", "null"})
579 public void ignoreBackReferenceNotFinishedGroups() throws Exception {
580 final String html = "<html><head><script>\n"
581 + LOG_TITLE_FUNCTION
582 + "function test() {\n"
583 + " var re = /(?:a)(b)c(x\\2)/;\n"
584 + " log('abcx'.match(re));\n"
585 + " log('abcx\\02'.match(re));\n"
586 + " log('abb'.match(re));\n"
587 + " log('abbxx'.match(re));\n"
588 + "}\n"
589 + "</script></head><body onload='test()'>\n"
590 + "</body></html>";
591
592 loadPageVerifyTitle2(html);
593 }
594
595
596
597
598
599
600 @Test
601 @Alerts({"null", "abb,a,b", "abd,a,b"})
602 public void ignoreBackReferenceInCharacterClass() throws Exception {
603 final String html = "<html><head><script>\n"
604 + LOG_TITLE_FUNCTION
605 + "function test() {\n"
606 + " var re = /(a)(b)[^\\2c]/;\n"
607 + " log('abc'.match(re));\n"
608 + " log('abb'.match(re));\n"
609 + " log('abd'.match(re));\n"
610 + "}\n"
611 + "</script></head><body onload='test()'>\n"
612 + "</body></html>";
613
614 loadPageVerifyTitle2(html);
615 }
616
617
618
619
620
621 @Test
622 @Alerts("afood$0$7b")
623 public void replace_backReferences() throws Exception {
624 testEvaluate("'afoob'.replace(/(foo)/g, '$1d$0$7')");
625 }
626
627
628
629
630
631 @Test
632 @Alerts("hllo")
633 public void replace_backReference_existing() throws Exception {
634 testEvaluate("'hello'.replace(/(h)e/g, '$1')");
635 }
636
637
638
639
640
641 @Test
642 @Alerts("$1llo")
643 public void replace_backReference_notExisting() throws Exception {
644 testEvaluate("'hello'.replace(/he/g, '$1')");
645 }
646
647
648
649
650
651 @Test
652 @Alerts("I$want$these$periods$to$be$$s")
653 public void replace_backReferences_2() throws Exception {
654 testEvaluate("'I.want.these.periods.to.be.$s'.replace(/\\./g, '$')");
655 }
656
657
658
659
660 @Test
661 @Alerts("food bar")
662 public void replace_backReference_ampersand() throws Exception {
663 testEvaluate("'foo bar'.replace(/foo/g, '$&d')");
664 testEvaluate("'foo bar'.replace(/foo/, '$&d')");
665 }
666
667
668
669
670 @Test
671 @Alerts("foo foo ")
672 public void replace_backReference_backtick() throws Exception {
673 testEvaluate("'foo bar'.replace(/bar/g, '$`')");
674 testEvaluate("'foo bar'.replace(/bar/, '$`')");
675 }
676
677
678
679
680 @Test
681 @Alerts(" bar bar")
682 public void replace_backReference_tick() throws Exception {
683 testEvaluate("'foo bar'.replace(/foo/g, '$\\'')");
684 testEvaluate("'foo bar'.replace(/foo/, '$\\'')");
685 }
686
687
688
689
690 @Test
691 @Alerts("$' bar")
692 public void replace_$backReference_tick() throws Exception {
693 testEvaluate("'foo bar'.replace(/foo/g, '$$\\'')");
694 testEvaluate("'foo bar'.replace(/foo/, '$$\\'')");
695 }
696
697
698
699
700 @Test
701 @Alerts("ad$0db")
702 public void replace_backReference_$0() throws Exception {
703 testEvaluate("'afoob'.replace(/(foo)/g, 'd$0d')");
704 testEvaluate("'afoob'.replace(/(foo)/, 'd$0d')");
705 }
706
707
708
709
710 @Test
711 @Alerts("ad$0db")
712 public void replace_backReference_$0WithMultipleGroups() throws Exception {
713 testEvaluate("'afkxxxkob'.replace(/(f)k(.*)k(o)/g, 'd$0d')");
714 testEvaluate("'afkxxxkob'.replace(/(f)k(.*)k(o)/, 'd$0d')");
715 }
716
717
718
719
720 @Test
721 @Alerts("ad$0db")
722 public void replace_backReference_$0WithNoGroups() throws Exception {
723 testEvaluate("'afoob'.replace(/foo/g, 'd$0d')");
724 testEvaluate("'afoob'.replace(/foo/, 'd$0d')");
725 }
726
727
728
729
730 @Test
731 @Alerts("ad$0dbfuoc")
732 public void replace_backReference_$0WithMultipleHits() throws Exception {
733 testEvaluate("'afoobfuoc'.replace(/(f.o)/, 'd$0d')");
734 }
735
736
737
738
739 @Test
740 @Alerts("ad$0dbd$0dc")
741 public void replace_backReference_$0WithMultipleHitsGlobal() throws Exception {
742 testEvaluate("'afoobfuoc'.replace(/(f.o)/g, 'd$0d')");
743 }
744
745
746
747
748 @Test
749 @Alerts("kid\\'s toys")
750 public void escapeQuote() throws Exception {
751 testEvaluate("\"kid's toys\".replace(/'/g, \"\\\\'\")");
752 }
753
754 private static String buildHtml(final String script) {
755 return "<html><head><script>\n"
756 + LOG_TEXTAREA_FUNCTION
757 + "function test() {\n"
758 + script
759 + "\n}</script>\n"
760 + "</head>\n"
761 + "<body onload='test()'>\n"
762 + LOG_TEXTAREA
763 + "</body></html>";
764 }
765
766
767
768
769 @Test
770 @Alerts("INPUT")
771 public void test2() throws Exception {
772 final String html = buildHtml("var description = 'INPUT#BasisRenameInput';\n"
773 + "if(description.match(/^\\s*([a-z0-9\\_\\-]+)/i)) {\n"
774 + " log(RegExp.$1);\n"
775 + "}");
776 loadPageVerifyTextArea2(html);
777 }
778
779
780
781
782 @Test
783 @Alerts("a")
784 public void stackOverflow() throws Exception {
785 final String s = IOUtils.toString(getClass().getResourceAsStream("stackOverflow.txt"),
786 ISO_8859_1);
787 final String html = buildHtml(
788 "var s = '" + s + "';\n"
789 + "s = s.replace(/(\\s*\\S+)*/, 'a');\n"
790 + "log(s);\n");
791 loadPageVerifyTextArea2(html);
792 }
793
794
795
796
797
798 @Test
799 @Alerts("$$x$")
800 public void replaceDollarDollar() throws Exception {
801 testEvaluate("'x'.replace(/(x)/g, '$$$$x$$')");
802 }
803
804
805
806
807
808
809
810 @Test
811 @Alerts({"\\*\\[", "\\\\", "+1", "abcdef", "1\\1abc123\\123de1234\\1234f", "\n \n", "x x", "x\"\\", "$$x$"})
812 public void replaceAll() throws Exception {
813 final String html = "<html>\n"
814 + "<head>\n"
815 + " <script>\n"
816 + LOG_TEXTAREA_FUNCTION
817 + " function test() {\n"
818 + " var regex, replacement, x1, x2, x3, x4, x5;\n"
819 + " regex = $replaceAll('*[', "
820 + "'([/\\\\\\\\\\\\.\\\\*\\\\+\\\\?\\\\|\\\\(\\\\)\\\\[\\\\]\\\\{\\\\}])', '\\\\\\\\$1');\n"
821 + " log(regex);\n"
822 + " replacement = "
823 + "$replaceAll($replaceAll('\\\\', '\\\\\\\\', '\\\\\\\\\\\\\\\\'), '\\\\$', '\\\\\\\\$');\n"
824 + " log(replacement);\n"
825 + " log($replaceAll('*[1', regex, '+'));\n"
826 + " x1 = 'xxxabcxxdexf';\n"
827 + " log($replaceAll(x1, 'x*', ''));\n"
828 + " x2 = '1abc123de1234f';\n"
829 + " log($replaceAll(x2, '([1234]+)', '$1\\\\\\\\$1'));\n"
830 + " x3 = 'x x';\n"
831 + " log($replaceAll(x3, 'x', '\\n'));\n"
832 + " x4 = 'x \\n';\n"
833 + " log($replaceAll(x4, '\\\\\\n', 'x'));\n"
834 + " x5 = 'x';\n"
835 + " log($replaceAll(x5, 'x', '\\\\x\\\\\"\\\\\\\\'));\n"
836 + " log($replaceAll(x5, '(x)', '\\\\$\\\\$$1\\\\$'));\n"
837 + " }\n"
838 + " function $replaceAll(this$static, regex, replace){\n"
839 + " replace = __translateReplaceString(replace);\n"
840 + " return this$static.replace(RegExp(regex, 'g'), replace);\n"
841 + " }\n"
842 + " function __translateReplaceString(replaceStr){\n"
843 + " var pos = 0;\n"
844 + " while (0 <= (pos = replaceStr.indexOf('\\\\', pos))) {\n"
845 + " if (replaceStr.charCodeAt(pos + 1) == 36) {\n"
846 + " replaceStr = replaceStr.substr(0, pos - 0) + '$' + $substring(replaceStr, ++pos);\n"
847 + " }\n"
848 + " else {\n"
849 + " replaceStr = replaceStr.substr(0, pos - 0) + $substring(replaceStr, ++pos);\n"
850 + " }\n"
851 + " }\n"
852 + " return replaceStr;\n"
853 + " }\n"
854 + " function $substring(this$static, beginIndex){\n"
855 + " return this$static.substr(beginIndex, this$static.length - beginIndex);\n"
856 + " }\n"
857 + " </script>\n"
858 + "</head><body onload='test()'>\n"
859 + LOG_TEXTAREA
860 + "</body></html>";
861
862 loadPageVerifyTextArea2(html);
863 }
864
865
866
867
868
869 @Test
870 @Alerts("\\$0")
871 public void replaceBackslashDollar() throws Exception {
872 testEvaluate("'$0'.replace(/\\$/g, '\\\\$')");
873 }
874
875 private void testEvaluate(final String expression) throws Exception {
876 final String script = "log(" + expression + ");";
877 final String html = buildHtml(script);
878
879 loadPageVerifyTextArea2(html);
880 }
881
882
883
884
885
886
887
888 @Test
889 @Alerts("null")
890 public void backslash() throws Exception {
891 final String html = buildHtml("var regexp = /(\\https:\\/\\/)/;\n"
892 + "var url = 'http://localhost/test.html';\n"
893 + "log(url.match(regexp));");
894 loadPageVerifyTextArea2(html);
895 }
896
897
898
899
900
901 @Test
902 @Alerts("\\\\$11")
903 public void dollar() throws Exception {
904 testEvaluate("'\\\\$1'.replace(/\\$/g, '\\\\$1')");
905 }
906
907
908
909
910 @Test
911 @Alerts({"foobar", "$0bar", "$1bar", "\\$1bar", "\\1", "cb", "cb", "a$$b", "a$1b", "a$`b", "a$'b"})
912 public void replaceString() throws Exception {
913 final String html = "<html>\n"
914 + "<head>\n"
915 + " <script>\n"
916 + LOG_TITLE_FUNCTION
917 + " function test() {\n"
918 + " log($replace('bazbar', 'baz', 'foo'));\n"
919 + " log($replace('foobar', 'foo', '$0'));\n"
920 + " log($replace('foobar', 'foo', '$1'));\n"
921 + " log($replace('foobar', 'foo', '\\\\$1'));\n"
922 + " log($replace('*[)1', '*[)', '\\\\'));\n"
923 + " log($replace('$ab', '$a', 'c'));\n"
924 + " log($replace('^ab', '^a', 'c'));\n"
925 + " log($replace('a[x]b', '[x]', '$$'));\n"
926 + " log($replace('a[x]b', '[x]', '$1'));\n"
927 + " log($replace('a[x]b', '[x]', '$`'));\n"
928 + " log($replace('a[x]b', '[x]', \"$'\"));\n"
929 + " }\n"
930 + " function $replace(this$static, from, to){\n"
931 + " var regex, replacement;\n"
932 + " regex = $replaceAll(from, "
933 + "'([/\\\\\\\\\\\\.\\\\*\\\\+\\\\?\\\\|\\\\(\\\\)\\\\[\\\\]\\\\{\\\\}$^])', '\\\\\\\\$1');\n"
934 + " replacement = $replaceAll("
935 + "$replaceAll(to, '\\\\\\\\', '\\\\\\\\\\\\\\\\'), '\\\\$', '\\\\\\\\$');\n"
936 + " return $replaceAll(this$static, regex, replacement);\n"
937 + " }\n"
938 + " function $replaceAll(this$static, regex, replace){\n"
939 + " replace = __translateReplaceString(replace);\n"
940 + " return this$static.replace(RegExp(regex, 'g'), replace);\n"
941 + " }\n"
942 + " function __translateReplaceString(replaceStr){\n"
943 + " var pos = 0;\n"
944 + " while (0 <= (pos = replaceStr.indexOf('\\\\', pos))) {\n"
945 + " if (replaceStr.charCodeAt(pos + 1) == 36) {\n"
946 + " replaceStr = replaceStr.substr(0, pos - 0) + '$' + $substring(replaceStr, ++pos);\n"
947 + " }\n"
948 + " else {\n"
949 + " replaceStr = replaceStr.substr(0, pos - 0) + $substring(replaceStr, ++pos);\n"
950 + " }\n"
951 + " }\n"
952 + " return replaceStr;\n"
953 + " }\n"
954 + " function $substring(this$static, beginIndex){\n"
955 + " return this$static.substr(beginIndex, this$static.length - beginIndex);\n"
956 + " }\n"
957 + " </script>\n"
958 + "</head><body onload='test()'>\n"
959 + "</body></html>";
960
961 loadPageVerifyTitle2(html);
962 }
963
964
965
966
967
968 @Test
969 @Alerts("[k]")
970 public void replace_group2digits() throws Exception {
971 testEvaluate("'abcdefghijkl'.replace(/(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)(l)/g, '[\\$11]')");
972 }
973
974
975
976
977
978 @Test
979 @Alerts("[a8]")
980 public void replace_group2digits_doesntExist() throws Exception {
981 testEvaluate("'abcdefghijkl'.replace(/(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)(l)/g, '[\\$18]')");
982 }
983
984
985
986
987
988 @Test
989 @Alerts(":")
990 public void replaceUnicode() throws Exception {
991 testEvaluate("'\\u003a'.replace(/[\\u00c0-\\u00c1]/, 'cat')");
992 }
993
994
995
996
997
998 @Test
999 @Alerts("part1:part2")
1000 public void replace_laterFilledGroup() throws Exception {
1001 testEvaluate("'#part1\\:part2'.replace(/^#|\\\\(:)/g, '$1')");
1002 }
1003
1004
1005
1006
1007
1008 @Test
1009 @Alerts("\\[ab\\]\\(xy\\)\\{z\\} \\{\\[\\(o\\}\\]\\)")
1010 public void replace_EscapeBrackets() throws Exception {
1011 testEvaluate("'[ab](xy){z} {[(o}])'.replace(/([.*+?^${}()|[\\]\\/\\\\])/g, \"\\\\$1\")");
1012 }
1013
1014
1015
1016
1017
1018 @Test
1019 @Alerts("><")
1020 public void replace_T() throws Exception {
1021 testEvaluate("'>' + 'a\\\\.b'.replace(/([\\u00C0-\\uFFFF]|[\\w]|\\\\.)+/, '') + '<'");
1022 }
1023
1024
1025
1026
1027
1028 @Test
1029 @Timeout(1)
1030 @Alerts({"2200915", "2000915"})
1031 public void replace_huge() throws Exception {
1032 final String html = "<html><body><script>\n"
1033 + LOG_TITLE_FUNCTION
1034 + "String.prototype.times = function(n) {\n"
1035 + " var s = '';\n"
1036 + " for (var i =0; i < n; i++) {\n"
1037 + " s += this;\n"
1038 + " }\n"
1039 + " return s;\n"
1040 + "}\n"
1041 + "var size = 100;\n"
1042 + "var longString = '\"' + '123456789\\\\\"'.times(size * 10) + '\"';\n"
1043 + "var object = '{' + longString + ': ' + longString + '},';\n"
1044 + "var huge = '[' + object.times(size) + '{\"test\": 123}]';\n"
1045 + "log(huge.length);\n"
1046 + "log(huge.replace(/\\\\./g, '@').length);\n"
1047 + "</script>\n"
1048 + "</body></html>";
1049
1050 loadPageVerifyTitle2(html);
1051 }
1052
1053
1054
1055
1056 @Test
1057 @Alerts({"true", "true", "true", "true", "true", "true", "true"})
1058 public void nullCharacter() throws Exception {
1059 final String html = "<html><head><script>\n"
1060 + LOG_TITLE_FUNCTION
1061 + " function test() {\n"
1062 + " var regex = new RegExp('[\\0-\\x08]');\n"
1063 + " log(regex.test('\\0'));\n"
1064 + " log('\\0'.match(regex) != null);\n"
1065 + " regex = new RegExp('[\\\\0-\\x08]');\n"
1066 + " log(regex.test('\\0'));\n"
1067 + " log('\\0'.match(regex) != null);\n"
1068 + " regex = new RegExp('[\\\\\\0-\\x08]');\n"
1069 + " log(regex.test('\\0'));\n"
1070 + " log('\\0'.match(regex) != null);\n"
1071 + " log('\\0'.match(/^\\0/) != null);\n"
1072 + " }\n"
1073 + "</script></head><body onload='test()'>\n"
1074 + "</body></html>";
1075
1076 loadPageVerifyTitle2(html);
1077 }
1078
1079
1080
1081
1082 @Test
1083 @Alerts("abc")
1084 public void specialBrackets1() throws Exception {
1085
1086 testEvaluate("'abc'.replace(/[]/, 'x')");
1087 }
1088
1089
1090
1091
1092 @Test
1093 @Alerts("")
1094 public void specialBrackets2() throws Exception {
1095
1096 testEvaluate("'abc'.match(/[]*/)[0]");
1097 }
1098
1099
1100
1101
1102 @Test
1103 @Alerts("xaxbxcx")
1104 public void specialBrackets3() throws Exception {
1105
1106 testEvaluate("'abc'.replace(/[]*/g, 'x')");
1107 }
1108
1109
1110
1111
1112 @Test
1113 @Alerts("xaxbxxcx*xdx")
1114 public void specialBrackets4() throws Exception {
1115
1116 testEvaluate("'ab]c*d'.replace(/[]*]*/g, 'x')");
1117 }
1118
1119
1120
1121
1122 @Test
1123 @Alerts("xxx")
1124 public void specialBrackets5() throws Exception {
1125
1126 testEvaluate("'abc'.replace(/[^]/g, 'x')");
1127 }
1128
1129
1130
1131
1132 @Test
1133 @Alerts("ab\nc")
1134 public void specialBrackets6() throws Exception {
1135
1136 testEvaluate("'ab\\nc'.match(/[^]*/)[0]");
1137 }
1138
1139
1140
1141
1142 @Test
1143 @Alerts("axcd")
1144 public void specialBrackets7() throws Exception {
1145
1146 testEvaluate("'ab]cd'.replace(/[^]]/g, 'x')");
1147 }
1148
1149
1150
1151
1152 @Test
1153 @Alerts("xcd")
1154 public void specialBrackets8() throws Exception {
1155
1156 testEvaluate("'abdcd'.replace(/a[^]d/g, 'x')");
1157 }
1158
1159
1160
1161
1162 @Test
1163 @Alerts("x abdu")
1164 public void specialBrackets9() throws Exception {
1165
1166 testEvaluate("'abdo abdu'.replace(/a[^]d[o]/g, 'x')");
1167 }
1168
1169
1170
1171
1172 @Test
1173 @Alerts("axbxc d efg 1 23")
1174 public void regExMinusInRangeBorderCase1() throws Exception {
1175 testEvaluate("'a-b_c d efg 1 23'.replace(/[_-]+/g, 'x')");
1176 }
1177
1178
1179
1180
1181 @Test
1182 @Alerts("axbxcxdxefgx1x23")
1183 public void regExMinusInRangeBorderCase2() throws Exception {
1184 testEvaluate("'a-b_c d efg 1 23'.replace(/[_-\\s]+/g, 'x')");
1185 }
1186
1187
1188
1189
1190 @Test
1191 @Alerts("x x x x x")
1192 public void regExMinusInRangeBorderCase3() throws Exception {
1193 testEvaluate("'a-b_c d efg 1 23'.replace(/[_-\\S]+/g, 'x')");
1194 }
1195
1196
1197
1198
1199 @Test
1200 @Alerts("x x x x x")
1201 public void regExMinusInRangeBorderCase4() throws Exception {
1202 testEvaluate("'a-b_c d efg 1 23'.replace(/[_-\\w]+/g, 'x')");
1203 }
1204
1205
1206
1207
1208 @Test
1209 @Alerts("axbxcxdxefgx1x23")
1210 public void regExMinusInRangeBorderCase5() throws Exception {
1211 testEvaluate("'a-b_c d efg 1 23'.replace(/[_-\\W]+/g, 'x')");
1212 }
1213
1214
1215
1216
1217 @Test
1218 @Alerts("axbxc d efg x x")
1219 public void regExMinusInRangeBorderCase6() throws Exception {
1220 testEvaluate("'a-b_c d efg 1 23'.replace(/[_-\\d]+/g, 'x')");
1221 }
1222
1223
1224
1225
1226 @Test
1227 @Alerts("x1x23")
1228 public void regExMinusInRangeBorderCase7() throws Exception {
1229 testEvaluate("'a-b_c d efg 1 23'.replace(/[_-\\D]+/g, 'x')");
1230 }
1231
1232
1233
1234
1235 @Test
1236 @Alerts("x-bxc d efg 1 23")
1237 public void regExMinusInRangeBorderCase8() throws Exception {
1238 testEvaluate("'a-b_c d efg 1 23'.replace(/[_-\\a]+/g, 'x')");
1239 }
1240
1241
1242
1243
1244 @Test
1245 @Alerts("https://www.youtube.com/watch?v=1234567,https,1234567")
1246 public void realWorld1() throws Exception {
1247 testEvaluate("'https://www.youtube.com/watch?v=1234567'.match(/^(?:(https?):\\/\\/)?(?:(?:www|m)\\.)?youtube\\.com\\/watch.*v=([a-zA-Z0-9_-]+)/)");
1248 }
1249
1250
1251
1252
1253 @Test
1254 @Alerts("http://m.youtu.be/abcd,http,abcd")
1255 public void realWorld2() throws Exception {
1256 testEvaluate("'http://m.youtu.be/abcd'.match(/^(?:(https?):\\/\\/)?(?:(?:www|m)\\.)?youtu\\.be\\/([a-zA-Z0-9_-]+)/)");
1257 }
1258 }