WebView.cpp revision a09e7cf49f43950a799f936bf42a9912d696547b
1/*
2 * Copyright 2007, The Android Open Source Project
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *  * Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 *  * Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#define LOG_TAG "webviewglue"
27
28#include "config.h"
29
30#include "AndroidAnimation.h"
31#include "AndroidLog.h"
32#include "BaseLayerAndroid.h"
33#include "CachedFrame.h"
34#include "CachedNode.h"
35#include "CachedRoot.h"
36#include "DrawExtra.h"
37#include "FindCanvas.h"
38#include "Frame.h"
39#include "GraphicsJNI.h"
40#include "HTMLInputElement.h"
41#include "IntPoint.h"
42#include "IntRect.h"
43#include "LayerAndroid.h"
44#include "Node.h"
45#include "utils/Functor.h"
46#include "private/hwui/DrawGlInfo.h"
47#include "PlatformGraphicsContext.h"
48#include "PlatformString.h"
49#include "ScrollableLayerAndroid.h"
50#include "SelectText.h"
51#include "SkCanvas.h"
52#include "SkDumpCanvas.h"
53#include "SkPicture.h"
54#include "SkRect.h"
55#include "SkTime.h"
56#include "TilesManager.h"
57#include "WebCoreJni.h"
58#include "WebRequestContext.h"
59#include "WebViewCore.h"
60#include "android_graphics.h"
61
62#ifdef GET_NATIVE_VIEW
63#undef GET_NATIVE_VIEW
64#endif
65
66#define GET_NATIVE_VIEW(env, obj) ((WebView*)env->GetIntField(obj, gWebViewField))
67
68#include <JNIUtility.h>
69#include <JNIHelp.h>
70#include <jni.h>
71#include <ui/KeycodeLabels.h>
72#include <wtf/text/AtomicString.h>
73#include <wtf/text/CString.h>
74
75// Free as much as we possible can
76#define TRIM_MEMORY_COMPLETE 80
77// Free a lot (all textures gone)
78#define TRIM_MEMORY_MODERATE 60
79// More moderate free (keep bare minimum to restore quickly-ish - possibly clear all textures)
80#define TRIM_MEMORY_BACKGROUND 40
81// Moderate free (clear cached tiles, keep visible ones)
82#define TRIM_MEMORY_UI_HIDDEN 20
83// Duration to show the pressed cursor ring
84#define PRESSED_STATE_DURATION 400
85
86namespace android {
87
88static jfieldID gWebViewField;
89
90//-------------------------------------
91
92static jmethodID GetJMethod(JNIEnv* env, jclass clazz, const char name[], const char signature[])
93{
94    jmethodID m = env->GetMethodID(clazz, name, signature);
95    LOG_ASSERT(m, "Could not find method %s", name);
96    return m;
97}
98
99//-------------------------------------
100// This class provides JNI for making calls into native code from the UI side
101// of the multi-threaded WebView.
102class WebView
103{
104public:
105enum FrameCachePermission {
106    DontAllowNewer,
107    AllowNewer
108};
109
110enum DrawExtras { // keep this in sync with WebView.java
111    DrawExtrasNone = 0,
112    DrawExtrasFind = 1,
113    DrawExtrasSelection = 2,
114    DrawExtrasCursorRing = 3
115};
116
117struct JavaGlue {
118    jweak       m_obj;
119    jmethodID   m_overrideLoading;
120    jmethodID   m_scrollBy;
121    jmethodID   m_sendMoveFocus;
122    jmethodID   m_sendMoveMouse;
123    jmethodID   m_sendMoveMouseIfLatest;
124    jmethodID   m_sendMotionUp;
125    jmethodID   m_domChangedFocus;
126    jmethodID   m_getScaledMaxXScroll;
127    jmethodID   m_getScaledMaxYScroll;
128    jmethodID   m_getVisibleRect;
129    jmethodID   m_rebuildWebTextView;
130    jmethodID   m_viewInvalidate;
131    jmethodID   m_viewInvalidateRect;
132    jmethodID   m_postInvalidateDelayed;
133    jmethodID   m_pageSwapCallback;
134    jfieldID    m_rectLeft;
135    jfieldID    m_rectTop;
136    jmethodID   m_rectWidth;
137    jmethodID   m_rectHeight;
138    jfieldID    m_rectFLeft;
139    jfieldID    m_rectFTop;
140    jmethodID   m_rectFWidth;
141    jmethodID   m_rectFHeight;
142    jmethodID   m_getTextHandleScale;
143    AutoJObject object(JNIEnv* env) {
144        return getRealObject(env, m_obj);
145    }
146} m_javaGlue;
147
148WebView(JNIEnv* env, jobject javaWebView, int viewImpl, WTF::String drawableDir,
149        bool isHighEndGfx) :
150    m_ring((WebViewCore*) viewImpl)
151    , m_isHighEndGfx(isHighEndGfx)
152{
153    jclass clazz = env->FindClass("android/webkit/WebView");
154 //   m_javaGlue = new JavaGlue;
155    m_javaGlue.m_obj = env->NewWeakGlobalRef(javaWebView);
156    m_javaGlue.m_scrollBy = GetJMethod(env, clazz, "setContentScrollBy", "(IIZ)Z");
157    m_javaGlue.m_overrideLoading = GetJMethod(env, clazz, "overrideLoading", "(Ljava/lang/String;)V");
158    m_javaGlue.m_sendMoveFocus = GetJMethod(env, clazz, "sendMoveFocus", "(II)V");
159    m_javaGlue.m_sendMoveMouse = GetJMethod(env, clazz, "sendMoveMouse", "(IIII)V");
160    m_javaGlue.m_sendMoveMouseIfLatest = GetJMethod(env, clazz, "sendMoveMouseIfLatest", "(ZZ)V");
161    m_javaGlue.m_sendMotionUp = GetJMethod(env, clazz, "sendMotionUp", "(IIIII)V");
162    m_javaGlue.m_domChangedFocus = GetJMethod(env, clazz, "domChangedFocus", "()V");
163    m_javaGlue.m_getScaledMaxXScroll = GetJMethod(env, clazz, "getScaledMaxXScroll", "()I");
164    m_javaGlue.m_getScaledMaxYScroll = GetJMethod(env, clazz, "getScaledMaxYScroll", "()I");
165    m_javaGlue.m_getVisibleRect = GetJMethod(env, clazz, "sendOurVisibleRect", "()Landroid/graphics/Rect;");
166    m_javaGlue.m_rebuildWebTextView = GetJMethod(env, clazz, "rebuildWebTextView", "()V");
167    m_javaGlue.m_viewInvalidate = GetJMethod(env, clazz, "viewInvalidate", "()V");
168    m_javaGlue.m_viewInvalidateRect = GetJMethod(env, clazz, "viewInvalidate", "(IIII)V");
169    m_javaGlue.m_postInvalidateDelayed = GetJMethod(env, clazz,
170        "viewInvalidateDelayed", "(JIIII)V");
171    m_javaGlue.m_pageSwapCallback = GetJMethod(env, clazz, "pageSwapCallback", "(Z)V");
172    m_javaGlue.m_getTextHandleScale = GetJMethod(env, clazz, "getTextHandleScale", "()F");
173    env->DeleteLocalRef(clazz);
174
175    jclass rectClass = env->FindClass("android/graphics/Rect");
176    LOG_ASSERT(rectClass, "Could not find Rect class");
177    m_javaGlue.m_rectLeft = env->GetFieldID(rectClass, "left", "I");
178    m_javaGlue.m_rectTop = env->GetFieldID(rectClass, "top", "I");
179    m_javaGlue.m_rectWidth = GetJMethod(env, rectClass, "width", "()I");
180    m_javaGlue.m_rectHeight = GetJMethod(env, rectClass, "height", "()I");
181    env->DeleteLocalRef(rectClass);
182
183    jclass rectClassF = env->FindClass("android/graphics/RectF");
184    LOG_ASSERT(rectClassF, "Could not find RectF class");
185    m_javaGlue.m_rectFLeft = env->GetFieldID(rectClassF, "left", "F");
186    m_javaGlue.m_rectFTop = env->GetFieldID(rectClassF, "top", "F");
187    m_javaGlue.m_rectFWidth = GetJMethod(env, rectClassF, "width", "()F");
188    m_javaGlue.m_rectFHeight = GetJMethod(env, rectClassF, "height", "()F");
189    env->DeleteLocalRef(rectClassF);
190
191    env->SetIntField(javaWebView, gWebViewField, (jint)this);
192    m_viewImpl = (WebViewCore*) viewImpl;
193    m_frameCacheUI = 0;
194    m_navPictureUI = 0;
195    m_generation = 0;
196    m_heightCanMeasure = false;
197    m_lastDx = 0;
198    m_lastDxTime = 0;
199    m_ringAnimationEnd = 0;
200    m_baseLayer = 0;
201    m_glDrawFunctor = 0;
202    m_isDrawingPaused = false;
203    m_buttonSkin = drawableDir.isEmpty() ? 0 : new RenderSkinButton(drawableDir);
204#if USE(ACCELERATED_COMPOSITING)
205    m_glWebViewState = 0;
206    m_pageSwapCallbackRegistered = false;
207#endif
208}
209
210~WebView()
211{
212    if (m_javaGlue.m_obj)
213    {
214        JNIEnv* env = JSC::Bindings::getJNIEnv();
215        env->DeleteWeakGlobalRef(m_javaGlue.m_obj);
216        m_javaGlue.m_obj = 0;
217    }
218#if USE(ACCELERATED_COMPOSITING)
219    // We must remove the m_glWebViewState prior to deleting m_baseLayer. If we
220    // do not remove it here, we risk having BaseTiles trying to paint using a
221    // deallocated base layer.
222    stopGL();
223#endif
224    delete m_frameCacheUI;
225    delete m_navPictureUI;
226    SkSafeUnref(m_baseLayer);
227    delete m_glDrawFunctor;
228    delete m_buttonSkin;
229}
230
231void stopGL()
232{
233#if USE(ACCELERATED_COMPOSITING)
234    delete m_glWebViewState;
235    m_glWebViewState = 0;
236#endif
237}
238
239WebViewCore* getWebViewCore() const {
240    return m_viewImpl;
241}
242
243float getTextHandleScale()
244{
245    LOG_ASSERT(m_javaGlue.m_obj, "A java object was not associated with this native WebView!");
246    JNIEnv* env = JSC::Bindings::getJNIEnv();
247    AutoJObject javaObject = m_javaGlue.object(env);
248    if (!javaObject.get())
249        return 0;
250    float result = env->CallFloatMethod(javaObject.get(), m_javaGlue.m_getTextHandleScale);
251    checkException(env);
252    return result;
253}
254
255void updateSelectionHandles()
256{
257    if (!m_baseLayer)
258        return;
259    // Adjust for device density & scale
260    m_selectText.updateHandleScale(getTextHandleScale());
261}
262
263// removes the cursor altogether (e.g., when going to a new page)
264void clearCursor()
265{
266    CachedRoot* root = getFrameCache(AllowNewer);
267    if (!root)
268        return;
269    DBG_NAV_LOG("");
270    m_viewImpl->m_hasCursorBounds = false;
271    root->clearCursor();
272    viewInvalidate();
273}
274
275// leaves the cursor where it is, but suppresses drawing it
276void hideCursor()
277{
278    CachedRoot* root = getFrameCache(AllowNewer);
279    if (!root)
280        return;
281    DBG_NAV_LOG("");
282    hideCursor(root);
283    viewInvalidate();
284}
285
286void hideCursor(CachedRoot* root)
287{
288    DBG_NAV_LOG("inner");
289    m_viewImpl->m_hasCursorBounds = false;
290    root->hideCursor();
291}
292
293#if DUMP_NAV_CACHE
294void debugDump()
295{
296    CachedRoot* root = getFrameCache(DontAllowNewer);
297    if (root)
298        root->mDebug.print();
299}
300#endif
301
302void scrollToCurrentMatch()
303{
304    if (!m_findOnPage.currentMatchIsInLayer()) {
305        scrollRectOnScreen(m_findOnPage.currentMatchBounds());
306        return;
307    }
308
309    SkRect matchBounds = m_findOnPage.currentMatchBounds();
310    LayerAndroid* rootLayer = getFrameCache(DontAllowNewer)->rootLayer();
311    Layer* layerContainingMatch = rootLayer->findById(m_findOnPage.currentMatchLayerId());
312    ASSERT(layerContainingMatch);
313
314    // If the match is in a fixed position layer, there's nothing to do.
315    if (layerContainingMatch->shouldInheritFromRootTransform())
316        return;
317
318    // If the match is in a scrollable layer or a descendant of such a layer,
319    // there may be a range of of scroll configurations that will make the
320    // current match visible. Our approach is the simplest possible. Starting at
321    // the layer in which the match is found, we move up the layer tree,
322    // scrolling any scrollable layers as little as possible to make sure that
323    // the current match is in view. This approach has the disadvantage that we
324    // may end up scrolling a larger number of elements than is necessary, which
325    // may be visually jarring. However, minimising the number of layers
326    // scrolled would complicate the code significantly.
327
328    bool didScrollLayer = false;
329    for (Layer* layer = layerContainingMatch; layer; layer = layer->getParent()) {
330        ASSERT(layer->getParent() || layer == rootLayer);
331
332        if (layer->contentIsScrollable()) {
333            // Convert the match location to layer's local space and scroll it.
334            // Repeatedly calling Layer::localToAncestor() is inefficient as
335            // each call repeats part of the calculation. It would be more
336            // efficient to maintain the transform here and update it on each
337            // iteration, but that would mean duplicating logic from
338            // Layer::localToAncestor() and would complicate things.
339            SkMatrix transform;
340            layerContainingMatch->localToAncestor(layer, &transform);
341            SkRect transformedMatchBounds;
342            transform.mapRect(&transformedMatchBounds, matchBounds);
343            SkIRect roundedTransformedMatchBounds;
344            transformedMatchBounds.roundOut(&roundedTransformedMatchBounds);
345            // Only ScrollableLayerAndroid returns true for contentIsScrollable().
346            didScrollLayer |= static_cast<ScrollableLayerAndroid*>(layer)->scrollRectIntoView(roundedTransformedMatchBounds);
347        }
348    }
349    // Invalidate, as the call below to scroll the main page may be a no-op.
350    if (didScrollLayer)
351        viewInvalidate();
352
353    // Convert matchBounds to the global space so we can scroll the main page.
354    SkMatrix transform;
355    layerContainingMatch->localToGlobal(&transform);
356    SkRect transformedMatchBounds;
357    transform.mapRect(&transformedMatchBounds, matchBounds);
358    SkIRect roundedTransformedMatchBounds;
359    transformedMatchBounds.roundOut(&roundedTransformedMatchBounds);
360    scrollRectOnScreen(roundedTransformedMatchBounds);
361}
362
363void scrollRectOnScreen(const IntRect& rect)
364{
365    if (rect.isEmpty())
366        return;
367    int dx = 0;
368    int left = rect.x();
369    int right = rect.maxX();
370    if (left < m_visibleRect.fLeft)
371        dx = left - m_visibleRect.fLeft;
372    // Only scroll right if the entire width can fit on screen.
373    else if (right > m_visibleRect.fRight
374            && right - left < m_visibleRect.width())
375        dx = right - m_visibleRect.fRight;
376    int dy = 0;
377    int top = rect.y();
378    int bottom = rect.maxY();
379    if (top < m_visibleRect.fTop)
380        dy = top - m_visibleRect.fTop;
381    // Only scroll down if the entire height can fit on screen
382    else if (bottom > m_visibleRect.fBottom
383            && bottom - top < m_visibleRect.height())
384        dy = bottom - m_visibleRect.fBottom;
385    if ((dx|dy) == 0 || !scrollBy(dx, dy))
386        return;
387    viewInvalidate();
388}
389
390void resetCursorRing()
391{
392    m_ringAnimationEnd = 0;
393    m_viewImpl->m_hasCursorBounds = false;
394}
395
396bool drawCursorPreamble(CachedRoot* root)
397{
398    if (!root) return false;
399    const CachedFrame* frame;
400    const CachedNode* node = root->currentCursor(&frame);
401    if (!node) {
402        DBG_NAV_LOGV("%s", "!node");
403        resetCursorRing();
404        return false;
405    }
406    m_ring.setIsButton(node);
407    if (node->isHidden()) {
408        DBG_NAV_LOG("node->isHidden()");
409        m_viewImpl->m_hasCursorBounds = false;
410        return false;
411    }
412#if USE(ACCELERATED_COMPOSITING)
413    if (node->isInLayer() && root->rootLayer()) {
414        LayerAndroid* layer = root->rootLayer();
415        layer->updateFixedLayersPositions(m_visibleRect);
416        layer->updatePositions();
417    }
418#endif
419    setVisibleRect(root);
420    m_ring.m_root = root;
421    m_ring.m_frame = frame;
422    m_ring.m_node = node;
423    SkMSec time = SkTime::GetMSecs();
424    m_ring.m_isPressed = time < m_ringAnimationEnd
425        && m_ringAnimationEnd != UINT_MAX;
426    return true;
427}
428
429void drawCursorPostamble()
430{
431    if (m_ringAnimationEnd == UINT_MAX)
432        return;
433    SkMSec time = SkTime::GetMSecs();
434    if (time < m_ringAnimationEnd) {
435        // views assume that inval bounds coordinates are non-negative
436        WebCore::IntRect invalBounds(0, 0, INT_MAX, INT_MAX);
437        invalBounds.intersect(m_ring.m_absBounds);
438        postInvalidateDelayed(m_ringAnimationEnd - time, invalBounds);
439    } else {
440        hideCursor(const_cast<CachedRoot*>(m_ring.m_root));
441    }
442}
443
444bool drawGL(WebCore::IntRect& viewRect, WebCore::IntRect* invalRect,
445        WebCore::IntRect& webViewRect, int titleBarHeight,
446        WebCore::IntRect& clip, float scale, int extras)
447{
448#if USE(ACCELERATED_COMPOSITING)
449    if (!m_baseLayer)
450        return false;
451
452    if (!m_glWebViewState) {
453        m_glWebViewState = new GLWebViewState();
454        m_glWebViewState->setHighEndGfx(m_isHighEndGfx);
455        m_glWebViewState->glExtras()->setCursorRingExtra(&m_ring);
456        m_glWebViewState->glExtras()->setFindOnPageExtra(&m_findOnPage);
457        if (m_baseLayer->content()) {
458            SkRegion region;
459            SkIRect rect;
460            rect.set(0, 0, m_baseLayer->content()->width(), m_baseLayer->content()->height());
461            region.setRect(rect);
462            m_glWebViewState->setBaseLayer(m_baseLayer, region, false, true);
463        }
464    }
465
466    CachedRoot* root = getFrameCache(AllowNewer);
467    if (!root) {
468        DBG_NAV_LOG("!root");
469        if (extras == DrawExtrasCursorRing)
470            resetCursorRing();
471    }
472    DrawExtra* extra = 0;
473    switch (extras) {
474        case DrawExtrasFind:
475            extra = &m_findOnPage;
476            break;
477        case DrawExtrasSelection:
478            // This will involve a JNI call, but under normal circumstances we will
479            // not hit this anyway. Only if USE_JAVA_TEXT_SELECTION is disabled
480            // in WebView.java will we hit this (so really debug only)
481            updateSelectionHandles();
482            extra = &m_selectText;
483            break;
484        case DrawExtrasCursorRing:
485            if (drawCursorPreamble(root) && m_ring.setup()) {
486                if (m_ring.m_isPressed || m_ringAnimationEnd == UINT_MAX)
487                    extra = &m_ring;
488                drawCursorPostamble();
489            }
490            break;
491        default:
492            ;
493    }
494
495    unsigned int pic = m_glWebViewState->currentPictureCounter();
496    m_glWebViewState->glExtras()->setDrawExtra(extra);
497
498    // Make sure we have valid coordinates. We might not have valid coords
499    // if the zoom manager is still initializing. We will be redrawn
500    // once the correct scale is set
501    if (!m_visibleRect.hasValidCoordinates())
502        return false;
503    bool treesSwapped = false;
504    bool newTreeHasAnim = false;
505    bool ret = m_glWebViewState->drawGL(viewRect, m_visibleRect, invalRect,
506                                        webViewRect, titleBarHeight, clip, scale,
507                                        &treesSwapped, &newTreeHasAnim);
508    if (treesSwapped && (m_pageSwapCallbackRegistered || newTreeHasAnim)) {
509        m_pageSwapCallbackRegistered = false;
510        LOG_ASSERT(m_javaGlue.m_obj, "A java object was not associated with this native WebView!");
511        JNIEnv* env = JSC::Bindings::getJNIEnv();
512        AutoJObject javaObject = m_javaGlue.object(env);
513        if (javaObject.get()) {
514            env->CallVoidMethod(javaObject.get(), m_javaGlue.m_pageSwapCallback, newTreeHasAnim);
515            checkException(env);
516        }
517    }
518    if (ret || m_glWebViewState->currentPictureCounter() != pic)
519        return !m_isDrawingPaused;
520#endif
521    return false;
522}
523
524PictureSet* draw(SkCanvas* canvas, SkColor bgColor, int extras, bool split)
525{
526    PictureSet* ret = 0;
527    if (!m_baseLayer) {
528        canvas->drawColor(bgColor);
529        return ret;
530    }
531
532    // draw the content of the base layer first
533    PictureSet* content = m_baseLayer->content();
534    int sc = canvas->save(SkCanvas::kClip_SaveFlag);
535    canvas->clipRect(SkRect::MakeLTRB(0, 0, content->width(),
536                content->height()), SkRegion::kDifference_Op);
537    canvas->drawColor(bgColor);
538    canvas->restoreToCount(sc);
539    if (content->draw(canvas))
540        ret = split ? new PictureSet(*content) : 0;
541
542    CachedRoot* root = getFrameCache(AllowNewer);
543    if (!root) {
544        DBG_NAV_LOG("!root");
545        if (extras == DrawExtrasCursorRing)
546            resetCursorRing();
547    }
548    LayerAndroid mainPicture(m_navPictureUI);
549    DrawExtra* extra = 0;
550    switch (extras) {
551        case DrawExtrasFind:
552            extra = &m_findOnPage;
553            break;
554        case DrawExtrasSelection:
555            // This will involve a JNI call, but under normal circumstances we will
556            // not hit this anyway. Only if USE_JAVA_TEXT_SELECTION is disabled
557            // in WebView.java will we hit this (so really debug only)
558            updateSelectionHandles();
559            extra = &m_selectText;
560            break;
561        case DrawExtrasCursorRing:
562            if (drawCursorPreamble(root) && m_ring.setup()) {
563                extra = &m_ring;
564                drawCursorPostamble();
565            }
566            break;
567        default:
568            ;
569    }
570#if USE(ACCELERATED_COMPOSITING)
571    LayerAndroid* compositeLayer = compositeRoot();
572    if (compositeLayer) {
573        // call this to be sure we've adjusted for any scrolling or animations
574        // before we actually draw
575        compositeLayer->updateFixedLayersPositions(m_visibleRect);
576        compositeLayer->updatePositions();
577        // We have to set the canvas' matrix on the base layer
578        // (to have fixed layers work as intended)
579        SkAutoCanvasRestore restore(canvas, true);
580        m_baseLayer->setMatrix(canvas->getTotalMatrix());
581        canvas->resetMatrix();
582        m_baseLayer->draw(canvas);
583    }
584#endif
585    if (extra) {
586        IntRect dummy; // inval area, unused for now
587        extra->draw(canvas, &mainPicture, &dummy);
588    }
589    return ret;
590}
591
592
593bool cursorIsTextInput(FrameCachePermission allowNewer)
594{
595    CachedRoot* root = getFrameCache(allowNewer);
596    if (!root) {
597        DBG_NAV_LOG("!root");
598        return false;
599    }
600    const CachedNode* cursor = root->currentCursor();
601    if (!cursor) {
602        DBG_NAV_LOG("!cursor");
603        return false;
604    }
605    DBG_NAV_LOGD("%s", cursor->isTextInput() ? "true" : "false");
606    return cursor->isTextInput();
607}
608
609void cursorRingBounds(WebCore::IntRect* bounds)
610{
611    DBG_NAV_LOGD("%s", "");
612    CachedRoot* root = getFrameCache(DontAllowNewer);
613    if (root) {
614        const CachedFrame* cachedFrame;
615        const CachedNode* cachedNode = root->currentCursor(&cachedFrame);
616        if (cachedNode) {
617            *bounds = cachedNode->cursorRingBounds(cachedFrame);
618            DBG_NAV_LOGD("bounds={%d,%d,%d,%d}", bounds->x(), bounds->y(),
619                bounds->width(), bounds->height());
620            return;
621        }
622    }
623    *bounds = WebCore::IntRect(0, 0, 0, 0);
624}
625
626void fixCursor()
627{
628    m_viewImpl->gCursorBoundsMutex.lock();
629    bool hasCursorBounds = m_viewImpl->m_hasCursorBounds;
630    IntRect bounds = m_viewImpl->m_cursorBounds;
631    m_viewImpl->gCursorBoundsMutex.unlock();
632    if (!hasCursorBounds)
633        return;
634    int x, y;
635    const CachedFrame* frame;
636    const CachedNode* node = m_frameCacheUI->findAt(bounds, &frame, &x, &y, true);
637    if (!node)
638        return;
639    // require that node have approximately the same bounds (+/- 4) and the same
640    // center (+/- 2)
641    IntPoint oldCenter = IntPoint(bounds.x() + (bounds.width() >> 1),
642        bounds.y() + (bounds.height() >> 1));
643    IntRect newBounds = node->bounds(frame);
644    IntPoint newCenter = IntPoint(newBounds.x() + (newBounds.width() >> 1),
645        newBounds.y() + (newBounds.height() >> 1));
646    DBG_NAV_LOGD("oldCenter=(%d,%d) newCenter=(%d,%d)"
647        " bounds=(%d,%d,w=%d,h=%d) newBounds=(%d,%d,w=%d,h=%d)",
648        oldCenter.x(), oldCenter.y(), newCenter.x(), newCenter.y(),
649        bounds.x(), bounds.y(), bounds.width(), bounds.height(),
650        newBounds.x(), newBounds.y(), newBounds.width(), newBounds.height());
651    if (abs(oldCenter.x() - newCenter.x()) > 2)
652        return;
653    if (abs(oldCenter.y() - newCenter.y()) > 2)
654        return;
655    if (abs(bounds.x() - newBounds.x()) > 4)
656        return;
657    if (abs(bounds.y() - newBounds.y()) > 4)
658        return;
659    if (abs(bounds.maxX() - newBounds.maxX()) > 4)
660        return;
661    if (abs(bounds.maxY() - newBounds.maxY()) > 4)
662        return;
663    DBG_NAV_LOGD("node=%p frame=%p x=%d y=%d bounds=(%d,%d,w=%d,h=%d)",
664        node, frame, x, y, bounds.x(), bounds.y(), bounds.width(),
665        bounds.height());
666    m_frameCacheUI->setCursor(const_cast<CachedFrame*>(frame),
667        const_cast<CachedNode*>(node));
668}
669
670CachedRoot* getFrameCache(FrameCachePermission allowNewer)
671{
672    if (!m_viewImpl->m_updatedFrameCache) {
673        DBG_NAV_LOGV("%s", "!m_viewImpl->m_updatedFrameCache");
674        return m_frameCacheUI;
675    }
676    if (allowNewer == DontAllowNewer && m_viewImpl->m_lastGeneration < m_generation) {
677        DBG_NAV_LOGD("allowNewer==DontAllowNewer m_viewImpl->m_lastGeneration=%d"
678            " < m_generation=%d", m_viewImpl->m_lastGeneration, m_generation);
679        return m_frameCacheUI;
680    }
681    DBG_NAV_LOGD("%s", "m_viewImpl->m_updatedFrameCache == true");
682    const CachedFrame* oldCursorFrame;
683    const CachedNode* oldCursorNode = m_frameCacheUI ?
684        m_frameCacheUI->currentCursor(&oldCursorFrame) : 0;
685#if USE(ACCELERATED_COMPOSITING)
686    int layerId = -1;
687    if (oldCursorNode && oldCursorNode->isInLayer()) {
688        const LayerAndroid* cursorLayer = oldCursorFrame->layer(oldCursorNode)
689            ->layer(m_frameCacheUI->rootLayer());
690        if (cursorLayer)
691            layerId = cursorLayer->uniqueId();
692    }
693#endif
694    // get id from old layer and use to find new layer
695    bool oldFocusIsTextInput = false;
696    void* oldFocusNodePointer = 0;
697    if (m_frameCacheUI) {
698        const CachedNode* oldFocus = m_frameCacheUI->currentFocus();
699        if (oldFocus) {
700            oldFocusIsTextInput = oldFocus->isTextInput();
701            oldFocusNodePointer = oldFocus->nodePointer();
702        }
703    }
704    m_viewImpl->gFrameCacheMutex.lock();
705    delete m_frameCacheUI;
706    SkSafeUnref(m_navPictureUI);
707    m_viewImpl->m_updatedFrameCache = false;
708    m_frameCacheUI = m_viewImpl->m_frameCacheKit;
709    m_navPictureUI = m_viewImpl->m_navPictureKit;
710    m_viewImpl->m_frameCacheKit = 0;
711    m_viewImpl->m_navPictureKit = 0;
712    m_viewImpl->gFrameCacheMutex.unlock();
713    if (m_frameCacheUI)
714        m_frameCacheUI->setRootLayer(compositeRoot());
715#if USE(ACCELERATED_COMPOSITING)
716    if (layerId >= 0) {
717        LayerAndroid* layer = const_cast<LayerAndroid*>(
718                                                m_frameCacheUI->rootLayer());
719        if (layer) {
720            layer->updateFixedLayersPositions(m_visibleRect);
721            layer->updatePositions();
722        }
723    }
724#endif
725    fixCursor();
726    if (oldFocusIsTextInput) {
727        const CachedNode* newFocus = m_frameCacheUI->currentFocus();
728        if (newFocus && oldFocusNodePointer != newFocus->nodePointer()
729                && newFocus->isTextInput()
730                && newFocus != m_frameCacheUI->currentCursor()) {
731            // The focus has changed.  We may need to update things.
732            LOG_ASSERT(m_javaGlue.m_obj, "A java object was not associated with this native WebView!");
733            JNIEnv* env = JSC::Bindings::getJNIEnv();
734            AutoJObject javaObject = m_javaGlue.object(env);
735            if (javaObject.get()) {
736                env->CallVoidMethod(javaObject.get(), m_javaGlue.m_domChangedFocus);
737                checkException(env);
738            }
739        }
740    }
741    if (oldCursorNode && (!m_frameCacheUI || !m_frameCacheUI->currentCursor()))
742        viewInvalidate(); // redraw in case cursor ring is still visible
743    return m_frameCacheUI;
744}
745
746int getScaledMaxXScroll()
747{
748    LOG_ASSERT(m_javaGlue.m_obj, "A java object was not associated with this native WebView!");
749    JNIEnv* env = JSC::Bindings::getJNIEnv();
750    AutoJObject javaObject = m_javaGlue.object(env);
751    if (!javaObject.get())
752        return 0;
753    int result = env->CallIntMethod(javaObject.get(), m_javaGlue.m_getScaledMaxXScroll);
754    checkException(env);
755    return result;
756}
757
758int getScaledMaxYScroll()
759{
760    LOG_ASSERT(m_javaGlue.m_obj, "A java object was not associated with this native WebView!");
761    JNIEnv* env = JSC::Bindings::getJNIEnv();
762    AutoJObject javaObject = m_javaGlue.object(env);
763    if (!javaObject.get())
764        return 0;
765    int result = env->CallIntMethod(javaObject.get(), m_javaGlue.m_getScaledMaxYScroll);
766    checkException(env);
767    return result;
768}
769
770IntRect getVisibleRect()
771{
772    IntRect rect;
773    LOG_ASSERT(m_javaGlue.m_obj, "A java object was not associated with this native WebView!");
774    JNIEnv* env = JSC::Bindings::getJNIEnv();
775    AutoJObject javaObject = m_javaGlue.object(env);
776    if (!javaObject.get())
777        return rect;
778    jobject jRect = env->CallObjectMethod(javaObject.get(), m_javaGlue.m_getVisibleRect);
779    checkException(env);
780    rect.setX(env->GetIntField(jRect, m_javaGlue.m_rectLeft));
781    checkException(env);
782    rect.setY(env->GetIntField(jRect, m_javaGlue.m_rectTop));
783    checkException(env);
784    rect.setWidth(env->CallIntMethod(jRect, m_javaGlue.m_rectWidth));
785    checkException(env);
786    rect.setHeight(env->CallIntMethod(jRect, m_javaGlue.m_rectHeight));
787    checkException(env);
788    env->DeleteLocalRef(jRect);
789    checkException(env);
790    return rect;
791}
792
793static CachedFrame::Direction KeyToDirection(int32_t keyCode)
794{
795    switch (keyCode) {
796        case AKEYCODE_DPAD_RIGHT:
797            DBG_NAV_LOGD("keyCode=%s", "right");
798            return CachedFrame::RIGHT;
799        case AKEYCODE_DPAD_LEFT:
800            DBG_NAV_LOGD("keyCode=%s", "left");
801            return CachedFrame::LEFT;
802        case AKEYCODE_DPAD_DOWN:
803            DBG_NAV_LOGD("keyCode=%s", "down");
804            return CachedFrame::DOWN;
805        case AKEYCODE_DPAD_UP:
806            DBG_NAV_LOGD("keyCode=%s", "up");
807            return CachedFrame::UP;
808        default:
809            DBG_NAV_LOGD("bad key %d sent", keyCode);
810            return CachedFrame::UNINITIALIZED;
811    }
812}
813
814WTF::String imageURI(int x, int y)
815{
816    const CachedRoot* root = getFrameCache(DontAllowNewer);
817    return root ? root->imageURI(x, y) : WTF::String();
818}
819
820bool cursorWantsKeyEvents()
821{
822    const CachedRoot* root = getFrameCache(DontAllowNewer);
823    if (root) {
824        const CachedNode* focus = root->currentCursor();
825        if (focus)
826            return focus->wantsKeyEvents();
827    }
828    return false;
829}
830
831
832/* returns true if the key had no effect (neither scrolled nor changed cursor) */
833bool moveCursor(int keyCode, int count, bool ignoreScroll)
834{
835    CachedRoot* root = getFrameCache(AllowNewer);
836    if (!root) {
837        DBG_NAV_LOG("!root");
838        return true;
839    }
840
841    m_viewImpl->m_moveGeneration++;
842    CachedFrame::Direction direction = KeyToDirection(keyCode);
843    const CachedFrame* cachedFrame, * oldFrame = 0;
844    const CachedNode* cursor = root->currentCursor(&oldFrame);
845    WebCore::IntPoint cursorLocation = root->cursorLocation();
846    DBG_NAV_LOGD("old cursor %d (nativeNode=%p) cursorLocation={%d, %d}",
847        cursor ? cursor->index() : 0,
848        cursor ? cursor->nodePointer() : 0, cursorLocation.x(), cursorLocation.y());
849    WebCore::IntRect visibleRect = setVisibleRect(root);
850    int xMax = getScaledMaxXScroll();
851    int yMax = getScaledMaxYScroll();
852    root->setMaxScroll(xMax, yMax);
853    const CachedNode* cachedNode = 0;
854    int dx = 0;
855    int dy = 0;
856    int counter = count;
857    while (--counter >= 0) {
858        WebCore::IntPoint scroll = WebCore::IntPoint(0, 0);
859        cachedNode = root->moveCursor(direction, &cachedFrame, &scroll);
860        dx += scroll.x();
861        dy += scroll.y();
862    }
863    DBG_NAV_LOGD("new cursor %d (nativeNode=%p) cursorLocation={%d, %d}"
864        "bounds={%d,%d,w=%d,h=%d}", cachedNode ? cachedNode->index() : 0,
865        cachedNode ? cachedNode->nodePointer() : 0,
866            root->cursorLocation().x(), root->cursorLocation().y(),
867            cachedNode ? cachedNode->bounds(cachedFrame).x() : 0,
868            cachedNode ? cachedNode->bounds(cachedFrame).y() : 0,
869            cachedNode ? cachedNode->bounds(cachedFrame).width() : 0,
870            cachedNode ? cachedNode->bounds(cachedFrame).height() : 0);
871    // If !m_heightCanMeasure (such as in the browser), we want to scroll no
872    // matter what
873    if (!ignoreScroll && (!m_heightCanMeasure ||
874            !cachedNode ||
875            (cursor && cursor->nodePointer() == cachedNode->nodePointer())))
876    {
877        if (count == 1 && dx != 0 && dy == 0 && -m_lastDx == dx &&
878                SkTime::GetMSecs() - m_lastDxTime < 1000)
879            root->checkForJiggle(&dx);
880        DBG_NAV_LOGD("scrollBy %d,%d", dx, dy);
881        if ((dx | dy))
882            this->scrollBy(dx, dy);
883        m_lastDx = dx;
884        m_lastDxTime = SkTime::GetMSecs();
885    }
886    bool result = false;
887    if (cachedNode) {
888        showCursorUntimed();
889        m_viewImpl->updateCursorBounds(root, cachedFrame, cachedNode);
890        root->setCursor(const_cast<CachedFrame*>(cachedFrame),
891                const_cast<CachedNode*>(cachedNode));
892        const CachedNode* focus = root->currentFocus();
893        bool clearTextEntry = cachedNode != focus && focus
894                && cachedNode->nodePointer() != focus->nodePointer() && focus->isTextInput();
895        // Stop painting the caret if the old focus was a text input and so is the new cursor.
896        bool stopPaintingCaret = clearTextEntry && cachedNode->wantsKeyEvents();
897        sendMoveMouseIfLatest(clearTextEntry, stopPaintingCaret);
898    } else {
899        int docHeight = root->documentHeight();
900        int docWidth = root->documentWidth();
901        if (visibleRect.maxY() + dy > docHeight)
902            dy = docHeight - visibleRect.maxY();
903        else if (visibleRect.y() + dy < 0)
904            dy = -visibleRect.y();
905        if (visibleRect.maxX() + dx > docWidth)
906            dx = docWidth - visibleRect.maxX();
907        else if (visibleRect.x() < 0)
908            dx = -visibleRect.x();
909        result = direction == CachedFrame::LEFT ? dx >= 0 :
910            direction == CachedFrame::RIGHT ? dx <= 0 :
911            direction == CachedFrame::UP ? dy >= 0 : dy <= 0;
912    }
913    return result;
914}
915
916void notifyProgressFinished()
917{
918    DBG_NAV_LOGD("cursorIsTextInput=%d", cursorIsTextInput(DontAllowNewer));
919    rebuildWebTextView();
920#if DEBUG_NAV_UI
921    if (m_frameCacheUI) {
922        const CachedNode* focus = m_frameCacheUI->currentFocus();
923        DBG_NAV_LOGD("focus %d (nativeNode=%p)",
924            focus ? focus->index() : 0,
925            focus ? focus->nodePointer() : 0);
926    }
927#endif
928}
929
930const CachedNode* findAt(CachedRoot* root, const WebCore::IntRect& rect,
931    const CachedFrame** framePtr, int* rxPtr, int* ryPtr)
932{
933    *rxPtr = 0;
934    *ryPtr = 0;
935    *framePtr = 0;
936    if (!root)
937        return 0;
938    setVisibleRect(root);
939    return root->findAt(rect, framePtr, rxPtr, ryPtr, true);
940}
941
942IntRect setVisibleRect(CachedRoot* root)
943{
944    IntRect visibleRect = getVisibleRect();
945    DBG_NAV_LOGD("getVisibleRect %d,%d,%d,%d",
946        visibleRect.x(), visibleRect.y(), visibleRect.width(), visibleRect.height());
947    root->setVisibleRect(visibleRect);
948    return visibleRect;
949}
950
951void selectBestAt(const WebCore::IntRect& rect)
952{
953    const CachedFrame* frame;
954    int rx, ry;
955    CachedRoot* root = getFrameCache(AllowNewer);
956    if (!root)
957        return;
958    const CachedNode* node = findAt(root, rect, &frame, &rx, &ry);
959    if (!node) {
960        DBG_NAV_LOGD("no nodes found root=%p", root);
961        root->rootHistory()->setMouseBounds(rect);
962        m_viewImpl->m_hasCursorBounds = false;
963        root->setCursor(0, 0);
964        viewInvalidate();
965    } else {
966        DBG_NAV_LOGD("CachedNode:%p (%d)", node, node->index());
967        WebCore::IntRect bounds = node->bounds(frame);
968        root->rootHistory()->setMouseBounds(bounds);
969        m_viewImpl->updateCursorBounds(root, frame, node);
970        showCursorTimed();
971        root->setCursor(const_cast<CachedFrame*>(frame),
972                const_cast<CachedNode*>(node));
973    }
974    sendMoveMouseIfLatest(false, false);
975}
976
977const CachedNode* m_cacheHitNode;
978const CachedFrame* m_cacheHitFrame;
979
980bool pointInNavCache(int x, int y, int slop)
981{
982    CachedRoot* root = getFrameCache(AllowNewer);
983    if (!root)
984        return false;
985    IntRect rect = IntRect(x - slop, y - slop, slop * 2, slop * 2);
986    int rx, ry;
987    return (m_cacheHitNode = findAt(root, rect, &m_cacheHitFrame, &rx, &ry));
988}
989
990bool motionUp(int x, int y, int slop)
991{
992    bool pageScrolled = false;
993    IntRect rect = IntRect(x - slop, y - slop, slop * 2, slop * 2);
994    int rx, ry;
995    CachedRoot* root = getFrameCache(AllowNewer);
996    if (!root)
997        return 0;
998    const CachedFrame* frame = 0;
999    const CachedNode* result = findAt(root, rect, &frame, &rx, &ry);
1000    CachedHistory* history = root->rootHistory();
1001    if (!result) {
1002        DBG_NAV_LOGD("no nodes found root=%p", root);
1003        history->setNavBounds(rect);
1004        m_viewImpl->m_hasCursorBounds = false;
1005        root->hideCursor();
1006        int dx = root->checkForCenter(x, y);
1007        if (dx) {
1008            scrollBy(dx, 0);
1009            pageScrolled = true;
1010        }
1011        sendMotionUp(frame ? (WebCore::Frame*) frame->framePointer() : 0,
1012            0, x, y);
1013        viewInvalidate();
1014        return pageScrolled;
1015    }
1016    DBG_NAV_LOGD("CachedNode:%p (%d) x=%d y=%d rx=%d ry=%d", result,
1017        result->index(), x, y, rx, ry);
1018    WebCore::IntRect navBounds = WebCore::IntRect(rx, ry, 1, 1);
1019    history->setNavBounds(navBounds);
1020    history->setMouseBounds(navBounds);
1021    m_viewImpl->updateCursorBounds(root, frame, result);
1022    root->setCursor(const_cast<CachedFrame*>(frame),
1023        const_cast<CachedNode*>(result));
1024    if (result->isSyntheticLink())
1025        overrideUrlLoading(result->getExport());
1026    else {
1027        sendMotionUp(
1028            (WebCore::Frame*) frame->framePointer(),
1029            (WebCore::Node*) result->nodePointer(), rx, ry);
1030    }
1031    if (result->isTextInput() || result->isSelect()
1032            || result->isContentEditable()) {
1033        showCursorUntimed();
1034    } else
1035        showCursorTimed();
1036    return pageScrolled;
1037}
1038
1039#if USE(ACCELERATED_COMPOSITING)
1040static const ScrollableLayerAndroid* findScrollableLayer(
1041    const LayerAndroid* parent, int x, int y, SkIRect* foundBounds) {
1042    SkRect bounds;
1043    parent->bounds(&bounds);
1044    // Check the parent bounds first; this will clip to within a masking layer's
1045    // bounds.
1046    if (parent->masksToBounds() && !bounds.contains(x, y))
1047        return 0;
1048    // Move the hit test local to parent.
1049    x -= bounds.fLeft;
1050    y -= bounds.fTop;
1051    int count = parent->countChildren();
1052    while (count--) {
1053        const LayerAndroid* child = parent->getChild(count);
1054        const ScrollableLayerAndroid* result = findScrollableLayer(child, x, y,
1055            foundBounds);
1056        if (result) {
1057            foundBounds->offset(bounds.fLeft, bounds.fTop);
1058            if (parent->masksToBounds()) {
1059                if (bounds.width() < foundBounds->width())
1060                    foundBounds->fRight = foundBounds->fLeft + bounds.width();
1061                if (bounds.height() < foundBounds->height())
1062                    foundBounds->fBottom = foundBounds->fTop + bounds.height();
1063            }
1064            return result;
1065        }
1066    }
1067    if (parent->contentIsScrollable()) {
1068        foundBounds->set(0, 0, bounds.width(), bounds.height());
1069        return static_cast<const ScrollableLayerAndroid*>(parent);
1070    }
1071    return 0;
1072}
1073#endif
1074
1075int scrollableLayer(int x, int y, SkIRect* layerRect, SkIRect* bounds)
1076{
1077#if USE(ACCELERATED_COMPOSITING)
1078    const LayerAndroid* layerRoot = compositeRoot();
1079    if (!layerRoot)
1080        return 0;
1081    const ScrollableLayerAndroid* result = findScrollableLayer(layerRoot, x, y,
1082        bounds);
1083    if (result) {
1084        result->getScrollRect(layerRect);
1085        return result->uniqueId();
1086    }
1087#endif
1088    return 0;
1089}
1090
1091void scrollLayer(int layerId, int x, int y)
1092{
1093    if (m_glWebViewState)
1094        m_glWebViewState->scrollLayer(layerId, x, y);
1095}
1096
1097int getBlockLeftEdge(int x, int y, float scale)
1098{
1099    CachedRoot* root = getFrameCache(AllowNewer);
1100    if (root)
1101        return root->getBlockLeftEdge(x, y, scale);
1102    return -1;
1103}
1104
1105void overrideUrlLoading(const WTF::String& url)
1106{
1107    JNIEnv* env = JSC::Bindings::getJNIEnv();
1108    AutoJObject javaObject = m_javaGlue.object(env);
1109    if (!javaObject.get())
1110        return;
1111    jstring jName = wtfStringToJstring(env, url);
1112    env->CallVoidMethod(javaObject.get(), m_javaGlue.m_overrideLoading, jName);
1113    env->DeleteLocalRef(jName);
1114}
1115
1116void setFindIsUp(bool up)
1117{
1118    DBG_NAV_LOGD("up=%d", up);
1119    m_viewImpl->m_findIsUp = up;
1120}
1121
1122void setFindIsEmpty()
1123{
1124    DBG_NAV_LOG("");
1125    m_findOnPage.clearCurrentLocation();
1126}
1127
1128void showCursorTimed()
1129{
1130    DBG_NAV_LOG("");
1131    m_ringAnimationEnd = SkTime::GetMSecs() + PRESSED_STATE_DURATION;
1132    viewInvalidate();
1133}
1134
1135void showCursorUntimed()
1136{
1137    DBG_NAV_LOG("");
1138    m_ring.m_isPressed = false;
1139    m_ringAnimationEnd = UINT_MAX;
1140    viewInvalidate();
1141}
1142
1143void setHeightCanMeasure(bool measure)
1144{
1145    m_heightCanMeasure = measure;
1146}
1147
1148String getSelection()
1149{
1150    return m_selectText.getSelection();
1151}
1152
1153void moveSelection(int x, int y)
1154{
1155    m_selectText.moveSelection(getVisibleRect(), x, y);
1156}
1157
1158IntPoint selectableText()
1159{
1160    const CachedRoot* root = getFrameCache(DontAllowNewer);
1161    if (!root)
1162        return IntPoint(0, 0);
1163    return m_selectText.selectableText(root);
1164}
1165
1166void selectAll()
1167{
1168    m_selectText.selectAll();
1169}
1170
1171int selectionX()
1172{
1173    return m_selectText.selectionX();
1174}
1175
1176int selectionY()
1177{
1178    return m_selectText.selectionY();
1179}
1180
1181void resetSelection()
1182{
1183    m_selectText.reset();
1184}
1185
1186bool startSelection(int x, int y)
1187{
1188    const CachedRoot* root = getFrameCache(DontAllowNewer);
1189    if (!root)
1190        return false;
1191    updateSelectionHandles();
1192    return m_selectText.startSelection(root, getVisibleRect(), x, y);
1193}
1194
1195bool wordSelection(int x, int y)
1196{
1197    const CachedRoot* root = getFrameCache(DontAllowNewer);
1198    if (!root)
1199        return false;
1200    updateSelectionHandles();
1201    return m_selectText.wordSelection(root, getVisibleRect(), x, y);
1202}
1203
1204bool extendSelection(int x, int y)
1205{
1206    m_selectText.extendSelection(getVisibleRect(), x, y);
1207    return true;
1208}
1209
1210bool hitSelection(int x, int y)
1211{
1212    updateSelectionHandles();
1213    return m_selectText.hitSelection(x, y);
1214}
1215
1216void setExtendSelection()
1217{
1218    m_selectText.setExtendSelection(true);
1219}
1220
1221void setSelectionPointer(bool set, float scale, int x, int y)
1222{
1223    m_selectText.setDrawPointer(set);
1224    if (!set)
1225        return;
1226    m_selectText.m_inverseScale = scale;
1227    m_selectText.m_selectX = x;
1228    m_selectText.m_selectY = y;
1229}
1230
1231void sendMoveFocus(WebCore::Frame* framePtr, WebCore::Node* nodePtr)
1232{
1233    DBG_NAV_LOGD("framePtr=%p nodePtr=%p", framePtr, nodePtr);
1234    JNIEnv* env = JSC::Bindings::getJNIEnv();
1235    AutoJObject javaObject = m_javaGlue.object(env);
1236    if (!javaObject.get())
1237        return;
1238    env->CallVoidMethod(javaObject.get(), m_javaGlue.m_sendMoveFocus, (jint) framePtr, (jint) nodePtr);
1239    checkException(env);
1240}
1241
1242void sendMoveMouse(WebCore::Frame* framePtr, WebCore::Node* nodePtr, int x, int y)
1243{
1244    DBG_NAV_LOGD("framePtr=%p nodePtr=%p x=%d y=%d", framePtr, nodePtr, x, y);
1245    JNIEnv* env = JSC::Bindings::getJNIEnv();
1246    AutoJObject javaObject = m_javaGlue.object(env);
1247    if (!javaObject.get())
1248        return;
1249    env->CallVoidMethod(javaObject.get(), m_javaGlue.m_sendMoveMouse, reinterpret_cast<jint>(framePtr), reinterpret_cast<jint>(nodePtr), x, y);
1250    checkException(env);
1251}
1252
1253void sendMoveMouseIfLatest(bool clearTextEntry, bool stopPaintingCaret)
1254{
1255    LOG_ASSERT(m_javaGlue.m_obj, "A java object was not associated with this native WebView!");
1256    JNIEnv* env = JSC::Bindings::getJNIEnv();
1257    AutoJObject javaObject = m_javaGlue.object(env);
1258    if (!javaObject.get())
1259        return;
1260    env->CallVoidMethod(javaObject.get(), m_javaGlue.m_sendMoveMouseIfLatest, clearTextEntry, stopPaintingCaret);
1261    checkException(env);
1262}
1263
1264void sendMotionUp(WebCore::Frame* framePtr, WebCore::Node* nodePtr, int x, int y)
1265{
1266    DBG_NAV_LOGD("m_generation=%d framePtr=%p nodePtr=%p x=%d y=%d", m_generation, framePtr, nodePtr, x, y);
1267    LOG_ASSERT(m_javaGlue.m_obj, "A WebView was not associated with this WebViewNative!");
1268
1269    JNIEnv* env = JSC::Bindings::getJNIEnv();
1270    AutoJObject javaObject = m_javaGlue.object(env);
1271    if (!javaObject.get())
1272        return;
1273    m_viewImpl->m_touchGeneration = ++m_generation;
1274    env->CallVoidMethod(javaObject.get(), m_javaGlue.m_sendMotionUp, m_generation, (jint) framePtr, (jint) nodePtr, x, y);
1275    checkException(env);
1276}
1277
1278void findNext(bool forward)
1279{
1280    m_findOnPage.findNext(forward);
1281    scrollToCurrentMatch();
1282    viewInvalidate();
1283}
1284
1285// With this call, WebView takes ownership of matches, and is responsible for
1286// deleting it.
1287void setMatches(WTF::Vector<MatchInfo>* matches, jboolean sameAsLastSearch)
1288{
1289    // If this search is the same as the last one, check against the old
1290    // location to determine whether to scroll.  If the same word is found
1291    // in the same place, then do not scroll.
1292    IntRect oldLocation;
1293    bool checkAgainstOldLocation = false;
1294    if (sameAsLastSearch && m_findOnPage.isCurrentLocationValid()) {
1295        oldLocation = m_findOnPage.currentMatchBounds();
1296        checkAgainstOldLocation = true;
1297    }
1298
1299    m_findOnPage.setMatches(matches);
1300
1301    if (!checkAgainstOldLocation || oldLocation != m_findOnPage.currentMatchBounds())
1302        scrollToCurrentMatch();
1303    viewInvalidate();
1304}
1305
1306int currentMatchIndex()
1307{
1308    return m_findOnPage.currentMatchIndex();
1309}
1310
1311bool scrollBy(int dx, int dy)
1312{
1313    LOG_ASSERT(m_javaGlue.m_obj, "A java object was not associated with this native WebView!");
1314
1315    JNIEnv* env = JSC::Bindings::getJNIEnv();
1316    AutoJObject javaObject = m_javaGlue.object(env);
1317    if (!javaObject.get())
1318        return false;
1319    bool result = env->CallBooleanMethod(javaObject.get(), m_javaGlue.m_scrollBy, dx, dy, true);
1320    checkException(env);
1321    return result;
1322}
1323
1324void setIsScrolling(bool isScrolling)
1325{
1326#if USE(ACCELERATED_COMPOSITING)
1327    if (m_glWebViewState)
1328        m_glWebViewState->setIsScrolling(isScrolling);
1329#endif
1330}
1331
1332bool hasCursorNode()
1333{
1334    CachedRoot* root = getFrameCache(DontAllowNewer);
1335    if (!root) {
1336        DBG_NAV_LOG("!root");
1337        return false;
1338    }
1339    const CachedNode* cursorNode = root->currentCursor();
1340    DBG_NAV_LOGD("cursorNode=%d (nodePointer=%p)",
1341        cursorNode ? cursorNode->index() : -1,
1342        cursorNode ? cursorNode->nodePointer() : 0);
1343    return cursorNode;
1344}
1345
1346bool hasFocusNode()
1347{
1348    CachedRoot* root = getFrameCache(DontAllowNewer);
1349    if (!root) {
1350        DBG_NAV_LOG("!root");
1351        return false;
1352    }
1353    const CachedNode* focusNode = root->currentFocus();
1354    DBG_NAV_LOGD("focusNode=%d (nodePointer=%p)",
1355        focusNode ? focusNode->index() : -1,
1356        focusNode ? focusNode->nodePointer() : 0);
1357    return focusNode;
1358}
1359
1360void rebuildWebTextView()
1361{
1362    JNIEnv* env = JSC::Bindings::getJNIEnv();
1363    AutoJObject javaObject = m_javaGlue.object(env);
1364    if (!javaObject.get())
1365        return;
1366    env->CallVoidMethod(javaObject.get(), m_javaGlue.m_rebuildWebTextView);
1367    checkException(env);
1368}
1369
1370void viewInvalidate()
1371{
1372    JNIEnv* env = JSC::Bindings::getJNIEnv();
1373    AutoJObject javaObject = m_javaGlue.object(env);
1374    if (!javaObject.get())
1375        return;
1376    env->CallVoidMethod(javaObject.get(), m_javaGlue.m_viewInvalidate);
1377    checkException(env);
1378}
1379
1380void viewInvalidateRect(int l, int t, int r, int b)
1381{
1382    JNIEnv* env = JSC::Bindings::getJNIEnv();
1383    AutoJObject javaObject = m_javaGlue.object(env);
1384    if (!javaObject.get())
1385        return;
1386    env->CallVoidMethod(javaObject.get(), m_javaGlue.m_viewInvalidateRect, l, r, t, b);
1387    checkException(env);
1388}
1389
1390void postInvalidateDelayed(int64_t delay, const WebCore::IntRect& bounds)
1391{
1392    JNIEnv* env = JSC::Bindings::getJNIEnv();
1393    AutoJObject javaObject = m_javaGlue.object(env);
1394    if (!javaObject.get())
1395        return;
1396    env->CallVoidMethod(javaObject.get(), m_javaGlue.m_postInvalidateDelayed,
1397        delay, bounds.x(), bounds.y(), bounds.maxX(), bounds.maxY());
1398    checkException(env);
1399}
1400
1401int moveGeneration()
1402{
1403    return m_viewImpl->m_moveGeneration;
1404}
1405
1406LayerAndroid* compositeRoot() const
1407{
1408    LOG_ASSERT(!m_baseLayer || m_baseLayer->countChildren() == 1,
1409            "base layer can't have more than one child %s", __FUNCTION__);
1410    if (m_baseLayer && m_baseLayer->countChildren() == 1)
1411        return static_cast<LayerAndroid*>(m_baseLayer->getChild(0));
1412    else
1413        return 0;
1414}
1415
1416#if ENABLE(ANDROID_OVERFLOW_SCROLL)
1417static void copyScrollPositionRecursive(const LayerAndroid* from,
1418                                        LayerAndroid* root)
1419{
1420    if (!from || !root)
1421        return;
1422    for (int i = 0; i < from->countChildren(); i++) {
1423        const LayerAndroid* l = from->getChild(i);
1424        if (l->contentIsScrollable()) {
1425            const SkPoint& pos = l->getPosition();
1426            LayerAndroid* match = root->findById(l->uniqueId());
1427            if (match && match->contentIsScrollable())
1428                match->setPosition(pos.fX, pos.fY);
1429        }
1430        copyScrollPositionRecursive(l, root);
1431    }
1432}
1433#endif
1434
1435void registerPageSwapCallback()
1436{
1437    m_pageSwapCallbackRegistered = true;
1438}
1439
1440void setBaseLayer(BaseLayerAndroid* layer, SkRegion& inval, bool showVisualIndicator,
1441                  bool isPictureAfterFirstLayout, bool registerPageSwapCallback)
1442{
1443#if USE(ACCELERATED_COMPOSITING)
1444    if (m_glWebViewState)
1445        m_glWebViewState->setBaseLayer(layer, inval, showVisualIndicator,
1446                                       isPictureAfterFirstLayout);
1447    m_pageSwapCallbackRegistered |= registerPageSwapCallback;
1448#endif
1449
1450#if ENABLE(ANDROID_OVERFLOW_SCROLL)
1451    if (layer) {
1452        // TODO: the below tree copies are only necessary in software rendering
1453        LayerAndroid* newCompositeRoot = static_cast<LayerAndroid*>(layer->getChild(0));
1454        copyScrollPositionRecursive(compositeRoot(), newCompositeRoot);
1455    }
1456#endif
1457    SkSafeUnref(m_baseLayer);
1458    m_baseLayer = layer;
1459    CachedRoot* root = getFrameCache(DontAllowNewer);
1460    if (!root)
1461        return;
1462    root->resetLayers();
1463    root->setRootLayer(compositeRoot());
1464}
1465
1466void getTextSelectionRegion(SkRegion *region)
1467{
1468    m_selectText.getSelectionRegion(getVisibleRect(), region, compositeRoot());
1469}
1470
1471void getTextSelectionHandles(int* handles)
1472{
1473    m_selectText.getSelectionHandles(handles, compositeRoot());
1474}
1475
1476void replaceBaseContent(PictureSet* set)
1477{
1478    if (!m_baseLayer)
1479        return;
1480    m_baseLayer->setContent(*set);
1481    delete set;
1482}
1483
1484void copyBaseContentToPicture(SkPicture* picture)
1485{
1486    if (!m_baseLayer)
1487        return;
1488    PictureSet* content = m_baseLayer->content();
1489    m_baseLayer->drawCanvas(picture->beginRecording(content->width(), content->height(),
1490            SkPicture::kUsePathBoundsForClip_RecordingFlag));
1491    picture->endRecording();
1492}
1493
1494bool hasContent() {
1495    if (!m_baseLayer)
1496        return false;
1497    return !m_baseLayer->content()->isEmpty();
1498}
1499
1500void setFunctor(Functor* functor) {
1501    delete m_glDrawFunctor;
1502    m_glDrawFunctor = functor;
1503}
1504
1505Functor* getFunctor() {
1506    return m_glDrawFunctor;
1507}
1508
1509BaseLayerAndroid* getBaseLayer() {
1510    return m_baseLayer;
1511}
1512
1513void setVisibleRect(SkRect& visibleRect) {
1514    m_visibleRect = visibleRect;
1515}
1516
1517    bool m_isDrawingPaused;
1518private: // local state for WebView
1519    // private to getFrameCache(); other functions operate in a different thread
1520    CachedRoot* m_frameCacheUI; // navigation data ready for use
1521    WebViewCore* m_viewImpl;
1522    int m_generation; // associate unique ID with sent kit focus to match with ui
1523    SkPicture* m_navPictureUI;
1524    SkMSec m_ringAnimationEnd;
1525    // Corresponds to the same-named boolean on the java side.
1526    bool m_heightCanMeasure;
1527    int m_lastDx;
1528    SkMSec m_lastDxTime;
1529    SelectText m_selectText;
1530    FindOnPage m_findOnPage;
1531    CursorRing m_ring;
1532    BaseLayerAndroid* m_baseLayer;
1533    Functor* m_glDrawFunctor;
1534#if USE(ACCELERATED_COMPOSITING)
1535    GLWebViewState* m_glWebViewState;
1536    bool m_pageSwapCallbackRegistered;
1537#endif
1538    RenderSkinButton* m_buttonSkin;
1539    SkRect m_visibleRect;
1540    bool m_isHighEndGfx;
1541}; // end of WebView class
1542
1543
1544/**
1545 * This class holds a function pointer and parameters for calling drawGL into a specific
1546 * viewport. The pointer to the Functor will be put on a framework display list to be called
1547 * when the display list is replayed.
1548 */
1549class GLDrawFunctor : Functor {
1550    public:
1551    GLDrawFunctor(WebView* _wvInstance,
1552            bool(WebView::*_funcPtr)(WebCore::IntRect&, WebCore::IntRect*,
1553                    WebCore::IntRect&, int, WebCore::IntRect&, jfloat, jint),
1554            WebCore::IntRect _viewRect, float _scale, int _extras) {
1555        wvInstance = _wvInstance;
1556        funcPtr = _funcPtr;
1557        viewRect = _viewRect;
1558        scale = _scale;
1559        extras = _extras;
1560    };
1561    status_t operator()(int messageId, void* data) {
1562        if (viewRect.isEmpty()) {
1563            // NOOP operation if viewport is empty
1564            return 0;
1565        }
1566
1567        WebCore::IntRect inval;
1568        int titlebarHeight = webViewRect.height() - viewRect.height();
1569
1570        uirenderer::DrawGlInfo* info = reinterpret_cast<uirenderer::DrawGlInfo*>(data);
1571        WebCore::IntRect localViewRect = viewRect;
1572        if (info->isLayer)
1573            localViewRect.move(-1 * localViewRect.x(), -1 * localViewRect.y());
1574
1575        WebCore::IntRect clip(info->clipLeft, info->clipTop,
1576                              info->clipRight - info->clipLeft,
1577                              info->clipBottom - info->clipTop);
1578        TilesManager::instance()->shader()->setWebViewMatrix(info->transform, info->isLayer);
1579
1580        bool retVal = (*wvInstance.*funcPtr)(localViewRect, &inval, webViewRect,
1581                titlebarHeight, clip, scale, extras);
1582        if (retVal) {
1583            IntRect finalInval;
1584            if (inval.isEmpty()) {
1585                finalInval = webViewRect;
1586                retVal = true;
1587            } else {
1588                finalInval.setX(webViewRect.x() + inval.x());
1589                finalInval.setY(webViewRect.y() + titlebarHeight + inval.y());
1590                finalInval.setWidth(inval.width());
1591                finalInval.setHeight(inval.height());
1592            }
1593            info->dirtyLeft = finalInval.x();
1594            info->dirtyTop = finalInval.y();
1595            info->dirtyRight = finalInval.maxX();
1596            info->dirtyBottom = finalInval.maxY();
1597        }
1598        // return 1 if invalidation needed, 0 otherwise
1599        return retVal ? 1 : 0;
1600    }
1601    void updateRect(WebCore::IntRect& _viewRect) {
1602        viewRect = _viewRect;
1603    }
1604    void updateViewRect(WebCore::IntRect& _viewRect) {
1605        webViewRect = _viewRect;
1606    }
1607    private:
1608    WebView* wvInstance;
1609    bool (WebView::*funcPtr)(WebCore::IntRect&, WebCore::IntRect*,
1610            WebCore::IntRect&, int, WebCore::IntRect&, float, int);
1611    WebCore::IntRect viewRect;
1612    WebCore::IntRect webViewRect;
1613    jfloat scale;
1614    jint extras;
1615};
1616
1617static jobject createJavaRect(JNIEnv* env, int x, int y, int right, int bottom)
1618{
1619    jclass rectClass = env->FindClass("android/graphics/Rect");
1620    jmethodID init = env->GetMethodID(rectClass, "<init>", "(IIII)V");
1621    jobject rect = env->NewObject(rectClass, init, x, y, right, bottom);
1622    env->DeleteLocalRef(rectClass);
1623    return rect;
1624}
1625
1626/*
1627 * Native JNI methods
1628 */
1629static int nativeCacheHitFramePointer(JNIEnv *env, jobject obj)
1630{
1631    return reinterpret_cast<int>(GET_NATIVE_VIEW(env, obj)
1632            ->m_cacheHitFrame->framePointer());
1633}
1634
1635static jobject nativeCacheHitNodeBounds(JNIEnv *env, jobject obj)
1636{
1637    WebCore::IntRect bounds = GET_NATIVE_VIEW(env, obj)
1638        ->m_cacheHitNode->originalAbsoluteBounds();
1639    return createJavaRect(env, bounds.x(), bounds.y(),
1640                          bounds.maxX(), bounds.maxY());
1641}
1642
1643static int nativeCacheHitNodePointer(JNIEnv *env, jobject obj)
1644{
1645    return reinterpret_cast<int>(GET_NATIVE_VIEW(env, obj)
1646        ->m_cacheHitNode->nodePointer());
1647}
1648
1649static bool nativeCacheHitIsPlugin(JNIEnv *env, jobject obj)
1650{
1651    return GET_NATIVE_VIEW(env, obj)->m_cacheHitNode->isPlugin();
1652}
1653
1654static void nativeClearCursor(JNIEnv *env, jobject obj)
1655{
1656    WebView* view = GET_NATIVE_VIEW(env, obj);
1657    LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
1658    view->clearCursor();
1659}
1660
1661static void nativeCreate(JNIEnv *env, jobject obj, int viewImpl,
1662                         jstring drawableDir, jboolean isHighEndGfx)
1663{
1664    WTF::String dir = jstringToWtfString(env, drawableDir);
1665    WebView* webview = new WebView(env, obj, viewImpl, dir, isHighEndGfx);
1666    // NEED THIS OR SOMETHING LIKE IT!
1667    //Release(obj);
1668}
1669
1670static jint nativeCursorFramePointer(JNIEnv *env, jobject obj)
1671{
1672    WebView* view = GET_NATIVE_VIEW(env, obj);
1673    CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
1674    if (!root)
1675        return 0;
1676    const CachedFrame* frame = 0;
1677    (void) root->currentCursor(&frame);
1678    return reinterpret_cast<int>(frame ? frame->framePointer() : 0);
1679}
1680
1681static const CachedNode* getCursorNode(JNIEnv *env, jobject obj)
1682{
1683    WebView* view = GET_NATIVE_VIEW(env, obj);
1684    CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
1685    return root ? root->currentCursor() : 0;
1686}
1687
1688static const CachedNode* getCursorNode(JNIEnv *env, jobject obj,
1689    const CachedFrame** frame)
1690{
1691    WebView* view = GET_NATIVE_VIEW(env, obj);
1692    CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
1693    return root ? root->currentCursor(frame) : 0;
1694}
1695
1696static const CachedNode* getFocusCandidate(JNIEnv *env, jobject obj,
1697    const CachedFrame** frame)
1698{
1699    WebView* view = GET_NATIVE_VIEW(env, obj);
1700    CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
1701    if (!root)
1702        return 0;
1703    const CachedNode* cursor = root->currentCursor(frame);
1704    if (cursor && cursor->wantsKeyEvents())
1705        return cursor;
1706    return root->currentFocus(frame);
1707}
1708
1709static bool focusCandidateHasNextTextfield(JNIEnv *env, jobject obj)
1710{
1711    WebView* view = GET_NATIVE_VIEW(env, obj);
1712    CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
1713    if (!root)
1714        return false;
1715    const CachedNode* cursor = root->currentCursor();
1716    if (!cursor || !cursor->isTextInput())
1717        cursor = root->currentFocus();
1718    if (!cursor || !cursor->isTextInput()) return false;
1719    return root->nextTextField(cursor, 0);
1720}
1721
1722static const CachedNode* getFocusNode(JNIEnv *env, jobject obj)
1723{
1724    WebView* view = GET_NATIVE_VIEW(env, obj);
1725    CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
1726    return root ? root->currentFocus() : 0;
1727}
1728
1729static const CachedNode* getFocusNode(JNIEnv *env, jobject obj,
1730    const CachedFrame** frame)
1731{
1732    WebView* view = GET_NATIVE_VIEW(env, obj);
1733    CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
1734    return root ? root->currentFocus(frame) : 0;
1735}
1736
1737static const CachedInput* getInputCandidate(JNIEnv *env, jobject obj)
1738{
1739    WebView* view = GET_NATIVE_VIEW(env, obj);
1740    CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
1741    if (!root)
1742        return 0;
1743    const CachedFrame* frame;
1744    const CachedNode* cursor = root->currentCursor(&frame);
1745    if (!cursor || !cursor->wantsKeyEvents())
1746        cursor = root->currentFocus(&frame);
1747    return cursor ? frame->textInput(cursor) : 0;
1748}
1749
1750static jboolean nativePageShouldHandleShiftAndArrows(JNIEnv *env, jobject obj)
1751{
1752    const CachedNode* focus = getFocusNode(env, obj);
1753    if (!focus) return false;
1754    // Plugins handle shift and arrows whether or not they have focus.
1755    if (focus->isPlugin()) return true;
1756    const CachedNode* cursor = getCursorNode(env, obj);
1757    // ContentEditable nodes should only receive shift and arrows if they have
1758    // both the cursor and the focus.
1759    return cursor && cursor->nodePointer() == focus->nodePointer()
1760            && cursor->isContentEditable();
1761}
1762
1763static jobject nativeCursorNodeBounds(JNIEnv *env, jobject obj)
1764{
1765    const CachedFrame* frame;
1766    const CachedNode* node = getCursorNode(env, obj, &frame);
1767    WebCore::IntRect bounds = node ? node->bounds(frame)
1768        : WebCore::IntRect(0, 0, 0, 0);
1769    return createJavaRect(env, bounds.x(), bounds.y(),
1770                          bounds.maxX(), bounds.maxY());
1771}
1772
1773static jint nativeCursorNodePointer(JNIEnv *env, jobject obj)
1774{
1775    const CachedNode* node = getCursorNode(env, obj);
1776    return reinterpret_cast<int>(node ? node->nodePointer() : 0);
1777}
1778
1779static jobject nativeCursorPosition(JNIEnv *env, jobject obj)
1780{
1781    WebView* view = GET_NATIVE_VIEW(env, obj);
1782    const CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
1783    WebCore::IntPoint pos = WebCore::IntPoint(0, 0);
1784    if (root)
1785        root->getSimulatedMousePosition(&pos);
1786    jclass pointClass = env->FindClass("android/graphics/Point");
1787    jmethodID init = env->GetMethodID(pointClass, "<init>", "(II)V");
1788    jobject point = env->NewObject(pointClass, init, pos.x(), pos.y());
1789    env->DeleteLocalRef(pointClass);
1790    return point;
1791}
1792
1793static WebCore::IntRect jrect_to_webrect(JNIEnv* env, jobject obj)
1794{
1795    if (obj) {
1796        int L, T, R, B;
1797        GraphicsJNI::get_jrect(env, obj, &L, &T, &R, &B);
1798        return WebCore::IntRect(L, T, R - L, B - T);
1799    } else
1800        return WebCore::IntRect();
1801}
1802
1803static SkRect jrectf_to_rect(JNIEnv* env, jobject obj)
1804{
1805    SkRect rect = SkRect::MakeEmpty();
1806    if (obj)
1807        GraphicsJNI::jrectf_to_rect(env, obj, &rect);
1808    return rect;
1809}
1810
1811static bool nativeCursorIntersects(JNIEnv *env, jobject obj, jobject visRect)
1812{
1813    const CachedFrame* frame;
1814    const CachedNode* node = getCursorNode(env, obj, &frame);
1815    return node ? node->bounds(frame).intersects(
1816        jrect_to_webrect(env, visRect)) : false;
1817}
1818
1819static bool nativeCursorIsAnchor(JNIEnv *env, jobject obj)
1820{
1821    const CachedNode* node = getCursorNode(env, obj);
1822    return node ? node->isAnchor() : false;
1823}
1824
1825static bool nativeCursorIsTextInput(JNIEnv *env, jobject obj)
1826{
1827    const CachedNode* node = getCursorNode(env, obj);
1828    return node ? node->isTextInput() : false;
1829}
1830
1831static jobject nativeCursorText(JNIEnv *env, jobject obj)
1832{
1833    const CachedNode* node = getCursorNode(env, obj);
1834    if (!node)
1835        return 0;
1836    WTF::String value = node->getExport();
1837    return wtfStringToJstring(env, value);
1838}
1839
1840static void nativeDebugDump(JNIEnv *env, jobject obj)
1841{
1842#if DUMP_NAV_CACHE
1843    WebView* view = GET_NATIVE_VIEW(env, obj);
1844    LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
1845    view->debugDump();
1846#endif
1847}
1848
1849static jint nativeDraw(JNIEnv *env, jobject obj, jobject canv,
1850        jobject visible, jint color,
1851        jint extras, jboolean split) {
1852    SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, canv);
1853    WebView* webView = GET_NATIVE_VIEW(env, obj);
1854    SkRect visibleRect = jrectf_to_rect(env, visible);
1855    webView->setVisibleRect(visibleRect);
1856    PictureSet* pictureSet = webView->draw(canvas, color, extras, split);
1857    return reinterpret_cast<jint>(pictureSet);
1858}
1859
1860static jint nativeGetDrawGLFunction(JNIEnv *env, jobject obj, jint nativeView,
1861                                    jobject jrect, jobject jviewrect,
1862                                    jobject jvisiblerect,
1863                                    jfloat scale, jint extras) {
1864    WebCore::IntRect viewRect = jrect_to_webrect(env, jrect);
1865    WebView *wvInstance = (WebView*) nativeView;
1866    SkRect visibleRect = jrectf_to_rect(env, jvisiblerect);
1867    wvInstance->setVisibleRect(visibleRect);
1868
1869    GLDrawFunctor* functor = new GLDrawFunctor(wvInstance,
1870            &android::WebView::drawGL, viewRect, scale, extras);
1871    wvInstance->setFunctor((Functor*) functor);
1872
1873    WebCore::IntRect webViewRect = jrect_to_webrect(env, jviewrect);
1874    functor->updateViewRect(webViewRect);
1875
1876    return (jint)functor;
1877}
1878
1879static void nativeUpdateDrawGLFunction(JNIEnv *env, jobject obj, jobject jrect,
1880        jobject jviewrect, jobject jvisiblerect) {
1881    WebView *wvInstance = GET_NATIVE_VIEW(env, obj);
1882    if (wvInstance) {
1883        GLDrawFunctor* functor = (GLDrawFunctor*) wvInstance->getFunctor();
1884        if (functor) {
1885            WebCore::IntRect viewRect = jrect_to_webrect(env, jrect);
1886            functor->updateRect(viewRect);
1887
1888            SkRect visibleRect = jrectf_to_rect(env, jvisiblerect);
1889            wvInstance->setVisibleRect(visibleRect);
1890
1891            WebCore::IntRect webViewRect = jrect_to_webrect(env, jviewrect);
1892            functor->updateViewRect(webViewRect);
1893        }
1894    }
1895}
1896
1897static bool nativeEvaluateLayersAnimations(JNIEnv *env, jobject obj, jint nativeView)
1898{
1899    // only call in software rendering, initialize and evaluate animations
1900#if USE(ACCELERATED_COMPOSITING)
1901    LayerAndroid* root = ((WebView*)nativeView)->compositeRoot();
1902    if (root) {
1903        root->initAnimations();
1904        return root->evaluateAnimations();
1905    }
1906#endif
1907    return false;
1908}
1909
1910static void nativeSetBaseLayer(JNIEnv *env, jobject obj, jint nativeView, jint layer, jobject inval,
1911                                jboolean showVisualIndicator,
1912                                jboolean isPictureAfterFirstLayout,
1913                                jboolean registerPageSwapCallback)
1914{
1915    BaseLayerAndroid* layerImpl = reinterpret_cast<BaseLayerAndroid*>(layer);
1916    SkRegion invalRegion;
1917    if (inval)
1918        invalRegion = *GraphicsJNI::getNativeRegion(env, inval);
1919    ((WebView*)nativeView)->setBaseLayer(layerImpl, invalRegion, showVisualIndicator,
1920                                            isPictureAfterFirstLayout,
1921                                            registerPageSwapCallback);
1922}
1923
1924static void nativeGetTextSelectionRegion(JNIEnv *env, jobject obj, jint view,
1925                                         jobject region)
1926{
1927    if (!region)
1928        return;
1929    SkRegion* nregion = GraphicsJNI::getNativeRegion(env, region);
1930    ((WebView*)view)->getTextSelectionRegion(nregion);
1931}
1932
1933static void nativeGetSelectionHandles(JNIEnv *env, jobject obj, jint view,
1934                                      jintArray arr)
1935{
1936    int handles[4];
1937    ((WebView*)view)->getTextSelectionHandles(handles);
1938    env->SetIntArrayRegion(arr, 0, 4, handles);
1939    checkException(env);
1940}
1941
1942static BaseLayerAndroid* nativeGetBaseLayer(JNIEnv *env, jobject obj)
1943{
1944    return GET_NATIVE_VIEW(env, obj)->getBaseLayer();
1945}
1946
1947static void nativeReplaceBaseContent(JNIEnv *env, jobject obj, jint content)
1948{
1949    PictureSet* set = reinterpret_cast<PictureSet*>(content);
1950    GET_NATIVE_VIEW(env, obj)->replaceBaseContent(set);
1951}
1952
1953static void nativeCopyBaseContentToPicture(JNIEnv *env, jobject obj, jobject pict)
1954{
1955    SkPicture* picture = GraphicsJNI::getNativePicture(env, pict);
1956    GET_NATIVE_VIEW(env, obj)->copyBaseContentToPicture(picture);
1957}
1958
1959static bool nativeHasContent(JNIEnv *env, jobject obj)
1960{
1961    return GET_NATIVE_VIEW(env, obj)->hasContent();
1962}
1963
1964static jobject nativeImageURI(JNIEnv *env, jobject obj, jint x, jint y)
1965{
1966    WebView* view = GET_NATIVE_VIEW(env, obj);
1967    LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
1968    WTF::String uri = view->imageURI(x, y);
1969    return wtfStringToJstring(env, uri);
1970}
1971
1972static jint nativeFocusCandidateFramePointer(JNIEnv *env, jobject obj)
1973{
1974    WebView* view = GET_NATIVE_VIEW(env, obj);
1975    CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
1976    if (!root)
1977        return 0;
1978    const CachedFrame* frame = 0;
1979    const CachedNode* cursor = root->currentCursor(&frame);
1980    if (!cursor || !cursor->wantsKeyEvents())
1981        (void) root->currentFocus(&frame);
1982    return reinterpret_cast<int>(frame ? frame->framePointer() : 0);
1983}
1984
1985static bool nativeFocusCandidateIsPassword(JNIEnv *env, jobject obj)
1986{
1987    const CachedInput* input = getInputCandidate(env, obj);
1988    return input && input->getType() == CachedInput::PASSWORD;
1989}
1990
1991static bool nativeFocusCandidateIsRtlText(JNIEnv *env, jobject obj)
1992{
1993    const CachedInput* input = getInputCandidate(env, obj);
1994    return input ? input->isRtlText() : false;
1995}
1996
1997static bool nativeFocusCandidateIsTextInput(JNIEnv *env, jobject obj)
1998{
1999    const CachedNode* node = getFocusCandidate(env, obj, 0);
2000    return node ? node->isTextInput() : false;
2001}
2002
2003static jint nativeFocusCandidateMaxLength(JNIEnv *env, jobject obj)
2004{
2005    const CachedInput* input = getInputCandidate(env, obj);
2006    return input ? input->maxLength() : false;
2007}
2008
2009static jint nativeFocusCandidateIsAutoComplete(JNIEnv *env, jobject obj)
2010{
2011    const CachedInput* input = getInputCandidate(env, obj);
2012    return input ? input->autoComplete() : false;
2013}
2014
2015static jobject nativeFocusCandidateName(JNIEnv *env, jobject obj)
2016{
2017    const CachedInput* input = getInputCandidate(env, obj);
2018    if (!input)
2019        return 0;
2020    const WTF::String& name = input->name();
2021    return wtfStringToJstring(env, name);
2022}
2023
2024static jobject nativeFocusCandidateNodeBounds(JNIEnv *env, jobject obj)
2025{
2026    const CachedFrame* frame;
2027    const CachedNode* node = getFocusCandidate(env, obj, &frame);
2028    WebCore::IntRect bounds = node ? node->originalAbsoluteBounds()
2029        : WebCore::IntRect(0, 0, 0, 0);
2030    // Inset the rect by 1 unit, so that the focus candidate's border can still
2031    // be seen behind it.
2032    return createJavaRect(env, bounds.x(), bounds.y(),
2033                          bounds.maxX(), bounds.maxY());
2034}
2035
2036static jobject nativeFocusCandidatePaddingRect(JNIEnv *env, jobject obj)
2037{
2038    const CachedInput* input = getInputCandidate(env, obj);
2039    if (!input)
2040        return 0;
2041    // Note that the Java Rect is being used to pass four integers, rather than
2042    // being used as an actual rectangle.
2043    return createJavaRect(env, input->paddingLeft(), input->paddingTop(),
2044            input->paddingRight(), input->paddingBottom());
2045}
2046
2047static jint nativeFocusCandidatePointer(JNIEnv *env, jobject obj)
2048{
2049    const CachedNode* node = getFocusCandidate(env, obj, 0);
2050    return reinterpret_cast<int>(node ? node->nodePointer() : 0);
2051}
2052
2053static jint nativeFocusCandidateIsSpellcheck(JNIEnv *env, jobject obj)
2054{
2055    const CachedInput* input = getInputCandidate(env, obj);
2056    return input ? input->spellcheck() : false;
2057}
2058
2059static jobject nativeFocusCandidateText(JNIEnv *env, jobject obj)
2060{
2061    const CachedNode* node = getFocusCandidate(env, obj, 0);
2062    if (!node)
2063        return 0;
2064    WTF::String value = node->getExport();
2065    return wtfStringToJstring(env, value);
2066}
2067
2068static int nativeFocusCandidateLineHeight(JNIEnv *env, jobject obj)
2069{
2070    const CachedInput* input = getInputCandidate(env, obj);
2071    return input ? input->lineHeight() : 0;
2072}
2073
2074static jfloat nativeFocusCandidateTextSize(JNIEnv *env, jobject obj)
2075{
2076    const CachedInput* input = getInputCandidate(env, obj);
2077    return input ? input->textSize() : 0.f;
2078}
2079
2080static int nativeFocusCandidateType(JNIEnv *env, jobject obj)
2081{
2082    const CachedInput* input = getInputCandidate(env, obj);
2083    if (!input)
2084        return CachedInput::NONE;
2085
2086    if (input->isTextArea())
2087        return CachedInput::TEXT_AREA;
2088
2089    return input->getType();
2090}
2091
2092static int nativeFocusCandidateLayerId(JNIEnv *env, jobject obj)
2093{
2094    const CachedFrame* frame = 0;
2095    const CachedNode* node = getFocusNode(env, obj, &frame);
2096    if (!node || !frame)
2097        return -1;
2098    const CachedLayer* layer = frame->layer(node);
2099    if (!layer)
2100        return -1;
2101    return layer->uniqueId();
2102}
2103
2104static bool nativeFocusIsPlugin(JNIEnv *env, jobject obj)
2105{
2106    const CachedNode* node = getFocusNode(env, obj);
2107    return node ? node->isPlugin() : false;
2108}
2109
2110static jobject nativeFocusNodeBounds(JNIEnv *env, jobject obj)
2111{
2112    const CachedFrame* frame;
2113    const CachedNode* node = getFocusNode(env, obj, &frame);
2114    WebCore::IntRect bounds = node ? node->bounds(frame)
2115        : WebCore::IntRect(0, 0, 0, 0);
2116    return createJavaRect(env, bounds.x(), bounds.y(),
2117                          bounds.maxX(), bounds.maxY());
2118}
2119
2120static jint nativeFocusNodePointer(JNIEnv *env, jobject obj)
2121{
2122    const CachedNode* node = getFocusNode(env, obj);
2123    return node ? reinterpret_cast<int>(node->nodePointer()) : 0;
2124}
2125
2126static bool nativeCursorWantsKeyEvents(JNIEnv* env, jobject jwebview) {
2127    WebView* view = GET_NATIVE_VIEW(env, jwebview);
2128    LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
2129    return view->cursorWantsKeyEvents();
2130}
2131
2132static void nativeHideCursor(JNIEnv *env, jobject obj)
2133{
2134    WebView* view = GET_NATIVE_VIEW(env, obj);
2135    LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
2136    view->hideCursor();
2137}
2138
2139static void nativeSelectBestAt(JNIEnv *env, jobject obj, jobject jrect)
2140{
2141    WebView* view = GET_NATIVE_VIEW(env, obj);
2142    LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
2143    WebCore::IntRect rect = jrect_to_webrect(env, jrect);
2144    view->selectBestAt(rect);
2145}
2146
2147static void nativeSelectAt(JNIEnv *env, jobject obj, jint x, jint y)
2148{
2149    WebView* view = GET_NATIVE_VIEW(env, obj);
2150    LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
2151    WebCore::IntRect rect = IntRect(x, y , 1, 1);
2152    view->selectBestAt(rect);
2153    if (view->hasCursorNode())
2154        view->showCursorUntimed();
2155}
2156
2157static jobject nativeLayerBounds(JNIEnv* env, jobject obj, jint jlayer)
2158{
2159    SkRect r;
2160#if USE(ACCELERATED_COMPOSITING)
2161    LayerAndroid* layer = (LayerAndroid*) jlayer;
2162    r = layer->bounds();
2163#else
2164    r.setEmpty();
2165#endif
2166    SkIRect irect;
2167    r.round(&irect);
2168    jclass rectClass = env->FindClass("android/graphics/Rect");
2169    jmethodID init = env->GetMethodID(rectClass, "<init>", "(IIII)V");
2170    jobject rect = env->NewObject(rectClass, init, irect.fLeft, irect.fTop,
2171        irect.fRight, irect.fBottom);
2172    env->DeleteLocalRef(rectClass);
2173    return rect;
2174}
2175
2176static jobject nativeSubtractLayers(JNIEnv* env, jobject obj, jobject jrect)
2177{
2178    SkIRect irect = jrect_to_webrect(env, jrect);
2179#if USE(ACCELERATED_COMPOSITING)
2180    LayerAndroid* root = GET_NATIVE_VIEW(env, obj)->compositeRoot();
2181    if (root) {
2182        SkRect rect;
2183        rect.set(irect);
2184        rect = root->subtractLayers(rect);
2185        rect.round(&irect);
2186    }
2187#endif
2188    jclass rectClass = env->FindClass("android/graphics/Rect");
2189    jmethodID init = env->GetMethodID(rectClass, "<init>", "(IIII)V");
2190    jobject rect = env->NewObject(rectClass, init, irect.fLeft, irect.fTop,
2191        irect.fRight, irect.fBottom);
2192    env->DeleteLocalRef(rectClass);
2193    return rect;
2194}
2195
2196static jint nativeTextGeneration(JNIEnv *env, jobject obj)
2197{
2198    WebView* view = GET_NATIVE_VIEW(env, obj);
2199    CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
2200    return root ? root->textGeneration() : 0;
2201}
2202
2203static bool nativePointInNavCache(JNIEnv *env, jobject obj,
2204    int x, int y, int slop)
2205{
2206    return GET_NATIVE_VIEW(env, obj)->pointInNavCache(x, y, slop);
2207}
2208
2209static bool nativeMotionUp(JNIEnv *env, jobject obj,
2210    int x, int y, int slop)
2211{
2212    WebView* view = GET_NATIVE_VIEW(env, obj);
2213    LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
2214    return view->motionUp(x, y, slop);
2215}
2216
2217static bool nativeHasCursorNode(JNIEnv *env, jobject obj)
2218{
2219    return GET_NATIVE_VIEW(env, obj)->hasCursorNode();
2220}
2221
2222static bool nativeHasFocusNode(JNIEnv *env, jobject obj)
2223{
2224    return GET_NATIVE_VIEW(env, obj)->hasFocusNode();
2225}
2226
2227static bool nativeMoveCursor(JNIEnv *env, jobject obj,
2228    int key, int count, bool ignoreScroll)
2229{
2230    WebView* view = GET_NATIVE_VIEW(env, obj);
2231    DBG_NAV_LOGD("env=%p obj=%p view=%p", env, obj, view);
2232    LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
2233    return view->moveCursor(key, count, ignoreScroll);
2234}
2235
2236static void nativeSetFindIsUp(JNIEnv *env, jobject obj, jboolean isUp)
2237{
2238    WebView* view = GET_NATIVE_VIEW(env, obj);
2239    LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
2240    view->setFindIsUp(isUp);
2241}
2242
2243static void nativeSetFindIsEmpty(JNIEnv *env, jobject obj)
2244{
2245    GET_NATIVE_VIEW(env, obj)->setFindIsEmpty();
2246}
2247
2248static void nativeShowCursorTimed(JNIEnv *env, jobject obj)
2249{
2250    GET_NATIVE_VIEW(env, obj)->showCursorTimed();
2251}
2252
2253static void nativeSetHeightCanMeasure(JNIEnv *env, jobject obj, bool measure)
2254{
2255    WebView* view = GET_NATIVE_VIEW(env, obj);
2256    LOG_ASSERT(view, "view not set in nativeSetHeightCanMeasure");
2257    view->setHeightCanMeasure(measure);
2258}
2259
2260static jobject nativeGetCursorRingBounds(JNIEnv *env, jobject obj)
2261{
2262    WebView* view = GET_NATIVE_VIEW(env, obj);
2263    LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
2264    jclass rectClass = env->FindClass("android/graphics/Rect");
2265    LOG_ASSERT(rectClass, "Could not find Rect class!");
2266    jmethodID init = env->GetMethodID(rectClass, "<init>", "(IIII)V");
2267    LOG_ASSERT(init, "Could not find constructor for Rect");
2268    WebCore::IntRect webRect;
2269    view->cursorRingBounds(&webRect);
2270    jobject rect = env->NewObject(rectClass, init, webRect.x(),
2271        webRect.y(), webRect.maxX(), webRect.maxY());
2272    env->DeleteLocalRef(rectClass);
2273    return rect;
2274}
2275
2276static int nativeFindAll(JNIEnv *env, jobject obj, jstring findLower,
2277        jstring findUpper, jboolean sameAsLastSearch)
2278{
2279    // If one or the other is null, do not search.
2280    if (!(findLower && findUpper))
2281        return 0;
2282    // Obtain the characters for both the lower case string and the upper case
2283    // string representing the same word.
2284    const jchar* findLowerChars = env->GetStringChars(findLower, 0);
2285    const jchar* findUpperChars = env->GetStringChars(findUpper, 0);
2286    // If one or the other is null, do not search.
2287    if (!(findLowerChars && findUpperChars)) {
2288        if (findLowerChars)
2289            env->ReleaseStringChars(findLower, findLowerChars);
2290        if (findUpperChars)
2291            env->ReleaseStringChars(findUpper, findUpperChars);
2292        checkException(env);
2293        return 0;
2294    }
2295    WebView* view = GET_NATIVE_VIEW(env, obj);
2296    LOG_ASSERT(view, "view not set in nativeFindAll");
2297    CachedRoot* root = view->getFrameCache(WebView::AllowNewer);
2298    if (!root) {
2299        env->ReleaseStringChars(findLower, findLowerChars);
2300        env->ReleaseStringChars(findUpper, findUpperChars);
2301        checkException(env);
2302        return 0;
2303    }
2304    int length = env->GetStringLength(findLower);
2305    // If the lengths of the strings do not match, then they are not the same
2306    // word, so do not search.
2307    if (!length || env->GetStringLength(findUpper) != length) {
2308        env->ReleaseStringChars(findLower, findLowerChars);
2309        env->ReleaseStringChars(findUpper, findUpperChars);
2310        checkException(env);
2311        return 0;
2312    }
2313    int width = root->documentWidth();
2314    int height = root->documentHeight();
2315    // Create a FindCanvas, which allows us to fake draw into it so we can
2316    // figure out where our search string is rendered (and how many times).
2317    FindCanvas canvas(width, height, (const UChar*) findLowerChars,
2318            (const UChar*) findUpperChars, length << 1);
2319    SkBitmap bitmap;
2320    bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
2321    canvas.setBitmapDevice(bitmap);
2322    root->draw(canvas);
2323    WTF::Vector<MatchInfo>* matches = canvas.detachMatches();
2324    // With setMatches, the WebView takes ownership of matches
2325    view->setMatches(matches, sameAsLastSearch);
2326
2327    env->ReleaseStringChars(findLower, findLowerChars);
2328    env->ReleaseStringChars(findUpper, findUpperChars);
2329    checkException(env);
2330    return canvas.found();
2331}
2332
2333static void nativeFindNext(JNIEnv *env, jobject obj, bool forward)
2334{
2335    WebView* view = GET_NATIVE_VIEW(env, obj);
2336    LOG_ASSERT(view, "view not set in nativeFindNext");
2337    view->findNext(forward);
2338}
2339
2340static int nativeFindIndex(JNIEnv *env, jobject obj)
2341{
2342    WebView* view = GET_NATIVE_VIEW(env, obj);
2343    LOG_ASSERT(view, "view not set in nativeFindIndex");
2344    return view->currentMatchIndex();
2345}
2346
2347static void nativeUpdateCachedTextfield(JNIEnv *env, jobject obj, jstring updatedText, jint generation)
2348{
2349    WebView* view = GET_NATIVE_VIEW(env, obj);
2350    LOG_ASSERT(view, "view not set in nativeUpdateCachedTextfield");
2351    CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
2352    if (!root)
2353        return;
2354    const CachedNode* cachedFocusNode = root->currentFocus();
2355    if (!cachedFocusNode || !cachedFocusNode->isTextInput())
2356        return;
2357    WTF::String webcoreString = jstringToWtfString(env, updatedText);
2358    (const_cast<CachedNode*>(cachedFocusNode))->setExport(webcoreString);
2359    root->setTextGeneration(generation);
2360    checkException(env);
2361}
2362
2363static jint nativeGetBlockLeftEdge(JNIEnv *env, jobject obj, jint x, jint y,
2364        jfloat scale)
2365{
2366    WebView* view = GET_NATIVE_VIEW(env, obj);
2367    LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
2368    if (!view)
2369        return -1;
2370    return view->getBlockLeftEdge(x, y, scale);
2371}
2372
2373static void nativeDestroy(JNIEnv *env, jobject obj)
2374{
2375    WebView* view = GET_NATIVE_VIEW(env, obj);
2376    ALOGD("nativeDestroy view: %p", view);
2377    LOG_ASSERT(view, "view not set in nativeDestroy");
2378    delete view;
2379}
2380
2381static void nativeStopGL(JNIEnv *env, jobject obj)
2382{
2383    GET_NATIVE_VIEW(env, obj)->stopGL();
2384}
2385
2386static bool nativeMoveCursorToNextTextInput(JNIEnv *env, jobject obj)
2387{
2388    WebView* view = GET_NATIVE_VIEW(env, obj);
2389    CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
2390    if (!root)
2391        return false;
2392    const CachedNode* current = root->currentCursor();
2393    if (!current || !current->isTextInput())
2394        current = root->currentFocus();
2395    if (!current || !current->isTextInput())
2396        return false;
2397    const CachedFrame* frame;
2398    const CachedNode* next = root->nextTextField(current, &frame);
2399    if (!next)
2400        return false;
2401    const WebCore::IntRect& bounds = next->bounds(frame);
2402    root->rootHistory()->setMouseBounds(bounds);
2403    view->getWebViewCore()->updateCursorBounds(root, frame, next);
2404    view->showCursorUntimed();
2405    root->setCursor(const_cast<CachedFrame*>(frame),
2406            const_cast<CachedNode*>(next));
2407    view->sendMoveFocus(static_cast<WebCore::Frame*>(frame->framePointer()),
2408            static_cast<WebCore::Node*>(next->nodePointer()));
2409    if (!next->isInLayer())
2410        view->scrollRectOnScreen(bounds);
2411    view->getWebViewCore()->m_moveGeneration++;
2412    return true;
2413}
2414
2415static int nativeMoveGeneration(JNIEnv *env, jobject obj)
2416{
2417    WebView* view = GET_NATIVE_VIEW(env, obj);
2418    if (!view)
2419        return 0;
2420    return view->moveGeneration();
2421}
2422
2423static void nativeMoveSelection(JNIEnv *env, jobject obj, int x, int y)
2424{
2425    GET_NATIVE_VIEW(env, obj)->moveSelection(x, y);
2426}
2427
2428static void nativeResetSelection(JNIEnv *env, jobject obj)
2429{
2430    return GET_NATIVE_VIEW(env, obj)->resetSelection();
2431}
2432
2433static jobject nativeSelectableText(JNIEnv* env, jobject obj)
2434{
2435    IntPoint pos = GET_NATIVE_VIEW(env, obj)->selectableText();
2436    jclass pointClass = env->FindClass("android/graphics/Point");
2437    jmethodID init = env->GetMethodID(pointClass, "<init>", "(II)V");
2438    jobject point = env->NewObject(pointClass, init, pos.x(), pos.y());
2439    env->DeleteLocalRef(pointClass);
2440    return point;
2441}
2442
2443static void nativeSelectAll(JNIEnv* env, jobject obj)
2444{
2445    GET_NATIVE_VIEW(env, obj)->selectAll();
2446}
2447
2448static void nativeSetExtendSelection(JNIEnv *env, jobject obj)
2449{
2450    GET_NATIVE_VIEW(env, obj)->setExtendSelection();
2451}
2452
2453static jboolean nativeStartSelection(JNIEnv *env, jobject obj, int x, int y)
2454{
2455    return GET_NATIVE_VIEW(env, obj)->startSelection(x, y);
2456}
2457
2458static jboolean nativeWordSelection(JNIEnv *env, jobject obj, int x, int y)
2459{
2460    return GET_NATIVE_VIEW(env, obj)->wordSelection(x, y);
2461}
2462
2463static void nativeExtendSelection(JNIEnv *env, jobject obj, int x, int y)
2464{
2465    GET_NATIVE_VIEW(env, obj)->extendSelection(x, y);
2466}
2467
2468static jobject nativeGetSelection(JNIEnv *env, jobject obj)
2469{
2470    WebView* view = GET_NATIVE_VIEW(env, obj);
2471    LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
2472    String selection = view->getSelection();
2473    return wtfStringToJstring(env, selection);
2474}
2475
2476static jboolean nativeHitSelection(JNIEnv *env, jobject obj, int x, int y)
2477{
2478    return GET_NATIVE_VIEW(env, obj)->hitSelection(x, y);
2479}
2480
2481static jint nativeSelectionX(JNIEnv *env, jobject obj)
2482{
2483    return GET_NATIVE_VIEW(env, obj)->selectionX();
2484}
2485
2486static jint nativeSelectionY(JNIEnv *env, jobject obj)
2487{
2488    return GET_NATIVE_VIEW(env, obj)->selectionY();
2489}
2490
2491static void nativeSetSelectionPointer(JNIEnv *env, jobject obj, jint nativeView,
2492                                      jboolean set, jfloat scale, jint x, jint y)
2493{
2494    ((WebView*)nativeView)->setSelectionPointer(set, scale, x, y);
2495}
2496
2497static void nativeRegisterPageSwapCallback(JNIEnv *env, jobject obj, jint nativeView)
2498{
2499    ((WebView*)nativeView)->registerPageSwapCallback();
2500}
2501
2502static void nativeDiscardAllTextures(JNIEnv *env, jobject obj)
2503{
2504    //discard all textures for debugging/test purposes, but not gl backing memory
2505    bool allTextures = true, deleteGLTextures = false;
2506    TilesManager::instance()->discardTextures(allTextures, deleteGLTextures);
2507}
2508
2509static void nativeTileProfilingStart(JNIEnv *env, jobject obj)
2510{
2511    TilesManager::instance()->getProfiler()->start();
2512}
2513
2514static float nativeTileProfilingStop(JNIEnv *env, jobject obj)
2515{
2516    return TilesManager::instance()->getProfiler()->stop();
2517}
2518
2519static void nativeTileProfilingClear(JNIEnv *env, jobject obj)
2520{
2521    TilesManager::instance()->getProfiler()->clear();
2522}
2523
2524static int nativeTileProfilingNumFrames(JNIEnv *env, jobject obj)
2525{
2526    return TilesManager::instance()->getProfiler()->numFrames();
2527}
2528
2529static int nativeTileProfilingNumTilesInFrame(JNIEnv *env, jobject obj, int frame)
2530{
2531    return TilesManager::instance()->getProfiler()->numTilesInFrame(frame);
2532}
2533
2534static int nativeTileProfilingGetInt(JNIEnv *env, jobject obj, int frame, int tile, jstring jkey)
2535{
2536    WTF::String key = jstringToWtfString(env, jkey);
2537    TileProfileRecord* record = TilesManager::instance()->getProfiler()->getTile(frame, tile);
2538
2539    if (key == "left")
2540        return record->left;
2541    if (key == "top")
2542        return record->top;
2543    if (key == "right")
2544        return record->right;
2545    if (key == "bottom")
2546        return record->bottom;
2547    if (key == "level")
2548        return record->level;
2549    if (key == "isReady")
2550        return record->isReady ? 1 : 0;
2551    return -1;
2552}
2553
2554static float nativeTileProfilingGetFloat(JNIEnv *env, jobject obj, int frame, int tile, jstring jkey)
2555{
2556    TileProfileRecord* record = TilesManager::instance()->getProfiler()->getTile(frame, tile);
2557    return record->scale;
2558}
2559
2560#ifdef ANDROID_DUMP_DISPLAY_TREE
2561static void dumpToFile(const char text[], void* file) {
2562    fwrite(text, 1, strlen(text), reinterpret_cast<FILE*>(file));
2563    fwrite("\n", 1, 1, reinterpret_cast<FILE*>(file));
2564}
2565#endif
2566
2567static bool nativeSetProperty(JNIEnv *env, jobject obj, jstring jkey, jstring jvalue)
2568{
2569    WTF::String key = jstringToWtfString(env, jkey);
2570    WTF::String value = jstringToWtfString(env, jvalue);
2571    if (key == "inverted") {
2572        if (value == "true")
2573            TilesManager::instance()->setInvertedScreen(true);
2574        else
2575            TilesManager::instance()->setInvertedScreen(false);
2576        return true;
2577    }
2578    else if (key == "inverted_contrast") {
2579        float contrast = value.toFloat();
2580        TilesManager::instance()->setInvertedScreenContrast(contrast);
2581        return true;
2582    }
2583    else if (key == "enable_cpu_upload_path") {
2584        TilesManager::instance()->transferQueue()->setTextureUploadType(
2585            value == "true" ? CpuUpload : GpuUpload);
2586        return true;
2587    }
2588    else if (key == "use_minimal_memory") {
2589        TilesManager::instance()->setUseMinimalMemory(value == "true");
2590        return true;
2591    }
2592    return false;
2593}
2594
2595static jstring nativeGetProperty(JNIEnv *env, jobject obj, jstring key)
2596{
2597    return 0;
2598}
2599
2600static void nativeOnTrimMemory(JNIEnv *env, jobject obj, jint level)
2601{
2602    if (TilesManager::hardwareAccelerationEnabled()) {
2603        bool freeAllTextures = (level > TRIM_MEMORY_UI_HIDDEN), glTextures = true;
2604        TilesManager::instance()->discardTextures(freeAllTextures, glTextures);
2605    }
2606}
2607
2608static void nativeDumpDisplayTree(JNIEnv* env, jobject jwebview, jstring jurl)
2609{
2610#ifdef ANDROID_DUMP_DISPLAY_TREE
2611    WebView* view = GET_NATIVE_VIEW(env, jwebview);
2612    LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
2613
2614    if (view && view->getWebViewCore()) {
2615        FILE* file = fopen(DISPLAY_TREE_LOG_FILE, "w");
2616        if (file) {
2617            SkFormatDumper dumper(dumpToFile, file);
2618            // dump the URL
2619            if (jurl) {
2620                const char* str = env->GetStringUTFChars(jurl, 0);
2621                SkDebugf("Dumping %s to %s\n", str, DISPLAY_TREE_LOG_FILE);
2622                dumpToFile(str, file);
2623                env->ReleaseStringUTFChars(jurl, str);
2624            }
2625            // now dump the display tree
2626            SkDumpCanvas canvas(&dumper);
2627            // this will playback the picture into the canvas, which will
2628            // spew its contents to the dumper
2629            view->draw(&canvas, 0, 0, false);
2630            // we're done with the file now
2631            fwrite("\n", 1, 1, file);
2632            fclose(file);
2633        }
2634#if USE(ACCELERATED_COMPOSITING)
2635        const LayerAndroid* rootLayer = view->compositeRoot();
2636        if (rootLayer) {
2637          FILE* file = fopen(LAYERS_TREE_LOG_FILE,"w");
2638          if (file) {
2639              rootLayer->dumpLayers(file, 0);
2640              fclose(file);
2641          }
2642        }
2643#endif
2644    }
2645#endif
2646}
2647
2648static int nativeScrollableLayer(JNIEnv* env, jobject jwebview, jint x, jint y,
2649    jobject rect, jobject bounds)
2650{
2651    WebView* view = GET_NATIVE_VIEW(env, jwebview);
2652    LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
2653    SkIRect nativeRect, nativeBounds;
2654    int id = view->scrollableLayer(x, y, &nativeRect, &nativeBounds);
2655    if (rect)
2656        GraphicsJNI::irect_to_jrect(nativeRect, env, rect);
2657    if (bounds)
2658        GraphicsJNI::irect_to_jrect(nativeBounds, env, bounds);
2659    return id;
2660}
2661
2662static bool nativeScrollLayer(JNIEnv* env, jobject obj, jint layerId, jint x,
2663        jint y)
2664{
2665#if ENABLE(ANDROID_OVERFLOW_SCROLL)
2666    WebView* view = GET_NATIVE_VIEW(env, obj);
2667    view->scrollLayer(layerId, x, y);
2668
2669    //TODO: the below only needed for the SW rendering path
2670    LayerAndroid* root = view->compositeRoot();
2671    if (!root)
2672        return false;
2673    LayerAndroid* layer = root->findById(layerId);
2674    if (!layer || !layer->contentIsScrollable())
2675        return false;
2676    return static_cast<ScrollableLayerAndroid*>(layer)->scrollTo(x, y);
2677#endif
2678    return false;
2679}
2680
2681static void nativeSetIsScrolling(JNIEnv* env, jobject jwebview, jboolean isScrolling)
2682{
2683    WebView* view = GET_NATIVE_VIEW(env, jwebview);
2684    LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
2685    view->setIsScrolling(isScrolling);
2686}
2687
2688static void nativeUseHardwareAccelSkia(JNIEnv*, jobject, jboolean enabled)
2689{
2690    BaseRenderer::setCurrentRendererType(enabled ? BaseRenderer::Ganesh : BaseRenderer::Raster);
2691}
2692
2693static int nativeGetBackgroundColor(JNIEnv* env, jobject obj)
2694{
2695    WebView* view = GET_NATIVE_VIEW(env, obj);
2696    BaseLayerAndroid* baseLayer = view->getBaseLayer();
2697    if (baseLayer) {
2698        WebCore::Color color = baseLayer->getBackgroundColor();
2699        if (color.isValid())
2700            return SkColorSetARGB(color.alpha(), color.red(),
2701                                  color.green(), color.blue());
2702    }
2703    return SK_ColorWHITE;
2704}
2705
2706static void nativeSetPauseDrawing(JNIEnv *env, jobject obj, jint nativeView,
2707                                      jboolean pause)
2708{
2709    ((WebView*)nativeView)->m_isDrawingPaused = pause;
2710}
2711
2712/*
2713 * JNI registration
2714 */
2715static JNINativeMethod gJavaWebViewMethods[] = {
2716    { "nativeCacheHitFramePointer", "()I",
2717        (void*) nativeCacheHitFramePointer },
2718    { "nativeCacheHitIsPlugin", "()Z",
2719        (void*) nativeCacheHitIsPlugin },
2720    { "nativeCacheHitNodeBounds", "()Landroid/graphics/Rect;",
2721        (void*) nativeCacheHitNodeBounds },
2722    { "nativeCacheHitNodePointer", "()I",
2723        (void*) nativeCacheHitNodePointer },
2724    { "nativeClearCursor", "()V",
2725        (void*) nativeClearCursor },
2726    { "nativeCreate", "(ILjava/lang/String;Z)V",
2727        (void*) nativeCreate },
2728    { "nativeCursorFramePointer", "()I",
2729        (void*) nativeCursorFramePointer },
2730    { "nativePageShouldHandleShiftAndArrows", "()Z",
2731        (void*) nativePageShouldHandleShiftAndArrows },
2732    { "nativeCursorNodeBounds", "()Landroid/graphics/Rect;",
2733        (void*) nativeCursorNodeBounds },
2734    { "nativeCursorNodePointer", "()I",
2735        (void*) nativeCursorNodePointer },
2736    { "nativeCursorIntersects", "(Landroid/graphics/Rect;)Z",
2737        (void*) nativeCursorIntersects },
2738    { "nativeCursorIsAnchor", "()Z",
2739        (void*) nativeCursorIsAnchor },
2740    { "nativeCursorIsTextInput", "()Z",
2741        (void*) nativeCursorIsTextInput },
2742    { "nativeCursorPosition", "()Landroid/graphics/Point;",
2743        (void*) nativeCursorPosition },
2744    { "nativeCursorText", "()Ljava/lang/String;",
2745        (void*) nativeCursorText },
2746    { "nativeCursorWantsKeyEvents", "()Z",
2747        (void*)nativeCursorWantsKeyEvents },
2748    { "nativeDebugDump", "()V",
2749        (void*) nativeDebugDump },
2750    { "nativeDestroy", "()V",
2751        (void*) nativeDestroy },
2752    { "nativeDraw", "(Landroid/graphics/Canvas;Landroid/graphics/RectF;IIZ)I",
2753        (void*) nativeDraw },
2754    { "nativeGetDrawGLFunction", "(ILandroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/RectF;FI)I",
2755        (void*) nativeGetDrawGLFunction },
2756    { "nativeUpdateDrawGLFunction", "(Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/RectF;)V",
2757        (void*) nativeUpdateDrawGLFunction },
2758    { "nativeDumpDisplayTree", "(Ljava/lang/String;)V",
2759        (void*) nativeDumpDisplayTree },
2760    { "nativeEvaluateLayersAnimations", "(I)Z",
2761        (void*) nativeEvaluateLayersAnimations },
2762    { "nativeExtendSelection", "(II)V",
2763        (void*) nativeExtendSelection },
2764    { "nativeFindAll", "(Ljava/lang/String;Ljava/lang/String;Z)I",
2765        (void*) nativeFindAll },
2766    { "nativeFindNext", "(Z)V",
2767        (void*) nativeFindNext },
2768    { "nativeFindIndex", "()I",
2769        (void*) nativeFindIndex},
2770    { "nativeFocusCandidateFramePointer", "()I",
2771        (void*) nativeFocusCandidateFramePointer },
2772    { "nativeFocusCandidateHasNextTextfield", "()Z",
2773        (void*) focusCandidateHasNextTextfield },
2774    { "nativeFocusCandidateIsPassword", "()Z",
2775        (void*) nativeFocusCandidateIsPassword },
2776    { "nativeFocusCandidateIsRtlText", "()Z",
2777        (void*) nativeFocusCandidateIsRtlText },
2778    { "nativeFocusCandidateIsTextInput", "()Z",
2779        (void*) nativeFocusCandidateIsTextInput },
2780    { "nativeFocusCandidateLineHeight", "()I",
2781        (void*) nativeFocusCandidateLineHeight },
2782    { "nativeFocusCandidateMaxLength", "()I",
2783        (void*) nativeFocusCandidateMaxLength },
2784    { "nativeFocusCandidateIsAutoComplete", "()Z",
2785        (void*) nativeFocusCandidateIsAutoComplete },
2786    { "nativeFocusCandidateIsSpellcheck", "()Z",
2787        (void*) nativeFocusCandidateIsSpellcheck },
2788    { "nativeFocusCandidateName", "()Ljava/lang/String;",
2789        (void*) nativeFocusCandidateName },
2790    { "nativeFocusCandidateNodeBounds", "()Landroid/graphics/Rect;",
2791        (void*) nativeFocusCandidateNodeBounds },
2792    { "nativeFocusCandidatePaddingRect", "()Landroid/graphics/Rect;",
2793        (void*) nativeFocusCandidatePaddingRect },
2794    { "nativeFocusCandidatePointer", "()I",
2795        (void*) nativeFocusCandidatePointer },
2796    { "nativeFocusCandidateText", "()Ljava/lang/String;",
2797        (void*) nativeFocusCandidateText },
2798    { "nativeFocusCandidateTextSize", "()F",
2799        (void*) nativeFocusCandidateTextSize },
2800    { "nativeFocusCandidateType", "()I",
2801        (void*) nativeFocusCandidateType },
2802    { "nativeFocusCandidateLayerId", "()I",
2803        (void*) nativeFocusCandidateLayerId },
2804    { "nativeFocusIsPlugin", "()Z",
2805        (void*) nativeFocusIsPlugin },
2806    { "nativeFocusNodeBounds", "()Landroid/graphics/Rect;",
2807        (void*) nativeFocusNodeBounds },
2808    { "nativeFocusNodePointer", "()I",
2809        (void*) nativeFocusNodePointer },
2810    { "nativeGetCursorRingBounds", "()Landroid/graphics/Rect;",
2811        (void*) nativeGetCursorRingBounds },
2812    { "nativeGetSelection", "()Ljava/lang/String;",
2813        (void*) nativeGetSelection },
2814    { "nativeHasCursorNode", "()Z",
2815        (void*) nativeHasCursorNode },
2816    { "nativeHasFocusNode", "()Z",
2817        (void*) nativeHasFocusNode },
2818    { "nativeHideCursor", "()V",
2819        (void*) nativeHideCursor },
2820    { "nativeHitSelection", "(II)Z",
2821        (void*) nativeHitSelection },
2822    { "nativeImageURI", "(II)Ljava/lang/String;",
2823        (void*) nativeImageURI },
2824    { "nativeLayerBounds", "(I)Landroid/graphics/Rect;",
2825        (void*) nativeLayerBounds },
2826    { "nativeMotionUp", "(III)Z",
2827        (void*) nativeMotionUp },
2828    { "nativeMoveCursor", "(IIZ)Z",
2829        (void*) nativeMoveCursor },
2830    { "nativeMoveCursorToNextTextInput", "()Z",
2831        (void*) nativeMoveCursorToNextTextInput },
2832    { "nativeMoveGeneration", "()I",
2833        (void*) nativeMoveGeneration },
2834    { "nativeMoveSelection", "(II)V",
2835        (void*) nativeMoveSelection },
2836    { "nativePointInNavCache", "(III)Z",
2837        (void*) nativePointInNavCache },
2838    { "nativeResetSelection", "()V",
2839        (void*) nativeResetSelection },
2840    { "nativeSelectableText", "()Landroid/graphics/Point;",
2841        (void*) nativeSelectableText },
2842    { "nativeSelectAll", "()V",
2843        (void*) nativeSelectAll },
2844    { "nativeSelectBestAt", "(Landroid/graphics/Rect;)V",
2845        (void*) nativeSelectBestAt },
2846    { "nativeSelectAt", "(II)V",
2847        (void*) nativeSelectAt },
2848    { "nativeSelectionX", "()I",
2849        (void*) nativeSelectionX },
2850    { "nativeSelectionY", "()I",
2851        (void*) nativeSelectionY },
2852    { "nativeSetExtendSelection", "()V",
2853        (void*) nativeSetExtendSelection },
2854    { "nativeSetFindIsEmpty", "()V",
2855        (void*) nativeSetFindIsEmpty },
2856    { "nativeSetFindIsUp", "(Z)V",
2857        (void*) nativeSetFindIsUp },
2858    { "nativeSetHeightCanMeasure", "(Z)V",
2859        (void*) nativeSetHeightCanMeasure },
2860    { "nativeSetBaseLayer", "(IILandroid/graphics/Region;ZZZ)V",
2861        (void*) nativeSetBaseLayer },
2862    { "nativeGetTextSelectionRegion", "(ILandroid/graphics/Region;)V",
2863        (void*) nativeGetTextSelectionRegion },
2864    { "nativeGetSelectionHandles", "(I[I)V",
2865        (void*) nativeGetSelectionHandles },
2866    { "nativeGetBaseLayer", "()I",
2867        (void*) nativeGetBaseLayer },
2868    { "nativeReplaceBaseContent", "(I)V",
2869        (void*) nativeReplaceBaseContent },
2870    { "nativeCopyBaseContentToPicture", "(Landroid/graphics/Picture;)V",
2871        (void*) nativeCopyBaseContentToPicture },
2872    { "nativeHasContent", "()Z",
2873        (void*) nativeHasContent },
2874    { "nativeSetSelectionPointer", "(IZFII)V",
2875        (void*) nativeSetSelectionPointer },
2876    { "nativeShowCursorTimed", "()V",
2877        (void*) nativeShowCursorTimed },
2878    { "nativeRegisterPageSwapCallback", "(I)V",
2879        (void*) nativeRegisterPageSwapCallback },
2880    { "nativeDiscardAllTextures", "()V",
2881        (void*) nativeDiscardAllTextures },
2882    { "nativeTileProfilingStart", "()V",
2883        (void*) nativeTileProfilingStart },
2884    { "nativeTileProfilingStop", "()F",
2885        (void*) nativeTileProfilingStop },
2886    { "nativeTileProfilingClear", "()V",
2887        (void*) nativeTileProfilingClear },
2888    { "nativeTileProfilingNumFrames", "()I",
2889        (void*) nativeTileProfilingNumFrames },
2890    { "nativeTileProfilingNumTilesInFrame", "(I)I",
2891        (void*) nativeTileProfilingNumTilesInFrame },
2892    { "nativeTileProfilingGetInt", "(IILjava/lang/String;)I",
2893        (void*) nativeTileProfilingGetInt },
2894    { "nativeTileProfilingGetFloat", "(IILjava/lang/String;)F",
2895        (void*) nativeTileProfilingGetFloat },
2896    { "nativeStartSelection", "(II)Z",
2897        (void*) nativeStartSelection },
2898    { "nativeStopGL", "()V",
2899        (void*) nativeStopGL },
2900    { "nativeSubtractLayers", "(Landroid/graphics/Rect;)Landroid/graphics/Rect;",
2901        (void*) nativeSubtractLayers },
2902    { "nativeTextGeneration", "()I",
2903        (void*) nativeTextGeneration },
2904    { "nativeUpdateCachedTextfield", "(Ljava/lang/String;I)V",
2905        (void*) nativeUpdateCachedTextfield },
2906    {  "nativeWordSelection", "(II)Z",
2907        (void*) nativeWordSelection },
2908    { "nativeGetBlockLeftEdge", "(IIF)I",
2909        (void*) nativeGetBlockLeftEdge },
2910    { "nativeScrollableLayer", "(IILandroid/graphics/Rect;Landroid/graphics/Rect;)I",
2911        (void*) nativeScrollableLayer },
2912    { "nativeScrollLayer", "(III)Z",
2913        (void*) nativeScrollLayer },
2914    { "nativeSetIsScrolling", "(Z)V",
2915        (void*) nativeSetIsScrolling },
2916    { "nativeUseHardwareAccelSkia", "(Z)V",
2917        (void*) nativeUseHardwareAccelSkia },
2918    { "nativeGetBackgroundColor", "()I",
2919        (void*) nativeGetBackgroundColor },
2920    { "nativeSetProperty", "(Ljava/lang/String;Ljava/lang/String;)Z",
2921        (void*) nativeSetProperty },
2922    { "nativeGetProperty", "(Ljava/lang/String;)Ljava/lang/String;",
2923        (void*) nativeGetProperty },
2924    { "nativeOnTrimMemory", "(I)V",
2925        (void*) nativeOnTrimMemory },
2926    { "nativeSetPauseDrawing", "(IZ)V",
2927        (void*) nativeSetPauseDrawing },
2928};
2929
2930int registerWebView(JNIEnv* env)
2931{
2932    jclass clazz = env->FindClass("android/webkit/WebView");
2933    LOG_ASSERT(clazz, "Unable to find class android/webkit/WebView");
2934    gWebViewField = env->GetFieldID(clazz, "mNativeClass", "I");
2935    LOG_ASSERT(gWebViewField, "Unable to find android/webkit/WebView.mNativeClass");
2936    env->DeleteLocalRef(clazz);
2937
2938    return jniRegisterNativeMethods(env, "android/webkit/WebView", gJavaWebViewMethods, NELEM(gJavaWebViewMethods));
2939}
2940
2941} // namespace android
2942