OSDN Git Service

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