/* * 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 #include #include #include #include #include // Free as much as we possible can #define TRIM_MEMORY_COMPLETE 80 // Free a lot (all textures gone) #define TRIM_MEMORY_MODERATE 60 // More moderate free (keep bare minimum to restore quickly-ish - possibly clear all textures) #define TRIM_MEMORY_BACKGROUND 40 // Moderate free (clear cached tiles, keep visible ones) #define TRIM_MEMORY_UI_HIDDEN 20 // Duration to show the pressed cursor ring #define PRESSED_STATE_DURATION 400 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 }; 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_pageSwapCallback; 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; jmethodID m_getTextHandleScale; AutoJObject object(JNIEnv* env) { return getRealObject(env, m_obj); } } m_javaGlue; WebView(JNIEnv* env, jobject javaWebView, int viewImpl, WTF::String drawableDir) : 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_pageSwapCallback = GetJMethod(env, clazz, "pageSwapCallback", "()V"); m_javaGlue.m_inFullScreenMode = GetJMethod(env, clazz, "inFullScreenMode", "()Z"); m_javaGlue.m_getTextHandleScale = GetJMethod(env, clazz, "getTextHandleScale", "()F"); 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; m_buttonSkin = drawableDir.isEmpty() ? 0 : new RenderSkinButton(drawableDir); #if USE(ACCELERATED_COMPOSITING) m_glWebViewState = 0; m_pageSwapCallbackRegistered = false; #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; } float getTextHandleScale() { LOG_ASSERT(m_javaGlue.m_obj, "A java object was not associated with this native WebView!"); JNIEnv* env = JSC::Bindings::getJNIEnv(); AutoJObject javaObject = m_javaGlue.object(env); if (!javaObject.get()) return 0; float result = env->CallFloatMethod(javaObject.get(), m_javaGlue.m_getTextHandleScale); checkException(env); return result; } void updateSelectionHandles() { if (!m_baseLayer) return; // Adjust for device density & scale m_selectText.updateHandleScale(getTextHandleScale()); } // 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); viewInvalidate(); } void hideCursor(CachedRoot* root) { DBG_NAV_LOG("inner"); m_viewImpl->m_hasCursorBounds = false; root->hideCursor(); } #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.maxX(), b.maxY()); } } void scrollToCurrentMatch() { if (!m_findOnPage.currentMatchIsInLayer()) { scrollRectOnScreen(m_findOnPage.currentMatchBounds()); return; } SkRect matchBounds = m_findOnPage.currentMatchBounds(); LayerAndroid* rootLayer = getFrameCache(DontAllowNewer)->rootLayer(); Layer* layerContainingMatch = rootLayer->findById(m_findOnPage.currentMatchLayerId()); ASSERT(layerContainingMatch); // If the match is in a fixed position layer, there's nothing to do. if (layerContainingMatch->shouldInheritFromRootTransform()) return; // If the match is in a scrollable layer or a descendant of such a layer, // there may be a range of of scroll configurations that will make the // current match visible. Our approach is the simplest possible. Starting at // the layer in which the match is found, we move up the layer tree, // scrolling any scrollable layers as little as possible to make sure that // the current match is in view. This approach has the disadvantage that we // may end up scrolling a larger number of elements than is necessary, which // may be visually jarring. However, minimising the number of layers // scrolled would complicate the code significantly. bool didScrollLayer = false; for (Layer* layer = layerContainingMatch; layer; layer = layer->getParent()) { ASSERT(layer->getParent() || layer == rootLayer); if (layer->contentIsScrollable()) { // Convert the match location to layer's local space and scroll it. // Repeatedly calling Layer::localToAncestor() is inefficient as // each call repeats part of the calculation. It would be more // efficient to maintain the transform here and update it on each // iteration, but that would mean duplicating logic from // Layer::localToAncestor() and would complicate things. SkMatrix transform; layerContainingMatch->localToAncestor(layer, &transform); SkRect transformedMatchBounds; transform.mapRect(&transformedMatchBounds, matchBounds); SkIRect roundedTransformedMatchBounds; transformedMatchBounds.roundOut(&roundedTransformedMatchBounds); // Only ScrollableLayerAndroid returns true for contentIsScrollable(). didScrollLayer |= static_cast(layer)->scrollRectIntoView(roundedTransformedMatchBounds); } } // Invalidate, as the call below to scroll the main page may be a no-op. if (didScrollLayer) viewInvalidate(); // Convert matchBounds to the global space so we can scroll the main page. SkMatrix transform; layerContainingMatch->localToGlobal(&transform); SkRect transformedMatchBounds; transform.mapRect(&transformedMatchBounds, matchBounds); SkIRect roundedTransformedMatchBounds; transformedMatchBounds.roundOut(&roundedTransformedMatchBounds); scrollRectOnScreen(roundedTransformedMatchBounds); } void scrollRectOnScreen(const IntRect& rect) { if (rect.isEmpty()) return; SkRect visible; calcOurContentVisibleRect(&visible); int dx = 0; int left = rect.x(); int right = rect.maxX(); 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.maxY(); 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(); AutoJObject javaObject = m_javaGlue.object(env); if (!javaObject.get()) return; jclass rectClass = env->FindClass("android/graphics/RectF"); jmethodID init = env->GetMethodID(rectClass, "", "(FFFF)V"); jobject jRect = env->NewObject(rectClass, init, 0, 0, 0, 0); env->CallVoidMethod(javaObject.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) { if (!root) return false; 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 = 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(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); m_glWebViewState->glExtras()->setCursorRingExtra(&m_ring); m_glWebViewState->glExtras()->setFindOnPageExtra(&m_findOnPage); 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, true); } } CachedRoot* root = getFrameCache(AllowNewer); if (!root) { DBG_NAV_LOG("!root"); if (extras == DrawExtrasCursorRing) resetCursorRing(); } DrawExtra* extra = 0; switch (extras) { case DrawExtrasFind: extra = &m_findOnPage; break; case DrawExtrasSelection: // This will involve a JNI call, but under normal circumstances we will // not hit this anyway. Only if USE_JAVA_TEXT_SELECTION is disabled // in WebView.java will we hit this (so really debug only) updateSelectionHandles(); extra = &m_selectText; break; case DrawExtrasCursorRing: if (drawCursorPreamble(root) && m_ring.setup()) { if (m_ring.m_isPressed || m_ringAnimationEnd == UINT_MAX) extra = &m_ring; drawCursorPostamble(); } break; default: ; } unsigned int pic = m_glWebViewState->currentPictureCounter(); if (extra == &m_ring) { if (m_ring.m_isButton || root != m_ring.m_frame) { // TODO: Fix the navcache to work with layers correctly // In the meantime, this works around the bug. However, the rings // it produces are not as nice for some reason, thus we use // m_ring.rings() as produced by navcache for the base layer and // for layers we generate a new ring set m_ring.rings().clear(); for (size_t i = 0; i < m_ring.m_node->rings().size(); i++) { IntRect rect = m_ring.m_node->rings().at(i); rect = m_ring.m_frame->adjustBounds(m_ring.m_node, rect); if (!m_ring.m_isButton) rect.inflate(4); m_ring.rings().append(rect); } } } m_glWebViewState->glExtras()->setDrawExtra(extra); LayerAndroid* compositeLayer = compositeRoot(); if (compositeLayer) compositeLayer->setExtra(0); SkRect visibleRect; calcOurContentVisibleRect(&visibleRect); // Make sure we have valid coordinates. We might not have valid coords // if the zoom manager is still initializing. We will be redrawn // once the correct scale is set if (!visibleRect.hasValidCoordinates()) return false; bool pagesSwapped = false; bool ret = m_glWebViewState->drawGL(viewRect, visibleRect, invalRect, webViewRect, titleBarHeight, clip, scale, &pagesSwapped); if (m_pageSwapCallbackRegistered && pagesSwapped) { m_pageSwapCallbackRegistered = false; LOG_ASSERT(m_javaGlue.m_obj, "A java object was not associated with this native WebView!"); JNIEnv* env = JSC::Bindings::getJNIEnv(); AutoJObject javaObject = m_javaGlue.object(env); if (javaObject.get()) { env->CallVoidMethod(javaObject.get(), m_javaGlue.m_pageSwapCallback); checkException(env); } } 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(); } LayerAndroid mainPicture(m_navPictureUI); DrawExtra* extra = 0; switch (extras) { case DrawExtrasFind: extra = &m_findOnPage; break; case DrawExtrasSelection: // This will involve a JNI call, but under normal circumstances we will // not hit this anyway. Only if USE_JAVA_TEXT_SELECTION is disabled // in WebView.java will we hit this (so really debug only) updateSelectionHandles(); 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.maxX() - newBounds.maxX()) > 4) return; if (abs(bounds.maxY() - newBounds.maxY()) > 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(frame), const_cast(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( 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(); AutoJObject javaObject = m_javaGlue.object(env); if (javaObject.get()) { env->CallVoidMethod(javaObject.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(); AutoJObject javaObject = m_javaGlue.object(env); if (!javaObject.get()) return 0; int result = env->CallIntMethod(javaObject.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(); AutoJObject javaObject = m_javaGlue.object(env); if (!javaObject.get()) return 0; int result = env->CallIntMethod(javaObject.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(); AutoJObject javaObject = m_javaGlue.object(env); if (!javaObject.get()) return rect; jobject jRect = env->CallObjectMethod(javaObject.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), const_cast(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.maxY() + dy > docHeight) dy = docHeight - visibleRect.maxY(); else if (visibleRect.y() + dy < 0) dy = -visibleRect.y(); if (visibleRect.maxX() + dx > docWidth) dx = docWidth - visibleRect.maxX(); 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(frame), const_cast(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(frame), const_cast(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(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(); AutoJObject javaObject = m_javaGlue.object(env); if (!javaObject.get()) return; jstring jName = wtfStringToJstring(env, url); env->CallVoidMethod(javaObject.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() + PRESSED_STATE_DURATION; 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; updateSelectionHandles(); return m_selectText.startSelection(root, getVisibleRect(), x, y); } bool wordSelection(int x, int y) { const CachedRoot* root = getFrameCache(DontAllowNewer); if (!root) return false; updateSelectionHandles(); 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) { updateSelectionHandles(); 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(); AutoJObject javaObject = m_javaGlue.object(env); if (!javaObject.get()) return; env->CallVoidMethod(javaObject.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(); AutoJObject javaObject = m_javaGlue.object(env); if (!javaObject.get()) return; env->CallVoidMethod(javaObject.get(), m_javaGlue.m_sendMoveMouse, reinterpret_cast(framePtr), reinterpret_cast(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(); AutoJObject javaObject = m_javaGlue.object(env); if (!javaObject.get()) return; env->CallVoidMethod(javaObject.get(), m_javaGlue.m_sendMoveMouseIfLatest, clearTextEntry, stopPaintingCaret); checkException(env); } void sendMotionUp(WebCore::Frame* framePtr, WebCore::Node* nodePtr, int x, int y) { 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(); AutoJObject javaObject = m_javaGlue.object(env); if (!javaObject.get()) return; m_viewImpl->m_touchGeneration = ++m_generation; env->CallVoidMethod(javaObject.get(), m_javaGlue.m_sendMotionUp, m_generation, (jint) framePtr, (jint) nodePtr, x, y); checkException(env); } void findNext(bool forward) { m_findOnPage.findNext(forward); scrollToCurrentMatch(); viewInvalidate(); } // With this call, WebView takes ownership of matches, and is responsible for // deleting it. void setMatches(WTF::Vector* 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 = false; if (sameAsLastSearch && m_findOnPage.isCurrentLocationValid()) { oldLocation = m_findOnPage.currentMatchBounds(); checkAgainstOldLocation = true; } m_findOnPage.setMatches(matches); if (!checkAgainstOldLocation || oldLocation != m_findOnPage.currentMatchBounds()) scrollToCurrentMatch(); 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(); AutoJObject javaObject = m_javaGlue.object(env); if (!javaObject.get()) return false; bool result = env->CallBooleanMethod(javaObject.get(), m_javaGlue.m_scrollBy, dx, dy, true); checkException(env); return result; } void setIsScrolling(bool isScrolling) { #if USE(ACCELERATED_COMPOSITING) if (m_glWebViewState) m_glWebViewState->setIsScrolling(isScrolling); #endif } 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(); AutoJObject javaObject = m_javaGlue.object(env); if (!javaObject.get()) return; env->CallVoidMethod(javaObject.get(), m_javaGlue.m_rebuildWebTextView); checkException(env); } void viewInvalidate() { JNIEnv* env = JSC::Bindings::getJNIEnv(); AutoJObject javaObject = m_javaGlue.object(env); if (!javaObject.get()) return; env->CallVoidMethod(javaObject.get(), m_javaGlue.m_viewInvalidate); checkException(env); } void viewInvalidateRect(int l, int t, int r, int b) { JNIEnv* env = JSC::Bindings::getJNIEnv(); AutoJObject javaObject = m_javaGlue.object(env); if (!javaObject.get()) return; env->CallVoidMethod(javaObject.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(); AutoJObject javaObject = m_javaGlue.object(env); if (!javaObject.get()) return; env->CallVoidMethod(javaObject.get(), m_javaGlue.m_postInvalidateDelayed, delay, bounds.x(), bounds.y(), bounds.maxX(), bounds.maxY()); checkException(env); } bool inFullScreenMode() { JNIEnv* env = JSC::Bindings::getJNIEnv(); AutoJObject javaObject = m_javaGlue.object(env); if (!javaObject.get()) return false; jboolean result = env->CallBooleanMethod(javaObject.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(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 registerPageSwapCallback() { m_pageSwapCallbackRegistered = true; } void setBaseLayer(BaseLayerAndroid* layer, SkRegion& inval, bool showVisualIndicator, bool isPictureAfterFirstLayout, bool registerPageSwapCallback) { #if USE(ACCELERATED_COMPOSITING) if (m_glWebViewState) m_glWebViewState->setBaseLayer(layer, inval, showVisualIndicator, isPictureAfterFirstLayout); m_pageSwapCallbackRegistered |= registerPageSwapCallback; #endif #if ENABLE(ANDROID_OVERFLOW_SCROLL) if (layer) { LayerAndroid* newCompositeRoot = static_cast(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 getTextSelectionRegion(SkRegion *region) { m_selectText.getSelectionRegion(getVisibleRect(), region); } 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; } BaseLayerAndroid* getBaseLayer() { return m_baseLayer; } 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; bool m_pageSwapCallbackRegistered; #endif 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(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.maxX(); info->dirtyBottom = finalInval.maxY(); } // 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; }; 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, "", "(IIII)V"); jobject rect = env->NewObject(rectClass, init, x, y, right, bottom); env->DeleteLocalRef(rectClass); return rect; } /* * Native JNI methods */ static int nativeCacheHitFramePointer(JNIEnv *env, jobject obj) { return reinterpret_cast(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(); return createJavaRect(env, bounds.x(), bounds.y(), bounds.maxX(), bounds.maxY()); } static int nativeCacheHitNodePointer(JNIEnv *env, jobject obj) { return reinterpret_cast(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) { WTF::String dir = jstringToWtfString(env, drawableDir); WebView* webview = new WebView(env, obj, viewImpl, dir); // 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(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); return createJavaRect(env, bounds.x(), bounds.y(), bounds.maxX(), bounds.maxY()); } static jint nativeCursorNodePointer(JNIEnv *env, jobject obj) { const CachedNode* node = getCursorNode(env, obj); return reinterpret_cast(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, "", "(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(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, jboolean registerPageSwapCallback) { BaseLayerAndroid* layerImpl = reinterpret_cast(layer); SkRegion invalRegion; if (inval) invalRegion = *GraphicsJNI::getNativeRegion(env, inval); GET_NATIVE_VIEW(env, obj)->setBaseLayer(layerImpl, invalRegion, showVisualIndicator, isPictureAfterFirstLayout, registerPageSwapCallback); } static void nativeGetTextSelectionRegion(JNIEnv *env, jobject obj, jobject region) { if (!region) return; SkRegion* nregion = GraphicsJNI::getNativeRegion(env, region); GET_NATIVE_VIEW(env, obj)->getTextSelectionRegion(nregion); } static BaseLayerAndroid* nativeGetBaseLayer(JNIEnv *env, jobject obj) { return GET_NATIVE_VIEW(env, obj)->getBaseLayer(); } static void nativeReplaceBaseContent(JNIEnv *env, jobject obj, jint content) { PictureSet* set = reinterpret_cast(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(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 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); // Inset the rect by 1 unit, so that the focus candidate's border can still // be seen behind it. return createJavaRect(env, bounds.x() + 1, bounds.y() + 1, bounds.maxX() - 1, bounds.maxY() - 1); } 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(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); return createJavaRect(env, bounds.x(), bounds.y(), bounds.maxX(), bounds.maxY()); } static jint nativeFocusNodePointer(JNIEnv *env, jobject obj) { const CachedNode* node = getFocusNode(env, obj); return node ? reinterpret_cast(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, "", "(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, "", "(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, "", "(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.maxX(), webRect.maxY()); 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* 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(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(frame), const_cast(next)); view->sendMoveFocus(static_cast(frame->framePointer()), static_cast(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, "", "(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); } static void nativeRegisterPageSwapCallback(JNIEnv *env, jobject obj) { GET_NATIVE_VIEW(env, obj)->registerPageSwapCallback(); } static void nativeTileProfilingStart(JNIEnv *env, jobject obj) { TilesManager::instance()->getProfiler()->start(); } static float nativeTileProfilingStop(JNIEnv *env, jobject obj) { return TilesManager::instance()->getProfiler()->stop(); } static void nativeTileProfilingClear(JNIEnv *env, jobject obj) { TilesManager::instance()->getProfiler()->clear(); } static int nativeTileProfilingNumFrames(JNIEnv *env, jobject obj) { return TilesManager::instance()->getProfiler()->numFrames(); } static int nativeTileProfilingNumTilesInFrame(JNIEnv *env, jobject obj, int frame) { return TilesManager::instance()->getProfiler()->numTilesInFrame(frame); } static int nativeTileProfilingGetInt(JNIEnv *env, jobject obj, int frame, int tile, jstring jkey) { WTF::String key = jstringToWtfString(env, jkey); TileProfileRecord* record = TilesManager::instance()->getProfiler()->getTile(frame, tile); if (key == "left") return record->left; if (key == "top") return record->top; if (key == "right") return record->right; if (key == "bottom") return record->bottom; if (key == "level") return record->level; if (key == "isReady") return record->isReady ? 1 : 0; return -1; } static float nativeTileProfilingGetFloat(JNIEnv *env, jobject obj, int frame, int tile, jstring jkey) { TileProfileRecord* record = TilesManager::instance()->getProfiler()->getTile(frame, tile); return record->scale; } #ifdef ANDROID_DUMP_DISPLAY_TREE static void dumpToFile(const char text[], void* file) { fwrite(text, 1, strlen(text), reinterpret_cast(file)); fwrite("\n", 1, 1, reinterpret_cast(file)); } #endif static bool nativeSetProperty(JNIEnv *env, jobject obj, jstring jkey, jstring jvalue) { WTF::String key = jstringToWtfString(env, jkey); WTF::String value = jstringToWtfString(env, jvalue); if (key == "inverted") { if (value == "true") TilesManager::instance()->setInvertedScreen(true); else TilesManager::instance()->setInvertedScreen(false); return true; } if (key == "inverted_contrast") { float contrast = value.toFloat(); TilesManager::instance()->setInvertedScreenContrast(contrast); return true; } if (key == "enable_cpu_upload_path") { TilesManager::instance()->transferQueue()->setTextureUploadType( value == "true" ? CpuUpload : GpuUpload); return true; } return false; } static jstring nativeGetProperty(JNIEnv *env, jobject obj, jstring key) { return 0; } static void nativeOnTrimMemory(JNIEnv *env, jobject obj, jint level) { if (TilesManager::hardwareAccelerationEnabled()) { bool freeAllTextures = (level > TRIM_MEMORY_UI_HIDDEN); TilesManager::instance()->deallocateTextures(freeAllTextures); } } 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(layer)->scrollTo(x, y); #endif return false; } static void nativeSetIsScrolling(JNIEnv* env, jobject jwebview, jboolean isScrolling) { WebView* view = GET_NATIVE_VIEW(env, jwebview); LOG_ASSERT(view, "view not set in %s", __FUNCTION__); view->setIsScrolling(isScrolling); } static void nativeUseHardwareAccelSkia(JNIEnv*, jobject, jboolean enabled) { BaseRenderer::setCurrentRendererType(enabled ? BaseRenderer::Ganesh : BaseRenderer::Raster); } static int nativeGetBackgroundColor(JNIEnv* env, jobject obj) { WebView* view = GET_NATIVE_VIEW(env, obj); BaseLayerAndroid* baseLayer = view->getBaseLayer(); if (baseLayer) { WebCore::Color color = baseLayer->getBackgroundColor(); if (color.isValid()) return SkColorSetARGB(color.alpha(), color.red(), color.green(), color.blue()); } return SK_ColorWHITE; } /* * 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;)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;ZZZ)V", (void*) nativeSetBaseLayer }, { "nativeGetTextSelectionRegion", "(Landroid/graphics/Region;)V", (void*) nativeGetTextSelectionRegion }, { "nativeGetBaseLayer", "()I", (void*) nativeGetBaseLayer }, { "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 }, { "nativeRegisterPageSwapCallback", "()V", (void*) nativeRegisterPageSwapCallback }, { "nativeTileProfilingStart", "()V", (void*) nativeTileProfilingStart }, { "nativeTileProfilingStop", "()F", (void*) nativeTileProfilingStop }, { "nativeTileProfilingClear", "()V", (void*) nativeTileProfilingClear }, { "nativeTileProfilingNumFrames", "()I", (void*) nativeTileProfilingNumFrames }, { "nativeTileProfilingNumTilesInFrame", "(I)I", (void*) nativeTileProfilingNumTilesInFrame }, { "nativeTileProfilingGetInt", "(IILjava/lang/String;)I", (void*) nativeTileProfilingGetInt }, { "nativeTileProfilingGetFloat", "(IILjava/lang/String;)F", (void*) nativeTileProfilingGetFloat }, { "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 }, { "nativeSetIsScrolling", "(Z)V", (void*) nativeSetIsScrolling }, { "nativeUseHardwareAccelSkia", "(Z)V", (void*) nativeUseHardwareAccelSkia }, { "nativeGetBackgroundColor", "()I", (void*) nativeGetBackgroundColor }, { "nativeSetProperty", "(Ljava/lang/String;Ljava/lang/String;)Z", (void*) nativeSetProperty }, { "nativeGetProperty", "(Ljava/lang/String;)Ljava/lang/String;", (void*) nativeGetProperty }, { "nativeOnTrimMemory", "(I)V", (void*) nativeOnTrimMemory }, }; 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