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