2 * Copyright (c) 2013, Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
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.
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.
32 #include "core/editing/TextIterator.h"
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>
54 using namespace blink;
58 class TextIteratorTest : public ::testing::Test {
60 virtual void SetUp() override;
62 HTMLDocument& document() const;
64 Vector<String> iterate(TextIteratorBehavior = TextIteratorDefaultBehavior);
65 Vector<String> iteratePartial(const Position& start, const Position& end, TextIteratorBehavior = TextIteratorDefaultBehavior);
67 void setBodyInnerHTML(const char*);
68 PassRefPtrWillBeRawPtr<Range> getBodyRange() const;
71 Vector<String> iterateWithIterator(TextIterator&);
73 OwnPtr<DummyPageHolder> m_dummyPageHolder;
75 HTMLDocument* m_document;
78 void TextIteratorTest::SetUp()
80 m_dummyPageHolder = DummyPageHolder::create(IntSize(800, 600));
81 m_document = toHTMLDocument(&m_dummyPageHolder->document());
85 Vector<String> TextIteratorTest::iterate(TextIteratorBehavior iteratorBehavior)
87 RefPtrWillBeRawPtr<Range> range = getBodyRange();
88 TextIterator iterator(range.get(), iteratorBehavior);
89 return iterateWithIterator(iterator);
92 Vector<String> TextIteratorTest::iteratePartial(const Position& start, const Position& end, TextIteratorBehavior iteratorBehavior)
94 TextIterator iterator(start, end, iteratorBehavior);
95 return iterateWithIterator(iterator);
98 Vector<String> TextIteratorTest::iterateWithIterator(TextIterator& iterator)
100 Vector<String> textChunks;
101 while (!iterator.atEnd()) {
102 textChunks.append(iterator.substring(0, iterator.length()));
108 HTMLDocument& TextIteratorTest::document() const
113 void TextIteratorTest::setBodyInnerHTML(const char* bodyContent)
115 document().body()->setInnerHTML(String::fromUTF8(bodyContent), ASSERT_NO_EXCEPTION);
118 PassRefPtrWillBeRawPtr<Range> TextIteratorTest::getBodyRange() const
120 RefPtrWillBeRawPtr<Range> range(Range::create(document()));
121 range->selectNode(document().body());
122 return range.release();
125 Vector<String> createVectorString(const char* const* rawStrings, size_t size)
127 Vector<String> result;
128 result.append(rawStrings, size);
132 PassRefPtrWillBeRawPtr<ShadowRoot> createShadowRootForElementWithIDAndSetInnerHTML(TreeScope& scope, const char* hostElementID, const char* shadowRootContent)
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();
139 TEST_F(TextIteratorTest, BasicIteration)
141 static const char* input = "<p>Hello, \ntext</p><p>iterator.</p>";
142 static const char* expectedTextChunksRawString[] = {
149 Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
151 setBodyInnerHTML(input);
152 EXPECT_EQ(expectedTextChunks, iterate());
155 TEST_F(TextIteratorTest, NotEnteringTextControls)
157 static const char* input = "<p>Hello <input type=\"text\" value=\"input\">!</p>";
158 static const char* expectedTextChunksRawString[] = {
163 Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
165 setBodyInnerHTML(input);
166 EXPECT_EQ(expectedTextChunks, iterate());
169 TEST_F(TextIteratorTest, EnteringTextControlsWithOption)
171 static const char* input = "<p>Hello <input type=\"text\" value=\"input\">!</p>";
172 static const char* expectedTextChunksRawString[] = {
178 Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
180 setBodyInnerHTML(input);
181 EXPECT_EQ(expectedTextChunks, iterate(TextIteratorEntersTextControls));
184 TEST_F(TextIteratorTest, EnteringTextControlsWithOptionComplex)
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",
195 Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
197 setBodyInnerHTML(input);
198 EXPECT_EQ(expectedTextChunks, iterate(TextIteratorEntersTextControls));
201 TEST_F(TextIteratorTest, NotEnteringTextControlHostingShadowTreeEvenWithOption)
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[] = {
209 "", // FIXME: Why is an empty string emitted here?
212 Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
214 setBodyInnerHTML(bodyContent);
215 createShadowRootForElementWithIDAndSetInnerHTML(document(), "input", shadowContent);
217 EXPECT_EQ(expectedTextChunks, iterate());
220 TEST_F(TextIteratorTest, NotEnteringShadowTree)
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.
228 Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
230 setBodyInnerHTML(bodyContent);
231 createShadowRootForElementWithIDAndSetInnerHTML(document(), "host", shadowContent);
233 EXPECT_EQ(expectedTextChunks, iterate());
236 TEST_F(TextIteratorTest, NotEnteringShadowTreeWithMultipleShadowTrees)
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[] = {
245 Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
247 setBodyInnerHTML(bodyContent);
248 createShadowRootForElementWithIDAndSetInnerHTML(document(), "host", shadowContent1);
249 createShadowRootForElementWithIDAndSetInnerHTML(document(), "host", shadowContent2);
251 EXPECT_EQ(expectedTextChunks, iterate());
254 TEST_F(TextIteratorTest, NotEnteringShadowTreeWithNestedShadowTrees)
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[] = {
263 Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
265 setBodyInnerHTML(bodyContent);
266 RefPtrWillBeRawPtr<ShadowRoot> shadowRoot1 = createShadowRootForElementWithIDAndSetInnerHTML(document(), "host-in-document", shadowContent1);
267 createShadowRootForElementWithIDAndSetInnerHTML(*shadowRoot1, "host-in-shadow", shadowContent2);
269 EXPECT_EQ(expectedTextChunks, iterate());
272 TEST_F(TextIteratorTest, NotEnteringShadowTreeWithContentInsertionPoint)
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[] = {
278 "text", // In this case a renderer for "text" is created, so it shows up here.
281 Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
283 setBodyInnerHTML(bodyContent);
284 createShadowRootForElementWithIDAndSetInnerHTML(document(), "host", shadowContent);
286 EXPECT_EQ(expectedTextChunks, iterate());
289 TEST_F(TextIteratorTest, EnteringShadowTreeWithOption)
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[] = {
295 "shadow", // TextIterator emits "shadow" since TextIteratorEntersAuthorShadowRoots is specified.
298 Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
300 setBodyInnerHTML(bodyContent);
301 createShadowRootForElementWithIDAndSetInnerHTML(document(), "host", shadowContent);
303 EXPECT_EQ(expectedTextChunks, iterate(TextIteratorEntersAuthorShadowRoots));
306 TEST_F(TextIteratorTest, EnteringShadowTreeWithMultipleShadowTreesWithOption)
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[] = {
313 "second shadow", // The first isn't emitted because a renderer for the first is not created.
316 Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
318 setBodyInnerHTML(bodyContent);
319 createShadowRootForElementWithIDAndSetInnerHTML(document(), "host", shadowContent1);
320 createShadowRootForElementWithIDAndSetInnerHTML(document(), "host", shadowContent2);
322 EXPECT_EQ(expectedTextChunks, iterate(TextIteratorEntersAuthorShadowRoots));
325 TEST_F(TextIteratorTest, EnteringShadowTreeWithNestedShadowTreesWithOption)
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[] = {
336 Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
338 setBodyInnerHTML(bodyContent);
339 RefPtrWillBeRawPtr<ShadowRoot> shadowRoot1 = createShadowRootForElementWithIDAndSetInnerHTML(document(), "host-in-document", shadowContent1);
340 createShadowRootForElementWithIDAndSetInnerHTML(*shadowRoot1, "host-in-shadow", shadowContent2);
342 EXPECT_EQ(expectedTextChunks, iterate(TextIteratorEntersAuthorShadowRoots));
345 TEST_F(TextIteratorTest, EnteringShadowTreeWithContentInsertionPointWithOption)
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[] = {
358 Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
360 setBodyInnerHTML(bodyContent);
361 createShadowRootForElementWithIDAndSetInnerHTML(document(), "host", shadowContent);
363 EXPECT_EQ(expectedTextChunks, iterate(TextIteratorEntersAuthorShadowRoots));
366 TEST_F(TextIteratorTest, StartingAtNodeInShadowRoot)
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[] = {
375 Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
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);
384 EXPECT_EQ(expectedTextChunks, iteratePartial(start, end, TextIteratorEntersAuthorShadowRoots));
387 TEST_F(TextIteratorTest, FinishingAtNodeInShadowRoot)
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[] = {
395 Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
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);
404 EXPECT_EQ(expectedTextChunks, iteratePartial(start, end, TextIteratorEntersAuthorShadowRoots));
407 TEST_F(TextIteratorTest, FullyClipsContents)
409 static const char* bodyContent =
410 "<div style=\"overflow: hidden; width: 200px; height: 0;\">"
413 Vector<String> expectedTextChunks; // Empty.
415 setBodyInnerHTML(bodyContent);
416 EXPECT_EQ(expectedTextChunks, iterate());
419 TEST_F(TextIteratorTest, IgnoresContainerClip)
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;\">"
428 static const char* expectedTextChunksRawString[] = {
431 Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
433 setBodyInnerHTML(bodyContent);
434 EXPECT_EQ(expectedTextChunks, iterate());
437 TEST_F(TextIteratorTest, FullyClippedContentsDistributed)
439 static const char* bodyContent =
441 "<div>Am I visible?</div>"
443 static const char* shadowContent =
444 "<div style=\"overflow: hidden; width: 200px; height: 0;\">"
445 "<content></content>"
447 static const char* expectedTextChunksRawString[] = {
449 // FIXME: The text below is actually invisible but TextIterator currently thinks it's visible.
452 Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
454 setBodyInnerHTML(bodyContent);
455 createShadowRootForElementWithIDAndSetInnerHTML(document(), "host", shadowContent);
457 EXPECT_EQ(expectedTextChunks, iterate(TextIteratorEntersAuthorShadowRoots));
460 TEST_F(TextIteratorTest, IgnoresContainersClipDistributed)
462 static const char* bodyContent =
463 "<div id=\"host\" style=\"overflow: hidden; width: 200px; height: 0;\">"
464 "<div>Nobody can find me!</div>"
466 static const char* shadowContent =
467 "<div style=\"position: absolute; width: 200px; height: 200px; top: 0; right: 0;\">"
468 "<content></content>"
470 // FIXME: The text below is actually visible but TextIterator currently thinks it's invisible.
471 // static const char* expectedTextChunksRawString[] = {
473 // "Nobody can find me!"
475 // Vector<String> expectedTextChunks = createVectorString(expectedTextChunksRawString, WTF_ARRAY_LENGTH(expectedTextChunksRawString));
476 Vector<String> expectedTextChunks; // Empty.
478 setBodyInnerHTML(bodyContent);
479 createShadowRootForElementWithIDAndSetInnerHTML(document(), "host", shadowContent);
481 EXPECT_EQ(expectedTextChunks, iterate(TextIteratorEntersAuthorShadowRoots));
484 TEST_F(TextIteratorTest, FindPlainTextInvalidTarget)
486 static const char* bodyContent = "<div>foo bar test</div>";
487 setBodyInnerHTML(bodyContent);
488 RefPtrWillBeRawPtr<Range> range = getBodyRange();
490 RefPtrWillBeRawPtr<Range> expectedRange = range->cloneRange();
491 expectedRange->collapse(false);
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
500 // A lone trailing surrogate (U+DC01).
501 static const UChar invalid2[] = {
502 0x1461u, 0x2130u, 0x129bu, 0xdc01u, 0xd6feu, 0xccadu, 0
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
509 static const UChar* invalidUStrings[] = { invalid1, invalid2, invalid3 };
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()));
518 TEST_F(TextIteratorTest, EmitsReplacementCharForInput)
520 static const char* bodyContent =
521 "<div contenteditable=\"true\">"
523 "<img src=\"foo.png\">"
527 static const UChar expectedRawString1[] = { 0x42, 0x65, 0x66, 0x6F, 0x72, 0x65, 0 };
528 // Object replacement char.
529 static const UChar expectedRawString2[] = { 0xFFFC, 0 };
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));
536 setBodyInnerHTML(bodyContent);
537 EXPECT_EQ(expectedTextChunks, iterate(TextIteratorEmitsObjectReplacementCharacter));
540 TEST_F(TextIteratorTest, RangeLengthWithReplacedElements)
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();
547 Node* divNode = document().getElementById("div");
548 RefPtrWillBeRawPtr<Range> range = Range::create(document(), divNode, 0, divNode, 3);
550 EXPECT_EQ(3, TextIterator::rangeLength(range.get()));
553 TEST_F(TextIteratorTest, SubrangeWithReplacedElements)
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();
560 Node* divNode = document().getElementById("div");
561 RefPtrWillBeRawPtr<Range> entireRange = Range::create(document(), divNode, 0, divNode, 3);
563 RefPtrWillBeRawPtr<Range> subrange = TextIterator::subrange(entireRange.get(), 2, 3);
564 EXPECT_EQ(0, subrange->startOffset());
565 EXPECT_EQ(3, subrange->endOffset());