1 | // Copyright (c) 2012 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 | package org.chromium.android_webview.test; |
6 | |
7 | import java.util.concurrent.Callable; |
8 | import java.util.concurrent.CountDownLatch; |
9 | import java.util.concurrent.TimeUnit; |
10 | import java.util.concurrent.TimeoutException; |
11 | |
12 | import org.chromium.android_webview.AwContents; |
13 | |
14 | /** |
15 | * Base class for WebView find-in-page API tests. |
16 | */ |
17 | public class WebViewFindApisTestBase extends AwTestBase { |
18 | |
19 | private static final String WOODCHUCK = |
20 | "How much WOOD would a woodchuck chuck if a woodchuck could chuck wOoD?"; |
21 | |
22 | private FindResultListener mFindResultListener; |
23 | private AwContents mContents; |
24 | |
25 | @Override |
26 | protected void setUp() throws Exception { |
27 | super.setUp(); |
28 | try { |
29 | mContents = loadContentsFromStringSync(WOODCHUCK); |
30 | } catch (Throwable t) { |
31 | throw new Exception(t); |
32 | } |
33 | } |
34 | |
35 | protected AwContents contents() { |
36 | return mContents; |
37 | } |
38 | |
39 | // Internal interface to intercept find results from AwContentsClient. |
40 | private interface FindResultListener { |
41 | public void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches, |
42 | boolean isDoneCounting); |
43 | }; |
44 | |
45 | private AwContents loadContentsFromStringSync(final String html) throws Throwable { |
46 | final TestAwContentsClient contentsClient = new TestAwContentsClient() { |
47 | @Override |
48 | public void onFindResultReceived(int activeMatchOrdinal, |
49 | int numberOfMatches, boolean isDoneCounting) { |
50 | if (mFindResultListener == null) return; |
51 | mFindResultListener.onFindResultReceived(activeMatchOrdinal, numberOfMatches, |
52 | isDoneCounting); |
53 | } |
54 | }; |
55 | |
56 | final AwContents contents = |
57 | createAwTestContainerViewOnMainSync(contentsClient).getAwContents(); |
58 | final String data = "<html><head></head><body>" + html + "</body></html>"; |
59 | loadDataSync(contents, contentsClient.getOnPageFinishedHelper(), |
60 | data, "text/html", false); |
61 | return contents; |
62 | } |
63 | |
64 | /** |
65 | * Invokes findAllAsync on the UI thread, blocks until find results are |
66 | * received, and returns the number of matches. |
67 | * |
68 | * @param searchString A string to search for. |
69 | * @return The number of instances of the string that were found. |
70 | * @throws Throwable |
71 | */ |
72 | protected int findAllAsyncOnUiThread(final String searchString) |
73 | throws Throwable { |
74 | final IntegerFuture future = new IntegerFuture() { |
75 | @Override |
76 | public void run() { |
77 | mFindResultListener = new FindResultListener() { |
78 | @Override |
79 | public void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches, |
80 | boolean isDoneCounting) { |
81 | if (isDoneCounting) set(numberOfMatches); |
82 | } |
83 | }; |
84 | mContents.findAllAsync(searchString); |
85 | } |
86 | }; |
87 | runTestOnUiThread(future); |
88 | return future.get(10, TimeUnit.SECONDS); |
89 | } |
90 | |
91 | /** |
92 | * Invokes findNext on the UI thread, blocks until find results are |
93 | * received, and returns the ordinal of the highlighted match. |
94 | * |
95 | * @param forwards The direction to search as a boolean, with forwards |
96 | * represented as true and backwards as false. |
97 | * @return The ordinal of the highlighted match. |
98 | * @throws Throwable |
99 | */ |
100 | protected int findNextOnUiThread(final boolean forwards) |
101 | throws Throwable { |
102 | final IntegerFuture future = new IntegerFuture() { |
103 | @Override |
104 | public void run() { |
105 | mFindResultListener = new FindResultListener() { |
106 | @Override |
107 | public void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches, |
108 | boolean isDoneCounting) { |
109 | if (isDoneCounting) set(activeMatchOrdinal); |
110 | } |
111 | }; |
112 | mContents.findNext(forwards); |
113 | } |
114 | }; |
115 | runTestOnUiThread(future); |
116 | return future.get(10, TimeUnit.SECONDS); |
117 | } |
118 | |
119 | /** |
120 | * Invokes clearMatches on the UI thread. |
121 | * |
122 | * @throws Throwable |
123 | */ |
124 | protected void clearMatchesOnUiThread() throws Throwable { |
125 | runTestOnUiThread(new Runnable() { |
126 | @Override |
127 | public void run() { |
128 | mContents.clearMatches(); |
129 | } |
130 | }); |
131 | } |
132 | |
133 | // Similar to java.util.concurrent.Future, but without the ability to cancel. |
134 | private static abstract class IntegerFuture implements Runnable { |
135 | private CountDownLatch mLatch = new CountDownLatch(1); |
136 | private int mValue; |
137 | |
138 | @Override |
139 | public abstract void run(); |
140 | |
141 | /** |
142 | * Gets the value of this Future, blocking for up to the specified |
143 | * timeout for it become available. Throws a TimeoutException if the |
144 | * timeout expires. |
145 | */ |
146 | public int get(long timeout, TimeUnit unit) throws Throwable { |
147 | if (!mLatch.await(timeout, unit)) { |
148 | throw new TimeoutException(); |
149 | } |
150 | return mValue; |
151 | } |
152 | |
153 | /** |
154 | * Sets the value of this Future. |
155 | */ |
156 | protected void set(int value) { |
157 | mValue = value; |
158 | mLatch.countDown(); |
159 | } |
160 | } |
161 | } |