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