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