OSDN Git Service

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