diff options
Diffstat (limited to 'WebKit/android/jni/WebViewCore.cpp')
-rw-r--r-- | WebKit/android/jni/WebViewCore.cpp | 858 |
1 files changed, 364 insertions, 494 deletions
diff --git a/WebKit/android/jni/WebViewCore.cpp b/WebKit/android/jni/WebViewCore.cpp index 8361c17..8c0fade 100644 --- a/WebKit/android/jni/WebViewCore.cpp +++ b/WebKit/android/jni/WebViewCore.cpp @@ -35,6 +35,8 @@ #include "Chrome.h" #include "ChromeClientAndroid.h" #include "ChromiumIncludes.h" +#include "ClientRect.h" +#include "ClientRectList.h" #include "Color.h" #include "CSSPropertyNames.h" #include "CSSValueKeywords.h" @@ -250,9 +252,7 @@ struct WebViewCoreFields { struct WebViewCore::JavaGlue { jweak m_obj; - jmethodID m_spawnScrollTo; jmethodID m_scrollTo; - jmethodID m_scrollBy; jmethodID m_contentDraw; jmethodID m_layersDraw; jmethodID m_requestListBox; @@ -342,15 +342,14 @@ WebViewCore::WebViewCore(JNIEnv* env, jobject javaWebViewCore, WebCore::Frame* m #endif m_isPaused = false; m_screenOnCounter = 0; + m_onlyScrollIfImeIsShowing = false; LOG_ASSERT(m_mainFrame, "Uh oh, somehow a frameview was made without an initial frame!"); jclass clazz = env->GetObjectClass(javaWebViewCore); m_javaGlue = new JavaGlue; m_javaGlue->m_obj = env->NewWeakGlobalRef(javaWebViewCore); - m_javaGlue->m_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", "(IIZ)V"); + m_javaGlue->m_scrollTo = GetJMethod(env, clazz, "contentScrollTo", "(IIZZ)V"); m_javaGlue->m_contentDraw = GetJMethod(env, clazz, "contentDraw", "()V"); m_javaGlue->m_layersDraw = GetJMethod(env, clazz, "layersDraw", "()V"); m_javaGlue->m_requestListBox = GetJMethod(env, clazz, "requestListBox", "([Ljava/lang/String;[I[I)V"); @@ -486,8 +485,6 @@ void WebViewCore::reset(bool fromConstructor) m_scrollOffsetY = 0; m_screenWidth = 0; m_screenHeight = 0; - m_visibleScreenWidth = 0; - m_visibleScreenHeight = 0; m_groupForVisitedLinks = NULL; m_currentNodeDomNavigationAxis = 0; } @@ -947,9 +944,8 @@ void WebViewCore::scrollTo(int x, int y, bool animate) // LOGD("WebViewCore::scrollTo(%d %d)\n", x, y); JNIEnv* env = JSC::Bindings::getJNIEnv(); - env->CallVoidMethod(m_javaGlue->object(env).get(), - animate ? m_javaGlue->m_spawnScrollTo : m_javaGlue->m_scrollTo, - x, y); + env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_scrollTo, + x, y, animate, m_onlyScrollIfImeIsShowing); checkException(env); } @@ -971,16 +967,6 @@ void WebViewCore::viewInvalidate(const WebCore::IntRect& rect) checkException(env); } -void WebViewCore::scrollBy(int dx, int dy, bool animate) -{ - if (!(dx | dy)) - return; - JNIEnv* env = JSC::Bindings::getJNIEnv(); - env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_scrollBy, - dx, dy, animate); - checkException(env); -} - void WebViewCore::contentDraw() { JNIEnv* env = JSC::Bindings::getJNIEnv(); @@ -1151,13 +1137,14 @@ void WebViewCore::doMaxScroll(CacheBuilder::Direction dir) default: LOG_ASSERT(0, "unexpected focus selector"); } - this->scrollBy(dx, dy, true); + WebCore::FrameView* view = m_mainFrame->view(); + this->scrollTo(view->scrollX() + dx, view->scrollY() + dy, true); } -void WebViewCore::setScrollOffset(int moveGeneration, int userScrolled, int dx, int dy) +void WebViewCore::setScrollOffset(int moveGeneration, bool sendScrollEvent, int dx, int dy) { - DBG_NAV_LOGD("{%d,%d} m_scrollOffset=(%d,%d), userScrolled=%d", dx, dy, - m_scrollOffsetX, m_scrollOffsetY, userScrolled); + DBG_NAV_LOGD("{%d,%d} m_scrollOffset=(%d,%d), sendScrollEvent=%d", dx, dy, + m_scrollOffsetX, m_scrollOffsetY, sendScrollEvent); if (m_scrollOffsetX != dx || m_scrollOffsetY != dy) { m_scrollOffsetX = dx; m_scrollOffsetY = dy; @@ -1166,24 +1153,25 @@ void WebViewCore::setScrollOffset(int moveGeneration, int userScrolled, int dx, // testing work correctly. m_mainFrame->view()->platformWidget()->setLocation(m_scrollOffsetX, m_scrollOffsetY); - if (userScrolled) { + if (sendScrollEvent) { m_mainFrame->eventHandler()->sendScrollEvent(); - } - // Update history item to reflect the new scroll position. - // This also helps save the history information when the browser goes to - // background, so scroll position will be restored if browser gets - // killed while in background. - WebCore::HistoryController* history = m_mainFrame->loader()->history(); - // Because the history item saving could be heavy for large sites and - // scrolling can generate lots of small scroll offset, the following code - // reduces the saving frequency. - static const int MIN_SCROLL_DIFF = 32; - if (history->currentItem()) { - WebCore::IntPoint currentPoint = history->currentItem()->scrollPoint(); - if (std::abs(currentPoint.x() - dx) >= MIN_SCROLL_DIFF || - std::abs(currentPoint.y() - dy) >= MIN_SCROLL_DIFF) { - history->saveScrollPositionAndViewStateToItem(history->currentItem()); + // Only update history position if it's user scrolled. + // Update history item to reflect the new scroll position. + // This also helps save the history information when the browser goes to + // background, so scroll position will be restored if browser gets + // killed while in background. + WebCore::HistoryController* history = m_mainFrame->loader()->history(); + // Because the history item saving could be heavy for large sites and + // scrolling can generate lots of small scroll offset, the following code + // reduces the saving frequency. + static const int MIN_SCROLL_DIFF = 32; + if (history->currentItem()) { + WebCore::IntPoint currentPoint = history->currentItem()->scrollPoint(); + if (std::abs(currentPoint.x() - dx) >= MIN_SCROLL_DIFF || + std::abs(currentPoint.y() - dy) >= MIN_SCROLL_DIFF) { + history->saveScrollPositionAndViewStateToItem(history->currentItem()); + } } } @@ -1229,6 +1217,12 @@ void WebViewCore::setSizeScreenWidthAndScale(int width, int height, // Don't reflow if the diff is small. const bool reflow = otw && textWrapWidth && ((float) abs(otw - textWrapWidth) / textWrapWidth) >= 0.01f; + + // When the screen size change, fixed positioned element should be updated. + // This is supposed to be light weighted operation without a full layout. + if (osh != screenHeight || osw != screenWidth) + m_mainFrame->view()->updatePositionedObjects(); + if (ow != width || (!ignoreHeight && oh != height) || reflow) { WebCore::RenderObject *r = m_mainFrame->contentRenderer(); DBG_NAV_LOGD("renderer=%p view=(w=%d,h=%d)", r, @@ -1322,8 +1316,11 @@ void WebViewCore::setSizeScreenWidthAndScale(int width, int height, // If this was in response to touching a textfield and showing the IME, // the IME may now cover textfield. Bring it back into view. // If the scale changed, however, this was the result of a zoom. - if (oldScale == m_scale) + if (oldScale == m_scale && osh > screenHeight) { + m_onlyScrollIfImeIsShowing = true; revealSelection(); + m_onlyScrollIfImeIsShowing = false; + } // update the currently visible screen as perceived by the plugin sendPluginVisibleScreen(); } @@ -2084,18 +2081,18 @@ String WebViewCore::modifySelection(const int direction, const int axis) if (selection->rangeCount() > 1) selection->removeAllRanges(); switch (axis) { - case AXIS_CHARACTER: - case AXIS_WORD: - case AXIS_SENTENCE: - return modifySelectionTextNavigationAxis(selection, direction, axis); - case AXIS_HEADING: - case AXIS_SIBLING: - case AXIS_PARENT_FIRST_CHILD: - case AXIS_DOCUMENT: - return modifySelectionDomNavigationAxis(selection, direction, axis); - default: - LOGE("Invalid navigation axis: %d", axis); - return String(); + case AXIS_CHARACTER: + case AXIS_WORD: + case AXIS_SENTENCE: + return modifySelectionTextNavigationAxis(selection, direction, axis); + case AXIS_HEADING: + case AXIS_SIBLING: + case AXIS_PARENT_FIRST_CHILD: + case AXIS_DOCUMENT: + return modifySelectionDomNavigationAxis(selection, direction, axis); + default: + LOGE("Invalid navigation axis: %d", axis); + return String(); } } @@ -2123,10 +2120,10 @@ void WebViewCore::scrollNodeIntoView(Frame* frame, Node* node) String WebViewCore::modifySelectionTextNavigationAxis(DOMSelection* selection, int direction, int axis) { - // TODO: Add support of IFrames. - HTMLElement* body = m_mainFrame->document()->body(); + Node* body = m_mainFrame->document()->body(); ExceptionCode ec = 0; + String markup; // initialize the selection if necessary if (selection->rangeCount() == 0) { @@ -2151,14 +2148,13 @@ String WebViewCore::modifySelectionTextNavigationAxis(DOMSelection* selection, i if (ec) return String(); selection->addRange(rangeRef.get()); - } else if (direction == DIRECTION_FORWARD) { - selection->setPosition(body->firstDescendant(), 0, ec); } else { - selection->setPosition(body->lastDescendant(), 0, ec); + selection->setPosition(body, 0, ec); } if (ec) return String(); } + // collapse the selection if (direction == DIRECTION_FORWARD) selection->collapseToEnd(ec); @@ -2167,126 +2163,52 @@ String WebViewCore::modifySelectionTextNavigationAxis(DOMSelection* selection, i if (ec) return String(); - Node* oldAnchorNode = selection->anchorNode(); - if (!oldAnchorNode) - return String(); - // Make sure the anchor node is a text node since we are generating // the markup of the selection which includes the anchor, the focus, // and any crossed nodes. Forcing the condition that the selection // starts and ends on text nodes guarantees symmetric selection markup. + // Also this way the text content, rather its container, is highlighted. Node* anchorNode = selection->anchorNode(); if (anchorNode->isElementNode()) { - int anchorOffset = rangeCompliantChildOffset(anchorNode, - selection->anchorOffset()); - anchorNode = selection->anchorNode()->childNodes()->item(anchorOffset); - Node* nextAnchorNode = traverseVisibleNonEmptyNonWhitespaceTextNode( - anchorNode, body, direction); - if (!nextAnchorNode) + // Collapsed selection while moving forward points to the + // next unvisited node and while moving backward to the + // last visited node. + if (direction == DIRECTION_FORWARD) + advanceAnchorNode(selection, direction, markup, false, ec); + else + advanceAnchorNode(selection, direction, markup, true, ec); + if (ec) return String(); - if (direction == DIRECTION_FORWARD) { - Node* skippedControl = getFirstIntermediaryInputOrButton(anchorNode, - nextAnchorNode); - if (skippedControl) { - IntRect bounds = static_cast<Element*>( - skippedControl)->boundsInWindowSpace(); - selectAt(bounds.center().x(), bounds.center().y()); - selection->setBaseAndExtent(skippedControl, - caretMinOffset(skippedControl), skippedControl, - caretMaxOffset(skippedControl), ec); - if (ec) - return String(); - return formatMarkup(selection).stripWhiteSpace(); - } else { - selection->setPosition(nextAnchorNode, 0, ec); - if (ec) - return String(); - } - } else { - Node* skippedControl = getFirstIntermediaryInputOrButton( - nextAnchorNode, anchorNode); - if (skippedControl) { - IntRect bounds = static_cast<Element*>( - skippedControl)->boundsInWindowSpace(); - selectAt(bounds.center().x(), bounds.center().y()); - selection->setBaseAndExtent(skippedControl, - caretMaxOffset(skippedControl), skippedControl, - caretMinOffset(skippedControl), ec); - if (ec) - return String(); - return formatMarkup(selection).stripWhiteSpace(); - } else { - selection->setPosition(nextAnchorNode, - caretMaxOffset(nextAnchorNode), ec); - if (ec) - return String(); - } - } + if (!markup.isEmpty()) + return markup; } // If the selection is at the end of a non white space text move // it to the next visible text node with non white space content. // This is a workaround for the selection getting stuck. anchorNode = selection->anchorNode(); - if (!anchorNode) - return String(); if (anchorNode->isTextNode()) { if (direction == DIRECTION_FORWARD) { String suffix = anchorNode->textContent().substring( - selection->anchorOffset(), caretMaxOffset(anchorNode)); + selection->anchorOffset(), caretMaxOffset(anchorNode)); + // If at the end of non white space text we advance the + // anchor node to either an input element or non empty text. if (suffix.stripWhiteSpace().isEmpty()) { - Node* nextAnchorNode = - traverseVisibleNonEmptyNonWhitespaceTextNode(anchorNode, - body, direction); - if (!nextAnchorNode) - return String(); - Node* skippedControl = getFirstIntermediaryInputOrButton( - anchorNode, nextAnchorNode); - if (skippedControl) { - IntRect bounds = static_cast<Element*>( - skippedControl)->boundsInWindowSpace(); - selectAt(bounds.center().x(), bounds.center().y()); - selection->setBaseAndExtent(skippedControl, - caretMinOffset(skippedControl), skippedControl, - caretMaxOffset(skippedControl), ec); - if (ec) - return String(); - return formatMarkup(selection).stripWhiteSpace(); - } else { - selection->setPosition(nextAnchorNode, 0, ec); - if (ec) - return String(); - } + advanceAnchorNode(selection, direction, markup, true, ec); } } else { String prefix = anchorNode->textContent().substring(0, - selection->anchorOffset()); + selection->anchorOffset()); + // If at the end of non white space text we advance the + // anchor node to either an input element or non empty text. if (prefix.stripWhiteSpace().isEmpty()) { - Node* nextAnchorNode = - traverseVisibleNonEmptyNonWhitespaceTextNode(anchorNode, - body, direction); - if (!nextAnchorNode) - return String(); - Node* skippedControl = getFirstIntermediaryInputOrButton( - nextAnchorNode, anchorNode); - if (skippedControl) { - IntRect bounds = static_cast<Element*>( - skippedControl)->boundsInWindowSpace(); - selectAt(bounds.center().x(), bounds.center().y()); - selection->setBaseAndExtent(skippedControl, - caretMaxOffset(skippedControl), skippedControl, - caretMinOffset(skippedControl), ec); - if (ec) - return String(); - return formatMarkup(selection).stripWhiteSpace(); - } else { - selection->setPosition(nextAnchorNode, - caretMaxOffset(nextAnchorNode), ec); - if (ec) - return String(); - } + advanceAnchorNode(selection, direction, markup, true, ec); } } + if (ec) + return String(); + if (!markup.isEmpty()) + return markup; } // extend the selection @@ -2308,176 +2230,111 @@ String WebViewCore::modifySelectionTextNavigationAxis(DOMSelection* selection, i // Make sure the focus node is a text node in order to have the // selection generate symmetric markup because the latter - // includes all nodes crossed by the selection. + // includes all nodes crossed by the selection. Also this way + // the text content, rather its container, is highlighted. Node* focusNode = selection->focusNode(); if (focusNode->isElementNode()) { - int focusOffset = rangeCompliantChildOffset(focusNode, - selection->focusOffset()); + focusNode = getImplicitAnchorOrFocusNode(selection->focusNode(), + selection->focusOffset(), direction); + if (!focusNode) + return String(); if (direction == DIRECTION_FORWARD) { - focusNode = - focusNode->childNodes()->item(focusOffset)->lastDescendant(); - if (!isVisibleNonEmptyNonWhitespaceTextNode(focusNode)) { - focusNode = traverseVisibleNonEmptyNonWhitespaceTextNode( - focusNode, body, DIRECTION_BACKWARD); - if (!focusNode) + focusNode = focusNode->traversePreviousSiblingPostOrder(body); + if (focusNode && !isContentTextNode(focusNode)) { + Node* textNode = traverseNextContentTextNode(focusNode, + anchorNode, DIRECTION_BACKWARD); + if (textNode) + anchorNode = textNode; + } + if (focusNode && isContentTextNode(focusNode)) { + selection->extend(focusNode, caretMaxOffset(focusNode), ec); + if (ec) return String(); } - selection->extend(focusNode, caretMaxOffset(focusNode), ec); - if (ec) - return String(); } else { - focusNode = - focusNode->childNodes()->item(focusOffset)->firstDescendant(); - if (!isVisibleNonEmptyNonWhitespaceTextNode(focusNode)) { - focusNode = traverseVisibleNonEmptyNonWhitespaceTextNode( - focusNode, body, DIRECTION_FORWARD); - if (!focusNode) + focusNode = focusNode->traverseNextSibling(); + if (focusNode && !isContentTextNode(focusNode)) { + Node* textNode = traverseNextContentTextNode(focusNode, + anchorNode, DIRECTION_FORWARD); + if (textNode) + anchorNode = textNode; + } + if (anchorNode && isContentTextNode(anchorNode)) { + selection->extend(focusNode, 0, ec); + if (ec) return String(); } - selection->extend(focusNode, 0, ec); - if (ec) - return String(); } } // Enforce that the selection does not cross anchor boundaries. This is // a workaround for the asymmetric behavior of WebKit while crossing // anchors. - // NOTE: The code is asymmetric since the logic is based off the common - // ancestor in both directions - backward and forward. - // TODO: Factor out common code repeated below. - anchorNode = selection->anchorNode(); - focusNode = selection->focusNode(); - if (anchorNode != focusNode - && anchorNode->isTextNode() - && focusNode->isTextNode()) { - Node* commonAncestor = Range::commonAncestorContainer(anchorNode, - focusNode); - Node* currentNode = 0; - bool selectionAdjusted = false; - if (direction == DIRECTION_FORWARD) { - // catch if the anchor is in a link but the focus is not - if (!commonAncestor->hasTagName(WebCore::HTMLNames::aTag)) { - currentNode = anchorNode; - while (currentNode != commonAncestor) { - if (isVisible(currentNode) && isInputControl(currentNode)) { - focusNode = currentNode->lastDescendant(); - if (!isVisibleNonEmptyNonWhitespaceTextNode(focusNode)) { - focusNode = - traverseVisibleNonEmptyNonWhitespaceTextNode( - focusNode, commonAncestor, - DIRECTION_BACKWARD); - if (!focusNode) - return String(); - } - selection->extend(focusNode, caretMaxOffset(focusNode), - ec); - if (ec) - return String(); - selectionAdjusted = true; - break; - } - currentNode = currentNode->parentNode(); + anchorNode = getImplicitAnchorOrFocusNode(selection->anchorNode(), + selection->anchorOffset(), direction); + focusNode = getImplicitAnchorOrFocusNode(selection->focusNode(), + selection->focusOffset(), direction); + if (anchorNode && focusNode && anchorNode != focusNode) { + Node* inputControl = getIntermediaryInputElement(anchorNode, focusNode, + direction); + if (inputControl) { + if (direction == DIRECTION_FORWARD) { + if (isDescendantOf(inputControl, anchorNode)) { + focusNode = inputControl; + } else { + focusNode = inputControl->traversePreviousSiblingPostOrder( + body); + if (!focusNode) + focusNode = inputControl; } - // catch if there is a link between the anchor and focus - if (!selectionAdjusted) { - currentNode = anchorNode; - while (currentNode != focusNode) { - if (isVisible(currentNode) - && isInputControl(currentNode)) { - focusNode = currentNode; - if (!isVisibleNonEmptyNonWhitespaceTextNode(focusNode)) { - focusNode = - traverseVisibleNonEmptyNonWhitespaceTextNode( - focusNode, commonAncestor, - DIRECTION_BACKWARD); - if (!focusNode) - return String(); - } - selection->extend(focusNode, - caretMaxOffset(focusNode), ec); - if (ec) - return String(); - break; - } - currentNode = currentNode->traverseNextNode(); - } + // We prefer a text node contained in the input element. + if (!isContentTextNode(focusNode)) { + Node* textNode = traverseNextContentTextNode(focusNode, + anchorNode, DIRECTION_BACKWARD); + if (textNode) + focusNode = textNode; } - } - } else { - // catch if the anchor is in a link but the focus is not - // NOTE: There is not such case in forward direction because - // it is implicitly covered the second case. Also the - // base position used for computing the the common - // ancestor which is asymmteric. - if (!commonAncestor->hasTagName(WebCore::HTMLNames::aTag)) { - currentNode = anchorNode; - while (currentNode != commonAncestor) { - if (isVisible(currentNode) && isInputControl(currentNode)) { - focusNode = currentNode->firstDescendant(); - if (!isVisibleNonEmptyNonWhitespaceTextNode(focusNode)) { - focusNode = - traverseVisibleNonEmptyNonWhitespaceTextNode( - focusNode, commonAncestor, - DIRECTION_FORWARD); - if (!focusNode) - return String(); - } - selection->extend(focusNode, 0, ec); - if (ec) - return String(); - selectionAdjusted = true; - break; - } - currentNode = currentNode->parentNode(); + // If we found text in the input select it. + // Otherwise, select the input element itself. + if (isContentTextNode(focusNode)) { + selection->extend(focusNode, caretMaxOffset(focusNode), ec); + } else if (anchorNode != focusNode) { + // Note that the focusNode always has parent and that + // the offset can be one more that the index of the last + // element - this is how WebKit selects such elements. + selection->extend(focusNode->parentNode(), + focusNode->nodeIndex() + 1, ec); } - // catch if there is a link between the anchor and focus - if (!selectionAdjusted) { - currentNode = anchorNode; - while (currentNode != focusNode) { - if (isVisible(currentNode) - && isInputControl(currentNode)) { - focusNode = currentNode->traverseNextSibling(); - if (!isVisibleNonEmptyNonWhitespaceTextNode(focusNode)) { - focusNode = - traverseVisibleNonEmptyNonWhitespaceTextNode( - focusNode, commonAncestor, - DIRECTION_FORWARD); - if (!focusNode) - return String(); - } - selection->extend(focusNode, 0, ec); - if (ec) - return String(); - selectionAdjusted = true; - break; - } - currentNode = currentNode->traversePreviousNode(); - } + if (ec) + return String(); + } else { + if (isDescendantOf(inputControl, anchorNode)) { + focusNode = inputControl; + } else { + focusNode = inputControl->traverseNextSibling(); + if (!focusNode) + focusNode = inputControl; } - // catch if the focus is in a link but the anchor is not - if (!selectionAdjusted) { - currentNode = focusNode; - while (currentNode != commonAncestor) { - if (isVisible(currentNode) - && isInputControl(currentNode)) { - focusNode = currentNode->traverseNextSibling(); - if (!isVisibleNonEmptyNonWhitespaceTextNode(focusNode)) { - focusNode = - traverseVisibleNonEmptyNonWhitespaceTextNode( - focusNode, commonAncestor, - DIRECTION_FORWARD); - if (!focusNode) - return String(); - } - selection->extend(focusNode, 0, ec); - if (ec) - return String(); - break; - } - currentNode = currentNode->parentNode(); - } + // We prefer a text node contained in the input element. + if (!isContentTextNode(focusNode)) { + Node* textNode = traverseNextContentTextNode(focusNode, + anchorNode, DIRECTION_FORWARD); + if (textNode) + focusNode = textNode; + } + // If we found text in the input select it. + // Otherwise, select the input element itself. + if (isContentTextNode(focusNode)) { + selection->extend(focusNode, caretMinOffset(focusNode), ec); + } else if (anchorNode != focusNode) { + // Note that the focusNode always has parent and that + // the offset can be one more that the index of the last + // element - this is how WebKit selects such elements. + selection->extend(focusNode->parentNode(), + focusNode->nodeIndex() + 1, ec); } + if (ec) + return String(); } } } @@ -2494,30 +2351,136 @@ String WebViewCore::modifySelectionTextNavigationAxis(DOMSelection* selection, i return String(); IntRect bounds = range->boundingBox(); selectAt(bounds.center().x(), bounds.center().y()); - String markup = formatMarkup(selection).stripWhiteSpace(); + markup = formatMarkup(selection); LOGV("Selection markup: %s", markup.utf8().data()); return markup; } -bool WebViewCore::isInputControl(Node* node) +Node* WebViewCore::getImplicitAnchorOrFocusNode(Node* node, unsigned offset, int direction) { - return (node->hasTagName(WebCore::HTMLNames::aTag) - || node->hasTagName(WebCore::HTMLNames::inputTag) - || node->hasTagName(WebCore::HTMLNames::buttonTag)); + if (node->offsetInCharacters()) + return node; + if (!node->hasChildNodes()) + return node; + if (offset < node->childNodeCount()) + return node->childNode(offset); + else + if (direction == DIRECTION_FORWARD) + return node->traverseNextSibling(); + else + return node->traversePreviousNodePostOrder( + node->document()->body()); } -int WebViewCore::rangeCompliantChildOffset(Node* parent, int offset) +Node* WebViewCore::getNextAnchorNode(Node* anchorNode, bool ignoreFirstNode, int direction) { - if (offset < 0) - return 0; - int lastChildIndex = parent->childNodes()->length() - 1; - if (offset > lastChildIndex) - return lastChildIndex; - return offset; + Node* body = 0; + Node* currentNode = 0; + if (direction == DIRECTION_FORWARD) { + if (ignoreFirstNode) + currentNode = anchorNode->traverseNextNode(body); + else + currentNode = anchorNode; + } else { + body = anchorNode->document()->body(); + if (ignoreFirstNode) + currentNode = anchorNode->traversePreviousSiblingPostOrder(body); + else + currentNode = anchorNode; + } + while (currentNode) { + if (isContentTextNode(currentNode) + || isContentInputElement(currentNode)) + return currentNode; + if (direction == DIRECTION_FORWARD) + currentNode = currentNode->traverseNextNode(); + else + currentNode = currentNode->traversePreviousNodePostOrder(body); + } + return 0; +} + +void WebViewCore::advanceAnchorNode(DOMSelection* selection, int direction, + String& markup, bool ignoreFirstNode, ExceptionCode& ec) +{ + Node* anchorNode = getImplicitAnchorOrFocusNode(selection->anchorNode(), + selection->anchorOffset(), direction); + if (!anchorNode) { + ec = NOT_FOUND_ERR; + return; + } + // If the anchor offset is invalid i.e. the anchor node has no + // child with that index getImplicitAnchorNode returns the next + // logical node in the current direction. In such a case our + // position in the DOM tree was has already been advanced, + // therefore we there is no need to do that again. + if (selection->anchorNode()->isElementNode()) { + unsigned anchorOffset = selection->anchorOffset(); + unsigned childNodeCount = selection->anchorNode()->childNodeCount(); + if (anchorOffset >= childNodeCount) + ignoreFirstNode = false; + } + // Find the next anchor node given our position in the DOM and + // whether we want the current node to be considered as well. + Node* nextAnchorNode = getNextAnchorNode(anchorNode, ignoreFirstNode, + direction); + if (!nextAnchorNode) { + ec = NOT_FOUND_ERR; + return; + } + if (nextAnchorNode->isElementNode()) { + // If this is an input element tell the WebView thread + // to set the cursor to that control. + if (isContentInputElement(nextAnchorNode)) { + IntRect bounds = nextAnchorNode->getRect(); + selectAt(bounds.center().x(), bounds.center().y()); + } + Node* textNode = 0; + // Treat the text content of links as any other text but + // for the rest input elements select the control itself. + if (nextAnchorNode->hasTagName(WebCore::HTMLNames::aTag)) + textNode = traverseNextContentTextNode(nextAnchorNode, + nextAnchorNode, direction); + // We prefer to select the text content of the link if such, + // otherwise just select the element itself. + if (textNode) { + nextAnchorNode = textNode; + } else { + if (direction == DIRECTION_FORWARD) { + selection->setBaseAndExtent(nextAnchorNode, + caretMinOffset(nextAnchorNode), nextAnchorNode, + caretMaxOffset(nextAnchorNode), ec); + } else { + selection->setBaseAndExtent(nextAnchorNode, + caretMaxOffset(nextAnchorNode), nextAnchorNode, + caretMinOffset(nextAnchorNode), ec); + } + if (!ec) + markup = formatMarkup(selection); + // make sure the selection is visible + scrollNodeIntoView(selection->frame(), nextAnchorNode); + return; + } + } + if (direction == DIRECTION_FORWARD) + selection->setPosition(nextAnchorNode, + caretMinOffset(nextAnchorNode), ec); + else + selection->setPosition(nextAnchorNode, + caretMaxOffset(nextAnchorNode), ec); +} + +bool WebViewCore::isContentInputElement(Node* node) +{ + return (isVisible(node) + && (node->hasTagName(WebCore::HTMLNames::selectTag) + || node->hasTagName(WebCore::HTMLNames::aTag) + || node->hasTagName(WebCore::HTMLNames::inputTag) + || node->hasTagName(WebCore::HTMLNames::buttonTag))); } -bool WebViewCore::isVisibleNonEmptyNonWhitespaceTextNode(Node* node) +bool WebViewCore::isContentTextNode(Node* node) { if (!node || !node->isTextNode()) return false; @@ -2526,21 +2489,66 @@ bool WebViewCore::isVisibleNonEmptyNonWhitespaceTextNode(Node* node) && !textNode->containsOnlyWhitespace()); } -Text* WebViewCore::traverseVisibleNonEmptyNonWhitespaceTextNode(Node* fromNode, Node* toNode, int direction) +Text* WebViewCore::traverseNextContentTextNode(Node* fromNode, Node* toNode, int direction) { Node* currentNode = fromNode; do { if (direction == DIRECTION_FORWARD) currentNode = currentNode->traverseNextNode(toNode); else - currentNode = currentNode->traversePreviousNode(toNode); - } while (currentNode && !isVisibleNonEmptyNonWhitespaceTextNode(currentNode)); + currentNode = currentNode->traversePreviousNodePostOrder(toNode); + } while (currentNode && !isContentTextNode(currentNode)); return static_cast<Text*>(currentNode); } +Node* WebViewCore::getIntermediaryInputElement(Node* fromNode, Node* toNode, int direction) +{ + if (fromNode == toNode) + return 0; + if (direction == DIRECTION_FORWARD) { + Node* currentNode = fromNode; + while (currentNode && currentNode != toNode) { + if (isContentInputElement(currentNode)) + return currentNode; + currentNode = currentNode->traverseNextNodePostOrder(); + } + currentNode = fromNode; + while (currentNode && currentNode != toNode) { + if (isContentInputElement(currentNode)) + return currentNode; + currentNode = currentNode->traverseNextNode(); + } + } else { + Node* currentNode = fromNode->traversePreviousNode(); + while (currentNode && currentNode != toNode) { + if (isContentInputElement(currentNode)) + return currentNode; + currentNode = currentNode->traversePreviousNode(); + } + currentNode = fromNode->traversePreviousNodePostOrder(); + while (currentNode && currentNode != toNode) { + if (isContentInputElement(currentNode)) + return currentNode; + currentNode = currentNode->traversePreviousNodePostOrder(); + } + } + return 0; +} + +bool WebViewCore::isDescendantOf(Node* parent, Node* node) +{ + Node* currentNode = node; + while (currentNode) { + if (currentNode == parent) { + return true; + } + currentNode = currentNode->parentNode(); + } + return false; +} + String WebViewCore::modifySelectionDomNavigationAxis(DOMSelection* selection, int direction, int axis) { - // TODO: Add support of IFrames. HTMLElement* body = m_mainFrame->document()->body(); if (!m_currentNodeDomNavigationAxis && selection->focusNode()) { m_currentNodeDomNavigationAxis = selection->focusNode(); @@ -2633,12 +2641,27 @@ bool WebViewCore::isHeading(Node* node) bool WebViewCore::isVisible(Node* node) { - // TODO: Use DOMWindow#getComputedStyle instead. + // start off an element + Element* element = 0; + if (node->isElementNode()) + element = static_cast<Element*>(node); + else + element = node->parentElement(); + // check renderer + if (!element->renderer()) { + return false; + } + // check size + if (element->offsetHeight() == 0 || element->offsetWidth() == 0) { + return false; + } + // check style Node* body = m_mainFrame->document()->body(); - Node* currentNode = node; + Node* currentNode = element; while (currentNode && currentNode != body) { RenderStyle* style = currentNode->computedStyle(); - if (style->display() == NONE || style->visibility() == HIDDEN) { + if (style && + (style->display() == NONE || style->visibility() == HIDDEN)) { return false; } currentNode = currentNode->parentNode(); @@ -2650,20 +2673,18 @@ String WebViewCore::formatMarkup(DOMSelection* selection) { ExceptionCode ec = 0; String markup = String(); - PassRefPtr<Range> wholeRange = selection->getRangeAt(0, ec); if (ec) - return markup; - + return String(); if (!wholeRange->startContainer() || !wholeRange->startContainer()) - return markup; - + return String(); // Since formatted markup contains invisible nodes it // is created from the concatenation of the visible fragments. Node* firstNode = wholeRange->firstNode(); Node* pastLastNode = wholeRange->pastLastNode(); Node* currentNode = firstNode; PassRefPtr<Range> currentRange; + while (currentNode != pastLastNode) { Node* nextNode = currentNode->traverseNextNode(); if (!isVisible(currentNode)) { @@ -2676,24 +2697,24 @@ String WebViewCore::formatMarkup(DOMSelection* selection) if (!currentRange) { currentRange = selection->frame()->document()->createRange(); if (ec) - return markup; + break; if (currentNode == firstNode) { currentRange->setStart(wholeRange->startContainer(), wholeRange->startOffset(), ec); if (ec) - return markup; + break; } else { currentRange->setStart(currentNode->parentNode(), currentNode->nodeIndex(), ec); if (ec) - return markup; + break; } } if (nextNode == pastLastNode) { currentRange->setEnd(wholeRange->endContainer(), wholeRange->endOffset(), ec); if (ec) - return markup; + break; markup = markup + stripAppleSpanFromMarkup( currentRange->toHTML()).utf8().data(); } else { @@ -2704,12 +2725,12 @@ String WebViewCore::formatMarkup(DOMSelection* selection) currentRange->setEnd(currentNode->parentNode(), currentNode->nodeIndex() + 1, ec); if (ec) - return markup; + break; } } currentNode = nextNode; } - return markup; + return markup.stripWhiteSpace(); } String WebViewCore::stripAppleSpanFromMarkup(String markup) @@ -2732,32 +2753,6 @@ void WebViewCore::selectAt(int x, int y) checkException(env); } -Node* WebViewCore::getFirstIntermediaryInputOrButton(Node* fromNode, Node* toNode) -{ - // do bidirectional traversal to catch the case in which - // the toNode is a descendant of a control but the fromNode - // is not and the other way around - Node* currentNode = fromNode->traverseNextNode(); - while (currentNode && currentNode != toNode) { - if (isVisible(currentNode) - && (currentNode->hasTagName(WebCore::HTMLNames::inputTag) - || currentNode->hasTagName(WebCore::HTMLNames::buttonTag))) { - return currentNode; - } - currentNode = currentNode->traverseNextNode(); - } - currentNode = fromNode->traverseNextNodePostOrder(); - while (currentNode && currentNode != toNode) { - if (isVisible(currentNode) - && (currentNode->hasTagName(WebCore::HTMLNames::inputTag) - || currentNode->hasTagName(WebCore::HTMLNames::buttonTag))) { - return currentNode; - } - currentNode = currentNode->traverseNextNodePostOrder(); - } - return 0; -} - void WebViewCore::deleteSelection(int start, int end, int textGeneration) { setSelection(start, end); @@ -2882,94 +2877,6 @@ void WebViewCore::saveDocumentState(WebCore::Frame* frame) } } -// Convert a WTF::String into an array of characters where the first -// character represents the length, for easy conversion to java. -static uint16_t* stringConverter(const WTF::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 for a multiple selection listbox if the user did not change - // anything, in which case -2 is used. - // Also used by a listbox which has single selection but a size is set. - virtual void replyInt(int index) - { - if (-2 == index) { - // Special value for cancel. Do nothing. - return; - } - // If the select element no longer exists, due to a page change, etc, - // silently return. - if (!m_select || !CacheBuilder::validNode(m_viewImpl->m_mainFrame, - m_frame, m_select)) - return; - // Use a pointer to HTMLSelectElement's superclass, where - // listToOptionIndex is public - SelectElement* selectElement = m_select; - int optionIndex = selectElement->listToOptionIndex(index); - m_select->setSelectedIndex(optionIndex, true); - m_select->dispatchFormControlChangeEvent(); - 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, due to a page change, etc, - // silently return. - if (!m_select || !CacheBuilder::validNode(m_viewImpl->m_mainFrame, - m_frame, m_select)) - return; - - const WTF::Vector<Element*>& items = m_select->listItems(); - int totalItems = static_cast<int>(items.size()); - // Keep track of the position of the value we are comparing against. - int arrayIndex = 0; - // The value we are comparing against. - int selection = array[arrayIndex]; - WebCore::HTMLOptionElement* option; - for (int listIndex = 0; listIndex < totalItems; listIndex++) { - if (items[listIndex]->hasLocalName(WebCore::HTMLNames::optionTag)) { - option = static_cast<WebCore::HTMLOptionElement*>( - items[listIndex]); - if (listIndex == selection) { - option->setSelectedState(true); - arrayIndex++; - if (arrayIndex == count) - selection = -1; - else - selection = array[arrayIndex]; - } else - option->setSelectedState(false); - } - } - m_select->dispatchFormControlChangeEvent(); - 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) { @@ -3284,46 +3191,7 @@ bool WebViewCore::handleMouseClick(WebCore::Frame* framePtr, WebCore::Node* node return true; } - WebCore::RenderObject* renderer = nodePtr->renderer(); - if (renderer && renderer->isListBox()) { - WebCore::HTMLSelectElement* select = static_cast<WebCore::HTMLSelectElement*>(nodePtr); - const WTF::Vector<WebCore::Element*>& listItems = select->listItems(); - SkTDArray<const uint16_t*> names; - // Possible values for enabledArray. Keep in Sync with values in - // InvokeListBox.Container in WebView.java - enum OptionStatus { - OPTGROUP = -1, - OPTION_DISABLED = 0, - OPTION_ENABLED = 1, - }; - 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->textIndentedToRespectGroupLabel()); - *enabledArray.append() = option->disabled() ? OPTION_DISABLED : OPTION_ENABLED; - 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() = OPTGROUP; - } - } - WebCoreReply* reply = new ListBoxReply(select, select->document()->frame(), this); - // Use a pointer to HTMLSelectElement's superclass, where - // optionToListIndex is public. - SelectElement* selectElement = select; - listBoxRequest(reply, names.begin(), size, enabledArray.begin(), enabledArray.count(), - multiple, selectedArray.begin(), multiple ? selectedArray.count() : - selectElement->optionToListIndex(select->selectedIndex())); - DBG_NAV_LOG("list box"); - return true; - } - scrollLayer(renderer, &m_mousePos); + scrollLayer(nodePtr->renderer(), &m_mousePos); } if (!valid || !framePtr) framePtr = m_mainFrame; @@ -3789,10 +3657,12 @@ void WebViewCore::notifyWebAppCanBeInstalled() void WebViewCore::setWebTextViewAutoFillable(int queryId, const string16& previewSummary) { +#if ENABLE(WEB_AUTOFILL) JNIEnv* env = JSC::Bindings::getJNIEnv(); jstring preview = env->NewString(previewSummary.data(), previewSummary.length()); env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_setWebTextViewAutoFillable, queryId, preview); env->DeleteLocalRef(preview); +#endif } bool WebViewCore::drawIsPaused() const @@ -3875,7 +3745,7 @@ static void SetSize(JNIEnv *env, jobject obj, jint width, jint height, screenWidth, screenHeight, anchorX, anchorY, ignoreHeight); } -static void SetScrollOffset(JNIEnv *env, jobject obj, jint gen, jint userScrolled, jint x, jint y) +static void SetScrollOffset(JNIEnv *env, jobject obj, jint gen, jboolean sendScrollEvent, jint x, jint y) { #ifdef ANDROID_INSTRUMENT TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter); @@ -3883,7 +3753,7 @@ static void SetScrollOffset(JNIEnv *env, jobject obj, jint gen, jint userScrolle WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj); LOG_ASSERT(viewImpl, "need viewImpl"); - viewImpl->setScrollOffset(gen, userScrolled, x, y); + viewImpl->setScrollOffset(gen, sendScrollEvent, x, y); } static void SetGlobalBounds(JNIEnv *env, jobject obj, jint x, jint y, jint h, @@ -4535,7 +4405,7 @@ static JNINativeMethod gJavaWebViewCoreMethods[] = { (void*) SendListBoxChoice }, { "nativeSetSize", "(IIIFIIIIZ)V", (void*) SetSize }, - { "nativeSetScrollOffset", "(IIII)V", + { "nativeSetScrollOffset", "(IZII)V", (void*) SetScrollOffset }, { "nativeSetGlobalBounds", "(IIII)V", (void*) SetGlobalBounds }, |