Skip to content

Commit

Permalink
Implement hit test related methods in Android WebView
Browse files Browse the repository at this point in the history
The public Android WebView APIs are WebView.getHitTestResult,
WebView.requestFocusNodeHref, and WebView.requestImageRef.

These APIs are mostly used for creating context menus but have
become broken APIs as the Android evolved from the single
threaded initial version of WebView.

* The names are misleading since the value retrieved is based both on
  a combination of touch events and FocusedNodeChanged callback
  from WebKit.
* The methods are synchronous and there are inherently racy. This is
  the case with with the latest version of Android WebView as well.
  However this may become more of a problem as Chromium is
  multi-processed.
* The methods are inefficient since work needs to be done even if the
  client application never call these methods.

This is the first part of largely replicating the broken code with tests
and extensive comments. The goal is to replicate existing logic but
there are probably corner cases that are not matched.

Overall flow is on a touch event or focused node changed event, perform
a WebKit hit test, get the relevant result data and save it in the browser.
The return what is saved when apps query for the data.

Work still needs to be done after this:
* Implement updating hit test data after tab-ing to a link
* Hook up content detection code in content/
* Implement focus highlighting with DPAD controls.

BUG=

All bots are green except unrelated instrumentation tests.
AndroidWebView instrumentation tests are green.
NOTRY=true

Review URL: https://chromiumcodereview.appspot.com/11360037

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@166712 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
boliu@chromium.org committed Nov 8, 2012
1 parent 72bd37e commit a7198a1
Show file tree
Hide file tree
Showing 14 changed files with 603 additions and 3 deletions.
2 changes: 2 additions & 0 deletions android_webview/android_webview.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@
'common/android_webview_message_generator.h',
'common/aw_content_client.cc',
'common/aw_content_client.h',
'common/aw_hit_test_data.cc',
'common/aw_hit_test_data.h',
'common/aw_resource.h',
'common/render_view_messages.cc',
'common/render_view_messages.h',
Expand Down
21 changes: 21 additions & 0 deletions android_webview/browser/renderer_host/aw_render_view_host_ext.cc
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,22 @@ void AwRenderViewHostExt::DocumentHasImages(DocumentHasImagesResult result) {
}

void AwRenderViewHostExt::ClearCache() {
DCHECK(CalledOnValidThread());
Send(new AwViewMsg_ClearCache);
}

void AwRenderViewHostExt::RequestNewHitTestDataAt(int view_x, int view_y) {
DCHECK(CalledOnValidThread());
Send(new AwViewMsg_DoHitTest(web_contents()->GetRoutingID(),
view_x,
view_y));
}

const AwHitTestData& AwRenderViewHostExt::GetLastHitTestData() const {
DCHECK(CalledOnValidThread());
return last_hit_test_data_;
}

void AwRenderViewHostExt::RenderViewGone(base::TerminationStatus status) {
DCHECK(CalledOnValidThread());
for (std::map<int, DocumentHasImagesResult>::iterator pending_req =
Expand All @@ -51,6 +64,8 @@ bool AwRenderViewHostExt::OnMessageReceived(const IPC::Message& message) {
IPC_BEGIN_MESSAGE_MAP(AwRenderViewHostExt, message)
IPC_MESSAGE_HANDLER(AwViewHostMsg_DocumentHasImagesResponse,
OnDocumentHasImagesResponse)
IPC_MESSAGE_HANDLER(AwViewHostMsg_UpdateHitTestData,
OnUpdateHitTestData)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()

Expand All @@ -70,4 +85,10 @@ void AwRenderViewHostExt::OnDocumentHasImagesResponse(int msg_id,
}
}

void AwRenderViewHostExt::OnUpdateHitTestData(
const AwHitTestData& hit_test_data) {
DCHECK(CalledOnValidThread());
last_hit_test_data_ = hit_test_data;
}

} // namespace android_webview
16 changes: 16 additions & 0 deletions android_webview/browser/renderer_host/aw_render_view_host_ext.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include "content/public/browser/web_contents_observer.h"

#include "android_webview/common/aw_hit_test_data.h"
#include "base/threading/non_thread_safe.h"

namespace android_webview {
Expand All @@ -28,15 +29,30 @@ class AwRenderViewHostExt : public content::WebContentsObserver,
// Clear all WebCore memory cache (not only for this view).
void ClearCache();

// Do a hit test at the view port coordinates and asynchronously update
// |last_hit_test_data_|.
void RequestNewHitTestDataAt(int view_x, int view_y);

// Return |last_hit_test_data_|. Note that this is unavoidably racy;
// the corresponding public WebView API is as well.
const AwHitTestData& GetLastHitTestData() const;

private:
// content::WebContentsObserver implementation.
virtual void RenderViewGone(base::TerminationStatus status) OVERRIDE;
virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;

void OnDocumentHasImagesResponse(int msg_id, bool has_images);
void OnUpdateHitTestData(
const AwHitTestData& hit_test_data);

std::map<int, DocumentHasImagesResult> pending_document_has_images_requests_;

// Master copy of hit test data on the browser side. This is updated
// as a result of DoHitTest called explicitly or when the FocusedNodeChanged
// is called in AwRenderViewExt.
AwHitTestData last_hit_test_data_;

DISALLOW_COPY_AND_ASSIGN(AwRenderViewHostExt);
};

Expand Down
13 changes: 13 additions & 0 deletions android_webview/common/aw_hit_test_data.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// 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/common/aw_hit_test_data.h"

namespace android_webview {

AwHitTestData::AwHitTestData() : type(UNKNOWN_TYPE) {}

AwHitTestData::~AwHitTestData() {}

} // namespace android_webview
76 changes: 76 additions & 0 deletions android_webview/common/aw_hit_test_data.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// 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_COMMON_AW_HIT_TEST_DATA_H_
#define ANDROID_WEBVIEW_COMMON_AW_HIT_TEST_DATA_H_

#include "base/string16.h"
#include "googleurl/src/gurl.h"

namespace android_webview {

// Holdes all hit test data needed by public WebView APIs.
// The Java counter part to this is AwContents.HitTestData.
struct AwHitTestData {

// Matches exactly with constants in WebView.HitTestResult, with deprecated
// values removed.
enum Type {
// Default type where nothing we are interested in is hit.
// |extra_data_for_type| will be empty. All other values should be emtpy
// except the special case described below.
// For special case of invalid or javascript scheme url that would
// otherwise be type an LINK type, |href|, |anchor_text|, |img_src| contain
// their normal values for the respective type.
UNKNOWN_TYPE = 0,

// Content detection types. Not used yet.
// TODO(boliu): Hook up content detection.
PHONE_TYPE = 2,
GEO_TYPE = 3,
EMAIL_TYPE = 4,

// Hit on a pure image (without links). |extra_data_for_type|, |href|,
// and |anchor_text| will be empty. |img_src| will contain the absolute
// source url of the image.
IMAGE_TYPE = 5,

// Hit on a link with valid and non-javascript url and without embedded
// image. |extra_data_for_type| is the valid absolute url of the link.
// |href| will contain the exact href attribute string. |anchor_text| will
// contain the anchor text if the link is an anchor tag. |img_src| will be
// empty.
// Note 1: If the link url is invalid or javascript scheme, then the type
// will be UNKNOWN_TYPE.
// Note 2: Note that this matches SRC_ANCHOR_TYPE in the public WebView
// Java API, but the actual tag can be something other than <a>, such as
// <link> or <area>.
SRC_LINK_TYPE = 7,

// Same as SRC_LINK_TYPE except the link contains an image. |img_src| and
// |extra_data_for_type| will contain the absolute valid url of the image
// source. |href| will contain the (possibly invalid or javascript-scheme)
// link href attribute. |anchor_text| will be empty.
// Both notes from SRC_LINK_TYPE apply.
SRC_IMAGE_LINK_TYPE = 8,

// Hit on an editable text input element. All other values will be empty.
EDIT_TEXT_TYPE = 9,
};

// For all strings/GURLs, empty/invalid will become null upon conversion to
// Java.
int type; // Only values from enum Type above.
std::string extra_data_for_type;
string16 href;
string16 anchor_text;
GURL img_src;

AwHitTestData();
~AwHitTestData();
};

} // namespace android_webview

#endif // ANDROID_WEBVIEW_COMMON_AW_HIT_TEST_DATA_H_
23 changes: 21 additions & 2 deletions android_webview/common/render_view_messages.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
// found in the LICENSE file.

// Multiply-included file, no traditional include guard.
#include "android_webview/common/aw_hit_test_data.h"
#include "content/public/common/common_param_traits.h"
#include "ipc/ipc_channel_handle.h"
#include "ipc/ipc_message_macros.h"
#include "ipc/ipc_platform_file.h"
Expand All @@ -11,7 +13,6 @@
#ifndef ANDROID_WEBVIEW_COMMON_RENDER_VIEW_MESSAGES_H_
#define ANDROID_WEBVIEW_COMMON_RENDER_VIEW_MESSAGES_H_


namespace IPC {

// TODO - add enums and custom IPC traits here when needed.
Expand All @@ -20,21 +21,36 @@ namespace IPC {

#endif // ANDROID_WEBVIEW_COMMON_RENDER_VIEW_MESSAGES_H_

IPC_STRUCT_TRAITS_BEGIN(android_webview::AwHitTestData)
IPC_STRUCT_TRAITS_MEMBER(type)
IPC_STRUCT_TRAITS_MEMBER(extra_data_for_type)
IPC_STRUCT_TRAITS_MEMBER(href)
IPC_STRUCT_TRAITS_MEMBER(anchor_text)
IPC_STRUCT_TRAITS_MEMBER(img_src)
IPC_STRUCT_TRAITS_END()

#define IPC_MESSAGE_START AndroidWebViewMsgStart

//-----------------------------------------------------------------------------
// RenderView messages
// These are messages sent from the browser to the renderer process.

// Tells the renderer to drop all WebCore memory cache.
IPC_MESSAGE_CONTROL0(AwViewMsg_ClearCache);
IPC_MESSAGE_CONTROL0(AwViewMsg_ClearCache)

// Request for the renderer to determine if the document contains any image
// elements. The id should be passed in the response message so the response
// can be associated with the request.
IPC_MESSAGE_ROUTED1(AwViewMsg_DocumentHasImages,
int /* id */)

// Do hit test at the given webview coordinate. "Webview" coordinates are
// physical pixel values with the 0,0 at the top left of the current displayed
// view (ie 0,0 is not the top left of the page if the page is scrolled).
IPC_MESSAGE_ROUTED2(AwViewMsg_DoHitTest,
int /* view_x */,
int /* view_y */)

//-----------------------------------------------------------------------------
// RenderView messages
// These are messages sent from the renderer to the browser process.
Expand All @@ -44,3 +60,6 @@ IPC_MESSAGE_ROUTED2(AwViewHostMsg_DocumentHasImagesResponse,
int, /* id */
bool /* has_images */)

// Response to AwViewMsg_DoHitTest.
IPC_MESSAGE_ROUTED1(AwViewHostMsg_UpdateHitTestData,
android_webview::AwHitTestData)
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@
import android.graphics.Canvas;
import android.net.http.SslCertificate;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ViewGroup;
import android.webkit.ValueCallback;

Expand Down Expand Up @@ -48,6 +50,35 @@ public class AwContents {

private static final String WEB_ARCHIVE_EXTENSION = ".mht";

/**
* WebKit hit test related data strcutre. These are used to implement
* getHitTestResult, requestFocusNodeHref, requestImageRef methods in WebView.
* All values should be updated together. The native counterpart is
* AwHitTestData.
*/
public static class HitTestData {
// Used in getHitTestResult.
public final int hitTestResultType;
public final String hitTestResultExtraData;

// Used in requestFocusNodeHref (all three) and requestImageRef (only imgSrc).
public final String href;
public final String anchorText;
public final String imgSrc;

private HitTestData(int type,
String extra,
String href,
String anchorText,
String imgSrc) {
this.hitTestResultType = type;
this.hitTestResultExtraData = extra;
this.href = href;
this.anchorText = anchorText;
this.imgSrc = imgSrc;
}
}

private int mNativeAwContents;
private ContentViewCore mContentViewCore;
private AwContentsClient mContentsClient;
Expand Down Expand Up @@ -366,6 +397,60 @@ public SslCertificate getCertificate() {
return null;
}

/**
* This should be called from the onTouchEvent of the view.
*/
public void considerMotionEventForHitTest(MotionEvent event) {
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
int actionIndex = event.getActionIndex();

// Note this will trigger IPC back to browser even if nothing is hit.
nativeRequestNewHitTestDataAt(mNativeAwContents,
Math.round(event.getX(actionIndex)),
Math.round(event.getY(actionIndex)));
}
}

/**
* Method to return all hit test values relevant to public WebView API.
* Note that this expose more data than needed for WebView.getHitTestResult.
*/
public HitTestData getLastHitTestResult() {
return nativeGetLastHitTestData(mNativeAwContents);
}

/**
* @see android.webkit.WebView#requestFocusNodeHref()
*/
public void requestFocusNodeHref(Message msg) {
if (msg == null) {
return;
}

HitTestData hitTestData = nativeGetLastHitTestData(mNativeAwContents);
Bundle data = msg.getData();
data.putString("url", hitTestData.href);
data.putString("title", hitTestData.anchorText);
data.putString("src", hitTestData.imgSrc);
msg.setData(data);
msg.sendToTarget();
}

/**
* @see android.webkit.WebView#requestImageRef()
*/
public void requestImageRef(Message msg) {
if (msg == null) {
return;
}

HitTestData hitTestData = nativeGetLastHitTestData(mNativeAwContents);
Bundle data = msg.getData();
data.putString("url", hitTestData.imgSrc);
msg.setData(data);
msg.sendToTarget();
}

//--------------------------------------------------------------------------------------------
// Methods called from native via JNI
//--------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -395,6 +480,12 @@ public void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches,
mContentsClient.onFindResultReceived(activeMatchOrdinal, numberOfMatches, isDoneCounting);
}

@CalledByNative
private static HitTestData createHitTestData(
int type, String extra, String href, String anchorText, String imgSrc) {
return new HitTestData(type, extra, href, anchorText, imgSrc);
}

// -------------------------------------------------------------------------------------------
// Helper methods
// -------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -493,4 +584,6 @@ private native void nativeSetInterceptNavigationDelegate(int nativeAwContents,
private native void nativeClearMatches(int nativeAwContents);
private native void nativeClearCache(int nativeAwContents, boolean includeDiskFiles);
private native byte[] nativeGetCertificate(int nativeAwContents);
private native void nativeRequestNewHitTestDataAt(int nativeAwContents, int x, int y);
private native HitTestData nativeGetLastHitTestData(int nativeAwContents);
}
Loading

0 comments on commit a7198a1

Please sign in to comment.