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