Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / web / tests / TextFinderTest.cpp
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/core/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/platform/Platform.h"
16 #include "public/web/WebDocument.h"
17 #include "web/FindInPageCoordinates.h"
18 #include "web/WebLocalFrameImpl.h"
19 #include "web/tests/FrameTestHelpers.h"
20 #include "wtf/OwnPtr.h"
21 #include <gtest/gtest.h>
22
23 using namespace blink;
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
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
51 Document& TextFinderTest::document() const
52 {
53     return *m_document;
54 }
55
56 TextFinder& TextFinderTest::textFinder() const
57 {
58     return *m_textFinder;
59 }
60
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
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
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
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
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
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 TEST_F(TextFinderTest, ScopeRepeatPatternTextMatches)
293 {
294     document().body()->setInnerHTML("ab ab ab ab ab", ASSERT_NO_EXCEPTION);
295     Node* textNode = document().body()->firstChild();
296
297     int identifier = 0;
298     WebString searchText(String("ab ab"));
299     WebFindOptions findOptions; // Default.
300
301     textFinder().resetMatchCount();
302     textFinder().scopeStringMatches(identifier, searchText, findOptions, true);
303     while (textFinder().scopingInProgress())
304         FrameTestHelpers::runPendingTasks();
305
306     EXPECT_EQ(2, textFinder().totalMatchCount());
307     WebVector<WebFloatRect> matchRects;
308     textFinder().findMatchRects(matchRects);
309     ASSERT_EQ(2u, matchRects.size());
310     EXPECT_EQ(findInPageRect(textNode, 0, textNode, 5), matchRects[0]);
311     EXPECT_EQ(findInPageRect(textNode, 6, textNode, 11), matchRects[1]);
312 }
313
314 TEST_F(TextFinderTest, OverlappingMatches)
315 {
316     document().body()->setInnerHTML("aababaa", ASSERT_NO_EXCEPTION);
317     Node* textNode = document().body()->firstChild();
318
319     int identifier = 0;
320     WebString searchText(String("aba"));
321     WebFindOptions findOptions; // Default.
322
323     textFinder().resetMatchCount();
324     textFinder().scopeStringMatches(identifier, searchText, findOptions, true);
325     while (textFinder().scopingInProgress())
326         FrameTestHelpers::runPendingTasks();
327
328     // We shouldn't find overlapped matches.
329     EXPECT_EQ(1, textFinder().totalMatchCount());
330     WebVector<WebFloatRect> matchRects;
331     textFinder().findMatchRects(matchRects);
332     ASSERT_EQ(1u, matchRects.size());
333     EXPECT_EQ(findInPageRect(textNode, 1, textNode, 4), matchRects[0]);
334 }
335
336 TEST_F(TextFinderTest, SequentialMatches)
337 {
338     document().body()->setInnerHTML("ababab", ASSERT_NO_EXCEPTION);
339     Node* textNode = document().body()->firstChild();
340
341     int identifier = 0;
342     WebString searchText(String("ab"));
343     WebFindOptions findOptions; // Default.
344
345     textFinder().resetMatchCount();
346     textFinder().scopeStringMatches(identifier, searchText, findOptions, true);
347     while (textFinder().scopingInProgress())
348         FrameTestHelpers::runPendingTasks();
349
350     EXPECT_EQ(3, textFinder().totalMatchCount());
351     WebVector<WebFloatRect> matchRects;
352     textFinder().findMatchRects(matchRects);
353     ASSERT_EQ(3u, matchRects.size());
354     EXPECT_EQ(findInPageRect(textNode, 0, textNode, 2), matchRects[0]);
355     EXPECT_EQ(findInPageRect(textNode, 2, textNode, 4), matchRects[1]);
356     EXPECT_EQ(findInPageRect(textNode, 4, textNode, 6), matchRects[2]);
357 }
358
359 class TextFinderFakeTimerTest : public TextFinderTest {
360 protected:
361     virtual void SetUp() override;
362     virtual void TearDown() override;
363
364     // A simple platform that mocks out the clock.
365     class TimeProxyPlatform : public Platform {
366     public:
367         TimeProxyPlatform()
368             : m_timeCounter(0.)
369             , m_fallbackPlatform(0)
370         { }
371
372         void install()
373         {
374             // Check that the proxy wasn't installed yet.
375             ASSERT_NE(Platform::current(), this);
376             m_fallbackPlatform = Platform::current();
377             m_timeCounter = m_fallbackPlatform->currentTime();
378             Platform::initialize(this);
379             ASSERT_EQ(Platform::current(), this);
380         }
381
382         void remove()
383         {
384             // Check that the proxy was installed.
385             ASSERT_EQ(Platform::current(), this);
386             Platform::initialize(m_fallbackPlatform);
387             ASSERT_EQ(Platform::current(), m_fallbackPlatform);
388             m_fallbackPlatform = 0;
389         }
390
391     private:
392         Platform& ensureFallback()
393         {
394             ASSERT(m_fallbackPlatform);
395             return *m_fallbackPlatform;
396         }
397
398         // From blink::Platform:
399         virtual double currentTime() override
400         {
401             return ++m_timeCounter;
402         }
403
404         // These blink::Platform methods must be overriden to make a usable object.
405         virtual void cryptographicallyRandomValues(unsigned char* buffer, size_t length) override
406         {
407             ensureFallback().cryptographicallyRandomValues(buffer, length);
408         }
409
410         virtual const unsigned char* getTraceCategoryEnabledFlag(const char* categoryName) override
411         {
412             return ensureFallback().getTraceCategoryEnabledFlag(categoryName);
413         }
414
415         // These two methods allow timers to work correctly.
416         virtual double monotonicallyIncreasingTime() override
417         {
418             return ensureFallback().monotonicallyIncreasingTime();
419         }
420
421         virtual void setSharedTimerFireInterval(double interval) override
422         {
423             ensureFallback().setSharedTimerFireInterval(interval);
424         }
425
426         virtual WebThread* currentThread() override { return ensureFallback().currentThread(); }
427         virtual WebUnitTestSupport* unitTestSupport() override { return ensureFallback().unitTestSupport(); }
428         virtual WebString defaultLocale() override { return ensureFallback().defaultLocale(); }
429         virtual WebCompositorSupport* compositorSupport() override { return ensureFallback().compositorSupport(); }
430
431         double m_timeCounter;
432         Platform* m_fallbackPlatform;
433     };
434
435     TimeProxyPlatform m_proxyTimePlatform;
436 };
437
438 void TextFinderFakeTimerTest::SetUp()
439 {
440     TextFinderTest::SetUp();
441     m_proxyTimePlatform.install();
442 }
443
444 void TextFinderFakeTimerTest::TearDown()
445 {
446     m_proxyTimePlatform.remove();
447     TextFinderTest::TearDown();
448 }
449
450 TEST_F(TextFinderFakeTimerTest, ScopeWithTimeouts)
451 {
452     // Make a long string.
453     String text(Vector<UChar>(100));
454     text.fill('a');
455     String searchPattern("abc");
456     // Make 4 substrings "abc" in text.
457     text.insert(searchPattern, 1);
458     text.insert(searchPattern, 10);
459     text.insert(searchPattern, 50);
460     text.insert(searchPattern, 90);
461
462     document().body()->setInnerHTML(text, ASSERT_NO_EXCEPTION);
463
464     int identifier = 0;
465     WebFindOptions findOptions; // Default.
466
467     textFinder().resetMatchCount();
468
469     // There will be only one iteration before timeout, because increment
470     // of the TimeProxyPlatform timer is greater than timeout threshold.
471     textFinder().scopeStringMatches(identifier, searchPattern, findOptions, true);
472     while (textFinder().scopingInProgress())
473         FrameTestHelpers::runPendingTasks();
474
475     EXPECT_EQ(4, textFinder().totalMatchCount());
476 }
477
478 } // namespace