diff options
Diffstat (limited to 'WebKit/android/jni/WebViewCore.cpp')
| -rw-r--r-- | WebKit/android/jni/WebViewCore.cpp | 681 |
1 files changed, 474 insertions, 207 deletions
diff --git a/WebKit/android/jni/WebViewCore.cpp b/WebKit/android/jni/WebViewCore.cpp index 1562775..d3e32d3 100644 --- a/WebKit/android/jni/WebViewCore.cpp +++ b/WebKit/android/jni/WebViewCore.cpp @@ -29,6 +29,7 @@ #include "WebViewCore.h" #include "AtomicString.h" +#include "BaseLayerAndroid.h" #include "CachedNode.h" #include "CachedRoot.h" #include "Chrome.h" @@ -37,6 +38,7 @@ #include "DatabaseTracker.h" #include "Document.h" #include "DOMWindow.h" +#include "DOMSelection.h" #include "Element.h" #include "Editor.h" #include "EditorClientAndroid.h" @@ -65,9 +67,9 @@ #include "HTMLSelectElement.h" #include "HTMLTextAreaElement.h" #include "HistoryItem.h" +#include "HitTestRequest.h" #include "HitTestResult.h" #include "InlineTextBox.h" -#include "KeyboardCodes.h" #include "Navigator.h" #include "Node.h" #include "NodeList.h" @@ -79,7 +81,9 @@ #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" @@ -101,6 +105,7 @@ #include "TypingCommand.h" #include "WebCoreFrameBridge.h" #include "WebFrameView.h" +#include "WindowsKeyboardCodes.h" #include "android_graphics.h" #include <JNIHelp.h> @@ -109,9 +114,9 @@ #include <wtf/CurrentTime.h> #if USE(V8) -#include "CString.h" #include "ScriptController.h" #include "V8Counters.h" +#include <wtf/text/CString.h> #endif #if DEBUG_NAV_UI @@ -125,7 +130,7 @@ #ifdef ANDROID_DOM_LOGGING #include "AndroidLog.h" #include "RenderTreeAsText.h" -#include "CString.h" +#include <wtf/text/CString.h> FILE* gDomTreeFile = 0; FILE* gRenderTreeFile = 0; @@ -226,13 +231,10 @@ struct WebViewCore::JavaGlue { jmethodID m_updateViewport; jmethodID m_sendNotifyProgressFinished; jmethodID m_sendViewInvalidate; - jmethodID m_sendImmediateRepaint; - jmethodID m_setRootLayer; jmethodID m_updateTextfield; jmethodID m_updateTextSelection; jmethodID m_clearTextEntry; jmethodID m_restoreScale; - jmethodID m_restoreScreenWidthScale; jmethodID m_needTouchEvents; jmethodID m_requestKeyboard; jmethodID m_requestKeyboardWithSelection; @@ -253,6 +255,7 @@ struct WebViewCore::JavaGlue { jmethodID m_showRect; jmethodID m_centerFitRect; jmethodID m_setScrollbarModes; + jmethodID m_setInstallableWebApp; AutoJObject object(JNIEnv* env) { return getRealObject(env, m_obj); } @@ -272,7 +275,6 @@ static jmethodID GetJMethod(JNIEnv* env, jclass clazz, const char name[], const Mutex WebViewCore::gFrameCacheMutex; Mutex WebViewCore::gButtonMutex; Mutex WebViewCore::gCursorBoundsMutex; -Mutex WebViewCore::m_contentMutex; WebViewCore::WebViewCore(JNIEnv* env, jobject javaWebViewCore, WebCore::Frame* mainframe) : m_pluginInvalTimer(this, &WebViewCore::pluginInvalTimerFired) @@ -289,8 +291,8 @@ WebViewCore::WebViewCore(JNIEnv* env, jobject javaWebViewCore, WebCore::Frame* m m_maxYScroll = 240/4; m_textGeneration = 0; m_screenWidth = 320; + m_textWrapWidth = 320; m_scale = 1; - m_screenWidthScale = 1; #if ENABLE(TOUCH_EVENTS) m_forwardingTouchEvents = false; #endif @@ -306,7 +308,7 @@ WebViewCore::WebViewCore(JNIEnv* env, jobject javaWebViewCore, WebCore::Frame* m m_javaGlue->m_scrollBy = GetJMethod(env, clazz, "contentScrollBy", "(IIZ)V"); m_javaGlue->m_contentDraw = GetJMethod(env, clazz, "contentDraw", "()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;"); + 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"); @@ -317,13 +319,10 @@ WebViewCore::WebViewCore(JNIEnv* env, jobject javaWebViewCore, WebCore::Frame* m 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_sendImmediateRepaint = GetJMethod(env, clazz, "sendImmediateRepaint", "()V"); - m_javaGlue->m_setRootLayer = GetJMethod(env, clazz, "setRootLayer", "(I)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", "(I)V"); - m_javaGlue->m_restoreScreenWidthScale = GetJMethod(env, clazz, "restoreScreenWidthScale", "(I)V"); + m_javaGlue->m_restoreScale = GetJMethod(env, clazz, "restoreScale", "(II)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"); @@ -344,6 +343,7 @@ WebViewCore::WebViewCore(JNIEnv* env, jobject javaWebViewCore, WebCore::Frame* m 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"); env->SetIntField(javaWebViewCore, gWebViewCoreFields.m_nativeClass, (jint)this); @@ -485,7 +485,8 @@ void WebViewCore::recordPicture(SkPicture* picture) WebCore::PlatformGraphicsContext pgc(arp.getRecordingCanvas(), &buttons); WebCore::GraphicsContext gc(&pgc); - view->platformWidget()->draw(&gc, WebCore::IntRect(0, 0, INT_MAX, INT_MAX)); + view->platformWidget()->draw(&gc, WebCore::IntRect(0, 0, + view->contentsWidth(), view->contentsHeight())); gButtonMutex.lock(); updateButtonList(&buttons); @@ -589,6 +590,9 @@ void WebViewCore::recordPictureSet(PictureSet* content) height = view->contentsHeight(); } + if (cacheBuilder().pictureSetDisabled()) + content->clear(); + content->checkDimensions(width, height, &m_addInval); // The inval region may replace existing pictures. The existing pictures @@ -741,50 +745,11 @@ void WebViewCore::updateCursorBounds(const CachedRoot* root, void WebViewCore::clearContent() { DBG_SET_LOG(""); - m_contentMutex.lock(); m_content.clear(); - m_contentMutex.unlock(); m_addInval.setEmpty(); m_rebuildInval.setEmpty(); } -void WebViewCore::copyContentToPicture(SkPicture* picture) -{ - DBG_SET_LOG("start"); - m_contentMutex.lock(); - PictureSet copyContent = PictureSet(m_content); - m_contentMutex.unlock(); - - int w = copyContent.width(); - int h = copyContent.height(); - copyContent.draw(picture->beginRecording(w, h, PICT_RECORD_FLAGS)); - picture->endRecording(); - DBG_SET_LOG("end"); -} - -bool WebViewCore::drawContent(SkCanvas* canvas, SkColor color) -{ -#ifdef ANDROID_INSTRUMENT - TimeCounterAuto counter(TimeCounter::WebViewUIDrawTimeCounter); -#endif - 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; -} - bool WebViewCore::focusBoundsChanged() { bool result = m_focusBoundsChanged; @@ -792,18 +757,6 @@ bool WebViewCore::focusBoundsChanged() return result; } -bool WebViewCore::pictureReady() -{ - bool done; - m_contentMutex.lock(); - PictureSet copyContent = PictureSet(m_content); - done = m_progressDone; - m_contentMutex.unlock(); - DBG_NAV_LOGD("done=%s empty=%s", done ? "true" : "false", - copyContent.isEmpty() ? "true" : "false"); - return done || !copyContent.isEmpty(); -} - SkPicture* WebViewCore::rebuildPicture(const SkIRect& inval) { WebCore::FrameView* view = m_mainFrame->view(); @@ -851,48 +804,52 @@ void WebViewCore::rebuildPictureSet(PictureSet* pictureSet) pictureSet->validate(__FUNCTION__); } -bool WebViewCore::recordContent(SkRegion* region, SkIPoint* point) +BaseLayerAndroid* WebViewCore::recordContent(SkRegion* region, SkIPoint* point) { DBG_SET_LOG("start"); float progress = (float) m_mainFrame->page()->progress()->estimatedProgress(); - m_contentMutex.lock(); - PictureSet contentCopy(m_content); m_progressDone = progress <= 0.0f || progress >= 1.0f; - m_contentMutex.unlock(); - recordPictureSet(&contentCopy); - if (!m_progressDone && contentCopy.isEmpty()) { + recordPictureSet(&m_content); + if (!m_progressDone && m_content.isEmpty()) { DBG_SET_LOGD("empty (progress=%g)", progress); - return false; + return 0; } region->set(m_addInval); m_addInval.setEmpty(); region->op(m_rebuildInval, SkRegion::kUnion_Op); m_rebuildInval.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; + + BaseLayerAndroid* base = new BaseLayerAndroid(); + base->setContent(m_content); + +#if USE(ACCELERATED_COMPOSITING) + // 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; } -void WebViewCore::splitContent() +void WebViewCore::splitContent(PictureSet* content) { 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(); + content->split(&m_content); + rebuildPictureSet(&m_content); + content->set(m_content); } void WebViewCore::scrollTo(int x, int y, bool animate) @@ -936,28 +893,6 @@ void WebViewCore::scrollBy(int dx, int dy, bool animate) checkException(env); } -#if USE(ACCELERATED_COMPOSITING) - -void WebViewCore::immediateRepaint() -{ - 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_sendImmediateRepaint); - checkException(env); -} - -void WebViewCore::setUIRootLayer(const LayerAndroid* layer) -{ - JNIEnv* env = JSC::Bindings::getJNIEnv(); - env->CallVoidMethod(m_javaGlue->object(env).get(), - m_javaGlue->m_setRootLayer, - reinterpret_cast<jint>(layer)); - checkException(env); -} - -#endif // USE(ACCELERATED_COMPOSITING) - void WebViewCore::contentDraw() { JNIEnv* env = JSC::Bindings::getJNIEnv(); @@ -1034,24 +969,13 @@ void WebViewCore::updateViewport() checkException(env); } -void WebViewCore::restoreScale(int scale) +void WebViewCore::restoreScale(int scale, int 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); - checkException(env); -} - -void WebViewCore::restoreScreenWidthScale(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 = JSC::Bindings::getJNIEnv(); - env->CallVoidMethod(m_javaGlue->object(env).get(), - m_javaGlue->m_restoreScreenWidthScale, scale); + env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_restoreScale, scale, textWrapScale); checkException(env); } @@ -1162,42 +1086,45 @@ void WebViewCore::setGlobalBounds(int x, int y, int h, int v) } void WebViewCore::setSizeScreenWidthAndScale(int width, int height, - int screenWidth, float scale, int realScreenWidth, int screenHeight, + 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(); 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); + } int osw = m_screenWidth; - int orsw = m_screenWidth * m_screenWidthScale / m_scale; int osh = m_screenHeight; + int otw = m_textWrapWidth; 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; - if (scale >= 0) { // negative means ignore + m_textWrapWidth = textWrapWidth; + if (scale >= 0) // negative means keep the current scale m_scale = scale; - if (screenWidth != realScreenWidth) - m_screenWidthScale = realScreenWidth * scale / screenWidth; - else - m_screenWidthScale = m_scale; - } m_maxXScroll = screenWidth >> 2; - m_maxYScroll = (screenWidth * height / width) >> 2; - if (ow != width || (!ignoreHeight && oh != height) || osw != screenWidth) { + m_maxYScroll = m_maxXScroll * height / width; + if (ow != width || (!ignoreHeight && oh != height) || otw != textWrapWidth) { WebCore::RenderObject *r = m_mainFrame->contentRenderer(); DBG_NAV_LOGD("renderer=%p view=(w=%d,h=%d)", r, - realScreenWidth, screenHeight); + screenWidth, screenHeight); if (r) { WebCore::IntPoint anchorPoint = WebCore::IntPoint(anchorX, anchorY); DBG_NAV_LOGD("anchorX=%d anchorY=%d", anchorX, anchorY); - WebCore::Node* node = 0; + RefPtr<WebCore::Node> node; WebCore::IntRect bounds; WebCore::IntPoint offset; - // If the screen width changed, it is probably zoom change or + // If the text wrap changed, it is probably zoom change or // orientation change. Try to keep the anchor at the same place. - if (osw && screenWidth && osw != screenWidth) { + if (otw && textWrapWidth && otw != textWrapWidth) { WebCore::HitTestResult hitTestResult = m_mainFrame->eventHandler()-> hitTestResultAtPoint( anchorPoint, false); @@ -1227,19 +1154,19 @@ void WebViewCore::setSizeScreenWidthAndScale(int width, int height, DBG_NAV_LOGD("nb:(x=%d,y=%d,w=%d," "h=%d)", newBounds.x(), newBounds.y(), newBounds.width(), newBounds.height()); - if ((orsw && osh && bounds.width() && bounds.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 = (osw != m_screenWidth) + 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) / orsw; + 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(), @@ -1272,7 +1199,7 @@ void WebViewCore::dumpDomTree(bool useFile) void WebViewCore::dumpRenderTree(bool useFile) { #ifdef ANDROID_DOM_LOGGING - WebCore::CString renderDump = WebCore::externalRepresentation(m_mainFrame).utf8(); + WTF::CString renderDump = WebCore::externalRepresentation(m_mainFrame).utf8(); const char* data = renderDump.data(); if (useFile) { gRenderTreeFile = fopen(RENDER_TREE_LOG_FILE, "w"); @@ -1331,7 +1258,7 @@ WebCore::String WebViewCore::requestLabel(WebCore::Frame* frame, for (unsigned i = 0; i < length; i++) { WebCore::HTMLLabelElement* label = static_cast<WebCore::HTMLLabelElement*>( list->item(i)); - if (label->correspondingControl() == node) { + if (label->control() == node) { Node* node = label; String result; while ((node = node->traverseNextNode(label))) { @@ -1437,6 +1364,252 @@ void WebViewCore::updateFrameCacheIfLoading() 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)->linesVisibleOverflowBoundingBox(); + 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); +#ifdef ANDROID_HITTEST_WITHSIZE + HitTestResult hitTestResult = m_mainFrame->eventHandler()->hitTestResultAtPoint(IntPoint(x, y), + false, false, DontHitTestScrollbars, IntSize(slop, slop)); + if (!hitTestResult.innerNode() || !hitTestResult.innerNode()->inDocument()) { + LOGE("Should not happen: no in document Node found"); + return rects; + } + const Vector<RefPtr<Node> >& list = hitTestResult.rawNodeList(); + if (list.isEmpty()) { + LOGE("Should not happen: no raw node found"); + return rects; + } + Frame* frame = hitTestResult.innerNode()->document()->frame(); + Vector<TouchNodeData> nodeDataList; + Vector<RefPtr<Node> >::const_iterator last = list.end(); + for (Vector<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 rawNodeList() 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); + } + } +#endif + return rects; +} + /////////////////////////////////////////////////////////////////////////////// void WebViewCore::addPlugin(PluginWidgetAndroid* w) @@ -1675,6 +1848,51 @@ void WebViewCore::setSelection(int start, int end) setFocusControllerActive(focusedFrame, true); } +String WebViewCore::modifySelection(const String& alter, const String& direction, const String& granularity) +{ + DOMSelection* selection = m_mainFrame->domWindow()->getSelection(); + + if (selection->rangeCount() == 0) { + Document* document = m_mainFrame->document(); + HTMLElement* body = document->body(); + ExceptionCode ec; + + PassRefPtr<Range> rangeRef = document->createRange(); + rangeRef->setStart(PassRefPtr<Node>(body), 0, ec); + if (ec) { + LOGE("Error setting range start. Error code: %d", ec); + return String(); + } + + rangeRef->setEnd(PassRefPtr<Node>(body), 0, ec); + if (ec) { + LOGE("Error setting range end. Error code: %d", ec); + return String(); + } + + selection->addRange(rangeRef.get()); + } + + if (equalIgnoringCase(direction, "forward")) { + selection->collapseToEnd(); + } else if (equalIgnoringCase(direction, "backward")) { + selection->collapseToStart(); + } else { + LOGE("Invalid direction: %s", direction.utf8().data()); + return String(); + } + + // NOTE: The selection of WebKit misbehaves and I need to add some + // voodoo here to force it behave well. Rachel did something similar + // in JS and I want to make sure it is optimal before adding it here. + + selection->modify(alter, direction, granularity); + String selection_string = selection->toString(); + LOGD("Selection string: %s", selection_string.utf8().data()); + + return selection_string; +} + void WebViewCore::deleteSelection(int start, int end, int textGeneration) { setSelection(start, end); @@ -1918,15 +2136,24 @@ void WebViewCore::openFileChooser(PassRefPtr<WebCore::FileChooser> chooser) { if (!chooser) return; JNIEnv* env = JSC::Bindings::getJNIEnv(); + + WebCore::String acceptType = chooser->acceptTypes(); + jstring jAcceptType = env->NewString(const_cast<unsigned short*>(acceptType.characters()), acceptType.length()); jstring jName = (jstring) env->CallObjectMethod( - m_javaGlue->object(env).get(), m_javaGlue->m_openFileChooser); + m_javaGlue->object(env).get(), m_javaGlue->m_openFileChooser, jAcceptType); checkException(env); - const UChar* string = (const UChar*) env->GetStringChars(jName, NULL); + env->DeleteLocalRef(jAcceptType); + + const UChar* string = static_cast<const UChar*>(env->GetStringChars(jName, NULL)); + if (!string) return; + WebCore::String webcoreString = to_string(env, jName); env->ReleaseStringChars(jName, string); - chooser->chooseFile(webcoreString); + + if (webcoreString.length()) + chooser->chooseFile(webcoreString); } void WebViewCore::listBoxRequest(WebCoreReply* reply, const uint16_t** labels, size_t count, const int enabled[], size_t enabledCount, @@ -1985,12 +2212,25 @@ void WebViewCore::listBoxRequest(WebCoreReply* reply, const uint16_t** labels, s bool WebViewCore::key(const PlatformKeyboardEvent& event) { - WebCore::EventHandler* eventHandler = m_mainFrame->eventHandler(); + WebCore::EventHandler* eventHandler; WebCore::Node* focusNode = currentFocus(); - if (focusNode) - eventHandler = focusNode->document()->frame()->eventHandler(); DBG_NAV_LOGD("keyCode=%s unichar=%d focusNode=%p", event.keyIdentifier().utf8().data(), event.unichar(), focusNode); + if (focusNode) { + WebCore::Frame* frame = focusNode->document()->frame(); + eventHandler = frame->eventHandler(); + if (focusNode->isContentEditable()) { + // 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. + VisibleSelection old = frame->selection()->selection(); + eventHandler->keyEvent(event); + return frame->selection()->selection() != old; + } + } else { + eventHandler = m_mainFrame->eventHandler(); + } return eventHandler->keyEvent(event); } @@ -2075,14 +2315,6 @@ bool WebViewCore::handleTouchEvent(int action, int x, int y, int metaState) // Track previous touch and if stationary set the state. WebCore::IntPoint pt(x - m_scrollOffsetX, y - m_scrollOffsetY); -// handleTouchEvent() in EventHandler.cpp doesn't handle TouchStationary, which -// causes preventDefault be false when it returns. As our Java side may continue -// process the events if WebKit doesn't, it can cause unexpected result. -// if (type == WebCore::TouchMove && pt == m_lastTouchPoint) -// touchState = WebCore::PlatformTouchPoint::TouchStationary; - - m_lastTouchPoint = pt; - WebCore::PlatformTouchEvent te(pt, type, touchState, metaState); preventDefault = m_mainFrame->eventHandler()->handleTouchEvent(te); #endif @@ -2097,6 +2329,16 @@ bool WebViewCore::handleTouchEvent(int action, int x, int y, int metaState) 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); @@ -2106,6 +2348,7 @@ void WebViewCore::touchUp(int touchGeneration, // 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(); } @@ -2203,6 +2446,9 @@ bool WebViewCore::handleMouseClick(WebCore::Frame* framePtr, WebCore::Node* node } else { requestKeyboard(false); } + } else if (focusNode->isContentEditable()) { + setFocusControllerActive(framePtr, true); + requestKeyboard(true); } } return handled; @@ -2542,6 +2788,13 @@ void WebViewCore::setScrollbarModes(ScrollbarMode horizontalMode, ScrollbarMode checkException(env); } +void WebViewCore::notifyWebAppCanBeInstalled() +{ + JNIEnv* env = JSC::Bindings::getJNIEnv(); + env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_setInstallableWebApp); + checkException(env); +} + //---------------------------------------------------------------------- // Native JNI methods //---------------------------------------------------------------------- @@ -2568,7 +2821,7 @@ static void UpdateFrameCacheIfLoading(JNIEnv *env, jobject obj) } static void SetSize(JNIEnv *env, jobject obj, jint width, jint height, - jint screenWidth, jfloat scale, jint realScreenWidth, jint screenHeight, + jint textWrapWidth, jfloat scale, jint screenWidth, jint screenHeight, jint anchorX, jint anchorY, jboolean ignoreHeight) { #ifdef ANDROID_INSTRUMENT @@ -2577,8 +2830,8 @@ static void SetSize(JNIEnv *env, jobject obj, jint width, jint height, 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, screenWidth, scale, - realScreenWidth, screenHeight, anchorX, anchorY, ignoreHeight); + viewImpl->setSizeScreenWidthAndScale(width, height, textWrapWidth, scale, + screenWidth, screenHeight, anchorX, anchorY, ignoreHeight); } static void SetScrollOffset(JNIEnv *env, jobject obj, jint gen, jint x, jint y) @@ -2646,6 +2899,22 @@ static void SetSelection(JNIEnv *env, jobject obj, jint start, jint end) viewImpl->setSelection(start, end); } +static jstring ModifySelection(JNIEnv *env, jobject obj, jstring alter, jstring direction, jstring granularity) +{ +#ifdef ANDROID_INSTRUMENT + TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter); +#endif + String alterString = to_string(env, alter); + String directionString = to_string(env, direction); + String granularityString = to_string(env, granularity); + + WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); + String selection_string = viewImpl->modifySelection(alterString, + directionString, + granularityString); + + return WebCoreStringToJString(env, selection_string); +} static void ReplaceTextfieldText(JNIEnv *env, jobject obj, jint oldStart, jint oldEnd, jstring replace, jint start, jint end, @@ -2710,7 +2979,7 @@ void WebViewCore::addVisitedLink(const UChar* string, int length) m_groupForVisitedLinks->addVisitedLink(string, length); } -static bool RecordContent(JNIEnv *env, jobject obj, jobject region, jobject pt) +static jint RecordContent(JNIEnv *env, jobject obj, jobject region, jobject pt) { #ifdef ANDROID_INSTRUMENT TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter); @@ -2718,18 +2987,18 @@ static bool RecordContent(JNIEnv *env, jobject obj, jobject region, jobject pt) WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); SkRegion* nativeRegion = GraphicsJNI::getNativeRegion(env, region); SkIPoint nativePt; - bool result = viewImpl->recordContent(nativeRegion, &nativePt); + BaseLayerAndroid* result = viewImpl->recordContent(nativeRegion, &nativePt); GraphicsJNI::ipoint_to_jpoint(nativePt, env, pt); - return result; + return reinterpret_cast<jint>(result); } -static void SplitContent(JNIEnv *env, jobject obj) +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(); + viewImpl->splitContent(reinterpret_cast<PictureSet*>(content)); } static void SendListBoxChoice(JNIEnv* env, jobject obj, jint choice) @@ -2981,7 +3250,7 @@ static void SetJsFlags(JNIEnv *env, jobject obj, jstring flags) { #if USE(V8) WebCore::String flagsString = to_string(env, flags); - WebCore::CString utf8String = flagsString.utf8(); + WTF::CString utf8String = flagsString.utf8(); WebCore::ScriptController::setFlags(utf8String.data(), utf8String.length()); #endif } @@ -3018,45 +3287,11 @@ static void RegisterURLSchemeAsLocal(JNIEnv* env, jobject obj, jstring scheme) { WebCore::SecurityOrigin::registerURLSchemeAsLocal(to_string(env, scheme)); } -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 CopyContentToPicture(JNIEnv *env, jobject obj, jobject pict) -{ -#ifdef ANDROID_INSTRUMENT - TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter); -#endif - 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) -{ - // Note: this is called from UI thread, don't count it for WebViewCoreTimeCounter - WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); - SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, canv); - return viewImpl->drawContent(canvas, color); -} - static bool FocusBoundsChanged(JNIEnv* env, jobject obj) { return GET_NATIVE_VIEW(env, obj)->focusBoundsChanged(); } -static bool PictureReady(JNIEnv* env, jobject obj) -{ - return GET_NATIVE_VIEW(env, obj)->pictureReady(); -} - static void Pause(JNIEnv* env, jobject obj) { // This is called for the foreground tab when the browser is put to the @@ -3151,26 +3386,54 @@ static bool ValidNodeAndBounds(JNIEnv *env, jobject obj, int frame, int node, 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 NULL; + Vector<IntRect> rects = viewImpl->getTouchHighlightRects(x, y, slop); + if (rects.isEmpty()) + return NULL; + + 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(vector, "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; +} + // ---------------------------------------------------------------------------- /* * JNI registration. */ static JNINativeMethod gJavaWebViewCoreMethods[] = { - { "nativeClearContent", "()V", - (void*) ClearContent }, - { "nativeCopyContentToPicture", "(Landroid/graphics/Picture;)V", - (void*) CopyContentToPicture }, - { "nativeDrawContent", "(Landroid/graphics/Canvas;I)Z", - (void*) DrawContent } , { "nativeFocusBoundsChanged", "()Z", (void*) FocusBoundsChanged } , { "nativeKey", "(IIIZZZZ)Z", (void*) Key }, { "nativeClick", "(II)V", (void*) Click }, - { "nativePictureReady", "()Z", - (void*) PictureReady } , { "nativeSendListBoxChoices", "([ZI)V", (void*) SendListBoxChoices }, { "nativeSendListBoxChoice", "(I)V", @@ -3183,6 +3446,8 @@ static JNINativeMethod gJavaWebViewCoreMethods[] = { (void*) SetGlobalBounds }, { "nativeSetSelection", "(II)V", (void*) SetSelection } , + { "nativeModifySelection", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", + (void*) ModifySelection }, { "nativeDeleteSelection", "(III)V", (void*) DeleteSelection } , { "nativeReplaceTextfieldText", "(IILjava/lang/String;III)V", @@ -3215,11 +3480,11 @@ static JNINativeMethod gJavaWebViewCoreMethods[] = { (void*) UpdateFrameCache }, { "nativeGetContentMinPrefWidth", "()I", (void*) GetContentMinPrefWidth }, - { "nativeRecordContent", "(Landroid/graphics/Region;Landroid/graphics/Point;)Z", + { "nativeRecordContent", "(Landroid/graphics/Region;Landroid/graphics/Point;)I", (void*) RecordContent }, { "setViewportSettingsFromNative", "()V", (void*) SetViewportSettingsFromNative }, - { "nativeSplitContent", "()V", + { "nativeSplitContent", "(I)V", (void*) SplitContent }, { "nativeSetBackgroundColor", "(I)V", (void*) SetBackgroundColor }, @@ -3251,6 +3516,8 @@ static JNINativeMethod gJavaWebViewCoreMethods[] = { (void*) FullScreenPluginHidden }, { "nativeValidNodeAndBounds", "(IILandroid/graphics/Rect;)Z", (void*) ValidNodeAndBounds }, + { "nativeGetTouchHighlightRects", "(III)Ljava/util/ArrayList;", + (void*) GetTouchHighlightRects }, }; int register_webviewcore(JNIEnv* env) |
