summaryrefslogtreecommitdiffstats
path: root/WebKit/android/jni/WebViewCore.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'WebKit/android/jni/WebViewCore.cpp')
-rw-r--r--WebKit/android/jni/WebViewCore.cpp2502
1 files changed, 2502 insertions, 0 deletions
diff --git a/WebKit/android/jni/WebViewCore.cpp b/WebKit/android/jni/WebViewCore.cpp
new file mode 100644
index 0000000..363f2be
--- /dev/null
+++ b/WebKit/android/jni/WebViewCore.cpp
@@ -0,0 +1,2502 @@
+/*
+ * Copyright 2006, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "webcoreglue"
+
+#include <config.h>
+#include "WebViewCore.h"
+
+#include "AtomicString.h"
+#include "CachedNode.h"
+#include "CachedRoot.h"
+#include "Color.h"
+#include "Document.h"
+#include "Element.h"
+#include "Editor.h"
+#include "EditorClientAndroid.h"
+#include "EventHandler.h"
+#include "EventNames.h"
+#include "Font.h"
+#include "FrameLoader.h"
+#include "FrameTree.h"
+#include "FrameView.h"
+#include "GraphicsContext.h"
+#include "GraphicsJNI.h"
+#include "HitTestResult.h"
+#include "HTMLAnchorElement.h"
+#include "HTMLAreaElement.h"
+#include "HTMLElement.h"
+#include "HTMLImageElement.h"
+#include "HTMLInputElement.h"
+#include "HTMLMapElement.h"
+#include "HTMLNames.h"
+#include "HTMLOptGroupElement.h"
+#include "HTMLOptionElement.h"
+#include "HTMLSelectElement.h"
+#include "HTMLTextAreaElement.h"
+#include "InlineTextBox.h"
+#include <JNIHelp.h>
+#include "KeyboardCodes.h"
+#include "Node.h"
+#include "Page.h"
+#include "PlatformKeyboardEvent.h"
+#include "PlatformString.h"
+#include "PluginInfoStore.h"
+#include "PluginWidgetAndroid.h"
+#include "Position.h"
+#include "ProgressTracker.h"
+#include "RenderLayer.h"
+#include "RenderText.h"
+#include "RenderTextControl.h"
+#include "RenderThemeAndroid.h"
+#include "RenderView.h"
+#include "ResourceRequest.h"
+#include "SelectionController.h"
+#include "Settings.h"
+#include "SkTemplates.h"
+#include "SkTypes.h"
+#include "SkCanvas.h"
+#include "SkPicture.h"
+#include "SkUtils.h"
+#include "StringImpl.h"
+#include "SystemTime.h"
+#include "Text.h"
+#include "TypingCommand.h"
+#include "WebCoreFrameBridge.h"
+#include "WebFrameView.h"
+#include "HistoryItem.h"
+#include "android_graphics.h"
+#include <ui/KeycodeLabels.h>
+
+#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 "CString.h"
+
+FILE* gDomTreeFile = 0;
+FILE* gRenderTreeFile = 0;
+#endif
+
+#ifdef ANDROID_INSTRUMENT
+static uint32_t sTotalTimeUsed = 0;
+static uint32_t sTotalPaintTimeUsed = 0;
+static uint32_t sCounter = 0;
+
+namespace WebCore {
+void Frame::resetWebViewCoreTimeCounter()
+{
+ sTotalTimeUsed = 0;
+}
+
+void Frame::reportWebViewCoreTimeCounter()
+{
+ LOG(LOG_DEBUG, "WebCore", "*-* Total native 4 (webview core) time: %d ms\n",
+ sTotalTimeUsed);
+}
+// This should be in Frame.cpp, but android LOG is conflict with webcore LOG
+void Frame::resetPaintTimeCounter()
+{
+ sTotalPaintTimeUsed = 0;
+ sCounter = 0;
+}
+
+void Frame::reportPaintTimeCounter()
+{
+ LOG(LOG_DEBUG, "WebCore", "*-* Total draw time: %d ms called %d times\n",
+ sTotalPaintTimeUsed, sCounter);
+}
+}
+#endif
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+
+namespace android {
+
+#ifdef ANDROID_INSTRUMENT
+class TimeCounterWV {
+public:
+ TimeCounterWV() {
+ m_startTime = WebCore::get_thread_msec();
+ }
+
+ ~TimeCounterWV() {
+ sTotalTimeUsed += WebCore::get_thread_msec() - m_startTime;
+ }
+
+private:
+ uint32_t m_startTime;
+};
+#endif
+
+// ----------------------------------------------------------------------------
+
+#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_webView;
+} gWebViewCoreFields;
+
+// ----------------------------------------------------------------------------
+
+struct WebViewCore::JavaGlue {
+ JavaVM* m_JVM;
+ jobject m_obj;
+ jmethodID m_spawnScrollTo;
+ jmethodID m_scrollTo;
+ jmethodID m_scrollBy;
+ jmethodID m_contentDraw;
+ jmethodID m_requestListBox;
+ jmethodID m_requestSingleListBox;
+ jmethodID m_jsAlert;
+ jmethodID m_jsConfirm;
+ jmethodID m_jsPrompt;
+ jmethodID m_jsUnload;
+ jmethodID m_didFirstLayout;
+ jmethodID m_sendMarkNodeInvalid;
+ jmethodID m_sendNotifyFocusSet;
+ jmethodID m_sendNotifyProgressFinished;
+ jmethodID m_sendRecomputeFocus;
+ jmethodID m_sendViewInvalidate;
+ jmethodID m_updateTextfield;
+ jmethodID m_restoreScale;
+ jmethodID m_needTouchEvents;
+ 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::gFrameGenerationMutex;
+Mutex WebViewCore::gRecomputeFocusMutex;
+Mutex WebViewCore::gButtonMutex;
+Mutex WebViewCore::m_contentMutex;
+
+WebViewCore::WebViewCore(JNIEnv* env, jobject javaWebViewCore, WebCore::Frame* mainframe)
+ : m_pluginInvalTimer(this, &WebViewCore::pluginInvalTimerFired)
+{
+ m_mainFrame = mainframe;
+
+ m_popupReply = 0;
+ m_buildGeneration = 0;
+ m_moveGeneration = 0;
+ m_generation = 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_scale = 1;
+
+ 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_JVM = jnienv_to_javavm(env);
+ m_javaGlue->m_obj = adoptGlobalRef(env, javaWebViewCore);
+ m_javaGlue->m_spawnScrollTo = GetJMethod(env, clazz, "contentSpawnScrollTo", "(II)V");
+ m_javaGlue->m_scrollTo = GetJMethod(env, clazz, "contentScrollTo", "(II)V");
+ m_javaGlue->m_scrollBy = GetJMethod(env, clazz, "contentScrollBy", "(II)V");
+ m_javaGlue->m_contentDraw = GetJMethod(env, clazz, "contentDraw", "()V");
+ m_javaGlue->m_requestListBox = GetJMethod(env, clazz, "requestListBox", "([Ljava/lang/String;[Z[I)V");
+ m_javaGlue->m_requestSingleListBox = GetJMethod(env, clazz, "requestListBox", "([Ljava/lang/String;[ZI)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_didFirstLayout = GetJMethod(env, clazz, "didFirstLayout", "()V");
+ m_javaGlue->m_sendMarkNodeInvalid = GetJMethod(env, clazz, "sendMarkNodeInvalid", "(I)V");
+ m_javaGlue->m_sendNotifyFocusSet = GetJMethod(env, clazz, "sendNotifyFocusSet", "()V");
+ m_javaGlue->m_sendNotifyProgressFinished = GetJMethod(env, clazz, "sendNotifyProgressFinished", "()V");
+ m_javaGlue->m_sendRecomputeFocus = GetJMethod(env, clazz, "sendRecomputeFocus", "()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_restoreScale = GetJMethod(env, clazz, "restoreScale", "(I)V");
+ m_javaGlue->m_needTouchEvents = GetJMethod(env, clazz, "needTouchEvents", "(Z)V");
+
+ env->SetIntField(javaWebViewCore, gWebViewCoreFields.m_nativeClass, (jint)this);
+
+ m_scrollOffsetX = m_scrollOffsetY = 0;
+
+ reset(true);
+}
+
+WebViewCore::~WebViewCore()
+{
+ // Release the focused view
+ Release(m_popupReply);
+
+ if (m_javaGlue->m_obj) {
+ JNIEnv* env = javavm_to_jnienv(m_javaGlue->m_JVM);
+ env->DeleteGlobalRef(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)
+{
+ WebFrameView* webFrameView = static_cast<WebFrameView*>(view->platformWidget());
+ 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_lastFocusedBounds = WebCore::IntRect(0,0,0,0);
+ clearContent();
+ m_updatedFrameCache = true;
+ m_frameCacheOutOfDate = true;
+ m_blockFocusChange = false;
+ m_snapAnchorNode = 0;
+ m_useReplay = false;
+ m_skipContentDraw = false;
+ m_findIsUp = false;
+}
+
+static bool layoutIfNeededRecursive(WebCore::Frame* f)
+{
+ if (!f)
+ return true;
+
+ WebCore::FrameView* v = f->view();
+ if (!v)
+ return true;
+
+ if (v->needsLayout())
+ v->layout();
+
+ WebCore::Frame* child = f->tree()->firstChild();
+ bool success = true;
+ while (child) {
+ success &= layoutIfNeededRecursive(child);
+ child = child->tree()->nextSibling();
+ }
+
+ return success && !v->needsLayout();
+}
+
+void WebViewCore::recordPicture(SkPicture* picture)
+{
+ // if there is no document yet, just return
+ if (!m_mainFrame->document())
+ return;
+ // Call layout to ensure that the contentWidth and contentHeight are correct
+ if (!layoutIfNeededRecursive(m_mainFrame))
+ return;
+ // draw into the picture's recording canvas
+ WebCore::FrameView* view = m_mainFrame->view();
+ SkAutoPictureRecord arp(picture, view->contentsWidth(), view->contentsHeight());
+ 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, INT_MAX, INT_MAX));
+
+ 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 (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;
+ // 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();
+ 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);
+ picture->safeUnref();
+ }
+ // 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);
+ WebCore::CacheBuilder& builder = m_mainFrame->getCacheBuilder();
+ WebCore::Node* oldFocusNode = builder.currentFocus();
+ m_frameCacheOutOfDate = true;
+ WebCore::IntRect oldBounds = oldFocusNode ?
+ oldFocusNode->getRect() : WebCore::IntRect(0,0,0,0);
+ DBG_NAV_LOGD_THROTTLE("m_lastFocused=%p oldFocusNode=%p"
+ " m_lastFocusedBounds={%d,%d,%d,%d} oldBounds={%d,%d,%d,%d}",
+ m_lastFocused, oldFocusNode,
+ m_lastFocusedBounds.x(), m_lastFocusedBounds.y(), m_lastFocusedBounds.width(), m_lastFocusedBounds.height(),
+ oldBounds.x(), oldBounds.y(), oldBounds.width(), oldBounds.height());
+ if (m_lastFocused != oldFocusNode || m_lastFocusedBounds != oldBounds
+ || m_findIsUp) {
+ m_lastFocused = oldFocusNode;
+ m_lastFocusedBounds = oldBounds;
+ DBG_NAV_LOG("call updateFrameCache");
+ updateFrameCache();
+ }
+}
+
+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++;
+ }
+ }
+}
+
+void WebViewCore::clearContent()
+{
+ DBG_SET_LOG("");
+ m_contentMutex.lock();
+ m_content.clear();
+ m_contentMutex.unlock();
+ m_addInval.setEmpty();
+}
+
+void WebViewCore::copyContentToPicture(SkPicture* picture)
+{
+ DBG_SET_LOG("start");
+ m_contentMutex.lock();
+ PictureSet copyContent = PictureSet(m_content);
+ m_contentMutex.unlock();
+ copyContent.toPicture(picture);
+ DBG_SET_LOG("end");
+}
+
+bool WebViewCore::drawContent(SkCanvas* canvas, SkColor color)
+{
+ DBG_SET_LOG("start");
+ m_contentMutex.lock();
+ PictureSet copyContent = PictureSet(m_content);
+ m_contentMutex.unlock();
+ int sc = canvas->save(SkCanvas::kClip_SaveFlag);
+ SkRect clip;
+ clip.set(0, 0, copyContent.width(), copyContent.height());
+ canvas->clipRect(clip, SkRegion::kDifference_Op);
+ canvas->drawColor(color);
+ canvas->restoreToCount(sc);
+ bool tookTooLong = copyContent.draw(canvas);
+ m_contentMutex.lock();
+ m_content.setDrawTimes(copyContent);
+ m_contentMutex.unlock();
+ DBG_SET_LOG("end");
+ return tookTooLong;
+}
+
+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);
+ 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()));
+
+ 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("draw [%d] {%d,%d,w=%d,h=%d}", index, inval.fLeft,
+ inval.fTop, inval.width(), inval.height());
+ pictureSet->setPicture(index, rebuildPicture(inval));
+ }
+ pictureSet->validate(__FUNCTION__);
+}
+
+bool WebViewCore::recordContent(SkRegion* region, SkIPoint* point)
+{
+ DBG_SET_LOG("start");
+ m_contentMutex.lock();
+ PictureSet contentCopy(m_content);
+ m_contentMutex.unlock();
+ recordPictureSet(&contentCopy);
+ float progress = (float) m_mainFrame->page()->progress()->estimatedProgress();
+ if (progress > 0.0f && progress < 1.0f && contentCopy.isEmpty()) {
+ DBG_SET_LOGD("empty (progress=%g)", progress);
+ return false;
+ }
+ region->set(m_addInval);
+ m_addInval.setEmpty();
+ m_contentMutex.lock();
+ contentCopy.setDrawTimes(m_content);
+ m_content.set(contentCopy);
+ point->fX = m_content.width();
+ point->fY = m_content.height();
+ m_contentMutex.unlock();
+ 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 true;
+}
+
+void WebViewCore::splitContent()
+{
+ bool layoutSuceeded = layoutIfNeededRecursive(m_mainFrame);
+ LOG_ASSERT(layoutSuceeded, "Can never be called recursively");
+ PictureSet tempPictureSet;
+ m_contentMutex.lock();
+ m_content.split(&tempPictureSet);
+ m_contentMutex.unlock();
+ rebuildPictureSet(&tempPictureSet);
+ m_contentMutex.lock();
+ m_content.set(tempPictureSet);
+ m_contentMutex.unlock();
+}
+
+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 = javavm_to_jnienv(m_javaGlue->m_JVM);
+ env->CallVoidMethod(m_javaGlue->object(env).get(), animate ? m_javaGlue->m_spawnScrollTo : m_javaGlue->m_scrollTo, x, y);
+ checkException(env);
+}
+
+void WebViewCore::sendMarkNodeInvalid(WebCore::Node* node)
+{
+ LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
+ JNIEnv* env = javavm_to_jnienv(m_javaGlue->m_JVM);
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_sendMarkNodeInvalid, (int) node);
+ checkException(env);
+}
+
+void WebViewCore::sendNotifyFocusSet()
+{
+ LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
+ JNIEnv* env = javavm_to_jnienv(m_javaGlue->m_JVM);
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_sendNotifyFocusSet);
+ checkException(env);
+}
+
+void WebViewCore::sendNotifyProgressFinished()
+{
+ LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
+ JNIEnv* env = javavm_to_jnienv(m_javaGlue->m_JVM);
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_sendNotifyProgressFinished);
+ checkException(env);
+}
+
+void WebViewCore::sendRecomputeFocus()
+{
+ LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
+ JNIEnv* env = javavm_to_jnienv(m_javaGlue->m_JVM);
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_sendRecomputeFocus);
+ checkException(env);
+}
+
+void WebViewCore::viewInvalidate(const SkIRect& rect)
+{
+ LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
+ JNIEnv* env = javavm_to_jnienv(m_javaGlue->m_JVM);
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_sendViewInvalidate,
+ rect.fLeft, rect.fTop, rect.fRight, rect.fBottom);
+ 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 = javavm_to_jnienv(m_javaGlue->m_JVM);
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_sendViewInvalidate,
+ rect.x(), rect.y(), rect.right(), rect.bottom());
+ checkException(env);
+}
+
+void WebViewCore::scrollBy(int dx, int dy)
+{
+ if (!(dx | dy))
+ return;
+ JNIEnv* env = javavm_to_jnienv(m_javaGlue->m_JVM);
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_scrollBy, dx, dy);
+ checkException(env);
+}
+
+void WebViewCore::contentDraw()
+{
+ JNIEnv* env = javavm_to_jnienv(m_javaGlue->m_JVM);
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_contentDraw);
+ 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, max;
+ android_setrect(&rect, r);
+ max.set(0, 0, INT_MAX, INT_MAX);
+ if (!rect.intersect(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::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!");
+
+ const WebCore::KURL& url = m_mainFrame->loader()->url();
+ if (url.isEmpty())
+ return;
+ LOGV("::WebCore:: didFirstLayout %s", url.string().ascii().data());
+
+ JNIEnv* env = javavm_to_jnienv(m_javaGlue->m_JVM);
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_didFirstLayout);
+ checkException(env);
+
+ DBG_NAV_LOG("call updateFrameCache");
+ updateFrameCache();
+ m_history.setDidFirstLayout(true);
+}
+
+void WebViewCore::restoreScale(int scale)
+{
+ DEBUG_NAV_UI_LOGD("%s", __FUNCTION__);
+ LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
+
+ JNIEnv* env = javavm_to_jnienv(m_javaGlue->m_JVM);
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_restoreScale, scale);
+ 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) // Android
+ JNIEnv* env = javavm_to_jnienv(m_javaGlue->m_JVM);
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_needTouchEvents, need);
+ checkException(env);
+#endif
+}
+
+void WebViewCore::notifyFocusSet()
+{
+ sendNotifyFocusSet();
+}
+
+void WebViewCore::notifyProgressFinished()
+{
+ DBG_NAV_LOG("call updateFrameCache");
+ updateFrameCache();
+ sendNotifyProgressFinished();
+}
+
+void WebViewCore::doMaxScroll(WebCore::CacheBuilder::Direction dir)
+{
+ int dx = 0, dy = 0;
+
+ switch (dir) {
+ case WebCore::CacheBuilder::LEFT:
+ dx = -m_maxXScroll;
+ break;
+ case WebCore::CacheBuilder::UP:
+ dy = -m_maxYScroll;
+ break;
+ case WebCore::CacheBuilder::RIGHT:
+ dx = m_maxXScroll;
+ break;
+ case WebCore::CacheBuilder::DOWN:
+ dy = m_maxYScroll;
+ break;
+ case WebCore::CacheBuilder::UNINITIALIZED:
+ default:
+ LOG_ASSERT(0, "unexpected focus selector");
+ }
+ this->scrollBy(dx, dy);
+}
+
+void WebViewCore::setScrollOffset(int dx, int dy)
+{
+ DBG_NAV_LOGD("{%d,%d}", dx, dy);
+ if (m_scrollOffsetX != dx || m_scrollOffsetY != dy) {
+ m_scrollOffsetX = dx;
+ m_scrollOffsetY = dy;
+ m_mainFrame->sendScrollEvent();
+ // 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);
+ }
+}
+
+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 screenWidth, int scale)
+{
+ WebCoreViewBridge* window = m_mainFrame->view()->platformWidget();
+ int ow = window->width();
+ int oh = window->height();
+ window->setSize(width, height);
+ int osw = m_screenWidth;
+ m_screenWidth = screenWidth;
+ m_scale = scale;
+ m_maxXScroll = screenWidth >> 2;
+ m_maxYScroll = (screenWidth * height / width) >> 2;
+ DBG_NAV_LOGD("old:(w=%d,h=%d,s=%d) new:(w=%d,h=%d,s=%d)",
+ ow, oh, osw, width, height, screenWidth);
+ if (ow != width || oh != height || osw != screenWidth) {
+ WebCore::RenderObject *r = m_mainFrame->contentRenderer();
+ DBG_NAV_LOGD("renderer=%p", r);
+ if (r) {
+ r->setNeedsLayoutAndPrefWidthsRecalc();
+ m_mainFrame->forceLayout(true);
+ }
+ }
+}
+
+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
+ if (useFile)
+ gRenderTreeFile = fopen(RENDER_TREE_LOG_FILE, "w");
+ WebCore::CString renderDump = WebCore::externalRepresentation(m_mainFrame->contentRenderer()).utf8();
+ const char* data = renderDump.data();
+ int length = renderDump.length();
+ int last = 0;
+ for (int i = 0; i < length; i++) {
+ if (data[i] == '\n') {
+ if (i != last) {
+ char* chunk = new char[i - last + 1];
+ strncpy(chunk, (data + last), i - last);
+ chunk[i - last] = '\0';
+ DUMP_RENDER_LOGD("%s", chunk);
+ }
+ last = i + 1;
+ }
+ }
+ if (gRenderTreeFile) {
+ fclose(gRenderTreeFile);
+ gRenderTreeFile = 0;
+ }
+#endif
+}
+
+void WebViewCore::dumpNavTree()
+{
+#if DUMP_NAV_CACHE
+ m_mainFrame->getCacheBuilder().mDebug.print();
+#endif
+}
+
+WebCore::String WebViewCore::retrieveHref(WebCore::Frame* frame, WebCore::Node* node)
+{
+ WebCore::CacheBuilder& builder = m_mainFrame->getCacheBuilder();
+ if (!builder.validNode(frame, node))
+ return WebCore::String();
+ if (!node->hasTagName(WebCore::HTMLNames::aTag))
+ return WebCore::String();
+ WebCore::HTMLAnchorElement* anchor = static_cast<WebCore::HTMLAnchorElement*>(node);
+ return anchor->href();
+}
+
+bool WebViewCore::prepareFrameCache()
+{
+ if (!m_frameCacheOutOfDate) {
+ DBG_NAV_LOG("!m_frameCacheOutOfDate");
+ return false;
+ }
+ m_frameCacheOutOfDate = false;
+#if DEBUG_NAV_UI
+ DBG_NAV_LOG("m_frameCacheOutOfDate was true");
+ m_now = SkTime::GetMSecs();
+#endif
+ m_temp = new CachedRoot();
+ m_temp->init(m_mainFrame, &m_history);
+ m_temp->setGeneration(++m_buildGeneration);
+ WebCore::CacheBuilder& builder = m_mainFrame->getCacheBuilder();
+ 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);
+ return true;
+}
+
+void WebViewCore::releaseFrameCache(bool newCache)
+{
+ if (!newCache) {
+ DBG_NAV_LOG("!newCache");
+ return;
+ }
+ 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();
+ notifyFocusSet();
+ // it's tempting to send an invalidate here, but it's a bad idea
+ // the cache is now up to date, but the focus is not -- the event
+ // may need to be recomputed from the prior history. An invalidate
+ // will draw the stale location causing the ring to flash at the wrong place.
+}
+
+void WebViewCore::updateFrameCache()
+{
+ m_useReplay = false;
+ releaseFrameCache(prepareFrameCache());
+}
+
+void WebViewCore::removeFrameGeneration(WebCore::Frame* frame)
+{
+ DBG_NAV_LOGD("frame=%p m_generation=%d", frame, m_generation);
+ gFrameGenerationMutex.lock();
+ int last = m_frameGenerations.size() - 1;
+ for (int index = 0; index <= last; index++) {
+ if (m_frameGenerations[index].m_frame == frame) {
+ DBG_NAV_LOGD("index=%d last=%d", index, last);
+ if (index != last)
+ m_frameGenerations[index] = m_frameGenerations[last];
+ m_frameGenerations.removeLast();
+ break;
+ }
+ }
+ gFrameGenerationMutex.unlock();
+}
+
+void WebViewCore::updateFrameGeneration(WebCore::Frame* frame)
+{
+ DBG_NAV_LOGD("frame=%p m_generation=%d", frame, m_generation);
+ gFrameGenerationMutex.lock();
+ ++m_buildGeneration;
+ for (size_t index = 0; index < m_frameGenerations.size(); index++) {
+ if (m_frameGenerations[index].m_frame == frame) {
+ DBG_NAV_LOG("replace");
+ m_frameGenerations[index].m_generation = m_buildGeneration;
+ goto done;
+ }
+ }
+ {
+ FrameGen frameGen = {frame, m_buildGeneration};
+ m_frameGenerations.append(frameGen);
+ DBG_NAV_LOG("append");
+ }
+done:
+ gFrameGenerationMutex.unlock();
+}
+
+int WebViewCore::retrieveFrameGeneration(WebCore::Frame* frame)
+{
+ int result = INT_MAX;
+ gFrameGenerationMutex.lock();
+ for (size_t index = 0; index < m_frameGenerations.size(); index++) {
+ if (m_frameGenerations[index].m_frame == frame) {
+ result = m_frameGenerations[index].m_generation;
+ break;
+ }
+ }
+ gFrameGenerationMutex.unlock();
+ DBG_NAV_LOGD("frame=%p m_generation=%d result=%d", frame, m_generation, result);
+ return result;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void WebViewCore::addPlugin(PluginWidgetAndroid* w)
+{
+ SkDebugf("----------- addPlugin %p", w);
+ *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);
+ }
+}
+
+void WebViewCore::invalPlugin(PluginWidgetAndroid* w)
+{
+ const double PLUGIN_INVAL_DELAY = 0; // should this be non-zero?
+
+ 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();
+ w->localToPageCoords(&dirty);
+ inval.op(dirty, SkRegion::kUnion_Op);
+ }
+ }
+
+ if (!inval.isEmpty()) {
+ // inval.getBounds() is our rectangle
+ this->viewInvalidate(inval.getBounds());
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void WebViewCore::setFinalFocus(WebCore::Frame* frame, WebCore::Node* node,
+ int x, int y, bool block)
+{
+ DBG_NAV_LOGD("frame=%p node=%p x=%d y=%d", frame, node, x, y);
+ bool result = finalKitFocus(frame, node, x, y);
+ if (block) {
+ m_blockFocusChange = true;
+ if (!result && node)
+ touchUp(m_touchGeneration, 0, 0, 0, x, y, 0, true, true);
+ }
+}
+
+void WebViewCore::setKitFocus(int moveGeneration, int buildGeneration,
+ WebCore::Frame* frame, WebCore::Node* node, int x, int y,
+ bool ignoreNullFocus)
+{
+ DBG_NAV_LOGD("m_moveGeneration=%d moveGeneration=%d"
+ " buildGeneration=%d frame=%p node=%p x=%d y=%d",
+ m_moveGeneration, moveGeneration, buildGeneration, frame, node, x, y);
+ if (m_blockFocusChange) {
+ DBG_NAV_LOG("m_blockFocusChange");
+ return;
+ }
+ 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
+ }
+ if (!commonKitFocus(moveGeneration, buildGeneration, frame, node, x, y,
+ ignoreNullFocus))
+ return;
+ m_lastGeneration = moveGeneration;
+}
+
+bool WebViewCore::commonKitFocus(int generation, int buildGeneration,
+ WebCore::Frame* frame, WebCore::Node* node, int x, int y,
+ bool ignoreNullFocus)
+{
+ DBG_NAV_LOGD("generation=%d buildGeneration=%d frame=%p"
+ " node=%p x=%d y=%d", generation, buildGeneration, frame, node, x, y);
+ m_useReplay = true;
+ bool newCache = prepareFrameCache(); // must wait for possible recompute before using
+ if (m_moveGeneration > generation) {
+ DBG_NAV_LOGD("m_moveGeneration=%d > generation=%d",
+ m_moveGeneration, generation);
+ releaseFrameCache(newCache);
+ return false; // short-circuit if a newer move has already been generated
+ }
+ // if the nav cache has been rebuilt since this focus request was generated,
+ // send a request back to the UI side to recompute the kit-side focus
+ if (m_buildGeneration > buildGeneration || (node && !m_mainFrame->getCacheBuilder().validNode(frame, node))) {
+ DBG_NAV_LOGD("m_buildGeneration=%d > buildGeneration=%d",
+ m_buildGeneration, buildGeneration);
+ gRecomputeFocusMutex.lock();
+ bool first = !m_recomputeEvents.size();
+ m_recomputeEvents.append(generation);
+ gRecomputeFocusMutex.unlock();
+ releaseFrameCache(newCache);
+ if (first)
+ sendRecomputeFocus();
+ return false;
+ }
+ releaseFrameCache(newCache);
+ if (!node && ignoreNullFocus)
+ return true;
+ finalKitFocus(frame, node, x, y);
+ return true;
+}
+
+bool WebViewCore::finalKitFocus(WebCore::Frame* frame, WebCore::Node* node,
+ int x, int y)
+{
+ if (!frame)
+ frame = m_mainFrame;
+ WebCore::CacheBuilder& builder = m_mainFrame->getCacheBuilder();
+ WebCore::Node* oldFocusNode = builder.currentFocus();
+ // 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.
+ bool valid = builder.validNode(frame, node);
+ if (valid) {
+ WebCore::PlatformMouseEvent mouseEvent(m_mousePos, m_mousePos, WebCore::NoButton,
+ WebCore::MouseEventMoved, 1, false, false, false, false, WebCore::currentTime());
+ frame->eventHandler()->handleMouseMoveEvent(mouseEvent);
+ }
+ WebCore::Document* oldDoc = oldFocusNode ? oldFocusNode->document() : 0;
+ if (!node) {
+ if (oldFocusNode)
+ oldDoc->setFocusedNode(0);
+ return false;
+ } else if (!valid) {
+ DBG_NAV_LOGD("sendMarkNodeInvalid node=%p", node);
+ sendMarkNodeInvalid(node);
+ if (oldFocusNode)
+ oldDoc->setFocusedNode(0);
+ return false;
+ }
+ // If we jump frames (docs), kill the focus on the old doc
+ builder.setLastFocus(node);
+ if (oldFocusNode && node->document() != oldDoc) {
+ oldDoc->setFocusedNode(0);
+ }
+ if (!node->isTextNode())
+ static_cast<WebCore::Element*>(node)->focus(false);
+ //setFocus on things that WebCore doesn't recognize as supporting focus
+ //for instance, if there is an onclick element that does not support focus
+ DBG_NAV_LOGD("setFocusedNode node=%p", node);
+ node->document()->setFocusedNode(node);
+ m_lastFocused = node;
+ m_lastFocusedBounds = node->getRect();
+ return true;
+}
+
+// helper function to find the frame that has focus
+static WebCore::Frame* FocusedFrame(WebCore::Frame* frame)
+{
+ if (!frame)
+ return 0;
+ WebCore::Node* focusNode = frame->getCacheBuilder().currentFocus();
+ if (!focusNode)
+ return 0;
+ WebCore::Document* doc = focusNode->document();
+ if (!doc)
+ return 0;
+ return doc->frame();
+}
+
+static WebCore::RenderTextControl* FocusedTextControl(WebCore::Frame* frame)
+{
+ WebCore::Node* focusNode = frame->getCacheBuilder().currentFocus();
+ WebCore::RenderObject* renderer = focusNode->renderer();
+ if (renderer && (renderer->isTextField() || renderer->isTextArea())) {
+ return static_cast<WebCore::RenderTextControl*>(renderer);
+ }
+ return 0;
+}
+
+WebCore::Frame* WebViewCore::changedKitFocus(WebCore::Frame* frame,
+ WebCore::Node* node, int x, int y)
+{
+ if (!frame || !node)
+ return m_mainFrame;
+ WebCore::Node* current = m_mainFrame->getCacheBuilder().currentFocus();
+ if (current == node)
+ return frame;
+ return finalKitFocus(frame, node, x, y) ? frame : m_mainFrame;
+}
+
+static int findTextBoxIndex(WebCore::Node* node, const WebCore::IntPoint& pt)
+{
+ if (!node->isTextNode()) {
+ DBG_NAV_LOGD("node=%p pt=(%d,%d) isText=false", node, pt.x(), pt.y());
+ return -2; // error
+ }
+ WebCore::RenderText* renderText = (WebCore::RenderText*) node->renderer();
+ if (!renderText) {
+ DBG_NAV_LOGD("node=%p pt=(%d,%d) renderText=0", node, pt.x(), pt.y());
+ return -3; // error
+ }
+ int renderX, renderY;
+ renderText->absolutePosition(renderX, renderY);
+ WebCore::InlineTextBox *textBox = renderText->firstTextBox();
+ int globalX, globalY;
+ WebCore::CacheBuilder::GetGlobalOffset(node, &globalX, &globalY);
+ int x = pt.x() - globalX;
+ int y = pt.y() - globalY;
+ do {
+ int textBoxStart = textBox->start();
+ int textBoxEnd = textBoxStart + textBox->len();
+ if (textBoxEnd <= textBoxStart)
+ continue;
+ WebCore::IntRect bounds = textBox->selectionRect(renderX, renderY,
+ textBoxStart, textBoxEnd);
+ if (!bounds.contains(x, y))
+ continue;
+ int offset = textBox->offsetForPosition(x - renderX);
+#if DEBUG_NAV_UI
+ int prior = offset > 0 ? textBox->positionForOffset(offset - 1) : -1;
+ int current = textBox->positionForOffset(offset);
+ int next = textBox->positionForOffset(offset + 1);
+ DBG_NAV_LOGD(
+ "offset=%d pt.x=%d globalX=%d renderX=%d x=%d "
+ "textBox->x()=%d textBox->start()=%d prior=%d current=%d next=%d",
+ offset, pt.x(), globalX, renderX, x,
+ textBox->xPos(), textBox->start(), prior, current, next
+ );
+#endif
+ return textBox->start() + offset;
+ } while ((textBox = textBox->nextTextBox()));
+ return -1; // couldn't find point, may have walked off end
+}
+
+static inline bool isPunctuation(UChar c)
+{
+ return WTF::Unicode::category(c) & (0
+ | WTF::Unicode::Punctuation_Dash
+ | WTF::Unicode::Punctuation_Open
+ | WTF::Unicode::Punctuation_Close
+ | WTF::Unicode::Punctuation_Connector
+ | WTF::Unicode::Punctuation_Other
+ | WTF::Unicode::Punctuation_InitialQuote
+ | WTF::Unicode::Punctuation_FinalQuote
+ );
+}
+
+static int centerX(const SkIRect& rect)
+{
+ return (rect.fLeft + rect.fRight) >> 1;
+}
+
+static int centerY(const SkIRect& rect)
+{
+ return (rect.fTop + rect.fBottom) >> 1;
+}
+
+WebCore::String WebViewCore::getSelection(SkRegion* selRgn)
+{
+ SkRegion::Iterator iter(*selRgn);
+ // FIXME: switch this to use StringBuilder instead
+ WebCore::String result;
+ WebCore::Node* lastStartNode = 0;
+ int lastStartEnd = -1;
+ UChar lastChar = 0xffff;
+ for (; !iter.done(); iter.next()) {
+ const SkIRect& rect = iter.rect();
+ DBG_NAV_LOGD("rect=(%d, %d, %d, %d)", rect.fLeft, rect.fTop,
+ rect.fRight, rect.fBottom);
+ int cy = centerY(rect);
+ WebCore::IntPoint startPt = WebCore::IntPoint(rect.fLeft + 1, cy);
+ WebCore::HitTestResult hitTestResult = m_mainFrame->eventHandler()->
+ hitTestResultAtPoint(startPt, false);
+ WebCore::Node* node = hitTestResult.innerNode();
+ if (!node) {
+ DBG_NAV_LOG("!node");
+ return result;
+ }
+ WebCore::IntPoint endPt = WebCore::IntPoint(rect.fRight - 2, cy);
+ hitTestResult = m_mainFrame->eventHandler()->hitTestResultAtPoint(endPt, false);
+ WebCore::Node* endNode = hitTestResult.innerNode();
+ if (!endNode) {
+ DBG_NAV_LOG("!endNode");
+ return result;
+ }
+ int start = findTextBoxIndex(node, startPt);
+ if (start < 0)
+ continue;
+ int end = findTextBoxIndex(endNode, endPt);
+ if (end < -1) // use node if endNode is not valid
+ endNode = node;
+ if (end <= 0)
+ end = static_cast<WebCore::Text*>(endNode)->string()->length();
+ DBG_NAV_LOGD("node=%p start=%d endNode=%p end=%d", node, start, endNode, end);
+ WebCore::Node* startNode = node;
+ do {
+ if (!node->isTextNode())
+ continue;
+ if (node->getRect().isEmpty())
+ continue;
+ WebCore::Text* textNode = static_cast<WebCore::Text*>(node);
+ WebCore::StringImpl* string = textNode->string();
+ if (!string->length())
+ continue;
+ const UChar* chars = string->characters();
+ int newLength = node == endNode ? end : string->length();
+ if (node == startNode) {
+ #if DEBUG_NAV_UI
+ if (node == lastStartNode)
+ DBG_NAV_LOGD("start=%d last=%d", start, lastStartEnd);
+ #endif
+ if (node == lastStartNode && start < lastStartEnd)
+ break; // skip rect if text overlaps already written text
+ lastStartNode = node;
+ lastStartEnd = newLength - start;
+ }
+ if (newLength < start) {
+ DBG_NAV_LOGD("newLen=%d < start=%d", newLength, start);
+ break;
+ }
+ if (!isPunctuation(chars[start]))
+ result.append(' ');
+ result.append(chars + start, newLength - start);
+ start = 0;
+ } while (node != endNode && (node = node->traverseNextNode()));
+ }
+ result = result.simplifyWhiteSpace().stripWhiteSpace();
+#if DUMP_NAV_CACHE
+ {
+ char buffer[256];
+ WebCore::CacheBuilder::Debug debug;
+ debug.init(buffer, sizeof(buffer));
+ debug.print("copy: ");
+ debug.wideString(result);
+ DUMP_NAV_LOGD("%s", buffer);
+ }
+#endif
+ return result;
+}
+
+static void selectInFrame(WebCore::Frame* frame, int start, int end)
+{
+ WebCore::Document* doc = frame->document();
+ if (!doc)
+ return;
+
+ WebCore::Node* focus = doc->focusedNode();
+ if (!focus)
+ return;
+
+ WebCore::RenderObject* renderer = focus->renderer();
+ if (renderer && (renderer->isTextField() || renderer->isTextArea())) {
+ WebCore::RenderTextControl* rtc = static_cast<WebCore::RenderTextControl*>(renderer);
+ if (start > end) {
+ int temp = start;
+ start = end;
+ end = temp;
+ }
+ rtc->setSelectionRange(start, end);
+ frame->revealSelection();
+ }
+}
+
+WebCore::Frame* WebViewCore::setSelection(WebCore::Frame* frame, WebCore::Node* node,
+ int x, int y, int start, int end)
+{
+ // FIXME: Consider using a generation number to avoid doing this many more times than necessary.
+ frame = changedKitFocus(frame, node, x, y);
+ if (!frame)
+ return 0;
+ selectInFrame(frame, start, end);
+ return frame;
+}
+
+// Shortcut for no modifier keys
+#define NO_MODIFIER_KEYS (static_cast<WebCore::PlatformKeyboardEvent::ModifierKey>(0))
+
+WebCore::Frame* WebViewCore::deleteSelection(WebCore::Frame* frame, WebCore::Node* node,
+ int x, int y, int start, int end)
+{
+ frame = setSelection(frame, node, x, y, start, end);
+ if (start != end) {
+ WebCore::PlatformKeyboardEvent downEvent(kKeyCodeDel, WebCore::VK_BACK,
+ WebCore::PlatformKeyboardEvent::KeyDown, 0, NO_MODIFIER_KEYS);
+ frame->eventHandler()->keyEvent(downEvent);
+ WebCore::PlatformKeyboardEvent upEvent(kKeyCodeDel, WebCore::VK_BACK,
+ WebCore::PlatformKeyboardEvent::KeyUp, 0, NO_MODIFIER_KEYS);
+ frame->eventHandler()->keyEvent(upEvent);
+ }
+ return frame;
+}
+
+void WebViewCore::replaceTextfieldText(WebCore::Frame* frame, WebCore::Node* node, int x, int y,
+ int oldStart, int oldEnd, jstring replace, int start, int end)
+{
+ JNIEnv* env = javavm_to_jnienv(m_javaGlue->m_JVM);
+
+ WebCore::String webcoreString = to_string(env, replace);
+ frame = setSelection(frame, node, x, y, oldStart, oldEnd);
+ WebCore::TypingCommand::insertText(frame->document(), webcoreString, false);
+ selectInFrame(frame, start, end);
+}
+
+void WebViewCore::passToJs(WebCore::Frame* frame, WebCore::Node* node, int x, int y, int generation,
+ jstring currentText, int keyCode, int keyValue, bool down, bool cap, bool fn, bool sym)
+{
+ frame = changedKitFocus(frame, node, x, y);
+ // Construct the ModifierKey value
+ int mods = 0;
+ if (cap) {
+ mods |= WebCore::PlatformKeyboardEvent::ShiftKey;
+ }
+ if (fn) {
+ mods |= WebCore::PlatformKeyboardEvent::AltKey;
+ }
+ if (sym) {
+ mods |= WebCore::PlatformKeyboardEvent::CtrlKey;
+ }
+ WebCore::PlatformKeyboardEvent event(keyCode, keyValue,
+ down ? WebCore::PlatformKeyboardEvent::KeyDown :
+ WebCore::PlatformKeyboardEvent::KeyUp,
+ 0, static_cast<WebCore::PlatformKeyboardEvent::ModifierKey>(mods));
+ // Block text field updates during a key press.
+ m_blockTextfieldUpdates = true;
+ frame->eventHandler()->keyEvent(event);
+ m_blockTextfieldUpdates = false;
+ m_textGeneration = generation;
+
+ WebCore::Node* currentFocus = m_mainFrame->getCacheBuilder().currentFocus();
+ // Make sure we have the same focus and it is a text field.
+ if (node == currentFocus && currentFocus) {
+ WebCore::RenderObject* renderer = currentFocus->renderer();
+ if (renderer && (renderer->isTextField() || renderer->isTextArea())) {
+ WebCore::RenderTextControl* renderText = static_cast<WebCore::RenderTextControl*>(renderer);
+ JNIEnv* env = javavm_to_jnienv(m_javaGlue->m_JVM);
+ WebCore::String current = to_string(env, currentText);
+ WebCore::String test = renderText->text();
+ // If the text changed during the key event, update the UI text field.
+ if (test != current)
+ updateTextfield(currentFocus, false, test);
+ }
+ }
+}
+
+void WebViewCore::saveDocumentState(WebCore::Frame* frame, WebCore::Node* node, int x, int y)
+{
+ frame = changedKitFocus(frame, node, x, y);
+ WebCore::HistoryItem *item = frame->loader()->currentHistoryItem();
+
+ // 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());
+ }
+}
+
+// Convert a WebCore::String into an array of characters where the first
+// character represents the length, for easy conversion to java.
+static uint16_t* stringConverter(const WebCore::String& text)
+{
+ size_t length = text.length();
+ uint16_t* itemName = new uint16_t[length+1];
+ itemName[0] = (uint16_t)length;
+ uint16_t* firstChar = &(itemName[1]);
+ memcpy((void*)firstChar, text.characters(), sizeof(UChar)*length);
+ return itemName;
+}
+
+// Response to dropdown created for a listbox.
+class ListBoxReply : public WebCoreReply {
+public:
+ ListBoxReply(WebCore::HTMLSelectElement* select, WebCore::Frame* frame, WebViewCore* view)
+ : m_select(select)
+ , m_frame(frame)
+ , m_viewImpl(view)
+ {}
+
+ // Response used if the listbox only allows single selection.
+ // index is listIndex of the selected item, or -1 if nothing is selected.
+ virtual void replyInt(int index)
+ {
+ // If the select element no longer exists, do to a page change, etc, silently return.
+ if (!m_select || !m_viewImpl->m_mainFrame->getCacheBuilder().validNode(m_frame, m_select))
+ return;
+ if (-1 == index) {
+ if (m_select->selectedIndex() != -1) {
+#ifdef ANDROID_DESELECT_SELECT
+ m_select->deselectItems();
+#endif
+ m_select->onChange();
+ m_viewImpl->contentInvalidate(m_select->getRect());
+ }
+ return;
+ }
+ WebCore::HTMLOptionElement* option = static_cast<WebCore::HTMLOptionElement*>(
+ m_select->item(m_select->listToOptionIndex(index)));
+ if (!option->selected()) {
+ option->setSelected(true);
+ m_select->onChange();
+ m_viewImpl->contentInvalidate(m_select->getRect());
+ }
+ }
+
+ // Response if the listbox allows multiple selection. array stores the listIndices
+ // of selected positions.
+ virtual void replyIntArray(const int* array, int count)
+ {
+ // If the select element no longer exists, do to a page change, etc, silently return.
+ if (!m_select || !m_viewImpl->m_mainFrame->getCacheBuilder().validNode(m_frame, m_select))
+ return;
+#ifdef ANDROID_DESELECT_SELECT
+ m_select->deselectItems();
+#endif
+ WebCore::HTMLOptionElement* option;
+ for (int i = 0; i < count; i++) {
+ option = static_cast<WebCore::HTMLOptionElement*>(
+ m_select->item(array[m_select->listToOptionIndex(i)]));
+ option->setSelected(true);
+ }
+ m_viewImpl->contentInvalidate(m_select->getRect());
+ }
+private:
+ // The select element associated with this listbox.
+ WebCore::HTMLSelectElement* m_select;
+ // The frame of this select element, to verify that it is valid.
+ WebCore::Frame* m_frame;
+ // For calling invalidate and checking the select element's validity
+ WebViewCore* m_viewImpl;
+};
+
+// 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::listBoxRequest(WebCoreReply* reply, const uint16_t** labels, size_t count, const int enabled[], size_t enabledCount,
+ bool multiple, const int selected[], size_t selectedCountOrSelection)
+{
+ // Reuse m_popupReply
+ Release(m_popupReply);
+ m_popupReply = 0;
+
+ 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 = javavm_to_jnienv(m_javaGlue->m_JVM);
+ jobjectArray labelArray = makeLabelArray(env, labels, count);
+
+ // Create an array determining whether each item is enabled.
+ jbooleanArray enabledArray = env->NewBooleanArray(enabledCount);
+ checkException(env);
+ jboolean* ptrArray = env->GetBooleanArrayElements(enabledArray, 0);
+ checkException(env);
+ for (size_t i = 0; i < enabledCount; i++) {
+ ptrArray[i] = enabled[i];
+ }
+ env->ReleaseBooleanArrayElements(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::keyUp(KeyCode keyCode, int keyVal)
+{
+ DBG_NAV_LOGD("keyCode=%d", keyCode);
+ bool keyHandled = false;
+ WebCore::Node* focusNode = m_mainFrame->getCacheBuilder().currentFocus();
+ if (focusNode) {
+ WebCore::Frame* focusFrame = focusNode->document()->frame();
+ switch (keyCode) {
+ case kKeyCodeNewline:
+ case kKeyCodeDpadCenter: {
+ focusFrame->loader()->resetMultipleFormSubmissionProtection();
+ WebFrame* webFrame = WebFrame::getWebFrame(m_mainFrame);
+ webFrame->setUserInitiatedClick(true);
+ if ((focusNode->hasTagName(WebCore::HTMLNames::inputTag) &&
+ ((WebCore::HTMLInputElement*)focusNode)->isTextField()) ||
+ focusNode->hasTagName(WebCore::HTMLNames::textareaTag)) {
+ // Create the key down event.
+ WebCore::PlatformKeyboardEvent keydown(keyCode, keyVal,
+ WebCore::PlatformKeyboardEvent::KeyDown, 0,
+ NO_MODIFIER_KEYS);
+ // Create the key up event.
+ WebCore::PlatformKeyboardEvent keyup(keyCode, keyVal,
+ WebCore::PlatformKeyboardEvent::KeyUp, 0,
+ NO_MODIFIER_KEYS);
+ // Send both events.
+ keyHandled = focusFrame->eventHandler()->keyEvent(keydown);
+ keyHandled |= focusFrame->eventHandler()->keyEvent(keyup);
+ } else {
+ keyHandled = handleMouseClick(focusFrame, focusNode);
+ }
+ webFrame->setUserInitiatedClick(false);
+ break;
+ }
+ default:
+ keyHandled = false;
+ }
+ }
+ m_blockFocusChange = false;
+ return keyHandled;
+}
+
+bool WebViewCore::sendKeyToFocusNode(int keyCode, UChar32 unichar,
+ int repeatCount, bool isShift, bool isAlt, KeyAction action) {
+ WebCore::Node* focusNode = m_mainFrame->getCacheBuilder().currentFocus();
+ if (focusNode) {
+ WebCore::Frame* focusFrame = focusNode->document()->frame();
+ WebCore::PlatformKeyboardEvent::Type type;
+ switch (action) {
+ case DownKeyAction:
+ type = WebCore::PlatformKeyboardEvent::KeyDown;
+ break;
+ case UpKeyAction:
+ type = WebCore::PlatformKeyboardEvent::KeyUp;
+ break;
+ default:
+ SkDebugf("---- unexpected KeyAction %d\n", action);
+ return false;
+ }
+
+ int mods = 0; // PlatformKeyboardEvent::ModifierKey
+ if (isShift) {
+ mods |= WebCore::PlatformKeyboardEvent::ShiftKey;
+ }
+ if (isAlt) {
+ mods |= WebCore::PlatformKeyboardEvent::AltKey;
+ }
+
+ WebCore::PlatformKeyboardEvent evt(keyCode, unichar, type, repeatCount,
+ static_cast<WebCore::PlatformKeyboardEvent::ModifierKey>(mods));
+ return focusFrame->eventHandler()->keyEvent(evt);
+ }
+ return false;
+}
+
+bool WebViewCore::handleTouchEvent(int action, int x, int y)
+{
+ bool preventDefault = false;
+
+#if ENABLE(TOUCH_EVENTS) // Android
+ WebCore::TouchEventType type = WebCore::TouchEventCancel;
+ switch (action) {
+ case 0: // MotionEvent.ACTION_DOWN
+ type = WebCore::TouchEventStart;
+ break;
+ case 1: // MotionEvent.ACTION_UP
+ type = WebCore::TouchEventEnd;
+ break;
+ case 2: // MotionEvent.ACTION_MOVE
+ type = WebCore::TouchEventMove;
+ break;
+ case 3: // MotionEvent.ACTION_CANCEL
+ type = WebCore::TouchEventCancel;
+ break;
+ }
+ WebCore::IntPoint pt(x - m_scrollOffsetX, y - m_scrollOffsetY);
+ WebCore::PlatformTouchEvent te(pt, pt, type);
+ preventDefault = m_mainFrame->eventHandler()->handleTouchEvent(te);
+#endif
+
+ return preventDefault;
+}
+
+void WebViewCore::touchUp(int touchGeneration, int buildGeneration,
+ WebCore::Frame* frame, WebCore::Node* node, int x, int y, int size,
+ bool isClick, bool retry)
+{
+ 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
+ }
+ if (retry)
+ finalKitFocus(frame, node, x, y);
+ else if (!commonKitFocus(touchGeneration, buildGeneration,
+ frame, node, x, y, false)) {
+ return;
+ }
+ m_lastGeneration = touchGeneration;
+ // If this is just a touch and not a click, we have already done the change in focus,
+ // so just leave the function now.
+ if (!isClick)
+ return;
+ if (frame) {
+ frame->loader()->resetMultipleFormSubmissionProtection();
+ }
+ WebCore::EditorClientAndroid* client = static_cast<WebCore::EditorClientAndroid*>(m_mainFrame->editor()->client());
+ client->setFromClick(true);
+ DBG_NAV_LOGD("touchGeneration=%d handleMouseClick frame=%p node=%p"
+ " x=%d y=%d", touchGeneration, frame, node, x, y);
+ handleMouseClick(frame, node);
+ client->setFromClick(false);
+}
+
+bool WebViewCore::handleMouseClick(WebCore::Frame* framePtr, WebCore::Node* nodePtr)
+{
+ if (framePtr && !m_mainFrame->getCacheBuilder().validNode(framePtr, nodePtr))
+ return false;
+ WebFrame* webFrame = WebFrame::getWebFrame(m_mainFrame);
+ // 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 && nodePtr->hasTagName(WebCore::HTMLNames::areaTag)) {
+ webFrame->setUserInitiatedClick(true);
+ WebCore::EventTargetNodeCast(nodePtr)->dispatchSimulatedClick(0, true, true);
+ webFrame->setUserInitiatedClick(false);
+ return true;
+ }
+ WebCore::RenderObject* renderer = nodePtr ? nodePtr->renderer() : 0;
+ if (renderer) {
+ if (renderer->isMenuList()) {
+ WebCore::HTMLSelectElement* select = static_cast<WebCore::HTMLSelectElement*>(nodePtr);
+ const WTF::Vector<WebCore::HTMLElement*>& listItems = select->listItems();
+ SkTDArray<const uint16_t*> names;
+ SkTDArray<int> enabledArray;
+ SkTDArray<int> selectedArray;
+ int size = listItems.size();
+ bool multiple = select->multiple();
+ for (int i = 0; i < size; i++) {
+ if (listItems[i]->hasTagName(WebCore::HTMLNames::optionTag)) {
+ WebCore::HTMLOptionElement* option = static_cast<WebCore::HTMLOptionElement*>(listItems[i]);
+ *names.append() = stringConverter(option->optionText());
+ *enabledArray.append() = option->disabled() ? 0 : 1;
+ if (multiple && option->selected())
+ *selectedArray.append() = i;
+ } else if (listItems[i]->hasTagName(WebCore::HTMLNames::optgroupTag)) {
+ WebCore::HTMLOptGroupElement* optGroup = static_cast<WebCore::HTMLOptGroupElement*>(listItems[i]);
+ *names.append() = stringConverter(optGroup->groupLabelText());
+ *enabledArray.append() = 0;
+ if (multiple)
+ *selectedArray.append() = 0;
+ }
+ }
+ WebCoreReply* reply = new ListBoxReply(select, select->document()->frame(), this);
+ listBoxRequest(reply, names.begin(), size, enabledArray.begin(), enabledArray.count(),
+ multiple, selectedArray.begin(), multiple ? selectedArray.count() :
+ select->optionToListIndex(select->selectedIndex()));
+ return true;
+ }
+ }
+ if (!framePtr)
+ framePtr = m_mainFrame;
+ webFrame->setUserInitiatedClick(true);
+ DBG_NAV_LOGD("m_mousePos={%d,%d}", m_mousePos.x(), m_mousePos.y());
+ WebCore::PlatformMouseEvent mouseDown(m_mousePos, m_mousePos, WebCore::LeftButton,
+ WebCore::MouseEventPressed, 1, false, false, false, false,
+ WebCore::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,
+ WebCore::currentTime());
+ bool handled = framePtr->eventHandler()->handleMouseReleaseEvent(mouseUp);
+ webFrame->setUserInitiatedClick(false);
+ 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 = NULL;
+ }
+}
+
+void WebViewCore::jsAlert(const WebCore::String& url, const WebCore::String& text)
+{
+ JNIEnv* env = javavm_to_jnienv(m_javaGlue->m_JVM);
+ jstring jInputStr = env->NewString((unsigned short *)text.characters(), text.length());
+ jstring jUrlStr = env->NewString((unsigned short *)url.characters(), url.length());
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_jsAlert, jUrlStr, jInputStr);
+ env->DeleteLocalRef(jInputStr);
+ env->DeleteLocalRef(jUrlStr);
+ checkException(env);
+}
+
+bool WebViewCore::jsConfirm(const WebCore::String& url, const WebCore::String& text)
+{
+ JNIEnv* env = javavm_to_jnienv(m_javaGlue->m_JVM);
+ jstring jInputStr = env->NewString((unsigned short *)text.characters(), text.length());
+ jstring jUrlStr = env->NewString((unsigned short *)url.characters(), url.length());
+ 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 WebCore::String& url, const WebCore::String& text, const WebCore::String& defaultValue, WebCore::String& result)
+{
+ JNIEnv* env = javavm_to_jnienv(m_javaGlue->m_JVM);
+ jstring jInputStr = env->NewString((unsigned short *)text.characters(), text.length());
+ jstring jDefaultStr = env->NewString((unsigned short *)defaultValue.characters(), defaultValue.length());
+ jstring jUrlStr = env->NewString((unsigned short *)url.characters(), url.length());
+ jstring returnVal = (jstring) env->CallObjectMethod(m_javaGlue->object(env).get(), m_javaGlue->m_jsPrompt, jUrlStr, jInputStr, jDefaultStr);
+ // If returnVal is null, it means that the user cancelled the dialog.
+ if (!returnVal)
+ return false;
+
+ result = to_string(env, returnVal);
+ env->DeleteLocalRef(jInputStr);
+ env->DeleteLocalRef(jDefaultStr);
+ env->DeleteLocalRef(jUrlStr);
+ checkException(env);
+ return true;
+}
+
+bool WebViewCore::jsUnload(const WebCore::String& url, const WebCore::String& message)
+{
+ JNIEnv* env = javavm_to_jnienv(m_javaGlue->m_JVM);
+ jstring jInputStr = env->NewString((unsigned short *)message.characters(), message.length());
+ jstring jUrlStr = env->NewString((unsigned short *)url.characters(), url.length());
+ 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;
+}
+
+AutoJObject
+WebViewCore::getJavaObject()
+{
+ return getRealObject(javavm_to_jnienv(m_javaGlue->m_JVM), m_javaGlue->m_obj);
+}
+
+jobject
+WebViewCore::getWebViewJavaObject()
+{
+ JNIEnv* env = javavm_to_jnienv(m_javaGlue->m_JVM);
+ return env->GetObjectField(m_javaGlue->object(env).get(), gWebViewCoreFields.m_webView);
+}
+
+void WebViewCore::updateTextfield(WebCore::Node* ptr, bool changeToPassword,
+ const WebCore::String& text)
+{
+ if (m_blockTextfieldUpdates)
+ return;
+ JNIEnv* env = javavm_to_jnienv(m_javaGlue->m_JVM);
+ if (changeToPassword) {
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_updateTextfield,
+ (int) ptr, true, 0, m_textGeneration);
+ checkException(env);
+ return;
+ }
+ int length = text.length();
+ jstring string = env->NewString((unsigned short *) text.characters(), length);
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_updateTextfield,
+ (int) ptr, false, string, m_textGeneration);
+ env->DeleteLocalRef(string);
+ checkException(env);
+}
+
+void WebViewCore::setSnapAnchor(int x, int y)
+{
+ m_snapAnchorNode = 0;
+ if (!x && !y) {
+ return;
+ }
+
+ WebCore::IntPoint point = WebCore::IntPoint(x, y);
+ WebCore::Node* node = m_mainFrame->eventHandler()->hitTestResultAtPoint(point, false).innerNode();
+ if (node) {
+// LOGD("found focus node name: %s, type %d\n", node->nodeName().utf8().data(), node->nodeType());
+ while (node) {
+ if (node->hasTagName(WebCore::HTMLNames::divTag) ||
+ node->hasTagName(WebCore::HTMLNames::tableTag)) {
+ m_snapAnchorNode = node;
+ return;
+ }
+// LOGD("parent node name: %s, type %d\n", node->nodeName().utf8().data(), node->nodeType());
+ node = node->parentNode();
+ }
+ }
+}
+
+void WebViewCore::snapToAnchor()
+{
+ if (m_snapAnchorNode) {
+ if (m_snapAnchorNode->inDocument()) {
+ int rx, ry;
+ m_snapAnchorNode->renderer()->absolutePosition(rx, ry);
+ scrollTo(rx, ry);
+ } else {
+ m_snapAnchorNode = 0;
+ }
+ }
+}
+
+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);
+}
+
+//----------------------------------------------------------------------
+// Native JNI methods
+//----------------------------------------------------------------------
+static jstring WebCoreStringToJString(JNIEnv *env, WebCore::String string)
+{
+ int length = string.length();
+ if (!length)
+ return 0;
+ jstring ret = env->NewString((jchar *)string.characters(), length);
+ env->DeleteLocalRef(ret);
+ return ret;
+}
+
+static void SetSize(JNIEnv *env, jobject obj, jint width, jint height,
+ jint screenWidth, jfloat scale)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterWV counter;
+#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");
+ // convert the scale to an int
+ int s = (int) (scale * 100);
+ // a negative value indicates that we should not change the scale
+ if (scale < 0)
+ s = viewImpl->scale();
+
+ viewImpl->setSizeScreenWidthAndScale(width, height, screenWidth, s);
+}
+
+static void SetScrollOffset(JNIEnv *env, jobject obj, jint dx, jint dy)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterWV counter;
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "need viewImpl");
+
+ viewImpl->setScrollOffset(dx, dy);
+}
+
+static void SetGlobalBounds(JNIEnv *env, jobject obj, jint x, jint y, jint h,
+ jint v)
+{
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "need viewImpl");
+
+ viewImpl->setGlobalBounds(x, y, h, v);
+}
+
+static jboolean KeyUp(JNIEnv *env, jobject obj, jint key, jint val)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterWV counter;
+#endif
+// LOGV("webviewcore::nativeKeyUp(%u)\n", (unsigned)key);
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in nativeKeyUp");
+ return viewImpl->keyUp((KeyCode)key, val);
+}
+
+static bool SendKeyToFocusNode(JNIEnv *env, jobject jwebviewcore,
+ jint keyCode, jint unichar, jint repeatCount,
+ jboolean isShift, jboolean isAlt, jint action)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterWV counter;
+#endif
+
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, jwebviewcore);
+ LOG_ASSERT(viewImpl, "viewImpl not set in SendKeyToFocusNode");
+ return viewImpl->sendKeyToFocusNode((KeyCode)keyCode, unichar, repeatCount,
+ isShift, isAlt, static_cast<WebViewCore::KeyAction>(action));
+}
+
+static void DeleteSelection(JNIEnv *env, jobject obj,
+ jint frame, jint node, jint x, jint y, jint start, jint end)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterWV counter;
+#endif
+ LOGV("webviewcore::nativeDeleteSelection()\n");
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in nativeDeleteSelection");
+ viewImpl->deleteSelection((WebCore::Frame*) frame, (WebCore::Node*) node,
+ x, y, start, end);
+}
+
+static void SetSelection(JNIEnv *env, jobject obj,
+ jint frame, jint node, jint x, jint y, jint start, jint end)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterWV counter;
+#endif
+ LOGV("webviewcore::nativeSetSelection()\n");
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in nativeDeleteSelection");
+ viewImpl->setSelection((WebCore::Frame*) frame, (WebCore::Node*) node,
+ x, y, start, end);
+}
+
+
+static void ReplaceTextfieldText(JNIEnv *env, jobject obj,
+ jint framePtr, jint nodePtr, jint x, jint y, jint oldStart, jint oldEnd,
+ jstring replace, jint start, jint end)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterWV counter;
+#endif
+ LOGV("webviewcore::nativeReplaceTextfieldText()\n");
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in nativeReplaceTextfieldText");
+ viewImpl->replaceTextfieldText((WebCore::Frame*) framePtr, (WebCore::Node*) nodePtr, x, y, oldStart,
+ oldEnd, replace, start, end);
+}
+
+static void PassToJs(JNIEnv *env, jobject obj, jint frame, jint node,
+ jint x, jint y, jint generation, jstring currentText, jint keyCode,
+ jint keyValue, jboolean down, jboolean cap, jboolean fn, jboolean sym)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterWV counter;
+#endif
+ LOGV("webviewcore::nativePassToJs()\n");
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in nativePassToJs");
+ viewImpl->passToJs((WebCore::Frame*) frame, (WebCore::Node*) node,
+ x, y, generation, currentText, keyCode, keyValue, down, cap, fn, sym);
+}
+
+static void SaveDocumentState(JNIEnv *env, jobject obj, jint frame, jint node, jint x, jint y)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterWV counter;
+#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, (WebCore::Node*) node, x, y);
+}
+
+static bool RecordContent(JNIEnv *env, jobject obj, jobject region, jobject pt)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterWV counter;
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ SkRegion* nativeRegion = GraphicsJNI::getNativeRegion(env, region);
+ SkIPoint nativePt;
+ bool result = viewImpl->recordContent(nativeRegion, &nativePt);
+ GraphicsJNI::ipoint_to_jpoint(nativePt, env, pt);
+ return result;
+}
+
+static void SplitContent(JNIEnv *env, jobject obj)
+{
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ viewImpl->splitContent();
+}
+
+static void SendListBoxChoice(JNIEnv* env, jobject obj, jint choice)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterWV counter;
+#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
+ TimeCounterWV counter;
+#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)
+{
+ 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 = WebCore::CacheBuilder::FindAddress(addrChars, length,
+ &start, &end) == WebCore::CacheBuilder::FOUND_COMPLETE;
+ jstring ret = 0;
+ if (success) {
+ ret = env->NewString((jchar*) addrChars + start, end - start);
+ env->DeleteLocalRef(ret);
+ }
+ env->ReleaseStringChars(addr, addrChars);
+ return ret;
+}
+
+static jboolean HandleTouchEvent(JNIEnv *env, jobject obj, jint action, jint x, jint y)
+{
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+ return viewImpl->handleTouchEvent(action, x, y);
+}
+
+static void TouchUp(JNIEnv *env, jobject obj, jint touchGeneration,
+ jint buildGeneration, jint frame, jint node, jint x, jint y, jint size,
+ jboolean isClick, jboolean retry)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterWV counter;
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+ viewImpl->touchUp(touchGeneration, buildGeneration,
+ (WebCore::Frame*) frame, (WebCore::Node*) node, x, y, size, isClick, retry);
+}
+
+static jstring RetrieveHref(JNIEnv *env, jobject obj, jint frame,
+ jint node)
+{
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+ WebCore::String result = viewImpl->retrieveHref((WebCore::Frame*) frame,
+ (WebCore::Node*) node);
+ if (!result.isEmpty())
+ return WebCoreStringToJString(env, result);
+ return 0;
+}
+
+static void SetFinalFocus(JNIEnv *env, jobject obj, jint frame, jint node,
+ jint x, jint y, jboolean block)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterWV counter;
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+ viewImpl->setFinalFocus((WebCore::Frame*) frame, (WebCore::Node*) node, x,
+ y, block);
+}
+
+static void SetKitFocus(JNIEnv *env, jobject obj, jint moveGeneration,
+ jint buildGeneration, jint frame, jint node, jint x, jint y,
+ jboolean ignoreNullFocus)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterWV counter;
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+ viewImpl->setKitFocus(moveGeneration, buildGeneration,
+ (WebCore::Frame*) frame, (WebCore::Node*) node, x, y,
+ ignoreNullFocus);
+}
+
+static void UnblockFocus(JNIEnv *env, jobject obj)
+{
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+ viewImpl->unblockFocus();
+}
+
+static void UpdateFrameCache(JNIEnv *env, jobject obj)
+{
+ 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)
+{
+ 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->minPrefWidth();
+ }
+ }
+ }
+ return 0;
+}
+
+static void SetViewportSettingsFromNative(JNIEnv *env, jobject obj)
+{
+ 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());
+#endif
+}
+
+static void SetSnapAnchor(JNIEnv *env, jobject obj, jint x, jint y)
+{
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+
+ viewImpl->setSnapAnchor(x, y);
+}
+
+static void SnapToAnchor(JNIEnv *env, jobject obj)
+{
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+
+ viewImpl->snapToAnchor();
+}
+
+static void SetBackgroundColor(JNIEnv *env, jobject obj, jint color)
+{
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+
+ viewImpl->setBackgroundColor((SkColor) color);
+}
+
+static jstring GetSelection(JNIEnv *env, jobject obj, jobject selRgn)
+{
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+ SkRegion* selectionRegion = GraphicsJNI::getNativeRegion(env, selRgn);
+ WebCore::String result = viewImpl->getSelection(selectionRegion);
+ if (!result.isEmpty())
+ return WebCoreStringToJString(env, result);
+ return 0;
+}
+
+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 RefreshPlugins(JNIEnv *env,
+ jobject obj,
+ jboolean reloadOpenPages)
+{
+ // Refresh the list of plugins, optionally reloading all open
+ // pages.
+ WebCore::refreshPlugins(reloadOpenPages);
+}
+
+static void RegisterURLSchemeAsLocal(JNIEnv* env, jobject obj, jstring scheme) {
+ WebCore::FrameLoader::registerURLSchemeAsLocal(to_string(env, scheme));
+}
+
+static void ClearContent(JNIEnv *env, jobject obj)
+{
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ viewImpl->clearContent();
+}
+
+static void CopyContentToPicture(JNIEnv *env, jobject obj, jobject pict)
+{
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ if (!viewImpl)
+ return;
+ SkPicture* picture = GraphicsJNI::getNativePicture(env, pict);
+ viewImpl->copyContentToPicture(picture);
+}
+
+static bool DrawContent(JNIEnv *env, jobject obj, jobject canv, jint color)
+{
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, canv);
+ return viewImpl->drawContent(canvas, color);
+}
+
+// ----------------------------------------------------------------------------
+
+/*
+ * JNI registration.
+ */
+static JNINativeMethod gJavaWebViewCoreMethods[] = {
+ { "nativeClearContent", "()V",
+ (void*) ClearContent },
+ { "nativeCopyContentToPicture", "(Landroid/graphics/Picture;)V",
+ (void*) CopyContentToPicture },
+ { "nativeDrawContent", "(Landroid/graphics/Canvas;I)Z",
+ (void*) DrawContent } ,
+ { "nativeKeyUp", "(II)Z",
+ (void*) KeyUp },
+ { "nativeSendKeyToFocusNode", "(IIIZZI)Z", (void*) SendKeyToFocusNode },
+ { "nativeSendListBoxChoices", "([ZI)V",
+ (void*) SendListBoxChoices },
+ { "nativeSendListBoxChoice", "(I)V",
+ (void*) SendListBoxChoice },
+ { "nativeSetSize", "(IIIF)V",
+ (void*) SetSize },
+ { "nativeSetScrollOffset", "(II)V",
+ (void*) SetScrollOffset },
+ { "nativeSetGlobalBounds", "(IIII)V",
+ (void*) SetGlobalBounds },
+ { "nativeSetSelection", "(IIIIII)V",
+ (void*) SetSelection } ,
+ { "nativeDeleteSelection", "(IIIIII)V",
+ (void*) DeleteSelection } ,
+ { "nativeReplaceTextfieldText", "(IIIIIILjava/lang/String;II)V",
+ (void*) ReplaceTextfieldText } ,
+ { "passToJs", "(IIIIILjava/lang/String;IIZZZZ)V",
+ (void*) PassToJs } ,
+ { "nativeSaveDocumentState", "(IIII)V",
+ (void*) SaveDocumentState },
+ { "nativeFindAddress", "(Ljava/lang/String;)Ljava/lang/String;",
+ (void*) FindAddress },
+ { "nativeHandleTouchEvent", "(III)Z",
+ (void*) HandleTouchEvent },
+ { "nativeTouchUp", "(IIIIIIIZZ)V",
+ (void*) TouchUp },
+ { "nativeRetrieveHref", "(II)Ljava/lang/String;",
+ (void*) RetrieveHref },
+ { "nativeSetFinalFocus", "(IIIIZ)V",
+ (void*) SetFinalFocus },
+ { "nativeSetKitFocus", "(IIIIIIZ)V",
+ (void*) SetKitFocus },
+ { "nativeUnblockFocus", "()V",
+ (void*) UnblockFocus },
+ { "nativeUpdateFrameCache", "()V",
+ (void*) UpdateFrameCache },
+ { "nativeGetContentMinPrefWidth", "()I",
+ (void*) GetContentMinPrefWidth },
+ { "nativeRecordContent", "(Landroid/graphics/Region;Landroid/graphics/Point;)Z",
+ (void*) RecordContent },
+ { "setViewportSettingsFromNative", "()V",
+ (void*) SetViewportSettingsFromNative },
+ { "nativeSetSnapAnchor", "(II)V",
+ (void*) SetSnapAnchor },
+ { "nativeSnapToAnchor", "()V",
+ (void*) SnapToAnchor },
+ { "nativeSplitContent", "()V",
+ (void*) SplitContent },
+ { "nativeSetBackgroundColor", "(I)V",
+ (void*) SetBackgroundColor },
+ { "nativeGetSelection", "(Landroid/graphics/Region;)Ljava/lang/String;",
+ (void*) GetSelection },
+ { "nativeRefreshPlugins", "(Z)V",
+ (void*) RefreshPlugins },
+ { "nativeRegisterURLSchemeAsLocal", "(Ljava/lang/String;)V",
+ (void*) RegisterURLSchemeAsLocal },
+ { "nativeDumpDomTree", "(Z)V",
+ (void*) DumpDomTree },
+ { "nativeDumpRenderTree", "(Z)V",
+ (void*) DumpRenderTree },
+ { "nativeDumpNavTree", "()V",
+ (void*) DumpNavTree }
+};
+
+int register_webviewcore(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_webView = env->GetFieldID(widget,
+ "mWebView", "Landroid/webkit/WebView;");
+ LOG_ASSERT(gWebViewCoreFields.m_webView,
+ "Unable to find android/webkit/WebViewCore.mWebView");
+
+ return jniRegisterNativeMethods(env, "android/webkit/WebViewCore",
+ gJavaWebViewCoreMethods, NELEM(gJavaWebViewCoreMethods));
+}
+
+} /* namespace android */