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 "AtomicString.h"
32#include "CachedNode.h"
33#include "CachedRoot.h"
34#include "Chrome.h"
35#include "ChromeClientAndroid.h"
36#include "Color.h"
37#include "DatabaseTracker.h"
38#include "Document.h"
39#include "DOMWindow.h"
40#include "Element.h"
41#include "Editor.h"
42#include "EditorClientAndroid.h"
43#include "EventHandler.h"
44#include "EventNames.h"
45#include "FocusController.h"
46#include "Font.h"
47#include "Frame.h"
48#include "FrameLoader.h"
49#include "FrameLoaderClientAndroid.h"
50#include "FrameTree.h"
51#include "FrameView.h"
52#include "Geolocation.h"
53#include "GraphicsContext.h"
54#include "GraphicsJNI.h"
55#include "HTMLAnchorElement.h"
56#include "HTMLAreaElement.h"
57#include "HTMLElement.h"
58#include "HTMLImageElement.h"
59#include "HTMLInputElement.h"
60#include "HTMLLabelElement.h"
61#include "HTMLMapElement.h"
62#include "HTMLNames.h"
63#include "HTMLOptGroupElement.h"
64#include "HTMLOptionElement.h"
65#include "HTMLSelectElement.h"
66#include "HTMLTextAreaElement.h"
67#include "HistoryItem.h"
68#include "HitTestResult.h"
69#include "InlineTextBox.h"
70#include "KeyboardCodes.h"
71#include "Navigator.h"
72#include "Node.h"
73#include "NodeList.h"
74#include "Page.h"
75#include "PageGroup.h"
76#include "PlatformKeyboardEvent.h"
77#include "PlatformString.h"
78#include "PluginWidgetAndroid.h"
79#include "PluginView.h"
80#include "Position.h"
81#include "ProgressTracker.h"
82#include "RenderBox.h"
83#include "RenderLayer.h"
84#include "RenderPart.h"
85#include "RenderText.h"
86#include "RenderTextControl.h"
87#include "RenderThemeAndroid.h"
88#include "RenderView.h"
89#include "ResourceRequest.h"
90#include "SelectionController.h"
91#include "Settings.h"
92#include "SkANP.h"
93#include "SkTemplates.h"
94#include "SkTDArray.h"
95#include "SkTypes.h"
96#include "SkCanvas.h"
97#include "SkPicture.h"
98#include "SkUtils.h"
99#include "StringImpl.h"
100#include "Text.h"
101#include "TypingCommand.h"
102#include "WebCoreFrameBridge.h"
103#include "WebFrameView.h"
104#include "android_graphics.h"
105
106#include <JNIHelp.h>
107#include <JNIUtility.h>
108#include <ui/KeycodeLabels.h>
109#include <wtf/CurrentTime.h>
110
111#if USE(V8)
112#include "CString.h"
113#include "ScriptController.h"
114#include "V8Counters.h"
115#endif
116
117#if DEBUG_NAV_UI
118#include "SkTime.h"
119#endif
120
121#if ENABLE(TOUCH_EVENTS) // Android
122#include "PlatformTouchEvent.h"
123#endif
124
125#ifdef ANDROID_DOM_LOGGING
126#include "AndroidLog.h"
127#include "RenderTreeAsText.h"
128#include "CString.h"
129
130FILE* gDomTreeFile = 0;
131FILE* gRenderTreeFile = 0;
132#endif
133
134#ifdef ANDROID_INSTRUMENT
135#include "TimeCounter.h"
136#endif
137
138#if USE(ACCELERATED_COMPOSITING)
139#include "GraphicsLayerAndroid.h"
140#include "RenderLayerCompositor.h"
141#endif
142
143/*  We pass this flag when recording the actual content, so that we don't spend
144    time actually regionizing complex path clips, when all we really want to do
145    is record them.
146 */
147#define PICT_RECORD_FLAGS   SkPicture::kUsePathBoundsForClip_RecordingFlag
148
149////////////////////////////////////////////////////////////////////////////////////////////////
150
151namespace android {
152
153static SkTDArray<WebViewCore*> gInstanceList;
154
155void WebViewCore::addInstance(WebViewCore* inst) {
156    *gInstanceList.append() = inst;
157}
158
159void WebViewCore::removeInstance(WebViewCore* inst) {
160    int index = gInstanceList.find(inst);
161    LOG_ASSERT(index >= 0, "RemoveInstance inst not found");
162    if (index >= 0) {
163        gInstanceList.removeShuffle(index);
164    }
165}
166
167bool WebViewCore::isInstance(WebViewCore* inst) {
168    return gInstanceList.find(inst) >= 0;
169}
170
171jobject WebViewCore::getApplicationContext() {
172
173    // check to see if there is a valid webviewcore object
174    if (gInstanceList.isEmpty())
175        return 0;
176
177    // get the context from the webview
178    jobject context = gInstanceList[0]->getContext();
179
180    if (!context)
181        return 0;
182
183    // get the application context using JNI
184    JNIEnv* env = JSC::Bindings::getJNIEnv();
185    jclass contextClass = env->GetObjectClass(context);
186    jmethodID appContextMethod = env->GetMethodID(contextClass, "getApplicationContext", "()Landroid/content/Context;");
187    jobject result = env->CallObjectMethod(context, appContextMethod);
188    checkException(env);
189    return result;
190}
191
192
193struct WebViewCoreStaticMethods {
194    jmethodID    m_supportsMimeType;
195} gWebViewCoreStaticMethods;
196
197// Check whether a media mimeType is supported in Android media framework.
198bool WebViewCore::supportsMimeType(const WebCore::String& mimeType) {
199    JNIEnv* env = JSC::Bindings::getJNIEnv();
200    jstring jMimeType = env->NewString(mimeType.characters(), mimeType.length());
201    jclass webViewCore = env->FindClass("android/webkit/WebViewCore");
202    bool val = env->CallStaticBooleanMethod(webViewCore,
203          gWebViewCoreStaticMethods.m_supportsMimeType, jMimeType);
204    checkException(env);
205    env->DeleteLocalRef(jMimeType);
206
207    return val;
208}
209
210// ----------------------------------------------------------------------------
211
212#define GET_NATIVE_VIEW(env, obj) ((WebViewCore*)env->GetIntField(obj, gWebViewCoreFields.m_nativeClass))
213
214// Field ids for WebViewCore
215struct WebViewCoreFields {
216    jfieldID    m_nativeClass;
217    jfieldID    m_viewportWidth;
218    jfieldID    m_viewportHeight;
219    jfieldID    m_viewportInitialScale;
220    jfieldID    m_viewportMinimumScale;
221    jfieldID    m_viewportMaximumScale;
222    jfieldID    m_viewportUserScalable;
223    jfieldID    m_viewportDensityDpi;
224    jfieldID    m_webView;
225} gWebViewCoreFields;
226
227// ----------------------------------------------------------------------------
228
229struct WebViewCore::JavaGlue {
230    jweak       m_obj;
231    jmethodID   m_spawnScrollTo;
232    jmethodID   m_scrollTo;
233    jmethodID   m_scrollBy;
234    jmethodID   m_contentDraw;
235    jmethodID   m_requestListBox;
236    jmethodID   m_openFileChooser;
237    jmethodID   m_requestSingleListBox;
238    jmethodID   m_jsAlert;
239    jmethodID   m_jsConfirm;
240    jmethodID   m_jsPrompt;
241    jmethodID   m_jsUnload;
242    jmethodID   m_jsInterrupt;
243    jmethodID   m_didFirstLayout;
244    jmethodID   m_updateViewport;
245    jmethodID   m_sendNotifyProgressFinished;
246    jmethodID   m_sendViewInvalidate;
247    jmethodID   m_sendImmediateRepaint;
248    jmethodID   m_setRootLayer;
249    jmethodID   m_updateTextfield;
250    jmethodID   m_updateTextSelection;
251    jmethodID   m_clearTextEntry;
252    jmethodID   m_restoreScale;
253    jmethodID   m_restoreScreenWidthScale;
254    jmethodID   m_needTouchEvents;
255    jmethodID   m_requestKeyboard;
256    jmethodID   m_requestKeyboardWithSelection;
257    jmethodID   m_exceededDatabaseQuota;
258    jmethodID   m_reachedMaxAppCacheSize;
259    jmethodID   m_populateVisitedLinks;
260    jmethodID   m_geolocationPermissionsShowPrompt;
261    jmethodID   m_geolocationPermissionsHidePrompt;
262    jmethodID   m_addMessageToConsole;
263    jmethodID   m_getPluginClass;
264    jmethodID   m_showFullScreenPlugin;
265    jmethodID   m_hideFullScreenPlugin;
266    jmethodID   m_addSurface;
267    jmethodID   m_updateSurface;
268    jmethodID   m_destroySurface;
269    jmethodID   m_getContext;
270    jmethodID   m_sendFindAgain;
271    jmethodID   m_showRect;
272    jmethodID   m_centerFitRect;
273    jmethodID   m_setScrollbarModes;
274    AutoJObject object(JNIEnv* env) {
275        return getRealObject(env, m_obj);
276    }
277};
278
279/*
280 * WebViewCore Implementation
281 */
282
283static jmethodID GetJMethod(JNIEnv* env, jclass clazz, const char name[], const char signature[])
284{
285    jmethodID m = env->GetMethodID(clazz, name, signature);
286    LOG_ASSERT(m, "Could not find method %s", name);
287    return m;
288}
289
290Mutex WebViewCore::gFrameCacheMutex;
291Mutex WebViewCore::gButtonMutex;
292Mutex WebViewCore::gCursorBoundsMutex;
293Mutex WebViewCore::m_contentMutex;
294
295WebViewCore::WebViewCore(JNIEnv* env, jobject javaWebViewCore, WebCore::Frame* mainframe)
296        : m_pluginInvalTimer(this, &WebViewCore::pluginInvalTimerFired)
297{
298    m_mainFrame = mainframe;
299
300    m_popupReply = 0;
301    m_moveGeneration = 0;
302    m_lastGeneration = 0;
303    m_touchGeneration = 0;
304    m_blockTextfieldUpdates = false;
305    // just initial values. These should be set by client
306    m_maxXScroll = 320/4;
307    m_maxYScroll = 240/4;
308    m_textGeneration = 0;
309    m_screenWidth = 320;
310    m_scale = 1;
311    m_screenWidthScale = 1;
312#if ENABLE(TOUCH_EVENTS)
313    m_forwardingTouchEvents = false;
314#endif
315    m_isPaused = false;
316
317    LOG_ASSERT(m_mainFrame, "Uh oh, somehow a frameview was made without an initial frame!");
318
319    jclass clazz = env->GetObjectClass(javaWebViewCore);
320    m_javaGlue = new JavaGlue;
321    m_javaGlue->m_obj = env->NewWeakGlobalRef(javaWebViewCore);
322    m_javaGlue->m_spawnScrollTo = GetJMethod(env, clazz, "contentSpawnScrollTo", "(II)V");
323    m_javaGlue->m_scrollTo = GetJMethod(env, clazz, "contentScrollTo", "(II)V");
324    m_javaGlue->m_scrollBy = GetJMethod(env, clazz, "contentScrollBy", "(IIZ)V");
325    m_javaGlue->m_contentDraw = GetJMethod(env, clazz, "contentDraw", "()V");
326    m_javaGlue->m_requestListBox = GetJMethod(env, clazz, "requestListBox", "([Ljava/lang/String;[I[I)V");
327    m_javaGlue->m_openFileChooser = GetJMethod(env, clazz, "openFileChooser", "()Ljava/lang/String;");
328    m_javaGlue->m_requestSingleListBox = GetJMethod(env, clazz, "requestListBox", "([Ljava/lang/String;[II)V");
329    m_javaGlue->m_jsAlert = GetJMethod(env, clazz, "jsAlert", "(Ljava/lang/String;Ljava/lang/String;)V");
330    m_javaGlue->m_jsConfirm = GetJMethod(env, clazz, "jsConfirm", "(Ljava/lang/String;Ljava/lang/String;)Z");
331    m_javaGlue->m_jsPrompt = GetJMethod(env, clazz, "jsPrompt", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
332    m_javaGlue->m_jsUnload = GetJMethod(env, clazz, "jsUnload", "(Ljava/lang/String;Ljava/lang/String;)Z");
333    m_javaGlue->m_jsInterrupt = GetJMethod(env, clazz, "jsInterrupt", "()Z");
334    m_javaGlue->m_didFirstLayout = GetJMethod(env, clazz, "didFirstLayout", "(Z)V");
335    m_javaGlue->m_updateViewport = GetJMethod(env, clazz, "updateViewport", "()V");
336    m_javaGlue->m_sendNotifyProgressFinished = GetJMethod(env, clazz, "sendNotifyProgressFinished", "()V");
337    m_javaGlue->m_sendViewInvalidate = GetJMethod(env, clazz, "sendViewInvalidate", "(IIII)V");
338    m_javaGlue->m_sendImmediateRepaint = GetJMethod(env, clazz, "sendImmediateRepaint", "()V");
339    m_javaGlue->m_setRootLayer = GetJMethod(env, clazz, "setRootLayer", "(I)V");
340    m_javaGlue->m_updateTextfield = GetJMethod(env, clazz, "updateTextfield", "(IZLjava/lang/String;I)V");
341    m_javaGlue->m_updateTextSelection = GetJMethod(env, clazz, "updateTextSelection", "(IIII)V");
342    m_javaGlue->m_clearTextEntry = GetJMethod(env, clazz, "clearTextEntry", "()V");
343    m_javaGlue->m_restoreScale = GetJMethod(env, clazz, "restoreScale", "(I)V");
344    m_javaGlue->m_restoreScreenWidthScale = GetJMethod(env, clazz, "restoreScreenWidthScale", "(I)V");
345    m_javaGlue->m_needTouchEvents = GetJMethod(env, clazz, "needTouchEvents", "(Z)V");
346    m_javaGlue->m_requestKeyboard = GetJMethod(env, clazz, "requestKeyboard", "(Z)V");
347    m_javaGlue->m_requestKeyboardWithSelection = GetJMethod(env, clazz, "requestKeyboardWithSelection", "(IIII)V");
348    m_javaGlue->m_exceededDatabaseQuota = GetJMethod(env, clazz, "exceededDatabaseQuota", "(Ljava/lang/String;Ljava/lang/String;JJ)V");
349    m_javaGlue->m_reachedMaxAppCacheSize = GetJMethod(env, clazz, "reachedMaxAppCacheSize", "(J)V");
350    m_javaGlue->m_populateVisitedLinks = GetJMethod(env, clazz, "populateVisitedLinks", "()V");
351    m_javaGlue->m_geolocationPermissionsShowPrompt = GetJMethod(env, clazz, "geolocationPermissionsShowPrompt", "(Ljava/lang/String;)V");
352    m_javaGlue->m_geolocationPermissionsHidePrompt = GetJMethod(env, clazz, "geolocationPermissionsHidePrompt", "()V");
353    m_javaGlue->m_addMessageToConsole = GetJMethod(env, clazz, "addMessageToConsole", "(Ljava/lang/String;ILjava/lang/String;I)V");
354    m_javaGlue->m_getPluginClass = GetJMethod(env, clazz, "getPluginClass", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Class;");
355    m_javaGlue->m_showFullScreenPlugin = GetJMethod(env, clazz, "showFullScreenPlugin", "(Landroid/webkit/ViewManager$ChildView;I)V");
356    m_javaGlue->m_hideFullScreenPlugin = GetJMethod(env, clazz, "hideFullScreenPlugin", "()V");
357    m_javaGlue->m_addSurface = GetJMethod(env, clazz, "addSurface", "(Landroid/view/View;IIII)Landroid/webkit/ViewManager$ChildView;");
358    m_javaGlue->m_updateSurface = GetJMethod(env, clazz, "updateSurface", "(Landroid/webkit/ViewManager$ChildView;IIII)V");
359    m_javaGlue->m_destroySurface = GetJMethod(env, clazz, "destroySurface", "(Landroid/webkit/ViewManager$ChildView;)V");
360    m_javaGlue->m_getContext = GetJMethod(env, clazz, "getContext", "()Landroid/content/Context;");
361    m_javaGlue->m_sendFindAgain = GetJMethod(env, clazz, "sendFindAgain", "()V");
362    m_javaGlue->m_showRect = GetJMethod(env, clazz, "showRect", "(IIIIIIFFFF)V");
363    m_javaGlue->m_centerFitRect = GetJMethod(env, clazz, "centerFitRect", "(IIII)V");
364    m_javaGlue->m_setScrollbarModes = GetJMethod(env, clazz, "setScrollbarModes", "(II)V");
365
366    env->SetIntField(javaWebViewCore, gWebViewCoreFields.m_nativeClass, (jint)this);
367
368    m_scrollOffsetX = m_scrollOffsetY = 0;
369
370    PageGroup::setShouldTrackVisitedLinks(true);
371
372    reset(true);
373
374    WebViewCore::addInstance(this);
375}
376
377WebViewCore::~WebViewCore()
378{
379    WebViewCore::removeInstance(this);
380
381    // Release the focused view
382    Release(m_popupReply);
383
384    if (m_javaGlue->m_obj) {
385        JNIEnv* env = JSC::Bindings::getJNIEnv();
386        env->DeleteWeakGlobalRef(m_javaGlue->m_obj);
387        m_javaGlue->m_obj = 0;
388    }
389    delete m_javaGlue;
390    delete m_frameCacheKit;
391    delete m_navPictureKit;
392}
393
394WebViewCore* WebViewCore::getWebViewCore(const WebCore::FrameView* view)
395{
396    return getWebViewCore(static_cast<const WebCore::ScrollView*>(view));
397}
398
399WebViewCore* WebViewCore::getWebViewCore(const WebCore::ScrollView* view)
400{
401    if (!view)
402        return 0;
403
404    WebFrameView* webFrameView = static_cast<WebFrameView*>(view->platformWidget());
405    if (!webFrameView)
406        return 0;
407    return webFrameView->webViewCore();
408}
409
410void WebViewCore::reset(bool fromConstructor)
411{
412    DBG_SET_LOG("");
413    if (fromConstructor) {
414        m_frameCacheKit = 0;
415        m_navPictureKit = 0;
416    } else {
417        gFrameCacheMutex.lock();
418        delete m_frameCacheKit;
419        delete m_navPictureKit;
420        m_frameCacheKit = 0;
421        m_navPictureKit = 0;
422        gFrameCacheMutex.unlock();
423    }
424
425    m_lastFocused = 0;
426    m_lastFocusedBounds = WebCore::IntRect(0,0,0,0);
427    m_focusBoundsChanged = false;
428    m_lastFocusedSelStart = 0;
429    m_lastFocusedSelEnd = 0;
430    clearContent();
431    m_updatedFrameCache = true;
432    m_frameCacheOutOfDate = true;
433    m_skipContentDraw = false;
434    m_findIsUp = false;
435    m_domtree_version = 0;
436    m_check_domtree_version = true;
437    m_progressDone = false;
438    m_hasCursorBounds = false;
439
440    m_scrollOffsetX = 0;
441    m_scrollOffsetY = 0;
442    m_screenWidth = 0;
443    m_screenHeight = 0;
444    m_groupForVisitedLinks = NULL;
445}
446
447static bool layoutIfNeededRecursive(WebCore::Frame* f)
448{
449    if (!f)
450        return true;
451
452    WebCore::FrameView* v = f->view();
453    if (!v)
454        return true;
455
456    if (v->needsLayout())
457        v->layout(f->tree()->parent());
458
459    WebCore::Frame* child = f->tree()->firstChild();
460    bool success = true;
461    while (child) {
462        success &= layoutIfNeededRecursive(child);
463        child = child->tree()->nextSibling();
464    }
465
466    return success && !v->needsLayout();
467}
468
469CacheBuilder& WebViewCore::cacheBuilder()
470{
471    return FrameLoaderClientAndroid::get(m_mainFrame)->getCacheBuilder();
472}
473
474WebCore::Node* WebViewCore::currentFocus()
475{
476    return cacheBuilder().currentFocus();
477}
478
479void WebViewCore::recordPicture(SkPicture* picture)
480{
481    // if there is no document yet, just return
482    if (!m_mainFrame->document()) {
483        DBG_NAV_LOG("no document");
484        return;
485    }
486    // Call layout to ensure that the contentWidth and contentHeight are correct
487    if (!layoutIfNeededRecursive(m_mainFrame)) {
488        DBG_NAV_LOG("layout failed");
489        return;
490    }
491    // draw into the picture's recording canvas
492    WebCore::FrameView* view = m_mainFrame->view();
493    DBG_NAV_LOGD("view=(w=%d,h=%d)", view->contentsWidth(),
494        view->contentsHeight());
495    SkAutoPictureRecord arp(picture, view->contentsWidth(),
496                            view->contentsHeight(), PICT_RECORD_FLAGS);
497    SkAutoMemoryUsageProbe mup(__FUNCTION__);
498
499    // Copy m_buttons so we can pass it to our graphics context.
500    gButtonMutex.lock();
501    WTF::Vector<Container> buttons(m_buttons);
502    gButtonMutex.unlock();
503
504    WebCore::PlatformGraphicsContext pgc(arp.getRecordingCanvas(), &buttons);
505    WebCore::GraphicsContext gc(&pgc);
506    view->platformWidget()->draw(&gc, WebCore::IntRect(0, 0, INT_MAX, INT_MAX));
507
508    gButtonMutex.lock();
509    updateButtonList(&buttons);
510    gButtonMutex.unlock();
511}
512
513void WebViewCore::recordPictureSet(PictureSet* content)
514{
515    // if there is no document yet, just return
516    if (!m_mainFrame->document()) {
517        DBG_SET_LOG("!m_mainFrame->document()");
518        return;
519    }
520    if (m_addInval.isEmpty()) {
521        DBG_SET_LOG("m_addInval.isEmpty()");
522        return;
523    }
524    // Call layout to ensure that the contentWidth and contentHeight are correct
525    // it's fine for layout to gather invalidates, but defeat sending a message
526    // back to java to call webkitDraw, since we're already in the middle of
527    // doing that
528    m_skipContentDraw = true;
529    bool success = layoutIfNeededRecursive(m_mainFrame);
530    m_skipContentDraw = false;
531
532    // We may be mid-layout and thus cannot draw.
533    if (!success)
534        return;
535
536    {   // collect WebViewCoreRecordTimeCounter after layoutIfNeededRecursive
537#ifdef ANDROID_INSTRUMENT
538    TimeCounterAuto counter(TimeCounter::WebViewCoreRecordTimeCounter);
539#endif
540
541    // if the webkit page dimensions changed, discard the pictureset and redraw.
542    WebCore::FrameView* view = m_mainFrame->view();
543    int width = view->contentsWidth();
544    int height = view->contentsHeight();
545
546    // Use the contents width and height as a starting point.
547    SkIRect contentRect;
548    contentRect.set(0, 0, width, height);
549    SkIRect total(contentRect);
550
551    // Traverse all the frames and add their sizes if they are in the visible
552    // rectangle.
553    for (WebCore::Frame* frame = m_mainFrame->tree()->traverseNext(); frame;
554            frame = frame->tree()->traverseNext()) {
555        // If the frame doesn't have an owner then it is the top frame and the
556        // view size is the frame size.
557        WebCore::RenderPart* owner = frame->ownerRenderer();
558        if (owner && owner->style()->visibility() == VISIBLE) {
559            int x = owner->x();
560            int y = owner->y();
561
562            // Traverse the tree up to the parent to find the absolute position
563            // of this frame.
564            WebCore::Frame* parent = frame->tree()->parent();
565            while (parent) {
566                WebCore::RenderPart* parentOwner = parent->ownerRenderer();
567                if (parentOwner) {
568                    x += parentOwner->x();
569                    y += parentOwner->y();
570                }
571                parent = parent->tree()->parent();
572            }
573            // Use the owner dimensions so that padding and border are
574            // included.
575            int right = x + owner->width();
576            int bottom = y + owner->height();
577            SkIRect frameRect = {x, y, right, bottom};
578            // Ignore a width or height that is smaller than 1. Some iframes
579            // have small dimensions in order to be hidden. The iframe
580            // expansion code does not expand in that case so we should ignore
581            // them here.
582            if (frameRect.width() > 1 && frameRect.height() > 1
583                    && SkIRect::Intersects(total, frameRect))
584                total.join(x, y, right, bottom);
585        }
586    }
587
588    // If the new total is larger than the content, resize the view to include
589    // all the content.
590    if (!contentRect.contains(total)) {
591        // Resize the view to change the overflow clip.
592        view->resize(total.fRight, total.fBottom);
593
594        // We have to force a layout in order for the clip to change.
595        m_mainFrame->contentRenderer()->setNeedsLayoutAndPrefWidthsRecalc();
596        view->forceLayout();
597
598        // Relayout similar to above
599        m_skipContentDraw = true;
600        bool success = layoutIfNeededRecursive(m_mainFrame);
601        m_skipContentDraw = false;
602        if (!success)
603            return;
604
605        // Set the computed content width
606        width = view->contentsWidth();
607        height = view->contentsHeight();
608    }
609
610    content->checkDimensions(width, height, &m_addInval);
611
612    // The inval region may replace existing pictures. The existing pictures
613    // may have already been split into pieces. If reuseSubdivided() returns
614    // true, the split pieces are the last entries in the picture already. They
615    // are marked as invalid, and are rebuilt by rebuildPictureSet().
616
617    // If the new region doesn't match a set of split pieces, add it to the end.
618    if (!content->reuseSubdivided(m_addInval)) {
619        const SkIRect& inval = m_addInval.getBounds();
620        SkPicture* picture = rebuildPicture(inval);
621        DBG_SET_LOGD("{%d,%d,w=%d,h=%d}", inval.fLeft,
622            inval.fTop, inval.width(), inval.height());
623        content->add(m_addInval, picture, 0, false);
624        picture->safeUnref();
625    }
626    // Remove any pictures already in the set that are obscured by the new one,
627    // and check to see if any already split pieces need to be redrawn.
628    if (content->build())
629        rebuildPictureSet(content);
630    } // WebViewCoreRecordTimeCounter
631    WebCore::Node* oldFocusNode = currentFocus();
632    m_frameCacheOutOfDate = true;
633    WebCore::IntRect oldBounds;
634    int oldSelStart = 0;
635    int oldSelEnd = 0;
636    if (oldFocusNode) {
637        oldBounds = oldFocusNode->getRect();
638        RenderObject* renderer = oldFocusNode->renderer();
639        if (renderer && (renderer->isTextArea() || renderer->isTextField())) {
640            WebCore::RenderTextControl* rtc =
641                static_cast<WebCore::RenderTextControl*>(renderer);
642            oldSelStart = rtc->selectionStart();
643            oldSelEnd = rtc->selectionEnd();
644        }
645    } else
646        oldBounds = WebCore::IntRect(0,0,0,0);
647    unsigned latestVersion = 0;
648    if (m_check_domtree_version) {
649        // as domTreeVersion only increment, we can just check the sum to see
650        // whether we need to update the frame cache
651        for (Frame* frame = m_mainFrame; frame; frame = frame->tree()->traverseNext()) {
652            latestVersion += frame->document()->domTreeVersion();
653        }
654    }
655    DBG_NAV_LOGD("m_lastFocused=%p oldFocusNode=%p"
656        " m_lastFocusedBounds={%d,%d,%d,%d} oldBounds={%d,%d,%d,%d}"
657        " m_lastFocusedSelection={%d,%d} oldSelection={%d,%d}"
658        " m_check_domtree_version=%s latestVersion=%d m_domtree_version=%d",
659        m_lastFocused, oldFocusNode,
660        m_lastFocusedBounds.x(), m_lastFocusedBounds.y(),
661        m_lastFocusedBounds.width(), m_lastFocusedBounds.height(),
662        oldBounds.x(), oldBounds.y(), oldBounds.width(), oldBounds.height(),
663        m_lastFocusedSelStart, m_lastFocusedSelEnd, oldSelStart, oldSelEnd,
664        m_check_domtree_version ? "true" : "false",
665        latestVersion, m_domtree_version);
666    if (m_lastFocused == oldFocusNode && m_lastFocusedBounds == oldBounds
667            && m_lastFocusedSelStart == oldSelStart
668            && m_lastFocusedSelEnd == oldSelEnd
669            && !m_findIsUp
670            && (!m_check_domtree_version || latestVersion == m_domtree_version))
671    {
672        return;
673    }
674    m_focusBoundsChanged |= m_lastFocused == oldFocusNode
675        && m_lastFocusedBounds != oldBounds;
676    m_lastFocused = oldFocusNode;
677    m_lastFocusedBounds = oldBounds;
678    m_lastFocusedSelStart = oldSelStart;
679    m_lastFocusedSelEnd = oldSelEnd;
680    m_domtree_version = latestVersion;
681    DBG_NAV_LOG("call updateFrameCache");
682    updateFrameCache();
683    if (m_findIsUp) {
684        LOG_ASSERT(m_javaGlue->m_obj,
685                "A Java widget was not associated with this view bridge!");
686        JNIEnv* env = JSC::Bindings::getJNIEnv();
687        env->CallVoidMethod(m_javaGlue->object(env).get(),
688                m_javaGlue->m_sendFindAgain);
689        checkException(env);
690    }
691}
692
693void WebViewCore::updateButtonList(WTF::Vector<Container>* buttons)
694{
695    // All the entries in buttons are either updates of previous entries in
696    // m_buttons or they need to be added to it.
697    Container* end = buttons->end();
698    for (Container* updatedContainer = buttons->begin();
699            updatedContainer != end; updatedContainer++) {
700        bool updated = false;
701        // Search for a previous entry that references the same node as our new
702        // data
703        Container* lastPossibleMatch = m_buttons.end();
704        for (Container* possibleMatch = m_buttons.begin();
705                possibleMatch != lastPossibleMatch; possibleMatch++) {
706            if (updatedContainer->matches(possibleMatch->node())) {
707                // Update our record, and skip to the next one.
708                possibleMatch->setRect(updatedContainer->rect());
709                updated = true;
710                break;
711            }
712        }
713        if (!updated) {
714            // This is a brand new button, so append it to m_buttons
715            m_buttons.append(*updatedContainer);
716        }
717    }
718    size_t i = 0;
719    // count will decrease each time one is removed, so check count each time.
720    while (i < m_buttons.size()) {
721        if (m_buttons[i].canBeRemoved()) {
722            m_buttons[i] = m_buttons.last();
723            m_buttons.removeLast();
724        } else {
725            i++;
726        }
727    }
728}
729
730// note: updateCursorBounds is called directly by the WebView thread
731// This needs to be called each time we call CachedRoot::setCursor() with
732// non-null CachedNode/CachedFrame, since otherwise the WebViewCore's data
733// about the cursor is incorrect.  When we call setCursor(0,0), we need
734// to set hasCursorBounds to false.
735void WebViewCore::updateCursorBounds(const CachedRoot* root,
736        const CachedFrame* cachedFrame, const CachedNode* cachedNode)
737{
738    LOG_ASSERT(root, "updateCursorBounds: root cannot be null");
739    LOG_ASSERT(cachedNode, "updateCursorBounds: cachedNode cannot be null");
740    LOG_ASSERT(cachedFrame, "updateCursorBounds: cachedFrame cannot be null");
741    gCursorBoundsMutex.lock();
742    m_hasCursorBounds = !cachedNode->isHidden();
743    // If m_hasCursorBounds is false, we never look at the other
744    // values, so do not bother setting them.
745    if (m_hasCursorBounds) {
746        WebCore::IntRect bounds = cachedNode->bounds(cachedFrame);
747        if (m_cursorBounds != bounds)
748            DBG_NAV_LOGD("new cursor bounds=(%d,%d,w=%d,h=%d)",
749                bounds.x(), bounds.y(), bounds.width(), bounds.height());
750        m_cursorBounds = bounds;
751        m_cursorHitBounds = cachedNode->hitBounds(cachedFrame);
752        m_cursorFrame = cachedFrame->framePointer();
753        root->getSimulatedMousePosition(&m_cursorLocation);
754        m_cursorNode = cachedNode->nodePointer();
755    }
756    gCursorBoundsMutex.unlock();
757}
758
759void WebViewCore::clearContent()
760{
761    DBG_SET_LOG("");
762    m_contentMutex.lock();
763    m_content.clear();
764    m_contentMutex.unlock();
765    m_addInval.setEmpty();
766    m_rebuildInval.setEmpty();
767}
768
769void WebViewCore::copyContentToPicture(SkPicture* picture)
770{
771    DBG_SET_LOG("start");
772    m_contentMutex.lock();
773    PictureSet copyContent = PictureSet(m_content);
774    m_contentMutex.unlock();
775
776    int w = copyContent.width();
777    int h = copyContent.height();
778    copyContent.draw(picture->beginRecording(w, h, PICT_RECORD_FLAGS));
779    picture->endRecording();
780    DBG_SET_LOG("end");
781}
782
783bool WebViewCore::drawContent(SkCanvas* canvas, SkColor color)
784{
785#ifdef ANDROID_INSTRUMENT
786    TimeCounterAuto counter(TimeCounter::WebViewUIDrawTimeCounter);
787#endif
788    DBG_SET_LOG("start");
789    m_contentMutex.lock();
790    PictureSet copyContent = PictureSet(m_content);
791    m_contentMutex.unlock();
792    int sc = canvas->save(SkCanvas::kClip_SaveFlag);
793    SkRect clip;
794    clip.set(0, 0, copyContent.width(), copyContent.height());
795    canvas->clipRect(clip, SkRegion::kDifference_Op);
796    canvas->drawColor(color);
797    canvas->restoreToCount(sc);
798    bool tookTooLong = copyContent.draw(canvas);
799    m_contentMutex.lock();
800    m_content.setDrawTimes(copyContent);
801    m_contentMutex.unlock();
802    DBG_SET_LOG("end");
803    return tookTooLong;
804}
805
806bool WebViewCore::focusBoundsChanged()
807{
808    bool result = m_focusBoundsChanged;
809    m_focusBoundsChanged = false;
810    return result;
811}
812
813bool WebViewCore::pictureReady()
814{
815    bool done;
816    m_contentMutex.lock();
817    PictureSet copyContent = PictureSet(m_content);
818    done = m_progressDone;
819    m_contentMutex.unlock();
820    DBG_NAV_LOGD("done=%s empty=%s", done ? "true" : "false",
821        copyContent.isEmpty() ? "true" : "false");
822    return done || !copyContent.isEmpty();
823}
824
825SkPicture* WebViewCore::rebuildPicture(const SkIRect& inval)
826{
827    WebCore::FrameView* view = m_mainFrame->view();
828    int width = view->contentsWidth();
829    int height = view->contentsHeight();
830    SkPicture* picture = new SkPicture();
831    SkAutoPictureRecord arp(picture, width, height, PICT_RECORD_FLAGS);
832    SkAutoMemoryUsageProbe mup(__FUNCTION__);
833    SkCanvas* recordingCanvas = arp.getRecordingCanvas();
834
835    gButtonMutex.lock();
836    WTF::Vector<Container> buttons(m_buttons);
837    gButtonMutex.unlock();
838
839    WebCore::PlatformGraphicsContext pgc(recordingCanvas, &buttons);
840    WebCore::GraphicsContext gc(&pgc);
841    recordingCanvas->translate(-inval.fLeft, -inval.fTop);
842    recordingCanvas->save();
843    view->platformWidget()->draw(&gc, WebCore::IntRect(inval.fLeft,
844        inval.fTop, inval.width(), inval.height()));
845    m_rebuildInval.op(inval, SkRegion::kUnion_Op);
846    DBG_SET_LOGD("m_rebuildInval={%d,%d,r=%d,b=%d}",
847        m_rebuildInval.getBounds().fLeft, m_rebuildInval.getBounds().fTop,
848        m_rebuildInval.getBounds().fRight, m_rebuildInval.getBounds().fBottom);
849
850    gButtonMutex.lock();
851    updateButtonList(&buttons);
852    gButtonMutex.unlock();
853
854    return picture;
855}
856
857void WebViewCore::rebuildPictureSet(PictureSet* pictureSet)
858{
859    WebCore::FrameView* view = m_mainFrame->view();
860    size_t size = pictureSet->size();
861    for (size_t index = 0; index < size; index++) {
862        if (pictureSet->upToDate(index))
863            continue;
864        const SkIRect& inval = pictureSet->bounds(index);
865        DBG_SET_LOGD("pictSet=%p [%d] {%d,%d,w=%d,h=%d}", pictureSet, index,
866            inval.fLeft, inval.fTop, inval.width(), inval.height());
867        pictureSet->setPicture(index, rebuildPicture(inval));
868    }
869    pictureSet->validate(__FUNCTION__);
870}
871
872bool WebViewCore::recordContent(SkRegion* region, SkIPoint* point)
873{
874    DBG_SET_LOG("start");
875    float progress = (float) m_mainFrame->page()->progress()->estimatedProgress();
876    m_contentMutex.lock();
877    PictureSet contentCopy(m_content);
878    m_progressDone = progress <= 0.0f || progress >= 1.0f;
879    m_contentMutex.unlock();
880    recordPictureSet(&contentCopy);
881    if (!m_progressDone && contentCopy.isEmpty()) {
882        DBG_SET_LOGD("empty (progress=%g)", progress);
883        return false;
884    }
885    region->set(m_addInval);
886    m_addInval.setEmpty();
887    region->op(m_rebuildInval, SkRegion::kUnion_Op);
888    m_rebuildInval.setEmpty();
889    m_contentMutex.lock();
890    contentCopy.setDrawTimes(m_content);
891    m_content.set(contentCopy);
892    point->fX = m_content.width();
893    point->fY = m_content.height();
894    m_contentMutex.unlock();
895    DBG_SET_LOGD("region={%d,%d,r=%d,b=%d}", region->getBounds().fLeft,
896        region->getBounds().fTop, region->getBounds().fRight,
897        region->getBounds().fBottom);
898    DBG_SET_LOG("end");
899    return true;
900}
901
902void WebViewCore::splitContent()
903{
904    bool layoutSuceeded = layoutIfNeededRecursive(m_mainFrame);
905    LOG_ASSERT(layoutSuceeded, "Can never be called recursively");
906    PictureSet tempPictureSet;
907    m_contentMutex.lock();
908    m_content.split(&tempPictureSet);
909    m_contentMutex.unlock();
910    rebuildPictureSet(&tempPictureSet);
911    m_contentMutex.lock();
912    m_content.set(tempPictureSet);
913    m_contentMutex.unlock();
914}
915
916void WebViewCore::scrollTo(int x, int y, bool animate)
917{
918    LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
919
920//    LOGD("WebViewCore::scrollTo(%d %d)\n", x, y);
921
922    JNIEnv* env = JSC::Bindings::getJNIEnv();
923    env->CallVoidMethod(m_javaGlue->object(env).get(),
924            animate ? m_javaGlue->m_spawnScrollTo : m_javaGlue->m_scrollTo,
925            x, y);
926    checkException(env);
927}
928
929void WebViewCore::sendNotifyProgressFinished()
930{
931    LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
932    JNIEnv* env = JSC::Bindings::getJNIEnv();
933    env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_sendNotifyProgressFinished);
934    checkException(env);
935}
936
937void WebViewCore::viewInvalidate(const WebCore::IntRect& rect)
938{
939    LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
940    JNIEnv* env = JSC::Bindings::getJNIEnv();
941    env->CallVoidMethod(m_javaGlue->object(env).get(),
942                        m_javaGlue->m_sendViewInvalidate,
943                        rect.x(), rect.y(), rect.right(), rect.bottom());
944    checkException(env);
945}
946
947void WebViewCore::scrollBy(int dx, int dy, bool animate)
948{
949    if (!(dx | dy))
950        return;
951    JNIEnv* env = JSC::Bindings::getJNIEnv();
952    env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_scrollBy,
953        dx, dy, animate);
954    checkException(env);
955}
956
957#if USE(ACCELERATED_COMPOSITING)
958
959void WebViewCore::immediateRepaint()
960{
961    LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
962    JNIEnv* env = JSC::Bindings::getJNIEnv();
963    env->CallVoidMethod(m_javaGlue->object(env).get(),
964                        m_javaGlue->m_sendImmediateRepaint);
965    checkException(env);
966}
967
968void WebViewCore::setUIRootLayer(const LayerAndroid* layer)
969{
970    JNIEnv* env = JSC::Bindings::getJNIEnv();
971    env->CallVoidMethod(m_javaGlue->object(env).get(),
972                        m_javaGlue->m_setRootLayer,
973                        reinterpret_cast<jint>(layer));
974    checkException(env);
975}
976
977#endif // USE(ACCELERATED_COMPOSITING)
978
979void WebViewCore::contentDraw()
980{
981    JNIEnv* env = JSC::Bindings::getJNIEnv();
982    env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_contentDraw);
983    checkException(env);
984}
985
986void WebViewCore::contentInvalidate(const WebCore::IntRect &r)
987{
988    DBG_SET_LOGD("rect={%d,%d,w=%d,h=%d}", r.x(), r.y(), r.width(), r.height());
989    SkIRect rect(r);
990    if (!rect.intersect(0, 0, INT_MAX, INT_MAX))
991        return;
992    m_addInval.op(rect, SkRegion::kUnion_Op);
993    DBG_SET_LOGD("m_addInval={%d,%d,r=%d,b=%d}",
994        m_addInval.getBounds().fLeft, m_addInval.getBounds().fTop,
995        m_addInval.getBounds().fRight, m_addInval.getBounds().fBottom);
996    if (!m_skipContentDraw)
997        contentDraw();
998}
999
1000void WebViewCore::offInvalidate(const WebCore::IntRect &r)
1001{
1002    // FIXME: these invalidates are offscreen, and can be throttled or
1003    // deferred until the area is visible. For now, treat them as
1004    // regular invals so that drawing happens (inefficiently) for now.
1005    contentInvalidate(r);
1006}
1007
1008static int pin_pos(int x, int width, int targetWidth)
1009{
1010    if (x + width > targetWidth)
1011        x = targetWidth - width;
1012    if (x < 0)
1013        x = 0;
1014    return x;
1015}
1016
1017void WebViewCore::didFirstLayout()
1018{
1019    DEBUG_NAV_UI_LOGD("%s", __FUNCTION__);
1020    LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
1021
1022    WebCore::FrameLoader* loader = m_mainFrame->loader();
1023    const WebCore::KURL& url = loader->url();
1024    if (url.isEmpty())
1025        return;
1026    LOGV("::WebCore:: didFirstLayout %s", url.string().ascii().data());
1027
1028    WebCore::FrameLoadType loadType = loader->loadType();
1029
1030    JNIEnv* env = JSC::Bindings::getJNIEnv();
1031    env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_didFirstLayout,
1032            loadType == WebCore::FrameLoadTypeStandard
1033            // When redirect with locked history, we would like to reset the
1034            // scale factor. This is important for www.yahoo.com as it is
1035            // redirected to www.yahoo.com/?rs=1 on load.
1036            || loadType == WebCore::FrameLoadTypeRedirectWithLockedBackForwardList);
1037    checkException(env);
1038
1039    DBG_NAV_LOG("call updateFrameCache");
1040    m_check_domtree_version = false;
1041    updateFrameCache();
1042    m_history.setDidFirstLayout(true);
1043}
1044
1045void WebViewCore::updateViewport()
1046{
1047    DEBUG_NAV_UI_LOGD("%s", __FUNCTION__);
1048    LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
1049
1050    JNIEnv* env = JSC::Bindings::getJNIEnv();
1051    env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_updateViewport);
1052    checkException(env);
1053}
1054
1055void WebViewCore::restoreScale(int scale)
1056{
1057    DEBUG_NAV_UI_LOGD("%s", __FUNCTION__);
1058    LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
1059
1060    JNIEnv* env = JSC::Bindings::getJNIEnv();
1061    env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_restoreScale, scale);
1062    checkException(env);
1063}
1064
1065void WebViewCore::restoreScreenWidthScale(int scale)
1066{
1067    DEBUG_NAV_UI_LOGD("%s", __FUNCTION__);
1068    LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
1069
1070    JNIEnv* env = JSC::Bindings::getJNIEnv();
1071    env->CallVoidMethod(m_javaGlue->object(env).get(),
1072            m_javaGlue->m_restoreScreenWidthScale, scale);
1073    checkException(env);
1074}
1075
1076void WebViewCore::needTouchEvents(bool need)
1077{
1078    DEBUG_NAV_UI_LOGD("%s", __FUNCTION__);
1079    LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
1080
1081#if ENABLE(TOUCH_EVENTS)
1082    if (m_forwardingTouchEvents == need)
1083        return;
1084
1085    JNIEnv* env = JSC::Bindings::getJNIEnv();
1086    env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_needTouchEvents, need);
1087    checkException(env);
1088
1089    m_forwardingTouchEvents = need;
1090#endif
1091}
1092
1093void WebViewCore::requestKeyboardWithSelection(const WebCore::Node* node,
1094        int selStart, int selEnd)
1095{
1096    DEBUG_NAV_UI_LOGD("%s", __FUNCTION__);
1097    LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
1098
1099    JNIEnv* env = JSC::Bindings::getJNIEnv();
1100    env->CallVoidMethod(m_javaGlue->object(env).get(),
1101            m_javaGlue->m_requestKeyboardWithSelection,
1102            reinterpret_cast<int>(node), selStart, selEnd, m_textGeneration);
1103    checkException(env);
1104}
1105
1106void WebViewCore::requestKeyboard(bool showKeyboard)
1107{
1108    DEBUG_NAV_UI_LOGD("%s", __FUNCTION__);
1109    LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
1110
1111    JNIEnv* env = JSC::Bindings::getJNIEnv();
1112    env->CallVoidMethod(m_javaGlue->object(env).get(),
1113            m_javaGlue->m_requestKeyboard, showKeyboard);
1114    checkException(env);
1115}
1116
1117void WebViewCore::notifyProgressFinished()
1118{
1119    DBG_NAV_LOG("call updateFrameCache");
1120    m_check_domtree_version = true;
1121    updateFrameCache();
1122    sendNotifyProgressFinished();
1123}
1124
1125void WebViewCore::doMaxScroll(CacheBuilder::Direction dir)
1126{
1127    int dx = 0, dy = 0;
1128
1129    switch (dir) {
1130    case CacheBuilder::LEFT:
1131        dx = -m_maxXScroll;
1132        break;
1133    case CacheBuilder::UP:
1134        dy = -m_maxYScroll;
1135        break;
1136    case CacheBuilder::RIGHT:
1137        dx = m_maxXScroll;
1138        break;
1139    case CacheBuilder::DOWN:
1140        dy = m_maxYScroll;
1141        break;
1142    case CacheBuilder::UNINITIALIZED:
1143    default:
1144        LOG_ASSERT(0, "unexpected focus selector");
1145    }
1146    this->scrollBy(dx, dy, true);
1147}
1148
1149void WebViewCore::setScrollOffset(int moveGeneration, int dx, int dy)
1150{
1151    DBG_NAV_LOGD("{%d,%d} m_scrollOffset=(%d,%d)", dx, dy,
1152        m_scrollOffsetX, m_scrollOffsetY);
1153    if (m_scrollOffsetX != dx || m_scrollOffsetY != dy) {
1154        m_scrollOffsetX = dx;
1155        m_scrollOffsetY = dy;
1156        // The visible rect is located within our coordinate space so it
1157        // contains the actual scroll position. Setting the location makes hit
1158        // testing work correctly.
1159        m_mainFrame->view()->platformWidget()->setLocation(m_scrollOffsetX,
1160                m_scrollOffsetY);
1161        m_mainFrame->eventHandler()->sendScrollEvent();
1162
1163        // update the currently visible screen
1164        sendPluginVisibleScreen();
1165    }
1166    gCursorBoundsMutex.lock();
1167    bool hasCursorBounds = m_hasCursorBounds;
1168    Frame* frame = (Frame*) m_cursorFrame;
1169    IntPoint location = m_cursorLocation;
1170    gCursorBoundsMutex.unlock();
1171    if (!hasCursorBounds)
1172        return;
1173    moveMouseIfLatest(moveGeneration, frame, location.x(), location.y());
1174}
1175
1176void WebViewCore::setGlobalBounds(int x, int y, int h, int v)
1177{
1178    DBG_NAV_LOGD("{%d,%d}", x, y);
1179    m_mainFrame->view()->platformWidget()->setWindowBounds(x, y, h, v);
1180}
1181
1182void WebViewCore::setSizeScreenWidthAndScale(int width, int height,
1183    int screenWidth, float scale, int realScreenWidth, int screenHeight,
1184    int anchorX, int anchorY, bool ignoreHeight)
1185{
1186    WebCoreViewBridge* window = m_mainFrame->view()->platformWidget();
1187    int ow = window->width();
1188    int oh = window->height();
1189    window->setSize(width, height);
1190    int osw = m_screenWidth;
1191    int orsw = m_screenWidth * m_screenWidthScale / m_scale;
1192    int osh = m_screenHeight;
1193    DBG_NAV_LOGD("old:(w=%d,h=%d,sw=%d,scale=%g) new:(w=%d,h=%d,sw=%d,scale=%g)",
1194        ow, oh, osw, m_scale, width, height, screenWidth, scale);
1195    m_screenWidth = screenWidth;
1196    m_screenHeight = screenHeight;
1197    if (scale >= 0) { // negative means ignore
1198        m_scale = scale;
1199        if (screenWidth != realScreenWidth)
1200            m_screenWidthScale = realScreenWidth * scale / screenWidth;
1201        else
1202            m_screenWidthScale = m_scale;
1203    }
1204    m_maxXScroll = screenWidth >> 2;
1205    m_maxYScroll = (screenWidth * height / width) >> 2;
1206    if (ow != width || (!ignoreHeight && oh != height) || osw != screenWidth) {
1207        WebCore::RenderObject *r = m_mainFrame->contentRenderer();
1208        DBG_NAV_LOGD("renderer=%p view=(w=%d,h=%d)", r,
1209            realScreenWidth, screenHeight);
1210        if (r) {
1211            WebCore::IntPoint anchorPoint = WebCore::IntPoint(anchorX, anchorY);
1212            DBG_NAV_LOGD("anchorX=%d anchorY=%d", anchorX, anchorY);
1213            WebCore::Node* node = 0;
1214            WebCore::IntRect bounds;
1215            WebCore::IntPoint offset;
1216            // If the screen width changed, it is probably zoom change or
1217            // orientation change. Try to keep the anchor at the same place.
1218            if (osw && screenWidth && osw != screenWidth) {
1219                WebCore::HitTestResult hitTestResult =
1220                        m_mainFrame->eventHandler()-> hitTestResultAtPoint(
1221                                anchorPoint, false);
1222                node = hitTestResult.innerNode();
1223            }
1224            if (node) {
1225                bounds = node->getRect();
1226                DBG_NAV_LOGD("ob:(x=%d,y=%d,w=%d,h=%d)",
1227                    bounds.x(), bounds.y(), bounds.width(), bounds.height());
1228                // sites like nytimes.com insert a non-standard tag <nyt_text>
1229                // in the html. If it is the HitTestResult, it may have zero
1230                // width and height. In this case, use its parent node.
1231                if (bounds.width() == 0) {
1232                    node = node->parent();
1233                    if (node) {
1234                        bounds = node->getRect();
1235                        DBG_NAV_LOGD("found a zero width node and use its parent, whose ob:(x=%d,y=%d,w=%d,h=%d)",
1236                                bounds.x(), bounds.y(), bounds.width(), bounds.height());
1237                    }
1238                }
1239            }
1240            r->setNeedsLayoutAndPrefWidthsRecalc();
1241            m_mainFrame->view()->forceLayout();
1242            // scroll to restore current screen center
1243            if (node) {
1244                const WebCore::IntRect& newBounds = node->getRect();
1245                DBG_NAV_LOGD("nb:(x=%d,y=%d,w=%d,"
1246                    "h=%d)", newBounds.x(), newBounds.y(),
1247                    newBounds.width(), newBounds.height());
1248                if ((orsw && osh && bounds.width() && bounds.height())
1249                    && (bounds != newBounds)) {
1250                    WebCore::FrameView* view = m_mainFrame->view();
1251                    // force left align if width is not changed while height changed.
1252                    // the anchorPoint is probably at some white space in the node
1253                    // which is affected by text wrap around the screen width.
1254                    const bool leftAlign = (osw != m_screenWidth)
1255                        && (bounds.width() == newBounds.width())
1256                        && (bounds.height() != newBounds.height());
1257                    const float xPercentInDoc =
1258                        leftAlign ? 0.0 : (float) (anchorX - bounds.x()) / bounds.width();
1259                    const float xPercentInView =
1260                        leftAlign ? 0.0 : (float) (anchorX - m_scrollOffsetX) / orsw;
1261                    const float yPercentInDoc = (float) (anchorY - bounds.y()) / bounds.height();
1262                    const float yPercentInView = (float) (anchorY - m_scrollOffsetY) / osh;
1263                    showRect(newBounds.x(), newBounds.y(), newBounds.width(),
1264                             newBounds.height(), view->contentsWidth(),
1265                             view->contentsHeight(),
1266                             xPercentInDoc, xPercentInView,
1267                             yPercentInDoc, yPercentInView);
1268                }
1269            }
1270        }
1271    }
1272
1273    // update the currently visible screen as perceived by the plugin
1274    sendPluginVisibleScreen();
1275}
1276
1277void WebViewCore::dumpDomTree(bool useFile)
1278{
1279#ifdef ANDROID_DOM_LOGGING
1280    if (useFile)
1281        gDomTreeFile = fopen(DOM_TREE_LOG_FILE, "w");
1282    m_mainFrame->document()->showTreeForThis();
1283    if (gDomTreeFile) {
1284        fclose(gDomTreeFile);
1285        gDomTreeFile = 0;
1286    }
1287#endif
1288}
1289
1290void WebViewCore::dumpRenderTree(bool useFile)
1291{
1292#ifdef ANDROID_DOM_LOGGING
1293    WebCore::CString renderDump = WebCore::externalRepresentation(m_mainFrame).utf8();
1294    const char* data = renderDump.data();
1295    if (useFile) {
1296        gRenderTreeFile = fopen(RENDER_TREE_LOG_FILE, "w");
1297        DUMP_RENDER_LOGD("%s", data);
1298        fclose(gRenderTreeFile);
1299        gRenderTreeFile = 0;
1300    } else {
1301        // adb log can only output 1024 characters, so write out line by line.
1302        // exclude '\n' as adb log adds it for each output.
1303        int length = renderDump.length();
1304        for (int i = 0, last = 0; i < length; i++) {
1305            if (data[i] == '\n') {
1306                if (i != last)
1307                    DUMP_RENDER_LOGD("%.*s", (i - last), &(data[last]));
1308                last = i + 1;
1309            }
1310        }
1311    }
1312#endif
1313}
1314
1315void WebViewCore::dumpNavTree()
1316{
1317#if DUMP_NAV_CACHE
1318    cacheBuilder().mDebug.print();
1319#endif
1320}
1321
1322WebCore::HTMLAnchorElement* WebViewCore::retrieveAnchorElement(WebCore::Frame* frame, WebCore::Node* node)
1323{
1324    if (!CacheBuilder::validNode(m_mainFrame, frame, node))
1325        return 0;
1326    if (!node->hasTagName(WebCore::HTMLNames::aTag))
1327        return 0;
1328    return static_cast<WebCore::HTMLAnchorElement*>(node);
1329}
1330
1331WebCore::String WebViewCore::retrieveHref(WebCore::Frame* frame, WebCore::Node* node)
1332{
1333    WebCore::HTMLAnchorElement* anchor = retrieveAnchorElement(frame, node);
1334    return anchor ? anchor->href() : WebCore::String();
1335}
1336
1337WebCore::String WebViewCore::retrieveAnchorText(WebCore::Frame* frame, WebCore::Node* node)
1338{
1339    WebCore::HTMLAnchorElement* anchor = retrieveAnchorElement(frame, node);
1340    return anchor ? anchor->text() : WebCore::String();
1341}
1342
1343WebCore::String WebViewCore::requestLabel(WebCore::Frame* frame,
1344        WebCore::Node* node)
1345{
1346    if (node && CacheBuilder::validNode(m_mainFrame, frame, node)) {
1347        RefPtr<WebCore::NodeList> list = node->document()->getElementsByTagName("label");
1348        unsigned length = list->length();
1349        for (unsigned i = 0; i < length; i++) {
1350            WebCore::HTMLLabelElement* label = static_cast<WebCore::HTMLLabelElement*>(
1351                    list->item(i));
1352            if (label->correspondingControl() == node) {
1353                Node* node = label;
1354                String result;
1355                while ((node = node->traverseNextNode(label))) {
1356                    if (node->isTextNode()) {
1357                        Text* textNode = static_cast<Text*>(node);
1358                        result += textNode->dataImpl();
1359                    }
1360                }
1361                return result;
1362            }
1363        }
1364    }
1365    return WebCore::String();
1366}
1367
1368void WebViewCore::updateCacheOnNodeChange()
1369{
1370    gCursorBoundsMutex.lock();
1371    bool hasCursorBounds = m_hasCursorBounds;
1372    Frame* frame = (Frame*) m_cursorFrame;
1373    Node* node = (Node*) m_cursorNode;
1374    IntRect bounds = m_cursorHitBounds;
1375    gCursorBoundsMutex.unlock();
1376    if (!hasCursorBounds || !node)
1377        return;
1378    if (CacheBuilder::validNode(m_mainFrame, frame, node)) {
1379        RenderObject* renderer = node->renderer();
1380        if (renderer && renderer->style()->visibility() != HIDDEN) {
1381            IntRect absBox = renderer->absoluteBoundingBoxRect();
1382            int globalX, globalY;
1383            CacheBuilder::GetGlobalOffset(frame, &globalX, &globalY);
1384            absBox.move(globalX, globalY);
1385            if (absBox == bounds)
1386                return;
1387            DBG_NAV_LOGD("absBox=(%d,%d,%d,%d) bounds=(%d,%d,%d,%d)",
1388                absBox.x(), absBox.y(), absBox.width(), absBox.height(),
1389                bounds.x(), bounds.y(), bounds.width(), bounds.height());
1390        }
1391    }
1392    DBG_NAV_LOGD("updateFrameCache node=%p", node);
1393    updateFrameCache();
1394}
1395
1396void WebViewCore::updateFrameCache()
1397{
1398    if (!m_frameCacheOutOfDate) {
1399        DBG_NAV_LOG("!m_frameCacheOutOfDate");
1400        return;
1401    }
1402#ifdef ANDROID_INSTRUMENT
1403    TimeCounterAuto counter(TimeCounter::WebViewCoreBuildNavTimeCounter);
1404#endif
1405    m_frameCacheOutOfDate = false;
1406#if DEBUG_NAV_UI
1407    m_now = SkTime::GetMSecs();
1408#endif
1409    m_temp = new CachedRoot();
1410    m_temp->init(m_mainFrame, &m_history);
1411#if USE(ACCELERATED_COMPOSITING)
1412    GraphicsLayerAndroid* graphicsLayer = graphicsRootLayer();
1413    if (graphicsLayer)
1414        m_temp->setRootLayer(graphicsLayer->contentLayer());
1415#endif
1416    CacheBuilder& builder = cacheBuilder();
1417    WebCore::Settings* settings = m_mainFrame->page()->settings();
1418    builder.allowAllTextDetection();
1419#ifdef ANDROID_META_SUPPORT
1420    if (settings) {
1421        if (!settings->formatDetectionAddress())
1422            builder.disallowAddressDetection();
1423        if (!settings->formatDetectionEmail())
1424            builder.disallowEmailDetection();
1425        if (!settings->formatDetectionTelephone())
1426            builder.disallowPhoneDetection();
1427    }
1428#endif
1429    builder.buildCache(m_temp);
1430    m_tempPict = new SkPicture();
1431    recordPicture(m_tempPict);
1432    m_temp->setPicture(m_tempPict);
1433    m_temp->setTextGeneration(m_textGeneration);
1434    WebCoreViewBridge* window = m_mainFrame->view()->platformWidget();
1435    m_temp->setVisibleRect(WebCore::IntRect(m_scrollOffsetX,
1436        m_scrollOffsetY, window->width(), window->height()));
1437    gFrameCacheMutex.lock();
1438    delete m_frameCacheKit;
1439    delete m_navPictureKit;
1440    m_frameCacheKit = m_temp;
1441    m_navPictureKit = m_tempPict;
1442    m_updatedFrameCache = true;
1443#if DEBUG_NAV_UI
1444    const CachedNode* cachedFocusNode = m_frameCacheKit->currentFocus();
1445    DBG_NAV_LOGD("cachedFocusNode=%d (nodePointer=%p)",
1446        cachedFocusNode ? cachedFocusNode->index() : 0,
1447        cachedFocusNode ? cachedFocusNode->nodePointer() : 0);
1448#endif
1449    gFrameCacheMutex.unlock();
1450}
1451
1452void WebViewCore::updateFrameCacheIfLoading()
1453{
1454    if (!m_check_domtree_version)
1455        updateFrameCache();
1456}
1457
1458///////////////////////////////////////////////////////////////////////////////
1459
1460void WebViewCore::addPlugin(PluginWidgetAndroid* w)
1461{
1462//    SkDebugf("----------- addPlugin %p", w);
1463    /* The plugin must be appended to the end of the array. This ensures that if
1464       the plugin is added while iterating through the array (e.g. sendEvent(...))
1465       that the iteration process is not corrupted.
1466     */
1467    *m_plugins.append() = w;
1468}
1469
1470void WebViewCore::removePlugin(PluginWidgetAndroid* w)
1471{
1472//    SkDebugf("----------- removePlugin %p", w);
1473    int index = m_plugins.find(w);
1474    if (index < 0) {
1475        SkDebugf("--------------- pluginwindow not found! %p\n", w);
1476    } else {
1477        m_plugins.removeShuffle(index);
1478    }
1479}
1480
1481bool WebViewCore::isPlugin(PluginWidgetAndroid* w) const
1482{
1483    return m_plugins.find(w) >= 0;
1484}
1485
1486void WebViewCore::invalPlugin(PluginWidgetAndroid* w)
1487{
1488    const double PLUGIN_INVAL_DELAY = 1.0 / 60;
1489
1490    if (!m_pluginInvalTimer.isActive()) {
1491        m_pluginInvalTimer.startOneShot(PLUGIN_INVAL_DELAY);
1492    }
1493}
1494
1495void WebViewCore::drawPlugins()
1496{
1497    SkRegion inval; // accumualte what needs to be redrawn
1498    PluginWidgetAndroid** iter = m_plugins.begin();
1499    PluginWidgetAndroid** stop = m_plugins.end();
1500
1501    for (; iter < stop; ++iter) {
1502        PluginWidgetAndroid* w = *iter;
1503        SkIRect dirty;
1504        if (w->isDirty(&dirty)) {
1505            w->draw();
1506            inval.op(dirty, SkRegion::kUnion_Op);
1507        }
1508    }
1509
1510    if (!inval.isEmpty()) {
1511        // inval.getBounds() is our rectangle
1512        const SkIRect& bounds = inval.getBounds();
1513        WebCore::IntRect r(bounds.fLeft, bounds.fTop,
1514                           bounds.width(), bounds.height());
1515        this->viewInvalidate(r);
1516    }
1517}
1518
1519void WebViewCore::notifyPluginsOnFrameLoad(const Frame* frame) {
1520    // if frame is the parent then notify all plugins
1521    if (!frame->tree()->parent()) {
1522        // trigger an event notifying the plugins that the page has loaded
1523        ANPEvent event;
1524        SkANP::InitEvent(&event, kLifecycle_ANPEventType);
1525        event.data.lifecycle.action = kOnLoad_ANPLifecycleAction;
1526        sendPluginEvent(event);
1527    }
1528    // else if frame's parent has completed
1529    else if (!frame->tree()->parent()->loader()->isLoading()) {
1530        // send to all plugins who have this frame in their heirarchy
1531        PluginWidgetAndroid** iter = m_plugins.begin();
1532        PluginWidgetAndroid** stop = m_plugins.end();
1533        for (; iter < stop; ++iter) {
1534            Frame* currentFrame = (*iter)->pluginView()->parentFrame();
1535            while (currentFrame) {
1536                if (frame == currentFrame) {
1537                    ANPEvent event;
1538                    SkANP::InitEvent(&event, kLifecycle_ANPEventType);
1539                    event.data.lifecycle.action = kOnLoad_ANPLifecycleAction;
1540                    (*iter)->sendEvent(event);
1541                    break;
1542                }
1543                currentFrame = currentFrame->tree()->parent();
1544            }
1545        }
1546    }
1547}
1548
1549void WebViewCore::sendPluginVisibleScreen()
1550{
1551    /* We may want to cache the previous values and only send the notification
1552       to the plugin in the event that one of the values has changed.
1553     */
1554
1555    ANPRectI visibleRect;
1556    visibleRect.left = m_scrollOffsetX;
1557    visibleRect.top = m_scrollOffsetY;
1558    visibleRect.right = m_scrollOffsetX + m_screenWidth;
1559    visibleRect.bottom = m_scrollOffsetY + m_screenHeight;
1560
1561    PluginWidgetAndroid** iter = m_plugins.begin();
1562    PluginWidgetAndroid** stop = m_plugins.end();
1563    for (; iter < stop; ++iter) {
1564        (*iter)->setVisibleScreen(visibleRect, m_scale);
1565    }
1566}
1567
1568void WebViewCore::sendPluginEvent(const ANPEvent& evt)
1569{
1570    /* The list of plugins may be manipulated as we iterate through the list.
1571       This implementation allows for the addition of new plugins during an
1572       iteration, but may fail if a plugin is removed. Currently, there are not
1573       any use cases where a plugin is deleted while processing this loop, but
1574       if it does occur we will have to use an alternate data structure and/or
1575       iteration mechanism.
1576     */
1577    for (int x = 0; x < m_plugins.count(); x++) {
1578        m_plugins[x]->sendEvent(evt);
1579    }
1580}
1581
1582PluginWidgetAndroid* WebViewCore::getPluginWidget(NPP npp)
1583{
1584    PluginWidgetAndroid** iter = m_plugins.begin();
1585    PluginWidgetAndroid** stop = m_plugins.end();
1586    for (; iter < stop; ++iter) {
1587        if ((*iter)->pluginView()->instance() == npp) {
1588            return (*iter);
1589        }
1590    }
1591    return NULL;
1592}
1593
1594static PluginView* nodeIsPlugin(Node* node) {
1595    RenderObject* renderer = node->renderer();
1596    if (renderer && renderer->isWidget()) {
1597        Widget* widget = static_cast<RenderWidget*>(renderer)->widget();
1598        if (widget && widget->isPluginView())
1599            return static_cast<PluginView*>(widget);
1600    }
1601    return 0;
1602}
1603
1604Node* WebViewCore::cursorNodeIsPlugin() {
1605    gCursorBoundsMutex.lock();
1606    bool hasCursorBounds = m_hasCursorBounds;
1607    Frame* frame = (Frame*) m_cursorFrame;
1608    Node* node = (Node*) m_cursorNode;
1609    gCursorBoundsMutex.unlock();
1610    if (hasCursorBounds && CacheBuilder::validNode(m_mainFrame, frame, node)
1611            && nodeIsPlugin(node)) {
1612        return node;
1613    }
1614    return 0;
1615}
1616
1617///////////////////////////////////////////////////////////////////////////////
1618void WebViewCore::moveMouseIfLatest(int moveGeneration,
1619    WebCore::Frame* frame, int x, int y)
1620{
1621    DBG_NAV_LOGD("m_moveGeneration=%d moveGeneration=%d"
1622        " frame=%p x=%d y=%d",
1623        m_moveGeneration, moveGeneration, frame, x, y);
1624    if (m_moveGeneration > moveGeneration) {
1625        DBG_NAV_LOGD("m_moveGeneration=%d > moveGeneration=%d",
1626            m_moveGeneration, moveGeneration);
1627        return; // short-circuit if a newer move has already been generated
1628    }
1629    m_lastGeneration = moveGeneration;
1630    moveMouse(frame, x, y);
1631}
1632
1633void WebViewCore::moveFocus(WebCore::Frame* frame, WebCore::Node* node)
1634{
1635    DBG_NAV_LOGD("frame=%p node=%p", frame, node);
1636    if (!node || !CacheBuilder::validNode(m_mainFrame, frame, node)
1637            || !node->isElementNode())
1638        return;
1639    // Code borrowed from FocusController::advanceFocus
1640    WebCore::FocusController* focusController
1641            = m_mainFrame->page()->focusController();
1642    WebCore::Document* oldDoc
1643            = focusController->focusedOrMainFrame()->document();
1644    if (oldDoc->focusedNode() == node)
1645        return;
1646    if (node->document() != oldDoc)
1647        oldDoc->setFocusedNode(0);
1648    focusController->setFocusedFrame(frame);
1649    static_cast<WebCore::Element*>(node)->focus(false);
1650}
1651
1652// Update mouse position
1653void WebViewCore::moveMouse(WebCore::Frame* frame, int x, int y)
1654{
1655    DBG_NAV_LOGD("frame=%p x=%d y=%d scrollOffset=(%d,%d)", frame,
1656        x, y, m_scrollOffsetX, m_scrollOffsetY);
1657    if (!frame || CacheBuilder::validNode(m_mainFrame, frame, NULL) == false)
1658        frame = m_mainFrame;
1659    // mouse event expects the position in the window coordinate
1660    m_mousePos = WebCore::IntPoint(x - m_scrollOffsetX, y - m_scrollOffsetY);
1661    // validNode will still return true if the node is null, as long as we have
1662    // a valid frame.  Do not want to make a call on frame unless it is valid.
1663    WebCore::PlatformMouseEvent mouseEvent(m_mousePos, m_mousePos,
1664        WebCore::NoButton, WebCore::MouseEventMoved, 1, false, false, false,
1665        false, WTF::currentTime());
1666    frame->eventHandler()->handleMouseMoveEvent(mouseEvent);
1667    updateCacheOnNodeChange();
1668}
1669
1670void WebViewCore::setSelection(int start, int end)
1671{
1672    WebCore::Node* focus = currentFocus();
1673    if (!focus)
1674        return;
1675    WebCore::RenderObject* renderer = focus->renderer();
1676    if (!renderer || (!renderer->isTextField() && !renderer->isTextArea()))
1677        return;
1678    WebCore::RenderTextControl* rtc = static_cast<WebCore::RenderTextControl*>(renderer);
1679    if (start > end) {
1680        int temp = start;
1681        start = end;
1682        end = temp;
1683    }
1684    // Tell our EditorClient that this change was generated from the UI, so it
1685    // does not need to echo it to the UI.
1686    EditorClientAndroid* client = static_cast<EditorClientAndroid*>(
1687            m_mainFrame->editor()->client());
1688    client->setUiGeneratedSelectionChange(true);
1689    rtc->setSelectionRange(start, end);
1690    client->setUiGeneratedSelectionChange(false);
1691    WebCore::Frame* focusedFrame = focus->document()->frame();
1692    focusedFrame->revealSelection();
1693    setFocusControllerActive(focusedFrame, true);
1694}
1695
1696void WebViewCore::deleteSelection(int start, int end, int textGeneration)
1697{
1698    setSelection(start, end);
1699    if (start == end)
1700        return;
1701    WebCore::Node* focus = currentFocus();
1702    if (!focus)
1703        return;
1704    // Prevent our editor client from passing a message to change the
1705    // selection.
1706    EditorClientAndroid* client = static_cast<EditorClientAndroid*>(
1707            m_mainFrame->editor()->client());
1708    client->setUiGeneratedSelectionChange(true);
1709    PlatformKeyboardEvent down(AKEYCODE_DEL, 0, 0, true, false, false, false);
1710    PlatformKeyboardEvent up(AKEYCODE_DEL, 0, 0, false, false, false, false);
1711    key(down);
1712    key(up);
1713    client->setUiGeneratedSelectionChange(false);
1714    m_textGeneration = textGeneration;
1715}
1716
1717void WebViewCore::replaceTextfieldText(int oldStart,
1718        int oldEnd, const WebCore::String& replace, int start, int end,
1719        int textGeneration)
1720{
1721    WebCore::Node* focus = currentFocus();
1722    if (!focus)
1723        return;
1724    setSelection(oldStart, oldEnd);
1725    // Prevent our editor client from passing a message to change the
1726    // selection.
1727    EditorClientAndroid* client = static_cast<EditorClientAndroid*>(
1728            m_mainFrame->editor()->client());
1729    client->setUiGeneratedSelectionChange(true);
1730    WebCore::TypingCommand::insertText(focus->document(), replace,
1731        false);
1732    client->setUiGeneratedSelectionChange(false);
1733    setSelection(start, end);
1734    m_textGeneration = textGeneration;
1735}
1736
1737void WebViewCore::passToJs(int generation, const WebCore::String& current,
1738    const PlatformKeyboardEvent& event)
1739{
1740    WebCore::Node* focus = currentFocus();
1741    if (!focus) {
1742        DBG_NAV_LOG("!focus");
1743        clearTextEntry();
1744        return;
1745    }
1746    WebCore::RenderObject* renderer = focus->renderer();
1747    if (!renderer || (!renderer->isTextField() && !renderer->isTextArea())) {
1748        DBG_NAV_LOGD("renderer==%p || not text", renderer);
1749        clearTextEntry();
1750        return;
1751    }
1752    // Block text field updates during a key press.
1753    m_blockTextfieldUpdates = true;
1754    // Also prevent our editor client from passing a message to change the
1755    // selection.
1756    EditorClientAndroid* client = static_cast<EditorClientAndroid*>(
1757            m_mainFrame->editor()->client());
1758    client->setUiGeneratedSelectionChange(true);
1759    key(event);
1760    client->setUiGeneratedSelectionChange(false);
1761    m_blockTextfieldUpdates = false;
1762    m_textGeneration = generation;
1763    setFocusControllerActive(focus->document()->frame(), true);
1764    WebCore::RenderTextControl* renderText =
1765        static_cast<WebCore::RenderTextControl*>(renderer);
1766    WebCore::String test = renderText->text();
1767    if (test == current) {
1768        DBG_NAV_LOG("test == current");
1769        return;
1770    }
1771    // If the text changed during the key event, update the UI text field.
1772    updateTextfield(focus, false, test);
1773}
1774
1775void WebViewCore::scrollFocusedTextInput(float xPercent, int y)
1776{
1777    WebCore::Node* focus = currentFocus();
1778    if (!focus) {
1779        DBG_NAV_LOG("!focus");
1780        clearTextEntry();
1781        return;
1782    }
1783    WebCore::RenderObject* renderer = focus->renderer();
1784    if (!renderer || (!renderer->isTextField() && !renderer->isTextArea())) {
1785        DBG_NAV_LOGD("renderer==%p || not text", renderer);
1786        clearTextEntry();
1787        return;
1788    }
1789    WebCore::RenderTextControl* renderText =
1790        static_cast<WebCore::RenderTextControl*>(renderer);
1791    int x = (int) (xPercent * (renderText->scrollWidth() -
1792        renderText->clientWidth()));
1793    DBG_NAV_LOGD("x=%d y=%d xPercent=%g scrollW=%d clientW=%d", x, y,
1794        xPercent, renderText->scrollWidth(), renderText->clientWidth());
1795    renderText->setScrollLeft(x);
1796    renderText->setScrollTop(y);
1797}
1798
1799void WebViewCore::setFocusControllerActive(WebCore::Frame* frame, bool active)
1800{
1801    if (!frame) {
1802        WebCore::Node* focus = currentFocus();
1803        if (focus)
1804            frame = focus->document()->frame();
1805        else
1806            frame = m_mainFrame;
1807    }
1808    WebCore::FocusController* controller = frame->page()->focusController();
1809    controller->setActive(active);
1810    controller->setFocused(active);
1811}
1812
1813void WebViewCore::saveDocumentState(WebCore::Frame* frame)
1814{
1815    if (!CacheBuilder::validNode(m_mainFrame, frame, 0))
1816        frame = m_mainFrame;
1817    WebCore::HistoryItem *item = frame->loader()->history()->currentItem();
1818
1819    // item can be null when there is no offical URL for the current page. This happens
1820    // when the content is loaded using with WebCoreFrameBridge::LoadData() and there
1821    // is no failing URL (common case is when content is loaded using data: scheme)
1822    if (item) {
1823        item->setDocumentState(frame->document()->formElementsState());
1824    }
1825}
1826
1827// Convert a WebCore::String into an array of characters where the first
1828// character represents the length, for easy conversion to java.
1829static uint16_t* stringConverter(const WebCore::String& text)
1830{
1831    size_t length = text.length();
1832    uint16_t* itemName = new uint16_t[length+1];
1833    itemName[0] = (uint16_t)length;
1834    uint16_t* firstChar = &(itemName[1]);
1835    memcpy((void*)firstChar, text.characters(), sizeof(UChar)*length);
1836    return itemName;
1837}
1838
1839// Response to dropdown created for a listbox.
1840class ListBoxReply : public WebCoreReply {
1841public:
1842    ListBoxReply(WebCore::HTMLSelectElement* select, WebCore::Frame* frame, WebViewCore* view)
1843        : m_select(select)
1844        , m_frame(frame)
1845        , m_viewImpl(view)
1846    {}
1847
1848    // Response used if the listbox only allows single selection.
1849    // index is listIndex of the selected item, or -1 if nothing is selected.
1850    virtual void replyInt(int index)
1851    {
1852        if (-2 == index) {
1853            // Special value for cancel. Do nothing.
1854            return;
1855        }
1856        // If the select element no longer exists, due to a page change, etc,
1857        // silently return.
1858        if (!m_select || !CacheBuilder::validNode(m_viewImpl->m_mainFrame,
1859                m_frame, m_select))
1860            return;
1861        // Use a pointer to HTMLSelectElement's superclass, where
1862        // listToOptionIndex is public.
1863        SelectElement* selectElement = m_select;
1864        int optionIndex = selectElement->listToOptionIndex(index);
1865        m_select->setSelectedIndex(optionIndex, true);
1866        m_select->dispatchFormControlChangeEvent();
1867        m_viewImpl->contentInvalidate(m_select->getRect());
1868    }
1869
1870    // Response if the listbox allows multiple selection.  array stores the listIndices
1871    // of selected positions.
1872    virtual void replyIntArray(const int* array, int count)
1873    {
1874        // If the select element no longer exists, due to a page change, etc,
1875        // silently return.
1876        if (!m_select || !CacheBuilder::validNode(m_viewImpl->m_mainFrame,
1877                m_frame, m_select))
1878            return;
1879
1880        // If count is 1 or 0, use replyInt.
1881        SkASSERT(count > 1);
1882
1883        const WTF::Vector<Element*>& items = m_select->listItems();
1884        int totalItems = static_cast<int>(items.size());
1885        // Keep track of the position of the value we are comparing against.
1886        int arrayIndex = 0;
1887        // The value we are comparing against.
1888        int selection = array[arrayIndex];
1889        WebCore::HTMLOptionElement* option;
1890        for (int listIndex = 0; listIndex < totalItems; listIndex++) {
1891            if (items[listIndex]->hasLocalName(WebCore::HTMLNames::optionTag)) {
1892                option = static_cast<WebCore::HTMLOptionElement*>(
1893                        items[listIndex]);
1894                if (listIndex == selection) {
1895                    option->setSelectedState(true);
1896                    arrayIndex++;
1897                    if (arrayIndex == count)
1898                        selection = -1;
1899                    else
1900                        selection = array[arrayIndex];
1901                } else
1902                    option->setSelectedState(false);
1903            }
1904        }
1905        m_select->dispatchFormControlChangeEvent();
1906        m_viewImpl->contentInvalidate(m_select->getRect());
1907    }
1908private:
1909    // The select element associated with this listbox.
1910    WebCore::HTMLSelectElement* m_select;
1911    // The frame of this select element, to verify that it is valid.
1912    WebCore::Frame* m_frame;
1913    // For calling invalidate and checking the select element's validity
1914    WebViewCore* m_viewImpl;
1915};
1916
1917// Create an array of java Strings.
1918static jobjectArray makeLabelArray(JNIEnv* env, const uint16_t** labels, size_t count)
1919{
1920    jclass stringClass = env->FindClass("java/lang/String");
1921    LOG_ASSERT(stringClass, "Could not find java/lang/String");
1922    jobjectArray array = env->NewObjectArray(count, stringClass, 0);
1923    LOG_ASSERT(array, "Could not create new string array");
1924
1925    for (size_t i = 0; i < count; i++) {
1926        jobject newString = env->NewString(&labels[i][1], labels[i][0]);
1927        env->SetObjectArrayElement(array, i, newString);
1928        env->DeleteLocalRef(newString);
1929        checkException(env);
1930    }
1931    env->DeleteLocalRef(stringClass);
1932    return array;
1933}
1934
1935void WebViewCore::openFileChooser(PassRefPtr<WebCore::FileChooser> chooser) {
1936    if (!chooser)
1937        return;
1938    JNIEnv* env = JSC::Bindings::getJNIEnv();
1939    jstring jName = (jstring) env->CallObjectMethod(
1940            m_javaGlue->object(env).get(), m_javaGlue->m_openFileChooser);
1941    checkException(env);
1942    const UChar* string = (const UChar*) env->GetStringChars(jName, NULL);
1943    if (!string)
1944        return;
1945    WebCore::String webcoreString = to_string(env, jName);
1946    env->ReleaseStringChars(jName, string);
1947    chooser->chooseFile(webcoreString);
1948}
1949
1950void WebViewCore::listBoxRequest(WebCoreReply* reply, const uint16_t** labels, size_t count, const int enabled[], size_t enabledCount,
1951        bool multiple, const int selected[], size_t selectedCountOrSelection)
1952{
1953    // If m_popupReply is not null, then we already have a list showing.
1954    if (m_popupReply != 0)
1955        return;
1956
1957    LOG_ASSERT(m_javaGlue->m_obj, "No java widget associated with this view!");
1958
1959    // Create an array of java Strings for the drop down.
1960    JNIEnv* env = JSC::Bindings::getJNIEnv();
1961    jobjectArray labelArray = makeLabelArray(env, labels, count);
1962
1963    // Create an array determining whether each item is enabled.
1964    jintArray enabledArray = env->NewIntArray(enabledCount);
1965    checkException(env);
1966    jint* ptrArray = env->GetIntArrayElements(enabledArray, 0);
1967    checkException(env);
1968    for (size_t i = 0; i < enabledCount; i++) {
1969        ptrArray[i] = enabled[i];
1970    }
1971    env->ReleaseIntArrayElements(enabledArray, ptrArray, 0);
1972    checkException(env);
1973
1974    if (multiple) {
1975        // Pass up an array representing which items are selected.
1976        jintArray selectedArray = env->NewIntArray(selectedCountOrSelection);
1977        checkException(env);
1978        jint* selArray = env->GetIntArrayElements(selectedArray, 0);
1979        checkException(env);
1980        for (size_t i = 0; i < selectedCountOrSelection; i++) {
1981            selArray[i] = selected[i];
1982        }
1983        env->ReleaseIntArrayElements(selectedArray, selArray, 0);
1984
1985        env->CallVoidMethod(m_javaGlue->object(env).get(),
1986                m_javaGlue->m_requestListBox, labelArray, enabledArray,
1987                selectedArray);
1988        env->DeleteLocalRef(selectedArray);
1989    } else {
1990        // Pass up the single selection.
1991        env->CallVoidMethod(m_javaGlue->object(env).get(),
1992                m_javaGlue->m_requestSingleListBox, labelArray, enabledArray,
1993                selectedCountOrSelection);
1994    }
1995
1996    env->DeleteLocalRef(labelArray);
1997    env->DeleteLocalRef(enabledArray);
1998    checkException(env);
1999
2000    Retain(reply);
2001    m_popupReply = reply;
2002}
2003
2004bool WebViewCore::key(const PlatformKeyboardEvent& event)
2005{
2006    WebCore::EventHandler* eventHandler = m_mainFrame->eventHandler();
2007    WebCore::Node* focusNode = currentFocus();
2008    if (focusNode)
2009        eventHandler = focusNode->document()->frame()->eventHandler();
2010    DBG_NAV_LOGD("keyCode=%s unichar=%d focusNode=%p",
2011        event.keyIdentifier().utf8().data(), event.unichar(), focusNode);
2012    return eventHandler->keyEvent(event);
2013}
2014
2015// For when the user clicks the trackball
2016void WebViewCore::click(WebCore::Frame* frame, WebCore::Node* node) {
2017    if (!node) {
2018        WebCore::IntPoint pt = m_mousePos;
2019        pt.move(m_scrollOffsetX, m_scrollOffsetY);
2020        WebCore::HitTestResult hitTestResult = m_mainFrame->eventHandler()->
2021                hitTestResultAtPoint(pt, false);
2022        node = hitTestResult.innerNode();
2023        frame = node->document()->frame();
2024        DBG_NAV_LOGD("m_mousePos=(%d,%d) m_scrollOffset=(%d,%d) pt=(%d,%d)"
2025            " node=%p", m_mousePos.x(), m_mousePos.y(),
2026            m_scrollOffsetX, m_scrollOffsetY, pt.x(), pt.y(), node);
2027    }
2028    if (node) {
2029        EditorClientAndroid* client
2030                = static_cast<EditorClientAndroid*>(
2031                m_mainFrame->editor()->client());
2032        client->setShouldChangeSelectedRange(false);
2033        handleMouseClick(frame, node);
2034        client->setShouldChangeSelectedRange(true);
2035    }
2036}
2037
2038#if USE(ACCELERATED_COMPOSITING)
2039GraphicsLayerAndroid* WebViewCore::graphicsRootLayer() const
2040{
2041    RenderView* contentRenderer = m_mainFrame->contentRenderer();
2042    if (!contentRenderer)
2043        return 0;
2044    return static_cast<GraphicsLayerAndroid*>(
2045          contentRenderer->compositor()->rootPlatformLayer());
2046}
2047#endif
2048
2049bool WebViewCore::handleTouchEvent(int action, int x, int y, int metaState)
2050{
2051    bool preventDefault = false;
2052
2053#if USE(ACCELERATED_COMPOSITING)
2054    GraphicsLayerAndroid* rootLayer = graphicsRootLayer();
2055    if (rootLayer)
2056      rootLayer->pauseDisplay(true);
2057#endif
2058
2059#if ENABLE(TOUCH_EVENTS) // Android
2060    WebCore::TouchEventType type = WebCore::TouchStart;
2061    WebCore::PlatformTouchPoint::State touchState = WebCore::PlatformTouchPoint::TouchPressed;
2062    switch (action) {
2063    case 0: // MotionEvent.ACTION_DOWN
2064        type = WebCore::TouchStart;
2065        break;
2066    case 1: // MotionEvent.ACTION_UP
2067        type = WebCore::TouchEnd;
2068        touchState = WebCore::PlatformTouchPoint::TouchReleased;
2069        break;
2070    case 2: // MotionEvent.ACTION_MOVE
2071        type = WebCore::TouchMove;
2072        touchState = WebCore::PlatformTouchPoint::TouchMoved;
2073        break;
2074    case 3: // MotionEvent.ACTION_CANCEL
2075        type = WebCore::TouchCancel;
2076        touchState = WebCore::PlatformTouchPoint::TouchCancelled;
2077        break;
2078    case 0x100: // WebViewCore.ACTION_LONGPRESS
2079        type = WebCore::TouchLongPress;
2080        touchState = WebCore::PlatformTouchPoint::TouchPressed;
2081        break;
2082    case 0x200: // WebViewCore.ACTION_DOUBLETAP
2083        type = WebCore::TouchDoubleTap;
2084        touchState = WebCore::PlatformTouchPoint::TouchPressed;
2085        break;
2086    default:
2087        // We do not support other kinds of touch event inside WebCore
2088        // at the moment.
2089        LOGW("Java passed a touch event type that we do not support in WebCore: %d", action);
2090        return 0;
2091    }
2092
2093    // Track previous touch and if stationary set the state.
2094    WebCore::IntPoint pt(x - m_scrollOffsetX, y - m_scrollOffsetY);
2095
2096//  handleTouchEvent() in EventHandler.cpp doesn't handle TouchStationary, which
2097//  causes preventDefault be false when it returns. As our Java side may continue
2098//  process the events if WebKit doesn't, it can cause unexpected result.
2099//    if (type == WebCore::TouchMove && pt == m_lastTouchPoint)
2100//        touchState = WebCore::PlatformTouchPoint::TouchStationary;
2101
2102    m_lastTouchPoint = pt;
2103
2104    WebCore::PlatformTouchEvent te(pt, type, touchState, metaState);
2105    preventDefault = m_mainFrame->eventHandler()->handleTouchEvent(te);
2106#endif
2107
2108#if USE(ACCELERATED_COMPOSITING)
2109    if (rootLayer)
2110      rootLayer->pauseDisplay(false);
2111#endif
2112    return preventDefault;
2113}
2114
2115void WebViewCore::touchUp(int touchGeneration,
2116    WebCore::Frame* frame, WebCore::Node* node, int x, int y)
2117{
2118    if (m_touchGeneration > touchGeneration) {
2119        DBG_NAV_LOGD("m_touchGeneration=%d > touchGeneration=%d"
2120            " x=%d y=%d", m_touchGeneration, touchGeneration, x, y);
2121        return; // short circuit if a newer touch has been generated
2122    }
2123    // This moves m_mousePos to the correct place, and handleMouseClick uses
2124    // m_mousePos to determine where the click happens.
2125    moveMouse(frame, x, y);
2126    m_lastGeneration = touchGeneration;
2127    if (frame && CacheBuilder::validNode(m_mainFrame, frame, 0)) {
2128        frame->loader()->resetMultipleFormSubmissionProtection();
2129    }
2130    DBG_NAV_LOGD("touchGeneration=%d handleMouseClick frame=%p node=%p"
2131        " x=%d y=%d", touchGeneration, frame, node, x, y);
2132    handleMouseClick(frame, node);
2133}
2134
2135// Common code for both clicking with the trackball and touchUp
2136bool WebViewCore::handleMouseClick(WebCore::Frame* framePtr, WebCore::Node* nodePtr)
2137{
2138    bool valid = framePtr == NULL
2139            || CacheBuilder::validNode(m_mainFrame, framePtr, nodePtr);
2140    WebFrame* webFrame = WebFrame::getWebFrame(m_mainFrame);
2141    if (valid && nodePtr) {
2142    // Need to special case area tags because an image map could have an area element in the middle
2143    // so when attempting to get the default, the point chosen would be follow the wrong link.
2144        if (nodePtr->hasTagName(WebCore::HTMLNames::areaTag)) {
2145            webFrame->setUserInitiatedClick(true);
2146            nodePtr->dispatchSimulatedClick(0, true, true);
2147            webFrame->setUserInitiatedClick(false);
2148            DBG_NAV_LOG("area");
2149            return true;
2150        }
2151        WebCore::RenderObject* renderer = nodePtr->renderer();
2152        if (renderer && (renderer->isMenuList() || renderer->isListBox())) {
2153            WebCore::HTMLSelectElement* select = static_cast<WebCore::HTMLSelectElement*>(nodePtr);
2154            const WTF::Vector<WebCore::Element*>& listItems = select->listItems();
2155            SkTDArray<const uint16_t*> names;
2156            // Possible values for enabledArray.  Keep in Sync with values in
2157            // InvokeListBox.Container in WebView.java
2158            enum OptionStatus {
2159                OPTGROUP = -1,
2160                OPTION_DISABLED = 0,
2161                OPTION_ENABLED = 1,
2162            };
2163            SkTDArray<int> enabledArray;
2164            SkTDArray<int> selectedArray;
2165            int size = listItems.size();
2166            bool multiple = select->multiple();
2167            for (int i = 0; i < size; i++) {
2168                if (listItems[i]->hasTagName(WebCore::HTMLNames::optionTag)) {
2169                    WebCore::HTMLOptionElement* option = static_cast<WebCore::HTMLOptionElement*>(listItems[i]);
2170                    *names.append() = stringConverter(option->textIndentedToRespectGroupLabel());
2171                    *enabledArray.append() = option->disabled() ? OPTION_DISABLED : OPTION_ENABLED;
2172                    if (multiple && option->selected())
2173                        *selectedArray.append() = i;
2174                } else if (listItems[i]->hasTagName(WebCore::HTMLNames::optgroupTag)) {
2175                    WebCore::HTMLOptGroupElement* optGroup = static_cast<WebCore::HTMLOptGroupElement*>(listItems[i]);
2176                    *names.append() = stringConverter(optGroup->groupLabelText());
2177                    *enabledArray.append() = OPTGROUP;
2178                }
2179            }
2180            WebCoreReply* reply = new ListBoxReply(select, select->document()->frame(), this);
2181            // Use a pointer to HTMLSelectElement's superclass, where
2182            // optionToListIndex is public.
2183            SelectElement* selectElement = select;
2184            listBoxRequest(reply, names.begin(), size, enabledArray.begin(), enabledArray.count(),
2185                    multiple, selectedArray.begin(), multiple ? selectedArray.count() :
2186                    selectElement->optionToListIndex(select->selectedIndex()));
2187            DBG_NAV_LOG("menu list");
2188            return true;
2189        }
2190    }
2191    if (!valid || !framePtr)
2192        framePtr = m_mainFrame;
2193    webFrame->setUserInitiatedClick(true);
2194    WebCore::PlatformMouseEvent mouseDown(m_mousePos, m_mousePos, WebCore::LeftButton,
2195            WebCore::MouseEventPressed, 1, false, false, false, false,
2196            WTF::currentTime());
2197    // ignore the return from as it will return true if the hit point can trigger selection change
2198    framePtr->eventHandler()->handleMousePressEvent(mouseDown);
2199    WebCore::PlatformMouseEvent mouseUp(m_mousePos, m_mousePos, WebCore::LeftButton,
2200            WebCore::MouseEventReleased, 1, false, false, false, false,
2201            WTF::currentTime());
2202    bool handled = framePtr->eventHandler()->handleMouseReleaseEvent(mouseUp);
2203    webFrame->setUserInitiatedClick(false);
2204
2205    // If the user clicked on a textfield, make the focusController active
2206    // so we show the blinking cursor.
2207    WebCore::Node* focusNode = currentFocus();
2208    DBG_NAV_LOGD("m_mousePos={%d,%d} focusNode=%p handled=%s", m_mousePos.x(),
2209        m_mousePos.y(), focusNode, handled ? "true" : "false");
2210    if (focusNode) {
2211        WebCore::RenderObject* renderer = focusNode->renderer();
2212        if (renderer && (renderer->isTextField() || renderer->isTextArea())) {
2213            bool ime = !(static_cast<WebCore::HTMLInputElement*>(focusNode))
2214                    ->readOnly();
2215            setFocusControllerActive(framePtr, ime);
2216            if (ime) {
2217                RenderTextControl* rtc
2218                        = static_cast<RenderTextControl*> (renderer);
2219                requestKeyboardWithSelection(focusNode, rtc->selectionStart(),
2220                        rtc->selectionEnd());
2221            } else {
2222                requestKeyboard(false);
2223            }
2224        }
2225    }
2226    return handled;
2227}
2228
2229void WebViewCore::popupReply(int index)
2230{
2231    if (m_popupReply) {
2232        m_popupReply->replyInt(index);
2233        Release(m_popupReply);
2234        m_popupReply = 0;
2235    }
2236}
2237
2238void WebViewCore::popupReply(const int* array, int count)
2239{
2240    if (m_popupReply) {
2241        m_popupReply->replyIntArray(array, count);
2242        Release(m_popupReply);
2243        m_popupReply = NULL;
2244    }
2245}
2246
2247void WebViewCore::addMessageToConsole(const WebCore::String& message, unsigned int lineNumber, const WebCore::String& sourceID, int msgLevel) {
2248    JNIEnv* env = JSC::Bindings::getJNIEnv();
2249    jstring jMessageStr = env->NewString((unsigned short *)message.characters(), message.length());
2250    jstring jSourceIDStr = env->NewString((unsigned short *)sourceID.characters(), sourceID.length());
2251    env->CallVoidMethod(m_javaGlue->object(env).get(),
2252            m_javaGlue->m_addMessageToConsole, jMessageStr, lineNumber,
2253            jSourceIDStr, msgLevel);
2254    env->DeleteLocalRef(jMessageStr);
2255    env->DeleteLocalRef(jSourceIDStr);
2256    checkException(env);
2257}
2258
2259void WebViewCore::jsAlert(const WebCore::String& url, const WebCore::String& text)
2260{
2261    JNIEnv* env = JSC::Bindings::getJNIEnv();
2262    jstring jInputStr = env->NewString((unsigned short *)text.characters(), text.length());
2263    jstring jUrlStr = env->NewString((unsigned short *)url.characters(), url.length());
2264    env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_jsAlert, jUrlStr, jInputStr);
2265    env->DeleteLocalRef(jInputStr);
2266    env->DeleteLocalRef(jUrlStr);
2267    checkException(env);
2268}
2269
2270void WebViewCore::exceededDatabaseQuota(const WebCore::String& url, const WebCore::String& databaseIdentifier, const unsigned long long currentQuota, unsigned long long estimatedSize)
2271{
2272#if ENABLE(DATABASE)
2273    JNIEnv* env = JSC::Bindings::getJNIEnv();
2274    jstring jDatabaseIdentifierStr = env->NewString((unsigned short *)databaseIdentifier.characters(), databaseIdentifier.length());
2275    jstring jUrlStr = env->NewString((unsigned short *)url.characters(), url.length());
2276    env->CallVoidMethod(m_javaGlue->object(env).get(),
2277            m_javaGlue->m_exceededDatabaseQuota, jUrlStr,
2278            jDatabaseIdentifierStr, currentQuota, estimatedSize);
2279    env->DeleteLocalRef(jDatabaseIdentifierStr);
2280    env->DeleteLocalRef(jUrlStr);
2281    checkException(env);
2282#endif
2283}
2284
2285void WebViewCore::reachedMaxAppCacheSize(const unsigned long long spaceNeeded)
2286{
2287#if ENABLE(OFFLINE_WEB_APPLICATIONS)
2288    JNIEnv* env = JSC::Bindings::getJNIEnv();
2289    env->CallVoidMethod(m_javaGlue->object(env).get(),
2290            m_javaGlue->m_reachedMaxAppCacheSize, spaceNeeded);
2291    checkException(env);
2292#endif
2293}
2294
2295void WebViewCore::populateVisitedLinks(WebCore::PageGroup* group)
2296{
2297    m_groupForVisitedLinks = group;
2298    JNIEnv* env = JSC::Bindings::getJNIEnv();
2299    env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_populateVisitedLinks);
2300    checkException(env);
2301}
2302
2303void WebViewCore::geolocationPermissionsShowPrompt(const WebCore::String& origin)
2304{
2305    JNIEnv* env = JSC::Bindings::getJNIEnv();
2306    jstring originString = env->NewString((unsigned short *)origin.characters(), origin.length());
2307    env->CallVoidMethod(m_javaGlue->object(env).get(),
2308                        m_javaGlue->m_geolocationPermissionsShowPrompt,
2309                        originString);
2310    env->DeleteLocalRef(originString);
2311    checkException(env);
2312}
2313
2314void WebViewCore::geolocationPermissionsHidePrompt()
2315{
2316    JNIEnv* env = JSC::Bindings::getJNIEnv();
2317    env->CallVoidMethod(m_javaGlue->object(env).get(),
2318                        m_javaGlue->m_geolocationPermissionsHidePrompt);
2319    checkException(env);
2320}
2321
2322bool WebViewCore::jsConfirm(const WebCore::String& url, const WebCore::String& text)
2323{
2324    JNIEnv* env = JSC::Bindings::getJNIEnv();
2325    jstring jInputStr = env->NewString((unsigned short *)text.characters(), text.length());
2326    jstring jUrlStr = env->NewString((unsigned short *)url.characters(), url.length());
2327    jboolean result = env->CallBooleanMethod(m_javaGlue->object(env).get(), m_javaGlue->m_jsConfirm, jUrlStr, jInputStr);
2328    env->DeleteLocalRef(jInputStr);
2329    env->DeleteLocalRef(jUrlStr);
2330    checkException(env);
2331    return result;
2332}
2333
2334bool WebViewCore::jsPrompt(const WebCore::String& url, const WebCore::String& text, const WebCore::String& defaultValue, WebCore::String& result)
2335{
2336    JNIEnv* env = JSC::Bindings::getJNIEnv();
2337    jstring jInputStr = env->NewString((unsigned short *)text.characters(), text.length());
2338    jstring jDefaultStr = env->NewString((unsigned short *)defaultValue.characters(), defaultValue.length());
2339    jstring jUrlStr = env->NewString((unsigned short *)url.characters(), url.length());
2340    jstring returnVal = (jstring) env->CallObjectMethod(m_javaGlue->object(env).get(), m_javaGlue->m_jsPrompt, jUrlStr, jInputStr, jDefaultStr);
2341    // If returnVal is null, it means that the user cancelled the dialog.
2342    if (!returnVal)
2343        return false;
2344
2345    result = to_string(env, returnVal);
2346    env->DeleteLocalRef(jInputStr);
2347    env->DeleteLocalRef(jDefaultStr);
2348    env->DeleteLocalRef(jUrlStr);
2349    checkException(env);
2350    return true;
2351}
2352
2353bool WebViewCore::jsUnload(const WebCore::String& url, const WebCore::String& message)
2354{
2355    JNIEnv* env = JSC::Bindings::getJNIEnv();
2356    jstring jInputStr = env->NewString((unsigned short *)message.characters(), message.length());
2357    jstring jUrlStr = env->NewString((unsigned short *)url.characters(), url.length());
2358    jboolean result = env->CallBooleanMethod(m_javaGlue->object(env).get(), m_javaGlue->m_jsUnload, jUrlStr, jInputStr);
2359    env->DeleteLocalRef(jInputStr);
2360    env->DeleteLocalRef(jUrlStr);
2361    checkException(env);
2362    return result;
2363}
2364
2365bool WebViewCore::jsInterrupt()
2366{
2367    JNIEnv* env = JSC::Bindings::getJNIEnv();
2368    jboolean result = env->CallBooleanMethod(m_javaGlue->object(env).get(), m_javaGlue->m_jsInterrupt);
2369    checkException(env);
2370    return result;
2371}
2372
2373AutoJObject
2374WebViewCore::getJavaObject()
2375{
2376    return getRealObject(JSC::Bindings::getJNIEnv(), m_javaGlue->m_obj);
2377}
2378
2379jobject
2380WebViewCore::getWebViewJavaObject()
2381{
2382    JNIEnv* env = JSC::Bindings::getJNIEnv();
2383    return env->GetObjectField(m_javaGlue->object(env).get(), gWebViewCoreFields.m_webView);
2384}
2385
2386void WebViewCore::updateTextSelection() {
2387    WebCore::Node* focusNode = currentFocus();
2388    if (!focusNode)
2389        return;
2390    RenderObject* renderer = focusNode->renderer();
2391    if (!renderer || (!renderer->isTextArea() && !renderer->isTextField()))
2392        return;
2393    RenderTextControl* rtc = static_cast<RenderTextControl*>(renderer);
2394    JNIEnv* env = JSC::Bindings::getJNIEnv();
2395    env->CallVoidMethod(m_javaGlue->object(env).get(),
2396            m_javaGlue->m_updateTextSelection, reinterpret_cast<int>(focusNode),
2397            rtc->selectionStart(), rtc->selectionEnd(), m_textGeneration);
2398    checkException(env);
2399}
2400
2401void WebViewCore::updateTextfield(WebCore::Node* ptr, bool changeToPassword,
2402        const WebCore::String& text)
2403{
2404    if (m_blockTextfieldUpdates)
2405        return;
2406    JNIEnv* env = JSC::Bindings::getJNIEnv();
2407    if (changeToPassword) {
2408        env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_updateTextfield,
2409                (int) ptr, true, 0, m_textGeneration);
2410        checkException(env);
2411        return;
2412    }
2413    int length = text.length();
2414    jstring string = env->NewString((unsigned short *) text.characters(), length);
2415    env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_updateTextfield,
2416            (int) ptr, false, string, m_textGeneration);
2417    env->DeleteLocalRef(string);
2418    checkException(env);
2419}
2420
2421void WebViewCore::clearTextEntry()
2422{
2423    JNIEnv* env = JSC::Bindings::getJNIEnv();
2424    env->CallVoidMethod(m_javaGlue->object(env).get(),
2425        m_javaGlue->m_clearTextEntry);
2426}
2427
2428void WebViewCore::setBackgroundColor(SkColor c)
2429{
2430    WebCore::FrameView* view = m_mainFrame->view();
2431    if (!view)
2432        return;
2433
2434    // need (int) cast to find the right constructor
2435    WebCore::Color bcolor((int)SkColorGetR(c), (int)SkColorGetG(c),
2436                          (int)SkColorGetB(c), (int)SkColorGetA(c));
2437    view->setBaseBackgroundColor(bcolor);
2438
2439    // Background color of 0 indicates we want a transparent background
2440    if (c == 0)
2441        view->setTransparent(true);
2442}
2443
2444jclass WebViewCore::getPluginClass(const WebCore::String& libName, const char* className)
2445{
2446    JNIEnv* env = JSC::Bindings::getJNIEnv();
2447
2448    jstring libString = env->NewString(libName.characters(), libName.length());
2449    jstring classString = env->NewStringUTF(className);
2450    jobject pluginClass = env->CallObjectMethod(m_javaGlue->object(env).get(),
2451                                           m_javaGlue->m_getPluginClass,
2452                                           libString, classString);
2453    checkException(env);
2454
2455    // cleanup unneeded local JNI references
2456    env->DeleteLocalRef(libString);
2457    env->DeleteLocalRef(classString);
2458
2459    if (pluginClass != NULL) {
2460        return static_cast<jclass>(pluginClass);
2461    } else {
2462        return NULL;
2463    }
2464}
2465
2466void WebViewCore::showFullScreenPlugin(jobject childView, NPP npp)
2467{
2468    JNIEnv* env = JSC::Bindings::getJNIEnv();
2469    AutoJObject obj = m_javaGlue->object(env);
2470
2471    env->CallVoidMethod(obj.get(),
2472                        m_javaGlue->m_showFullScreenPlugin, childView, (int)npp);
2473    checkException(env);
2474}
2475
2476void WebViewCore::hideFullScreenPlugin()
2477{
2478    JNIEnv* env = JSC::Bindings::getJNIEnv();
2479    env->CallVoidMethod(m_javaGlue->object(env).get(),
2480                        m_javaGlue->m_hideFullScreenPlugin);
2481    checkException(env);
2482}
2483
2484jobject WebViewCore::addSurface(jobject view, int x, int y, int width, int height)
2485{
2486    JNIEnv* env = JSC::Bindings::getJNIEnv();
2487    jobject result = env->CallObjectMethod(m_javaGlue->object(env).get(),
2488                                           m_javaGlue->m_addSurface,
2489                                           view, x, y, width, height);
2490    checkException(env);
2491    return result;
2492}
2493
2494void WebViewCore::updateSurface(jobject childView, int x, int y, int width, int height)
2495{
2496    JNIEnv* env = JSC::Bindings::getJNIEnv();
2497    env->CallVoidMethod(m_javaGlue->object(env).get(),
2498                        m_javaGlue->m_updateSurface, childView,
2499                        x, y, width, height);
2500    checkException(env);
2501}
2502
2503void WebViewCore::destroySurface(jobject childView)
2504{
2505    JNIEnv* env = JSC::Bindings::getJNIEnv();
2506    env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_destroySurface, childView);
2507    checkException(env);
2508}
2509
2510jobject WebViewCore::getContext()
2511{
2512    JNIEnv* env = JSC::Bindings::getJNIEnv();
2513    AutoJObject obj = m_javaGlue->object(env);
2514
2515    jobject result = env->CallObjectMethod(obj.get(), m_javaGlue->m_getContext);
2516    checkException(env);
2517    return result;
2518}
2519
2520bool WebViewCore::validNodeAndBounds(Frame* frame, Node* node,
2521    const IntRect& originalAbsoluteBounds)
2522{
2523    bool valid = CacheBuilder::validNode(m_mainFrame, frame, node);
2524    if (!valid)
2525        return false;
2526    RenderObject* renderer = node->renderer();
2527    if (!renderer)
2528        return false;
2529    IntRect absBounds = node->hasTagName(HTMLNames::areaTag)
2530        ? CacheBuilder::getAreaRect(static_cast<HTMLAreaElement*>(node))
2531        : renderer->absoluteBoundingBoxRect();
2532    return absBounds == originalAbsoluteBounds;
2533}
2534
2535void WebViewCore::showRect(int left, int top, int width, int height,
2536        int contentWidth, int contentHeight, float xPercentInDoc,
2537        float xPercentInView, float yPercentInDoc, float yPercentInView)
2538{
2539    JNIEnv* env = JSC::Bindings::getJNIEnv();
2540    env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_showRect,
2541            left, top, width, height, contentWidth, contentHeight,
2542            xPercentInDoc, xPercentInView, yPercentInDoc, yPercentInView);
2543    checkException(env);
2544}
2545
2546void WebViewCore::centerFitRect(int x, int y, int width, int height)
2547{
2548    JNIEnv* env = JSC::Bindings::getJNIEnv();
2549    env->CallVoidMethod(m_javaGlue->object(env).get(),
2550            m_javaGlue->m_centerFitRect, x, y, width, height);
2551    checkException(env);
2552}
2553
2554
2555void WebViewCore::setScrollbarModes(ScrollbarMode horizontalMode, ScrollbarMode verticalMode)
2556{
2557    JNIEnv* env = JSC::Bindings::getJNIEnv();
2558    env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_setScrollbarModes,
2559            horizontalMode, verticalMode);
2560    checkException(env);
2561}
2562
2563//----------------------------------------------------------------------
2564// Native JNI methods
2565//----------------------------------------------------------------------
2566static jstring WebCoreStringToJString(JNIEnv *env, WebCore::String string)
2567{
2568    int length = string.length();
2569    if (!length)
2570        return 0;
2571    jstring ret = env->NewString((jchar *)string.characters(), length);
2572    env->DeleteLocalRef(ret);
2573    return ret;
2574}
2575
2576static jstring RequestLabel(JNIEnv *env, jobject obj, int framePointer,
2577        int nodePointer)
2578{
2579    return WebCoreStringToJString(env, GET_NATIVE_VIEW(env, obj)->requestLabel(
2580            (WebCore::Frame*) framePointer, (WebCore::Node*) nodePointer));
2581}
2582
2583static void UpdateFrameCacheIfLoading(JNIEnv *env, jobject obj)
2584{
2585    GET_NATIVE_VIEW(env, obj)->updateFrameCacheIfLoading();
2586}
2587
2588static void SetSize(JNIEnv *env, jobject obj, jint width, jint height,
2589        jint screenWidth, jfloat scale, jint realScreenWidth, jint screenHeight,
2590        jint anchorX, jint anchorY, jboolean ignoreHeight)
2591{
2592#ifdef ANDROID_INSTRUMENT
2593    TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
2594#endif
2595    WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
2596    LOGV("webviewcore::nativeSetSize(%u %u)\n viewImpl: %p", (unsigned)width, (unsigned)height, viewImpl);
2597    LOG_ASSERT(viewImpl, "viewImpl not set in nativeSetSize");
2598    viewImpl->setSizeScreenWidthAndScale(width, height, screenWidth, scale,
2599        realScreenWidth, screenHeight, anchorX, anchorY, ignoreHeight);
2600}
2601
2602static void SetScrollOffset(JNIEnv *env, jobject obj, jint gen, jint x, jint y)
2603{
2604#ifdef ANDROID_INSTRUMENT
2605    TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
2606#endif
2607    WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
2608    LOG_ASSERT(viewImpl, "need viewImpl");
2609
2610    viewImpl->setScrollOffset(gen, x, y);
2611}
2612
2613static void SetGlobalBounds(JNIEnv *env, jobject obj, jint x, jint y, jint h,
2614                            jint v)
2615{
2616#ifdef ANDROID_INSTRUMENT
2617    TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
2618#endif
2619    WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
2620    LOG_ASSERT(viewImpl, "need viewImpl");
2621
2622    viewImpl->setGlobalBounds(x, y, h, v);
2623}
2624
2625static jboolean Key(JNIEnv *env, jobject obj, jint keyCode, jint unichar,
2626        jint repeatCount, jboolean isShift, jboolean isAlt, jboolean isSym,
2627        jboolean isDown)
2628{
2629#ifdef ANDROID_INSTRUMENT
2630    TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
2631#endif
2632    return GET_NATIVE_VIEW(env, obj)->key(PlatformKeyboardEvent(keyCode,
2633        unichar, repeatCount, isDown, isShift, isAlt, isSym));
2634}
2635
2636static void Click(JNIEnv *env, jobject obj, int framePtr, int nodePtr)
2637{
2638#ifdef ANDROID_INSTRUMENT
2639    TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
2640#endif
2641    WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
2642    LOG_ASSERT(viewImpl, "viewImpl not set in Click");
2643
2644    viewImpl->click(reinterpret_cast<WebCore::Frame*>(framePtr),
2645        reinterpret_cast<WebCore::Node*>(nodePtr));
2646}
2647
2648static void DeleteSelection(JNIEnv *env, jobject obj, jint start, jint end,
2649        jint textGeneration)
2650{
2651#ifdef ANDROID_INSTRUMENT
2652    TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
2653#endif
2654    WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
2655    viewImpl->deleteSelection(start, end, textGeneration);
2656}
2657
2658static void SetSelection(JNIEnv *env, jobject obj, jint start, jint end)
2659{
2660#ifdef ANDROID_INSTRUMENT
2661    TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
2662#endif
2663    WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
2664    viewImpl->setSelection(start, end);
2665}
2666
2667
2668static void ReplaceTextfieldText(JNIEnv *env, jobject obj,
2669    jint oldStart, jint oldEnd, jstring replace, jint start, jint end,
2670    jint textGeneration)
2671{
2672#ifdef ANDROID_INSTRUMENT
2673    TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
2674#endif
2675    WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
2676    WebCore::String webcoreString = to_string(env, replace);
2677    viewImpl->replaceTextfieldText(oldStart,
2678            oldEnd, webcoreString, start, end, textGeneration);
2679}
2680
2681static void PassToJs(JNIEnv *env, jobject obj,
2682    jint generation, jstring currentText, jint keyCode,
2683    jint keyValue, jboolean down, jboolean cap, jboolean fn, jboolean sym)
2684{
2685#ifdef ANDROID_INSTRUMENT
2686    TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
2687#endif
2688    WebCore::String current = to_string(env, currentText);
2689    GET_NATIVE_VIEW(env, obj)->passToJs(generation, current,
2690        PlatformKeyboardEvent(keyCode, keyValue, 0, down, cap, fn, sym));
2691}
2692
2693static void ScrollFocusedTextInput(JNIEnv *env, jobject obj, jfloat xPercent,
2694    jint y)
2695{
2696#ifdef ANDROID_INSTRUMENT
2697    TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
2698#endif
2699    WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
2700    viewImpl->scrollFocusedTextInput(xPercent, y);
2701}
2702
2703static void SetFocusControllerActive(JNIEnv *env, jobject obj, jboolean active)
2704{
2705#ifdef ANDROID_INSTRUMENT
2706    TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
2707#endif
2708    LOGV("webviewcore::nativeSetFocusControllerActive()\n");
2709    WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
2710    LOG_ASSERT(viewImpl, "viewImpl not set in nativeSetFocusControllerActive");
2711    viewImpl->setFocusControllerActive(0, active);
2712}
2713
2714static void SaveDocumentState(JNIEnv *env, jobject obj, jint frame)
2715{
2716#ifdef ANDROID_INSTRUMENT
2717    TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
2718#endif
2719    LOGV("webviewcore::nativeSaveDocumentState()\n");
2720    WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
2721    LOG_ASSERT(viewImpl, "viewImpl not set in nativeSaveDocumentState");
2722    viewImpl->saveDocumentState((WebCore::Frame*) frame);
2723}
2724
2725void WebViewCore::addVisitedLink(const UChar* string, int length)
2726{
2727    if (m_groupForVisitedLinks)
2728        m_groupForVisitedLinks->addVisitedLink(string, length);
2729}
2730
2731static bool RecordContent(JNIEnv *env, jobject obj, jobject region, jobject pt)
2732{
2733#ifdef ANDROID_INSTRUMENT
2734    TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
2735#endif
2736    WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
2737    SkRegion* nativeRegion = GraphicsJNI::getNativeRegion(env, region);
2738    SkIPoint nativePt;
2739    bool result = viewImpl->recordContent(nativeRegion, &nativePt);
2740    GraphicsJNI::ipoint_to_jpoint(nativePt, env, pt);
2741    return result;
2742}
2743
2744static void SplitContent(JNIEnv *env, jobject obj)
2745{
2746#ifdef ANDROID_INSTRUMENT
2747    TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
2748#endif
2749    WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
2750    viewImpl->splitContent();
2751}
2752
2753static void SendListBoxChoice(JNIEnv* env, jobject obj, jint choice)
2754{
2755#ifdef ANDROID_INSTRUMENT
2756    TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
2757#endif
2758    WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
2759    LOG_ASSERT(viewImpl, "viewImpl not set in nativeSendListBoxChoice");
2760    viewImpl->popupReply(choice);
2761}
2762
2763// Set aside a predetermined amount of space in which to place the listbox
2764// choices, to avoid unnecessary allocations.
2765// The size here is arbitrary.  We want the size to be at least as great as the
2766// number of items in the average multiple-select listbox.
2767#define PREPARED_LISTBOX_STORAGE 10
2768
2769static void SendListBoxChoices(JNIEnv* env, jobject obj, jbooleanArray jArray,
2770        jint size)
2771{
2772#ifdef ANDROID_INSTRUMENT
2773    TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
2774#endif
2775    WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
2776    LOG_ASSERT(viewImpl, "viewImpl not set in nativeSendListBoxChoices");
2777    jboolean* ptrArray = env->GetBooleanArrayElements(jArray, 0);
2778    SkAutoSTMalloc<PREPARED_LISTBOX_STORAGE, int> storage(size);
2779    int* array = storage.get();
2780    int count = 0;
2781    for (int i = 0; i < size; i++) {
2782        if (ptrArray[i]) {
2783            array[count++] = i;
2784        }
2785    }
2786    env->ReleaseBooleanArrayElements(jArray, ptrArray, JNI_ABORT);
2787    viewImpl->popupReply(array, count);
2788}
2789
2790static jstring FindAddress(JNIEnv *env, jobject obj, jstring addr,
2791    jboolean caseInsensitive)
2792{
2793#ifdef ANDROID_INSTRUMENT
2794    TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
2795#endif
2796    if (!addr)
2797        return 0;
2798    int length = env->GetStringLength(addr);
2799    if (!length)
2800        return 0;
2801    const jchar* addrChars = env->GetStringChars(addr, 0);
2802    int start, end;
2803    bool success = CacheBuilder::FindAddress(addrChars, length,
2804        &start, &end, caseInsensitive) == CacheBuilder::FOUND_COMPLETE;
2805    jstring ret = 0;
2806    if (success) {
2807        ret = env->NewString((jchar*) addrChars + start, end - start);
2808        env->DeleteLocalRef(ret);
2809    }
2810    env->ReleaseStringChars(addr, addrChars);
2811    return ret;
2812}
2813
2814static jboolean HandleTouchEvent(JNIEnv *env, jobject obj, jint action, jint x, jint y, jint metaState)
2815{
2816#ifdef ANDROID_INSTRUMENT
2817    TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
2818#endif
2819    WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
2820    LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
2821    return viewImpl->handleTouchEvent(action, x, y, metaState);
2822}
2823
2824static void TouchUp(JNIEnv *env, jobject obj, jint touchGeneration,
2825        jint frame, jint node, jint x, jint y)
2826{
2827#ifdef ANDROID_INSTRUMENT
2828    TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
2829#endif
2830    WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
2831    LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
2832    viewImpl->touchUp(touchGeneration,
2833        (WebCore::Frame*) frame, (WebCore::Node*) node, x, y);
2834}
2835
2836static jstring RetrieveHref(JNIEnv *env, jobject obj, jint frame,
2837        jint node)
2838{
2839#ifdef ANDROID_INSTRUMENT
2840    TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
2841#endif
2842    WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
2843    LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
2844    WebCore::String result = viewImpl->retrieveHref((WebCore::Frame*) frame,
2845            (WebCore::Node*) node);
2846    if (!result.isEmpty())
2847        return WebCoreStringToJString(env, result);
2848    return 0;
2849}
2850
2851static jstring RetrieveAnchorText(JNIEnv *env, jobject obj, jint frame,
2852        jint node)
2853{
2854#ifdef ANDROID_INSTRUMENT
2855    TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
2856#endif
2857    WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
2858    LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
2859    WebCore::String result = viewImpl->retrieveAnchorText((WebCore::Frame*) frame,
2860            (WebCore::Node*) node);
2861    if (!result.isEmpty())
2862        return WebCoreStringToJString(env, result);
2863    return 0;
2864}
2865
2866
2867static void MoveFocus(JNIEnv *env, jobject obj, jint framePtr, jint nodePtr)
2868{
2869#ifdef ANDROID_INSTRUMENT
2870    TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
2871#endif
2872    WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
2873    LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
2874    viewImpl->moveFocus((WebCore::Frame*) framePtr, (WebCore::Node*) nodePtr);
2875}
2876
2877static void MoveMouse(JNIEnv *env, jobject obj, jint frame,
2878        jint x, jint y)
2879{
2880#ifdef ANDROID_INSTRUMENT
2881    TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
2882#endif
2883    WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
2884    LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
2885    viewImpl->moveMouse((WebCore::Frame*) frame, x, y);
2886}
2887
2888static void MoveMouseIfLatest(JNIEnv *env, jobject obj, jint moveGeneration,
2889        jint frame, jint x, jint y)
2890{
2891#ifdef ANDROID_INSTRUMENT
2892    TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
2893#endif
2894    WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
2895    LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
2896    viewImpl->moveMouseIfLatest(moveGeneration,
2897        (WebCore::Frame*) frame, x, y);
2898}
2899
2900static void UpdateFrameCache(JNIEnv *env, jobject obj)
2901{
2902#ifdef ANDROID_INSTRUMENT
2903    TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
2904#endif
2905    WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
2906    LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
2907    viewImpl->updateFrameCache();
2908}
2909
2910static jint GetContentMinPrefWidth(JNIEnv *env, jobject obj)
2911{
2912#ifdef ANDROID_INSTRUMENT
2913    TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
2914#endif
2915    WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
2916    LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
2917
2918    WebCore::Frame* frame = viewImpl->mainFrame();
2919    if (frame) {
2920        WebCore::Document* document = frame->document();
2921        if (document) {
2922            WebCore::RenderObject* renderer = document->renderer();
2923            if (renderer && renderer->isRenderView()) {
2924                return renderer->minPrefWidth();
2925            }
2926        }
2927    }
2928    return 0;
2929}
2930
2931static void SetViewportSettingsFromNative(JNIEnv *env, jobject obj)
2932{
2933#ifdef ANDROID_INSTRUMENT
2934    TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
2935#endif
2936    WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
2937    LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
2938
2939    WebCore::Settings* s = viewImpl->mainFrame()->page()->settings();
2940    if (!s)
2941        return;
2942
2943#ifdef ANDROID_META_SUPPORT
2944    env->SetIntField(obj, gWebViewCoreFields.m_viewportWidth, s->viewportWidth());
2945    env->SetIntField(obj, gWebViewCoreFields.m_viewportHeight, s->viewportHeight());
2946    env->SetIntField(obj, gWebViewCoreFields.m_viewportInitialScale, s->viewportInitialScale());
2947    env->SetIntField(obj, gWebViewCoreFields.m_viewportMinimumScale, s->viewportMinimumScale());
2948    env->SetIntField(obj, gWebViewCoreFields.m_viewportMaximumScale, s->viewportMaximumScale());
2949    env->SetBooleanField(obj, gWebViewCoreFields.m_viewportUserScalable, s->viewportUserScalable());
2950    env->SetIntField(obj, gWebViewCoreFields.m_viewportDensityDpi, s->viewportTargetDensityDpi());
2951#endif
2952}
2953
2954static void SetBackgroundColor(JNIEnv *env, jobject obj, jint color)
2955{
2956#ifdef ANDROID_INSTRUMENT
2957    TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
2958#endif
2959    WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
2960    LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
2961
2962    viewImpl->setBackgroundColor((SkColor) color);
2963}
2964
2965static void DumpDomTree(JNIEnv *env, jobject obj, jboolean useFile)
2966{
2967    WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
2968    LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
2969
2970    viewImpl->dumpDomTree(useFile);
2971}
2972
2973static void DumpRenderTree(JNIEnv *env, jobject obj, jboolean useFile)
2974{
2975    WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
2976    LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
2977
2978    viewImpl->dumpRenderTree(useFile);
2979}
2980
2981static void DumpNavTree(JNIEnv *env, jobject obj)
2982{
2983    WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
2984    LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
2985
2986    viewImpl->dumpNavTree();
2987}
2988
2989static void DumpV8Counters(JNIEnv*, jobject)
2990{
2991#if USE(V8)
2992#ifdef ANDROID_INSTRUMENT
2993    V8Counters::dumpCounters();
2994#endif
2995#endif
2996}
2997
2998static void SetJsFlags(JNIEnv *env, jobject obj, jstring flags)
2999{
3000#if USE(V8)
3001    WebCore::String flagsString = to_string(env, flags);
3002    WebCore::CString utf8String = flagsString.utf8();
3003    WebCore::ScriptController::setFlags(utf8String.data(), utf8String.length());
3004#endif
3005}
3006
3007
3008// Called from the Java side to set a new quota for the origin or new appcache
3009// max size in response to a notification that the original quota was exceeded or
3010// that the appcache has reached its maximum size.
3011static void SetNewStorageLimit(JNIEnv* env, jobject obj, jlong quota) {
3012#if ENABLE(DATABASE) || ENABLE(OFFLINE_WEB_APPLICATIONS)
3013    WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
3014    Frame* frame = viewImpl->mainFrame();
3015
3016    // The main thread is blocked awaiting this response, so now we can wake it
3017    // up.
3018    ChromeClientAndroid* chromeC = static_cast<ChromeClientAndroid*>(frame->page()->chrome()->client());
3019    chromeC->wakeUpMainThreadWithNewQuota(quota);
3020#endif
3021}
3022
3023// Called from Java to provide a Geolocation permission state for the specified origin.
3024static void GeolocationPermissionsProvide(JNIEnv* env, jobject obj, jstring origin, jboolean allow, jboolean remember) {
3025    WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
3026    Frame* frame = viewImpl->mainFrame();
3027
3028    ChromeClientAndroid* chromeClient = static_cast<ChromeClientAndroid*>(frame->page()->chrome()->client());
3029    chromeClient->provideGeolocationPermissions(to_string(env, origin), allow, remember);
3030}
3031
3032static void RegisterURLSchemeAsLocal(JNIEnv* env, jobject obj, jstring scheme) {
3033#ifdef ANDROID_INSTRUMENT
3034    TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
3035#endif
3036    WebCore::SecurityOrigin::registerURLSchemeAsLocal(to_string(env, scheme));
3037}
3038
3039static void ClearContent(JNIEnv *env, jobject obj)
3040{
3041#ifdef ANDROID_INSTRUMENT
3042    TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
3043#endif
3044    WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
3045    viewImpl->clearContent();
3046}
3047
3048static void CopyContentToPicture(JNIEnv *env, jobject obj, jobject pict)
3049{
3050#ifdef ANDROID_INSTRUMENT
3051    TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
3052#endif
3053    WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
3054    if (!viewImpl)
3055        return;
3056    SkPicture* picture = GraphicsJNI::getNativePicture(env, pict);
3057    viewImpl->copyContentToPicture(picture);
3058}
3059
3060static bool DrawContent(JNIEnv *env, jobject obj, jobject canv, jint color)
3061{
3062    // Note: this is called from UI thread, don't count it for WebViewCoreTimeCounter
3063    WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
3064    SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, canv);
3065    return viewImpl->drawContent(canvas, color);
3066}
3067
3068static bool FocusBoundsChanged(JNIEnv* env, jobject obj)
3069{
3070    return GET_NATIVE_VIEW(env, obj)->focusBoundsChanged();
3071}
3072
3073static bool PictureReady(JNIEnv* env, jobject obj)
3074{
3075    return GET_NATIVE_VIEW(env, obj)->pictureReady();
3076}
3077
3078static void Pause(JNIEnv* env, jobject obj)
3079{
3080    // This is called for the foreground tab when the browser is put to the
3081    // background (and also for any tab when it is put to the background of the
3082    // browser). The browser can only be killed by the system when it is in the
3083    // background, so saving the Geolocation permission state now ensures that
3084    // is maintained when the browser is killed.
3085    ChromeClient* chromeClient = GET_NATIVE_VIEW(env, obj)->mainFrame()->page()->chrome()->client();
3086    ChromeClientAndroid* chromeClientAndroid = static_cast<ChromeClientAndroid*>(chromeClient);
3087    chromeClientAndroid->storeGeolocationPermissions();
3088
3089    Frame* mainFrame = GET_NATIVE_VIEW(env, obj)->mainFrame();
3090    for (Frame* frame = mainFrame; frame; frame = frame->tree()->traverseNext()) {
3091        Geolocation* geolocation = frame->domWindow()->navigator()->optionalGeolocation();
3092        if (geolocation)
3093            geolocation->suspend();
3094    }
3095
3096    ANPEvent event;
3097    SkANP::InitEvent(&event, kLifecycle_ANPEventType);
3098    event.data.lifecycle.action = kPause_ANPLifecycleAction;
3099    GET_NATIVE_VIEW(env, obj)->sendPluginEvent(event);
3100
3101    GET_NATIVE_VIEW(env, obj)->setIsPaused(true);
3102}
3103
3104static void Resume(JNIEnv* env, jobject obj)
3105{
3106    Frame* mainFrame = GET_NATIVE_VIEW(env, obj)->mainFrame();
3107    for (Frame* frame = mainFrame; frame; frame = frame->tree()->traverseNext()) {
3108        Geolocation* geolocation = frame->domWindow()->navigator()->optionalGeolocation();
3109        if (geolocation)
3110            geolocation->resume();
3111    }
3112
3113    ANPEvent event;
3114    SkANP::InitEvent(&event, kLifecycle_ANPEventType);
3115    event.data.lifecycle.action = kResume_ANPLifecycleAction;
3116    GET_NATIVE_VIEW(env, obj)->sendPluginEvent(event);
3117
3118    GET_NATIVE_VIEW(env, obj)->setIsPaused(false);
3119}
3120
3121static void FreeMemory(JNIEnv* env, jobject obj)
3122{
3123    ANPEvent event;
3124    SkANP::InitEvent(&event, kLifecycle_ANPEventType);
3125    event.data.lifecycle.action = kFreeMemory_ANPLifecycleAction;
3126    GET_NATIVE_VIEW(env, obj)->sendPluginEvent(event);
3127}
3128
3129static void ProvideVisitedHistory(JNIEnv *env, jobject obj, jobject hist)
3130{
3131    WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
3132    LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
3133
3134    jobjectArray array = static_cast<jobjectArray>(hist);
3135
3136    jsize len = env->GetArrayLength(array);
3137    for (jsize i = 0; i < len; i++) {
3138        jstring item = static_cast<jstring>(env->GetObjectArrayElement(array, i));
3139        const UChar* str = static_cast<const UChar*>(env->GetStringChars(item, NULL));
3140        jsize len = env->GetStringLength(item);
3141        viewImpl->addVisitedLink(str, len);
3142        env->ReleaseStringChars(item, str);
3143        env->DeleteLocalRef(item);
3144    }
3145}
3146
3147// Notification from the UI thread that the plugin's full-screen surface has been discarded
3148static void FullScreenPluginHidden(JNIEnv* env, jobject obj, jint npp)
3149{
3150    WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
3151    PluginWidgetAndroid* plugin = viewImpl->getPluginWidget((NPP)npp);
3152    if (plugin)
3153        plugin->exitFullScreen(false);
3154}
3155
3156static WebCore::IntRect jrect_to_webrect(JNIEnv* env, jobject obj)
3157{
3158    int L, T, R, B;
3159    GraphicsJNI::get_jrect(env, obj, &L, &T, &R, &B);
3160    return WebCore::IntRect(L, T, R - L, B - T);
3161}
3162
3163static bool ValidNodeAndBounds(JNIEnv *env, jobject obj, int frame, int node,
3164    jobject rect)
3165{
3166    IntRect nativeRect = jrect_to_webrect(env, rect);
3167    return GET_NATIVE_VIEW(env, obj)->validNodeAndBounds(
3168            reinterpret_cast<Frame*>(frame),
3169            reinterpret_cast<Node*>(node), nativeRect);
3170}
3171
3172// ----------------------------------------------------------------------------
3173
3174/*
3175 * JNI registration.
3176 */
3177static JNINativeMethod gJavaWebViewCoreMethods[] = {
3178    { "nativeClearContent", "()V",
3179        (void*) ClearContent },
3180    { "nativeCopyContentToPicture", "(Landroid/graphics/Picture;)V",
3181        (void*) CopyContentToPicture },
3182    { "nativeDrawContent", "(Landroid/graphics/Canvas;I)Z",
3183        (void*) DrawContent } ,
3184    { "nativeFocusBoundsChanged", "()Z",
3185        (void*) FocusBoundsChanged } ,
3186    { "nativeKey", "(IIIZZZZ)Z",
3187        (void*) Key },
3188    { "nativeClick", "(II)V",
3189        (void*) Click },
3190    { "nativePictureReady", "()Z",
3191        (void*) PictureReady } ,
3192    { "nativeSendListBoxChoices", "([ZI)V",
3193        (void*) SendListBoxChoices },
3194    { "nativeSendListBoxChoice", "(I)V",
3195        (void*) SendListBoxChoice },
3196    { "nativeSetSize", "(IIIFIIIIZ)V",
3197        (void*) SetSize },
3198    { "nativeSetScrollOffset", "(III)V",
3199        (void*) SetScrollOffset },
3200    { "nativeSetGlobalBounds", "(IIII)V",
3201        (void*) SetGlobalBounds },
3202    { "nativeSetSelection", "(II)V",
3203        (void*) SetSelection } ,
3204    { "nativeDeleteSelection", "(III)V",
3205        (void*) DeleteSelection } ,
3206    { "nativeReplaceTextfieldText", "(IILjava/lang/String;III)V",
3207        (void*) ReplaceTextfieldText } ,
3208    { "nativeMoveFocus", "(II)V",
3209        (void*) MoveFocus },
3210    { "nativeMoveMouse", "(III)V",
3211        (void*) MoveMouse },
3212    { "nativeMoveMouseIfLatest", "(IIII)V",
3213        (void*) MoveMouseIfLatest },
3214    { "passToJs", "(ILjava/lang/String;IIZZZZ)V",
3215        (void*) PassToJs },
3216    { "nativeScrollFocusedTextInput", "(FI)V",
3217        (void*) ScrollFocusedTextInput },
3218    { "nativeSetFocusControllerActive", "(Z)V",
3219        (void*) SetFocusControllerActive },
3220    { "nativeSaveDocumentState", "(I)V",
3221        (void*) SaveDocumentState },
3222    { "nativeFindAddress", "(Ljava/lang/String;Z)Ljava/lang/String;",
3223        (void*) FindAddress },
3224    { "nativeHandleTouchEvent", "(IIII)Z",
3225            (void*) HandleTouchEvent },
3226    { "nativeTouchUp", "(IIIII)V",
3227        (void*) TouchUp },
3228    { "nativeRetrieveHref", "(II)Ljava/lang/String;",
3229        (void*) RetrieveHref },
3230    { "nativeRetrieveAnchorText", "(II)Ljava/lang/String;",
3231        (void*) RetrieveAnchorText },
3232    { "nativeUpdateFrameCache", "()V",
3233        (void*) UpdateFrameCache },
3234    { "nativeGetContentMinPrefWidth", "()I",
3235        (void*) GetContentMinPrefWidth },
3236    { "nativeRecordContent", "(Landroid/graphics/Region;Landroid/graphics/Point;)Z",
3237        (void*) RecordContent },
3238    { "setViewportSettingsFromNative", "()V",
3239        (void*) SetViewportSettingsFromNative },
3240    { "nativeSplitContent", "()V",
3241        (void*) SplitContent },
3242    { "nativeSetBackgroundColor", "(I)V",
3243        (void*) SetBackgroundColor },
3244    { "nativeRegisterURLSchemeAsLocal", "(Ljava/lang/String;)V",
3245        (void*) RegisterURLSchemeAsLocal },
3246    { "nativeDumpDomTree", "(Z)V",
3247        (void*) DumpDomTree },
3248    { "nativeDumpRenderTree", "(Z)V",
3249        (void*) DumpRenderTree },
3250    { "nativeDumpNavTree", "()V",
3251        (void*) DumpNavTree },
3252    { "nativeDumpV8Counters", "()V",
3253        (void*) DumpV8Counters },
3254    { "nativeSetNewStorageLimit", "(J)V",
3255        (void*) SetNewStorageLimit },
3256    { "nativeGeolocationPermissionsProvide", "(Ljava/lang/String;ZZ)V",
3257        (void*) GeolocationPermissionsProvide },
3258    { "nativePause", "()V", (void*) Pause },
3259    { "nativeResume", "()V", (void*) Resume },
3260    { "nativeFreeMemory", "()V", (void*) FreeMemory },
3261    { "nativeSetJsFlags", "(Ljava/lang/String;)V", (void*) SetJsFlags },
3262    { "nativeRequestLabel", "(II)Ljava/lang/String;",
3263        (void*) RequestLabel },
3264    { "nativeUpdateFrameCacheIfLoading", "()V",
3265        (void*) UpdateFrameCacheIfLoading },
3266    { "nativeProvideVisitedHistory", "([Ljava/lang/String;)V",
3267        (void*) ProvideVisitedHistory },
3268    { "nativeFullScreenPluginHidden", "(I)V",
3269        (void*) FullScreenPluginHidden },
3270    { "nativeValidNodeAndBounds", "(IILandroid/graphics/Rect;)Z",
3271        (void*) ValidNodeAndBounds },
3272};
3273
3274int register_webviewcore(JNIEnv* env)
3275{
3276    jclass widget = env->FindClass("android/webkit/WebViewCore");
3277    LOG_ASSERT(widget,
3278            "Unable to find class android/webkit/WebViewCore");
3279    gWebViewCoreFields.m_nativeClass = env->GetFieldID(widget, "mNativeClass",
3280            "I");
3281    LOG_ASSERT(gWebViewCoreFields.m_nativeClass,
3282            "Unable to find android/webkit/WebViewCore.mNativeClass");
3283    gWebViewCoreFields.m_viewportWidth = env->GetFieldID(widget,
3284            "mViewportWidth", "I");
3285    LOG_ASSERT(gWebViewCoreFields.m_viewportWidth,
3286            "Unable to find android/webkit/WebViewCore.mViewportWidth");
3287    gWebViewCoreFields.m_viewportHeight = env->GetFieldID(widget,
3288            "mViewportHeight", "I");
3289    LOG_ASSERT(gWebViewCoreFields.m_viewportHeight,
3290            "Unable to find android/webkit/WebViewCore.mViewportHeight");
3291    gWebViewCoreFields.m_viewportInitialScale = env->GetFieldID(widget,
3292            "mViewportInitialScale", "I");
3293    LOG_ASSERT(gWebViewCoreFields.m_viewportInitialScale,
3294            "Unable to find android/webkit/WebViewCore.mViewportInitialScale");
3295    gWebViewCoreFields.m_viewportMinimumScale = env->GetFieldID(widget,
3296            "mViewportMinimumScale", "I");
3297    LOG_ASSERT(gWebViewCoreFields.m_viewportMinimumScale,
3298            "Unable to find android/webkit/WebViewCore.mViewportMinimumScale");
3299    gWebViewCoreFields.m_viewportMaximumScale = env->GetFieldID(widget,
3300            "mViewportMaximumScale", "I");
3301    LOG_ASSERT(gWebViewCoreFields.m_viewportMaximumScale,
3302            "Unable to find android/webkit/WebViewCore.mViewportMaximumScale");
3303    gWebViewCoreFields.m_viewportUserScalable = env->GetFieldID(widget,
3304            "mViewportUserScalable", "Z");
3305    LOG_ASSERT(gWebViewCoreFields.m_viewportUserScalable,
3306            "Unable to find android/webkit/WebViewCore.mViewportUserScalable");
3307    gWebViewCoreFields.m_viewportDensityDpi = env->GetFieldID(widget,
3308            "mViewportDensityDpi", "I");
3309    LOG_ASSERT(gWebViewCoreFields.m_viewportDensityDpi,
3310            "Unable to find android/webkit/WebViewCore.mViewportDensityDpi");
3311    gWebViewCoreFields.m_webView = env->GetFieldID(widget,
3312            "mWebView", "Landroid/webkit/WebView;");
3313    LOG_ASSERT(gWebViewCoreFields.m_webView,
3314            "Unable to find android/webkit/WebViewCore.mWebView");
3315
3316    gWebViewCoreStaticMethods.m_supportsMimeType =
3317        env->GetStaticMethodID(widget, "supportsMimeType", "(Ljava/lang/String;)Z");
3318    LOG_ASSERT(gWebViewCoreStaticMethods.m_supportsMimeType == NULL,
3319        "Could not find static method supportsMimeType from WebViewCore");
3320
3321    return jniRegisterNativeMethods(env, "android/webkit/WebViewCore",
3322            gJavaWebViewCoreMethods, NELEM(gJavaWebViewCoreMethods));
3323}
3324
3325} /* namespace android */
3326