From 14be0ec9a67949d5d05e778fefe8d944a74dd486 Mon Sep 17 00:00:00 2001 From: John Reck Date: Fri, 13 Jan 2012 13:31:40 -0800 Subject: Refactor hit test object Change-Id: I9729ca695fda4eefd3aa57d96a872010a3cfba9a --- Source/WebKit/Android.mk | 1 + Source/WebKit/android/jni/AndroidHitTestResult.cpp | 154 +++++++++ Source/WebKit/android/jni/AndroidHitTestResult.h | 55 ++++ Source/WebKit/android/jni/WebViewCore.cpp | 351 +++++++++------------ Source/WebKit/android/jni/WebViewCore.h | 3 + 5 files changed, 361 insertions(+), 203 deletions(-) create mode 100644 Source/WebKit/android/jni/AndroidHitTestResult.cpp create mode 100644 Source/WebKit/android/jni/AndroidHitTestResult.h (limited to 'Source') diff --git a/Source/WebKit/Android.mk b/Source/WebKit/Android.mk index fb6ee31..c715609 100644 --- a/Source/WebKit/Android.mk +++ b/Source/WebKit/Android.mk @@ -60,6 +60,7 @@ LOCAL_SRC_FILES += \ \ android/icu/unicode/ucnv.cpp \ \ + android/jni/AndroidHitTestResult.cpp \ android/jni/CacheManager.cpp \ android/jni/CookieManager.cpp \ android/jni/DeviceMotionAndOrientationManager.cpp \ diff --git a/Source/WebKit/android/jni/AndroidHitTestResult.cpp b/Source/WebKit/android/jni/AndroidHitTestResult.cpp new file mode 100644 index 0000000..fed8a67 --- /dev/null +++ b/Source/WebKit/android/jni/AndroidHitTestResult.cpp @@ -0,0 +1,154 @@ +/* + * Copyright 2012, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_TAG "AndroidHitTestResult" + +#include "config.h" +#include "AndroidHitTestResult.h" + +#include "Element.h" +#include "HitTestResult.h" +#include "KURL.h" +#include "PlatformString.h" +#include "WebCoreJni.h" + +#include +#include + +namespace android { + +using namespace WebCore; + +static bool gJniInitialized = false; +static struct JavaGlue { + jmethodID m_rectInit; + + jmethodID m_hitTestInit; + jfieldID m_hitTestLinkUrl; + jfieldID m_hitTestAnchorText; + jfieldID m_hitTestImageUrl; + jfieldID m_hitTestAltDisplayString; + jfieldID m_hitTestTitle; + jfieldID m_hitTestEditable; + jfieldID m_hitTestTouchRects; +} gJavaGlue; + +struct field { + jclass m_class; + const char *m_fieldName; + const char *m_fieldType; + jfieldID *m_jfield; +}; + +static void InitJni(JNIEnv* env) +{ + if (gJniInitialized) + return; + + jclass rectClass = env->FindClass("android/graphics/Rect"); + ALOG_ASSERT(rectClass, "Could not find android/graphics/Rect"); + jclass hitTestClass = env->FindClass("android/webkit/WebViewCore$WebKitHitTest"); + ALOG_ASSERT(hitTestClass, "Could not find android/webkit/WebViewCore$WebKitHitTest"); + + gJavaGlue.m_rectInit = env->GetMethodID(rectClass, "", "(IIII)V"); + ALOG_ASSERT(gJavaGlue.m_rectInit, "Could not find init method on Rect"); + + gJavaGlue.m_hitTestInit = env->GetMethodID(hitTestClass, "", "()V"); + ALOG_ASSERT(gJavaGlue.m_hitTestInit, "Could not find init method on android/webkit/WebViewCore$WebKitHitTest"); + + field fields[] = { + { hitTestClass, "mTouchRects", "[Landroid/graphics/Rect;", &gJavaGlue.m_hitTestTouchRects }, + { hitTestClass, "mEditable", "Z", &gJavaGlue.m_hitTestEditable }, + { hitTestClass, "mLinkUrl", "Ljava/lang/String;", &gJavaGlue.m_hitTestLinkUrl }, + { hitTestClass, "mAnchorText", "Ljava/lang/String;", &gJavaGlue.m_hitTestAnchorText }, + { hitTestClass, "mImageUrl", "Ljava/lang/String;", &gJavaGlue.m_hitTestImageUrl }, + { hitTestClass, "mAltDisplayString", "Ljava/lang/String;", &gJavaGlue.m_hitTestAltDisplayString }, + { hitTestClass, "mTitle", "Ljava/lang/String;", &gJavaGlue.m_hitTestTitle }, + {0, 0, 0, 0}, + }; + + for (int i = 0; fields[i].m_jfield; i++) { + field *f = &fields[i]; + jfieldID field = env->GetFieldID(f->m_class, f->m_fieldName, f->m_fieldType); + ALOG_ASSERT(field, "Can't find %s", f->m_fieldName); + *(f->m_jfield) = field; + } + + gJniInitialized = true; +} + +AndroidHitTestResult::AndroidHitTestResult(WebCore::HitTestResult& hitTestResult) + : m_hitTestResult(hitTestResult) +{ +} + +void setStringField(JNIEnv* env, jobject obj, jfieldID field, const String& str) +{ + jstring jstr = wtfStringToJstring(env, str, false); + env->SetObjectField(obj, field, jstr); + env->DeleteLocalRef(jstr); +} + +// Some helper macros specific to setting hitTest fields +#define _SET(jtype, jfield, value) env->Set ## jtype ## Field(hitTest, gJavaGlue.m_hitTest ## jfield, value) +#define SET_BOOL(jfield, value) _SET(Boolean, jfield, value) +#define SET_STRING(jfield, value) setStringField(env, hitTest, gJavaGlue.m_hitTest ## jfield, value) + +jobject AndroidHitTestResult::createJavaObject(JNIEnv* env) +{ + InitJni(env); + jclass rectClass = env->FindClass("android/graphics/Rect"); + ALOG_ASSERT(rectClass, "Could not find android/graphics/Rect"); + jclass hitTestClass = env->FindClass("android/webkit/WebViewCore$WebKitHitTest"); + ALOG_ASSERT(hitTestClass, "Could not find android/webkit/WebViewCore$WebKitHitTest"); + + jobjectArray array = env->NewObjectArray(m_highlightRects.size(), rectClass, 0); + ALOG_ASSERT(array, "Could not create a Rect array"); + + for (size_t i = 0; i < m_highlightRects.size(); i++) { + jobject rect = env->NewObject(rectClass, gJavaGlue.m_rectInit, + m_highlightRects[i].x(), m_highlightRects[i].y(), + m_highlightRects[i].maxX(), m_highlightRects[i].maxY()); + if (rect) { + env->SetObjectArrayElement(array, i, rect); + env->DeleteLocalRef(rect); + } + } + + TextDirection titleTextDirection; + jobject hitTest = env->NewObject(hitTestClass, gJavaGlue.m_hitTestInit); + env->SetObjectField(hitTest, gJavaGlue.m_hitTestTouchRects, array); + SET_BOOL(Editable, m_hitTestResult.isContentEditable()); + SET_STRING(LinkUrl, m_hitTestResult.absoluteLinkURL().string()); + SET_STRING(ImageUrl, m_hitTestResult.absoluteImageURL().string()); + SET_STRING(AltDisplayString, m_hitTestResult.altDisplayString()); + SET_STRING(Title, m_hitTestResult.title(titleTextDirection)); + if (m_hitTestResult.URLElement()) + SET_STRING(AnchorText, m_hitTestResult.URLElement()->innerText()); + + return hitTest; +} + +} /* namespace android */ diff --git a/Source/WebKit/android/jni/AndroidHitTestResult.h b/Source/WebKit/android/jni/AndroidHitTestResult.h new file mode 100644 index 0000000..28a9ee3 --- /dev/null +++ b/Source/WebKit/android/jni/AndroidHitTestResult.h @@ -0,0 +1,55 @@ +/* + * Copyright 2012, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef AndroidHitTestResult_h +#define AndroidHitTestResult_h + +#include "HitTestResult.h" +#include "IntRect.h" +#include "wtf/Vector.h" + +#include + +namespace android { + +class AndroidHitTestResult +{ +public: + AndroidHitTestResult(WebCore::HitTestResult&); + ~AndroidHitTestResult() {} + + WebCore::HitTestResult& hitTestResult() { return m_hitTestResult; } + Vector& highlightRects() { return m_highlightRects; } + + jobject createJavaObject(JNIEnv*); + +private: + WebCore::HitTestResult m_hitTestResult; + Vector m_highlightRects; +}; + +} // namespace android + +#endif // AndroidHitTestResult_h diff --git a/Source/WebKit/android/jni/WebViewCore.cpp b/Source/WebKit/android/jni/WebViewCore.cpp index 42e71b2..7fb10bf 100644 --- a/Source/WebKit/android/jni/WebViewCore.cpp +++ b/Source/WebKit/android/jni/WebViewCore.cpp @@ -29,6 +29,7 @@ #include "WebViewCore.h" #include "AccessibilityObject.h" +#include "AndroidHitTestResult.h" #include "Attribute.h" #include "BaseLayerAndroid.h" #include "CachedNode.h" @@ -1637,7 +1638,8 @@ void WebViewCore::updateFrameCacheIfLoading() #endif struct TouchNodeData { - Node* mNode; + Node* mUrlNode; + Node* mInnerNode; IntRect mBounds; }; @@ -1661,135 +1663,135 @@ static IntRect getAbsoluteBoundingBox(Node* node) { } // get the highlight rectangles for the touch point (x, y) with the slop -Vector WebViewCore::getTouchHighlightRects(int x, int y, int slop, - Node** node, HitTestResult* hitTestResult) +AndroidHitTestResult WebViewCore::hitTestAtPoint(int x, int y, int slop, bool doMoveMouse) { - Vector rects; - moveMouse(m_mainFrame, x, y); - *hitTestResult = m_mainFrame->eventHandler()->hitTestResultAtPoint(IntPoint(x, y), + if (doMoveMouse) + moveMouse(m_mainFrame, x, y); + HitTestResult hitTestResult = m_mainFrame->eventHandler()->hitTestResultAtPoint(IntPoint(x, y), false, false, DontHitTestScrollbars, HitTestRequest::Active | HitTestRequest::ReadOnly, IntSize(slop, slop)); - if (!hitTestResult->innerNode() || !hitTestResult->innerNode()->inDocument()) { + AndroidHitTestResult androidHitResult(hitTestResult); + if (!hitTestResult.innerNode() || !hitTestResult.innerNode()->inDocument()) { ALOGE("Should not happen: no in document Node found"); - return rects; + return androidHitResult; } - const ListHashSet >& list = hitTestResult->rectBasedTestResult(); + const ListHashSet >& list = hitTestResult.rectBasedTestResult(); if (list.isEmpty()) { ALOGE("Should not happen: no rect-based-test nodes found"); - return rects; + return androidHitResult; } - Frame* frame = hitTestResult->innerNode()->document()->frame(); + Frame* frame = hitTestResult.innerNode()->document()->frame(); Vector nodeDataList; - if (hitTestResult->innerNode() != hitTestResult->innerNonSharedNode() - && hitTestResult->innerNode()->hasTagName(WebCore::HTMLNames::areaTag)) { - TouchNodeData newNode; - HTMLAreaElement* area = static_cast(hitTestResult->innerNode()); - newNode.mNode = area; - newNode.mBounds = area->computeRect(hitTestResult->innerNonSharedNode()->renderer()); - nodeDataList.append(newNode); - } else { - ListHashSet >::const_iterator last = list.end(); - for (ListHashSet >::const_iterator it = list.begin(); it != last; ++it) { - // TODO: it seems reasonable to not search across the frame. Isn't it? - // if the node is not in the same frame as the innerNode, skip it - if (it->get()->document()->frame() != frame) - continue; - // traverse up the tree to find the first node that needs highlight - bool found = false; - Node* eventNode = it->get(); - while (eventNode) { - RenderObject* render = eventNode->renderer(); - if (render && (render->isBody() || render->isRenderView())) - break; - if (eventNode->supportsFocus() - || eventNode->hasEventListeners(eventNames().clickEvent) - || eventNode->hasEventListeners(eventNames().mousedownEvent) - || eventNode->hasEventListeners(eventNames().mouseupEvent) - || eventNode->hasEventListeners(eventNames().mouseoverEvent)) { - found = true; - break; - } - // the nodes in the rectBasedTestResult() are ordered based on z-index during hit testing. - // so do not search for the eventNode across explicit z-index border. - // TODO: this is a hard one to call. z-index is quite complicated as its value only - // matters when you compare two RenderLayer in the same hierarchy level. e.g. in - // the following example, "b" is on the top as its z level is the highest. even "c" - // has 100 as z-index, it is still below "d" as its parent has the same z-index as - // "d" and logically before "d". Of course "a" is the lowest in the z level. - // - // z-index:auto "a" - // z-index:2 "b" - // z-index:1 - // z-index:100 "c" - // z-index:1 "d" - // - // If the fat point touches everyone, the order in the list should be "b", "d", "c" - // and "a". When we search for the event node for "b", we really don't want "a" as - // in the z-order it is behind everything else. - if (render && !render->style()->hasAutoZIndex()) - break; - eventNode = eventNode->parentNode(); + if (hitTestResult.innerNode() != hitTestResult.innerNonSharedNode() + && hitTestResult.innerNode()->hasTagName(WebCore::HTMLNames::areaTag)) { + HTMLAreaElement* area = static_cast(hitTestResult.innerNode()); + androidHitResult.hitTestResult().setURLElement(area); + androidHitResult.highlightRects().append(area->computeRect( + hitTestResult.innerNonSharedNode()->renderer())); + return androidHitResult; + } + ListHashSet >::const_iterator last = list.end(); + for (ListHashSet >::const_iterator it = list.begin(); it != last; ++it) { + // TODO: it seems reasonable to not search across the frame. Isn't it? + // if the node is not in the same frame as the innerNode, skip it + if (it->get()->document()->frame() != frame) + continue; + // traverse up the tree to find the first node that needs highlight + bool found = false; + Node* eventNode = it->get(); + Node* innerNode = eventNode; + while (eventNode) { + RenderObject* render = eventNode->renderer(); + if (render && (render->isBody() || render->isRenderView())) + break; + if (eventNode->supportsFocus() + || eventNode->hasEventListeners(eventNames().clickEvent) + || eventNode->hasEventListeners(eventNames().mousedownEvent) + || eventNode->hasEventListeners(eventNames().mouseupEvent) + || eventNode->hasEventListeners(eventNames().mouseoverEvent)) { + found = true; + break; } - // didn't find any eventNode, skip it - if (!found) - continue; - // first quick check whether it is a duplicated node before computing bounding box - Vector::const_iterator nlast = nodeDataList.end(); - for (Vector::const_iterator n = nodeDataList.begin(); n != nlast; ++n) { - // found the same node, skip it - if (eventNode == n->mNode) { - found = false; - break; - } + // the nodes in the rectBasedTestResult() are ordered based on z-index during hit testing. + // so do not search for the eventNode across explicit z-index border. + // TODO: this is a hard one to call. z-index is quite complicated as its value only + // matters when you compare two RenderLayer in the same hierarchy level. e.g. in + // the following example, "b" is on the top as its z level is the highest. even "c" + // has 100 as z-index, it is still below "d" as its parent has the same z-index as + // "d" and logically before "d". Of course "a" is the lowest in the z level. + // + // z-index:auto "a" + // z-index:2 "b" + // z-index:1 + // z-index:100 "c" + // z-index:1 "d" + // + // If the fat point touches everyone, the order in the list should be "b", "d", "c" + // and "a". When we search for the event node for "b", we really don't want "a" as + // in the z-order it is behind everything else. + if (render && !render->style()->hasAutoZIndex()) + break; + eventNode = eventNode->parentNode(); + } + // didn't find any eventNode, skip it + if (!found) + continue; + // first quick check whether it is a duplicated node before computing bounding box + Vector::const_iterator nlast = nodeDataList.end(); + for (Vector::const_iterator n = nodeDataList.begin(); n != nlast; ++n) { + // found the same node, skip it + if (eventNode == n->mUrlNode) { + found = false; + break; } - if (!found) + } + if (!found) + continue; + // next check whether the node is fully covered by or fully covering another node. + found = false; + IntRect rect = getAbsoluteBoundingBox(eventNode); + if (rect.isEmpty()) { + // if the node's bounds is empty and it is not a ContainerNode, skip it. + if (!eventNode->isContainerNode()) continue; - // next check whether the node is fully covered by or fully covering another node. - found = false; - IntRect rect = getAbsoluteBoundingBox(eventNode); - if (rect.isEmpty()) { - // if the node's bounds is empty and it is not a ContainerNode, skip it. - if (!eventNode->isContainerNode()) - continue; - // if the node's children are all positioned objects, its bounds can be empty. - // Walk through the children to find the bounding box. - Node* child = static_cast(eventNode)->firstChild(); - while (child) { - IntRect childrect; - if (child->renderer()) - childrect = getAbsoluteBoundingBox(child); - if (!childrect.isEmpty()) { - rect.unite(childrect); - child = child->traverseNextSibling(eventNode); - } else - child = child->traverseNextNode(eventNode); - } - } - for (int i = nodeDataList.size() - 1; i >= 0; i--) { - TouchNodeData n = nodeDataList.at(i); - // the new node is enclosing an existing node, skip it - if (rect.contains(n.mBounds)) { - found = true; - break; - } - // the new node is fully inside an existing node, remove the existing node - if (n.mBounds.contains(rect)) - nodeDataList.remove(i); + // if the node's children are all positioned objects, its bounds can be empty. + // Walk through the children to find the bounding box. + Node* child = static_cast(eventNode)->firstChild(); + while (child) { + IntRect childrect; + if (child->renderer()) + childrect = getAbsoluteBoundingBox(child); + if (!childrect.isEmpty()) { + rect.unite(childrect); + child = child->traverseNextSibling(eventNode); + } else + child = child->traverseNextNode(eventNode); } - if (!found) { - TouchNodeData newNode; - newNode.mNode = eventNode; - newNode.mBounds = rect; - nodeDataList.append(newNode); + } + for (int i = nodeDataList.size() - 1; i >= 0; i--) { + TouchNodeData n = nodeDataList.at(i); + // the new node is enclosing an existing node, skip it + if (rect.contains(n.mBounds)) { + found = true; + break; } + // the new node is fully inside an existing node, remove the existing node + if (n.mBounds.contains(rect)) + nodeDataList.remove(i); } - if (!nodeDataList.size()) { - *node = 0; - return rects; + if (!found) { + TouchNodeData newNode; + newNode.mUrlNode = eventNode; + newNode.mBounds = rect; + newNode.mInnerNode = innerNode; + nodeDataList.append(newNode); } } + if (!nodeDataList.size()) { + return androidHitResult; + } // finally select the node with the largest overlap with the fat point TouchNodeData final; - final.mNode = 0; + final.mUrlNode = 0; IntPoint docPos = frame->view()->windowToContents(m_mousePos); IntRect testRect(docPos.x() - slop, docPos.y() - slop, 2 * slop + 1, 2 * slop + 1); int area = 0; @@ -1804,23 +1806,32 @@ Vector WebViewCore::getTouchHighlightRects(int x, int y, int slop, } } // now get the node's highlight rectangles in the page coordinate system - if (final.mNode) { - if (final.mNode->isElementNode()) { + if (final.mUrlNode) { + if (final.mUrlNode->isElementNode()) { // We found a URL element. Update the hitTestResult - *node = final.mNode; - hitTestResult->setURLElement(static_cast(final.mNode)); + androidHitResult.hitTestResult().setURLElement(static_cast(final.mUrlNode)); + } else { + androidHitResult.hitTestResult().setURLElement(0); } + // Update innerNode and innerNonSharedNode + androidHitResult.hitTestResult().setInnerNode(final.mInnerNode); + androidHitResult.hitTestResult().setInnerNonSharedNode(final.mInnerNode); IntPoint frameAdjust; if (frame != m_mainFrame) { frameAdjust = frame->view()->contentsToWindow(IntPoint()); frameAdjust.move(m_scrollOffsetX, m_scrollOffsetY); } - if (final.mNode->isLink() && final.mNode->renderer()) { + Vector& rects = androidHitResult.highlightRects(); + if (final.mUrlNode->isLink() && final.mUrlNode->renderer()) { // most of the links are inline instead of box style. So the bounding box is not // a good representation for the highlights. Get the list of rectangles instead. - RenderObject* render = final.mNode->renderer(); + RenderObject* render = final.mUrlNode->renderer(); IntPoint offset = roundedIntPoint(render->localToAbsolute()); render->absoluteRects(rects, offset.x() + frameAdjust.x(), offset.y() + frameAdjust.y()); + if (final.mInnerNode && final.mInnerNode->renderer()) { + final.mInnerNode->renderer()->absoluteRects(rects, + offset.x() + frameAdjust.x(), offset.y() + frameAdjust.y()); + } bool inside = false; int distance = INT_MAX; int newx = x, newy = y; @@ -1868,7 +1879,7 @@ Vector WebViewCore::getTouchHighlightRects(int x, int y, int slop, } } if (!rects.isEmpty()) { - if (!inside) { + if (!inside && doMoveMouse) { // if neither x nor y has overlap, just pick the top/left of the first rectangle if (newx == x && newy == y) { newx = rects[0].x(); @@ -1879,23 +1890,25 @@ Vector WebViewCore::getTouchHighlightRects(int x, int y, int slop, x, y, m_mousePos.x() + m_scrollOffsetX, m_mousePos.y() + m_scrollOffsetY, m_scrollOffsetX, m_scrollOffsetY); } - return rects; + return androidHitResult; } } IntRect rect = final.mBounds; rect.move(frameAdjust.x(), frameAdjust.y()); rects.append(rect); - // adjust m_mousePos if it is not inside the returned highlight rectangle - testRect.move(frameAdjust.x(), frameAdjust.y()); - testRect.intersect(rect); - if (!testRect.contains(x, y)) { - moveMouse(m_mainFrame, testRect.center().x(), testRect.center().y()); - DBG_NAV_LOGD("Move x/y from (%d, %d) to (%d, %d) scrollOffset is (%d, %d)", - x, y, m_mousePos.x() + m_scrollOffsetX, m_mousePos.y() + m_scrollOffsetY, - m_scrollOffsetX, m_scrollOffsetY); + if (doMoveMouse) { + // adjust m_mousePos if it is not inside the returned highlight rectangle + testRect.move(frameAdjust.x(), frameAdjust.y()); + testRect.intersect(rect); + if (!testRect.contains(x, y)) { + moveMouse(m_mainFrame, testRect.center().x(), testRect.center().y()); + DBG_NAV_LOGD("Move x/y from (%d, %d) to (%d, %d) scrollOffset is (%d, %d)", + x, y, m_mousePos.x() + m_scrollOffsetX, m_mousePos.y() + m_scrollOffsetY, + m_scrollOffsetX, m_scrollOffsetY); + } } } - return rects; + return androidHitResult; } /////////////////////////////////////////////////////////////////////////////// @@ -4559,84 +4572,16 @@ static bool ValidNodeAndBounds(JNIEnv* env, jobject obj, jint nativeClass, reinterpret_cast(node), nativeRect); } -static int GetHitTestExtra(Node* node, HitTestResult& hitTestResult, WTF::String& extra) -{ - /* - UNKNOWN_TYPE = 0; - PHONE_TYPE = 2; - GEO_TYPE = 3; - EMAIL_TYPE = 4; - IMAGE_TYPE = 5; - SRC_ANCHOR_TYPE = 7; - SRC_IMAGE_ANCHOR_TYPE = 8; - EDIT_TEXT_TYPE = 9; - */ - if (!node) - return 0; - KURL imageUrl = hitTestResult.absoluteImageURL(); - if (node->isLink()) { - if (!imageUrl.isEmpty()) { - extra = imageUrl.string(); - return 8; - } - extra = hitTestResult.absoluteLinkURL(); - return 7; - - } - if (!imageUrl.isEmpty()) { - extra = imageUrl.string(); - return 5; - } - if (hitTestResult.isContentEditable()) { - return 9; - } - return 0; -} - -static jobject HitTest(JNIEnv* env, jobject obj, jint nativeClass, jint x, jint y, jint slop) +static jobject HitTest(JNIEnv* env, jobject obj, jint nativeClass, jint x, + jint y, jint slop, jboolean doMoveMouse) { WebViewCore* viewImpl = reinterpret_cast(nativeClass); if (!viewImpl) return 0; Node* node = 0; - HitTestResult hitTestResult; - Vector rects = viewImpl->getTouchHighlightRects(x, y, slop, &node, &hitTestResult); - if (rects.isEmpty() && !node) - return 0; - - jclass rectClass = env->FindClass("android/graphics/Rect"); - ALOG_ASSERT(rectClass, "Could not find android/graphics/Rect"); - jmethodID rectinit = env->GetMethodID(rectClass, "", "(IIII)V"); - ALOG_ASSERT(rectinit, "Could not find init method on Rect"); - jobjectArray array = env->NewObjectArray(rects.size(), rectClass, 0); - ALOG_ASSERT(array, "Could not create a Rect array"); - - for (size_t i = 0; i < rects.size(); i++) { - jobject rect = env->NewObject(rectClass, rectinit, rects[i].x(), - rects[i].y(), rects[i].maxX(), rects[i].maxY()); - if (rect) { - env->SetObjectArrayElement(array, i, rect); - env->DeleteLocalRef(rect); - } - } - - env->DeleteLocalRef(rectClass); - - jclass hitTestClass = env->FindClass("android/webkit/WebViewCore$WebKitHitTest"); - ALOG_ASSERT(hitTestClass, "Could not find android/webkit/WebViewCore$WebKitHitTest"); - jmethodID hitTestInit = env->GetMethodID(hitTestClass, "", "()V"); - jobject hitTest = env->NewObject(hitTestClass, hitTestInit); - jfieldID htTouchRects = env->GetFieldID(hitTestClass, "mTouchRects", "[Landroid/graphics/Rect;"); - env->SetObjectField(hitTest, htTouchRects, array); - jfieldID htType = env->GetFieldID(hitTestClass, "mType", "I"); - jfieldID htExtra = env->GetFieldID(hitTestClass, "mExtra", "Ljava/lang/String;"); - WTF::String extra; - int type = GetHitTestExtra(node, hitTestResult, extra); - env->SetIntField(hitTest, htType, type); - jstring jextra = wtfStringToJstring(env, extra, false); - env->SetObjectField(hitTest, htExtra, jextra); - env->DeleteLocalRef(hitTestClass); - return hitTest; + AndroidHitTestResult result = viewImpl->hitTestAtPoint(x, y, slop, doMoveMouse); + Vector& rects = result.highlightRects(); + return result.createJavaObject(env); } static void AutoFillForm(JNIEnv* env, jobject obj, jint nativeClass, @@ -4782,7 +4727,7 @@ static JNINativeMethod gJavaWebViewCoreMethods[] = { (void*) PluginSurfaceReady }, { "nativeValidNodeAndBounds", "(IIILandroid/graphics/Rect;)Z", (void*) ValidNodeAndBounds }, - { "nativeHitTest", "(IIII)Landroid/webkit/WebViewCore$WebKitHitTest;", + { "nativeHitTest", "(IIIIZ)Landroid/webkit/WebViewCore$WebKitHitTest;", (void*) HitTest }, { "nativeAutoFillForm", "(II)V", (void*) AutoFillForm }, diff --git a/Source/WebKit/android/jni/WebViewCore.h b/Source/WebKit/android/jni/WebViewCore.h index 9e7c4af..0f38ccf 100644 --- a/Source/WebKit/android/jni/WebViewCore.h +++ b/Source/WebKit/android/jni/WebViewCore.h @@ -101,6 +101,7 @@ namespace android { class CachedNode; class CachedRoot; class ListBoxReply; + class AndroidHitTestResult; class WebCoreReply : public WebCoreRefObject { public: @@ -526,6 +527,8 @@ namespace android { // return a list of rects matching the touch point (x, y) with the slop Vector getTouchHighlightRects(int x, int y, int slop, Node** node, HitTestResult* hitTestResult); + // This does a sloppy hit test + AndroidHitTestResult hitTestAtPoint(int x, int y, int slop, bool doMoveMouse = false); // Open a file chooser for selecting a file to upload void openFileChooser(PassRefPtr ); -- cgit v1.1