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