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