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