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