1 /*
2 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 #include "config.h"
27 #include "visible_units.h"
28
29 #include "Document.h"
30 #include "Element.h"
31 #include "HTMLNames.h"
32 #include "RenderBlock.h"
33 #include "RenderLayer.h"
34 #include "TextBoundaries.h"
35 #include "TextBreakIterator.h"
36 #include "TextIterator.h"
37 #include "VisiblePosition.h"
38 #include "htmlediting.h"
39
40 namespace WebCore {
41
42 using namespace HTMLNames;
43
previousBoundary(const VisiblePosition & c,unsigned (* searchFunction)(const UChar *,unsigned))44 static VisiblePosition previousBoundary(const VisiblePosition &c, unsigned (*searchFunction)(const UChar *, unsigned))
45 {
46 Position pos = c.deepEquivalent();
47 Node *n = pos.node();
48 if (!n)
49 return VisiblePosition();
50 Document *d = n->document();
51 Node *de = d->documentElement();
52 if (!de)
53 return VisiblePosition();
54 Node *boundary = n->enclosingBlockFlowElement();
55 if (!boundary)
56 return VisiblePosition();
57 bool isContentEditable = boundary->isContentEditable();
58 while (boundary && boundary != de && boundary->parentNode() && isContentEditable == boundary->parentNode()->isContentEditable())
59 boundary = boundary->parentNode();
60
61 Position start = rangeCompliantEquivalent(Position(boundary, 0));
62 Position end = rangeCompliantEquivalent(pos);
63 RefPtr<Range> searchRange = Range::create(d);
64
65 int exception = 0;
66 searchRange->setStart(start.node(), start.offset(), exception);
67 searchRange->setEnd(end.node(), end.offset(), exception);
68
69 ASSERT(!exception);
70 if (exception)
71 return VisiblePosition();
72
73 SimplifiedBackwardsTextIterator it(searchRange.get());
74 Vector<UChar, 1024> string;
75 unsigned next = 0;
76 bool inTextSecurityMode = start.node() && start.node()->renderer() && start.node()->renderer()->style()->textSecurity() != TSNONE;
77 while (!it.atEnd()) {
78 // iterate to get chunks until the searchFunction returns a non-zero value.
79 if (!inTextSecurityMode)
80 string.prepend(it.characters(), it.length());
81 else {
82 // Treat bullets used in the text security mode as regular characters when looking for boundaries
83 String iteratorString(it.characters(), it.length());
84 iteratorString = iteratorString.impl()->secure('x');
85 string.prepend(iteratorString.characters(), iteratorString.length());
86 }
87
88 next = searchFunction(string.data(), string.size());
89 if (next != 0)
90 break;
91 it.advance();
92 }
93
94 if (it.atEnd() && next == 0) {
95 pos = it.range()->startPosition();
96 } else if (next != 0) {
97 Node *node = it.range()->startContainer(exception);
98 if (node->isTextNode() || (node->renderer() && node->renderer()->isBR()))
99 // The next variable contains a usable index into a text node
100 pos = Position(node, next);
101 else {
102 // Use the end of the found range, the start is not guaranteed to
103 // be correct.
104 Position end = it.range()->endPosition();
105 VisiblePosition boundary(end);
106 unsigned i = it.length() - next;
107 while (i--)
108 boundary = boundary.previous();
109 return boundary;
110 }
111 }
112
113 return VisiblePosition(pos, DOWNSTREAM);
114 }
115
nextBoundary(const VisiblePosition & c,unsigned (* searchFunction)(const UChar *,unsigned))116 static VisiblePosition nextBoundary(const VisiblePosition &c, unsigned (*searchFunction)(const UChar *, unsigned))
117 {
118 Position pos = c.deepEquivalent();
119 Node *n = pos.node();
120 if (!n)
121 return VisiblePosition();
122 Document *d = n->document();
123 Node *de = d->documentElement();
124 if (!de)
125 return VisiblePosition();
126 Node *boundary = n->enclosingBlockFlowElement();
127 if (!boundary)
128 return VisiblePosition();
129 bool isContentEditable = boundary->isContentEditable();
130 while (boundary && boundary != de && boundary->parentNode() && isContentEditable == boundary->parentNode()->isContentEditable())
131 boundary = boundary->parentNode();
132
133 RefPtr<Range> searchRange(d->createRange());
134 Position start(rangeCompliantEquivalent(pos));
135 ExceptionCode ec = 0;
136 searchRange->selectNodeContents(boundary, ec);
137 searchRange->setStart(start.node(), start.offset(), ec);
138 TextIterator it(searchRange.get(), true);
139 Vector<UChar, 1024> string;
140 unsigned next = 0;
141 bool inTextSecurityMode = start.node() && start.node()->renderer() && start.node()->renderer()->style()->textSecurity() != TSNONE;
142 while (!it.atEnd()) {
143 // Keep asking the iterator for chunks until the search function
144 // returns an end value not equal to the length of the string passed to it.
145 if (!inTextSecurityMode)
146 string.append(it.characters(), it.length());
147 else {
148 // Treat bullets used in the text security mode as regular characters when looking for boundaries
149 String iteratorString(it.characters(), it.length());
150 iteratorString = iteratorString.impl()->secure('x');
151 string.append(iteratorString.characters(), iteratorString.length());
152 }
153
154 next = searchFunction(string.data(), string.size());
155 if (next != string.size())
156 break;
157 it.advance();
158 }
159
160 if (it.atEnd() && next == string.size()) {
161 pos = it.range()->startPosition();
162 } else if (next != 0) {
163 // Use the character iterator to translate the next value into a DOM position.
164 CharacterIterator charIt(searchRange.get(), true);
165 charIt.advance(next - 1);
166 pos = charIt.range()->endPosition();
167
168 // FIXME: workaround for collapsed range (where only start position is correct) emitted for some emitted newlines (see rdar://5192593)
169 VisiblePosition visPos = VisiblePosition(pos);
170 if (visPos == VisiblePosition(charIt.range()->startPosition()))
171 pos = visPos.next(true).deepEquivalent();
172 }
173
174 // generate VisiblePosition, use UPSTREAM affinity if possible
175 return VisiblePosition(pos, VP_UPSTREAM_IF_POSSIBLE);
176 }
177
178 // ---------
179
startWordBoundary(const UChar * characters,unsigned length)180 static unsigned startWordBoundary(const UChar* characters, unsigned length)
181 {
182 int start, end;
183 findWordBoundary(characters, length, length, &start, &end);
184 return start;
185 }
186
startOfWord(const VisiblePosition & c,EWordSide side)187 VisiblePosition startOfWord(const VisiblePosition &c, EWordSide side)
188 {
189 // FIXME: This returns a null VP for c at the start of the document
190 // and side == LeftWordIfOnBoundary
191 VisiblePosition p = c;
192 if (side == RightWordIfOnBoundary) {
193 // at paragraph end, the startofWord is the current position
194 if (isEndOfParagraph(c))
195 return c;
196
197 p = c.next();
198 if (p.isNull())
199 return c;
200 }
201 return previousBoundary(p, startWordBoundary);
202 }
203
endWordBoundary(const UChar * characters,unsigned length)204 static unsigned endWordBoundary(const UChar* characters, unsigned length)
205 {
206 int start, end;
207 findWordBoundary(characters, length, 0, &start, &end);
208 return end;
209 }
210
endOfWord(const VisiblePosition & c,EWordSide side)211 VisiblePosition endOfWord(const VisiblePosition &c, EWordSide side)
212 {
213 VisiblePosition p = c;
214 if (side == LeftWordIfOnBoundary) {
215 if (isStartOfParagraph(c))
216 return c;
217
218 p = c.previous();
219 if (p.isNull())
220 return c;
221 } else if (isEndOfParagraph(c))
222 return c;
223
224 return nextBoundary(p, endWordBoundary);
225 }
226
previousWordPositionBoundary(const UChar * characters,unsigned length)227 static unsigned previousWordPositionBoundary(const UChar* characters, unsigned length)
228 {
229 return findNextWordFromIndex(characters, length, length, false);
230 }
231
previousWordPosition(const VisiblePosition & c)232 VisiblePosition previousWordPosition(const VisiblePosition &c)
233 {
234 VisiblePosition prev = previousBoundary(c, previousWordPositionBoundary);
235 return c.honorEditableBoundaryAtOrAfter(prev);
236 }
237
nextWordPositionBoundary(const UChar * characters,unsigned length)238 static unsigned nextWordPositionBoundary(const UChar* characters, unsigned length)
239 {
240 return findNextWordFromIndex(characters, length, 0, true);
241 }
242
nextWordPosition(const VisiblePosition & c)243 VisiblePosition nextWordPosition(const VisiblePosition &c)
244 {
245 VisiblePosition next = nextBoundary(c, nextWordPositionBoundary);
246 return c.honorEditableBoundaryAtOrBefore(next);
247 }
248
249 // ---------
250
rootBoxForLine(const VisiblePosition & c)251 static RootInlineBox *rootBoxForLine(const VisiblePosition &c)
252 {
253 Position p = c.deepEquivalent();
254 Node *node = p.node();
255 if (!node)
256 return 0;
257
258 RenderObject *renderer = node->renderer();
259 if (!renderer)
260 return 0;
261
262 InlineBox* box;
263 int offset;
264 c.getInlineBoxAndOffset(box, offset);
265
266 return box ? box->root() : 0;
267 }
268
positionAvoidingFirstPositionInTable(const VisiblePosition & c)269 static VisiblePosition positionAvoidingFirstPositionInTable(const VisiblePosition& c)
270 {
271 // return table offset 0 instead of the first VisiblePosition inside the table
272 VisiblePosition previous = c.previous();
273 if (isLastPositionBeforeTable(previous))
274 return previous;
275
276 return c;
277 }
278
startPositionForLine(const VisiblePosition & c)279 static VisiblePosition startPositionForLine(const VisiblePosition& c)
280 {
281 if (c.isNull())
282 return VisiblePosition();
283
284 RootInlineBox *rootBox = rootBoxForLine(c);
285 if (!rootBox) {
286 // There are VisiblePositions at offset 0 in blocks without
287 // RootInlineBoxes, like empty editable blocks and bordered blocks.
288 Position p = c.deepEquivalent();
289 if (p.node()->renderer() && p.node()->renderer()->isRenderBlock() && p.offset() == 0)
290 return positionAvoidingFirstPositionInTable(c);
291
292 return VisiblePosition();
293 }
294
295 // Generated content (e.g. list markers and CSS :before and :after
296 // pseudoelements) have no corresponding DOM element, and so cannot be
297 // represented by a VisiblePosition. Use whatever follows instead.
298 InlineBox *startBox = rootBox->firstLeafChild();
299 Node *startNode;
300 while (1) {
301 if (!startBox)
302 return VisiblePosition();
303
304 RenderObject *startRenderer = startBox->object();
305 if (!startRenderer)
306 return VisiblePosition();
307
308 startNode = startRenderer->element();
309 if (startNode)
310 break;
311
312 startBox = startBox->nextLeafChild();
313 }
314
315 int startOffset = 0;
316 if (startBox->isInlineTextBox()) {
317 InlineTextBox *startTextBox = static_cast<InlineTextBox *>(startBox);
318 startOffset = startTextBox->start();
319 }
320
321 VisiblePosition visPos = VisiblePosition(startNode, startOffset, DOWNSTREAM);
322 return positionAvoidingFirstPositionInTable(visPos);
323 }
324
startOfLine(const VisiblePosition & c)325 VisiblePosition startOfLine(const VisiblePosition& c)
326 {
327 VisiblePosition visPos = startPositionForLine(c);
328
329 if (visPos.isNotNull()) {
330 // Make sure the start of line is not greater than the given input position. Else use the previous position to
331 // obtain start of line. This condition happens when the input position is before the space character at the end
332 // of a soft-wrapped non-editable line. In this scenario, startPositionForLine would incorrectly hand back a position
333 // greater than the input position. This fix is to account for the discrepancy between lines with webkit-line-break:after-white-space
334 // style versus lines without that style, which would break before a space by default.
335 Position p = visPos.deepEquivalent();
336 if (p.offset() > c.deepEquivalent().offset() && p.node()->isSameNode(c.deepEquivalent().node())) {
337 visPos = c.previous();
338 if (visPos.isNull())
339 return VisiblePosition();
340 visPos = startPositionForLine(visPos);
341 }
342 }
343
344 return c.honorEditableBoundaryAtOrAfter(visPos);
345 }
346
endPositionForLine(const VisiblePosition & c)347 static VisiblePosition endPositionForLine(const VisiblePosition& c)
348 {
349 if (c.isNull())
350 return VisiblePosition();
351
352 RootInlineBox *rootBox = rootBoxForLine(c);
353 if (!rootBox) {
354 // There are VisiblePositions at offset 0 in blocks without
355 // RootInlineBoxes, like empty editable blocks and bordered blocks.
356 Position p = c.deepEquivalent();
357 if (p.node()->renderer() && p.node()->renderer()->isRenderBlock() && p.offset() == 0)
358 return c;
359 return VisiblePosition();
360 }
361
362 // Generated content (e.g. list markers and CSS :before and :after
363 // pseudoelements) have no corresponding DOM element, and so cannot be
364 // represented by a VisiblePosition. Use whatever precedes instead.
365 Node *endNode;
366 InlineBox *endBox = rootBox->lastLeafChild();
367 while (1) {
368 if (!endBox)
369 return VisiblePosition();
370
371 RenderObject *endRenderer = endBox->object();
372 if (!endRenderer)
373 return VisiblePosition();
374
375 endNode = endRenderer->element();
376 if (endNode)
377 break;
378
379 endBox = endBox->prevLeafChild();
380 }
381
382 int endOffset = 1;
383 if (endNode->hasTagName(brTag)) {
384 endOffset = 0;
385 } else if (endBox->isInlineTextBox()) {
386 InlineTextBox *endTextBox = static_cast<InlineTextBox *>(endBox);
387 endOffset = endTextBox->start();
388 if (!endTextBox->isLineBreak())
389 endOffset += endTextBox->len();
390 }
391
392 return VisiblePosition(endNode, endOffset, VP_UPSTREAM_IF_POSSIBLE);
393 }
394
endOfLine(const VisiblePosition & c)395 VisiblePosition endOfLine(const VisiblePosition& c)
396 {
397 VisiblePosition visPos = endPositionForLine(c);
398
399 // Make sure the end of line is at the same line as the given input position. Else use the previous position to
400 // obtain end of line. This condition happens when the input position is before the space character at the end
401 // of a soft-wrapped non-editable line. In this scenario, endPositionForLine would incorrectly hand back a position
402 // in the next line instead. This fix is to account for the discrepancy between lines with webkit-line-break:after-white-space style
403 // versus lines without that style, which would break before a space by default.
404 if (!inSameLine(c, visPos)) {
405 visPos = c.previous();
406 if (visPos.isNull())
407 return VisiblePosition();
408 visPos = endPositionForLine(visPos);
409 }
410
411 return c.honorEditableBoundaryAtOrBefore(visPos);
412 }
413
inSameLine(const VisiblePosition & a,const VisiblePosition & b)414 bool inSameLine(const VisiblePosition &a, const VisiblePosition &b)
415 {
416 return a.isNotNull() && startOfLine(a) == startOfLine(b);
417 }
418
isStartOfLine(const VisiblePosition & p)419 bool isStartOfLine(const VisiblePosition &p)
420 {
421 return p.isNotNull() && p == startOfLine(p);
422 }
423
isEndOfLine(const VisiblePosition & p)424 bool isEndOfLine(const VisiblePosition &p)
425 {
426 return p.isNotNull() && p == endOfLine(p);
427 }
428
429 // The first leaf before node that has the same editability as node.
previousLeafWithSameEditability(Node * node)430 static Node* previousLeafWithSameEditability(Node* node)
431 {
432 bool editable = node->isContentEditable();
433 Node* n = node->previousLeafNode();
434 while (n) {
435 if (editable == n->isContentEditable())
436 return n;
437 n = n->previousLeafNode();
438 }
439 return 0;
440 }
441
previousLinePosition(const VisiblePosition & visiblePosition,int x)442 VisiblePosition previousLinePosition(const VisiblePosition &visiblePosition, int x)
443 {
444 Position p = visiblePosition.deepEquivalent();
445 Node *node = p.node();
446 Node* highestRoot = highestEditableRoot(p);
447 if (!node)
448 return VisiblePosition();
449
450 node->document()->updateLayoutIgnorePendingStylesheets();
451
452 RenderObject *renderer = node->renderer();
453 if (!renderer)
454 return VisiblePosition();
455
456 RenderBlock *containingBlock = 0;
457 RootInlineBox *root = 0;
458 InlineBox* box;
459 int ignoredCaretOffset;
460 visiblePosition.getInlineBoxAndOffset(box, ignoredCaretOffset);
461 if (box) {
462 root = box->root()->prevRootBox();
463 if (root)
464 containingBlock = renderer->containingBlock();
465 }
466
467 if (!root) {
468 // This containing editable block does not have a previous line.
469 // Need to move back to previous containing editable block in this root editable
470 // block and find the last root line box in that block.
471 Node* startBlock = enclosingBlock(node);
472 Node* n = previousLeafWithSameEditability(node);
473 while (n && startBlock == enclosingBlock(n))
474 n = previousLeafWithSameEditability(n);
475 while (n) {
476 if (highestEditableRoot(Position(n, 0)) != highestRoot)
477 break;
478 Position pos(n, caretMinOffset(n));
479 if (pos.isCandidate()) {
480 ASSERT(n->renderer());
481 Position maxPos(n, caretMaxOffset(n));
482 maxPos.getInlineBoxAndOffset(DOWNSTREAM, box, ignoredCaretOffset);
483 if (box) {
484 // previous root line box found
485 root = box->root();
486 containingBlock = n->renderer()->containingBlock();
487 break;
488 }
489
490 return VisiblePosition(pos, DOWNSTREAM);
491 }
492 n = previousLeafWithSameEditability(n);
493 }
494 }
495
496 if (root) {
497 // FIXME: Can be wrong for multi-column layout and with transforms.
498 FloatPoint absPos = containingBlock->localToAbsolute(FloatPoint());
499 if (containingBlock->hasOverflowClip())
500 absPos -= containingBlock->layer()->scrolledContentOffset();
501 RenderObject *renderer = root->closestLeafChildForXPos(x - absPos.x(), isEditablePosition(p))->object();
502 Node* node = renderer->node();
503 if (editingIgnoresContent(node))
504 return Position(node->parent(), node->nodeIndex());
505 return renderer->positionForCoordinates(x - absPos.x(), root->topOverflow());
506 }
507
508 // Could not find a previous line. This means we must already be on the first line.
509 // Move to the start of the content in this block, which effectively moves us
510 // to the start of the line we're on.
511 Node* rootElement = node->isContentEditable() ? node->rootEditableElement() : node->document()->documentElement();
512 return VisiblePosition(rootElement, 0, DOWNSTREAM);
513 }
514
nextLeafWithSameEditability(Node * node,int offset)515 static Node* nextLeafWithSameEditability(Node* node, int offset)
516 {
517 bool editable = node->isContentEditable();
518 ASSERT(offset >= 0);
519 Node* child = node->childNode(offset);
520 Node* n = child ? child->nextLeafNode() : node->nextLeafNode();
521 while (n) {
522 if (editable == n->isContentEditable())
523 return n;
524 n = n->nextLeafNode();
525 }
526 return 0;
527 }
528
nextLeafWithSameEditability(Node * node)529 static Node* nextLeafWithSameEditability(Node* node)
530 {
531 if (!node)
532 return 0;
533
534 bool editable = node->isContentEditable();
535 Node* n = node->nextLeafNode();
536 while (n) {
537 if (editable == n->isContentEditable())
538 return n;
539 n = n->nextLeafNode();
540 }
541 return 0;
542 }
543
nextLinePosition(const VisiblePosition & visiblePosition,int x)544 VisiblePosition nextLinePosition(const VisiblePosition &visiblePosition, int x)
545 {
546 Position p = visiblePosition.deepEquivalent();
547 Node *node = p.node();
548 Node* highestRoot = highestEditableRoot(p);
549 if (!node)
550 return VisiblePosition();
551
552 node->document()->updateLayoutIgnorePendingStylesheets();
553
554 RenderObject *renderer = node->renderer();
555 if (!renderer)
556 return VisiblePosition();
557
558 RenderBlock *containingBlock = 0;
559 RootInlineBox *root = 0;
560 InlineBox* box;
561 int ignoredCaretOffset;
562 visiblePosition.getInlineBoxAndOffset(box, ignoredCaretOffset);
563 if (box) {
564 root = box->root()->nextRootBox();
565 if (root)
566 containingBlock = renderer->containingBlock();
567 }
568
569 if (!root) {
570 // This containing editable block does not have a next line.
571 // Need to move forward to next containing editable block in this root editable
572 // block and find the first root line box in that block.
573 Node* startBlock = enclosingBlock(node);
574 Node* n = nextLeafWithSameEditability(node, p.offset());
575 while (n && startBlock == enclosingBlock(n))
576 n = nextLeafWithSameEditability(n);
577 while (n) {
578 if (highestEditableRoot(Position(n, 0)) != highestRoot)
579 break;
580 Position pos(n, caretMinOffset(n));
581 if (pos.isCandidate()) {
582 ASSERT(n->renderer());
583 pos.getInlineBoxAndOffset(DOWNSTREAM, box, ignoredCaretOffset);
584 if (box) {
585 // next root line box found
586 root = box->root();
587 containingBlock = n->renderer()->containingBlock();
588 break;
589 }
590
591 return VisiblePosition(pos, DOWNSTREAM);
592 }
593 n = nextLeafWithSameEditability(n);
594 }
595 }
596
597 if (root) {
598 // FIXME: Can be wrong for multi-column layout and with transforms.
599 FloatPoint absPos = containingBlock->localToAbsolute(FloatPoint());
600 if (containingBlock->hasOverflowClip())
601 absPos -= containingBlock->layer()->scrolledContentOffset();
602 RenderObject *renderer = root->closestLeafChildForXPos(x - absPos.x(), isEditablePosition(p))->object();
603 Node* node = renderer->node();
604 if (editingIgnoresContent(node))
605 return Position(node->parent(), node->nodeIndex());
606 return renderer->positionForCoordinates(x - absPos.x(), root->topOverflow());
607 }
608
609 // Could not find a next line. This means we must already be on the last line.
610 // Move to the end of the content in this block, which effectively moves us
611 // to the end of the line we're on.
612 Element* rootElement = node->isContentEditable() ? node->rootEditableElement() : node->document()->documentElement();
613 return VisiblePosition(rootElement, rootElement ? rootElement->childNodeCount() : 0, DOWNSTREAM);
614 }
615
616 // ---------
617
startSentenceBoundary(const UChar * characters,unsigned length)618 static unsigned startSentenceBoundary(const UChar* characters, unsigned length)
619 {
620 TextBreakIterator* iterator = sentenceBreakIterator(characters, length);
621 // FIXME: The following function can return -1; we don't handle that.
622 return textBreakPreceding(iterator, length);
623 }
624
startOfSentence(const VisiblePosition & c)625 VisiblePosition startOfSentence(const VisiblePosition &c)
626 {
627 return previousBoundary(c, startSentenceBoundary);
628 }
629
endSentenceBoundary(const UChar * characters,unsigned length)630 static unsigned endSentenceBoundary(const UChar* characters, unsigned length)
631 {
632 TextBreakIterator* iterator = sentenceBreakIterator(characters, length);
633 return textBreakNext(iterator);
634 }
635
636 // FIXME: This includes the space after the punctuation that marks the end of the sentence.
endOfSentence(const VisiblePosition & c)637 VisiblePosition endOfSentence(const VisiblePosition &c)
638 {
639 return nextBoundary(c, endSentenceBoundary);
640 }
641
previousSentencePositionBoundary(const UChar * characters,unsigned length)642 static unsigned previousSentencePositionBoundary(const UChar* characters, unsigned length)
643 {
644 // FIXME: This is identical to startSentenceBoundary. I'm pretty sure that's not right.
645 TextBreakIterator* iterator = sentenceBreakIterator(characters, length);
646 // FIXME: The following function can return -1; we don't handle that.
647 return textBreakPreceding(iterator, length);
648 }
649
previousSentencePosition(const VisiblePosition & c)650 VisiblePosition previousSentencePosition(const VisiblePosition &c)
651 {
652 VisiblePosition prev = previousBoundary(c, previousSentencePositionBoundary);
653 return c.honorEditableBoundaryAtOrAfter(prev);
654 }
655
nextSentencePositionBoundary(const UChar * characters,unsigned length)656 static unsigned nextSentencePositionBoundary(const UChar* characters, unsigned length)
657 {
658 // FIXME: This is identical to endSentenceBoundary. This isn't right, it needs to
659 // move to the equivlant position in the following sentence.
660 TextBreakIterator* iterator = sentenceBreakIterator(characters, length);
661 return textBreakFollowing(iterator, 0);
662 }
663
nextSentencePosition(const VisiblePosition & c)664 VisiblePosition nextSentencePosition(const VisiblePosition &c)
665 {
666 VisiblePosition next = nextBoundary(c, nextSentencePositionBoundary);
667 return c.honorEditableBoundaryAtOrBefore(next);
668 }
669
670 // FIXME: Broken for positions before/after images that aren't inline (5027702)
startOfParagraph(const VisiblePosition & c)671 VisiblePosition startOfParagraph(const VisiblePosition &c)
672 {
673 Position p = c.deepEquivalent();
674 Node *startNode = p.node();
675
676 if (!startNode)
677 return VisiblePosition();
678
679 if (startNode->renderer()
680 && ((startNode->renderer()->isTable() && !startNode->renderer()->isInline())
681 || startNode->renderer()->isHR())
682 && p.offset() == maxDeepOffset(startNode))
683 return VisiblePosition(Position(startNode, 0));
684
685 Node* startBlock = enclosingBlock(startNode);
686
687 Node *node = startNode;
688 int offset = p.offset();
689
690 Node *n = startNode;
691 while (n) {
692 if (n->isContentEditable() != startNode->isContentEditable())
693 break;
694 RenderObject *r = n->renderer();
695 if (!r) {
696 n = n->traversePreviousNodePostOrder(startBlock);
697 continue;
698 }
699 RenderStyle *style = r->style();
700 if (style->visibility() != VISIBLE) {
701 n = n->traversePreviousNodePostOrder(startBlock);
702 continue;
703 }
704
705 if (r->isBR() || isBlock(n))
706 break;
707
708 if (r->isText()) {
709 if (style->preserveNewline()) {
710 const UChar* chars = toRenderText(r)->characters();
711 int i = toRenderText(r)->textLength();
712 int o = offset;
713 if (n == startNode && o < i)
714 i = max(0, o);
715 while (--i >= 0)
716 if (chars[i] == '\n')
717 return VisiblePosition(n, i + 1, DOWNSTREAM);
718 }
719 node = n;
720 offset = 0;
721 n = n->traversePreviousNodePostOrder(startBlock);
722 } else if (editingIgnoresContent(n) || isTableElement(n)) {
723 node = n;
724 offset = 0;
725 n = n->previousSibling() ? n->previousSibling() : n->traversePreviousNodePostOrder(startBlock);
726 } else
727 n = n->traversePreviousNodePostOrder(startBlock);
728 }
729
730 return VisiblePosition(node, offset, DOWNSTREAM);
731 }
732
733 // FIXME: Broken for positions before/after images that aren't inline (5027702)
endOfParagraph(const VisiblePosition & c)734 VisiblePosition endOfParagraph(const VisiblePosition &c)
735 {
736 if (c.isNull())
737 return VisiblePosition();
738
739 Position p = c.deepEquivalent();
740 Node* startNode = p.node();
741
742 if (startNode->renderer()
743 && ((startNode->renderer()->isTable() && !startNode->renderer()->isInline())
744 || startNode->renderer()->isHR())
745 && p.offset() == 0)
746 return VisiblePosition(Position(startNode, maxDeepOffset(startNode)));
747
748 Node* startBlock = enclosingBlock(startNode);
749 Node *stayInsideBlock = startBlock;
750
751 Node *node = startNode;
752 int offset = p.offset();
753
754 Node *n = startNode;
755 while (n) {
756 if (n->isContentEditable() != startNode->isContentEditable())
757 break;
758 RenderObject *r = n->renderer();
759 if (!r) {
760 n = n->traverseNextNode(stayInsideBlock);
761 continue;
762 }
763 RenderStyle *style = r->style();
764 if (style->visibility() != VISIBLE) {
765 n = n->traverseNextNode(stayInsideBlock);
766 continue;
767 }
768
769 if (r->isBR() || isBlock(n))
770 break;
771
772 // FIXME: We avoid returning a position where the renderer can't accept the caret.
773 // We should probably do this in other cases such as startOfParagraph.
774 if (r->isText() && r->caretMaxRenderedOffset() > 0) {
775 int length = toRenderText(r)->textLength();
776 if (style->preserveNewline()) {
777 const UChar* chars = toRenderText(r)->characters();
778 int o = n == startNode ? offset : 0;
779 for (int i = o; i < length; ++i)
780 if (chars[i] == '\n')
781 return VisiblePosition(n, i, DOWNSTREAM);
782 }
783 node = n;
784 offset = r->caretMaxOffset();
785 n = n->traverseNextNode(stayInsideBlock);
786 } else if (editingIgnoresContent(n) || isTableElement(n)) {
787 node = n;
788 offset = maxDeepOffset(n);
789 n = n->traverseNextSibling(stayInsideBlock);
790 } else
791 n = n->traverseNextNode(stayInsideBlock);
792 }
793
794 return VisiblePosition(node, offset, DOWNSTREAM);
795 }
796
startOfNextParagraph(const VisiblePosition & visiblePosition)797 VisiblePosition startOfNextParagraph(const VisiblePosition& visiblePosition)
798 {
799 VisiblePosition paragraphEnd(endOfParagraph(visiblePosition));
800 VisiblePosition afterParagraphEnd(paragraphEnd.next(true));
801 // The position after the last position in the last cell of a table
802 // is not the start of the next paragraph.
803 if (isFirstPositionAfterTable(afterParagraphEnd))
804 return afterParagraphEnd.next(true);
805 return afterParagraphEnd;
806 }
807
inSameParagraph(const VisiblePosition & a,const VisiblePosition & b)808 bool inSameParagraph(const VisiblePosition &a, const VisiblePosition &b)
809 {
810 return a.isNotNull() && startOfParagraph(a) == startOfParagraph(b);
811 }
812
isStartOfParagraph(const VisiblePosition & pos)813 bool isStartOfParagraph(const VisiblePosition &pos)
814 {
815 return pos.isNotNull() && pos == startOfParagraph(pos);
816 }
817
isEndOfParagraph(const VisiblePosition & pos)818 bool isEndOfParagraph(const VisiblePosition &pos)
819 {
820 return pos.isNotNull() && pos == endOfParagraph(pos);
821 }
822
previousParagraphPosition(const VisiblePosition & p,int x)823 VisiblePosition previousParagraphPosition(const VisiblePosition &p, int x)
824 {
825 VisiblePosition pos = p;
826 do {
827 VisiblePosition n = previousLinePosition(pos, x);
828 if (n.isNull() || n == pos)
829 return p;
830 pos = n;
831 } while (inSameParagraph(p, pos));
832 return pos;
833 }
834
nextParagraphPosition(const VisiblePosition & p,int x)835 VisiblePosition nextParagraphPosition(const VisiblePosition &p, int x)
836 {
837 VisiblePosition pos = p;
838 do {
839 VisiblePosition n = nextLinePosition(pos, x);
840 if (n.isNull() || n == pos)
841 return p;
842 pos = n;
843 } while (inSameParagraph(p, pos));
844 return pos;
845 }
846
847 // ---------
848
startOfBlock(const VisiblePosition & c)849 VisiblePosition startOfBlock(const VisiblePosition &c)
850 {
851 Position p = c.deepEquivalent();
852 Node *startNode = p.node();
853 if (!startNode)
854 return VisiblePosition();
855 return VisiblePosition(Position(startNode->enclosingBlockFlowElement(), 0), DOWNSTREAM);
856 }
857
endOfBlock(const VisiblePosition & c)858 VisiblePosition endOfBlock(const VisiblePosition &c)
859 {
860 Position p = c.deepEquivalent();
861
862 Node *startNode = p.node();
863 if (!startNode)
864 return VisiblePosition();
865
866 Node *startBlock = startNode->enclosingBlockFlowElement();
867
868 return VisiblePosition(startBlock, startBlock->childNodeCount(), VP_DEFAULT_AFFINITY);
869 }
870
inSameBlock(const VisiblePosition & a,const VisiblePosition & b)871 bool inSameBlock(const VisiblePosition &a, const VisiblePosition &b)
872 {
873 return !a.isNull() && enclosingBlockFlowElement(a) == enclosingBlockFlowElement(b);
874 }
875
isStartOfBlock(const VisiblePosition & pos)876 bool isStartOfBlock(const VisiblePosition &pos)
877 {
878 return pos.isNotNull() && pos == startOfBlock(pos);
879 }
880
isEndOfBlock(const VisiblePosition & pos)881 bool isEndOfBlock(const VisiblePosition &pos)
882 {
883 return pos.isNotNull() && pos == endOfBlock(pos);
884 }
885
886 // ---------
887
startOfDocument(const Node * node)888 VisiblePosition startOfDocument(const Node* node)
889 {
890 if (!node)
891 return VisiblePosition();
892
893 return VisiblePosition(node->document()->documentElement(), 0, DOWNSTREAM);
894 }
895
startOfDocument(const VisiblePosition & c)896 VisiblePosition startOfDocument(const VisiblePosition &c)
897 {
898 return startOfDocument(c.deepEquivalent().node());
899 }
900
endOfDocument(const Node * node)901 VisiblePosition endOfDocument(const Node* node)
902 {
903 if (!node || !node->document())
904 return VisiblePosition();
905
906 Element* doc = node->document()->documentElement();
907 return VisiblePosition(doc, doc->childNodeCount(), DOWNSTREAM);
908 }
909
endOfDocument(const VisiblePosition & c)910 VisiblePosition endOfDocument(const VisiblePosition &c)
911 {
912 return endOfDocument(c.deepEquivalent().node());
913 }
914
inSameDocument(const VisiblePosition & a,const VisiblePosition & b)915 bool inSameDocument(const VisiblePosition &a, const VisiblePosition &b)
916 {
917 Position ap = a.deepEquivalent();
918 Node *an = ap.node();
919 if (!an)
920 return false;
921 Position bp = b.deepEquivalent();
922 Node *bn = bp.node();
923 if (an == bn)
924 return true;
925
926 return an->document() == bn->document();
927 }
928
isStartOfDocument(const VisiblePosition & p)929 bool isStartOfDocument(const VisiblePosition &p)
930 {
931 return p.isNotNull() && p.previous().isNull();
932 }
933
isEndOfDocument(const VisiblePosition & p)934 bool isEndOfDocument(const VisiblePosition &p)
935 {
936 return p.isNotNull() && p.next().isNull();
937 }
938
939 // ---------
940
startOfEditableContent(const VisiblePosition & visiblePosition)941 VisiblePosition startOfEditableContent(const VisiblePosition& visiblePosition)
942 {
943 Node* highestRoot = highestEditableRoot(visiblePosition.deepEquivalent());
944 if (!highestRoot)
945 return VisiblePosition();
946
947 return VisiblePosition(highestRoot, 0, DOWNSTREAM);
948 }
949
endOfEditableContent(const VisiblePosition & visiblePosition)950 VisiblePosition endOfEditableContent(const VisiblePosition& visiblePosition)
951 {
952 Node* highestRoot = highestEditableRoot(visiblePosition.deepEquivalent());
953 if (!highestRoot)
954 return VisiblePosition();
955
956 return VisiblePosition(highestRoot, maxDeepOffset(highestRoot), DOWNSTREAM);
957 }
958
959 }
960