From 55750b34b9db6b705e2cdcdb72df223716fd4aa7 Mon Sep 17 00:00:00 2001 From: "leandrogracia@chromium.org" Date: Fri, 21 Sep 2012 14:05:03 +0000 Subject: [Android] Upstream the WebView find-in-page API implementation. This API requires implementing a browser -> renderer synchronous findAll message. While this method is deprecated and an alternative asynchronous version is suggested we still need to implement it for legacy WebView compatibility. BUG=136762 TEST=3 new java tests. Review URL: https://codereview.chromium.org/10941015 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@157975 0039d316-1c4b-4281-b951-d872f2087c98 --- android_webview/browser/DEPS | 3 + android_webview/browser/find_helper.cc | 173 +++++++++++++++++++++ android_webview/browser/find_helper.h | 77 +++++++++ .../scoped_allow_wait_for_legacy_web_view_api.h | 20 +++ 4 files changed, 273 insertions(+) create mode 100644 android_webview/browser/find_helper.cc create mode 100644 android_webview/browser/find_helper.h create mode 100644 android_webview/browser/scoped_allow_wait_for_legacy_web_view_api.h (limited to 'android_webview/browser') 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 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 -- cgit v1.1