diff options
-rw-r--r-- | Source/WebCore/platform/graphics/android/GLExtras.cpp | 4 | ||||
-rw-r--r-- | Source/WebCore/platform/graphics/android/GraphicsLayerAndroid.cpp | 6 | ||||
-rw-r--r-- | Source/WebCore/platform/graphics/android/VerticalTextMap.cpp | 7 | ||||
-rw-r--r-- | Source/WebCore/platform/graphics/android/android_graphics.cpp | 23 | ||||
-rw-r--r-- | Source/WebCore/platform/graphics/android/android_graphics.h | 4 | ||||
-rw-r--r-- | Source/WebCore/rendering/RenderLayerCompositor.cpp | 14 | ||||
-rw-r--r-- | Source/WebKit/Android.mk | 1 | ||||
-rw-r--r-- | Source/WebKit/android/jni/AndroidHitTestResult.cpp | 154 | ||||
-rw-r--r-- | Source/WebKit/android/jni/AndroidHitTestResult.h | 55 | ||||
-rw-r--r-- | Source/WebKit/android/jni/JavaSharedClient.cpp | 47 | ||||
-rw-r--r-- | Source/WebKit/android/jni/WebHistory.cpp | 475 | ||||
-rw-r--r-- | Source/WebKit/android/jni/WebViewCore.cpp | 358 | ||||
-rw-r--r-- | Source/WebKit/android/jni/WebViewCore.h | 3 | ||||
-rw-r--r-- | Source/WebKit/android/plugins/ANPSoundInterface.cpp | 2 |
14 files changed, 705 insertions, 448 deletions
diff --git a/Source/WebCore/platform/graphics/android/GLExtras.cpp b/Source/WebCore/platform/graphics/android/GLExtras.cpp index bf489c9..9ad369a 100644 --- a/Source/WebCore/platform/graphics/android/GLExtras.cpp +++ b/Source/WebCore/platform/graphics/android/GLExtras.cpp @@ -165,6 +165,10 @@ void GLExtras::drawRegion(const SkRegion& region, bool fill, void GLExtras::drawCursorRings(const LayerAndroid* layer) { + int layerId = layer ? layer->uniqueId() : -1; + if (layerId != m_ring->layerId()) + return; + SkRegion region; for (size_t i = 0; i < m_ring->rings().size(); i++) { IntRect rect = m_ring->rings().at(i); diff --git a/Source/WebCore/platform/graphics/android/GraphicsLayerAndroid.cpp b/Source/WebCore/platform/graphics/android/GraphicsLayerAndroid.cpp index fc2eddd..779eb36 100644 --- a/Source/WebCore/platform/graphics/android/GraphicsLayerAndroid.cpp +++ b/Source/WebCore/platform/graphics/android/GraphicsLayerAndroid.cpp @@ -588,8 +588,10 @@ bool GraphicsLayerAndroid::repaint() RenderBox* box = layer->renderBox(); int outline = box->view()->maximalOutlineSize(); IntRect contentsRect(0, 0, - box->borderLeft() + box->borderRight() + layer->scrollWidth(), - box->borderTop() + box->borderBottom() + layer->scrollHeight()); + box->borderLeft() + box->borderRight() + layer->scrollWidth() + + layer->verticalScrollbarWidth(), + box->borderTop() + box->borderBottom() + layer->scrollHeight() + + layer->horizontalScrollbarHeight()); contentsRect.inflate(outline); // Update the foreground layer size. m_foregroundLayer->setSize(contentsRect.width(), contentsRect.height()); diff --git a/Source/WebCore/platform/graphics/android/VerticalTextMap.cpp b/Source/WebCore/platform/graphics/android/VerticalTextMap.cpp index 47c0613..42aa385 100644 --- a/Source/WebCore/platform/graphics/android/VerticalTextMap.cpp +++ b/Source/WebCore/platform/graphics/android/VerticalTextMap.cpp @@ -33,7 +33,6 @@ #include <wtf/RefPtr.h> static const UChar vTextCnvTable[][2] = { - // TODO: uncomment mappings once we add glyphs for vertical forms. {0x0021, 0xfe15}, // exclamation mark {0x0028, 0xfe35}, // left paren {0x0029, 0xfe36}, // right paren @@ -55,8 +54,7 @@ static const UChar vTextCnvTable[][2] = { {0x2015, 0xfe31}, // horizontal bar {0x2025, 0xfe30}, // two dot leader {0x2026, 0xfe19}, // three dot leader - // TODO: change the mapping 0x3001 -> 0xFE11 once Android has the glyph for 0xFE11. - {0x3001, 0xfe10}, // Ideographic comma + {0x3001, 0xfe11}, // Ideographic comma {0x3002, 0xfe12}, // Ideographic full stop {0x3008, 0xfe3f}, // left angle bracket {0x3009, 0xfe40}, // right angle bracket @@ -83,8 +81,7 @@ static const UChar vTextCnvTable[][2] = { {0xff5c, 0xfe31}, // fullwidth vertical line {0xff5d, 0xfe38}, // full width right curly bracket {0xff5e, 0x007c}, // tilde to vertical line - // TODO: change the mapping 0xff64 -> 0xFE11 once Android has the glyph for 0xFE11. - {0xff64, 0xfe10}, // halfwidth ideo comma + {0xff64, 0xfe11}, // halfwidth ideo comma {0xff61, 0xfe12}, // halfwidth ideo full stop }; diff --git a/Source/WebCore/platform/graphics/android/android_graphics.cpp b/Source/WebCore/platform/graphics/android/android_graphics.cpp index e88c65d..57f1b41 100644 --- a/Source/WebCore/platform/graphics/android/android_graphics.cpp +++ b/Source/WebCore/platform/graphics/android/android_graphics.cpp @@ -37,6 +37,12 @@ namespace android { +CursorRing::CursorRing(WebViewCore* core) + : m_viewImpl(core) + , m_layerId(-1) +{ +} + // The CSS values for the inner and outer widths may be specified as fractions #define WIDTH_SCALE 0.0625f // 1/16, to offset the scale in CSSStyleSelector @@ -116,12 +122,27 @@ void CursorRing::setIsButton(const CachedNode* node) bool CursorRing::setup() { - m_node->cursorRings(m_frame, &m_rings); + m_layerId = -1; + if (m_frame && m_root) { + const CachedLayer* cachedLayer = m_frame->layer(m_node); + if (cachedLayer) { + const WebCore::LayerAndroid* rootLayer = m_root->rootLayer(); + const LayerAndroid* aLayer = cachedLayer->layer(rootLayer); + if (aLayer) + m_layerId = aLayer->uniqueId(); + } + } + if (m_layerId == -1) + m_node->cursorRings(m_frame, &m_rings); + else + m_node->localCursorRings(m_frame, &m_rings); + if (!m_rings.size()) { DBG_NAV_LOG("!rings.size()"); m_viewImpl->m_hasCursorBounds = false; return false; } + setIsButton(m_node); m_bounds = m_node->bounds(m_frame); m_viewImpl->updateCursorBounds(m_root, m_frame, m_node); diff --git a/Source/WebCore/platform/graphics/android/android_graphics.h b/Source/WebCore/platform/graphics/android/android_graphics.h index 60ac115..bd08a6e 100644 --- a/Source/WebCore/platform/graphics/android/android_graphics.h +++ b/Source/WebCore/platform/graphics/android/android_graphics.h @@ -52,12 +52,13 @@ class WebViewCore; class CursorRing : public DrawExtra { public: - CursorRing(WebViewCore* core) : m_viewImpl(core) {} + CursorRing(WebViewCore* core); virtual ~CursorRing() {} virtual void draw(SkCanvas* , LayerAndroid* , IntRect* ); void setIsButton(const CachedNode* ); bool setup(); WTF::Vector<IntRect>& rings() { return m_rings; } + int layerId() const { return m_layerId; } private: friend class WebView; friend class WebCore::GLExtras; @@ -71,6 +72,7 @@ private: const CachedNode* m_node; bool m_isButton; bool m_isPressed; + int m_layerId; }; } diff --git a/Source/WebCore/rendering/RenderLayerCompositor.cpp b/Source/WebCore/rendering/RenderLayerCompositor.cpp index 1ef3b77..33bf2f7 100644 --- a/Source/WebCore/rendering/RenderLayerCompositor.cpp +++ b/Source/WebCore/rendering/RenderLayerCompositor.cpp @@ -1366,7 +1366,21 @@ bool RenderLayerCompositor::needsToBeComposited(const RenderLayer* layer) const return false; // The root layer always has a compositing layer, but it may not have backing. +#if PLATFORM(ANDROID) + // If we do not have a root platform layer, don't use the + // mustOverlapCompositedLayers() as a cue that this layer needs to be + // composited -- the layers tree has been detached. + // Otherwise we can end up in a cycle where updateBacking() switches composited + // mode on because a layer has mustOverlapCompositedLayers() (by calling + // enableCompositingMode()), while computeCompositingRequirements() will + // (correctly) say that we do not need to be in composited mode and turns it + // off, rince and repeat... + return requiresCompositingLayer(layer) + || (m_rootPlatformLayer && layer->mustOverlapCompositedLayers()) + || (inCompositingMode() && layer->isRootLayer()); +#else return requiresCompositingLayer(layer) || layer->mustOverlapCompositedLayers() || (inCompositingMode() && layer->isRootLayer()); +#endif } #if PLATFORM(ANDROID) 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 <JNIHelp.h> +#include <JNIUtility.h> + +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, "<init>", "(IIII)V"); + ALOG_ASSERT(gJavaGlue.m_rectInit, "Could not find init method on Rect"); + + gJavaGlue.m_hitTestInit = env->GetMethodID(hitTestClass, "<init>", "()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 <jni.h> + +namespace android { + +class AndroidHitTestResult +{ +public: + AndroidHitTestResult(WebCore::HitTestResult&); + ~AndroidHitTestResult() {} + + WebCore::HitTestResult& hitTestResult() { return m_hitTestResult; } + Vector<WebCore::IntRect>& highlightRects() { return m_highlightRects; } + + jobject createJavaObject(JNIEnv*); + +private: + WebCore::HitTestResult m_hitTestResult; + Vector<WebCore::IntRect> m_highlightRects; +}; + +} // namespace android + +#endif // AndroidHitTestResult_h diff --git a/Source/WebKit/android/jni/JavaSharedClient.cpp b/Source/WebKit/android/jni/JavaSharedClient.cpp index 4f40355..4d073c2 100644 --- a/Source/WebKit/android/jni/JavaSharedClient.cpp +++ b/Source/WebKit/android/jni/JavaSharedClient.cpp @@ -88,12 +88,12 @@ namespace android { FileSystemClient* JavaSharedClient::gFileSystemClient = NULL; /////////////////////////////////////////////////////////////////////////// - + struct FuncPtrRec { void (*fProc)(void* payload); void* fPayload; }; - + static SkMutex gFuncPtrQMutex; static SkDeque gFuncPtrQ(sizeof(FuncPtrRec)); @@ -105,33 +105,34 @@ namespace android { FuncPtrRec* rec = (FuncPtrRec*)gFuncPtrQ.push_back(); rec->fProc = proc; rec->fPayload = payload; - + gFuncPtrQMutex.release(); - + gTimerClient->signalServiceFuncPtrQueue(); } void JavaSharedClient::ServiceFunctionPtrQueue() { - for (;;) { - void (*proc)(void*) = 0; - void* payload = 0; - const FuncPtrRec* rec; - - // we have to copy the proc/payload (if present). we do this so we - // don't call the proc inside the mutex (possible deadlock!) - gFuncPtrQMutex.acquire(); - rec = (const FuncPtrRec*)gFuncPtrQ.front(); - if (rec) { - proc = rec->fProc; - payload = rec->fPayload; - gFuncPtrQ.pop_front(); - } - gFuncPtrQMutex.release(); - - if (!rec) - break; - proc(payload); + // Don't let execution block the WebViewCore thread for too long. + void (*proc)(void*) = 0; + void* payload = 0; + const FuncPtrRec* rec; + + // we have to copy the proc/payload (if present). we do this so we + // don't call the proc inside the mutex (possible deadlock!) + gFuncPtrQMutex.acquire(); + rec = (const FuncPtrRec*)gFuncPtrQ.front(); + if (rec) { + proc = rec->fProc; + payload = rec->fPayload; + gFuncPtrQ.pop_front(); } + bool scheduleAdditionalCall = (gFuncPtrQ.count() > 0); + gFuncPtrQMutex.release(); + + if (rec) + proc(payload); + if (scheduleAdditionalCall) + gTimerClient->signalServiceFuncPtrQueue(); } } diff --git a/Source/WebKit/android/jni/WebHistory.cpp b/Source/WebKit/android/jni/WebHistory.cpp index f782c37..db31429 100644 --- a/Source/WebKit/android/jni/WebHistory.cpp +++ b/Source/WebKit/android/jni/WebHistory.cpp @@ -24,6 +24,7 @@ */ #define LOG_TAG "webhistory" +#define LOG_NDEBUG 0 // enable ALOGV and ALOG_ASSERT #include "config.h" #include "WebHistory.h" @@ -54,9 +55,9 @@ namespace android { // Forward declarations -static void write_item(WTF::Vector<char>& v, WebCore::HistoryItem* item); -static void write_children_recursive(WTF::Vector<char>& v, WebCore::HistoryItem* parent); -static bool read_item_recursive(WebCore::HistoryItem* child, const char** pData, int length); +static void writeItem(WTF::Vector<char>& vector, WebCore::HistoryItem* item); +static void writeChildrenRecursive(WTF::Vector<char>& vector, WebCore::HistoryItem* parent); +static bool readItemRecursive(WebCore::HistoryItem* child, const char** pData, int length); // Field ids for WebHistoryItems struct WebHistoryItemFields { @@ -178,7 +179,7 @@ static void WebHistoryInflate(JNIEnv* env, jobject obj, jint frame, jbyteArray d // ptr's value. We can't pass &bytes since we have to send bytes to // ReleaseByteArrayElements unchanged. const char* ptr = reinterpret_cast<const char*>(bytes); - read_item_recursive(newItem.get(), &ptr, (int)size); + readItemRecursive(newItem.get(), &ptr, (int)size); env->ReleaseByteArrayElements(data, const_cast<jbyte*>(bytes), JNI_ABORT); bridge->setActive(); @@ -194,27 +195,27 @@ static void WebHistoryInflate(JNIEnv* env, jobject obj, jint frame, jbyteArray d // 1 char for isTargetItem. #define HISTORY_MIN_SIZE ((int)(sizeof(unsigned) * 10 + sizeof(char))) -jbyteArray WebHistory::Flatten(JNIEnv* env, WTF::Vector<char>& v, WebCore::HistoryItem* item) +jbyteArray WebHistory::Flatten(JNIEnv* env, WTF::Vector<char>& vector, WebCore::HistoryItem* item) { if (!item) return NULL; // Reserve a vector of chars with an initial size of HISTORY_MIN_SIZE. - v.reserveCapacity(HISTORY_MIN_SIZE); + vector.reserveCapacity(HISTORY_MIN_SIZE); // Write the top-level history item and then write all the children // recursively. ALOG_ASSERT(item->bridge(), "Why don't we have a bridge object here?"); - write_item(v, item); - write_children_recursive(v, item); + writeItem(vector, item); + writeChildrenRecursive(vector, item); // Try to create a new java byte array. - jbyteArray b = env->NewByteArray(v.size()); + jbyteArray b = env->NewByteArray(vector.size()); if (!b) return NULL; // Write our flattened data to the java array. - env->SetByteArrayRegion(b, 0, v.size(), (const jbyte*)v.data()); + env->SetByteArrayRegion(b, 0, vector.size(), (const jbyte*)vector.data()); return b; } @@ -358,7 +359,7 @@ void WebHistory::UpdateHistoryIndex(const AutoJObject& list, int newIndex) list.env()->CallVoidMethod(list.get(), gWebBackForwardList.mSetCurrentIndex, newIndex); } -static void write_string(WTF::Vector<char>& v, const WTF::String& str) +static void writeString(WTF::Vector<char>& vector, const WTF::String& str) { unsigned strLen = str.length(); // Only do work if the string has data. @@ -366,90 +367,90 @@ static void write_string(WTF::Vector<char>& v, const WTF::String& str) // Determine how much to grow the vector. Use the worst case for utf8 to // avoid reading the string twice. Add sizeof(unsigned) to hold the // string length in utf8. - unsigned vectorLen = v.size() + sizeof(unsigned); + unsigned vectorLen = vector.size() + sizeof(unsigned); unsigned length = (strLen << 2) + vectorLen; // Grow the vector. This will change the value of v.size() but we // remember the original size above. - v.grow(length); + vector.grow(length); // Grab the position to write to. - char* data = v.begin() + vectorLen; + char* data = vector.begin() + vectorLen; // Write the actual string int l = SkUTF16_ToUTF8(str.characters(), strLen, data); - LOG_VERBOSE(History, "Writing string %d %.*s", l, l, data); + ALOGV("Writing string %d %.*s", l, l, data); // Go back and write the utf8 length. Subtract sizeof(unsigned) from // data to get the position to write the length. memcpy(data - sizeof(unsigned), (char*)&l, sizeof(unsigned)); // Shrink the internal state of the vector so we match what was // actually written. - v.shrink(vectorLen + l); + vector.shrink(vectorLen + l); } else - v.append((char*)&strLen, sizeof(unsigned)); + vector.append((char*)&strLen, sizeof(unsigned)); } -static void write_item(WTF::Vector<char>& v, WebCore::HistoryItem* item) +static void writeItem(WTF::Vector<char>& vector, WebCore::HistoryItem* item) { // Original url - write_string(v, item->originalURLString()); + writeString(vector, item->originalURLString()); // Url - write_string(v, item->urlString()); + writeString(vector, item->urlString()); // Title - write_string(v, item->title()); + writeString(vector, item->title()); // Form content type - write_string(v, item->formContentType()); + writeString(vector, item->formContentType()); // Form data const WebCore::FormData* formData = item->formData(); if (formData) { - write_string(v, formData->flattenToString()); + writeString(vector, formData->flattenToString()); // save the identifier as it is not included in the flatten data int64_t id = formData->identifier(); - v.append((char*)&id, sizeof(int64_t)); + vector.append((char*)&id, sizeof(int64_t)); } else - write_string(v, WTF::String()); // Empty constructor does not allocate a buffer. + writeString(vector, WTF::String()); // Empty constructor does not allocate a buffer. // Target - write_string(v, item->target()); + writeString(vector, item->target()); AndroidWebHistoryBridge* bridge = item->bridge(); ALOG_ASSERT(bridge, "We should have a bridge here!"); // Screen scale const float scale = bridge->scale(); - LOG_VERBOSE(History, "Writing scale %f", scale); - v.append((char*)&scale, sizeof(float)); + ALOGV("Writing scale %f", scale); + vector.append((char*)&scale, sizeof(float)); const float textWrapScale = bridge->textWrapScale(); - LOG_VERBOSE(History, "Writing text wrap scale %f", textWrapScale); - v.append((char*)&textWrapScale, sizeof(float)); + ALOGV("Writing text wrap scale %f", textWrapScale); + vector.append((char*)&textWrapScale, sizeof(float)); // Scroll position. const int scrollX = item->scrollPoint().x(); - v.append((char*)&scrollX, sizeof(int)); + vector.append((char*)&scrollX, sizeof(int)); const int scrollY = item->scrollPoint().y(); - v.append((char*)&scrollY, sizeof(int)); + vector.append((char*)&scrollY, sizeof(int)); // Document state const WTF::Vector<WTF::String>& docState = item->documentState(); WTF::Vector<WTF::String>::const_iterator end = docState.end(); unsigned stateSize = docState.size(); - LOG_VERBOSE(History, "Writing docState %d", stateSize); - v.append((char*)&stateSize, sizeof(unsigned)); + ALOGV("Writing docState %d", stateSize); + vector.append((char*)&stateSize, sizeof(unsigned)); for (WTF::Vector<WTF::String>::const_iterator i = docState.begin(); i != end; ++i) { - write_string(v, *i); + writeString(vector, *i); } // Is target item - LOG_VERBOSE(History, "Writing isTargetItem %d", item->isTargetItem()); - v.append((char)item->isTargetItem()); + ALOGV("Writing isTargetItem %d", item->isTargetItem()); + vector.append((char)item->isTargetItem()); // Children count unsigned childCount = item->children().size(); - LOG_VERBOSE(History, "Writing childCount %d", childCount); - v.append((char*)&childCount, sizeof(unsigned)); + ALOGV("Writing childCount %d", childCount); + vector.append((char*)&childCount, sizeof(unsigned)); } -static void write_children_recursive(WTF::Vector<char>& v, WebCore::HistoryItem* parent) +static void writeChildrenRecursive(WTF::Vector<char>& vector, WebCore::HistoryItem* parent) { const WebCore::HistoryItemVector& children = parent->children(); WebCore::HistoryItemVector::const_iterator end = children.end(); @@ -472,111 +473,199 @@ static void write_children_recursive(WTF::Vector<char>& v, WebCore::HistoryItem* "Somehow this item has an incorrect parent"); bridge->setParent(parentBridge); } - write_item(v, item); - write_children_recursive(v, item); + writeItem(vector, item); + writeChildrenRecursive(vector, item); } } -static bool read_item_recursive(WebCore::HistoryItem* newItem, +bool readUnsigned(const char*& data, const char* end, unsigned& result, const char* dbgLabel = 0); +bool readInt(const char*& data, const char* end, int& result, const char* dbgLabel = 0); +bool readInt64(const char*& data, const char* end, int64_t& result, const char* dbgLabel = 0); +bool readFloat(const char*& data, const char* end, float& result, const char* dbgLabel = 0); +bool readBool(const char*& data, const char* end, bool& result, const char* dbgLabel = 0); +bool readString(const char*& data, const char* end, String& result, const char* dbgLabel = 0); + +bool readUnsigned(const char*& data, const char* end, unsigned& result, const char* dbgLabel) +{ + // Check if we have enough data left to continue. + if ((end < data) || (static_cast<size_t>(end - data) < sizeof(unsigned))) { + ALOGW("\tNot enough data to read unsigned; end=%p data=%p", end, data); + return false; + } + + memcpy(&result, data, sizeof(unsigned)); + data += sizeof(unsigned); + if (dbgLabel) + ALOGV("Reading %-16s %u", dbgLabel, result); + return true; +} + +bool readInt(const char*& data, const char* end, int& result, const char* dbgLabel) +{ + // Check if we have enough data left to continue. + if ((end < data) || (static_cast<size_t>(end - data) < sizeof(int))) { + ALOGW("\tNot enough data to read int; end=%p data=%p", end, data); + return false; + } + + memcpy(&result, data, sizeof(int)); + data += sizeof(int); + if (dbgLabel) + ALOGV("Reading %-16s %d", dbgLabel, result); + return true; +} + +bool readInt64(const char*& data, const char* end, int64_t& result, const char* dbgLabel) +{ + // Check if we have enough data left to continue. + if ((end < data) || (static_cast<size_t>(end - data) < sizeof(int64_t))) { + ALOGW("\tNot enough data to read int64_t; end=%p data=%p", end, data); + return false; + } + + memcpy(&result, data, sizeof(int64_t)); + data += sizeof(int64_t); + if (dbgLabel) + ALOGV("Reading %-16s %ll", dbgLabel, result); + return true; +} + +bool readFloat(const char*& data, const char* end, float& result, const char* dbgLabel) +{ + // Check if we have enough data left to continue. + if ((end < data) || (static_cast<size_t>(end - data) < sizeof(float))) { + ALOGW("\tNot enough data to read float; end=%p data=%p", end, data); + return false; + } + + memcpy(&result, data, sizeof(float)); + data += sizeof(float); + if (dbgLabel) + ALOGV("Reading %-16s %f", dbgLabel, result); + return true; +} + +// Note that the return value indicates success or failure, while the result +// parameter indicates the read value of the bool +bool readBool(const char*& data, const char* end, bool& result, const char* dbgLabel) +{ + // Check if we have enough data left to continue. + if ((end < data) || (static_cast<size_t>(end - data) < sizeof(char))) { + ALOGW("\tNot enough data to read bool; end=%p data=%p", end, data); + return false; + } + + char c; + memcpy(&c, data, sizeof(char)); + data += sizeof(char); + if (dbgLabel) + ALOGV("Reading %-16s %d", dbgLabel, c); + result = c; + + // Valid bool results are 0 or 1 + if ((c != 0) && (c != 1)) { + ALOGW("\tInvalid value for bool; end=%p data=%p c=%u", end, data, c); + return false; + } + + return true; +} + +bool readString(const char*& data, const char* end, String& result, const char* dbgLabel) +{ + unsigned stringLength; + if (!readUnsigned(data, end, stringLength)) { + ALOGW("Not enough data to read string length; end=%p data=%p", end, data); + return false; + } + + if (dbgLabel) + ALOGV("Reading %-16s %d %.*s", dbgLabel, stringLength, stringLength, data); + + // If length was 0, there will be no string content, but still return true + if (!stringLength) { + result = String(); + return true; + } + + if ((end < data) || ((unsigned)(end - data) < stringLength)) { + ALOGW("\tNot enough data to read content; end=%p data=%p stringLength=%u", end, data, stringLength); + return false; + } + + bool decodeFailed; + static const WebCore::TextEncoding& encoding = WebCore::UTF8Encoding(); + result = encoding.decode(data, stringLength, true, decodeFailed); + if (decodeFailed) { + ALOGW("\tdecode failed, end=%p data=%p stringLength=%u content=\"%s\"", + end, data, stringLength, result.utf8().data()); + // Although an error was reported, the previous implementation did not + // stop here, and debug output of the result, which looks correct, makes + // it unclear just what the error was. + } + + data += stringLength; + return true; +} + +static bool readItemRecursive(WebCore::HistoryItem* newItem, const char** pData, int length) { - if (!pData || length < HISTORY_MIN_SIZE) + if (!pData || length < HISTORY_MIN_SIZE) { + ALOGW("readItemRecursive() bad params; pData=%p length=%d", pData, length); return false; + } - const WebCore::TextEncoding& e = WebCore::UTF8Encoding(); const char* data = *pData; const char* end = data + length; - int sizeofUnsigned = (int)sizeof(unsigned); + String content; // Read the original url - // Read the expected length of the string. - size_t l; - memcpy(&l, data, sizeofUnsigned); - // Increment data pointer by the size of an unsigned int. - data += sizeofUnsigned; - if (l) { - LOG_VERBOSE(History, "Original url %d %.*s", l, l, data); - // If we have a length, check if that length exceeds the data length - // and return null if there is not enough data. - if (data + l < end) - newItem->setOriginalURLString(e.decode(data, l)); - else - return false; - // Increment the data pointer by the length of the string. - data += l; - } - // Check if we have enough data left to continue. - if (end - data < sizeofUnsigned) + if (readString(data, end, content, "Original url")) + newItem->setOriginalURLString(content); + else return false; // Read the url - memcpy(&l, data, sizeofUnsigned); - data += sizeofUnsigned; - if (l) { - LOG_VERBOSE(History, "Url %d %.*s", l, l, data); - if (data + l < end) - newItem->setURLString(e.decode(data, l)); - else - return false; - data += l; - } - if (end - data < sizeofUnsigned) + if (readString(data, end, content, "Url")) + newItem->setURLString(content); + else return false; // Read the title - memcpy(&l, data, sizeofUnsigned); - data += sizeofUnsigned; - if (l) { - LOG_VERBOSE(History, "Title %d %.*s", l, l, data); - if (data + l < end) - newItem->setTitle(e.decode(data, l)); - else - return false; - data += l; - } - if (end - data < sizeofUnsigned) + if (readString(data, end, content, "Title")) + newItem->setTitle(content); + else return false; // Generate a new ResourceRequest object for populating form information. + // Read the form content type WTF::String formContentType; - WTF::RefPtr<WebCore::FormData> formData = NULL; + if (!readString(data, end, formContentType, "Content type")) + return false; - // Read the form content type - memcpy(&l, data, sizeofUnsigned); - data += sizeofUnsigned; - if (l) { - LOG_VERBOSE(History, "Content type %d %.*s", l, l, data); - if (data + l < end) - formContentType = e.decode(data, l); - else - return false; - data += l; - } - if (end - data < sizeofUnsigned) + // Read the form data size + unsigned formDataSize; + if (!readUnsigned(data, end, formDataSize, "Form data size")) return false; // Read the form data - memcpy(&l, data, sizeofUnsigned); - data += sizeofUnsigned; - if (l) { - LOG_VERBOSE(History, "Form data %d %.*s", l, l, data); - if (data + l < end) - formData = WebCore::FormData::create(data, l); - else + WTF::RefPtr<WebCore::FormData> formData; + if (formDataSize) { + ALOGV("Reading Form data %d %.*s", formDataSize, formDataSize, data); + if ((end < data) || ((size_t)(end - data) < formDataSize)) { + ALOGW("\tNot enough data to read form data; returning"); return false; - data += l; - // Read the identifier - { - int64_t id; - int size = (int)sizeof(int64_t); - memcpy(&id, data, size); - data += size; - if (id) - formData->setIdentifier(id); } + formData = WebCore::FormData::create(data, formDataSize); + data += formDataSize; + // Read the identifier + int64_t id; + if (!readInt64(data, end, id, "Form id")) + return false; + if (id) + formData->setIdentifier(id); } - if (end - data < sizeofUnsigned) - return false; // Set up the form info if (formData != NULL) { @@ -588,112 +677,76 @@ static bool read_item_recursive(WebCore::HistoryItem* newItem, } // Read the target - memcpy(&l, data, sizeofUnsigned); - data += sizeofUnsigned; - if (l) { - LOG_VERBOSE(History, "Target %d %.*s", l, l, data); - if (data + l < end) - newItem->setTarget(e.decode(data, l)); - else - return false; - data += l; - } - if (end - data < sizeofUnsigned) + if (readString(data, end, content, "Target")) + newItem->setTarget(content); + else return false; AndroidWebHistoryBridge* bridge = newItem->bridge(); ALOG_ASSERT(bridge, "There should be a bridge object during inflate"); - float fValue; + // Read the screen scale - memcpy(&fValue, data, sizeof(float)); - LOG_VERBOSE(History, "Screen scale %f", fValue); - bridge->setScale(fValue); - data += sizeof(float); - memcpy(&fValue, data, sizeofUnsigned); - LOG_VERBOSE(History, "Text wrap scale %f", fValue); - bridge->setTextWrapScale(fValue); - data += sizeof(float); + float fValue; + if (readFloat(data, end, fValue, "Screen scale")) + bridge->setScale(fValue); + else + return false; - if (end - data < sizeofUnsigned) + // Read the text wrap scale + if (readFloat(data, end, fValue, "Text wrap scale")) + bridge->setTextWrapScale(fValue); + else return false; // Read scroll position. - int scrollX = 0; - memcpy(&scrollX, data, sizeofUnsigned); - data += sizeofUnsigned; - int scrollY = 0; - memcpy(&scrollY, data, sizeofUnsigned); - data += sizeofUnsigned; - newItem->setScrollPoint(IntPoint(scrollX, scrollY)); - - if (end - data < sizeofUnsigned) + int scrollX; + if (!readInt(data, end, scrollX, "Scroll pos x")) + return false; + int scrollY; + if (!readInt(data, end, scrollY, "Scroll pos y")) return false; + newItem->setScrollPoint(IntPoint(scrollX, scrollY)); // Read the document state - memcpy(&l, data, sizeofUnsigned); - LOG_VERBOSE(History, "Document state %d", l); - data += sizeofUnsigned; - if (l) { - // Check if we have enough data to at least parse the sizes of each - // document state string. - if (data + l * sizeofUnsigned >= end) - return false; + unsigned docStateCount; + if (!readUnsigned(data, end, docStateCount, "Doc state count")) + return false; + if (docStateCount) { // Create a new vector and reserve enough space for the document state. WTF::Vector<WTF::String> docState; - docState.reserveCapacity(l); - while (l--) { - // Check each time if we have enough to parse the length of the next - // string. - if (end - data < sizeofUnsigned) - return false; - int strLen; - memcpy(&strLen, data, sizeofUnsigned); - data += sizeofUnsigned; - if (data + strLen < end) - docState.append(e.decode(data, strLen)); + docState.reserveCapacity(docStateCount); + while (docStateCount--) { + // Read a document state string + if (readString(data, end, content, "Document state")) + docState.append(content); else return false; - LOG_VERBOSE(History, "\t\t%d %.*s", strLen, strLen, data); - data += strLen; } newItem->setDocumentState(docState); } - // Check if we have enough to read the next byte - if (data >= end) - return false; // Read is target item - // Cast the value to unsigned char in order to make a negative value larger - // than 1. A value that is not 0 or 1 is a failure. - unsigned char c = (unsigned char)data[0]; - if (c > 1) - return false; - LOG_VERBOSE(History, "Target item %d", c); - newItem->setIsTargetItem((bool)c); - data++; - if (end - data < sizeofUnsigned) + bool c; + if (readBool(data, end, c, "Target item")) + newItem->setIsTargetItem(c); + else return false; // Read the child count - memcpy(&l, data, sizeofUnsigned); - LOG_VERBOSE(History, "Child count %d", l); - data += sizeofUnsigned; + unsigned count; + if (!readUnsigned(data, end, count, "Child count")) + return false; *pData = data; - if (l) { - // Check if we have the minimum amount need to parse l children. - if (data + l * HISTORY_MIN_SIZE >= end) - return false; - while (l--) { + if (count) { + while (count--) { // No need to check the length each time because read_item_recursive // will return null if there isn't enough data left to parse. WTF::RefPtr<WebCore::HistoryItem> child = WebCore::HistoryItem::create(); // Set a bridge that will not call into java. child->setBridge(new WebHistoryItem(static_cast<WebHistoryItem*>(bridge))); // Read the child item. - if (!read_item_recursive(child.get(), pData, end - data)) { - child.clear(); + if (!readItemRecursive(child.get(), pData, end - data)) return false; - } child->bridge()->setActive(); newItem->addChildItem(child); } @@ -709,83 +762,86 @@ static bool read_item_recursive(WebCore::HistoryItem* newItem, // main thread will be incorrect and an assert will fire later. // In conclusion, define UNIT_TEST only if you know what you are doing. #ifdef UNIT_TEST -static void unit_test() +static void unitTest() { ALOGD("Entering history unit test!"); const char* test1 = new char[0]; WTF::RefPtr<WebCore::HistoryItem> item = WebCore::HistoryItem::create(); WebCore::HistoryItem* testItem = item.get(); testItem->setBridge(new WebHistoryItem(0)); - ALOG_ASSERT(!read_item_recursive(testItem, &test1, 0), "0 length array should fail!"); + ALOG_ASSERT(!readItemRecursive(testItem, &test1, 0), "0 length array should fail!"); delete[] test1; const char* test2 = new char[2]; - ALOG_ASSERT(!read_item_recursive(testItem, &test2, 2), "Small array should fail!"); + ALOG_ASSERT(!readItemRecursive(testItem, &test2, 2), "Small array should fail!"); delete[] test2; - ALOG_ASSERT(!read_item_recursive(testItem, NULL, HISTORY_MIN_SIZE), "Null data should fail!"); + ALOG_ASSERT(!readItemRecursive(testItem, NULL, HISTORY_MIN_SIZE), "Null data should fail!"); // Original Url char* test3 = new char[HISTORY_MIN_SIZE]; const char* ptr = (const char*)test3; memset(test3, 0, HISTORY_MIN_SIZE); *(int*)test3 = 4000; - ALOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length originalUrl should fail!"); + ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length originalUrl should fail!"); // Url int offset = 4; memset(test3, 0, HISTORY_MIN_SIZE); ptr = (const char*)test3; *(int*)(test3 + offset) = 4000; - ALOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length url should fail!"); + ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length url should fail!"); // Title offset += 4; memset(test3, 0, HISTORY_MIN_SIZE); ptr = (const char*)test3; *(int*)(test3 + offset) = 4000; - ALOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length title should fail!"); + ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length title should fail!"); // Form content type offset += 4; memset(test3, 0, HISTORY_MIN_SIZE); ptr = (const char*)test3; *(int*)(test3 + offset) = 4000; - ALOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length contentType should fail!"); + ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length contentType should fail!"); // Form data offset += 4; memset(test3, 0, HISTORY_MIN_SIZE); ptr = (const char*)test3; *(int*)(test3 + offset) = 4000; - ALOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length form data should fail!"); + ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length form data should fail!"); // Target offset += 4; memset(test3, 0, HISTORY_MIN_SIZE); ptr = (const char*)test3; *(int*)(test3 + offset) = 4000; - ALOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length target should fail!"); - offset += 4; // Scale + ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length target should fail!"); + offset += 4; // Screen scale + offset += 4; // Text wrap scale + offset += 4; // Scroll pos x + offset += 4; // Scroll pos y // Document state offset += 4; memset(test3, 0, HISTORY_MIN_SIZE); ptr = (const char*)test3; *(int*)(test3 + offset) = 4000; - ALOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length document state should fail!"); + ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length document state should fail!"); // Is target item offset += 1; memset(test3, 0, HISTORY_MIN_SIZE); ptr = (const char*)test3; *(char*)(test3 + offset) = '!'; - ALOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "IsTargetItem should fail with ! as the value!"); + ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE), "IsTargetItem should fail with ! as the value!"); // Child count offset += 4; memset(test3, 0, HISTORY_MIN_SIZE); ptr = (const char*)test3; *(int*)(test3 + offset) = 4000; - ALOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 kids should fail!"); - offset = 36; + ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 kids should fail!"); // Test document state + offset = 40; delete[] test3; test3 = new char[HISTORY_MIN_SIZE + sizeof(unsigned)]; memset(test3, 0, HISTORY_MIN_SIZE + sizeof(unsigned)); ptr = (const char*)test3; *(int*)(test3 + offset) = 1; *(int*)(test3 + offset + 4) = 20; - ALOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE + sizeof(unsigned)), "1 20 length document state string should fail!"); + ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE + sizeof(unsigned)), "1 20 length document state string should fail!"); delete[] test3; test3 = new char[HISTORY_MIN_SIZE + 2 * sizeof(unsigned)]; memset(test3, 0, HISTORY_MIN_SIZE + 2 * sizeof(unsigned)); @@ -793,8 +849,9 @@ static void unit_test() *(int*)(test3 + offset) = 2; *(int*)(test3 + offset + 4) = 0; *(int*)(test3 + offset + 8) = 20; - ALOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE + 2 * sizeof(unsigned) ), "2 20 length document state string should fail!"); + ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE + 2 * sizeof(unsigned) ), "2 20 length document state string should fail!"); delete[] test3; + ALOGD("Leaving history unit test!"); } #endif @@ -818,7 +875,7 @@ int registerWebHistory(JNIEnv* env) // Get notified of all changes to history items. WebCore::notifyHistoryItemChanged = historyItemChanged; #ifdef UNIT_TEST - unit_test(); + unitTest(); #endif // Find WebHistoryItem, its constructor, and the update method. jclass clazz = env->FindClass("android/webkit/WebHistoryItem"); diff --git a/Source/WebKit/android/jni/WebViewCore.cpp b/Source/WebKit/android/jni/WebViewCore.cpp index 6737329..703f177 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" @@ -941,7 +942,7 @@ BaseLayerAndroid* WebViewCore::recordContent(SkRegion* region, SkIPoint* point) DBG_SET_LOG("start"); // If there is a pending style recalculation, just return. if (m_mainFrame->document()->isPendingStyleRecalc()) { - DBG_SET_LOGD("recordContent: pending style recalc, ignoring."); + DBG_SET_LOG("recordContent: pending style recalc, ignoring."); return 0; } float progress = (float) m_mainFrame->page()->progress()->estimatedProgress(); @@ -1640,7 +1641,8 @@ void WebViewCore::updateFrameCacheIfLoading() #endif struct TouchNodeData { - Node* mNode; + Node* mUrlNode; + Node* mInnerNode; IntRect mBounds; }; @@ -1664,136 +1666,135 @@ static IntRect getAbsoluteBoundingBox(Node* node) { } // get the highlight rectangles for the touch point (x, y) with the slop -Vector<IntRect> WebViewCore::getTouchHighlightRects(int x, int y, int slop, - Node** node, HitTestResult* hitTestResult) +AndroidHitTestResult WebViewCore::hitTestAtPoint(int x, int y, int slop, bool doMoveMouse) { - Vector<IntRect> rects; - m_mousePos = IntPoint(x - m_scrollOffsetX, y - m_scrollOffsetY); - *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<RefPtr<Node> >& list = hitTestResult->rectBasedTestResult(); + const ListHashSet<RefPtr<Node> >& 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<TouchNodeData> nodeDataList; - ALOGD("innerNode: %p, %s", hitTestResult->innerNode(), hitTestResult->innerNode()->nodeName().ascii().data()); - ALOGD("innerNonSharedNode: %p, %s", hitTestResult->innerNonSharedNode(), hitTestResult->innerNonSharedNode()->nodeName().ascii().data()); - if (hitTestResult->innerNode() != hitTestResult->innerNonSharedNode() - && hitTestResult->innerNode()->hasTagName(WebCore::HTMLNames::areaTag)) { - TouchNodeData newNode; - HTMLAreaElement* area = static_cast<HTMLAreaElement*>(hitTestResult->innerNode()); - newNode.mNode = area; - newNode.mBounds = area->computeRect(hitTestResult->innerNonSharedNode()->renderer()); - nodeDataList.append(newNode); - } else { - ListHashSet<RefPtr<Node> >::const_iterator last = list.end(); - for (ListHashSet<RefPtr<Node> >::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)) { - 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<HTMLAreaElement*>(hitTestResult.innerNode()); + androidHitResult.hitTestResult().setURLElement(area); + androidHitResult.highlightRects().append(area->computeRect( + hitTestResult.innerNonSharedNode()->renderer())); + return androidHitResult; + } + ListHashSet<RefPtr<Node> >::const_iterator last = list.end(); + for (ListHashSet<RefPtr<Node> >::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<TouchNodeData>::const_iterator nlast = nodeDataList.end(); - for (Vector<TouchNodeData>::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<TouchNodeData>::const_iterator nlast = nodeDataList.end(); + for (Vector<TouchNodeData>::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<const ContainerNode*>(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 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<const ContainerNode*>(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 (!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; @@ -1807,20 +1808,33 @@ Vector<IntRect> WebViewCore::getTouchHighlightRects(int x, int y, int slop, area = a; } } - *node = final.mNode; // now get the node's highlight rectangles in the page coordinate system - if (final.mNode) { + if (final.mUrlNode) { + if (final.mUrlNode->isElementNode()) { + // We found a URL element. Update the hitTestResult + androidHitResult.hitTestResult().setURLElement(static_cast<Element*>(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<IntRect>& 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,36 +1882,36 @@ Vector<IntRect> 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(); newy = rects[0].y(); } - m_mousePos.setX(newx - m_scrollOffsetX); - m_mousePos.setY(newy - m_scrollOffsetY); + moveMouse(m_mainFrame, newx, newy); 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; } } 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)) { - m_mousePos = testRect.center(); - m_mousePos.move(-m_scrollOffsetX, -m_scrollOffsetY); - 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; } /////////////////////////////////////////////////////////////////////////////// @@ -4575,84 +4589,16 @@ static bool ValidNodeAndBounds(JNIEnv* env, jobject obj, jint nativeClass, reinterpret_cast<Node*>(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<WebViewCore*>(nativeClass); if (!viewImpl) return 0; Node* node = 0; - HitTestResult hitTestResult; - Vector<IntRect> 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, "<init>", "(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, "<init>", "()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<IntRect>& rects = result.highlightRects(); + return result.createJavaObject(env); } static void AutoFillForm(JNIEnv* env, jobject obj, jint nativeClass, @@ -4798,7 +4744,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 69b805c..06f6b97 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<IntRect> 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<WebCore::FileChooser> ); diff --git a/Source/WebKit/android/plugins/ANPSoundInterface.cpp b/Source/WebKit/android/plugins/ANPSoundInterface.cpp index a4f192f..12c9176 100644 --- a/Source/WebKit/android/plugins/ANPSoundInterface.cpp +++ b/Source/WebKit/android/plugins/ANPSoundInterface.cpp @@ -71,7 +71,7 @@ static void callbackProc(int event, void* user, void* info) { src = reinterpret_cast<android::AudioTrack::Buffer*>(info); dst.bufferData = src->raw; dst.channelCount = src->channelCount; - dst.format = toANPFormat(src->format); + dst.format = toANPFormat((audio_format_t) src->format); dst.size = src->size; track->mProc(kMoreData_ANPAudioEvent, track->mUser, &dst); // return the updated size field |