diff options
Diffstat (limited to 'Source/WebKit/android/jni/AndroidHitTestResult.cpp')
| -rw-r--r-- | Source/WebKit/android/jni/AndroidHitTestResult.cpp | 268 |
1 files changed, 268 insertions, 0 deletions
diff --git a/Source/WebKit/android/jni/AndroidHitTestResult.cpp b/Source/WebKit/android/jni/AndroidHitTestResult.cpp new file mode 100644 index 0000000..9be5613 --- /dev/null +++ b/Source/WebKit/android/jni/AndroidHitTestResult.cpp @@ -0,0 +1,268 @@ +/* + * 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 "content/address_detector.h" +#include "content/PhoneEmailDetector.h" +#include "android/WebHitTestInfo.h" +#include "Document.h" +#include "Element.h" +#include "Frame.h" +#include "HitTestResult.h" +#include "KURL.h" +#include "LayerAndroid.h" +#include "PlatformString.h" +#include "Range.h" +#include "RenderLayer.h" +#include "RenderLayerBacking.h" +#include "RenderObject.h" +#include "WebCoreJni.h" +#include "WebViewCore.h" + +#include <cutils/log.h> +#include <JNIHelp.h> +#include <JNIUtility.h> + +namespace android { + +using namespace WebCore; + +static bool gJniInitialized = false; +static struct { + jmethodID m_Init; + jfieldID m_LinkUrl; + jfieldID m_AnchorText; + jfieldID m_ImageUrl; + jfieldID m_AltDisplayString; + jfieldID m_Title; + jfieldID m_Editable; + jfieldID m_TouchRects; + jfieldID m_TapHighlightColor; + jfieldID m_EnclosingParentRects; + jfieldID m_HasFocus; + jfieldID m_IntentUrl; +} gHitTestGlue; + +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"); + + gHitTestGlue.m_Init = env->GetMethodID(hitTestClass, "<init>", "()V"); + ALOG_ASSERT(gHitTestGlue.m_Init, "Could not find init method on android/webkit/WebViewCore$WebKitHitTest"); + + field fields[] = { + { hitTestClass, "mTouchRects", "[Landroid/graphics/Rect;", &gHitTestGlue.m_TouchRects }, + { hitTestClass, "mEditable", "Z", &gHitTestGlue.m_Editable }, + { hitTestClass, "mLinkUrl", "Ljava/lang/String;", &gHitTestGlue.m_LinkUrl }, + { hitTestClass, "mIntentUrl", "Ljava/lang/String;", &gHitTestGlue.m_IntentUrl }, + { hitTestClass, "mAnchorText", "Ljava/lang/String;", &gHitTestGlue.m_AnchorText }, + { hitTestClass, "mImageUrl", "Ljava/lang/String;", &gHitTestGlue.m_ImageUrl }, + { hitTestClass, "mAltDisplayString", "Ljava/lang/String;", &gHitTestGlue.m_AltDisplayString }, + { hitTestClass, "mTitle", "Ljava/lang/String;", &gHitTestGlue.m_Title }, + { hitTestClass, "mTapHighlightColor", "I", &gHitTestGlue.m_TapHighlightColor }, + { hitTestClass, "mEnclosingParentRects", "[Landroid/graphics/Rect;", &gHitTestGlue.m_EnclosingParentRects }, + { hitTestClass, "mHasFocus", "Z", &gHitTestGlue.m_HasFocus }, + {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(WebViewCore* webViewCore, WebCore::HitTestResult& hitTestResult) + : m_webViewCore(webViewCore) + , m_hitTestResult(hitTestResult) +{ + buildHighlightRects(); +} + +void AndroidHitTestResult::setURLElement(Element* element) +{ + m_hitTestResult.setURLElement(element); + buildHighlightRects(); +} + +void AndroidHitTestResult::buildHighlightRects() +{ + m_highlightRects.clear(); + Node* node = m_hitTestResult.URLElement(); + if (!node || !node->renderer()) + node = m_hitTestResult.innerNode(); + if (!node || !node->renderer()) + return; + if (!WebViewCore::nodeIsClickableOrFocusable(node)) + return; + Frame* frame = node->document()->frame(); + IntPoint frameOffset = m_webViewCore->convertGlobalContentToFrameContent(IntPoint(), frame); + RenderObject* renderer = node->renderer(); + Vector<FloatQuad> quads; + renderer->absoluteFocusRingQuads(quads); + if (!quads.size()) + renderer->absoluteQuads(quads); // No fancy rings, grab some backups + for (size_t i = 0; i < quads.size(); i++) { + IntRect boundingBox = quads[i].enclosingBoundingBox(); + boundingBox.move(-frameOffset.x(), -frameOffset.y()); + m_highlightRects.append(boundingBox); + } +} + +void AndroidHitTestResult::searchContentDetectors() +{ + AddressDetector address; + PhoneEmailDetector phoneEmail; + Node* node = m_hitTestResult.innerNode(); + if (!node || !node->isTextNode()) + return; + if (!m_hitTestResult.absoluteLinkURL().isEmpty()) + return; + WebKit::WebHitTestInfo webHitTest(m_hitTestResult); + m_searchResult = address.FindTappedContent(webHitTest); + if (!m_searchResult.valid) { + m_searchResult = phoneEmail.FindTappedContent(webHitTest); + } + if (m_searchResult.valid) { + m_highlightRects.clear(); + RefPtr<Range> range = (PassRefPtr<Range>) m_searchResult.range; + range->textRects(m_highlightRects, true); + } +} + +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); +} + +void setStringField(JNIEnv* env, jobject obj, jfieldID field, const GURL& url) +{ + jstring jstr = stdStringToJstring(env, url.spec(), false); + env->SetObjectField(obj, field, jstr); + env->DeleteLocalRef(jstr); +} + +void setRectArray(JNIEnv* env, jobject obj, jfieldID field, Vector<IntRect> &rects) +{ + jobjectArray array = intRectVectorToRectArray(env, rects); + env->SetObjectField(obj, field, array); + env->DeleteLocalRef(array); +} + +// Some helper macros specific to setting hitTest fields +#define _SET(jtype, jfield, value) env->Set ## jtype ## Field(hitTest, gHitTestGlue.m_ ## jfield, value) +#define SET_BOOL(jfield, value) _SET(Boolean, jfield, value) +#define SET_STRING(jfield, value) setStringField(env, hitTest, gHitTestGlue.m_ ## jfield, value) +#define SET_INT(jfield, value) _SET(Int, jfield, value) + +jobject AndroidHitTestResult::createJavaObject(JNIEnv* env) +{ + InitJni(env); + jclass hitTestClass = env->FindClass("android/webkit/WebViewCore$WebKitHitTest"); + ALOG_ASSERT(hitTestClass, "Could not find android/webkit/WebViewCore$WebKitHitTest"); + + jobject hitTest = env->NewObject(hitTestClass, gHitTestGlue.m_Init); + setRectArray(env, hitTest, gHitTestGlue.m_TouchRects, m_highlightRects); + + Vector<IntRect> rects = enclosingParentRects(m_hitTestResult.innerNode()); + setRectArray(env, hitTest, gHitTestGlue.m_EnclosingParentRects, rects); + + SET_BOOL(Editable, m_hitTestResult.isContentEditable()); + SET_STRING(LinkUrl, m_hitTestResult.absoluteLinkURL().string()); + if (m_searchResult.valid) + SET_STRING(IntentUrl, m_searchResult.intent_url); + SET_STRING(ImageUrl, m_hitTestResult.absoluteImageURL().string()); + SET_STRING(AltDisplayString, m_hitTestResult.altDisplayString()); + TextDirection titleTextDirection; + SET_STRING(Title, m_hitTestResult.title(titleTextDirection)); + if (m_hitTestResult.URLElement()) { + Element* urlElement = m_hitTestResult.URLElement(); + SET_STRING(AnchorText, urlElement->innerText()); + if (urlElement->renderer()) { + SET_INT(TapHighlightColor, + urlElement->renderer()->style()->tapHighlightColor().rgb()); + } + } + Node* focusedNode = m_webViewCore->focusedFrame()->document()->focusedNode(); + SET_BOOL(HasFocus, + focusedNode == m_hitTestResult.URLElement() + || focusedNode == m_hitTestResult.innerNode() + || focusedNode == m_hitTestResult.innerNonSharedNode()); + + env->DeleteLocalRef(hitTestClass); + + return hitTest; +} + +Vector<IntRect> AndroidHitTestResult::enclosingParentRects(Node* node) +{ + int count = 0; + int lastX = 0; + Vector<IntRect> rects; + + while (node && count < 5) { + RenderObject* render = node->renderer(); + if (!render || render->isBody()) + break; + + IntPoint frameOffset = m_webViewCore->convertGlobalContentToFrameContent(IntPoint(), + node->document()->frame()); + IntRect rect = render->absoluteBoundingBoxRect(); + rect.move(-frameOffset.x(), -frameOffset.y()); + if (count == 0 || rect.x() != lastX) { + rects.append(rect); + lastX = rect.x(); + count++; + } + + node = node->parentNode(); + } + + return rects; +} + +} /* namespace android */ |
