WebKit Bugzilla
New
Browse
Search+
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
[patch]
patch w/ layout test
78856.v5 (text/plain), 78.38 KB, created by
Xiaomei Ji
on 2012-03-15 14:27:57 PDT
(
hide
)
Description:
patch w/ layout test
Filename:
MIME Type:
Creator:
Xiaomei Ji
Created:
2012-03-15 14:27:57 PDT
Size:
78.38 KB
patch
obsolete
>Index: Source/WebCore/ChangeLog >=================================================================== >--- Source/WebCore/ChangeLog (revision 110884) >+++ Source/WebCore/ChangeLog (working copy) >@@ -1,3 +1,52 @@ >+2012-03-15 Xiaomei Ji <xji@chromium.org> >+ >+ Using ICU break iterator to simplify visual word movement implementation. >+ https://bugs.webkit.org/show_bug.cgi?id=78856 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ This patch relies on ICU word break iterator and cursor visual movement by character to get the word break >+ position in visual order. It reduces the complexity of old implementation. >+ >+ Test: editing/selection/move-by-word-visually-wrong-left-right.html >+ >+ * editing/FrameSelection.cpp: Exclude WinCE from visual word movement since isWordTextBreak is not implemented. >+ (WebCore::FrameSelection::modifyMovingRight): >+ (WebCore::FrameSelection::modifyMovingLeft): >+ * editing/visible_units.cpp: >+ (WebCore): >+ (WebCore::previousLeafWithSameEditability): Just moving to the top without functionality change. >+ (WebCore::enclosingNodeWithNonInlineRenderer): ditto. >+ (WebCore::nextLeafWithSameEditability): ditto. >+ (WebCore::previousRootInlineBox): return previous RootInlineBox which is in different renderer. >+ (WebCore::nextRootInlineBox): return next RootInlineBox which is in different renderer. >+ (WebCore::boxIndexInVector): >+ (WebCore::previousBoxInLine): returns logically previous box in one line. >+ (WebCore::logicallyPreviousBox): returns logically previous box. >+ (WebCore::nextBoxInLine): returns logically next box in one line. >+ (WebCore::logicallyNextBox): returns logically next box. >+ (WebCore::wordBreakIteratorForMinOffsetBoundary): create word break iterator for position that is a box's min offset. >+ (WebCore::wordBreakIteratorForMaxOffsetBoundary): create word break iterator for position that is a box's max offset. >+ (WebCore::isLogicalStartOfWord): return whether a position is logically start of word. >+ (WebCore::islogicalEndOfWord): return whether a position is logically end of word. >+ (WebCore::visualWordPosition): returns the visual left or right word position. >+ (WebCore::leftWordPosition): >+ (WebCore::rightWordPosition): >+ * platform/text/TextBreakIterator.h: Add isWordTextBreak(). >+ (WebCore): >+ * platform/text/TextBreakIteratorICU.cpp: >+ (WebCore::isWordTextBreak): >+ (WebCore): >+ * platform/text/gtk/TextBreakIteratorGtk.cpp: >+ (WebCore::isWordTextBreak): >+ (WebCore): >+ * platform/text/qt/TextBreakIteratorQt.cpp: >+ (WebCore::isWordTextBreak): >+ (WebCore): >+ * platform/text/wince/TextBreakIteratorWinCE.cpp: >+ (WebCore::isWordTextBreak): >+ (WebCore): >+ > 2012-03-15 Jessie Berlin <jberlin@apple.com> > > Assertion failures in WebCore::Page::goBackOrForward causing multiple "crashes" on Lion Intel >Index: Source/WebCore/editing/FrameSelection.cpp >=================================================================== >--- Source/WebCore/editing/FrameSelection.cpp (revision 110473) >+++ Source/WebCore/editing/FrameSelection.cpp (working copy) >@@ -642,11 +642,14 @@ VisiblePosition FrameSelection::modifyMo > pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).right(true); > break; > case WordGranularity: >+#if !OS(WINCE) >+ // Visual word movement relies on isWordTextBreak which is not implemented in WinCE. > if (visualWordMovementEnabled() > || (m_frame && m_frame->editor()->behavior().shouldMoveLeftRightByWordInVisualOrder())) { > pos = rightWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity())); > break; > } >+#endif > case SentenceGranularity: > case LineGranularity: > case ParagraphGranularity: >@@ -809,11 +812,13 @@ VisiblePosition FrameSelection::modifyMo > pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).left(true); > break; > case WordGranularity: >+#if !OS(WINCE) > if (visualWordMovementEnabled() > || (m_frame && m_frame->editor()->behavior().shouldMoveLeftRightByWordInVisualOrder())) { > pos = leftWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity())); > break; > } >+#endif > case SentenceGranularity: > case LineGranularity: > case ParagraphGranularity: >Index: Source/WebCore/editing/visible_units.cpp >=================================================================== >--- Source/WebCore/editing/visible_units.cpp (revision 110473) >+++ Source/WebCore/editing/visible_units.cpp (working copy) >@@ -49,6 +49,375 @@ namespace WebCore { > using namespace HTMLNames; > using namespace WTF::Unicode; > >+static Node* previousLeafWithSameEditability(Node* node, EditableType editableType) >+{ >+ bool editable = node->rendererIsEditable(editableType); >+ node = node->previousLeafNode(); >+ while (node) { >+ if (editable == node->rendererIsEditable(editableType)) >+ return node; >+ node = node->previousLeafNode(); >+ } >+ return 0; >+} >+ >+static Node* enclosingNodeWithNonInlineRenderer(Node* node) >+{ >+ for (; node; node = node->parentNode()) { >+ if (node->renderer() && !node->renderer()->isInline()) >+ return node; >+ } >+ return 0; >+} >+ >+static Node* nextLeafWithSameEditability(Node* node, int offset) >+{ >+ bool editable = node->rendererIsEditable(); >+ ASSERT(offset >= 0); >+ Node* child = node->childNode(offset); >+ node = child ? child->nextLeafNode() : node->lastDescendant()->nextLeafNode(); >+ while (node) { >+ if (editable == node->rendererIsEditable()) >+ return node; >+ node = node->nextLeafNode(); >+ } >+ return 0; >+} >+ >+static Node* nextLeafWithSameEditability(Node* node, EditableType editableType = ContentIsEditable) >+{ >+ if (!node) >+ return 0; >+ >+ bool editable = node->rendererIsEditable(editableType); >+ node = node->nextLeafNode(); >+ while (node) { >+ if (editable == node->rendererIsEditable(editableType)) >+ return node; >+ node = node->nextLeafNode(); >+ } >+ return 0; >+} >+ >+// FIXME: consolidate with code in previousLinePosition. >+static const RootInlineBox* previousRootInlineBox(const InlineBox* box, const VisiblePosition& visiblePosition) >+{ >+ Node* highestRoot = highestEditableRoot(visiblePosition.deepEquivalent(), ContentIsEditable); >+ Node* node = box->renderer()->node(); >+ Node* enclosingBlockNode = enclosingNodeWithNonInlineRenderer(node); >+ Node* previousNode = previousLeafWithSameEditability(node, ContentIsEditable); >+ >+ while (previousNode && enclosingBlockNode == enclosingNodeWithNonInlineRenderer(previousNode)) >+ previousNode = previousLeafWithSameEditability(previousNode, ContentIsEditable); >+ >+ while (previousNode && !previousNode->isShadowRoot()) { >+ if (highestEditableRoot(firstPositionInOrBeforeNode(previousNode), ContentIsEditable) != highestRoot) >+ break; >+ >+ Position pos = previousNode->hasTagName(brTag) ? positionBeforeNode(previousNode) : >+ createLegacyEditingPosition(previousNode, caretMaxOffset(previousNode)); >+ >+ if (pos.isCandidate()) { >+ RenderedPosition renderedPos(pos, DOWNSTREAM); >+ RootInlineBox* root = renderedPos.rootBox(); >+ if (root) >+ return root; >+ } >+ >+ previousNode = previousLeafWithSameEditability(previousNode, ContentIsEditable); >+ } >+ return 0; >+} >+ >+static const RootInlineBox* nextRootInlineBox(const InlineBox* box, const VisiblePosition& visiblePosition) >+{ >+ Node* highestRoot = highestEditableRoot(visiblePosition.deepEquivalent(), ContentIsEditable); >+ Node* node = box->renderer()->node(); >+ Node* enclosingBlockNode = enclosingNodeWithNonInlineRenderer(node); >+ Node* nextNode = nextLeafWithSameEditability(node, ContentIsEditable); >+ while (nextNode && enclosingBlockNode == enclosingNodeWithNonInlineRenderer(nextNode)) >+ nextNode = nextLeafWithSameEditability(nextNode, ContentIsEditable); >+ >+ while (nextNode && !nextNode->isShadowRoot()) { >+ if (highestEditableRoot(firstPositionInOrBeforeNode(nextNode), ContentIsEditable) != highestRoot) >+ break; >+ >+ Position pos; >+ pos = createLegacyEditingPosition(nextNode, caretMinOffset(nextNode)); >+ >+ if (pos.isCandidate()) { >+ RenderedPosition renderedPos(pos, DOWNSTREAM); >+ RootInlineBox* root = renderedPos.rootBox(); >+ if (root) >+ return root; >+ } >+ >+ nextNode = nextLeafWithSameEditability(nextNode, ContentIsEditable); >+ } >+ return 0; >+} >+ >+static int boxIndexInVector(const InlineTextBox* box, const Vector<InlineBox*>& leafBoxesInLogicalOrder) >+{ >+ for (size_t i = 0; i < leafBoxesInLogicalOrder.size(); ++i) { >+ if (box == leafBoxesInLogicalOrder[i]) >+ return i; >+ } >+ return 0; >+} >+ >+static const InlineTextBox* previousBoxInLine(const RootInlineBox* root, const InlineTextBox* box, Vector<InlineBox*>& leafBoxesInLogicalOrder) >+{ >+ if (!root) >+ return 0; >+ >+ leafBoxesInLogicalOrder.clear(); >+ root->collectLeafBoxesInLogicalOrder(leafBoxesInLogicalOrder); >+ >+ // If box is null, root is box's previous RootInlineBox, and previousBox is the last logical box in root. >+ int boxIndex = leafBoxesInLogicalOrder.size() - 1; >+ if (box) >+ boxIndex = boxIndexInVector(box, leafBoxesInLogicalOrder) - 1; >+ >+ for (int i = boxIndex; i >= 0; --i) { >+ if (leafBoxesInLogicalOrder[i]->isInlineTextBox()) >+ return toInlineTextBox(leafBoxesInLogicalOrder[i]); >+ } >+ >+ return 0; >+} >+ >+static const InlineTextBox* logicallyPreviousBox(const VisiblePosition& visiblePosition, const InlineTextBox* textBox, bool& previousBoxInDifferentBlock) >+{ >+ const InlineBox* startBox = textBox; >+ Vector<InlineBox*> leafBoxesInLogicalOrder; >+ >+ const InlineTextBox* previousBox = previousBoxInLine(startBox->root(), textBox, leafBoxesInLogicalOrder); >+ if (previousBox) >+ return previousBox; >+ >+ previousBox = previousBoxInLine(startBox->root()->prevRootBox(), 0, leafBoxesInLogicalOrder); >+ if (previousBox) >+ return previousBox; >+ >+ while (1) { >+ const RootInlineBox* previousRoot = previousRootInlineBox(startBox, visiblePosition); >+ if (!previousRoot) >+ break; >+ >+ previousBox = previousBoxInLine(previousRoot, 0, leafBoxesInLogicalOrder); >+ if (previousBox) { >+ previousBoxInDifferentBlock = true; >+ return previousBox; >+ } >+ >+ if (!leafBoxesInLogicalOrder.size()) >+ break; >+ startBox = leafBoxesInLogicalOrder[0]; >+ } >+ return 0; >+} >+ >+static const InlineTextBox* nextBoxInLine(const RootInlineBox* root, const InlineTextBox* box, Vector<InlineBox*>& leafBoxesInLogicalOrder) >+{ >+ if (!root) >+ return 0; >+ >+ leafBoxesInLogicalOrder.clear(); >+ root->collectLeafBoxesInLogicalOrder(leafBoxesInLogicalOrder); >+ >+ // If box is null, root is box's next RootInlineBox, and nextBox is the first logical box in root. >+ // Otherwise, root is box's RootInlineBox, and nextBox is the next logical box in the same line. >+ size_t nextBoxIndex = 0; >+ if (box) >+ nextBoxIndex = boxIndexInVector(box, leafBoxesInLogicalOrder) + 1; >+ >+ for (size_t i = nextBoxIndex; i < leafBoxesInLogicalOrder.size(); ++i) { >+ if (leafBoxesInLogicalOrder[i]->isInlineTextBox()) >+ return toInlineTextBox(leafBoxesInLogicalOrder[i]); >+ } >+ >+ return 0; >+} >+ >+static const InlineTextBox* logicallyNextBox(const VisiblePosition& visiblePosition, const InlineTextBox* textBox, bool& nextBoxInDifferentBlock) >+{ >+ const InlineBox* startBox = textBox; >+ Vector<InlineBox*> leafBoxesInLogicalOrder; >+ >+ const InlineTextBox* nextBox = nextBoxInLine(startBox->root(), textBox, leafBoxesInLogicalOrder); >+ if (nextBox) >+ return nextBox; >+ >+ nextBox = nextBoxInLine(startBox->root()->nextRootBox(), 0, leafBoxesInLogicalOrder); >+ if (nextBox) >+ return nextBox; >+ >+ while (1) { >+ const RootInlineBox* nextRoot = nextRootInlineBox(startBox, visiblePosition); >+ if (!nextRoot) >+ break; >+ >+ nextBox = nextBoxInLine(nextRoot, 0, leafBoxesInLogicalOrder); >+ if (nextBox) { >+ nextBoxInDifferentBlock = true; >+ return nextBox; >+ } >+ >+ if (!leafBoxesInLogicalOrder.size()) >+ break; >+ startBox = leafBoxesInLogicalOrder[0]; >+ } >+ return 0; >+} >+ >+static TextBreakIterator* wordBreakIteratorForMinOffsetBoundary(const VisiblePosition& visiblePosition, const InlineTextBox* textBox, >+ int& previousBoxLength, bool& previousBoxInDifferentBlock) >+{ >+ previousBoxInDifferentBlock = false; >+ >+ // FIXME: Handle the case when we don't have an inline text box. >+ const InlineTextBox* previousBox = logicallyPreviousBox(visiblePosition, textBox, previousBoxInDifferentBlock); >+ >+ int len = 0; >+ Vector<UChar, 1024> string; >+ if (previousBox) { >+ previousBoxLength = previousBox->len(); >+ string.append(previousBox->textRenderer()->text()->characters() + previousBox->start(), previousBoxLength); >+ len += previousBoxLength; >+ } >+ string.append(textBox->textRenderer()->text()->characters() + textBox->start(), textBox->len()); >+ len += textBox->len(); >+ >+ return wordBreakIterator(string.data(), len); >+} >+ >+static TextBreakIterator* wordBreakIteratorForMaxOffsetBoundary(const VisiblePosition& visiblePosition, const InlineTextBox* textBox, bool& nextBoxInDifferentBlock) >+{ >+ nextBoxInDifferentBlock = false; >+ >+ // FIXME: Handle the case when we don't have an inline text box. >+ const InlineTextBox* nextBox = logicallyNextBox(visiblePosition, textBox, nextBoxInDifferentBlock); >+ >+ int len = 0; >+ Vector<UChar, 1024> string; >+ string.append(textBox->textRenderer()->text()->characters() + textBox->start(), textBox->len()); >+ len += textBox->len(); >+ if (nextBox) { >+ string.append(nextBox->textRenderer()->text()->characters() + nextBox->start(), nextBox->len()); >+ len += nextBox->len(); >+ } >+ >+ return wordBreakIterator(string.data(), len); >+} >+ >+static bool isLogicalStartOfWord(TextBreakIterator* iter, int position, bool hardLineBreak) >+{ >+ bool boundary = hardLineBreak ? true : isTextBreak(iter, position); >+ if (!boundary) >+ return false; >+ >+ textBreakFollowing(iter, position); >+ // isWordTextBreak returns true after moving across a word and false after moving across a punctuation/space. >+ return isWordTextBreak(iter); >+} >+ >+static bool islogicalEndOfWord(TextBreakIterator* iter, int position, bool hardLineBreak) >+{ >+ bool boundary = isTextBreak(iter, position); >+ return (hardLineBreak || boundary) && isWordTextBreak(iter); >+} >+ >+enum CursorMovementDirection { MoveLeft, MoveRight }; >+ >+static VisiblePosition visualWordPosition(const VisiblePosition& visiblePosition, CursorMovementDirection direction) >+{ >+ if (visiblePosition.isNull()) >+ return VisiblePosition(); >+ >+ TextDirection blockDirection = directionOfEnclosingBlock(visiblePosition.deepEquivalent()); >+ InlineBox* previouslyVisitedBox = 0; >+ VisiblePosition current = visiblePosition; >+ TextBreakIterator* iter = 0; >+ >+ while (1) { >+ VisiblePosition adjacentCharacterPosition = direction == MoveRight ? current.right(true) : current.left(true); >+ if (adjacentCharacterPosition == current || adjacentCharacterPosition.isNull()) >+ return VisiblePosition(); >+ >+ InlineBox* box; >+ int offsetInBox; >+ adjacentCharacterPosition.deepEquivalent().getInlineBoxAndOffset(UPSTREAM, box, offsetInBox); >+ >+ if (!box) >+ break; >+ if (!box->isInlineTextBox()) { >+ current = adjacentCharacterPosition; >+ continue; >+ } >+ >+ InlineTextBox* textBox = toInlineTextBox(box); >+ int previousBoxLength = 0; >+ bool previousBoxInDifferentBlock = false; >+ bool nextBoxInDifferentBlock = false; >+ bool movingIntoNewBox = previouslyVisitedBox != box; >+ >+ if (offsetInBox == box->caretMinOffset()) >+ iter = wordBreakIteratorForMinOffsetBoundary(visiblePosition, textBox, previousBoxLength, previousBoxInDifferentBlock); >+ else if (offsetInBox == box->caretMaxOffset()) >+ iter = wordBreakIteratorForMaxOffsetBoundary(visiblePosition, textBox, nextBoxInDifferentBlock); >+ else if (movingIntoNewBox) { >+ iter = wordBreakIterator(textBox->textRenderer()->text()->characters() + textBox->start(), textBox->len()); >+ previouslyVisitedBox = box; >+ } >+ >+ textBreakFirst(iter); >+ int offsetInIterator = offsetInBox - textBox->start() + previousBoxLength; >+ >+ bool isWordBreak; >+ if (box->direction() == blockDirection) { >+ bool logicalStartInRenderer = offsetInBox == static_cast<int>(textBox->start()) && previousBoxInDifferentBlock; >+ isWordBreak = isLogicalStartOfWord(iter, offsetInIterator, logicalStartInRenderer); >+ } else { >+ bool logicalEndInRenderer = offsetInBox == static_cast<int>(textBox->start() + textBox->len()) && nextBoxInDifferentBlock; >+ isWordBreak = islogicalEndOfWord(iter, offsetInIterator, logicalEndInRenderer); >+ } >+ >+ if (isWordBreak) >+ return adjacentCharacterPosition; >+ >+ current = adjacentCharacterPosition; >+ } >+ return VisiblePosition(); >+} >+ >+VisiblePosition leftWordPosition(const VisiblePosition& visiblePosition) >+{ >+ VisiblePosition leftWordBreak = visualWordPosition(visiblePosition, MoveLeft); >+ leftWordBreak = visiblePosition.honorEditingBoundaryAtOrBefore(leftWordBreak); >+ >+ // FIXME: How should we handle a non-editable position? >+ if (leftWordBreak.isNull() && isEditablePosition(visiblePosition.deepEquivalent())) { >+ TextDirection blockDirection = directionOfEnclosingBlock(visiblePosition.deepEquivalent()); >+ leftWordBreak = blockDirection == LTR ? startOfEditableContent(visiblePosition) : endOfEditableContent(visiblePosition); >+ } >+ return leftWordBreak; >+} >+ >+VisiblePosition rightWordPosition(const VisiblePosition& visiblePosition) >+{ >+ VisiblePosition rightWordBreak = visualWordPosition(visiblePosition, MoveRight); >+ rightWordBreak = visiblePosition.honorEditingBoundaryAtOrBefore(rightWordBreak); >+ >+ // FIXME: How should we handle a non-editable position? >+ if (rightWordBreak.isNull() && isEditablePosition(visiblePosition.deepEquivalent())) { >+ TextDirection blockDirection = directionOfEnclosingBlock(visiblePosition.deepEquivalent()); >+ rightWordBreak = blockDirection == LTR ? endOfEditableContent(visiblePosition) : startOfEditableContent(visiblePosition); >+ } >+ return rightWordBreak; >+} >+ >+ > enum BoundarySearchContextAvailability { DontHaveMoreContext, MayHaveMoreContext }; > > typedef unsigned (*BoundarySearchFunction)(const UChar*, unsigned length, unsigned offset, BoundarySearchContextAvailability, bool& needMoreContext); >@@ -517,28 +886,6 @@ bool isEndOfLine(const VisiblePosition & > return p.isNotNull() && p == endOfLine(p); > } > >-// The first leaf before node that has the same editability as node. >-static Node* previousLeafWithSameEditability(Node* node, EditableType editableType) >-{ >- bool editable = node->rendererIsEditable(editableType); >- Node* n = node->previousLeafNode(); >- while (n) { >- if (editable == n->rendererIsEditable(editableType)) >- return n; >- n = n->previousLeafNode(); >- } >- return 0; >-} >- >-static Node* enclosingNodeWithNonInlineRenderer(Node* n) >-{ >- for (Node* p = n; p; p = p->parentNode()) { >- if (p->renderer() && !p->renderer()->isInline()) >- return p; >- } >- return 0; >-} >- > static inline IntPoint absoluteLineDirectionPointToLocalPointInBlock(RootInlineBox* root, int lineDirectionPoint) > { > ASSERT(root); >@@ -625,34 +972,6 @@ VisiblePosition previousLinePosition(con > return VisiblePosition(firstPositionInNode(rootElement), DOWNSTREAM); > } > >-static Node* nextLeafWithSameEditability(Node* node, int offset) >-{ >- bool editable = node->rendererIsEditable(); >- ASSERT(offset >= 0); >- Node* child = node->childNode(offset); >- Node* n = child ? child->nextLeafNode() : node->lastDescendant()->nextLeafNode(); >- while (n) { >- if (editable == n->rendererIsEditable()) >- return n; >- n = n->nextLeafNode(); >- } >- return 0; >-} >- >-static Node* nextLeafWithSameEditability(Node* node, EditableType editableType = ContentIsEditable) >-{ >- if (!node) >- return 0; >- >- bool editable = node->rendererIsEditable(editableType); >- Node* n = node->nextLeafNode(); >- while (n) { >- if (editable == n->rendererIsEditable(editableType)) >- return n; >- n = n->nextLeafNode(); >- } >- return 0; >-} > > VisiblePosition nextLinePosition(const VisiblePosition &visiblePosition, int lineDirectionPoint, EditableType editableType) > { >@@ -1099,543 +1418,4 @@ VisiblePosition rightBoundaryOfLine(cons > return direction == LTR ? logicalEndOfLine(c) : logicalStartOfLine(c); > } > >-static const int invalidOffset = -1; >-static const int offsetNotFound = -1; >- >-static bool positionIsInBox(const VisiblePosition& wordBreak, const InlineBox* box, int& offsetOfWordBreak) >-{ >- if (wordBreak.isNull()) >- return false; >- >- InlineBox* boxOfWordBreak; >- wordBreak.getInlineBoxAndOffset(boxOfWordBreak, offsetOfWordBreak); >- return box == boxOfWordBreak; >-} >- >-static VisiblePosition previousWordBreakInBoxInsideBlockWithSameDirectionality(const InlineBox* box, const VisiblePosition& previousWordBreak, int& offsetOfWordBreak) >-{ >- // In a LTR block, the word break should be on the left boundary of a word. >- // In a RTL block, the word break should be on the right boundary of a word. >- // Because nextWordPosition() returns the word break on the right boundary of the word for LTR text, >- // we need to use previousWordPosition() to traverse words within the inline boxes from right to left >- // to find the previous word break (i.e. the first word break on the left). The same applies to RTL text. >- >- bool hasSeenWordBreakInThisBox = previousWordBreak.isNotNull(); >- >- VisiblePosition wordBreak; >- >- if (hasSeenWordBreakInThisBox) >- wordBreak = previousWordBreak; >- else { >- wordBreak = createLegacyEditingPosition(box->renderer()->node(), box->caretMaxOffset()); >- >- // Return the rightmost word boundary of LTR box or leftmost word boundary of RTL box if >- // it is not in the previously visited boxes. For example, given a logical text >- // "abc def hij opq", there are 2 boxes: the "abc def " (starts at 0 and length is 8) >- // and the "hij opq" (starts at 12 and length is 7). The word breaks are >- // "abc |def | hij |opq". We normally catch the word break between "def" and "hij" when >- // we visit the box that contains "hij opq", but this word break doesn't exist in the box >- // that contains "hij opq" when there are multiple spaces. So we detect it when we're >- // traversing the box that contains "abc def " instead. >- >- if ((box->isLeftToRightDirection() && box->nextLeafChild()) >- || (!box->isLeftToRightDirection() && box->prevLeafChild())) { >- >- VisiblePosition positionAfterWord = nextBoundary(wordBreak, nextWordPositionBoundary); >- if (positionAfterWord.isNotNull()) { >- VisiblePosition positionBeforeWord = previousBoundary(positionAfterWord, previousWordPositionBoundary); >- >- if (positionIsInBox(positionBeforeWord, box, offsetOfWordBreak)) >- return positionBeforeWord; >- } >- } >- } >- >- wordBreak = previousBoundary(wordBreak, previousWordPositionBoundary); >- if (previousWordBreak == wordBreak) >- return VisiblePosition(); >- >- return positionIsInBox(wordBreak, box, offsetOfWordBreak) ? wordBreak : VisiblePosition(); >-} >- >-static VisiblePosition leftmostPositionInRTLBoxInLTRBlock(const InlineBox* box) >-{ >- // FIXME: Probably need to take care of bidi level too. >- Node* node = box->renderer()->node(); >- InlineBox* previousLeaf = box->prevLeafChild(); >- InlineBox* nextLeaf = box->nextLeafChild(); >- >- if (previousLeaf && !previousLeaf->isLeftToRightDirection()) >- return createLegacyEditingPosition(node, box->caretMaxOffset()); >- >- if (nextLeaf && !nextLeaf->isLeftToRightDirection()) { >- if (previousLeaf) >- return createLegacyEditingPosition(previousLeaf->renderer()->node(), previousLeaf->caretMaxOffset()); >- >- InlineBox* lastRTLLeaf; >- do { >- lastRTLLeaf = nextLeaf; >- nextLeaf = nextLeaf->nextLeafChild(); >- } while (nextLeaf && !nextLeaf->isLeftToRightDirection()); >- return createLegacyEditingPosition(lastRTLLeaf->renderer()->node(), lastRTLLeaf->caretMinOffset()); >- } >- >- return createLegacyEditingPosition(node, box->caretMinOffset()); >-} >- >-static VisiblePosition rightmostPositionInLTRBoxInRTLBlock(const InlineBox* box) >-{ >- // FIXME: Probably need to take care of bidi level too. >- Node* node = box->renderer()->node(); >- InlineBox* previousLeaf = box->prevLeafChild(); >- InlineBox* nextLeaf = box->nextLeafChild(); >- >- if (nextLeaf && nextLeaf->isLeftToRightDirection()) >- return createLegacyEditingPosition(node, box->caretMaxOffset()); >- >- if (previousLeaf && previousLeaf->isLeftToRightDirection()) { >- if (nextLeaf) >- return createLegacyEditingPosition(nextLeaf->renderer()->node(), nextLeaf->caretMaxOffset()); >- >- InlineBox* firstLTRLeaf; >- do { >- firstLTRLeaf = previousLeaf; >- previousLeaf = previousLeaf->prevLeafChild(); >- } while (previousLeaf && previousLeaf->isLeftToRightDirection()); >- return createLegacyEditingPosition(firstLTRLeaf->renderer()->node(), firstLTRLeaf->caretMinOffset()); >- } >- >- return createLegacyEditingPosition(node, box->caretMinOffset()); >-} >- >-static VisiblePosition lastWordBreakInBox(const InlineBox* box, int& offsetOfWordBreak) >-{ >- // Add the leftmost word break for RTL box or rightmost word break for LTR box. >- InlineBox* previousLeaf = box->prevLeafChild(); >- InlineBox* nextLeaf = box->nextLeafChild(); >- VisiblePosition boundaryPosition; >- if (box->direction() == RTL && (!previousLeaf || previousLeaf->isLeftToRightDirection())) >- boundaryPosition = leftmostPositionInRTLBoxInLTRBlock(box); >- else if (box->direction() == LTR && (!nextLeaf || !nextLeaf->isLeftToRightDirection())) >- boundaryPosition = rightmostPositionInLTRBoxInRTLBlock(box); >- >- if (boundaryPosition.isNull()) >- return VisiblePosition(); >- >- VisiblePosition wordBreak = nextBoundary(boundaryPosition, nextWordPositionBoundary); >- if (wordBreak.isNull()) >- wordBreak = boundaryPosition; >- else if (wordBreak != boundaryPosition) >- wordBreak = previousBoundary(wordBreak, previousWordPositionBoundary); >- >- return positionIsInBox(wordBreak, box, offsetOfWordBreak) ? wordBreak : VisiblePosition(); >-} >- >-static bool positionIsVisuallyOrderedInBoxInBlockWithDifferentDirectionality(const VisiblePosition& wordBreak, const InlineBox* box, int& offsetOfWordBreak) >-{ >- int previousOffset = offsetOfWordBreak; >- return positionIsInBox(wordBreak, box, offsetOfWordBreak) >- && (previousOffset == invalidOffset || previousOffset < offsetOfWordBreak); >-} >- >-static VisiblePosition nextWordBreakInBoxInsideBlockWithDifferentDirectionality( >- const InlineBox* box, const VisiblePosition& previousWordBreak, int& offsetOfWordBreak, bool& isLastWordBreakInBox) >-{ >- // FIXME: Probably need to take care of bidi level too. >- >- // In a LTR block, the word break should be on the left boundary of a word. >- // In a RTL block, the word break should be on the right boundary of a word. >- // Because previousWordPosition() returns the word break on the right boundary of the word for RTL text, >- // we need to use nextWordPosition() to traverse words within the inline boxes from right to left to find the next word break. >- // The same applies to LTR text, in which words are traversed within the inline boxes from left to right. >- >- bool hasSeenWordBreakInThisBox = previousWordBreak.isNotNull(); >- VisiblePosition wordBreak = hasSeenWordBreakInThisBox ? previousWordBreak : >- createLegacyEditingPosition(box->renderer()->node(), box->caretMinOffset()); >- >- wordBreak = nextBoundary(wordBreak, nextWordPositionBoundary); >- >- // Given RTL box "ABC DEF" either follows a LTR box or is the first visual box in an LTR block as an example, >- // the visual display of the RTL box is: "(0)J(10)I(9)H(8) (7)F(6)E(5)D(4) (3)C(2)B(1)A(11)", >- // where the number in parenthesis represents offset in visiblePosition. >- // Start at offset 0, the first word break is at offset 3, the 2nd word break is at offset 7, and the 3rd word break should be at offset 0. >- // But nextWordPosition() of offset 7 is offset 11, which should be ignored, >- // and the position at offset 0 should be manually added as the last word break within the box. >- if (wordBreak != previousWordBreak && positionIsVisuallyOrderedInBoxInBlockWithDifferentDirectionality(wordBreak, box, offsetOfWordBreak)) { >- isLastWordBreakInBox = false; >- return wordBreak; >- } >- >- isLastWordBreakInBox = true; >- return lastWordBreakInBox(box, offsetOfWordBreak); >-} >- >-struct WordBoundaryEntry { >- WordBoundaryEntry() >- : offsetInInlineBox(invalidOffset) >- { >- } >- >- WordBoundaryEntry(const VisiblePosition& position, int offset) >- : visiblePosition(position) >- , offsetInInlineBox(offset) >- { >- } >- >- VisiblePosition visiblePosition; >- int offsetInInlineBox; >-}; >- >-typedef Vector<WordBoundaryEntry, 50> WordBoundaryVector; >- >-static void collectWordBreaksInBoxInsideBlockWithSameDirectionality(const InlineBox* box, WordBoundaryVector& orderedWordBoundaries) >-{ >- orderedWordBoundaries.clear(); >- >- VisiblePosition wordBreak; >- int offsetOfWordBreak = invalidOffset; >- while (1) { >- wordBreak = previousWordBreakInBoxInsideBlockWithSameDirectionality(box, wordBreak, offsetOfWordBreak); >- if (wordBreak.isNull()) >- break; >- WordBoundaryEntry wordBoundaryEntry(wordBreak, offsetOfWordBreak); >- orderedWordBoundaries.append(wordBoundaryEntry); >- } >-} >- >-static void collectWordBreaksInBoxInsideBlockWithDifferntDirectionality(const InlineBox* box, WordBoundaryVector& orderedWordBoundaries) >-{ >- orderedWordBoundaries.clear(); >- >- VisiblePosition wordBreak; >- int offsetOfWordBreak = invalidOffset; >- bool isLastWordBreakInBox = false; >- while (1) { >- wordBreak = nextWordBreakInBoxInsideBlockWithDifferentDirectionality(box, wordBreak, offsetOfWordBreak, isLastWordBreakInBox); >- if (wordBreak.isNotNull()) { >- WordBoundaryEntry wordBoundaryEntry(wordBreak, offsetOfWordBreak); >- orderedWordBoundaries.append(wordBoundaryEntry); >- } >- if (isLastWordBreakInBox) >- break; >- } >-} >- >-static void collectWordBreaksInBox(const InlineBox* box, WordBoundaryVector& orderedWordBoundaries, TextDirection blockDirection) >-{ >- if (box->direction() == blockDirection) >- collectWordBreaksInBoxInsideBlockWithSameDirectionality(box, orderedWordBoundaries); >- else >- collectWordBreaksInBoxInsideBlockWithDifferntDirectionality(box, orderedWordBoundaries); >-} >- >-static VisiblePosition previousWordBoundaryInBox(const InlineBox* box, int offset) >-{ >- int offsetOfWordBreak = 0; >- VisiblePosition wordBreak; >- while (true) { >- wordBreak = previousWordBreakInBoxInsideBlockWithSameDirectionality(box, wordBreak, offsetOfWordBreak); >- if (wordBreak.isNull()) >- break; >- if (offset == invalidOffset || offsetOfWordBreak != offset) >- return wordBreak; >- } >- return VisiblePosition(); >-} >- >-static VisiblePosition nextWordBoundaryInBox(const InlineBox* box, int offset) >-{ >- int offsetOfWordBreak = 0; >- VisiblePosition wordBreak; >- bool isLastWordBreakInBox = false; >- do { >- wordBreak = nextWordBreakInBoxInsideBlockWithDifferentDirectionality(box, wordBreak, offsetOfWordBreak, isLastWordBreakInBox); >- if (wordBreak.isNotNull() && (offset == invalidOffset || offsetOfWordBreak != offset)) >- return wordBreak; >- } while (!isLastWordBreakInBox); >- return VisiblePosition(); >-} >- >-static VisiblePosition visuallyLastWordBoundaryInBox(const InlineBox* box, int offset, TextDirection blockDirection) >-{ >- WordBoundaryVector orderedWordBoundaries; >- collectWordBreaksInBox(box, orderedWordBoundaries, blockDirection); >- if (!orderedWordBoundaries.size()) >- return VisiblePosition(); >- if (offset == invalidOffset || orderedWordBoundaries[orderedWordBoundaries.size() - 1].offsetInInlineBox != offset) >- return orderedWordBoundaries[orderedWordBoundaries.size() - 1].visiblePosition; >- if (orderedWordBoundaries.size() > 1) >- return orderedWordBoundaries[orderedWordBoundaries.size() - 2].visiblePosition; >- return VisiblePosition(); >-} >- >-static int greatestOffsetUnder(int offset, bool boxAndBlockAreInSameDirection, const WordBoundaryVector& orderedWordBoundaries) >-{ >- if (!orderedWordBoundaries.size()) >- return offsetNotFound; >- // FIXME: binary search. >- if (boxAndBlockAreInSameDirection) { >- for (unsigned i = 0; i < orderedWordBoundaries.size(); ++i) { >- if (orderedWordBoundaries[i].offsetInInlineBox < offset) >- return i; >- } >- return offsetNotFound; >- } >- for (int i = orderedWordBoundaries.size() - 1; i >= 0; --i) { >- if (orderedWordBoundaries[i].offsetInInlineBox < offset) >- return i; >- } >- return offsetNotFound; >-} >- >-static int smallestOffsetAbove(int offset, bool boxAndBlockAreInSameDirection, const WordBoundaryVector& orderedWordBoundaries) >-{ >- if (!orderedWordBoundaries.size()) >- return offsetNotFound; >- // FIXME: binary search. >- if (boxAndBlockAreInSameDirection) { >- for (int i = orderedWordBoundaries.size() - 1; i >= 0; --i) { >- if (orderedWordBoundaries[i].offsetInInlineBox > offset) >- return i; >- } >- return offsetNotFound; >- } >- for (unsigned i = 0; i < orderedWordBoundaries.size(); ++i) { >- if (orderedWordBoundaries[i].offsetInInlineBox > offset) >- return i; >- } >- return offsetNotFound; >-} >- >-static const RootInlineBox* previousRootInlineBox(const InlineBox* box) >-{ >- Node* node = box->renderer()->node(); >- Node* enclosingBlockNode = enclosingNodeWithNonInlineRenderer(node); >- Node* previousNode = node->previousLeafNode(); >- while (previousNode && enclosingBlockNode == enclosingNodeWithNonInlineRenderer(previousNode)) >- previousNode = previousNode->previousLeafNode(); >- >- while (previousNode && !previousNode->isShadowRoot()) { >- Position pos = createLegacyEditingPosition(previousNode, caretMaxOffset(previousNode)); >- >- if (pos.isCandidate()) { >- RenderedPosition renderedPos(pos, DOWNSTREAM); >- RootInlineBox* root = renderedPos.rootBox(); >- if (root) >- return root; >- } >- >- previousNode = previousNode->previousLeafNode(); >- } >- return 0; >-} >- >-static const RootInlineBox* nextRootInlineBox(const InlineBox* box) >-{ >- Node* node = box->renderer()->node(); >- Node* enclosingBlockNode = enclosingNodeWithNonInlineRenderer(node); >- Node* nextNode = node->nextLeafNode(); >- while (nextNode && enclosingBlockNode == enclosingNodeWithNonInlineRenderer(nextNode)) >- nextNode = nextNode->nextLeafNode(); >- >- while (nextNode && !nextNode->isShadowRoot()) { >- Position pos; >- pos = createLegacyEditingPosition(nextNode, caretMinOffset(nextNode)); >- >- if (pos.isCandidate()) { >- RenderedPosition renderedPos(pos, DOWNSTREAM); >- RootInlineBox* root = renderedPos.rootBox(); >- if (root) >- return root; >- } >- >- nextNode = nextNode->nextLeafNode(); >- } >- return 0; >-} >- >-static const InlineBox* leftInlineBox(const InlineBox* box, TextDirection blockDirection) >-{ >- if (box->prevLeafChild()) >- return box->prevLeafChild(); >- >- const RootInlineBox* rootBox = box->root(); >- const bool isBlockLTR = blockDirection == LTR; >- const InlineFlowBox* leftLineBox = isBlockLTR ? rootBox->prevLineBox() : rootBox->nextLineBox(); >- if (leftLineBox) >- return leftLineBox->lastLeafChild(); >- >- const RootInlineBox* leftRootInlineBox = isBlockLTR ? previousRootInlineBox(box) : >- nextRootInlineBox(box); >- return leftRootInlineBox ? leftRootInlineBox->lastLeafChild() : 0; >-} >- >-static const InlineBox* rightInlineBox(const InlineBox* box, TextDirection blockDirection) >-{ >- if (box->nextLeafChild()) >- return box->nextLeafChild(); >- >- const RootInlineBox* rootBox = box->root(); >- const bool isBlockLTR = blockDirection == LTR; >- const InlineFlowBox* rightLineBox = isBlockLTR ? rootBox->nextLineBox() : rootBox->prevLineBox(); >- if (rightLineBox) >- return rightLineBox->firstLeafChild(); >- >- const RootInlineBox* rightRootInlineBox = isBlockLTR ? nextRootInlineBox(box) : >- previousRootInlineBox(box); >- return rightRootInlineBox ? rightRootInlineBox->firstLeafChild() : 0; >-} >- >-static VisiblePosition leftWordBoundary(const InlineBox* box, int offset, TextDirection blockDirection) >-{ >- VisiblePosition wordBreak; >- for (const InlineBox* adjacentBox = box; adjacentBox; adjacentBox = leftInlineBox(adjacentBox, blockDirection)) { >- if (blockDirection == LTR) { >- if (adjacentBox->isLeftToRightDirection()) >- wordBreak = previousWordBoundaryInBox(adjacentBox, adjacentBox == box ? offset : invalidOffset); >- else >- wordBreak = nextWordBoundaryInBox(adjacentBox, adjacentBox == box ? offset : invalidOffset); >- } else >- wordBreak = visuallyLastWordBoundaryInBox(adjacentBox, adjacentBox == box ? offset : invalidOffset, blockDirection); >- if (wordBreak.isNotNull()) >- return wordBreak; >- } >- return VisiblePosition(); >-} >- >-static VisiblePosition rightWordBoundary(const InlineBox* box, int offset, TextDirection blockDirection) >-{ >- >- VisiblePosition wordBreak; >- for (const InlineBox* adjacentBox = box; adjacentBox; adjacentBox = rightInlineBox(adjacentBox, blockDirection)) { >- if (blockDirection == RTL) { >- if (adjacentBox->isLeftToRightDirection()) >- wordBreak = nextWordBoundaryInBox(adjacentBox, adjacentBox == box ? offset : invalidOffset); >- else >- wordBreak = previousWordBoundaryInBox(adjacentBox, adjacentBox == box ? offset : invalidOffset); >- } else >- wordBreak = visuallyLastWordBoundaryInBox(adjacentBox, adjacentBox == box ? offset : invalidOffset, blockDirection); >- if (!wordBreak.isNull()) >- return wordBreak; >- } >- return VisiblePosition(); >-} >- >-static bool positionIsInBoxButNotOnBoundary(const VisiblePosition& wordBreak, const InlineBox* box) >-{ >- int offsetOfWordBreak; >- return positionIsInBox(wordBreak, box, offsetOfWordBreak) >- && offsetOfWordBreak != box->caretMaxOffset() && offsetOfWordBreak != box->caretMinOffset(); >-} >- >-static VisiblePosition leftWordPositionIgnoringEditingBoundary(const VisiblePosition& visiblePosition) >-{ >- InlineBox* box; >- int offset; >- visiblePosition.getInlineBoxAndOffset(box, offset); >- >- if (!box) >- return VisiblePosition(); >- >- TextDirection blockDirection = directionOfEnclosingBlock(visiblePosition.deepEquivalent()); >- >- // FIXME: If the box's directionality is the same as that of the enclosing block, when the offset is at the box boundary >- // and the direction is towards inside the box, do I still need to make it a special case? For example, a LTR box inside a LTR block, >- // when offset is at box's caretMinOffset and the direction is DirectionRight, should it be taken care as a general case? >- if (offset == box->caretLeftmostOffset()) >- return leftWordBoundary(leftInlineBox(box, blockDirection), invalidOffset, blockDirection); >- if (offset == box->caretRightmostOffset()) >- return leftWordBoundary(box, offset, blockDirection); >- >- >- VisiblePosition wordBreak; >- if (blockDirection == LTR) { >- if (box->direction() == blockDirection) >- wordBreak = previousBoundary(visiblePosition, previousWordPositionBoundary); >- else >- wordBreak = nextBoundary(visiblePosition, nextWordPositionBoundary); >- } >- if (wordBreak.isNotNull() && positionIsInBoxButNotOnBoundary(wordBreak, box)) >- return wordBreak; >- >- WordBoundaryVector orderedWordBoundaries; >- collectWordBreaksInBox(box, orderedWordBoundaries, blockDirection); >- >- int index = box->isLeftToRightDirection() ? greatestOffsetUnder(offset, blockDirection == LTR, orderedWordBoundaries) >- : smallestOffsetAbove(offset, blockDirection == RTL, orderedWordBoundaries); >- if (index >= 0) >- return orderedWordBoundaries[index].visiblePosition; >- >- return leftWordBoundary(leftInlineBox(box, blockDirection), invalidOffset, blockDirection); >-} >- >-static VisiblePosition rightWordPositionIgnoringEditingBoundary(const VisiblePosition& visiblePosition) >-{ >- InlineBox* box; >- int offset; >- visiblePosition.getInlineBoxAndOffset(box, offset); >- >- if (!box) >- return VisiblePosition(); >- >- TextDirection blockDirection = directionOfEnclosingBlock(visiblePosition.deepEquivalent()); >- >- if (offset == box->caretLeftmostOffset()) >- return rightWordBoundary(box, offset, blockDirection); >- if (offset == box->caretRightmostOffset()) >- return rightWordBoundary(rightInlineBox(box, blockDirection), invalidOffset, blockDirection); >- >- VisiblePosition wordBreak; >- if (blockDirection == RTL) { >- if (box->direction() == blockDirection) >- wordBreak = previousBoundary(visiblePosition, previousWordPositionBoundary); >- else >- wordBreak = nextBoundary(visiblePosition, nextWordPositionBoundary); >- } >- if (wordBreak.isNotNull() && positionIsInBoxButNotOnBoundary(wordBreak, box)) >- return wordBreak; >- >- WordBoundaryVector orderedWordBoundaries; >- collectWordBreaksInBox(box, orderedWordBoundaries, blockDirection); >- >- int index = box->isLeftToRightDirection() ? smallestOffsetAbove(offset, blockDirection == LTR, orderedWordBoundaries) >- : greatestOffsetUnder(offset, blockDirection == RTL, orderedWordBoundaries); >- if (index >= 0) >- return orderedWordBoundaries[index].visiblePosition; >- >- return rightWordBoundary(rightInlineBox(box, blockDirection), invalidOffset, blockDirection); >-} >- >-VisiblePosition leftWordPosition(const VisiblePosition& visiblePosition) >-{ >- if (visiblePosition.isNull()) >- return VisiblePosition(); >- >- VisiblePosition leftWordBreak = leftWordPositionIgnoringEditingBoundary(visiblePosition); >- leftWordBreak = visiblePosition.honorEditingBoundaryAtOrBefore(leftWordBreak); >- >- // FIXME: How should we handle a non-editable position? >- if (leftWordBreak.isNull() && isEditablePosition(visiblePosition.deepEquivalent())) { >- TextDirection blockDirection = directionOfEnclosingBlock(visiblePosition.deepEquivalent()); >- leftWordBreak = blockDirection == LTR ? startOfEditableContent(visiblePosition) : endOfEditableContent(visiblePosition); >- } >- return leftWordBreak; >-} >- >-VisiblePosition rightWordPosition(const VisiblePosition& visiblePosition) >-{ >- if (visiblePosition.isNull()) >- return VisiblePosition(); >- >- VisiblePosition rightWordBreak = rightWordPositionIgnoringEditingBoundary(visiblePosition); >- rightWordBreak = visiblePosition.honorEditingBoundaryAtOrBefore(rightWordBreak); >- >- // FIXME: How should we handle a non-editable position? >- if (rightWordBreak.isNull() && isEditablePosition(visiblePosition.deepEquivalent())) { >- TextDirection blockDirection = directionOfEnclosingBlock(visiblePosition.deepEquivalent()); >- rightWordBreak = blockDirection == LTR ? endOfEditableContent(visiblePosition) : startOfEditableContent(visiblePosition); >- } >- return rightWordBreak; >-} >- > } >Index: Source/WebCore/platform/text/TextBreakIterator.h >=================================================================== >--- Source/WebCore/platform/text/TextBreakIterator.h (revision 110473) >+++ Source/WebCore/platform/text/TextBreakIterator.h (working copy) >@@ -56,6 +56,7 @@ namespace WebCore { > int textBreakPreceding(TextBreakIterator*, int); > int textBreakFollowing(TextBreakIterator*, int); > bool isTextBreak(TextBreakIterator*, int); >+ bool isWordTextBreak(TextBreakIterator*); > > const int TextBreakDone = -1; > >Index: Source/WebCore/platform/text/TextBreakIteratorICU.cpp >=================================================================== >--- Source/WebCore/platform/text/TextBreakIteratorICU.cpp (revision 110473) >+++ Source/WebCore/platform/text/TextBreakIteratorICU.cpp (working copy) >@@ -139,6 +139,12 @@ bool isTextBreak(TextBreakIterator* iter > return ubrk_isBoundary(reinterpret_cast<UBreakIterator*>(iterator), position); > } > >+bool isWordTextBreak(TextBreakIterator* iterator) >+{ >+ int ruleStatus = ubrk_getRuleStatus(reinterpret_cast<UBreakIterator*>(iterator)); >+ return ruleStatus != UBRK_WORD_NONE; >+} >+ > static TextBreakIterator* setUpIteratorWithRules(bool& createdIterator, TextBreakIterator*& iterator, > const char* breakRules, const UChar* string, int length) > { >Index: Source/WebCore/platform/text/gtk/TextBreakIteratorGtk.cpp >=================================================================== >--- Source/WebCore/platform/text/gtk/TextBreakIteratorGtk.cpp (revision 110473) >+++ Source/WebCore/platform/text/gtk/TextBreakIteratorGtk.cpp (working copy) >@@ -385,4 +385,9 @@ bool isTextBreak(TextBreakIterator* iter > return iterator->m_charIterator.getIndex() == index; > } > >+bool isWordTextBreak(TextBreakIterator*) >+{ >+ return true; >+} >+ > } >Index: Source/WebCore/platform/text/qt/TextBreakIteratorQt.cpp >=================================================================== >--- Source/WebCore/platform/text/qt/TextBreakIteratorQt.cpp (revision 110473) >+++ Source/WebCore/platform/text/qt/TextBreakIteratorQt.cpp (working copy) >@@ -150,4 +150,9 @@ namespace WebCore { > return true; > } > >+ bool isWordTextBreak(TextBreakIterator*) >+ { >+ return true; >+ } >+ > } >Index: Source/WebCore/platform/text/wince/TextBreakIteratorWinCE.cpp >=================================================================== >--- Source/WebCore/platform/text/wince/TextBreakIteratorWinCE.cpp (revision 110473) >+++ Source/WebCore/platform/text/wince/TextBreakIteratorWinCE.cpp (working copy) >@@ -317,6 +317,11 @@ bool isTextBreak(TextBreakIterator*, int > return true; > } > >+bool isWordTextBreak(TextBreakIterator*) >+{ >+ return true; >+} >+ > TextBreakIterator* cursorMovementIterator(const UChar* string, int length) > { > return characterBreakIterator(string, length); >Index: LayoutTests/ChangeLog >=================================================================== >--- LayoutTests/ChangeLog (revision 110884) >+++ LayoutTests/ChangeLog (working copy) >@@ -1,3 +1,36 @@ >+2012-03-15 Xiaomei Ji <xji@chromium.org> >+ >+ Using ICU break iterator to simplify visual word movement implementation. >+ https://bugs.webkit.org/show_bug.cgi?id=78856 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ * editing/selection/move-by-word-visually-inline-block-positioned-element-expected.txt: >+ * editing/selection/move-by-word-visually-inline-block-positioned-element.html: >+ The word break stops at the beginning of fixed positioned element, which is correct and consistent behavior. >+ >+ * editing/selection/move-by-word-visually-multi-line-expected.txt: >+ * editing/selection/move-by-word-visually-multi-line.html: >+ The word break does not stop at the end of a text if there is next line of text available under the same >+ editable root, which is a consistent behavior cross the board. >+ Add more test cases, including one with non InlineTextBox. >+ >+ * editing/selection/move-by-word-visually-single-space-inline-element-expected.txt: >+ Fixing of previously missing or extra word break positions. >+ >+ * editing/selection/move-by-word-visually-single-space-one-element-expected.txt: >+ * editing/selection/move-by-word-visually-single-space-one-element.html: >+ Add a test case including bidi control character which renders wrong result since right-arrow could not reach >+ a position which is the word break position. >+ Add another test case containing non InlineTextBox. >+ >+ * editing/selection/move-by-word-visually-wrong-left-right-expected.txt: Added. >+ * editing/selection/move-by-word-visually-wrong-left-right.html: Added. >+ Add a test case which renders wrong result due to left/right-arrow returns wrong result. >+ >+ * editing/selection/resources/move-by-word-visually.js: >+ (moveByWordOnEveryChar): Handle a special case when left/right arrow missing certain position. >+ > 2012-03-15 Brady Eidson <beidson@apple.com> > > <rdar://problem/11036900> and https://bugs.webkit.org/show_bug.cgi?id=81079 >Index: LayoutTests/editing/selection/move-by-word-visually-inline-block-positioned-element-expected.txt >=================================================================== >--- LayoutTests/editing/selection/move-by-word-visually-inline-block-positioned-element-expected.txt (revision 110473) >+++ LayoutTests/editing/selection/move-by-word-visually-inline-block-positioned-element-expected.txt (working copy) >@@ -2,37 +2,37 @@ > ======== Move By Word ==== > Test 1, LTR: > Move right by one word >-"begin start"[0, 6], "abc def"[0, 4], "end ing"[0, 4], "this is float"[0, 5, 8], "this is fixed"[5, 8], "this is relative"[0, 5, 8], "this is absolute"[0, 5, 8, 16] >+"begin start"[0, 6], "abc def"[0, 4], "end ing"[0, 4], "this is float"[0, 5, 8], "this is fixed"[0, 5, 8], "this is relative"[0, 5, 8], "this is absolute"[0, 5, 8, 16] > Move left by one word >-"this is absolute"[16, 8, 5, 0], "this is relative"[8, 5, 0], "this is fixed"[8, 5], "this is float"[8, 5, 0], "end ing"[4, 0], "abc def"[4, 0], "begin start"[6, 0] >+"this is absolute"[16, 8, 5, 0], "this is relative"[8, 5, 0], "this is fixed"[8, 5, 0], "this is float"[8, 5, 0], "end ing"[4, 0], "abc def"[4, 0], "begin start"[6, 0] > Test 2, LTR: > Move right by one word >-"abc def"[0, 4], "end ing"[0, 4], "this is float"[0, 5, 8], "this is fixed"[5, 8], "this is relative"[0, 5, 8], "this is absolute"[0, 5, 8, 16] >+"abc def"[0, 4], "end ing"[0, 4], "this is float"[0, 5, 8], "this is fixed"[0, 5, 8], "this is relative"[0, 5, 8], "this is absolute"[0, 5, 8, 16] > Move left by one word >-"this is absolute"[16, 8, 5, 0], "this is relative"[8, 5, 0], "this is fixed"[8, 5], "this is float"[8, 5, 0], "end ing"[4, 0], "abc def"[4, 0], "begin start"[6, 0] >+"this is absolute"[16, 8, 5, 0], "this is relative"[8, 5, 0], "this is fixed"[8, 5, 0], "this is float"[8, 5, 0], "end ing"[4, 0], "abc def"[4, 0], "begin start"[6, 0] > Test 3, LTR: > Move right by one word >-"end ing"[0, 4], "this is float"[0, 5, 8], "this is fixed"[5, 8], "this is relative"[0, 5, 8], "this is absolute"[0, 5, 8, 16] >+"end ing"[0, 4], "this is float"[0, 5, 8], "this is fixed"[0, 5, 8], "this is relative"[0, 5, 8], "this is absolute"[0, 5, 8, 16] > Move left by one word >-"this is absolute"[16, 8, 5, 0], "this is relative"[8, 5, 0], "this is fixed"[8, 5], "this is float"[8, 5, 0], "end ing"[4, 0], "abc def"[4, 0], "begin start"[6, 0] >+"this is absolute"[16, 8, 5, 0], "this is relative"[8, 5, 0], "this is fixed"[8, 5, 0], "this is float"[8, 5, 0], "end ing"[4, 0], "abc def"[4, 0], "begin start"[6, 0] > Test 4, LTR: > Move right by one word >-"this is float"[0, 5, 8], "this is fixed"[5, 8], "this is relative"[0, 5, 8], "this is absolute"[0, 5, 8, 16] >+"this is float"[0, 5, 8], "this is fixed"[0, 5, 8], "this is relative"[0, 5, 8], "this is absolute"[0, 5, 8, 16] > Move left by one word >-"this is absolute"[16, 8, 5, 0], "this is relative"[8, 5, 0], "this is fixed"[8, 5], "this is float"[8, 5, 0], "end ing"[4, 0], "abc def"[4, 0], "begin start"[6, 0] >+"this is absolute"[16, 8, 5, 0], "this is relative"[8, 5, 0], "this is fixed"[8, 5, 0], "this is float"[8, 5, 0], "end ing"[4, 0], "abc def"[4, 0], "begin start"[6, 0] > Test 5, LTR: > Move right by one word > "this is fixed"[0, 5, 8], "this is relative"[0, 5, 8], "this is absolute"[0, 5, 8, 16] > Move left by one word >-"this is absolute"[16, 8, 5, 0], "this is relative"[8, 5, 0], "this is fixed"[8, 5], "this is float"[8, 5, 0], "end ing"[4, 0], "abc def"[4, 0], "begin start"[6, 0] >+"this is absolute"[16, 8, 5, 0], "this is relative"[8, 5, 0], "this is fixed"[8, 5, 0], "this is float"[8, 5, 0], "end ing"[4, 0], "abc def"[4, 0], "begin start"[6, 0] > Test 6, LTR: > Move right by one word > "this is relative"[0, 5, 8], "this is absolute"[0, 5, 8, 16] > Move left by one word >-"this is absolute"[16, 8, 5, 0], "this is relative"[8, 5, 0], "this is fixed"[8, 5], "this is float"[8, 5, 0], "end ing"[4, 0], "abc def"[4, 0], "begin start"[6, 0] >+"this is absolute"[16, 8, 5, 0], "this is relative"[8, 5, 0], "this is fixed"[8, 5, 0], "this is float"[8, 5, 0], "end ing"[4, 0], "abc def"[4, 0], "begin start"[6, 0] > Test 7, LTR: > Move right by one word > "this is absolute"[0, 5, 8, 16] > Move left by one word >-"this is absolute"[16, 8, 5, 0], "this is relative"[8, 5, 0], "this is fixed"[8, 5], "this is float"[8, 5, 0], "end ing"[4, 0], "abc def"[4, 0], "begin start"[6, 0] >+"this is absolute"[16, 8, 5, 0], "this is relative"[8, 5, 0], "this is fixed"[8, 5, 0], "this is float"[8, 5, 0], "end ing"[4, 0], "abc def"[4, 0], "begin start"[6, 0] > >Index: LayoutTests/editing/selection/move-by-word-visually-inline-block-positioned-element.html >=================================================================== >--- LayoutTests/editing/selection/move-by-word-visually-inline-block-positioned-element.html (revision 110473) >+++ LayoutTests/editing/selection/move-by-word-visually-inline-block-positioned-element.html (working copy) >@@ -17,38 +17,38 @@ if (window.layoutTestController) { > > <div id="testMoveByWord" contenteditable style="width:2000px; height:2000px"> > <div id="d_1" dir=ltr >-title="[d_1, 0][d_1, 6][d_2, 0][d_2, 4][d_3, 0][d_3, 4][d_4, 0][d_4, 5][d_4, 8][d_5, 5][d_5, 8][d_6, 0][d_6, 5][d_6, 8][d_7, 0][d_7, 5][d_7, 8][d_7, 16]| >-[d_7, 16][d_7, 8][d_7, 5][d_7, 0][d_6, 8][d_6, 5][d_6, 0][d_5, 8][d_5, 5][d_4, 8][d_4, 5][d_4, 0][d_3, 4][d_3, 0][d_2, 4][d_2, 0][d_1, 6][d_1, 0]" >+title="[d_1, 0][d_1, 6][d_2, 0][d_2, 4][d_3, 0][d_3, 4][d_4, 0][d_4, 5][d_4, 8][d_5, 0][d_5, 5][d_5, 8][d_6, 0][d_6, 5][d_6, 8][d_7, 0][d_7, 5][d_7, 8][d_7, 16]| >+[d_7, 16][d_7, 8][d_7, 5][d_7, 0][d_6, 8][d_6, 5][d_6, 0][d_5, 8][d_5, 5][d_5, 0][d_4, 8][d_4, 5][d_4, 0][d_3, 4][d_3, 0][d_2, 4][d_2, 0][d_1, 6][d_1, 0]" > class="test_move_by_word">begin start</div> > > <div id="d_2" dir=ltr >-title="[d_2, 0][d_2, 4][d_3, 0][d_3, 4][d_4, 0][d_4, 5][d_4, 8][d_5, 5][d_5, 8][d_6, 0][d_6, 5][d_6, 8][d_7, 0][d_7, 5][d_7, 8][d_7, 16]| >-[d_7, 16][d_7, 8][d_7, 5][d_7, 0][d_6, 8][d_6, 5][d_6, 0][d_5, 8][d_5, 5][d_4, 8][d_4, 5][d_4, 0][d_3, 4][d_3, 0][d_2, 4][d_2, 0][d_1, 6][d_1, 0]" >+title="[d_2, 0][d_2, 4][d_3, 0][d_3, 4][d_4, 0][d_4, 5][d_4, 8][d_5, 0][d_5, 5][d_5, 8][d_6, 0][d_6, 5][d_6, 8][d_7, 0][d_7, 5][d_7, 8][d_7, 16]| >+[d_7, 16][d_7, 8][d_7, 5][d_7, 0][d_6, 8][d_6, 5][d_6, 0][d_5, 8][d_5, 5][d_5, 0][d_4, 8][d_4, 5][d_4, 0][d_3, 4][d_3, 0][d_2, 4][d_2, 0][d_1, 6][d_1, 0]" > class="test_move_by_word" style="display:inline-block">abc def</div> > > <div id="d_3" dir=ltr >-title="[d_3, 0][d_3, 4][d_4, 0][d_4, 5][d_4, 8][d_5, 5][d_5, 8][d_6, 0][d_6, 5][d_6, 8][d_7, 0][d_7, 5][d_7, 8][d_7, 16]| >-[d_7, 16][d_7, 8][d_7, 5][d_7, 0][d_6, 8][d_6, 5][d_6, 0][d_5, 8][d_5, 5][d_4, 8][d_4, 5][d_4, 0][d_3, 4][d_3, 0][d_2, 4][d_2, 0][d_1, 6][d_1, 0]" >+title="[d_3, 0][d_3, 4][d_4, 0][d_4, 5][d_4, 8][d_5, 0][d_5, 5][d_5, 8][d_6, 0][d_6, 5][d_6, 8][d_7, 0][d_7, 5][d_7, 8][d_7, 16]| >+[d_7, 16][d_7, 8][d_7, 5][d_7, 0][d_6, 8][d_6, 5][d_6, 0][d_5, 8][d_5, 5][d_5, 0][d_4, 8][d_4, 5][d_4, 0][d_3, 4][d_3, 0][d_2, 4][d_2, 0][d_1, 6][d_1, 0]" > class="test_move_by_word">end ing</div> > > <div id="d_4" dir=ltr >-title="[d_4, 0][d_4, 5][d_4, 8][d_5, 5][d_5, 8][d_6, 0][d_6, 5][d_6, 8][d_7, 0][d_7, 5][d_7, 8][d_7, 16]| >-[d_7, 16][d_7, 8][d_7, 5][d_7, 0][d_6, 8][d_6, 5][d_6, 0][d_5, 8][d_5, 5][d_4, 8][d_4, 5][d_4, 0][d_3, 4][d_3, 0][d_2, 4][d_2, 0][d_1, 6][d_1, 0]" >+title="[d_4, 0][d_4, 5][d_4, 8][d_5, 0][d_5, 5][d_5, 8][d_6, 0][d_6, 5][d_6, 8][d_7, 0][d_7, 5][d_7, 8][d_7, 16]| >+[d_7, 16][d_7, 8][d_7, 5][d_7, 0][d_6, 8][d_6, 5][d_6, 0][d_5, 8][d_5, 5][d_5, 0][d_4, 8][d_4, 5][d_4, 0][d_3, 4][d_3, 0][d_2, 4][d_2, 0][d_1, 6][d_1, 0]" > class="test_move_by_word" style="float:left">this is float</div> > > <div id="d_5" dir=ltr > title="[d_5, 0][d_5, 5][d_5, 8][d_6, 0][d_6, 5][d_6, 8][d_7, 0][d_7, 5][d_7, 8][d_7, 16]| >-[d_7, 16][d_7, 8][d_7, 5][d_7, 0][d_6, 8][d_6, 5][d_6, 0][d_5, 8][d_5, 5][d_4, 8][d_4, 5][d_4, 0][d_3, 4][d_3, 0][d_2, 4][d_2, 0][d_1, 6][d_1, 0]" >+[d_7, 16][d_7, 8][d_7, 5][d_7, 0][d_6, 8][d_6, 5][d_6, 0][d_5, 8][d_5, 5][d_5, 0][d_4, 8][d_4, 5][d_4, 0][d_3, 4][d_3, 0][d_2, 4][d_2, 0][d_1, 6][d_1, 0]" > class="test_move_by_word" style="position:fixed; top:30px; right:5px">this is fixed</div> > > <div id="d_6" dir=ltr > title="[d_6, 0][d_6, 5][d_6, 8][d_7, 0][d_7, 5][d_7, 8][d_7, 16]| >-[d_7, 16][d_7, 8][d_7, 5][d_7, 0][d_6, 8][d_6, 5][d_6, 0][d_5, 8][d_5, 5][d_4, 8][d_4, 5][d_4, 0][d_3, 4][d_3, 0][d_2, 4][d_2, 0][d_1, 6][d_1, 0]" >+[d_7, 16][d_7, 8][d_7, 5][d_7, 0][d_6, 8][d_6, 5][d_6, 0][d_5, 8][d_5, 5][d_5, 0][d_4, 8][d_4, 5][d_4, 0][d_3, 4][d_3, 0][d_2, 4][d_2, 0][d_1, 6][d_1, 0]" > class="test_move_by_word" style="position:relative; left:20px">this is relative</div> > > <div id="d_7" dir=ltr > title="[d_7, 0][d_7, 5][d_7, 8][d_7, 16]| >-[d_7, 16][d_7, 8][d_7, 5][d_7, 0][d_6, 8][d_6, 5][d_6, 0][d_5, 8][d_5, 5][d_4, 8][d_4, 5][d_4, 0][d_3, 4][d_3, 0][d_2, 4][d_2, 0][d_1, 6][d_1, 0]" >+[d_7, 16][d_7, 8][d_7, 5][d_7, 0][d_6, 8][d_6, 5][d_6, 0][d_5, 8][d_5, 5][d_5, 0][d_4, 8][d_4, 5][d_4, 0][d_3, 4][d_3, 0][d_2, 4][d_2, 0][d_1, 6][d_1, 0]" > class="test_move_by_word" style="position:absolute; left:100px; top:150px">this is absolute</div> > > </div> >Index: LayoutTests/editing/selection/move-by-word-visually-multi-line-expected.txt >=================================================================== >--- LayoutTests/editing/selection/move-by-word-visually-multi-line-expected.txt (revision 110473) >+++ LayoutTests/editing/selection/move-by-word-visually-multi-line-expected.txt (working copy) >@@ -37,11 +37,9 @@ Move right by one word > "opq rst uvw xyz"[15, 11, 8, 3, 0], "abc def ghi jkl mn "[16, 11, 8, 3, 0] > Test 8, RTL: > Move left by one word >-"abc def ghi jkl mn "[0, 3, 8, 11, 16, 18], "opq rst uvw xyz"[0, 3, 8, 11, 15] FAIL expected: ["abc def ghi jkl mn "[ 0, 3, 8, 11, 16, ]"opq rst uvw xyz"[ 0, 3, 8, 11, 15] >-"abc def ghi jkl mn "[16, 18] FAIL expected "opq rst uvw xyz"[ 0] >-"abc def ghi jkl mn "[17, 18] FAIL expected "opq rst uvw xyz"[ 0] >+"abc def ghi jkl mn "[0, 3, 8, 11, 16], "opq rst uvw xyz"[0, 3, 8, 11, 15] > Move right by one word >-"opq rst uvw xyz"[15, 11, 8, 3, 0], "abc def ghi jkl mn "[18, 16, 11, 8, 3, 0] >+"opq rst uvw xyz"[15, 11, 8, 3, 0], "abc def ghi jkl mn "[16, 11, 8, 3, 0] > Test 9, RTL: > Move left by one word > "AAA AAA AAA AAA "[0, 4, 8, 12], "AAA AAA AAA AAA"[0, 4, 8, 12, 15] >@@ -59,23 +57,35 @@ Move right by one word > "AAA kj AAA mn opq AAA AAA"[25, 22, 18, 14, 11, 7, 4, 0], " abc def AAA AAA hij AAA AAA uvw xyz "[33, 29, 25, 21, 17, 13, 9, 4, 1] > Test 12, RTL: > Move left by one word >-" abc def AAA AAA hij AAA AAA uvw xyz "[1, 4, 9, 13, 17, 21, 25, 29, 33, 36], "AAA kj AAA mn opq AAA AAA"[0, 4, 7, 11, 14, 18, 22, 25] FAIL expected: [" abc def AAA AAA hij AAA AAA uvw xyz "[ 1, 4, 9, 13, 17, 21, 25, 29, 33, ]"AAA kj AAA mn opq AAA AAA"[ 0, 4, 7, 11, 14, 18, 22, 25] >-" abc def AAA AAA hij AAA AAA uvw xyz "[33, 36] FAIL expected "AAA kj AAA mn opq AAA AAA"[ 0] >-" abc def AAA AAA hij AAA AAA uvw xyz "[35, 36] FAIL expected "AAA kj AAA mn opq AAA AAA"[ 0] >-" abc def AAA AAA hij AAA AAA uvw xyz "[34, 36] FAIL expected "AAA kj AAA mn opq AAA AAA"[ 0] >+" abc def AAA AAA hij AAA AAA uvw xyz "[1, 4, 9, 13, 17, 21, 25, 29, 33], "AAA kj AAA mn opq AAA AAA"[0, 4, 7, 11, 14, 18, 22, 25] > Move right by one word >-"AAA kj AAA mn opq AAA AAA"[25, 22, 18, 14, 11, 7, 4, 0], " abc def AAA AAA hij AAA AAA uvw xyz "[36, 33, 29, 25, 21, 17, 13, 9, 4, 1] >+"AAA kj AAA mn opq AAA AAA"[25, 22, 18, 14, 11, 7, 4, 0], " abc def AAA AAA hij AAA AAA uvw xyz "[33, 29, 25, 21, 17, 13, 9, 4, 1] > Test 13, LTR: > Move right by one word >-"abc def "[0, 4] >+"abc def ghi jkl mn "[0, 4, 8, 12, 16], "opq rst uvw xyz"[0, 4, 8, 12, 15] > Move left by one word >-" hij opq"[8, 5, 1] >+"opq rst uvw xyz"[15, 12, 8, 4, 0], "abc def ghi jkl mn "[16, 12, 8, 4, 0] > Test 14, LTR: > Move right by one word >+"abc def ghi jkl mn "[0, 4, 8, 12, 16, 18] >+Move left by one word >+"abc def ghi jkl mn "[18, 16, 12, 8, 4, 0] >+Test 15, LTR: >+Move right by one word >+"abc def ghi jkl mn "[0, 4, 8, 12, 16], "opq rst uvw xyz"[0, 4, 8, 12, 15] >+Move left by one word >+"opq rst uvw xyz"[15, 12, 8, 4, 0], "abc def ghi jkl mn "[16, 12, 8, 4, 0] >+Test 16, LTR: >+Move right by one word >+"abc def "[0, 4, 8] >+Move left by one word >+" hij opq"[8, 5, 1] >+Test 17, LTR: >+Move right by one word > <DIV>[0] > Move left by one word > <DIV>[0] >-Test 15, LTR: >+Test 18, LTR: > Move right by one word > "\n00"[3] > Move left by one word >Index: LayoutTests/editing/selection/move-by-word-visually-multi-line.html >=================================================================== >--- LayoutTests/editing/selection/move-by-word-visually-multi-line.html (revision 110473) >+++ LayoutTests/editing/selection/move-by-word-visually-multi-line.html (working copy) >@@ -54,7 +54,7 @@ where child_node_index is optional, defa > > <div contenteditable dir=rtl id="ml_7" class="test_move_by_word fix_width" title="[ml_7, 15, 5][ml_7, 11, 5][ml_7, 8, 5][ml_7, 3, 5][ml_7, 0, 5][ml_7, 16][ml_7, 11][ml_7, 8][ml_7, 3][ml_7, 0]|[ml_7, 0][ml_7, 3][ml_7, 8][ml_7, 11][ml_7, 16][ml_7, 0, 5][ml_7, 3, 5][ml_7, 8, 5][ml_7, 11, 5][ml_7, 15, 5]">abc def ghi jkl mn <br/><br/><br/>opq rst uvw xyz</div> > >-<div contenteditable dir=rtl id="ml_8" class="test_move_by_word fix_width" title="[ml_8, 15, 5][ml_8, 11, 5][ml_8, 8, 5][ml_8, 3, 5][ml_8, 0, 5][ml_8, 18][ml_8, 16][ml_8, 11][ml_8, 8][ml_8, 3][ml_8, 0]|[ml_8, 0][ml_8, 3][ml_8, 8][ml_8, 11][ml_8, 16][ml_8, 0, 5][ml_8, 3, 5][ml_8, 8, 5][ml_8, 11, 5][ml_8, 15, 5]">abc def ghi jkl mn <div><br/></div><div><br/></div><div><br/></div>opq rst uvw xyz</div> >+<div contenteditable dir=rtl id="ml_8" class="test_move_by_word fix_width" title="[ml_8, 15, 5][ml_8, 11, 5][ml_8, 8, 5][ml_8, 3, 5][ml_8, 0, 5][ml_8, 16][ml_8, 11][ml_8, 8][ml_8, 3][ml_8, 0]|[ml_8, 0][ml_8, 3][ml_8, 8][ml_8, 11][ml_8, 16][ml_8, 0, 5][ml_8, 3, 5][ml_8, 8, 5][ml_8, 11, 5][ml_8, 15, 5]">abc def ghi jkl mn <div><br/></div><div><br/></div><div><br/></div>opq rst uvw xyz</div> > > <div contenteditable dir=rtl id="ml_9" class="test_move_by_word fix_width" title="[ml_9, 15, 5][ml_9, 12, 5][ml_9, 8, 5][ml_9, 4, 5][ml_9, 0, 5][ml_9, 12][ml_9, 8][ml_9, 4][ml_9, 0]|[ml_9, 0][ml_9, 4][ml_9, 8][ml_9, 12][ml_9, 0, 5][ml_9, 4, 5][ml_9, 8, 5][ml_9, 12, 5][ml_9, 15, 5]">××× ××× ××× ××× <br/><br/><br/>××× ××× ××× ×××</div> > >@@ -65,11 +65,17 @@ where child_node_index is optional, defa > "> abc def ××× ××× hij ××× ××× uvw xyz <br/><br/><br/>××× kj ××× mn opq ××× ×××</div> > > <div contenteditable dir=rtl id="ml_12" class="test_move_by_word fix_width" title=" >-[ml_12, 25, 5][ml_12, 22, 5][ml_12, 18, 5][ml_12, 14, 5][ml_12, 11, 5][ml_12, 7, 5][ml_12, 4, 5][ml_12, 0, 5][ml_12, 36][ml_12, 33][ml_12, 29][ml_12, 25][ml_12, 21][ml_12, 17][ml_12, 13][ml_12, 9][ml_12, 4][ml_12, 1]|[ml_12, 1][ml_12, 4][ml_12, 9][ml_12, 13][ml_12, 17][ml_12, 21][ml_12, 25][ml_12, 29][ml_12, 33][ml_12, 0, 5][ml_12, 4, 5][ml_12, 7, 5][ml_12, 11, 5][ml_12, 14, 5][ml_12, 18, 5][ml_12, 22, 5][ml_12, 25, 5] >+[ml_12, 25, 5][ml_12, 22, 5][ml_12, 18, 5][ml_12, 14, 5][ml_12, 11, 5][ml_12, 7, 5][ml_12, 4, 5][ml_12, 0, 5][ml_12, 33][ml_12, 29][ml_12, 25][ml_12, 21][ml_12, 17][ml_12, 13][ml_12, 9][ml_12, 4][ml_12, 1]|[ml_12, 1][ml_12, 4][ml_12, 9][ml_12, 13][ml_12, 17][ml_12, 21][ml_12, 25][ml_12, 29][ml_12, 33][ml_12, 0, 5][ml_12, 4, 5][ml_12, 7, 5][ml_12, 11, 5][ml_12, 14, 5][ml_12, 18, 5][ml_12, 22, 5][ml_12, 25, 5] > "> abc def ××× ××× hij ××× ××× uvw xyz <div><br/></div><div><br/></div><div><br/></div>××× kj ××× mn opq ××× ×××</div> > >+<div contenteditable dir=ltr id="ml_13" class="test_move_by_word fix_width" title="[ml_13, 0][ml_13, 4][ml_13, 8][ml_13, 12][ml_13, 16][ml_13, 0, 5][ml_13, 4, 5][ml_13, 8, 5][ml_13, 12, 5][ml_13, 15, 5]|[ml_13, 15, 5][ml_13, 12, 5][ml_13, 8, 5][ml_13, 4, 5][ml_13, 0, 5][ml_13, 16][ml_13, 12][ml_13, 8][ml_13, 4][ml_13, 0]">abc def ghi jkl mn <div></div><div></div><div></div>opq rst uvw xyz</div> >+ >+<div contenteditable dir=ltr id="ml_14" class="test_move_by_word fix_width" title="[ml_14, 0][ml_14, 4][ml_14, 8][ml_14, 12][ml_14, 16][ml_14, 18]|[ml_14, 18][ml_14, 16][ml_14, 12][ml_14, 8][ml_14, 4][ml_14, 0]">abc def ghi jkl mn <div></div><div></div><div></div></div> >+ >+<div contenteditable dir=ltr id="ml_15" class="test_move_by_word fix_width" title="[ml_15, 0][ml_15, 4][ml_15, 8][ml_15, 12][ml_15, 16][ml_15, 0, 5][ml_15, 4, 5][ml_15, 8, 5][ml_15, 12, 5][ml_15, 15, 5]|[ml_15, 15, 5][ml_15, 12, 5][ml_15, 8, 5][ml_15, 4, 5][ml_15, 0, 5][ml_15, 16][ml_15, 12][ml_15, 8][ml_15, 4][ml_15, 0]">abc def ghi jkl mn <div><img src=../../accessibility/resources/cake.png></div><div></div><div></div>opq rst uvw xyz</div> >+ > <!-- mixed editability --> >-<div dir=ltr class="test_move_by_word" title="0 4|8 5 1">abc def <span contenteditable> inside span </span> hij opq</div> >+<div dir=ltr class="test_move_by_word" title="0 4 8|8 5 1">abc def <span contenteditable> inside span </span> hij opq</div> > > <div class="test_move_by_word" contenteditable dir=ltr title="0|0"></div> > >Index: LayoutTests/editing/selection/move-by-word-visually-single-space-inline-element-expected.txt >=================================================================== >--- LayoutTests/editing/selection/move-by-word-visually-single-space-inline-element-expected.txt (revision 110473) >+++ LayoutTests/editing/selection/move-by-word-visually-single-space-inline-element-expected.txt (working copy) >@@ -17,30 +17,14 @@ Move left by one word > " rst uvw"[8, 5, 1], "hij opq"[4], "abc def "[8, 4, 0] > Test 4, RTL: > Move left by one word >-"abc def "[0], " rst uvw"[4], "hij opq"[3], "abc def "[7, 3], " rst uvw"[8] FAIL expected: ["abc def "[ 0, ]" rst uvw"[ 4, ]"hij opq"[ 7, 3, ]"abc def "[ 7, 3, ]" rst uvw"[ 8] >-" rst uvw"[4], "hij opq"[3] FAIL expected "hij opq"[ 7] >-" rst uvw"[3], "hij opq"[3] FAIL expected "hij opq"[ 7] >-" rst uvw"[2], "hij opq"[3] FAIL expected "hij opq"[ 7] >-" rst uvw"[1], "hij opq"[3] FAIL expected "hij opq"[ 7] >-Move right by one word >-" rst uvw"[8], "abc def "[3, 7], "hij opq"[3], " rst uvw"[4], "abc def "[0] FAIL expected: [" rst uvw"[ 8, ]"abc def "[ 3, 7, ]"hij opq"[ 3, 7, ]" rst uvw"[ 4, ]"abc def "[ 0] >-"hij opq"[3], " rst uvw"[4] FAIL expected "hij opq"[ 7] >-"hij opq"[4], " rst uvw"[4] FAIL expected "hij opq"[ 7] >-"hij opq"[5], " rst uvw"[4] FAIL expected "hij opq"[ 7] >-"hij opq"[6], " rst uvw"[4] FAIL expected "hij opq"[ 7] >+"abc def "[0], " rst uvw"[4], "hij opq"[7, 3], "abc def "[7, 3], " rst uvw"[8] >+Move right by one word >+" rst uvw"[8], "abc def "[3, 7], "hij opq"[3, 7], " rst uvw"[4], "abc def "[0] > Test 5, RTL: > Move left by one word >-"abc def "[0], " rst uvw"[4], "hij opq"[3], "abc def "[7, 3], " rst uvw"[8] FAIL expected: ["abc def "[ 0, ]" rst uvw"[ 4, ]"hij opq"[ 7, 3, ]"abc def "[ 7, 3, ]" rst uvw"[ 8] >-" rst uvw"[4], "hij opq"[3] FAIL expected "hij opq"[ 7] >-" rst uvw"[3], "hij opq"[3] FAIL expected "hij opq"[ 7] >-" rst uvw"[2], "hij opq"[3] FAIL expected "hij opq"[ 7] >-" rst uvw"[1], "hij opq"[3] FAIL expected "hij opq"[ 7] >-Move right by one word >-" rst uvw"[8], "abc def "[3, 7], "hij opq"[3], " rst uvw"[4], "abc def "[0] FAIL expected: [" rst uvw"[ 8, ]"abc def "[ 3, 7, ]"hij opq"[ 3, 7, ]" rst uvw"[ 4, ]"abc def "[ 0] >-"hij opq"[3], " rst uvw"[4] FAIL expected "hij opq"[ 7] >-"hij opq"[4], " rst uvw"[4] FAIL expected "hij opq"[ 7] >-"hij opq"[5], " rst uvw"[4] FAIL expected "hij opq"[ 7] >-"hij opq"[6], " rst uvw"[4] FAIL expected "hij opq"[ 7] >+"abc def "[0], " rst uvw"[4], "hij opq"[7, 3], "abc def "[7, 3], " rst uvw"[8] >+Move right by one word >+" rst uvw"[8], "abc def "[3, 7], "hij opq"[3, 7], " rst uvw"[4], "abc def "[0] > Test 6, LTR: > Move right by one word > "abc def "[0, 4, 8], "hij opq"[4], " rst uvw"[1, 5, 8] >@@ -88,32 +72,14 @@ Move left by one word > "FFZ LIG"[7, 3], "abc def"[4], "ABD DSU "[8, 3, 0] > Test 15, RTL: > Move left by one word >-"ABD opq DSU "[0, 4, 8, 12], "abc AAA def"[8, 4, 3], "FFZ rst LIG"[4, 8, 11] FAIL expected: ["ABD opq DSU "[ 0, 4, 8, 12, ]"abc AAA def"[ 4, 3, ]"FFZ rst LIG"[ 4, 8, 11] >-"ABD opq DSU "[12], "abc AAA def"[8] FAIL expected "abc AAA def"[ 4] >-"abc AAA def"[10, 8] FAIL expected "abc AAA def"[ 4] >-"abc AAA def"[9, 8] FAIL expected "abc AAA def"[ 4] >+"ABD opq DSU "[0, 4, 8, 12], "abc AAA def"[4, 3], "FFZ rst LIG"[4, 8, 11] > Move right by one word >-"FFZ rst LIG"[11, 8, 4], "abc AAA def"[3, 4, 8], "ABD opq DSU "[12, 8, 4, 0] FAIL expected: ["FFZ rst LIG"[ 11, 8, 4, ]"abc AAA def"[ 3, 4, ]"ABD opq DSU "[ 12, 8, 4, 0] >-"abc AAA def"[4, 8] FAIL expected "ABD opq DSU "[ 12] >+"FFZ rst LIG"[11, 8, 4], "abc AAA def"[3, 4], "ABD opq DSU "[12, 8, 4, 0] > Test 16, LTR: > Move right by one word >-"ABD opq DSU "[0, 4], "abc AAA def"[8, 4], "ABD opq DSU "[12, 11], "FFZ rst LIG"[4, 8, 11] FAIL expected: ["ABD opq DSU "[ 0, 4, 8, ]"abc AAA def"[ 8, 7, ]"ABD opq DSU "[ 12, 11, ]"FFZ rst LIG"[ 4, 8, 11] >-"ABD opq DSU "[4], "abc AAA def"[8] FAIL expected "ABD opq DSU "[ 8] >-"ABD opq DSU "[5], "abc AAA def"[8] FAIL expected "ABD opq DSU "[ 8] >-"ABD opq DSU "[6], "abc AAA def"[8] FAIL expected "ABD opq DSU "[ 8] >-"ABD opq DSU "[7], "abc AAA def"[8] FAIL expected "ABD opq DSU "[ 8] >-"abc AAA def"[8, 4] FAIL expected "abc AAA def"[ 7] >-"abc AAA def"[9, 4] FAIL expected "abc AAA def"[ 7] >-"abc AAA def"[10, 4] FAIL expected "abc AAA def"[ 7] >-"abc AAA def"[11, 4] FAIL expected "abc AAA def"[ 7] >-"abc AAA def"[7, 4] FAIL expected "ABD opq DSU "[ 12] >-"abc AAA def"[6, 4] FAIL expected "ABD opq DSU "[ 12] >-"abc AAA def"[5, 4] FAIL expected "ABD opq DSU "[ 12] >-Move left by one word >-"FFZ rst LIG"[11, 8, 4], "ABD opq DSU "[11, 12], "abc AAA def"[7, 8], "ABD opq DSU "[4, 0] FAIL expected: ["FFZ rst LIG"[ 11, 8, 4, ]"ABD opq DSU "[ 11, 12, ]"abc AAA def"[ 7, 8, ]"ABD opq DSU "[ 8, 4, 0] >-"abc AAA def"[8], "ABD opq DSU "[4] FAIL expected "ABD opq DSU "[ 8] >-"FFZ rst LIG"[1], "ABD opq DSU "[4] FAIL expected "ABD opq DSU "[ 8] >-"FFZ rst LIG"[2], "ABD opq DSU "[4] FAIL expected "ABD opq DSU "[ 8] >+"ABD opq DSU "[0, 4, 8], "abc AAA def"[8, 7], "ABD opq DSU "[12, 11], "FFZ rst LIG"[4, 8, 11] >+Move left by one word >+"FFZ rst LIG"[11, 8, 4], "ABD opq DSU "[11, 12], "abc AAA def"[7, 8], "ABD opq DSU "[8, 4, 0] > Test 17, RTL: > Move left by one word > "ABD opq DSU "[0, 4, 8, 12], "abc AAA def"[4, 8], "FFZ rst LIG"[4, 8, 11] >@@ -131,14 +97,7 @@ Move left by one word > "FFZ"[3], "bbb AAA "[7, 4], "aaa "[4, 0] > Test 20, RTL: > Move left by one word >-"ABD opq rst DSU "[0, 4, 7, 12, 16], "abc uvw AAA def lmn"[12, 8, 7, 3], "ABW hij xyz FXX"[4, 7, 12, 15] FAIL expected: ["ABD opq rst DSU "[ 0, 4, 7, 12, 16, ]"abc uvw AAA def lmn"[ 15, 8, 7, 3, ]"ABW hij xyz FXX"[ 4, 7, 12, 15] >-"ABD opq rst DSU "[16], "abc uvw AAA def lmn"[12] FAIL expected "abc uvw AAA def lmn"[ 15] >-"abc uvw AAA def lmn"[18, 12] FAIL expected "abc uvw AAA def lmn"[ 15] >-"abc uvw AAA def lmn"[17, 12] FAIL expected "abc uvw AAA def lmn"[ 15] >-"abc uvw AAA def lmn"[16, 12] FAIL expected "abc uvw AAA def lmn"[ 15] >-"abc uvw AAA def lmn"[15, 12] FAIL expected "abc uvw AAA def lmn"[ 8] >-"abc uvw AAA def lmn"[14, 12] FAIL expected "abc uvw AAA def lmn"[ 8] >-"abc uvw AAA def lmn"[13, 12] FAIL expected "abc uvw AAA def lmn"[ 8] >+"ABD opq rst DSU "[0, 4, 7, 12, 16], "abc uvw AAA def lmn"[15, 8, 7, 3], "ABW hij xyz FXX"[4, 7, 12, 15] > Move right by one word > "ABW hij xyz FXX"[15, 12, 7, 4], "abc uvw AAA def lmn"[3, 7, 8, 15], "ABD opq rst DSU "[16, 12, 7, 4, 0] > >Index: LayoutTests/editing/selection/move-by-word-visually-single-space-one-element-expected.txt >=================================================================== >--- LayoutTests/editing/selection/move-by-word-visually-single-space-one-element-expected.txt (revision 110473) >+++ LayoutTests/editing/selection/move-by-word-visually-single-space-one-element-expected.txt (working copy) >@@ -82,6 +82,21 @@ Move right by one word > "ZQB abc RIG"[11, 8, 4, 0] > Test 17, LTR: > Move right by one word >+"abc áªBAD def᪠xyz"[0, 8, 14, 17] FAIL expected: [0, 9, 8, 14, 17] >+"abc áªBAD def᪠xyz"[0, 8] FAIL expected 9 >+"abc áªBAD def᪠xyz"[1, 8] FAIL expected 9 >+"abc áªBAD def᪠xyz"[2, 8] FAIL expected 9 >+"abc áªBAD def᪠xyz"[3, 8] FAIL expected 9 >+"abc áªBAD def᪠xyz"[4, 8] FAIL expected 9 >+Move left by one word >+"abc áªBAD def᪠xyz"[17, 14, 8, 9, 0] >+Test 18, LTR: >+Move right by one word >+"abc def hij "[0, 4, 8], " opq rst "[1, 5, 8] >+Move left by one word >+" opq rst "[8, 5, 1], "abc def hij "[8, 4, 0] >+Test 19, LTR: >+Move right by one word > <DIV>[0] > Move left by one word > <DIV>[0] >Index: LayoutTests/editing/selection/move-by-word-visually-single-space-one-element.html >=================================================================== >--- LayoutTests/editing/selection/move-by-word-visually-single-space-one-element.html (revision 110473) >+++ LayoutTests/editing/selection/move-by-word-visually-single-space-one-element.html (working copy) >@@ -65,6 +65,12 @@ where child_node_index is optional, defa > <div dir=ltr class="test_move_by_word" title="0 4 8 11|11 8 4 0" contenteditable>×©× × abc ס××</div> > <div dir=rtl class="test_move_by_word" title="11 8 4 0|0 4 8 11" contenteditable>×©× × abc ס××</div> > >+<!-- Test with Bidi control characters --> >+<div dir=ltr class="test_move_by_word" id="notReachablePosition" title="0 9 8 14 17|17 14 8 9 0" contenteditable>abc ‫××× def‬ xyz</div> >+ >+<!-- Test with image -- non-inline-text-box --> >+<div id="d_1" dir=ltr class="test_move_by_word" contenteditable title="[d_1, 0, 1][d_1, 4, 1][d_1, 8, 1][d_1, 1, 3][d_1, 5, 3][d_1, 8, 3]|[d_1, 8, 3][d_1, 5, 3][d_1, 1, 3][d_1, 8, 1][d_1, 4, 1][d_1, 0, 1]">abc def hij <img src=../../accessibility/resources/cake.png> opq rst </div> >+ > <!-- empty div --> > <div dir=ltr class="test_move_by_word" title="0|0" contenteditable></div> > >Index: LayoutTests/editing/selection/move-by-word-visually-wrong-left-right-expected.txt >=================================================================== >--- LayoutTests/editing/selection/move-by-word-visually-wrong-left-right-expected.txt (revision 0) >+++ LayoutTests/editing/selection/move-by-word-visually-wrong-left-right-expected.txt (revision 0) >@@ -0,0 +1,2 @@ >+abc ששש def ×©× × opq ס×× uvw ששש xyz >+ctrl/alt+right from left of ס×× should move cursor to left of opq. But it actually moved to position 4 >Index: LayoutTests/editing/selection/move-by-word-visually-wrong-left-right.html >=================================================================== >--- LayoutTests/editing/selection/move-by-word-visually-wrong-left-right.html (revision 0) >+++ LayoutTests/editing/selection/move-by-word-visually-wrong-left-right.html (revision 0) >@@ -0,0 +1,36 @@ >+<!DOCTYPE HTML> >+<html> >+<head> >+<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> >+<link rel="stylesheet" type="text/css" href="resources/extend-selection.css" /> >+<script src="resources/move-by-word-visually.js"></script> >+<script> >+onload = function() { >+ try { >+ var sel = window.getSelection(); >+ sel.setPosition(document.getElementById('d_1'), 0); >+ sel.modify("move", "right", "word"); >+ if (sel.anchorOffset == 5) >+ log("PASS"); >+ else >+ log("ctrl/alt+right from left of ס×× should move cursor to left of opq. " + >+ "But it actually moved to position " + sel.anchorOffset); >+ } finally { >+ flushLog(); >+ } >+}; >+ >+if (window.layoutTestController) { >+ layoutTestController.dumpAsText(); >+ layoutTestController.setEditingBehavior('win'); >+} >+</script> >+<title>wrong result because VisiblePosition.left()/right() returns wrong result</title> >+</head> >+<body> >+<div id="testMoveByWord"> >+<div dir=ltr contenteditable>abc ששש def <span id="d_1" dir=rtl>×©× × opq ס××</span> uvw ששש xyz</div> >+</div> >+<ul id="console"></ul> >+</body> >+</html> >Index: LayoutTests/editing/selection/resources/move-by-word-visually.js >=================================================================== >--- LayoutTests/editing/selection/resources/move-by-word-visually.js (revision 110473) >+++ LayoutTests/editing/selection/resources/move-by-word-visually.js (working copy) >@@ -175,6 +175,10 @@ function moveByWordOnEveryChar(sel, test > var prevOffset = sel.anchorOffset; > var prevNode = sel.anchorNode; > >+ // advance is used to special handling the case that arrow key is not able to reach certain position. >+ // In which case, we will need to manually skip this word break position in order to report correct log. >+ var advance = true; >+ > while (1) { > var positions = []; > positions.push({ node: sel.anchorNode, offset: sel.anchorOffset }); >@@ -205,9 +209,15 @@ function moveByWordOnEveryChar(sel, test > break; > > position = { node: sel.anchorNode, offset: sel.anchorOffset }; >- if (wordBreakIndex < wordBreaks.length >+ if ((wordBreakIndex < wordBreaks.length > && positionEqualToWordBreak(position, wordBreaks[wordBreakIndex])) >+ || (test == document.getElementById("notReachablePosition") >+ && sel.anchorOffset > wordBreaks[wordBreakIndex] >+ && advance >+ && searchDirection == "right")) { > ++wordBreakIndex; >+ advance = false; >+ } > > prevNode = sel.anchorNode; > prevOffset = sel.anchorOffset;
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 78856
:
127480
|
128018
|
128053
|
128769
|
131476
|
131956
|
132101
| 132121