• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "config.h"
6 
7 #include "web/TextFinder.h"
8 
9 #include "bindings/v8/ExceptionStatePlaceholder.h"
10 #include "core/dom/Document.h"
11 #include "core/dom/NodeList.h"
12 #include "core/dom/Range.h"
13 #include "core/dom/shadow/ShadowRoot.h"
14 #include "core/html/HTMLElement.h"
15 #include "public/web/WebDocument.h"
16 #include "web/FindInPageCoordinates.h"
17 #include "web/WebLocalFrameImpl.h"
18 #include "web/tests/FrameTestHelpers.h"
19 #include "wtf/OwnPtr.h"
20 #include <gtest/gtest.h>
21 
22 using namespace blink;
23 using namespace WebCore;
24 
25 namespace {
26 
27 class TextFinderTest : public ::testing::Test {
28 protected:
29     virtual void SetUp() OVERRIDE;
30 
31     Document& document() const;
32     TextFinder& textFinder() const;
33 
34     static WebFloatRect findInPageRect(Node* startContainer, int startOffset, Node* endContainer, int endOffset);
35 
36 private:
37     FrameTestHelpers::WebViewHelper m_webViewHelper;
38     RefPtrWillBePersistent<Document> m_document;
39     TextFinder* m_textFinder;
40 };
41 
SetUp()42 void TextFinderTest::SetUp()
43 {
44     m_webViewHelper.initialize();
45     WebLocalFrameImpl& frameImpl = *m_webViewHelper.webViewImpl()->mainFrameImpl();
46     frameImpl.viewImpl()->resize(WebSize(640, 480));
47     m_document = PassRefPtrWillBeRawPtr<Document>(frameImpl.document());
48     m_textFinder = &frameImpl.ensureTextFinder();
49 }
50 
document() const51 Document& TextFinderTest::document() const
52 {
53     return *m_document;
54 }
55 
textFinder() const56 TextFinder& TextFinderTest::textFinder() const
57 {
58     return *m_textFinder;
59 }
60 
findInPageRect(Node * startContainer,int startOffset,Node * endContainer,int endOffset)61 WebFloatRect TextFinderTest::findInPageRect(Node* startContainer, int startOffset, Node* endContainer, int endOffset)
62 {
63     RefPtrWillBeRawPtr<Range> range = Range::create(startContainer->document(), startContainer, startOffset, endContainer, endOffset);
64     return WebFloatRect(findInPageRectFromRange(range.get()));
65 }
66 
TEST_F(TextFinderTest,FindTextSimple)67 TEST_F(TextFinderTest, FindTextSimple)
68 {
69     document().body()->setInnerHTML("XXXXFindMeYYYYfindmeZZZZ", ASSERT_NO_EXCEPTION);
70     Node* textNode = document().body()->firstChild();
71 
72     int identifier = 0;
73     WebString searchText(String("FindMe"));
74     WebFindOptions findOptions; // Default.
75     bool wrapWithinFrame = true;
76     WebRect* selectionRect = 0;
77 
78     ASSERT_TRUE(textFinder().find(identifier, searchText, findOptions, wrapWithinFrame, selectionRect));
79     Range* activeMatch = textFinder().activeMatch();
80     ASSERT_TRUE(activeMatch);
81     EXPECT_EQ(textNode, activeMatch->startContainer());
82     EXPECT_EQ(4, activeMatch->startOffset());
83     EXPECT_EQ(textNode, activeMatch->endContainer());
84     EXPECT_EQ(10, activeMatch->endOffset());
85 
86     findOptions.findNext = true;
87     ASSERT_TRUE(textFinder().find(identifier, searchText, findOptions, wrapWithinFrame, selectionRect));
88     activeMatch = textFinder().activeMatch();
89     ASSERT_TRUE(activeMatch);
90     EXPECT_EQ(textNode, activeMatch->startContainer());
91     EXPECT_EQ(14, activeMatch->startOffset());
92     EXPECT_EQ(textNode, activeMatch->endContainer());
93     EXPECT_EQ(20, activeMatch->endOffset());
94 
95     // Should wrap to the first match.
96     ASSERT_TRUE(textFinder().find(identifier, searchText, findOptions, wrapWithinFrame, selectionRect));
97     activeMatch = textFinder().activeMatch();
98     ASSERT_TRUE(activeMatch);
99     EXPECT_EQ(textNode, activeMatch->startContainer());
100     EXPECT_EQ(4, activeMatch->startOffset());
101     EXPECT_EQ(textNode, activeMatch->endContainer());
102     EXPECT_EQ(10, activeMatch->endOffset());
103 
104     // Search in the reverse order.
105     identifier = 1;
106     findOptions = WebFindOptions();
107     findOptions.forward = false;
108 
109     ASSERT_TRUE(textFinder().find(identifier, searchText, findOptions, wrapWithinFrame, selectionRect));
110     activeMatch = textFinder().activeMatch();
111     ASSERT_TRUE(activeMatch);
112     EXPECT_EQ(textNode, activeMatch->startContainer());
113     EXPECT_EQ(14, activeMatch->startOffset());
114     EXPECT_EQ(textNode, activeMatch->endContainer());
115     EXPECT_EQ(20, activeMatch->endOffset());
116 
117     findOptions.findNext = true;
118     ASSERT_TRUE(textFinder().find(identifier, searchText, findOptions, wrapWithinFrame, selectionRect));
119     activeMatch = textFinder().activeMatch();
120     ASSERT_TRUE(activeMatch);
121     EXPECT_EQ(textNode, activeMatch->startContainer());
122     EXPECT_EQ(4, activeMatch->startOffset());
123     EXPECT_EQ(textNode, activeMatch->endContainer());
124     EXPECT_EQ(10, activeMatch->endOffset());
125 
126     // Wrap to the first match (last occurence in the document).
127     ASSERT_TRUE(textFinder().find(identifier, searchText, findOptions, wrapWithinFrame, selectionRect));
128     activeMatch = textFinder().activeMatch();
129     ASSERT_TRUE(activeMatch);
130     EXPECT_EQ(textNode, activeMatch->startContainer());
131     EXPECT_EQ(14, activeMatch->startOffset());
132     EXPECT_EQ(textNode, activeMatch->endContainer());
133     EXPECT_EQ(20, activeMatch->endOffset());
134 }
135 
TEST_F(TextFinderTest,FindTextNotFound)136 TEST_F(TextFinderTest, FindTextNotFound)
137 {
138     document().body()->setInnerHTML("XXXXFindMeYYYYfindmeZZZZ", ASSERT_NO_EXCEPTION);
139 
140     int identifier = 0;
141     WebString searchText(String("Boo"));
142     WebFindOptions findOptions; // Default.
143     bool wrapWithinFrame = true;
144     WebRect* selectionRect = 0;
145 
146     EXPECT_FALSE(textFinder().find(identifier, searchText, findOptions, wrapWithinFrame, selectionRect));
147     EXPECT_FALSE(textFinder().activeMatch());
148 }
149 
TEST_F(TextFinderTest,FindTextInShadowDOM)150 TEST_F(TextFinderTest, FindTextInShadowDOM)
151 {
152     document().body()->setInnerHTML("<b>FOO</b><i>foo</i>", ASSERT_NO_EXCEPTION);
153     RefPtrWillBeRawPtr<ShadowRoot> shadowRoot = document().body()->createShadowRoot(ASSERT_NO_EXCEPTION);
154     shadowRoot->setInnerHTML("<content select=\"i\"></content><u>Foo</u><content></content>", ASSERT_NO_EXCEPTION);
155     Node* textInBElement = document().body()->firstChild()->firstChild();
156     Node* textInIElement = document().body()->lastChild()->firstChild();
157     Node* textInUElement = shadowRoot->childNodes()->item(1)->firstChild();
158 
159     int identifier = 0;
160     WebString searchText(String("foo"));
161     WebFindOptions findOptions; // Default.
162     bool wrapWithinFrame = true;
163     WebRect* selectionRect = 0;
164 
165     // TextIterator currently returns the matches in the document order, instead of the visual order. It visits
166     // the shadow roots first, so in this case the matches will be returned in the order of <u> -> <b> -> <i>.
167     ASSERT_TRUE(textFinder().find(identifier, searchText, findOptions, wrapWithinFrame, selectionRect));
168     Range* activeMatch = textFinder().activeMatch();
169     ASSERT_TRUE(activeMatch);
170     EXPECT_EQ(textInUElement, activeMatch->startContainer());
171     EXPECT_EQ(0, activeMatch->startOffset());
172     EXPECT_EQ(textInUElement, activeMatch->endContainer());
173     EXPECT_EQ(3, activeMatch->endOffset());
174 
175     findOptions.findNext = true;
176     ASSERT_TRUE(textFinder().find(identifier, searchText, findOptions, wrapWithinFrame, selectionRect));
177     activeMatch = textFinder().activeMatch();
178     ASSERT_TRUE(activeMatch);
179     EXPECT_EQ(textInBElement, activeMatch->startContainer());
180     EXPECT_EQ(0, activeMatch->startOffset());
181     EXPECT_EQ(textInBElement, activeMatch->endContainer());
182     EXPECT_EQ(3, activeMatch->endOffset());
183 
184     ASSERT_TRUE(textFinder().find(identifier, searchText, findOptions, wrapWithinFrame, selectionRect));
185     activeMatch = textFinder().activeMatch();
186     ASSERT_TRUE(activeMatch);
187     EXPECT_EQ(textInIElement, activeMatch->startContainer());
188     EXPECT_EQ(0, activeMatch->startOffset());
189     EXPECT_EQ(textInIElement, activeMatch->endContainer());
190     EXPECT_EQ(3, activeMatch->endOffset());
191 
192     // Should wrap to the first match.
193     ASSERT_TRUE(textFinder().find(identifier, searchText, findOptions, wrapWithinFrame, selectionRect));
194     activeMatch = textFinder().activeMatch();
195     ASSERT_TRUE(activeMatch);
196     EXPECT_EQ(textInUElement, activeMatch->startContainer());
197     EXPECT_EQ(0, activeMatch->startOffset());
198     EXPECT_EQ(textInUElement, activeMatch->endContainer());
199     EXPECT_EQ(3, activeMatch->endOffset());
200 
201     // Fresh search in the reverse order.
202     identifier = 1;
203     findOptions = WebFindOptions();
204     findOptions.forward = false;
205 
206     ASSERT_TRUE(textFinder().find(identifier, searchText, findOptions, wrapWithinFrame, selectionRect));
207     activeMatch = textFinder().activeMatch();
208     ASSERT_TRUE(activeMatch);
209     EXPECT_EQ(textInIElement, activeMatch->startContainer());
210     EXPECT_EQ(0, activeMatch->startOffset());
211     EXPECT_EQ(textInIElement, activeMatch->endContainer());
212     EXPECT_EQ(3, activeMatch->endOffset());
213 
214     findOptions.findNext = true;
215     ASSERT_TRUE(textFinder().find(identifier, searchText, findOptions, wrapWithinFrame, selectionRect));
216     activeMatch = textFinder().activeMatch();
217     ASSERT_TRUE(activeMatch);
218     EXPECT_EQ(textInBElement, activeMatch->startContainer());
219     EXPECT_EQ(0, activeMatch->startOffset());
220     EXPECT_EQ(textInBElement, activeMatch->endContainer());
221     EXPECT_EQ(3, activeMatch->endOffset());
222 
223     ASSERT_TRUE(textFinder().find(identifier, searchText, findOptions, wrapWithinFrame, selectionRect));
224     activeMatch = textFinder().activeMatch();
225     ASSERT_TRUE(activeMatch);
226     EXPECT_EQ(textInUElement, activeMatch->startContainer());
227     EXPECT_EQ(0, activeMatch->startOffset());
228     EXPECT_EQ(textInUElement, activeMatch->endContainer());
229     EXPECT_EQ(3, activeMatch->endOffset());
230 
231     // And wrap.
232     ASSERT_TRUE(textFinder().find(identifier, searchText, findOptions, wrapWithinFrame, selectionRect));
233     activeMatch = textFinder().activeMatch();
234     ASSERT_TRUE(activeMatch);
235     EXPECT_EQ(textInIElement, activeMatch->startContainer());
236     EXPECT_EQ(0, activeMatch->startOffset());
237     EXPECT_EQ(textInIElement, activeMatch->endContainer());
238     EXPECT_EQ(3, activeMatch->endOffset());
239 }
240 
TEST_F(TextFinderTest,ScopeTextMatchesSimple)241 TEST_F(TextFinderTest, ScopeTextMatchesSimple)
242 {
243     document().body()->setInnerHTML("XXXXFindMeYYYYfindmeZZZZ", ASSERT_NO_EXCEPTION);
244     Node* textNode = document().body()->firstChild();
245 
246     int identifier = 0;
247     WebString searchText(String("FindMe"));
248     WebFindOptions findOptions; // Default.
249 
250     textFinder().resetMatchCount();
251     textFinder().scopeStringMatches(identifier, searchText, findOptions, true);
252     while (textFinder().scopingInProgress())
253         FrameTestHelpers::runPendingTasks();
254 
255     EXPECT_EQ(2, textFinder().totalMatchCount());
256     WebVector<WebFloatRect> matchRects;
257     textFinder().findMatchRects(matchRects);
258     ASSERT_EQ(2u, matchRects.size());
259     EXPECT_EQ(findInPageRect(textNode, 4, textNode, 10), matchRects[0]);
260     EXPECT_EQ(findInPageRect(textNode, 14, textNode, 20), matchRects[1]);
261 }
262 
TEST_F(TextFinderTest,ScopeTextMatchesWithShadowDOM)263 TEST_F(TextFinderTest, ScopeTextMatchesWithShadowDOM)
264 {
265     document().body()->setInnerHTML("<b>FOO</b><i>foo</i>", ASSERT_NO_EXCEPTION);
266     RefPtrWillBeRawPtr<ShadowRoot> shadowRoot = document().body()->createShadowRoot(ASSERT_NO_EXCEPTION);
267     shadowRoot->setInnerHTML("<content select=\"i\"></content><u>Foo</u><content></content>", ASSERT_NO_EXCEPTION);
268     Node* textInBElement = document().body()->firstChild()->firstChild();
269     Node* textInIElement = document().body()->lastChild()->firstChild();
270     Node* textInUElement = shadowRoot->childNodes()->item(1)->firstChild();
271 
272     int identifier = 0;
273     WebString searchText(String("fOO"));
274     WebFindOptions findOptions; // Default.
275 
276     textFinder().resetMatchCount();
277     textFinder().scopeStringMatches(identifier, searchText, findOptions, true);
278     while (textFinder().scopingInProgress())
279         FrameTestHelpers::runPendingTasks();
280 
281     // TextIterator currently returns the matches in the document order, instead of the visual order. It visits
282     // the shadow roots first, so in this case the matches will be returned in the order of <u> -> <b> -> <i>.
283     EXPECT_EQ(3, textFinder().totalMatchCount());
284     WebVector<WebFloatRect> matchRects;
285     textFinder().findMatchRects(matchRects);
286     ASSERT_EQ(3u, matchRects.size());
287     EXPECT_EQ(findInPageRect(textInUElement, 0, textInUElement, 3), matchRects[0]);
288     EXPECT_EQ(findInPageRect(textInBElement, 0, textInBElement, 3), matchRects[1]);
289     EXPECT_EQ(findInPageRect(textInIElement, 0, textInIElement, 3), matchRects[2]);
290 }
291 
292 } // namespace
293