Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1059)

Unified Diff: Source/core/html/HTMLTextFormControlElement.cpp

Issue 357603003: Add functions searching a word boundary without VisualPosition to HTMLTextFormControlElement. (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Fix nits Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « Source/core/html/HTMLTextFormControlElement.h ('k') | Source/core/html/HTMLTextFormControlElementTest.cpp » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: Source/core/html/HTMLTextFormControlElement.cpp
diff --git a/Source/core/html/HTMLTextFormControlElement.cpp b/Source/core/html/HTMLTextFormControlElement.cpp
index 0434728f24a557dcb6aa4a94af75ce08ceaccc60..440b5a4b5c469b6b818eaf0158c70baee0b12a97 100644
--- a/Source/core/html/HTMLTextFormControlElement.cpp
+++ b/Source/core/html/HTMLTextFormControlElement.cpp
@@ -30,6 +30,7 @@
#include "core/HTMLNames.h"
#include "core/accessibility/AXObjectCache.h"
#include "core/dom/Document.h"
+#include "core/dom/NodeList.h"
#include "core/dom/NodeTraversal.h"
#include "core/dom/Text.h"
#include "core/dom/shadow/ShadowRoot.h"
@@ -43,6 +44,7 @@
#include "core/rendering/RenderBlock.h"
#include "core/rendering/RenderTheme.h"
#include "platform/heap/Handle.h"
+#include "platform/text/TextBoundaries.h"
#include "wtf/text/StringBuilder.h"
namespace WebCore {
@@ -659,4 +661,251 @@ HTMLElement* HTMLTextFormControlElement::innerEditorElement() const
return toHTMLElement(userAgentShadowRoot()->getElementById(ShadowElementNames::innerEditor()));
}
+static Position innerNodePosition(const Position& innerPosition)
+{
+ HTMLElement* element = toHTMLElement(innerPosition.anchorNode());
+ RefPtrWillBeRawPtr<NodeList> childNodes = element->childNodes();
+ if (!childNodes->length())
+ return Position(element, 0, Position::PositionIsOffsetInAnchor);
+
+ unsigned offset = 0;
+
+ switch (innerPosition.anchorType()) {
+ case Position::PositionIsOffsetInAnchor:
+ {
yosin_UTC9 2014/07/03 05:17:07 nit: We don't need "{}" here.
yoichio 2014/07/03 05:31:41 Done.
+ offset = std::max(0, std::min(innerPosition.computeOffsetInContainerNode(), (int)childNodes->length()));
+ break;
+ }
+ case Position::PositionIsAfterChildren:
+ {
yosin_UTC9 2014/07/03 05:17:07 nit: We don't need "{}" here.
yoichio 2014/07/03 05:31:41 Done.
+ offset = childNodes->length();
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (offset == childNodes->length()) {
+ return Position(childNodes->item(childNodes->length() - 1), Position::PositionIsAfterAnchor);
+ }
+
+ Node* node = childNodes->item(offset);
+ if (node->isTextNode())
+ return Position(toText(node), 0);
+
+ return Position(node, Position::PositionIsBeforeAnchor);
+}
+
+static Position findWordBoundary(const HTMLElement* innerEditor, const Position& startPosition, const Position endPosition, bool findStart)
+{
+ Vector<UChar> characters;
+ Vector<unsigned> lengthList;
+ Vector<Text*> textList;
+ // Trace text nodes.
+ for (Node* node = startPosition.anchorNode(); node; node = NodeTraversal::next(*node, innerEditor)) {
+ bool isStartNode = node == startPosition.anchorNode();
+ bool isEndNode = node == endPosition.anchorNode();
+ if (node->isTextNode()) {
+ Text* text = toText(node);
+ const unsigned start = isStartNode ? startPosition.computeOffsetInContainerNode() : 0;
+ const unsigned end = isEndNode ? endPosition.computeOffsetInContainerNode() : text->data().length();
+ for (unsigned offset = start; offset < end; ++offset)
+ characters.append(text->data()[offset]);
+ const unsigned length = end - start;
+
+ lengthList.append(length);
+ textList.append(text);
+ }
+
+ if (isEndNode)
+ break;
+ }
+
+ if (!characters.size())
+ return startPosition;
+
+ int start, end;
+ if (!findStart && characters.data()[0] == '\n') {
+ // findWordBoundary("\ntext", 0, &start, &end) assigns 1 to |end| but we expect 0 at the case.
+ start = 0;
+ end = 0;
+ } else {
+ findWordBoundary(characters.data(), characters.size(), findStart ? characters.size() : 0, &start, &end);
+ }
+ ASSERT(start >= 0);
+ ASSERT(end >= 0);
+ unsigned restOffset = findStart ? start : end;
+ // Find position.
+ for (unsigned i = 0; i < lengthList.size(); ++i) {
+ if (restOffset <= lengthList[i])
+ return Position(textList[i], (textList[i] == startPosition.anchorNode()) ? restOffset + startPosition.computeOffsetInContainerNode() : restOffset);
+ restOffset -= lengthList[i];
+ }
+
+ ASSERT_NOT_REACHED();
+ return Position();
+}
+
+Position HTMLTextFormControlElement::startOfWord(const Position& position)
+{
+ const HTMLTextFormControlElement* textFormControl = enclosingTextFormControl(position);
+ ASSERT(textFormControl);
+ HTMLElement* innerEditor = textFormControl->innerEditorElement();
+
+ const Position startPosition = startOfSentence(position);
+ if (startPosition == position)
+ return position;
+ const Position endPosition = (position.anchorNode() == innerEditor) ? innerNodePosition(position) : position;
+
+ return findWordBoundary(innerEditor, startPosition, endPosition, true);
+}
+
+Position HTMLTextFormControlElement::endOfWord(const Position& position)
+{
+ const HTMLTextFormControlElement* textFormControl = enclosingTextFormControl(position);
+ ASSERT(textFormControl);
+ HTMLElement* innerEditor = textFormControl->innerEditorElement();
+
+
+ const Position endPosition = endOfSentence(position);
+ if (endPosition == position)
+ return position;
+ const Position startPosition = (position.anchorNode() == innerEditor) ? innerNodePosition(position) : position;
+
+ return findWordBoundary(innerEditor, startPosition, endPosition, false);
+}
+
+static int findLastLineBreak(const Text& text, int offset)
+{
+ for (; offset >= 0; --offset) {
+ if (text.data()[offset] == '\n')
+ return offset;
+ }
+ return -1;
+}
+
+static Position endOfPrevious(const Node& node, HTMLElement* innerEditor)
+{
+ Node* prev = NodeTraversal::previous(node, innerEditor);
+ if (!prev)
+ return Position();
+
+ if (prev->hasTagName(brTag))
+ return Position(prev, Position::PositionIsAfterAnchor);
+
+ if (prev->isTextNode()) {
+ Text* textNode = toText(prev);
+ return Position(textNode, textNode->length());
+ }
+
+ return Position();
+}
+
+static Position previousIfPositionIsAfterLineBreak(const Position& position, HTMLElement* innerEditor)
+{
+ if (position.isNull())
+ return Position();
+
+ // Move back if position is just after line break.
+ if (isHTMLBRElement(*position.anchorNode())) {
+ switch (position.anchorType()) {
+ case Position::PositionIsAfterAnchor:
+ return Position(position.anchorNode(), Position::PositionIsBeforeAnchor);
+ case Position::PositionIsBeforeAnchor:
+ return previousIfPositionIsAfterLineBreak(endOfPrevious(*position.anchorNode(), innerEditor), innerEditor);
+ default:
+ ASSERT_NOT_REACHED();
+ }
+ } else if (position.anchorNode()->isTextNode()) {
+ Text* textNode = toText(position.anchorNode());
+ unsigned offset = position.offsetInContainerNode();
+ if (textNode->length() == 0 || offset <= 0) {
+ return previousIfPositionIsAfterLineBreak(endOfPrevious(*position.anchorNode(), innerEditor), innerEditor);
+ }
+
+ if (offset <= textNode->length() && textNode->data()[offset - 1] == '\n') {
+ return Position(textNode, offset - 1);
+ }
+ }
+
+ return position;
+}
+
+static inline Position startOfInnerText(const HTMLTextFormControlElement* textFormControl)
+{
+ return Position(textFormControl->innerEditorElement(), 0, Position::PositionIsOffsetInAnchor);
+}
+
+Position HTMLTextFormControlElement::startOfSentence(const Position& position)
+{
+ HTMLTextFormControlElement* textFormControl = enclosingTextFormControl(position);
+ ASSERT(textFormControl);
+
+ HTMLElement* innerEditor = textFormControl->innerEditorElement();
+ if (!innerEditor->childNodes()->length())
+ return startOfInnerText(textFormControl);
+
+ const Position innerPosition = position.anchorNode() == innerEditor ? innerNodePosition(position) : position;
+ const Position pivotPosition = previousIfPositionIsAfterLineBreak(innerPosition, innerEditor);
+ if (pivotPosition.isNull())
+ return startOfInnerText(textFormControl);
+
+ for (Node* node = pivotPosition.anchorNode(); node; node = NodeTraversal::previous(*node, innerEditor)) {
+ bool isPivotNode = (node == pivotPosition.anchorNode());
+
+ if (node->isTextNode()) {
+ Text* textNode = toText(node);
+ int lastLineBreak = findLastLineBreak(*textNode, isPivotNode ? pivotPosition.offsetInContainerNode() - 1 : (int)textNode->length() - 1);
+ if (lastLineBreak >= 0)
+ return Position(textNode, lastLineBreak + 1);
+ } else if (isHTMLBRElement(node) && (!isPivotNode || pivotPosition.anchorType() == Position::PositionIsAfterAnchor)) {
+ return Position(node, Position::PositionIsAfterAnchor);
+ }
+ }
+ return startOfInnerText(textFormControl);
+}
+
+static int findFirstLineBreak(const Text& text, int offset)
+{
+ for (; (unsigned)offset < text.length(); ++offset) {
+ if (text.data()[offset] == '\n')
+ return offset;
+ }
+ return -1;
+}
+
+static Position endOfInnerText(const HTMLTextFormControlElement* textFormControl)
+{
+ HTMLElement* innerEditor = textFormControl->innerEditorElement();
+ return Position(innerEditor, innerEditor->childNodes()->length(), Position::PositionIsOffsetInAnchor);
+}
+
+Position HTMLTextFormControlElement::endOfSentence(const Position& position)
+{
+ HTMLTextFormControlElement* textFormControl = enclosingTextFormControl(position);
+ ASSERT(textFormControl);
+
+ HTMLElement* innerEditor = textFormControl->innerEditorElement();
+ if (!innerEditor->childNodes()->length())
+ return startOfInnerText(textFormControl);
+
+ const Position pivotPosition = position.anchorNode() == innerEditor ? innerNodePosition(position) : position;
+ if (pivotPosition.isNull())
+ return startOfInnerText(textFormControl);
+
+ for (Node* node = pivotPosition.anchorNode(); node; node = NodeTraversal::next(*node, innerEditor)) {
+ bool isPivotNode = (node == pivotPosition.anchorNode());
+
+ if (node->isTextNode()) {
+ Text* textNode = toText(node);
+ int firstLineBreak = findFirstLineBreak(*textNode, isPivotNode ? pivotPosition.offsetInContainerNode() : 0);
+ if (firstLineBreak >= 0)
+ return Position(textNode, firstLineBreak + 1);
+ } else if (isHTMLBRElement(node)) {
+ return Position(node, Position::PositionIsAfterAnchor);
+ }
+ }
+ return endOfInnerText(textFormControl);
+}
+
} // namespace Webcore
« no previous file with comments | « Source/core/html/HTMLTextFormControlElement.h ('k') | Source/core/html/HTMLTextFormControlElementTest.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698