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