| /* |
| * 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 <ui/KeycodeLabels.h> |
| #include <wtf/text/AtomicString.h> |
| #include <wtf/text/CString.h> |
| |
| // 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_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, |
| bool isHighEndGfx) : |
| m_ring((WebViewCore*) viewImpl) |
| , m_isHighEndGfx(isHighEndGfx) |
| { |
| 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_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", "(Z)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_isDrawingPaused = false; |
| 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 |
| |
| 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<ScrollableLayerAndroid*>(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; |
| int dx = 0; |
| int left = rect.x(); |
| int right = rect.maxX(); |
| if (left < m_visibleRect.fLeft) |
| dx = left - m_visibleRect.fLeft; |
| // Only scroll right if the entire width can fit on screen. |
| else if (right > m_visibleRect.fRight |
| && right - left < m_visibleRect.width()) |
| dx = right - m_visibleRect.fRight; |
| int dy = 0; |
| int top = rect.y(); |
| int bottom = rect.maxY(); |
| if (top < m_visibleRect.fTop) |
| dy = top - m_visibleRect.fTop; |
| // Only scroll down if the entire height can fit on screen |
| else if (bottom > m_visibleRect.fBottom |
| && bottom - top < m_visibleRect.height()) |
| dy = bottom - m_visibleRect.fBottom; |
| if ((dx|dy) == 0 || !scrollBy(dx, dy)) |
| return; |
| viewInvalidate(); |
| } |
| |
| 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(); |
| layer->updateFixedLayersPositions(m_visibleRect); |
| 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_glWebViewState->setHighEndGfx(m_isHighEndGfx); |
| 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(); |
| m_glWebViewState->glExtras()->setDrawExtra(extra); |
| |
| // 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 (!m_visibleRect.hasValidCoordinates()) |
| return false; |
| bool treesSwapped = false; |
| bool newTreeHasAnim = false; |
| bool ret = m_glWebViewState->drawGL(viewRect, m_visibleRect, invalRect, |
| webViewRect, titleBarHeight, clip, scale, |
| &treesSwapped, &newTreeHasAnim); |
| if (treesSwapped && (m_pageSwapCallbackRegistered || newTreeHasAnim)) { |
| 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, newTreeHasAnim); |
| checkException(env); |
| } |
| } |
| if (ret || m_glWebViewState->currentPictureCounter() != pic) |
| return !m_isDrawingPaused; |
| #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()) { |
| extra = &m_ring; |
| drawCursorPostamble(); |
| } |
| break; |
| default: |
| ; |
| } |
| #if USE(ACCELERATED_COMPOSITING) |
| LayerAndroid* compositeLayer = compositeRoot(); |
| if (compositeLayer) { |
| // call this to be sure we've adjusted for any scrolling or animations |
| // before we actually draw |
| compositeLayer->updateFixedLayersPositions(m_visibleRect); |
| 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 |
| if (extra) { |
| IntRect dummy; // inval area, unused for now |
| extra->draw(canvas, &mainPicture, &dummy); |
| } |
| 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<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) { |
| LayerAndroid* layer = const_cast<LayerAndroid*>( |
| m_frameCacheUI->rootLayer()); |
| if (layer) { |
| layer->updateFixedLayersPositions(m_visibleRect); |
| 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*>(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.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<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; |
| } |
| |
| void scrollLayer(int layerId, int x, int y) |
| { |
| if (m_glWebViewState) |
| m_glWebViewState->scrollLayer(layerId, x, y); |
| } |
| |
| 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<jint>(framePtr), reinterpret_cast<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(); |
| 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<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 = 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<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 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) { |
| // TODO: the below tree copies are only necessary in software rendering |
| 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 getTextSelectionRegion(SkRegion *region) |
| { |
| m_selectText.getSelectionRegion(getVisibleRect(), region, compositeRoot()); |
| } |
| |
| void getTextSelectionHandles(int* handles) |
| { |
| m_selectText.getSelectionHandles(handles, 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; |
| } |
| |
| BaseLayerAndroid* getBaseLayer() { |
| return m_baseLayer; |
| } |
| |
| void setVisibleRect(SkRect& visibleRect) { |
| m_visibleRect = visibleRect; |
| } |
| |
| bool m_isDrawingPaused; |
| 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; |
| SkRect m_visibleRect; |
| bool m_isHighEndGfx; |
| }; // 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); |
| TilesManager::instance()->shader()->setWebViewMatrix(info->transform, info->isLayer); |
| |
| 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, "<init>", "(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<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(); |
| return createJavaRect(env, bounds.x(), bounds.y(), |
| bounds.maxX(), bounds.maxY()); |
| } |
| |
| 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, jboolean isHighEndGfx) |
| { |
| WTF::String dir = jstringToWtfString(env, drawableDir); |
| WebView* webview = new WebView(env, obj, viewImpl, dir, isHighEndGfx); |
| // 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); |
| 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<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) |
| { |
| if (obj) { |
| int L, T, R, B; |
| GraphicsJNI::get_jrect(env, obj, &L, &T, &R, &B); |
| return WebCore::IntRect(L, T, R - L, B - T); |
| } else |
| return WebCore::IntRect(); |
| } |
| |
| static SkRect jrectf_to_rect(JNIEnv* env, jobject obj) |
| { |
| SkRect rect = SkRect::MakeEmpty(); |
| if (obj) |
| GraphicsJNI::jrectf_to_rect(env, obj, &rect); |
| return rect; |
| } |
| |
| 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, |
| jobject visible, jint color, |
| jint extras, jboolean split) { |
| SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, canv); |
| WebView* webView = GET_NATIVE_VIEW(env, obj); |
| SkRect visibleRect = jrectf_to_rect(env, visible); |
| webView->setVisibleRect(visibleRect); |
| PictureSet* pictureSet = webView->draw(canvas, color, extras, split); |
| return reinterpret_cast<jint>(pictureSet); |
| } |
| |
| static jint nativeGetDrawGLFunction(JNIEnv *env, jobject obj, jint nativeView, |
| jobject jrect, jobject jviewrect, |
| jobject jvisiblerect, |
| jfloat scale, jint extras) { |
| WebCore::IntRect viewRect = jrect_to_webrect(env, jrect); |
| WebView *wvInstance = (WebView*) nativeView; |
| SkRect visibleRect = jrectf_to_rect(env, jvisiblerect); |
| wvInstance->setVisibleRect(visibleRect); |
| |
| GLDrawFunctor* functor = new GLDrawFunctor(wvInstance, |
| &android::WebView::drawGL, viewRect, scale, extras); |
| wvInstance->setFunctor((Functor*) functor); |
| |
| WebCore::IntRect webViewRect = jrect_to_webrect(env, jviewrect); |
| functor->updateViewRect(webViewRect); |
| |
| return (jint)functor; |
| } |
| |
| static void nativeUpdateDrawGLFunction(JNIEnv *env, jobject obj, jobject jrect, |
| jobject jviewrect, jobject jvisiblerect) { |
| WebView *wvInstance = GET_NATIVE_VIEW(env, obj); |
| if (wvInstance) { |
| GLDrawFunctor* functor = (GLDrawFunctor*) wvInstance->getFunctor(); |
| if (functor) { |
| WebCore::IntRect viewRect = jrect_to_webrect(env, jrect); |
| functor->updateRect(viewRect); |
| |
| SkRect visibleRect = jrectf_to_rect(env, jvisiblerect); |
| wvInstance->setVisibleRect(visibleRect); |
| |
| WebCore::IntRect webViewRect = jrect_to_webrect(env, jviewrect); |
| functor->updateViewRect(webViewRect); |
| } |
| } |
| } |
| |
| static bool nativeEvaluateLayersAnimations(JNIEnv *env, jobject obj, jint nativeView) |
| { |
| // only call in software rendering, initialize and evaluate animations |
| #if USE(ACCELERATED_COMPOSITING) |
| LayerAndroid* root = ((WebView*)nativeView)->compositeRoot(); |
| if (root) { |
| root->initAnimations(); |
| 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<BaseLayerAndroid*>(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, jint view, |
| jobject region) |
| { |
| if (!region) |
| return; |
| SkRegion* nregion = GraphicsJNI::getNativeRegion(env, region); |
| ((WebView*)view)->getTextSelectionRegion(nregion); |
| } |
| |
| static void nativeGetSelectionHandles(JNIEnv *env, jobject obj, jint view, |
| jintArray arr) |
| { |
| int handles[4]; |
| ((WebView*)view)->getTextSelectionHandles(handles); |
| env->SetIntArrayRegion(arr, 0, 4, handles); |
| checkException(env); |
| } |
| |
| 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<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 nativeFocusCandidateNodeBounds(JNIEnv *env, jobject obj) |
| { |
| const CachedFrame* frame; |
| const CachedNode* node = getFocusCandidate(env, obj, &frame); |
| WebCore::IntRect bounds = node ? node->originalAbsoluteBounds() |
| : 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(), bounds.y(), |
| bounds.maxX(), bounds.maxY()); |
| } |
| |
| 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 jint nativeFocusCandidateIsSpellcheck(JNIEnv *env, jobject obj) |
| { |
| const CachedInput* input = getInputCandidate(env, obj); |
| return input ? input->spellcheck() : false; |
| } |
| |
| 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 int nativeFocusCandidateLayerId(JNIEnv *env, jobject obj) |
| { |
| const CachedFrame* frame = 0; |
| const CachedNode* node = getFocusNode(env, obj, &frame); |
| if (!node || !frame) |
| return -1; |
| const CachedLayer* layer = frame->layer(node); |
| if (!layer) |
| return -1; |
| return layer->uniqueId(); |
| } |
| |
| 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<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 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.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<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, jint nativeView, |
| jboolean set, jfloat scale, jint x, jint y) |
| { |
| ((WebView*)nativeView)->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*>(file)); |
| fwrite("\n", 1, 1, reinterpret_cast<FILE*>(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; |
| } |
| else if (key == "inverted_contrast") { |
| float contrast = value.toFloat(); |
| TilesManager::instance()->setInvertedScreenContrast(contrast); |
| return true; |
| } |
| else if (key == "enable_cpu_upload_path") { |
| TilesManager::instance()->transferQueue()->setTextureUploadType( |
| value == "true" ? CpuUpload : GpuUpload); |
| return true; |
| } |
| else if (key == "use_minimal_memory") { |
| TilesManager::instance()->setUseMinimalMemory(value == "true"); |
| 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); |
| view->scrollLayer(layerId, x, y); |
| |
| //TODO: the below only needed for the SW rendering path |
| 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 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; |
| } |
| |
| static void nativeSetPauseDrawing(JNIEnv *env, jobject obj, jint nativeView, |
| jboolean pause) |
| { |
| ((WebView*)nativeView)->m_isDrawingPaused = pause; |
| } |
| |
| /* |
| * 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;Z)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;Landroid/graphics/RectF;IIZ)I", |
| (void*) nativeDraw }, |
| { "nativeGetDrawGLFunction", "(ILandroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/RectF;FI)I", |
| (void*) nativeGetDrawGLFunction }, |
| { "nativeUpdateDrawGLFunction", "(Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/RectF;)V", |
| (void*) nativeUpdateDrawGLFunction }, |
| { "nativeDumpDisplayTree", "(Ljava/lang/String;)V", |
| (void*) nativeDumpDisplayTree }, |
| { "nativeEvaluateLayersAnimations", "(I)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 }, |
| { "nativeFocusCandidateIsSpellcheck", "()Z", |
| (void*) nativeFocusCandidateIsSpellcheck }, |
| { "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 }, |
| { "nativeFocusCandidateLayerId", "()I", |
| (void*) nativeFocusCandidateLayerId }, |
| { "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 }, |
| { "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", "(ILandroid/graphics/Region;)V", |
| (void*) nativeGetTextSelectionRegion }, |
| { "nativeGetSelectionHandles", "(I[I)V", |
| (void*) nativeGetSelectionHandles }, |
| { "nativeGetBaseLayer", "()I", |
| (void*) nativeGetBaseLayer }, |
| { "nativeReplaceBaseContent", "(I)V", |
| (void*) nativeReplaceBaseContent }, |
| { "nativeCopyBaseContentToPicture", "(Landroid/graphics/Picture;)V", |
| (void*) nativeCopyBaseContentToPicture }, |
| { "nativeHasContent", "()Z", |
| (void*) nativeHasContent }, |
| { "nativeSetSelectionPointer", "(IZFII)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 }, |
| { "nativeSetPauseDrawing", "(IZ)V", |
| (void*) nativeSetPauseDrawing }, |
| }; |
| |
| 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 |