WebViewCore.cpp revision 9bce99defc63ccfabdb05a4a0fdb43d4c6447c54
1/*
2 * Copyright 2006, 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 "webcoreglue"
27
28#include "config.h"
29#include "WebViewCore.h"
30
31#include "AccessibilityObject.h"
32#include "Attribute.h"
33#include "BaseLayerAndroid.h"
34#include "CachedNode.h"
35#include "CachedRoot.h"
36#include "Chrome.h"
37#include "ChromeClientAndroid.h"
38#include "ChromiumIncludes.h"
39#include "ClientRect.h"
40#include "ClientRectList.h"
41#include "Color.h"
42#include "CSSPropertyNames.h"
43#include "CSSValueKeywords.h"
44#include "DatabaseTracker.h"
45#include "Document.h"
46#include "DOMWindow.h"
47#include "DOMSelection.h"
48#include "Element.h"
49#include "Editor.h"
50#include "EditorClientAndroid.h"
51#include "EventHandler.h"
52#include "EventNames.h"
53#include "ExceptionCode.h"
54#include "FocusController.h"
55#include "Font.h"
56#include "Frame.h"
57#include "FrameLoader.h"
58#include "FrameLoaderClientAndroid.h"
59#include "FrameTree.h"
60#include "FrameView.h"
61#include "Geolocation.h"
62#include "GraphicsContext.h"
63#include "GraphicsJNI.h"
64#include "HTMLAnchorElement.h"
65#include "HTMLAreaElement.h"
66#include "HTMLElement.h"
67#include "HTMLFormControlElement.h"
68#include "HTMLImageElement.h"
69#include "HTMLInputElement.h"
70#include "HTMLLabelElement.h"
71#include "HTMLMapElement.h"
72#include "HTMLNames.h"
73#include "HTMLOptGroupElement.h"
74#include "HTMLOptionElement.h"
75#include "HTMLSelectElement.h"
76#include "HTMLTextAreaElement.h"
77#include "HistoryItem.h"
78#include "HitTestRequest.h"
79#include "HitTestResult.h"
80#include "InlineTextBox.h"
81#include "MemoryUsage.h"
82#include "NamedNodeMap.h"
83#include "Navigator.h"
84#include "Node.h"
85#include "NodeList.h"
86#include "Page.h"
87#include "PageGroup.h"
88#include "PlatformKeyboardEvent.h"
89#include "PlatformString.h"
90#include "PluginWidgetAndroid.h"
91#include "PluginView.h"
92#include "Position.h"
93#include "ProgressTracker.h"
94#include "Range.h"
95#include "RenderBox.h"
96#include "RenderInline.h"
97#include "RenderLayer.h"
98#include "RenderPart.h"
99#include "RenderText.h"
100#include "RenderTextControl.h"
101#include "RenderThemeAndroid.h"
102#include "RenderView.h"
103#include "ResourceRequest.h"
104#include "RuntimeEnabledFeatures.h"
105#include "SchemeRegistry.h"
106#include "SelectionController.h"
107#include "Settings.h"
108#include "SkANP.h"
109#include "SkTemplates.h"
110#include "SkTDArray.h"
111#include "SkTypes.h"
112#include "SkCanvas.h"
113#include "SkPicture.h"
114#include "SkUtils.h"
115#include "Text.h"
116#include "TypingCommand.h"
117#include "WebCache.h"
118#include "WebCoreFrameBridge.h"
119#include "WebFrameView.h"
120#include "WindowsKeyboardCodes.h"
121#include "android_graphics.h"
122#include "autofill/WebAutofill.h"
123#include "htmlediting.h"
124#include "markup.h"
125
126#include <JNIHelp.h>
127#include <JNIUtility.h>
128#include <ui/KeycodeLabels.h>
129#include <wtf/CurrentTime.h>
130#include <wtf/text/AtomicString.h>
131#include <wtf/text/StringImpl.h>
132
133#if USE(V8)
134#include "ScriptController.h"
135#include <wtf/text/CString.h>
136#endif
137
138#if DEBUG_NAV_UI
139#include "SkTime.h"
140#endif
141
142#if ENABLE(TOUCH_EVENTS) // Android
143#include "PlatformTouchEvent.h"
144#endif
145
146#ifdef ANDROID_DOM_LOGGING
147#include "AndroidLog.h"
148#include "RenderTreeAsText.h"
149#include <wtf/text/CString.h>
150
151FILE* gDomTreeFile = 0;
152FILE* gRenderTreeFile = 0;
153#endif
154
155#if USE(ACCELERATED_COMPOSITING)
156#include "GraphicsLayerAndroid.h"
157#include "RenderLayerCompositor.h"
158#endif
159
160#if USE(V8)
161#include <v8.h>
162#endif
163
164// In some cases, too many invalidations passed to the UI will slow us down.
165// Limit ourselves to 32 rectangles, past this just send the area bounds to the UI.
166// see WebViewCore::recordPictureSet().
167#define MAX_INVALIDATIONS 32
168
169/*  We pass this flag when recording the actual content, so that we don't spend
170    time actually regionizing complex path clips, when all we really want to do
171    is record them.
172 */
173#define PICT_RECORD_FLAGS   SkPicture::kUsePathBoundsForClip_RecordingFlag
174
175////////////////////////////////////////////////////////////////////////////////////////////////
176
177namespace android {
178
179static SkTDArray<WebViewCore*> gInstanceList;
180
181void WebViewCore::addInstance(WebViewCore* inst) {
182    *gInstanceList.append() = inst;
183}
184
185void WebViewCore::removeInstance(WebViewCore* inst) {
186    int index = gInstanceList.find(inst);
187    ALOG_ASSERT(index >= 0, "RemoveInstance inst not found");
188    if (index >= 0) {
189        gInstanceList.removeShuffle(index);
190    }
191}
192
193bool WebViewCore::isInstance(WebViewCore* inst) {
194    return gInstanceList.find(inst) >= 0;
195}
196
197jobject WebViewCore::getApplicationContext() {
198
199    // check to see if there is a valid webviewcore object
200    if (gInstanceList.isEmpty())
201        return 0;
202
203    // get the context from the webview
204    jobject context = gInstanceList[0]->getContext();
205
206    if (!context)
207        return 0;
208
209    // get the application context using JNI
210    JNIEnv* env = JSC::Bindings::getJNIEnv();
211    jclass contextClass = env->GetObjectClass(context);
212    jmethodID appContextMethod = env->GetMethodID(contextClass, "getApplicationContext", "()Landroid/content/Context;");
213    env->DeleteLocalRef(contextClass);
214    jobject result = env->CallObjectMethod(context, appContextMethod);
215    checkException(env);
216    return result;
217}
218
219
220struct WebViewCoreStaticMethods {
221    jmethodID    m_isSupportedMediaMimeType;
222} gWebViewCoreStaticMethods;
223
224// Check whether a media mimeType is supported in Android media framework.
225bool WebViewCore::isSupportedMediaMimeType(const WTF::String& mimeType) {
226    JNIEnv* env = JSC::Bindings::getJNIEnv();
227    jstring jMimeType = wtfStringToJstring(env, mimeType);
228    jclass webViewCore = env->FindClass("android/webkit/WebViewCore");
229    bool val = env->CallStaticBooleanMethod(webViewCore,
230          gWebViewCoreStaticMethods.m_isSupportedMediaMimeType, jMimeType);
231    checkException(env);
232    env->DeleteLocalRef(webViewCore);
233    env->DeleteLocalRef(jMimeType);
234
235    return val;
236}
237
238// ----------------------------------------------------------------------------
239
240// Field ids for WebViewCore
241struct WebViewCoreFields {
242    jfieldID    m_nativeClass;
243    jfieldID    m_viewportWidth;
244    jfieldID    m_viewportHeight;
245    jfieldID    m_viewportInitialScale;
246    jfieldID    m_viewportMinimumScale;
247    jfieldID    m_viewportMaximumScale;
248    jfieldID    m_viewportUserScalable;
249    jfieldID    m_viewportDensityDpi;
250    jfieldID    m_webView;
251    jfieldID    m_drawIsPaused;
252    jfieldID    m_lowMemoryUsageMb;
253    jfieldID    m_highMemoryUsageMb;
254    jfieldID    m_highUsageDeltaMb;
255} gWebViewCoreFields;
256
257// ----------------------------------------------------------------------------
258
259struct WebViewCore::JavaGlue {
260    jweak       m_obj;
261    jmethodID   m_scrollTo;
262    jmethodID   m_contentDraw;
263    jmethodID   m_layersDraw;
264    jmethodID   m_requestListBox;
265    jmethodID   m_openFileChooser;
266    jmethodID   m_requestSingleListBox;
267    jmethodID   m_jsAlert;
268    jmethodID   m_jsConfirm;
269    jmethodID   m_jsPrompt;
270    jmethodID   m_jsUnload;
271    jmethodID   m_jsInterrupt;
272    jmethodID   m_didFirstLayout;
273    jmethodID   m_updateViewport;
274    jmethodID   m_sendNotifyProgressFinished;
275    jmethodID   m_sendViewInvalidate;
276    jmethodID   m_updateTextfield;
277    jmethodID   m_updateTextSelection;
278    jmethodID   m_clearTextEntry;
279    jmethodID   m_restoreScale;
280    jmethodID   m_needTouchEvents;
281    jmethodID   m_requestKeyboard;
282    jmethodID   m_requestKeyboardWithSelection;
283    jmethodID   m_exceededDatabaseQuota;
284    jmethodID   m_reachedMaxAppCacheSize;
285    jmethodID   m_populateVisitedLinks;
286    jmethodID   m_geolocationPermissionsShowPrompt;
287    jmethodID   m_geolocationPermissionsHidePrompt;
288    jmethodID   m_getDeviceMotionService;
289    jmethodID   m_getDeviceOrientationService;
290    jmethodID   m_addMessageToConsole;
291    jmethodID   m_formDidBlur;
292    jmethodID   m_getPluginClass;
293    jmethodID   m_showFullScreenPlugin;
294    jmethodID   m_hideFullScreenPlugin;
295    jmethodID   m_createSurface;
296    jmethodID   m_addSurface;
297    jmethodID   m_updateSurface;
298    jmethodID   m_destroySurface;
299    jmethodID   m_getContext;
300    jmethodID   m_keepScreenOn;
301    jmethodID   m_sendFindAgain;
302    jmethodID   m_showRect;
303    jmethodID   m_centerFitRect;
304    jmethodID   m_setScrollbarModes;
305    jmethodID   m_setInstallableWebApp;
306    jmethodID   m_enterFullscreenForVideoLayer;
307    jmethodID   m_setWebTextViewAutoFillable;
308    jmethodID   m_selectAt;
309    AutoJObject object(JNIEnv* env) {
310        // We hold a weak reference to the Java WebViewCore to avoid memeory
311        // leaks due to circular references when WebView.destroy() is not
312        // called manually. The WebView and hence the WebViewCore could become
313        // weakly reachable at any time, after which the GC could null our weak
314        // reference, so we have to check the return value of this method at
315        // every use. Note that our weak reference will be nulled before the
316        // WebViewCore is finalized.
317        return getRealObject(env, m_obj);
318    }
319};
320
321/*
322 * WebViewCore Implementation
323 */
324
325static jmethodID GetJMethod(JNIEnv* env, jclass clazz, const char name[], const char signature[])
326{
327    jmethodID m = env->GetMethodID(clazz, name, signature);
328    ALOG_ASSERT(m, "Could not find method %s", name);
329    return m;
330}
331
332Mutex WebViewCore::gFrameCacheMutex;
333Mutex WebViewCore::gCursorBoundsMutex;
334
335WebViewCore::WebViewCore(JNIEnv* env, jobject javaWebViewCore, WebCore::Frame* mainframe)
336    : m_frameCacheKit(0)
337    , m_navPictureKit(0)
338    , m_moveGeneration(0)
339    , m_touchGeneration(0)
340    , m_lastGeneration(0)
341    , m_updatedFrameCache(true)
342    , m_findIsUp(false)
343    , m_hasCursorBounds(false)
344    , m_cursorBounds(WebCore::IntRect(0, 0, 0, 0))
345    , m_cursorHitBounds(WebCore::IntRect(0, 0, 0, 0))
346    , m_cursorFrame(0)
347    , m_cursorLocation(WebCore::IntPoint(0, 0))
348    , m_cursorNode(0)
349    , m_javaGlue(new JavaGlue)
350    , m_mainFrame(mainframe)
351    , m_popupReply(0)
352    , m_lastFocused(0)
353    , m_lastFocusedBounds(WebCore::IntRect(0,0,0,0))
354    , m_blurringNodePointer(0)
355    , m_lastFocusedSelStart(0)
356    , m_lastFocusedSelEnd(0)
357    , m_blockTextfieldUpdates(false)
358    , m_focusBoundsChanged(false)
359    , m_skipContentDraw(false)
360    , m_textGeneration(0)
361    , m_temp(0)
362    , m_tempPict(0)
363    , m_maxXScroll(320/4)
364    , m_maxYScroll(240/4)
365    , m_scrollOffsetX(0)
366    , m_scrollOffsetY(0)
367    , m_mousePos(WebCore::IntPoint(0,0))
368    , m_frameCacheOutOfDate(true)
369    , m_progressDone(false)
370    , m_screenWidth(320)
371    , m_screenHeight(240)
372    , m_textWrapWidth(320)
373    , m_scale(1.0f)
374    , m_domtree_version(0)
375    , m_check_domtree_version(true)
376    , m_groupForVisitedLinks(0)
377    , m_isPaused(false)
378    , m_cacheMode(0)
379    , m_shouldPaintCaret(true)
380    , m_pluginInvalTimer(this, &WebViewCore::pluginInvalTimerFired)
381    , m_screenOnCounter(0)
382    , m_currentNodeDomNavigationAxis(0)
383    , m_deviceMotionAndOrientationManager(this)
384#if ENABLE(TOUCH_EVENTS)
385    , m_forwardingTouchEvents(false)
386#endif
387#if USE(CHROME_NETWORK_STACK)
388    , m_webRequestContext(0)
389#endif
390{
391    ALOG_ASSERT(m_mainFrame, "Uh oh, somehow a frameview was made without an initial frame!");
392
393    jclass clazz = env->GetObjectClass(javaWebViewCore);
394    m_javaGlue->m_obj = env->NewWeakGlobalRef(javaWebViewCore);
395    m_javaGlue->m_scrollTo = GetJMethod(env, clazz, "contentScrollTo", "(IIZZ)V");
396    m_javaGlue->m_contentDraw = GetJMethod(env, clazz, "contentDraw", "()V");
397    m_javaGlue->m_layersDraw = GetJMethod(env, clazz, "layersDraw", "()V");
398    m_javaGlue->m_requestListBox = GetJMethod(env, clazz, "requestListBox", "([Ljava/lang/String;[I[I)V");
399    m_javaGlue->m_openFileChooser = GetJMethod(env, clazz, "openFileChooser", "(Ljava/lang/String;)Ljava/lang/String;");
400    m_javaGlue->m_requestSingleListBox = GetJMethod(env, clazz, "requestListBox", "([Ljava/lang/String;[II)V");
401    m_javaGlue->m_jsAlert = GetJMethod(env, clazz, "jsAlert", "(Ljava/lang/String;Ljava/lang/String;)V");
402    m_javaGlue->m_jsConfirm = GetJMethod(env, clazz, "jsConfirm", "(Ljava/lang/String;Ljava/lang/String;)Z");
403    m_javaGlue->m_jsPrompt = GetJMethod(env, clazz, "jsPrompt", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
404    m_javaGlue->m_jsUnload = GetJMethod(env, clazz, "jsUnload", "(Ljava/lang/String;Ljava/lang/String;)Z");
405    m_javaGlue->m_jsInterrupt = GetJMethod(env, clazz, "jsInterrupt", "()Z");
406    m_javaGlue->m_didFirstLayout = GetJMethod(env, clazz, "didFirstLayout", "(Z)V");
407    m_javaGlue->m_updateViewport = GetJMethod(env, clazz, "updateViewport", "()V");
408    m_javaGlue->m_sendNotifyProgressFinished = GetJMethod(env, clazz, "sendNotifyProgressFinished", "()V");
409    m_javaGlue->m_sendViewInvalidate = GetJMethod(env, clazz, "sendViewInvalidate", "(IIII)V");
410    m_javaGlue->m_updateTextfield = GetJMethod(env, clazz, "updateTextfield", "(IZLjava/lang/String;I)V");
411    m_javaGlue->m_updateTextSelection = GetJMethod(env, clazz, "updateTextSelection", "(IIII)V");
412    m_javaGlue->m_clearTextEntry = GetJMethod(env, clazz, "clearTextEntry", "()V");
413    m_javaGlue->m_restoreScale = GetJMethod(env, clazz, "restoreScale", "(FF)V");
414    m_javaGlue->m_needTouchEvents = GetJMethod(env, clazz, "needTouchEvents", "(Z)V");
415    m_javaGlue->m_requestKeyboard = GetJMethod(env, clazz, "requestKeyboard", "(Z)V");
416    m_javaGlue->m_requestKeyboardWithSelection = GetJMethod(env, clazz, "requestKeyboardWithSelection", "(IIII)V");
417    m_javaGlue->m_exceededDatabaseQuota = GetJMethod(env, clazz, "exceededDatabaseQuota", "(Ljava/lang/String;Ljava/lang/String;JJ)V");
418    m_javaGlue->m_reachedMaxAppCacheSize = GetJMethod(env, clazz, "reachedMaxAppCacheSize", "(J)V");
419    m_javaGlue->m_populateVisitedLinks = GetJMethod(env, clazz, "populateVisitedLinks", "()V");
420    m_javaGlue->m_geolocationPermissionsShowPrompt = GetJMethod(env, clazz, "geolocationPermissionsShowPrompt", "(Ljava/lang/String;)V");
421    m_javaGlue->m_geolocationPermissionsHidePrompt = GetJMethod(env, clazz, "geolocationPermissionsHidePrompt", "()V");
422    m_javaGlue->m_getDeviceMotionService = GetJMethod(env, clazz, "getDeviceMotionService", "()Landroid/webkit/DeviceMotionService;");
423    m_javaGlue->m_getDeviceOrientationService = GetJMethod(env, clazz, "getDeviceOrientationService", "()Landroid/webkit/DeviceOrientationService;");
424    m_javaGlue->m_addMessageToConsole = GetJMethod(env, clazz, "addMessageToConsole", "(Ljava/lang/String;ILjava/lang/String;I)V");
425    m_javaGlue->m_formDidBlur = GetJMethod(env, clazz, "formDidBlur", "(I)V");
426    m_javaGlue->m_getPluginClass = GetJMethod(env, clazz, "getPluginClass", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Class;");
427    m_javaGlue->m_showFullScreenPlugin = GetJMethod(env, clazz, "showFullScreenPlugin", "(Landroid/webkit/ViewManager$ChildView;II)V");
428    m_javaGlue->m_hideFullScreenPlugin = GetJMethod(env, clazz, "hideFullScreenPlugin", "()V");
429    m_javaGlue->m_createSurface = GetJMethod(env, clazz, "createSurface", "(Landroid/view/View;)Landroid/webkit/ViewManager$ChildView;");
430    m_javaGlue->m_addSurface = GetJMethod(env, clazz, "addSurface", "(Landroid/view/View;IIII)Landroid/webkit/ViewManager$ChildView;");
431    m_javaGlue->m_updateSurface = GetJMethod(env, clazz, "updateSurface", "(Landroid/webkit/ViewManager$ChildView;IIII)V");
432    m_javaGlue->m_destroySurface = GetJMethod(env, clazz, "destroySurface", "(Landroid/webkit/ViewManager$ChildView;)V");
433    m_javaGlue->m_getContext = GetJMethod(env, clazz, "getContext", "()Landroid/content/Context;");
434    m_javaGlue->m_keepScreenOn = GetJMethod(env, clazz, "keepScreenOn", "(Z)V");
435    m_javaGlue->m_sendFindAgain = GetJMethod(env, clazz, "sendFindAgain", "()V");
436    m_javaGlue->m_showRect = GetJMethod(env, clazz, "showRect", "(IIIIIIFFFF)V");
437    m_javaGlue->m_centerFitRect = GetJMethod(env, clazz, "centerFitRect", "(IIII)V");
438    m_javaGlue->m_setScrollbarModes = GetJMethod(env, clazz, "setScrollbarModes", "(II)V");
439    m_javaGlue->m_setInstallableWebApp = GetJMethod(env, clazz, "setInstallableWebApp", "()V");
440#if ENABLE(VIDEO)
441    m_javaGlue->m_enterFullscreenForVideoLayer = GetJMethod(env, clazz, "enterFullscreenForVideoLayer", "(ILjava/lang/String;)V");
442#endif
443    m_javaGlue->m_setWebTextViewAutoFillable = GetJMethod(env, clazz, "setWebTextViewAutoFillable", "(ILjava/lang/String;)V");
444    m_javaGlue->m_selectAt = GetJMethod(env, clazz, "selectAt", "(II)V");
445    env->DeleteLocalRef(clazz);
446
447    env->SetIntField(javaWebViewCore, gWebViewCoreFields.m_nativeClass, (jint)this);
448
449    PageGroup::setShouldTrackVisitedLinks(true);
450
451    clearContent();
452
453    MemoryUsage::setLowMemoryUsageMb(env->GetIntField(javaWebViewCore, gWebViewCoreFields.m_lowMemoryUsageMb));
454    MemoryUsage::setHighMemoryUsageMb(env->GetIntField(javaWebViewCore, gWebViewCoreFields.m_highMemoryUsageMb));
455    MemoryUsage::setHighUsageDeltaMb(env->GetIntField(javaWebViewCore, gWebViewCoreFields.m_highUsageDeltaMb));
456
457    WebViewCore::addInstance(this);
458
459#if USE(CHROME_NETWORK_STACK)
460    AndroidNetworkLibraryImpl::InitWithApplicationContext(env, 0);
461#endif
462
463#if USE(V8)
464    // Static initialisation of certain important V8 static data gets performed at system startup when
465    // libwebcore gets loaded. We now need to associate the WebCore thread with V8 to complete
466    // initialisation.
467    v8::V8::Initialize();
468#endif
469
470    // Configure any RuntimeEnabled features that we need to change from their default now.
471    // See WebCore/bindings/generic/RuntimeEnabledFeatures.h
472
473    // HTML5 History API
474    RuntimeEnabledFeatures::setPushStateEnabled(true);
475}
476
477WebViewCore::~WebViewCore()
478{
479    WebViewCore::removeInstance(this);
480
481    // Release the focused view
482    Release(m_popupReply);
483
484    if (m_javaGlue->m_obj) {
485        JNIEnv* env = JSC::Bindings::getJNIEnv();
486        env->DeleteWeakGlobalRef(m_javaGlue->m_obj);
487        m_javaGlue->m_obj = 0;
488    }
489    delete m_javaGlue;
490    delete m_frameCacheKit;
491    delete m_navPictureKit;
492}
493
494WebViewCore* WebViewCore::getWebViewCore(const WebCore::FrameView* view)
495{
496    return getWebViewCore(static_cast<const WebCore::ScrollView*>(view));
497}
498
499WebViewCore* WebViewCore::getWebViewCore(const WebCore::ScrollView* view)
500{
501    if (!view)
502        return 0;
503
504    WebFrameView* webFrameView = static_cast<WebFrameView*>(view->platformWidget());
505    if (!webFrameView)
506        return 0;
507    return webFrameView->webViewCore();
508}
509
510static bool layoutIfNeededRecursive(WebCore::Frame* f)
511{
512    if (!f)
513        return true;
514
515    WebCore::FrameView* v = f->view();
516    if (!v)
517        return true;
518
519    if (v->needsLayout())
520        v->layout(f->tree()->parent());
521
522    WebCore::Frame* child = f->tree()->firstChild();
523    bool success = true;
524    while (child) {
525        success &= layoutIfNeededRecursive(child);
526        child = child->tree()->nextSibling();
527    }
528
529    return success && !v->needsLayout();
530}
531
532#if ENABLE(ANDROID_NAVCACHE)
533CacheBuilder& WebViewCore::cacheBuilder()
534{
535    return FrameLoaderClientAndroid::get(m_mainFrame)->getCacheBuilder();
536}
537#endif
538
539WebCore::Node* WebViewCore::currentFocus()
540{
541    return m_mainFrame->document()->focusedNode();
542}
543
544void WebViewCore::recordPicture(SkPicture* picture)
545{
546    // if there is no document yet, just return
547    if (!m_mainFrame->document()) {
548        DBG_NAV_LOG("no document");
549        return;
550    }
551    // Call layout to ensure that the contentWidth and contentHeight are correct
552    if (!layoutIfNeededRecursive(m_mainFrame)) {
553        DBG_NAV_LOG("layout failed");
554        return;
555    }
556    // draw into the picture's recording canvas
557    WebCore::FrameView* view = m_mainFrame->view();
558    DBG_NAV_LOGD("view=(w=%d,h=%d)", view->contentsWidth(),
559        view->contentsHeight());
560    SkAutoPictureRecord arp(picture, view->contentsWidth(),
561                            view->contentsHeight(), PICT_RECORD_FLAGS);
562    SkAutoMemoryUsageProbe mup(__FUNCTION__);
563
564    WebCore::PlatformGraphicsContext pgc(arp.getRecordingCanvas());
565    WebCore::GraphicsContext gc(&pgc);
566    view->platformWidget()->draw(&gc, WebCore::IntRect(0, 0,
567        view->contentsWidth(), view->contentsHeight()));
568}
569
570void WebViewCore::recordPictureSet(PictureSet* content)
571{
572    // if there is no document yet, just return
573    if (!m_mainFrame->document()) {
574        DBG_SET_LOG("!m_mainFrame->document()");
575        return;
576    }
577    if (m_addInval.isEmpty()) {
578        DBG_SET_LOG("m_addInval.isEmpty()");
579        return;
580    }
581    // Call layout to ensure that the contentWidth and contentHeight are correct
582    // it's fine for layout to gather invalidates, but defeat sending a message
583    // back to java to call webkitDraw, since we're already in the middle of
584    // doing that
585    m_skipContentDraw = true;
586    bool success = layoutIfNeededRecursive(m_mainFrame);
587    m_skipContentDraw = false;
588
589    // We may be mid-layout and thus cannot draw.
590    if (!success)
591        return;
592
593    // if the webkit page dimensions changed, discard the pictureset and redraw.
594    WebCore::FrameView* view = m_mainFrame->view();
595    int width = view->contentsWidth();
596    int height = view->contentsHeight();
597
598    // Use the contents width and height as a starting point.
599    SkIRect contentRect;
600    contentRect.set(0, 0, width, height);
601    SkIRect total(contentRect);
602
603    // Traverse all the frames and add their sizes if they are in the visible
604    // rectangle.
605    for (WebCore::Frame* frame = m_mainFrame->tree()->traverseNext(); frame;
606            frame = frame->tree()->traverseNext()) {
607        // If the frame doesn't have an owner then it is the top frame and the
608        // view size is the frame size.
609        WebCore::RenderPart* owner = frame->ownerRenderer();
610        if (owner && owner->style()->visibility() == VISIBLE) {
611            int x = owner->x();
612            int y = owner->y();
613
614            // Traverse the tree up to the parent to find the absolute position
615            // of this frame.
616            WebCore::Frame* parent = frame->tree()->parent();
617            while (parent) {
618                WebCore::RenderPart* parentOwner = parent->ownerRenderer();
619                if (parentOwner) {
620                    x += parentOwner->x();
621                    y += parentOwner->y();
622                }
623                parent = parent->tree()->parent();
624            }
625            // Use the owner dimensions so that padding and border are
626            // included.
627            int right = x + owner->width();
628            int bottom = y + owner->height();
629            SkIRect frameRect = {x, y, right, bottom};
630            // Ignore a width or height that is smaller than 1. Some iframes
631            // have small dimensions in order to be hidden. The iframe
632            // expansion code does not expand in that case so we should ignore
633            // them here.
634            if (frameRect.width() > 1 && frameRect.height() > 1
635                    && SkIRect::Intersects(total, frameRect))
636                total.join(x, y, right, bottom);
637        }
638    }
639
640    // If the new total is larger than the content, resize the view to include
641    // all the content.
642    if (!contentRect.contains(total)) {
643        // Resize the view to change the overflow clip.
644        view->resize(total.fRight, total.fBottom);
645
646        // We have to force a layout in order for the clip to change.
647        m_mainFrame->contentRenderer()->setNeedsLayoutAndPrefWidthsRecalc();
648        view->forceLayout();
649
650        // Relayout similar to above
651        m_skipContentDraw = true;
652        bool success = layoutIfNeededRecursive(m_mainFrame);
653        m_skipContentDraw = false;
654        if (!success)
655            return;
656
657        // Set the computed content width
658        width = view->contentsWidth();
659        height = view->contentsHeight();
660    }
661
662#if ENABLE(ANDROID_NAVCACHE)
663    if (cacheBuilder().pictureSetDisabled())
664        content->clear();
665#endif
666
667#if USE(ACCELERATED_COMPOSITING)
668    // The invals are not always correct when the content size has changed. For
669    // now, let's just reset the inval so that it invalidates the entire content
670    // -- the pictureset will be fully repainted, tiles will be marked dirty and
671    // will have to be repainted.
672
673    // FIXME: the webkit invals ought to have been enough...
674    if (content->width() != width || content->height() != height) {
675        SkIRect r;
676        r.fLeft = 0;
677        r.fTop = 0;
678        r.fRight = width;
679        r.fBottom = height;
680        m_addInval.setRect(r);
681    }
682#endif
683
684    content->setDimensions(width, height, &m_addInval);
685
686    // Add the current inval rects to the PictureSet, and rebuild it.
687    content->add(m_addInval, 0, 0, false);
688
689    // If we have too many invalidations, just get the area bounds
690    SkRegion::Iterator iterator(m_addInval);
691    int nbInvals = 0;
692    while (!iterator.done()) {
693        iterator.next();
694        nbInvals++;
695        if (nbInvals > MAX_INVALIDATIONS)
696            break;
697    }
698    if (nbInvals > MAX_INVALIDATIONS) {
699        SkIRect r = m_addInval.getBounds();
700        m_addInval.setRect(r);
701    }
702
703    // Rebuild the pictureset (webkit repaint)
704    rebuildPictureSet(content);
705
706#if ENABLE(ANDROID_NAVCACHE)
707    WebCore::Node* oldFocusNode = currentFocus();
708    m_frameCacheOutOfDate = true;
709    WebCore::IntRect oldBounds;
710    int oldSelStart = 0;
711    int oldSelEnd = 0;
712    if (oldFocusNode) {
713        oldBounds = oldFocusNode->getRect();
714        RenderObject* renderer = oldFocusNode->renderer();
715        if (renderer && (renderer->isTextArea() || renderer->isTextField())) {
716            WebCore::RenderTextControl* rtc =
717                static_cast<WebCore::RenderTextControl*>(renderer);
718            oldSelStart = rtc->selectionStart();
719            oldSelEnd = rtc->selectionEnd();
720        }
721    } else
722        oldBounds = WebCore::IntRect(0,0,0,0);
723    unsigned latestVersion = 0;
724    if (m_check_domtree_version) {
725        // as domTreeVersion only increment, we can just check the sum to see
726        // whether we need to update the frame cache
727        for (Frame* frame = m_mainFrame; frame; frame = frame->tree()->traverseNext()) {
728            const Document* doc = frame->document();
729            latestVersion += doc->domTreeVersion() + doc->styleVersion();
730        }
731    }
732    DBG_NAV_LOGD("m_lastFocused=%p oldFocusNode=%p"
733        " m_lastFocusedBounds={%d,%d,%d,%d} oldBounds={%d,%d,%d,%d}"
734        " m_lastFocusedSelection={%d,%d} oldSelection={%d,%d}"
735        " m_check_domtree_version=%s latestVersion=%d m_domtree_version=%d",
736        m_lastFocused, oldFocusNode,
737        m_lastFocusedBounds.x(), m_lastFocusedBounds.y(),
738        m_lastFocusedBounds.width(), m_lastFocusedBounds.height(),
739        oldBounds.x(), oldBounds.y(), oldBounds.width(), oldBounds.height(),
740        m_lastFocusedSelStart, m_lastFocusedSelEnd, oldSelStart, oldSelEnd,
741        m_check_domtree_version ? "true" : "false",
742        latestVersion, m_domtree_version);
743    if (m_lastFocused == oldFocusNode && m_lastFocusedBounds == oldBounds
744            && m_lastFocusedSelStart == oldSelStart
745            && m_lastFocusedSelEnd == oldSelEnd
746            && !m_findIsUp
747            && (!m_check_domtree_version || latestVersion == m_domtree_version))
748    {
749        return;
750    }
751    m_focusBoundsChanged |= m_lastFocused == oldFocusNode
752        && m_lastFocusedBounds != oldBounds;
753    m_lastFocused = oldFocusNode;
754    m_lastFocusedBounds = oldBounds;
755    m_lastFocusedSelStart = oldSelStart;
756    m_lastFocusedSelEnd = oldSelEnd;
757    m_domtree_version = latestVersion;
758    DBG_NAV_LOG("call updateFrameCache");
759    updateFrameCache();
760#endif
761    if (m_findIsUp) {
762        ALOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
763        JNIEnv* env = JSC::Bindings::getJNIEnv();
764        AutoJObject javaObject = m_javaGlue->object(env);
765        if (javaObject.get()) {
766            env->CallVoidMethod(javaObject.get(), m_javaGlue->m_sendFindAgain);
767            checkException(env);
768        }
769    }
770}
771
772// note: updateCursorBounds is called directly by the WebView thread
773// This needs to be called each time we call CachedRoot::setCursor() with
774// non-null CachedNode/CachedFrame, since otherwise the WebViewCore's data
775// about the cursor is incorrect.  When we call setCursor(0,0), we need
776// to set hasCursorBounds to false.
777void WebViewCore::updateCursorBounds(const CachedRoot* root,
778        const CachedFrame* cachedFrame, const CachedNode* cachedNode)
779{
780    ALOG_ASSERT(root, "updateCursorBounds: root cannot be null");
781    ALOG_ASSERT(cachedNode, "updateCursorBounds: cachedNode cannot be null");
782    ALOG_ASSERT(cachedFrame, "updateCursorBounds: cachedFrame cannot be null");
783    gCursorBoundsMutex.lock();
784    m_hasCursorBounds = !cachedNode->isHidden();
785    // If m_hasCursorBounds is false, we never look at the other
786    // values, so do not bother setting them.
787    if (m_hasCursorBounds) {
788        WebCore::IntRect bounds = cachedNode->bounds(cachedFrame);
789        if (m_cursorBounds != bounds)
790            DBG_NAV_LOGD("new cursor bounds=(%d,%d,w=%d,h=%d)",
791                bounds.x(), bounds.y(), bounds.width(), bounds.height());
792        m_cursorBounds = bounds;
793        m_cursorHitBounds = cachedNode->hitBounds(cachedFrame);
794        m_cursorFrame = cachedFrame->framePointer();
795        root->getSimulatedMousePosition(&m_cursorLocation);
796        m_cursorNode = cachedNode->nodePointer();
797    }
798    gCursorBoundsMutex.unlock();
799}
800
801void WebViewCore::clearContent()
802{
803    DBG_SET_LOG("");
804    m_content.clear();
805    m_addInval.setEmpty();
806    m_rebuildInval.setEmpty();
807}
808
809bool WebViewCore::focusBoundsChanged()
810{
811    bool result = m_focusBoundsChanged;
812    m_focusBoundsChanged = false;
813    return result;
814}
815
816SkPicture* WebViewCore::rebuildPicture(const SkIRect& inval)
817{
818    WebCore::FrameView* view = m_mainFrame->view();
819    int width = view->contentsWidth();
820    int height = view->contentsHeight();
821    SkPicture* picture = new SkPicture();
822    SkAutoPictureRecord arp(picture, width, height, PICT_RECORD_FLAGS);
823    SkAutoMemoryUsageProbe mup(__FUNCTION__);
824    SkCanvas* recordingCanvas = arp.getRecordingCanvas();
825
826    WebCore::PlatformGraphicsContext pgc(recordingCanvas);
827    WebCore::GraphicsContext gc(&pgc);
828    IntPoint origin = view->minimumScrollPosition();
829    WebCore::IntRect drawArea(inval.fLeft + origin.x(), inval.fTop + origin.y(),
830            inval.width(), inval.height());
831    recordingCanvas->translate(-drawArea.x(), -drawArea.y());
832    recordingCanvas->save();
833    view->platformWidget()->draw(&gc, drawArea);
834    m_rebuildInval.op(inval, SkRegion::kUnion_Op);
835    DBG_SET_LOGD("m_rebuildInval={%d,%d,r=%d,b=%d}",
836        m_rebuildInval.getBounds().fLeft, m_rebuildInval.getBounds().fTop,
837        m_rebuildInval.getBounds().fRight, m_rebuildInval.getBounds().fBottom);
838
839    return picture;
840}
841
842void WebViewCore::rebuildPictureSet(PictureSet* pictureSet)
843{
844    WebCore::FrameView* view = m_mainFrame->view();
845
846#ifdef FAST_PICTURESET
847    WTF::Vector<Bucket*>* buckets = pictureSet->bucketsToUpdate();
848
849    for (unsigned int i = 0; i < buckets->size(); i++) {
850        Bucket* bucket = (*buckets)[i];
851        for (unsigned int j = 0; j < bucket->size(); j++) {
852            BucketPicture& bucketPicture = (*bucket)[j];
853            const SkIRect& inval = bucketPicture.mRealArea;
854            SkPicture* picture = rebuildPicture(inval);
855            SkSafeUnref(bucketPicture.mPicture);
856            bucketPicture.mPicture = picture;
857        }
858    }
859    buckets->clear();
860#else
861    size_t size = pictureSet->size();
862    for (size_t index = 0; index < size; index++) {
863        if (pictureSet->upToDate(index))
864            continue;
865        const SkIRect& inval = pictureSet->bounds(index);
866        DBG_SET_LOGD("pictSet=%p [%d] {%d,%d,w=%d,h=%d}", pictureSet, index,
867            inval.fLeft, inval.fTop, inval.width(), inval.height());
868        pictureSet->setPicture(index, rebuildPicture(inval));
869    }
870
871    pictureSet->validate(__FUNCTION__);
872#endif
873}
874
875bool WebViewCore::updateLayers(LayerAndroid* layers)
876{
877    // We update the layers
878    ChromeClientAndroid* chromeC = static_cast<ChromeClientAndroid*>(m_mainFrame->page()->chrome()->client());
879    GraphicsLayerAndroid* root = static_cast<GraphicsLayerAndroid*>(chromeC->layersSync());
880    if (root) {
881        LayerAndroid* updatedLayer = root->contentLayer();
882        return layers->updateWithTree(updatedLayer);
883    }
884    return true;
885}
886
887void WebViewCore::notifyAnimationStarted()
888{
889    // We notify webkit that the animations have begun
890    // TODO: handle case where not all have begun
891    ChromeClientAndroid* chromeC = static_cast<ChromeClientAndroid*>(m_mainFrame->page()->chrome()->client());
892    GraphicsLayerAndroid* root = static_cast<GraphicsLayerAndroid*>(chromeC->layersSync());
893    if (root)
894        root->notifyClientAnimationStarted();
895
896}
897
898BaseLayerAndroid* WebViewCore::createBaseLayer(SkRegion* region)
899{
900    BaseLayerAndroid* base = new BaseLayerAndroid();
901    base->setContent(m_content);
902
903    m_skipContentDraw = true;
904    bool layoutSucceeded = layoutIfNeededRecursive(m_mainFrame);
905    m_skipContentDraw = false;
906    // Layout only fails if called during a layout.
907    ALOG_ASSERT(layoutSucceeded, "Can never be called recursively");
908
909#if USE(ACCELERATED_COMPOSITING)
910    // We set the background color
911    if (m_mainFrame && m_mainFrame->document()
912        && m_mainFrame->document()->body()) {
913        Document* document = m_mainFrame->document();
914        RefPtr<RenderStyle> style = document->styleForElementIgnoringPendingStylesheets(document->body());
915        if (style->hasBackground()) {
916            Color color = style->visitedDependentColor(CSSPropertyBackgroundColor);
917            if (color.isValid() && color.alpha() > 0)
918                base->setBackgroundColor(color);
919        }
920    }
921
922    // We update the layers
923    ChromeClientAndroid* chromeC = static_cast<ChromeClientAndroid*>(m_mainFrame->page()->chrome()->client());
924    GraphicsLayerAndroid* root = static_cast<GraphicsLayerAndroid*>(chromeC->layersSync());
925    if (root) {
926        LayerAndroid* copyLayer = new LayerAndroid(*root->contentLayer());
927        base->addChild(copyLayer);
928        copyLayer->unref();
929        root->contentLayer()->clearDirtyRegion();
930    }
931#endif
932
933    return base;
934}
935
936BaseLayerAndroid* WebViewCore::recordContent(SkRegion* region, SkIPoint* point)
937{
938    DBG_SET_LOG("start");
939    // If there is a pending style recalculation, just return.
940    if (m_mainFrame->document()->isPendingStyleRecalc()) {
941        DBG_SET_LOGD("recordContent: pending style recalc, ignoring.");
942        return 0;
943    }
944    float progress = (float) m_mainFrame->page()->progress()->estimatedProgress();
945    m_progressDone = progress <= 0.0f || progress >= 1.0f;
946    recordPictureSet(&m_content);
947    if (!m_progressDone && m_content.isEmpty()) {
948        DBG_SET_LOGD("empty (progress=%g)", progress);
949        return 0;
950    }
951    region->set(m_addInval);
952    m_addInval.setEmpty();
953#if USE(ACCELERATED_COMPOSITING)
954#else
955    region->op(m_rebuildInval, SkRegion::kUnion_Op);
956#endif
957    m_rebuildInval.setEmpty();
958    point->fX = m_content.width();
959    point->fY = m_content.height();
960    DBG_SET_LOGD("region={%d,%d,r=%d,b=%d}", region->getBounds().fLeft,
961        region->getBounds().fTop, region->getBounds().fRight,
962        region->getBounds().fBottom);
963    DBG_SET_LOG("end");
964
965    return createBaseLayer(region);
966}
967
968void WebViewCore::splitContent(PictureSet* content)
969{
970#ifdef FAST_PICTURESET
971#else
972    bool layoutSucceeded = layoutIfNeededRecursive(m_mainFrame);
973    ALOG_ASSERT(layoutSucceeded, "Can never be called recursively");
974    content->split(&m_content);
975    rebuildPictureSet(&m_content);
976    content->set(m_content);
977#endif // FAST_PICTURESET
978}
979
980void WebViewCore::scrollTo(int x, int y, bool animate)
981{
982    ALOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
983
984//    ALOGD("WebViewCore::scrollTo(%d %d)\n", x, y);
985
986    JNIEnv* env = JSC::Bindings::getJNIEnv();
987    AutoJObject javaObject = m_javaGlue->object(env);
988    if (!javaObject.get())
989        return;
990    env->CallVoidMethod(javaObject.get(), m_javaGlue->m_scrollTo,
991            x, y, animate, false);
992    checkException(env);
993}
994
995void WebViewCore::sendNotifyProgressFinished()
996{
997    ALOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
998    JNIEnv* env = JSC::Bindings::getJNIEnv();
999    AutoJObject javaObject = m_javaGlue->object(env);
1000    if (!javaObject.get())
1001        return;
1002    env->CallVoidMethod(javaObject.get(), m_javaGlue->m_sendNotifyProgressFinished);
1003    checkException(env);
1004}
1005
1006void WebViewCore::viewInvalidate(const WebCore::IntRect& rect)
1007{
1008    ALOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
1009    JNIEnv* env = JSC::Bindings::getJNIEnv();
1010    AutoJObject javaObject = m_javaGlue->object(env);
1011    if (!javaObject.get())
1012        return;
1013    env->CallVoidMethod(javaObject.get(),
1014                        m_javaGlue->m_sendViewInvalidate,
1015                        rect.x(), rect.y(), rect.maxX(), rect.maxY());
1016    checkException(env);
1017}
1018
1019void WebViewCore::contentDraw()
1020{
1021    JNIEnv* env = JSC::Bindings::getJNIEnv();
1022    AutoJObject javaObject = m_javaGlue->object(env);
1023    if (!javaObject.get())
1024        return;
1025    env->CallVoidMethod(javaObject.get(), m_javaGlue->m_contentDraw);
1026    checkException(env);
1027}
1028
1029void WebViewCore::layersDraw()
1030{
1031    JNIEnv* env = JSC::Bindings::getJNIEnv();
1032    AutoJObject javaObject = m_javaGlue->object(env);
1033    if (!javaObject.get())
1034        return;
1035    env->CallVoidMethod(javaObject.get(), m_javaGlue->m_layersDraw);
1036    checkException(env);
1037}
1038
1039void WebViewCore::contentInvalidate(const WebCore::IntRect &r)
1040{
1041    DBG_SET_LOGD("rect={%d,%d,w=%d,h=%d}", r.x(), r.y(), r.width(), r.height());
1042    SkIRect rect(r);
1043    if (!rect.intersect(0, 0, INT_MAX, INT_MAX))
1044        return;
1045    m_addInval.op(rect, SkRegion::kUnion_Op);
1046    DBG_SET_LOGD("m_addInval={%d,%d,r=%d,b=%d}",
1047        m_addInval.getBounds().fLeft, m_addInval.getBounds().fTop,
1048        m_addInval.getBounds().fRight, m_addInval.getBounds().fBottom);
1049    if (!m_skipContentDraw)
1050        contentDraw();
1051}
1052
1053void WebViewCore::contentInvalidateAll()
1054{
1055    WebCore::FrameView* view = m_mainFrame->view();
1056    contentInvalidate(WebCore::IntRect(0, 0,
1057        view->contentsWidth(), view->contentsHeight()));
1058}
1059
1060void WebViewCore::offInvalidate(const WebCore::IntRect &r)
1061{
1062    // FIXME: these invalidates are offscreen, and can be throttled or
1063    // deferred until the area is visible. For now, treat them as
1064    // regular invals so that drawing happens (inefficiently) for now.
1065    contentInvalidate(r);
1066}
1067
1068static int pin_pos(int x, int width, int targetWidth)
1069{
1070    if (x + width > targetWidth)
1071        x = targetWidth - width;
1072    if (x < 0)
1073        x = 0;
1074    return x;
1075}
1076
1077void WebViewCore::didFirstLayout()
1078{
1079    DEBUG_NAV_UI_LOGD("%s", __FUNCTION__);
1080    ALOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
1081
1082    JNIEnv* env = JSC::Bindings::getJNIEnv();
1083    AutoJObject javaObject = m_javaGlue->object(env);
1084    if (!javaObject.get())
1085        return;
1086
1087    const WebCore::KURL& url = m_mainFrame->document()->url();
1088    if (url.isEmpty())
1089        return;
1090    ALOGV("::WebCore:: didFirstLayout %s", url.string().ascii().data());
1091
1092    WebCore::FrameLoadType loadType = m_mainFrame->loader()->loadType();
1093
1094    env->CallVoidMethod(javaObject.get(), m_javaGlue->m_didFirstLayout,
1095            loadType == WebCore::FrameLoadTypeStandard
1096            // When redirect with locked history, we would like to reset the
1097            // scale factor. This is important for www.yahoo.com as it is
1098            // redirected to www.yahoo.com/?rs=1 on load.
1099            || loadType == WebCore::FrameLoadTypeRedirectWithLockedBackForwardList
1100            // When "request desktop page" is used, we want to treat it as
1101            // a newly-loaded page.
1102            || loadType == WebCore::FrameLoadTypeSame);
1103    checkException(env);
1104
1105#if ENABLE(ANDROID_NAVCACHE)
1106    DBG_NAV_LOG("call updateFrameCache");
1107    m_check_domtree_version = false;
1108    updateFrameCache();
1109#endif
1110    m_history.setDidFirstLayout(true);
1111}
1112
1113void WebViewCore::updateViewport()
1114{
1115    DEBUG_NAV_UI_LOGD("%s", __FUNCTION__);
1116    ALOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
1117
1118    JNIEnv* env = JSC::Bindings::getJNIEnv();
1119    AutoJObject javaObject = m_javaGlue->object(env);
1120    if (!javaObject.get())
1121        return;
1122    env->CallVoidMethod(javaObject.get(), m_javaGlue->m_updateViewport);
1123    checkException(env);
1124}
1125
1126void WebViewCore::restoreScale(float scale, float textWrapScale)
1127{
1128    DEBUG_NAV_UI_LOGD("%s", __FUNCTION__);
1129    ALOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
1130
1131    JNIEnv* env = JSC::Bindings::getJNIEnv();
1132    AutoJObject javaObject = m_javaGlue->object(env);
1133    if (!javaObject.get())
1134        return;
1135    env->CallVoidMethod(javaObject.get(), m_javaGlue->m_restoreScale, scale, textWrapScale);
1136    checkException(env);
1137}
1138
1139void WebViewCore::needTouchEvents(bool need)
1140{
1141    DEBUG_NAV_UI_LOGD("%s", __FUNCTION__);
1142    ALOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
1143
1144#if ENABLE(TOUCH_EVENTS)
1145    JNIEnv* env = JSC::Bindings::getJNIEnv();
1146    AutoJObject javaObject = m_javaGlue->object(env);
1147    if (!javaObject.get())
1148        return;
1149
1150    if (m_forwardingTouchEvents == need)
1151        return;
1152
1153    env->CallVoidMethod(javaObject.get(), m_javaGlue->m_needTouchEvents, need);
1154    checkException(env);
1155
1156    m_forwardingTouchEvents = need;
1157#endif
1158}
1159
1160void WebViewCore::requestKeyboardWithSelection(const WebCore::Node* node,
1161        int selStart, int selEnd)
1162{
1163    DEBUG_NAV_UI_LOGD("%s", __FUNCTION__);
1164    ALOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
1165
1166    JNIEnv* env = JSC::Bindings::getJNIEnv();
1167    AutoJObject javaObject = m_javaGlue->object(env);
1168    if (!javaObject.get())
1169        return;
1170    env->CallVoidMethod(javaObject.get(),
1171            m_javaGlue->m_requestKeyboardWithSelection,
1172            reinterpret_cast<int>(node), selStart, selEnd, m_textGeneration);
1173    checkException(env);
1174}
1175
1176void WebViewCore::requestKeyboard(bool showKeyboard)
1177{
1178    DEBUG_NAV_UI_LOGD("%s", __FUNCTION__);
1179    ALOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
1180
1181    JNIEnv* env = JSC::Bindings::getJNIEnv();
1182    AutoJObject javaObject = m_javaGlue->object(env);
1183    if (!javaObject.get())
1184        return;
1185    env->CallVoidMethod(javaObject.get(), m_javaGlue->m_requestKeyboard, showKeyboard);
1186    checkException(env);
1187}
1188
1189void WebViewCore::notifyProgressFinished()
1190{
1191    m_check_domtree_version = true;
1192    sendNotifyProgressFinished();
1193}
1194
1195void WebViewCore::setScrollOffset(int moveGeneration, bool sendScrollEvent, int dx, int dy)
1196{
1197    DBG_NAV_LOGD("{%d,%d} m_scrollOffset=(%d,%d), sendScrollEvent=%d", dx, dy,
1198        m_scrollOffsetX, m_scrollOffsetY, sendScrollEvent);
1199    if (m_scrollOffsetX != dx || m_scrollOffsetY != dy) {
1200        m_scrollOffsetX = dx;
1201        m_scrollOffsetY = dy;
1202        // The visible rect is located within our coordinate space so it
1203        // contains the actual scroll position. Setting the location makes hit
1204        // testing work correctly.
1205        m_mainFrame->view()->platformWidget()->setLocation(m_scrollOffsetX,
1206                m_scrollOffsetY);
1207        if (sendScrollEvent) {
1208            m_mainFrame->eventHandler()->sendScrollEvent();
1209
1210            // Only update history position if it's user scrolled.
1211            // Update history item to reflect the new scroll position.
1212            // This also helps save the history information when the browser goes to
1213            // background, so scroll position will be restored if browser gets
1214            // killed while in background.
1215            WebCore::HistoryController* history = m_mainFrame->loader()->history();
1216            // Because the history item saving could be heavy for large sites and
1217            // scrolling can generate lots of small scroll offset, the following code
1218            // reduces the saving frequency.
1219            static const int MIN_SCROLL_DIFF = 32;
1220            if (history->currentItem()) {
1221                WebCore::IntPoint currentPoint = history->currentItem()->scrollPoint();
1222                if (std::abs(currentPoint.x() - dx) >= MIN_SCROLL_DIFF ||
1223                    std::abs(currentPoint.y() - dy) >= MIN_SCROLL_DIFF) {
1224                    history->saveScrollPositionAndViewStateToItem(history->currentItem());
1225                }
1226            }
1227        }
1228
1229        // update the currently visible screen
1230        sendPluginVisibleScreen();
1231    }
1232    gCursorBoundsMutex.lock();
1233    bool hasCursorBounds = m_hasCursorBounds;
1234    Frame* frame = (Frame*) m_cursorFrame;
1235    IntPoint location = m_cursorLocation;
1236    gCursorBoundsMutex.unlock();
1237    if (!hasCursorBounds)
1238        return;
1239    moveMouseIfLatest(moveGeneration, frame, location.x(), location.y());
1240}
1241
1242void WebViewCore::setGlobalBounds(int x, int y, int h, int v)
1243{
1244    DBG_NAV_LOGD("{%d,%d}", x, y);
1245    m_mainFrame->view()->platformWidget()->setWindowBounds(x, y, h, v);
1246}
1247
1248void WebViewCore::setSizeScreenWidthAndScale(int width, int height,
1249    int textWrapWidth, float scale, int screenWidth, int screenHeight,
1250    int anchorX, int anchorY, bool ignoreHeight)
1251{
1252    // Ignore the initial empty document.
1253    const WebCore::KURL& url = m_mainFrame->document()->url();
1254    if (url.isEmpty())
1255        return;
1256
1257    WebCoreViewBridge* window = m_mainFrame->view()->platformWidget();
1258    int ow = window->width();
1259    int oh = window->height();
1260    int osw = m_screenWidth;
1261    int osh = m_screenHeight;
1262    int otw = m_textWrapWidth;
1263    float oldScale = m_scale;
1264    DBG_NAV_LOGD("old:(w=%d,h=%d,sw=%d,scale=%g) new:(w=%d,h=%d,sw=%d,scale=%g)",
1265        ow, oh, osw, m_scale, width, height, screenWidth, scale);
1266    m_screenWidth = screenWidth;
1267    m_screenHeight = screenHeight;
1268    m_textWrapWidth = textWrapWidth;
1269    if (scale >= 0) // negative means keep the current scale
1270        m_scale = scale;
1271    m_maxXScroll = screenWidth >> 2;
1272    m_maxYScroll = m_maxXScroll * height / width;
1273    // Don't reflow if the diff is small.
1274    const bool reflow = otw && textWrapWidth &&
1275        ((float) abs(otw - textWrapWidth) / textWrapWidth) >= 0.01f;
1276
1277    // When the screen size change, fixed positioned element should be updated.
1278    // This is supposed to be light weighted operation without a full layout.
1279    if (osh != screenHeight || osw != screenWidth)
1280        m_mainFrame->view()->updatePositionedObjects();
1281
1282    if (ow != width || (!ignoreHeight && oh != height) || reflow) {
1283        WebCore::RenderObject *r = m_mainFrame->contentRenderer();
1284        DBG_NAV_LOGD("renderer=%p view=(w=%d,h=%d)", r,
1285                screenWidth, screenHeight);
1286        if (r) {
1287            WebCore::IntPoint anchorPoint = WebCore::IntPoint(anchorX, anchorY);
1288            DBG_NAV_LOGD("anchorX=%d anchorY=%d", anchorX, anchorY);
1289            RefPtr<WebCore::Node> node;
1290            WebCore::IntRect bounds;
1291            WebCore::IntPoint offset;
1292            // If the text wrap changed, it is probably zoom change or
1293            // orientation change. Try to keep the anchor at the same place.
1294            if (otw && textWrapWidth && otw != textWrapWidth &&
1295                (anchorX != 0 || anchorY != 0)) {
1296                WebCore::HitTestResult hitTestResult =
1297                        m_mainFrame->eventHandler()->hitTestResultAtPoint(
1298                                anchorPoint, false);
1299                node = hitTestResult.innerNode();
1300            }
1301            if (node) {
1302                bounds = node->getRect();
1303                DBG_NAV_LOGD("ob:(x=%d,y=%d,w=%d,h=%d)",
1304                    bounds.x(), bounds.y(), bounds.width(), bounds.height());
1305                // sites like nytimes.com insert a non-standard tag <nyt_text>
1306                // in the html. If it is the HitTestResult, it may have zero
1307                // width and height. In this case, use its parent node.
1308                if (bounds.width() == 0) {
1309                    node = node->parentOrHostNode();
1310                    if (node) {
1311                        bounds = node->getRect();
1312                        DBG_NAV_LOGD("found a zero width node and use its parent, whose ob:(x=%d,y=%d,w=%d,h=%d)",
1313                                bounds.x(), bounds.y(), bounds.width(), bounds.height());
1314                    }
1315                }
1316            }
1317
1318            // Set the size after finding the old anchor point as
1319            // hitTestResultAtPoint causes a layout.
1320            window->setSize(width, height);
1321            window->setVisibleSize(screenWidth, screenHeight);
1322            if (width != screenWidth) {
1323                m_mainFrame->view()->setUseFixedLayout(true);
1324                m_mainFrame->view()->setFixedLayoutSize(IntSize(width, height));
1325            } else
1326                m_mainFrame->view()->setUseFixedLayout(false);
1327            r->setNeedsLayoutAndPrefWidthsRecalc();
1328            if (m_mainFrame->view()->didFirstLayout())
1329                m_mainFrame->view()->forceLayout();
1330
1331            // scroll to restore current screen center
1332            if (node) {
1333                const WebCore::IntRect& newBounds = node->getRect();
1334                DBG_NAV_LOGD("nb:(x=%d,y=%d,w=%d,"
1335                    "h=%d)", newBounds.x(), newBounds.y(),
1336                    newBounds.width(), newBounds.height());
1337                if ((osw && osh && bounds.width() && bounds.height())
1338                    && (bounds != newBounds)) {
1339                    WebCore::FrameView* view = m_mainFrame->view();
1340                    // force left align if width is not changed while height changed.
1341                    // the anchorPoint is probably at some white space in the node
1342                    // which is affected by text wrap around the screen width.
1343                    const bool leftAlign = (otw != textWrapWidth)
1344                        && (bounds.width() == newBounds.width())
1345                        && (bounds.height() != newBounds.height());
1346                    const float xPercentInDoc =
1347                        leftAlign ? 0.0 : (float) (anchorX - bounds.x()) / bounds.width();
1348                    const float xPercentInView =
1349                        leftAlign ? 0.0 : (float) (anchorX - m_scrollOffsetX) / osw;
1350                    const float yPercentInDoc = (float) (anchorY - bounds.y()) / bounds.height();
1351                    const float yPercentInView = (float) (anchorY - m_scrollOffsetY) / osh;
1352                    showRect(newBounds.x(), newBounds.y(), newBounds.width(),
1353                             newBounds.height(), view->contentsWidth(),
1354                             view->contentsHeight(),
1355                             xPercentInDoc, xPercentInView,
1356                             yPercentInDoc, yPercentInView);
1357                }
1358            }
1359        }
1360    } else {
1361        window->setSize(width, height);
1362        window->setVisibleSize(screenWidth, screenHeight);
1363        m_mainFrame->view()->resize(width, height);
1364        if (width != screenWidth) {
1365            m_mainFrame->view()->setUseFixedLayout(true);
1366            m_mainFrame->view()->setFixedLayoutSize(IntSize(width, height));
1367        } else
1368            m_mainFrame->view()->setUseFixedLayout(false);
1369    }
1370
1371    // update the currently visible screen as perceived by the plugin
1372    sendPluginVisibleScreen();
1373}
1374
1375void WebViewCore::dumpDomTree(bool useFile)
1376{
1377#ifdef ANDROID_DOM_LOGGING
1378    if (useFile)
1379        gDomTreeFile = fopen(DOM_TREE_LOG_FILE, "w");
1380    m_mainFrame->document()->showTreeForThis();
1381    if (gDomTreeFile) {
1382        fclose(gDomTreeFile);
1383        gDomTreeFile = 0;
1384    }
1385#endif
1386}
1387
1388void WebViewCore::dumpRenderTree(bool useFile)
1389{
1390#ifdef ANDROID_DOM_LOGGING
1391    WTF::CString renderDump = WebCore::externalRepresentation(m_mainFrame).utf8();
1392    const char* data = renderDump.data();
1393    if (useFile) {
1394        gRenderTreeFile = fopen(RENDER_TREE_LOG_FILE, "w");
1395        DUMP_RENDER_LOGD("%s", data);
1396        fclose(gRenderTreeFile);
1397        gRenderTreeFile = 0;
1398    } else {
1399        // adb log can only output 1024 characters, so write out line by line.
1400        // exclude '\n' as adb log adds it for each output.
1401        int length = renderDump.length();
1402        for (int i = 0, last = 0; i < length; i++) {
1403            if (data[i] == '\n') {
1404                if (i != last)
1405                    DUMP_RENDER_LOGD("%.*s", (i - last), &(data[last]));
1406                last = i + 1;
1407            }
1408        }
1409    }
1410#endif
1411}
1412
1413void WebViewCore::dumpNavTree()
1414{
1415#if DUMP_NAV_CACHE
1416    cacheBuilder().mDebug.print();
1417#endif
1418}
1419
1420HTMLElement* WebViewCore::retrieveElement(int x, int y,
1421    const QualifiedName& tagName)
1422{
1423    HitTestResult hitTestResult = m_mainFrame->eventHandler()
1424        ->hitTestResultAtPoint(IntPoint(x, y), false, false,
1425        DontHitTestScrollbars, HitTestRequest::Active | HitTestRequest::ReadOnly,
1426        IntSize(1, 1));
1427    if (!hitTestResult.innerNode() || !hitTestResult.innerNode()->inDocument()) {
1428        ALOGE("Should not happen: no in document Node found");
1429        return 0;
1430    }
1431    const ListHashSet<RefPtr<Node> >& list = hitTestResult.rectBasedTestResult();
1432    if (list.isEmpty()) {
1433        ALOGE("Should not happen: no rect-based-test nodes found");
1434        return 0;
1435    }
1436    Node* node = hitTestResult.innerNode();
1437    Node* element = node;
1438    while (element && (!element->isElementNode()
1439        || !element->hasTagName(tagName))) {
1440        element = element->parentNode();
1441    }
1442    DBG_NAV_LOGD("node=%p element=%p x=%d y=%d nodeName=%s tagName=%s", node,
1443        element, x, y, node->nodeName().utf8().data(),
1444        element ? ((Element*) element)->tagName().utf8().data() : "<none>");
1445    return static_cast<WebCore::HTMLElement*>(element);
1446}
1447
1448HTMLAnchorElement* WebViewCore::retrieveAnchorElement(int x, int y)
1449{
1450    return static_cast<HTMLAnchorElement*>
1451        (retrieveElement(x, y, HTMLNames::aTag));
1452}
1453
1454HTMLImageElement* WebViewCore::retrieveImageElement(int x, int y)
1455{
1456    return static_cast<HTMLImageElement*>
1457        (retrieveElement(x, y, HTMLNames::imgTag));
1458}
1459
1460WTF::String WebViewCore::retrieveHref(int x, int y)
1461{
1462    // TODO: This is expensive, cache
1463    HitTestResult result = m_mainFrame->eventHandler()->hitTestResultAtPoint(IntPoint(x, y),
1464                false, false, DontHitTestScrollbars, HitTestRequest::Active | HitTestRequest::ReadOnly, IntSize(1, 1));
1465    return result.absoluteLinkURL();
1466}
1467
1468WTF::String WebViewCore::retrieveAnchorText(int x, int y)
1469{
1470    WebCore::HTMLAnchorElement* anchor = retrieveAnchorElement(x, y);
1471    return anchor ? anchor->text() : WTF::String();
1472}
1473
1474WTF::String WebViewCore::retrieveImageSource(int x, int y)
1475{
1476    // TODO: This is expensive, cache
1477    HitTestResult result = m_mainFrame->eventHandler()->hitTestResultAtPoint(IntPoint(x, y),
1478                false, false, DontHitTestScrollbars, HitTestRequest::Active | HitTestRequest::ReadOnly, IntSize(1, 1));
1479    return result.absoluteImageURL();
1480}
1481
1482WTF::String WebViewCore::requestLabel(WebCore::Frame* frame,
1483        WebCore::Node* node)
1484{
1485    if (node && CacheBuilder::validNode(m_mainFrame, frame, node)) {
1486        RefPtr<WebCore::NodeList> list = node->document()->getElementsByTagName("label");
1487        unsigned length = list->length();
1488        for (unsigned i = 0; i < length; i++) {
1489            WebCore::HTMLLabelElement* label = static_cast<WebCore::HTMLLabelElement*>(
1490                    list->item(i));
1491            if (label->control() == node) {
1492                Node* node = label;
1493                String result;
1494                while ((node = node->traverseNextNode(label))) {
1495                    if (node->isTextNode()) {
1496                        Text* textNode = static_cast<Text*>(node);
1497                        result += textNode->dataImpl();
1498                    }
1499                }
1500                return result;
1501            }
1502        }
1503    }
1504    return WTF::String();
1505}
1506
1507static bool isContentEditable(const WebCore::Node* node)
1508{
1509    if (!node)
1510        return false;
1511    Frame* frame = node->document()->frame();
1512    if (!frame)
1513        return false;
1514    return frame->selection()->isContentEditable();
1515}
1516
1517// Returns true if the node is a textfield, textarea, or contentEditable
1518static bool isTextInput(const WebCore::Node* node)
1519{
1520    if (isContentEditable(node))
1521        return true;
1522    if (!node)
1523        return false;
1524    WebCore::RenderObject* renderer = node->renderer();
1525    return renderer && (renderer->isTextField() || renderer->isTextArea());
1526}
1527
1528void WebViewCore::revealSelection()
1529{
1530    WebCore::Node* focus = currentFocus();
1531    if (!focus)
1532        return;
1533    if (!isTextInput(focus))
1534        return;
1535    WebCore::Frame* focusedFrame = focus->document()->frame();
1536    if (!focusedFrame->page()->focusController()->isActive())
1537        return;
1538    focusedFrame->selection()->revealSelection(ScrollAlignment::alignToEdgeIfNeeded);
1539}
1540
1541#if ENABLE(ANDROID_NAVCACHE)
1542void WebViewCore::updateCacheOnNodeChange()
1543{
1544    gCursorBoundsMutex.lock();
1545    bool hasCursorBounds = m_hasCursorBounds;
1546    Frame* frame = (Frame*) m_cursorFrame;
1547    Node* node = (Node*) m_cursorNode;
1548    IntRect bounds = m_cursorHitBounds;
1549    gCursorBoundsMutex.unlock();
1550    if (!hasCursorBounds || !node)
1551        return;
1552    if (CacheBuilder::validNode(m_mainFrame, frame, node)) {
1553        RenderObject* renderer = node->renderer();
1554        if (renderer && renderer->style()->visibility() != HIDDEN) {
1555            IntRect absBox = renderer->absoluteBoundingBoxRect();
1556            int globalX, globalY;
1557            CacheBuilder::GetGlobalOffset(frame, &globalX, &globalY);
1558            absBox.move(globalX, globalY);
1559            if (absBox == bounds)
1560                return;
1561            DBG_NAV_LOGD("absBox=(%d,%d,%d,%d) bounds=(%d,%d,%d,%d)",
1562                absBox.x(), absBox.y(), absBox.width(), absBox.height(),
1563                bounds.x(), bounds.y(), bounds.width(), bounds.height());
1564        }
1565    }
1566    DBG_NAV_LOGD("updateFrameCache node=%p", node);
1567    updateFrameCache();
1568}
1569
1570void WebViewCore::updateFrameCache()
1571{
1572    if (!m_frameCacheOutOfDate) {
1573        DBG_NAV_LOG("!m_frameCacheOutOfDate");
1574        return;
1575    }
1576
1577    // If there is a pending style recalculation, do not update the frame cache.
1578    // Until the recalculation is complete, there may be internal objects that
1579    // are in an inconsistent state (such as font pointers).
1580    // In any event, there's not much point to updating the cache while a style
1581    // recalculation is pending, since it will simply have to be updated again
1582    // once the recalculation is complete.
1583    // TODO: Do we need to reschedule an update for after the style is recalculated?
1584    if (m_mainFrame && m_mainFrame->document() && m_mainFrame->document()->isPendingStyleRecalc()) {
1585        ALOGW("updateFrameCache: pending style recalc, ignoring.");
1586        return;
1587    }
1588    m_frameCacheOutOfDate = false;
1589    m_temp = new CachedRoot();
1590    m_temp->init(m_mainFrame, &m_history);
1591#if USE(ACCELERATED_COMPOSITING)
1592    GraphicsLayerAndroid* graphicsLayer = graphicsRootLayer();
1593    if (graphicsLayer)
1594        m_temp->setRootLayer(graphicsLayer->contentLayer());
1595#endif
1596    CacheBuilder& builder = cacheBuilder();
1597    WebCore::Settings* settings = m_mainFrame->page()->settings();
1598    builder.allowAllTextDetection();
1599#ifdef ANDROID_META_SUPPORT
1600    if (settings) {
1601        if (!settings->formatDetectionAddress())
1602            builder.disallowAddressDetection();
1603        if (!settings->formatDetectionEmail())
1604            builder.disallowEmailDetection();
1605        if (!settings->formatDetectionTelephone())
1606            builder.disallowPhoneDetection();
1607    }
1608#endif
1609    builder.buildCache(m_temp);
1610    m_tempPict = new SkPicture();
1611    recordPicture(m_tempPict);
1612    m_temp->setPicture(m_tempPict);
1613    m_temp->setTextGeneration(m_textGeneration);
1614    WebCoreViewBridge* window = m_mainFrame->view()->platformWidget();
1615    m_temp->setVisibleRect(WebCore::IntRect(m_scrollOffsetX,
1616        m_scrollOffsetY, window->width(), window->height()));
1617    gFrameCacheMutex.lock();
1618    delete m_frameCacheKit;
1619    delete m_navPictureKit;
1620    m_frameCacheKit = m_temp;
1621    m_navPictureKit = m_tempPict;
1622    m_updatedFrameCache = true;
1623#if DEBUG_NAV_UI
1624    const CachedNode* cachedFocusNode = m_frameCacheKit->currentFocus();
1625    DBG_NAV_LOGD("cachedFocusNode=%d (nodePointer=%p)",
1626        cachedFocusNode ? cachedFocusNode->index() : 0,
1627        cachedFocusNode ? cachedFocusNode->nodePointer() : 0);
1628#endif
1629    gFrameCacheMutex.unlock();
1630}
1631
1632void WebViewCore::updateFrameCacheIfLoading()
1633{
1634    if (!m_check_domtree_version)
1635        updateFrameCache();
1636}
1637#endif
1638
1639struct TouchNodeData {
1640    Node* mNode;
1641    IntRect mBounds;
1642};
1643
1644// get the bounding box of the Node
1645static IntRect getAbsoluteBoundingBox(Node* node) {
1646    IntRect rect;
1647    RenderObject* render = node->renderer();
1648    if (!render)
1649        return rect;
1650    if (render->isRenderInline())
1651        rect = toRenderInline(render)->linesVisualOverflowBoundingBox();
1652    else if (render->isBox())
1653        rect = toRenderBox(render)->visualOverflowRect();
1654    else if (render->isText())
1655        rect = toRenderText(render)->linesBoundingBox();
1656    else
1657        ALOGE("getAbsoluteBoundingBox failed for node %p, name %s", node, render->renderName());
1658    FloatPoint absPos = render->localToAbsolute();
1659    rect.move(absPos.x(), absPos.y());
1660    return rect;
1661}
1662
1663// get the highlight rectangles for the touch point (x, y) with the slop
1664Vector<IntRect> WebViewCore::getTouchHighlightRects(int x, int y, int slop,
1665                                                    Node** node, HitTestResult* hitTestResult)
1666{
1667    Vector<IntRect> rects;
1668    moveMouse(m_mainFrame, x, y);
1669    *hitTestResult = m_mainFrame->eventHandler()->hitTestResultAtPoint(IntPoint(x, y),
1670            false, false, DontHitTestScrollbars, HitTestRequest::Active | HitTestRequest::ReadOnly, IntSize(slop, slop));
1671    if (!hitTestResult->innerNode() || !hitTestResult->innerNode()->inDocument()) {
1672        ALOGE("Should not happen: no in document Node found");
1673        return rects;
1674    }
1675    const ListHashSet<RefPtr<Node> >& list = hitTestResult->rectBasedTestResult();
1676    if (list.isEmpty()) {
1677        ALOGE("Should not happen: no rect-based-test nodes found");
1678        return rects;
1679    }
1680    Frame* frame = hitTestResult->innerNode()->document()->frame();
1681    Vector<TouchNodeData> nodeDataList;
1682    if (hitTestResult->innerNode() != hitTestResult->innerNonSharedNode()
1683            && hitTestResult->innerNode()->hasTagName(WebCore::HTMLNames::areaTag)) {
1684        TouchNodeData newNode;
1685        HTMLAreaElement* area = static_cast<HTMLAreaElement*>(hitTestResult->innerNode());
1686        newNode.mNode = area;
1687        newNode.mBounds = area->computeRect(hitTestResult->innerNonSharedNode()->renderer());
1688        nodeDataList.append(newNode);
1689    } else {
1690        ListHashSet<RefPtr<Node> >::const_iterator last = list.end();
1691        for (ListHashSet<RefPtr<Node> >::const_iterator it = list.begin(); it != last; ++it) {
1692            // TODO: it seems reasonable to not search across the frame. Isn't it?
1693            // if the node is not in the same frame as the innerNode, skip it
1694            if (it->get()->document()->frame() != frame)
1695                continue;
1696            // traverse up the tree to find the first node that needs highlight
1697            bool found = false;
1698            Node* eventNode = it->get();
1699            while (eventNode) {
1700                RenderObject* render = eventNode->renderer();
1701                if (render && (render->isBody() || render->isRenderView()))
1702                    break;
1703                if (eventNode->supportsFocus()
1704                        || eventNode->hasEventListeners(eventNames().clickEvent)
1705                        || eventNode->hasEventListeners(eventNames().mousedownEvent)
1706                        || eventNode->hasEventListeners(eventNames().mouseupEvent)
1707                        || eventNode->hasEventListeners(eventNames().mouseoverEvent)) {
1708                    found = true;
1709                    break;
1710                }
1711                // the nodes in the rectBasedTestResult() are ordered based on z-index during hit testing.
1712                // so do not search for the eventNode across explicit z-index border.
1713                // TODO: this is a hard one to call. z-index is quite complicated as its value only
1714                // matters when you compare two RenderLayer in the same hierarchy level. e.g. in
1715                // the following example, "b" is on the top as its z level is the highest. even "c"
1716                // has 100 as z-index, it is still below "d" as its parent has the same z-index as
1717                // "d" and logically before "d". Of course "a" is the lowest in the z level.
1718                //
1719                // z-index:auto "a"
1720                //   z-index:2 "b"
1721                //   z-index:1
1722                //     z-index:100 "c"
1723                //   z-index:1 "d"
1724                //
1725                // If the fat point touches everyone, the order in the list should be "b", "d", "c"
1726                // and "a". When we search for the event node for "b", we really don't want "a" as
1727                // in the z-order it is behind everything else.
1728                if (render && !render->style()->hasAutoZIndex())
1729                    break;
1730                eventNode = eventNode->parentNode();
1731            }
1732            // didn't find any eventNode, skip it
1733            if (!found)
1734                continue;
1735            // first quick check whether it is a duplicated node before computing bounding box
1736            Vector<TouchNodeData>::const_iterator nlast = nodeDataList.end();
1737            for (Vector<TouchNodeData>::const_iterator n = nodeDataList.begin(); n != nlast; ++n) {
1738                // found the same node, skip it
1739                if (eventNode == n->mNode) {
1740                    found = false;
1741                    break;
1742                }
1743            }
1744            if (!found)
1745                continue;
1746            // next check whether the node is fully covered by or fully covering another node.
1747            found = false;
1748            IntRect rect = getAbsoluteBoundingBox(eventNode);
1749            if (rect.isEmpty()) {
1750                // if the node's bounds is empty and it is not a ContainerNode, skip it.
1751                if (!eventNode->isContainerNode())
1752                    continue;
1753                // if the node's children are all positioned objects, its bounds can be empty.
1754                // Walk through the children to find the bounding box.
1755                Node* child = static_cast<const ContainerNode*>(eventNode)->firstChild();
1756                while (child) {
1757                    IntRect childrect;
1758                    if (child->renderer())
1759                        childrect = getAbsoluteBoundingBox(child);
1760                    if (!childrect.isEmpty()) {
1761                        rect.unite(childrect);
1762                        child = child->traverseNextSibling(eventNode);
1763                    } else
1764                        child = child->traverseNextNode(eventNode);
1765                }
1766            }
1767            for (int i = nodeDataList.size() - 1; i >= 0; i--) {
1768                TouchNodeData n = nodeDataList.at(i);
1769                // the new node is enclosing an existing node, skip it
1770                if (rect.contains(n.mBounds)) {
1771                    found = true;
1772                    break;
1773                }
1774                // the new node is fully inside an existing node, remove the existing node
1775                if (n.mBounds.contains(rect))
1776                    nodeDataList.remove(i);
1777            }
1778            if (!found) {
1779                TouchNodeData newNode;
1780                newNode.mNode = eventNode;
1781                newNode.mBounds = rect;
1782                nodeDataList.append(newNode);
1783            }
1784        }
1785        if (!nodeDataList.size()) {
1786            *node = 0;
1787            return rects;
1788        }
1789    }
1790    // finally select the node with the largest overlap with the fat point
1791    TouchNodeData final;
1792    final.mNode = 0;
1793    IntPoint docPos = frame->view()->windowToContents(m_mousePos);
1794    IntRect testRect(docPos.x() - slop, docPos.y() - slop, 2 * slop + 1, 2 * slop + 1);
1795    int area = 0;
1796    Vector<TouchNodeData>::const_iterator nlast = nodeDataList.end();
1797    for (Vector<TouchNodeData>::const_iterator n = nodeDataList.begin(); n != nlast; ++n) {
1798        IntRect rect = n->mBounds;
1799        rect.intersect(testRect);
1800        int a = rect.width() * rect.height();
1801        if (a > area) {
1802            final = *n;
1803            area = a;
1804        }
1805    }
1806    // now get the node's highlight rectangles in the page coordinate system
1807    if (final.mNode) {
1808        if (final.mNode->isElementNode()) {
1809            // We found a URL element. Update the hitTestResult
1810            *node = final.mNode;
1811            hitTestResult->setURLElement(static_cast<Element*>(final.mNode));
1812        }
1813        IntPoint frameAdjust;
1814        if (frame != m_mainFrame) {
1815            frameAdjust = frame->view()->contentsToWindow(IntPoint());
1816            frameAdjust.move(m_scrollOffsetX, m_scrollOffsetY);
1817        }
1818        if (final.mNode->isLink() && final.mNode->renderer()) {
1819            // most of the links are inline instead of box style. So the bounding box is not
1820            // a good representation for the highlights. Get the list of rectangles instead.
1821            RenderObject* render = final.mNode->renderer();
1822            IntPoint offset = roundedIntPoint(render->localToAbsolute());
1823            render->absoluteRects(rects, offset.x() + frameAdjust.x(), offset.y() + frameAdjust.y());
1824            bool inside = false;
1825            int distance = INT_MAX;
1826            int newx = x, newy = y;
1827            int i = rects.size();
1828            while (i--) {
1829                if (rects[i].isEmpty()) {
1830                    rects.remove(i);
1831                    continue;
1832                }
1833                // check whether the point (x, y) is inside one of the rectangles.
1834                if (inside)
1835                    continue;
1836                if (rects[i].contains(x, y)) {
1837                    inside = true;
1838                    continue;
1839                }
1840                if (x >= rects[i].x() && x < rects[i].maxX()) {
1841                    if (y < rects[i].y()) {
1842                        if (rects[i].y() - y < distance) {
1843                            newx = x;
1844                            newy = rects[i].y();
1845                            distance = rects[i].y() - y;
1846                        }
1847                    } else if (y >= rects[i].maxY()) {
1848                        if (y - rects[i].maxY() + 1 < distance) {
1849                            newx = x;
1850                            newy = rects[i].maxY() - 1;
1851                            distance = y - rects[i].maxY() + 1;
1852                        }
1853                    }
1854                } else if (y >= rects[i].y() && y < rects[i].maxY()) {
1855                    if (x < rects[i].x()) {
1856                        if (rects[i].x() - x < distance) {
1857                            newx = rects[i].x();
1858                            newy = y;
1859                            distance = rects[i].x() - x;
1860                        }
1861                    } else if (x >= rects[i].maxX()) {
1862                        if (x - rects[i].maxX() + 1 < distance) {
1863                            newx = rects[i].maxX() - 1;
1864                            newy = y;
1865                            distance = x - rects[i].maxX() + 1;
1866                        }
1867                    }
1868                }
1869            }
1870            if (!rects.isEmpty()) {
1871                if (!inside) {
1872                    // if neither x nor y has overlap, just pick the top/left of the first rectangle
1873                    if (newx == x && newy == y) {
1874                        newx = rects[0].x();
1875                        newy = rects[0].y();
1876                    }
1877                    moveMouse(m_mainFrame, newx, newy);
1878                    DBG_NAV_LOGD("Move x/y from (%d, %d) to (%d, %d) scrollOffset is (%d, %d)",
1879                            x, y, m_mousePos.x() + m_scrollOffsetX, m_mousePos.y() + m_scrollOffsetY,
1880                            m_scrollOffsetX, m_scrollOffsetY);
1881                }
1882                return rects;
1883            }
1884        }
1885        IntRect rect = final.mBounds;
1886        rect.move(frameAdjust.x(), frameAdjust.y());
1887        rects.append(rect);
1888        // adjust m_mousePos if it is not inside the returned highlight rectangle
1889        testRect.move(frameAdjust.x(), frameAdjust.y());
1890        testRect.intersect(rect);
1891        if (!testRect.contains(x, y)) {
1892            moveMouse(m_mainFrame, testRect.center().x(), testRect.center().y());
1893            DBG_NAV_LOGD("Move x/y from (%d, %d) to (%d, %d) scrollOffset is (%d, %d)",
1894                    x, y, m_mousePos.x() + m_scrollOffsetX, m_mousePos.y() + m_scrollOffsetY,
1895                    m_scrollOffsetX, m_scrollOffsetY);
1896        }
1897    }
1898    return rects;
1899}
1900
1901///////////////////////////////////////////////////////////////////////////////
1902
1903void WebViewCore::addPlugin(PluginWidgetAndroid* w)
1904{
1905//    SkDebugf("----------- addPlugin %p", w);
1906    /* The plugin must be appended to the end of the array. This ensures that if
1907       the plugin is added while iterating through the array (e.g. sendEvent(...))
1908       that the iteration process is not corrupted.
1909     */
1910    *m_plugins.append() = w;
1911}
1912
1913void WebViewCore::removePlugin(PluginWidgetAndroid* w)
1914{
1915//    SkDebugf("----------- removePlugin %p", w);
1916    int index = m_plugins.find(w);
1917    if (index < 0) {
1918        SkDebugf("--------------- pluginwindow not found! %p\n", w);
1919    } else {
1920        m_plugins.removeShuffle(index);
1921    }
1922}
1923
1924bool WebViewCore::isPlugin(PluginWidgetAndroid* w) const
1925{
1926    return m_plugins.find(w) >= 0;
1927}
1928
1929void WebViewCore::invalPlugin(PluginWidgetAndroid* w)
1930{
1931    const double PLUGIN_INVAL_DELAY = 1.0 / 60;
1932
1933    if (!m_pluginInvalTimer.isActive()) {
1934        m_pluginInvalTimer.startOneShot(PLUGIN_INVAL_DELAY);
1935    }
1936}
1937
1938void WebViewCore::drawPlugins()
1939{
1940    SkRegion inval; // accumualte what needs to be redrawn
1941    PluginWidgetAndroid** iter = m_plugins.begin();
1942    PluginWidgetAndroid** stop = m_plugins.end();
1943
1944    for (; iter < stop; ++iter) {
1945        PluginWidgetAndroid* w = *iter;
1946        SkIRect dirty;
1947        if (w->isDirty(&dirty)) {
1948            w->draw();
1949            inval.op(dirty, SkRegion::kUnion_Op);
1950        }
1951    }
1952
1953    if (!inval.isEmpty()) {
1954        // inval.getBounds() is our rectangle
1955        const SkIRect& bounds = inval.getBounds();
1956        WebCore::IntRect r(bounds.fLeft, bounds.fTop,
1957                           bounds.width(), bounds.height());
1958        this->viewInvalidate(r);
1959    }
1960}
1961
1962void WebViewCore::notifyPluginsOnFrameLoad(const Frame* frame) {
1963    // if frame is the parent then notify all plugins
1964    if (!frame->tree()->parent()) {
1965        // trigger an event notifying the plugins that the page has loaded
1966        ANPEvent event;
1967        SkANP::InitEvent(&event, kLifecycle_ANPEventType);
1968        event.data.lifecycle.action = kOnLoad_ANPLifecycleAction;
1969        sendPluginEvent(event);
1970        // trigger the on/off screen notification if the page was reloaded
1971        sendPluginVisibleScreen();
1972    }
1973    // else if frame's parent has completed
1974    else if (!frame->tree()->parent()->loader()->isLoading()) {
1975        // send to all plugins who have this frame in their heirarchy
1976        PluginWidgetAndroid** iter = m_plugins.begin();
1977        PluginWidgetAndroid** stop = m_plugins.end();
1978        for (; iter < stop; ++iter) {
1979            Frame* currentFrame = (*iter)->pluginView()->parentFrame();
1980            while (currentFrame) {
1981                if (frame == currentFrame) {
1982                    ANPEvent event;
1983                    SkANP::InitEvent(&event, kLifecycle_ANPEventType);
1984                    event.data.lifecycle.action = kOnLoad_ANPLifecycleAction;
1985                    (*iter)->sendEvent(event);
1986
1987                    // trigger the on/off screen notification if the page was reloaded
1988                    ANPRectI visibleRect;
1989                    getVisibleScreen(visibleRect);
1990                    (*iter)->setVisibleScreen(visibleRect, m_scale);
1991
1992                    break;
1993                }
1994                currentFrame = currentFrame->tree()->parent();
1995            }
1996        }
1997    }
1998}
1999
2000void WebViewCore::getVisibleScreen(ANPRectI& visibleRect)
2001{
2002    visibleRect.left = m_scrollOffsetX;
2003    visibleRect.top = m_scrollOffsetY;
2004    visibleRect.right = m_scrollOffsetX + m_screenWidth;
2005    visibleRect.bottom = m_scrollOffsetY + m_screenHeight;
2006}
2007
2008void WebViewCore::sendPluginVisibleScreen()
2009{
2010    /* We may want to cache the previous values and only send the notification
2011       to the plugin in the event that one of the values has changed.
2012     */
2013
2014    ANPRectI visibleRect;
2015    getVisibleScreen(visibleRect);
2016
2017    PluginWidgetAndroid** iter = m_plugins.begin();
2018    PluginWidgetAndroid** stop = m_plugins.end();
2019    for (; iter < stop; ++iter) {
2020        (*iter)->setVisibleScreen(visibleRect, m_scale);
2021    }
2022}
2023
2024void WebViewCore::sendPluginSurfaceReady()
2025{
2026    PluginWidgetAndroid** iter = m_plugins.begin();
2027    PluginWidgetAndroid** stop = m_plugins.end();
2028    for (; iter < stop; ++iter) {
2029        (*iter)->checkSurfaceReady();
2030    }
2031}
2032
2033void WebViewCore::sendPluginEvent(const ANPEvent& evt)
2034{
2035    /* The list of plugins may be manipulated as we iterate through the list.
2036       This implementation allows for the addition of new plugins during an
2037       iteration, but may fail if a plugin is removed. Currently, there are not
2038       any use cases where a plugin is deleted while processing this loop, but
2039       if it does occur we will have to use an alternate data structure and/or
2040       iteration mechanism.
2041     */
2042    for (int x = 0; x < m_plugins.count(); x++) {
2043        m_plugins[x]->sendEvent(evt);
2044    }
2045}
2046
2047PluginWidgetAndroid* WebViewCore::getPluginWidget(NPP npp)
2048{
2049    PluginWidgetAndroid** iter = m_plugins.begin();
2050    PluginWidgetAndroid** stop = m_plugins.end();
2051    for (; iter < stop; ++iter) {
2052        if ((*iter)->pluginView()->instance() == npp) {
2053            return (*iter);
2054        }
2055    }
2056    return 0;
2057}
2058
2059static PluginView* nodeIsPlugin(Node* node) {
2060    RenderObject* renderer = node->renderer();
2061    if (renderer && renderer->isWidget()) {
2062        Widget* widget = static_cast<RenderWidget*>(renderer)->widget();
2063        if (widget && widget->isPluginView())
2064            return static_cast<PluginView*>(widget);
2065    }
2066    return 0;
2067}
2068
2069Node* WebViewCore::cursorNodeIsPlugin() {
2070    gCursorBoundsMutex.lock();
2071    bool hasCursorBounds = m_hasCursorBounds;
2072    Frame* frame = (Frame*) m_cursorFrame;
2073    Node* node = (Node*) m_cursorNode;
2074    gCursorBoundsMutex.unlock();
2075    if (hasCursorBounds && CacheBuilder::validNode(m_mainFrame, frame, node)
2076            && nodeIsPlugin(node)) {
2077        return node;
2078    }
2079    return 0;
2080}
2081
2082///////////////////////////////////////////////////////////////////////////////
2083void WebViewCore::moveMouseIfLatest(int moveGeneration,
2084    WebCore::Frame* frame, int x, int y)
2085{
2086    DBG_NAV_LOGD("m_moveGeneration=%d moveGeneration=%d"
2087        " frame=%p x=%d y=%d",
2088        m_moveGeneration, moveGeneration, frame, x, y);
2089    if (m_moveGeneration > moveGeneration) {
2090        DBG_NAV_LOGD("m_moveGeneration=%d > moveGeneration=%d",
2091            m_moveGeneration, moveGeneration);
2092        return; // short-circuit if a newer move has already been generated
2093    }
2094    m_lastGeneration = moveGeneration;
2095    moveMouse(frame, x, y);
2096}
2097
2098void WebViewCore::moveFocus(WebCore::Frame* frame, WebCore::Node* node)
2099{
2100    DBG_NAV_LOGD("frame=%p node=%p", frame, node);
2101    if (!node || !CacheBuilder::validNode(m_mainFrame, frame, node)
2102            || !node->isElementNode())
2103        return;
2104    // Code borrowed from FocusController::advanceFocus
2105    WebCore::FocusController* focusController
2106            = m_mainFrame->page()->focusController();
2107    WebCore::Document* oldDoc
2108            = focusController->focusedOrMainFrame()->document();
2109    if (oldDoc->focusedNode() == node)
2110        return;
2111    if (node->document() != oldDoc)
2112        oldDoc->setFocusedNode(0);
2113    focusController->setFocusedFrame(frame);
2114    static_cast<WebCore::Element*>(node)->focus(false);
2115}
2116
2117// Update mouse position
2118void WebViewCore::moveMouse(WebCore::Frame* frame, int x, int y)
2119{
2120    DBG_NAV_LOGD("frame=%p x=%d y=%d scrollOffset=(%d,%d)", frame,
2121        x, y, m_scrollOffsetX, m_scrollOffsetY);
2122    if (!frame || !CacheBuilder::validNode(m_mainFrame, frame, 0))
2123        frame = m_mainFrame;
2124    // mouse event expects the position in the window coordinate
2125    m_mousePos = WebCore::IntPoint(x - m_scrollOffsetX, y - m_scrollOffsetY);
2126    // validNode will still return true if the node is null, as long as we have
2127    // a valid frame.  Do not want to make a call on frame unless it is valid.
2128    WebCore::PlatformMouseEvent mouseEvent(m_mousePos, m_mousePos,
2129        WebCore::NoButton, WebCore::MouseEventMoved, 1, false, false, false,
2130        false, WTF::currentTime());
2131    frame->eventHandler()->handleMouseMoveEvent(mouseEvent);
2132#if ENABLE(ANDROID_NAVCACHE)
2133    updateCacheOnNodeChange();
2134#endif
2135}
2136
2137void WebViewCore::setSelection(int start, int end)
2138{
2139    WebCore::Node* focus = currentFocus();
2140    if (!focus)
2141        return;
2142    WebCore::RenderObject* renderer = focus->renderer();
2143    if (!renderer || (!renderer->isTextField() && !renderer->isTextArea()))
2144        return;
2145    if (start > end) {
2146        int temp = start;
2147        start = end;
2148        end = temp;
2149    }
2150    // Tell our EditorClient that this change was generated from the UI, so it
2151    // does not need to echo it to the UI.
2152    EditorClientAndroid* client = static_cast<EditorClientAndroid*>(
2153            m_mainFrame->editor()->client());
2154    client->setUiGeneratedSelectionChange(true);
2155    setSelectionRange(focus, start, end);
2156    if (start != end) {
2157        // Fire a select event. No event is sent when the selection reduces to
2158        // an insertion point
2159        RenderTextControl* control = toRenderTextControl(renderer);
2160        control->selectionChanged(true);
2161    }
2162    client->setUiGeneratedSelectionChange(false);
2163    WebCore::Frame* focusedFrame = focus->document()->frame();
2164    bool isPasswordField = false;
2165    if (focus->isElementNode()) {
2166        WebCore::Element* element = static_cast<WebCore::Element*>(focus);
2167        if (WebCore::InputElement* inputElement = element->toInputElement())
2168            isPasswordField = static_cast<WebCore::HTMLInputElement*>(inputElement)->isPasswordField();
2169    }
2170    // For password fields, this is done in the UI side via
2171    // bringPointIntoView, since the UI does the drawing.
2172    if (renderer->isTextArea() || !isPasswordField)
2173        revealSelection();
2174}
2175
2176String WebViewCore::modifySelection(const int direction, const int axis)
2177{
2178    DOMSelection* selection = m_mainFrame->domWindow()->getSelection();
2179    ASSERT(selection);
2180    // We've seen crashes where selection is null, but we don't know why
2181    // See http://b/5244036
2182    if (!selection)
2183        return String();
2184    if (selection->rangeCount() > 1)
2185        selection->removeAllRanges();
2186    switch (axis) {
2187        case AXIS_CHARACTER:
2188        case AXIS_WORD:
2189        case AXIS_SENTENCE:
2190            return modifySelectionTextNavigationAxis(selection, direction, axis);
2191        case AXIS_HEADING:
2192        case AXIS_SIBLING:
2193        case AXIS_PARENT_FIRST_CHILD:
2194        case AXIS_DOCUMENT:
2195            return modifySelectionDomNavigationAxis(selection, direction, axis);
2196        default:
2197            ALOGE("Invalid navigation axis: %d", axis);
2198            return String();
2199    }
2200}
2201
2202void WebViewCore::scrollNodeIntoView(Frame* frame, Node* node)
2203{
2204    if (!frame || !node)
2205        return;
2206
2207    Element* elementNode = 0;
2208
2209    // If not an Element, find a visible predecessor
2210    // Element to scroll into view.
2211    if (!node->isElementNode()) {
2212        HTMLElement* body = frame->document()->body();
2213        do {
2214            if (node == body)
2215                return;
2216            node = node->parentNode();
2217        } while (node && !node->isElementNode() && !isVisible(node));
2218    }
2219
2220    // Couldn't find a visible predecessor.
2221    if (!node)
2222        return;
2223
2224    elementNode = static_cast<Element*>(node);
2225    elementNode->scrollIntoViewIfNeeded(true);
2226}
2227
2228String WebViewCore::modifySelectionTextNavigationAxis(DOMSelection* selection, int direction, int axis)
2229{
2230    Node* body = m_mainFrame->document()->body();
2231
2232    ExceptionCode ec = 0;
2233    String markup;
2234
2235    // initialize the selection if necessary
2236    if (selection->rangeCount() == 0) {
2237        if (m_currentNodeDomNavigationAxis
2238                && CacheBuilder::validNode(m_mainFrame,
2239                m_mainFrame, m_currentNodeDomNavigationAxis)) {
2240            RefPtr<Range> rangeRef =
2241                selection->frame()->document()->createRange();
2242            rangeRef->selectNode(m_currentNodeDomNavigationAxis, ec);
2243            m_currentNodeDomNavigationAxis = 0;
2244            if (ec)
2245                return String();
2246            selection->addRange(rangeRef.get());
2247        } else if (currentFocus()) {
2248            selection->setPosition(currentFocus(), 0, ec);
2249        } else if (m_cursorNode
2250                && CacheBuilder::validNode(m_mainFrame,
2251                m_mainFrame, m_cursorNode)) {
2252            RefPtr<Range> rangeRef =
2253                selection->frame()->document()->createRange();
2254            rangeRef->selectNode(reinterpret_cast<Node*>(m_cursorNode), ec);
2255            if (ec)
2256                return String();
2257            selection->addRange(rangeRef.get());
2258        } else {
2259            selection->setPosition(body, 0, ec);
2260        }
2261        if (ec)
2262            return String();
2263    }
2264
2265    // collapse the selection
2266    if (direction == DIRECTION_FORWARD)
2267        selection->collapseToEnd(ec);
2268    else
2269        selection->collapseToStart(ec);
2270    if (ec)
2271        return String();
2272
2273    // Make sure the anchor node is a text node since we are generating
2274    // the markup of the selection which includes the anchor, the focus,
2275    // and any crossed nodes. Forcing the condition that the selection
2276    // starts and ends on text nodes guarantees symmetric selection markup.
2277    // Also this way the text content, rather its container, is highlighted.
2278    Node* anchorNode = selection->anchorNode();
2279    if (anchorNode->isElementNode()) {
2280        // Collapsed selection while moving forward points to the
2281        // next unvisited node and while moving backward to the
2282        // last visited node.
2283        if (direction == DIRECTION_FORWARD)
2284            advanceAnchorNode(selection, direction, markup, false, ec);
2285        else
2286            advanceAnchorNode(selection, direction, markup, true, ec);
2287        if (ec)
2288            return String();
2289        if (!markup.isEmpty())
2290            return markup;
2291    }
2292
2293    // If the selection is at the end of a non white space text move
2294    // it to the next visible text node with non white space content.
2295    // This is a workaround for the selection getting stuck.
2296    anchorNode = selection->anchorNode();
2297    if (anchorNode->isTextNode()) {
2298        if (direction == DIRECTION_FORWARD) {
2299            String suffix = anchorNode->textContent().substring(
2300                    selection->anchorOffset(), caretMaxOffset(anchorNode));
2301            // If at the end of non white space text we advance the
2302            // anchor node to either an input element or non empty text.
2303            if (suffix.stripWhiteSpace().isEmpty()) {
2304                advanceAnchorNode(selection, direction, markup, true, ec);
2305            }
2306        } else {
2307            String prefix = anchorNode->textContent().substring(0,
2308                    selection->anchorOffset());
2309            // If at the end of non white space text we advance the
2310            // anchor node to either an input element or non empty text.
2311            if (prefix.stripWhiteSpace().isEmpty()) {
2312                advanceAnchorNode(selection, direction, markup, true, ec);
2313            }
2314        }
2315        if (ec)
2316            return String();
2317        if (!markup.isEmpty())
2318            return markup;
2319    }
2320
2321    // extend the selection
2322    String directionStr;
2323    if (direction == DIRECTION_FORWARD)
2324        directionStr = "forward";
2325    else
2326        directionStr = "backward";
2327
2328    String axisStr;
2329    if (axis == AXIS_CHARACTER)
2330        axisStr = "character";
2331    else if (axis == AXIS_WORD)
2332        axisStr = "word";
2333    else
2334        axisStr = "sentence";
2335
2336    selection->modify("extend", directionStr, axisStr);
2337
2338    // Make sure the focus node is a text node in order to have the
2339    // selection generate symmetric markup because the latter
2340    // includes all nodes crossed by the selection.  Also this way
2341    // the text content, rather its container, is highlighted.
2342    Node* focusNode = selection->focusNode();
2343    if (focusNode->isElementNode()) {
2344        focusNode = getImplicitBoundaryNode(selection->focusNode(),
2345                selection->focusOffset(), direction);
2346        if (!focusNode)
2347            return String();
2348        if (direction == DIRECTION_FORWARD) {
2349            focusNode = focusNode->traversePreviousSiblingPostOrder(body);
2350            if (focusNode && !isContentTextNode(focusNode)) {
2351                Node* textNode = traverseNextContentTextNode(focusNode,
2352                        anchorNode, DIRECTION_BACKWARD);
2353                if (textNode)
2354                    anchorNode = textNode;
2355            }
2356            if (focusNode && isContentTextNode(focusNode)) {
2357                selection->extend(focusNode, caretMaxOffset(focusNode), ec);
2358                if (ec)
2359                    return String();
2360            }
2361        } else {
2362            focusNode = focusNode->traverseNextSibling();
2363            if (focusNode && !isContentTextNode(focusNode)) {
2364                Node* textNode = traverseNextContentTextNode(focusNode,
2365                        anchorNode, DIRECTION_FORWARD);
2366                if (textNode)
2367                    anchorNode = textNode;
2368            }
2369            if (anchorNode && isContentTextNode(anchorNode)) {
2370                selection->extend(focusNode, 0, ec);
2371                if (ec)
2372                    return String();
2373            }
2374        }
2375    }
2376
2377    // Enforce that the selection does not cross anchor boundaries. This is
2378    // a workaround for the asymmetric behavior of WebKit while crossing
2379    // anchors.
2380    anchorNode = getImplicitBoundaryNode(selection->anchorNode(),
2381            selection->anchorOffset(), direction);
2382    focusNode = getImplicitBoundaryNode(selection->focusNode(),
2383            selection->focusOffset(), direction);
2384    if (anchorNode && focusNode && anchorNode != focusNode) {
2385        Node* inputControl = getIntermediaryInputElement(anchorNode, focusNode,
2386                direction);
2387        if (inputControl) {
2388            if (direction == DIRECTION_FORWARD) {
2389                if (isDescendantOf(inputControl, anchorNode)) {
2390                    focusNode = inputControl;
2391                } else {
2392                    focusNode = inputControl->traversePreviousSiblingPostOrder(
2393                            body);
2394                    if (!focusNode)
2395                        focusNode = inputControl;
2396                }
2397                // We prefer a text node contained in the input element.
2398                if (!isContentTextNode(focusNode)) {
2399                    Node* textNode = traverseNextContentTextNode(focusNode,
2400                        anchorNode, DIRECTION_BACKWARD);
2401                    if (textNode)
2402                        focusNode = textNode;
2403                }
2404                // If we found text in the input select it.
2405                // Otherwise, select the input element itself.
2406                if (isContentTextNode(focusNode)) {
2407                    selection->extend(focusNode, caretMaxOffset(focusNode), ec);
2408                } else if (anchorNode != focusNode) {
2409                    // Note that the focusNode always has parent and that
2410                    // the offset can be one more that the index of the last
2411                    // element - this is how WebKit selects such elements.
2412                    selection->extend(focusNode->parentNode(),
2413                            focusNode->nodeIndex() + 1, ec);
2414                }
2415                if (ec)
2416                    return String();
2417            } else {
2418                if (isDescendantOf(inputControl, anchorNode)) {
2419                    focusNode = inputControl;
2420                } else {
2421                    focusNode = inputControl->traverseNextSibling();
2422                    if (!focusNode)
2423                        focusNode = inputControl;
2424                }
2425                // We prefer a text node contained in the input element.
2426                if (!isContentTextNode(focusNode)) {
2427                    Node* textNode = traverseNextContentTextNode(focusNode,
2428                            anchorNode, DIRECTION_FORWARD);
2429                    if (textNode)
2430                        focusNode = textNode;
2431                }
2432                // If we found text in the input select it.
2433                // Otherwise, select the input element itself.
2434                if (isContentTextNode(focusNode)) {
2435                    selection->extend(focusNode, caretMinOffset(focusNode), ec);
2436                } else if (anchorNode != focusNode) {
2437                    // Note that the focusNode always has parent and that
2438                    // the offset can be one more that the index of the last
2439                    // element - this is how WebKit selects such elements.
2440                    selection->extend(focusNode->parentNode(),
2441                            focusNode->nodeIndex() + 1, ec);
2442                }
2443                if (ec)
2444                   return String();
2445            }
2446        }
2447    }
2448
2449    // make sure the selection is visible
2450    if (direction == DIRECTION_FORWARD)
2451        scrollNodeIntoView(m_mainFrame, selection->focusNode());
2452    else
2453        scrollNodeIntoView(m_mainFrame, selection->anchorNode());
2454
2455    // format markup for the visible content
2456    RefPtr<Range> range = selection->getRangeAt(0, ec);
2457    if (ec)
2458        return String();
2459    IntRect bounds = range->boundingBox();
2460    selectAt(bounds.center().x(), bounds.center().y());
2461    markup = formatMarkup(selection);
2462    ALOGV("Selection markup: %s", markup.utf8().data());
2463
2464    return markup;
2465}
2466
2467Node* WebViewCore::getImplicitBoundaryNode(Node* node, unsigned offset, int direction)
2468{
2469    if (node->offsetInCharacters())
2470        return node;
2471    if (!node->hasChildNodes())
2472        return node;
2473    if (offset < node->childNodeCount())
2474        return node->childNode(offset);
2475    else
2476        if (direction == DIRECTION_FORWARD)
2477            return node->traverseNextSibling();
2478        else
2479            return node->traversePreviousNodePostOrder(
2480                    node->document()->body());
2481}
2482
2483Node* WebViewCore::getNextAnchorNode(Node* anchorNode, bool ignoreFirstNode, int direction)
2484{
2485    Node* body = 0;
2486    Node* currentNode = 0;
2487    if (direction == DIRECTION_FORWARD) {
2488        if (ignoreFirstNode)
2489            currentNode = anchorNode->traverseNextNode(body);
2490        else
2491            currentNode = anchorNode;
2492    } else {
2493        body = anchorNode->document()->body();
2494        if (ignoreFirstNode)
2495            currentNode = anchorNode->traversePreviousSiblingPostOrder(body);
2496        else
2497            currentNode = anchorNode;
2498    }
2499    while (currentNode) {
2500        if (isContentTextNode(currentNode)
2501                || isContentInputElement(currentNode))
2502            return currentNode;
2503        if (direction == DIRECTION_FORWARD)
2504            currentNode = currentNode->traverseNextNode();
2505        else
2506            currentNode = currentNode->traversePreviousNodePostOrder(body);
2507    }
2508    return 0;
2509}
2510
2511void WebViewCore::advanceAnchorNode(DOMSelection* selection, int direction,
2512        String& markup, bool ignoreFirstNode, ExceptionCode& ec)
2513{
2514    Node* anchorNode = getImplicitBoundaryNode(selection->anchorNode(),
2515            selection->anchorOffset(), direction);
2516    if (!anchorNode) {
2517        ec = NOT_FOUND_ERR;
2518        return;
2519    }
2520    // If the anchor offset is invalid i.e. the anchor node has no
2521    // child with that index getImplicitAnchorNode returns the next
2522    // logical node in the current direction. In such a case our
2523    // position in the DOM tree was has already been advanced,
2524    // therefore we there is no need to do that again.
2525    if (selection->anchorNode()->isElementNode()) {
2526        unsigned anchorOffset = selection->anchorOffset();
2527        unsigned childNodeCount = selection->anchorNode()->childNodeCount();
2528        if (anchorOffset >= childNodeCount)
2529            ignoreFirstNode = false;
2530    }
2531    // Find the next anchor node given our position in the DOM and
2532    // whether we want the current node to be considered as well.
2533    Node* nextAnchorNode = getNextAnchorNode(anchorNode, ignoreFirstNode,
2534            direction);
2535    if (!nextAnchorNode) {
2536        ec = NOT_FOUND_ERR;
2537        return;
2538    }
2539    if (nextAnchorNode->isElementNode()) {
2540        // If this is an input element tell the WebView thread
2541        // to set the cursor to that control.
2542        if (isContentInputElement(nextAnchorNode)) {
2543            IntRect bounds = nextAnchorNode->getRect();
2544            selectAt(bounds.center().x(), bounds.center().y());
2545        }
2546        Node* textNode = 0;
2547        // Treat the text content of links as any other text but
2548        // for the rest input elements select the control itself.
2549        if (nextAnchorNode->hasTagName(WebCore::HTMLNames::aTag))
2550            textNode = traverseNextContentTextNode(nextAnchorNode,
2551                    nextAnchorNode, direction);
2552        // We prefer to select the text content of the link if such,
2553        // otherwise just select the element itself.
2554        if (textNode) {
2555            nextAnchorNode = textNode;
2556        } else {
2557            if (direction == DIRECTION_FORWARD) {
2558                selection->setBaseAndExtent(nextAnchorNode,
2559                        caretMinOffset(nextAnchorNode), nextAnchorNode,
2560                        caretMaxOffset(nextAnchorNode), ec);
2561            } else {
2562                selection->setBaseAndExtent(nextAnchorNode,
2563                        caretMaxOffset(nextAnchorNode), nextAnchorNode,
2564                        caretMinOffset(nextAnchorNode), ec);
2565            }
2566            if (!ec)
2567                markup = formatMarkup(selection);
2568            // make sure the selection is visible
2569            scrollNodeIntoView(selection->frame(), nextAnchorNode);
2570            return;
2571        }
2572    }
2573    if (direction == DIRECTION_FORWARD)
2574        selection->setPosition(nextAnchorNode,
2575                caretMinOffset(nextAnchorNode), ec);
2576    else
2577        selection->setPosition(nextAnchorNode,
2578                caretMaxOffset(nextAnchorNode), ec);
2579}
2580
2581bool WebViewCore::isContentInputElement(Node* node)
2582{
2583  return (isVisible(node)
2584          && (node->hasTagName(WebCore::HTMLNames::selectTag)
2585          || node->hasTagName(WebCore::HTMLNames::aTag)
2586          || node->hasTagName(WebCore::HTMLNames::inputTag)
2587          || node->hasTagName(WebCore::HTMLNames::buttonTag)));
2588}
2589
2590bool WebViewCore::isContentTextNode(Node* node)
2591{
2592   if (!node || !node->isTextNode())
2593       return false;
2594   Text* textNode = static_cast<Text*>(node);
2595   return (isVisible(textNode) && textNode->length() > 0
2596       && !textNode->containsOnlyWhitespace());
2597}
2598
2599Text* WebViewCore::traverseNextContentTextNode(Node* fromNode, Node* toNode, int direction)
2600{
2601    Node* currentNode = fromNode;
2602    do {
2603        if (direction == DIRECTION_FORWARD)
2604            currentNode = currentNode->traverseNextNode(toNode);
2605        else
2606            currentNode = currentNode->traversePreviousNodePostOrder(toNode);
2607    } while (currentNode && !isContentTextNode(currentNode));
2608    return static_cast<Text*>(currentNode);
2609}
2610
2611Node* WebViewCore::getIntermediaryInputElement(Node* fromNode, Node* toNode, int direction)
2612{
2613    if (fromNode == toNode)
2614        return 0;
2615    if (direction == DIRECTION_FORWARD) {
2616        Node* currentNode = fromNode;
2617        while (currentNode && currentNode != toNode) {
2618            if (isContentInputElement(currentNode))
2619                return currentNode;
2620            currentNode = currentNode->traverseNextNodePostOrder();
2621        }
2622        currentNode = fromNode;
2623        while (currentNode && currentNode != toNode) {
2624            if (isContentInputElement(currentNode))
2625                return currentNode;
2626            currentNode = currentNode->traverseNextNode();
2627        }
2628    } else {
2629        Node* currentNode = fromNode->traversePreviousNode();
2630        while (currentNode && currentNode != toNode) {
2631            if (isContentInputElement(currentNode))
2632                return currentNode;
2633            currentNode = currentNode->traversePreviousNode();
2634        }
2635        currentNode = fromNode->traversePreviousNodePostOrder();
2636        while (currentNode && currentNode != toNode) {
2637            if (isContentInputElement(currentNode))
2638                return currentNode;
2639            currentNode = currentNode->traversePreviousNodePostOrder();
2640        }
2641    }
2642    return 0;
2643}
2644
2645bool WebViewCore::isDescendantOf(Node* parent, Node* node)
2646{
2647    Node* currentNode = node;
2648    while (currentNode) {
2649        if (currentNode == parent) {
2650            return true;
2651        }
2652        currentNode = currentNode->parentNode();
2653    }
2654    return false;
2655}
2656
2657String WebViewCore::modifySelectionDomNavigationAxis(DOMSelection* selection, int direction, int axis)
2658{
2659    HTMLElement* body = m_mainFrame->document()->body();
2660    if (!m_currentNodeDomNavigationAxis && selection->focusNode()) {
2661        m_currentNodeDomNavigationAxis = selection->focusNode();
2662        selection->empty();
2663        if (m_currentNodeDomNavigationAxis->isTextNode())
2664            m_currentNodeDomNavigationAxis =
2665                m_currentNodeDomNavigationAxis->parentNode();
2666    }
2667    if (!m_currentNodeDomNavigationAxis)
2668        m_currentNodeDomNavigationAxis = currentFocus();
2669    if (!m_currentNodeDomNavigationAxis
2670            || !CacheBuilder::validNode(m_mainFrame, m_mainFrame,
2671                                        m_currentNodeDomNavigationAxis))
2672        m_currentNodeDomNavigationAxis = body;
2673    Node* currentNode = m_currentNodeDomNavigationAxis;
2674    if (axis == AXIS_HEADING) {
2675        if (currentNode == body && direction == DIRECTION_BACKWARD)
2676            currentNode = currentNode->lastDescendant();
2677        do {
2678            if (direction == DIRECTION_FORWARD)
2679                currentNode = currentNode->traverseNextNode(body);
2680            else
2681                currentNode = currentNode->traversePreviousNode(body);
2682        } while (currentNode && (currentNode->isTextNode()
2683            || !isVisible(currentNode) || !isHeading(currentNode)));
2684    } else if (axis == AXIS_PARENT_FIRST_CHILD) {
2685        if (direction == DIRECTION_FORWARD) {
2686            currentNode = currentNode->firstChild();
2687            while (currentNode && (currentNode->isTextNode()
2688                    || !isVisible(currentNode)))
2689                currentNode = currentNode->nextSibling();
2690        } else {
2691            do {
2692                if (currentNode == body)
2693                    return String();
2694                currentNode = currentNode->parentNode();
2695            } while (currentNode && (currentNode->isTextNode()
2696                    || !isVisible(currentNode)));
2697        }
2698    } else if (axis == AXIS_SIBLING) {
2699        do {
2700            if (direction == DIRECTION_FORWARD)
2701                currentNode = currentNode->nextSibling();
2702            else {
2703                if (currentNode == body)
2704                    return String();
2705                currentNode = currentNode->previousSibling();
2706            }
2707        } while (currentNode && (currentNode->isTextNode()
2708                || !isVisible(currentNode)));
2709    } else if (axis == AXIS_DOCUMENT) {
2710        currentNode = body;
2711        if (direction == DIRECTION_FORWARD)
2712            currentNode = currentNode->lastDescendant();
2713    } else {
2714        ALOGE("Invalid axis: %d", axis);
2715        return String();
2716    }
2717    if (currentNode) {
2718        m_currentNodeDomNavigationAxis = currentNode;
2719        scrollNodeIntoView(m_mainFrame, currentNode);
2720        String selectionString = createMarkup(currentNode);
2721        ALOGV("Selection markup: %s", selectionString.utf8().data());
2722        return selectionString;
2723    }
2724    return String();
2725}
2726
2727bool WebViewCore::isHeading(Node* node)
2728{
2729    if (node->hasTagName(WebCore::HTMLNames::h1Tag)
2730            || node->hasTagName(WebCore::HTMLNames::h2Tag)
2731            || node->hasTagName(WebCore::HTMLNames::h3Tag)
2732            || node->hasTagName(WebCore::HTMLNames::h4Tag)
2733            || node->hasTagName(WebCore::HTMLNames::h5Tag)
2734            || node->hasTagName(WebCore::HTMLNames::h6Tag)) {
2735        return true;
2736    }
2737
2738    if (node->isElementNode()) {
2739        Element* element = static_cast<Element*>(node);
2740        String roleAttribute =
2741            element->getAttribute(WebCore::HTMLNames::roleAttr).string();
2742        if (equalIgnoringCase(roleAttribute, "heading"))
2743            return true;
2744    }
2745
2746    return false;
2747}
2748
2749bool WebViewCore::isVisible(Node* node)
2750{
2751    // start off an element
2752    Element* element = 0;
2753    if (node->isElementNode())
2754        element = static_cast<Element*>(node);
2755    else
2756        element = node->parentElement();
2757    // check renderer
2758    if (!element->renderer()) {
2759        return false;
2760    }
2761    // check size
2762    if (element->offsetHeight() == 0 || element->offsetWidth() == 0) {
2763        return false;
2764    }
2765    // check style
2766    Node* body = m_mainFrame->document()->body();
2767    Node* currentNode = element;
2768    while (currentNode && currentNode != body) {
2769        RenderStyle* style = currentNode->computedStyle();
2770        if (style &&
2771                (style->display() == NONE || style->visibility() == HIDDEN)) {
2772            return false;
2773        }
2774        currentNode = currentNode->parentNode();
2775    }
2776    return true;
2777}
2778
2779String WebViewCore::formatMarkup(DOMSelection* selection)
2780{
2781    ExceptionCode ec = 0;
2782    String markup = String();
2783    RefPtr<Range> wholeRange = selection->getRangeAt(0, ec);
2784    if (ec)
2785        return String();
2786    if (!wholeRange->startContainer() || !wholeRange->startContainer())
2787        return String();
2788    // Since formatted markup contains invisible nodes it
2789    // is created from the concatenation of the visible fragments.
2790    Node* firstNode = wholeRange->firstNode();
2791    Node* pastLastNode = wholeRange->pastLastNode();
2792    Node* currentNode = firstNode;
2793    RefPtr<Range> currentRange;
2794
2795    while (currentNode != pastLastNode) {
2796        Node* nextNode = currentNode->traverseNextNode();
2797        if (!isVisible(currentNode)) {
2798            if (currentRange) {
2799                markup = markup + currentRange->toHTML().utf8().data();
2800                currentRange = 0;
2801            }
2802        } else {
2803            if (!currentRange) {
2804                currentRange = selection->frame()->document()->createRange();
2805                if (ec)
2806                    break;
2807                if (currentNode == firstNode) {
2808                    currentRange->setStart(wholeRange->startContainer(),
2809                        wholeRange->startOffset(), ec);
2810                    if (ec)
2811                        break;
2812                } else {
2813                    currentRange->setStart(currentNode->parentNode(),
2814                        currentNode->nodeIndex(), ec);
2815                    if (ec)
2816                       break;
2817                }
2818            }
2819            if (nextNode == pastLastNode) {
2820                currentRange->setEnd(wholeRange->endContainer(),
2821                    wholeRange->endOffset(), ec);
2822                if (ec)
2823                    break;
2824                markup = markup + currentRange->toHTML().utf8().data();
2825            } else {
2826                if (currentNode->offsetInCharacters())
2827                    currentRange->setEnd(currentNode,
2828                        currentNode->maxCharacterOffset(), ec);
2829                else
2830                    currentRange->setEnd(currentNode->parentNode(),
2831                            currentNode->nodeIndex() + 1, ec);
2832                if (ec)
2833                    break;
2834            }
2835        }
2836        currentNode = nextNode;
2837    }
2838    return markup.stripWhiteSpace();
2839}
2840
2841void WebViewCore::selectAt(int x, int y)
2842{
2843    JNIEnv* env = JSC::Bindings::getJNIEnv();
2844    AutoJObject javaObject = m_javaGlue->object(env);
2845    if (!javaObject.get())
2846        return;
2847    env->CallVoidMethod(javaObject.get(), m_javaGlue->m_selectAt, x, y);
2848    checkException(env);
2849}
2850
2851void WebViewCore::deleteSelection(int start, int end, int textGeneration)
2852{
2853    setSelection(start, end);
2854    if (start == end)
2855        return;
2856    WebCore::Node* focus = currentFocus();
2857    if (!focus)
2858        return;
2859    // Prevent our editor client from passing a message to change the
2860    // selection.
2861    EditorClientAndroid* client = static_cast<EditorClientAndroid*>(
2862            m_mainFrame->editor()->client());
2863    client->setUiGeneratedSelectionChange(true);
2864    PlatformKeyboardEvent down(AKEYCODE_DEL, 0, 0, true, false, false, false);
2865    PlatformKeyboardEvent up(AKEYCODE_DEL, 0, 0, false, false, false, false);
2866    key(down);
2867    key(up);
2868    client->setUiGeneratedSelectionChange(false);
2869    m_textGeneration = textGeneration;
2870    m_shouldPaintCaret = true;
2871}
2872
2873void WebViewCore::deleteSurroundingText(int leftLength, int rightLength)
2874{
2875    WebCore::Node* focus = currentFocus();
2876    if (!isTextInput(focus))
2877        return;
2878
2879    Frame* frame = focus->document()->frame();
2880    if (!frame)
2881        return;
2882    SelectionController* selection = frame->selection();
2883    Position endPosition = selection->end();
2884
2885    Position deleteStart = endPosition;
2886    int leftDelete = leftLength;
2887    while (leftDelete > 0) {
2888        leftDelete--;
2889        deleteStart = deleteStart.previous(Character);
2890    }
2891    Position deleteEnd = endPosition;
2892    int rightDelete = rightLength;
2893    while (rightDelete > 0) {
2894        rightDelete--;
2895        deleteEnd = deleteEnd.next(Character);
2896    }
2897
2898    // Select the text to delete.
2899    VisibleSelection deletedText(deleteStart, deleteEnd);
2900    selection->setSelection(deletedText);
2901    // Prevent our editor client from passing a message to change the
2902    // selection.
2903    EditorClientAndroid* client = static_cast<EditorClientAndroid*>(
2904            m_mainFrame->editor()->client());
2905    client->setUiGeneratedSelectionChange(true);
2906    WebCore::TypingCommand::deleteSelection(focus->document(), 0);
2907    client->setUiGeneratedSelectionChange(false);
2908
2909    // set the new cursor position
2910    VisibleSelection endCarat(deleteStart);
2911    selection->setSelection(endCarat);
2912}
2913
2914void WebViewCore::replaceTextfieldText(int oldStart,
2915        int oldEnd, const WTF::String& replace, int start, int end,
2916        int textGeneration)
2917{
2918    WebCore::Node* focus = currentFocus();
2919    if (!focus)
2920        return;
2921    setSelection(oldStart, oldEnd);
2922    // Prevent our editor client from passing a message to change the
2923    // selection.
2924    EditorClientAndroid* client = static_cast<EditorClientAndroid*>(
2925            m_mainFrame->editor()->client());
2926    client->setUiGeneratedSelectionChange(true);
2927    WebCore::TypingCommand::insertText(focus->document(), replace,
2928        false);
2929    client->setUiGeneratedSelectionChange(false);
2930    // setSelection calls revealSelection, so there is no need to do it here.
2931    setSelection(start, end);
2932    m_textGeneration = textGeneration;
2933    m_shouldPaintCaret = true;
2934}
2935
2936void WebViewCore::passToJs(int generation, const WTF::String& current,
2937    const PlatformKeyboardEvent& event)
2938{
2939    WebCore::Node* focus = currentFocus();
2940    if (!focus) {
2941        DBG_NAV_LOG("!focus");
2942        clearTextEntry();
2943        return;
2944    }
2945    WebCore::RenderObject* renderer = focus->renderer();
2946    if (!renderer || (!renderer->isTextField() && !renderer->isTextArea())) {
2947        DBG_NAV_LOGD("renderer==%p || not text", renderer);
2948        clearTextEntry();
2949        return;
2950    }
2951    // Block text field updates during a key press.
2952    m_blockTextfieldUpdates = true;
2953    // Also prevent our editor client from passing a message to change the
2954    // selection.
2955    EditorClientAndroid* client = static_cast<EditorClientAndroid*>(
2956            m_mainFrame->editor()->client());
2957    client->setUiGeneratedSelectionChange(true);
2958    key(event);
2959    client->setUiGeneratedSelectionChange(false);
2960    m_blockTextfieldUpdates = false;
2961    m_textGeneration = generation;
2962    WebCore::RenderTextControl* renderText =
2963        static_cast<WebCore::RenderTextControl*>(renderer);
2964    WTF::String test = renderText->text();
2965    if (test != current) {
2966        // If the text changed during the key event, update the UI text field.
2967        updateTextfield(focus, false, test);
2968    } else {
2969        DBG_NAV_LOG("test == current");
2970    }
2971    // Now that the selection has settled down, send it.
2972    updateTextSelection();
2973    m_shouldPaintCaret = true;
2974}
2975
2976void WebViewCore::scrollFocusedTextInput(float xPercent, int y)
2977{
2978    WebCore::Node* focus = currentFocus();
2979    if (!focus) {
2980        DBG_NAV_LOG("!focus");
2981        clearTextEntry();
2982        return;
2983    }
2984    WebCore::RenderObject* renderer = focus->renderer();
2985    if (!renderer || (!renderer->isTextField() && !renderer->isTextArea())) {
2986        DBG_NAV_LOGD("renderer==%p || not text", renderer);
2987        clearTextEntry();
2988        return;
2989    }
2990    WebCore::RenderTextControl* renderText =
2991        static_cast<WebCore::RenderTextControl*>(renderer);
2992    int x = (int) (xPercent * (renderText->scrollWidth() -
2993        renderText->clientWidth()));
2994    DBG_NAV_LOGD("x=%d y=%d xPercent=%g scrollW=%d clientW=%d", x, y,
2995        xPercent, renderText->scrollWidth(), renderText->clientWidth());
2996    renderText->setScrollLeft(x);
2997    renderText->setScrollTop(y);
2998}
2999
3000void WebViewCore::setFocusControllerActive(bool active)
3001{
3002    m_mainFrame->page()->focusController()->setActive(active);
3003}
3004
3005void WebViewCore::saveDocumentState(WebCore::Frame* frame)
3006{
3007    if (!CacheBuilder::validNode(m_mainFrame, frame, 0))
3008        frame = m_mainFrame;
3009    WebCore::HistoryItem *item = frame->loader()->history()->currentItem();
3010
3011    // item can be null when there is no offical URL for the current page. This happens
3012    // when the content is loaded using with WebCoreFrameBridge::LoadData() and there
3013    // is no failing URL (common case is when content is loaded using data: scheme)
3014    if (item) {
3015        item->setDocumentState(frame->document()->formElementsState());
3016    }
3017}
3018
3019// Create an array of java Strings.
3020static jobjectArray makeLabelArray(JNIEnv* env, const uint16_t** labels, size_t count)
3021{
3022    jclass stringClass = env->FindClass("java/lang/String");
3023    ALOG_ASSERT(stringClass, "Could not find java/lang/String");
3024    jobjectArray array = env->NewObjectArray(count, stringClass, 0);
3025    ALOG_ASSERT(array, "Could not create new string array");
3026
3027    for (size_t i = 0; i < count; i++) {
3028        jobject newString = env->NewString(&labels[i][1], labels[i][0]);
3029        env->SetObjectArrayElement(array, i, newString);
3030        env->DeleteLocalRef(newString);
3031        checkException(env);
3032    }
3033    env->DeleteLocalRef(stringClass);
3034    return array;
3035}
3036
3037void WebViewCore::openFileChooser(PassRefPtr<WebCore::FileChooser> chooser)
3038{
3039    JNIEnv* env = JSC::Bindings::getJNIEnv();
3040    AutoJObject javaObject = m_javaGlue->object(env);
3041    if (!javaObject.get())
3042        return;
3043
3044    if (!chooser)
3045        return;
3046
3047    WTF::String acceptType = chooser->acceptTypes();
3048    jstring jAcceptType = wtfStringToJstring(env, acceptType, true);
3049    jstring jName = (jstring) env->CallObjectMethod(
3050            javaObject.get(), m_javaGlue->m_openFileChooser, jAcceptType);
3051    checkException(env);
3052    env->DeleteLocalRef(jAcceptType);
3053
3054    WTF::String wtfString = jstringToWtfString(env, jName);
3055    env->DeleteLocalRef(jName);
3056
3057    if (!wtfString.isEmpty())
3058        chooser->chooseFile(wtfString);
3059}
3060
3061void WebViewCore::listBoxRequest(WebCoreReply* reply, const uint16_t** labels, size_t count, const int enabled[], size_t enabledCount,
3062        bool multiple, const int selected[], size_t selectedCountOrSelection)
3063{
3064    ALOG_ASSERT(m_javaGlue->m_obj, "No java widget associated with this view!");
3065
3066    JNIEnv* env = JSC::Bindings::getJNIEnv();
3067    AutoJObject javaObject = m_javaGlue->object(env);
3068    if (!javaObject.get())
3069        return;
3070
3071    // If m_popupReply is not null, then we already have a list showing.
3072    if (m_popupReply != 0)
3073        return;
3074
3075    // Create an array of java Strings for the drop down.
3076    jobjectArray labelArray = makeLabelArray(env, labels, count);
3077
3078    // Create an array determining whether each item is enabled.
3079    jintArray enabledArray = env->NewIntArray(enabledCount);
3080    checkException(env);
3081    jint* ptrArray = env->GetIntArrayElements(enabledArray, 0);
3082    checkException(env);
3083    for (size_t i = 0; i < enabledCount; i++) {
3084        ptrArray[i] = enabled[i];
3085    }
3086    env->ReleaseIntArrayElements(enabledArray, ptrArray, 0);
3087    checkException(env);
3088
3089    if (multiple) {
3090        // Pass up an array representing which items are selected.
3091        jintArray selectedArray = env->NewIntArray(selectedCountOrSelection);
3092        checkException(env);
3093        jint* selArray = env->GetIntArrayElements(selectedArray, 0);
3094        checkException(env);
3095        for (size_t i = 0; i < selectedCountOrSelection; i++) {
3096            selArray[i] = selected[i];
3097        }
3098        env->ReleaseIntArrayElements(selectedArray, selArray, 0);
3099
3100        env->CallVoidMethod(javaObject.get(),
3101                m_javaGlue->m_requestListBox, labelArray, enabledArray,
3102                selectedArray);
3103        env->DeleteLocalRef(selectedArray);
3104    } else {
3105        // Pass up the single selection.
3106        env->CallVoidMethod(javaObject.get(),
3107                m_javaGlue->m_requestSingleListBox, labelArray, enabledArray,
3108                selectedCountOrSelection);
3109    }
3110
3111    env->DeleteLocalRef(labelArray);
3112    env->DeleteLocalRef(enabledArray);
3113    checkException(env);
3114
3115    Retain(reply);
3116    m_popupReply = reply;
3117}
3118
3119bool WebViewCore::key(const PlatformKeyboardEvent& event)
3120{
3121    WebCore::EventHandler* eventHandler;
3122    WebCore::Node* focusNode = currentFocus();
3123    DBG_NAV_LOGD("keyCode=%s unichar=%d focusNode=%p",
3124        event.keyIdentifier().utf8().data(), event.unichar(), focusNode);
3125    if (focusNode) {
3126        WebCore::Frame* frame = focusNode->document()->frame();
3127        WebFrame* webFrame = WebFrame::getWebFrame(frame);
3128        eventHandler = frame->eventHandler();
3129        VisibleSelection old = frame->selection()->selection();
3130        bool handled = eventHandler->keyEvent(event);
3131        if (isContentEditable(focusNode)) {
3132            // keyEvent will return true even if the contentEditable did not
3133            // change its selection.  In the case that it does not, we want to
3134            // return false so that the key will be sent back to our navigation
3135            // system.
3136            handled |= frame->selection()->selection() != old;
3137        }
3138        return handled;
3139    } else {
3140        eventHandler = m_mainFrame->eventHandler();
3141    }
3142    return eventHandler->keyEvent(event);
3143}
3144
3145// For when the user clicks the trackball, presses dpad center, or types into an
3146// unfocused textfield.  In the latter case, 'fake' will be true
3147void WebViewCore::click(WebCore::Frame* frame, WebCore::Node* node, bool fake) {
3148    if (!node) {
3149        WebCore::IntPoint pt = m_mousePos;
3150        pt.move(m_scrollOffsetX, m_scrollOffsetY);
3151        WebCore::HitTestResult hitTestResult = m_mainFrame->eventHandler()->
3152                hitTestResultAtPoint(pt, false);
3153        node = hitTestResult.innerNode();
3154        frame = node->document()->frame();
3155        DBG_NAV_LOGD("m_mousePos=(%d,%d) m_scrollOffset=(%d,%d) pt=(%d,%d)"
3156            " node=%p", m_mousePos.x(), m_mousePos.y(),
3157            m_scrollOffsetX, m_scrollOffsetY, pt.x(), pt.y(), node);
3158    }
3159    if (node) {
3160        EditorClientAndroid* client
3161                = static_cast<EditorClientAndroid*>(
3162                m_mainFrame->editor()->client());
3163        client->setShouldChangeSelectedRange(false);
3164        handleMouseClick(frame, node, fake);
3165        client->setShouldChangeSelectedRange(true);
3166    }
3167}
3168
3169#if USE(ACCELERATED_COMPOSITING)
3170GraphicsLayerAndroid* WebViewCore::graphicsRootLayer() const
3171{
3172    RenderView* contentRenderer = m_mainFrame->contentRenderer();
3173    if (!contentRenderer)
3174        return 0;
3175    return static_cast<GraphicsLayerAndroid*>(
3176          contentRenderer->compositor()->rootPlatformLayer());
3177}
3178#endif
3179
3180bool WebViewCore::handleTouchEvent(int action, Vector<int>& ids, Vector<IntPoint>& points, int actionIndex, int metaState)
3181{
3182    bool preventDefault = false;
3183
3184#if USE(ACCELERATED_COMPOSITING)
3185    GraphicsLayerAndroid* rootLayer = graphicsRootLayer();
3186    if (rootLayer)
3187      rootLayer->pauseDisplay(true);
3188#endif
3189
3190#if ENABLE(TOUCH_EVENTS) // Android
3191    #define MOTION_EVENT_ACTION_POINTER_DOWN 5
3192    #define MOTION_EVENT_ACTION_POINTER_UP 6
3193
3194    WebCore::TouchEventType type = WebCore::TouchStart;
3195    WebCore::PlatformTouchPoint::State defaultTouchState;
3196    Vector<WebCore::PlatformTouchPoint::State> touchStates(points.size());
3197
3198    switch (action) {
3199    case 0: // MotionEvent.ACTION_DOWN
3200        type = WebCore::TouchStart;
3201        defaultTouchState = WebCore::PlatformTouchPoint::TouchPressed;
3202        break;
3203    case 1: // MotionEvent.ACTION_UP
3204        type = WebCore::TouchEnd;
3205        defaultTouchState = WebCore::PlatformTouchPoint::TouchReleased;
3206        break;
3207    case 2: // MotionEvent.ACTION_MOVE
3208        type = WebCore::TouchMove;
3209        defaultTouchState = WebCore::PlatformTouchPoint::TouchMoved;
3210        break;
3211    case 3: // MotionEvent.ACTION_CANCEL
3212        type = WebCore::TouchCancel;
3213        defaultTouchState = WebCore::PlatformTouchPoint::TouchCancelled;
3214        break;
3215    case 5: // MotionEvent.ACTION_POINTER_DOWN
3216        type = WebCore::TouchStart;
3217        defaultTouchState = WebCore::PlatformTouchPoint::TouchStationary;
3218        break;
3219    case 6: // MotionEvent.ACTION_POINTER_UP
3220        type = WebCore::TouchEnd;
3221        defaultTouchState = WebCore::PlatformTouchPoint::TouchStationary;
3222        break;
3223    case 0x100: // WebViewCore.ACTION_LONGPRESS
3224        type = WebCore::TouchLongPress;
3225        defaultTouchState = WebCore::PlatformTouchPoint::TouchPressed;
3226        break;
3227    case 0x200: // WebViewCore.ACTION_DOUBLETAP
3228        type = WebCore::TouchDoubleTap;
3229        defaultTouchState = WebCore::PlatformTouchPoint::TouchPressed;
3230        break;
3231    default:
3232        // We do not support other kinds of touch event inside WebCore
3233        // at the moment.
3234        ALOGW("Java passed a touch event type that we do not support in WebCore: %d", action);
3235        return 0;
3236    }
3237
3238    for (int c = 0; c < static_cast<int>(points.size()); c++) {
3239        points[c].setX(points[c].x() - m_scrollOffsetX);
3240        points[c].setY(points[c].y() - m_scrollOffsetY);
3241
3242        // Setting the touch state for each point.
3243        // Note: actionIndex will be 0 for all actions that are not ACTION_POINTER_DOWN/UP.
3244        if (action == MOTION_EVENT_ACTION_POINTER_DOWN && c == actionIndex) {
3245            touchStates[c] = WebCore::PlatformTouchPoint::TouchPressed;
3246        } else if (action == MOTION_EVENT_ACTION_POINTER_UP && c == actionIndex) {
3247            touchStates[c] = WebCore::PlatformTouchPoint::TouchReleased;
3248        } else {
3249            touchStates[c] = defaultTouchState;
3250        };
3251    }
3252
3253    WebCore::PlatformTouchEvent te(ids, points, type, touchStates, metaState);
3254    preventDefault = m_mainFrame->eventHandler()->handleTouchEvent(te);
3255#endif
3256
3257#if USE(ACCELERATED_COMPOSITING)
3258    if (rootLayer)
3259      rootLayer->pauseDisplay(false);
3260#endif
3261    return preventDefault;
3262}
3263
3264void WebViewCore::touchUp(int touchGeneration,
3265    WebCore::Frame* frame, WebCore::Node* node, int x, int y)
3266{
3267    if (touchGeneration == 0) {
3268        // m_mousePos should be set in getTouchHighlightRects()
3269        WebCore::HitTestResult hitTestResult = m_mainFrame->eventHandler()->hitTestResultAtPoint(m_mousePos, false);
3270        node = hitTestResult.innerNode();
3271        if (node)
3272            frame = node->document()->frame();
3273        else
3274            frame = 0;
3275        DBG_NAV_LOGD("touch up on (%d, %d), scrollOffset is (%d, %d), node:%p, frame:%p", m_mousePos.x() + m_scrollOffsetX, m_mousePos.y() + m_scrollOffsetY, m_scrollOffsetX, m_scrollOffsetY, node, frame);
3276    } else {
3277        if (m_touchGeneration > touchGeneration) {
3278            DBG_NAV_LOGD("m_touchGeneration=%d > touchGeneration=%d"
3279                " x=%d y=%d", m_touchGeneration, touchGeneration, x, y);
3280            return; // short circuit if a newer touch has been generated
3281        }
3282        // This moves m_mousePos to the correct place, and handleMouseClick uses
3283        // m_mousePos to determine where the click happens.
3284        moveMouse(frame, x, y);
3285        m_lastGeneration = touchGeneration;
3286    }
3287    if (frame && CacheBuilder::validNode(m_mainFrame, frame, 0)) {
3288        frame->loader()->resetMultipleFormSubmissionProtection();
3289    }
3290    DBG_NAV_LOGD("touchGeneration=%d handleMouseClick frame=%p node=%p"
3291        " x=%d y=%d", touchGeneration, frame, node, x, y);
3292    handleMouseClick(frame, node, false);
3293}
3294
3295// Check for the "x-webkit-soft-keyboard" attribute.  If it is there and
3296// set to hidden, do not show the soft keyboard.  Node passed as a parameter
3297// must not be null.
3298static bool shouldSuppressKeyboard(const WebCore::Node* node) {
3299    ALOG_ASSERT(node, "node passed to shouldSuppressKeyboard cannot be null");
3300    const NamedNodeMap* attributes = node->attributes();
3301    if (!attributes) return false;
3302    size_t length = attributes->length();
3303    for (size_t i = 0; i < length; i++) {
3304        const Attribute* a = attributes->attributeItem(i);
3305        if (a->localName() == "x-webkit-soft-keyboard" && a->value() == "hidden")
3306            return true;
3307    }
3308    return false;
3309}
3310
3311// Common code for both clicking with the trackball and touchUp
3312// Also used when typing into a non-focused textfield to give the textfield focus,
3313// in which case, 'fake' is set to true
3314bool WebViewCore::handleMouseClick(WebCore::Frame* framePtr, WebCore::Node* nodePtr, bool fake)
3315{
3316    bool valid = !framePtr || CacheBuilder::validNode(m_mainFrame, framePtr, nodePtr);
3317    WebFrame* webFrame = WebFrame::getWebFrame(m_mainFrame);
3318    if (valid && nodePtr) {
3319    // Need to special case area tags because an image map could have an area element in the middle
3320    // so when attempting to get the default, the point chosen would be follow the wrong link.
3321        if (nodePtr->hasTagName(WebCore::HTMLNames::areaTag)) {
3322            webFrame->setUserInitiatedAction(true);
3323            nodePtr->dispatchSimulatedClick(0, true, true);
3324            webFrame->setUserInitiatedAction(false);
3325            DBG_NAV_LOG("area");
3326            return true;
3327        }
3328    }
3329    if (!valid || !framePtr)
3330        framePtr = m_mainFrame;
3331    webFrame->setUserInitiatedAction(true);
3332    WebCore::PlatformMouseEvent mouseDown(m_mousePos, m_mousePos, WebCore::LeftButton,
3333            WebCore::MouseEventPressed, 1, false, false, false, false,
3334            WTF::currentTime());
3335    // ignore the return from as it will return true if the hit point can trigger selection change
3336    framePtr->eventHandler()->handleMousePressEvent(mouseDown);
3337    WebCore::PlatformMouseEvent mouseUp(m_mousePos, m_mousePos, WebCore::LeftButton,
3338            WebCore::MouseEventReleased, 1, false, false, false, false,
3339            WTF::currentTime());
3340    bool handled = framePtr->eventHandler()->handleMouseReleaseEvent(mouseUp);
3341    webFrame->setUserInitiatedAction(false);
3342
3343    // If the user clicked on a textfield, make the focusController active
3344    // so we show the blinking cursor.
3345    WebCore::Node* focusNode = currentFocus();
3346    DBG_NAV_LOGD("m_mousePos={%d,%d} focusNode=%p handled=%s", m_mousePos.x(),
3347        m_mousePos.y(), focusNode, handled ? "true" : "false");
3348    if (focusNode) {
3349        WebCore::RenderObject* renderer = focusNode->renderer();
3350        if (renderer && (renderer->isTextField() || renderer->isTextArea())) {
3351            bool ime = !shouldSuppressKeyboard(focusNode)
3352                    && !(static_cast<WebCore::HTMLInputElement*>(focusNode))->readOnly();
3353            if (ime) {
3354#if ENABLE(WEB_AUTOFILL)
3355                if (renderer->isTextField()) {
3356                    EditorClientAndroid* editorC = static_cast<EditorClientAndroid*>(framePtr->page()->editorClient());
3357                    WebAutofill* autoFill = editorC->getAutofill();
3358                    autoFill->formFieldFocused(static_cast<HTMLFormControlElement*>(focusNode));
3359                }
3360#endif
3361                if (!fake) {
3362                    RenderTextControl* rtc
3363                            = static_cast<RenderTextControl*> (renderer);
3364#if ENABLE(ANDROID_NAVCACHE)
3365                    // Force an update of the navcache as this will fire off a
3366                    // message to WebView that *must* have an updated focus.
3367                    m_frameCacheOutOfDate = true;
3368                    updateFrameCache();
3369#endif
3370                    requestKeyboardWithSelection(focusNode, rtc->selectionStart(),
3371                            rtc->selectionEnd());
3372                }
3373            } else if (!fake) {
3374                requestKeyboard(false);
3375            }
3376        } else if (!fake){
3377            // If the selection is contentEditable, show the keyboard so the
3378            // user can type.  Otherwise hide the keyboard because no text
3379            // input is needed.
3380            if (isContentEditable(focusNode)) {
3381                requestKeyboard(true);
3382            } else if (!nodeIsPlugin(focusNode)) {
3383                clearTextEntry();
3384            }
3385        }
3386    } else if (!fake) {
3387        // There is no focusNode, so the keyboard is not needed.
3388        clearTextEntry();
3389    }
3390    return handled;
3391}
3392
3393void WebViewCore::popupReply(int index)
3394{
3395    if (m_popupReply) {
3396        m_popupReply->replyInt(index);
3397        Release(m_popupReply);
3398        m_popupReply = 0;
3399    }
3400}
3401
3402void WebViewCore::popupReply(const int* array, int count)
3403{
3404    if (m_popupReply) {
3405        m_popupReply->replyIntArray(array, count);
3406        Release(m_popupReply);
3407        m_popupReply = 0;
3408    }
3409}
3410
3411void WebViewCore::formDidBlur(const WebCore::Node* node)
3412{
3413    // If the blur is on a text input, keep track of the node so we can
3414    // hide the soft keyboard when the new focus is set, if it is not a
3415    // text input.
3416    if (isTextInput(node))
3417        m_blurringNodePointer = reinterpret_cast<int>(node);
3418}
3419
3420void WebViewCore::focusNodeChanged(const WebCore::Node* newFocus)
3421{
3422    if (isTextInput(newFocus))
3423        m_shouldPaintCaret = true;
3424    else if (m_blurringNodePointer) {
3425        JNIEnv* env = JSC::Bindings::getJNIEnv();
3426        AutoJObject javaObject = m_javaGlue->object(env);
3427        if (!javaObject.get())
3428            return;
3429        env->CallVoidMethod(javaObject.get(), m_javaGlue->m_formDidBlur, m_blurringNodePointer);
3430        checkException(env);
3431        m_blurringNodePointer = 0;
3432    }
3433}
3434
3435void WebViewCore::addMessageToConsole(const WTF::String& message, unsigned int lineNumber, const WTF::String& sourceID, int msgLevel) {
3436    JNIEnv* env = JSC::Bindings::getJNIEnv();
3437    AutoJObject javaObject = m_javaGlue->object(env);
3438    if (!javaObject.get())
3439        return;
3440    jstring jMessageStr = wtfStringToJstring(env, message);
3441    jstring jSourceIDStr = wtfStringToJstring(env, sourceID);
3442    env->CallVoidMethod(javaObject.get(),
3443            m_javaGlue->m_addMessageToConsole, jMessageStr, lineNumber,
3444            jSourceIDStr, msgLevel);
3445    env->DeleteLocalRef(jMessageStr);
3446    env->DeleteLocalRef(jSourceIDStr);
3447    checkException(env);
3448}
3449
3450void WebViewCore::jsAlert(const WTF::String& url, const WTF::String& text)
3451{
3452    JNIEnv* env = JSC::Bindings::getJNIEnv();
3453    AutoJObject javaObject = m_javaGlue->object(env);
3454    if (!javaObject.get())
3455        return;
3456    jstring jInputStr = wtfStringToJstring(env, text);
3457    jstring jUrlStr = wtfStringToJstring(env, url);
3458    env->CallVoidMethod(javaObject.get(), m_javaGlue->m_jsAlert, jUrlStr, jInputStr);
3459    env->DeleteLocalRef(jInputStr);
3460    env->DeleteLocalRef(jUrlStr);
3461    checkException(env);
3462}
3463
3464bool WebViewCore::exceededDatabaseQuota(const WTF::String& url, const WTF::String& databaseIdentifier, const unsigned long long currentQuota, unsigned long long estimatedSize)
3465{
3466#if ENABLE(DATABASE)
3467    JNIEnv* env = JSC::Bindings::getJNIEnv();
3468    AutoJObject javaObject = m_javaGlue->object(env);
3469    if (!javaObject.get())
3470        return false;
3471    jstring jDatabaseIdentifierStr = wtfStringToJstring(env, databaseIdentifier);
3472    jstring jUrlStr = wtfStringToJstring(env, url);
3473    env->CallVoidMethod(javaObject.get(),
3474            m_javaGlue->m_exceededDatabaseQuota, jUrlStr,
3475            jDatabaseIdentifierStr, currentQuota, estimatedSize);
3476    env->DeleteLocalRef(jDatabaseIdentifierStr);
3477    env->DeleteLocalRef(jUrlStr);
3478    checkException(env);
3479    return true;
3480#endif
3481}
3482
3483bool WebViewCore::reachedMaxAppCacheSize(const unsigned long long spaceNeeded)
3484{
3485#if ENABLE(OFFLINE_WEB_APPLICATIONS)
3486    JNIEnv* env = JSC::Bindings::getJNIEnv();
3487    AutoJObject javaObject = m_javaGlue->object(env);
3488    if (!javaObject.get())
3489        return false;
3490    env->CallVoidMethod(javaObject.get(), m_javaGlue->m_reachedMaxAppCacheSize, spaceNeeded);
3491    checkException(env);
3492    return true;
3493#endif
3494}
3495
3496void WebViewCore::populateVisitedLinks(WebCore::PageGroup* group)
3497{
3498    JNIEnv* env = JSC::Bindings::getJNIEnv();
3499    AutoJObject javaObject = m_javaGlue->object(env);
3500    if (!javaObject.get())
3501        return;
3502    m_groupForVisitedLinks = group;
3503    env->CallVoidMethod(javaObject.get(), m_javaGlue->m_populateVisitedLinks);
3504    checkException(env);
3505}
3506
3507void WebViewCore::geolocationPermissionsShowPrompt(const WTF::String& origin)
3508{
3509    JNIEnv* env = JSC::Bindings::getJNIEnv();
3510    AutoJObject javaObject = m_javaGlue->object(env);
3511    if (!javaObject.get())
3512        return;
3513    jstring originString = wtfStringToJstring(env, origin);
3514    env->CallVoidMethod(javaObject.get(),
3515                        m_javaGlue->m_geolocationPermissionsShowPrompt,
3516                        originString);
3517    env->DeleteLocalRef(originString);
3518    checkException(env);
3519}
3520
3521void WebViewCore::geolocationPermissionsHidePrompt()
3522{
3523    JNIEnv* env = JSC::Bindings::getJNIEnv();
3524    AutoJObject javaObject = m_javaGlue->object(env);
3525    if (!javaObject.get())
3526        return;
3527    env->CallVoidMethod(javaObject.get(), m_javaGlue->m_geolocationPermissionsHidePrompt);
3528    checkException(env);
3529}
3530
3531jobject WebViewCore::getDeviceMotionService()
3532{
3533    JNIEnv* env = JSC::Bindings::getJNIEnv();
3534    AutoJObject javaObject = m_javaGlue->object(env);
3535    if (!javaObject.get())
3536        return 0;
3537    jobject object = env->CallObjectMethod(javaObject.get(), m_javaGlue->m_getDeviceMotionService);
3538    checkException(env);
3539    return object;
3540}
3541
3542jobject WebViewCore::getDeviceOrientationService()
3543{
3544    JNIEnv* env = JSC::Bindings::getJNIEnv();
3545    AutoJObject javaObject = m_javaGlue->object(env);
3546    if (!javaObject.get())
3547        return 0;
3548    jobject object = env->CallObjectMethod(javaObject.get(), m_javaGlue->m_getDeviceOrientationService);
3549    checkException(env);
3550    return object;
3551}
3552
3553bool WebViewCore::jsConfirm(const WTF::String& url, const WTF::String& text)
3554{
3555    JNIEnv* env = JSC::Bindings::getJNIEnv();
3556    AutoJObject javaObject = m_javaGlue->object(env);
3557    if (!javaObject.get())
3558        return false;
3559    jstring jInputStr = wtfStringToJstring(env, text);
3560    jstring jUrlStr = wtfStringToJstring(env, url);
3561    jboolean result = env->CallBooleanMethod(javaObject.get(), m_javaGlue->m_jsConfirm, jUrlStr, jInputStr);
3562    env->DeleteLocalRef(jInputStr);
3563    env->DeleteLocalRef(jUrlStr);
3564    checkException(env);
3565    return result;
3566}
3567
3568bool WebViewCore::jsPrompt(const WTF::String& url, const WTF::String& text, const WTF::String& defaultValue, WTF::String& result)
3569{
3570    JNIEnv* env = JSC::Bindings::getJNIEnv();
3571    AutoJObject javaObject = m_javaGlue->object(env);
3572    if (!javaObject.get())
3573        return false;
3574    jstring jUrlStr = wtfStringToJstring(env, url);
3575    jstring jInputStr = wtfStringToJstring(env, text);
3576    jstring jDefaultStr = wtfStringToJstring(env, defaultValue);
3577    jstring returnVal = static_cast<jstring>(env->CallObjectMethod(javaObject.get(), m_javaGlue->m_jsPrompt, jUrlStr, jInputStr, jDefaultStr));
3578    env->DeleteLocalRef(jUrlStr);
3579    env->DeleteLocalRef(jInputStr);
3580    env->DeleteLocalRef(jDefaultStr);
3581    checkException(env);
3582
3583    // If returnVal is null, it means that the user cancelled the dialog.
3584    if (!returnVal)
3585        return false;
3586
3587    result = jstringToWtfString(env, returnVal);
3588    env->DeleteLocalRef(returnVal);
3589    return true;
3590}
3591
3592bool WebViewCore::jsUnload(const WTF::String& url, const WTF::String& message)
3593{
3594    JNIEnv* env = JSC::Bindings::getJNIEnv();
3595    AutoJObject javaObject = m_javaGlue->object(env);
3596    if (!javaObject.get())
3597        return false;
3598    jstring jInputStr = wtfStringToJstring(env, message);
3599    jstring jUrlStr = wtfStringToJstring(env, url);
3600    jboolean result = env->CallBooleanMethod(javaObject.get(), m_javaGlue->m_jsUnload, jUrlStr, jInputStr);
3601    env->DeleteLocalRef(jInputStr);
3602    env->DeleteLocalRef(jUrlStr);
3603    checkException(env);
3604    return result;
3605}
3606
3607bool WebViewCore::jsInterrupt()
3608{
3609    JNIEnv* env = JSC::Bindings::getJNIEnv();
3610    AutoJObject javaObject = m_javaGlue->object(env);
3611    if (!javaObject.get())
3612        return false;
3613    jboolean result = env->CallBooleanMethod(javaObject.get(), m_javaGlue->m_jsInterrupt);
3614    checkException(env);
3615    return result;
3616}
3617
3618AutoJObject
3619WebViewCore::getJavaObject()
3620{
3621    return m_javaGlue->object(JSC::Bindings::getJNIEnv());
3622}
3623
3624jobject
3625WebViewCore::getWebViewJavaObject()
3626{
3627    JNIEnv* env = JSC::Bindings::getJNIEnv();
3628    AutoJObject javaObject = m_javaGlue->object(env);
3629    if (!javaObject.get())
3630        return 0;
3631    return env->GetObjectField(javaObject.get(), gWebViewCoreFields.m_webView);
3632}
3633
3634void WebViewCore::updateTextSelection()
3635{
3636    JNIEnv* env = JSC::Bindings::getJNIEnv();
3637    AutoJObject javaObject = m_javaGlue->object(env);
3638    if (!javaObject.get())
3639        return;
3640    WebCore::Node* focusNode = currentFocus();
3641    if (!focusNode)
3642        return;
3643    RenderObject* renderer = focusNode->renderer();
3644    if (!renderer || (!renderer->isTextArea() && !renderer->isTextField()))
3645        return;
3646    RenderTextControl* rtc = static_cast<RenderTextControl*>(renderer);
3647    env->CallVoidMethod(javaObject.get(),
3648            m_javaGlue->m_updateTextSelection, reinterpret_cast<int>(focusNode),
3649            rtc->selectionStart(), rtc->selectionEnd(), m_textGeneration);
3650    checkException(env);
3651}
3652
3653void WebViewCore::updateTextfield(WebCore::Node* ptr, bool changeToPassword,
3654        const WTF::String& text)
3655{
3656    JNIEnv* env = JSC::Bindings::getJNIEnv();
3657    AutoJObject javaObject = m_javaGlue->object(env);
3658    if (!javaObject.get())
3659        return;
3660    if (m_blockTextfieldUpdates)
3661        return;
3662    if (changeToPassword) {
3663        env->CallVoidMethod(javaObject.get(), m_javaGlue->m_updateTextfield,
3664                (int) ptr, true, 0, m_textGeneration);
3665        checkException(env);
3666        return;
3667    }
3668    jstring string = wtfStringToJstring(env, text);
3669    env->CallVoidMethod(javaObject.get(), m_javaGlue->m_updateTextfield,
3670            (int) ptr, false, string, m_textGeneration);
3671    env->DeleteLocalRef(string);
3672    checkException(env);
3673}
3674
3675void WebViewCore::clearTextEntry()
3676{
3677    JNIEnv* env = JSC::Bindings::getJNIEnv();
3678    AutoJObject javaObject = m_javaGlue->object(env);
3679    if (!javaObject.get())
3680        return;
3681    env->CallVoidMethod(javaObject.get(), m_javaGlue->m_clearTextEntry);
3682}
3683
3684void WebViewCore::setBackgroundColor(SkColor c)
3685{
3686    WebCore::FrameView* view = m_mainFrame->view();
3687    if (!view)
3688        return;
3689
3690    // need (int) cast to find the right constructor
3691    WebCore::Color bcolor((int)SkColorGetR(c), (int)SkColorGetG(c),
3692                          (int)SkColorGetB(c), (int)SkColorGetA(c));
3693    view->setBaseBackgroundColor(bcolor);
3694
3695    // Background color of 0 indicates we want a transparent background
3696    if (c == 0)
3697        view->setTransparent(true);
3698}
3699
3700jclass WebViewCore::getPluginClass(const WTF::String& libName, const char* className)
3701{
3702    JNIEnv* env = JSC::Bindings::getJNIEnv();
3703    AutoJObject javaObject = m_javaGlue->object(env);
3704    if (!javaObject.get())
3705        return 0;
3706
3707    jstring libString = wtfStringToJstring(env, libName);
3708    jstring classString = env->NewStringUTF(className);
3709    jobject pluginClass = env->CallObjectMethod(javaObject.get(),
3710                                           m_javaGlue->m_getPluginClass,
3711                                           libString, classString);
3712    checkException(env);
3713
3714    // cleanup unneeded local JNI references
3715    env->DeleteLocalRef(libString);
3716    env->DeleteLocalRef(classString);
3717
3718    if (pluginClass != 0) {
3719        return static_cast<jclass>(pluginClass);
3720    } else {
3721        return 0;
3722    }
3723}
3724
3725void WebViewCore::showFullScreenPlugin(jobject childView, int32_t orientation, NPP npp)
3726{
3727    JNIEnv* env = JSC::Bindings::getJNIEnv();
3728    AutoJObject javaObject = m_javaGlue->object(env);
3729    if (!javaObject.get())
3730        return;
3731
3732    env->CallVoidMethod(javaObject.get(),
3733                        m_javaGlue->m_showFullScreenPlugin,
3734                        childView, orientation, reinterpret_cast<int>(npp));
3735    checkException(env);
3736}
3737
3738void WebViewCore::hideFullScreenPlugin()
3739{
3740    JNIEnv* env = JSC::Bindings::getJNIEnv();
3741    AutoJObject javaObject = m_javaGlue->object(env);
3742    if (!javaObject.get())
3743        return;
3744    env->CallVoidMethod(javaObject.get(), m_javaGlue->m_hideFullScreenPlugin);
3745    checkException(env);
3746}
3747
3748jobject WebViewCore::createSurface(jobject view)
3749{
3750    JNIEnv* env = JSC::Bindings::getJNIEnv();
3751    AutoJObject javaObject = m_javaGlue->object(env);
3752    if (!javaObject.get())
3753        return 0;
3754    jobject result = env->CallObjectMethod(javaObject.get(), m_javaGlue->m_createSurface, view);
3755    checkException(env);
3756    return result;
3757}
3758
3759jobject WebViewCore::addSurface(jobject view, int x, int y, int width, int height)
3760{
3761    JNIEnv* env = JSC::Bindings::getJNIEnv();
3762    AutoJObject javaObject = m_javaGlue->object(env);
3763    if (!javaObject.get())
3764        return 0;
3765    jobject result = env->CallObjectMethod(javaObject.get(),
3766                                           m_javaGlue->m_addSurface,
3767                                           view, x, y, width, height);
3768    checkException(env);
3769    return result;
3770}
3771
3772void WebViewCore::updateSurface(jobject childView, int x, int y, int width, int height)
3773{
3774    JNIEnv* env = JSC::Bindings::getJNIEnv();
3775    AutoJObject javaObject = m_javaGlue->object(env);
3776    if (!javaObject.get())
3777        return;
3778    env->CallVoidMethod(javaObject.get(),
3779                        m_javaGlue->m_updateSurface, childView,
3780                        x, y, width, height);
3781    checkException(env);
3782}
3783
3784void WebViewCore::destroySurface(jobject childView)
3785{
3786    JNIEnv* env = JSC::Bindings::getJNIEnv();
3787    AutoJObject javaObject = m_javaGlue->object(env);
3788    if (!javaObject.get())
3789        return;
3790    env->CallVoidMethod(javaObject.get(), m_javaGlue->m_destroySurface, childView);
3791    checkException(env);
3792}
3793
3794jobject WebViewCore::getContext()
3795{
3796    JNIEnv* env = JSC::Bindings::getJNIEnv();
3797    AutoJObject javaObject = m_javaGlue->object(env);
3798    if (!javaObject.get())
3799        return 0;
3800
3801    jobject result = env->CallObjectMethod(javaObject.get(), m_javaGlue->m_getContext);
3802    checkException(env);
3803    return result;
3804}
3805
3806void WebViewCore::keepScreenOn(bool screenOn) {
3807    if ((screenOn && m_screenOnCounter == 0) || (!screenOn && m_screenOnCounter == 1)) {
3808        JNIEnv* env = JSC::Bindings::getJNIEnv();
3809        AutoJObject javaObject = m_javaGlue->object(env);
3810        if (!javaObject.get())
3811            return;
3812        env->CallVoidMethod(javaObject.get(), m_javaGlue->m_keepScreenOn, screenOn);
3813        checkException(env);
3814    }
3815
3816    // update the counter
3817    if (screenOn)
3818        m_screenOnCounter++;
3819    else if (m_screenOnCounter > 0)
3820        m_screenOnCounter--;
3821}
3822
3823bool WebViewCore::validNodeAndBounds(Frame* frame, Node* node,
3824    const IntRect& originalAbsoluteBounds)
3825{
3826    bool valid = CacheBuilder::validNode(m_mainFrame, frame, node);
3827    if (!valid)
3828        return false;
3829    RenderObject* renderer = node->renderer();
3830    if (!renderer)
3831        return false;
3832    IntRect absBounds = node->hasTagName(HTMLNames::areaTag)
3833        ? CacheBuilder::getAreaRect(static_cast<HTMLAreaElement*>(node))
3834        : renderer->absoluteBoundingBoxRect();
3835    return absBounds == originalAbsoluteBounds;
3836}
3837
3838void WebViewCore::showRect(int left, int top, int width, int height,
3839        int contentWidth, int contentHeight, float xPercentInDoc,
3840        float xPercentInView, float yPercentInDoc, float yPercentInView)
3841{
3842    JNIEnv* env = JSC::Bindings::getJNIEnv();
3843    AutoJObject javaObject = m_javaGlue->object(env);
3844    if (!javaObject.get())
3845        return;
3846    env->CallVoidMethod(javaObject.get(), m_javaGlue->m_showRect,
3847            left, top, width, height, contentWidth, contentHeight,
3848            xPercentInDoc, xPercentInView, yPercentInDoc, yPercentInView);
3849    checkException(env);
3850}
3851
3852void WebViewCore::centerFitRect(int x, int y, int width, int height)
3853{
3854    JNIEnv* env = JSC::Bindings::getJNIEnv();
3855    AutoJObject javaObject = m_javaGlue->object(env);
3856    if (!javaObject.get())
3857        return;
3858    env->CallVoidMethod(javaObject.get(), m_javaGlue->m_centerFitRect, x, y, width, height);
3859    checkException(env);
3860}
3861
3862void WebViewCore::setScrollbarModes(ScrollbarMode horizontalMode, ScrollbarMode verticalMode)
3863{
3864    JNIEnv* env = JSC::Bindings::getJNIEnv();
3865    AutoJObject javaObject = m_javaGlue->object(env);
3866    if (!javaObject.get())
3867        return;
3868    env->CallVoidMethod(javaObject.get(), m_javaGlue->m_setScrollbarModes, horizontalMode, verticalMode);
3869    checkException(env);
3870}
3871
3872void WebViewCore::notifyWebAppCanBeInstalled()
3873{
3874    JNIEnv* env = JSC::Bindings::getJNIEnv();
3875    AutoJObject javaObject = m_javaGlue->object(env);
3876    if (!javaObject.get())
3877        return;
3878    env->CallVoidMethod(javaObject.get(), m_javaGlue->m_setInstallableWebApp);
3879    checkException(env);
3880}
3881
3882#if ENABLE(VIDEO)
3883void WebViewCore::enterFullscreenForVideoLayer(int layerId, const WTF::String& url)
3884{
3885    JNIEnv* env = JSC::Bindings::getJNIEnv();
3886    AutoJObject javaObject = m_javaGlue->object(env);
3887    if (!javaObject.get())
3888        return;
3889    jstring jUrlStr = wtfStringToJstring(env, url);
3890    env->CallVoidMethod(javaObject.get(), m_javaGlue->m_enterFullscreenForVideoLayer, layerId, jUrlStr);
3891    checkException(env);
3892}
3893#endif
3894
3895void WebViewCore::setWebTextViewAutoFillable(int queryId, const string16& previewSummary)
3896{
3897#if ENABLE(WEB_AUTOFILL)
3898    JNIEnv* env = JSC::Bindings::getJNIEnv();
3899    AutoJObject javaObject = m_javaGlue->object(env);
3900    if (!javaObject.get())
3901        return;
3902    jstring preview = env->NewString(previewSummary.data(), previewSummary.length());
3903    env->CallVoidMethod(javaObject.get(), m_javaGlue->m_setWebTextViewAutoFillable, queryId, preview);
3904    env->DeleteLocalRef(preview);
3905#endif
3906}
3907
3908bool WebViewCore::drawIsPaused() const
3909{
3910    // returning true says scrollview should be offscreen, which pauses
3911    // gifs. because this is not again queried when we stop scrolling, we don't
3912    // use the stopping currently.
3913    return false;
3914}
3915
3916#if USE(CHROME_NETWORK_STACK)
3917void WebViewCore::setWebRequestContextUserAgent()
3918{
3919    // We cannot create a WebRequestContext, because we might not know it this is a private tab or not yet
3920    if (m_webRequestContext)
3921        m_webRequestContext->setUserAgent(WebFrame::getWebFrame(m_mainFrame)->userAgentForURL(0)); // URL not used
3922}
3923
3924void WebViewCore::setWebRequestContextCacheMode(int cacheMode)
3925{
3926    m_cacheMode = cacheMode;
3927    // We cannot create a WebRequestContext, because we might not know it this is a private tab or not yet
3928    if (!m_webRequestContext)
3929        return;
3930
3931    m_webRequestContext->setCacheMode(cacheMode);
3932}
3933
3934WebRequestContext* WebViewCore::webRequestContext()
3935{
3936    if (!m_webRequestContext) {
3937        Settings* settings = mainFrame()->settings();
3938        m_webRequestContext = new WebRequestContext(settings && settings->privateBrowsingEnabled());
3939        setWebRequestContextUserAgent();
3940        setWebRequestContextCacheMode(m_cacheMode);
3941    }
3942    return m_webRequestContext.get();
3943}
3944#endif
3945
3946void WebViewCore::scrollRenderLayer(int layer, const SkRect& rect)
3947{
3948#if USE(ACCELERATED_COMPOSITING)
3949    GraphicsLayerAndroid* root = graphicsRootLayer();
3950    if (!root)
3951        return;
3952
3953    LayerAndroid* layerAndroid = root->platformLayer();
3954    if (!layerAndroid)
3955        return;
3956
3957    LayerAndroid* target = layerAndroid->findById(layer);
3958    if (!target)
3959        return;
3960
3961    RenderLayer* owner = target->owningLayer();
3962    if (!owner)
3963        return;
3964
3965    if (owner->stackingContext())
3966        owner->scrollToOffset(rect.fLeft, rect.fTop);
3967#endif
3968}
3969
3970//----------------------------------------------------------------------
3971// Native JNI methods
3972//----------------------------------------------------------------------
3973static void RevealSelection(JNIEnv* env, jobject obj, jint nativeClass)
3974{
3975    reinterpret_cast<WebViewCore*>(nativeClass)->revealSelection();
3976}
3977
3978static jstring RequestLabel(JNIEnv* env, jobject obj, jint nativeClass,
3979        int framePointer, int nodePointer)
3980{
3981    WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
3982    return wtfStringToJstring(env, viewImpl->requestLabel(
3983            (WebCore::Frame*) framePointer, (WebCore::Node*) nodePointer));
3984}
3985
3986static void ClearContent(JNIEnv* env, jobject obj, jint nativeClass)
3987{
3988    WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
3989    viewImpl->clearContent();
3990}
3991
3992static void UpdateFrameCacheIfLoading(JNIEnv* env, jobject obj, jint nativeClass)
3993{
3994#if ENABLE(ANDROID_NAVCACHE)
3995        reinterpret_cast<WebViewCore*>(nativeClass)->updateFrameCacheIfLoading();
3996#endif
3997}
3998
3999static void SetSize(JNIEnv* env, jobject obj, jint nativeClass, jint width,
4000        jint height, jint textWrapWidth, jfloat scale, jint screenWidth,
4001        jint screenHeight, jint anchorX, jint anchorY, jboolean ignoreHeight)
4002{
4003    WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
4004    ALOGV("webviewcore::nativeSetSize(%u %u)\n viewImpl: %p", (unsigned)width, (unsigned)height, viewImpl);
4005    ALOG_ASSERT(viewImpl, "viewImpl not set in nativeSetSize");
4006    viewImpl->setSizeScreenWidthAndScale(width, height, textWrapWidth, scale,
4007            screenWidth, screenHeight, anchorX, anchorY, ignoreHeight);
4008}
4009
4010static void SetScrollOffset(JNIEnv* env, jobject obj, jint nativeClass,
4011        jint gen, jboolean sendScrollEvent, jint x, jint y)
4012{
4013    WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
4014    ALOG_ASSERT(viewImpl, "need viewImpl");
4015
4016    viewImpl->setScrollOffset(gen, sendScrollEvent, x, y);
4017}
4018
4019static void SetGlobalBounds(JNIEnv* env, jobject obj, jint nativeClass,
4020        jint x, jint y, jint h, jint v)
4021{
4022    WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
4023    ALOG_ASSERT(viewImpl, "need viewImpl");
4024
4025    viewImpl->setGlobalBounds(x, y, h, v);
4026}
4027
4028static jboolean Key(JNIEnv* env, jobject obj, jint nativeClass, jint keyCode,
4029        jint unichar, jint repeatCount, jboolean isShift, jboolean isAlt,
4030        jboolean isSym, jboolean isDown)
4031{
4032    WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
4033    return viewImpl->key(PlatformKeyboardEvent(keyCode,
4034        unichar, repeatCount, isDown, isShift, isAlt, isSym));
4035}
4036
4037static void Click(JNIEnv* env, jobject obj, jint nativeClass, int framePtr,
4038        int nodePtr, jboolean fake)
4039{
4040    WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
4041    ALOG_ASSERT(viewImpl, "viewImpl not set in Click");
4042
4043    viewImpl->click(reinterpret_cast<WebCore::Frame*>(framePtr),
4044        reinterpret_cast<WebCore::Node*>(nodePtr), fake);
4045}
4046
4047static void ContentInvalidateAll(JNIEnv* env, jobject obj, jint nativeClass)
4048{
4049    reinterpret_cast<WebViewCore*>(nativeClass)->contentInvalidateAll();
4050}
4051
4052static void DeleteSelection(JNIEnv* env, jobject obj, jint nativeClass,
4053        jint start, jint end, jint textGeneration)
4054{
4055    WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
4056    viewImpl->deleteSelection(start, end, textGeneration);
4057}
4058
4059static void DeleteSurroundingText(JNIEnv *env, jobject obj, jint nativeClass,
4060        jint leftLength, jint rightLength)
4061{
4062    WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
4063    viewImpl->deleteSurroundingText(leftLength, rightLength);
4064}
4065
4066static void SetSelection(JNIEnv* env, jobject obj, jint nativeClass,
4067        jint start, jint end)
4068{
4069    WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
4070    viewImpl->setSelection(start, end);
4071}
4072
4073static jstring ModifySelection(JNIEnv* env, jobject obj, jint nativeClass,
4074        jint direction, jint granularity)
4075{
4076    WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
4077    String selectionString = viewImpl->modifySelection(direction, granularity);
4078    return wtfStringToJstring(env, selectionString);
4079}
4080
4081static void ReplaceTextfieldText(JNIEnv* env, jobject obj, jint nativeClass,
4082    jint oldStart, jint oldEnd, jstring replace, jint start, jint end,
4083    jint textGeneration)
4084{
4085    WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
4086    WTF::String webcoreString = jstringToWtfString(env, replace);
4087    viewImpl->replaceTextfieldText(oldStart,
4088            oldEnd, webcoreString, start, end, textGeneration);
4089}
4090
4091static void PassToJs(JNIEnv* env, jobject obj, jint nativeClass,
4092    jint generation, jstring currentText, jint keyCode,
4093    jint keyValue, jboolean down, jboolean cap, jboolean fn, jboolean sym)
4094{
4095    WTF::String current = jstringToWtfString(env, currentText);
4096    reinterpret_cast<WebViewCore*>(nativeClass)->passToJs(generation, current,
4097        PlatformKeyboardEvent(keyCode, keyValue, 0, down, cap, fn, sym));
4098}
4099
4100static void ScrollFocusedTextInput(JNIEnv* env, jobject obj, jint nativeClass,
4101        jfloat xPercent, jint y)
4102{
4103    WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
4104    viewImpl->scrollFocusedTextInput(xPercent, y);
4105}
4106
4107static void SetFocusControllerActive(JNIEnv* env, jobject obj, jint nativeClass,
4108        jboolean active)
4109{
4110    ALOGV("webviewcore::nativeSetFocusControllerActive()\n");
4111    WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
4112    ALOG_ASSERT(viewImpl, "viewImpl not set in nativeSetFocusControllerActive");
4113    viewImpl->setFocusControllerActive(active);
4114}
4115
4116static void SaveDocumentState(JNIEnv* env, jobject obj, jint nativeClass,
4117        jint frame)
4118{
4119    ALOGV("webviewcore::nativeSaveDocumentState()\n");
4120    WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
4121    ALOG_ASSERT(viewImpl, "viewImpl not set in nativeSaveDocumentState");
4122    viewImpl->saveDocumentState((WebCore::Frame*) frame);
4123}
4124
4125void WebViewCore::addVisitedLink(const UChar* string, int length)
4126{
4127    if (m_groupForVisitedLinks)
4128        m_groupForVisitedLinks->addVisitedLink(string, length);
4129}
4130
4131static bool UpdateLayers(JNIEnv* env, jobject obj, jint nativeClass,
4132        jint jbaseLayer)
4133{
4134    WebViewCore* viewImpl = (WebViewCore*) nativeClass;
4135    BaseLayerAndroid* baseLayer = (BaseLayerAndroid*)  jbaseLayer;
4136    if (baseLayer) {
4137        LayerAndroid* root = static_cast<LayerAndroid*>(baseLayer->getChild(0));
4138        if (root)
4139            return viewImpl->updateLayers(root);
4140    }
4141    return true;
4142}
4143
4144static void NotifyAnimationStarted(JNIEnv* env, jobject obj, jint nativeClass)
4145{
4146    WebViewCore* viewImpl = (WebViewCore*) nativeClass;
4147    viewImpl->notifyAnimationStarted();
4148}
4149
4150static jint RecordContent(JNIEnv* env, jobject obj, jint nativeClass,
4151        jobject region, jobject pt)
4152{
4153    WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
4154    SkRegion* nativeRegion = GraphicsJNI::getNativeRegion(env, region);
4155    SkIPoint nativePt;
4156    BaseLayerAndroid* result = viewImpl->recordContent(nativeRegion, &nativePt);
4157    GraphicsJNI::ipoint_to_jpoint(nativePt, env, pt);
4158    return reinterpret_cast<jint>(result);
4159}
4160
4161static void SplitContent(JNIEnv* env, jobject obj, jint nativeClass,
4162        jint content)
4163{
4164    WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
4165    viewImpl->splitContent(reinterpret_cast<PictureSet*>(content));
4166}
4167
4168static void SendListBoxChoice(JNIEnv* env, jobject obj, jint nativeClass,
4169        jint choice)
4170{
4171    WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
4172    ALOG_ASSERT(viewImpl, "viewImpl not set in nativeSendListBoxChoice");
4173    viewImpl->popupReply(choice);
4174}
4175
4176// Set aside a predetermined amount of space in which to place the listbox
4177// choices, to avoid unnecessary allocations.
4178// The size here is arbitrary.  We want the size to be at least as great as the
4179// number of items in the average multiple-select listbox.
4180#define PREPARED_LISTBOX_STORAGE 10
4181
4182static void SendListBoxChoices(JNIEnv* env, jobject obj, jint nativeClass,
4183        jbooleanArray jArray, jint size)
4184{
4185    WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
4186    ALOG_ASSERT(viewImpl, "viewImpl not set in nativeSendListBoxChoices");
4187    jboolean* ptrArray = env->GetBooleanArrayElements(jArray, 0);
4188    SkAutoSTMalloc<PREPARED_LISTBOX_STORAGE, int> storage(size);
4189    int* array = storage.get();
4190    int count = 0;
4191    for (int i = 0; i < size; i++) {
4192        if (ptrArray[i]) {
4193            array[count++] = i;
4194        }
4195    }
4196    env->ReleaseBooleanArrayElements(jArray, ptrArray, JNI_ABORT);
4197    viewImpl->popupReply(array, count);
4198}
4199
4200static jstring FindAddress(JNIEnv* env, jobject obj, jstring addr,
4201        jboolean caseInsensitive)
4202{
4203    if (!addr)
4204        return 0;
4205    int length = env->GetStringLength(addr);
4206    if (!length)
4207        return 0;
4208    const jchar* addrChars = env->GetStringChars(addr, 0);
4209    int start, end;
4210    bool success = CacheBuilder::FindAddress(addrChars, length,
4211        &start, &end, caseInsensitive) == CacheBuilder::FOUND_COMPLETE;
4212    jstring ret = 0;
4213    if (success)
4214        ret = env->NewString(addrChars + start, end - start);
4215    env->ReleaseStringChars(addr, addrChars);
4216    return ret;
4217}
4218
4219static jboolean HandleTouchEvent(JNIEnv* env, jobject obj, jint nativeClass,
4220        jint action, jintArray idArray, jintArray xArray, jintArray yArray,
4221        jint count, jint actionIndex, jint metaState)
4222{
4223    WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
4224    ALOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
4225    jint* ptrIdArray = env->GetIntArrayElements(idArray, 0);
4226    jint* ptrXArray = env->GetIntArrayElements(xArray, 0);
4227    jint* ptrYArray = env->GetIntArrayElements(yArray, 0);
4228    Vector<int> ids(count);
4229    Vector<IntPoint> points(count);
4230    for (int c = 0; c < count; c++) {
4231        ids[c] = ptrIdArray[c];
4232        points[c].setX(ptrXArray[c]);
4233        points[c].setY(ptrYArray[c]);
4234    }
4235    env->ReleaseIntArrayElements(idArray, ptrIdArray, JNI_ABORT);
4236    env->ReleaseIntArrayElements(xArray, ptrXArray, JNI_ABORT);
4237    env->ReleaseIntArrayElements(yArray, ptrYArray, JNI_ABORT);
4238
4239    return viewImpl->handleTouchEvent(action, ids, points, actionIndex, metaState);
4240}
4241
4242static void TouchUp(JNIEnv* env, jobject obj, jint nativeClass,
4243        jint touchGeneration, jint frame, jint node, jint x, jint y)
4244{
4245    WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
4246    ALOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
4247    viewImpl->touchUp(touchGeneration,
4248        (WebCore::Frame*) frame, (WebCore::Node*) node, x, y);
4249}
4250
4251static jstring RetrieveHref(JNIEnv* env, jobject obj, jint nativeClass,
4252        jint x, jint y)
4253{
4254    WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
4255    ALOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
4256    WTF::String result = viewImpl->retrieveHref(x, y);
4257    if (!result.isEmpty())
4258        return wtfStringToJstring(env, result);
4259    return 0;
4260}
4261
4262static jstring RetrieveAnchorText(JNIEnv* env, jobject obj, jint nativeClass,
4263        jint x, jint y)
4264{
4265    WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
4266    ALOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
4267    WTF::String result = viewImpl->retrieveAnchorText(x, y);
4268    if (!result.isEmpty())
4269        return wtfStringToJstring(env, result);
4270    return 0;
4271}
4272
4273static jstring RetrieveImageSource(JNIEnv* env, jobject obj, jint nativeClass,
4274        jint x, jint y)
4275{
4276    WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
4277    WTF::String result = viewImpl->retrieveImageSource(x, y);
4278    return !result.isEmpty() ? wtfStringToJstring(env, result) : 0;
4279}
4280
4281static void StopPaintingCaret(JNIEnv* env, jobject obj, jint nativeClass)
4282{
4283    reinterpret_cast<WebViewCore*>(nativeClass)->setShouldPaintCaret(false);
4284}
4285
4286static void MoveFocus(JNIEnv* env, jobject obj, jint nativeClass, jint framePtr,
4287        jint nodePtr)
4288{
4289    WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
4290    ALOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
4291    viewImpl->moveFocus((WebCore::Frame*) framePtr, (WebCore::Node*) nodePtr);
4292}
4293
4294static void MoveMouse(JNIEnv* env, jobject obj, jint nativeClass, jint frame,
4295        jint x, jint y)
4296{
4297    WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
4298    ALOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
4299    viewImpl->moveMouse((WebCore::Frame*) frame, x, y);
4300}
4301
4302static void MoveMouseIfLatest(JNIEnv* env, jobject obj, jint nativeClass,
4303        jint moveGeneration, jint frame, jint x, jint y)
4304{
4305    WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
4306    ALOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
4307    viewImpl->moveMouseIfLatest(moveGeneration,
4308        (WebCore::Frame*) frame, x, y);
4309}
4310
4311static void UpdateFrameCache(JNIEnv* env, jobject obj, jint nativeClass)
4312{
4313#if ENABLE(ANDROID_NAVCACHE)
4314        WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
4315    ALOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
4316    viewImpl->updateFrameCache();
4317#endif
4318}
4319
4320static jint GetContentMinPrefWidth(JNIEnv* env, jobject obj, jint nativeClass)
4321{
4322    WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
4323    ALOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
4324
4325    WebCore::Frame* frame = viewImpl->mainFrame();
4326    if (frame) {
4327        WebCore::Document* document = frame->document();
4328        if (document) {
4329            WebCore::RenderObject* renderer = document->renderer();
4330            if (renderer && renderer->isRenderView()) {
4331                return renderer->minPreferredLogicalWidth();
4332            }
4333        }
4334    }
4335    return 0;
4336}
4337
4338static void SetViewportSettingsFromNative(JNIEnv* env, jobject obj,
4339        jint nativeClass)
4340{
4341    WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
4342    ALOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
4343
4344    WebCore::Settings* s = viewImpl->mainFrame()->page()->settings();
4345    if (!s)
4346        return;
4347
4348#ifdef ANDROID_META_SUPPORT
4349    env->SetIntField(obj, gWebViewCoreFields.m_viewportWidth, s->viewportWidth());
4350    env->SetIntField(obj, gWebViewCoreFields.m_viewportHeight, s->viewportHeight());
4351    env->SetIntField(obj, gWebViewCoreFields.m_viewportInitialScale, s->viewportInitialScale());
4352    env->SetIntField(obj, gWebViewCoreFields.m_viewportMinimumScale, s->viewportMinimumScale());
4353    env->SetIntField(obj, gWebViewCoreFields.m_viewportMaximumScale, s->viewportMaximumScale());
4354    env->SetBooleanField(obj, gWebViewCoreFields.m_viewportUserScalable, s->viewportUserScalable());
4355    env->SetIntField(obj, gWebViewCoreFields.m_viewportDensityDpi, s->viewportTargetDensityDpi());
4356#endif
4357}
4358
4359static void SetBackgroundColor(JNIEnv* env, jobject obj, jint nativeClass,
4360        jint color)
4361{
4362    WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
4363    ALOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
4364
4365    viewImpl->setBackgroundColor((SkColor) color);
4366}
4367
4368static void DumpDomTree(JNIEnv* env, jobject obj, jint nativeClass,
4369        jboolean useFile)
4370{
4371    WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
4372    ALOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
4373
4374    viewImpl->dumpDomTree(useFile);
4375}
4376
4377static void DumpRenderTree(JNIEnv* env, jobject obj, jint nativeClass,
4378        jboolean useFile)
4379{
4380    WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
4381    ALOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
4382
4383    viewImpl->dumpRenderTree(useFile);
4384}
4385
4386static void DumpNavTree(JNIEnv* env, jobject obj, jint nativeClass)
4387{
4388    WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
4389    ALOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
4390
4391    viewImpl->dumpNavTree();
4392}
4393
4394static void SetJsFlags(JNIEnv* env, jobject obj, jint nativeClass, jstring flags)
4395{
4396#if USE(V8)
4397    WTF::String flagsString = jstringToWtfString(env, flags);
4398    WTF::CString utf8String = flagsString.utf8();
4399    WebCore::ScriptController::setFlags(utf8String.data(), utf8String.length());
4400#endif
4401}
4402
4403
4404// Called from the Java side to set a new quota for the origin or new appcache
4405// max size in response to a notification that the original quota was exceeded or
4406// that the appcache has reached its maximum size.
4407static void SetNewStorageLimit(JNIEnv* env, jobject obj, jint nativeClass,
4408        jlong quota)
4409{
4410#if ENABLE(DATABASE) || ENABLE(OFFLINE_WEB_APPLICATIONS)
4411    WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
4412    Frame* frame = viewImpl->mainFrame();
4413
4414    // The main thread is blocked awaiting this response, so now we can wake it
4415    // up.
4416    ChromeClientAndroid* chromeC = static_cast<ChromeClientAndroid*>(frame->page()->chrome()->client());
4417    chromeC->wakeUpMainThreadWithNewQuota(quota);
4418#endif
4419}
4420
4421// Called from Java to provide a Geolocation permission state for the specified origin.
4422static void GeolocationPermissionsProvide(JNIEnv* env, jobject obj,
4423        jint nativeClass, jstring origin, jboolean allow, jboolean remember)
4424{
4425    WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
4426    Frame* frame = viewImpl->mainFrame();
4427
4428    ChromeClientAndroid* chromeClient = static_cast<ChromeClientAndroid*>(frame->page()->chrome()->client());
4429    chromeClient->provideGeolocationPermissions(jstringToWtfString(env, origin), allow, remember);
4430}
4431
4432static void RegisterURLSchemeAsLocal(JNIEnv* env, jobject obj, jint nativeClass,
4433        jstring scheme)
4434{
4435    WebCore::SchemeRegistry::registerURLSchemeAsLocal(jstringToWtfString(env, scheme));
4436}
4437
4438static bool FocusBoundsChanged(JNIEnv* env, jobject obj, jint nativeClass)
4439{
4440    return reinterpret_cast<WebViewCore*>(nativeClass)->focusBoundsChanged();
4441}
4442
4443static void SetIsPaused(JNIEnv* env, jobject obj, jint nativeClass,
4444        jboolean isPaused)
4445{
4446    // tell the webcore thread to stop thinking while we do other work
4447    // (selection and scrolling). This has nothing to do with the lifecycle
4448    // pause and resume.
4449    reinterpret_cast<WebViewCore*>(nativeClass)->setIsPaused(isPaused);
4450}
4451
4452static void Pause(JNIEnv* env, jobject obj, jint nativeClass)
4453{
4454    // This is called for the foreground tab when the browser is put to the
4455    // background (and also for any tab when it is put to the background of the
4456    // browser). The browser can only be killed by the system when it is in the
4457    // background, so saving the Geolocation permission state now ensures that
4458    // is maintained when the browser is killed.
4459    WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
4460    ChromeClient* chromeClient = viewImpl->mainFrame()->page()->chrome()->client();
4461    ChromeClientAndroid* chromeClientAndroid = static_cast<ChromeClientAndroid*>(chromeClient);
4462    chromeClientAndroid->storeGeolocationPermissions();
4463
4464    Frame* mainFrame = viewImpl->mainFrame();
4465    for (Frame* frame = mainFrame; frame; frame = frame->tree()->traverseNext()) {
4466        Geolocation* geolocation = frame->domWindow()->navigator()->optionalGeolocation();
4467        if (geolocation)
4468            geolocation->suspend();
4469    }
4470
4471    viewImpl->deviceMotionAndOrientationManager()->maybeSuspendClients();
4472
4473    ANPEvent event;
4474    SkANP::InitEvent(&event, kLifecycle_ANPEventType);
4475    event.data.lifecycle.action = kPause_ANPLifecycleAction;
4476    viewImpl->sendPluginEvent(event);
4477
4478    viewImpl->setIsPaused(true);
4479}
4480
4481static void Resume(JNIEnv* env, jobject obj, jint nativeClass)
4482{
4483    WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
4484    Frame* mainFrame = viewImpl->mainFrame();
4485    for (Frame* frame = mainFrame; frame; frame = frame->tree()->traverseNext()) {
4486        Geolocation* geolocation = frame->domWindow()->navigator()->optionalGeolocation();
4487        if (geolocation)
4488            geolocation->resume();
4489    }
4490
4491    viewImpl->deviceMotionAndOrientationManager()->maybeResumeClients();
4492
4493    ANPEvent event;
4494    SkANP::InitEvent(&event, kLifecycle_ANPEventType);
4495    event.data.lifecycle.action = kResume_ANPLifecycleAction;
4496    viewImpl->sendPluginEvent(event);
4497
4498    viewImpl->setIsPaused(false);
4499}
4500
4501static void FreeMemory(JNIEnv* env, jobject obj, jint nativeClass)
4502{
4503    ANPEvent event;
4504    SkANP::InitEvent(&event, kLifecycle_ANPEventType);
4505    event.data.lifecycle.action = kFreeMemory_ANPLifecycleAction;
4506    reinterpret_cast<WebViewCore*>(nativeClass)->sendPluginEvent(event);
4507}
4508
4509static void ProvideVisitedHistory(JNIEnv* env, jobject obj, jint nativeClass,
4510        jobject hist)
4511{
4512    WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
4513    ALOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
4514
4515    jobjectArray array = static_cast<jobjectArray>(hist);
4516
4517    jsize len = env->GetArrayLength(array);
4518    for (jsize i = 0; i < len; i++) {
4519        jstring item = static_cast<jstring>(env->GetObjectArrayElement(array, i));
4520        const UChar* str = static_cast<const UChar*>(env->GetStringChars(item, 0));
4521        jsize len = env->GetStringLength(item);
4522        viewImpl->addVisitedLink(str, len);
4523        env->ReleaseStringChars(item, str);
4524        env->DeleteLocalRef(item);
4525    }
4526}
4527
4528static void PluginSurfaceReady(JNIEnv* env, jobject obj, jint nativeClass)
4529{
4530    WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
4531    if (viewImpl)
4532        viewImpl->sendPluginSurfaceReady();
4533}
4534
4535// Notification from the UI thread that the plugin's full-screen surface has been discarded
4536static void FullScreenPluginHidden(JNIEnv* env, jobject obj, jint nativeClass,
4537        jint npp)
4538{
4539    WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
4540    PluginWidgetAndroid* plugin = viewImpl->getPluginWidget((NPP)npp);
4541    if (plugin)
4542        plugin->exitFullScreen(false);
4543}
4544
4545static WebCore::IntRect jrect_to_webrect(JNIEnv* env, jobject obj)
4546{
4547    int L, T, R, B;
4548    GraphicsJNI::get_jrect(env, obj, &L, &T, &R, &B);
4549    return WebCore::IntRect(L, T, R - L, B - T);
4550}
4551
4552static bool ValidNodeAndBounds(JNIEnv* env, jobject obj, jint nativeClass,
4553        int frame, int node, jobject rect)
4554{
4555    IntRect nativeRect = jrect_to_webrect(env, rect);
4556    WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
4557    return viewImpl->validNodeAndBounds(
4558            reinterpret_cast<Frame*>(frame),
4559            reinterpret_cast<Node*>(node), nativeRect);
4560}
4561
4562static int GetHitTestExtra(Node* node, HitTestResult& hitTestResult, WTF::String& extra)
4563{
4564    /*
4565        UNKNOWN_TYPE = 0;
4566        PHONE_TYPE = 2;
4567        GEO_TYPE = 3;
4568        EMAIL_TYPE = 4;
4569        IMAGE_TYPE = 5;
4570        SRC_ANCHOR_TYPE = 7;
4571        SRC_IMAGE_ANCHOR_TYPE = 8;
4572        EDIT_TEXT_TYPE = 9;
4573     */
4574    if (!node)
4575        return 0;
4576    KURL imageUrl = hitTestResult.absoluteImageURL();
4577    if (node->isLink()) {
4578        if (!imageUrl.isEmpty()) {
4579            extra = imageUrl.string();
4580            return 8;
4581        }
4582        extra = hitTestResult.absoluteLinkURL();
4583        return 7;
4584
4585    }
4586    if (!imageUrl.isEmpty()) {
4587        extra = imageUrl.string();
4588        return 5;
4589    }
4590    if (hitTestResult.isContentEditable()) {
4591        return 9;
4592    }
4593    return 0;
4594}
4595
4596static jobject HitTest(JNIEnv* env, jobject obj, jint nativeClass, jint x, jint y, jint slop)
4597{
4598    WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
4599    if (!viewImpl)
4600        return 0;
4601    Node* node = 0;
4602    HitTestResult hitTestResult;
4603    Vector<IntRect> rects = viewImpl->getTouchHighlightRects(x, y, slop, &node, &hitTestResult);
4604    if (rects.isEmpty() && !node)
4605        return 0;
4606
4607    jclass rectClass = env->FindClass("android/graphics/Rect");
4608    ALOG_ASSERT(rectClass, "Could not find android/graphics/Rect");
4609    jmethodID rectinit = env->GetMethodID(rectClass, "<init>", "(IIII)V");
4610    ALOG_ASSERT(rectinit, "Could not find init method on Rect");
4611    jobjectArray array = env->NewObjectArray(rects.size(), rectClass, 0);
4612    ALOG_ASSERT(array, "Could not create a Rect array");
4613
4614    for (size_t i = 0; i < rects.size(); i++) {
4615        jobject rect = env->NewObject(rectClass, rectinit, rects[i].x(),
4616                rects[i].y(), rects[i].maxX(), rects[i].maxY());
4617        if (rect) {
4618            env->SetObjectArrayElement(array, i, rect);
4619            env->DeleteLocalRef(rect);
4620        }
4621    }
4622
4623    env->DeleteLocalRef(rectClass);
4624
4625    jclass hitTestClass = env->FindClass("android/webkit/WebViewCore$WebKitHitTest");
4626    ALOG_ASSERT(hitTestClass, "Could not find android/webkit/WebViewCore$WebKitHitTest");
4627    jmethodID hitTestInit = env->GetMethodID(hitTestClass, "<init>",  "()V");
4628    jobject hitTest = env->NewObject(hitTestClass, hitTestInit);
4629    jfieldID htTouchRects = env->GetFieldID(hitTestClass, "mTouchRects", "[Landroid/graphics/Rect;");
4630    env->SetObjectField(hitTest, htTouchRects, array);
4631    jfieldID htType = env->GetFieldID(hitTestClass, "mType", "I");
4632    jfieldID htExtra = env->GetFieldID(hitTestClass, "mExtra", "Ljava/lang/String;");
4633    WTF::String extra;
4634    int type = GetHitTestExtra(node, hitTestResult, extra);
4635    env->SetIntField(hitTest, htType, type);
4636    jstring jextra = wtfStringToJstring(env, extra, false);
4637    env->SetObjectField(hitTest, htExtra, jextra);
4638    env->DeleteLocalRef(hitTestClass);
4639    return hitTest;
4640}
4641
4642static void AutoFillForm(JNIEnv* env, jobject obj, jint nativeClass,
4643        jint queryId)
4644{
4645#if ENABLE(WEB_AUTOFILL)
4646    WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass);
4647    if (!viewImpl)
4648        return;
4649
4650    WebCore::Frame* frame = viewImpl->mainFrame();
4651    if (frame) {
4652        EditorClientAndroid* editorC = static_cast<EditorClientAndroid*>(frame->page()->editorClient());
4653        WebAutofill* autoFill = editorC->getAutofill();
4654        autoFill->fillFormFields(queryId);
4655    }
4656#endif
4657}
4658
4659static void CloseIdleConnections(JNIEnv* env, jobject obj, jint nativeClass)
4660{
4661#if USE(CHROME_NETWORK_STACK)
4662    WebCache::get(true)->closeIdleConnections();
4663    WebCache::get(false)->closeIdleConnections();
4664#endif
4665}
4666
4667static void ScrollRenderLayer(JNIEnv* env, jobject obj, jint nativeClass,
4668        jint layer, jobject jRect)
4669{
4670    SkRect rect;
4671    GraphicsJNI::jrect_to_rect(env, jRect, &rect);
4672    reinterpret_cast<WebViewCore*>(nativeClass)->scrollRenderLayer(layer, rect);
4673}
4674
4675// ----------------------------------------------------------------------------
4676
4677/*
4678 * JNI registration.
4679 */
4680static JNINativeMethod gJavaWebViewCoreMethods[] = {
4681    { "nativeClearContent", "(I)V",
4682            (void*) ClearContent },
4683    { "nativeFocusBoundsChanged", "(I)Z",
4684        (void*) FocusBoundsChanged } ,
4685    { "nativeKey", "(IIIIZZZZ)Z",
4686        (void*) Key },
4687    { "nativeClick", "(IIIZ)V",
4688        (void*) Click },
4689    { "nativeContentInvalidateAll", "(I)V",
4690        (void*) ContentInvalidateAll },
4691    { "nativeSendListBoxChoices", "(I[ZI)V",
4692        (void*) SendListBoxChoices },
4693    { "nativeSendListBoxChoice", "(II)V",
4694        (void*) SendListBoxChoice },
4695    { "nativeSetSize", "(IIIIFIIIIZ)V",
4696        (void*) SetSize },
4697    { "nativeSetScrollOffset", "(IIZII)V",
4698        (void*) SetScrollOffset },
4699    { "nativeSetGlobalBounds", "(IIIII)V",
4700        (void*) SetGlobalBounds },
4701    { "nativeSetSelection", "(III)V",
4702        (void*) SetSelection } ,
4703    { "nativeModifySelection", "(III)Ljava/lang/String;",
4704        (void*) ModifySelection },
4705    { "nativeDeleteSelection", "(IIII)V",
4706        (void*) DeleteSelection } ,
4707    { "nativeDeleteSurroundingText", "(III)V",
4708        (void*) DeleteSurroundingText } ,
4709    { "nativeReplaceTextfieldText", "(IIILjava/lang/String;III)V",
4710        (void*) ReplaceTextfieldText } ,
4711    { "nativeMoveFocus", "(III)V",
4712        (void*) MoveFocus },
4713    { "nativeMoveMouse", "(IIII)V",
4714        (void*) MoveMouse },
4715    { "nativeMoveMouseIfLatest", "(IIIII)V",
4716        (void*) MoveMouseIfLatest },
4717    { "passToJs", "(IILjava/lang/String;IIZZZZ)V",
4718        (void*) PassToJs },
4719    { "nativeScrollFocusedTextInput", "(IFI)V",
4720        (void*) ScrollFocusedTextInput },
4721    { "nativeSetFocusControllerActive", "(IZ)V",
4722        (void*) SetFocusControllerActive },
4723    { "nativeSaveDocumentState", "(II)V",
4724        (void*) SaveDocumentState },
4725    { "nativeFindAddress", "(Ljava/lang/String;Z)Ljava/lang/String;",
4726        (void*) FindAddress },
4727    { "nativeHandleTouchEvent", "(II[I[I[IIII)Z",
4728            (void*) HandleTouchEvent },
4729    { "nativeTouchUp", "(IIIIII)V",
4730        (void*) TouchUp },
4731    { "nativeRetrieveHref", "(III)Ljava/lang/String;",
4732        (void*) RetrieveHref },
4733    { "nativeRetrieveAnchorText", "(III)Ljava/lang/String;",
4734        (void*) RetrieveAnchorText },
4735    { "nativeRetrieveImageSource", "(III)Ljava/lang/String;",
4736        (void*) RetrieveImageSource },
4737    { "nativeStopPaintingCaret", "(I)V",
4738        (void*) StopPaintingCaret },
4739    { "nativeUpdateFrameCache", "(I)V",
4740        (void*) UpdateFrameCache },
4741    { "nativeGetContentMinPrefWidth", "(I)I",
4742        (void*) GetContentMinPrefWidth },
4743    { "nativeUpdateLayers", "(II)Z",
4744        (void*) UpdateLayers },
4745    { "nativeNotifyAnimationStarted", "(I)V",
4746        (void*) NotifyAnimationStarted },
4747    { "nativeRecordContent", "(ILandroid/graphics/Region;Landroid/graphics/Point;)I",
4748        (void*) RecordContent },
4749    { "setViewportSettingsFromNative", "(I)V",
4750        (void*) SetViewportSettingsFromNative },
4751    { "nativeSplitContent", "(II)V",
4752        (void*) SplitContent },
4753    { "nativeSetBackgroundColor", "(II)V",
4754        (void*) SetBackgroundColor },
4755    { "nativeRegisterURLSchemeAsLocal", "(ILjava/lang/String;)V",
4756        (void*) RegisterURLSchemeAsLocal },
4757    { "nativeDumpDomTree", "(IZ)V",
4758        (void*) DumpDomTree },
4759    { "nativeDumpRenderTree", "(IZ)V",
4760        (void*) DumpRenderTree },
4761    { "nativeDumpNavTree", "(I)V",
4762        (void*) DumpNavTree },
4763    { "nativeSetNewStorageLimit", "(IJ)V",
4764        (void*) SetNewStorageLimit },
4765    { "nativeGeolocationPermissionsProvide", "(ILjava/lang/String;ZZ)V",
4766        (void*) GeolocationPermissionsProvide },
4767    { "nativeSetIsPaused", "(IZ)V", (void*) SetIsPaused },
4768    { "nativePause", "(I)V", (void*) Pause },
4769    { "nativeResume", "(I)V", (void*) Resume },
4770    { "nativeFreeMemory", "(I)V", (void*) FreeMemory },
4771    { "nativeSetJsFlags", "(ILjava/lang/String;)V", (void*) SetJsFlags },
4772    { "nativeRequestLabel", "(III)Ljava/lang/String;",
4773        (void*) RequestLabel },
4774    { "nativeRevealSelection", "(I)V", (void*) RevealSelection },
4775    { "nativeUpdateFrameCacheIfLoading", "(I)V",
4776        (void*) UpdateFrameCacheIfLoading },
4777    { "nativeProvideVisitedHistory", "(I[Ljava/lang/String;)V",
4778        (void*) ProvideVisitedHistory },
4779    { "nativeFullScreenPluginHidden", "(II)V",
4780        (void*) FullScreenPluginHidden },
4781    { "nativePluginSurfaceReady", "(I)V",
4782        (void*) PluginSurfaceReady },
4783    { "nativeValidNodeAndBounds", "(IIILandroid/graphics/Rect;)Z",
4784        (void*) ValidNodeAndBounds },
4785    { "nativeHitTest", "(IIII)Landroid/webkit/WebViewCore$WebKitHitTest;",
4786        (void*) HitTest },
4787    { "nativeAutoFillForm", "(II)V",
4788        (void*) AutoFillForm },
4789    { "nativeScrollLayer", "(IILandroid/graphics/Rect;)V",
4790        (void*) ScrollRenderLayer },
4791    { "nativeCloseIdleConnections", "(I)V",
4792        (void*) CloseIdleConnections },
4793};
4794
4795int registerWebViewCore(JNIEnv* env)
4796{
4797    jclass widget = env->FindClass("android/webkit/WebViewCore");
4798    ALOG_ASSERT(widget,
4799            "Unable to find class android/webkit/WebViewCore");
4800    gWebViewCoreFields.m_nativeClass = env->GetFieldID(widget, "mNativeClass",
4801            "I");
4802    ALOG_ASSERT(gWebViewCoreFields.m_nativeClass,
4803            "Unable to find android/webkit/WebViewCore.mNativeClass");
4804    gWebViewCoreFields.m_viewportWidth = env->GetFieldID(widget,
4805            "mViewportWidth", "I");
4806    ALOG_ASSERT(gWebViewCoreFields.m_viewportWidth,
4807            "Unable to find android/webkit/WebViewCore.mViewportWidth");
4808    gWebViewCoreFields.m_viewportHeight = env->GetFieldID(widget,
4809            "mViewportHeight", "I");
4810    ALOG_ASSERT(gWebViewCoreFields.m_viewportHeight,
4811            "Unable to find android/webkit/WebViewCore.mViewportHeight");
4812    gWebViewCoreFields.m_viewportInitialScale = env->GetFieldID(widget,
4813            "mViewportInitialScale", "I");
4814    ALOG_ASSERT(gWebViewCoreFields.m_viewportInitialScale,
4815            "Unable to find android/webkit/WebViewCore.mViewportInitialScale");
4816    gWebViewCoreFields.m_viewportMinimumScale = env->GetFieldID(widget,
4817            "mViewportMinimumScale", "I");
4818    ALOG_ASSERT(gWebViewCoreFields.m_viewportMinimumScale,
4819            "Unable to find android/webkit/WebViewCore.mViewportMinimumScale");
4820    gWebViewCoreFields.m_viewportMaximumScale = env->GetFieldID(widget,
4821            "mViewportMaximumScale", "I");
4822    ALOG_ASSERT(gWebViewCoreFields.m_viewportMaximumScale,
4823            "Unable to find android/webkit/WebViewCore.mViewportMaximumScale");
4824    gWebViewCoreFields.m_viewportUserScalable = env->GetFieldID(widget,
4825            "mViewportUserScalable", "Z");
4826    ALOG_ASSERT(gWebViewCoreFields.m_viewportUserScalable,
4827            "Unable to find android/webkit/WebViewCore.mViewportUserScalable");
4828    gWebViewCoreFields.m_viewportDensityDpi = env->GetFieldID(widget,
4829            "mViewportDensityDpi", "I");
4830    ALOG_ASSERT(gWebViewCoreFields.m_viewportDensityDpi,
4831            "Unable to find android/webkit/WebViewCore.mViewportDensityDpi");
4832    gWebViewCoreFields.m_webView = env->GetFieldID(widget,
4833            "mWebView", "Landroid/webkit/WebView;");
4834    ALOG_ASSERT(gWebViewCoreFields.m_webView,
4835            "Unable to find android/webkit/WebViewCore.mWebView");
4836    gWebViewCoreFields.m_drawIsPaused = env->GetFieldID(widget,
4837            "mDrawIsPaused", "Z");
4838    ALOG_ASSERT(gWebViewCoreFields.m_drawIsPaused,
4839            "Unable to find android/webkit/WebViewCore.mDrawIsPaused");
4840    gWebViewCoreFields.m_lowMemoryUsageMb = env->GetFieldID(widget, "mLowMemoryUsageThresholdMb", "I");
4841    gWebViewCoreFields.m_highMemoryUsageMb = env->GetFieldID(widget, "mHighMemoryUsageThresholdMb", "I");
4842    gWebViewCoreFields.m_highUsageDeltaMb = env->GetFieldID(widget, "mHighUsageDeltaMb", "I");
4843
4844    gWebViewCoreStaticMethods.m_isSupportedMediaMimeType =
4845        env->GetStaticMethodID(widget, "isSupportedMediaMimeType", "(Ljava/lang/String;)Z");
4846    LOG_FATAL_IF(!gWebViewCoreStaticMethods.m_isSupportedMediaMimeType,
4847        "Could not find static method isSupportedMediaMimeType from WebViewCore");
4848
4849    env->DeleteLocalRef(widget);
4850
4851    return jniRegisterNativeMethods(env, "android/webkit/WebViewCore",
4852            gJavaWebViewCoreMethods, NELEM(gJavaWebViewCoreMethods));
4853}
4854
4855} /* namespace android */
4856