summaryrefslogtreecommitdiffstats
path: root/Source/WebKit/android/jni/WebViewCore.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebKit/android/jni/WebViewCore.cpp')
-rw-r--r--Source/WebKit/android/jni/WebViewCore.cpp4598
1 files changed, 4598 insertions, 0 deletions
diff --git a/Source/WebKit/android/jni/WebViewCore.cpp b/Source/WebKit/android/jni/WebViewCore.cpp
new file mode 100644
index 0000000..f2680b5
--- /dev/null
+++ b/Source/WebKit/android/jni/WebViewCore.cpp
@@ -0,0 +1,4598 @@
+/*
+ * Copyright 2006, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define LOG_TAG "webcoreglue"
+
+#include "config.h"
+#include "WebViewCore.h"
+
+#include "AccessibilityObject.h"
+#include "Attribute.h"
+#include "BaseLayerAndroid.h"
+#include "CachedNode.h"
+#include "CachedRoot.h"
+#include "Chrome.h"
+#include "ChromeClientAndroid.h"
+#include "ChromiumIncludes.h"
+#include "ClientRect.h"
+#include "ClientRectList.h"
+#include "Color.h"
+#include "CSSPropertyNames.h"
+#include "CSSValueKeywords.h"
+#include "DatabaseTracker.h"
+#include "Document.h"
+#include "DOMWindow.h"
+#include "DOMSelection.h"
+#include "Element.h"
+#include "Editor.h"
+#include "EditorClientAndroid.h"
+#include "EventHandler.h"
+#include "EventNames.h"
+#include "ExceptionCode.h"
+#include "FocusController.h"
+#include "Font.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "FrameLoaderClientAndroid.h"
+#include "FrameTree.h"
+#include "FrameView.h"
+#include "Geolocation.h"
+#include "GraphicsContext.h"
+#include "GraphicsJNI.h"
+#include "HTMLAnchorElement.h"
+#include "HTMLAreaElement.h"
+#include "HTMLElement.h"
+#include "HTMLFormControlElement.h"
+#include "HTMLImageElement.h"
+#include "HTMLInputElement.h"
+#include "HTMLLabelElement.h"
+#include "HTMLMapElement.h"
+#include "HTMLNames.h"
+#include "HTMLOptGroupElement.h"
+#include "HTMLOptionElement.h"
+#include "HTMLSelectElement.h"
+#include "HTMLTextAreaElement.h"
+#include "HistoryItem.h"
+#include "HitTestRequest.h"
+#include "HitTestResult.h"
+#include "InlineTextBox.h"
+#include "MemoryUsage.h"
+#include "NamedNodeMap.h"
+#include "Navigator.h"
+#include "Node.h"
+#include "NodeList.h"
+#include "Page.h"
+#include "PageGroup.h"
+#include "PlatformKeyboardEvent.h"
+#include "PlatformString.h"
+#include "PluginWidgetAndroid.h"
+#include "PluginView.h"
+#include "Position.h"
+#include "ProgressTracker.h"
+#include "Range.h"
+#include "RenderBox.h"
+#include "RenderInline.h"
+#include "RenderLayer.h"
+#include "RenderPart.h"
+#include "RenderText.h"
+#include "RenderTextControl.h"
+#include "RenderThemeAndroid.h"
+#include "RenderView.h"
+#include "ResourceRequest.h"
+#include "SchemeRegistry.h"
+#include "SelectionController.h"
+#include "Settings.h"
+#include "SkANP.h"
+#include "SkTemplates.h"
+#include "SkTDArray.h"
+#include "SkTypes.h"
+#include "SkCanvas.h"
+#include "SkPicture.h"
+#include "SkUtils.h"
+#include "Text.h"
+#include "TypingCommand.h"
+#include "WebCoreFrameBridge.h"
+#include "WebFrameView.h"
+#include "WindowsKeyboardCodes.h"
+#include "android_graphics.h"
+#include "autofill/WebAutoFill.h"
+#include "htmlediting.h"
+#include "markup.h"
+
+#include <JNIHelp.h>
+#include <JNIUtility.h>
+#include <ui/KeycodeLabels.h>
+#include <wtf/CurrentTime.h>
+#include <wtf/text/AtomicString.h>
+#include <wtf/text/StringImpl.h>
+
+#if USE(V8)
+#include "ScriptController.h"
+#include "V8Counters.h"
+#include <wtf/text/CString.h>
+#endif
+
+#if DEBUG_NAV_UI
+#include "SkTime.h"
+#endif
+
+#if ENABLE(TOUCH_EVENTS) // Android
+#include "PlatformTouchEvent.h"
+#endif
+
+#ifdef ANDROID_DOM_LOGGING
+#include "AndroidLog.h"
+#include "RenderTreeAsText.h"
+#include <wtf/text/CString.h>
+
+FILE* gDomTreeFile = 0;
+FILE* gRenderTreeFile = 0;
+#endif
+
+#ifdef ANDROID_INSTRUMENT
+#include "TimeCounter.h"
+#endif
+
+#if USE(ACCELERATED_COMPOSITING)
+#include "GraphicsLayerAndroid.h"
+#include "RenderLayerCompositor.h"
+#endif
+
+/* We pass this flag when recording the actual content, so that we don't spend
+ time actually regionizing complex path clips, when all we really want to do
+ is record them.
+ */
+#define PICT_RECORD_FLAGS SkPicture::kUsePathBoundsForClip_RecordingFlag
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+
+namespace android {
+
+static SkTDArray<WebViewCore*> gInstanceList;
+
+void WebViewCore::addInstance(WebViewCore* inst) {
+ *gInstanceList.append() = inst;
+}
+
+void WebViewCore::removeInstance(WebViewCore* inst) {
+ int index = gInstanceList.find(inst);
+ LOG_ASSERT(index >= 0, "RemoveInstance inst not found");
+ if (index >= 0) {
+ gInstanceList.removeShuffle(index);
+ }
+}
+
+bool WebViewCore::isInstance(WebViewCore* inst) {
+ return gInstanceList.find(inst) >= 0;
+}
+
+jobject WebViewCore::getApplicationContext() {
+
+ // check to see if there is a valid webviewcore object
+ if (gInstanceList.isEmpty())
+ return 0;
+
+ // get the context from the webview
+ jobject context = gInstanceList[0]->getContext();
+
+ if (!context)
+ return 0;
+
+ // get the application context using JNI
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jclass contextClass = env->GetObjectClass(context);
+ jmethodID appContextMethod = env->GetMethodID(contextClass, "getApplicationContext", "()Landroid/content/Context;");
+ env->DeleteLocalRef(contextClass);
+ jobject result = env->CallObjectMethod(context, appContextMethod);
+ checkException(env);
+ return result;
+}
+
+
+struct WebViewCoreStaticMethods {
+ jmethodID m_isSupportedMediaMimeType;
+} gWebViewCoreStaticMethods;
+
+// Check whether a media mimeType is supported in Android media framework.
+bool WebViewCore::isSupportedMediaMimeType(const WTF::String& mimeType) {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jstring jMimeType = wtfStringToJstring(env, mimeType);
+ jclass webViewCore = env->FindClass("android/webkit/WebViewCore");
+ bool val = env->CallStaticBooleanMethod(webViewCore,
+ gWebViewCoreStaticMethods.m_isSupportedMediaMimeType, jMimeType);
+ checkException(env);
+ env->DeleteLocalRef(webViewCore);
+ env->DeleteLocalRef(jMimeType);
+
+ return val;
+}
+
+// ----------------------------------------------------------------------------
+
+#define GET_NATIVE_VIEW(env, obj) ((WebViewCore*)env->GetIntField(obj, gWebViewCoreFields.m_nativeClass))
+
+// Field ids for WebViewCore
+struct WebViewCoreFields {
+ jfieldID m_nativeClass;
+ jfieldID m_viewportWidth;
+ jfieldID m_viewportHeight;
+ jfieldID m_viewportInitialScale;
+ jfieldID m_viewportMinimumScale;
+ jfieldID m_viewportMaximumScale;
+ jfieldID m_viewportUserScalable;
+ jfieldID m_viewportDensityDpi;
+ jfieldID m_webView;
+ jfieldID m_drawIsPaused;
+ jfieldID m_lowMemoryUsageMb;
+ jfieldID m_highMemoryUsageMb;
+ jfieldID m_highUsageDeltaMb;
+} gWebViewCoreFields;
+
+// ----------------------------------------------------------------------------
+
+struct WebViewCore::JavaGlue {
+ jweak m_obj;
+ jmethodID m_scrollTo;
+ jmethodID m_contentDraw;
+ jmethodID m_layersDraw;
+ jmethodID m_requestListBox;
+ jmethodID m_openFileChooser;
+ jmethodID m_requestSingleListBox;
+ jmethodID m_jsAlert;
+ jmethodID m_jsConfirm;
+ jmethodID m_jsPrompt;
+ jmethodID m_jsUnload;
+ jmethodID m_jsInterrupt;
+ jmethodID m_didFirstLayout;
+ jmethodID m_updateViewport;
+ jmethodID m_sendNotifyProgressFinished;
+ jmethodID m_sendViewInvalidate;
+ jmethodID m_updateTextfield;
+ jmethodID m_updateTextSelection;
+ jmethodID m_clearTextEntry;
+ jmethodID m_restoreScale;
+ jmethodID m_needTouchEvents;
+ jmethodID m_requestKeyboard;
+ jmethodID m_requestKeyboardWithSelection;
+ jmethodID m_exceededDatabaseQuota;
+ jmethodID m_reachedMaxAppCacheSize;
+ jmethodID m_populateVisitedLinks;
+ jmethodID m_geolocationPermissionsShowPrompt;
+ jmethodID m_geolocationPermissionsHidePrompt;
+ jmethodID m_getDeviceMotionService;
+ jmethodID m_getDeviceOrientationService;
+ jmethodID m_addMessageToConsole;
+ jmethodID m_formDidBlur;
+ jmethodID m_getPluginClass;
+ jmethodID m_showFullScreenPlugin;
+ jmethodID m_hideFullScreenPlugin;
+ jmethodID m_createSurface;
+ jmethodID m_addSurface;
+ jmethodID m_updateSurface;
+ jmethodID m_destroySurface;
+ jmethodID m_getContext;
+ jmethodID m_keepScreenOn;
+ jmethodID m_sendFindAgain;
+ jmethodID m_showRect;
+ jmethodID m_centerFitRect;
+ jmethodID m_setScrollbarModes;
+ jmethodID m_setInstallableWebApp;
+ jmethodID m_enterFullscreenForVideoLayer;
+ jmethodID m_setWebTextViewAutoFillable;
+ jmethodID m_selectAt;
+ AutoJObject object(JNIEnv* env) {
+ return getRealObject(env, m_obj);
+ }
+};
+
+/*
+ * WebViewCore Implementation
+ */
+
+static jmethodID GetJMethod(JNIEnv* env, jclass clazz, const char name[], const char signature[])
+{
+ jmethodID m = env->GetMethodID(clazz, name, signature);
+ LOG_ASSERT(m, "Could not find method %s", name);
+ return m;
+}
+
+Mutex WebViewCore::gFrameCacheMutex;
+Mutex WebViewCore::gButtonMutex;
+Mutex WebViewCore::gCursorBoundsMutex;
+
+WebViewCore::WebViewCore(JNIEnv* env, jobject javaWebViewCore, WebCore::Frame* mainframe)
+ : m_pluginInvalTimer(this, &WebViewCore::pluginInvalTimerFired)
+ , m_deviceMotionAndOrientationManager(this)
+{
+ m_mainFrame = mainframe;
+
+ m_popupReply = 0;
+ m_moveGeneration = 0;
+ m_lastGeneration = 0;
+ m_touchGeneration = 0;
+ m_blockTextfieldUpdates = false;
+ // just initial values. These should be set by client
+ m_maxXScroll = 320/4;
+ m_maxYScroll = 240/4;
+ m_textGeneration = 0;
+ m_screenWidth = 320;
+ m_textWrapWidth = 320;
+ m_scale = 1;
+#if ENABLE(TOUCH_EVENTS)
+ m_forwardingTouchEvents = false;
+#endif
+ m_isPaused = false;
+ m_screenOnCounter = 0;
+ m_shouldPaintCaret = true;
+
+ LOG_ASSERT(m_mainFrame, "Uh oh, somehow a frameview was made without an initial frame!");
+
+ jclass clazz = env->GetObjectClass(javaWebViewCore);
+ m_javaGlue = new JavaGlue;
+ m_javaGlue->m_obj = env->NewWeakGlobalRef(javaWebViewCore);
+ m_javaGlue->m_scrollTo = GetJMethod(env, clazz, "contentScrollTo", "(IIZZ)V");
+ m_javaGlue->m_contentDraw = GetJMethod(env, clazz, "contentDraw", "()V");
+ m_javaGlue->m_layersDraw = GetJMethod(env, clazz, "layersDraw", "()V");
+ m_javaGlue->m_requestListBox = GetJMethod(env, clazz, "requestListBox", "([Ljava/lang/String;[I[I)V");
+ m_javaGlue->m_openFileChooser = GetJMethod(env, clazz, "openFileChooser", "(Ljava/lang/String;)Ljava/lang/String;");
+ m_javaGlue->m_requestSingleListBox = GetJMethod(env, clazz, "requestListBox", "([Ljava/lang/String;[II)V");
+ m_javaGlue->m_jsAlert = GetJMethod(env, clazz, "jsAlert", "(Ljava/lang/String;Ljava/lang/String;)V");
+ m_javaGlue->m_jsConfirm = GetJMethod(env, clazz, "jsConfirm", "(Ljava/lang/String;Ljava/lang/String;)Z");
+ m_javaGlue->m_jsPrompt = GetJMethod(env, clazz, "jsPrompt", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
+ m_javaGlue->m_jsUnload = GetJMethod(env, clazz, "jsUnload", "(Ljava/lang/String;Ljava/lang/String;)Z");
+ m_javaGlue->m_jsInterrupt = GetJMethod(env, clazz, "jsInterrupt", "()Z");
+ m_javaGlue->m_didFirstLayout = GetJMethod(env, clazz, "didFirstLayout", "(Z)V");
+ m_javaGlue->m_updateViewport = GetJMethod(env, clazz, "updateViewport", "()V");
+ m_javaGlue->m_sendNotifyProgressFinished = GetJMethod(env, clazz, "sendNotifyProgressFinished", "()V");
+ m_javaGlue->m_sendViewInvalidate = GetJMethod(env, clazz, "sendViewInvalidate", "(IIII)V");
+ m_javaGlue->m_updateTextfield = GetJMethod(env, clazz, "updateTextfield", "(IZLjava/lang/String;I)V");
+ m_javaGlue->m_updateTextSelection = GetJMethod(env, clazz, "updateTextSelection", "(IIII)V");
+ m_javaGlue->m_clearTextEntry = GetJMethod(env, clazz, "clearTextEntry", "()V");
+ m_javaGlue->m_restoreScale = GetJMethod(env, clazz, "restoreScale", "(FF)V");
+ m_javaGlue->m_needTouchEvents = GetJMethod(env, clazz, "needTouchEvents", "(Z)V");
+ m_javaGlue->m_requestKeyboard = GetJMethod(env, clazz, "requestKeyboard", "(Z)V");
+ m_javaGlue->m_requestKeyboardWithSelection = GetJMethod(env, clazz, "requestKeyboardWithSelection", "(IIII)V");
+ m_javaGlue->m_exceededDatabaseQuota = GetJMethod(env, clazz, "exceededDatabaseQuota", "(Ljava/lang/String;Ljava/lang/String;JJ)V");
+ m_javaGlue->m_reachedMaxAppCacheSize = GetJMethod(env, clazz, "reachedMaxAppCacheSize", "(J)V");
+ m_javaGlue->m_populateVisitedLinks = GetJMethod(env, clazz, "populateVisitedLinks", "()V");
+ m_javaGlue->m_geolocationPermissionsShowPrompt = GetJMethod(env, clazz, "geolocationPermissionsShowPrompt", "(Ljava/lang/String;)V");
+ m_javaGlue->m_geolocationPermissionsHidePrompt = GetJMethod(env, clazz, "geolocationPermissionsHidePrompt", "()V");
+ m_javaGlue->m_getDeviceMotionService = GetJMethod(env, clazz, "getDeviceMotionService", "()Landroid/webkit/DeviceMotionService;");
+ m_javaGlue->m_getDeviceOrientationService = GetJMethod(env, clazz, "getDeviceOrientationService", "()Landroid/webkit/DeviceOrientationService;");
+ m_javaGlue->m_addMessageToConsole = GetJMethod(env, clazz, "addMessageToConsole", "(Ljava/lang/String;ILjava/lang/String;I)V");
+ m_javaGlue->m_formDidBlur = GetJMethod(env, clazz, "formDidBlur", "(I)V");
+ m_javaGlue->m_getPluginClass = GetJMethod(env, clazz, "getPluginClass", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Class;");
+ m_javaGlue->m_showFullScreenPlugin = GetJMethod(env, clazz, "showFullScreenPlugin", "(Landroid/webkit/ViewManager$ChildView;I)V");
+ m_javaGlue->m_hideFullScreenPlugin = GetJMethod(env, clazz, "hideFullScreenPlugin", "()V");
+ m_javaGlue->m_createSurface = GetJMethod(env, clazz, "createSurface", "(Landroid/view/View;)Landroid/webkit/ViewManager$ChildView;");
+ m_javaGlue->m_addSurface = GetJMethod(env, clazz, "addSurface", "(Landroid/view/View;IIII)Landroid/webkit/ViewManager$ChildView;");
+ m_javaGlue->m_updateSurface = GetJMethod(env, clazz, "updateSurface", "(Landroid/webkit/ViewManager$ChildView;IIII)V");
+ m_javaGlue->m_destroySurface = GetJMethod(env, clazz, "destroySurface", "(Landroid/webkit/ViewManager$ChildView;)V");
+ m_javaGlue->m_getContext = GetJMethod(env, clazz, "getContext", "()Landroid/content/Context;");
+ m_javaGlue->m_keepScreenOn = GetJMethod(env, clazz, "keepScreenOn", "(Z)V");
+ m_javaGlue->m_sendFindAgain = GetJMethod(env, clazz, "sendFindAgain", "()V");
+ m_javaGlue->m_showRect = GetJMethod(env, clazz, "showRect", "(IIIIIIFFFF)V");
+ m_javaGlue->m_centerFitRect = GetJMethod(env, clazz, "centerFitRect", "(IIII)V");
+ m_javaGlue->m_setScrollbarModes = GetJMethod(env, clazz, "setScrollbarModes", "(II)V");
+ m_javaGlue->m_setInstallableWebApp = GetJMethod(env, clazz, "setInstallableWebApp", "()V");
+#if ENABLE(VIDEO)
+ m_javaGlue->m_enterFullscreenForVideoLayer = GetJMethod(env, clazz, "enterFullscreenForVideoLayer", "(ILjava/lang/String;)V");
+#endif
+ m_javaGlue->m_setWebTextViewAutoFillable = GetJMethod(env, clazz, "setWebTextViewAutoFillable", "(ILjava/lang/String;)V");
+ m_javaGlue->m_selectAt = GetJMethod(env, clazz, "selectAt", "(II)V");
+ env->DeleteLocalRef(clazz);
+
+ env->SetIntField(javaWebViewCore, gWebViewCoreFields.m_nativeClass, (jint)this);
+
+ m_scrollOffsetX = m_scrollOffsetY = 0;
+
+ PageGroup::setShouldTrackVisitedLinks(true);
+
+ reset(true);
+
+ MemoryUsage::setLowMemoryUsageMb(env->GetIntField(javaWebViewCore, gWebViewCoreFields.m_lowMemoryUsageMb));
+ MemoryUsage::setHighMemoryUsageMb(env->GetIntField(javaWebViewCore, gWebViewCoreFields.m_highMemoryUsageMb));
+ MemoryUsage::setHighUsageDeltaMb(env->GetIntField(javaWebViewCore, gWebViewCoreFields.m_highUsageDeltaMb));
+
+ WebViewCore::addInstance(this);
+
+#if USE(CHROME_NETWORK_STACK)
+ AndroidNetworkLibraryImpl::InitWithApplicationContext(env, 0);
+#endif
+}
+
+WebViewCore::~WebViewCore()
+{
+ WebViewCore::removeInstance(this);
+
+ // Release the focused view
+ Release(m_popupReply);
+
+ if (m_javaGlue->m_obj) {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->DeleteWeakGlobalRef(m_javaGlue->m_obj);
+ m_javaGlue->m_obj = 0;
+ }
+ delete m_javaGlue;
+ delete m_frameCacheKit;
+ delete m_navPictureKit;
+}
+
+WebViewCore* WebViewCore::getWebViewCore(const WebCore::FrameView* view)
+{
+ return getWebViewCore(static_cast<const WebCore::ScrollView*>(view));
+}
+
+WebViewCore* WebViewCore::getWebViewCore(const WebCore::ScrollView* view)
+{
+ if (!view)
+ return 0;
+
+ WebFrameView* webFrameView = static_cast<WebFrameView*>(view->platformWidget());
+ if (!webFrameView)
+ return 0;
+ return webFrameView->webViewCore();
+}
+
+void WebViewCore::reset(bool fromConstructor)
+{
+ DBG_SET_LOG("");
+ if (fromConstructor) {
+ m_frameCacheKit = 0;
+ m_navPictureKit = 0;
+ } else {
+ gFrameCacheMutex.lock();
+ delete m_frameCacheKit;
+ delete m_navPictureKit;
+ m_frameCacheKit = 0;
+ m_navPictureKit = 0;
+ gFrameCacheMutex.unlock();
+ }
+
+ m_lastFocused = 0;
+ m_blurringNodePointer = 0;
+ m_lastFocusedBounds = WebCore::IntRect(0,0,0,0);
+ m_focusBoundsChanged = false;
+ m_lastFocusedSelStart = 0;
+ m_lastFocusedSelEnd = 0;
+ clearContent();
+ m_updatedFrameCache = true;
+ m_frameCacheOutOfDate = true;
+ m_skipContentDraw = false;
+ m_findIsUp = false;
+ m_domtree_version = 0;
+ m_check_domtree_version = true;
+ m_progressDone = false;
+ m_hasCursorBounds = false;
+
+ m_scrollOffsetX = 0;
+ m_scrollOffsetY = 0;
+ m_screenWidth = 0;
+ m_screenHeight = 0;
+ m_groupForVisitedLinks = 0;
+ m_currentNodeDomNavigationAxis = 0;
+}
+
+static bool layoutIfNeededRecursive(WebCore::Frame* f)
+{
+ if (!f)
+ return true;
+
+ WebCore::FrameView* v = f->view();
+ if (!v)
+ return true;
+
+ if (v->needsLayout())
+ v->layout(f->tree()->parent());
+
+ WebCore::Frame* child = f->tree()->firstChild();
+ bool success = true;
+ while (child) {
+ success &= layoutIfNeededRecursive(child);
+ child = child->tree()->nextSibling();
+ }
+
+ return success && !v->needsLayout();
+}
+
+CacheBuilder& WebViewCore::cacheBuilder()
+{
+ return FrameLoaderClientAndroid::get(m_mainFrame)->getCacheBuilder();
+}
+
+WebCore::Node* WebViewCore::currentFocus()
+{
+ return cacheBuilder().currentFocus();
+}
+
+void WebViewCore::recordPicture(SkPicture* picture)
+{
+ // if there is no document yet, just return
+ if (!m_mainFrame->document()) {
+ DBG_NAV_LOG("no document");
+ return;
+ }
+ // Call layout to ensure that the contentWidth and contentHeight are correct
+ if (!layoutIfNeededRecursive(m_mainFrame)) {
+ DBG_NAV_LOG("layout failed");
+ return;
+ }
+ // draw into the picture's recording canvas
+ WebCore::FrameView* view = m_mainFrame->view();
+ DBG_NAV_LOGD("view=(w=%d,h=%d)", view->contentsWidth(),
+ view->contentsHeight());
+ SkAutoPictureRecord arp(picture, view->contentsWidth(),
+ view->contentsHeight(), PICT_RECORD_FLAGS);
+ SkAutoMemoryUsageProbe mup(__FUNCTION__);
+
+ // Copy m_buttons so we can pass it to our graphics context.
+ gButtonMutex.lock();
+ WTF::Vector<Container> buttons(m_buttons);
+ gButtonMutex.unlock();
+
+ WebCore::PlatformGraphicsContext pgc(arp.getRecordingCanvas(), &buttons);
+ WebCore::GraphicsContext gc(&pgc);
+ view->platformWidget()->draw(&gc, WebCore::IntRect(0, 0,
+ view->contentsWidth(), view->contentsHeight()));
+
+ gButtonMutex.lock();
+ updateButtonList(&buttons);
+ gButtonMutex.unlock();
+}
+
+void WebViewCore::recordPictureSet(PictureSet* content)
+{
+ // if there is no document yet, just return
+ if (!m_mainFrame->document()) {
+ DBG_SET_LOG("!m_mainFrame->document()");
+ return;
+ }
+ // If there is a pending style recalculation, just return.
+ if (m_mainFrame->document()->isPendingStyleRecalc()) {
+ LOGW("recordPictureSet: pending style recalc, ignoring.");
+ return;
+ }
+ if (m_addInval.isEmpty()) {
+ DBG_SET_LOG("m_addInval.isEmpty()");
+ return;
+ }
+ // Call layout to ensure that the contentWidth and contentHeight are correct
+ // it's fine for layout to gather invalidates, but defeat sending a message
+ // back to java to call webkitDraw, since we're already in the middle of
+ // doing that
+ m_skipContentDraw = true;
+ bool success = layoutIfNeededRecursive(m_mainFrame);
+ m_skipContentDraw = false;
+
+ // We may be mid-layout and thus cannot draw.
+ if (!success)
+ return;
+
+ { // collect WebViewCoreRecordTimeCounter after layoutIfNeededRecursive
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreRecordTimeCounter);
+#endif
+
+ // if the webkit page dimensions changed, discard the pictureset and redraw.
+ WebCore::FrameView* view = m_mainFrame->view();
+ int width = view->contentsWidth();
+ int height = view->contentsHeight();
+
+ // Use the contents width and height as a starting point.
+ SkIRect contentRect;
+ contentRect.set(0, 0, width, height);
+ SkIRect total(contentRect);
+
+ // Traverse all the frames and add their sizes if they are in the visible
+ // rectangle.
+ for (WebCore::Frame* frame = m_mainFrame->tree()->traverseNext(); frame;
+ frame = frame->tree()->traverseNext()) {
+ // If the frame doesn't have an owner then it is the top frame and the
+ // view size is the frame size.
+ WebCore::RenderPart* owner = frame->ownerRenderer();
+ if (owner && owner->style()->visibility() == VISIBLE) {
+ int x = owner->x();
+ int y = owner->y();
+
+ // Traverse the tree up to the parent to find the absolute position
+ // of this frame.
+ WebCore::Frame* parent = frame->tree()->parent();
+ while (parent) {
+ WebCore::RenderPart* parentOwner = parent->ownerRenderer();
+ if (parentOwner) {
+ x += parentOwner->x();
+ y += parentOwner->y();
+ }
+ parent = parent->tree()->parent();
+ }
+ // Use the owner dimensions so that padding and border are
+ // included.
+ int right = x + owner->width();
+ int bottom = y + owner->height();
+ SkIRect frameRect = {x, y, right, bottom};
+ // Ignore a width or height that is smaller than 1. Some iframes
+ // have small dimensions in order to be hidden. The iframe
+ // expansion code does not expand in that case so we should ignore
+ // them here.
+ if (frameRect.width() > 1 && frameRect.height() > 1
+ && SkIRect::Intersects(total, frameRect))
+ total.join(x, y, right, bottom);
+ }
+ }
+
+ // If the new total is larger than the content, resize the view to include
+ // all the content.
+ if (!contentRect.contains(total)) {
+ // Resize the view to change the overflow clip.
+ view->resize(total.fRight, total.fBottom);
+
+ // We have to force a layout in order for the clip to change.
+ m_mainFrame->contentRenderer()->setNeedsLayoutAndPrefWidthsRecalc();
+ view->forceLayout();
+
+ // Relayout similar to above
+ m_skipContentDraw = true;
+ bool success = layoutIfNeededRecursive(m_mainFrame);
+ m_skipContentDraw = false;
+ if (!success)
+ return;
+
+ // Set the computed content width
+ width = view->contentsWidth();
+ height = view->contentsHeight();
+ }
+
+ if (cacheBuilder().pictureSetDisabled())
+ content->clear();
+
+ content->checkDimensions(width, height, &m_addInval);
+
+ // The inval region may replace existing pictures. The existing pictures
+ // may have already been split into pieces. If reuseSubdivided() returns
+ // true, the split pieces are the last entries in the picture already. They
+ // are marked as invalid, and are rebuilt by rebuildPictureSet().
+
+ // If the new region doesn't match a set of split pieces, add it to the end.
+ if (!content->reuseSubdivided(m_addInval)) {
+ const SkIRect& inval = m_addInval.getBounds();
+ SkPicture* picture = rebuildPicture(inval);
+ DBG_SET_LOGD("{%d,%d,w=%d,h=%d}", inval.fLeft,
+ inval.fTop, inval.width(), inval.height());
+ content->add(m_addInval, picture, 0, false);
+ SkSafeUnref(picture);
+ }
+ // Remove any pictures already in the set that are obscured by the new one,
+ // and check to see if any already split pieces need to be redrawn.
+ if (content->build())
+ rebuildPictureSet(content);
+ } // WebViewCoreRecordTimeCounter
+ WebCore::Node* oldFocusNode = currentFocus();
+ m_frameCacheOutOfDate = true;
+ WebCore::IntRect oldBounds;
+ int oldSelStart = 0;
+ int oldSelEnd = 0;
+ if (oldFocusNode) {
+ oldBounds = oldFocusNode->getRect();
+ RenderObject* renderer = oldFocusNode->renderer();
+ if (renderer && (renderer->isTextArea() || renderer->isTextField())) {
+ WebCore::RenderTextControl* rtc =
+ static_cast<WebCore::RenderTextControl*>(renderer);
+ oldSelStart = rtc->selectionStart();
+ oldSelEnd = rtc->selectionEnd();
+ }
+ } else
+ oldBounds = WebCore::IntRect(0,0,0,0);
+ unsigned latestVersion = 0;
+ if (m_check_domtree_version) {
+ // as domTreeVersion only increment, we can just check the sum to see
+ // whether we need to update the frame cache
+ for (Frame* frame = m_mainFrame; frame; frame = frame->tree()->traverseNext()) {
+ const Document* doc = frame->document();
+ latestVersion += doc->domTreeVersion() + doc->styleVersion();
+ }
+ }
+ DBG_NAV_LOGD("m_lastFocused=%p oldFocusNode=%p"
+ " m_lastFocusedBounds={%d,%d,%d,%d} oldBounds={%d,%d,%d,%d}"
+ " m_lastFocusedSelection={%d,%d} oldSelection={%d,%d}"
+ " m_check_domtree_version=%s latestVersion=%d m_domtree_version=%d",
+ m_lastFocused, oldFocusNode,
+ m_lastFocusedBounds.x(), m_lastFocusedBounds.y(),
+ m_lastFocusedBounds.width(), m_lastFocusedBounds.height(),
+ oldBounds.x(), oldBounds.y(), oldBounds.width(), oldBounds.height(),
+ m_lastFocusedSelStart, m_lastFocusedSelEnd, oldSelStart, oldSelEnd,
+ m_check_domtree_version ? "true" : "false",
+ latestVersion, m_domtree_version);
+ if (m_lastFocused == oldFocusNode && m_lastFocusedBounds == oldBounds
+ && m_lastFocusedSelStart == oldSelStart
+ && m_lastFocusedSelEnd == oldSelEnd
+ && !m_findIsUp
+ && (!m_check_domtree_version || latestVersion == m_domtree_version))
+ {
+ return;
+ }
+ m_focusBoundsChanged |= m_lastFocused == oldFocusNode
+ && m_lastFocusedBounds != oldBounds;
+ m_lastFocused = oldFocusNode;
+ m_lastFocusedBounds = oldBounds;
+ m_lastFocusedSelStart = oldSelStart;
+ m_lastFocusedSelEnd = oldSelEnd;
+ m_domtree_version = latestVersion;
+ DBG_NAV_LOG("call updateFrameCache");
+ updateFrameCache();
+ if (m_findIsUp) {
+ LOG_ASSERT(m_javaGlue->m_obj,
+ "A Java widget was not associated with this view bridge!");
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_sendFindAgain);
+ checkException(env);
+ }
+}
+
+void WebViewCore::updateButtonList(WTF::Vector<Container>* buttons)
+{
+ // All the entries in buttons are either updates of previous entries in
+ // m_buttons or they need to be added to it.
+ Container* end = buttons->end();
+ for (Container* updatedContainer = buttons->begin();
+ updatedContainer != end; updatedContainer++) {
+ bool updated = false;
+ // Search for a previous entry that references the same node as our new
+ // data
+ Container* lastPossibleMatch = m_buttons.end();
+ for (Container* possibleMatch = m_buttons.begin();
+ possibleMatch != lastPossibleMatch; possibleMatch++) {
+ if (updatedContainer->matches(possibleMatch->node())) {
+ // Update our record, and skip to the next one.
+ possibleMatch->setRect(updatedContainer->rect());
+ updated = true;
+ break;
+ }
+ }
+ if (!updated) {
+ // This is a brand new button, so append it to m_buttons
+ m_buttons.append(*updatedContainer);
+ }
+ }
+ size_t i = 0;
+ // count will decrease each time one is removed, so check count each time.
+ while (i < m_buttons.size()) {
+ if (m_buttons[i].canBeRemoved()) {
+ m_buttons[i] = m_buttons.last();
+ m_buttons.removeLast();
+ } else {
+ i++;
+ }
+ }
+}
+
+// note: updateCursorBounds is called directly by the WebView thread
+// This needs to be called each time we call CachedRoot::setCursor() with
+// non-null CachedNode/CachedFrame, since otherwise the WebViewCore's data
+// about the cursor is incorrect. When we call setCursor(0,0), we need
+// to set hasCursorBounds to false.
+void WebViewCore::updateCursorBounds(const CachedRoot* root,
+ const CachedFrame* cachedFrame, const CachedNode* cachedNode)
+{
+ LOG_ASSERT(root, "updateCursorBounds: root cannot be null");
+ LOG_ASSERT(cachedNode, "updateCursorBounds: cachedNode cannot be null");
+ LOG_ASSERT(cachedFrame, "updateCursorBounds: cachedFrame cannot be null");
+ gCursorBoundsMutex.lock();
+ m_hasCursorBounds = !cachedNode->isHidden();
+ // If m_hasCursorBounds is false, we never look at the other
+ // values, so do not bother setting them.
+ if (m_hasCursorBounds) {
+ WebCore::IntRect bounds = cachedNode->bounds(cachedFrame);
+ if (m_cursorBounds != bounds)
+ DBG_NAV_LOGD("new cursor bounds=(%d,%d,w=%d,h=%d)",
+ bounds.x(), bounds.y(), bounds.width(), bounds.height());
+ m_cursorBounds = bounds;
+ m_cursorHitBounds = cachedNode->hitBounds(cachedFrame);
+ m_cursorFrame = cachedFrame->framePointer();
+ root->getSimulatedMousePosition(&m_cursorLocation);
+ m_cursorNode = cachedNode->nodePointer();
+ }
+ gCursorBoundsMutex.unlock();
+}
+
+void WebViewCore::clearContent()
+{
+ DBG_SET_LOG("");
+ m_content.clear();
+ m_addInval.setEmpty();
+ m_rebuildInval.setEmpty();
+}
+
+bool WebViewCore::focusBoundsChanged()
+{
+ bool result = m_focusBoundsChanged;
+ m_focusBoundsChanged = false;
+ return result;
+}
+
+SkPicture* WebViewCore::rebuildPicture(const SkIRect& inval)
+{
+ WebCore::FrameView* view = m_mainFrame->view();
+ int width = view->contentsWidth();
+ int height = view->contentsHeight();
+ SkPicture* picture = new SkPicture();
+ SkAutoPictureRecord arp(picture, width, height, PICT_RECORD_FLAGS);
+ SkAutoMemoryUsageProbe mup(__FUNCTION__);
+ SkCanvas* recordingCanvas = arp.getRecordingCanvas();
+
+ gButtonMutex.lock();
+ WTF::Vector<Container> buttons(m_buttons);
+ gButtonMutex.unlock();
+
+ WebCore::PlatformGraphicsContext pgc(recordingCanvas, &buttons);
+ WebCore::GraphicsContext gc(&pgc);
+ recordingCanvas->translate(-inval.fLeft, -inval.fTop);
+ recordingCanvas->save();
+ view->platformWidget()->draw(&gc, WebCore::IntRect(inval.fLeft,
+ inval.fTop, inval.width(), inval.height()));
+ m_rebuildInval.op(inval, SkRegion::kUnion_Op);
+ DBG_SET_LOGD("m_rebuildInval={%d,%d,r=%d,b=%d}",
+ m_rebuildInval.getBounds().fLeft, m_rebuildInval.getBounds().fTop,
+ m_rebuildInval.getBounds().fRight, m_rebuildInval.getBounds().fBottom);
+
+ gButtonMutex.lock();
+ updateButtonList(&buttons);
+ gButtonMutex.unlock();
+
+ return picture;
+}
+
+void WebViewCore::rebuildPictureSet(PictureSet* pictureSet)
+{
+ WebCore::FrameView* view = m_mainFrame->view();
+ size_t size = pictureSet->size();
+ for (size_t index = 0; index < size; index++) {
+ if (pictureSet->upToDate(index))
+ continue;
+ const SkIRect& inval = pictureSet->bounds(index);
+ DBG_SET_LOGD("pictSet=%p [%d] {%d,%d,w=%d,h=%d}", pictureSet, index,
+ inval.fLeft, inval.fTop, inval.width(), inval.height());
+ pictureSet->setPicture(index, rebuildPicture(inval));
+ }
+ pictureSet->validate(__FUNCTION__);
+}
+
+BaseLayerAndroid* WebViewCore::createBaseLayer()
+{
+ BaseLayerAndroid* base = new BaseLayerAndroid();
+ base->setContent(m_content);
+
+ bool layoutSucceeded = layoutIfNeededRecursive(m_mainFrame);
+ // Layout only fails if called during a layout.
+ LOG_ASSERT(layoutSucceeded, "Can never be called recursively");
+
+#if USE(ACCELERATED_COMPOSITING)
+ // We set the background color
+ if (m_mainFrame && m_mainFrame->document()
+ && m_mainFrame->document()->body()) {
+ Document* document = m_mainFrame->document();
+ RefPtr<RenderStyle> style = document->styleForElementIgnoringPendingStylesheets(document->body());
+ if (style->hasBackground()) {
+ Color color = style->visitedDependentColor(CSSPropertyBackgroundColor);
+ if (color.isValid() && color.alpha() > 0)
+ base->setBackgroundColor(color);
+ }
+ }
+
+ // We update the layers
+ ChromeClientAndroid* chromeC = static_cast<ChromeClientAndroid*>(m_mainFrame->page()->chrome()->client());
+ GraphicsLayerAndroid* root = static_cast<GraphicsLayerAndroid*>(chromeC->layersSync());
+ if (root) {
+ root->notifyClientAnimationStarted();
+ LayerAndroid* copyLayer = new LayerAndroid(*root->contentLayer());
+ base->addChild(copyLayer);
+ copyLayer->unref();
+ }
+#endif
+
+ return base;
+}
+
+BaseLayerAndroid* WebViewCore::recordContent(SkRegion* region, SkIPoint* point)
+{
+ DBG_SET_LOG("start");
+ float progress = (float) m_mainFrame->page()->progress()->estimatedProgress();
+ m_progressDone = progress <= 0.0f || progress >= 1.0f;
+ recordPictureSet(&m_content);
+ if (!m_progressDone && m_content.isEmpty()) {
+ DBG_SET_LOGD("empty (progress=%g)", progress);
+ return 0;
+ }
+ region->set(m_addInval);
+ m_addInval.setEmpty();
+ region->op(m_rebuildInval, SkRegion::kUnion_Op);
+ m_rebuildInval.setEmpty();
+ point->fX = m_content.width();
+ point->fY = m_content.height();
+ DBG_SET_LOGD("region={%d,%d,r=%d,b=%d}", region->getBounds().fLeft,
+ region->getBounds().fTop, region->getBounds().fRight,
+ region->getBounds().fBottom);
+ DBG_SET_LOG("end");
+
+ return createBaseLayer();
+}
+
+void WebViewCore::splitContent(PictureSet* content)
+{
+ bool layoutSucceeded = layoutIfNeededRecursive(m_mainFrame);
+ LOG_ASSERT(layoutSucceeded, "Can never be called recursively");
+ content->split(&m_content);
+ rebuildPictureSet(&m_content);
+ content->set(m_content);
+}
+
+void WebViewCore::scrollTo(int x, int y, bool animate)
+{
+ LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
+
+// LOGD("WebViewCore::scrollTo(%d %d)\n", x, y);
+
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_scrollTo,
+ x, y, animate, false);
+ checkException(env);
+}
+
+void WebViewCore::sendNotifyProgressFinished()
+{
+ LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_sendNotifyProgressFinished);
+ checkException(env);
+}
+
+void WebViewCore::viewInvalidate(const WebCore::IntRect& rect)
+{
+ LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_sendViewInvalidate,
+ rect.x(), rect.y(), rect.right(), rect.bottom());
+ checkException(env);
+}
+
+void WebViewCore::contentDraw()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_contentDraw);
+ checkException(env);
+}
+
+void WebViewCore::layersDraw()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_layersDraw);
+ checkException(env);
+}
+
+void WebViewCore::contentInvalidate(const WebCore::IntRect &r)
+{
+ DBG_SET_LOGD("rect={%d,%d,w=%d,h=%d}", r.x(), r.y(), r.width(), r.height());
+ SkIRect rect(r);
+ if (!rect.intersect(0, 0, INT_MAX, INT_MAX))
+ return;
+ m_addInval.op(rect, SkRegion::kUnion_Op);
+ DBG_SET_LOGD("m_addInval={%d,%d,r=%d,b=%d}",
+ m_addInval.getBounds().fLeft, m_addInval.getBounds().fTop,
+ m_addInval.getBounds().fRight, m_addInval.getBounds().fBottom);
+ if (!m_skipContentDraw)
+ contentDraw();
+}
+
+void WebViewCore::contentInvalidateAll()
+{
+ WebCore::FrameView* view = m_mainFrame->view();
+ contentInvalidate(WebCore::IntRect(0, 0,
+ view->contentsWidth(), view->contentsHeight()));
+}
+
+void WebViewCore::offInvalidate(const WebCore::IntRect &r)
+{
+ // FIXME: these invalidates are offscreen, and can be throttled or
+ // deferred until the area is visible. For now, treat them as
+ // regular invals so that drawing happens (inefficiently) for now.
+ contentInvalidate(r);
+}
+
+static int pin_pos(int x, int width, int targetWidth)
+{
+ if (x + width > targetWidth)
+ x = targetWidth - width;
+ if (x < 0)
+ x = 0;
+ return x;
+}
+
+void WebViewCore::didFirstLayout()
+{
+ DEBUG_NAV_UI_LOGD("%s", __FUNCTION__);
+ LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
+
+ WebCore::FrameLoader* loader = m_mainFrame->loader();
+ const WebCore::KURL& url = loader->url();
+ if (url.isEmpty())
+ return;
+ LOGV("::WebCore:: didFirstLayout %s", url.string().ascii().data());
+
+ WebCore::FrameLoadType loadType = loader->loadType();
+
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_didFirstLayout,
+ loadType == WebCore::FrameLoadTypeStandard
+ // When redirect with locked history, we would like to reset the
+ // scale factor. This is important for www.yahoo.com as it is
+ // redirected to www.yahoo.com/?rs=1 on load.
+ || loadType == WebCore::FrameLoadTypeRedirectWithLockedBackForwardList);
+ checkException(env);
+
+ DBG_NAV_LOG("call updateFrameCache");
+ m_check_domtree_version = false;
+ updateFrameCache();
+ m_history.setDidFirstLayout(true);
+}
+
+void WebViewCore::updateViewport()
+{
+ DEBUG_NAV_UI_LOGD("%s", __FUNCTION__);
+ LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
+
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_updateViewport);
+ checkException(env);
+}
+
+void WebViewCore::restoreScale(float scale, float textWrapScale)
+{
+ DEBUG_NAV_UI_LOGD("%s", __FUNCTION__);
+ LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
+
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_restoreScale, scale, textWrapScale);
+ checkException(env);
+}
+
+void WebViewCore::needTouchEvents(bool need)
+{
+ DEBUG_NAV_UI_LOGD("%s", __FUNCTION__);
+ LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
+
+#if ENABLE(TOUCH_EVENTS)
+ if (m_forwardingTouchEvents == need)
+ return;
+
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_needTouchEvents, need);
+ checkException(env);
+
+ m_forwardingTouchEvents = need;
+#endif
+}
+
+void WebViewCore::requestKeyboardWithSelection(const WebCore::Node* node,
+ int selStart, int selEnd)
+{
+ DEBUG_NAV_UI_LOGD("%s", __FUNCTION__);
+ LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
+
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_requestKeyboardWithSelection,
+ reinterpret_cast<int>(node), selStart, selEnd, m_textGeneration);
+ checkException(env);
+}
+
+void WebViewCore::requestKeyboard(bool showKeyboard)
+{
+ DEBUG_NAV_UI_LOGD("%s", __FUNCTION__);
+ LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
+
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_requestKeyboard, showKeyboard);
+ checkException(env);
+}
+
+void WebViewCore::notifyProgressFinished()
+{
+ m_check_domtree_version = true;
+ sendNotifyProgressFinished();
+}
+
+void WebViewCore::doMaxScroll(CacheBuilder::Direction dir)
+{
+ int dx = 0, dy = 0;
+
+ switch (dir) {
+ case CacheBuilder::LEFT:
+ dx = -m_maxXScroll;
+ break;
+ case CacheBuilder::UP:
+ dy = -m_maxYScroll;
+ break;
+ case CacheBuilder::RIGHT:
+ dx = m_maxXScroll;
+ break;
+ case CacheBuilder::DOWN:
+ dy = m_maxYScroll;
+ break;
+ case CacheBuilder::UNINITIALIZED:
+ default:
+ LOG_ASSERT(0, "unexpected focus selector");
+ }
+ WebCore::FrameView* view = m_mainFrame->view();
+ this->scrollTo(view->scrollX() + dx, view->scrollY() + dy, true);
+}
+
+void WebViewCore::setScrollOffset(int moveGeneration, bool sendScrollEvent, int dx, int dy)
+{
+ DBG_NAV_LOGD("{%d,%d} m_scrollOffset=(%d,%d), sendScrollEvent=%d", dx, dy,
+ m_scrollOffsetX, m_scrollOffsetY, sendScrollEvent);
+ if (m_scrollOffsetX != dx || m_scrollOffsetY != dy) {
+ m_scrollOffsetX = dx;
+ m_scrollOffsetY = dy;
+ // The visible rect is located within our coordinate space so it
+ // contains the actual scroll position. Setting the location makes hit
+ // testing work correctly.
+ m_mainFrame->view()->platformWidget()->setLocation(m_scrollOffsetX,
+ m_scrollOffsetY);
+ if (sendScrollEvent) {
+ m_mainFrame->eventHandler()->sendScrollEvent();
+
+ // Only update history position if it's user scrolled.
+ // Update history item to reflect the new scroll position.
+ // This also helps save the history information when the browser goes to
+ // background, so scroll position will be restored if browser gets
+ // killed while in background.
+ WebCore::HistoryController* history = m_mainFrame->loader()->history();
+ // Because the history item saving could be heavy for large sites and
+ // scrolling can generate lots of small scroll offset, the following code
+ // reduces the saving frequency.
+ static const int MIN_SCROLL_DIFF = 32;
+ if (history->currentItem()) {
+ WebCore::IntPoint currentPoint = history->currentItem()->scrollPoint();
+ if (std::abs(currentPoint.x() - dx) >= MIN_SCROLL_DIFF ||
+ std::abs(currentPoint.y() - dy) >= MIN_SCROLL_DIFF) {
+ history->saveScrollPositionAndViewStateToItem(history->currentItem());
+ }
+ }
+ }
+
+ // update the currently visible screen
+ sendPluginVisibleScreen();
+ }
+ gCursorBoundsMutex.lock();
+ bool hasCursorBounds = m_hasCursorBounds;
+ Frame* frame = (Frame*) m_cursorFrame;
+ IntPoint location = m_cursorLocation;
+ gCursorBoundsMutex.unlock();
+ if (!hasCursorBounds)
+ return;
+ moveMouseIfLatest(moveGeneration, frame, location.x(), location.y());
+}
+
+void WebViewCore::setGlobalBounds(int x, int y, int h, int v)
+{
+ DBG_NAV_LOGD("{%d,%d}", x, y);
+ m_mainFrame->view()->platformWidget()->setWindowBounds(x, y, h, v);
+}
+
+void WebViewCore::setSizeScreenWidthAndScale(int width, int height,
+ int textWrapWidth, float scale, int screenWidth, int screenHeight,
+ int anchorX, int anchorY, bool ignoreHeight)
+{
+ WebCoreViewBridge* window = m_mainFrame->view()->platformWidget();
+ int ow = window->width();
+ int oh = window->height();
+ int osw = m_screenWidth;
+ int osh = m_screenHeight;
+ int otw = m_textWrapWidth;
+ float oldScale = m_scale;
+ DBG_NAV_LOGD("old:(w=%d,h=%d,sw=%d,scale=%g) new:(w=%d,h=%d,sw=%d,scale=%g)",
+ ow, oh, osw, m_scale, width, height, screenWidth, scale);
+ m_screenWidth = screenWidth;
+ m_screenHeight = screenHeight;
+ m_textWrapWidth = textWrapWidth;
+ if (scale >= 0) // negative means keep the current scale
+ m_scale = scale;
+ m_maxXScroll = screenWidth >> 2;
+ m_maxYScroll = m_maxXScroll * height / width;
+ // Don't reflow if the diff is small.
+ const bool reflow = otw && textWrapWidth &&
+ ((float) abs(otw - textWrapWidth) / textWrapWidth) >= 0.01f;
+
+ // When the screen size change, fixed positioned element should be updated.
+ // This is supposed to be light weighted operation without a full layout.
+ if (osh != screenHeight || osw != screenWidth)
+ m_mainFrame->view()->updatePositionedObjects();
+
+ if (ow != width || (!ignoreHeight && oh != height) || reflow) {
+ WebCore::RenderObject *r = m_mainFrame->contentRenderer();
+ DBG_NAV_LOGD("renderer=%p view=(w=%d,h=%d)", r,
+ screenWidth, screenHeight);
+ if (r) {
+ WebCore::IntPoint anchorPoint = WebCore::IntPoint(anchorX, anchorY);
+ DBG_NAV_LOGD("anchorX=%d anchorY=%d", anchorX, anchorY);
+ RefPtr<WebCore::Node> node;
+ WebCore::IntRect bounds;
+ WebCore::IntPoint offset;
+ // If the text wrap changed, it is probably zoom change or
+ // orientation change. Try to keep the anchor at the same place.
+ if (otw && textWrapWidth && otw != textWrapWidth &&
+ (anchorX != 0 || anchorY != 0)) {
+ WebCore::HitTestResult hitTestResult =
+ m_mainFrame->eventHandler()->hitTestResultAtPoint(
+ anchorPoint, false);
+ node = hitTestResult.innerNode();
+ }
+ if (node) {
+ bounds = node->getRect();
+ DBG_NAV_LOGD("ob:(x=%d,y=%d,w=%d,h=%d)",
+ bounds.x(), bounds.y(), bounds.width(), bounds.height());
+ // sites like nytimes.com insert a non-standard tag <nyt_text>
+ // in the html. If it is the HitTestResult, it may have zero
+ // width and height. In this case, use its parent node.
+ if (bounds.width() == 0) {
+ node = node->parentOrHostNode();
+ if (node) {
+ bounds = node->getRect();
+ DBG_NAV_LOGD("found a zero width node and use its parent, whose ob:(x=%d,y=%d,w=%d,h=%d)",
+ bounds.x(), bounds.y(), bounds.width(), bounds.height());
+ }
+ }
+ }
+
+ // Set the size after finding the old anchor point as
+ // hitTestResultAtPoint causes a layout.
+ window->setSize(width, height);
+ window->setVisibleSize(screenWidth, screenHeight);
+ if (width != screenWidth) {
+ m_mainFrame->view()->setUseFixedLayout(true);
+ m_mainFrame->view()->setFixedLayoutSize(IntSize(width, height));
+ } else {
+ m_mainFrame->view()->setUseFixedLayout(false);
+ }
+ r->setNeedsLayoutAndPrefWidthsRecalc();
+ m_mainFrame->view()->forceLayout();
+
+ // scroll to restore current screen center
+ if (node) {
+ const WebCore::IntRect& newBounds = node->getRect();
+ DBG_NAV_LOGD("nb:(x=%d,y=%d,w=%d,"
+ "h=%d)", newBounds.x(), newBounds.y(),
+ newBounds.width(), newBounds.height());
+ if ((osw && osh && bounds.width() && bounds.height())
+ && (bounds != newBounds)) {
+ WebCore::FrameView* view = m_mainFrame->view();
+ // force left align if width is not changed while height changed.
+ // the anchorPoint is probably at some white space in the node
+ // which is affected by text wrap around the screen width.
+ const bool leftAlign = (otw != textWrapWidth)
+ && (bounds.width() == newBounds.width())
+ && (bounds.height() != newBounds.height());
+ const float xPercentInDoc =
+ leftAlign ? 0.0 : (float) (anchorX - bounds.x()) / bounds.width();
+ const float xPercentInView =
+ leftAlign ? 0.0 : (float) (anchorX - m_scrollOffsetX) / osw;
+ const float yPercentInDoc = (float) (anchorY - bounds.y()) / bounds.height();
+ const float yPercentInView = (float) (anchorY - m_scrollOffsetY) / osh;
+ showRect(newBounds.x(), newBounds.y(), newBounds.width(),
+ newBounds.height(), view->contentsWidth(),
+ view->contentsHeight(),
+ xPercentInDoc, xPercentInView,
+ yPercentInDoc, yPercentInView);
+ }
+ }
+ }
+ } else {
+ window->setSize(width, height);
+ window->setVisibleSize(screenWidth, screenHeight);
+ m_mainFrame->view()->resize(width, height);
+ if (width != screenWidth) {
+ m_mainFrame->view()->setUseFixedLayout(true);
+ m_mainFrame->view()->setFixedLayoutSize(IntSize(width, height));
+ } else {
+ m_mainFrame->view()->setUseFixedLayout(false);
+ }
+ }
+
+ // update the currently visible screen as perceived by the plugin
+ sendPluginVisibleScreen();
+}
+
+void WebViewCore::dumpDomTree(bool useFile)
+{
+#ifdef ANDROID_DOM_LOGGING
+ if (useFile)
+ gDomTreeFile = fopen(DOM_TREE_LOG_FILE, "w");
+ m_mainFrame->document()->showTreeForThis();
+ if (gDomTreeFile) {
+ fclose(gDomTreeFile);
+ gDomTreeFile = 0;
+ }
+#endif
+}
+
+void WebViewCore::dumpRenderTree(bool useFile)
+{
+#ifdef ANDROID_DOM_LOGGING
+ WTF::CString renderDump = WebCore::externalRepresentation(m_mainFrame).utf8();
+ const char* data = renderDump.data();
+ if (useFile) {
+ gRenderTreeFile = fopen(RENDER_TREE_LOG_FILE, "w");
+ DUMP_RENDER_LOGD("%s", data);
+ fclose(gRenderTreeFile);
+ gRenderTreeFile = 0;
+ } else {
+ // adb log can only output 1024 characters, so write out line by line.
+ // exclude '\n' as adb log adds it for each output.
+ int length = renderDump.length();
+ for (int i = 0, last = 0; i < length; i++) {
+ if (data[i] == '\n') {
+ if (i != last)
+ DUMP_RENDER_LOGD("%.*s", (i - last), &(data[last]));
+ last = i + 1;
+ }
+ }
+ }
+#endif
+}
+
+void WebViewCore::dumpNavTree()
+{
+#if DUMP_NAV_CACHE
+ cacheBuilder().mDebug.print();
+#endif
+}
+
+HTMLElement* WebViewCore::retrieveElement(int x, int y,
+ const QualifiedName& tagName)
+{
+ HitTestResult hitTestResult = m_mainFrame->eventHandler()
+ ->hitTestResultAtPoint(IntPoint(x, y), false, false,
+ DontHitTestScrollbars, HitTestRequest::Active | HitTestRequest::ReadOnly,
+ IntSize(1, 1));
+ if (!hitTestResult.innerNode() || !hitTestResult.innerNode()->inDocument()) {
+ LOGE("Should not happen: no in document Node found");
+ return 0;
+ }
+ const ListHashSet<RefPtr<Node> >& list = hitTestResult.rectBasedTestResult();
+ if (list.isEmpty()) {
+ LOGE("Should not happen: no rect-based-test nodes found");
+ return 0;
+ }
+ Node* node = hitTestResult.innerNode();
+ Node* element = node;
+ while (element && (!element->isElementNode()
+ || !element->hasTagName(tagName))) {
+ element = element->parentNode();
+ }
+ DBG_NAV_LOGD("node=%p element=%p x=%d y=%d nodeName=%s tagName=%s", node,
+ element, x, y, node->nodeName().utf8().data(),
+ element ? ((Element*) element)->tagName().utf8().data() : "<none>");
+ return static_cast<WebCore::HTMLElement*>(element);
+}
+
+HTMLAnchorElement* WebViewCore::retrieveAnchorElement(int x, int y)
+{
+ return static_cast<HTMLAnchorElement*>
+ (retrieveElement(x, y, HTMLNames::aTag));
+}
+
+HTMLImageElement* WebViewCore::retrieveImageElement(int x, int y)
+{
+ return static_cast<HTMLImageElement*>
+ (retrieveElement(x, y, HTMLNames::imgTag));
+}
+
+WTF::String WebViewCore::retrieveHref(int x, int y)
+{
+ WebCore::HTMLAnchorElement* anchor = retrieveAnchorElement(x, y);
+ return anchor ? anchor->href() : WTF::String();
+}
+
+WTF::String WebViewCore::retrieveAnchorText(int x, int y)
+{
+ WebCore::HTMLAnchorElement* anchor = retrieveAnchorElement(x, y);
+ return anchor ? anchor->text() : WTF::String();
+}
+
+WTF::String WebViewCore::retrieveImageSource(int x, int y)
+{
+ HTMLImageElement* image = retrieveImageElement(x, y);
+ return image ? image->src().string() : WTF::String();
+}
+
+WTF::String WebViewCore::requestLabel(WebCore::Frame* frame,
+ WebCore::Node* node)
+{
+ if (node && CacheBuilder::validNode(m_mainFrame, frame, node)) {
+ RefPtr<WebCore::NodeList> list = node->document()->getElementsByTagName("label");
+ unsigned length = list->length();
+ for (unsigned i = 0; i < length; i++) {
+ WebCore::HTMLLabelElement* label = static_cast<WebCore::HTMLLabelElement*>(
+ list->item(i));
+ if (label->control() == node) {
+ Node* node = label;
+ String result;
+ while ((node = node->traverseNextNode(label))) {
+ if (node->isTextNode()) {
+ Text* textNode = static_cast<Text*>(node);
+ result += textNode->dataImpl();
+ }
+ }
+ return result;
+ }
+ }
+ }
+ return WTF::String();
+}
+
+static bool isContentEditable(const WebCore::Node* node)
+{
+ if (!node) return false;
+ return node->document()->frame()->selection()->isContentEditable();
+}
+
+// Returns true if the node is a textfield, textarea, or contentEditable
+static bool isTextInput(const WebCore::Node* node)
+{
+ if (isContentEditable(node))
+ return true;
+ if (!node)
+ return false;
+ WebCore::RenderObject* renderer = node->renderer();
+ return renderer && (renderer->isTextField() || renderer->isTextArea());
+}
+
+void WebViewCore::revealSelection()
+{
+ WebCore::Node* focus = currentFocus();
+ if (!focus)
+ return;
+ if (!isTextInput(focus))
+ return;
+ WebCore::Frame* focusedFrame = focus->document()->frame();
+ if (!focusedFrame->page()->focusController()->isActive())
+ return;
+ focusedFrame->selection()->revealSelection(ScrollAlignment::alignToEdgeIfNeeded);
+}
+
+void WebViewCore::updateCacheOnNodeChange()
+{
+ gCursorBoundsMutex.lock();
+ bool hasCursorBounds = m_hasCursorBounds;
+ Frame* frame = (Frame*) m_cursorFrame;
+ Node* node = (Node*) m_cursorNode;
+ IntRect bounds = m_cursorHitBounds;
+ gCursorBoundsMutex.unlock();
+ if (!hasCursorBounds || !node)
+ return;
+ if (CacheBuilder::validNode(m_mainFrame, frame, node)) {
+ RenderObject* renderer = node->renderer();
+ if (renderer && renderer->style()->visibility() != HIDDEN) {
+ IntRect absBox = renderer->absoluteBoundingBoxRect();
+ int globalX, globalY;
+ CacheBuilder::GetGlobalOffset(frame, &globalX, &globalY);
+ absBox.move(globalX, globalY);
+ if (absBox == bounds)
+ return;
+ DBG_NAV_LOGD("absBox=(%d,%d,%d,%d) bounds=(%d,%d,%d,%d)",
+ absBox.x(), absBox.y(), absBox.width(), absBox.height(),
+ bounds.x(), bounds.y(), bounds.width(), bounds.height());
+ }
+ }
+ DBG_NAV_LOGD("updateFrameCache node=%p", node);
+ updateFrameCache();
+}
+
+void WebViewCore::updateFrameCache()
+{
+ if (!m_frameCacheOutOfDate) {
+ DBG_NAV_LOG("!m_frameCacheOutOfDate");
+ return;
+ }
+
+ // If there is a pending style recalculation, do not update the frame cache.
+ // Until the recalculation is complete, there may be internal objects that
+ // are in an inconsistent state (such as font pointers).
+ // In any event, there's not much point to updating the cache while a style
+ // recalculation is pending, since it will simply have to be updated again
+ // once the recalculation is complete.
+ // TODO: Do we need to reschedule an update for after the style is recalculated?
+ if (m_mainFrame && m_mainFrame->document() && m_mainFrame->document()->isPendingStyleRecalc()) {
+ LOGW("updateFrameCache: pending style recalc, ignoring.");
+ return;
+ }
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreBuildNavTimeCounter);
+#endif
+ m_frameCacheOutOfDate = false;
+#if DEBUG_NAV_UI
+ m_now = SkTime::GetMSecs();
+#endif
+ m_temp = new CachedRoot();
+ m_temp->init(m_mainFrame, &m_history);
+#if USE(ACCELERATED_COMPOSITING)
+ GraphicsLayerAndroid* graphicsLayer = graphicsRootLayer();
+ if (graphicsLayer)
+ m_temp->setRootLayer(graphicsLayer->contentLayer());
+#endif
+ CacheBuilder& builder = cacheBuilder();
+ WebCore::Settings* settings = m_mainFrame->page()->settings();
+ builder.allowAllTextDetection();
+#ifdef ANDROID_META_SUPPORT
+ if (settings) {
+ if (!settings->formatDetectionAddress())
+ builder.disallowAddressDetection();
+ if (!settings->formatDetectionEmail())
+ builder.disallowEmailDetection();
+ if (!settings->formatDetectionTelephone())
+ builder.disallowPhoneDetection();
+ }
+#endif
+ builder.buildCache(m_temp);
+ m_tempPict = new SkPicture();
+ recordPicture(m_tempPict);
+ m_temp->setPicture(m_tempPict);
+ m_temp->setTextGeneration(m_textGeneration);
+ WebCoreViewBridge* window = m_mainFrame->view()->platformWidget();
+ m_temp->setVisibleRect(WebCore::IntRect(m_scrollOffsetX,
+ m_scrollOffsetY, window->width(), window->height()));
+ gFrameCacheMutex.lock();
+ delete m_frameCacheKit;
+ delete m_navPictureKit;
+ m_frameCacheKit = m_temp;
+ m_navPictureKit = m_tempPict;
+ m_updatedFrameCache = true;
+#if DEBUG_NAV_UI
+ const CachedNode* cachedFocusNode = m_frameCacheKit->currentFocus();
+ DBG_NAV_LOGD("cachedFocusNode=%d (nodePointer=%p)",
+ cachedFocusNode ? cachedFocusNode->index() : 0,
+ cachedFocusNode ? cachedFocusNode->nodePointer() : 0);
+#endif
+ gFrameCacheMutex.unlock();
+}
+
+void WebViewCore::updateFrameCacheIfLoading()
+{
+ if (!m_check_domtree_version)
+ updateFrameCache();
+}
+
+struct TouchNodeData {
+ Node* mNode;
+ IntRect mBounds;
+};
+
+// get the bounding box of the Node
+static IntRect getAbsoluteBoundingBox(Node* node) {
+ IntRect rect;
+ RenderObject* render = node->renderer();
+ if (render->isRenderInline())
+ rect = toRenderInline(render)->linesVisualOverflowBoundingBox();
+ else if (render->isBox())
+ rect = toRenderBox(render)->visualOverflowRect();
+ else if (render->isText())
+ rect = toRenderText(render)->linesBoundingBox();
+ else
+ LOGE("getAbsoluteBoundingBox failed for node %p, name %s", node, render->renderName());
+ FloatPoint absPos = render->localToAbsolute();
+ rect.move(absPos.x(), absPos.y());
+ return rect;
+}
+
+// get the highlight rectangles for the touch point (x, y) with the slop
+Vector<IntRect> WebViewCore::getTouchHighlightRects(int x, int y, int slop)
+{
+ Vector<IntRect> rects;
+ m_mousePos = IntPoint(x - m_scrollOffsetX, y - m_scrollOffsetY);
+ HitTestResult hitTestResult = m_mainFrame->eventHandler()->hitTestResultAtPoint(IntPoint(x, y),
+ false, false, DontHitTestScrollbars, HitTestRequest::Active | HitTestRequest::ReadOnly, IntSize(slop, slop));
+ if (!hitTestResult.innerNode() || !hitTestResult.innerNode()->inDocument()) {
+ LOGE("Should not happen: no in document Node found");
+ return rects;
+ }
+ const ListHashSet<RefPtr<Node> >& list = hitTestResult.rectBasedTestResult();
+ if (list.isEmpty()) {
+ LOGE("Should not happen: no rect-based-test nodes found");
+ return rects;
+ }
+ Frame* frame = hitTestResult.innerNode()->document()->frame();
+ Vector<TouchNodeData> nodeDataList;
+ ListHashSet<RefPtr<Node> >::const_iterator last = list.end();
+ for (ListHashSet<RefPtr<Node> >::const_iterator it = list.begin(); it != last; ++it) {
+ // TODO: it seems reasonable to not search across the frame. Isn't it?
+ // if the node is not in the same frame as the innerNode, skip it
+ if (it->get()->document()->frame() != frame)
+ continue;
+ // traverse up the tree to find the first node that needs highlight
+ bool found = false;
+ Node* eventNode = it->get();
+ while (eventNode) {
+ RenderObject* render = eventNode->renderer();
+ if (render->isBody() || render->isRenderView())
+ break;
+ if (eventNode->supportsFocus()
+ || eventNode->hasEventListeners(eventNames().clickEvent)
+ || eventNode->hasEventListeners(eventNames().mousedownEvent)
+ || eventNode->hasEventListeners(eventNames().mouseupEvent)) {
+ found = true;
+ break;
+ }
+ // the nodes in the rectBasedTestResult() are ordered based on z-index during hit testing.
+ // so do not search for the eventNode across explicit z-index border.
+ // TODO: this is a hard one to call. z-index is quite complicated as its value only
+ // matters when you compare two RenderLayer in the same hierarchy level. e.g. in
+ // the following example, "b" is on the top as its z level is the highest. even "c"
+ // has 100 as z-index, it is still below "d" as its parent has the same z-index as
+ // "d" and logically before "d". Of course "a" is the lowest in the z level.
+ //
+ // z-index:auto "a"
+ // z-index:2 "b"
+ // z-index:1
+ // z-index:100 "c"
+ // z-index:1 "d"
+ //
+ // If the fat point touches everyone, the order in the list should be "b", "d", "c"
+ // and "a". When we search for the event node for "b", we really don't want "a" as
+ // in the z-order it is behind everything else.
+ if (!render->style()->hasAutoZIndex())
+ break;
+ eventNode = eventNode->parentNode();
+ }
+ // didn't find any eventNode, skip it
+ if (!found)
+ continue;
+ // first quick check whether it is a duplicated node before computing bounding box
+ Vector<TouchNodeData>::const_iterator nlast = nodeDataList.end();
+ for (Vector<TouchNodeData>::const_iterator n = nodeDataList.begin(); n != nlast; ++n) {
+ // found the same node, skip it
+ if (eventNode == n->mNode) {
+ found = false;
+ break;
+ }
+ }
+ if (!found)
+ continue;
+ // next check whether the node is fully covered by or fully covering another node.
+ found = false;
+ IntRect rect = getAbsoluteBoundingBox(eventNode);
+ if (rect.isEmpty()) {
+ // if the node's bounds is empty and it is not a ContainerNode, skip it.
+ if (!eventNode->isContainerNode())
+ continue;
+ // if the node's children are all positioned objects, its bounds can be empty.
+ // Walk through the children to find the bounding box.
+ Node* child = static_cast<const ContainerNode*>(eventNode)->firstChild();
+ while (child) {
+ IntRect childrect;
+ if (child->renderer())
+ childrect = getAbsoluteBoundingBox(child);
+ if (!childrect.isEmpty()) {
+ rect.unite(childrect);
+ child = child->traverseNextSibling(eventNode);
+ } else
+ child = child->traverseNextNode(eventNode);
+ }
+ }
+ for (int i = nodeDataList.size() - 1; i >= 0; i--) {
+ TouchNodeData n = nodeDataList.at(i);
+ // the new node is enclosing an existing node, skip it
+ if (rect.contains(n.mBounds)) {
+ found = true;
+ break;
+ }
+ // the new node is fully inside an existing node, remove the existing node
+ if (n.mBounds.contains(rect))
+ nodeDataList.remove(i);
+ }
+ if (!found) {
+ TouchNodeData newNode;
+ newNode.mNode = eventNode;
+ newNode.mBounds = rect;
+ nodeDataList.append(newNode);
+ }
+ }
+ if (!nodeDataList.size())
+ return rects;
+ // finally select the node with the largest overlap with the fat point
+ TouchNodeData final;
+ final.mNode = 0;
+ IntPoint docPos = frame->view()->windowToContents(m_mousePos);
+ IntRect testRect(docPos.x() - slop, docPos.y() - slop, 2 * slop + 1, 2 * slop + 1);
+ int area = 0;
+ Vector<TouchNodeData>::const_iterator nlast = nodeDataList.end();
+ for (Vector<TouchNodeData>::const_iterator n = nodeDataList.begin(); n != nlast; ++n) {
+ IntRect rect = n->mBounds;
+ rect.intersect(testRect);
+ int a = rect.width() * rect.height();
+ if (a > area) {
+ final = *n;
+ area = a;
+ }
+ }
+ // now get the node's highlight rectangles in the page coordinate system
+ if (final.mNode) {
+ IntPoint frameAdjust;
+ if (frame != m_mainFrame) {
+ frameAdjust = frame->view()->contentsToWindow(IntPoint());
+ frameAdjust.move(m_scrollOffsetX, m_scrollOffsetY);
+ }
+ if (final.mNode->isLink()) {
+ // most of the links are inline instead of box style. So the bounding box is not
+ // a good representation for the highlights. Get the list of rectangles instead.
+ RenderObject* render = final.mNode->renderer();
+ IntPoint offset = roundedIntPoint(render->localToAbsolute());
+ render->absoluteRects(rects, offset.x() + frameAdjust.x(), offset.y() + frameAdjust.y());
+ bool inside = false;
+ int distance = INT_MAX;
+ int newx = x, newy = y;
+ int i = rects.size();
+ while (i--) {
+ if (rects[i].isEmpty()) {
+ rects.remove(i);
+ continue;
+ }
+ // check whether the point (x, y) is inside one of the rectangles.
+ if (inside)
+ continue;
+ if (rects[i].contains(x, y)) {
+ inside = true;
+ continue;
+ }
+ if (x >= rects[i].x() && x < rects[i].right()) {
+ if (y < rects[i].y()) {
+ if (rects[i].y() - y < distance) {
+ newx = x;
+ newy = rects[i].y();
+ distance = rects[i].y() - y;
+ }
+ } else if (y >= rects[i].bottom()) {
+ if (y - rects[i].bottom() + 1 < distance) {
+ newx = x;
+ newy = rects[i].bottom() - 1;
+ distance = y - rects[i].bottom() + 1;
+ }
+ }
+ } else if (y >= rects[i].y() && y < rects[i].bottom()) {
+ if (x < rects[i].x()) {
+ if (rects[i].x() - x < distance) {
+ newx = rects[i].x();
+ newy = y;
+ distance = rects[i].x() - x;
+ }
+ } else if (x >= rects[i].right()) {
+ if (x - rects[i].right() + 1 < distance) {
+ newx = rects[i].right() - 1;
+ newy = y;
+ distance = x - rects[i].right() + 1;
+ }
+ }
+ }
+ }
+ if (!rects.isEmpty()) {
+ if (!inside) {
+ // if neither x nor y has overlap, just pick the top/left of the first rectangle
+ if (newx == x && newy == y) {
+ newx = rects[0].x();
+ newy = rects[0].y();
+ }
+ m_mousePos.setX(newx - m_scrollOffsetX);
+ m_mousePos.setY(newy - m_scrollOffsetY);
+ DBG_NAV_LOGD("Move x/y from (%d, %d) to (%d, %d) scrollOffset is (%d, %d)",
+ x, y, m_mousePos.x() + m_scrollOffsetX, m_mousePos.y() + m_scrollOffsetY,
+ m_scrollOffsetX, m_scrollOffsetY);
+ }
+ return rects;
+ }
+ }
+ IntRect rect = final.mBounds;
+ rect.move(frameAdjust.x(), frameAdjust.y());
+ rects.append(rect);
+ // adjust m_mousePos if it is not inside the returned highlight rectangle
+ testRect.move(frameAdjust.x(), frameAdjust.y());
+ testRect.intersect(rect);
+ if (!testRect.contains(x, y)) {
+ m_mousePos = testRect.center();
+ m_mousePos.move(-m_scrollOffsetX, -m_scrollOffsetY);
+ DBG_NAV_LOGD("Move x/y from (%d, %d) to (%d, %d) scrollOffset is (%d, %d)",
+ x, y, m_mousePos.x() + m_scrollOffsetX, m_mousePos.y() + m_scrollOffsetY,
+ m_scrollOffsetX, m_scrollOffsetY);
+ }
+ }
+ return rects;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void WebViewCore::addPlugin(PluginWidgetAndroid* w)
+{
+// SkDebugf("----------- addPlugin %p", w);
+ /* The plugin must be appended to the end of the array. This ensures that if
+ the plugin is added while iterating through the array (e.g. sendEvent(...))
+ that the iteration process is not corrupted.
+ */
+ *m_plugins.append() = w;
+}
+
+void WebViewCore::removePlugin(PluginWidgetAndroid* w)
+{
+// SkDebugf("----------- removePlugin %p", w);
+ int index = m_plugins.find(w);
+ if (index < 0) {
+ SkDebugf("--------------- pluginwindow not found! %p\n", w);
+ } else {
+ m_plugins.removeShuffle(index);
+ }
+}
+
+bool WebViewCore::isPlugin(PluginWidgetAndroid* w) const
+{
+ return m_plugins.find(w) >= 0;
+}
+
+void WebViewCore::invalPlugin(PluginWidgetAndroid* w)
+{
+ const double PLUGIN_INVAL_DELAY = 1.0 / 60;
+
+ if (!m_pluginInvalTimer.isActive()) {
+ m_pluginInvalTimer.startOneShot(PLUGIN_INVAL_DELAY);
+ }
+}
+
+void WebViewCore::drawPlugins()
+{
+ SkRegion inval; // accumualte what needs to be redrawn
+ PluginWidgetAndroid** iter = m_plugins.begin();
+ PluginWidgetAndroid** stop = m_plugins.end();
+
+ for (; iter < stop; ++iter) {
+ PluginWidgetAndroid* w = *iter;
+ SkIRect dirty;
+ if (w->isDirty(&dirty)) {
+ w->draw();
+ inval.op(dirty, SkRegion::kUnion_Op);
+ }
+ }
+
+ if (!inval.isEmpty()) {
+ // inval.getBounds() is our rectangle
+ const SkIRect& bounds = inval.getBounds();
+ WebCore::IntRect r(bounds.fLeft, bounds.fTop,
+ bounds.width(), bounds.height());
+ this->viewInvalidate(r);
+ }
+}
+
+void WebViewCore::notifyPluginsOnFrameLoad(const Frame* frame) {
+ // if frame is the parent then notify all plugins
+ if (!frame->tree()->parent()) {
+ // trigger an event notifying the plugins that the page has loaded
+ ANPEvent event;
+ SkANP::InitEvent(&event, kLifecycle_ANPEventType);
+ event.data.lifecycle.action = kOnLoad_ANPLifecycleAction;
+ sendPluginEvent(event);
+ // trigger the on/off screen notification if the page was reloaded
+ sendPluginVisibleScreen();
+ }
+ // else if frame's parent has completed
+ else if (!frame->tree()->parent()->loader()->isLoading()) {
+ // send to all plugins who have this frame in their heirarchy
+ PluginWidgetAndroid** iter = m_plugins.begin();
+ PluginWidgetAndroid** stop = m_plugins.end();
+ for (; iter < stop; ++iter) {
+ Frame* currentFrame = (*iter)->pluginView()->parentFrame();
+ while (currentFrame) {
+ if (frame == currentFrame) {
+ ANPEvent event;
+ SkANP::InitEvent(&event, kLifecycle_ANPEventType);
+ event.data.lifecycle.action = kOnLoad_ANPLifecycleAction;
+ (*iter)->sendEvent(event);
+
+ // trigger the on/off screen notification if the page was reloaded
+ ANPRectI visibleRect;
+ getVisibleScreen(visibleRect);
+ (*iter)->setVisibleScreen(visibleRect, m_scale);
+
+ break;
+ }
+ currentFrame = currentFrame->tree()->parent();
+ }
+ }
+ }
+}
+
+void WebViewCore::getVisibleScreen(ANPRectI& visibleRect)
+{
+ visibleRect.left = m_scrollOffsetX;
+ visibleRect.top = m_scrollOffsetY;
+ visibleRect.right = m_scrollOffsetX + m_screenWidth;
+ visibleRect.bottom = m_scrollOffsetY + m_screenHeight;
+}
+
+void WebViewCore::sendPluginVisibleScreen()
+{
+ /* We may want to cache the previous values and only send the notification
+ to the plugin in the event that one of the values has changed.
+ */
+
+ ANPRectI visibleRect;
+ getVisibleScreen(visibleRect);
+
+ PluginWidgetAndroid** iter = m_plugins.begin();
+ PluginWidgetAndroid** stop = m_plugins.end();
+ for (; iter < stop; ++iter) {
+ (*iter)->setVisibleScreen(visibleRect, m_scale);
+ }
+}
+
+void WebViewCore::sendPluginEvent(const ANPEvent& evt)
+{
+ /* The list of plugins may be manipulated as we iterate through the list.
+ This implementation allows for the addition of new plugins during an
+ iteration, but may fail if a plugin is removed. Currently, there are not
+ any use cases where a plugin is deleted while processing this loop, but
+ if it does occur we will have to use an alternate data structure and/or
+ iteration mechanism.
+ */
+ for (int x = 0; x < m_plugins.count(); x++) {
+ m_plugins[x]->sendEvent(evt);
+ }
+}
+
+PluginWidgetAndroid* WebViewCore::getPluginWidget(NPP npp)
+{
+ PluginWidgetAndroid** iter = m_plugins.begin();
+ PluginWidgetAndroid** stop = m_plugins.end();
+ for (; iter < stop; ++iter) {
+ if ((*iter)->pluginView()->instance() == npp) {
+ return (*iter);
+ }
+ }
+ return 0;
+}
+
+static PluginView* nodeIsPlugin(Node* node) {
+ RenderObject* renderer = node->renderer();
+ if (renderer && renderer->isWidget()) {
+ Widget* widget = static_cast<RenderWidget*>(renderer)->widget();
+ if (widget && widget->isPluginView())
+ return static_cast<PluginView*>(widget);
+ }
+ return 0;
+}
+
+Node* WebViewCore::cursorNodeIsPlugin() {
+ gCursorBoundsMutex.lock();
+ bool hasCursorBounds = m_hasCursorBounds;
+ Frame* frame = (Frame*) m_cursorFrame;
+ Node* node = (Node*) m_cursorNode;
+ gCursorBoundsMutex.unlock();
+ if (hasCursorBounds && CacheBuilder::validNode(m_mainFrame, frame, node)
+ && nodeIsPlugin(node)) {
+ return node;
+ }
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void WebViewCore::moveMouseIfLatest(int moveGeneration,
+ WebCore::Frame* frame, int x, int y)
+{
+ DBG_NAV_LOGD("m_moveGeneration=%d moveGeneration=%d"
+ " frame=%p x=%d y=%d",
+ m_moveGeneration, moveGeneration, frame, x, y);
+ if (m_moveGeneration > moveGeneration) {
+ DBG_NAV_LOGD("m_moveGeneration=%d > moveGeneration=%d",
+ m_moveGeneration, moveGeneration);
+ return; // short-circuit if a newer move has already been generated
+ }
+ m_lastGeneration = moveGeneration;
+ moveMouse(frame, x, y);
+}
+
+void WebViewCore::moveFocus(WebCore::Frame* frame, WebCore::Node* node)
+{
+ DBG_NAV_LOGD("frame=%p node=%p", frame, node);
+ if (!node || !CacheBuilder::validNode(m_mainFrame, frame, node)
+ || !node->isElementNode())
+ return;
+ // Code borrowed from FocusController::advanceFocus
+ WebCore::FocusController* focusController
+ = m_mainFrame->page()->focusController();
+ WebCore::Document* oldDoc
+ = focusController->focusedOrMainFrame()->document();
+ if (oldDoc->focusedNode() == node)
+ return;
+ if (node->document() != oldDoc)
+ oldDoc->setFocusedNode(0);
+ focusController->setFocusedFrame(frame);
+ static_cast<WebCore::Element*>(node)->focus(false);
+}
+
+// Update mouse position
+void WebViewCore::moveMouse(WebCore::Frame* frame, int x, int y)
+{
+ DBG_NAV_LOGD("frame=%p x=%d y=%d scrollOffset=(%d,%d)", frame,
+ x, y, m_scrollOffsetX, m_scrollOffsetY);
+ if (!frame || !CacheBuilder::validNode(m_mainFrame, frame, 0))
+ frame = m_mainFrame;
+ // mouse event expects the position in the window coordinate
+ m_mousePos = WebCore::IntPoint(x - m_scrollOffsetX, y - m_scrollOffsetY);
+ // validNode will still return true if the node is null, as long as we have
+ // a valid frame. Do not want to make a call on frame unless it is valid.
+ WebCore::PlatformMouseEvent mouseEvent(m_mousePos, m_mousePos,
+ WebCore::NoButton, WebCore::MouseEventMoved, 1, false, false, false,
+ false, WTF::currentTime());
+ frame->eventHandler()->handleMouseMoveEvent(mouseEvent);
+ updateCacheOnNodeChange();
+}
+
+void WebViewCore::setSelection(int start, int end)
+{
+ WebCore::Node* focus = currentFocus();
+ if (!focus)
+ return;
+ WebCore::RenderObject* renderer = focus->renderer();
+ if (!renderer || (!renderer->isTextField() && !renderer->isTextArea()))
+ return;
+ if (start > end) {
+ int temp = start;
+ start = end;
+ end = temp;
+ }
+ // Tell our EditorClient that this change was generated from the UI, so it
+ // does not need to echo it to the UI.
+ EditorClientAndroid* client = static_cast<EditorClientAndroid*>(
+ m_mainFrame->editor()->client());
+ client->setUiGeneratedSelectionChange(true);
+ setSelectionRange(focus, start, end);
+ client->setUiGeneratedSelectionChange(false);
+ WebCore::Frame* focusedFrame = focus->document()->frame();
+ bool isPasswordField = false;
+ if (focus->isElementNode()) {
+ WebCore::Element* element = static_cast<WebCore::Element*>(focus);
+ if (WebCore::InputElement* inputElement = WebCore::toInputElement(element))
+ isPasswordField = static_cast<WebCore::HTMLInputElement*>(inputElement)->isPasswordField();
+ }
+ // For password fields, this is done in the UI side via
+ // bringPointIntoView, since the UI does the drawing.
+ if (renderer->isTextArea() || !isPasswordField)
+ revealSelection();
+}
+
+String WebViewCore::modifySelection(const int direction, const int axis)
+{
+ DOMSelection* selection = m_mainFrame->domWindow()->getSelection();
+ if (selection->rangeCount() > 1)
+ selection->removeAllRanges();
+ switch (axis) {
+ case AXIS_CHARACTER:
+ case AXIS_WORD:
+ case AXIS_SENTENCE:
+ return modifySelectionTextNavigationAxis(selection, direction, axis);
+ case AXIS_HEADING:
+ case AXIS_SIBLING:
+ case AXIS_PARENT_FIRST_CHILD:
+ case AXIS_DOCUMENT:
+ return modifySelectionDomNavigationAxis(selection, direction, axis);
+ default:
+ LOGE("Invalid navigation axis: %d", axis);
+ return String();
+ }
+}
+
+void WebViewCore::scrollNodeIntoView(Frame* frame, Node* node)
+{
+ if (!frame || !node)
+ return;
+
+ Element* elementNode = 0;
+
+ // If not an Element, find a visible predecessor
+ // Element to scroll into view.
+ if (!node->isElementNode()) {
+ HTMLElement* body = frame->document()->body();
+ do {
+ if (!node || node == body)
+ return;
+ node = node->parentNode();
+ } while (!node->isElementNode() && !isVisible(node));
+ }
+
+ elementNode = static_cast<Element*>(node);
+ elementNode->scrollIntoViewIfNeeded(true);
+}
+
+String WebViewCore::modifySelectionTextNavigationAxis(DOMSelection* selection, int direction, int axis)
+{
+ Node* body = m_mainFrame->document()->body();
+
+ ExceptionCode ec = 0;
+ String markup;
+
+ // initialize the selection if necessary
+ if (selection->rangeCount() == 0) {
+ if (m_currentNodeDomNavigationAxis
+ && CacheBuilder::validNode(m_mainFrame,
+ m_mainFrame, m_currentNodeDomNavigationAxis)) {
+ PassRefPtr<Range> rangeRef =
+ selection->frame()->document()->createRange();
+ rangeRef->selectNode(m_currentNodeDomNavigationAxis, ec);
+ m_currentNodeDomNavigationAxis = 0;
+ if (ec)
+ return String();
+ selection->addRange(rangeRef.get());
+ } else if (currentFocus()) {
+ selection->setPosition(currentFocus(), 0, ec);
+ } else if (m_cursorNode
+ && CacheBuilder::validNode(m_mainFrame,
+ m_mainFrame, m_cursorNode)) {
+ PassRefPtr<Range> rangeRef =
+ selection->frame()->document()->createRange();
+ rangeRef->selectNode(reinterpret_cast<Node*>(m_cursorNode), ec);
+ if (ec)
+ return String();
+ selection->addRange(rangeRef.get());
+ } else {
+ selection->setPosition(body, 0, ec);
+ }
+ if (ec)
+ return String();
+ }
+
+ // collapse the selection
+ if (direction == DIRECTION_FORWARD)
+ selection->collapseToEnd(ec);
+ else
+ selection->collapseToStart(ec);
+ if (ec)
+ return String();
+
+ // Make sure the anchor node is a text node since we are generating
+ // the markup of the selection which includes the anchor, the focus,
+ // and any crossed nodes. Forcing the condition that the selection
+ // starts and ends on text nodes guarantees symmetric selection markup.
+ // Also this way the text content, rather its container, is highlighted.
+ Node* anchorNode = selection->anchorNode();
+ if (anchorNode->isElementNode()) {
+ // Collapsed selection while moving forward points to the
+ // next unvisited node and while moving backward to the
+ // last visited node.
+ if (direction == DIRECTION_FORWARD)
+ advanceAnchorNode(selection, direction, markup, false, ec);
+ else
+ advanceAnchorNode(selection, direction, markup, true, ec);
+ if (ec)
+ return String();
+ if (!markup.isEmpty())
+ return markup;
+ }
+
+ // If the selection is at the end of a non white space text move
+ // it to the next visible text node with non white space content.
+ // This is a workaround for the selection getting stuck.
+ anchorNode = selection->anchorNode();
+ if (anchorNode->isTextNode()) {
+ if (direction == DIRECTION_FORWARD) {
+ String suffix = anchorNode->textContent().substring(
+ selection->anchorOffset(), caretMaxOffset(anchorNode));
+ // If at the end of non white space text we advance the
+ // anchor node to either an input element or non empty text.
+ if (suffix.stripWhiteSpace().isEmpty()) {
+ advanceAnchorNode(selection, direction, markup, true, ec);
+ }
+ } else {
+ String prefix = anchorNode->textContent().substring(0,
+ selection->anchorOffset());
+ // If at the end of non white space text we advance the
+ // anchor node to either an input element or non empty text.
+ if (prefix.stripWhiteSpace().isEmpty()) {
+ advanceAnchorNode(selection, direction, markup, true, ec);
+ }
+ }
+ if (ec)
+ return String();
+ if (!markup.isEmpty())
+ return markup;
+ }
+
+ // extend the selection
+ String directionStr;
+ if (direction == DIRECTION_FORWARD)
+ directionStr = "forward";
+ else
+ directionStr = "backward";
+
+ String axisStr;
+ if (axis == AXIS_CHARACTER)
+ axisStr = "character";
+ else if (axis == AXIS_WORD)
+ axisStr = "word";
+ else
+ axisStr = "sentence";
+
+ selection->modify("extend", directionStr, axisStr);
+
+ // Make sure the focus node is a text node in order to have the
+ // selection generate symmetric markup because the latter
+ // includes all nodes crossed by the selection. Also this way
+ // the text content, rather its container, is highlighted.
+ Node* focusNode = selection->focusNode();
+ if (focusNode->isElementNode()) {
+ focusNode = getImplicitBoundaryNode(selection->focusNode(),
+ selection->focusOffset(), direction);
+ if (!focusNode)
+ return String();
+ if (direction == DIRECTION_FORWARD) {
+ focusNode = focusNode->traversePreviousSiblingPostOrder(body);
+ if (focusNode && !isContentTextNode(focusNode)) {
+ Node* textNode = traverseNextContentTextNode(focusNode,
+ anchorNode, DIRECTION_BACKWARD);
+ if (textNode)
+ anchorNode = textNode;
+ }
+ if (focusNode && isContentTextNode(focusNode)) {
+ selection->extend(focusNode, caretMaxOffset(focusNode), ec);
+ if (ec)
+ return String();
+ }
+ } else {
+ focusNode = focusNode->traverseNextSibling();
+ if (focusNode && !isContentTextNode(focusNode)) {
+ Node* textNode = traverseNextContentTextNode(focusNode,
+ anchorNode, DIRECTION_FORWARD);
+ if (textNode)
+ anchorNode = textNode;
+ }
+ if (anchorNode && isContentTextNode(anchorNode)) {
+ selection->extend(focusNode, 0, ec);
+ if (ec)
+ return String();
+ }
+ }
+ }
+
+ // Enforce that the selection does not cross anchor boundaries. This is
+ // a workaround for the asymmetric behavior of WebKit while crossing
+ // anchors.
+ anchorNode = getImplicitBoundaryNode(selection->anchorNode(),
+ selection->anchorOffset(), direction);
+ focusNode = getImplicitBoundaryNode(selection->focusNode(),
+ selection->focusOffset(), direction);
+ if (anchorNode && focusNode && anchorNode != focusNode) {
+ Node* inputControl = getIntermediaryInputElement(anchorNode, focusNode,
+ direction);
+ if (inputControl) {
+ if (direction == DIRECTION_FORWARD) {
+ if (isDescendantOf(inputControl, anchorNode)) {
+ focusNode = inputControl;
+ } else {
+ focusNode = inputControl->traversePreviousSiblingPostOrder(
+ body);
+ if (!focusNode)
+ focusNode = inputControl;
+ }
+ // We prefer a text node contained in the input element.
+ if (!isContentTextNode(focusNode)) {
+ Node* textNode = traverseNextContentTextNode(focusNode,
+ anchorNode, DIRECTION_BACKWARD);
+ if (textNode)
+ focusNode = textNode;
+ }
+ // If we found text in the input select it.
+ // Otherwise, select the input element itself.
+ if (isContentTextNode(focusNode)) {
+ selection->extend(focusNode, caretMaxOffset(focusNode), ec);
+ } else if (anchorNode != focusNode) {
+ // Note that the focusNode always has parent and that
+ // the offset can be one more that the index of the last
+ // element - this is how WebKit selects such elements.
+ selection->extend(focusNode->parentNode(),
+ focusNode->nodeIndex() + 1, ec);
+ }
+ if (ec)
+ return String();
+ } else {
+ if (isDescendantOf(inputControl, anchorNode)) {
+ focusNode = inputControl;
+ } else {
+ focusNode = inputControl->traverseNextSibling();
+ if (!focusNode)
+ focusNode = inputControl;
+ }
+ // We prefer a text node contained in the input element.
+ if (!isContentTextNode(focusNode)) {
+ Node* textNode = traverseNextContentTextNode(focusNode,
+ anchorNode, DIRECTION_FORWARD);
+ if (textNode)
+ focusNode = textNode;
+ }
+ // If we found text in the input select it.
+ // Otherwise, select the input element itself.
+ if (isContentTextNode(focusNode)) {
+ selection->extend(focusNode, caretMinOffset(focusNode), ec);
+ } else if (anchorNode != focusNode) {
+ // Note that the focusNode always has parent and that
+ // the offset can be one more that the index of the last
+ // element - this is how WebKit selects such elements.
+ selection->extend(focusNode->parentNode(),
+ focusNode->nodeIndex() + 1, ec);
+ }
+ if (ec)
+ return String();
+ }
+ }
+ }
+
+ // make sure the selection is visible
+ if (direction == DIRECTION_FORWARD)
+ scrollNodeIntoView(m_mainFrame, selection->focusNode());
+ else
+ scrollNodeIntoView(m_mainFrame, selection->anchorNode());
+
+ // format markup for the visible content
+ PassRefPtr<Range> range = selection->getRangeAt(0, ec);
+ if (ec)
+ return String();
+ IntRect bounds = range->boundingBox();
+ selectAt(bounds.center().x(), bounds.center().y());
+ markup = formatMarkup(selection);
+ LOGV("Selection markup: %s", markup.utf8().data());
+
+ return markup;
+}
+
+Node* WebViewCore::getImplicitBoundaryNode(Node* node, unsigned offset, int direction)
+{
+ if (node->offsetInCharacters())
+ return node;
+ if (!node->hasChildNodes())
+ return node;
+ if (offset < node->childNodeCount())
+ return node->childNode(offset);
+ else
+ if (direction == DIRECTION_FORWARD)
+ return node->traverseNextSibling();
+ else
+ return node->traversePreviousNodePostOrder(
+ node->document()->body());
+}
+
+Node* WebViewCore::getNextAnchorNode(Node* anchorNode, bool ignoreFirstNode, int direction)
+{
+ Node* body = 0;
+ Node* currentNode = 0;
+ if (direction == DIRECTION_FORWARD) {
+ if (ignoreFirstNode)
+ currentNode = anchorNode->traverseNextNode(body);
+ else
+ currentNode = anchorNode;
+ } else {
+ body = anchorNode->document()->body();
+ if (ignoreFirstNode)
+ currentNode = anchorNode->traversePreviousSiblingPostOrder(body);
+ else
+ currentNode = anchorNode;
+ }
+ while (currentNode) {
+ if (isContentTextNode(currentNode)
+ || isContentInputElement(currentNode))
+ return currentNode;
+ if (direction == DIRECTION_FORWARD)
+ currentNode = currentNode->traverseNextNode();
+ else
+ currentNode = currentNode->traversePreviousNodePostOrder(body);
+ }
+ return 0;
+}
+
+void WebViewCore::advanceAnchorNode(DOMSelection* selection, int direction,
+ String& markup, bool ignoreFirstNode, ExceptionCode& ec)
+{
+ Node* anchorNode = getImplicitBoundaryNode(selection->anchorNode(),
+ selection->anchorOffset(), direction);
+ if (!anchorNode) {
+ ec = NOT_FOUND_ERR;
+ return;
+ }
+ // If the anchor offset is invalid i.e. the anchor node has no
+ // child with that index getImplicitAnchorNode returns the next
+ // logical node in the current direction. In such a case our
+ // position in the DOM tree was has already been advanced,
+ // therefore we there is no need to do that again.
+ if (selection->anchorNode()->isElementNode()) {
+ unsigned anchorOffset = selection->anchorOffset();
+ unsigned childNodeCount = selection->anchorNode()->childNodeCount();
+ if (anchorOffset >= childNodeCount)
+ ignoreFirstNode = false;
+ }
+ // Find the next anchor node given our position in the DOM and
+ // whether we want the current node to be considered as well.
+ Node* nextAnchorNode = getNextAnchorNode(anchorNode, ignoreFirstNode,
+ direction);
+ if (!nextAnchorNode) {
+ ec = NOT_FOUND_ERR;
+ return;
+ }
+ if (nextAnchorNode->isElementNode()) {
+ // If this is an input element tell the WebView thread
+ // to set the cursor to that control.
+ if (isContentInputElement(nextAnchorNode)) {
+ IntRect bounds = nextAnchorNode->getRect();
+ selectAt(bounds.center().x(), bounds.center().y());
+ }
+ Node* textNode = 0;
+ // Treat the text content of links as any other text but
+ // for the rest input elements select the control itself.
+ if (nextAnchorNode->hasTagName(WebCore::HTMLNames::aTag))
+ textNode = traverseNextContentTextNode(nextAnchorNode,
+ nextAnchorNode, direction);
+ // We prefer to select the text content of the link if such,
+ // otherwise just select the element itself.
+ if (textNode) {
+ nextAnchorNode = textNode;
+ } else {
+ if (direction == DIRECTION_FORWARD) {
+ selection->setBaseAndExtent(nextAnchorNode,
+ caretMinOffset(nextAnchorNode), nextAnchorNode,
+ caretMaxOffset(nextAnchorNode), ec);
+ } else {
+ selection->setBaseAndExtent(nextAnchorNode,
+ caretMaxOffset(nextAnchorNode), nextAnchorNode,
+ caretMinOffset(nextAnchorNode), ec);
+ }
+ if (!ec)
+ markup = formatMarkup(selection);
+ // make sure the selection is visible
+ scrollNodeIntoView(selection->frame(), nextAnchorNode);
+ return;
+ }
+ }
+ if (direction == DIRECTION_FORWARD)
+ selection->setPosition(nextAnchorNode,
+ caretMinOffset(nextAnchorNode), ec);
+ else
+ selection->setPosition(nextAnchorNode,
+ caretMaxOffset(nextAnchorNode), ec);
+}
+
+bool WebViewCore::isContentInputElement(Node* node)
+{
+ return (isVisible(node)
+ && (node->hasTagName(WebCore::HTMLNames::selectTag)
+ || node->hasTagName(WebCore::HTMLNames::aTag)
+ || node->hasTagName(WebCore::HTMLNames::inputTag)
+ || node->hasTagName(WebCore::HTMLNames::buttonTag)));
+}
+
+bool WebViewCore::isContentTextNode(Node* node)
+{
+ if (!node || !node->isTextNode())
+ return false;
+ Text* textNode = static_cast<Text*>(node);
+ return (isVisible(textNode) && textNode->length() > 0
+ && !textNode->containsOnlyWhitespace());
+}
+
+Text* WebViewCore::traverseNextContentTextNode(Node* fromNode, Node* toNode, int direction)
+{
+ Node* currentNode = fromNode;
+ do {
+ if (direction == DIRECTION_FORWARD)
+ currentNode = currentNode->traverseNextNode(toNode);
+ else
+ currentNode = currentNode->traversePreviousNodePostOrder(toNode);
+ } while (currentNode && !isContentTextNode(currentNode));
+ return static_cast<Text*>(currentNode);
+}
+
+Node* WebViewCore::getIntermediaryInputElement(Node* fromNode, Node* toNode, int direction)
+{
+ if (fromNode == toNode)
+ return 0;
+ if (direction == DIRECTION_FORWARD) {
+ Node* currentNode = fromNode;
+ while (currentNode && currentNode != toNode) {
+ if (isContentInputElement(currentNode))
+ return currentNode;
+ currentNode = currentNode->traverseNextNodePostOrder();
+ }
+ currentNode = fromNode;
+ while (currentNode && currentNode != toNode) {
+ if (isContentInputElement(currentNode))
+ return currentNode;
+ currentNode = currentNode->traverseNextNode();
+ }
+ } else {
+ Node* currentNode = fromNode->traversePreviousNode();
+ while (currentNode && currentNode != toNode) {
+ if (isContentInputElement(currentNode))
+ return currentNode;
+ currentNode = currentNode->traversePreviousNode();
+ }
+ currentNode = fromNode->traversePreviousNodePostOrder();
+ while (currentNode && currentNode != toNode) {
+ if (isContentInputElement(currentNode))
+ return currentNode;
+ currentNode = currentNode->traversePreviousNodePostOrder();
+ }
+ }
+ return 0;
+}
+
+bool WebViewCore::isDescendantOf(Node* parent, Node* node)
+{
+ Node* currentNode = node;
+ while (currentNode) {
+ if (currentNode == parent) {
+ return true;
+ }
+ currentNode = currentNode->parentNode();
+ }
+ return false;
+}
+
+String WebViewCore::modifySelectionDomNavigationAxis(DOMSelection* selection, int direction, int axis)
+{
+ HTMLElement* body = m_mainFrame->document()->body();
+ if (!m_currentNodeDomNavigationAxis && selection->focusNode()) {
+ m_currentNodeDomNavigationAxis = selection->focusNode();
+ selection->empty();
+ if (m_currentNodeDomNavigationAxis->isTextNode())
+ m_currentNodeDomNavigationAxis =
+ m_currentNodeDomNavigationAxis->parentNode();
+ }
+ if (!m_currentNodeDomNavigationAxis)
+ m_currentNodeDomNavigationAxis = currentFocus();
+ if (!m_currentNodeDomNavigationAxis
+ || !CacheBuilder::validNode(m_mainFrame, m_mainFrame,
+ m_currentNodeDomNavigationAxis))
+ m_currentNodeDomNavigationAxis = body;
+ Node* currentNode = m_currentNodeDomNavigationAxis;
+ if (axis == AXIS_HEADING) {
+ if (currentNode == body && direction == DIRECTION_BACKWARD)
+ currentNode = currentNode->lastDescendant();
+ do {
+ if (direction == DIRECTION_FORWARD)
+ currentNode = currentNode->traverseNextNode(body);
+ else
+ currentNode = currentNode->traversePreviousNode(body);
+ } while (currentNode && (currentNode->isTextNode()
+ || !isVisible(currentNode) || !isHeading(currentNode)));
+ } else if (axis == AXIS_PARENT_FIRST_CHILD) {
+ if (direction == DIRECTION_FORWARD) {
+ currentNode = currentNode->firstChild();
+ while (currentNode && (currentNode->isTextNode()
+ || !isVisible(currentNode)))
+ currentNode = currentNode->nextSibling();
+ } else {
+ do {
+ if (currentNode == body)
+ return String();
+ currentNode = currentNode->parentNode();
+ } while (currentNode && (currentNode->isTextNode()
+ || !isVisible(currentNode)));
+ }
+ } else if (axis == AXIS_SIBLING) {
+ do {
+ if (direction == DIRECTION_FORWARD)
+ currentNode = currentNode->nextSibling();
+ else {
+ if (currentNode == body)
+ return String();
+ currentNode = currentNode->previousSibling();
+ }
+ } while (currentNode && (currentNode->isTextNode()
+ || !isVisible(currentNode)));
+ } else if (axis == AXIS_DOCUMENT) {
+ currentNode = body;
+ if (direction == DIRECTION_FORWARD)
+ currentNode = currentNode->lastDescendant();
+ } else {
+ LOGE("Invalid axis: %d", axis);
+ return String();
+ }
+ if (currentNode) {
+ m_currentNodeDomNavigationAxis = currentNode;
+ scrollNodeIntoView(m_mainFrame, currentNode);
+ String selectionString = createMarkup(currentNode);
+ LOGV("Selection markup: %s", selectionString.utf8().data());
+ return selectionString;
+ }
+ return String();
+}
+
+bool WebViewCore::isHeading(Node* node)
+{
+ if (node->hasTagName(WebCore::HTMLNames::h1Tag)
+ || node->hasTagName(WebCore::HTMLNames::h2Tag)
+ || node->hasTagName(WebCore::HTMLNames::h3Tag)
+ || node->hasTagName(WebCore::HTMLNames::h4Tag)
+ || node->hasTagName(WebCore::HTMLNames::h5Tag)
+ || node->hasTagName(WebCore::HTMLNames::h6Tag)) {
+ return true;
+ }
+
+ if (node->isElementNode()) {
+ Element* element = static_cast<Element*>(node);
+ String roleAttribute =
+ element->getAttribute(WebCore::HTMLNames::roleAttr).string();
+ if (equalIgnoringCase(roleAttribute, "heading"))
+ return true;
+ }
+
+ return false;
+}
+
+bool WebViewCore::isVisible(Node* node)
+{
+ // start off an element
+ Element* element = 0;
+ if (node->isElementNode())
+ element = static_cast<Element*>(node);
+ else
+ element = node->parentElement();
+ // check renderer
+ if (!element->renderer()) {
+ return false;
+ }
+ // check size
+ if (element->offsetHeight() == 0 || element->offsetWidth() == 0) {
+ return false;
+ }
+ // check style
+ Node* body = m_mainFrame->document()->body();
+ Node* currentNode = element;
+ while (currentNode && currentNode != body) {
+ RenderStyle* style = currentNode->computedStyle();
+ if (style &&
+ (style->display() == NONE || style->visibility() == HIDDEN)) {
+ return false;
+ }
+ currentNode = currentNode->parentNode();
+ }
+ return true;
+}
+
+String WebViewCore::formatMarkup(DOMSelection* selection)
+{
+ ExceptionCode ec = 0;
+ String markup = String();
+ PassRefPtr<Range> wholeRange = selection->getRangeAt(0, ec);
+ if (ec)
+ return String();
+ if (!wholeRange->startContainer() || !wholeRange->startContainer())
+ return String();
+ // Since formatted markup contains invisible nodes it
+ // is created from the concatenation of the visible fragments.
+ Node* firstNode = wholeRange->firstNode();
+ Node* pastLastNode = wholeRange->pastLastNode();
+ Node* currentNode = firstNode;
+ PassRefPtr<Range> currentRange;
+
+ while (currentNode != pastLastNode) {
+ Node* nextNode = currentNode->traverseNextNode();
+ if (!isVisible(currentNode)) {
+ if (currentRange) {
+ markup = markup + currentRange->toHTML().utf8().data();
+ currentRange = 0;
+ }
+ } else {
+ if (!currentRange) {
+ currentRange = selection->frame()->document()->createRange();
+ if (ec)
+ break;
+ if (currentNode == firstNode) {
+ currentRange->setStart(wholeRange->startContainer(),
+ wholeRange->startOffset(), ec);
+ if (ec)
+ break;
+ } else {
+ currentRange->setStart(currentNode->parentNode(),
+ currentNode->nodeIndex(), ec);
+ if (ec)
+ break;
+ }
+ }
+ if (nextNode == pastLastNode) {
+ currentRange->setEnd(wholeRange->endContainer(),
+ wholeRange->endOffset(), ec);
+ if (ec)
+ break;
+ markup = markup + currentRange->toHTML().utf8().data();
+ } else {
+ if (currentNode->offsetInCharacters())
+ currentRange->setEnd(currentNode,
+ currentNode->maxCharacterOffset(), ec);
+ else
+ currentRange->setEnd(currentNode->parentNode(),
+ currentNode->nodeIndex() + 1, ec);
+ if (ec)
+ break;
+ }
+ }
+ currentNode = nextNode;
+ }
+ return markup.stripWhiteSpace();
+}
+
+void WebViewCore::selectAt(int x, int y)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_selectAt,
+ x, y);
+ checkException(env);
+}
+
+void WebViewCore::deleteSelection(int start, int end, int textGeneration)
+{
+ setSelection(start, end);
+ if (start == end)
+ return;
+ WebCore::Node* focus = currentFocus();
+ if (!focus)
+ return;
+ // Prevent our editor client from passing a message to change the
+ // selection.
+ EditorClientAndroid* client = static_cast<EditorClientAndroid*>(
+ m_mainFrame->editor()->client());
+ client->setUiGeneratedSelectionChange(true);
+ PlatformKeyboardEvent down(AKEYCODE_DEL, 0, 0, true, false, false, false);
+ PlatformKeyboardEvent up(AKEYCODE_DEL, 0, 0, false, false, false, false);
+ key(down);
+ key(up);
+ client->setUiGeneratedSelectionChange(false);
+ m_textGeneration = textGeneration;
+ m_shouldPaintCaret = true;
+}
+
+void WebViewCore::replaceTextfieldText(int oldStart,
+ int oldEnd, const WTF::String& replace, int start, int end,
+ int textGeneration)
+{
+ WebCore::Node* focus = currentFocus();
+ if (!focus)
+ return;
+ setSelection(oldStart, oldEnd);
+ // Prevent our editor client from passing a message to change the
+ // selection.
+ EditorClientAndroid* client = static_cast<EditorClientAndroid*>(
+ m_mainFrame->editor()->client());
+ client->setUiGeneratedSelectionChange(true);
+ WebCore::TypingCommand::insertText(focus->document(), replace,
+ false);
+ client->setUiGeneratedSelectionChange(false);
+ // setSelection calls revealSelection, so there is no need to do it here.
+ setSelection(start, end);
+ m_textGeneration = textGeneration;
+ m_shouldPaintCaret = true;
+}
+
+void WebViewCore::passToJs(int generation, const WTF::String& current,
+ const PlatformKeyboardEvent& event)
+{
+ WebCore::Node* focus = currentFocus();
+ if (!focus) {
+ DBG_NAV_LOG("!focus");
+ clearTextEntry();
+ return;
+ }
+ WebCore::RenderObject* renderer = focus->renderer();
+ if (!renderer || (!renderer->isTextField() && !renderer->isTextArea())) {
+ DBG_NAV_LOGD("renderer==%p || not text", renderer);
+ clearTextEntry();
+ return;
+ }
+ // Block text field updates during a key press.
+ m_blockTextfieldUpdates = true;
+ // Also prevent our editor client from passing a message to change the
+ // selection.
+ EditorClientAndroid* client = static_cast<EditorClientAndroid*>(
+ m_mainFrame->editor()->client());
+ client->setUiGeneratedSelectionChange(true);
+ key(event);
+ client->setUiGeneratedSelectionChange(false);
+ m_blockTextfieldUpdates = false;
+ m_textGeneration = generation;
+ WebCore::RenderTextControl* renderText =
+ static_cast<WebCore::RenderTextControl*>(renderer);
+ WTF::String test = renderText->text();
+ if (test != current) {
+ // If the text changed during the key event, update the UI text field.
+ updateTextfield(focus, false, test);
+ } else {
+ DBG_NAV_LOG("test == current");
+ }
+ // Now that the selection has settled down, send it.
+ updateTextSelection();
+ m_shouldPaintCaret = true;
+}
+
+void WebViewCore::scrollFocusedTextInput(float xPercent, int y)
+{
+ WebCore::Node* focus = currentFocus();
+ if (!focus) {
+ DBG_NAV_LOG("!focus");
+ clearTextEntry();
+ return;
+ }
+ WebCore::RenderObject* renderer = focus->renderer();
+ if (!renderer || (!renderer->isTextField() && !renderer->isTextArea())) {
+ DBG_NAV_LOGD("renderer==%p || not text", renderer);
+ clearTextEntry();
+ return;
+ }
+ WebCore::RenderTextControl* renderText =
+ static_cast<WebCore::RenderTextControl*>(renderer);
+ int x = (int) (xPercent * (renderText->scrollWidth() -
+ renderText->clientWidth()));
+ DBG_NAV_LOGD("x=%d y=%d xPercent=%g scrollW=%d clientW=%d", x, y,
+ xPercent, renderText->scrollWidth(), renderText->clientWidth());
+ renderText->setScrollLeft(x);
+ renderText->setScrollTop(y);
+}
+
+void WebViewCore::setFocusControllerActive(bool active)
+{
+ m_mainFrame->page()->focusController()->setActive(active);
+}
+
+void WebViewCore::saveDocumentState(WebCore::Frame* frame)
+{
+ if (!CacheBuilder::validNode(m_mainFrame, frame, 0))
+ frame = m_mainFrame;
+ WebCore::HistoryItem *item = frame->loader()->history()->currentItem();
+
+ // item can be null when there is no offical URL for the current page. This happens
+ // when the content is loaded using with WebCoreFrameBridge::LoadData() and there
+ // is no failing URL (common case is when content is loaded using data: scheme)
+ if (item) {
+ item->setDocumentState(frame->document()->formElementsState());
+ }
+}
+
+// Create an array of java Strings.
+static jobjectArray makeLabelArray(JNIEnv* env, const uint16_t** labels, size_t count)
+{
+ jclass stringClass = env->FindClass("java/lang/String");
+ LOG_ASSERT(stringClass, "Could not find java/lang/String");
+ jobjectArray array = env->NewObjectArray(count, stringClass, 0);
+ LOG_ASSERT(array, "Could not create new string array");
+
+ for (size_t i = 0; i < count; i++) {
+ jobject newString = env->NewString(&labels[i][1], labels[i][0]);
+ env->SetObjectArrayElement(array, i, newString);
+ env->DeleteLocalRef(newString);
+ checkException(env);
+ }
+ env->DeleteLocalRef(stringClass);
+ return array;
+}
+
+void WebViewCore::openFileChooser(PassRefPtr<WebCore::FileChooser> chooser) {
+ if (!chooser)
+ return;
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+
+ WTF::String acceptType = chooser->acceptTypes();
+ jstring jAcceptType = wtfStringToJstring(env, acceptType, true);
+ jstring jName = (jstring) env->CallObjectMethod(
+ m_javaGlue->object(env).get(), m_javaGlue->m_openFileChooser, jAcceptType);
+ checkException(env);
+ env->DeleteLocalRef(jAcceptType);
+
+ const UChar* string = static_cast<const UChar*>(env->GetStringChars(jName, NULL));
+
+ if (!string)
+ return;
+
+ WTF::String webcoreString = jstringToWtfString(env, jName);
+ env->ReleaseStringChars(jName, string);
+
+ if (webcoreString.length())
+ chooser->chooseFile(webcoreString);
+}
+
+void WebViewCore::listBoxRequest(WebCoreReply* reply, const uint16_t** labels, size_t count, const int enabled[], size_t enabledCount,
+ bool multiple, const int selected[], size_t selectedCountOrSelection)
+{
+ // If m_popupReply is not null, then we already have a list showing.
+ if (m_popupReply != 0)
+ return;
+
+ LOG_ASSERT(m_javaGlue->m_obj, "No java widget associated with this view!");
+
+ // Create an array of java Strings for the drop down.
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jobjectArray labelArray = makeLabelArray(env, labels, count);
+
+ // Create an array determining whether each item is enabled.
+ jintArray enabledArray = env->NewIntArray(enabledCount);
+ checkException(env);
+ jint* ptrArray = env->GetIntArrayElements(enabledArray, 0);
+ checkException(env);
+ for (size_t i = 0; i < enabledCount; i++) {
+ ptrArray[i] = enabled[i];
+ }
+ env->ReleaseIntArrayElements(enabledArray, ptrArray, 0);
+ checkException(env);
+
+ if (multiple) {
+ // Pass up an array representing which items are selected.
+ jintArray selectedArray = env->NewIntArray(selectedCountOrSelection);
+ checkException(env);
+ jint* selArray = env->GetIntArrayElements(selectedArray, 0);
+ checkException(env);
+ for (size_t i = 0; i < selectedCountOrSelection; i++) {
+ selArray[i] = selected[i];
+ }
+ env->ReleaseIntArrayElements(selectedArray, selArray, 0);
+
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_requestListBox, labelArray, enabledArray,
+ selectedArray);
+ env->DeleteLocalRef(selectedArray);
+ } else {
+ // Pass up the single selection.
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_requestSingleListBox, labelArray, enabledArray,
+ selectedCountOrSelection);
+ }
+
+ env->DeleteLocalRef(labelArray);
+ env->DeleteLocalRef(enabledArray);
+ checkException(env);
+
+ Retain(reply);
+ m_popupReply = reply;
+}
+
+bool WebViewCore::key(const PlatformKeyboardEvent& event)
+{
+ WebCore::EventHandler* eventHandler;
+ WebCore::Node* focusNode = currentFocus();
+ DBG_NAV_LOGD("keyCode=%s unichar=%d focusNode=%p",
+ event.keyIdentifier().utf8().data(), event.unichar(), focusNode);
+ if (focusNode) {
+ WebCore::Frame* frame = focusNode->document()->frame();
+ WebFrame* webFrame = WebFrame::getWebFrame(frame);
+ eventHandler = frame->eventHandler();
+ VisibleSelection old = frame->selection()->selection();
+ bool handled = eventHandler->keyEvent(event);
+ if (isContentEditable(focusNode)) {
+ // keyEvent will return true even if the contentEditable did not
+ // change its selection. In the case that it does not, we want to
+ // return false so that the key will be sent back to our navigation
+ // system.
+ handled |= frame->selection()->selection() != old;
+ }
+ return handled;
+ } else {
+ eventHandler = m_mainFrame->eventHandler();
+ }
+ return eventHandler->keyEvent(event);
+}
+
+// For when the user clicks the trackball, presses dpad center, or types into an
+// unfocused textfield. In the latter case, 'fake' will be true
+void WebViewCore::click(WebCore::Frame* frame, WebCore::Node* node, bool fake) {
+ if (!node) {
+ WebCore::IntPoint pt = m_mousePos;
+ pt.move(m_scrollOffsetX, m_scrollOffsetY);
+ WebCore::HitTestResult hitTestResult = m_mainFrame->eventHandler()->
+ hitTestResultAtPoint(pt, false);
+ node = hitTestResult.innerNode();
+ frame = node->document()->frame();
+ DBG_NAV_LOGD("m_mousePos=(%d,%d) m_scrollOffset=(%d,%d) pt=(%d,%d)"
+ " node=%p", m_mousePos.x(), m_mousePos.y(),
+ m_scrollOffsetX, m_scrollOffsetY, pt.x(), pt.y(), node);
+ }
+ if (node) {
+ EditorClientAndroid* client
+ = static_cast<EditorClientAndroid*>(
+ m_mainFrame->editor()->client());
+ client->setShouldChangeSelectedRange(false);
+ handleMouseClick(frame, node, fake);
+ client->setShouldChangeSelectedRange(true);
+ }
+}
+
+#if USE(ACCELERATED_COMPOSITING)
+GraphicsLayerAndroid* WebViewCore::graphicsRootLayer() const
+{
+ RenderView* contentRenderer = m_mainFrame->contentRenderer();
+ if (!contentRenderer)
+ return 0;
+ return static_cast<GraphicsLayerAndroid*>(
+ contentRenderer->compositor()->rootPlatformLayer());
+}
+#endif
+
+bool WebViewCore::handleTouchEvent(int action, Vector<int>& ids, Vector<IntPoint>& points, int actionIndex, int metaState)
+{
+ bool preventDefault = false;
+
+#if USE(ACCELERATED_COMPOSITING)
+ GraphicsLayerAndroid* rootLayer = graphicsRootLayer();
+ if (rootLayer)
+ rootLayer->pauseDisplay(true);
+#endif
+
+#if ENABLE(TOUCH_EVENTS) // Android
+ #define MOTION_EVENT_ACTION_POINTER_DOWN 5
+ #define MOTION_EVENT_ACTION_POINTER_UP 6
+
+ WebCore::TouchEventType type = WebCore::TouchStart;
+ WebCore::PlatformTouchPoint::State defaultTouchState;
+ Vector<WebCore::PlatformTouchPoint::State> touchStates(points.size());
+
+ switch (action) {
+ case 0: // MotionEvent.ACTION_DOWN
+ type = WebCore::TouchStart;
+ defaultTouchState = WebCore::PlatformTouchPoint::TouchPressed;
+ break;
+ case 1: // MotionEvent.ACTION_UP
+ type = WebCore::TouchEnd;
+ defaultTouchState = WebCore::PlatformTouchPoint::TouchReleased;
+ break;
+ case 2: // MotionEvent.ACTION_MOVE
+ type = WebCore::TouchMove;
+ defaultTouchState = WebCore::PlatformTouchPoint::TouchMoved;
+ break;
+ case 3: // MotionEvent.ACTION_CANCEL
+ type = WebCore::TouchCancel;
+ defaultTouchState = WebCore::PlatformTouchPoint::TouchCancelled;
+ break;
+ case 5: // MotionEvent.ACTION_POINTER_DOWN
+ type = WebCore::TouchStart;
+ defaultTouchState = WebCore::PlatformTouchPoint::TouchStationary;
+ break;
+ case 6: // MotionEvent.ACTION_POINTER_UP
+ type = WebCore::TouchEnd;
+ defaultTouchState = WebCore::PlatformTouchPoint::TouchStationary;
+ break;
+ case 0x100: // WebViewCore.ACTION_LONGPRESS
+ type = WebCore::TouchLongPress;
+ defaultTouchState = WebCore::PlatformTouchPoint::TouchPressed;
+ break;
+ case 0x200: // WebViewCore.ACTION_DOUBLETAP
+ type = WebCore::TouchDoubleTap;
+ defaultTouchState = WebCore::PlatformTouchPoint::TouchPressed;
+ break;
+ default:
+ // We do not support other kinds of touch event inside WebCore
+ // at the moment.
+ LOGW("Java passed a touch event type that we do not support in WebCore: %d", action);
+ return 0;
+ }
+
+ for (int c = 0; c < static_cast<int>(points.size()); c++) {
+ points[c].setX(points[c].x() - m_scrollOffsetX);
+ points[c].setY(points[c].y() - m_scrollOffsetY);
+
+ // Setting the touch state for each point.
+ // Note: actionIndex will be 0 for all actions that are not ACTION_POINTER_DOWN/UP.
+ if (action == MOTION_EVENT_ACTION_POINTER_DOWN && c == actionIndex) {
+ touchStates[c] = WebCore::PlatformTouchPoint::TouchPressed;
+ } else if (action == MOTION_EVENT_ACTION_POINTER_UP && c == actionIndex) {
+ touchStates[c] = WebCore::PlatformTouchPoint::TouchReleased;
+ } else {
+ touchStates[c] = defaultTouchState;
+ };
+ }
+
+ WebCore::PlatformTouchEvent te(ids, points, type, touchStates, metaState);
+ preventDefault = m_mainFrame->eventHandler()->handleTouchEvent(te);
+#endif
+
+#if USE(ACCELERATED_COMPOSITING)
+ if (rootLayer)
+ rootLayer->pauseDisplay(false);
+#endif
+ return preventDefault;
+}
+
+void WebViewCore::touchUp(int touchGeneration,
+ WebCore::Frame* frame, WebCore::Node* node, int x, int y)
+{
+ if (touchGeneration == 0) {
+ // m_mousePos should be set in getTouchHighlightRects()
+ WebCore::HitTestResult hitTestResult = m_mainFrame->eventHandler()->hitTestResultAtPoint(m_mousePos, false);
+ node = hitTestResult.innerNode();
+ if (node)
+ frame = node->document()->frame();
+ else
+ frame = 0;
+ DBG_NAV_LOGD("touch up on (%d, %d), scrollOffset is (%d, %d), node:%p, frame:%p", m_mousePos.x() + m_scrollOffsetX, m_mousePos.y() + m_scrollOffsetY, m_scrollOffsetX, m_scrollOffsetY, node, frame);
+ } else {
+ if (m_touchGeneration > touchGeneration) {
+ DBG_NAV_LOGD("m_touchGeneration=%d > touchGeneration=%d"
+ " x=%d y=%d", m_touchGeneration, touchGeneration, x, y);
+ return; // short circuit if a newer touch has been generated
+ }
+ // This moves m_mousePos to the correct place, and handleMouseClick uses
+ // m_mousePos to determine where the click happens.
+ moveMouse(frame, x, y);
+ m_lastGeneration = touchGeneration;
+ }
+ if (frame && CacheBuilder::validNode(m_mainFrame, frame, 0)) {
+ frame->loader()->resetMultipleFormSubmissionProtection();
+ }
+ DBG_NAV_LOGD("touchGeneration=%d handleMouseClick frame=%p node=%p"
+ " x=%d y=%d", touchGeneration, frame, node, x, y);
+ handleMouseClick(frame, node, false);
+}
+
+// Check for the "x-webkit-soft-keyboard" attribute. If it is there and
+// set to hidden, do not show the soft keyboard. Node passed as a parameter
+// must not be null.
+static bool shouldSuppressKeyboard(const WebCore::Node* node) {
+ LOG_ASSERT(node, "node passed to shouldSuppressKeyboard cannot be null");
+ const NamedNodeMap* attributes = node->attributes();
+ if (!attributes) return false;
+ size_t length = attributes->length();
+ for (size_t i = 0; i < length; i++) {
+ const Attribute* a = attributes->attributeItem(i);
+ if (a->localName() == "x-webkit-soft-keyboard" && a->value() == "hidden")
+ return true;
+ }
+ return false;
+}
+
+// Common code for both clicking with the trackball and touchUp
+// Also used when typing into a non-focused textfield to give the textfield focus,
+// in which case, 'fake' is set to true
+bool WebViewCore::handleMouseClick(WebCore::Frame* framePtr, WebCore::Node* nodePtr, bool fake)
+{
+ bool valid = !framePtr || CacheBuilder::validNode(m_mainFrame, framePtr, nodePtr);
+ WebFrame* webFrame = WebFrame::getWebFrame(m_mainFrame);
+ if (valid && nodePtr) {
+ // Need to special case area tags because an image map could have an area element in the middle
+ // so when attempting to get the default, the point chosen would be follow the wrong link.
+ if (nodePtr->hasTagName(WebCore::HTMLNames::areaTag)) {
+ webFrame->setUserInitiatedAction(true);
+ nodePtr->dispatchSimulatedClick(0, true, true);
+ webFrame->setUserInitiatedAction(false);
+ DBG_NAV_LOG("area");
+ return true;
+ }
+ }
+ if (!valid || !framePtr)
+ framePtr = m_mainFrame;
+ webFrame->setUserInitiatedAction(true);
+ WebCore::PlatformMouseEvent mouseDown(m_mousePos, m_mousePos, WebCore::LeftButton,
+ WebCore::MouseEventPressed, 1, false, false, false, false,
+ WTF::currentTime());
+ // ignore the return from as it will return true if the hit point can trigger selection change
+ framePtr->eventHandler()->handleMousePressEvent(mouseDown);
+ WebCore::PlatformMouseEvent mouseUp(m_mousePos, m_mousePos, WebCore::LeftButton,
+ WebCore::MouseEventReleased, 1, false, false, false, false,
+ WTF::currentTime());
+ bool handled = framePtr->eventHandler()->handleMouseReleaseEvent(mouseUp);
+ webFrame->setUserInitiatedAction(false);
+
+ // If the user clicked on a textfield, make the focusController active
+ // so we show the blinking cursor.
+ WebCore::Node* focusNode = currentFocus();
+ DBG_NAV_LOGD("m_mousePos={%d,%d} focusNode=%p handled=%s", m_mousePos.x(),
+ m_mousePos.y(), focusNode, handled ? "true" : "false");
+ if (focusNode) {
+ WebCore::RenderObject* renderer = focusNode->renderer();
+ if (renderer && (renderer->isTextField() || renderer->isTextArea())) {
+ bool ime = !shouldSuppressKeyboard(focusNode)
+ && !(static_cast<WebCore::HTMLInputElement*>(focusNode))->readOnly();
+ if (ime) {
+#if ENABLE(WEB_AUTOFILL)
+ if (renderer->isTextField()) {
+ EditorClientAndroid* editorC = static_cast<EditorClientAndroid*>(framePtr->page()->editorClient());
+ WebAutoFill* autoFill = editorC->getAutoFill();
+ autoFill->formFieldFocused(static_cast<HTMLFormControlElement*>(focusNode));
+ }
+#endif
+ if (!fake) {
+ RenderTextControl* rtc
+ = static_cast<RenderTextControl*> (renderer);
+ requestKeyboardWithSelection(focusNode, rtc->selectionStart(),
+ rtc->selectionEnd());
+ }
+ } else if (!fake) {
+ requestKeyboard(false);
+ }
+ } else if (!fake){
+ // If the selection is contentEditable, show the keyboard so the
+ // user can type. Otherwise hide the keyboard because no text
+ // input is needed.
+ if (isContentEditable(focusNode)) {
+ requestKeyboard(true);
+ } else if (!nodeIsPlugin(focusNode)) {
+ clearTextEntry();
+ }
+ }
+ } else if (!fake) {
+ // There is no focusNode, so the keyboard is not needed.
+ clearTextEntry();
+ }
+ return handled;
+}
+
+void WebViewCore::popupReply(int index)
+{
+ if (m_popupReply) {
+ m_popupReply->replyInt(index);
+ Release(m_popupReply);
+ m_popupReply = 0;
+ }
+}
+
+void WebViewCore::popupReply(const int* array, int count)
+{
+ if (m_popupReply) {
+ m_popupReply->replyIntArray(array, count);
+ Release(m_popupReply);
+ m_popupReply = 0;
+ }
+}
+
+void WebViewCore::formDidBlur(const WebCore::Node* node)
+{
+ // If the blur is on a text input, keep track of the node so we can
+ // hide the soft keyboard when the new focus is set, if it is not a
+ // text input.
+ if (isTextInput(node))
+ m_blurringNodePointer = reinterpret_cast<int>(node);
+}
+
+void WebViewCore::focusNodeChanged(const WebCore::Node* newFocus)
+{
+ if (isTextInput(newFocus))
+ m_shouldPaintCaret = true;
+ else if (m_blurringNodePointer) {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_formDidBlur, m_blurringNodePointer);
+ checkException(env);
+ m_blurringNodePointer = 0;
+ }
+}
+
+void WebViewCore::addMessageToConsole(const WTF::String& message, unsigned int lineNumber, const WTF::String& sourceID, int msgLevel) {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jstring jMessageStr = wtfStringToJstring(env, message);
+ jstring jSourceIDStr = wtfStringToJstring(env, sourceID);
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_addMessageToConsole, jMessageStr, lineNumber,
+ jSourceIDStr, msgLevel);
+ env->DeleteLocalRef(jMessageStr);
+ env->DeleteLocalRef(jSourceIDStr);
+ checkException(env);
+}
+
+void WebViewCore::jsAlert(const WTF::String& url, const WTF::String& text)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jstring jInputStr = wtfStringToJstring(env, text);
+ jstring jUrlStr = wtfStringToJstring(env, url);
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_jsAlert, jUrlStr, jInputStr);
+ env->DeleteLocalRef(jInputStr);
+ env->DeleteLocalRef(jUrlStr);
+ checkException(env);
+}
+
+void WebViewCore::exceededDatabaseQuota(const WTF::String& url, const WTF::String& databaseIdentifier, const unsigned long long currentQuota, unsigned long long estimatedSize)
+{
+#if ENABLE(DATABASE)
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jstring jDatabaseIdentifierStr = wtfStringToJstring(env, databaseIdentifier);
+ jstring jUrlStr = wtfStringToJstring(env, url);
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_exceededDatabaseQuota, jUrlStr,
+ jDatabaseIdentifierStr, currentQuota, estimatedSize);
+ env->DeleteLocalRef(jDatabaseIdentifierStr);
+ env->DeleteLocalRef(jUrlStr);
+ checkException(env);
+#endif
+}
+
+void WebViewCore::reachedMaxAppCacheSize(const unsigned long long spaceNeeded)
+{
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_reachedMaxAppCacheSize, spaceNeeded);
+ checkException(env);
+#endif
+}
+
+void WebViewCore::populateVisitedLinks(WebCore::PageGroup* group)
+{
+ m_groupForVisitedLinks = group;
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_populateVisitedLinks);
+ checkException(env);
+}
+
+void WebViewCore::geolocationPermissionsShowPrompt(const WTF::String& origin)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jstring originString = wtfStringToJstring(env, origin);
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_geolocationPermissionsShowPrompt,
+ originString);
+ env->DeleteLocalRef(originString);
+ checkException(env);
+}
+
+void WebViewCore::geolocationPermissionsHidePrompt()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_geolocationPermissionsHidePrompt);
+ checkException(env);
+}
+
+jobject WebViewCore::getDeviceMotionService()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jobject object = env->CallObjectMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_getDeviceMotionService);
+ checkException(env);
+ return object;
+}
+
+jobject WebViewCore::getDeviceOrientationService()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jobject object = env->CallObjectMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_getDeviceOrientationService);
+ checkException(env);
+ return object;
+}
+
+bool WebViewCore::jsConfirm(const WTF::String& url, const WTF::String& text)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jstring jInputStr = wtfStringToJstring(env, text);
+ jstring jUrlStr = wtfStringToJstring(env, url);
+ jboolean result = env->CallBooleanMethod(m_javaGlue->object(env).get(), m_javaGlue->m_jsConfirm, jUrlStr, jInputStr);
+ env->DeleteLocalRef(jInputStr);
+ env->DeleteLocalRef(jUrlStr);
+ checkException(env);
+ return result;
+}
+
+bool WebViewCore::jsPrompt(const WTF::String& url, const WTF::String& text, const WTF::String& defaultValue, WTF::String& result)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jstring jUrlStr = wtfStringToJstring(env, url);
+ jstring jInputStr = wtfStringToJstring(env, text);
+ jstring jDefaultStr = wtfStringToJstring(env, defaultValue);
+ jstring returnVal = static_cast<jstring>(env->CallObjectMethod(m_javaGlue->object(env).get(), m_javaGlue->m_jsPrompt, jUrlStr, jInputStr, jDefaultStr));
+ env->DeleteLocalRef(jUrlStr);
+ env->DeleteLocalRef(jInputStr);
+ env->DeleteLocalRef(jDefaultStr);
+ checkException(env);
+
+ // If returnVal is null, it means that the user cancelled the dialog.
+ if (!returnVal)
+ return false;
+
+ result = jstringToWtfString(env, returnVal);
+ env->DeleteLocalRef(returnVal);
+ return true;
+}
+
+bool WebViewCore::jsUnload(const WTF::String& url, const WTF::String& message)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jstring jInputStr = wtfStringToJstring(env, message);
+ jstring jUrlStr = wtfStringToJstring(env, url);
+ jboolean result = env->CallBooleanMethod(m_javaGlue->object(env).get(), m_javaGlue->m_jsUnload, jUrlStr, jInputStr);
+ env->DeleteLocalRef(jInputStr);
+ env->DeleteLocalRef(jUrlStr);
+ checkException(env);
+ return result;
+}
+
+bool WebViewCore::jsInterrupt()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jboolean result = env->CallBooleanMethod(m_javaGlue->object(env).get(), m_javaGlue->m_jsInterrupt);
+ checkException(env);
+ return result;
+}
+
+AutoJObject
+WebViewCore::getJavaObject()
+{
+ return m_javaGlue->object(JSC::Bindings::getJNIEnv());
+}
+
+jobject
+WebViewCore::getWebViewJavaObject()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ return env->GetObjectField(m_javaGlue->object(env).get(), gWebViewCoreFields.m_webView);
+}
+
+void WebViewCore::updateTextSelection() {
+ WebCore::Node* focusNode = currentFocus();
+ if (!focusNode)
+ return;
+ RenderObject* renderer = focusNode->renderer();
+ if (!renderer || (!renderer->isTextArea() && !renderer->isTextField()))
+ return;
+ RenderTextControl* rtc = static_cast<RenderTextControl*>(renderer);
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_updateTextSelection, reinterpret_cast<int>(focusNode),
+ rtc->selectionStart(), rtc->selectionEnd(), m_textGeneration);
+ checkException(env);
+}
+
+void WebViewCore::updateTextfield(WebCore::Node* ptr, bool changeToPassword,
+ const WTF::String& text)
+{
+ if (m_blockTextfieldUpdates)
+ return;
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ if (changeToPassword) {
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_updateTextfield,
+ (int) ptr, true, 0, m_textGeneration);
+ checkException(env);
+ return;
+ }
+ jstring string = wtfStringToJstring(env, text);
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_updateTextfield,
+ (int) ptr, false, string, m_textGeneration);
+ env->DeleteLocalRef(string);
+ checkException(env);
+}
+
+void WebViewCore::clearTextEntry()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_clearTextEntry);
+}
+
+void WebViewCore::setBackgroundColor(SkColor c)
+{
+ WebCore::FrameView* view = m_mainFrame->view();
+ if (!view)
+ return;
+
+ // need (int) cast to find the right constructor
+ WebCore::Color bcolor((int)SkColorGetR(c), (int)SkColorGetG(c),
+ (int)SkColorGetB(c), (int)SkColorGetA(c));
+ view->setBaseBackgroundColor(bcolor);
+
+ // Background color of 0 indicates we want a transparent background
+ if (c == 0)
+ view->setTransparent(true);
+}
+
+jclass WebViewCore::getPluginClass(const WTF::String& libName, const char* className)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+
+ jstring libString = wtfStringToJstring(env, libName);
+ jstring classString = env->NewStringUTF(className);
+ jobject pluginClass = env->CallObjectMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_getPluginClass,
+ libString, classString);
+ checkException(env);
+
+ // cleanup unneeded local JNI references
+ env->DeleteLocalRef(libString);
+ env->DeleteLocalRef(classString);
+
+ if (pluginClass != NULL) {
+ return static_cast<jclass>(pluginClass);
+ } else {
+ return NULL;
+ }
+}
+
+void WebViewCore::showFullScreenPlugin(jobject childView, NPP npp)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ AutoJObject obj = m_javaGlue->object(env);
+
+ env->CallVoidMethod(obj.get(),
+ m_javaGlue->m_showFullScreenPlugin, childView, (int)npp);
+ checkException(env);
+}
+
+void WebViewCore::hideFullScreenPlugin()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_hideFullScreenPlugin);
+ checkException(env);
+}
+
+jobject WebViewCore::createSurface(jobject view)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jobject result = env->CallObjectMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_createSurface, view);
+ checkException(env);
+ return result;
+}
+
+jobject WebViewCore::addSurface(jobject view, int x, int y, int width, int height)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jobject result = env->CallObjectMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_addSurface,
+ view, x, y, width, height);
+ checkException(env);
+ return result;
+}
+
+void WebViewCore::updateSurface(jobject childView, int x, int y, int width, int height)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_updateSurface, childView,
+ x, y, width, height);
+ checkException(env);
+}
+
+void WebViewCore::destroySurface(jobject childView)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_destroySurface, childView);
+ checkException(env);
+}
+
+jobject WebViewCore::getContext()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ AutoJObject obj = m_javaGlue->object(env);
+
+ jobject result = env->CallObjectMethod(obj.get(), m_javaGlue->m_getContext);
+ checkException(env);
+ return result;
+}
+
+void WebViewCore::keepScreenOn(bool screenOn) {
+ if ((screenOn && m_screenOnCounter == 0) || (!screenOn && m_screenOnCounter == 1)) {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_keepScreenOn, screenOn);
+ checkException(env);
+ }
+
+ // update the counter
+ if (screenOn)
+ m_screenOnCounter++;
+ else if (m_screenOnCounter > 0)
+ m_screenOnCounter--;
+}
+
+bool WebViewCore::validNodeAndBounds(Frame* frame, Node* node,
+ const IntRect& originalAbsoluteBounds)
+{
+ bool valid = CacheBuilder::validNode(m_mainFrame, frame, node);
+ if (!valid)
+ return false;
+ RenderObject* renderer = node->renderer();
+ if (!renderer)
+ return false;
+ IntRect absBounds = node->hasTagName(HTMLNames::areaTag)
+ ? CacheBuilder::getAreaRect(static_cast<HTMLAreaElement*>(node))
+ : renderer->absoluteBoundingBoxRect();
+ return absBounds == originalAbsoluteBounds;
+}
+
+void WebViewCore::showRect(int left, int top, int width, int height,
+ int contentWidth, int contentHeight, float xPercentInDoc,
+ float xPercentInView, float yPercentInDoc, float yPercentInView)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_showRect,
+ left, top, width, height, contentWidth, contentHeight,
+ xPercentInDoc, xPercentInView, yPercentInDoc, yPercentInView);
+ checkException(env);
+}
+
+void WebViewCore::centerFitRect(int x, int y, int width, int height)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_centerFitRect, x, y, width, height);
+ checkException(env);
+}
+
+
+void WebViewCore::setScrollbarModes(ScrollbarMode horizontalMode, ScrollbarMode verticalMode)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_setScrollbarModes,
+ horizontalMode, verticalMode);
+ checkException(env);
+}
+
+void WebViewCore::notifyWebAppCanBeInstalled()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_setInstallableWebApp);
+ checkException(env);
+}
+
+#if ENABLE(VIDEO)
+void WebViewCore::enterFullscreenForVideoLayer(int layerId, const WTF::String& url)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jstring jUrlStr = wtfStringToJstring(env, url);
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_enterFullscreenForVideoLayer, layerId, jUrlStr);
+ checkException(env);
+}
+#endif
+
+void WebViewCore::setWebTextViewAutoFillable(int queryId, const string16& previewSummary)
+{
+#if ENABLE(WEB_AUTOFILL)
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jstring preview = env->NewString(previewSummary.data(), previewSummary.length());
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_setWebTextViewAutoFillable, queryId, preview);
+ env->DeleteLocalRef(preview);
+#endif
+}
+
+bool WebViewCore::drawIsPaused() const
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ return env->GetBooleanField(m_javaGlue->object(env).get(),
+ gWebViewCoreFields.m_drawIsPaused);
+}
+
+#if USE(CHROME_NETWORK_STACK)
+void WebViewCore::setWebRequestContextUserAgent()
+{
+ // We cannot create a WebRequestContext, because we might not know it this is a private tab or not yet
+ if (m_webRequestContext)
+ m_webRequestContext->setUserAgent(WebFrame::getWebFrame(m_mainFrame)->userAgentForURL(0)); // URL not used
+}
+
+void WebViewCore::setWebRequestContextCacheMode(int cacheMode)
+{
+ m_cacheMode = cacheMode;
+ // We cannot create a WebRequestContext, because we might not know it this is a private tab or not yet
+ if (!m_webRequestContext)
+ return;
+
+ m_webRequestContext->setCacheMode(cacheMode);
+}
+
+WebRequestContext* WebViewCore::webRequestContext()
+{
+ if (!m_webRequestContext) {
+ Settings* settings = mainFrame()->settings();
+ m_webRequestContext = new WebRequestContext(settings && settings->privateBrowsingEnabled());
+ setWebRequestContextUserAgent();
+ setWebRequestContextCacheMode(m_cacheMode);
+ }
+ return m_webRequestContext.get();
+}
+#endif
+
+void WebViewCore::scrollRenderLayer(int layer, const SkRect& rect)
+{
+#if USE(ACCELERATED_COMPOSITING)
+ GraphicsLayerAndroid* root = graphicsRootLayer();
+ if (!root)
+ return;
+
+ LayerAndroid* layerAndroid = root->platformLayer();
+ if (!layerAndroid)
+ return;
+
+ LayerAndroid* target = layerAndroid->findById(layer);
+ if (!target)
+ return;
+
+ RenderLayer* owner = target->owningLayer();
+ if (!owner)
+ return;
+
+ if (owner->stackingContext())
+ owner->scrollToOffset(rect.fLeft, rect.fTop, true, false);
+#endif
+}
+
+//----------------------------------------------------------------------
+// Native JNI methods
+//----------------------------------------------------------------------
+static void RevealSelection(JNIEnv *env, jobject obj)
+{
+ GET_NATIVE_VIEW(env, obj)->revealSelection();
+}
+
+static jstring RequestLabel(JNIEnv *env, jobject obj, int framePointer,
+ int nodePointer)
+{
+ return wtfStringToJstring(env, GET_NATIVE_VIEW(env, obj)->requestLabel(
+ (WebCore::Frame*) framePointer, (WebCore::Node*) nodePointer));
+}
+
+static void ClearContent(JNIEnv *env, jobject obj)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ viewImpl->clearContent();
+}
+
+static void UpdateFrameCacheIfLoading(JNIEnv *env, jobject obj)
+{
+ GET_NATIVE_VIEW(env, obj)->updateFrameCacheIfLoading();
+}
+
+static void SetSize(JNIEnv *env, jobject obj, jint width, jint height,
+ jint textWrapWidth, jfloat scale, jint screenWidth, jint screenHeight,
+ jint anchorX, jint anchorY, jboolean ignoreHeight)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOGV("webviewcore::nativeSetSize(%u %u)\n viewImpl: %p", (unsigned)width, (unsigned)height, viewImpl);
+ LOG_ASSERT(viewImpl, "viewImpl not set in nativeSetSize");
+ viewImpl->setSizeScreenWidthAndScale(width, height, textWrapWidth, scale,
+ screenWidth, screenHeight, anchorX, anchorY, ignoreHeight);
+}
+
+static void SetScrollOffset(JNIEnv *env, jobject obj, jint gen, jboolean sendScrollEvent, jint x, jint y)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "need viewImpl");
+
+ viewImpl->setScrollOffset(gen, sendScrollEvent, x, y);
+}
+
+static void SetGlobalBounds(JNIEnv *env, jobject obj, jint x, jint y, jint h,
+ jint v)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "need viewImpl");
+
+ viewImpl->setGlobalBounds(x, y, h, v);
+}
+
+static jboolean Key(JNIEnv *env, jobject obj, jint keyCode, jint unichar,
+ jint repeatCount, jboolean isShift, jboolean isAlt, jboolean isSym,
+ jboolean isDown)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ return GET_NATIVE_VIEW(env, obj)->key(PlatformKeyboardEvent(keyCode,
+ unichar, repeatCount, isDown, isShift, isAlt, isSym));
+}
+
+static void Click(JNIEnv *env, jobject obj, int framePtr, int nodePtr, jboolean fake)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in Click");
+
+ viewImpl->click(reinterpret_cast<WebCore::Frame*>(framePtr),
+ reinterpret_cast<WebCore::Node*>(nodePtr), fake);
+}
+
+static void ContentInvalidateAll(JNIEnv *env, jobject obj)
+{
+ GET_NATIVE_VIEW(env, obj)->contentInvalidateAll();
+}
+
+static void DeleteSelection(JNIEnv *env, jobject obj, jint start, jint end,
+ jint textGeneration)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ viewImpl->deleteSelection(start, end, textGeneration);
+}
+
+static void SetSelection(JNIEnv *env, jobject obj, jint start, jint end)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ viewImpl->setSelection(start, end);
+}
+
+static jstring ModifySelection(JNIEnv *env, jobject obj, jint direction, jint granularity)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ String selectionString = viewImpl->modifySelection(direction, granularity);
+ return wtfStringToJstring(env, selectionString);
+}
+
+static void ReplaceTextfieldText(JNIEnv *env, jobject obj,
+ jint oldStart, jint oldEnd, jstring replace, jint start, jint end,
+ jint textGeneration)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ WTF::String webcoreString = jstringToWtfString(env, replace);
+ viewImpl->replaceTextfieldText(oldStart,
+ oldEnd, webcoreString, start, end, textGeneration);
+}
+
+static void PassToJs(JNIEnv *env, jobject obj,
+ jint generation, jstring currentText, jint keyCode,
+ jint keyValue, jboolean down, jboolean cap, jboolean fn, jboolean sym)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WTF::String current = jstringToWtfString(env, currentText);
+ GET_NATIVE_VIEW(env, obj)->passToJs(generation, current,
+ PlatformKeyboardEvent(keyCode, keyValue, 0, down, cap, fn, sym));
+}
+
+static void ScrollFocusedTextInput(JNIEnv *env, jobject obj, jfloat xPercent,
+ jint y)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ viewImpl->scrollFocusedTextInput(xPercent, y);
+}
+
+static void SetFocusControllerActive(JNIEnv *env, jobject obj, jboolean active)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ LOGV("webviewcore::nativeSetFocusControllerActive()\n");
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in nativeSetFocusControllerActive");
+ viewImpl->setFocusControllerActive(active);
+}
+
+static void SaveDocumentState(JNIEnv *env, jobject obj, jint frame)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ LOGV("webviewcore::nativeSaveDocumentState()\n");
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in nativeSaveDocumentState");
+ viewImpl->saveDocumentState((WebCore::Frame*) frame);
+}
+
+void WebViewCore::addVisitedLink(const UChar* string, int length)
+{
+ if (m_groupForVisitedLinks)
+ m_groupForVisitedLinks->addVisitedLink(string, length);
+}
+
+static jint UpdateLayers(JNIEnv *env, jobject obj, jobject region)
+{
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ BaseLayerAndroid* result = viewImpl->createBaseLayer();
+ SkRegion* nativeRegion = GraphicsJNI::getNativeRegion(env, region);
+ if (result) {
+ SkIRect bounds;
+ LayerAndroid* root = static_cast<LayerAndroid*>(result->getChild(0));
+ if (root) {
+ root->bounds().roundOut(&bounds);
+ nativeRegion->setRect(bounds);
+ }
+ }
+ return reinterpret_cast<jint>(result);
+}
+
+static jint RecordContent(JNIEnv *env, jobject obj, jobject region, jobject pt)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ SkRegion* nativeRegion = GraphicsJNI::getNativeRegion(env, region);
+ SkIPoint nativePt;
+ BaseLayerAndroid* result = viewImpl->recordContent(nativeRegion, &nativePt);
+ GraphicsJNI::ipoint_to_jpoint(nativePt, env, pt);
+ return reinterpret_cast<jint>(result);
+}
+
+static void SplitContent(JNIEnv *env, jobject obj, jint content)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ viewImpl->splitContent(reinterpret_cast<PictureSet*>(content));
+}
+
+static void SendListBoxChoice(JNIEnv* env, jobject obj, jint choice)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in nativeSendListBoxChoice");
+ viewImpl->popupReply(choice);
+}
+
+// Set aside a predetermined amount of space in which to place the listbox
+// choices, to avoid unnecessary allocations.
+// The size here is arbitrary. We want the size to be at least as great as the
+// number of items in the average multiple-select listbox.
+#define PREPARED_LISTBOX_STORAGE 10
+
+static void SendListBoxChoices(JNIEnv* env, jobject obj, jbooleanArray jArray,
+ jint size)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in nativeSendListBoxChoices");
+ jboolean* ptrArray = env->GetBooleanArrayElements(jArray, 0);
+ SkAutoSTMalloc<PREPARED_LISTBOX_STORAGE, int> storage(size);
+ int* array = storage.get();
+ int count = 0;
+ for (int i = 0; i < size; i++) {
+ if (ptrArray[i]) {
+ array[count++] = i;
+ }
+ }
+ env->ReleaseBooleanArrayElements(jArray, ptrArray, JNI_ABORT);
+ viewImpl->popupReply(array, count);
+}
+
+static jstring FindAddress(JNIEnv *env, jobject obj, jstring addr,
+ jboolean caseInsensitive)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ if (!addr)
+ return 0;
+ int length = env->GetStringLength(addr);
+ if (!length)
+ return 0;
+ const jchar* addrChars = env->GetStringChars(addr, 0);
+ int start, end;
+ bool success = CacheBuilder::FindAddress(addrChars, length,
+ &start, &end, caseInsensitive) == CacheBuilder::FOUND_COMPLETE;
+ jstring ret = 0;
+ if (success)
+ ret = env->NewString(addrChars + start, end - start);
+ env->ReleaseStringChars(addr, addrChars);
+ return ret;
+}
+
+static jboolean HandleTouchEvent(JNIEnv *env, jobject obj, jint action, jintArray idArray,
+ jintArray xArray, jintArray yArray,
+ jint count, jint actionIndex, jint metaState)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+ jint* ptrIdArray = env->GetIntArrayElements(idArray, 0);
+ jint* ptrXArray = env->GetIntArrayElements(xArray, 0);
+ jint* ptrYArray = env->GetIntArrayElements(yArray, 0);
+ Vector<int> ids(count);
+ Vector<IntPoint> points(count);
+ for (int c = 0; c < count; c++) {
+ ids[c] = ptrIdArray[c];
+ points[c].setX(ptrXArray[c]);
+ points[c].setY(ptrYArray[c]);
+ }
+ env->ReleaseIntArrayElements(idArray, ptrIdArray, JNI_ABORT);
+ env->ReleaseIntArrayElements(xArray, ptrXArray, JNI_ABORT);
+ env->ReleaseIntArrayElements(yArray, ptrYArray, JNI_ABORT);
+
+ return viewImpl->handleTouchEvent(action, ids, points, actionIndex, metaState);
+}
+
+static void TouchUp(JNIEnv *env, jobject obj, jint touchGeneration,
+ jint frame, jint node, jint x, jint y)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+ viewImpl->touchUp(touchGeneration,
+ (WebCore::Frame*) frame, (WebCore::Node*) node, x, y);
+}
+
+static jstring RetrieveHref(JNIEnv *env, jobject obj, jint x, jint y)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+ WTF::String result = viewImpl->retrieveHref(x, y);
+ if (!result.isEmpty())
+ return wtfStringToJstring(env, result);
+ return 0;
+}
+
+static jstring RetrieveAnchorText(JNIEnv *env, jobject obj, jint x, jint y)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+ WTF::String result = viewImpl->retrieveAnchorText(x, y);
+ if (!result.isEmpty())
+ return wtfStringToJstring(env, result);
+ return 0;
+}
+
+static jstring RetrieveImageSource(JNIEnv *env, jobject obj, jint x, jint y)
+{
+ WTF::String result = GET_NATIVE_VIEW(env, obj)->retrieveImageSource(x, y);
+ return !result.isEmpty() ? wtfStringToJstring(env, result) : 0;
+}
+
+static void StopPaintingCaret(JNIEnv *env, jobject obj)
+{
+ GET_NATIVE_VIEW(env, obj)->setShouldPaintCaret(false);
+}
+
+static void MoveFocus(JNIEnv *env, jobject obj, jint framePtr, jint nodePtr)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+ viewImpl->moveFocus((WebCore::Frame*) framePtr, (WebCore::Node*) nodePtr);
+}
+
+static void MoveMouse(JNIEnv *env, jobject obj, jint frame,
+ jint x, jint y)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+ viewImpl->moveMouse((WebCore::Frame*) frame, x, y);
+}
+
+static void MoveMouseIfLatest(JNIEnv *env, jobject obj, jint moveGeneration,
+ jint frame, jint x, jint y)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+ viewImpl->moveMouseIfLatest(moveGeneration,
+ (WebCore::Frame*) frame, x, y);
+}
+
+static void UpdateFrameCache(JNIEnv *env, jobject obj)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+ viewImpl->updateFrameCache();
+}
+
+static jint GetContentMinPrefWidth(JNIEnv *env, jobject obj)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+
+ WebCore::Frame* frame = viewImpl->mainFrame();
+ if (frame) {
+ WebCore::Document* document = frame->document();
+ if (document) {
+ WebCore::RenderObject* renderer = document->renderer();
+ if (renderer && renderer->isRenderView()) {
+ return renderer->minPreferredLogicalWidth();
+ }
+ }
+ }
+ return 0;
+}
+
+static void SetViewportSettingsFromNative(JNIEnv *env, jobject obj)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+
+ WebCore::Settings* s = viewImpl->mainFrame()->page()->settings();
+ if (!s)
+ return;
+
+#ifdef ANDROID_META_SUPPORT
+ env->SetIntField(obj, gWebViewCoreFields.m_viewportWidth, s->viewportWidth());
+ env->SetIntField(obj, gWebViewCoreFields.m_viewportHeight, s->viewportHeight());
+ env->SetIntField(obj, gWebViewCoreFields.m_viewportInitialScale, s->viewportInitialScale());
+ env->SetIntField(obj, gWebViewCoreFields.m_viewportMinimumScale, s->viewportMinimumScale());
+ env->SetIntField(obj, gWebViewCoreFields.m_viewportMaximumScale, s->viewportMaximumScale());
+ env->SetBooleanField(obj, gWebViewCoreFields.m_viewportUserScalable, s->viewportUserScalable());
+ env->SetIntField(obj, gWebViewCoreFields.m_viewportDensityDpi, s->viewportTargetDensityDpi());
+#endif
+}
+
+static void SetBackgroundColor(JNIEnv *env, jobject obj, jint color)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+
+ viewImpl->setBackgroundColor((SkColor) color);
+}
+
+static void DumpDomTree(JNIEnv *env, jobject obj, jboolean useFile)
+{
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+
+ viewImpl->dumpDomTree(useFile);
+}
+
+static void DumpRenderTree(JNIEnv *env, jobject obj, jboolean useFile)
+{
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+
+ viewImpl->dumpRenderTree(useFile);
+}
+
+static void DumpNavTree(JNIEnv *env, jobject obj)
+{
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+
+ viewImpl->dumpNavTree();
+}
+
+static void DumpV8Counters(JNIEnv*, jobject)
+{
+#if USE(V8)
+#ifdef ANDROID_INSTRUMENT
+ V8Counters::dumpCounters();
+#endif
+#endif
+}
+
+static void SetJsFlags(JNIEnv *env, jobject obj, jstring flags)
+{
+#if USE(V8)
+ WTF::String flagsString = jstringToWtfString(env, flags);
+ WTF::CString utf8String = flagsString.utf8();
+ WebCore::ScriptController::setFlags(utf8String.data(), utf8String.length());
+#endif
+}
+
+
+// Called from the Java side to set a new quota for the origin or new appcache
+// max size in response to a notification that the original quota was exceeded or
+// that the appcache has reached its maximum size.
+static void SetNewStorageLimit(JNIEnv* env, jobject obj, jlong quota) {
+#if ENABLE(DATABASE) || ENABLE(OFFLINE_WEB_APPLICATIONS)
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ Frame* frame = viewImpl->mainFrame();
+
+ // The main thread is blocked awaiting this response, so now we can wake it
+ // up.
+ ChromeClientAndroid* chromeC = static_cast<ChromeClientAndroid*>(frame->page()->chrome()->client());
+ chromeC->wakeUpMainThreadWithNewQuota(quota);
+#endif
+}
+
+// Called from Java to provide a Geolocation permission state for the specified origin.
+static void GeolocationPermissionsProvide(JNIEnv* env, jobject obj, jstring origin, jboolean allow, jboolean remember) {
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ Frame* frame = viewImpl->mainFrame();
+
+ ChromeClientAndroid* chromeClient = static_cast<ChromeClientAndroid*>(frame->page()->chrome()->client());
+ chromeClient->provideGeolocationPermissions(jstringToWtfString(env, origin), allow, remember);
+}
+
+static void RegisterURLSchemeAsLocal(JNIEnv* env, jobject obj, jstring scheme) {
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebCore::SchemeRegistry::registerURLSchemeAsLocal(jstringToWtfString(env, scheme));
+}
+
+static bool FocusBoundsChanged(JNIEnv* env, jobject obj)
+{
+ return GET_NATIVE_VIEW(env, obj)->focusBoundsChanged();
+}
+
+static void Pause(JNIEnv* env, jobject obj)
+{
+ // This is called for the foreground tab when the browser is put to the
+ // background (and also for any tab when it is put to the background of the
+ // browser). The browser can only be killed by the system when it is in the
+ // background, so saving the Geolocation permission state now ensures that
+ // is maintained when the browser is killed.
+ ChromeClient* chromeClient = GET_NATIVE_VIEW(env, obj)->mainFrame()->page()->chrome()->client();
+ ChromeClientAndroid* chromeClientAndroid = static_cast<ChromeClientAndroid*>(chromeClient);
+ chromeClientAndroid->storeGeolocationPermissions();
+
+ Frame* mainFrame = GET_NATIVE_VIEW(env, obj)->mainFrame();
+ for (Frame* frame = mainFrame; frame; frame = frame->tree()->traverseNext()) {
+ Geolocation* geolocation = frame->domWindow()->navigator()->optionalGeolocation();
+ if (geolocation)
+ geolocation->suspend();
+ }
+
+ GET_NATIVE_VIEW(env, obj)->deviceMotionAndOrientationManager()->maybeSuspendClients();
+
+ ANPEvent event;
+ SkANP::InitEvent(&event, kLifecycle_ANPEventType);
+ event.data.lifecycle.action = kPause_ANPLifecycleAction;
+ GET_NATIVE_VIEW(env, obj)->sendPluginEvent(event);
+
+ GET_NATIVE_VIEW(env, obj)->setIsPaused(true);
+}
+
+static void Resume(JNIEnv* env, jobject obj)
+{
+ Frame* mainFrame = GET_NATIVE_VIEW(env, obj)->mainFrame();
+ for (Frame* frame = mainFrame; frame; frame = frame->tree()->traverseNext()) {
+ Geolocation* geolocation = frame->domWindow()->navigator()->optionalGeolocation();
+ if (geolocation)
+ geolocation->resume();
+ }
+
+ GET_NATIVE_VIEW(env, obj)->deviceMotionAndOrientationManager()->maybeResumeClients();
+
+ ANPEvent event;
+ SkANP::InitEvent(&event, kLifecycle_ANPEventType);
+ event.data.lifecycle.action = kResume_ANPLifecycleAction;
+ GET_NATIVE_VIEW(env, obj)->sendPluginEvent(event);
+
+ GET_NATIVE_VIEW(env, obj)->setIsPaused(false);
+}
+
+static void FreeMemory(JNIEnv* env, jobject obj)
+{
+ ANPEvent event;
+ SkANP::InitEvent(&event, kLifecycle_ANPEventType);
+ event.data.lifecycle.action = kFreeMemory_ANPLifecycleAction;
+ GET_NATIVE_VIEW(env, obj)->sendPluginEvent(event);
+}
+
+static void ProvideVisitedHistory(JNIEnv *env, jobject obj, jobject hist)
+{
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+
+ jobjectArray array = static_cast<jobjectArray>(hist);
+
+ jsize len = env->GetArrayLength(array);
+ for (jsize i = 0; i < len; i++) {
+ jstring item = static_cast<jstring>(env->GetObjectArrayElement(array, i));
+ const UChar* str = static_cast<const UChar*>(env->GetStringChars(item, 0));
+ jsize len = env->GetStringLength(item);
+ viewImpl->addVisitedLink(str, len);
+ env->ReleaseStringChars(item, str);
+ env->DeleteLocalRef(item);
+ }
+}
+
+// Notification from the UI thread that the plugin's full-screen surface has been discarded
+static void FullScreenPluginHidden(JNIEnv* env, jobject obj, jint npp)
+{
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ PluginWidgetAndroid* plugin = viewImpl->getPluginWidget((NPP)npp);
+ if (plugin)
+ plugin->exitFullScreen(false);
+}
+
+static WebCore::IntRect jrect_to_webrect(JNIEnv* env, jobject obj)
+{
+ int L, T, R, B;
+ GraphicsJNI::get_jrect(env, obj, &L, &T, &R, &B);
+ return WebCore::IntRect(L, T, R - L, B - T);
+}
+
+static bool ValidNodeAndBounds(JNIEnv *env, jobject obj, int frame, int node,
+ jobject rect)
+{
+ IntRect nativeRect = jrect_to_webrect(env, rect);
+ return GET_NATIVE_VIEW(env, obj)->validNodeAndBounds(
+ reinterpret_cast<Frame*>(frame),
+ reinterpret_cast<Node*>(node), nativeRect);
+}
+
+static jobject GetTouchHighlightRects(JNIEnv* env, jobject obj, jint x, jint y, jint slop)
+{
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ if (!viewImpl)
+ return 0;
+ Vector<IntRect> rects = viewImpl->getTouchHighlightRects(x, y, slop);
+ if (rects.isEmpty())
+ return 0;
+
+ jclass arrayClass = env->FindClass("java/util/ArrayList");
+ LOG_ASSERT(arrayClass, "Could not find java/util/ArrayList");
+ jmethodID init = env->GetMethodID(arrayClass, "<init>", "(I)V");
+ LOG_ASSERT(init, "Could not find constructor for ArrayList");
+ jobject array = env->NewObject(arrayClass, init, rects.size());
+ LOG_ASSERT(array, "Could not create a new ArrayList");
+ jmethodID add = env->GetMethodID(arrayClass, "add", "(Ljava/lang/Object;)Z");
+ LOG_ASSERT(add, "Could not find add method on ArrayList");
+ jclass rectClass = env->FindClass("android/graphics/Rect");
+ LOG_ASSERT(rectClass, "Could not find android/graphics/Rect");
+ jmethodID rectinit = env->GetMethodID(rectClass, "<init>", "(IIII)V");
+ LOG_ASSERT(rectinit, "Could not find init method on Rect");
+
+ for (size_t i = 0; i < rects.size(); i++) {
+ jobject rect = env->NewObject(rectClass, rectinit, rects[i].x(),
+ rects[i].y(), rects[i].right(), rects[i].bottom());
+ if (rect) {
+ env->CallBooleanMethod(array, add, rect);
+ env->DeleteLocalRef(rect);
+ }
+ }
+
+ env->DeleteLocalRef(rectClass);
+ env->DeleteLocalRef(arrayClass);
+ return array;
+}
+
+static void AutoFillForm(JNIEnv* env, jobject obj, jint queryId)
+{
+#if ENABLE(WEB_AUTOFILL)
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ if (!viewImpl)
+ return;
+
+ WebCore::Frame* frame = viewImpl->mainFrame();
+ if (frame) {
+ EditorClientAndroid* editorC = static_cast<EditorClientAndroid*>(frame->page()->editorClient());
+ WebAutoFill* autoFill = editorC->getAutoFill();
+ autoFill->fillFormFields(queryId);
+ }
+#endif
+}
+
+static void ScrollRenderLayer(JNIEnv* env, jobject obj, jint layer, jobject jRect)
+{
+ SkRect rect;
+ GraphicsJNI::jrect_to_rect(env, jRect, &rect);
+ GET_NATIVE_VIEW(env, obj)->scrollRenderLayer(layer, rect);
+}
+
+// ----------------------------------------------------------------------------
+
+/*
+ * JNI registration.
+ */
+static JNINativeMethod gJavaWebViewCoreMethods[] = {
+ { "nativeClearContent", "()V",
+ (void*) ClearContent },
+ { "nativeFocusBoundsChanged", "()Z",
+ (void*) FocusBoundsChanged } ,
+ { "nativeKey", "(IIIZZZZ)Z",
+ (void*) Key },
+ { "nativeClick", "(IIZ)V",
+ (void*) Click },
+ { "nativeContentInvalidateAll", "()V",
+ (void*) ContentInvalidateAll },
+ { "nativeSendListBoxChoices", "([ZI)V",
+ (void*) SendListBoxChoices },
+ { "nativeSendListBoxChoice", "(I)V",
+ (void*) SendListBoxChoice },
+ { "nativeSetSize", "(IIIFIIIIZ)V",
+ (void*) SetSize },
+ { "nativeSetScrollOffset", "(IZII)V",
+ (void*) SetScrollOffset },
+ { "nativeSetGlobalBounds", "(IIII)V",
+ (void*) SetGlobalBounds },
+ { "nativeSetSelection", "(II)V",
+ (void*) SetSelection } ,
+ { "nativeModifySelection", "(II)Ljava/lang/String;",
+ (void*) ModifySelection },
+ { "nativeDeleteSelection", "(III)V",
+ (void*) DeleteSelection } ,
+ { "nativeReplaceTextfieldText", "(IILjava/lang/String;III)V",
+ (void*) ReplaceTextfieldText } ,
+ { "nativeMoveFocus", "(II)V",
+ (void*) MoveFocus },
+ { "nativeMoveMouse", "(III)V",
+ (void*) MoveMouse },
+ { "nativeMoveMouseIfLatest", "(IIII)V",
+ (void*) MoveMouseIfLatest },
+ { "passToJs", "(ILjava/lang/String;IIZZZZ)V",
+ (void*) PassToJs },
+ { "nativeScrollFocusedTextInput", "(FI)V",
+ (void*) ScrollFocusedTextInput },
+ { "nativeSetFocusControllerActive", "(Z)V",
+ (void*) SetFocusControllerActive },
+ { "nativeSaveDocumentState", "(I)V",
+ (void*) SaveDocumentState },
+ { "nativeFindAddress", "(Ljava/lang/String;Z)Ljava/lang/String;",
+ (void*) FindAddress },
+ { "nativeHandleTouchEvent", "(I[I[I[IIII)Z",
+ (void*) HandleTouchEvent },
+ { "nativeTouchUp", "(IIIII)V",
+ (void*) TouchUp },
+ { "nativeRetrieveHref", "(II)Ljava/lang/String;",
+ (void*) RetrieveHref },
+ { "nativeRetrieveAnchorText", "(II)Ljava/lang/String;",
+ (void*) RetrieveAnchorText },
+ { "nativeRetrieveImageSource", "(II)Ljava/lang/String;",
+ (void*) RetrieveImageSource },
+ { "nativeStopPaintingCaret", "()V",
+ (void*) StopPaintingCaret },
+ { "nativeUpdateFrameCache", "()V",
+ (void*) UpdateFrameCache },
+ { "nativeGetContentMinPrefWidth", "()I",
+ (void*) GetContentMinPrefWidth },
+ { "nativeUpdateLayers", "(Landroid/graphics/Region;)I",
+ (void*) UpdateLayers },
+ { "nativeRecordContent", "(Landroid/graphics/Region;Landroid/graphics/Point;)I",
+ (void*) RecordContent },
+ { "setViewportSettingsFromNative", "()V",
+ (void*) SetViewportSettingsFromNative },
+ { "nativeSplitContent", "(I)V",
+ (void*) SplitContent },
+ { "nativeSetBackgroundColor", "(I)V",
+ (void*) SetBackgroundColor },
+ { "nativeRegisterURLSchemeAsLocal", "(Ljava/lang/String;)V",
+ (void*) RegisterURLSchemeAsLocal },
+ { "nativeDumpDomTree", "(Z)V",
+ (void*) DumpDomTree },
+ { "nativeDumpRenderTree", "(Z)V",
+ (void*) DumpRenderTree },
+ { "nativeDumpNavTree", "()V",
+ (void*) DumpNavTree },
+ { "nativeDumpV8Counters", "()V",
+ (void*) DumpV8Counters },
+ { "nativeSetNewStorageLimit", "(J)V",
+ (void*) SetNewStorageLimit },
+ { "nativeGeolocationPermissionsProvide", "(Ljava/lang/String;ZZ)V",
+ (void*) GeolocationPermissionsProvide },
+ { "nativePause", "()V", (void*) Pause },
+ { "nativeResume", "()V", (void*) Resume },
+ { "nativeFreeMemory", "()V", (void*) FreeMemory },
+ { "nativeSetJsFlags", "(Ljava/lang/String;)V", (void*) SetJsFlags },
+ { "nativeRequestLabel", "(II)Ljava/lang/String;",
+ (void*) RequestLabel },
+ { "nativeRevealSelection", "()V", (void*) RevealSelection },
+ { "nativeUpdateFrameCacheIfLoading", "()V",
+ (void*) UpdateFrameCacheIfLoading },
+ { "nativeProvideVisitedHistory", "([Ljava/lang/String;)V",
+ (void*) ProvideVisitedHistory },
+ { "nativeFullScreenPluginHidden", "(I)V",
+ (void*) FullScreenPluginHidden },
+ { "nativeValidNodeAndBounds", "(IILandroid/graphics/Rect;)Z",
+ (void*) ValidNodeAndBounds },
+ { "nativeGetTouchHighlightRects", "(III)Ljava/util/ArrayList;",
+ (void*) GetTouchHighlightRects },
+ { "nativeAutoFillForm", "(I)V",
+ (void*) AutoFillForm },
+ { "nativeScrollLayer", "(ILandroid/graphics/Rect;)V",
+ (void*) ScrollRenderLayer },
+};
+
+int registerWebViewCore(JNIEnv* env)
+{
+ jclass widget = env->FindClass("android/webkit/WebViewCore");
+ LOG_ASSERT(widget,
+ "Unable to find class android/webkit/WebViewCore");
+ gWebViewCoreFields.m_nativeClass = env->GetFieldID(widget, "mNativeClass",
+ "I");
+ LOG_ASSERT(gWebViewCoreFields.m_nativeClass,
+ "Unable to find android/webkit/WebViewCore.mNativeClass");
+ gWebViewCoreFields.m_viewportWidth = env->GetFieldID(widget,
+ "mViewportWidth", "I");
+ LOG_ASSERT(gWebViewCoreFields.m_viewportWidth,
+ "Unable to find android/webkit/WebViewCore.mViewportWidth");
+ gWebViewCoreFields.m_viewportHeight = env->GetFieldID(widget,
+ "mViewportHeight", "I");
+ LOG_ASSERT(gWebViewCoreFields.m_viewportHeight,
+ "Unable to find android/webkit/WebViewCore.mViewportHeight");
+ gWebViewCoreFields.m_viewportInitialScale = env->GetFieldID(widget,
+ "mViewportInitialScale", "I");
+ LOG_ASSERT(gWebViewCoreFields.m_viewportInitialScale,
+ "Unable to find android/webkit/WebViewCore.mViewportInitialScale");
+ gWebViewCoreFields.m_viewportMinimumScale = env->GetFieldID(widget,
+ "mViewportMinimumScale", "I");
+ LOG_ASSERT(gWebViewCoreFields.m_viewportMinimumScale,
+ "Unable to find android/webkit/WebViewCore.mViewportMinimumScale");
+ gWebViewCoreFields.m_viewportMaximumScale = env->GetFieldID(widget,
+ "mViewportMaximumScale", "I");
+ LOG_ASSERT(gWebViewCoreFields.m_viewportMaximumScale,
+ "Unable to find android/webkit/WebViewCore.mViewportMaximumScale");
+ gWebViewCoreFields.m_viewportUserScalable = env->GetFieldID(widget,
+ "mViewportUserScalable", "Z");
+ LOG_ASSERT(gWebViewCoreFields.m_viewportUserScalable,
+ "Unable to find android/webkit/WebViewCore.mViewportUserScalable");
+ gWebViewCoreFields.m_viewportDensityDpi = env->GetFieldID(widget,
+ "mViewportDensityDpi", "I");
+ LOG_ASSERT(gWebViewCoreFields.m_viewportDensityDpi,
+ "Unable to find android/webkit/WebViewCore.mViewportDensityDpi");
+ gWebViewCoreFields.m_webView = env->GetFieldID(widget,
+ "mWebView", "Landroid/webkit/WebView;");
+ LOG_ASSERT(gWebViewCoreFields.m_webView,
+ "Unable to find android/webkit/WebViewCore.mWebView");
+ gWebViewCoreFields.m_drawIsPaused = env->GetFieldID(widget,
+ "mDrawIsPaused", "Z");
+ LOG_ASSERT(gWebViewCoreFields.m_drawIsPaused,
+ "Unable to find android/webkit/WebViewCore.mDrawIsPaused");
+ gWebViewCoreFields.m_lowMemoryUsageMb = env->GetFieldID(widget, "mLowMemoryUsageThresholdMb", "I");
+ gWebViewCoreFields.m_highMemoryUsageMb = env->GetFieldID(widget, "mHighMemoryUsageThresholdMb", "I");
+ gWebViewCoreFields.m_highUsageDeltaMb = env->GetFieldID(widget, "mHighUsageDeltaMb", "I");
+
+ gWebViewCoreStaticMethods.m_isSupportedMediaMimeType =
+ env->GetStaticMethodID(widget, "isSupportedMediaMimeType", "(Ljava/lang/String;)Z");
+ LOG_FATAL_IF(!gWebViewCoreStaticMethods.m_isSupportedMediaMimeType,
+ "Could not find static method isSupportedMediaMimeType from WebViewCore");
+
+ env->DeleteLocalRef(widget);
+
+ return jniRegisterNativeMethods(env, "android/webkit/WebViewCore",
+ gJavaWebViewCoreMethods, NELEM(gJavaWebViewCoreMethods));
+}
+
+} /* namespace android */