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