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