Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / editing / TextIteratorTest.cpp
1 /*
2  * Copyright (c) 2013, Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include "config.h"
32 #include "core/editing/TextIterator.h"
33
34 #include "bindings/v8/ExceptionStatePlaceholder.h"
35 #include "core/dom/Document.h"
36 #include "core/dom/Element.h"
37 #include "core/dom/Node.h"
38 #include "core/dom/Range.h"
39 #include "core/dom/shadow/ShadowRoot.h"
40 #include "core/frame/FrameView.h"
41 #include "core/html/HTMLDocument.h"
42 #include "core/html/HTMLElement.h"
43 #include "core/testing/DummyPageHolder.h"
44 #include "platform/geometry/IntSize.h"
45 #include "wtf/Compiler.h"
46 #include "wtf/OwnPtr.h"
47 #include "wtf/PassRefPtr.h"
48 #include "wtf/RefPtr.h"
49 #include "wtf/StdLibExtras.h"
50 #include "wtf/Vector.h"
51 #include "wtf/testing/WTFTestHelpers.h"
52 #include <gtest/gtest.h>
53
54 using namespace WebCore;
55
56 namespace {
57
58 class TextIteratorTest : public ::testing::Test {
59 protected:
60     virtual void SetUp() OVERRIDE;
61
62     HTMLDocument& document() const;
63
64     Vector<String> iterate(TextIteratorBehavior = TextIteratorDefaultBehavior);
65     Vector<String> iteratePartial(const Position& start, const Position& end, TextIteratorBehavior = TextIteratorDefaultBehavior);
66
67     void setBodyInnerHTML(const char*);
68     PassRefPtrWillBeRawPtr<Range> getBodyRange() const;
69
70 private:
71     Vector<String> iterateWithIterator(TextIterator&);
72
73     OwnPtr<DummyPageHolder> m_dummyPageHolder;
74
75     HTMLDocument* m_document;
76 };
77
78 void TextIteratorTest::SetUp()
79 {
80     m_dummyPageHolder = DummyPageHolder::create(IntSize(800, 600));
81     m_document = toHTMLDocument(&m_dummyPageHolder->document());
82     ASSERT(m_document);
83 }
84
85 Vector<String> TextIteratorTest::iterate(TextIteratorBehavior iteratorBehavior)
86 {
87     document().view()->updateLayoutAndStyleIfNeededRecursive(); // Force renderers to be created; TextIterator needs them.
88     RefPtrWillBeRawPtr<Range> range = getBodyRange();
89     TextIterator iterator(range.get(), iteratorBehavior);
90     return iterateWithIterator(iterator);
91 }
92
93 Vector<String> TextIteratorTest::iteratePartial(const Position& start, const Position& end, TextIteratorBehavior iteratorBehavior)
94 {
95     document().view()->updateLayoutAndStyleIfNeededRecursive();
96     TextIterator iterator(start, end, iteratorBehavior);
97     return iterateWithIterator(iterator);
98 }
99
100 Vector<String> TextIteratorTest::iterateWithIterator(TextIterator& iterator)
101 {
102     Vector<String> textChunks;
103     while (!iterator.atEnd()) {
104         textChunks.append(iterator.substring(0, iterator.length()));
105         iterator.advance();
106     }
107     return textChunks;
108 }
109
110 HTMLDocument& TextIteratorTest::document() const
111 {
112     return *m_document;
113 }
114
115 void TextIteratorTest::setBodyInnerHTML(const char* bodyContent)
116 {
117     document().body()->setInnerHTML(String::fromUTF8(bodyContent), ASSERT_NO_EXCEPTION);
118 }
119
120 PassRefPtrWillBeRawPtr<Range> TextIteratorTest::getBodyRange() const
121 {
122     RefPtrWillBeRawPtr<Range> range(Range::create(document()));
123     range->selectNode(document().body());
124     return range.release();
125 }
126
127 Vector<String> createVectorString(const char* const* rawStrings, size_t size)
128 {
129     Vector<String> result;
130     result.append(rawStrings, size);
131     return result;
132 }
133
134 PassRefPtr<ShadowRoot> createShadowRootForElementWithIDAndSetInnerHTML(TreeScope& scope, const char* hostElementID, const char* shadowRootContent)
135 {
136     RefPtr<ShadowRoot> shadowRoot = scope.getElementById(AtomicString::fromUTF8(hostElementID))->createShadowRoot(ASSERT_NO_EXCEPTION);
137     shadowRoot->setInnerHTML(String::fromUTF8(shadowRootContent), ASSERT_NO_EXCEPTION);
138     return shadowRoot.release();
139 }
140
141 TEST_F(TextIteratorTest, BasicIteration)
142 {
143     static const char* input = "<p>Hello, \ntext</p><p>iterator.</p>";
144     static const char* expectedTextChunksRawString[] = {
145         "Hello, ",
146         "text",
147         "\n",
148         "\n",
149         "iterator."
150     };
151     Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
152
153     setBodyInnerHTML(input);
154     EXPECT_EQ(expectedTextChunks, iterate());
155 }
156
157 TEST_F(TextIteratorTest, NotEnteringTextControls)
158 {
159     static const char* input = "<p>Hello <input type=\"text\" value=\"input\">!</p>";
160     static const char* expectedTextChunksRawString[] = {
161         "Hello ",
162         "",
163         "!",
164     };
165     Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
166
167     setBodyInnerHTML(input);
168     EXPECT_EQ(expectedTextChunks, iterate());
169 }
170
171 TEST_F(TextIteratorTest, EnteringTextControlsWithOption)
172 {
173     static const char* input = "<p>Hello <input type=\"text\" value=\"input\">!</p>";
174     static const char* expectedTextChunksRawString[] = {
175         "Hello ",
176         "\n",
177         "input",
178         "!",
179     };
180     Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
181
182     setBodyInnerHTML(input);
183     EXPECT_EQ(expectedTextChunks, iterate(TextIteratorEntersTextControls));
184 }
185
186 TEST_F(TextIteratorTest, EnteringTextControlsWithOptionComplex)
187 {
188     static const char* input = "<input type=\"text\" value=\"Beginning of range\"><div><div><input type=\"text\" value=\"Under DOM nodes\"></div></div><input type=\"text\" value=\"End of range\">";
189     static const char* expectedTextChunksRawString[] = {
190         "\n", // FIXME: Why newline here?
191         "Beginning of range",
192         "\n",
193         "Under DOM nodes",
194         "\n",
195         "End of range"
196     };
197     Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
198
199     setBodyInnerHTML(input);
200     EXPECT_EQ(expectedTextChunks, iterate(TextIteratorEntersTextControls));
201 }
202
203 TEST_F(TextIteratorTest, NotEnteringTextControlHostingShadowTreeEvenWithOption)
204 {
205     static const char* bodyContent = "<div>Hello, <input type=\"text\" value=\"input\" id=\"input\"> iterator.</div>";
206     static const char* shadowContent = "<span>shadow</span>";
207     // TextIterator doesn't emit "input" nor "shadow" since (1) the renderer for <input> is not created; and
208     // (2) we don't (yet) recurse into shadow trees.
209     static const char* expectedTextChunksRawString[] = {
210         "Hello, ",
211         "", // FIXME: Why is an empty string emitted here?
212         " iterator."
213     };
214     Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
215
216     setBodyInnerHTML(bodyContent);
217     createShadowRootForElementWithIDAndSetInnerHTML(document(), "input", shadowContent);
218
219     EXPECT_EQ(expectedTextChunks, iterate());
220 }
221
222 TEST_F(TextIteratorTest, NotEnteringShadowTree)
223 {
224     static const char* bodyContent = "<div>Hello, <span id=\"host\">text</span> iterator.</div>";
225     static const char* shadowContent = "<span>shadow</span>";
226     static const char* expectedTextChunksRawString[] = {
227         "Hello, ", // TextIterator doesn't emit "text" since its renderer is not created. The shadow tree is ignored.
228         " iterator."
229     };
230     Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
231
232     setBodyInnerHTML(bodyContent);
233     createShadowRootForElementWithIDAndSetInnerHTML(document(), "host", shadowContent);
234
235     EXPECT_EQ(expectedTextChunks, iterate());
236 }
237
238 TEST_F(TextIteratorTest, NotEnteringShadowTreeWithMultipleShadowTrees)
239 {
240     static const char* bodyContent = "<div>Hello, <span id=\"host\">text</span> iterator.</div>";
241     static const char* shadowContent1 = "<span>first shadow</span>";
242     static const char* shadowContent2 = "<span>second shadow</span>";
243     static const char* expectedTextChunksRawString[] = {
244         "Hello, ",
245         " iterator."
246     };
247     Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
248
249     setBodyInnerHTML(bodyContent);
250     createShadowRootForElementWithIDAndSetInnerHTML(document(), "host", shadowContent1);
251     createShadowRootForElementWithIDAndSetInnerHTML(document(), "host", shadowContent2);
252
253     EXPECT_EQ(expectedTextChunks, iterate());
254 }
255
256 TEST_F(TextIteratorTest, NotEnteringShadowTreeWithNestedShadowTrees)
257 {
258     static const char* bodyContent = "<div>Hello, <span id=\"host-in-document\">text</span> iterator.</div>";
259     static const char* shadowContent1 = "<span>first <span id=\"host-in-shadow\">shadow</span></span>";
260     static const char* shadowContent2 = "<span>second shadow</span>";
261     static const char* expectedTextChunksRawString[] = {
262         "Hello, ",
263         " iterator."
264     };
265     Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
266
267     setBodyInnerHTML(bodyContent);
268     RefPtr<ShadowRoot> shadowRoot1 = createShadowRootForElementWithIDAndSetInnerHTML(document(), "host-in-document", shadowContent1);
269     createShadowRootForElementWithIDAndSetInnerHTML(*shadowRoot1, "host-in-shadow", shadowContent2);
270
271     EXPECT_EQ(expectedTextChunks, iterate());
272 }
273
274 TEST_F(TextIteratorTest, NotEnteringShadowTreeWithContentInsertionPoint)
275 {
276     static const char* bodyContent = "<div>Hello, <span id=\"host\">text</span> iterator.</div>";
277     static const char* shadowContent = "<span>shadow <content>content</content></span>";
278     static const char* expectedTextChunksRawString[] = {
279         "Hello, ",
280         "text", // In this case a renderer for "text" is created, so it shows up here.
281         " iterator."
282     };
283     Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
284
285     setBodyInnerHTML(bodyContent);
286     createShadowRootForElementWithIDAndSetInnerHTML(document(), "host", shadowContent);
287
288     EXPECT_EQ(expectedTextChunks, iterate());
289 }
290
291 TEST_F(TextIteratorTest, EnteringShadowTreeWithOption)
292 {
293     static const char* bodyContent = "<div>Hello, <span id=\"host\">text</span> iterator.</div>";
294     static const char* shadowContent = "<span>shadow</span>";
295     static const char* expectedTextChunksRawString[] = {
296         "Hello, ",
297         "shadow", // TextIterator emits "shadow" since TextIteratorEntersAuthorShadowRoots is specified.
298         " iterator."
299     };
300     Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
301
302     setBodyInnerHTML(bodyContent);
303     createShadowRootForElementWithIDAndSetInnerHTML(document(), "host", shadowContent);
304
305     EXPECT_EQ(expectedTextChunks, iterate(TextIteratorEntersAuthorShadowRoots));
306 }
307
308 TEST_F(TextIteratorTest, EnteringShadowTreeWithMultipleShadowTreesWithOption)
309 {
310     static const char* bodyContent = "<div>Hello, <span id=\"host\">text</span> iterator.</div>";
311     static const char* shadowContent1 = "<span>first shadow</span>";
312     static const char* shadowContent2 = "<span>second shadow</span>";
313     static const char* expectedTextChunksRawString[] = {
314         "Hello, ",
315         "second shadow", // The first isn't emitted because a renderer for the first is not created.
316         " iterator."
317     };
318     Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
319
320     setBodyInnerHTML(bodyContent);
321     createShadowRootForElementWithIDAndSetInnerHTML(document(), "host", shadowContent1);
322     createShadowRootForElementWithIDAndSetInnerHTML(document(), "host", shadowContent2);
323
324     EXPECT_EQ(expectedTextChunks, iterate(TextIteratorEntersAuthorShadowRoots));
325 }
326
327 TEST_F(TextIteratorTest, EnteringShadowTreeWithNestedShadowTreesWithOption)
328 {
329     static const char* bodyContent = "<div>Hello, <span id=\"host-in-document\">text</span> iterator.</div>";
330     static const char* shadowContent1 = "<span>first <span id=\"host-in-shadow\">shadow</span></span>";
331     static const char* shadowContent2 = "<span>second shadow</span>";
332     static const char* expectedTextChunksRawString[] = {
333         "Hello, ",
334         "first ",
335         "second shadow",
336         " iterator."
337     };
338     Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
339
340     setBodyInnerHTML(bodyContent);
341     RefPtr<ShadowRoot> shadowRoot1 = createShadowRootForElementWithIDAndSetInnerHTML(document(), "host-in-document", shadowContent1);
342     createShadowRootForElementWithIDAndSetInnerHTML(*shadowRoot1, "host-in-shadow", shadowContent2);
343
344     EXPECT_EQ(expectedTextChunks, iterate(TextIteratorEntersAuthorShadowRoots));
345 }
346
347 TEST_F(TextIteratorTest, EnteringShadowTreeWithContentInsertionPointWithOption)
348 {
349     static const char* bodyContent = "<div>Hello, <span id=\"host\">text</span> iterator.</div>";
350     static const char* shadowContent = "<span><content>content</content> shadow</span>";
351     // In this case a renderer for "text" is created, and emitted AFTER any nodes in the shadow tree.
352     // This order does not match the order of the rendered texts, but at this moment it's the expected behavior.
353     // FIXME: Fix this. We probably need pure-renderer-based implementation of TextIterator to achieve this.
354     static const char* expectedTextChunksRawString[] = {
355         "Hello, ",
356         " shadow",
357         "text",
358         " iterator."
359     };
360     Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
361
362     setBodyInnerHTML(bodyContent);
363     createShadowRootForElementWithIDAndSetInnerHTML(document(), "host", shadowContent);
364
365     EXPECT_EQ(expectedTextChunks, iterate(TextIteratorEntersAuthorShadowRoots));
366 }
367
368 TEST_F(TextIteratorTest, StartingAtNodeInShadowRoot)
369 {
370     static const char* bodyContent = "<div id=\"outer\">Hello, <span id=\"host\">text</span> iterator.</div>";
371     static const char* shadowContent = "<span><content>content</content> shadow</span>";
372     static const char* expectedTextChunksRawString[] = {
373         " shadow",
374         "text",
375         " iterator."
376     };
377     Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
378
379     setBodyInnerHTML(bodyContent);
380     RefPtr<ShadowRoot> shadowRoot = createShadowRootForElementWithIDAndSetInnerHTML(document(), "host", shadowContent);
381     Node* outerDiv = document().getElementById("outer");
382     Node* spanInShadow = shadowRoot->firstChild();
383     Position start(spanInShadow, Position::PositionIsBeforeChildren);
384     Position end(outerDiv, Position::PositionIsAfterChildren);
385
386     EXPECT_EQ(expectedTextChunks, iteratePartial(start, end, TextIteratorEntersAuthorShadowRoots));
387 }
388
389 TEST_F(TextIteratorTest, FinishingAtNodeInShadowRoot)
390 {
391     static const char* bodyContent = "<div id=\"outer\">Hello, <span id=\"host\">text</span> iterator.</div>";
392     static const char* shadowContent = "<span><content>content</content> shadow</span>";
393     static const char* expectedTextChunksRawString[] = {
394         "Hello, ",
395         " shadow"
396     };
397     Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
398
399     setBodyInnerHTML(bodyContent);
400     RefPtr<ShadowRoot> shadowRoot = createShadowRootForElementWithIDAndSetInnerHTML(document(), "host", shadowContent);
401     Node* outerDiv = document().getElementById("outer");
402     Node* spanInShadow = shadowRoot->firstChild();
403     Position start(outerDiv, Position::PositionIsBeforeChildren);
404     Position end(spanInShadow, Position::PositionIsAfterChildren);
405
406     EXPECT_EQ(expectedTextChunks, iteratePartial(start, end, TextIteratorEntersAuthorShadowRoots));
407 }
408
409 TEST_F(TextIteratorTest, FullyClipsContents)
410 {
411     static const char* bodyContent =
412         "<div style=\"overflow: hidden; width: 200px; height: 0;\">"
413         "I'm invisible"
414         "</div>";
415     Vector<String> expectedTextChunks; // Empty.
416
417     setBodyInnerHTML(bodyContent);
418     EXPECT_EQ(expectedTextChunks, iterate());
419 }
420
421 TEST_F(TextIteratorTest, IgnoresContainerClip)
422 {
423     static const char* bodyContent =
424         "<div style=\"overflow: hidden; width: 200px; height: 0;\">"
425         "<div>I'm not visible</div>"
426         "<div style=\"position: absolute; width: 200px; height: 200px; top: 0; right: 0;\">"
427         "but I am!"
428         "</div>"
429         "</div>";
430     static const char* expectedTextChunksRawString[] = {
431         "but I am!"
432     };
433     Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
434
435     setBodyInnerHTML(bodyContent);
436     EXPECT_EQ(expectedTextChunks, iterate());
437 }
438
439 TEST_F(TextIteratorTest, FullyClippedContentsDistributed)
440 {
441     static const char* bodyContent =
442         "<div id=\"host\">"
443         "<div>Am I visible?</div>"
444         "</div>";
445     static const char* shadowContent =
446         "<div style=\"overflow: hidden; width: 200px; height: 0;\">"
447         "<content></content>"
448         "</div>";
449     static const char* expectedTextChunksRawString[] = {
450         "\n",
451         // FIXME: The text below is actually invisible but TextIterator currently thinks it's visible.
452         "Am I visible?"
453     };
454     Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
455
456     setBodyInnerHTML(bodyContent);
457     createShadowRootForElementWithIDAndSetInnerHTML(document(), "host", shadowContent);
458
459     EXPECT_EQ(expectedTextChunks, iterate(TextIteratorEntersAuthorShadowRoots));
460 }
461
462 TEST_F(TextIteratorTest, IgnoresContainersClipDistributed)
463 {
464     static const char* bodyContent =
465         "<div id=\"host\" style=\"overflow: hidden; width: 200px; height: 0;\">"
466         "<div>Nobody can find me!</div>"
467         "</div>";
468     static const char* shadowContent =
469         "<div style=\"position: absolute; width: 200px; height: 200px; top: 0; right: 0;\">"
470         "<content></content>"
471         "</div>";
472     // FIXME: The text below is actually visible but TextIterator currently thinks it's invisible.
473     // static const char* expectedTextChunksRawString[] = {
474     //     "\n",
475     //     "Nobody can find me!"
476     // };
477     // Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
478     Vector<String> expectedTextChunks; // Empty.
479
480     setBodyInnerHTML(bodyContent);
481     createShadowRootForElementWithIDAndSetInnerHTML(document(), "host", shadowContent);
482
483     EXPECT_EQ(expectedTextChunks, iterate(TextIteratorEntersAuthorShadowRoots));
484 }
485
486 TEST_F(TextIteratorTest, FindPlainTextInvalidTarget)
487 {
488     static const char* bodyContent = "<div>foo bar test</div>";
489     setBodyInnerHTML(bodyContent);
490     RefPtrWillBeRawPtr<Range> range = getBodyRange();
491
492     RefPtrWillBeRawPtr<Range> expectedRange = range->cloneRange();
493     expectedRange->collapse(false);
494
495     // A lone lead surrogate (0xDA0A) example taken from fuzz-58.
496     static const UChar invalid1[] = {
497         0x1461u, 0x2130u, 0x129bu, 0xd711u, 0xd6feu, 0xccadu, 0x7064u,
498         0xd6a0u, 0x4e3bu, 0x03abu, 0x17dcu, 0xb8b7u, 0xbf55u, 0xfca0u,
499         0x07fau, 0x0427u, 0xda0au, 0
500     };
501
502     // A lone trailing surrogate (U+DC01).
503     static const UChar invalid2[] = {
504         0x1461u, 0x2130u, 0x129bu, 0xdc01u, 0xd6feu, 0xccadu, 0
505     };
506     // A trailing surrogate followed by a lead surrogate (U+DC03 U+D901).
507     static const UChar invalid3[] = {
508         0xd800u, 0xdc00u, 0x0061u, 0xdc03u, 0xd901u, 0xccadu, 0
509     };
510
511     static const UChar* invalidUStrings[] = { invalid1, invalid2, invalid3 };
512
513     for (size_t i = 0; i < WTF_ARRAY_LENGTH(invalidUStrings); ++i) {
514         String invalidTarget(invalidUStrings[i]);
515         RefPtrWillBeRawPtr<Range> actualRange = findPlainText(range.get(), invalidTarget, 0);
516         EXPECT_TRUE(areRangesEqual(expectedRange.get(), actualRange.get()));
517     }
518 }
519
520 }