diff options
29 files changed, 1251 insertions, 192 deletions
diff --git a/android_webview/android_webview.gyp b/android_webview/android_webview.gyp index cb303f9..dd0b31a 100644 --- a/android_webview/android_webview.gyp +++ b/android_webview/android_webview.gyp @@ -32,12 +32,15 @@ 'browser/aw_http_auth_handler_base.h', 'browser/aw_login_delegate.cc', 'browser/aw_login_delegate.h', + 'browser/find_helper.cc', + 'browser/find_helper.h', 'browser/net/aw_network_delegate.cc', 'browser/net/aw_network_delegate.h', 'browser/renderer_host/aw_render_view_host_ext.cc', 'browser/renderer_host/aw_render_view_host_ext.h', 'browser/renderer_host/aw_resource_dispatcher_host_delegate.cc', 'browser/renderer_host/aw_resource_dispatcher_host_delegate.h', + 'browser/scoped_allow_wait_for_legacy_web_view_api.h', 'lib/aw_browser_dependency_factory_impl.cc', 'lib/aw_browser_dependency_factory_impl.h', 'lib/aw_content_browser_client.cc', diff --git a/android_webview/browser/DEPS b/android_webview/browser/DEPS index 923f45e..2a21cd21 100644 --- a/android_webview/browser/DEPS +++ b/android_webview/browser/DEPS @@ -2,4 +2,7 @@ include_rules = [ "-android_webview", "+android_webview/browser", "+android_webview/common", + + # POD structure required by the find-in-page IPC messages. + "+third_party/WebKit/Source/WebKit/chromium/public/WebFindOptions.h", ] diff --git a/android_webview/browser/find_helper.cc b/android_webview/browser/find_helper.cc new file mode 100644 index 0000000..b4c7669 --- /dev/null +++ b/android_webview/browser/find_helper.cc @@ -0,0 +1,173 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "android_webview/browser/find_helper.h" + +#include "android_webview/browser/scoped_allow_wait_for_legacy_web_view_api.h" +#include "base/message_loop.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/browser/web_contents.h" +#include "content/public/common/stop_find_action.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebFindOptions.h" + +using content::WebContents; +using WebKit::WebFindOptions; + +namespace android_webview { + +FindHelper::FindHelper(WebContents* web_contents) + : WebContentsObserver(web_contents), + listener_(NULL), + async_find_started_(false), + sync_find_started_(false), + find_request_id_counter_(0), + current_request_id_(0), + last_match_count_(-1), + last_active_ordinal_(-1), + weak_factory_(this) { +} + +FindHelper::~FindHelper() { +} + +void FindHelper::SetListener(Listener* listener) { + listener_ = listener; +} + +int FindHelper::FindAllSync(const string16& search_string) { + sync_find_started_ = true; + async_find_started_ = false; + + WebFindOptions options; + options.forward = true; + options.matchCase = false; + options.findNext = false; + + int match_count = 0; + int active_ordinal = 0; + + StartNewRequest(search_string); + + // Any ongoing asynchronous requests will be stopped in the renderer when + // calling SynchronousFind. Using the asynchronous StopFinding message could + // lead to deadblocks as the message could arrive in the middle of the + // synchronous operation and cancel the reply back. + ScopedAllowWaitForLegacyWebViewApi wait; + web_contents()->GetRenderViewHost()->SynchronousFind(current_request_id_, + search_string, + options, + &match_count, + &active_ordinal); + + // Post the task to ourselves to prevent trigerring the notification before + // we actually return from the request. + MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&FindHelper::NotifyResults, weak_factory_.GetWeakPtr(), + active_ordinal, match_count, true)); + + return match_count; +} + +void FindHelper::FindAllAsync(const string16& search_string) { + // Stop any ongoing asynchronous request. + web_contents()->GetRenderViewHost()->StopFinding( + content::STOP_FIND_ACTION_KEEP_SELECTION); + + sync_find_started_ = false; + async_find_started_ = true; + + WebFindOptions options; + options.forward = true; + options.matchCase = false; + options.findNext = false; + + StartNewRequest(search_string); + web_contents()->GetRenderViewHost()->Find(current_request_id_, + search_string, options); +} + +void FindHelper::HandleFindReply(int request_id, + int match_count, + int active_ordinal, + bool finished) { + if ((!async_find_started_ && !sync_find_started_) || + request_id != current_request_id_) { + return; + } + + NotifyResults(active_ordinal, match_count, finished); +} + +void FindHelper::FindNext(bool forward) { + if (!sync_find_started_ && !async_find_started_) + return; + + WebFindOptions options; + options.forward = forward; + options.matchCase = false; + options.findNext = true; + + web_contents()->GetRenderViewHost()->Find(current_request_id_, + last_search_string_, + options); +} + +void FindHelper::ClearMatches() { + web_contents()->GetRenderViewHost()->StopFinding( + content::STOP_FIND_ACTION_CLEAR_SELECTION); + + sync_find_started_ = false; + async_find_started_ = false; + last_search_string_.clear(); + last_match_count_ = -1; + last_active_ordinal_ = -1; +} + +void FindHelper::StartNewRequest(const string16& search_string) { + current_request_id_ = find_request_id_counter_++; + last_search_string_ = search_string; + last_match_count_ = -1; + last_active_ordinal_ = -1; +} + +void FindHelper::NotifyResults(int active_ordinal, + int match_count, + bool finished) { + // Match count or ordinal values set to -1 refer to the received replies. + if (match_count == -1) + match_count = last_match_count_; + else + last_match_count_ = match_count; + + if (active_ordinal == -1) + active_ordinal = last_active_ordinal_; + else + last_active_ordinal_ = active_ordinal; + + // Skip the update if we don't still have a valid ordinal. + // The next update, final or not, should have this information. + if (!finished && active_ordinal == -1) + return; + + // Safeguard in case of errors to prevent reporting -1 to the API listeners. + if (match_count == -1) { + NOTREACHED(); + match_count = 0; + } + + if (active_ordinal == -1) { + NOTREACHED(); + active_ordinal = 0; + } + + // WebView.FindListener active match ordinals are 0-based while WebKit sends + // 1-based ordinals. Still we can receive 0 ordinal in case of no results. + active_ordinal = std::max(active_ordinal - 1, 0); + + if (listener_) + listener_->OnFindResultReceived(active_ordinal, match_count, finished); +} + +} // namespace android_webview diff --git a/android_webview/browser/find_helper.h b/android_webview/browser/find_helper.h new file mode 100644 index 0000000..a9d7aed --- /dev/null +++ b/android_webview/browser/find_helper.h @@ -0,0 +1,77 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ANDROID_WEBVIEW_BROWSER_FIND_HELPER_H_ +#define ANDROID_WEBVIEW_BROWSER_FIND_HELPER_H_ + +#include "base/memory/weak_ptr.h" +#include "content/public/browser/web_contents_observer.h" + +namespace android_webview { + +// Handles the WebView find-in-page API requests. +class FindHelper : public content::WebContentsObserver { + public: + class Listener { + public: + // Called when receiving a new find-in-page update. + // This will be triggered when the results of FindAllSync, FindAllAsync and + // FindNext are available. The value provided in active_ordinal is 0-based. + virtual void OnFindResultReceived(int active_ordinal, + int match_count, + bool finished) = 0; + virtual ~Listener() {} + }; + + explicit FindHelper(content::WebContents* web_contents); + virtual ~FindHelper(); + + // Sets the listener to receive find result updates. + // Does not own the listener and must set to NULL when invalid. + void SetListener(Listener* listener); + + // Synchronous API. + int FindAllSync(const string16& search_string); + + // Asynchronous API. + void FindAllAsync(const string16& search_string); + void HandleFindReply(int request_id, + int match_count, + int active_ordinal, + bool finished); + + // Methods valid in both synchronous and asynchronous modes. + void FindNext(bool forward); + void ClearMatches(); + + private: + void StartNewRequest(const string16& search_string); + void NotifyResults(int active_ordinal, int match_count, bool finished); + + // Listener results are reported to. + Listener* listener_; + + // Used to check the validity of FindNext operations. + bool async_find_started_; + bool sync_find_started_; + + // Used to provide different ids to each request and for result + // verification in asynchronous calls. + int find_request_id_counter_; + int current_request_id_; + + // Required by FindNext and the incremental find replies. + string16 last_search_string_; + int last_match_count_; + int last_active_ordinal_; + + // Used to post synchronous result notifications to ourselves. + base::WeakPtrFactory<FindHelper> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(FindHelper); +}; + +} // namespace android_webview + +#endif // ANDROID_WEBVIEW_BROWSER_FIND_HELPER_H_ diff --git a/android_webview/browser/scoped_allow_wait_for_legacy_web_view_api.h b/android_webview/browser/scoped_allow_wait_for_legacy_web_view_api.h new file mode 100644 index 0000000..af2154b --- /dev/null +++ b/android_webview/browser/scoped_allow_wait_for_legacy_web_view_api.h @@ -0,0 +1,20 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ANDROID_WEBVIEW_BROWSER_SCOPED_ALLOW_WAIT_FOR_LEGACY_WEB_VIEW_API_H +#define ANDROID_WEBVIEW_BROWSER_SCOPED_ALLOW_WAIT_FOR_LEGACY_WEB_VIEW_API_H + +#include "base/threading/thread_restrictions.h" + +// This class is only available when building the chromium back-end for android +// webview: it is required where API backward compatibility demands that the UI +// thread must block waiting on other threads e.g. to obtain a synchronous +// return value. Long term, asynchronous overloads of all such methods will be +// added in the public API, and and no new uses of this will be allowed. +class ScopedAllowWaitForLegacyWebViewApi { + private: + base::ThreadRestrictions::ScopedAllowWait wait; +}; + +#endif // ANDROID_WEBVIEW_BROWSER_SCOPED_ALLOW_WAIT_FOR_LEGACY_WEB_VIEW_API_H diff --git a/android_webview/java/src/org/chromium/android_webview/AwContents.java b/android_webview/java/src/org/chromium/android_webview/AwContents.java index 3f826a9..c0a1a6f 100644 --- a/android_webview/java/src/org/chromium/android_webview/AwContents.java +++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java @@ -102,8 +102,24 @@ public class AwContents { mCleanupReference.cleanupNow(); } + public int findAllSync(String searchString) { + return nativeFindAllSync(mNativeAwContents, searchString); + } + + public void findAllAsync(String searchString) { + nativeFindAllAsync(mNativeAwContents, searchString); + } + + public void findNext(boolean forward) { + nativeFindNext(mNativeAwContents, forward); + } + + public void clearMatches() { + nativeClearMatches(mNativeAwContents); + } + /** - * @return load progress of the WebContents + * @return load progress of the WebContents. */ public int getMostRecentProgress() { // WebContentsDelegateAndroid conveniently caches the most recent notified value for us. @@ -182,6 +198,12 @@ public class AwContents { mContentsClient.onReceivedHttpAuthRequest(handler, host, realm); } + @CalledByNative + public void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches, + boolean isDoneCounting) { + mContentsClient.onFindResultReceived(activeMatchOrdinal, numberOfMatches, isDoneCounting); + } + // ------------------------------------------------------------------------------------------- // Helper methods // ------------------------------------------------------------------------------------------- @@ -270,4 +292,9 @@ public class AwContents { private native void nativeSetIoThreadClient(int nativeAwContents, AwContentsIoThreadClient ioThreadClient); + + private native int nativeFindAllSync(int nativeAwContents, String searchString); + private native void nativeFindAllAsync(int nativeAwContents, String searchString); + private native void nativeFindNext(int nativeAwContents, boolean forward); + private native void nativeClearMatches(int nativeAwContents); } diff --git a/android_webview/java/src/org/chromium/android_webview/AwContentsClient.java b/android_webview/java/src/org/chromium/android_webview/AwContentsClient.java index 4a4233d..b933a6e 100644 --- a/android_webview/java/src/org/chromium/android_webview/AwContentsClient.java +++ b/android_webview/java/src/org/chromium/android_webview/AwContentsClient.java @@ -133,6 +133,14 @@ public abstract class AwContentsClient extends ContentViewClient { JsPromptResultReceiver receiver); //-------------------------------------------------------------------------------------------- + // Other WebView-specific methods + //-------------------------------------------------------------------------------------------- + // + + public abstract void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches, + boolean isDoneCounting); + + //-------------------------------------------------------------------------------------------- // Stuff that we ignore since it only makes sense for Chrome browser //-------------------------------------------------------------------------------------------- // diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AndroidWebViewTestBase.java b/android_webview/javatests/src/org/chromium/android_webview/test/AndroidWebViewTestBase.java index 3b246fc..85a64f7 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/AndroidWebViewTestBase.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AndroidWebViewTestBase.java @@ -8,7 +8,6 @@ import android.app.Instrumentation; import android.content.Context; import android.test.ActivityInstrumentationTestCase2; import android.view.View; -import android.view.ViewGroup; import junit.framework.Assert; @@ -181,6 +180,16 @@ public class AndroidWebViewTestBase return testContainerView.get(); } + protected void destroyAwContentsOnMainSync(final AwContents contents) { + if (contents == null) return; + getInstrumentation().runOnMainSync(new Runnable() { + @Override + public void run() { + contents.destroy(); + } + }); + } + protected String getTitleOnUiThread(final ContentViewCore contentViewCore) throws Throwable { return runTestOnUiThreadAndGetResult(new Callable<String>() { @Override diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/NullContentsClient.java b/android_webview/javatests/src/org/chromium/android_webview/test/NullContentsClient.java index bd1809c..f50b6a8 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/NullContentsClient.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/NullContentsClient.java @@ -57,4 +57,9 @@ class NullContentsClient extends AwContentsClient { public void handleJsPrompt( String url, String message, String defaultValue, JsPromptResultReceiver receiver) { } + + @Override + public void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches, + boolean isDoneCounting) { + } } diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/WebViewAsynchronousFindApisTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/WebViewAsynchronousFindApisTest.java new file mode 100644 index 0000000..218c0a7 --- /dev/null +++ b/android_webview/javatests/src/org/chromium/android_webview/test/WebViewAsynchronousFindApisTest.java @@ -0,0 +1,141 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.android_webview.test; + +import android.test.suitebuilder.annotation.SmallTest; + +import org.chromium.base.test.Feature; + +/** + * Tests the asynchronous find-in-page APIs in WebView. + */ +public class WebViewAsynchronousFindApisTest extends WebViewFindApisTestBase { + + @SmallTest + @Feature({"Android-WebView", "FindInPage"}) + public void testFindAllFinds() throws Throwable { + assertEquals(4, findAllAsyncOnUiThread("wood")); + } + + @SmallTest + @Feature({"Android-WebView", "FindInPage"}) + public void testFindAllDouble() throws Throwable { + findAllAsyncOnUiThread("wood"); + assertEquals(4, findAllAsyncOnUiThread("chuck")); + } + + @SmallTest + @Feature({"Android-WebView", "FindInPage"}) + public void testFindAllDoubleNext() throws Throwable { + assertEquals(4, findAllAsyncOnUiThread("wood")); + assertEquals(4, findAllAsyncOnUiThread("wood")); + assertEquals(2, findNextOnUiThread(true)); + } + + @SmallTest + @Feature({"Android-WebView", "FindInPage"}) + public void testFindAllDoesNotFind() throws Throwable { + assertEquals(0, findAllAsyncOnUiThread("foo")); + } + + @SmallTest + @Feature({"Android-WebView", "FindInPage"}) + public void testFindAllEmptyPage() throws Throwable { + assertEquals(0, findAllAsyncOnUiThread("foo")); + } + + @SmallTest + @Feature({"Android-WebView", "FindInPage"}) + public void testFindAllEmptyString() throws Throwable { + assertEquals(0, findAllAsyncOnUiThread("")); + } + + @SmallTest + @Feature({"Android-WebView", "FindInPage"}) + public void testFindNextForward() throws Throwable { + assertEquals(4, findAllAsyncOnUiThread("wood")); + + for (int i = 2; i <= 4; i++) { + assertEquals(i - 1, findNextOnUiThread(true)); + } + assertEquals(0, findNextOnUiThread(true)); + } + + @SmallTest + @Feature({"Android-WebView", "FindInPage"}) + public void testFindNextBackward() throws Throwable { + assertEquals(4, findAllAsyncOnUiThread("wood")); + + for (int i = 4; i >= 1; i--) { + assertEquals(i - 1, findNextOnUiThread(false)); + } + assertEquals(3, findNextOnUiThread(false)); + } + + @SmallTest + @Feature({"Android-WebView", "FindInPage"}) + public void testFindNextBig() throws Throwable { + assertEquals(4, findAllAsyncOnUiThread("wood")); + + assertEquals(1, findNextOnUiThread(true)); + assertEquals(0, findNextOnUiThread(false)); + assertEquals(3, findNextOnUiThread(false)); + for (int i = 1; i <= 4; i++) { + assertEquals(i - 1, findNextOnUiThread(true)); + } + assertEquals(0, findNextOnUiThread(true)); + } + + @SmallTest + @Feature({"Android-WebView", "FindInPage"}) + public void testFindAllEmptyNext() throws Throwable { + assertEquals(4, findAllAsyncOnUiThread("wood")); + assertEquals(1, findNextOnUiThread(true)); + assertEquals(0, findAllAsyncOnUiThread("")); + assertEquals(0, findNextOnUiThread(true)); + assertEquals(0, findAllAsyncOnUiThread("")); + assertEquals(4, findAllAsyncOnUiThread("wood")); + assertEquals(1, findNextOnUiThread(true)); + } + + @SmallTest + @Feature({"Android-WebView", "FindInPage"}) + public void testClearMatches() throws Throwable { + assertEquals(4, findAllAsyncOnUiThread("wood")); + clearMatchesOnUiThread(); + } + + @SmallTest + @Feature({"Android-WebView", "FindInPage"}) + public void testClearFindNext() throws Throwable { + assertEquals(4, findAllAsyncOnUiThread("wood")); + clearMatchesOnUiThread(); + assertEquals(4, findAllAsyncOnUiThread("wood")); + assertEquals(2, findNextOnUiThread(true)); + } + + @SmallTest + @Feature({"Android-WebView", "FindInPage"}) + public void testFindEmptyNext() throws Throwable { + assertEquals(0, findAllAsyncOnUiThread("")); + assertEquals(0, findNextOnUiThread(true)); + assertEquals(4, findAllAsyncOnUiThread("wood")); + } + + @SmallTest + @Feature({"Android-WebView", "FindInPage"}) + public void testFindNextFirst() throws Throwable { + runTestOnUiThread(new Runnable() { + @Override + public void run() { + contents().findNext(true); + } + }); + assertEquals(4, findAllAsyncOnUiThread("wood")); + assertEquals(1, findNextOnUiThread(true)); + assertEquals(0, findNextOnUiThread(false)); + assertEquals(3, findNextOnUiThread(false)); + } +} diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/WebViewFindApisTestBase.java b/android_webview/javatests/src/org/chromium/android_webview/test/WebViewFindApisTestBase.java new file mode 100644 index 0000000..bdefbfb --- /dev/null +++ b/android_webview/javatests/src/org/chromium/android_webview/test/WebViewFindApisTestBase.java @@ -0,0 +1,184 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.android_webview.test; + +import java.util.concurrent.Callable; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.chromium.android_webview.AwContents; + +/** + * Base class for WebView find-in-page API tests. + */ +public class WebViewFindApisTestBase extends AndroidWebViewTestBase { + + private static final String WOODCHUCK = + "How much WOOD would a woodchuck chuck if a woodchuck could chuck wOoD?"; + + private FindResultListener mFindResultListener; + private AwContents mContents; + + @Override + protected void setUp() throws Exception { + super.setUp(); + try { + mContents = loadContentsFromStringSync(WOODCHUCK); + } catch (Throwable t) { + throw new Exception(t); + } + } + + @Override + protected void tearDown() throws Exception { + destroyAwContentsOnMainSync(mContents); + super.tearDown(); + } + + protected AwContents contents() { + return mContents; + } + + // Internal interface to intercept find results from AwContentsClient. + private interface FindResultListener { + public void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches, + boolean isDoneCounting); + }; + + private AwContents loadContentsFromStringSync(final String html) throws Throwable { + final TestAwContentsClient contentsClient = new TestAwContentsClient() { + @Override + public void onFindResultReceived(int activeMatchOrdinal, + int numberOfMatches, boolean isDoneCounting) { + if (mFindResultListener == null) return; + mFindResultListener.onFindResultReceived(activeMatchOrdinal, numberOfMatches, + isDoneCounting); + } + }; + + final AwContents contents = + createAwTestContainerViewOnMainSync(contentsClient).getAwContents(); + final String data = "<html><head></head><body>" + html + "</body></html>"; + loadDataSync(contents.getContentViewCore(), contentsClient.getOnPageFinishedHelper(), + data, "text/html", false); + return contents; + } + + /** + * Invokes findAllSync on the UI thread and returns the number of matches. + * + * @param searchString A string to search for. + * @return The number of instances of the string that were found. + * @throws Throwable + */ + protected int findAllSyncOnUiThread(final String searchString) + throws Throwable { + return runTestOnUiThreadAndGetResult(new Callable<Integer>() { + @Override + public Integer call() { + return mContents.findAllSync(searchString); + } + }); + } + + /** + * Invokes findAllAsync on the UI thread, blocks until find results are + * received, and returns the number of matches. + * + * @param searchString A string to search for. + * @return The number of instances of the string that were found. + * @throws Throwable + */ + protected int findAllAsyncOnUiThread(final String searchString) + throws Throwable { + final IntegerFuture future = new IntegerFuture() { + @Override + public void run() { + mFindResultListener = new FindResultListener() { + @Override + public void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches, + boolean isDoneCounting) { + if (isDoneCounting) set(numberOfMatches); + } + }; + mContents.findAllAsync(searchString); + } + }; + runTestOnUiThread(future); + return future.get(10, TimeUnit.SECONDS); + } + + /** + * Invokes findNext on the UI thread, blocks until find results are + * received, and returns the ordinal of the highlighted match. + * + * @param forwards The direction to search as a boolean, with forwards + * represented as true and backwards as false. + * @return The ordinal of the highlighted match. + * @throws Throwable + */ + protected int findNextOnUiThread(final boolean forwards) + throws Throwable { + final IntegerFuture future = new IntegerFuture() { + @Override + public void run() { + mFindResultListener = new FindResultListener() { + @Override + public void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches, + boolean isDoneCounting) { + if (isDoneCounting) set(activeMatchOrdinal); + } + }; + mContents.findNext(forwards); + } + }; + runTestOnUiThread(future); + return future.get(10, TimeUnit.SECONDS); + } + + /** + * Invokes clearMatches on the UI thread. + * + * @throws Throwable + */ + protected void clearMatchesOnUiThread() throws Throwable { + runTestOnUiThread(new Runnable() { + @Override + public void run() { + mContents.clearMatches(); + } + }); + } + + // Similar to java.util.concurrent.Future, but without the ability to cancel. + private static abstract class IntegerFuture implements Runnable { + private CountDownLatch mLatch = new CountDownLatch(1); + private int mValue; + + @Override + public abstract void run(); + + /** + * Gets the value of this Future, blocking for up to the specified + * timeout for it become available. Throws a TimeoutException if the + * timeout expires. + */ + public int get(long timeout, TimeUnit unit) throws Throwable { + if (!mLatch.await(timeout, unit)) { + throw new TimeoutException(); + } + return mValue; + } + + /** + * Sets the value of this Future. + */ + protected void set(int value) { + mValue = value; + mLatch.countDown(); + } + } +} diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/WebViewMixedFindApisTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/WebViewMixedFindApisTest.java new file mode 100644 index 0000000..b91523f --- /dev/null +++ b/android_webview/javatests/src/org/chromium/android_webview/test/WebViewMixedFindApisTest.java @@ -0,0 +1,42 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.android_webview.test; + +import android.test.suitebuilder.annotation.SmallTest; + +import org.chromium.base.test.Feature; + +/** + * Tests the mixed use of synchronous and asynchronous find-in-page APIs in WebView. + * Helps to spot race conditions or potential deadlocks caused by the renderer receiving + * asynchronous messages while synchronous requests are being processed. + */ +public class WebViewMixedFindApisTest extends WebViewFindApisTestBase { + + @SmallTest + @Feature({"Android-WebView", "FindInPage"}) + public void testAsyncFindOperationsMixedWithSyncFind() throws Throwable { + clearMatchesOnUiThread(); + assertEquals(4, findAllSyncOnUiThread("wood")); + assertEquals(4, findAllSyncOnUiThread("wood")); + clearMatchesOnUiThread(); + assertEquals(4, findAllAsyncOnUiThread("wood")); + assertEquals(3, findNextOnUiThread(true)); + clearMatchesOnUiThread(); + assertEquals(4, findAllSyncOnUiThread("wood")); + } + + @SmallTest + @Feature({"Android-WebView", "FindInPage"}) + public void testInterleavedFinds() throws Throwable { + assertEquals(4, findAllSyncOnUiThread("wood")); + assertEquals(4, findAllAsyncOnUiThread("wood")); + assertEquals(4, findAllSyncOnUiThread("wood")); + assertEquals(3, findNextOnUiThread(true)); + assertEquals(4, findAllAsyncOnUiThread("wood")); + assertEquals(1, findNextOnUiThread(true)); + assertEquals(4, findAllSyncOnUiThread("wood")); + } +} diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/WebViewSynchronousFindApisTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/WebViewSynchronousFindApisTest.java new file mode 100644 index 0000000..272e330 --- /dev/null +++ b/android_webview/javatests/src/org/chromium/android_webview/test/WebViewSynchronousFindApisTest.java @@ -0,0 +1,141 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.android_webview.test; + +import android.test.suitebuilder.annotation.SmallTest; + +import org.chromium.base.test.Feature; + +/** + * Tests the synchronous find-in-page APIs in WebView. + */ +public class WebViewSynchronousFindApisTest extends WebViewFindApisTestBase { + + @SmallTest + @Feature({"Android-WebView", "FindInPage"}) + public void testFindAllFinds() throws Throwable { + assertEquals(4, findAllSyncOnUiThread("wood")); + } + + @SmallTest + @Feature({"Android-WebView", "FindInPage"}) + public void testFindAllDouble() throws Throwable { + findAllSyncOnUiThread("wood"); + assertEquals(4, findAllSyncOnUiThread("chuck")); + } + + @SmallTest + @Feature({"Android-WebView", "FindInPage"}) + public void testFindAllDoubleNext() throws Throwable { + assertEquals(4, findAllSyncOnUiThread("wood")); + assertEquals(4, findAllSyncOnUiThread("wood")); + assertEquals(2, findNextOnUiThread(true)); + } + + @SmallTest + @Feature({"Android-WebView", "FindInPage"}) + public void testFindAllDoesNotFind() throws Throwable { + assertEquals(0, findAllSyncOnUiThread("foo")); + } + + @SmallTest + @Feature({"Android-WebView", "FindInPage"}) + public void testFindAllEmptyPage() throws Throwable { + assertEquals(0, findAllSyncOnUiThread("foo")); + } + + @SmallTest + @Feature({"Android-WebView", "FindInPage"}) + public void testFindAllEmptyString() throws Throwable { + assertEquals(0, findAllSyncOnUiThread("")); + } + + @SmallTest + @Feature({"Android-WebView", "FindInPage"}) + public void testFindNextForward() throws Throwable { + assertEquals(4, findAllSyncOnUiThread("wood")); + + for (int i = 2; i <= 4; i++) { + assertEquals(i - 1, findNextOnUiThread(true)); + } + assertEquals(0, findNextOnUiThread(true)); + } + + @SmallTest + @Feature({"Android-WebView", "FindInPage"}) + public void testFindNextBackward() throws Throwable { + assertEquals(4, findAllSyncOnUiThread("wood")); + + for (int i = 4; i >= 1; i--) { + assertEquals(i - 1, findNextOnUiThread(false)); + } + assertEquals(3, findNextOnUiThread(false)); + } + + @SmallTest + @Feature({"Android-WebView", "FindInPage"}) + public void testFindNextBig() throws Throwable { + assertEquals(4, findAllSyncOnUiThread("wood")); + + assertEquals(1, findNextOnUiThread(true)); + assertEquals(0, findNextOnUiThread(false)); + assertEquals(3, findNextOnUiThread(false)); + for (int i = 1; i <= 4; i++) { + assertEquals(i - 1, findNextOnUiThread(true)); + } + assertEquals(0, findNextOnUiThread(true)); + } + + @SmallTest + @Feature({"Android-WebView", "FindInPage"}) + public void testFindAllEmptyNext() throws Throwable { + assertEquals(4, findAllSyncOnUiThread("wood")); + assertEquals(1, findNextOnUiThread(true)); + assertEquals(0, findAllSyncOnUiThread("")); + assertEquals(0, findNextOnUiThread(true)); + assertEquals(0, findAllSyncOnUiThread("")); + assertEquals(4, findAllSyncOnUiThread("wood")); + assertEquals(1, findNextOnUiThread(true)); + } + + @SmallTest + @Feature({"Android-WebView", "FindInPage"}) + public void testClearMatches() throws Throwable { + assertEquals(4, findAllSyncOnUiThread("wood")); + clearMatchesOnUiThread(); + } + + @SmallTest + @Feature({"Android-WebView", "FindInPage"}) + public void testClearFindNext() throws Throwable { + assertEquals(4, findAllSyncOnUiThread("wood")); + clearMatchesOnUiThread(); + assertEquals(4, findAllSyncOnUiThread("wood")); + assertEquals(2, findNextOnUiThread(true)); + } + + @SmallTest + @Feature({"Android-WebView", "FindInPage"}) + public void testFindEmptyNext() throws Throwable { + assertEquals(0, findAllSyncOnUiThread("")); + assertEquals(0, findNextOnUiThread(true)); + assertEquals(4, findAllSyncOnUiThread("wood")); + } + + @SmallTest + @Feature({"Android-WebView", "FindInPage"}) + public void testFindNextFirst() throws Throwable { + runTestOnUiThread(new Runnable() { + @Override + public void run() { + contents().findNext(true); + } + }); + assertEquals(4, findAllSyncOnUiThread("wood")); + assertEquals(1, findNextOnUiThread(true)); + assertEquals(0, findNextOnUiThread(false)); + assertEquals(3, findNextOnUiThread(false)); + } +} diff --git a/android_webview/lib/main/webview_entry_point.cc b/android_webview/lib/main/webview_entry_point.cc index 567161e..8a4996f 100644 --- a/android_webview/lib/main/webview_entry_point.cc +++ b/android_webview/lib/main/webview_entry_point.cc @@ -5,8 +5,10 @@ #include "android_webview/lib/main/aw_main_delegate.h" #include "android_webview/native/android_webview_jni_registrar.h" #include "base/android/jni_android.h" +#include "base/command_line.h" #include "content/public/app/android_library_loader_hooks.h" #include "content/public/app/content_main.h" +#include "content/public/common/content_switches.h" // This is called by the VM when the shared library is first loaded. // Most of the initialization is done in LibraryLoadedOnMainThread(), not here. @@ -19,6 +21,11 @@ JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { if (!android_webview::RegisterJni(env)) return -1; + // Set the command line to enable synchronous API compatibility. + CommandLine::Init(0, NULL); + CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kEnableWebViewSynchronousAPIs); + content::SetContentMainDelegate(new android_webview::AwMainDelegate()); return JNI_VERSION_1_4; diff --git a/android_webview/native/aw_contents.cc b/android_webview/native/aw_contents.cc index fab4809..024da78 100644 --- a/android_webview/native/aw_contents.cc +++ b/android_webview/native/aw_contents.cc @@ -20,8 +20,11 @@ #include "jni/AwContents_jni.h" using base::android::AttachCurrentThread; +using base::android::ConvertJavaStringToUTF16; +using base::android::ConvertJavaStringToUTF8; using base::android::ConvertUTF16ToJavaString; using base::android::ConvertUTF8ToJavaString; +using base::android::JavaRef; using base::android::ScopedJavaGlobalRef; using base::android::ScopedJavaLocalRef; using content::BrowserThread; @@ -37,7 +40,14 @@ const void* kAwContentsUserDataKey = &kAwContentsUserDataKey; class AwContentsUserData : public base::SupportsUserData::Data { public: AwContentsUserData(AwContents* ptr) : contents_(ptr) {} - AwContents* get() { return contents_; } + + static AwContents* GetContents(WebContents* web_contents) { + if (!web_contents) + return NULL; + AwContentsUserData* data = reinterpret_cast<AwContentsUserData*>( + web_contents->GetUserData(kAwContentsUserDataKey)); + return data ? data->contents_ : NULL; + } private: AwContents* contents_; @@ -46,13 +56,8 @@ class AwContentsUserData : public base::SupportsUserData::Data { } // namespace // static -AwContents* AwContents::FromWebContents(content::WebContents* web_contents) { - if (web_contents) { - AwContentsUserData* data = reinterpret_cast<AwContentsUserData*>( - web_contents->GetUserData(kAwContentsUserDataKey)); - if (data) return data->get(); - } - return NULL; +AwContents* AwContents::FromWebContents(WebContents* web_contents) { + return AwContentsUserData::GetContents(web_contents); } AwContents::AwContents(JNIEnv* env, @@ -64,20 +69,26 @@ AwContents::AwContents(JNIEnv* env, new AwWebContentsDelegate(env, web_contents_delegate)) { android_webview::AwBrowserDependencyFactory* dependency_factory = android_webview::AwBrowserDependencyFactory::GetInstance(); - content::WebContents* web_contents = + + WebContents* web_contents = dependency_factory->CreateWebContents(private_browsing); + + DCHECK(!AwContents::FromWebContents(web_contents)); + web_contents->SetUserData(kAwContentsUserDataKey, + new AwContentsUserData(this)); + contents_container_.reset(dependency_factory->CreateContentsContainer( web_contents)); web_contents->SetDelegate(web_contents_delegate_.get()); - web_contents->SetUserData(kAwContentsUserDataKey, - new AwContentsUserData(this)); render_view_host_ext_.reset(new AwRenderViewHostExt(web_contents)); } AwContents::~AwContents() { - content::WebContents* web_contents = contents_container_->GetWebContents(); + WebContents* web_contents = contents_container_->GetWebContents(); DCHECK(AwContents::FromWebContents(web_contents) == this); web_contents->RemoveUserData(kAwContentsUserDataKey); + if (find_helper_.get()) + find_helper_->SetListener(NULL); } jint AwContents::GetWebContents(JNIEnv* env, jobject obj) { @@ -113,7 +124,7 @@ void GenerateMHTMLCallback(ScopedJavaGlobalRef<jobject>* callback, // Android files are UTF8, so the path conversion below is safe. Java_AwContents_generateMHTMLCallback( env, - base::android::ConvertUTF8ToJavaString(env, path.AsUTF8Unsafe()).obj(), + ConvertUTF8ToJavaString(env, path.AsUTF8Unsafe()).obj(), size, callback->obj()); } } // namespace @@ -123,7 +134,7 @@ void AwContents::GenerateMHTML(JNIEnv* env, jobject obj, ScopedJavaGlobalRef<jobject>* j_callback = new ScopedJavaGlobalRef<jobject>(); j_callback->Reset(env, callback); contents_container_->GetWebContents()->GenerateMHTML( - FilePath(base::android::ConvertJavaStringToUTF8(env, jpath)), + FilePath(ConvertJavaStringToUTF8(env, jpath)), base::Bind(&GenerateMHTMLCallback, base::Owned(j_callback))); } @@ -132,8 +143,8 @@ void AwContents::RunJavaScriptDialog( const GURL& origin_url, const string16& message_text, const string16& default_prompt_text, - const base::android::ScopedJavaLocalRef<jobject>& js_result) { - JNIEnv* env = base::android::AttachCurrentThread(); + const ScopedJavaLocalRef<jobject>& js_result) { + JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef<jobject> obj = java_ref_.get(env); if (obj.is_null()) @@ -168,8 +179,8 @@ void AwContents::RunJavaScriptDialog( void AwContents::RunBeforeUnloadDialog( const GURL& origin_url, const string16& message_text, - const base::android::ScopedJavaLocalRef<jobject>& js_result) { - JNIEnv* env = base::android::AttachCurrentThread(); + const ScopedJavaLocalRef<jobject>& js_result) { + JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef<jobject> obj = java_ref_.get(env); if (obj.is_null()) @@ -184,7 +195,7 @@ void AwContents::RunBeforeUnloadDialog( } void AwContents::onReceivedHttpAuthRequest( - const base::android::JavaRef<jobject>& handler, + const JavaRef<jobject>& handler, const std::string& host, const std::string& realm) { JNIEnv* env = AttachCurrentThread(); @@ -215,5 +226,42 @@ bool RegisterAwContents(JNIEnv* env) { return RegisterNativesImpl(env) >= 0; } +jint AwContents::FindAllSync(JNIEnv* env, jobject obj, jstring search_string) { + return GetFindHelper()->FindAllSync( + ConvertJavaStringToUTF16(env, search_string)); +} + +void AwContents::FindAllAsync(JNIEnv* env, jobject obj, jstring search_string) { + GetFindHelper()->FindAllAsync(ConvertJavaStringToUTF16(env, search_string)); +} + +void AwContents::FindNext(JNIEnv* env, jobject obj, jboolean forward) { + GetFindHelper()->FindNext(forward); +} + +void AwContents::ClearMatches(JNIEnv* env, jobject obj) { + GetFindHelper()->ClearMatches(); +} + +FindHelper* AwContents::GetFindHelper() { + if (!find_helper_.get()) { + WebContents* web_contents = contents_container_->GetWebContents(); + find_helper_.reset(new FindHelper(web_contents)); + find_helper_->SetListener(this); + } + return find_helper_.get(); +} + +void AwContents::OnFindResultReceived(int active_ordinal, + int match_count, + bool finished) { + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef<jobject> obj = java_ref_.get(env); + if (obj.is_null()) + return; + + Java_AwContents_onFindResultReceived( + env, obj.obj(), active_ordinal, match_count, finished); +} } // namespace android_webview diff --git a/android_webview/native/aw_contents.h b/android_webview/native/aw_contents.h index 472075e..556f5054 100644 --- a/android_webview/native/aw_contents.h +++ b/android_webview/native/aw_contents.h @@ -8,6 +8,7 @@ #include <jni.h> #include <string> +#include "android_webview/browser/find_helper.h" #include "base/android/scoped_java_ref.h" #include "base/android/jni_helper.h" #include "base/memory/scoped_ptr.h" @@ -29,7 +30,7 @@ class AwWebContentsDelegate; // Provides the ownership of and access to browser components required for // WebView functionality; analogous to chrome's TabContents, but with a // level of indirection provided by the AwContentsContainer abstraction. -class AwContents { +class AwContents : public FindHelper::Listener { public: // Returns the AwContents instance associated with |web_contents|, or NULL. static AwContents* FromWebContents(content::WebContents* web_contents); @@ -38,7 +39,7 @@ class AwContents { jobject obj, jobject web_contents_delegate, bool private_browsing); - ~AwContents(); + virtual ~AwContents(); void RunJavaScriptDialog( content::JavaScriptMessageType message_type, @@ -65,11 +66,24 @@ class AwContents { void GenerateMHTML(JNIEnv* env, jobject obj, jstring jpath, jobject callback); void SetIoThreadClient(JNIEnv* env, jobject obj, jobject client); + // Find-in-page API and related methods. + jint FindAllSync(JNIEnv* env, jobject obj, jstring search_string); + void FindAllAsync(JNIEnv* env, jobject obj, jstring search_string); + void FindNext(JNIEnv* env, jobject obj, jboolean forward); + void ClearMatches(JNIEnv* env, jobject obj); + + FindHelper* GetFindHelper(); + + // FindHelper::Listener implementation. + virtual void OnFindResultReceived(int active_ordinal, + int match_count, + bool finished) OVERRIDE; private: JavaObjectWeakGlobalRef java_ref_; scoped_ptr<AwContentsContainer> contents_container_; scoped_ptr<AwWebContentsDelegate> web_contents_delegate_; scoped_ptr<AwRenderViewHostExt> render_view_host_ext_; + scoped_ptr<FindHelper> find_helper_; DISALLOW_COPY_AND_ASSIGN(AwContents); }; diff --git a/android_webview/native/aw_web_contents_delegate.cc b/android_webview/native/aw_web_contents_delegate.cc index 249ee0f..2a04d2d 100644 --- a/android_webview/native/aw_web_contents_delegate.cc +++ b/android_webview/native/aw_web_contents_delegate.cc @@ -5,7 +5,12 @@ #include "android_webview/native/aw_web_contents_delegate.h" #include "base/lazy_instance.h" +#include "android_webview/browser/find_helper.h" +#include "android_webview/native/aw_contents.h" #include "android_webview/native/aw_javascript_dialog_creator.h" +#include "content/public/browser/web_contents.h" + +using content::WebContents; namespace android_webview { @@ -26,4 +31,20 @@ AwWebContentsDelegate::GetJavaScriptDialogCreator() { return g_javascript_dialog_creator.Pointer(); } +void AwWebContentsDelegate::FindReply(WebContents* web_contents, + int request_id, + int number_of_matches, + const gfx::Rect& selection_rect, + int active_match_ordinal, + bool final_update) { + AwContents* aw_contents = AwContents::FromWebContents(web_contents); + if (!aw_contents) + return; + + aw_contents->GetFindHelper()->HandleFindReply(request_id, + number_of_matches, + active_match_ordinal, + final_update); +} + } // namespace android_webview diff --git a/android_webview/native/aw_web_contents_delegate.h b/android_webview/native/aw_web_contents_delegate.h index 011451e..1ce3f31 100644 --- a/android_webview/native/aw_web_contents_delegate.h +++ b/android_webview/native/aw_web_contents_delegate.h @@ -21,6 +21,12 @@ class AwWebContentsDelegate virtual ~AwWebContentsDelegate(); virtual content::JavaScriptDialogCreator* GetJavaScriptDialogCreator() OVERRIDE; + virtual void FindReply(content::WebContents* web_contents, + int request_id, + int number_of_matches, + const gfx::Rect& selection_rect, + int active_match_ordinal, + bool final_update) OVERRIDE; }; } // namespace android_webview diff --git a/android_webview/native/cookie_manager.cc b/android_webview/native/cookie_manager.cc index fbbfc06..d4b6619 100644 --- a/android_webview/native/cookie_manager.cc +++ b/android_webview/native/cookie_manager.cc @@ -5,6 +5,7 @@ #include "android_webview/native/cookie_manager.h" #include "android_webview/browser/aw_cookie_access_policy.h" +#include "android_webview/browser/scoped_allow_wait_for_legacy_web_view_api.h" #include "android_webview/native/aw_browser_dependency_factory.h" #include "base/android/jni_string.h" #include "base/bind.h" @@ -26,16 +27,6 @@ using net::CookieList; using net::CookieMonster; using net::URLRequestContextGetter; -// This class is only available when building the chromium back-end for andriod -// webview: it is required where API backward compatibility demands that the UI -// thread must block waiting on other threads e.g. to obtain a synchronous -// return value. Long term, asynchronous overloads of all such methods will be -// added in the public API, and and no new uses of this will be allowed. -class ScopedAllowWaitForLegacyWebViewApi { - private: - base::ThreadRestrictions::ScopedAllowWait wait; -}; - // CookieManager should be refactored to not require all tasks accessing the // CookieStore to be piped through the IO thread. It is currently required as // the URLRequestContext provides the easiest mechanism for accessing the diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc index bc5c4bf..49ef084 100644 --- a/content/browser/renderer_host/render_process_host_impl.cc +++ b/content/browser/renderer_host/render_process_host_impl.cc @@ -111,6 +111,7 @@ #include "ipc/ipc_logging.h" #include "ipc/ipc_platform_file.h" #include "ipc/ipc_switches.h" +#include "ipc/ipc_sync_channel.h" #include "media/base/media_switches.h" #include "net/url_request/url_request_context_getter.h" #include "ui/base/ui_base_switches.h" @@ -343,6 +344,9 @@ RenderProcessHostImpl::RenderProcessHostImpl( storage_partition_impl_(storage_partition_impl), sudden_termination_allowed_(true), ignore_input_events_(false), +#if defined(OS_ANDROID) + dummy_shutdown_event_(false, false), +#endif is_guest_(is_guest) { widget_helper_ = new RenderWidgetHelper(); @@ -450,9 +454,19 @@ bool RenderProcessHostImpl::Init() { // Setup the IPC channel. const std::string channel_id = IPC::Channel::GenerateVerifiedChannelID(std::string()); - channel_.reset(new IPC::ChannelProxy( - channel_id, IPC::Channel::MODE_SERVER, this, - BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO))); + channel_.reset( +#if defined(OS_ANDROID) + // Android WebView needs to be able to wait from the UI thread to support + // the synchronous legacy APIs. + browser_command_line.HasSwitch(switches::kEnableWebViewSynchronousAPIs) ? + new IPC::SyncChannel( + channel_id, IPC::Channel::MODE_SERVER, this, + BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO), + true, &dummy_shutdown_event_) : +#endif + new IPC::ChannelProxy( + channel_id, IPC::Channel::MODE_SERVER, this, + BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO))); // Call the embedder first so that their IPC filters have priority. GetContentClient()->browser()->RenderProcessHostCreated(this); diff --git a/content/browser/renderer_host/render_process_host_impl.h b/content/browser/renderer_host/render_process_host_impl.h index 919115a..d28a69c 100644 --- a/content/browser/renderer_host/render_process_host_impl.h +++ b/content/browser/renderer_host/render_process_host_impl.h @@ -11,6 +11,7 @@ #include "base/memory/scoped_ptr.h" #include "base/process.h" +#include "base/synchronization/waitable_event.h" #include "base/timer.h" #include "content/browser/child_process_launcher.h" #include "content/common/content_export.h" @@ -293,6 +294,15 @@ class CONTENT_EXPORT RenderProcessHostImpl // Records the last time we regarded the child process active. base::TimeTicks child_process_activity_time_; +#if defined(OS_ANDROID) + // Android WebView needs to use a SyncChannel to block the browser process + // for synchronous find-in-page API support. In that case the shutdown event + // makes no sense as the Android port doesn't shutdown, but gets killed. + // SyncChannel still expects a shutdown event, so create a dummy one that + // will never will be signaled. + base::WaitableEvent dummy_shutdown_event_; +#endif + // Indicates whether this is a RenderProcessHost of a Browser Plugin guest // renderer. bool is_guest_; diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc index 7b99267..246d121 100644 --- a/content/browser/renderer_host/render_view_host_impl.cc +++ b/content/browser/renderer_host/render_view_host_impl.cc @@ -12,6 +12,7 @@ #include "base/i18n/rtl.h" #include "base/json/json_reader.h" #include "base/json/json_writer.h" +#include "base/command_line.h" #include "base/message_loop.h" #include "base/stl_util.h" #include "base/string_util.h" @@ -510,6 +511,20 @@ void RenderViewHostImpl::ActivateNearestFindResult(int request_id, void RenderViewHostImpl::RequestFindMatchRects(int current_version) { Send(new ViewMsg_FindMatchRects(GetRoutingID(), current_version)); } + +void RenderViewHostImpl::SynchronousFind(int request_id, + const string16& search_text, + const WebKit::WebFindOptions& options, + int* match_count, + int* active_ordinal) { + if (!CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableWebViewSynchronousAPIs)) { + return; + } + + Send(new ViewMsg_SynchronousFind(GetRoutingID(), request_id, search_text, + options, match_count, active_ordinal)); +} #endif void RenderViewHostImpl::DragTargetDragEnter( diff --git a/content/browser/renderer_host/render_view_host_impl.h b/content/browser/renderer_host/render_view_host_impl.h index 0e3f1dd..ef5e789 100644 --- a/content/browser/renderer_host/render_view_host_impl.h +++ b/content/browser/renderer_host/render_view_host_impl.h @@ -228,6 +228,11 @@ class CONTENT_EXPORT RenderViewHostImpl float x, float y) OVERRIDE; virtual void RequestFindMatchRects(int current_version) OVERRIDE; + virtual void SynchronousFind(int request_id, + const string16& search_text, + const WebKit::WebFindOptions& options, + int* match_count, + int* active_ordinal) OVERRIDE; #endif void set_delegate(RenderViewHostDelegate* d) { diff --git a/content/common/view_messages.h b/content/common/view_messages.h index 6751e30..f4e12ad 100644 --- a/content/common/view_messages.h +++ b/content/common/view_messages.h @@ -867,28 +867,6 @@ IPC_SYNC_MESSAGE_CONTROL0_1(ViewHostMsg_GetMonitorColorProfile, IPC_MESSAGE_CONTROL1(ViewMsg_New, ViewMsg_New_Params) -#if defined(OS_ANDROID) -// Sent when the user clicks on the find result bar to activate a find result. -// The point (x,y) is in fractions of the content document's width and height. -IPC_MESSAGE_ROUTED3(ViewMsg_ActivateNearestFindResult, - int /* request_id */, - float /* x */, - float /* y */) - -// Sent when the browser wants the bounding boxes of the current find matches. -// -// If match rects are already cached on the browser side, |current_version| -// should be the version number from the ViewHostMsg_FindMatchRects_Reply -// they came in, so the renderer can tell if it needs to send updated rects. -// Otherwise just pass -1 to always receive the list of rects. -// -// There must be an active search string (it is probably most useful to call -// this immediately after a ViewHostMsg_Find_Reply message arrives with -// final_update set to true). -IPC_MESSAGE_ROUTED1(ViewMsg_FindMatchRects, - int /* current_version */) -#endif - // Reply in response to ViewHostMsg_ShowView or ViewHostMsg_ShowWidget. // similar to the new command, but used when the renderer created a view // first, and we need to update it. @@ -1134,16 +1112,6 @@ IPC_MESSAGE_ROUTED2(ViewMsg_CSSInsertRequest, string16, /* frame_xpath */ std::string /* css string */) -// External popup menus. -#if defined(OS_MACOSX) -IPC_MESSAGE_ROUTED1(ViewMsg_SelectPopupMenuItem, - int /* selected index, -1 means no selection */) -#elif defined(OS_ANDROID) -IPC_MESSAGE_ROUTED2(ViewMsg_SelectPopupMenuItems, - bool /* user canceled the popup */, - std::vector<int> /* selected indices */) -#endif - // Change the zoom level for the current main frame. If the level actually // changes, a ViewHostMsg_DidZoomURL message will be sent back to the browser // telling it what url got zoomed and what its current zoom level is. @@ -1351,27 +1319,6 @@ IPC_MESSAGE_ROUTED1(ViewMsg_SetActive, IPC_MESSAGE_ROUTED1(ViewMsg_SetNavigationStartTime, base::TimeTicks /* browser_navigation_start */) -#if defined(OS_MACOSX) -// Let the RenderView know its window has changed visibility. -IPC_MESSAGE_ROUTED1(ViewMsg_SetWindowVisibility, - bool /* visibile */) - -// Let the RenderView know its window's frame has changed. -IPC_MESSAGE_ROUTED2(ViewMsg_WindowFrameChanged, - gfx::Rect /* window frame */, - gfx::Rect /* content view frame */) - -// Message sent from the browser to the renderer when the user starts or stops -// resizing the view. -IPC_MESSAGE_ROUTED1(ViewMsg_SetInLiveResize, - bool /* enable */) - -// Tell the renderer that plugin IME has completed. -IPC_MESSAGE_ROUTED2(ViewMsg_PluginImeCompositionCompleted, - string16 /* text */, - int /* plugin_id */) -#endif - // Response message to ViewHostMsg_CreateShared/DedicatedWorker. // Sent when the worker has started. IPC_MESSAGE_ROUTED0(ViewMsg_WorkerCreated) @@ -1426,6 +1373,72 @@ IPC_MESSAGE_CONTROL1(ViewMsg_TempCrashWithData, IPC_MESSAGE_ROUTED1(ViewMsg_SetAccessibilityMode, AccessibilityMode) +#if defined(OS_ANDROID) +// Sent when the user clicks on the find result bar to activate a find result. +// The point (x,y) is in fractions of the content document's width and height. +IPC_MESSAGE_ROUTED3(ViewMsg_ActivateNearestFindResult, + int /* request_id */, + float /* x */, + float /* y */) + +// Sent when the browser wants the bounding boxes of the current find matches. +// +// If match rects are already cached on the browser side, |current_version| +// should be the version number from the ViewHostMsg_FindMatchRects_Reply +// they came in, so the renderer can tell if it needs to send updated rects. +// Otherwise just pass -1 to always receive the list of rects. +// +// There must be an active search string (it is probably most useful to call +// this immediately after a ViewHostMsg_Find_Reply message arrives with +// final_update set to true). +IPC_MESSAGE_ROUTED1(ViewMsg_FindMatchRects, + int /* current_version */) + +// Sent when the user wants to search for all occurrences of a word or find +// the next result in a synchronous way. This method forces the UI thread in +// the browser to wait for the renderer to reply, therefore blocking the UI. +// +// This functionality is required for compatibility with the legacy Android +// WebView API. As this goes strongly against the Chromium design guidelines, +// don't use this as inspiration. +// +IPC_SYNC_MESSAGE_ROUTED3_2(ViewMsg_SynchronousFind, + int /* request_id */, + string16 /* search_string */, + WebKit::WebFindOptions /* options */, + int /* match_count */, + int /* active_ordinal */) + +// External popup menus. +IPC_MESSAGE_ROUTED2(ViewMsg_SelectPopupMenuItems, + bool /* user canceled the popup */, + std::vector<int> /* selected indices */) + +#elif defined(OS_MACOSX) +// Let the RenderView know its window has changed visibility. +IPC_MESSAGE_ROUTED1(ViewMsg_SetWindowVisibility, + bool /* visibile */) + +// Let the RenderView know its window's frame has changed. +IPC_MESSAGE_ROUTED2(ViewMsg_WindowFrameChanged, + gfx::Rect /* window frame */, + gfx::Rect /* content view frame */) + +// Message sent from the browser to the renderer when the user starts or stops +// resizing the view. +IPC_MESSAGE_ROUTED1(ViewMsg_SetInLiveResize, + bool /* enable */) + +// Tell the renderer that plugin IME has completed. +IPC_MESSAGE_ROUTED2(ViewMsg_PluginImeCompositionCompleted, + string16 /* text */, + int /* plugin_id */) + +// External popup menus. +IPC_MESSAGE_ROUTED1(ViewMsg_SelectPopupMenuItem, + int /* selected index, -1 means no selection */) +#endif + // ----------------------------------------------------------------------------- // Messages sent from the renderer to the browser. @@ -1984,15 +1997,6 @@ IPC_SYNC_MESSAGE_ROUTED1_0(ViewHostMsg_DestroyPluginContainer, gfx::PluginWindowHandle /* id */) #endif -#if defined(OS_MACOSX) -// Request that the browser load a font into shared memory for us. -IPC_SYNC_MESSAGE_CONTROL1_3(ViewHostMsg_LoadFont, - FontDescriptor /* font to load */, - uint32 /* buffer size */, - base::SharedMemoryHandle /* font data */, - uint32 /* font id */) -#endif - // Send the tooltip text for the current mouse position to the browser. IPC_MESSAGE_ROUTED2(ViewHostMsg_SetTooltipText, string16 /* tooltip text string */, @@ -2124,6 +2128,13 @@ IPC_MESSAGE_CONTROL1(ViewHostMsg_SuddenTerminationChanged, bool /* enabled */) #if defined(OS_MACOSX) +// Request that the browser load a font into shared memory for us. +IPC_SYNC_MESSAGE_CONTROL1_3(ViewHostMsg_LoadFont, + FontDescriptor /* font to load */, + uint32 /* buffer size */, + base::SharedMemoryHandle /* font data */, + uint32 /* font id */) + // On OSX, we cannot allocated shared memory from within the sandbox, so // this call exists for the renderer to ask the browser to allocate memory // on its behalf. We return a file descriptor to the POSIX shared memory. @@ -2292,12 +2303,6 @@ IPC_MESSAGE_ROUTED3(ViewHostMsg_SendSerializedHtmlData, std::string /* data buffer */, int32 /* complete status */) -#if defined(OS_ANDROID) -// Start an android intent with the given URI. -IPC_MESSAGE_ROUTED1(ViewHostMsg_StartContentIntent, - GURL /* content_url */) -#endif - // Notifies the browser of an event occurring in the media pipeline. IPC_MESSAGE_CONTROL1(ViewHostMsg_MediaLogEvent, media::MediaLogEvent /* event */) @@ -2391,6 +2396,10 @@ IPC_MESSAGE_ROUTED3(ViewHostMsg_FindMatchRects_Reply, std::vector<gfx::RectF> /* rects */, gfx::RectF /* active_rect */) +// Start an android intent with the given URI. +IPC_MESSAGE_ROUTED1(ViewHostMsg_StartContentIntent, + GURL /* content_url */) + // Messages for notifying the render process of media playback status ------- // Media buffering has updated. diff --git a/content/public/browser/render_view_host.h b/content/public/browser/render_view_host.h index 5f0e686..0e95386 100644 --- a/content/public/browser/render_view_host.h +++ b/content/public/browser/render_view_host.h @@ -273,6 +273,14 @@ class CONTENT_EXPORT RenderViewHost : virtual public RenderWidgetHost { // Asks the renderer to send the rects of the current find matches. virtual void RequestFindMatchRects(int current_version) = 0; + + // Synchronous find request. Returns the number of matches found and the + // ordinal of the active match. Required by the legacy Android WebView API. + virtual void SynchronousFind(int request_id, + const string16& search_text, + const WebKit::WebFindOptions& options, + int* match_count, + int* active_ordinal) = 0; #endif }; diff --git a/content/public/common/content_switches.cc b/content/public/common/content_switches.cc index 1126f02..7039ed8 100644 --- a/content/public/common/content_switches.cc +++ b/content/public/common/content_switches.cc @@ -651,6 +651,7 @@ const char kMediaPlayerInRenderProcess[] = "media-player-in-render-process"; // Set when Chromium should use a mobile user agent. const char kUseMobileUserAgent[] = "use-mobile-user-agent"; + // Omnibus flag setting an Android graphics mode. May be: // "basic" (untiled software path) // "compositor" (hardware-accelerated compositing), @@ -660,6 +661,9 @@ const char kGraphicsModeValueCompositor[] = "compositor"; // The telephony region (ISO country code) to use in phone number detection. const char kNetworkCountryIso[] = "network-country-iso"; + +// Set to enable compatibility with legacy WebView synchronous APIs. +const char kEnableWebViewSynchronousAPIs[] = "enable-webview-synchronous-apis"; #endif #if defined(OS_POSIX) diff --git a/content/public/common/content_switches.h b/content/public/common/content_switches.h index da345c7..d976a83 100644 --- a/content/public/common/content_switches.h +++ b/content/public/common/content_switches.h @@ -212,6 +212,7 @@ extern const char kGraphicsMode[]; // Not actual flags, just values: for example, --graphics-mode=compositor extern const char kGraphicsModeValueBasic[]; extern const char kGraphicsModeValueCompositor[]; +extern const char kEnableWebViewSynchronousAPIs[]; #endif #if defined(OS_POSIX) diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc index 33c5b12..181b273 100644 --- a/content/renderer/render_view_impl.cc +++ b/content/renderer/render_view_impl.cc @@ -618,6 +618,7 @@ RenderViewImpl::RenderViewImpl( #if defined(OS_ANDROID) expected_content_intent_id_(0), media_player_proxy_(NULL), + synchronous_find_active_match_ordinal_(-1), #endif session_storage_namespace_id_(session_storage_namespace_id), handling_select_range_(false), @@ -945,9 +946,6 @@ bool RenderViewImpl::OnMessageReceived(const IPC::Message& message) { IPC_MESSAGE_HANDLER(ViewMsg_Redo, OnRedo) IPC_MESSAGE_HANDLER(ViewMsg_Cut, OnCut) IPC_MESSAGE_HANDLER(ViewMsg_Copy, OnCopy) -#if defined(OS_MACOSX) - IPC_MESSAGE_HANDLER(ViewMsg_CopyToFindPboard, OnCopyToFindPboard) -#endif IPC_MESSAGE_HANDLER(ViewMsg_Paste, OnPaste) IPC_MESSAGE_HANDLER(ViewMsg_PasteAndMatchStyle, OnPasteAndMatchStyle) IPC_MESSAGE_HANDLER(ViewMsg_Replace, OnReplace) @@ -966,12 +964,6 @@ bool RenderViewImpl::OnMessageReceived(const IPC::Message& message) { IPC_MESSAGE_HANDLER(ViewMsg_ExecuteEditCommand, OnExecuteEditCommand) IPC_MESSAGE_HANDLER(ViewMsg_Find, OnFind) IPC_MESSAGE_HANDLER(ViewMsg_StopFinding, OnStopFinding) -#if defined(OS_ANDROID) - IPC_MESSAGE_HANDLER(ViewMsg_ActivateNearestFindResult, - OnActivateNearestFindResult) - IPC_MESSAGE_HANDLER(ViewMsg_FindMatchRects, - OnFindMatchRects) -#endif IPC_MESSAGE_HANDLER(ViewMsg_Zoom, OnZoom) IPC_MESSAGE_HANDLER(ViewMsg_SetZoomLevel, OnSetZoomLevel) IPC_MESSAGE_HANDLER(ViewMsg_ZoomFactor, OnZoomFactor) @@ -1024,12 +1016,6 @@ bool RenderViewImpl::OnMessageReceived(const IPC::Message& message) { IPC_MESSAGE_HANDLER(ViewMsg_SetActive, OnSetActive) IPC_MESSAGE_HANDLER(ViewMsg_SetNavigationStartTime, OnSetNavigationStartTime) -#if defined(OS_MACOSX) - IPC_MESSAGE_HANDLER(ViewMsg_SetWindowVisibility, OnSetWindowVisibility) - IPC_MESSAGE_HANDLER(ViewMsg_WindowFrameChanged, OnWindowFrameChanged) - IPC_MESSAGE_HANDLER(ViewMsg_PluginImeCompositionCompleted, - OnPluginImeCompositionCompleted) -#endif IPC_MESSAGE_HANDLER(ViewMsg_SetEditCommandsForNextKeyEvent, OnSetEditCommandsForNextKeyEvent) IPC_MESSAGE_HANDLER(ViewMsg_CustomContextMenuAction, @@ -1044,22 +1030,29 @@ bool RenderViewImpl::OnMessageReceived(const IPC::Message& message) { IPC_MESSAGE_HANDLER( ViewMsg_GetSerializedHtmlDataForCurrentPageWithLocalLinks, OnGetSerializedHtmlDataForCurrentPageWithLocalLinks) -#if defined(OS_MACOSX) - IPC_MESSAGE_HANDLER(ViewMsg_SelectPopupMenuItem, OnSelectPopupMenuItem) -#elif defined(OS_ANDROID) - IPC_MESSAGE_HANDLER(ViewMsg_SelectPopupMenuItems, OnSelectPopupMenuItems) -#endif IPC_MESSAGE_HANDLER(ViewMsg_ContextMenuClosed, OnContextMenuClosed) // TODO(viettrungluu): Move to a separate message filter. -#if defined(OS_MACOSX) - IPC_MESSAGE_HANDLER(ViewMsg_SetInLiveResize, OnSetInLiveResize) -#endif IPC_MESSAGE_HANDLER(ViewMsg_SetHistoryLengthAndPrune, OnSetHistoryLengthAndPrune) IPC_MESSAGE_HANDLER(ViewMsg_EnableViewSourceMode, OnEnableViewSourceMode) IPC_MESSAGE_HANDLER(JavaBridgeMsg_Init, OnJavaBridgeInit) IPC_MESSAGE_HANDLER(ViewMsg_SetAccessibilityMode, OnSetAccessibilityMode) IPC_MESSAGE_HANDLER(ViewMsg_UpdateFrameTree, OnUpdatedFrameTree) +#if defined(OS_ANDROID) + IPC_MESSAGE_HANDLER(ViewMsg_ActivateNearestFindResult, + OnActivateNearestFindResult) + IPC_MESSAGE_HANDLER(ViewMsg_FindMatchRects, OnFindMatchRects) + IPC_MESSAGE_HANDLER(ViewMsg_SelectPopupMenuItems, OnSelectPopupMenuItems) + IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewMsg_SynchronousFind, OnSynchronousFind) +#elif defined(OS_MACOSX) + IPC_MESSAGE_HANDLER(ViewMsg_CopyToFindPboard, OnCopyToFindPboard) + IPC_MESSAGE_HANDLER(ViewMsg_PluginImeCompositionCompleted, + OnPluginImeCompositionCompleted) + IPC_MESSAGE_HANDLER(ViewMsg_SelectPopupMenuItem, OnSelectPopupMenuItem) + IPC_MESSAGE_HANDLER(ViewMsg_SetInLiveResize, OnSetInLiveResize) + IPC_MESSAGE_HANDLER(ViewMsg_SetWindowVisibility, OnSetWindowVisibility) + IPC_MESSAGE_HANDLER(ViewMsg_WindowFrameChanged, OnWindowFrameChanged) +#endif // Have the super handle all other messages. IPC_MESSAGE_UNHANDLED(handled = RenderWidget::OnMessageReceived(message)) @@ -3926,30 +3919,64 @@ void RenderViewImpl::hasTouchEventHandlers(bool has_handlers) { Send(new ViewHostMsg_HasTouchEventHandlers(routing_id_, has_handlers)); } -void RenderViewImpl::reportFindInPageMatchCount(int request_id, int count, +void RenderViewImpl::SendFindReply(int request_id, + int match_count, + int ordinal, + const WebRect& selection_rect, + bool final_status_update) { +#if defined(OS_ANDROID) + if (synchronous_find_reply_message_.get()) { + if (final_status_update) { + ViewMsg_SynchronousFind::WriteReplyParams( + synchronous_find_reply_message_.get(), + match_count, + match_count ? synchronous_find_active_match_ordinal_ : 0); + Send(synchronous_find_reply_message_.release()); + } + return; + } +#endif + + Send(new ViewHostMsg_Find_Reply(routing_id_, + request_id, + match_count, + selection_rect, + ordinal, + final_status_update)); +} + +void RenderViewImpl::reportFindInPageMatchCount(int request_id, + int count, bool final_update) { int active_match_ordinal = -1; // -1 = don't update active match ordinal if (!count) active_match_ordinal = 0; - Send(new ViewHostMsg_Find_Reply(routing_id_, - request_id, - count, - gfx::Rect(), - active_match_ordinal, - final_update)); + // Send the search result over to the browser process. + SendFindReply(request_id, + count, + active_match_ordinal, + gfx::Rect(), + final_update); } void RenderViewImpl::reportFindInPageSelection(int request_id, int active_match_ordinal, const WebRect& selection_rect) { - // Send the search result over to the browser process. - Send(new ViewHostMsg_Find_Reply(routing_id_, - request_id, - -1, - selection_rect, - active_match_ordinal, - false)); +#if defined(OS_ANDROID) + // If this was a SynchronousFind request, we need to remember the ordinal + // value here for replying when reportFindInPageMatchCount is called. + if (synchronous_find_reply_message_.get()) { + synchronous_find_active_match_ordinal_ = active_match_ordinal; + return; + } +#endif + + SendFindReply(request_id, + -1, + active_match_ordinal, + selection_rect, + false); } void RenderViewImpl::openFileSystem( @@ -4557,8 +4584,22 @@ WebKit::WebPlugin* RenderViewImpl::GetWebPluginFromPluginDocument() { return webview()->mainFrame()->document().to<WebPluginDocument>().plugin(); } -void RenderViewImpl::OnFind(int request_id, const string16& search_text, +void RenderViewImpl::OnFind(int request_id, + const string16& search_text, const WebFindOptions& options) { +#if defined(OS_ANDROID) + // Make sure any asynchronous messages do not disrupt an ongoing synchronous + // find request as it might lead to deadlocks. Also, these should be safe to + // ignore since they would belong to a previous find request. + if (synchronous_find_reply_message_.get()) + return; +#endif + Find(request_id, search_text, options); +} + +void RenderViewImpl::Find(int request_id, + const string16& search_text, + const WebFindOptions& options) { WebFrame* main_frame = webview()->mainFrame(); // Check if the plugin still exists in the document. @@ -4568,16 +4609,10 @@ void RenderViewImpl::OnFind(int request_id, const string16& search_text, // Just navigate back/forward. GetWebPluginFromPluginDocument()->selectFindResult(options.forward); } else { - if (GetWebPluginFromPluginDocument()->startFind( + if (!GetWebPluginFromPluginDocument()->startFind( search_text, options.matchCase, request_id)) { - } else { // Send "no results". - Send(new ViewHostMsg_Find_Reply(routing_id_, - request_id, - 0, - gfx::Rect(), - 0, - true)); + SendFindReply(request_id, 0, 0, gfx::Rect(), true); } } return; @@ -4650,13 +4685,8 @@ void RenderViewImpl::OnFind(int request_id, const string16& search_text, // Otherwise the scoping effort will send more results. bool final_status_update = !result; - // Send the search result over to the browser process. - Send(new ViewHostMsg_Find_Reply(routing_id_, - request_id, - match_count, - selection_rect, - ordinal, - final_status_update)); + SendFindReply(request_id, match_count, ordinal, selection_rect, + final_status_update); // Scoping effort begins, starting with the mainframe. search_frame = main_frame; @@ -4686,6 +4716,18 @@ void RenderViewImpl::OnFind(int request_id, const string16& search_text, } void RenderViewImpl::OnStopFinding(content::StopFindAction action) { +#if defined(OS_ANDROID) + // Make sure any asynchronous messages do not disrupt an ongoing synchronous + // find request as it might lead to deadlocks. Also, these should be safe to + // ignore since they would belong to a previous find request. + if (synchronous_find_reply_message_.get()) + return; +#endif + + StopFinding(action); +} + +void RenderViewImpl::StopFinding(content::StopFindAction action) { WebView* view = webview(); if (!view) return; @@ -4720,6 +4762,23 @@ void RenderViewImpl::OnStopFinding(content::StopFindAction action) { } #if defined(OS_ANDROID) +void RenderViewImpl::OnSynchronousFind(int request_id, + const string16& search_string, + const WebFindOptions& options, + IPC::Message* reply_msg) { + // It is impossible for simultaneous blocking finds to occur. + CHECK(!synchronous_find_reply_message_.get()); + synchronous_find_reply_message_.reset(reply_msg); + + // Find next should be asynchronous in order to minimize blocking + // the UI thread as much as possible. + DCHECK(!options.findNext); + StopFinding(content::STOP_FIND_ACTION_KEEP_SELECTION); + synchronous_find_active_match_ordinal_ = -1; + + Find(request_id, search_string, options); +} + void RenderViewImpl::OnActivateNearestFindResult(int request_id, float x, float y) { if (!webview()) @@ -4737,12 +4796,11 @@ void RenderViewImpl::OnActivateNearestFindResult(int request_id, return; } - Send(new ViewHostMsg_Find_Reply(routing_id_, - request_id, - -1 /* number_of_matches */, - selection_rect, - ordinal, - true /* final_update */)); + SendFindReply(request_id, + -1 /* number_of_matches */, + ordinal, + selection_rect, + true /* final_update */); } void RenderViewImpl::OnFindMatchRects(int current_version) { diff --git a/content/renderer/render_view_impl.h b/content/renderer/render_view_impl.h index 3cabb8f..c9df7b7 100644 --- a/content/renderer/render_view_impl.h +++ b/content/renderer/render_view_impl.h @@ -904,9 +904,6 @@ class RenderViewImpl : public RenderWidget, // The documentation for these functions should be in // render_messages_internal.h for the message that the function is handling. -#if defined(OS_ANDROID) - void OnActivateNearestFindResult(int request_id, float x, float y); -#endif CONTENT_EXPORT void OnAllowBindings(int enabled_bindings_flags); void OnAllowScriptToClose(bool script_can_close); void OnAsyncFileOpened(base::PlatformFileError error_code, @@ -922,9 +919,6 @@ class RenderViewImpl : public RenderWidget, const content::CustomContextMenuContext& custom_context); void OnCopy(); void OnCopyImageAt(int x, int y); -#if defined(OS_MACOSX) - void OnCopyToFindPboard(); -#endif void OnCut(); void OnCSSInsertRequest(const string16& frame_xpath, const std::string& css); @@ -964,9 +958,6 @@ class RenderViewImpl : public RenderWidget, const std::vector<ui::SelectedFileInfo>& files); void OnFind(int request_id, const string16&, const WebKit::WebFindOptions&); void OnGetAllSavableResourceLinksForCurrentPage(const GURL& page_url); -#if defined(OS_ANDROID) - void OnFindMatchRects(int current_version); -#endif void OnGetSerializedHtmlDataForCurrentPageWithLocalLinks( const std::vector<GURL>& links, const std::vector<FilePath>& local_paths, @@ -983,9 +974,6 @@ class RenderViewImpl : public RenderWidget, CONTENT_EXPORT void OnNavigate(const ViewMsg_Navigate_Params& params); void OnPaste(); void OnPasteAndMatchStyle(); -#if defined(OS_MACOSX) - void OnPluginImeCompositionCompleted(const string16& text, int plugin_id); -#endif void OnPostMessageEvent(const ViewMsg_PostMessage_Params& params); void OnRedo(); void OnReloadFrame(); @@ -1014,15 +1002,9 @@ class RenderViewImpl : public RenderWidget, CONTENT_EXPORT void OnSetHistoryLengthAndPrune(int history_length, int32 minimum_page_id); void OnSetInitialFocus(bool reverse); -#if defined(OS_MACOSX) - void OnSetInLiveResize(bool in_live_resize); -#endif void OnScrollFocusedEditableNodeIntoRect(const gfx::Rect& rect); void OnSetPageEncoding(const std::string& encoding_name); void OnSetRendererPrefs(const content::RendererPreferences& renderer_prefs); -#if defined(OS_MACOSX) - void OnSetWindowVisibility(bool visible); -#endif void OnSetZoomLevel(double zoom_level); CONTENT_EXPORT void OnSetZoomLevelForLoadingURL(const GURL& url, double zoom_level); @@ -1038,17 +1020,6 @@ class RenderViewImpl : public RenderWidget, const webkit_glue::WebPreferences& prefs); CONTENT_EXPORT void OnUnselect(); -#if defined(OS_MACOSX) - void OnWindowFrameChanged(const gfx::Rect& window_frame, - const gfx::Rect& view_frame); - CONTENT_EXPORT void OnSelectPopupMenuItem(int selected_index); -#endif - -#if defined(OS_ANDROID) - void OnSelectPopupMenuItems(bool canceled, - const std::vector<int>& selected_indices); -#endif - void OnZoom(content::PageZoom zoom); void OnZoomFactor(content::PageZoom zoom, int zoom_center_x, int zoom_center_y); @@ -1061,6 +1032,26 @@ class RenderViewImpl : public RenderWidget, int route_id, const std::string& frame_tree); +#if defined(OS_ANDROID) + void OnActivateNearestFindResult(int request_id, float x, float y); + void OnFindMatchRects(int current_version); + void OnSelectPopupMenuItems(bool canceled, + const std::vector<int>& selected_indices); + void OnSynchronousFind(int request_id, + const string16& search_string, + const WebKit::WebFindOptions& options, + IPC::Message* reply_msg); +#elif defined(OS_MACOSX) + void OnCopyToFindPboard(); + void OnPluginImeCompositionCompleted(const string16& text, int plugin_id); + CONTENT_EXPORT void OnSelectPopupMenuItem(int selected_index); + void OnSetInLiveResize(bool in_live_resize); + void OnSetWindowVisibility(bool visible); + void OnWindowFrameChanged(const gfx::Rect& window_frame, + const gfx::Rect& view_frame); +#endif + + // Adding a new message handler? Please add it in alphabetical order above // and put it in the same position in the .cc file. @@ -1115,6 +1106,11 @@ class RenderViewImpl : public RenderWidget, // doesn't have a frame at the specified size, the first is returned. bool DownloadFavicon(int id, const GURL& image_url, int image_size); + // Starts a new find-in-page search or looks for the next match. + void Find(int request_id, + const string16& search_text, + const WebKit::WebFindOptions& options); + GURL GetAlternateErrorPageURL(const GURL& failed_url, ErrorPageType error_type); @@ -1161,9 +1157,20 @@ class RenderViewImpl : public RenderWidget, // --enable-fixed-layout[=w,h]. void ProcessViewLayoutFlags(const CommandLine& command_line); + // Sends a reply to the current find operation handling if it was a + // synchronous find request. + void SendFindReply(int request_id, + int match_count, + int ordinal, + const WebKit::WebRect& selection_rect, + bool final_status_update); + // Starts nav_state_sync_timer_ if it isn't already running. void StartNavStateSyncTimerIfNecessary(); + // Stops the current find-in-page search. + void StopFinding(content::StopFindAction action); + // Dispatches the current navigation state to the browser. Called on a // periodic timer so we don't send too many messages. void SyncNavigationState(); @@ -1422,6 +1429,14 @@ class RenderViewImpl : public RenderWidget, // Resource manager for all the android media player objects if they are // created in the renderer process. scoped_ptr<webkit_media::MediaPlayerBridgeManagerImpl> media_bridge_manager_; + + // Holds the message used to return find results to the browser during + // synchronous find-in-page requests. Only non-null during these requests. + scoped_ptr<IPC::Message> synchronous_find_reply_message_; + + // The active find-in-page match ordinal during synchronous requests. + // Needed to be remembered across WebKit callbacks. + int synchronous_find_active_match_ordinal_; #endif // Misc ---------------------------------------------------------------------- |