summaryrefslogtreecommitdiffstats
path: root/Source/WebKit/android/nav/WebView.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebKit/android/nav/WebView.cpp')
-rw-r--r--Source/WebKit/android/nav/WebView.cpp2673
1 files changed, 2673 insertions, 0 deletions
diff --git a/Source/WebKit/android/nav/WebView.cpp b/Source/WebKit/android/nav/WebView.cpp
new file mode 100644
index 0000000..ff5d73d
--- /dev/null
+++ b/Source/WebKit/android/nav/WebView.cpp
@@ -0,0 +1,2673 @@
+/*
+ * Copyright 2007, 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 "webviewglue"
+
+#include "config.h"
+
+#include "AndroidAnimation.h"
+#include "AndroidLog.h"
+#include "BaseLayerAndroid.h"
+#include "CachedFrame.h"
+#include "CachedNode.h"
+#include "CachedRoot.h"
+#include "DrawExtra.h"
+#include "FindCanvas.h"
+#include "Frame.h"
+#include "GraphicsJNI.h"
+#include "HTMLInputElement.h"
+#include "IntPoint.h"
+#include "IntRect.h"
+#include "LayerAndroid.h"
+#include "Node.h"
+#include "utils/Functor.h"
+#include "private/hwui/DrawGlInfo.h"
+#include "PlatformGraphicsContext.h"
+#include "PlatformString.h"
+#include "ScrollableLayerAndroid.h"
+#include "SelectText.h"
+#include "SkCanvas.h"
+#include "SkDumpCanvas.h"
+#include "SkPicture.h"
+#include "SkRect.h"
+#include "SkTime.h"
+#ifdef ANDROID_INSTRUMENT
+#include "TimeCounter.h"
+#endif
+#include "TilesManager.h"
+#include "WebCoreJni.h"
+#include "WebRequestContext.h"
+#include "WebViewCore.h"
+#include "android_graphics.h"
+
+#ifdef GET_NATIVE_VIEW
+#undef GET_NATIVE_VIEW
+#endif
+
+#define GET_NATIVE_VIEW(env, obj) ((WebView*)env->GetIntField(obj, gWebViewField))
+
+#include <JNIUtility.h>
+#include <JNIHelp.h>
+#include <jni.h>
+#include <android_runtime/android_util_AssetManager.h>
+#include <ui/KeycodeLabels.h>
+#include <utils/AssetManager.h>
+#include <wtf/text/AtomicString.h>
+#include <wtf/text/CString.h>
+
+namespace android {
+
+static jfieldID gWebViewField;
+
+//-------------------------------------
+
+static jmethodID GetJMethod(JNIEnv* env, jclass clazz, const char name[], const char signature[])
+{
+ jmethodID m = env->GetMethodID(clazz, name, signature);
+ LOG_ASSERT(m, "Could not find method %s", name);
+ return m;
+}
+
+//-------------------------------------
+// This class provides JNI for making calls into native code from the UI side
+// of the multi-threaded WebView.
+class WebView
+{
+public:
+enum FrameCachePermission {
+ DontAllowNewer,
+ AllowNewer,
+ AllowNewest
+};
+
+enum DrawExtras { // keep this in sync with WebView.java
+ DrawExtrasNone = 0,
+ DrawExtrasFind = 1,
+ DrawExtrasSelection = 2,
+ DrawExtrasCursorRing = 3
+};
+
+struct JavaGlue {
+ jweak m_obj;
+ jmethodID m_calcOurContentVisibleRectF;
+ jmethodID m_overrideLoading;
+ jmethodID m_scrollBy;
+ jmethodID m_sendMoveFocus;
+ jmethodID m_sendMoveMouse;
+ jmethodID m_sendMoveMouseIfLatest;
+ jmethodID m_sendMotionUp;
+ jmethodID m_domChangedFocus;
+ jmethodID m_getScaledMaxXScroll;
+ jmethodID m_getScaledMaxYScroll;
+ jmethodID m_getVisibleRect;
+ jmethodID m_rebuildWebTextView;
+ jmethodID m_viewInvalidate;
+ jmethodID m_viewInvalidateRect;
+ jmethodID m_postInvalidateDelayed;
+ jmethodID m_inFullScreenMode;
+ jfieldID m_rectLeft;
+ jfieldID m_rectTop;
+ jmethodID m_rectWidth;
+ jmethodID m_rectHeight;
+ jfieldID m_rectFLeft;
+ jfieldID m_rectFTop;
+ jmethodID m_rectFWidth;
+ jmethodID m_rectFHeight;
+ AutoJObject object(JNIEnv* env) {
+ return getRealObject(env, m_obj);
+ }
+} m_javaGlue;
+
+WebView(JNIEnv* env, jobject javaWebView, int viewImpl, WTF::String drawableDir, AssetManager* am) :
+ m_ring((WebViewCore*) viewImpl)
+{
+ jclass clazz = env->FindClass("android/webkit/WebView");
+ // m_javaGlue = new JavaGlue;
+ m_javaGlue.m_obj = env->NewWeakGlobalRef(javaWebView);
+ m_javaGlue.m_scrollBy = GetJMethod(env, clazz, "setContentScrollBy", "(IIZ)Z");
+ m_javaGlue.m_calcOurContentVisibleRectF = GetJMethod(env, clazz, "calcOurContentVisibleRectF", "(Landroid/graphics/RectF;)V");
+ m_javaGlue.m_overrideLoading = GetJMethod(env, clazz, "overrideLoading", "(Ljava/lang/String;)V");
+ m_javaGlue.m_sendMoveFocus = GetJMethod(env, clazz, "sendMoveFocus", "(II)V");
+ m_javaGlue.m_sendMoveMouse = GetJMethod(env, clazz, "sendMoveMouse", "(IIII)V");
+ m_javaGlue.m_sendMoveMouseIfLatest = GetJMethod(env, clazz, "sendMoveMouseIfLatest", "(ZZ)V");
+ m_javaGlue.m_sendMotionUp = GetJMethod(env, clazz, "sendMotionUp", "(IIIII)V");
+ m_javaGlue.m_domChangedFocus = GetJMethod(env, clazz, "domChangedFocus", "()V");
+ m_javaGlue.m_getScaledMaxXScroll = GetJMethod(env, clazz, "getScaledMaxXScroll", "()I");
+ m_javaGlue.m_getScaledMaxYScroll = GetJMethod(env, clazz, "getScaledMaxYScroll", "()I");
+ m_javaGlue.m_getVisibleRect = GetJMethod(env, clazz, "sendOurVisibleRect", "()Landroid/graphics/Rect;");
+ m_javaGlue.m_rebuildWebTextView = GetJMethod(env, clazz, "rebuildWebTextView", "()V");
+ m_javaGlue.m_viewInvalidate = GetJMethod(env, clazz, "viewInvalidate", "()V");
+ m_javaGlue.m_viewInvalidateRect = GetJMethod(env, clazz, "viewInvalidate", "(IIII)V");
+ m_javaGlue.m_postInvalidateDelayed = GetJMethod(env, clazz,
+ "viewInvalidateDelayed", "(JIIII)V");
+ m_javaGlue.m_inFullScreenMode = GetJMethod(env, clazz, "inFullScreenMode", "()Z");
+ env->DeleteLocalRef(clazz);
+
+ jclass rectClass = env->FindClass("android/graphics/Rect");
+ LOG_ASSERT(rectClass, "Could not find Rect class");
+ m_javaGlue.m_rectLeft = env->GetFieldID(rectClass, "left", "I");
+ m_javaGlue.m_rectTop = env->GetFieldID(rectClass, "top", "I");
+ m_javaGlue.m_rectWidth = GetJMethod(env, rectClass, "width", "()I");
+ m_javaGlue.m_rectHeight = GetJMethod(env, rectClass, "height", "()I");
+ env->DeleteLocalRef(rectClass);
+
+ jclass rectClassF = env->FindClass("android/graphics/RectF");
+ LOG_ASSERT(rectClassF, "Could not find RectF class");
+ m_javaGlue.m_rectFLeft = env->GetFieldID(rectClassF, "left", "F");
+ m_javaGlue.m_rectFTop = env->GetFieldID(rectClassF, "top", "F");
+ m_javaGlue.m_rectFWidth = GetJMethod(env, rectClassF, "width", "()F");
+ m_javaGlue.m_rectFHeight = GetJMethod(env, rectClassF, "height", "()F");
+ env->DeleteLocalRef(rectClassF);
+
+ env->SetIntField(javaWebView, gWebViewField, (jint)this);
+ m_viewImpl = (WebViewCore*) viewImpl;
+ m_frameCacheUI = 0;
+ m_navPictureUI = 0;
+ m_generation = 0;
+ m_heightCanMeasure = false;
+ m_lastDx = 0;
+ m_lastDxTime = 0;
+ m_ringAnimationEnd = 0;
+ m_baseLayer = 0;
+ m_glDrawFunctor = 0;
+ if (drawableDir.isEmpty())
+ m_buttonSkin = 0;
+ else
+ m_buttonSkin = new RenderSkinButton(am, drawableDir);
+#if USE(ACCELERATED_COMPOSITING)
+ m_glWebViewState = 0;
+#endif
+}
+
+~WebView()
+{
+ if (m_javaGlue.m_obj)
+ {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->DeleteWeakGlobalRef(m_javaGlue.m_obj);
+ m_javaGlue.m_obj = 0;
+ }
+#if USE(ACCELERATED_COMPOSITING)
+ // We must remove the m_glWebViewState prior to deleting m_baseLayer. If we
+ // do not remove it here, we risk having BaseTiles trying to paint using a
+ // deallocated base layer.
+ stopGL();
+#endif
+ delete m_frameCacheUI;
+ delete m_navPictureUI;
+ SkSafeUnref(m_baseLayer);
+ delete m_glDrawFunctor;
+ delete m_buttonSkin;
+}
+
+void stopGL()
+{
+#if USE(ACCELERATED_COMPOSITING)
+ delete m_glWebViewState;
+ m_glWebViewState = 0;
+#endif
+}
+
+WebViewCore* getWebViewCore() const {
+ return m_viewImpl;
+}
+
+// removes the cursor altogether (e.g., when going to a new page)
+void clearCursor()
+{
+ CachedRoot* root = getFrameCache(AllowNewer);
+ if (!root)
+ return;
+ DBG_NAV_LOG("");
+ m_viewImpl->m_hasCursorBounds = false;
+ root->clearCursor();
+ viewInvalidate();
+}
+
+// leaves the cursor where it is, but suppresses drawing it
+void hideCursor()
+{
+ CachedRoot* root = getFrameCache(AllowNewer);
+ if (!root)
+ return;
+ DBG_NAV_LOG("");
+ hideCursor(root);
+}
+
+void hideCursor(CachedRoot* root)
+{
+ DBG_NAV_LOG("inner");
+ m_viewImpl->m_hasCursorBounds = false;
+ root->hideCursor();
+ viewInvalidate();
+}
+
+#if DUMP_NAV_CACHE
+void debugDump()
+{
+ CachedRoot* root = getFrameCache(DontAllowNewer);
+ if (root)
+ root->mDebug.print();
+}
+#endif
+
+// Traverse our stored array of buttons that are in our picture, and update
+// their subpictures according to their current state.
+// Called from the UI thread. This is the one place in the UI thread where we
+// access the buttons stored in the WebCore thread.
+// hasFocus keeps track of whether the WebView has focus && windowFocus.
+// If not, we do not want to draw the button in a selected or pressed state
+void nativeRecordButtons(bool hasFocus, bool pressed, bool invalidate)
+{
+ bool cursorIsOnButton = false;
+ const CachedFrame* cachedFrame;
+ const CachedNode* cachedCursor = 0;
+ // Lock the mutex, since we now share with the WebCore thread.
+ m_viewImpl->gButtonMutex.lock();
+ if (m_viewImpl->m_buttons.size() && m_buttonSkin) {
+ // FIXME: In a future change, we should keep track of whether the selection
+ // has changed to short circuit (note that we would still need to update
+ // if we received new buttons from the WebCore thread).
+ WebCore::Node* cursor = 0;
+ CachedRoot* root = getFrameCache(DontAllowNewer);
+ if (root) {
+ cachedCursor = root->currentCursor(&cachedFrame);
+ if (cachedCursor)
+ cursor = (WebCore::Node*) cachedCursor->nodePointer();
+ }
+
+ // Traverse the array, and update each button, depending on whether it
+ // is selected.
+ Container* end = m_viewImpl->m_buttons.end();
+ for (Container* ptr = m_viewImpl->m_buttons.begin(); ptr != end; ptr++) {
+ RenderSkinAndroid::State state = RenderSkinAndroid::kNormal;
+ if (ptr->matches(cursor)) {
+ cursorIsOnButton = true;
+ // If the WebView is out of focus/window focus, set the state to
+ // normal, but still keep track of the fact that the selected is a
+ // button
+ if (hasFocus) {
+ if (pressed || m_ring.m_isPressed)
+ state = RenderSkinAndroid::kPressed;
+ else if (SkTime::GetMSecs() < m_ringAnimationEnd)
+ state = RenderSkinAndroid::kFocused;
+ }
+ }
+ ptr->updateFocusState(state, m_buttonSkin);
+ }
+ }
+ m_viewImpl->gButtonMutex.unlock();
+ if (invalidate && cachedCursor && cursorIsOnButton) {
+ const WebCore::IntRect& b = cachedCursor->bounds(cachedFrame);
+ viewInvalidateRect(b.x(), b.y(), b.right(), b.bottom());
+ }
+}
+
+// The caller has already determined that the desired document rect corresponds
+// to the main picture, and not a layer
+void scrollRectOnScreen(const IntRect& rect)
+{
+ if (rect.isEmpty())
+ return;
+ SkRect visible;
+ calcOurContentVisibleRect(&visible);
+ int dx = 0;
+ int left = rect.x();
+ int right = rect.right();
+ if (left < visible.fLeft) {
+ dx = left - visible.fLeft;
+ // Only scroll right if the entire width can fit on screen.
+ } else if (right > visible.fRight && right - left < visible.width()) {
+ dx = right - visible.fRight;
+ }
+ int dy = 0;
+ int top = rect.y();
+ int bottom = rect.bottom();
+ if (top < visible.fTop) {
+ dy = top - visible.fTop;
+ // Only scroll down if the entire height can fit on screen
+ } else if (bottom > visible.fBottom && bottom - top < visible.height()) {
+ dy = bottom - visible.fBottom;
+ }
+ if ((dx|dy) == 0 || !scrollBy(dx, dy))
+ return;
+ viewInvalidate();
+}
+
+void calcOurContentVisibleRect(SkRect* r)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jclass rectClass = env->FindClass("android/graphics/RectF");
+ jmethodID init = env->GetMethodID(rectClass, "<init>", "(FFFF)V");
+ jobject jRect = env->NewObject(rectClass, init, 0, 0, 0, 0);
+ env->CallVoidMethod(m_javaGlue.object(env).get(),
+ m_javaGlue.m_calcOurContentVisibleRectF, jRect);
+ r->fLeft = env->GetFloatField(jRect, m_javaGlue.m_rectFLeft);
+ r->fTop = env->GetFloatField(jRect, m_javaGlue.m_rectFTop);
+ r->fRight = r->fLeft + env->CallFloatMethod(jRect, m_javaGlue.m_rectFWidth);
+ r->fBottom = r->fTop + env->CallFloatMethod(jRect, m_javaGlue.m_rectFHeight);
+ env->DeleteLocalRef(rectClass);
+ env->DeleteLocalRef(jRect);
+ checkException(env);
+}
+
+void resetCursorRing()
+{
+ m_ringAnimationEnd = 0;
+ m_viewImpl->m_hasCursorBounds = false;
+}
+
+bool drawCursorPreamble(CachedRoot* root)
+{
+ const CachedFrame* frame;
+ const CachedNode* node = root->currentCursor(&frame);
+ if (!node) {
+ DBG_NAV_LOGV("%s", "!node");
+ resetCursorRing();
+ return false;
+ }
+ m_ring.setIsButton(node);
+ if (node->isHidden()) {
+ DBG_NAV_LOG("node->isHidden()");
+ m_viewImpl->m_hasCursorBounds = false;
+ return false;
+ }
+#if USE(ACCELERATED_COMPOSITING)
+ if (node->isInLayer() && root->rootLayer()) {
+ LayerAndroid* layer = const_cast<LayerAndroid*>(root->rootLayer());
+ SkRect visible;
+ calcOurContentVisibleRect(&visible);
+ layer->updateFixedLayersPositions(visible);
+ layer->updatePositions();
+ }
+#endif
+ setVisibleRect(root);
+ m_ring.m_root = root;
+ m_ring.m_frame = frame;
+ m_ring.m_node = node;
+ SkMSec time = SkTime::GetMSecs();
+ m_ring.m_isPressed = time < m_ringAnimationEnd
+ && m_ringAnimationEnd != UINT_MAX;
+ return true;
+}
+
+void drawCursorPostamble()
+{
+ if (m_ringAnimationEnd == UINT_MAX)
+ return;
+ SkMSec time = SkTime::GetMSecs();
+ if (time < m_ringAnimationEnd) {
+ // views assume that inval bounds coordinates are non-negative
+ WebCore::IntRect invalBounds(0, 0, INT_MAX, INT_MAX);
+ invalBounds.intersect(m_ring.m_absBounds);
+ postInvalidateDelayed(m_ringAnimationEnd - time, invalBounds);
+ } else {
+ hideCursor(const_cast<CachedRoot*>(m_ring.m_root));
+ }
+}
+
+bool drawGL(WebCore::IntRect& viewRect, WebCore::IntRect* invalRect, WebCore::IntRect& webViewRect,
+ int titleBarHeight, WebCore::IntRect& clip, float scale, int extras)
+{
+#if USE(ACCELERATED_COMPOSITING)
+ if (!m_baseLayer || inFullScreenMode())
+ return false;
+
+ if (!m_glWebViewState) {
+ m_glWebViewState = new GLWebViewState(&m_viewImpl->gButtonMutex);
+ if (m_baseLayer->content()) {
+ SkRegion region;
+ SkIRect rect;
+ rect.set(0, 0, m_baseLayer->content()->width(), m_baseLayer->content()->height());
+ region.setRect(rect);
+ m_glWebViewState->setBaseLayer(m_baseLayer, region, false, false);
+ }
+ }
+
+ CachedRoot* root = getFrameCache(AllowNewer);
+ if (!root) {
+ DBG_NAV_LOG("!root");
+ if (extras == DrawExtrasCursorRing)
+ resetCursorRing();
+ return false;
+ }
+ DrawExtra* extra = 0;
+ switch (extras) {
+ case DrawExtrasFind:
+ extra = &m_findOnPage;
+ break;
+ case DrawExtrasSelection:
+ extra = &m_selectText;
+ break;
+ case DrawExtrasCursorRing:
+ if (drawCursorPreamble(root) && m_ring.setup()) {
+ if (!m_ring.m_isButton)
+ extra = &m_ring;
+ drawCursorPostamble();
+ }
+ break;
+ default:
+ ;
+ }
+
+ unsigned int pic = m_glWebViewState->currentPictureCounter();
+
+ SkPicture picture;
+ IntRect rect(0, 0, 0, 0);
+ bool allowSame = false;
+ m_glWebViewState->resetRings();
+ if (extra) {
+ if (extra == &m_ring) {
+ m_glWebViewState->setRings(m_ring.rings(), m_ring.m_isPressed);
+ } else {
+ LayerAndroid mainPicture(m_navPictureUI);
+ PictureSet* content = m_baseLayer->content();
+ SkCanvas* canvas = picture.beginRecording(content->width(),
+ content->height());
+ extra->draw(canvas, &mainPicture, &rect);
+ picture.endRecording();
+ }
+ } else if (extras == DrawExtrasCursorRing && m_ring.m_isButton) {
+ const CachedFrame* cachedFrame;
+ const CachedNode* cachedCursor = root->currentCursor(&cachedFrame);
+ if (cachedCursor) {
+ rect = cachedCursor->bounds(cachedFrame);
+ allowSame = true;
+ }
+ }
+ m_glWebViewState->setExtra(m_baseLayer, picture, rect, allowSame);
+
+ LayerAndroid* compositeLayer = compositeRoot();
+ if (compositeLayer)
+ compositeLayer->setExtra(extra);
+
+ SkRect visibleRect;
+ calcOurContentVisibleRect(&visibleRect);
+ bool ret = m_glWebViewState->drawGL(viewRect, visibleRect, invalRect,
+ webViewRect, titleBarHeight, clip, scale);
+ if (ret || m_glWebViewState->currentPictureCounter() != pic)
+ return true;
+#endif
+ return false;
+}
+
+PictureSet* draw(SkCanvas* canvas, SkColor bgColor, int extras, bool split)
+{
+ PictureSet* ret = 0;
+ if (!m_baseLayer) {
+ canvas->drawColor(bgColor);
+ return ret;
+ }
+
+ // draw the content of the base layer first
+ PictureSet* content = m_baseLayer->content();
+ int sc = canvas->save(SkCanvas::kClip_SaveFlag);
+ canvas->clipRect(SkRect::MakeLTRB(0, 0, content->width(),
+ content->height()), SkRegion::kDifference_Op);
+ canvas->drawColor(bgColor);
+ canvas->restoreToCount(sc);
+ if (content->draw(canvas))
+ ret = split ? new PictureSet(*content) : 0;
+
+ CachedRoot* root = getFrameCache(AllowNewer);
+ if (!root) {
+ DBG_NAV_LOG("!root");
+ if (extras == DrawExtrasCursorRing)
+ resetCursorRing();
+ return ret;
+ }
+ LayerAndroid mainPicture(m_navPictureUI);
+ DrawExtra* extra = 0;
+ switch (extras) {
+ case DrawExtrasFind:
+ extra = &m_findOnPage;
+ break;
+ case DrawExtrasSelection:
+ extra = &m_selectText;
+ break;
+ case DrawExtrasCursorRing:
+ if (drawCursorPreamble(root) && m_ring.setup()) {
+ if (!m_ring.m_isButton)
+ extra = &m_ring;
+ drawCursorPostamble();
+ }
+ break;
+ default:
+ ;
+ }
+ if (extra) {
+ IntRect dummy; // inval area, unused for now
+ extra->draw(canvas, &mainPicture, &dummy);
+ }
+#if USE(ACCELERATED_COMPOSITING)
+ LayerAndroid* compositeLayer = compositeRoot();
+ if (!compositeLayer)
+ return ret;
+ compositeLayer->setExtra(extra);
+ SkRect visible;
+ calcOurContentVisibleRect(&visible);
+ // call this to be sure we've adjusted for any scrolling or animations
+ // before we actually draw
+ compositeLayer->updateFixedLayersPositions(visible);
+ compositeLayer->updatePositions();
+ // We have to set the canvas' matrix on the base layer
+ // (to have fixed layers work as intended)
+ SkAutoCanvasRestore restore(canvas, true);
+ m_baseLayer->setMatrix(canvas->getTotalMatrix());
+ canvas->resetMatrix();
+ m_baseLayer->draw(canvas);
+#endif
+ return ret;
+}
+
+
+bool cursorIsTextInput(FrameCachePermission allowNewer)
+{
+ CachedRoot* root = getFrameCache(allowNewer);
+ if (!root) {
+ DBG_NAV_LOG("!root");
+ return false;
+ }
+ const CachedNode* cursor = root->currentCursor();
+ if (!cursor) {
+ DBG_NAV_LOG("!cursor");
+ return false;
+ }
+ DBG_NAV_LOGD("%s", cursor->isTextInput() ? "true" : "false");
+ return cursor->isTextInput();
+}
+
+void cursorRingBounds(WebCore::IntRect* bounds)
+{
+ DBG_NAV_LOGD("%s", "");
+ CachedRoot* root = getFrameCache(DontAllowNewer);
+ if (root) {
+ const CachedFrame* cachedFrame;
+ const CachedNode* cachedNode = root->currentCursor(&cachedFrame);
+ if (cachedNode) {
+ *bounds = cachedNode->cursorRingBounds(cachedFrame);
+ DBG_NAV_LOGD("bounds={%d,%d,%d,%d}", bounds->x(), bounds->y(),
+ bounds->width(), bounds->height());
+ return;
+ }
+ }
+ *bounds = WebCore::IntRect(0, 0, 0, 0);
+}
+
+void fixCursor()
+{
+ m_viewImpl->gCursorBoundsMutex.lock();
+ bool hasCursorBounds = m_viewImpl->m_hasCursorBounds;
+ IntRect bounds = m_viewImpl->m_cursorBounds;
+ m_viewImpl->gCursorBoundsMutex.unlock();
+ if (!hasCursorBounds)
+ return;
+ int x, y;
+ const CachedFrame* frame;
+ const CachedNode* node = m_frameCacheUI->findAt(bounds, &frame, &x, &y, true);
+ if (!node)
+ return;
+ // require that node have approximately the same bounds (+/- 4) and the same
+ // center (+/- 2)
+ IntPoint oldCenter = IntPoint(bounds.x() + (bounds.width() >> 1),
+ bounds.y() + (bounds.height() >> 1));
+ IntRect newBounds = node->bounds(frame);
+ IntPoint newCenter = IntPoint(newBounds.x() + (newBounds.width() >> 1),
+ newBounds.y() + (newBounds.height() >> 1));
+ DBG_NAV_LOGD("oldCenter=(%d,%d) newCenter=(%d,%d)"
+ " bounds=(%d,%d,w=%d,h=%d) newBounds=(%d,%d,w=%d,h=%d)",
+ oldCenter.x(), oldCenter.y(), newCenter.x(), newCenter.y(),
+ bounds.x(), bounds.y(), bounds.width(), bounds.height(),
+ newBounds.x(), newBounds.y(), newBounds.width(), newBounds.height());
+ if (abs(oldCenter.x() - newCenter.x()) > 2)
+ return;
+ if (abs(oldCenter.y() - newCenter.y()) > 2)
+ return;
+ if (abs(bounds.x() - newBounds.x()) > 4)
+ return;
+ if (abs(bounds.y() - newBounds.y()) > 4)
+ return;
+ if (abs(bounds.right() - newBounds.right()) > 4)
+ return;
+ if (abs(bounds.bottom() - newBounds.bottom()) > 4)
+ return;
+ DBG_NAV_LOGD("node=%p frame=%p x=%d y=%d bounds=(%d,%d,w=%d,h=%d)",
+ node, frame, x, y, bounds.x(), bounds.y(), bounds.width(),
+ bounds.height());
+ m_frameCacheUI->setCursor(const_cast<CachedFrame*>(frame),
+ const_cast<CachedNode*>(node));
+}
+
+CachedRoot* getFrameCache(FrameCachePermission allowNewer)
+{
+ if (!m_viewImpl->m_updatedFrameCache) {
+ DBG_NAV_LOGV("%s", "!m_viewImpl->m_updatedFrameCache");
+ return m_frameCacheUI;
+ }
+ if (allowNewer == DontAllowNewer && m_viewImpl->m_lastGeneration < m_generation) {
+ DBG_NAV_LOGD("allowNewer==DontAllowNewer m_viewImpl->m_lastGeneration=%d"
+ " < m_generation=%d", m_viewImpl->m_lastGeneration, m_generation);
+ return m_frameCacheUI;
+ }
+ DBG_NAV_LOGD("%s", "m_viewImpl->m_updatedFrameCache == true");
+ const CachedFrame* oldCursorFrame;
+ const CachedNode* oldCursorNode = m_frameCacheUI ?
+ m_frameCacheUI->currentCursor(&oldCursorFrame) : 0;
+#if USE(ACCELERATED_COMPOSITING)
+ int layerId = -1;
+ if (oldCursorNode && oldCursorNode->isInLayer()) {
+ const LayerAndroid* cursorLayer = oldCursorFrame->layer(oldCursorNode)
+ ->layer(m_frameCacheUI->rootLayer());
+ if (cursorLayer)
+ layerId = cursorLayer->uniqueId();
+ }
+#endif
+ // get id from old layer and use to find new layer
+ bool oldFocusIsTextInput = false;
+ void* oldFocusNodePointer = 0;
+ if (m_frameCacheUI) {
+ const CachedNode* oldFocus = m_frameCacheUI->currentFocus();
+ if (oldFocus) {
+ oldFocusIsTextInput = oldFocus->isTextInput();
+ oldFocusNodePointer = oldFocus->nodePointer();
+ }
+ }
+ m_viewImpl->gFrameCacheMutex.lock();
+ delete m_frameCacheUI;
+ SkSafeUnref(m_navPictureUI);
+ m_viewImpl->m_updatedFrameCache = false;
+ m_frameCacheUI = m_viewImpl->m_frameCacheKit;
+ m_navPictureUI = m_viewImpl->m_navPictureKit;
+ m_viewImpl->m_frameCacheKit = 0;
+ m_viewImpl->m_navPictureKit = 0;
+ m_viewImpl->gFrameCacheMutex.unlock();
+ if (m_frameCacheUI)
+ m_frameCacheUI->setRootLayer(compositeRoot());
+#if USE(ACCELERATED_COMPOSITING)
+ if (layerId >= 0) {
+ SkRect visible;
+ calcOurContentVisibleRect(&visible);
+ LayerAndroid* layer = const_cast<LayerAndroid*>(
+ m_frameCacheUI->rootLayer());
+ if (layer) {
+ layer->updateFixedLayersPositions(visible);
+ layer->updatePositions();
+ }
+ }
+#endif
+ fixCursor();
+ if (oldFocusIsTextInput) {
+ const CachedNode* newFocus = m_frameCacheUI->currentFocus();
+ if (newFocus && oldFocusNodePointer != newFocus->nodePointer()
+ && newFocus->isTextInput()
+ && newFocus != m_frameCacheUI->currentCursor()) {
+ // The focus has changed. We may need to update things.
+ LOG_ASSERT(m_javaGlue.m_obj, "A java object was not associated with this native WebView!");
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue.object(env).get(),
+ m_javaGlue.m_domChangedFocus);
+ checkException(env);
+ }
+ }
+ if (oldCursorNode && (!m_frameCacheUI || !m_frameCacheUI->currentCursor()))
+ viewInvalidate(); // redraw in case cursor ring is still visible
+ return m_frameCacheUI;
+}
+
+int getScaledMaxXScroll()
+{
+ LOG_ASSERT(m_javaGlue.m_obj, "A java object was not associated with this native WebView!");
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ int result = env->CallIntMethod(m_javaGlue.object(env).get(), m_javaGlue.m_getScaledMaxXScroll);
+ checkException(env);
+ return result;
+}
+
+int getScaledMaxYScroll()
+{
+ LOG_ASSERT(m_javaGlue.m_obj, "A java object was not associated with this native WebView!");
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ int result = env->CallIntMethod(m_javaGlue.object(env).get(), m_javaGlue.m_getScaledMaxYScroll);
+ checkException(env);
+ return result;
+}
+
+IntRect getVisibleRect()
+{
+ IntRect rect;
+ LOG_ASSERT(m_javaGlue.m_obj, "A java object was not associated with this native WebView!");
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jobject jRect = env->CallObjectMethod(m_javaGlue.object(env).get(), m_javaGlue.m_getVisibleRect);
+ checkException(env);
+ rect.setX(env->GetIntField(jRect, m_javaGlue.m_rectLeft));
+ checkException(env);
+ rect.setY(env->GetIntField(jRect, m_javaGlue.m_rectTop));
+ checkException(env);
+ rect.setWidth(env->CallIntMethod(jRect, m_javaGlue.m_rectWidth));
+ checkException(env);
+ rect.setHeight(env->CallIntMethod(jRect, m_javaGlue.m_rectHeight));
+ checkException(env);
+ env->DeleteLocalRef(jRect);
+ checkException(env);
+ return rect;
+}
+
+static CachedFrame::Direction KeyToDirection(int32_t keyCode)
+{
+ switch (keyCode) {
+ case AKEYCODE_DPAD_RIGHT:
+ DBG_NAV_LOGD("keyCode=%s", "right");
+ return CachedFrame::RIGHT;
+ case AKEYCODE_DPAD_LEFT:
+ DBG_NAV_LOGD("keyCode=%s", "left");
+ return CachedFrame::LEFT;
+ case AKEYCODE_DPAD_DOWN:
+ DBG_NAV_LOGD("keyCode=%s", "down");
+ return CachedFrame::DOWN;
+ case AKEYCODE_DPAD_UP:
+ DBG_NAV_LOGD("keyCode=%s", "up");
+ return CachedFrame::UP;
+ default:
+ DBG_NAV_LOGD("bad key %d sent", keyCode);
+ return CachedFrame::UNINITIALIZED;
+ }
+}
+
+WTF::String imageURI(int x, int y)
+{
+ const CachedRoot* root = getFrameCache(DontAllowNewer);
+ return root ? root->imageURI(x, y) : WTF::String();
+}
+
+bool cursorWantsKeyEvents()
+{
+ const CachedRoot* root = getFrameCache(DontAllowNewer);
+ if (root) {
+ const CachedNode* focus = root->currentCursor();
+ if (focus)
+ return focus->wantsKeyEvents();
+ }
+ return false;
+}
+
+
+/* returns true if the key had no effect (neither scrolled nor changed cursor) */
+bool moveCursor(int keyCode, int count, bool ignoreScroll)
+{
+ CachedRoot* root = getFrameCache(AllowNewer);
+ if (!root) {
+ DBG_NAV_LOG("!root");
+ return true;
+ }
+
+ m_viewImpl->m_moveGeneration++;
+ CachedFrame::Direction direction = KeyToDirection(keyCode);
+ const CachedFrame* cachedFrame, * oldFrame = 0;
+ const CachedNode* cursor = root->currentCursor(&oldFrame);
+ WebCore::IntPoint cursorLocation = root->cursorLocation();
+ DBG_NAV_LOGD("old cursor %d (nativeNode=%p) cursorLocation={%d, %d}",
+ cursor ? cursor->index() : 0,
+ cursor ? cursor->nodePointer() : 0, cursorLocation.x(), cursorLocation.y());
+ WebCore::IntRect visibleRect = setVisibleRect(root);
+ int xMax = getScaledMaxXScroll();
+ int yMax = getScaledMaxYScroll();
+ root->setMaxScroll(xMax, yMax);
+ const CachedNode* cachedNode = 0;
+ int dx = 0;
+ int dy = 0;
+ int counter = count;
+ while (--counter >= 0) {
+ WebCore::IntPoint scroll = WebCore::IntPoint(0, 0);
+ cachedNode = root->moveCursor(direction, &cachedFrame, &scroll);
+ dx += scroll.x();
+ dy += scroll.y();
+ }
+ DBG_NAV_LOGD("new cursor %d (nativeNode=%p) cursorLocation={%d, %d}"
+ "bounds={%d,%d,w=%d,h=%d}", cachedNode ? cachedNode->index() : 0,
+ cachedNode ? cachedNode->nodePointer() : 0,
+ root->cursorLocation().x(), root->cursorLocation().y(),
+ cachedNode ? cachedNode->bounds(cachedFrame).x() : 0,
+ cachedNode ? cachedNode->bounds(cachedFrame).y() : 0,
+ cachedNode ? cachedNode->bounds(cachedFrame).width() : 0,
+ cachedNode ? cachedNode->bounds(cachedFrame).height() : 0);
+ // If !m_heightCanMeasure (such as in the browser), we want to scroll no
+ // matter what
+ if (!ignoreScroll && (!m_heightCanMeasure ||
+ !cachedNode ||
+ (cursor && cursor->nodePointer() == cachedNode->nodePointer())))
+ {
+ if (count == 1 && dx != 0 && dy == 0 && -m_lastDx == dx &&
+ SkTime::GetMSecs() - m_lastDxTime < 1000)
+ root->checkForJiggle(&dx);
+ DBG_NAV_LOGD("scrollBy %d,%d", dx, dy);
+ if ((dx | dy))
+ this->scrollBy(dx, dy);
+ m_lastDx = dx;
+ m_lastDxTime = SkTime::GetMSecs();
+ }
+ bool result = false;
+ if (cachedNode) {
+ showCursorUntimed();
+ m_viewImpl->updateCursorBounds(root, cachedFrame, cachedNode);
+ root->setCursor(const_cast<CachedFrame*>(cachedFrame),
+ const_cast<CachedNode*>(cachedNode));
+ const CachedNode* focus = root->currentFocus();
+ bool clearTextEntry = cachedNode != focus && focus
+ && cachedNode->nodePointer() != focus->nodePointer() && focus->isTextInput();
+ // Stop painting the caret if the old focus was a text input and so is the new cursor.
+ bool stopPaintingCaret = clearTextEntry && cachedNode->wantsKeyEvents();
+ sendMoveMouseIfLatest(clearTextEntry, stopPaintingCaret);
+ } else {
+ int docHeight = root->documentHeight();
+ int docWidth = root->documentWidth();
+ if (visibleRect.bottom() + dy > docHeight)
+ dy = docHeight - visibleRect.bottom();
+ else if (visibleRect.y() + dy < 0)
+ dy = -visibleRect.y();
+ if (visibleRect.right() + dx > docWidth)
+ dx = docWidth - visibleRect.right();
+ else if (visibleRect.x() < 0)
+ dx = -visibleRect.x();
+ result = direction == CachedFrame::LEFT ? dx >= 0 :
+ direction == CachedFrame::RIGHT ? dx <= 0 :
+ direction == CachedFrame::UP ? dy >= 0 : dy <= 0;
+ }
+ return result;
+}
+
+void notifyProgressFinished()
+{
+ DBG_NAV_LOGD("cursorIsTextInput=%d", cursorIsTextInput(DontAllowNewer));
+ rebuildWebTextView();
+#if DEBUG_NAV_UI
+ if (m_frameCacheUI) {
+ const CachedNode* focus = m_frameCacheUI->currentFocus();
+ DBG_NAV_LOGD("focus %d (nativeNode=%p)",
+ focus ? focus->index() : 0,
+ focus ? focus->nodePointer() : 0);
+ }
+#endif
+}
+
+const CachedNode* findAt(CachedRoot* root, const WebCore::IntRect& rect,
+ const CachedFrame** framePtr, int* rxPtr, int* ryPtr)
+{
+ *rxPtr = 0;
+ *ryPtr = 0;
+ *framePtr = 0;
+ if (!root)
+ return 0;
+ setVisibleRect(root);
+ return root->findAt(rect, framePtr, rxPtr, ryPtr, true);
+}
+
+IntRect setVisibleRect(CachedRoot* root)
+{
+ IntRect visibleRect = getVisibleRect();
+ DBG_NAV_LOGD("getVisibleRect %d,%d,%d,%d",
+ visibleRect.x(), visibleRect.y(), visibleRect.width(), visibleRect.height());
+ root->setVisibleRect(visibleRect);
+ return visibleRect;
+}
+
+void selectBestAt(const WebCore::IntRect& rect)
+{
+ const CachedFrame* frame;
+ int rx, ry;
+ CachedRoot* root = getFrameCache(AllowNewer);
+ if (!root)
+ return;
+ const CachedNode* node = findAt(root, rect, &frame, &rx, &ry);
+ if (!node) {
+ DBG_NAV_LOGD("no nodes found root=%p", root);
+ root->rootHistory()->setMouseBounds(rect);
+ m_viewImpl->m_hasCursorBounds = false;
+ root->setCursor(0, 0);
+ viewInvalidate();
+ } else {
+ DBG_NAV_LOGD("CachedNode:%p (%d)", node, node->index());
+ WebCore::IntRect bounds = node->bounds(frame);
+ root->rootHistory()->setMouseBounds(bounds);
+ m_viewImpl->updateCursorBounds(root, frame, node);
+ showCursorTimed();
+ root->setCursor(const_cast<CachedFrame*>(frame),
+ const_cast<CachedNode*>(node));
+ }
+ sendMoveMouseIfLatest(false, false);
+}
+
+const CachedNode* m_cacheHitNode;
+const CachedFrame* m_cacheHitFrame;
+
+bool pointInNavCache(int x, int y, int slop)
+{
+ CachedRoot* root = getFrameCache(AllowNewer);
+ if (!root)
+ return false;
+ IntRect rect = IntRect(x - slop, y - slop, slop * 2, slop * 2);
+ int rx, ry;
+ return (m_cacheHitNode = findAt(root, rect, &m_cacheHitFrame, &rx, &ry));
+}
+
+bool motionUp(int x, int y, int slop)
+{
+ bool pageScrolled = false;
+ IntRect rect = IntRect(x - slop, y - slop, slop * 2, slop * 2);
+ int rx, ry;
+ CachedRoot* root = getFrameCache(AllowNewer);
+ if (!root)
+ return 0;
+ const CachedFrame* frame = 0;
+ const CachedNode* result = findAt(root, rect, &frame, &rx, &ry);
+ CachedHistory* history = root->rootHistory();
+ if (!result) {
+ DBG_NAV_LOGD("no nodes found root=%p", root);
+ history->setNavBounds(rect);
+ m_viewImpl->m_hasCursorBounds = false;
+ root->hideCursor();
+ int dx = root->checkForCenter(x, y);
+ if (dx) {
+ scrollBy(dx, 0);
+ pageScrolled = true;
+ }
+ sendMotionUp(frame ? (WebCore::Frame*) frame->framePointer() : 0,
+ 0, x, y);
+ viewInvalidate();
+ return pageScrolled;
+ }
+ DBG_NAV_LOGD("CachedNode:%p (%d) x=%d y=%d rx=%d ry=%d", result,
+ result->index(), x, y, rx, ry);
+ WebCore::IntRect navBounds = WebCore::IntRect(rx, ry, 1, 1);
+ history->setNavBounds(navBounds);
+ history->setMouseBounds(navBounds);
+ m_viewImpl->updateCursorBounds(root, frame, result);
+ root->setCursor(const_cast<CachedFrame*>(frame),
+ const_cast<CachedNode*>(result));
+ if (result->isSyntheticLink())
+ overrideUrlLoading(result->getExport());
+ else {
+ sendMotionUp(
+ (WebCore::Frame*) frame->framePointer(),
+ (WebCore::Node*) result->nodePointer(), rx, ry);
+ }
+ if (result->isTextInput() || result->isSelect()
+ || result->isContentEditable()) {
+ showCursorUntimed();
+ } else
+ showCursorTimed();
+ return pageScrolled;
+}
+
+#if USE(ACCELERATED_COMPOSITING)
+static const ScrollableLayerAndroid* findScrollableLayer(
+ const LayerAndroid* parent, int x, int y, SkIRect* foundBounds) {
+ SkRect bounds;
+ parent->bounds(&bounds);
+ // Check the parent bounds first; this will clip to within a masking layer's
+ // bounds.
+ if (parent->masksToBounds() && !bounds.contains(x, y))
+ return 0;
+ // Move the hit test local to parent.
+ x -= bounds.fLeft;
+ y -= bounds.fTop;
+ int count = parent->countChildren();
+ while (count--) {
+ const LayerAndroid* child = parent->getChild(count);
+ const ScrollableLayerAndroid* result = findScrollableLayer(child, x, y,
+ foundBounds);
+ if (result) {
+ foundBounds->offset(bounds.fLeft, bounds.fTop);
+ if (parent->masksToBounds()) {
+ if (bounds.width() < foundBounds->width())
+ foundBounds->fRight = foundBounds->fLeft + bounds.width();
+ if (bounds.height() < foundBounds->height())
+ foundBounds->fBottom = foundBounds->fTop + bounds.height();
+ }
+ return result;
+ }
+ }
+ if (parent->contentIsScrollable()) {
+ foundBounds->set(0, 0, bounds.width(), bounds.height());
+ return static_cast<const ScrollableLayerAndroid*>(parent);
+ }
+ return 0;
+}
+#endif
+
+int scrollableLayer(int x, int y, SkIRect* layerRect, SkIRect* bounds)
+{
+#if USE(ACCELERATED_COMPOSITING)
+ const LayerAndroid* layerRoot = compositeRoot();
+ if (!layerRoot)
+ return 0;
+ const ScrollableLayerAndroid* result = findScrollableLayer(layerRoot, x, y,
+ bounds);
+ if (result) {
+ result->getScrollRect(layerRect);
+ return result->uniqueId();
+ }
+#endif
+ return 0;
+}
+
+int getBlockLeftEdge(int x, int y, float scale)
+{
+ CachedRoot* root = getFrameCache(AllowNewer);
+ if (root)
+ return root->getBlockLeftEdge(x, y, scale);
+ return -1;
+}
+
+void overrideUrlLoading(const WTF::String& url)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jstring jName = wtfStringToJstring(env, url);
+ env->CallVoidMethod(m_javaGlue.object(env).get(),
+ m_javaGlue.m_overrideLoading, jName);
+ env->DeleteLocalRef(jName);
+}
+
+void setFindIsUp(bool up)
+{
+ DBG_NAV_LOGD("up=%d", up);
+ m_viewImpl->m_findIsUp = up;
+}
+
+void setFindIsEmpty()
+{
+ DBG_NAV_LOG("");
+ m_findOnPage.clearCurrentLocation();
+}
+
+void showCursorTimed()
+{
+ DBG_NAV_LOG("");
+ m_ringAnimationEnd = SkTime::GetMSecs() + 500;
+ viewInvalidate();
+}
+
+void showCursorUntimed()
+{
+ DBG_NAV_LOG("");
+ m_ring.m_isPressed = false;
+ m_ringAnimationEnd = UINT_MAX;
+ viewInvalidate();
+}
+
+void setHeightCanMeasure(bool measure)
+{
+ m_heightCanMeasure = measure;
+}
+
+String getSelection()
+{
+ return m_selectText.getSelection();
+}
+
+void moveSelection(int x, int y)
+{
+ m_selectText.moveSelection(getVisibleRect(), x, y);
+}
+
+IntPoint selectableText()
+{
+ const CachedRoot* root = getFrameCache(DontAllowNewer);
+ if (!root)
+ return IntPoint(0, 0);
+ return m_selectText.selectableText(root);
+}
+
+void selectAll()
+{
+ m_selectText.selectAll();
+}
+
+int selectionX()
+{
+ return m_selectText.selectionX();
+}
+
+int selectionY()
+{
+ return m_selectText.selectionY();
+}
+
+void resetSelection()
+{
+ m_selectText.reset();
+}
+
+bool startSelection(int x, int y)
+{
+ const CachedRoot* root = getFrameCache(DontAllowNewer);
+ if (!root)
+ return false;
+ return m_selectText.startSelection(root, getVisibleRect(), x, y);
+}
+
+bool wordSelection(int x, int y)
+{
+ const CachedRoot* root = getFrameCache(DontAllowNewer);
+ if (!root)
+ return false;
+ return m_selectText.wordSelection(root, getVisibleRect(), x, y);
+}
+
+bool extendSelection(int x, int y)
+{
+ m_selectText.extendSelection(getVisibleRect(), x, y);
+ return true;
+}
+
+bool hitSelection(int x, int y)
+{
+ return m_selectText.hitSelection(x, y);
+}
+
+void setExtendSelection()
+{
+ m_selectText.setExtendSelection(true);
+}
+
+void setSelectionPointer(bool set, float scale, int x, int y)
+{
+ m_selectText.setDrawPointer(set);
+ if (!set)
+ return;
+ m_selectText.m_inverseScale = scale;
+ m_selectText.m_selectX = x;
+ m_selectText.m_selectY = y;
+}
+
+void sendMoveFocus(WebCore::Frame* framePtr, WebCore::Node* nodePtr)
+{
+ DBG_NAV_LOGD("framePtr=%p nodePtr=%p", framePtr, nodePtr);
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue.object(env).get(),
+ m_javaGlue.m_sendMoveFocus, (jint) framePtr, (jint) nodePtr);
+ checkException(env);
+}
+
+void sendMoveMouse(WebCore::Frame* framePtr, WebCore::Node* nodePtr, int x, int y)
+{
+ DBG_NAV_LOGD("framePtr=%p nodePtr=%p x=%d y=%d", framePtr, nodePtr, x, y);
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue.object(env).get(), m_javaGlue.m_sendMoveMouse,
+ (jint) framePtr, (jint) nodePtr, x, y);
+ checkException(env);
+}
+
+void sendMoveMouseIfLatest(bool clearTextEntry, bool stopPaintingCaret)
+{
+ LOG_ASSERT(m_javaGlue.m_obj, "A java object was not associated with this native WebView!");
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue.object(env).get(),
+ m_javaGlue.m_sendMoveMouseIfLatest, clearTextEntry, stopPaintingCaret);
+ checkException(env);
+}
+
+void sendMotionUp(
+ WebCore::Frame* framePtr, WebCore::Node* nodePtr, int x, int y)
+{
+ m_viewImpl->m_touchGeneration = ++m_generation;
+ DBG_NAV_LOGD("m_generation=%d framePtr=%p nodePtr=%p x=%d y=%d",
+ m_generation, framePtr, nodePtr, x, y);
+ LOG_ASSERT(m_javaGlue.m_obj, "A WebView was not associated with this WebViewNative!");
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue.object(env).get(), m_javaGlue.m_sendMotionUp,
+ m_generation, (jint) framePtr, (jint) nodePtr, x, y);
+ checkException(env);
+}
+
+void findNext(bool forward)
+{
+ m_findOnPage.findNext(forward);
+ if (!m_findOnPage.currentMatchIsInLayer())
+ scrollRectOnScreen(m_findOnPage.currentMatchBounds());
+ viewInvalidate();
+}
+
+// With this call, WebView takes ownership of matches, and is responsible for
+// deleting it.
+void setMatches(WTF::Vector<MatchInfo>* matches, jboolean sameAsLastSearch)
+{
+ // If this search is the same as the last one, check against the old
+ // location to determine whether to scroll. If the same word is found
+ // in the same place, then do not scroll.
+ IntRect oldLocation;
+ bool checkAgainstOldLocation;
+ if (sameAsLastSearch && m_findOnPage.isCurrentLocationValid()) {
+ oldLocation = m_findOnPage.currentMatchBounds();
+ checkAgainstOldLocation = true;
+ } else
+ checkAgainstOldLocation = false;
+
+ m_findOnPage.setMatches(matches);
+
+ if (!checkAgainstOldLocation
+ || oldLocation != m_findOnPage.currentMatchBounds()) {
+ // FIXME: Need to scroll if the match is in a layer.
+ if (!m_findOnPage.currentMatchIsInLayer())
+ scrollRectOnScreen(m_findOnPage.currentMatchBounds());
+ }
+ viewInvalidate();
+}
+
+int currentMatchIndex()
+{
+ return m_findOnPage.currentMatchIndex();
+}
+
+bool scrollBy(int dx, int dy)
+{
+ LOG_ASSERT(m_javaGlue.m_obj, "A java object was not associated with this native WebView!");
+
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ bool result = env->CallBooleanMethod(m_javaGlue.object(env).get(),
+ m_javaGlue.m_scrollBy, dx, dy, true);
+ checkException(env);
+ return result;
+}
+
+bool hasCursorNode()
+{
+ CachedRoot* root = getFrameCache(DontAllowNewer);
+ if (!root) {
+ DBG_NAV_LOG("!root");
+ return false;
+ }
+ const CachedNode* cursorNode = root->currentCursor();
+ DBG_NAV_LOGD("cursorNode=%d (nodePointer=%p)",
+ cursorNode ? cursorNode->index() : -1,
+ cursorNode ? cursorNode->nodePointer() : 0);
+ return cursorNode;
+}
+
+bool hasFocusNode()
+{
+ CachedRoot* root = getFrameCache(DontAllowNewer);
+ if (!root) {
+ DBG_NAV_LOG("!root");
+ return false;
+ }
+ const CachedNode* focusNode = root->currentFocus();
+ DBG_NAV_LOGD("focusNode=%d (nodePointer=%p)",
+ focusNode ? focusNode->index() : -1,
+ focusNode ? focusNode->nodePointer() : 0);
+ return focusNode;
+}
+
+void rebuildWebTextView()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue.object(env).get(),
+ m_javaGlue.m_rebuildWebTextView);
+ checkException(env);
+}
+
+void viewInvalidate()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue.object(env).get(), m_javaGlue.m_viewInvalidate);
+ checkException(env);
+}
+
+void viewInvalidateRect(int l, int t, int r, int b)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue.object(env).get(), m_javaGlue.m_viewInvalidateRect, l, r, t, b);
+ checkException(env);
+}
+
+void postInvalidateDelayed(int64_t delay, const WebCore::IntRect& bounds)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue.object(env).get(), m_javaGlue.m_postInvalidateDelayed,
+ delay, bounds.x(), bounds.y(), bounds.right(), bounds.bottom());
+ checkException(env);
+}
+
+bool inFullScreenMode()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jboolean result = env->CallBooleanMethod(m_javaGlue.object(env).get(),
+ m_javaGlue.m_inFullScreenMode);
+ checkException(env);
+ return result;
+}
+
+int moveGeneration()
+{
+ return m_viewImpl->m_moveGeneration;
+}
+
+LayerAndroid* compositeRoot() const
+{
+ LOG_ASSERT(!m_baseLayer || m_baseLayer->countChildren() == 1,
+ "base layer can't have more than one child %s", __FUNCTION__);
+ if (m_baseLayer && m_baseLayer->countChildren() == 1)
+ return static_cast<LayerAndroid*>(m_baseLayer->getChild(0));
+ else
+ return 0;
+}
+
+#if ENABLE(ANDROID_OVERFLOW_SCROLL)
+static void copyScrollPositionRecursive(const LayerAndroid* from,
+ LayerAndroid* root)
+{
+ if (!from || !root)
+ return;
+ for (int i = 0; i < from->countChildren(); i++) {
+ const LayerAndroid* l = from->getChild(i);
+ if (l->contentIsScrollable()) {
+ const SkPoint& pos = l->getPosition();
+ LayerAndroid* match = root->findById(l->uniqueId());
+ if (match && match->contentIsScrollable())
+ match->setPosition(pos.fX, pos.fY);
+ }
+ copyScrollPositionRecursive(l, root);
+ }
+}
+#endif
+
+void setBaseLayer(BaseLayerAndroid* layer, SkRegion& inval, bool showVisualIndicator,
+ bool isPictureAfterFirstLayout)
+{
+#if USE(ACCELERATED_COMPOSITING)
+ if (m_glWebViewState)
+ m_glWebViewState->setBaseLayer(layer, inval, showVisualIndicator,
+ isPictureAfterFirstLayout);
+#endif
+
+#if ENABLE(ANDROID_OVERFLOW_SCROLL)
+ if (layer) {
+ LayerAndroid* newCompositeRoot = static_cast<LayerAndroid*>(layer->getChild(0));
+ copyScrollPositionRecursive(compositeRoot(), newCompositeRoot);
+ }
+#endif
+ SkSafeUnref(m_baseLayer);
+ m_baseLayer = layer;
+ CachedRoot* root = getFrameCache(DontAllowNewer);
+ if (!root)
+ return;
+ root->resetLayers();
+ root->setRootLayer(compositeRoot());
+}
+
+void replaceBaseContent(PictureSet* set)
+{
+ if (!m_baseLayer)
+ return;
+ m_baseLayer->setContent(*set);
+ delete set;
+}
+
+void copyBaseContentToPicture(SkPicture* picture)
+{
+ if (!m_baseLayer)
+ return;
+ PictureSet* content = m_baseLayer->content();
+ m_baseLayer->drawCanvas(picture->beginRecording(content->width(), content->height(),
+ SkPicture::kUsePathBoundsForClip_RecordingFlag));
+ picture->endRecording();
+}
+
+bool hasContent() {
+ if (!m_baseLayer)
+ return false;
+ return !m_baseLayer->content()->isEmpty();
+}
+
+void setFunctor(Functor* functor) {
+ delete m_glDrawFunctor;
+ m_glDrawFunctor = functor;
+}
+
+Functor* getFunctor() {
+ return m_glDrawFunctor;
+}
+
+private: // local state for WebView
+ // private to getFrameCache(); other functions operate in a different thread
+ CachedRoot* m_frameCacheUI; // navigation data ready for use
+ WebViewCore* m_viewImpl;
+ int m_generation; // associate unique ID with sent kit focus to match with ui
+ SkPicture* m_navPictureUI;
+ SkMSec m_ringAnimationEnd;
+ // Corresponds to the same-named boolean on the java side.
+ bool m_heightCanMeasure;
+ int m_lastDx;
+ SkMSec m_lastDxTime;
+ SelectText m_selectText;
+ FindOnPage m_findOnPage;
+ CursorRing m_ring;
+ BaseLayerAndroid* m_baseLayer;
+ Functor* m_glDrawFunctor;
+#if USE(ACCELERATED_COMPOSITING)
+ GLWebViewState* m_glWebViewState;
+#endif
+ const RenderSkinButton* m_buttonSkin;
+}; // end of WebView class
+
+
+/**
+ * This class holds a function pointer and parameters for calling drawGL into a specific
+ * viewport. The pointer to the Functor will be put on a framework display list to be called
+ * when the display list is replayed.
+ */
+class GLDrawFunctor : Functor {
+ public:
+ GLDrawFunctor(WebView* _wvInstance,
+ bool(WebView::*_funcPtr)(WebCore::IntRect&, WebCore::IntRect*, WebCore::IntRect&, int, WebCore::IntRect&, jfloat, jint),
+ WebCore::IntRect _viewRect, float _scale, int _extras) {
+ wvInstance = _wvInstance;
+ funcPtr = _funcPtr;
+ viewRect = _viewRect;
+ scale = _scale;
+ extras = _extras;
+ };
+ status_t operator()(int messageId, void* data) {
+ if (viewRect.isEmpty()) {
+ // NOOP operation if viewport is empty
+ return 0;
+ }
+
+ WebCore::IntRect inval;
+ int titlebarHeight = webViewRect.height() - viewRect.height();
+
+ uirenderer::DrawGlInfo* info = reinterpret_cast<uirenderer::DrawGlInfo*>(data);
+ WebCore::IntRect localViewRect = viewRect;
+ if (info->isLayer)
+ localViewRect.move(-1 * localViewRect.x(), -1 * localViewRect.y());
+
+ WebCore::IntRect clip(info->clipLeft, info->clipTop,
+ info->clipRight - info->clipLeft,
+ info->clipBottom - info->clipTop);
+
+ bool retVal = (*wvInstance.*funcPtr)(localViewRect, &inval, webViewRect, titlebarHeight, clip, scale, extras);
+ if (retVal) {
+ IntRect finalInval;
+ if (inval.isEmpty()) {
+ finalInval = webViewRect;
+ retVal = true;
+ } else {
+ finalInval.setX(webViewRect.x() + inval.x());
+ finalInval.setY(webViewRect.y() + titlebarHeight + inval.y());
+ finalInval.setWidth(inval.width());
+ finalInval.setHeight(inval.height());
+ }
+ info->dirtyLeft = finalInval.x();
+ info->dirtyTop = finalInval.y();
+ info->dirtyRight = finalInval.right();
+ info->dirtyBottom = finalInval.bottom();
+ }
+ // return 1 if invalidation needed, 0 otherwise
+ return retVal ? 1 : 0;
+ }
+ void updateRect(WebCore::IntRect& _viewRect) {
+ viewRect = _viewRect;
+ }
+ void updateViewRect(WebCore::IntRect& _viewRect) {
+ webViewRect = _viewRect;
+ }
+ private:
+ WebView* wvInstance;
+ bool (WebView::*funcPtr)(WebCore::IntRect&, WebCore::IntRect*, WebCore::IntRect&, int, WebCore::IntRect&, float, int);
+ WebCore::IntRect viewRect;
+ WebCore::IntRect webViewRect;
+ jfloat scale;
+ jint extras;
+};
+
+/*
+ * Native JNI methods
+ */
+static int nativeCacheHitFramePointer(JNIEnv *env, jobject obj)
+{
+ return reinterpret_cast<int>(GET_NATIVE_VIEW(env, obj)
+ ->m_cacheHitFrame->framePointer());
+}
+
+static jobject nativeCacheHitNodeBounds(JNIEnv *env, jobject obj)
+{
+ WebCore::IntRect bounds = GET_NATIVE_VIEW(env, obj)
+ ->m_cacheHitNode->originalAbsoluteBounds();
+ jclass rectClass = env->FindClass("android/graphics/Rect");
+ jmethodID init = env->GetMethodID(rectClass, "<init>", "(IIII)V");
+ jobject rect = env->NewObject(rectClass, init, bounds.x(),
+ bounds.y(), bounds.right(), bounds.bottom());
+ env->DeleteLocalRef(rectClass);
+ return rect;
+}
+
+static int nativeCacheHitNodePointer(JNIEnv *env, jobject obj)
+{
+ return reinterpret_cast<int>(GET_NATIVE_VIEW(env, obj)
+ ->m_cacheHitNode->nodePointer());
+}
+
+static bool nativeCacheHitIsPlugin(JNIEnv *env, jobject obj)
+{
+ return GET_NATIVE_VIEW(env, obj)->m_cacheHitNode->isPlugin();
+}
+
+static void nativeClearCursor(JNIEnv *env, jobject obj)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
+ view->clearCursor();
+}
+
+static void nativeCreate(JNIEnv *env, jobject obj, int viewImpl, jstring drawableDir,
+ jobject jAssetManager)
+{
+ AssetManager* am = assetManagerForJavaObject(env, jAssetManager);
+ WTF::String dir = jstringToWtfString(env, drawableDir);
+ WebView* webview = new WebView(env, obj, viewImpl, dir, am);
+ // NEED THIS OR SOMETHING LIKE IT!
+ //Release(obj);
+}
+
+static jint nativeCursorFramePointer(JNIEnv *env, jobject obj)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
+ if (!root)
+ return 0;
+ const CachedFrame* frame = 0;
+ (void) root->currentCursor(&frame);
+ return reinterpret_cast<int>(frame ? frame->framePointer() : 0);
+}
+
+static const CachedNode* getCursorNode(JNIEnv *env, jobject obj)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
+ return root ? root->currentCursor() : 0;
+}
+
+static const CachedNode* getCursorNode(JNIEnv *env, jobject obj,
+ const CachedFrame** frame)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
+ return root ? root->currentCursor(frame) : 0;
+}
+
+static const CachedNode* getFocusCandidate(JNIEnv *env, jobject obj,
+ const CachedFrame** frame)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
+ if (!root)
+ return 0;
+ const CachedNode* cursor = root->currentCursor(frame);
+ if (cursor && cursor->wantsKeyEvents())
+ return cursor;
+ return root->currentFocus(frame);
+}
+
+static bool focusCandidateHasNextTextfield(JNIEnv *env, jobject obj)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
+ if (!root)
+ return false;
+ const CachedNode* cursor = root->currentCursor();
+ if (!cursor || !cursor->isTextInput())
+ cursor = root->currentFocus();
+ if (!cursor || !cursor->isTextInput()) return false;
+ return root->nextTextField(cursor, 0);
+}
+
+static const CachedNode* getFocusNode(JNIEnv *env, jobject obj)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
+ return root ? root->currentFocus() : 0;
+}
+
+static const CachedNode* getFocusNode(JNIEnv *env, jobject obj,
+ const CachedFrame** frame)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
+ return root ? root->currentFocus(frame) : 0;
+}
+
+static const CachedInput* getInputCandidate(JNIEnv *env, jobject obj)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
+ if (!root)
+ return 0;
+ const CachedFrame* frame;
+ const CachedNode* cursor = root->currentCursor(&frame);
+ if (!cursor || !cursor->wantsKeyEvents())
+ cursor = root->currentFocus(&frame);
+ return cursor ? frame->textInput(cursor) : 0;
+}
+
+static jboolean nativePageShouldHandleShiftAndArrows(JNIEnv *env, jobject obj)
+{
+ const CachedNode* focus = getFocusNode(env, obj);
+ if (!focus) return false;
+ // Plugins handle shift and arrows whether or not they have focus.
+ if (focus->isPlugin()) return true;
+ const CachedNode* cursor = getCursorNode(env, obj);
+ // ContentEditable nodes should only receive shift and arrows if they have
+ // both the cursor and the focus.
+ return cursor && cursor->nodePointer() == focus->nodePointer()
+ && cursor->isContentEditable();
+}
+
+static jobject nativeCursorNodeBounds(JNIEnv *env, jobject obj)
+{
+ const CachedFrame* frame;
+ const CachedNode* node = getCursorNode(env, obj, &frame);
+ WebCore::IntRect bounds = node ? node->bounds(frame)
+ : WebCore::IntRect(0, 0, 0, 0);
+ jclass rectClass = env->FindClass("android/graphics/Rect");
+ jmethodID init = env->GetMethodID(rectClass, "<init>", "(IIII)V");
+ jobject rect = env->NewObject(rectClass, init, bounds.x(),
+ bounds.y(), bounds.right(), bounds.bottom());
+ env->DeleteLocalRef(rectClass);
+ return rect;
+}
+
+static jint nativeCursorNodePointer(JNIEnv *env, jobject obj)
+{
+ const CachedNode* node = getCursorNode(env, obj);
+ return reinterpret_cast<int>(node ? node->nodePointer() : 0);
+}
+
+static jobject nativeCursorPosition(JNIEnv *env, jobject obj)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ const CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
+ WebCore::IntPoint pos = WebCore::IntPoint(0, 0);
+ if (root)
+ root->getSimulatedMousePosition(&pos);
+ jclass pointClass = env->FindClass("android/graphics/Point");
+ jmethodID init = env->GetMethodID(pointClass, "<init>", "(II)V");
+ jobject point = env->NewObject(pointClass, init, pos.x(), pos.y());
+ env->DeleteLocalRef(pointClass);
+ return point;
+}
+
+static WebCore::IntRect jrect_to_webrect(JNIEnv* env, jobject obj)
+{
+ int L, T, R, B;
+ GraphicsJNI::get_jrect(env, obj, &L, &T, &R, &B);
+ return WebCore::IntRect(L, T, R - L, B - T);
+}
+
+static bool nativeCursorIntersects(JNIEnv *env, jobject obj, jobject visRect)
+{
+ const CachedFrame* frame;
+ const CachedNode* node = getCursorNode(env, obj, &frame);
+ return node ? node->bounds(frame).intersects(
+ jrect_to_webrect(env, visRect)) : false;
+}
+
+static bool nativeCursorIsAnchor(JNIEnv *env, jobject obj)
+{
+ const CachedNode* node = getCursorNode(env, obj);
+ return node ? node->isAnchor() : false;
+}
+
+static bool nativeCursorIsTextInput(JNIEnv *env, jobject obj)
+{
+ const CachedNode* node = getCursorNode(env, obj);
+ return node ? node->isTextInput() : false;
+}
+
+static jobject nativeCursorText(JNIEnv *env, jobject obj)
+{
+ const CachedNode* node = getCursorNode(env, obj);
+ if (!node)
+ return 0;
+ WTF::String value = node->getExport();
+ return wtfStringToJstring(env, value);
+}
+
+static void nativeDebugDump(JNIEnv *env, jobject obj)
+{
+#if DUMP_NAV_CACHE
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
+ view->debugDump();
+#endif
+}
+
+static jint nativeDraw(JNIEnv *env, jobject obj, jobject canv, jint color,
+ jint extras, jboolean split) {
+ SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, canv);
+ return reinterpret_cast<jint>(GET_NATIVE_VIEW(env, obj)->draw(canvas, color, extras, split));
+}
+
+static jint nativeGetDrawGLFunction(JNIEnv *env, jobject obj, jobject jrect, jobject jviewrect,
+ jfloat scale, jint extras) {
+ WebCore::IntRect viewRect;
+ if (jrect == NULL) {
+ viewRect = WebCore::IntRect();
+ } else {
+ viewRect = jrect_to_webrect(env, jrect);
+ }
+ WebView *wvInstance = GET_NATIVE_VIEW(env, obj);
+ GLDrawFunctor* functor = new GLDrawFunctor(wvInstance, &android::WebView::drawGL,
+ viewRect, scale, extras);
+ wvInstance->setFunctor((Functor*) functor);
+
+ WebCore::IntRect webViewRect;
+ if (jviewrect == NULL) {
+ webViewRect = WebCore::IntRect();
+ } else {
+ webViewRect = jrect_to_webrect(env, jviewrect);
+ }
+ functor->updateViewRect(webViewRect);
+
+ return (jint)functor;
+}
+
+static void nativeUpdateDrawGLFunction(JNIEnv *env, jobject obj, jobject jrect, jobject jviewrect) {
+ WebView *wvInstance = GET_NATIVE_VIEW(env, obj);
+ if (wvInstance != NULL) {
+ GLDrawFunctor* functor = (GLDrawFunctor*) wvInstance->getFunctor();
+ if (functor != NULL) {
+ WebCore::IntRect viewRect;
+ if (jrect == NULL) {
+ viewRect = WebCore::IntRect();
+ } else {
+ viewRect = jrect_to_webrect(env, jrect);
+ }
+ functor->updateRect(viewRect);
+
+ WebCore::IntRect webViewRect;
+ if (jviewrect == NULL) {
+ webViewRect = WebCore::IntRect();
+ } else {
+ webViewRect = jrect_to_webrect(env, jviewrect);
+ }
+ functor->updateViewRect(webViewRect);
+ }
+ }
+}
+
+static bool nativeEvaluateLayersAnimations(JNIEnv *env, jobject obj)
+{
+#if USE(ACCELERATED_COMPOSITING)
+ LayerAndroid* root = GET_NATIVE_VIEW(env, obj)->compositeRoot();
+ if (root)
+ return root->evaluateAnimations();
+#endif
+ return false;
+}
+
+static void nativeSetBaseLayer(JNIEnv *env, jobject obj, jint layer, jobject inval,
+ jboolean showVisualIndicator,
+ jboolean isPictureAfterFirstLayout)
+{
+ BaseLayerAndroid* layerImpl = reinterpret_cast<BaseLayerAndroid*>(layer);
+ SkRegion invalRegion;
+ if (inval)
+ invalRegion = *GraphicsJNI::getNativeRegion(env, inval);
+ GET_NATIVE_VIEW(env, obj)->setBaseLayer(layerImpl, invalRegion, showVisualIndicator,
+ isPictureAfterFirstLayout);
+}
+
+static void nativeReplaceBaseContent(JNIEnv *env, jobject obj, jint content)
+{
+ PictureSet* set = reinterpret_cast<PictureSet*>(content);
+ GET_NATIVE_VIEW(env, obj)->replaceBaseContent(set);
+}
+
+static void nativeCopyBaseContentToPicture(JNIEnv *env, jobject obj, jobject pict)
+{
+ SkPicture* picture = GraphicsJNI::getNativePicture(env, pict);
+ GET_NATIVE_VIEW(env, obj)->copyBaseContentToPicture(picture);
+}
+
+static bool nativeHasContent(JNIEnv *env, jobject obj)
+{
+ return GET_NATIVE_VIEW(env, obj)->hasContent();
+}
+
+static jobject nativeImageURI(JNIEnv *env, jobject obj, jint x, jint y)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
+ WTF::String uri = view->imageURI(x, y);
+ return wtfStringToJstring(env, uri);
+}
+
+static jint nativeFocusCandidateFramePointer(JNIEnv *env, jobject obj)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
+ if (!root)
+ return 0;
+ const CachedFrame* frame = 0;
+ const CachedNode* cursor = root->currentCursor(&frame);
+ if (!cursor || !cursor->wantsKeyEvents())
+ (void) root->currentFocus(&frame);
+ return reinterpret_cast<int>(frame ? frame->framePointer() : 0);
+}
+
+static bool nativeFocusCandidateIsPassword(JNIEnv *env, jobject obj)
+{
+ const CachedInput* input = getInputCandidate(env, obj);
+ return input && input->getType() == CachedInput::PASSWORD;
+}
+
+static bool nativeFocusCandidateIsRtlText(JNIEnv *env, jobject obj)
+{
+ const CachedInput* input = getInputCandidate(env, obj);
+ return input ? input->isRtlText() : false;
+}
+
+static bool nativeFocusCandidateIsTextInput(JNIEnv *env, jobject obj)
+{
+ const CachedNode* node = getFocusCandidate(env, obj, 0);
+ return node ? node->isTextInput() : false;
+}
+
+static jint nativeFocusCandidateMaxLength(JNIEnv *env, jobject obj)
+{
+ const CachedInput* input = getInputCandidate(env, obj);
+ return input ? input->maxLength() : false;
+}
+
+static jint nativeFocusCandidateIsAutoComplete(JNIEnv *env, jobject obj)
+{
+ const CachedInput* input = getInputCandidate(env, obj);
+ return input ? input->autoComplete() : false;
+}
+
+static jobject nativeFocusCandidateName(JNIEnv *env, jobject obj)
+{
+ const CachedInput* input = getInputCandidate(env, obj);
+ if (!input)
+ return 0;
+ const WTF::String& name = input->name();
+ return wtfStringToJstring(env, name);
+}
+
+static jobject createJavaRect(JNIEnv* env, int x, int y, int right, int bottom)
+{
+ jclass rectClass = env->FindClass("android/graphics/Rect");
+ jmethodID init = env->GetMethodID(rectClass, "<init>", "(IIII)V");
+ jobject rect = env->NewObject(rectClass, init, x, y, right, bottom);
+ env->DeleteLocalRef(rectClass);
+ return rect;
+}
+
+static jobject nativeFocusCandidateNodeBounds(JNIEnv *env, jobject obj)
+{
+ const CachedFrame* frame;
+ const CachedNode* node = getFocusCandidate(env, obj, &frame);
+ WebCore::IntRect bounds = node ? node->bounds(frame)
+ : WebCore::IntRect(0, 0, 0, 0);
+ return createJavaRect(env, bounds.x(), bounds.y(), bounds.right(), bounds.bottom());
+}
+
+static jobject nativeFocusCandidatePaddingRect(JNIEnv *env, jobject obj)
+{
+ const CachedInput* input = getInputCandidate(env, obj);
+ if (!input)
+ return 0;
+ // Note that the Java Rect is being used to pass four integers, rather than
+ // being used as an actual rectangle.
+ return createJavaRect(env, input->paddingLeft(), input->paddingTop(),
+ input->paddingRight(), input->paddingBottom());
+}
+
+static jint nativeFocusCandidatePointer(JNIEnv *env, jobject obj)
+{
+ const CachedNode* node = getFocusCandidate(env, obj, 0);
+ return reinterpret_cast<int>(node ? node->nodePointer() : 0);
+}
+
+static jobject nativeFocusCandidateText(JNIEnv *env, jobject obj)
+{
+ const CachedNode* node = getFocusCandidate(env, obj, 0);
+ if (!node)
+ return 0;
+ WTF::String value = node->getExport();
+ return wtfStringToJstring(env, value);
+}
+
+static int nativeFocusCandidateLineHeight(JNIEnv *env, jobject obj)
+{
+ const CachedInput* input = getInputCandidate(env, obj);
+ return input ? input->lineHeight() : 0;
+}
+
+static jfloat nativeFocusCandidateTextSize(JNIEnv *env, jobject obj)
+{
+ const CachedInput* input = getInputCandidate(env, obj);
+ return input ? input->textSize() : 0.f;
+}
+
+static int nativeFocusCandidateType(JNIEnv *env, jobject obj)
+{
+ const CachedInput* input = getInputCandidate(env, obj);
+ if (!input)
+ return CachedInput::NONE;
+
+ if (input->isTextArea())
+ return CachedInput::TEXT_AREA;
+
+ return input->getType();
+}
+
+static bool nativeFocusIsPlugin(JNIEnv *env, jobject obj)
+{
+ const CachedNode* node = getFocusNode(env, obj);
+ return node ? node->isPlugin() : false;
+}
+
+static jobject nativeFocusNodeBounds(JNIEnv *env, jobject obj)
+{
+ const CachedFrame* frame;
+ const CachedNode* node = getFocusNode(env, obj, &frame);
+ WebCore::IntRect bounds = node ? node->bounds(frame)
+ : WebCore::IntRect(0, 0, 0, 0);
+ jclass rectClass = env->FindClass("android/graphics/Rect");
+ jmethodID init = env->GetMethodID(rectClass, "<init>", "(IIII)V");
+ jobject rect = env->NewObject(rectClass, init, bounds.x(),
+ bounds.y(), bounds.right(), bounds.bottom());
+ env->DeleteLocalRef(rectClass);
+ return rect;
+}
+
+static jint nativeFocusNodePointer(JNIEnv *env, jobject obj)
+{
+ const CachedNode* node = getFocusNode(env, obj);
+ return node ? reinterpret_cast<int>(node->nodePointer()) : 0;
+}
+
+static bool nativeCursorWantsKeyEvents(JNIEnv* env, jobject jwebview) {
+ WebView* view = GET_NATIVE_VIEW(env, jwebview);
+ LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
+ return view->cursorWantsKeyEvents();
+}
+
+static void nativeHideCursor(JNIEnv *env, jobject obj)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
+ view->hideCursor();
+}
+
+static void nativeInstrumentReport(JNIEnv *env, jobject obj)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounter::reportNow();
+#endif
+}
+
+static void nativeSelectBestAt(JNIEnv *env, jobject obj, jobject jrect)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
+ WebCore::IntRect rect = jrect_to_webrect(env, jrect);
+ view->selectBestAt(rect);
+}
+
+static void nativeSelectAt(JNIEnv *env, jobject obj, jint x, jint y)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
+ WebCore::IntRect rect = IntRect(x, y , 1, 1);
+ view->selectBestAt(rect);
+ if (view->hasCursorNode())
+ view->showCursorUntimed();
+}
+
+static jobject nativeLayerBounds(JNIEnv* env, jobject obj, jint jlayer)
+{
+ SkRect r;
+#if USE(ACCELERATED_COMPOSITING)
+ LayerAndroid* layer = (LayerAndroid*) jlayer;
+ r = layer->bounds();
+#else
+ r.setEmpty();
+#endif
+ SkIRect irect;
+ r.round(&irect);
+ jclass rectClass = env->FindClass("android/graphics/Rect");
+ jmethodID init = env->GetMethodID(rectClass, "<init>", "(IIII)V");
+ jobject rect = env->NewObject(rectClass, init, irect.fLeft, irect.fTop,
+ irect.fRight, irect.fBottom);
+ env->DeleteLocalRef(rectClass);
+ return rect;
+}
+
+static jobject nativeSubtractLayers(JNIEnv* env, jobject obj, jobject jrect)
+{
+ SkIRect irect = jrect_to_webrect(env, jrect);
+#if USE(ACCELERATED_COMPOSITING)
+ LayerAndroid* root = GET_NATIVE_VIEW(env, obj)->compositeRoot();
+ if (root) {
+ SkRect rect;
+ rect.set(irect);
+ rect = root->subtractLayers(rect);
+ rect.round(&irect);
+ }
+#endif
+ jclass rectClass = env->FindClass("android/graphics/Rect");
+ jmethodID init = env->GetMethodID(rectClass, "<init>", "(IIII)V");
+ jobject rect = env->NewObject(rectClass, init, irect.fLeft, irect.fTop,
+ irect.fRight, irect.fBottom);
+ env->DeleteLocalRef(rectClass);
+ return rect;
+}
+
+static jint nativeTextGeneration(JNIEnv *env, jobject obj)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
+ return root ? root->textGeneration() : 0;
+}
+
+static bool nativePointInNavCache(JNIEnv *env, jobject obj,
+ int x, int y, int slop)
+{
+ return GET_NATIVE_VIEW(env, obj)->pointInNavCache(x, y, slop);
+}
+
+static bool nativeMotionUp(JNIEnv *env, jobject obj,
+ int x, int y, int slop)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
+ return view->motionUp(x, y, slop);
+}
+
+static bool nativeHasCursorNode(JNIEnv *env, jobject obj)
+{
+ return GET_NATIVE_VIEW(env, obj)->hasCursorNode();
+}
+
+static bool nativeHasFocusNode(JNIEnv *env, jobject obj)
+{
+ return GET_NATIVE_VIEW(env, obj)->hasFocusNode();
+}
+
+static bool nativeMoveCursor(JNIEnv *env, jobject obj,
+ int key, int count, bool ignoreScroll)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ DBG_NAV_LOGD("env=%p obj=%p view=%p", env, obj, view);
+ LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
+ return view->moveCursor(key, count, ignoreScroll);
+}
+
+static void nativeRecordButtons(JNIEnv* env, jobject obj, bool hasFocus,
+ bool pressed, bool invalidate)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
+ view->nativeRecordButtons(hasFocus, pressed, invalidate);
+}
+
+static void nativeSetFindIsUp(JNIEnv *env, jobject obj, jboolean isUp)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
+ view->setFindIsUp(isUp);
+}
+
+static void nativeSetFindIsEmpty(JNIEnv *env, jobject obj)
+{
+ GET_NATIVE_VIEW(env, obj)->setFindIsEmpty();
+}
+
+static void nativeShowCursorTimed(JNIEnv *env, jobject obj)
+{
+ GET_NATIVE_VIEW(env, obj)->showCursorTimed();
+}
+
+static void nativeSetHeightCanMeasure(JNIEnv *env, jobject obj, bool measure)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(view, "view not set in nativeSetHeightCanMeasure");
+ view->setHeightCanMeasure(measure);
+}
+
+static jobject nativeGetCursorRingBounds(JNIEnv *env, jobject obj)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
+ jclass rectClass = env->FindClass("android/graphics/Rect");
+ LOG_ASSERT(rectClass, "Could not find Rect class!");
+ jmethodID init = env->GetMethodID(rectClass, "<init>", "(IIII)V");
+ LOG_ASSERT(init, "Could not find constructor for Rect");
+ WebCore::IntRect webRect;
+ view->cursorRingBounds(&webRect);
+ jobject rect = env->NewObject(rectClass, init, webRect.x(),
+ webRect.y(), webRect.right(), webRect.bottom());
+ env->DeleteLocalRef(rectClass);
+ return rect;
+}
+
+static int nativeFindAll(JNIEnv *env, jobject obj, jstring findLower,
+ jstring findUpper, jboolean sameAsLastSearch)
+{
+ // If one or the other is null, do not search.
+ if (!(findLower && findUpper))
+ return 0;
+ // Obtain the characters for both the lower case string and the upper case
+ // string representing the same word.
+ const jchar* findLowerChars = env->GetStringChars(findLower, 0);
+ const jchar* findUpperChars = env->GetStringChars(findUpper, 0);
+ // If one or the other is null, do not search.
+ if (!(findLowerChars && findUpperChars)) {
+ if (findLowerChars)
+ env->ReleaseStringChars(findLower, findLowerChars);
+ if (findUpperChars)
+ env->ReleaseStringChars(findUpper, findUpperChars);
+ checkException(env);
+ return 0;
+ }
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(view, "view not set in nativeFindAll");
+ CachedRoot* root = view->getFrameCache(WebView::AllowNewer);
+ if (!root) {
+ env->ReleaseStringChars(findLower, findLowerChars);
+ env->ReleaseStringChars(findUpper, findUpperChars);
+ checkException(env);
+ return 0;
+ }
+ int length = env->GetStringLength(findLower);
+ // If the lengths of the strings do not match, then they are not the same
+ // word, so do not search.
+ if (!length || env->GetStringLength(findUpper) != length) {
+ env->ReleaseStringChars(findLower, findLowerChars);
+ env->ReleaseStringChars(findUpper, findUpperChars);
+ checkException(env);
+ return 0;
+ }
+ int width = root->documentWidth();
+ int height = root->documentHeight();
+ // Create a FindCanvas, which allows us to fake draw into it so we can
+ // figure out where our search string is rendered (and how many times).
+ FindCanvas canvas(width, height, (const UChar*) findLowerChars,
+ (const UChar*) findUpperChars, length << 1);
+ SkBitmap bitmap;
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
+ canvas.setBitmapDevice(bitmap);
+ root->draw(canvas);
+ WTF::Vector<MatchInfo>* matches = canvas.detachMatches();
+ // With setMatches, the WebView takes ownership of matches
+ view->setMatches(matches, sameAsLastSearch);
+
+ env->ReleaseStringChars(findLower, findLowerChars);
+ env->ReleaseStringChars(findUpper, findUpperChars);
+ checkException(env);
+ return canvas.found();
+}
+
+static void nativeFindNext(JNIEnv *env, jobject obj, bool forward)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(view, "view not set in nativeFindNext");
+ view->findNext(forward);
+}
+
+static int nativeFindIndex(JNIEnv *env, jobject obj)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(view, "view not set in nativeFindIndex");
+ return view->currentMatchIndex();
+}
+
+static void nativeUpdateCachedTextfield(JNIEnv *env, jobject obj, jstring updatedText, jint generation)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(view, "view not set in nativeUpdateCachedTextfield");
+ CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
+ if (!root)
+ return;
+ const CachedNode* cachedFocusNode = root->currentFocus();
+ if (!cachedFocusNode || !cachedFocusNode->isTextInput())
+ return;
+ WTF::String webcoreString = jstringToWtfString(env, updatedText);
+ (const_cast<CachedNode*>(cachedFocusNode))->setExport(webcoreString);
+ root->setTextGeneration(generation);
+ checkException(env);
+}
+
+static jint nativeGetBlockLeftEdge(JNIEnv *env, jobject obj, jint x, jint y,
+ jfloat scale)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
+ if (!view)
+ return -1;
+ return view->getBlockLeftEdge(x, y, scale);
+}
+
+static void nativeDestroy(JNIEnv *env, jobject obj)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ LOGD("nativeDestroy view: %p", view);
+ LOG_ASSERT(view, "view not set in nativeDestroy");
+ delete view;
+}
+
+static void nativeStopGL(JNIEnv *env, jobject obj)
+{
+ GET_NATIVE_VIEW(env, obj)->stopGL();
+}
+
+static bool nativeMoveCursorToNextTextInput(JNIEnv *env, jobject obj)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
+ if (!root)
+ return false;
+ const CachedNode* current = root->currentCursor();
+ if (!current || !current->isTextInput())
+ current = root->currentFocus();
+ if (!current || !current->isTextInput())
+ return false;
+ const CachedFrame* frame;
+ const CachedNode* next = root->nextTextField(current, &frame);
+ if (!next)
+ return false;
+ const WebCore::IntRect& bounds = next->bounds(frame);
+ root->rootHistory()->setMouseBounds(bounds);
+ view->getWebViewCore()->updateCursorBounds(root, frame, next);
+ view->showCursorUntimed();
+ root->setCursor(const_cast<CachedFrame*>(frame),
+ const_cast<CachedNode*>(next));
+ view->sendMoveFocus(static_cast<WebCore::Frame*>(frame->framePointer()),
+ static_cast<WebCore::Node*>(next->nodePointer()));
+ if (!next->isInLayer())
+ view->scrollRectOnScreen(bounds);
+ view->getWebViewCore()->m_moveGeneration++;
+ return true;
+}
+
+static int nativeMoveGeneration(JNIEnv *env, jobject obj)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ if (!view)
+ return 0;
+ return view->moveGeneration();
+}
+
+static void nativeMoveSelection(JNIEnv *env, jobject obj, int x, int y)
+{
+ GET_NATIVE_VIEW(env, obj)->moveSelection(x, y);
+}
+
+static void nativeResetSelection(JNIEnv *env, jobject obj)
+{
+ return GET_NATIVE_VIEW(env, obj)->resetSelection();
+}
+
+static jobject nativeSelectableText(JNIEnv* env, jobject obj)
+{
+ IntPoint pos = GET_NATIVE_VIEW(env, obj)->selectableText();
+ jclass pointClass = env->FindClass("android/graphics/Point");
+ jmethodID init = env->GetMethodID(pointClass, "<init>", "(II)V");
+ jobject point = env->NewObject(pointClass, init, pos.x(), pos.y());
+ env->DeleteLocalRef(pointClass);
+ return point;
+}
+
+static void nativeSelectAll(JNIEnv* env, jobject obj)
+{
+ GET_NATIVE_VIEW(env, obj)->selectAll();
+}
+
+static void nativeSetExtendSelection(JNIEnv *env, jobject obj)
+{
+ GET_NATIVE_VIEW(env, obj)->setExtendSelection();
+}
+
+static jboolean nativeStartSelection(JNIEnv *env, jobject obj, int x, int y)
+{
+ return GET_NATIVE_VIEW(env, obj)->startSelection(x, y);
+}
+
+static jboolean nativeWordSelection(JNIEnv *env, jobject obj, int x, int y)
+{
+ return GET_NATIVE_VIEW(env, obj)->wordSelection(x, y);
+}
+
+static void nativeExtendSelection(JNIEnv *env, jobject obj, int x, int y)
+{
+ GET_NATIVE_VIEW(env, obj)->extendSelection(x, y);
+}
+
+static jobject nativeGetSelection(JNIEnv *env, jobject obj)
+{
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
+ String selection = view->getSelection();
+ return wtfStringToJstring(env, selection);
+}
+
+static jboolean nativeHitSelection(JNIEnv *env, jobject obj, int x, int y)
+{
+ return GET_NATIVE_VIEW(env, obj)->hitSelection(x, y);
+}
+
+static jint nativeSelectionX(JNIEnv *env, jobject obj)
+{
+ return GET_NATIVE_VIEW(env, obj)->selectionX();
+}
+
+static jint nativeSelectionY(JNIEnv *env, jobject obj)
+{
+ return GET_NATIVE_VIEW(env, obj)->selectionY();
+}
+
+static void nativeSetSelectionPointer(JNIEnv *env, jobject obj, jboolean set,
+ jfloat scale, jint x, jint y)
+{
+ GET_NATIVE_VIEW(env, obj)->setSelectionPointer(set, scale, x, y);
+}
+
+#ifdef ANDROID_DUMP_DISPLAY_TREE
+static void dumpToFile(const char text[], void* file) {
+ fwrite(text, 1, strlen(text), reinterpret_cast<FILE*>(file));
+ fwrite("\n", 1, 1, reinterpret_cast<FILE*>(file));
+}
+#endif
+
+static void nativeDumpDisplayTree(JNIEnv* env, jobject jwebview, jstring jurl)
+{
+#ifdef ANDROID_DUMP_DISPLAY_TREE
+ WebView* view = GET_NATIVE_VIEW(env, jwebview);
+ LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
+
+ if (view && view->getWebViewCore()) {
+ FILE* file = fopen(DISPLAY_TREE_LOG_FILE, "w");
+ if (file) {
+ SkFormatDumper dumper(dumpToFile, file);
+ // dump the URL
+ if (jurl) {
+ const char* str = env->GetStringUTFChars(jurl, 0);
+ SkDebugf("Dumping %s to %s\n", str, DISPLAY_TREE_LOG_FILE);
+ dumpToFile(str, file);
+ env->ReleaseStringUTFChars(jurl, str);
+ }
+ // now dump the display tree
+ SkDumpCanvas canvas(&dumper);
+ // this will playback the picture into the canvas, which will
+ // spew its contents to the dumper
+ view->draw(&canvas, 0, 0, false);
+ // we're done with the file now
+ fwrite("\n", 1, 1, file);
+ fclose(file);
+ }
+#if USE(ACCELERATED_COMPOSITING)
+ const LayerAndroid* rootLayer = view->compositeRoot();
+ if (rootLayer) {
+ FILE* file = fopen(LAYERS_TREE_LOG_FILE,"w");
+ if (file) {
+ rootLayer->dumpLayers(file, 0);
+ fclose(file);
+ }
+ }
+#endif
+ }
+#endif
+}
+
+static int nativeScrollableLayer(JNIEnv* env, jobject jwebview, jint x, jint y,
+ jobject rect, jobject bounds)
+{
+ WebView* view = GET_NATIVE_VIEW(env, jwebview);
+ LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
+ SkIRect nativeRect, nativeBounds;
+ int id = view->scrollableLayer(x, y, &nativeRect, &nativeBounds);
+ if (rect)
+ GraphicsJNI::irect_to_jrect(nativeRect, env, rect);
+ if (bounds)
+ GraphicsJNI::irect_to_jrect(nativeBounds, env, bounds);
+ return id;
+}
+
+static bool nativeScrollLayer(JNIEnv* env, jobject obj, jint layerId, jint x,
+ jint y)
+{
+#if ENABLE(ANDROID_OVERFLOW_SCROLL)
+ WebView* view = GET_NATIVE_VIEW(env, obj);
+ LayerAndroid* root = view->compositeRoot();
+ if (!root)
+ return false;
+ LayerAndroid* layer = root->findById(layerId);
+ if (!layer || !layer->contentIsScrollable())
+ return false;
+ return static_cast<ScrollableLayerAndroid*>(layer)->scrollTo(x, y);
+#endif
+ return false;
+}
+
+static void nativeSetExpandedTileBounds(JNIEnv*, jobject, jboolean enabled)
+{
+ TilesManager::instance()->setExpandedTileBounds(enabled);
+}
+
+/*
+ * JNI registration
+ */
+static JNINativeMethod gJavaWebViewMethods[] = {
+ { "nativeCacheHitFramePointer", "()I",
+ (void*) nativeCacheHitFramePointer },
+ { "nativeCacheHitIsPlugin", "()Z",
+ (void*) nativeCacheHitIsPlugin },
+ { "nativeCacheHitNodeBounds", "()Landroid/graphics/Rect;",
+ (void*) nativeCacheHitNodeBounds },
+ { "nativeCacheHitNodePointer", "()I",
+ (void*) nativeCacheHitNodePointer },
+ { "nativeClearCursor", "()V",
+ (void*) nativeClearCursor },
+ { "nativeCreate", "(ILjava/lang/String;Landroid/content/res/AssetManager;)V",
+ (void*) nativeCreate },
+ { "nativeCursorFramePointer", "()I",
+ (void*) nativeCursorFramePointer },
+ { "nativePageShouldHandleShiftAndArrows", "()Z",
+ (void*) nativePageShouldHandleShiftAndArrows },
+ { "nativeCursorNodeBounds", "()Landroid/graphics/Rect;",
+ (void*) nativeCursorNodeBounds },
+ { "nativeCursorNodePointer", "()I",
+ (void*) nativeCursorNodePointer },
+ { "nativeCursorIntersects", "(Landroid/graphics/Rect;)Z",
+ (void*) nativeCursorIntersects },
+ { "nativeCursorIsAnchor", "()Z",
+ (void*) nativeCursorIsAnchor },
+ { "nativeCursorIsTextInput", "()Z",
+ (void*) nativeCursorIsTextInput },
+ { "nativeCursorPosition", "()Landroid/graphics/Point;",
+ (void*) nativeCursorPosition },
+ { "nativeCursorText", "()Ljava/lang/String;",
+ (void*) nativeCursorText },
+ { "nativeCursorWantsKeyEvents", "()Z",
+ (void*)nativeCursorWantsKeyEvents },
+ { "nativeDebugDump", "()V",
+ (void*) nativeDebugDump },
+ { "nativeDestroy", "()V",
+ (void*) nativeDestroy },
+ { "nativeDraw", "(Landroid/graphics/Canvas;IIZ)I",
+ (void*) nativeDraw },
+ { "nativeGetDrawGLFunction", "(Landroid/graphics/Rect;Landroid/graphics/Rect;FI)I",
+ (void*) nativeGetDrawGLFunction },
+ { "nativeUpdateDrawGLFunction", "(Landroid/graphics/Rect;Landroid/graphics/Rect;)V",
+ (void*) nativeUpdateDrawGLFunction },
+ { "nativeDumpDisplayTree", "(Ljava/lang/String;)V",
+ (void*) nativeDumpDisplayTree },
+ { "nativeEvaluateLayersAnimations", "()Z",
+ (void*) nativeEvaluateLayersAnimations },
+ { "nativeExtendSelection", "(II)V",
+ (void*) nativeExtendSelection },
+ { "nativeFindAll", "(Ljava/lang/String;Ljava/lang/String;Z)I",
+ (void*) nativeFindAll },
+ { "nativeFindNext", "(Z)V",
+ (void*) nativeFindNext },
+ { "nativeFindIndex", "()I",
+ (void*) nativeFindIndex},
+ { "nativeFocusCandidateFramePointer", "()I",
+ (void*) nativeFocusCandidateFramePointer },
+ { "nativeFocusCandidateHasNextTextfield", "()Z",
+ (void*) focusCandidateHasNextTextfield },
+ { "nativeFocusCandidateIsPassword", "()Z",
+ (void*) nativeFocusCandidateIsPassword },
+ { "nativeFocusCandidateIsRtlText", "()Z",
+ (void*) nativeFocusCandidateIsRtlText },
+ { "nativeFocusCandidateIsTextInput", "()Z",
+ (void*) nativeFocusCandidateIsTextInput },
+ { "nativeFocusCandidateLineHeight", "()I",
+ (void*) nativeFocusCandidateLineHeight },
+ { "nativeFocusCandidateMaxLength", "()I",
+ (void*) nativeFocusCandidateMaxLength },
+ { "nativeFocusCandidateIsAutoComplete", "()Z",
+ (void*) nativeFocusCandidateIsAutoComplete },
+ { "nativeFocusCandidateName", "()Ljava/lang/String;",
+ (void*) nativeFocusCandidateName },
+ { "nativeFocusCandidateNodeBounds", "()Landroid/graphics/Rect;",
+ (void*) nativeFocusCandidateNodeBounds },
+ { "nativeFocusCandidatePaddingRect", "()Landroid/graphics/Rect;",
+ (void*) nativeFocusCandidatePaddingRect },
+ { "nativeFocusCandidatePointer", "()I",
+ (void*) nativeFocusCandidatePointer },
+ { "nativeFocusCandidateText", "()Ljava/lang/String;",
+ (void*) nativeFocusCandidateText },
+ { "nativeFocusCandidateTextSize", "()F",
+ (void*) nativeFocusCandidateTextSize },
+ { "nativeFocusCandidateType", "()I",
+ (void*) nativeFocusCandidateType },
+ { "nativeFocusIsPlugin", "()Z",
+ (void*) nativeFocusIsPlugin },
+ { "nativeFocusNodeBounds", "()Landroid/graphics/Rect;",
+ (void*) nativeFocusNodeBounds },
+ { "nativeFocusNodePointer", "()I",
+ (void*) nativeFocusNodePointer },
+ { "nativeGetCursorRingBounds", "()Landroid/graphics/Rect;",
+ (void*) nativeGetCursorRingBounds },
+ { "nativeGetSelection", "()Ljava/lang/String;",
+ (void*) nativeGetSelection },
+ { "nativeHasCursorNode", "()Z",
+ (void*) nativeHasCursorNode },
+ { "nativeHasFocusNode", "()Z",
+ (void*) nativeHasFocusNode },
+ { "nativeHideCursor", "()V",
+ (void*) nativeHideCursor },
+ { "nativeHitSelection", "(II)Z",
+ (void*) nativeHitSelection },
+ { "nativeImageURI", "(II)Ljava/lang/String;",
+ (void*) nativeImageURI },
+ { "nativeInstrumentReport", "()V",
+ (void*) nativeInstrumentReport },
+ { "nativeLayerBounds", "(I)Landroid/graphics/Rect;",
+ (void*) nativeLayerBounds },
+ { "nativeMotionUp", "(III)Z",
+ (void*) nativeMotionUp },
+ { "nativeMoveCursor", "(IIZ)Z",
+ (void*) nativeMoveCursor },
+ { "nativeMoveCursorToNextTextInput", "()Z",
+ (void*) nativeMoveCursorToNextTextInput },
+ { "nativeMoveGeneration", "()I",
+ (void*) nativeMoveGeneration },
+ { "nativeMoveSelection", "(II)V",
+ (void*) nativeMoveSelection },
+ { "nativePointInNavCache", "(III)Z",
+ (void*) nativePointInNavCache },
+ { "nativeRecordButtons", "(ZZZ)V",
+ (void*) nativeRecordButtons },
+ { "nativeResetSelection", "()V",
+ (void*) nativeResetSelection },
+ { "nativeSelectableText", "()Landroid/graphics/Point;",
+ (void*) nativeSelectableText },
+ { "nativeSelectAll", "()V",
+ (void*) nativeSelectAll },
+ { "nativeSelectBestAt", "(Landroid/graphics/Rect;)V",
+ (void*) nativeSelectBestAt },
+ { "nativeSelectAt", "(II)V",
+ (void*) nativeSelectAt },
+ { "nativeSelectionX", "()I",
+ (void*) nativeSelectionX },
+ { "nativeSelectionY", "()I",
+ (void*) nativeSelectionY },
+ { "nativeSetExtendSelection", "()V",
+ (void*) nativeSetExtendSelection },
+ { "nativeSetFindIsEmpty", "()V",
+ (void*) nativeSetFindIsEmpty },
+ { "nativeSetFindIsUp", "(Z)V",
+ (void*) nativeSetFindIsUp },
+ { "nativeSetHeightCanMeasure", "(Z)V",
+ (void*) nativeSetHeightCanMeasure },
+ { "nativeSetBaseLayer", "(ILandroid/graphics/Region;ZZ)V",
+ (void*) nativeSetBaseLayer },
+ { "nativeReplaceBaseContent", "(I)V",
+ (void*) nativeReplaceBaseContent },
+ { "nativeCopyBaseContentToPicture", "(Landroid/graphics/Picture;)V",
+ (void*) nativeCopyBaseContentToPicture },
+ { "nativeHasContent", "()Z",
+ (void*) nativeHasContent },
+ { "nativeSetSelectionPointer", "(ZFII)V",
+ (void*) nativeSetSelectionPointer },
+ { "nativeShowCursorTimed", "()V",
+ (void*) nativeShowCursorTimed },
+ { "nativeStartSelection", "(II)Z",
+ (void*) nativeStartSelection },
+ { "nativeStopGL", "()V",
+ (void*) nativeStopGL },
+ { "nativeSubtractLayers", "(Landroid/graphics/Rect;)Landroid/graphics/Rect;",
+ (void*) nativeSubtractLayers },
+ { "nativeTextGeneration", "()I",
+ (void*) nativeTextGeneration },
+ { "nativeUpdateCachedTextfield", "(Ljava/lang/String;I)V",
+ (void*) nativeUpdateCachedTextfield },
+ { "nativeWordSelection", "(II)Z",
+ (void*) nativeWordSelection },
+ { "nativeGetBlockLeftEdge", "(IIF)I",
+ (void*) nativeGetBlockLeftEdge },
+ { "nativeScrollableLayer", "(IILandroid/graphics/Rect;Landroid/graphics/Rect;)I",
+ (void*) nativeScrollableLayer },
+ { "nativeScrollLayer", "(III)Z",
+ (void*) nativeScrollLayer },
+ { "nativeSetExpandedTileBounds", "(Z)V",
+ (void*) nativeSetExpandedTileBounds },
+};
+
+int registerWebView(JNIEnv* env)
+{
+ jclass clazz = env->FindClass("android/webkit/WebView");
+ LOG_ASSERT(clazz, "Unable to find class android/webkit/WebView");
+ gWebViewField = env->GetFieldID(clazz, "mNativeClass", "I");
+ LOG_ASSERT(gWebViewField, "Unable to find android/webkit/WebView.mNativeClass");
+ env->DeleteLocalRef(clazz);
+
+ return jniRegisterNativeMethods(env, "android/webkit/WebView", gJavaWebViewMethods, NELEM(gJavaWebViewMethods));
+}
+
+} // namespace android