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.cpp858
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 },