Home | History | Annotate | Download | only in editing
      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 PassRefPtrWillBeRawPtr<ShadowRoot> createShadowRootForElementWithIDAndSetInnerHTML(TreeScope& scope, const char* hostElementID, const char* shadowRootContent)
    135 {
    136     RefPtrWillBeRawPtr<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     RefPtrWillBeRawPtr<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     RefPtrWillBeRawPtr<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     RefPtrWillBeRawPtr<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     RefPtrWillBeRawPtr<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 TEST_F(TextIteratorTest, EmitsReplacementCharForInput)
    521 {
    522     static const char* bodyContent =
    523         "<div contenteditable=\"true\">"
    524         "Before"
    525         "<img src=\"foo.png\">"
    526         "After"
    527         "</div>";
    528     // "Before".
    529     static const UChar expectedRawString1[] = { 0x42, 0x65, 0x66, 0x6F, 0x72, 0x65, 0 };
    530     // Object replacement char.
    531     static const UChar expectedRawString2[] = { 0xFFFC, 0 };
    532     // "After".
    533     static const UChar expectedRawString3[] = { 0x41, 0x66, 0x74, 0x65, 0x72, 0 };
    534     static const UChar* expectedRawStrings[] = { expectedRawString1, expectedRawString2, expectedRawString3 };
    535     Vector<String> expectedTextChunks;
    536     expectedTextChunks.append(expectedRawStrings, WTF_ARRAY_LENGTH(expectedRawStrings));
    537 
    538     setBodyInnerHTML(bodyContent);
    539     EXPECT_EQ(expectedTextChunks, iterate(TextIteratorEmitsObjectReplacementCharacter));
    540 }
    541 
    542 TEST_F(TextIteratorTest, RangeLengthWithReplacedElements)
    543 {
    544     static const char* bodyContent =
    545         "<div id=\"div\" contenteditable=\"true\">1<img src=\"foo.png\">3</div>";
    546     setBodyInnerHTML(bodyContent);
    547     document().view()->updateLayoutAndStyleIfNeededRecursive();
    548 
    549     Node* divNode = document().getElementById("div");
    550     RefPtrWillBeRawPtr<Range> range = Range::create(document(), divNode, 0, divNode, 3);
    551 
    552     EXPECT_EQ(3, TextIterator::rangeLength(range.get()));
    553 }
    554 
    555 }
    556