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