Update To 11.40.268.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/core/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 blink;
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     RefPtrWillBeRawPtr<Range> range = getBodyRange();
88     TextIterator iterator(range.get(), iteratorBehavior);
89     return iterateWithIterator(iterator);
90 }
91
92 Vector<String> TextIteratorTest::iteratePartial(const Position& start, const Position& end, TextIteratorBehavior iteratorBehavior)
93 {
94     TextIterator iterator(start, end, iteratorBehavior);
95     return iterateWithIterator(iterator);
96 }
97
98 Vector<String> TextIteratorTest::iterateWithIterator(TextIterator& iterator)
99 {
100     Vector<String> textChunks;
101     while (!iterator.atEnd()) {
102         textChunks.append(iterator.substring(0, iterator.length()));
103         iterator.advance();
104     }
105     return textChunks;
106 }
107
108 HTMLDocument& TextIteratorTest::document() const
109 {
110     return *m_document;
111 }
112
113 void TextIteratorTest::setBodyInnerHTML(const char* bodyContent)
114 {
115     document().body()->setInnerHTML(String::fromUTF8(bodyContent), ASSERT_NO_EXCEPTION);
116 }
117
118 PassRefPtrWillBeRawPtr<Range> TextIteratorTest::getBodyRange() const
119 {
120     RefPtrWillBeRawPtr<Range> range(Range::create(document()));
121     range->selectNode(document().body());
122     return range.release();
123 }
124
125 Vector<String> createVectorString(const char* const* rawStrings, size_t size)
126 {
127     Vector<String> result;
128     result.append(rawStrings, size);
129     return result;
130 }
131
132 PassRefPtrWillBeRawPtr<ShadowRoot> createShadowRootForElementWithIDAndSetInnerHTML(TreeScope& scope, const char* hostElementID, const char* shadowRootContent)
133 {
134     RefPtrWillBeRawPtr<ShadowRoot> shadowRoot = scope.getElementById(AtomicString::fromUTF8(hostElementID))->createShadowRoot(ASSERT_NO_EXCEPTION);
135     shadowRoot->setInnerHTML(String::fromUTF8(shadowRootContent), ASSERT_NO_EXCEPTION);
136     return shadowRoot.release();
137 }
138
139 TEST_F(TextIteratorTest, BasicIteration)
140 {
141     static const char* input = "<p>Hello, \ntext</p><p>iterator.</p>";
142     static const char* expectedTextChunksRawString[] = {
143         "Hello, ",
144         "text",
145         "\n",
146         "\n",
147         "iterator."
148     };
149     Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
150
151     setBodyInnerHTML(input);
152     EXPECT_EQ(expectedTextChunks, iterate());
153 }
154
155 TEST_F(TextIteratorTest, NotEnteringTextControls)
156 {
157     static const char* input = "<p>Hello <input type=\"text\" value=\"input\">!</p>";
158     static const char* expectedTextChunksRawString[] = {
159         "Hello ",
160         "",
161         "!",
162     };
163     Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
164
165     setBodyInnerHTML(input);
166     EXPECT_EQ(expectedTextChunks, iterate());
167 }
168
169 TEST_F(TextIteratorTest, EnteringTextControlsWithOption)
170 {
171     static const char* input = "<p>Hello <input type=\"text\" value=\"input\">!</p>";
172     static const char* expectedTextChunksRawString[] = {
173         "Hello ",
174         "\n",
175         "input",
176         "!",
177     };
178     Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
179
180     setBodyInnerHTML(input);
181     EXPECT_EQ(expectedTextChunks, iterate(TextIteratorEntersTextControls));
182 }
183
184 TEST_F(TextIteratorTest, EnteringTextControlsWithOptionComplex)
185 {
186     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\">";
187     static const char* expectedTextChunksRawString[] = {
188         "\n", // FIXME: Why newline here?
189         "Beginning of range",
190         "\n",
191         "Under DOM nodes",
192         "\n",
193         "End of range"
194     };
195     Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
196
197     setBodyInnerHTML(input);
198     EXPECT_EQ(expectedTextChunks, iterate(TextIteratorEntersTextControls));
199 }
200
201 TEST_F(TextIteratorTest, NotEnteringTextControlHostingShadowTreeEvenWithOption)
202 {
203     static const char* bodyContent = "<div>Hello, <input type=\"text\" value=\"input\" id=\"input\"> iterator.</div>";
204     static const char* shadowContent = "<span>shadow</span>";
205     // TextIterator doesn't emit "input" nor "shadow" since (1) the renderer for <input> is not created; and
206     // (2) we don't (yet) recurse into shadow trees.
207     static const char* expectedTextChunksRawString[] = {
208         "Hello, ",
209         "", // FIXME: Why is an empty string emitted here?
210         " iterator."
211     };
212     Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
213
214     setBodyInnerHTML(bodyContent);
215     createShadowRootForElementWithIDAndSetInnerHTML(document(), "input", shadowContent);
216
217     EXPECT_EQ(expectedTextChunks, iterate());
218 }
219
220 TEST_F(TextIteratorTest, NotEnteringShadowTree)
221 {
222     static const char* bodyContent = "<div>Hello, <span id=\"host\">text</span> iterator.</div>";
223     static const char* shadowContent = "<span>shadow</span>";
224     static const char* expectedTextChunksRawString[] = {
225         "Hello, ", // TextIterator doesn't emit "text" since its renderer is not created. The shadow tree is ignored.
226         " iterator."
227     };
228     Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
229
230     setBodyInnerHTML(bodyContent);
231     createShadowRootForElementWithIDAndSetInnerHTML(document(), "host", shadowContent);
232
233     EXPECT_EQ(expectedTextChunks, iterate());
234 }
235
236 TEST_F(TextIteratorTest, NotEnteringShadowTreeWithMultipleShadowTrees)
237 {
238     static const char* bodyContent = "<div>Hello, <span id=\"host\">text</span> iterator.</div>";
239     static const char* shadowContent1 = "<span>first shadow</span>";
240     static const char* shadowContent2 = "<span>second shadow</span>";
241     static const char* expectedTextChunksRawString[] = {
242         "Hello, ",
243         " iterator."
244     };
245     Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
246
247     setBodyInnerHTML(bodyContent);
248     createShadowRootForElementWithIDAndSetInnerHTML(document(), "host", shadowContent1);
249     createShadowRootForElementWithIDAndSetInnerHTML(document(), "host", shadowContent2);
250
251     EXPECT_EQ(expectedTextChunks, iterate());
252 }
253
254 TEST_F(TextIteratorTest, NotEnteringShadowTreeWithNestedShadowTrees)
255 {
256     static const char* bodyContent = "<div>Hello, <span id=\"host-in-document\">text</span> iterator.</div>";
257     static const char* shadowContent1 = "<span>first <span id=\"host-in-shadow\">shadow</span></span>";
258     static const char* shadowContent2 = "<span>second shadow</span>";
259     static const char* expectedTextChunksRawString[] = {
260         "Hello, ",
261         " iterator."
262     };
263     Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
264
265     setBodyInnerHTML(bodyContent);
266     RefPtrWillBeRawPtr<ShadowRoot> shadowRoot1 = createShadowRootForElementWithIDAndSetInnerHTML(document(), "host-in-document", shadowContent1);
267     createShadowRootForElementWithIDAndSetInnerHTML(*shadowRoot1, "host-in-shadow", shadowContent2);
268
269     EXPECT_EQ(expectedTextChunks, iterate());
270 }
271
272 TEST_F(TextIteratorTest, NotEnteringShadowTreeWithContentInsertionPoint)
273 {
274     static const char* bodyContent = "<div>Hello, <span id=\"host\">text</span> iterator.</div>";
275     static const char* shadowContent = "<span>shadow <content>content</content></span>";
276     static const char* expectedTextChunksRawString[] = {
277         "Hello, ",
278         "text", // In this case a renderer for "text" is created, so it shows up here.
279         " iterator."
280     };
281     Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
282
283     setBodyInnerHTML(bodyContent);
284     createShadowRootForElementWithIDAndSetInnerHTML(document(), "host", shadowContent);
285
286     EXPECT_EQ(expectedTextChunks, iterate());
287 }
288
289 TEST_F(TextIteratorTest, EnteringShadowTreeWithOption)
290 {
291     static const char* bodyContent = "<div>Hello, <span id=\"host\">text</span> iterator.</div>";
292     static const char* shadowContent = "<span>shadow</span>";
293     static const char* expectedTextChunksRawString[] = {
294         "Hello, ",
295         "shadow", // TextIterator emits "shadow" since TextIteratorEntersAuthorShadowRoots is specified.
296         " iterator."
297     };
298     Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
299
300     setBodyInnerHTML(bodyContent);
301     createShadowRootForElementWithIDAndSetInnerHTML(document(), "host", shadowContent);
302
303     EXPECT_EQ(expectedTextChunks, iterate(TextIteratorEntersAuthorShadowRoots));
304 }
305
306 TEST_F(TextIteratorTest, EnteringShadowTreeWithMultipleShadowTreesWithOption)
307 {
308     static const char* bodyContent = "<div>Hello, <span id=\"host\">text</span> iterator.</div>";
309     static const char* shadowContent1 = "<span>first shadow</span>";
310     static const char* shadowContent2 = "<span>second shadow</span>";
311     static const char* expectedTextChunksRawString[] = {
312         "Hello, ",
313         "second shadow", // The first isn't emitted because a renderer for the first is not created.
314         " iterator."
315     };
316     Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
317
318     setBodyInnerHTML(bodyContent);
319     createShadowRootForElementWithIDAndSetInnerHTML(document(), "host", shadowContent1);
320     createShadowRootForElementWithIDAndSetInnerHTML(document(), "host", shadowContent2);
321
322     EXPECT_EQ(expectedTextChunks, iterate(TextIteratorEntersAuthorShadowRoots));
323 }
324
325 TEST_F(TextIteratorTest, EnteringShadowTreeWithNestedShadowTreesWithOption)
326 {
327     static const char* bodyContent = "<div>Hello, <span id=\"host-in-document\">text</span> iterator.</div>";
328     static const char* shadowContent1 = "<span>first <span id=\"host-in-shadow\">shadow</span></span>";
329     static const char* shadowContent2 = "<span>second shadow</span>";
330     static const char* expectedTextChunksRawString[] = {
331         "Hello, ",
332         "first ",
333         "second shadow",
334         " iterator."
335     };
336     Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
337
338     setBodyInnerHTML(bodyContent);
339     RefPtrWillBeRawPtr<ShadowRoot> shadowRoot1 = createShadowRootForElementWithIDAndSetInnerHTML(document(), "host-in-document", shadowContent1);
340     createShadowRootForElementWithIDAndSetInnerHTML(*shadowRoot1, "host-in-shadow", shadowContent2);
341
342     EXPECT_EQ(expectedTextChunks, iterate(TextIteratorEntersAuthorShadowRoots));
343 }
344
345 TEST_F(TextIteratorTest, EnteringShadowTreeWithContentInsertionPointWithOption)
346 {
347     static const char* bodyContent = "<div>Hello, <span id=\"host\">text</span> iterator.</div>";
348     static const char* shadowContent = "<span><content>content</content> shadow</span>";
349     // In this case a renderer for "text" is created, and emitted AFTER any nodes in the shadow tree.
350     // This order does not match the order of the rendered texts, but at this moment it's the expected behavior.
351     // FIXME: Fix this. We probably need pure-renderer-based implementation of TextIterator to achieve this.
352     static const char* expectedTextChunksRawString[] = {
353         "Hello, ",
354         " shadow",
355         "text",
356         " iterator."
357     };
358     Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
359
360     setBodyInnerHTML(bodyContent);
361     createShadowRootForElementWithIDAndSetInnerHTML(document(), "host", shadowContent);
362
363     EXPECT_EQ(expectedTextChunks, iterate(TextIteratorEntersAuthorShadowRoots));
364 }
365
366 TEST_F(TextIteratorTest, StartingAtNodeInShadowRoot)
367 {
368     static const char* bodyContent = "<div id=\"outer\">Hello, <span id=\"host\">text</span> iterator.</div>";
369     static const char* shadowContent = "<span><content>content</content> shadow</span>";
370     static const char* expectedTextChunksRawString[] = {
371         " shadow",
372         "text",
373         " iterator."
374     };
375     Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
376
377     setBodyInnerHTML(bodyContent);
378     RefPtrWillBeRawPtr<ShadowRoot> shadowRoot = createShadowRootForElementWithIDAndSetInnerHTML(document(), "host", shadowContent);
379     Node* outerDiv = document().getElementById("outer");
380     Node* spanInShadow = shadowRoot->firstChild();
381     Position start(spanInShadow, Position::PositionIsBeforeChildren);
382     Position end(outerDiv, Position::PositionIsAfterChildren);
383
384     EXPECT_EQ(expectedTextChunks, iteratePartial(start, end, TextIteratorEntersAuthorShadowRoots));
385 }
386
387 TEST_F(TextIteratorTest, FinishingAtNodeInShadowRoot)
388 {
389     static const char* bodyContent = "<div id=\"outer\">Hello, <span id=\"host\">text</span> iterator.</div>";
390     static const char* shadowContent = "<span><content>content</content> shadow</span>";
391     static const char* expectedTextChunksRawString[] = {
392         "Hello, ",
393         " shadow"
394     };
395     Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
396
397     setBodyInnerHTML(bodyContent);
398     RefPtrWillBeRawPtr<ShadowRoot> shadowRoot = createShadowRootForElementWithIDAndSetInnerHTML(document(), "host", shadowContent);
399     Node* outerDiv = document().getElementById("outer");
400     Node* spanInShadow = shadowRoot->firstChild();
401     Position start(outerDiv, Position::PositionIsBeforeChildren);
402     Position end(spanInShadow, Position::PositionIsAfterChildren);
403
404     EXPECT_EQ(expectedTextChunks, iteratePartial(start, end, TextIteratorEntersAuthorShadowRoots));
405 }
406
407 TEST_F(TextIteratorTest, FullyClipsContents)
408 {
409     static const char* bodyContent =
410         "<div style=\"overflow: hidden; width: 200px; height: 0;\">"
411         "I'm invisible"
412         "</div>";
413     Vector<String> expectedTextChunks; // Empty.
414
415     setBodyInnerHTML(bodyContent);
416     EXPECT_EQ(expectedTextChunks, iterate());
417 }
418
419 TEST_F(TextIteratorTest, IgnoresContainerClip)
420 {
421     static const char* bodyContent =
422         "<div style=\"overflow: hidden; width: 200px; height: 0;\">"
423         "<div>I'm not visible</div>"
424         "<div style=\"position: absolute; width: 200px; height: 200px; top: 0; right: 0;\">"
425         "but I am!"
426         "</div>"
427         "</div>";
428     static const char* expectedTextChunksRawString[] = {
429         "but I am!"
430     };
431     Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
432
433     setBodyInnerHTML(bodyContent);
434     EXPECT_EQ(expectedTextChunks, iterate());
435 }
436
437 TEST_F(TextIteratorTest, FullyClippedContentsDistributed)
438 {
439     static const char* bodyContent =
440         "<div id=\"host\">"
441         "<div>Am I visible?</div>"
442         "</div>";
443     static const char* shadowContent =
444         "<div style=\"overflow: hidden; width: 200px; height: 0;\">"
445         "<content></content>"
446         "</div>";
447     static const char* expectedTextChunksRawString[] = {
448         "\n",
449         // FIXME: The text below is actually invisible but TextIterator currently thinks it's visible.
450         "Am I visible?"
451     };
452     Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
453
454     setBodyInnerHTML(bodyContent);
455     createShadowRootForElementWithIDAndSetInnerHTML(document(), "host", shadowContent);
456
457     EXPECT_EQ(expectedTextChunks, iterate(TextIteratorEntersAuthorShadowRoots));
458 }
459
460 TEST_F(TextIteratorTest, IgnoresContainersClipDistributed)
461 {
462     static const char* bodyContent =
463         "<div id=\"host\" style=\"overflow: hidden; width: 200px; height: 0;\">"
464         "<div>Nobody can find me!</div>"
465         "</div>";
466     static const char* shadowContent =
467         "<div style=\"position: absolute; width: 200px; height: 200px; top: 0; right: 0;\">"
468         "<content></content>"
469         "</div>";
470     // FIXME: The text below is actually visible but TextIterator currently thinks it's invisible.
471     // static const char* expectedTextChunksRawString[] = {
472     //     "\n",
473     //     "Nobody can find me!"
474     // };
475     // Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
476     Vector<String> expectedTextChunks; // Empty.
477
478     setBodyInnerHTML(bodyContent);
479     createShadowRootForElementWithIDAndSetInnerHTML(document(), "host", shadowContent);
480
481     EXPECT_EQ(expectedTextChunks, iterate(TextIteratorEntersAuthorShadowRoots));
482 }
483
484 TEST_F(TextIteratorTest, FindPlainTextInvalidTarget)
485 {
486     static const char* bodyContent = "<div>foo bar test</div>";
487     setBodyInnerHTML(bodyContent);
488     RefPtrWillBeRawPtr<Range> range = getBodyRange();
489
490     RefPtrWillBeRawPtr<Range> expectedRange = range->cloneRange();
491     expectedRange->collapse(false);
492
493     // A lone lead surrogate (0xDA0A) example taken from fuzz-58.
494     static const UChar invalid1[] = {
495         0x1461u, 0x2130u, 0x129bu, 0xd711u, 0xd6feu, 0xccadu, 0x7064u,
496         0xd6a0u, 0x4e3bu, 0x03abu, 0x17dcu, 0xb8b7u, 0xbf55u, 0xfca0u,
497         0x07fau, 0x0427u, 0xda0au, 0
498     };
499
500     // A lone trailing surrogate (U+DC01).
501     static const UChar invalid2[] = {
502         0x1461u, 0x2130u, 0x129bu, 0xdc01u, 0xd6feu, 0xccadu, 0
503     };
504     // A trailing surrogate followed by a lead surrogate (U+DC03 U+D901).
505     static const UChar invalid3[] = {
506         0xd800u, 0xdc00u, 0x0061u, 0xdc03u, 0xd901u, 0xccadu, 0
507     };
508
509     static const UChar* invalidUStrings[] = { invalid1, invalid2, invalid3 };
510
511     for (size_t i = 0; i < WTF_ARRAY_LENGTH(invalidUStrings); ++i) {
512         String invalidTarget(invalidUStrings[i]);
513         RefPtrWillBeRawPtr<Range> actualRange = findPlainText(range.get(), invalidTarget, 0);
514         EXPECT_TRUE(areRangesEqual(expectedRange.get(), actualRange.get()));
515     }
516 }
517
518 TEST_F(TextIteratorTest, EmitsReplacementCharForInput)
519 {
520     static const char* bodyContent =
521         "<div contenteditable=\"true\">"
522         "Before"
523         "<img src=\"foo.png\">"
524         "After"
525         "</div>";
526     // "Before".
527     static const UChar expectedRawString1[] = { 0x42, 0x65, 0x66, 0x6F, 0x72, 0x65, 0 };
528     // Object replacement char.
529     static const UChar expectedRawString2[] = { 0xFFFC, 0 };
530     // "After".
531     static const UChar expectedRawString3[] = { 0x41, 0x66, 0x74, 0x65, 0x72, 0 };
532     static const UChar* expectedRawStrings[] = { expectedRawString1, expectedRawString2, expectedRawString3 };
533     Vector<String> expectedTextChunks;
534     expectedTextChunks.append(expectedRawStrings, WTF_ARRAY_LENGTH(expectedRawStrings));
535
536     setBodyInnerHTML(bodyContent);
537     EXPECT_EQ(expectedTextChunks, iterate(TextIteratorEmitsObjectReplacementCharacter));
538 }
539
540 TEST_F(TextIteratorTest, RangeLengthWithReplacedElements)
541 {
542     static const char* bodyContent =
543         "<div id=\"div\" contenteditable=\"true\">1<img src=\"foo.png\">3</div>";
544     setBodyInnerHTML(bodyContent);
545     document().view()->updateLayoutAndStyleIfNeededRecursive();
546
547     Node* divNode = document().getElementById("div");
548     RefPtrWillBeRawPtr<Range> range = Range::create(document(), divNode, 0, divNode, 3);
549
550     EXPECT_EQ(3, TextIterator::rangeLength(range.get()));
551 }
552
553 TEST_F(TextIteratorTest, SubrangeWithReplacedElements)
554 {
555     static const char* bodyContent =
556         "<div id=\"div\" contenteditable=\"true\">1<img src=\"foo.png\">345</div>";
557     setBodyInnerHTML(bodyContent);
558     document().view()->updateLayoutAndStyleIfNeededRecursive();
559
560     Node* divNode = document().getElementById("div");
561     RefPtrWillBeRawPtr<Range> entireRange = Range::create(document(), divNode, 0, divNode, 3);
562
563     RefPtrWillBeRawPtr<Range> subrange = TextIterator::subrange(entireRange.get(), 2, 3);
564     EXPECT_EQ(0, subrange->startOffset());
565     EXPECT_EQ(3, subrange->endOffset());
566 }
567
568 }