WebView.cpp revision a5ffb7c279df240a07658953e1bd5df6d0480cb6
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 "PlatformGraphicsContext.h"
46#include "PlatformString.h"
47#include "SelectText.h"
48#include "SkCanvas.h"
49#include "SkDumpCanvas.h"
50#include "SkPicture.h"
51#include "SkRect.h"
52#include "SkTime.h"
53#ifdef ANDROID_INSTRUMENT
54#include "TimeCounter.h"
55#endif
56#include "TilesManager.h"
57#include "WebCoreJni.h"
58#include "WebRequestContext.h"
59#include "WebViewCore.h"
60#include "android_graphics.h"
61
62#ifdef GET_NATIVE_VIEW
63#undef GET_NATIVE_VIEW
64#endif
65
66#define GET_NATIVE_VIEW(env, obj) ((WebView*)env->GetIntField(obj, gWebViewField))
67
68#include <JNIUtility.h>
69#include <JNIHelp.h>
70#include <jni.h>
71#include <ui/KeycodeLabels.h>
72#include <wtf/text/AtomicString.h>
73#include <wtf/text/CString.h>
74
75namespace android {
76
77static jfieldID gWebViewField;
78
79//-------------------------------------
80
81static jmethodID GetJMethod(JNIEnv* env, jclass clazz, const char name[], const char signature[])
82{
83    jmethodID m = env->GetMethodID(clazz, name, signature);
84    LOG_ASSERT(m, "Could not find method %s", name);
85    return m;
86}
87
88//-------------------------------------
89// This class provides JNI for making calls into native code from the UI side
90// of the multi-threaded WebView.
91class WebView
92{
93public:
94enum FrameCachePermission {
95    DontAllowNewer,
96    AllowNewer,
97    AllowNewest
98};
99
100enum DrawExtras { // keep this in sync with WebView.java
101    DrawExtrasNone = 0,
102    DrawExtrasFind = 1,
103    DrawExtrasSelection = 2,
104    DrawExtrasCursorRing = 3
105};
106
107struct JavaGlue {
108    jweak       m_obj;
109    jmethodID   m_calcOurContentVisibleRectF;
110    jmethodID   m_overrideLoading;
111    jmethodID   m_scrollBy;
112    jmethodID   m_sendMoveFocus;
113    jmethodID   m_sendMoveMouse;
114    jmethodID   m_sendMoveMouseIfLatest;
115    jmethodID   m_sendMotionUp;
116    jmethodID   m_domChangedFocus;
117    jmethodID   m_getScaledMaxXScroll;
118    jmethodID   m_getScaledMaxYScroll;
119    jmethodID   m_getVisibleRect;
120    jmethodID   m_rebuildWebTextView;
121    jmethodID   m_viewInvalidate;
122    jmethodID   m_viewInvalidateRect;
123    jmethodID   m_postInvalidateDelayed;
124    jfieldID    m_rectLeft;
125    jfieldID    m_rectTop;
126    jmethodID   m_rectWidth;
127    jmethodID   m_rectHeight;
128    jfieldID    m_rectFLeft;
129    jfieldID    m_rectFTop;
130    jmethodID   m_rectFWidth;
131    jmethodID   m_rectFHeight;
132    AutoJObject object(JNIEnv* env) {
133        return getRealObject(env, m_obj);
134    }
135} m_javaGlue;
136
137WebView(JNIEnv* env, jobject javaWebView, int viewImpl) :
138    m_ring((WebViewCore*) viewImpl)
139{
140    jclass clazz = env->FindClass("android/webkit/WebView");
141 //   m_javaGlue = new JavaGlue;
142    m_javaGlue.m_obj = env->NewWeakGlobalRef(javaWebView);
143    m_javaGlue.m_scrollBy = GetJMethod(env, clazz, "setContentScrollBy", "(IIZ)Z");
144    m_javaGlue.m_calcOurContentVisibleRectF = GetJMethod(env, clazz, "calcOurContentVisibleRectF", "(Landroid/graphics/RectF;)V");
145    m_javaGlue.m_overrideLoading = GetJMethod(env, clazz, "overrideLoading", "(Ljava/lang/String;)V");
146    m_javaGlue.m_sendMoveFocus = GetJMethod(env, clazz, "sendMoveFocus", "(II)V");
147    m_javaGlue.m_sendMoveMouse = GetJMethod(env, clazz, "sendMoveMouse", "(IIII)V");
148    m_javaGlue.m_sendMoveMouseIfLatest = GetJMethod(env, clazz, "sendMoveMouseIfLatest", "(Z)V");
149    m_javaGlue.m_sendMotionUp = GetJMethod(env, clazz, "sendMotionUp", "(IIIII)V");
150    m_javaGlue.m_domChangedFocus = GetJMethod(env, clazz, "domChangedFocus", "()V");
151    m_javaGlue.m_getScaledMaxXScroll = GetJMethod(env, clazz, "getScaledMaxXScroll", "()I");
152    m_javaGlue.m_getScaledMaxYScroll = GetJMethod(env, clazz, "getScaledMaxYScroll", "()I");
153    m_javaGlue.m_getVisibleRect = GetJMethod(env, clazz, "sendOurVisibleRect", "()Landroid/graphics/Rect;");
154    m_javaGlue.m_rebuildWebTextView = GetJMethod(env, clazz, "rebuildWebTextView", "()V");
155    m_javaGlue.m_viewInvalidate = GetJMethod(env, clazz, "viewInvalidate", "()V");
156    m_javaGlue.m_viewInvalidateRect = GetJMethod(env, clazz, "viewInvalidate", "(IIII)V");
157    m_javaGlue.m_postInvalidateDelayed = GetJMethod(env, clazz,
158        "viewInvalidateDelayed", "(JIIII)V");
159    jclass rectClass = env->FindClass("android/graphics/Rect");
160    LOG_ASSERT(rectClass, "Could not find Rect class");
161    m_javaGlue.m_rectLeft = env->GetFieldID(rectClass, "left", "I");
162    m_javaGlue.m_rectTop = env->GetFieldID(rectClass, "top", "I");
163    m_javaGlue.m_rectWidth = GetJMethod(env, rectClass, "width", "()I");
164    m_javaGlue.m_rectHeight = GetJMethod(env, rectClass, "height", "()I");
165    jclass rectClassF = env->FindClass("android/graphics/RectF");
166    LOG_ASSERT(rectClassF, "Could not find RectF class");
167    m_javaGlue.m_rectFLeft = env->GetFieldID(rectClassF, "left", "F");
168    m_javaGlue.m_rectFTop = env->GetFieldID(rectClassF, "top", "F");
169    m_javaGlue.m_rectFWidth = GetJMethod(env, rectClassF, "width", "()F");
170    m_javaGlue.m_rectFHeight = GetJMethod(env, rectClassF, "height", "()F");
171    env->SetIntField(javaWebView, gWebViewField, (jint)this);
172    m_viewImpl = (WebViewCore*) viewImpl;
173    m_frameCacheUI = 0;
174    m_navPictureUI = 0;
175    m_generation = 0;
176    m_heightCanMeasure = false;
177    m_lastDx = 0;
178    m_lastDxTime = 0;
179    m_ringAnimationEnd = 0;
180    m_baseLayer = 0;
181}
182
183~WebView()
184{
185    if (m_javaGlue.m_obj)
186    {
187        JNIEnv* env = JSC::Bindings::getJNIEnv();
188        env->DeleteWeakGlobalRef(m_javaGlue.m_obj);
189        m_javaGlue.m_obj = 0;
190    }
191    delete m_frameCacheUI;
192    delete m_navPictureUI;
193    delete m_baseLayer;
194}
195
196WebViewCore* getWebViewCore() const {
197    return m_viewImpl;
198}
199
200// removes the cursor altogether (e.g., when going to a new page)
201void clearCursor()
202{
203    CachedRoot* root = getFrameCache(AllowNewer);
204    if (!root)
205        return;
206    DBG_NAV_LOG("");
207    m_viewImpl->m_hasCursorBounds = false;
208    root->clearCursor();
209    viewInvalidate();
210}
211
212// leaves the cursor where it is, but suppresses drawing it
213void hideCursor()
214{
215    CachedRoot* root = getFrameCache(AllowNewer);
216    if (!root)
217        return;
218    DBG_NAV_LOG("");
219    m_viewImpl->m_hasCursorBounds = false;
220    root->hideCursor();
221    viewInvalidate();
222}
223
224#if DUMP_NAV_CACHE
225void debugDump()
226{
227    CachedRoot* root = getFrameCache(DontAllowNewer);
228    if (root)
229        root->mDebug.print();
230}
231#endif
232
233// Traverse our stored array of buttons that are in our picture, and update
234// their subpictures according to their current state.
235// Called from the UI thread.  This is the one place in the UI thread where we
236// access the buttons stored in the WebCore thread.
237// hasFocus keeps track of whether the WebView has focus && windowFocus.
238// If not, we do not want to draw the button in a selected or pressed state
239void nativeRecordButtons(bool hasFocus, bool pressed, bool invalidate)
240{
241    bool cursorIsOnButton = false;
242    const CachedFrame* cachedFrame;
243    const CachedNode* cachedCursor = 0;
244    // Lock the mutex, since we now share with the WebCore thread.
245    m_viewImpl->gButtonMutex.lock();
246    if (m_viewImpl->m_buttons.size()) {
247        // FIXME: In a future change, we should keep track of whether the selection
248        // has changed to short circuit (note that we would still need to update
249        // if we received new buttons from the WebCore thread).
250        WebCore::Node* cursor = 0;
251        CachedRoot* root = getFrameCache(DontAllowNewer);
252        if (root) {
253            cachedCursor = root->currentCursor(&cachedFrame);
254            if (cachedCursor)
255                cursor = (WebCore::Node*) cachedCursor->nodePointer();
256        }
257
258        // Traverse the array, and update each button, depending on whether it
259        // is selected.
260        Container* end = m_viewImpl->m_buttons.end();
261        for (Container* ptr = m_viewImpl->m_buttons.begin(); ptr != end; ptr++) {
262            WebCore::RenderSkinAndroid::State state;
263            if (ptr->matches(cursor)) {
264                cursorIsOnButton = true;
265                // If the WebView is out of focus/window focus, set the state to
266                // normal, but still keep track of the fact that the selected is a
267                // button
268                if (!hasFocus) {
269                    state = WebCore::RenderSkinAndroid::kNormal;
270                } else if (pressed || m_ring.m_isPressed) {
271                    state = WebCore::RenderSkinAndroid::kPressed;
272                } else {
273                    state = WebCore::RenderSkinAndroid::kFocused;
274                }
275            } else {
276                state = WebCore::RenderSkinAndroid::kNormal;
277            }
278            ptr->updateFocusState(state);
279        }
280    }
281    m_viewImpl->gButtonMutex.unlock();
282    if (invalidate && cachedCursor && cursorIsOnButton) {
283        const WebCore::IntRect& b = cachedCursor->bounds(cachedFrame);
284        viewInvalidateRect(b.x(), b.y(), b.right(), b.bottom());
285    }
286}
287
288// The caller has already determined that the desired document rect corresponds
289// to the main picture, and not a layer
290void scrollRectOnScreen(const IntRect& rect)
291{
292    if (rect.isEmpty())
293        return;
294    SkRect visible;
295    calcOurContentVisibleRect(&visible);
296#if USE(ACCELERATED_COMPOSITING)
297    LayerAndroid* root = compositeRoot();
298    if (root) {
299        root->updateFixedLayersPositions(visible);
300        root->updatePositions();
301        visible = root->subtractLayers(visible);
302    }
303#endif
304    int dx = 0;
305    int left = rect.x();
306    int right = rect.right();
307    if (left < visible.fLeft) {
308        dx = left - visible.fLeft;
309    // Only scroll right if the entire width can fit on screen.
310    } else if (right > visible.fRight && right - left < visible.width()) {
311        dx = right - visible.fRight;
312    }
313    int dy = 0;
314    int top = rect.y();
315    int bottom = rect.bottom();
316    if (top < visible.fTop) {
317        dy = top - visible.fTop;
318    // Only scroll down if the entire height can fit on screen
319    } else if (bottom > visible.fBottom && bottom - top < visible.height()) {
320        dy = bottom - visible.fBottom;
321    }
322    if ((dx|dy) == 0 || !scrollBy(dx, dy))
323        return;
324    viewInvalidate();
325}
326
327void calcOurContentVisibleRect(SkRect* r)
328{
329    JNIEnv* env = JSC::Bindings::getJNIEnv();
330    jclass rectClass = env->FindClass("android/graphics/RectF");
331    jmethodID init = env->GetMethodID(rectClass, "<init>", "(FFFF)V");
332    jobject jRect = env->NewObject(rectClass, init, 0, 0, 0, 0);
333    env->CallVoidMethod(m_javaGlue.object(env).get(),
334        m_javaGlue.m_calcOurContentVisibleRectF, jRect);
335    r->fLeft = env->GetFloatField(jRect, m_javaGlue.m_rectFLeft);
336    r->fTop = env->GetFloatField(jRect, m_javaGlue.m_rectFTop);
337    r->fRight = r->fLeft + env->CallFloatMethod(jRect, m_javaGlue.m_rectFWidth);
338    r->fBottom = r->fTop + env->CallFloatMethod(jRect, m_javaGlue.m_rectFHeight);
339    env->DeleteLocalRef(jRect);
340    checkException(env);
341}
342
343void resetCursorRing()
344{
345    m_ringAnimationEnd = 0;
346    m_viewImpl->m_hasCursorBounds = false;
347}
348
349bool drawCursorPreamble(CachedRoot* root)
350{
351    const CachedFrame* frame;
352    const CachedNode* node = root->currentCursor(&frame);
353    if (!node) {
354        DBG_NAV_LOGV("%s", "!node");
355        resetCursorRing();
356        return false;
357    }
358    if (node->isHidden()) {
359        DBG_NAV_LOG("node->isHidden()");
360        m_viewImpl->m_hasCursorBounds = false;
361        return false;
362    }
363    setVisibleRect(root);
364    m_ring.m_root = root;
365    m_ring.m_frame = frame;
366    m_ring.m_node = node;
367    SkMSec time = SkTime::GetMSecs();
368    m_ring.m_isPressed = time < m_ringAnimationEnd
369        && m_ringAnimationEnd != UINT_MAX;
370    return true;
371}
372
373void drawCursorPostamble()
374{
375    if (m_ringAnimationEnd == UINT_MAX)
376        return;
377    SkMSec time = SkTime::GetMSecs();
378    if (time < m_ringAnimationEnd) {
379        // views assume that inval bounds coordinates are non-negative
380        WebCore::IntRect invalBounds(0, 0, INT_MAX, INT_MAX);
381        invalBounds.intersect(m_ring.m_absBounds);
382        postInvalidateDelayed(m_ringAnimationEnd - time, invalBounds);
383    } else {
384        hideCursor();
385    }
386}
387
388bool drawGL(WebCore::IntRect& viewRect, float scale, int extras)
389{
390#if USE(ACCELERATED_COMPOSITING)
391    if (!m_baseLayer)
392        return false;
393
394    m_glWebViewState.resetExtra(false);
395    CachedRoot* root = getFrameCache(AllowNewer);
396    if (!root) {
397        DBG_NAV_LOG("!root");
398        if (extras == DrawExtrasCursorRing)
399            resetCursorRing();
400        return false;
401    }
402    DrawExtra* extra = 0;
403    switch (extras) {
404        case DrawExtrasFind:
405            extra = &m_findOnPage;
406            break;
407        case DrawExtrasSelection:
408            extra = &m_selectText;
409            break;
410        case DrawExtrasCursorRing:
411            if (drawCursorPreamble(root) && m_ring.setup()) {
412                if (!m_ring.m_isButton)
413                    extra = &m_ring;
414                drawCursorPostamble();
415            }
416            break;
417        default:
418            ;
419    }
420
421    unsigned int pic = m_glWebViewState.currentPictureCounter();
422    if (extra) {
423        LayerAndroid* mainPicture = new LayerAndroid(m_navPictureUI);
424        m_glWebViewState.setExtra(extra, mainPicture);
425    } else {
426        m_glWebViewState.resetExtra(true);
427    }
428
429    SkRect visibleRect;
430    calcOurContentVisibleRect(&visibleRect);
431    bool ret = m_baseLayer->drawGL(viewRect, visibleRect, scale);
432    if (ret || m_glWebViewState.currentPictureCounter() != pic)
433        return true;
434#endif
435    return false;
436}
437
438PictureSet* draw(SkCanvas* canvas, SkColor bgColor, int extras, bool split)
439{
440    PictureSet* ret = 0;
441    if (!m_baseLayer) {
442        canvas->drawColor(bgColor);
443        return ret;
444    }
445
446    // draw the content of the base layer first
447    PictureSet* content = m_baseLayer->content();
448    int sc = canvas->save(SkCanvas::kClip_SaveFlag);
449    canvas->clipRect(SkRect::MakeLTRB(0, 0, content->width(),
450                content->height()), SkRegion::kDifference_Op);
451    canvas->drawColor(bgColor);
452    canvas->restoreToCount(sc);
453    if (content->draw(canvas))
454        ret = split ? new PictureSet(*content) : 0;
455
456    CachedRoot* root = getFrameCache(AllowNewer);
457    if (!root) {
458        DBG_NAV_LOG("!root");
459        if (extras == DrawExtrasCursorRing)
460            resetCursorRing();
461        return ret;
462    }
463    LayerAndroid mainPicture(m_navPictureUI);
464    DrawExtra* extra = 0;
465    switch (extras) {
466        case DrawExtrasFind:
467            extra = &m_findOnPage;
468            break;
469        case DrawExtrasSelection:
470            extra = &m_selectText;
471            break;
472        case DrawExtrasCursorRing:
473            if (drawCursorPreamble(root) && m_ring.setup()) {
474                if (!m_ring.m_isButton)
475                    extra = &m_ring;
476                drawCursorPostamble();
477            }
478            break;
479        default:
480            ;
481    }
482    if (extra)
483        extra->draw(canvas, &mainPicture);
484#if USE(ACCELERATED_COMPOSITING)
485    LayerAndroid* compositeLayer = compositeRoot();
486    if (!compositeLayer)
487        return ret;
488    compositeLayer->setExtra(extra);
489    SkRect visible;
490    calcOurContentVisibleRect(&visible);
491    // call this to be sure we've adjusted for any scrolling or animations
492    // before we actually draw
493    compositeLayer->updateFixedLayersPositions(visible);
494    compositeLayer->updatePositions();
495    // We have to set the canvas' matrix on the base layer
496    // (to have fixed layers work as intended)
497    SkAutoCanvasRestore restore(canvas, true);
498    m_baseLayer->setMatrix(canvas->getTotalMatrix());
499    canvas->resetMatrix();
500    m_baseLayer->draw(canvas);
501#endif
502    return ret;
503}
504
505
506bool cursorIsTextInput(FrameCachePermission allowNewer)
507{
508    CachedRoot* root = getFrameCache(allowNewer);
509    if (!root) {
510        DBG_NAV_LOG("!root");
511        return false;
512    }
513    const CachedNode* cursor = root->currentCursor();
514    if (!cursor) {
515        DBG_NAV_LOG("!cursor");
516        return false;
517    }
518    DBG_NAV_LOGD("%s", cursor->isTextInput() ? "true" : "false");
519    return cursor->isTextInput();
520}
521
522void cursorRingBounds(WebCore::IntRect* bounds)
523{
524    DBG_NAV_LOGD("%s", "");
525    CachedRoot* root = getFrameCache(DontAllowNewer);
526    if (root) {
527        const CachedFrame* cachedFrame;
528        const CachedNode* cachedNode = root->currentCursor(&cachedFrame);
529        if (cachedNode) {
530            *bounds = cachedNode->cursorRingBounds(cachedFrame);
531            DBG_NAV_LOGD("bounds={%d,%d,%d,%d}", bounds->x(), bounds->y(),
532                bounds->width(), bounds->height());
533            return;
534        }
535    }
536    *bounds = WebCore::IntRect(0, 0, 0, 0);
537}
538
539void fixCursor()
540{
541    m_viewImpl->gCursorBoundsMutex.lock();
542    bool hasCursorBounds = m_viewImpl->m_hasCursorBounds;
543    IntRect bounds = m_viewImpl->m_cursorBounds;
544    m_viewImpl->gCursorBoundsMutex.unlock();
545    if (!hasCursorBounds)
546        return;
547    int x, y;
548    const CachedFrame* frame;
549    const CachedNode* node = m_frameCacheUI->findAt(bounds, &frame, &x, &y, false);
550    if (!node)
551        return;
552    // require that node have approximately the same bounds (+/- 4) and the same
553    // center (+/- 2)
554    IntPoint oldCenter = IntPoint(bounds.x() + (bounds.width() >> 1),
555        bounds.y() + (bounds.height() >> 1));
556    IntRect newBounds = node->bounds(frame);
557    IntPoint newCenter = IntPoint(newBounds.x() + (newBounds.width() >> 1),
558        newBounds.y() + (newBounds.height() >> 1));
559    DBG_NAV_LOGD("oldCenter=(%d,%d) newCenter=(%d,%d)"
560        " bounds=(%d,%d,w=%d,h=%d) newBounds=(%d,%d,w=%d,h=%d)",
561        oldCenter.x(), oldCenter.y(), newCenter.x(), newCenter.y(),
562        bounds.x(), bounds.y(), bounds.width(), bounds.height(),
563        newBounds.x(), newBounds.y(), newBounds.width(), newBounds.height());
564    if (abs(oldCenter.x() - newCenter.x()) > 2)
565        return;
566    if (abs(oldCenter.y() - newCenter.y()) > 2)
567        return;
568    if (abs(bounds.x() - newBounds.x()) > 4)
569        return;
570    if (abs(bounds.y() - newBounds.y()) > 4)
571        return;
572    if (abs(bounds.right() - newBounds.right()) > 4)
573        return;
574    if (abs(bounds.bottom() - newBounds.bottom()) > 4)
575        return;
576    DBG_NAV_LOGD("node=%p frame=%p x=%d y=%d bounds=(%d,%d,w=%d,h=%d)",
577        node, frame, x, y, bounds.x(), bounds.y(), bounds.width(),
578        bounds.height());
579    m_frameCacheUI->setCursor(const_cast<CachedFrame*>(frame),
580        const_cast<CachedNode*>(node));
581}
582
583CachedRoot* getFrameCache(FrameCachePermission allowNewer)
584{
585    if (!m_viewImpl->m_updatedFrameCache) {
586        DBG_NAV_LOGV("%s", "!m_viewImpl->m_updatedFrameCache");
587        return m_frameCacheUI;
588    }
589    if (allowNewer == DontAllowNewer && m_viewImpl->m_lastGeneration < m_generation) {
590        DBG_NAV_LOGD("allowNewer==DontAllowNewer m_viewImpl->m_lastGeneration=%d"
591            " < m_generation=%d", m_viewImpl->m_lastGeneration, m_generation);
592        return m_frameCacheUI;
593    }
594    DBG_NAV_LOGD("%s", "m_viewImpl->m_updatedFrameCache == true");
595    const CachedFrame* oldCursorFrame;
596    const CachedNode* oldCursorNode = m_frameCacheUI ?
597        m_frameCacheUI->currentCursor(&oldCursorFrame) : 0;
598#if USE(ACCELERATED_COMPOSITING)
599    int layerId = -1;
600    if (oldCursorNode && oldCursorNode->isInLayer()) {
601        const LayerAndroid* cursorLayer = oldCursorFrame->layer(oldCursorNode)
602            ->layer(m_frameCacheUI->rootLayer());
603        if (cursorLayer)
604            layerId = cursorLayer->uniqueId();
605    }
606#endif
607    // get id from old layer and use to find new layer
608    bool oldFocusIsTextInput = false;
609    void* oldFocusNodePointer = 0;
610    if (m_frameCacheUI) {
611        const CachedNode* oldFocus = m_frameCacheUI->currentFocus();
612        if (oldFocus) {
613            oldFocusIsTextInput = oldFocus->isTextInput();
614            oldFocusNodePointer = oldFocus->nodePointer();
615        }
616    }
617    m_viewImpl->gFrameCacheMutex.lock();
618    delete m_frameCacheUI;
619    delete m_navPictureUI;
620    m_viewImpl->m_updatedFrameCache = false;
621    m_frameCacheUI = m_viewImpl->m_frameCacheKit;
622    m_navPictureUI = m_viewImpl->m_navPictureKit;
623    m_viewImpl->m_frameCacheKit = 0;
624    m_viewImpl->m_navPictureKit = 0;
625    m_viewImpl->gFrameCacheMutex.unlock();
626    if (m_frameCacheUI)
627        m_frameCacheUI->setRootLayer(compositeRoot());
628#if USE(ACCELERATED_COMPOSITING)
629    if (layerId >= 0) {
630        SkRect visible;
631        calcOurContentVisibleRect(&visible);
632        LayerAndroid* layer = const_cast<LayerAndroid*>(
633                                                m_frameCacheUI->rootLayer());
634        if (layer) {
635            layer->updateFixedLayersPositions(visible);
636            layer->updatePositions();
637        }
638    }
639#endif
640    fixCursor();
641    if (oldFocusIsTextInput) {
642        const CachedNode* newFocus = m_frameCacheUI->currentFocus();
643        if (newFocus && oldFocusNodePointer != newFocus->nodePointer()
644                && newFocus->isTextInput()
645                && newFocus != m_frameCacheUI->currentCursor()) {
646            // The focus has changed.  We may need to update things.
647            LOG_ASSERT(m_javaGlue.m_obj, "A java object was not associated with this native WebView!");
648            JNIEnv* env = JSC::Bindings::getJNIEnv();
649            env->CallVoidMethod(m_javaGlue.object(env).get(),
650                    m_javaGlue.m_domChangedFocus);
651            checkException(env);
652        }
653    }
654    if (oldCursorNode && (!m_frameCacheUI || !m_frameCacheUI->currentCursor()))
655        viewInvalidate(); // redraw in case cursor ring is still visible
656    return m_frameCacheUI;
657}
658
659int getScaledMaxXScroll()
660{
661    LOG_ASSERT(m_javaGlue.m_obj, "A java object was not associated with this native WebView!");
662    JNIEnv* env = JSC::Bindings::getJNIEnv();
663    int result = env->CallIntMethod(m_javaGlue.object(env).get(), m_javaGlue.m_getScaledMaxXScroll);
664    checkException(env);
665    return result;
666}
667
668int getScaledMaxYScroll()
669{
670    LOG_ASSERT(m_javaGlue.m_obj, "A java object was not associated with this native WebView!");
671    JNIEnv* env = JSC::Bindings::getJNIEnv();
672    int result = env->CallIntMethod(m_javaGlue.object(env).get(), m_javaGlue.m_getScaledMaxYScroll);
673    checkException(env);
674    return result;
675}
676
677void getVisibleRect(WebCore::IntRect* rect)
678{
679    LOG_ASSERT(m_javaGlue.m_obj, "A java object was not associated with this native WebView!");
680    JNIEnv* env = JSC::Bindings::getJNIEnv();
681    jobject jRect = env->CallObjectMethod(m_javaGlue.object(env).get(), m_javaGlue.m_getVisibleRect);
682    checkException(env);
683    int left = (int) env->GetIntField(jRect, m_javaGlue.m_rectLeft);
684    checkException(env);
685    rect->setX(left);
686    int top = (int) env->GetIntField(jRect, m_javaGlue.m_rectTop);
687    checkException(env);
688    rect->setY(top);
689    int width = (int) env->CallIntMethod(jRect, m_javaGlue.m_rectWidth);
690    checkException(env);
691    rect->setWidth(width);
692    int height = (int) env->CallIntMethod(jRect, m_javaGlue.m_rectHeight);
693    checkException(env);
694    rect->setHeight(height);
695    env->DeleteLocalRef(jRect);
696    checkException(env);
697}
698
699static CachedFrame::Direction KeyToDirection(int32_t keyCode)
700{
701    switch (keyCode) {
702        case AKEYCODE_DPAD_RIGHT:
703            DBG_NAV_LOGD("keyCode=%s", "right");
704            return CachedFrame::RIGHT;
705        case AKEYCODE_DPAD_LEFT:
706            DBG_NAV_LOGD("keyCode=%s", "left");
707            return CachedFrame::LEFT;
708        case AKEYCODE_DPAD_DOWN:
709            DBG_NAV_LOGD("keyCode=%s", "down");
710            return CachedFrame::DOWN;
711        case AKEYCODE_DPAD_UP:
712            DBG_NAV_LOGD("keyCode=%s", "up");
713            return CachedFrame::UP;
714        default:
715            DBG_NAV_LOGD("bad key %d sent", keyCode);
716            return CachedFrame::UNINITIALIZED;
717    }
718}
719
720WTF::String imageURI(int x, int y)
721{
722    const CachedRoot* root = getFrameCache(DontAllowNewer);
723    return root ? root->imageURI(x, y) : WTF::String();
724}
725
726bool cursorWantsKeyEvents()
727{
728    const CachedRoot* root = getFrameCache(DontAllowNewer);
729    if (root) {
730        const CachedNode* focus = root->currentCursor();
731        if (focus)
732            return focus->wantsKeyEvents();
733    }
734    return false;
735}
736
737
738/* returns true if the key had no effect (neither scrolled nor changed cursor) */
739bool moveCursor(int keyCode, int count, bool ignoreScroll)
740{
741    CachedRoot* root = getFrameCache(AllowNewer);
742    if (!root) {
743        DBG_NAV_LOG("!root");
744        return true;
745    }
746
747    m_viewImpl->m_moveGeneration++;
748    CachedFrame::Direction direction = KeyToDirection(keyCode);
749    const CachedFrame* cachedFrame, * oldFrame = 0;
750    const CachedNode* cursor = root->currentCursor(&oldFrame);
751    WebCore::IntPoint cursorLocation = root->cursorLocation();
752    DBG_NAV_LOGD("old cursor %d (nativeNode=%p) cursorLocation={%d, %d}",
753        cursor ? cursor->index() : 0,
754        cursor ? cursor->nodePointer() : 0, cursorLocation.x(), cursorLocation.y());
755    WebCore::IntRect visibleRect = setVisibleRect(root);
756    int xMax = getScaledMaxXScroll();
757    int yMax = getScaledMaxYScroll();
758    root->setMaxScroll(xMax, yMax);
759    const CachedNode* cachedNode = 0;
760    int dx = 0;
761    int dy = 0;
762    int counter = count;
763    while (--counter >= 0) {
764        WebCore::IntPoint scroll = WebCore::IntPoint(0, 0);
765        cachedNode = root->moveCursor(direction, &cachedFrame, &scroll);
766        dx += scroll.x();
767        dy += scroll.y();
768    }
769    DBG_NAV_LOGD("new cursor %d (nativeNode=%p) cursorLocation={%d, %d}"
770        "bounds={%d,%d,w=%d,h=%d}", cachedNode ? cachedNode->index() : 0,
771        cachedNode ? cachedNode->nodePointer() : 0,
772            root->cursorLocation().x(), root->cursorLocation().y(),
773            cachedNode ? cachedNode->bounds(cachedFrame).x() : 0,
774            cachedNode ? cachedNode->bounds(cachedFrame).y() : 0,
775            cachedNode ? cachedNode->bounds(cachedFrame).width() : 0,
776            cachedNode ? cachedNode->bounds(cachedFrame).height() : 0);
777    // If !m_heightCanMeasure (such as in the browser), we want to scroll no
778    // matter what
779    if (!ignoreScroll && (!m_heightCanMeasure ||
780            !cachedNode ||
781            (cursor && cursor->nodePointer() == cachedNode->nodePointer())))
782    {
783        if (count == 1 && dx != 0 && dy == 0 && -m_lastDx == dx &&
784                SkTime::GetMSecs() - m_lastDxTime < 1000)
785            root->checkForJiggle(&dx);
786        DBG_NAV_LOGD("scrollBy %d,%d", dx, dy);
787        if ((dx | dy))
788            this->scrollBy(dx, dy);
789        m_lastDx = dx;
790        m_lastDxTime = SkTime::GetMSecs();
791    }
792    bool result = false;
793    if (cachedNode) {
794        showCursorUntimed();
795        m_viewImpl->updateCursorBounds(root, cachedFrame, cachedNode);
796        root->setCursor(const_cast<CachedFrame*>(cachedFrame),
797                const_cast<CachedNode*>(cachedNode));
798        bool disableFocusController = cachedNode != root->currentFocus()
799                && cachedNode->wantsKeyEvents();
800        sendMoveMouseIfLatest(disableFocusController);
801    } else {
802        int docHeight = root->documentHeight();
803        int docWidth = root->documentWidth();
804        if (visibleRect.bottom() + dy > docHeight)
805            dy = docHeight - visibleRect.bottom();
806        else if (visibleRect.y() + dy < 0)
807            dy = -visibleRect.y();
808        if (visibleRect.right() + dx > docWidth)
809            dx = docWidth - visibleRect.right();
810        else if (visibleRect.x() < 0)
811            dx = -visibleRect.x();
812        result = direction == CachedFrame::LEFT ? dx >= 0 :
813            direction == CachedFrame::RIGHT ? dx <= 0 :
814            direction == CachedFrame::UP ? dy >= 0 : dy <= 0;
815    }
816    return result;
817}
818
819void notifyProgressFinished()
820{
821    DBG_NAV_LOGD("cursorIsTextInput=%d", cursorIsTextInput(DontAllowNewer));
822    rebuildWebTextView();
823#if DEBUG_NAV_UI
824    if (m_frameCacheUI) {
825        const CachedNode* focus = m_frameCacheUI->currentFocus();
826        DBG_NAV_LOGD("focus %d (nativeNode=%p)",
827            focus ? focus->index() : 0,
828            focus ? focus->nodePointer() : 0);
829    }
830#endif
831}
832
833const CachedNode* findAt(CachedRoot* root, const WebCore::IntRect& rect,
834    const CachedFrame** framePtr, int* rxPtr, int* ryPtr)
835{
836    *rxPtr = 0;
837    *ryPtr = 0;
838    *framePtr = 0;
839    if (!root)
840        return 0;
841    setVisibleRect(root);
842    return root->findAt(rect, framePtr, rxPtr, ryPtr, true);
843}
844
845IntRect setVisibleRect(CachedRoot* root)
846{
847    IntRect visibleRect;
848    getVisibleRect(&visibleRect);
849    DBG_NAV_LOGD("getVisibleRect %d,%d,%d,%d",
850        visibleRect.x(), visibleRect.y(), visibleRect.width(), visibleRect.height());
851    root->setVisibleRect(visibleRect);
852    return visibleRect;
853}
854
855void selectBestAt(const WebCore::IntRect& rect)
856{
857    const CachedFrame* frame;
858    int rx, ry;
859    CachedRoot* root = getFrameCache(DontAllowNewer);
860    const CachedNode* node = findAt(root, rect, &frame, &rx, &ry);
861
862    if (!node) {
863        DBG_NAV_LOGD("no nodes found root=%p", root);
864        m_viewImpl->m_hasCursorBounds = false;
865        if (root)
866            root->setCursor(0, 0);
867        viewInvalidate();
868    } else {
869        DBG_NAV_LOGD("CachedNode:%p (%d)", node, node->index());
870        WebCore::IntRect bounds = node->bounds(frame);
871        root->rootHistory()->setMouseBounds(frame->unadjustBounds(node, bounds));
872        m_viewImpl->updateCursorBounds(root, frame, node);
873        showCursorTimed();
874        root->setCursor(const_cast<CachedFrame*>(frame),
875                const_cast<CachedNode*>(node));
876    }
877    sendMoveMouseIfLatest(false);
878}
879
880const CachedNode* m_cacheHitNode;
881const CachedFrame* m_cacheHitFrame;
882
883bool pointInNavCache(int x, int y, int slop)
884{
885    CachedRoot* root = getFrameCache(AllowNewer);
886    if (!root)
887        return false;
888    IntRect rect = IntRect(x - slop, y - slop, slop * 2, slop * 2);
889    int rx, ry;
890    return (m_cacheHitNode = findAt(root, rect, &m_cacheHitFrame, &rx, &ry));
891}
892
893bool motionUp(int x, int y, int slop)
894{
895    bool pageScrolled = false;
896    IntRect rect = IntRect(x - slop, y - slop, slop * 2, slop * 2);
897    int rx, ry;
898    CachedRoot* root = getFrameCache(AllowNewer);
899    if (!root)
900        return 0;
901    const CachedFrame* frame = 0;
902    const CachedNode* result = findAt(root, rect, &frame, &rx, &ry);
903    CachedHistory* history = root->rootHistory();
904    if (!result) {
905        DBG_NAV_LOGD("no nodes found root=%p", root);
906        history->setNavBounds(rect);
907        m_viewImpl->m_hasCursorBounds = false;
908        root->hideCursor();
909        int dx = root->checkForCenter(x, y);
910        if (dx) {
911            scrollBy(dx, 0);
912            pageScrolled = true;
913        }
914        sendMotionUp(frame ? (WebCore::Frame*) frame->framePointer() : 0,
915            0, x, y);
916        viewInvalidate();
917        return pageScrolled;
918    }
919    DBG_NAV_LOGD("CachedNode:%p (%d) x=%d y=%d rx=%d ry=%d", result,
920        result->index(), x, y, rx, ry);
921    // No need to call unadjustBounds below.  rx and ry are already adjusted to
922    // the absolute position of the node.
923    WebCore::IntRect navBounds = WebCore::IntRect(rx, ry, 1, 1);
924    history->setNavBounds(navBounds);
925    history->setMouseBounds(navBounds);
926    m_viewImpl->updateCursorBounds(root, frame, result);
927    root->setCursor(const_cast<CachedFrame*>(frame),
928        const_cast<CachedNode*>(result));
929    if (result->isSyntheticLink())
930        overrideUrlLoading(result->getExport());
931    else {
932        sendMotionUp(
933            (WebCore::Frame*) frame->framePointer(),
934            (WebCore::Node*) result->nodePointer(), rx, ry);
935    }
936    if (result->isTextInput() || result->isSelect()
937            || result->isContentEditable()) {
938        showCursorUntimed();
939    } else
940        showCursorTimed();
941    return pageScrolled;
942}
943
944const LayerAndroid* scrollableLayer(int x, int y)
945{
946#if ENABLE(ANDROID_OVERFLOW_SCROLL) && USE(ACCELERATED_COMPOSITING)
947    const LayerAndroid* root = compositeRoot();
948    if (!root)
949        return 0;
950    const LayerAndroid* result = root->find(x, y);
951    if (result != 0 && result->contentIsScrollable())
952        return result;
953#endif
954    return 0;
955}
956
957int getBlockLeftEdge(int x, int y, float scale)
958{
959    CachedRoot* root = getFrameCache(AllowNewer);
960    if (root)
961        return root->getBlockLeftEdge(x, y, scale);
962    return -1;
963}
964
965void overrideUrlLoading(const WTF::String& url)
966{
967    JNIEnv* env = JSC::Bindings::getJNIEnv();
968    jstring jName = env->NewString((jchar*) url.characters(), url.length());
969    env->CallVoidMethod(m_javaGlue.object(env).get(),
970            m_javaGlue.m_overrideLoading, jName);
971    env->DeleteLocalRef(jName);
972}
973
974void setFindIsUp(bool up)
975{
976    DBG_NAV_LOGD("up=%d", up);
977    m_viewImpl->m_findIsUp = up;
978}
979
980void setFindIsEmpty()
981{
982    DBG_NAV_LOG("");
983    m_findOnPage.clearCurrentLocation();
984}
985
986void showCursorTimed()
987{
988    DBG_NAV_LOG("");
989    m_ringAnimationEnd = SkTime::GetMSecs() + 500;
990    viewInvalidate();
991}
992
993void showCursorUntimed()
994{
995    DBG_NAV_LOG("");
996    m_ring.m_isPressed = false;
997    m_ringAnimationEnd = UINT_MAX;
998    viewInvalidate();
999}
1000
1001void setHeightCanMeasure(bool measure)
1002{
1003    m_heightCanMeasure = measure;
1004}
1005
1006String getSelection()
1007{
1008    return m_selectText.getSelection();
1009}
1010
1011void moveSelection(int x, int y)
1012{
1013    const CachedRoot* root = getFrameCache(DontAllowNewer);
1014    if (!root)
1015        return;
1016    SkPicture* picture = root->pictureAt(x, y);
1017    // FIXME: use the visibleRect only for the main picture
1018    // for layer pictures, use the equivalent of the canvas clipping rect
1019    IntRect visibleRect;
1020    getVisibleRect(&visibleRect);
1021    m_selectText.setVisibleRect(visibleRect);
1022    m_selectText.moveSelection(picture, x, y);
1023}
1024
1025void selectAll()
1026{
1027    const CachedRoot* root = getFrameCache(DontAllowNewer);
1028    if (!root)
1029        return;
1030    SkPicture* picture = root->pictureAt(0, 0);
1031    m_selectText.selectAll(picture);
1032}
1033
1034int selectionX()
1035{
1036    return m_selectText.selectionX();
1037}
1038
1039int selectionY()
1040{
1041    return m_selectText.selectionY();
1042}
1043
1044void resetSelection()
1045{
1046    m_selectText.reset();
1047}
1048
1049bool startSelection(int x, int y)
1050{
1051    return m_selectText.startSelection(x, y);
1052}
1053
1054bool wordSelection(int x, int y)
1055{
1056    startSelection(x, y);
1057    if (!extendSelection(x, y))
1058        return false;
1059    m_selectText.setDrawPointer(false);
1060    SkPicture* picture = getFrameCache(DontAllowNewer)->pictureAt(x, y);
1061    return m_selectText.wordSelection(picture);
1062}
1063
1064bool extendSelection(int x, int y)
1065{
1066    const CachedRoot* root = getFrameCache(DontAllowNewer);
1067    if (!root)
1068        return false;
1069    SkPicture* picture = root->pictureAt(x, y);
1070    IntRect visibleRect;
1071    getVisibleRect(&visibleRect);
1072    m_selectText.setVisibleRect(visibleRect);
1073    m_selectText.extendSelection(picture, x, y);
1074    return true;
1075}
1076
1077bool hitSelection(int x, int y)
1078{
1079    return m_selectText.hitSelection(x, y);
1080}
1081
1082void setExtendSelection()
1083{
1084    m_selectText.setExtendSelection(true);
1085}
1086
1087void setSelectionPointer(bool set, float scale, int x, int y)
1088{
1089    m_selectText.setDrawPointer(set);
1090    if (!set)
1091        return;
1092    m_selectText.m_inverseScale = scale;
1093    m_selectText.m_selectX = x;
1094    m_selectText.m_selectY = y;
1095}
1096
1097void sendMoveFocus(WebCore::Frame* framePtr, WebCore::Node* nodePtr)
1098{
1099    DBG_NAV_LOGD("framePtr=%p nodePtr=%p", framePtr, nodePtr);
1100    JNIEnv* env = JSC::Bindings::getJNIEnv();
1101    env->CallVoidMethod(m_javaGlue.object(env).get(),
1102        m_javaGlue.m_sendMoveFocus, (jint) framePtr, (jint) nodePtr);
1103    checkException(env);
1104}
1105
1106void sendMoveMouse(WebCore::Frame* framePtr, WebCore::Node* nodePtr, int x, int y)
1107{
1108    DBG_NAV_LOGD("framePtr=%p nodePtr=%p x=%d y=%d", framePtr, nodePtr, x, y);
1109    JNIEnv* env = JSC::Bindings::getJNIEnv();
1110    env->CallVoidMethod(m_javaGlue.object(env).get(), m_javaGlue.m_sendMoveMouse,
1111        (jint) framePtr, (jint) nodePtr, x, y);
1112    checkException(env);
1113}
1114
1115void sendMoveMouseIfLatest(bool disableFocusController)
1116{
1117    LOG_ASSERT(m_javaGlue.m_obj, "A java object was not associated with this native WebView!");
1118    JNIEnv* env = JSC::Bindings::getJNIEnv();
1119    env->CallVoidMethod(m_javaGlue.object(env).get(),
1120            m_javaGlue.m_sendMoveMouseIfLatest, disableFocusController);
1121    checkException(env);
1122}
1123
1124void sendMotionUp(
1125    WebCore::Frame* framePtr, WebCore::Node* nodePtr, int x, int y)
1126{
1127    m_viewImpl->m_touchGeneration = ++m_generation;
1128    DBG_NAV_LOGD("m_generation=%d framePtr=%p nodePtr=%p x=%d y=%d",
1129        m_generation, framePtr, nodePtr, x, y);
1130    LOG_ASSERT(m_javaGlue.m_obj, "A WebView was not associated with this WebViewNative!");
1131    JNIEnv* env = JSC::Bindings::getJNIEnv();
1132    env->CallVoidMethod(m_javaGlue.object(env).get(), m_javaGlue.m_sendMotionUp,
1133        m_generation, (jint) framePtr, (jint) nodePtr, x, y);
1134    checkException(env);
1135}
1136
1137void findNext(bool forward)
1138{
1139    m_findOnPage.findNext(forward);
1140    if (!m_findOnPage.currentMatchIsInLayer())
1141        scrollRectOnScreen(m_findOnPage.currentMatchBounds());
1142    viewInvalidate();
1143}
1144
1145// With this call, WebView takes ownership of matches, and is responsible for
1146// deleting it.
1147void setMatches(WTF::Vector<MatchInfo>* matches)
1148{
1149    m_findOnPage.setMatches(matches);
1150    if (!m_findOnPage.currentMatchIsInLayer())
1151        scrollRectOnScreen(m_findOnPage.currentMatchBounds());
1152    viewInvalidate();
1153}
1154
1155int currentMatchIndex()
1156{
1157    return m_findOnPage.currentMatchIndex();
1158}
1159
1160bool scrollBy(int dx, int dy)
1161{
1162    LOG_ASSERT(m_javaGlue.m_obj, "A java object was not associated with this native WebView!");
1163
1164    JNIEnv* env = JSC::Bindings::getJNIEnv();
1165    bool result = env->CallBooleanMethod(m_javaGlue.object(env).get(),
1166        m_javaGlue.m_scrollBy, dx, dy, true);
1167    checkException(env);
1168    return result;
1169}
1170
1171bool hasCursorNode()
1172{
1173    CachedRoot* root = getFrameCache(DontAllowNewer);
1174    if (!root) {
1175        DBG_NAV_LOG("!root");
1176        return false;
1177    }
1178    const CachedNode* cursorNode = root->currentCursor();
1179    DBG_NAV_LOGD("cursorNode=%d (nodePointer=%p)",
1180        cursorNode ? cursorNode->index() : -1,
1181        cursorNode ? cursorNode->nodePointer() : 0);
1182    return cursorNode;
1183}
1184
1185bool hasFocusNode()
1186{
1187    CachedRoot* root = getFrameCache(DontAllowNewer);
1188    if (!root) {
1189        DBG_NAV_LOG("!root");
1190        return false;
1191    }
1192    const CachedNode* focusNode = root->currentFocus();
1193    DBG_NAV_LOGD("focusNode=%d (nodePointer=%p)",
1194        focusNode ? focusNode->index() : -1,
1195        focusNode ? focusNode->nodePointer() : 0);
1196    return focusNode;
1197}
1198
1199void rebuildWebTextView()
1200{
1201    JNIEnv* env = JSC::Bindings::getJNIEnv();
1202    env->CallVoidMethod(m_javaGlue.object(env).get(),
1203            m_javaGlue.m_rebuildWebTextView);
1204    checkException(env);
1205}
1206
1207void viewInvalidate()
1208{
1209    JNIEnv* env = JSC::Bindings::getJNIEnv();
1210    env->CallVoidMethod(m_javaGlue.object(env).get(), m_javaGlue.m_viewInvalidate);
1211    checkException(env);
1212}
1213
1214void viewInvalidateRect(int l, int t, int r, int b)
1215{
1216    JNIEnv* env = JSC::Bindings::getJNIEnv();
1217    env->CallVoidMethod(m_javaGlue.object(env).get(), m_javaGlue.m_viewInvalidateRect, l, r, t, b);
1218    checkException(env);
1219}
1220
1221void postInvalidateDelayed(int64_t delay, const WebCore::IntRect& bounds)
1222{
1223    JNIEnv* env = JSC::Bindings::getJNIEnv();
1224    env->CallVoidMethod(m_javaGlue.object(env).get(), m_javaGlue.m_postInvalidateDelayed,
1225        delay, bounds.x(), bounds.y(), bounds.right(), bounds.bottom());
1226    checkException(env);
1227}
1228
1229int moveGeneration()
1230{
1231    return m_viewImpl->m_moveGeneration;
1232}
1233
1234LayerAndroid* compositeRoot() const
1235{
1236    LOG_ASSERT(!m_baseLayer || m_baseLayer->countChildren() == 1,
1237            "base layer can't have more than one child %s", __FUNCTION__);
1238    if (m_baseLayer && m_baseLayer->countChildren() == 1)
1239        return static_cast<LayerAndroid*>(m_baseLayer->getChild(0));
1240    else
1241        return 0;
1242}
1243
1244static void copyScrollPositionRecursive(const LayerAndroid* from,
1245                                        LayerAndroid* root)
1246{
1247    if (!from || !root)
1248        return;
1249    for (int i = 0; i < from->countChildren(); i++) {
1250        const LayerAndroid* l = from->getChild(i);
1251        if (l->contentIsScrollable()) {
1252            LayerAndroid* match =
1253                    const_cast<LayerAndroid*>(root->findById(l->uniqueId()));
1254            if (match != 0)
1255                match->setScrollPosition(l->scrollPosition());
1256        }
1257        copyScrollPositionRecursive(l, root);
1258    }
1259}
1260
1261void setBaseLayer(BaseLayerAndroid* layer, WebCore::IntRect& rect)
1262{
1263#if USE(ACCELERATED_COMPOSITING)
1264    m_glWebViewState.setBaseLayer(layer, rect);
1265#endif
1266
1267    if (layer) {
1268        copyScrollPositionRecursive(compositeRoot(),
1269            static_cast<LayerAndroid*>(layer->getChild(0)));
1270    }
1271    delete m_baseLayer;
1272    m_baseLayer = layer;
1273    CachedRoot* root = getFrameCache(DontAllowNewer);
1274    if (!root)
1275        return;
1276    root->resetLayers();
1277    root->setRootLayer(compositeRoot());
1278}
1279
1280void replaceBaseContent(PictureSet* set)
1281{
1282    if (!m_baseLayer)
1283        return;
1284    m_baseLayer->setContent(*set);
1285    delete set;
1286}
1287
1288void copyBaseContentToPicture(SkPicture* picture)
1289{
1290    if (!m_baseLayer)
1291        return;
1292    PictureSet* content = m_baseLayer->content();
1293    content->draw(picture->beginRecording(content->width(), content->height(),
1294            SkPicture::kUsePathBoundsForClip_RecordingFlag));
1295    picture->endRecording();
1296}
1297
1298bool hasContent() {
1299    if (!m_baseLayer)
1300        return false;
1301    return !m_baseLayer->content()->isEmpty();
1302}
1303
1304private: // local state for WebView
1305    // private to getFrameCache(); other functions operate in a different thread
1306    CachedRoot* m_frameCacheUI; // navigation data ready for use
1307    WebViewCore* m_viewImpl;
1308    int m_generation; // associate unique ID with sent kit focus to match with ui
1309    SkPicture* m_navPictureUI;
1310    SkMSec m_ringAnimationEnd;
1311    // Corresponds to the same-named boolean on the java side.
1312    bool m_heightCanMeasure;
1313    int m_lastDx;
1314    SkMSec m_lastDxTime;
1315    SelectText m_selectText;
1316    FindOnPage m_findOnPage;
1317    CursorRing m_ring;
1318    BaseLayerAndroid* m_baseLayer;
1319#if USE(ACCELERATED_COMPOSITING)
1320    GLWebViewState m_glWebViewState;
1321#endif
1322}; // end of WebView class
1323
1324/*
1325 * Native JNI methods
1326 */
1327static jstring WebCoreStringToJString(JNIEnv *env, WTF::String string)
1328{
1329    int length = string.length();
1330    if (!length)
1331        return 0;
1332    jstring ret = env->NewString((jchar *)string.characters(), length);
1333    env->DeleteLocalRef(ret);
1334    return ret;
1335}
1336
1337static int nativeCacheHitFramePointer(JNIEnv *env, jobject obj)
1338{
1339    return reinterpret_cast<int>(GET_NATIVE_VIEW(env, obj)
1340            ->m_cacheHitFrame->framePointer());
1341}
1342
1343static jobject nativeCacheHitNodeBounds(JNIEnv *env, jobject obj)
1344{
1345    WebCore::IntRect bounds = GET_NATIVE_VIEW(env, obj)
1346        ->m_cacheHitNode->originalAbsoluteBounds();
1347    jclass rectClass = env->FindClass("android/graphics/Rect");
1348    jmethodID init = env->GetMethodID(rectClass, "<init>", "(IIII)V");
1349    jobject rect = env->NewObject(rectClass, init, bounds.x(),
1350        bounds.y(), bounds.right(), bounds.bottom());
1351    return rect;
1352}
1353
1354static int nativeCacheHitNodePointer(JNIEnv *env, jobject obj)
1355{
1356    return reinterpret_cast<int>(GET_NATIVE_VIEW(env, obj)
1357        ->m_cacheHitNode->nodePointer());
1358}
1359
1360static void nativeClearCursor(JNIEnv *env, jobject obj)
1361{
1362    WebView* view = GET_NATIVE_VIEW(env, obj);
1363    LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
1364    view->clearCursor();
1365}
1366
1367static void nativeCreate(JNIEnv *env, jobject obj, int viewImpl)
1368{
1369    WebView* webview = new WebView(env, obj, viewImpl);
1370    // NEED THIS OR SOMETHING LIKE IT!
1371    //Release(obj);
1372}
1373
1374static jint nativeCursorFramePointer(JNIEnv *env, jobject obj)
1375{
1376    WebView* view = GET_NATIVE_VIEW(env, obj);
1377    CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
1378    if (!root)
1379        return 0;
1380    const CachedFrame* frame = 0;
1381    (void) root->currentCursor(&frame);
1382    return reinterpret_cast<int>(frame ? frame->framePointer() : 0);
1383}
1384
1385static const CachedNode* getCursorNode(JNIEnv *env, jobject obj)
1386{
1387    WebView* view = GET_NATIVE_VIEW(env, obj);
1388    CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
1389    return root ? root->currentCursor() : 0;
1390}
1391
1392static const CachedNode* getCursorNode(JNIEnv *env, jobject obj,
1393    const CachedFrame** frame)
1394{
1395    WebView* view = GET_NATIVE_VIEW(env, obj);
1396    CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
1397    return root ? root->currentCursor(frame) : 0;
1398}
1399
1400static const CachedNode* getFocusCandidate(JNIEnv *env, jobject obj,
1401    const CachedFrame** frame)
1402{
1403    WebView* view = GET_NATIVE_VIEW(env, obj);
1404    CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
1405    if (!root)
1406        return 0;
1407    const CachedNode* cursor = root->currentCursor(frame);
1408    if (cursor && cursor->wantsKeyEvents())
1409        return cursor;
1410    return root->currentFocus();
1411}
1412
1413static bool focusCandidateHasNextTextfield(JNIEnv *env, jobject obj)
1414{
1415    WebView* view = GET_NATIVE_VIEW(env, obj);
1416    CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
1417    if (!root)
1418        return false;
1419    const CachedNode* cursor = root->currentCursor();
1420    if (!cursor || !cursor->isTextInput())
1421        cursor = root->currentFocus();
1422    if (!cursor || !cursor->isTextInput()) return false;
1423    return root->nextTextField(cursor, 0);
1424}
1425
1426static const CachedNode* getFocusNode(JNIEnv *env, jobject obj)
1427{
1428    WebView* view = GET_NATIVE_VIEW(env, obj);
1429    CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
1430    return root ? root->currentFocus() : 0;
1431}
1432
1433static const CachedNode* getFocusNode(JNIEnv *env, jobject obj,
1434    const CachedFrame** frame)
1435{
1436    WebView* view = GET_NATIVE_VIEW(env, obj);
1437    CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
1438    return root ? root->currentFocus(frame) : 0;
1439}
1440
1441static const CachedInput* getInputCandidate(JNIEnv *env, jobject obj)
1442{
1443    WebView* view = GET_NATIVE_VIEW(env, obj);
1444    CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
1445    if (!root)
1446        return 0;
1447    const CachedFrame* frame;
1448    const CachedNode* cursor = root->currentCursor(&frame);
1449    if (!cursor || !cursor->wantsKeyEvents())
1450        cursor = root->currentFocus(&frame);
1451    return cursor ? frame->textInput(cursor) : 0;
1452}
1453
1454static jboolean nativePageShouldHandleShiftAndArrows(JNIEnv *env, jobject obj)
1455{
1456    const CachedNode* focus = getFocusNode(env, obj);
1457    if (!focus) return false;
1458    // Plugins handle shift and arrows whether or not they have focus.
1459    if (focus->isPlugin()) return true;
1460    const CachedNode* cursor = getCursorNode(env, obj);
1461    // ContentEditable nodes should only receive shift and arrows if they have
1462    // both the cursor and the focus.
1463    return cursor && cursor->nodePointer() == focus->nodePointer()
1464            && cursor->isContentEditable();
1465}
1466
1467static jboolean nativeCursorMatchesFocus(JNIEnv *env, jobject obj)
1468{
1469    const CachedNode* cursor = getCursorNode(env, obj);
1470    const CachedNode* focus = getFocusNode(env, obj);
1471    return cursor && focus && cursor->nodePointer() == focus->nodePointer();
1472}
1473
1474static jobject nativeCursorNodeBounds(JNIEnv *env, jobject obj)
1475{
1476    const CachedFrame* frame;
1477    const CachedNode* node = getCursorNode(env, obj, &frame);
1478    WebCore::IntRect bounds = node ? node->bounds(frame)
1479        : WebCore::IntRect(0, 0, 0, 0);
1480    jclass rectClass = env->FindClass("android/graphics/Rect");
1481    jmethodID init = env->GetMethodID(rectClass, "<init>", "(IIII)V");
1482    jobject rect = env->NewObject(rectClass, init, bounds.x(),
1483        bounds.y(), bounds.right(), bounds.bottom());
1484    return rect;
1485}
1486
1487static jint nativeCursorNodePointer(JNIEnv *env, jobject obj)
1488{
1489    const CachedNode* node = getCursorNode(env, obj);
1490    return reinterpret_cast<int>(node ? node->nodePointer() : 0);
1491}
1492
1493static jobject nativeCursorPosition(JNIEnv *env, jobject obj)
1494{
1495    WebView* view = GET_NATIVE_VIEW(env, obj);
1496    const CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
1497    WebCore::IntPoint pos = WebCore::IntPoint(0, 0);
1498    if (root)
1499        root->getSimulatedMousePosition(&pos);
1500    jclass pointClass = env->FindClass("android/graphics/Point");
1501    jmethodID init = env->GetMethodID(pointClass, "<init>", "(II)V");
1502    jobject point = env->NewObject(pointClass, init, pos.x(), pos.y());
1503    return point;
1504}
1505
1506static WebCore::IntRect jrect_to_webrect(JNIEnv* env, jobject obj)
1507{
1508    int L, T, R, B;
1509    GraphicsJNI::get_jrect(env, obj, &L, &T, &R, &B);
1510    return WebCore::IntRect(L, T, R - L, B - T);
1511}
1512
1513static bool nativeCursorIntersects(JNIEnv *env, jobject obj, jobject visRect)
1514{
1515    const CachedFrame* frame;
1516    const CachedNode* node = getCursorNode(env, obj, &frame);
1517    return node ? node->bounds(frame).intersects(
1518        jrect_to_webrect(env, visRect)) : false;
1519}
1520
1521static bool nativeCursorIsAnchor(JNIEnv *env, jobject obj)
1522{
1523    const CachedNode* node = getCursorNode(env, obj);
1524    return node ? node->isAnchor() : false;
1525}
1526
1527static bool nativeCursorIsTextInput(JNIEnv *env, jobject obj)
1528{
1529    const CachedNode* node = getCursorNode(env, obj);
1530    return node ? node->isTextInput() : false;
1531}
1532
1533static jobject nativeCursorText(JNIEnv *env, jobject obj)
1534{
1535    const CachedNode* node = getCursorNode(env, obj);
1536    if (!node)
1537        return 0;
1538    WTF::String value = node->getExport();
1539    return !value.isEmpty() ? env->NewString((jchar *)value.characters(),
1540        value.length()) : 0;
1541}
1542
1543static void nativeDebugDump(JNIEnv *env, jobject obj)
1544{
1545#if DUMP_NAV_CACHE
1546    WebView* view = GET_NATIVE_VIEW(env, obj);
1547    LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
1548    view->debugDump();
1549#endif
1550}
1551
1552static jint nativeDraw(JNIEnv *env, jobject obj, jobject canv, jint color,
1553        jint extras, jboolean split) {
1554    SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, canv);
1555    return reinterpret_cast<jint>(GET_NATIVE_VIEW(env, obj)->draw(canvas, color, extras, split));
1556}
1557
1558static bool nativeDrawGL(JNIEnv *env, jobject obj, jobject jrect,
1559                         jfloat scale, jint extras)
1560{
1561    WebCore::IntRect viewRect = jrect_to_webrect(env, jrect);
1562    return GET_NATIVE_VIEW(env, obj)->drawGL(viewRect, scale, extras);
1563}
1564
1565static bool nativeEvaluateLayersAnimations(JNIEnv *env, jobject obj)
1566{
1567#if USE(ACCELERATED_COMPOSITING)
1568    const LayerAndroid* root = GET_NATIVE_VIEW(env, obj)->compositeRoot();
1569    if (root)
1570        return root->evaluateAnimations();
1571#endif
1572    return false;
1573}
1574
1575static void nativeSetBaseLayer(JNIEnv *env, jobject obj, jint layer, jobject jrect)
1576{
1577    BaseLayerAndroid* layerImpl = reinterpret_cast<BaseLayerAndroid*>(layer);
1578    WebCore::IntRect rect = jrect_to_webrect(env, jrect);
1579    GET_NATIVE_VIEW(env, obj)->setBaseLayer(layerImpl, rect);
1580}
1581
1582static void nativeReplaceBaseContent(JNIEnv *env, jobject obj, jint content)
1583{
1584    PictureSet* set = reinterpret_cast<PictureSet*>(content);
1585    GET_NATIVE_VIEW(env, obj)->replaceBaseContent(set);
1586}
1587
1588static void nativeCopyBaseContentToPicture(JNIEnv *env, jobject obj, jobject pict)
1589{
1590    SkPicture* picture = GraphicsJNI::getNativePicture(env, pict);
1591    GET_NATIVE_VIEW(env, obj)->copyBaseContentToPicture(picture);
1592}
1593
1594static bool nativeHasContent(JNIEnv *env, jobject obj)
1595{
1596    return GET_NATIVE_VIEW(env, obj)->hasContent();
1597}
1598
1599static jobject nativeImageURI(JNIEnv *env, jobject obj, jint x, jint y)
1600{
1601    WebView* view = GET_NATIVE_VIEW(env, obj);
1602    LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
1603    WTF::String uri = view->imageURI(x, y);
1604    jstring ret = 0;
1605    unsigned len = uri.length();
1606    if (len) {
1607        ret = env->NewString((jchar*) uri.characters(), len);
1608        env->DeleteLocalRef(ret);
1609    }
1610    return ret;
1611}
1612
1613static jint nativeFocusCandidateFramePointer(JNIEnv *env, jobject obj)
1614{
1615    WebView* view = GET_NATIVE_VIEW(env, obj);
1616    CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
1617    if (!root)
1618        return 0;
1619    const CachedFrame* frame = 0;
1620    const CachedNode* cursor = root->currentCursor(&frame);
1621    if (!cursor || !cursor->wantsKeyEvents())
1622        (void) root->currentFocus(&frame);
1623    return reinterpret_cast<int>(frame ? frame->framePointer() : 0);
1624}
1625
1626static bool nativeFocusCandidateIsPassword(JNIEnv *env, jobject obj)
1627{
1628    const CachedInput* input = getInputCandidate(env, obj);
1629    return input && input->inputType() == WebCore::HTMLInputElement::PASSWORD;
1630}
1631
1632static bool nativeFocusCandidateIsRtlText(JNIEnv *env, jobject obj)
1633{
1634    const CachedInput* input = getInputCandidate(env, obj);
1635    return input ? input->isRtlText() : false;
1636}
1637
1638static bool nativeFocusCandidateIsTextInput(JNIEnv *env, jobject obj)
1639{
1640    const CachedNode* node = getFocusCandidate(env, obj, 0);
1641    return node ? node->isTextInput() : false;
1642}
1643
1644static jint nativeFocusCandidateMaxLength(JNIEnv *env, jobject obj)
1645{
1646    const CachedInput* input = getInputCandidate(env, obj);
1647    return input ? input->maxLength() : false;
1648}
1649
1650static jobject nativeFocusCandidateName(JNIEnv *env, jobject obj)
1651{
1652    const CachedInput* input = getInputCandidate(env, obj);
1653    if (!input)
1654        return 0;
1655    const WTF::String& name = input->name();
1656    return env->NewString((jchar*)name.characters(), name.length());
1657}
1658
1659static jobject nativeFocusCandidateNodeBounds(JNIEnv *env, jobject obj)
1660{
1661    const CachedFrame* frame;
1662    const CachedNode* node = getFocusCandidate(env, obj, &frame);
1663    WebCore::IntRect bounds = node ? node->bounds(frame)
1664        : WebCore::IntRect(0, 0, 0, 0);
1665    jclass rectClass = env->FindClass("android/graphics/Rect");
1666    jmethodID init = env->GetMethodID(rectClass, "<init>", "(IIII)V");
1667    jobject rect = env->NewObject(rectClass, init, bounds.x(),
1668        bounds.y(), bounds.right(), bounds.bottom());
1669    return rect;
1670}
1671
1672static jint nativeFocusCandidatePointer(JNIEnv *env, jobject obj)
1673{
1674    const CachedNode* node = getFocusCandidate(env, obj, 0);
1675    return reinterpret_cast<int>(node ? node->nodePointer() : 0);
1676}
1677
1678static jobject nativeFocusCandidateText(JNIEnv *env, jobject obj)
1679{
1680    const CachedNode* node = getFocusCandidate(env, obj, 0);
1681    if (!node)
1682        return 0;
1683    WTF::String value = node->getExport();
1684    return !value.isEmpty() ? env->NewString((jchar *)value.characters(),
1685        value.length()) : 0;
1686}
1687
1688static jint nativeFocusCandidateTextSize(JNIEnv *env, jobject obj)
1689{
1690    const CachedInput* input = getInputCandidate(env, obj);
1691    return input ? input->textSize() : 0;
1692}
1693
1694enum type {
1695    NONE = -1,
1696    NORMAL_TEXT_FIELD = 0,
1697    TEXT_AREA = 1,
1698    PASSWORD = 2,
1699    SEARCH = 3,
1700    EMAIL = 4,
1701    NUMBER = 5,
1702    TELEPHONE = 6,
1703    URL = 7
1704};
1705
1706static int nativeFocusCandidateType(JNIEnv *env, jobject obj)
1707{
1708    const CachedInput* input = getInputCandidate(env, obj);
1709    if (!input) return NONE;
1710    if (!input->isTextField()) return TEXT_AREA;
1711    switch (input->inputType()) {
1712    case HTMLInputElement::PASSWORD:
1713        return PASSWORD;
1714    case HTMLInputElement::SEARCH:
1715        return SEARCH;
1716    case HTMLInputElement::EMAIL:
1717        return EMAIL;
1718    case HTMLInputElement::NUMBER:
1719        return NUMBER;
1720    case HTMLInputElement::TELEPHONE:
1721        return TELEPHONE;
1722    case HTMLInputElement::URL:
1723        return URL;
1724    default:
1725        return NORMAL_TEXT_FIELD;
1726    }
1727}
1728
1729static bool nativeFocusIsPlugin(JNIEnv *env, jobject obj)
1730{
1731    const CachedNode* node = getFocusNode(env, obj);
1732    return node ? node->isPlugin() : false;
1733}
1734
1735static jobject nativeFocusNodeBounds(JNIEnv *env, jobject obj)
1736{
1737    const CachedFrame* frame;
1738    const CachedNode* node = getFocusNode(env, obj, &frame);
1739    WebCore::IntRect bounds = node ? node->bounds(frame)
1740        : WebCore::IntRect(0, 0, 0, 0);
1741    jclass rectClass = env->FindClass("android/graphics/Rect");
1742    jmethodID init = env->GetMethodID(rectClass, "<init>", "(IIII)V");
1743    jobject rect = env->NewObject(rectClass, init, bounds.x(),
1744        bounds.y(), bounds.right(), bounds.bottom());
1745    return rect;
1746}
1747
1748static jint nativeFocusNodePointer(JNIEnv *env, jobject obj)
1749{
1750    const CachedNode* node = getFocusNode(env, obj);
1751    return node ? reinterpret_cast<int>(node->nodePointer()) : 0;
1752}
1753
1754static bool nativeCursorWantsKeyEvents(JNIEnv* env, jobject jwebview) {
1755    WebView* view = GET_NATIVE_VIEW(env, jwebview);
1756    LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
1757    return view->cursorWantsKeyEvents();
1758}
1759
1760static void nativeHideCursor(JNIEnv *env, jobject obj)
1761{
1762    WebView* view = GET_NATIVE_VIEW(env, obj);
1763    LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
1764    view->hideCursor();
1765}
1766
1767static void nativeInstrumentReport(JNIEnv *env, jobject obj)
1768{
1769#ifdef ANDROID_INSTRUMENT
1770    TimeCounter::reportNow();
1771#endif
1772}
1773
1774static void nativeSelectBestAt(JNIEnv *env, jobject obj, jobject jrect)
1775{
1776    WebView* view = GET_NATIVE_VIEW(env, obj);
1777    LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
1778    WebCore::IntRect rect = jrect_to_webrect(env, jrect);
1779    view->selectBestAt(rect);
1780}
1781
1782static jobject nativeSubtractLayers(JNIEnv* env, jobject obj, jobject jrect)
1783{
1784    SkIRect irect = jrect_to_webrect(env, jrect);
1785#if USE(ACCELERATED_COMPOSITING)
1786    LayerAndroid* root = GET_NATIVE_VIEW(env, obj)->compositeRoot();
1787    if (root) {
1788        SkRect rect;
1789        rect.set(irect);
1790        rect = root->subtractLayers(rect);
1791        rect.round(&irect);
1792    }
1793#endif
1794    jclass rectClass = env->FindClass("android/graphics/Rect");
1795    jmethodID init = env->GetMethodID(rectClass, "<init>", "(IIII)V");
1796    return env->NewObject(rectClass, init, irect.fLeft, irect.fTop,
1797        irect.fRight, irect.fBottom);
1798}
1799
1800static jint nativeTextGeneration(JNIEnv *env, jobject obj)
1801{
1802    WebView* view = GET_NATIVE_VIEW(env, obj);
1803    CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
1804    return root ? root->textGeneration() : 0;
1805}
1806
1807static bool nativePointInNavCache(JNIEnv *env, jobject obj,
1808    int x, int y, int slop)
1809{
1810    return GET_NATIVE_VIEW(env, obj)->pointInNavCache(x, y, slop);
1811}
1812
1813static bool nativeMotionUp(JNIEnv *env, jobject obj,
1814    int x, int y, int slop)
1815{
1816    WebView* view = GET_NATIVE_VIEW(env, obj);
1817    LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
1818    return view->motionUp(x, y, slop);
1819}
1820
1821static bool nativeHasCursorNode(JNIEnv *env, jobject obj)
1822{
1823    return GET_NATIVE_VIEW(env, obj)->hasCursorNode();
1824}
1825
1826static bool nativeHasFocusNode(JNIEnv *env, jobject obj)
1827{
1828    return GET_NATIVE_VIEW(env, obj)->hasFocusNode();
1829}
1830
1831static bool nativeMoveCursor(JNIEnv *env, jobject obj,
1832    int key, int count, bool ignoreScroll)
1833{
1834    WebView* view = GET_NATIVE_VIEW(env, obj);
1835    DBG_NAV_LOGD("env=%p obj=%p view=%p", env, obj, view);
1836    LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
1837    return view->moveCursor(key, count, ignoreScroll);
1838}
1839
1840static void nativeRecordButtons(JNIEnv* env, jobject obj, bool hasFocus,
1841        bool pressed, bool invalidate)
1842{
1843    WebView* view = GET_NATIVE_VIEW(env, obj);
1844    LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
1845    view->nativeRecordButtons(hasFocus, pressed, invalidate);
1846}
1847
1848static void nativeSetFindIsUp(JNIEnv *env, jobject obj, jboolean isUp)
1849{
1850    WebView* view = GET_NATIVE_VIEW(env, obj);
1851    LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
1852    view->setFindIsUp(isUp);
1853}
1854
1855static void nativeSetFindIsEmpty(JNIEnv *env, jobject obj)
1856{
1857    GET_NATIVE_VIEW(env, obj)->setFindIsEmpty();
1858}
1859
1860static void nativeShowCursorTimed(JNIEnv *env, jobject obj)
1861{
1862    GET_NATIVE_VIEW(env, obj)->showCursorTimed();
1863}
1864
1865static void nativeSetHeightCanMeasure(JNIEnv *env, jobject obj, bool measure)
1866{
1867    WebView* view = GET_NATIVE_VIEW(env, obj);
1868    LOG_ASSERT(view, "view not set in nativeSetHeightCanMeasure");
1869    view->setHeightCanMeasure(measure);
1870}
1871
1872static jobject nativeGetCursorRingBounds(JNIEnv *env, jobject obj)
1873{
1874    WebView* view = GET_NATIVE_VIEW(env, obj);
1875    LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
1876    jclass rectClass = env->FindClass("android/graphics/Rect");
1877    LOG_ASSERT(rectClass, "Could not find Rect class!");
1878    jmethodID init = env->GetMethodID(rectClass, "<init>", "(IIII)V");
1879    LOG_ASSERT(init, "Could not find constructor for Rect");
1880    WebCore::IntRect webRect;
1881    view->cursorRingBounds(&webRect);
1882    jobject rect = env->NewObject(rectClass, init, webRect.x(),
1883        webRect.y(), webRect.right(), webRect.bottom());
1884    return rect;
1885}
1886
1887static int nativeFindAll(JNIEnv *env, jobject obj, jstring findLower,
1888        jstring findUpper)
1889{
1890    // If one or the other is null, do not search.
1891    if (!(findLower && findUpper))
1892        return 0;
1893    // Obtain the characters for both the lower case string and the upper case
1894    // string representing the same word.
1895    const jchar* findLowerChars = env->GetStringChars(findLower, 0);
1896    const jchar* findUpperChars = env->GetStringChars(findUpper, 0);
1897    // If one or the other is null, do not search.
1898    if (!(findLowerChars && findUpperChars)) {
1899        if (findLowerChars)
1900            env->ReleaseStringChars(findLower, findLowerChars);
1901        if (findUpperChars)
1902            env->ReleaseStringChars(findUpper, findUpperChars);
1903        checkException(env);
1904        return 0;
1905    }
1906    WebView* view = GET_NATIVE_VIEW(env, obj);
1907    LOG_ASSERT(view, "view not set in nativeFindAll");
1908    CachedRoot* root = view->getFrameCache(WebView::AllowNewer);
1909    if (!root) {
1910        env->ReleaseStringChars(findLower, findLowerChars);
1911        env->ReleaseStringChars(findUpper, findUpperChars);
1912        checkException(env);
1913        return 0;
1914    }
1915    int length = env->GetStringLength(findLower);
1916    // If the lengths of the strings do not match, then they are not the same
1917    // word, so do not search.
1918    if (!length || env->GetStringLength(findUpper) != length) {
1919        env->ReleaseStringChars(findLower, findLowerChars);
1920        env->ReleaseStringChars(findUpper, findUpperChars);
1921        checkException(env);
1922        return 0;
1923    }
1924    int width = root->documentWidth();
1925    int height = root->documentHeight();
1926    // Create a FindCanvas, which allows us to fake draw into it so we can
1927    // figure out where our search string is rendered (and how many times).
1928    FindCanvas canvas(width, height, (const UChar*) findLowerChars,
1929            (const UChar*) findUpperChars, length << 1);
1930    SkBitmap bitmap;
1931    bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
1932    canvas.setBitmapDevice(bitmap);
1933    root->draw(canvas);
1934    WTF::Vector<MatchInfo>* matches = canvas.detachMatches();
1935    // With setMatches, the WebView takes ownership of matches
1936    view->setMatches(matches);
1937
1938    env->ReleaseStringChars(findLower, findLowerChars);
1939    env->ReleaseStringChars(findUpper, findUpperChars);
1940    checkException(env);
1941    return canvas.found();
1942}
1943
1944static void nativeFindNext(JNIEnv *env, jobject obj, bool forward)
1945{
1946    WebView* view = GET_NATIVE_VIEW(env, obj);
1947    LOG_ASSERT(view, "view not set in nativeFindNext");
1948    view->findNext(forward);
1949}
1950
1951static int nativeFindIndex(JNIEnv *env, jobject obj)
1952{
1953    WebView* view = GET_NATIVE_VIEW(env, obj);
1954    LOG_ASSERT(view, "view not set in nativeFindIndex");
1955    return view->currentMatchIndex();
1956}
1957
1958static void nativeUpdateCachedTextfield(JNIEnv *env, jobject obj, jstring updatedText, jint generation)
1959{
1960    WebView* view = GET_NATIVE_VIEW(env, obj);
1961    LOG_ASSERT(view, "view not set in nativeUpdateCachedTextfield");
1962    CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
1963    if (!root)
1964        return;
1965    const CachedNode* cachedFocusNode = root->currentFocus();
1966    if (!cachedFocusNode || !cachedFocusNode->isTextInput())
1967        return;
1968    WTF::String webcoreString = to_string(env, updatedText);
1969    (const_cast<CachedNode*>(cachedFocusNode))->setExport(webcoreString);
1970    root->setTextGeneration(generation);
1971    checkException(env);
1972}
1973
1974static jint nativeGetBlockLeftEdge(JNIEnv *env, jobject obj, jint x, jint y,
1975        jfloat scale)
1976{
1977    WebView* view = GET_NATIVE_VIEW(env, obj);
1978    LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
1979    if (!view)
1980        return -1;
1981    return view->getBlockLeftEdge(x, y, scale);
1982}
1983
1984static void nativeDestroy(JNIEnv *env, jobject obj)
1985{
1986    WebView* view = GET_NATIVE_VIEW(env, obj);
1987    LOGD("nativeDestroy view: %p", view);
1988    LOG_ASSERT(view, "view not set in nativeDestroy");
1989    delete view;
1990}
1991
1992static bool nativeMoveCursorToNextTextInput(JNIEnv *env, jobject obj)
1993{
1994    WebView* view = GET_NATIVE_VIEW(env, obj);
1995    CachedRoot* root = view->getFrameCache(WebView::DontAllowNewer);
1996    if (!root)
1997        return false;
1998    const CachedNode* current = root->currentCursor();
1999    if (!current || !current->isTextInput())
2000        current = root->currentFocus();
2001    if (!current || !current->isTextInput())
2002        return false;
2003    const CachedFrame* frame;
2004    const CachedNode* next = root->nextTextField(current, &frame);
2005    if (!next)
2006        return false;
2007    const WebCore::IntRect& bounds = next->bounds(frame);
2008    root->rootHistory()->setMouseBounds(frame->unadjustBounds(next, bounds));
2009    view->getWebViewCore()->updateCursorBounds(root, frame, next);
2010    view->showCursorUntimed();
2011    root->setCursor(const_cast<CachedFrame*>(frame),
2012            const_cast<CachedNode*>(next));
2013    view->sendMoveFocus(static_cast<WebCore::Frame*>(frame->framePointer()),
2014            static_cast<WebCore::Node*>(next->nodePointer()));
2015    if (!next->isInLayer())
2016        view->scrollRectOnScreen(bounds);
2017    view->getWebViewCore()->m_moveGeneration++;
2018    return true;
2019}
2020
2021static int nativeMoveGeneration(JNIEnv *env, jobject obj)
2022{
2023    WebView* view = GET_NATIVE_VIEW(env, obj);
2024    if (!view)
2025        return 0;
2026    return view->moveGeneration();
2027}
2028
2029static void nativeMoveSelection(JNIEnv *env, jobject obj, int x, int y)
2030{
2031    GET_NATIVE_VIEW(env, obj)->moveSelection(x, y);
2032}
2033
2034static jboolean nativeCleanupPrivateBrowsingFiles(
2035        JNIEnv *env, jobject obj, jstring databaseDirectoryJString, jstring cacheDirectoryJString) {
2036#if USE(CHROME_NETWORK_STACK)
2037    jboolean isCopy;
2038    const char* cString = env->GetStringUTFChars(databaseDirectoryJString, &isCopy);
2039    std::string databaseDirectory(cString);
2040    if (isCopy == JNI_TRUE)
2041        env->ReleaseStringUTFChars(databaseDirectoryJString, cString);
2042    cString = env->GetStringUTFChars(cacheDirectoryJString, &isCopy);
2043    std::string cacheDirectory(cString);
2044    if (isCopy == JNI_TRUE)
2045        env->ReleaseStringUTFChars(cacheDirectoryJString, cString);
2046    return WebRequestContext::CleanupPrivateBrowsingFiles(databaseDirectory, cacheDirectory);
2047#else
2048    return JNI_FALSE;
2049#endif
2050}
2051
2052static void nativeResetSelection(JNIEnv *env, jobject obj)
2053{
2054    return GET_NATIVE_VIEW(env, obj)->resetSelection();
2055}
2056
2057static void nativeSelectAll(JNIEnv* env, jobject obj)
2058{
2059    GET_NATIVE_VIEW(env, obj)->selectAll();
2060}
2061
2062static void nativeSetExtendSelection(JNIEnv *env, jobject obj)
2063{
2064    GET_NATIVE_VIEW(env, obj)->setExtendSelection();
2065}
2066
2067static jboolean nativeStartSelection(JNIEnv *env, jobject obj, int x, int y)
2068{
2069    return GET_NATIVE_VIEW(env, obj)->startSelection(x, y);
2070}
2071
2072static jboolean nativeWordSelection(JNIEnv *env, jobject obj, int x, int y)
2073{
2074    return GET_NATIVE_VIEW(env, obj)->wordSelection(x, y);
2075}
2076
2077static void nativeExtendSelection(JNIEnv *env, jobject obj, int x, int y)
2078{
2079    GET_NATIVE_VIEW(env, obj)->extendSelection(x, y);
2080}
2081
2082static jobject nativeGetSelection(JNIEnv *env, jobject obj)
2083{
2084    WebView* view = GET_NATIVE_VIEW(env, obj);
2085    LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
2086    String selection = view->getSelection();
2087    return env->NewString((jchar*)selection.characters(), selection.length());
2088}
2089
2090static jboolean nativeHitSelection(JNIEnv *env, jobject obj, int x, int y)
2091{
2092    return GET_NATIVE_VIEW(env, obj)->hitSelection(x, y);
2093}
2094
2095static jint nativeSelectionX(JNIEnv *env, jobject obj)
2096{
2097    return GET_NATIVE_VIEW(env, obj)->selectionX();
2098}
2099
2100static jint nativeSelectionY(JNIEnv *env, jobject obj)
2101{
2102    return GET_NATIVE_VIEW(env, obj)->selectionY();
2103}
2104
2105static void nativeSetSelectionPointer(JNIEnv *env, jobject obj, jboolean set,
2106    jfloat scale, jint x, jint y)
2107{
2108    GET_NATIVE_VIEW(env, obj)->setSelectionPointer(set, scale, x, y);
2109}
2110
2111#ifdef ANDROID_DUMP_DISPLAY_TREE
2112static void dumpToFile(const char text[], void* file) {
2113    fwrite(text, 1, strlen(text), reinterpret_cast<FILE*>(file));
2114    fwrite("\n", 1, 1, reinterpret_cast<FILE*>(file));
2115}
2116#endif
2117
2118static void nativeDumpDisplayTree(JNIEnv* env, jobject jwebview, jstring jurl)
2119{
2120#ifdef ANDROID_DUMP_DISPLAY_TREE
2121    WebView* view = GET_NATIVE_VIEW(env, jwebview);
2122    LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
2123
2124    if (view && view->getWebViewCore()) {
2125        FILE* file = fopen(DISPLAY_TREE_LOG_FILE, "w");
2126        if (file) {
2127            SkFormatDumper dumper(dumpToFile, file);
2128            // dump the URL
2129            if (jurl) {
2130                const char* str = env->GetStringUTFChars(jurl, 0);
2131                SkDebugf("Dumping %s to %s\n", str, DISPLAY_TREE_LOG_FILE);
2132                dumpToFile(str, file);
2133                env->ReleaseStringUTFChars(jurl, str);
2134            }
2135            // now dump the display tree
2136            SkDumpCanvas canvas(&dumper);
2137            // this will playback the picture into the canvas, which will
2138            // spew its contents to the dumper
2139            view->draw(&canvas, 0, 0, false);
2140            // we're done with the file now
2141            fwrite("\n", 1, 1, file);
2142            fclose(file);
2143        }
2144#if USE(ACCELERATED_COMPOSITING)
2145        const LayerAndroid* rootLayer = view->compositeRoot();
2146        if (rootLayer) {
2147          FILE* file = fopen(LAYERS_TREE_LOG_FILE,"w");
2148          if (file) {
2149              rootLayer->dumpLayers(file, 0);
2150              fclose(file);
2151          }
2152        }
2153#endif
2154    }
2155#endif
2156}
2157
2158static int nativeScrollableLayer(JNIEnv* env, jobject jwebview, jint x, jint y)
2159{
2160    WebView* view = GET_NATIVE_VIEW(env, jwebview);
2161    LOG_ASSERT(view, "view not set in %s", __FUNCTION__);
2162    return (int) view->scrollableLayer(x, y);
2163}
2164
2165static bool validLayer(const LayerAndroid* root, const LayerAndroid* layer) {
2166    if (root == layer)
2167        return true;
2168    for (int i = 0; i < root->countChildren(); i++) {
2169        const LayerAndroid* l = root->getChild(i);
2170        if (validLayer(l, layer))
2171            return true;
2172    }
2173    return false;
2174}
2175
2176static bool nativeScrollLayer(JNIEnv* env, jobject obj, jint pLayer, jint dx,
2177        jint dy)
2178{
2179#if ENABLE(ANDROID_OVERFLOW_SCROLL)
2180    WebView* view = GET_NATIVE_VIEW(env, obj);
2181    const LayerAndroid* root = view->compositeRoot();
2182    LayerAndroid* layer = (LayerAndroid*) pLayer;
2183    if (!validLayer(root, layer)) {
2184        return false;
2185    }
2186    LOG_ASSERT(layer, "layer not set in %s", __FUNCTION__);
2187    return layer->scrollBy(dx, dy);
2188#endif
2189    return false;
2190}
2191
2192/*
2193 * JNI registration
2194 */
2195static JNINativeMethod gJavaWebViewMethods[] = {
2196    { "nativeCacheHitFramePointer", "()I",
2197        (void*) nativeCacheHitFramePointer },
2198    { "nativeCacheHitNodeBounds", "()Landroid/graphics/Rect;",
2199        (void*) nativeCacheHitNodeBounds },
2200    { "nativeCacheHitNodePointer", "()I",
2201        (void*) nativeCacheHitNodePointer },
2202    { "nativeClearCursor", "()V",
2203        (void*) nativeClearCursor },
2204    { "nativeCreate", "(I)V",
2205        (void*) nativeCreate },
2206    { "nativeCursorFramePointer", "()I",
2207        (void*) nativeCursorFramePointer },
2208    { "nativePageShouldHandleShiftAndArrows", "()Z",
2209        (void*) nativePageShouldHandleShiftAndArrows },
2210    { "nativeCursorMatchesFocus", "()Z",
2211        (void*) nativeCursorMatchesFocus },
2212    { "nativeCursorNodeBounds", "()Landroid/graphics/Rect;",
2213        (void*) nativeCursorNodeBounds },
2214    { "nativeCursorNodePointer", "()I",
2215        (void*) nativeCursorNodePointer },
2216    { "nativeCursorIntersects", "(Landroid/graphics/Rect;)Z",
2217        (void*) nativeCursorIntersects },
2218    { "nativeCursorIsAnchor", "()Z",
2219        (void*) nativeCursorIsAnchor },
2220    { "nativeCursorIsTextInput", "()Z",
2221        (void*) nativeCursorIsTextInput },
2222    { "nativeCursorPosition", "()Landroid/graphics/Point;",
2223        (void*) nativeCursorPosition },
2224    { "nativeCursorText", "()Ljava/lang/String;",
2225        (void*) nativeCursorText },
2226    { "nativeCursorWantsKeyEvents", "()Z",
2227        (void*)nativeCursorWantsKeyEvents },
2228    { "nativeDebugDump", "()V",
2229        (void*) nativeDebugDump },
2230    { "nativeDestroy", "()V",
2231        (void*) nativeDestroy },
2232    { "nativeDraw", "(Landroid/graphics/Canvas;IIZ)I",
2233        (void*) nativeDraw },
2234    { "nativeDrawGL", "(Landroid/graphics/Rect;FI)Z",
2235        (void*) nativeDrawGL },
2236    { "nativeDumpDisplayTree", "(Ljava/lang/String;)V",
2237        (void*) nativeDumpDisplayTree },
2238    { "nativeEvaluateLayersAnimations", "()Z",
2239        (void*) nativeEvaluateLayersAnimations },
2240    { "nativeExtendSelection", "(II)V",
2241        (void*) nativeExtendSelection },
2242    { "nativeFindAll", "(Ljava/lang/String;Ljava/lang/String;)I",
2243        (void*) nativeFindAll },
2244    { "nativeFindNext", "(Z)V",
2245        (void*) nativeFindNext },
2246    { "nativeFindIndex", "()I",
2247        (void*) nativeFindIndex},
2248    { "nativeFocusCandidateFramePointer", "()I",
2249        (void*) nativeFocusCandidateFramePointer },
2250    { "nativeFocusCandidateHasNextTextfield", "()Z",
2251        (void*) focusCandidateHasNextTextfield },
2252    { "nativeFocusCandidateIsPassword", "()Z",
2253        (void*) nativeFocusCandidateIsPassword },
2254    { "nativeFocusCandidateIsRtlText", "()Z",
2255        (void*) nativeFocusCandidateIsRtlText },
2256    { "nativeFocusCandidateIsTextInput", "()Z",
2257        (void*) nativeFocusCandidateIsTextInput },
2258    { "nativeFocusCandidateMaxLength", "()I",
2259        (void*) nativeFocusCandidateMaxLength },
2260    { "nativeFocusCandidateName", "()Ljava/lang/String;",
2261        (void*) nativeFocusCandidateName },
2262    { "nativeFocusCandidateNodeBounds", "()Landroid/graphics/Rect;",
2263        (void*) nativeFocusCandidateNodeBounds },
2264    { "nativeFocusCandidatePointer", "()I",
2265        (void*) nativeFocusCandidatePointer },
2266    { "nativeFocusCandidateText", "()Ljava/lang/String;",
2267        (void*) nativeFocusCandidateText },
2268    { "nativeFocusCandidateTextSize", "()I",
2269        (void*) nativeFocusCandidateTextSize },
2270    { "nativeFocusCandidateType", "()I",
2271        (void*) nativeFocusCandidateType },
2272    { "nativeFocusIsPlugin", "()Z",
2273        (void*) nativeFocusIsPlugin },
2274    { "nativeFocusNodeBounds", "()Landroid/graphics/Rect;",
2275        (void*) nativeFocusNodeBounds },
2276    { "nativeFocusNodePointer", "()I",
2277        (void*) nativeFocusNodePointer },
2278    { "nativeGetCursorRingBounds", "()Landroid/graphics/Rect;",
2279        (void*) nativeGetCursorRingBounds },
2280    { "nativeGetSelection", "()Ljava/lang/String;",
2281        (void*) nativeGetSelection },
2282    { "nativeHasCursorNode", "()Z",
2283        (void*) nativeHasCursorNode },
2284    { "nativeHasFocusNode", "()Z",
2285        (void*) nativeHasFocusNode },
2286    { "nativeHideCursor", "()V",
2287        (void*) nativeHideCursor },
2288    { "nativeHitSelection", "(II)Z",
2289        (void*) nativeHitSelection },
2290    { "nativeImageURI", "(II)Ljava/lang/String;",
2291        (void*) nativeImageURI },
2292    { "nativeInstrumentReport", "()V",
2293        (void*) nativeInstrumentReport },
2294    { "nativeMotionUp", "(III)Z",
2295        (void*) nativeMotionUp },
2296    { "nativeMoveCursor", "(IIZ)Z",
2297        (void*) nativeMoveCursor },
2298    { "nativeMoveCursorToNextTextInput", "()Z",
2299        (void*) nativeMoveCursorToNextTextInput },
2300    { "nativeMoveGeneration", "()I",
2301        (void*) nativeMoveGeneration },
2302    { "nativeMoveSelection", "(II)V",
2303        (void*) nativeMoveSelection },
2304    { "nativeCleanupPrivateBrowsingFiles", "(Ljava/lang/String;Ljava/lang/String;)Z",
2305        (void*) nativeCleanupPrivateBrowsingFiles },
2306    { "nativePointInNavCache", "(III)Z",
2307        (void*) nativePointInNavCache },
2308    { "nativeRecordButtons", "(ZZZ)V",
2309        (void*) nativeRecordButtons },
2310    { "nativeResetSelection", "()V",
2311        (void*) nativeResetSelection },
2312    { "nativeSelectAll", "()V",
2313        (void*) nativeSelectAll },
2314    { "nativeSelectBestAt", "(Landroid/graphics/Rect;)V",
2315        (void*) nativeSelectBestAt },
2316    { "nativeSelectionX", "()I",
2317        (void*) nativeSelectionX },
2318    { "nativeSelectionY", "()I",
2319        (void*) nativeSelectionY },
2320    { "nativeSetExtendSelection", "()V",
2321        (void*) nativeSetExtendSelection },
2322    { "nativeSetFindIsEmpty", "()V",
2323        (void*) nativeSetFindIsEmpty },
2324    { "nativeSetFindIsUp", "(Z)V",
2325        (void*) nativeSetFindIsUp },
2326    { "nativeSetHeightCanMeasure", "(Z)V",
2327        (void*) nativeSetHeightCanMeasure },
2328    { "nativeSetBaseLayer", "(ILandroid/graphics/Rect;)V",
2329        (void*) nativeSetBaseLayer },
2330    { "nativeReplaceBaseContent", "(I)V",
2331        (void*) nativeReplaceBaseContent },
2332    { "nativeCopyBaseContentToPicture", "(Landroid/graphics/Picture;)V",
2333        (void*) nativeCopyBaseContentToPicture },
2334    { "nativeHasContent", "()Z",
2335        (void*) nativeHasContent },
2336    { "nativeSetSelectionPointer", "(ZFII)V",
2337        (void*) nativeSetSelectionPointer },
2338    { "nativeShowCursorTimed", "()V",
2339        (void*) nativeShowCursorTimed },
2340    { "nativeStartSelection", "(II)Z",
2341        (void*) nativeStartSelection },
2342    { "nativeSubtractLayers", "(Landroid/graphics/Rect;)Landroid/graphics/Rect;",
2343        (void*) nativeSubtractLayers },
2344    { "nativeTextGeneration", "()I",
2345        (void*) nativeTextGeneration },
2346    { "nativeUpdateCachedTextfield", "(Ljava/lang/String;I)V",
2347        (void*) nativeUpdateCachedTextfield },
2348    {  "nativeWordSelection", "(II)Z",
2349        (void*) nativeWordSelection },
2350    { "nativeGetBlockLeftEdge", "(IIF)I",
2351        (void*) nativeGetBlockLeftEdge },
2352    { "nativeScrollableLayer", "(II)I",
2353        (void*) nativeScrollableLayer },
2354    { "nativeScrollLayer", "(III)Z",
2355        (void*) nativeScrollLayer },
2356};
2357
2358int register_webview(JNIEnv* env)
2359{
2360    jclass clazz = env->FindClass("android/webkit/WebView");
2361    LOG_ASSERT(clazz, "Unable to find class android/webkit/WebView");
2362    gWebViewField = env->GetFieldID(clazz, "mNativeClass", "I");
2363    LOG_ASSERT(gWebViewField, "Unable to find android/webkit/WebView.mNativeClass");
2364
2365    return jniRegisterNativeMethods(env, "android/webkit/WebView", gJavaWebViewMethods, NELEM(gJavaWebViewMethods));
2366}
2367
2368} // namespace android
2369