1 /*
2 * Copyright (C) 2004, 2005, 2006, 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 "core/dom/Position.h"
28
29 #include <stdio.h>
30 #include "HTMLNames.h"
31 #include "core/css/CSSComputedStyleDeclaration.h"
32 #include "core/dom/PositionIterator.h"
33 #include "core/dom/Text.h"
34 #include "core/editing/TextIterator.h"
35 #include "core/editing/VisiblePosition.h"
36 #include "core/editing/VisibleUnits.h"
37 #include "core/editing/htmlediting.h"
38 #include "core/html/HTMLHtmlElement.h"
39 #include "core/html/HTMLTableElement.h"
40 #include "core/frame/Frame.h"
41 #include "core/frame/Settings.h"
42 #include "platform/Logging.h"
43 #include "core/rendering/InlineIterator.h"
44 #include "core/rendering/InlineTextBox.h"
45 #include "core/rendering/RenderBlock.h"
46 #include "core/rendering/RenderInline.h"
47 #include "core/rendering/RenderText.h"
48 #include "wtf/text/CString.h"
49 #include "wtf/unicode/CharacterNames.h"
50
51 namespace WebCore {
52
53 using namespace HTMLNames;
54
nextRenderedEditable(Node * node)55 static Node* nextRenderedEditable(Node* node)
56 {
57 while ((node = node->nextLeafNode())) {
58 RenderObject* renderer = node->renderer();
59 if (!renderer)
60 continue;
61 if (!node->rendererIsEditable())
62 continue;
63 if ((renderer->isBox() && toRenderBox(renderer)->inlineBoxWrapper()) || (renderer->isText() && toRenderText(renderer)->firstTextBox()))
64 return node;
65 }
66 return 0;
67 }
68
previousRenderedEditable(Node * node)69 static Node* previousRenderedEditable(Node* node)
70 {
71 while ((node = node->previousLeafNode())) {
72 RenderObject* renderer = node->renderer();
73 if (!renderer)
74 continue;
75 if (!node->rendererIsEditable())
76 continue;
77 if ((renderer->isBox() && toRenderBox(renderer)->inlineBoxWrapper()) || (renderer->isText() && toRenderText(renderer)->firstTextBox()))
78 return node;
79 }
80 return 0;
81 }
82
Position(PassRefPtr<Node> anchorNode,LegacyEditingOffset offset)83 Position::Position(PassRefPtr<Node> anchorNode, LegacyEditingOffset offset)
84 : m_anchorNode(anchorNode)
85 , m_offset(offset.value())
86 , m_anchorType(anchorTypeForLegacyEditingPosition(m_anchorNode.get(), m_offset))
87 , m_isLegacyEditingPosition(true)
88 {
89 ASSERT(!m_anchorNode || !m_anchorNode->isPseudoElement());
90 }
91
Position(PassRefPtr<Node> anchorNode,AnchorType anchorType)92 Position::Position(PassRefPtr<Node> anchorNode, AnchorType anchorType)
93 : m_anchorNode(anchorNode)
94 , m_offset(0)
95 , m_anchorType(anchorType)
96 , m_isLegacyEditingPosition(false)
97 {
98 ASSERT(!m_anchorNode || !m_anchorNode->isPseudoElement());
99
100 ASSERT(anchorType != PositionIsOffsetInAnchor);
101 ASSERT(!((anchorType == PositionIsBeforeChildren || anchorType == PositionIsAfterChildren)
102 && (m_anchorNode->isTextNode() || editingIgnoresContent(m_anchorNode.get()))));
103 }
104
Position(PassRefPtr<Node> anchorNode,int offset,AnchorType anchorType)105 Position::Position(PassRefPtr<Node> anchorNode, int offset, AnchorType anchorType)
106 : m_anchorNode(anchorNode)
107 , m_offset(offset)
108 , m_anchorType(anchorType)
109 , m_isLegacyEditingPosition(false)
110 {
111 ASSERT(!m_anchorNode || !m_anchorNode->isPseudoElement());
112
113 ASSERT(anchorType == PositionIsOffsetInAnchor);
114 }
115
Position(PassRefPtr<Text> textNode,unsigned offset)116 Position::Position(PassRefPtr<Text> textNode, unsigned offset)
117 : m_anchorNode(textNode)
118 , m_offset(static_cast<int>(offset))
119 , m_anchorType(PositionIsOffsetInAnchor)
120 , m_isLegacyEditingPosition(false)
121 {
122 ASSERT(m_anchorNode);
123 }
124
moveToPosition(PassRefPtr<Node> node,int offset)125 void Position::moveToPosition(PassRefPtr<Node> node, int offset)
126 {
127 ASSERT(!editingIgnoresContent(node.get()));
128 ASSERT(anchorType() == PositionIsOffsetInAnchor || m_isLegacyEditingPosition);
129 m_anchorNode = node;
130 m_offset = offset;
131 if (m_isLegacyEditingPosition)
132 m_anchorType = anchorTypeForLegacyEditingPosition(m_anchorNode.get(), m_offset);
133 }
moveToOffset(int offset)134 void Position::moveToOffset(int offset)
135 {
136 ASSERT(anchorType() == PositionIsOffsetInAnchor || m_isLegacyEditingPosition);
137 m_offset = offset;
138 if (m_isLegacyEditingPosition)
139 m_anchorType = anchorTypeForLegacyEditingPosition(m_anchorNode.get(), m_offset);
140 }
141
containerNode() const142 Node* Position::containerNode() const
143 {
144 if (!m_anchorNode)
145 return 0;
146
147 switch (anchorType()) {
148 case PositionIsBeforeChildren:
149 case PositionIsAfterChildren:
150 case PositionIsOffsetInAnchor:
151 return m_anchorNode.get();
152 case PositionIsBeforeAnchor:
153 case PositionIsAfterAnchor:
154 return m_anchorNode->parentNode();
155 }
156 ASSERT_NOT_REACHED();
157 return 0;
158 }
159
containerText() const160 Text* Position::containerText() const
161 {
162 switch (anchorType()) {
163 case PositionIsOffsetInAnchor:
164 return m_anchorNode && m_anchorNode->isTextNode() ? toText(m_anchorNode) : 0;
165 case PositionIsBeforeAnchor:
166 case PositionIsAfterAnchor:
167 return 0;
168 case PositionIsBeforeChildren:
169 case PositionIsAfterChildren:
170 ASSERT(!m_anchorNode || !m_anchorNode->isTextNode());
171 return 0;
172 }
173 ASSERT_NOT_REACHED();
174 return 0;
175 }
176
computeOffsetInContainerNode() const177 int Position::computeOffsetInContainerNode() const
178 {
179 if (!m_anchorNode)
180 return 0;
181
182 switch (anchorType()) {
183 case PositionIsBeforeChildren:
184 return 0;
185 case PositionIsAfterChildren:
186 return lastOffsetInNode(m_anchorNode.get());
187 case PositionIsOffsetInAnchor:
188 return minOffsetForNode(m_anchorNode.get(), m_offset);
189 case PositionIsBeforeAnchor:
190 return m_anchorNode->nodeIndex();
191 case PositionIsAfterAnchor:
192 return m_anchorNode->nodeIndex() + 1;
193 }
194 ASSERT_NOT_REACHED();
195 return 0;
196 }
197
offsetForPositionAfterAnchor() const198 int Position::offsetForPositionAfterAnchor() const
199 {
200 ASSERT(m_anchorType == PositionIsAfterAnchor || m_anchorType == PositionIsAfterChildren);
201 ASSERT(!m_isLegacyEditingPosition);
202 return lastOffsetForEditing(m_anchorNode.get());
203 }
204
205 // Neighbor-anchored positions are invalid DOM positions, so they need to be
206 // fixed up before handing them off to the Range object.
parentAnchoredEquivalent() const207 Position Position::parentAnchoredEquivalent() const
208 {
209 if (!m_anchorNode)
210 return Position();
211
212 // FIXME: This should only be necessary for legacy positions, but is also needed for positions before and after Tables
213 if (m_offset <= 0 && (m_anchorType != PositionIsAfterAnchor && m_anchorType != PositionIsAfterChildren)) {
214 if (m_anchorNode->parentNode() && (editingIgnoresContent(m_anchorNode.get()) || isTableElement(m_anchorNode.get())))
215 return positionInParentBeforeNode(m_anchorNode.get());
216 return Position(m_anchorNode.get(), 0, PositionIsOffsetInAnchor);
217 }
218 if (!m_anchorNode->offsetInCharacters()
219 && (m_anchorType == PositionIsAfterAnchor || m_anchorType == PositionIsAfterChildren || static_cast<unsigned>(m_offset) == m_anchorNode->childNodeCount())
220 && (editingIgnoresContent(m_anchorNode.get()) || isTableElement(m_anchorNode.get()))
221 && containerNode()) {
222 return positionInParentAfterNode(m_anchorNode.get());
223 }
224
225 return Position(containerNode(), computeOffsetInContainerNode(), PositionIsOffsetInAnchor);
226 }
227
computeNodeBeforePosition() const228 Node* Position::computeNodeBeforePosition() const
229 {
230 if (!m_anchorNode)
231 return 0;
232
233 switch (anchorType()) {
234 case PositionIsBeforeChildren:
235 return 0;
236 case PositionIsAfterChildren:
237 return m_anchorNode->lastChild();
238 case PositionIsOffsetInAnchor:
239 return m_anchorNode->childNode(m_offset - 1); // -1 converts to childNode((unsigned)-1) and returns null.
240 case PositionIsBeforeAnchor:
241 return m_anchorNode->previousSibling();
242 case PositionIsAfterAnchor:
243 return m_anchorNode.get();
244 }
245 ASSERT_NOT_REACHED();
246 return 0;
247 }
248
computeNodeAfterPosition() const249 Node* Position::computeNodeAfterPosition() const
250 {
251 if (!m_anchorNode)
252 return 0;
253
254 switch (anchorType()) {
255 case PositionIsBeforeChildren:
256 return m_anchorNode->firstChild();
257 case PositionIsAfterChildren:
258 return 0;
259 case PositionIsOffsetInAnchor:
260 return m_anchorNode->childNode(m_offset);
261 case PositionIsBeforeAnchor:
262 return m_anchorNode.get();
263 case PositionIsAfterAnchor:
264 return m_anchorNode->nextSibling();
265 }
266 ASSERT_NOT_REACHED();
267 return 0;
268 }
269
anchorTypeForLegacyEditingPosition(Node * anchorNode,int offset)270 Position::AnchorType Position::anchorTypeForLegacyEditingPosition(Node* anchorNode, int offset)
271 {
272 if (anchorNode && editingIgnoresContent(anchorNode)) {
273 if (offset == 0)
274 return Position::PositionIsBeforeAnchor;
275 return Position::PositionIsAfterAnchor;
276 }
277 return Position::PositionIsOffsetInAnchor;
278 }
279
280 // FIXME: This method is confusing (does it return anchorNode() or containerNode()?) and should be renamed or removed
element() const281 Element* Position::element() const
282 {
283 Node* n = anchorNode();
284 while (n && !n->isElementNode())
285 n = n->parentNode();
286 return toElement(n);
287 }
288
computedStyle() const289 PassRefPtr<CSSComputedStyleDeclaration> Position::computedStyle() const
290 {
291 Element* elem = element();
292 if (!elem)
293 return 0;
294 return CSSComputedStyleDeclaration::create(elem);
295 }
296
previous(PositionMoveType moveType) const297 Position Position::previous(PositionMoveType moveType) const
298 {
299 Node* node = deprecatedNode();
300 if (!node)
301 return *this;
302
303 int offset = deprecatedEditingOffset();
304 // FIXME: Negative offsets shouldn't be allowed. We should catch this earlier.
305 ASSERT(offset >= 0);
306
307 if (offset > 0) {
308 if (Node* child = node->childNode(offset - 1))
309 return lastPositionInOrAfterNode(child);
310
311 // There are two reasons child might be 0:
312 // 1) The node is node like a text node that is not an element, and therefore has no children.
313 // Going backward one character at a time is correct.
314 // 2) The old offset was a bogus offset like (<br>, 1), and there is no child.
315 // Going from 1 to 0 is correct.
316 switch (moveType) {
317 case CodePoint:
318 return createLegacyEditingPosition(node, offset - 1);
319 case Character:
320 return createLegacyEditingPosition(node, uncheckedPreviousOffset(node, offset));
321 case BackwardDeletion:
322 return createLegacyEditingPosition(node, uncheckedPreviousOffsetForBackwardDeletion(node, offset));
323 }
324 }
325
326 if (ContainerNode* parent = node->parentNode())
327 return createLegacyEditingPosition(parent, node->nodeIndex());
328 return *this;
329 }
330
next(PositionMoveType moveType) const331 Position Position::next(PositionMoveType moveType) const
332 {
333 ASSERT(moveType != BackwardDeletion);
334
335 Node* node = deprecatedNode();
336 if (!node)
337 return *this;
338
339 int offset = deprecatedEditingOffset();
340 // FIXME: Negative offsets shouldn't be allowed. We should catch this earlier.
341 ASSERT(offset >= 0);
342
343 if (Node* child = node->childNode(offset))
344 return firstPositionInOrBeforeNode(child);
345
346 if (!node->hasChildNodes() && offset < lastOffsetForEditing(node)) {
347 // There are two reasons child might be 0:
348 // 1) The node is node like a text node that is not an element, and therefore has no children.
349 // Going forward one character at a time is correct.
350 // 2) The new offset is a bogus offset like (<br>, 1), and there is no child.
351 // Going from 0 to 1 is correct.
352 return createLegacyEditingPosition(node, (moveType == Character) ? uncheckedNextOffset(node, offset) : offset + 1);
353 }
354
355 if (ContainerNode* parent = node->parentNode())
356 return createLegacyEditingPosition(parent, node->nodeIndex() + 1);
357 return *this;
358 }
359
uncheckedPreviousOffset(const Node * n,int current)360 int Position::uncheckedPreviousOffset(const Node* n, int current)
361 {
362 return n->renderer() ? n->renderer()->previousOffset(current) : current - 1;
363 }
364
uncheckedPreviousOffsetForBackwardDeletion(const Node * n,int current)365 int Position::uncheckedPreviousOffsetForBackwardDeletion(const Node* n, int current)
366 {
367 return n->renderer() ? n->renderer()->previousOffsetForBackwardDeletion(current) : current - 1;
368 }
369
uncheckedNextOffset(const Node * n,int current)370 int Position::uncheckedNextOffset(const Node* n, int current)
371 {
372 return n->renderer() ? n->renderer()->nextOffset(current) : current + 1;
373 }
374
atFirstEditingPositionForNode() const375 bool Position::atFirstEditingPositionForNode() const
376 {
377 if (isNull())
378 return true;
379 // FIXME: Position before anchor shouldn't be considered as at the first editing position for node
380 // since that position resides outside of the node.
381 switch (m_anchorType) {
382 case PositionIsOffsetInAnchor:
383 return m_offset <= 0;
384 case PositionIsBeforeChildren:
385 case PositionIsBeforeAnchor:
386 return true;
387 case PositionIsAfterChildren:
388 case PositionIsAfterAnchor:
389 return !lastOffsetForEditing(deprecatedNode());
390 }
391 ASSERT_NOT_REACHED();
392 return false;
393 }
394
atLastEditingPositionForNode() const395 bool Position::atLastEditingPositionForNode() const
396 {
397 if (isNull())
398 return true;
399 // FIXME: Position after anchor shouldn't be considered as at the first editing position for node
400 // since that position resides outside of the node.
401 return m_anchorType == PositionIsAfterAnchor || m_anchorType == PositionIsAfterChildren || m_offset >= lastOffsetForEditing(deprecatedNode());
402 }
403
404 // A position is considered at editing boundary if one of the following is true:
405 // 1. It is the first position in the node and the next visually equivalent position
406 // is non editable.
407 // 2. It is the last position in the node and the previous visually equivalent position
408 // is non editable.
409 // 3. It is an editable position and both the next and previous visually equivalent
410 // positions are both non editable.
atEditingBoundary() const411 bool Position::atEditingBoundary() const
412 {
413 Position nextPosition = downstream(CanCrossEditingBoundary);
414 if (atFirstEditingPositionForNode() && nextPosition.isNotNull() && !nextPosition.deprecatedNode()->rendererIsEditable())
415 return true;
416
417 Position prevPosition = upstream(CanCrossEditingBoundary);
418 if (atLastEditingPositionForNode() && prevPosition.isNotNull() && !prevPosition.deprecatedNode()->rendererIsEditable())
419 return true;
420
421 return nextPosition.isNotNull() && !nextPosition.deprecatedNode()->rendererIsEditable()
422 && prevPosition.isNotNull() && !prevPosition.deprecatedNode()->rendererIsEditable();
423 }
424
parentEditingBoundary() const425 Node* Position::parentEditingBoundary() const
426 {
427 if (!m_anchorNode)
428 return 0;
429
430 Node* documentElement = m_anchorNode->document().documentElement();
431 if (!documentElement)
432 return 0;
433
434 Node* boundary = m_anchorNode.get();
435 while (boundary != documentElement && boundary->nonShadowBoundaryParentNode() && m_anchorNode->rendererIsEditable() == boundary->parentNode()->rendererIsEditable())
436 boundary = boundary->nonShadowBoundaryParentNode();
437
438 return boundary;
439 }
440
441
atStartOfTree() const442 bool Position::atStartOfTree() const
443 {
444 if (isNull())
445 return true;
446 return !deprecatedNode()->parentNode() && m_offset <= 0;
447 }
448
atEndOfTree() const449 bool Position::atEndOfTree() const
450 {
451 if (isNull())
452 return true;
453 return !deprecatedNode()->parentNode() && m_offset >= lastOffsetForEditing(deprecatedNode());
454 }
455
renderedOffset() const456 int Position::renderedOffset() const
457 {
458 if (!deprecatedNode()->isTextNode())
459 return m_offset;
460
461 if (!deprecatedNode()->renderer())
462 return m_offset;
463
464 int result = 0;
465 RenderText* textRenderer = toRenderText(deprecatedNode()->renderer());
466 for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
467 int start = box->start();
468 int end = box->start() + box->len();
469 if (m_offset < start)
470 return result;
471 if (m_offset <= end) {
472 result += m_offset - start;
473 return result;
474 }
475 result += box->len();
476 }
477 return result;
478 }
479
480 // return first preceding DOM position rendered at a different location, or "this"
previousCharacterPosition(EAffinity affinity) const481 Position Position::previousCharacterPosition(EAffinity affinity) const
482 {
483 if (isNull())
484 return Position();
485
486 Node* fromRootEditableElement = deprecatedNode()->rootEditableElement();
487
488 bool atStartOfLine = isStartOfLine(VisiblePosition(*this, affinity));
489 bool rendered = isCandidate();
490
491 Position currentPos = *this;
492 while (!currentPos.atStartOfTree()) {
493 currentPos = currentPos.previous();
494
495 if (currentPos.deprecatedNode()->rootEditableElement() != fromRootEditableElement)
496 return *this;
497
498 if (atStartOfLine || !rendered) {
499 if (currentPos.isCandidate())
500 return currentPos;
501 } else if (rendersInDifferentPosition(currentPos))
502 return currentPos;
503 }
504
505 return *this;
506 }
507
508 // return first following position rendered at a different location, or "this"
nextCharacterPosition(EAffinity affinity) const509 Position Position::nextCharacterPosition(EAffinity affinity) const
510 {
511 if (isNull())
512 return Position();
513
514 Node* fromRootEditableElement = deprecatedNode()->rootEditableElement();
515
516 bool atEndOfLine = isEndOfLine(VisiblePosition(*this, affinity));
517 bool rendered = isCandidate();
518
519 Position currentPos = *this;
520 while (!currentPos.atEndOfTree()) {
521 currentPos = currentPos.next();
522
523 if (currentPos.deprecatedNode()->rootEditableElement() != fromRootEditableElement)
524 return *this;
525
526 if (atEndOfLine || !rendered) {
527 if (currentPos.isCandidate())
528 return currentPos;
529 } else if (rendersInDifferentPosition(currentPos))
530 return currentPos;
531 }
532
533 return *this;
534 }
535
536 // Whether or not [node, 0] and [node, lastOffsetForEditing(node)] are their own VisiblePositions.
537 // If true, adjacent candidates are visually distinct.
538 // FIXME: Disregard nodes with renderers that have no height, as we do in isCandidate.
539 // FIXME: Share code with isCandidate, if possible.
endsOfNodeAreVisuallyDistinctPositions(Node * node)540 static bool endsOfNodeAreVisuallyDistinctPositions(Node* node)
541 {
542 if (!node || !node->renderer())
543 return false;
544
545 if (!node->renderer()->isInline())
546 return true;
547
548 // Don't include inline tables.
549 if (isHTMLTableElement(node))
550 return false;
551
552 // There is a VisiblePosition inside an empty inline-block container.
553 return node->renderer()->isReplaced() && canHaveChildrenForEditing(node) && toRenderBox(node->renderer())->height() != 0 && !node->firstChild();
554 }
555
enclosingVisualBoundary(Node * node)556 static Node* enclosingVisualBoundary(Node* node)
557 {
558 while (node && !endsOfNodeAreVisuallyDistinctPositions(node))
559 node = node->parentNode();
560
561 return node;
562 }
563
564 // upstream() and downstream() want to return positions that are either in a
565 // text node or at just before a non-text node. This method checks for that.
isStreamer(const PositionIterator & pos)566 static bool isStreamer(const PositionIterator& pos)
567 {
568 if (!pos.node())
569 return true;
570
571 if (isAtomicNode(pos.node()))
572 return true;
573
574 return pos.atStartOfNode();
575 }
576
577 // This function and downstream() are used for moving back and forth between visually equivalent candidates.
578 // For example, for the text node "foo bar" where whitespace is collapsible, there are two candidates
579 // that map to the VisiblePosition between 'b' and the space. This function will return the left candidate
580 // and downstream() will return the right one.
581 // Also, upstream() will return [boundary, 0] for any of the positions from [boundary, 0] to the first candidate
582 // in boundary, where endsOfNodeAreVisuallyDistinctPositions(boundary) is true.
upstream(EditingBoundaryCrossingRule rule) const583 Position Position::upstream(EditingBoundaryCrossingRule rule) const
584 {
585 Node* startNode = deprecatedNode();
586 if (!startNode)
587 return Position();
588
589 // iterate backward from there, looking for a qualified position
590 Node* boundary = enclosingVisualBoundary(startNode);
591 // FIXME: PositionIterator should respect Before and After positions.
592 PositionIterator lastVisible = m_anchorType == PositionIsAfterAnchor ? createLegacyEditingPosition(m_anchorNode.get(), caretMaxOffset(m_anchorNode.get())) : *this;
593 PositionIterator currentPos = lastVisible;
594 bool startEditable = startNode->rendererIsEditable();
595 Node* lastNode = startNode;
596 bool boundaryCrossed = false;
597 for (; !currentPos.atStart(); currentPos.decrement()) {
598 Node* currentNode = currentPos.node();
599
600 // Don't check for an editability change if we haven't moved to a different node,
601 // to avoid the expense of computing rendererIsEditable().
602 if (currentNode != lastNode) {
603 // Don't change editability.
604 bool currentEditable = currentNode->rendererIsEditable();
605 if (startEditable != currentEditable) {
606 if (rule == CannotCrossEditingBoundary)
607 break;
608 boundaryCrossed = true;
609 }
610 lastNode = currentNode;
611 }
612
613 // If we've moved to a position that is visually distinct, return the last saved position. There
614 // is code below that terminates early if we're *about* to move to a visually distinct position.
615 if (endsOfNodeAreVisuallyDistinctPositions(currentNode) && currentNode != boundary)
616 return lastVisible;
617
618 // skip position in unrendered or invisible node
619 RenderObject* renderer = currentNode->renderer();
620 if (!renderer || renderer->style()->visibility() != VISIBLE)
621 continue;
622
623 if (rule == CanCrossEditingBoundary && boundaryCrossed) {
624 lastVisible = currentPos;
625 break;
626 }
627
628 // track last visible streamer position
629 if (isStreamer(currentPos))
630 lastVisible = currentPos;
631
632 // Don't move past a position that is visually distinct. We could rely on code above to terminate and
633 // return lastVisible on the next iteration, but we terminate early to avoid doing a nodeIndex() call.
634 if (endsOfNodeAreVisuallyDistinctPositions(currentNode) && currentPos.atStartOfNode())
635 return lastVisible;
636
637 // Return position after tables and nodes which have content that can be ignored.
638 if (editingIgnoresContent(currentNode) || isTableElement(currentNode)) {
639 if (currentPos.atEndOfNode())
640 return positionAfterNode(currentNode);
641 continue;
642 }
643
644 // return current position if it is in rendered text
645 if (renderer->isText() && toRenderText(renderer)->firstTextBox()) {
646 if (currentNode != startNode) {
647 // This assertion fires in layout tests in the case-transform.html test because
648 // of a mix-up between offsets in the text in the DOM tree with text in the
649 // render tree which can have a different length due to case transformation.
650 // Until we resolve that, disable this so we can run the layout tests!
651 //ASSERT(currentOffset >= renderer->caretMaxOffset());
652 return createLegacyEditingPosition(currentNode, renderer->caretMaxOffset());
653 }
654
655 unsigned textOffset = currentPos.offsetInLeafNode();
656 RenderText* textRenderer = toRenderText(renderer);
657 InlineTextBox* lastTextBox = textRenderer->lastTextBox();
658 for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
659 if (textOffset <= box->start() + box->len()) {
660 if (textOffset > box->start())
661 return currentPos;
662 continue;
663 }
664
665 if (box == lastTextBox || textOffset != box->start() + box->len() + 1)
666 continue;
667
668 // The text continues on the next line only if the last text box is not on this line and
669 // none of the boxes on this line have a larger start offset.
670
671 bool continuesOnNextLine = true;
672 InlineBox* otherBox = box;
673 while (continuesOnNextLine) {
674 otherBox = otherBox->nextLeafChild();
675 if (!otherBox)
676 break;
677 if (otherBox == lastTextBox || (otherBox->renderer() == textRenderer && toInlineTextBox(otherBox)->start() > textOffset))
678 continuesOnNextLine = false;
679 }
680
681 otherBox = box;
682 while (continuesOnNextLine) {
683 otherBox = otherBox->prevLeafChild();
684 if (!otherBox)
685 break;
686 if (otherBox == lastTextBox || (otherBox->renderer() == textRenderer && toInlineTextBox(otherBox)->start() > textOffset))
687 continuesOnNextLine = false;
688 }
689
690 if (continuesOnNextLine)
691 return currentPos;
692 }
693 }
694 }
695
696 return lastVisible;
697 }
698
699 // This function and upstream() are used for moving back and forth between visually equivalent candidates.
700 // For example, for the text node "foo bar" where whitespace is collapsible, there are two candidates
701 // that map to the VisiblePosition between 'b' and the space. This function will return the right candidate
702 // and upstream() will return the left one.
703 // Also, downstream() will return the last position in the last atomic node in boundary for all of the positions
704 // in boundary after the last candidate, where endsOfNodeAreVisuallyDistinctPositions(boundary).
705 // FIXME: This function should never be called when the line box tree is dirty. See https://bugs.webkit.org/show_bug.cgi?id=97264
downstream(EditingBoundaryCrossingRule rule) const706 Position Position::downstream(EditingBoundaryCrossingRule rule) const
707 {
708 Node* startNode = deprecatedNode();
709 if (!startNode)
710 return Position();
711
712 // iterate forward from there, looking for a qualified position
713 Node* boundary = enclosingVisualBoundary(startNode);
714 // FIXME: PositionIterator should respect Before and After positions.
715 PositionIterator lastVisible = m_anchorType == PositionIsAfterAnchor ? createLegacyEditingPosition(m_anchorNode.get(), caretMaxOffset(m_anchorNode.get())) : *this;
716 PositionIterator currentPos = lastVisible;
717 bool startEditable = startNode->rendererIsEditable();
718 Node* lastNode = startNode;
719 bool boundaryCrossed = false;
720 for (; !currentPos.atEnd(); currentPos.increment()) {
721 Node* currentNode = currentPos.node();
722
723 // Don't check for an editability change if we haven't moved to a different node,
724 // to avoid the expense of computing rendererIsEditable().
725 if (currentNode != lastNode) {
726 // Don't change editability.
727 bool currentEditable = currentNode->rendererIsEditable();
728 if (startEditable != currentEditable) {
729 if (rule == CannotCrossEditingBoundary)
730 break;
731 boundaryCrossed = true;
732 }
733
734 lastNode = currentNode;
735 }
736
737 // stop before going above the body, up into the head
738 // return the last visible streamer position
739 if (currentNode->hasTagName(bodyTag) && currentPos.atEndOfNode())
740 break;
741
742 // Do not move to a visually distinct position.
743 if (endsOfNodeAreVisuallyDistinctPositions(currentNode) && currentNode != boundary)
744 return lastVisible;
745 // Do not move past a visually disinct position.
746 // Note: The first position after the last in a node whose ends are visually distinct
747 // positions will be [boundary->parentNode(), originalBlock->nodeIndex() + 1].
748 if (boundary && boundary->parentNode() == currentNode)
749 return lastVisible;
750
751 // skip position in unrendered or invisible node
752 RenderObject* renderer = currentNode->renderer();
753 if (!renderer || renderer->style()->visibility() != VISIBLE)
754 continue;
755
756 if (rule == CanCrossEditingBoundary && boundaryCrossed) {
757 lastVisible = currentPos;
758 break;
759 }
760
761 // track last visible streamer position
762 if (isStreamer(currentPos))
763 lastVisible = currentPos;
764
765 // Return position before tables and nodes which have content that can be ignored.
766 if (editingIgnoresContent(currentNode) || isTableElement(currentNode)) {
767 if (currentPos.offsetInLeafNode() <= renderer->caretMinOffset())
768 return createLegacyEditingPosition(currentNode, renderer->caretMinOffset());
769 continue;
770 }
771
772 // return current position if it is in rendered text
773 if (renderer->isText() && toRenderText(renderer)->firstTextBox()) {
774 if (currentNode != startNode) {
775 ASSERT(currentPos.atStartOfNode());
776 return createLegacyEditingPosition(currentNode, renderer->caretMinOffset());
777 }
778
779 unsigned textOffset = currentPos.offsetInLeafNode();
780 RenderText* textRenderer = toRenderText(renderer);
781 InlineTextBox* lastTextBox = textRenderer->lastTextBox();
782 for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
783 if (textOffset <= box->end()) {
784 if (textOffset >= box->start())
785 return currentPos;
786 continue;
787 }
788
789 if (box == lastTextBox || textOffset != box->start() + box->len())
790 continue;
791
792 // The text continues on the next line only if the last text box is not on this line and
793 // none of the boxes on this line have a larger start offset.
794
795 bool continuesOnNextLine = true;
796 InlineBox* otherBox = box;
797 while (continuesOnNextLine) {
798 otherBox = otherBox->nextLeafChild();
799 if (!otherBox)
800 break;
801 if (otherBox == lastTextBox || (otherBox->renderer() == textRenderer && toInlineTextBox(otherBox)->start() >= textOffset))
802 continuesOnNextLine = false;
803 }
804
805 otherBox = box;
806 while (continuesOnNextLine) {
807 otherBox = otherBox->prevLeafChild();
808 if (!otherBox)
809 break;
810 if (otherBox == lastTextBox || (otherBox->renderer() == textRenderer && toInlineTextBox(otherBox)->start() >= textOffset))
811 continuesOnNextLine = false;
812 }
813
814 if (continuesOnNextLine)
815 return currentPos;
816 }
817 }
818 }
819
820 return lastVisible;
821 }
822
boundingBoxLogicalHeight(RenderObject * o,const IntRect & rect)823 static int boundingBoxLogicalHeight(RenderObject *o, const IntRect &rect)
824 {
825 return o->style()->isHorizontalWritingMode() ? rect.height() : rect.width();
826 }
827
hasRenderedNonAnonymousDescendantsWithHeight(RenderObject * renderer)828 bool Position::hasRenderedNonAnonymousDescendantsWithHeight(RenderObject* renderer)
829 {
830 RenderObject* stop = renderer->nextInPreOrderAfterChildren();
831 for (RenderObject *o = renderer->firstChild(); o && o != stop; o = o->nextInPreOrder())
832 if (o->nonPseudoNode()) {
833 if ((o->isText() && boundingBoxLogicalHeight(o, toRenderText(o)->linesBoundingBox()))
834 || (o->isBox() && toRenderBox(o)->pixelSnappedLogicalHeight())
835 || (o->isRenderInline() && isEmptyInline(o) && boundingBoxLogicalHeight(o, toRenderInline(o)->linesBoundingBox())))
836 return true;
837 }
838 return false;
839 }
840
nodeIsUserSelectNone(Node * node)841 bool Position::nodeIsUserSelectNone(Node* node)
842 {
843 return node && node->renderer() && !node->renderer()->isSelectable();
844 }
845
nodeIsUserSelectAll(const Node * node)846 bool Position::nodeIsUserSelectAll(const Node* node)
847 {
848 return RuntimeEnabledFeatures::userSelectAllEnabled() && node && node->renderer() && node->renderer()->style()->userSelect() == SELECT_ALL;
849 }
850
rootUserSelectAllForNode(Node * node)851 Node* Position::rootUserSelectAllForNode(Node* node)
852 {
853 if (!node || !nodeIsUserSelectAll(node))
854 return 0;
855 Node* parent = node->parentNode();
856 if (!parent)
857 return node;
858
859 Node* candidateRoot = node;
860 while (parent) {
861 if (!parent->renderer()) {
862 parent = parent->parentNode();
863 continue;
864 }
865 if (!nodeIsUserSelectAll(parent))
866 break;
867 candidateRoot = parent;
868 parent = candidateRoot->parentNode();
869 }
870 return candidateRoot;
871 }
872
isCandidate() const873 bool Position::isCandidate() const
874 {
875 if (isNull())
876 return false;
877
878 RenderObject* renderer = deprecatedNode()->renderer();
879 if (!renderer)
880 return false;
881
882 if (renderer->style()->visibility() != VISIBLE)
883 return false;
884
885 if (renderer->isBR())
886 // FIXME: The condition should be m_anchorType == PositionIsBeforeAnchor, but for now we still need to support legacy positions.
887 return !m_offset && m_anchorType != PositionIsAfterAnchor && !nodeIsUserSelectNone(deprecatedNode()->parentNode());
888
889 if (renderer->isText())
890 return !nodeIsUserSelectNone(deprecatedNode()) && inRenderedText();
891
892 if (isTableElement(deprecatedNode()) || editingIgnoresContent(deprecatedNode()))
893 return (atFirstEditingPositionForNode() || atLastEditingPositionForNode()) && !nodeIsUserSelectNone(deprecatedNode()->parentNode());
894
895 if (isHTMLHtmlElement(m_anchorNode.get()))
896 return false;
897
898 if (renderer->isRenderBlockFlow()) {
899 if (toRenderBlock(renderer)->logicalHeight() || m_anchorNode->hasTagName(bodyTag)) {
900 if (!Position::hasRenderedNonAnonymousDescendantsWithHeight(renderer))
901 return atFirstEditingPositionForNode() && !Position::nodeIsUserSelectNone(deprecatedNode());
902 return m_anchorNode->rendererIsEditable() && !Position::nodeIsUserSelectNone(deprecatedNode()) && atEditingBoundary();
903 }
904 } else {
905 Frame* frame = m_anchorNode->document().frame();
906 bool caretBrowsing = frame->settings() && frame->settings()->caretBrowsingEnabled();
907 return (caretBrowsing || m_anchorNode->rendererIsEditable()) && !Position::nodeIsUserSelectNone(deprecatedNode()) && atEditingBoundary();
908 }
909
910 return false;
911 }
912
inRenderedText() const913 bool Position::inRenderedText() const
914 {
915 if (isNull() || !deprecatedNode()->isTextNode())
916 return false;
917
918 RenderObject* renderer = deprecatedNode()->renderer();
919 if (!renderer)
920 return false;
921
922 RenderText *textRenderer = toRenderText(renderer);
923 for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
924 if (m_offset < static_cast<int>(box->start()) && !textRenderer->containsReversedText()) {
925 // The offset we're looking for is before this node
926 // this means the offset must be in content that is
927 // not rendered. Return false.
928 return false;
929 }
930 if (box->containsCaretOffset(m_offset))
931 // Return false for offsets inside composed characters.
932 return m_offset == 0 || m_offset == textRenderer->nextOffset(textRenderer->previousOffset(m_offset));
933 }
934
935 return false;
936 }
937
isRenderedCharacter() const938 bool Position::isRenderedCharacter() const
939 {
940 if (isNull() || !deprecatedNode()->isTextNode())
941 return false;
942
943 RenderObject* renderer = deprecatedNode()->renderer();
944 if (!renderer)
945 return false;
946
947 RenderText* textRenderer = toRenderText(renderer);
948 for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
949 if (m_offset < static_cast<int>(box->start()) && !textRenderer->containsReversedText()) {
950 // The offset we're looking for is before this node
951 // this means the offset must be in content that is
952 // not rendered. Return false.
953 return false;
954 }
955 if (m_offset >= static_cast<int>(box->start()) && m_offset < static_cast<int>(box->start() + box->len()))
956 return true;
957 }
958
959 return false;
960 }
961
rendersInDifferentPosition(const Position & pos) const962 bool Position::rendersInDifferentPosition(const Position &pos) const
963 {
964 if (isNull() || pos.isNull())
965 return false;
966
967 RenderObject* renderer = deprecatedNode()->renderer();
968 if (!renderer)
969 return false;
970
971 RenderObject* posRenderer = pos.deprecatedNode()->renderer();
972 if (!posRenderer)
973 return false;
974
975 if (renderer->style()->visibility() != VISIBLE ||
976 posRenderer->style()->visibility() != VISIBLE)
977 return false;
978
979 if (deprecatedNode() == pos.deprecatedNode()) {
980 if (deprecatedNode()->hasTagName(brTag))
981 return false;
982
983 if (m_offset == pos.deprecatedEditingOffset())
984 return false;
985
986 if (!deprecatedNode()->isTextNode() && !pos.deprecatedNode()->isTextNode()) {
987 if (m_offset != pos.deprecatedEditingOffset())
988 return true;
989 }
990 }
991
992 if (deprecatedNode()->hasTagName(brTag) && pos.isCandidate())
993 return true;
994
995 if (pos.deprecatedNode()->hasTagName(brTag) && isCandidate())
996 return true;
997
998 if (deprecatedNode()->enclosingBlockFlowElement() != pos.deprecatedNode()->enclosingBlockFlowElement())
999 return true;
1000
1001 if (deprecatedNode()->isTextNode() && !inRenderedText())
1002 return false;
1003
1004 if (pos.deprecatedNode()->isTextNode() && !pos.inRenderedText())
1005 return false;
1006
1007 int thisRenderedOffset = renderedOffset();
1008 int posRenderedOffset = pos.renderedOffset();
1009
1010 if (renderer == posRenderer && thisRenderedOffset == posRenderedOffset)
1011 return false;
1012
1013 int ignoredCaretOffset;
1014 InlineBox* b1;
1015 getInlineBoxAndOffset(DOWNSTREAM, b1, ignoredCaretOffset);
1016 InlineBox* b2;
1017 pos.getInlineBoxAndOffset(DOWNSTREAM, b2, ignoredCaretOffset);
1018
1019 WTF_LOG(Editing, "renderer: %p [%p]\n", renderer, b1);
1020 WTF_LOG(Editing, "thisRenderedOffset: %d\n", thisRenderedOffset);
1021 WTF_LOG(Editing, "posRenderer: %p [%p]\n", posRenderer, b2);
1022 WTF_LOG(Editing, "posRenderedOffset: %d\n", posRenderedOffset);
1023 WTF_LOG(Editing, "node min/max: %d:%d\n", caretMinOffset(deprecatedNode()), caretMaxOffset(deprecatedNode()));
1024 WTF_LOG(Editing, "pos node min/max: %d:%d\n", caretMinOffset(pos.deprecatedNode()), caretMaxOffset(pos.deprecatedNode()));
1025 WTF_LOG(Editing, "----------------------------------------------------------------------\n");
1026
1027 if (!b1 || !b2) {
1028 return false;
1029 }
1030
1031 if (b1->root() != b2->root()) {
1032 return true;
1033 }
1034
1035 if (nextRenderedEditable(deprecatedNode()) == pos.deprecatedNode()
1036 && thisRenderedOffset == caretMaxOffset(deprecatedNode()) && !posRenderedOffset) {
1037 return false;
1038 }
1039
1040 if (previousRenderedEditable(deprecatedNode()) == pos.deprecatedNode()
1041 && !thisRenderedOffset && posRenderedOffset == caretMaxOffset(pos.deprecatedNode())) {
1042 return false;
1043 }
1044
1045 return true;
1046 }
1047
1048 // This assumes that it starts in editable content.
leadingWhitespacePosition(EAffinity affinity,bool considerNonCollapsibleWhitespace) const1049 Position Position::leadingWhitespacePosition(EAffinity affinity, bool considerNonCollapsibleWhitespace) const
1050 {
1051 ASSERT(isEditablePosition(*this));
1052 if (isNull())
1053 return Position();
1054
1055 if (upstream().deprecatedNode()->hasTagName(brTag))
1056 return Position();
1057
1058 Position prev = previousCharacterPosition(affinity);
1059 if (prev != *this && prev.deprecatedNode()->inSameContainingBlockFlowElement(deprecatedNode()) && prev.deprecatedNode()->isTextNode()) {
1060 String string = toText(prev.deprecatedNode())->data();
1061 UChar c = string[prev.deprecatedEditingOffset()];
1062 if (considerNonCollapsibleWhitespace ? (isSpaceOrNewline(c) || c == noBreakSpace) : isCollapsibleWhitespace(c))
1063 if (isEditablePosition(prev))
1064 return prev;
1065 }
1066
1067 return Position();
1068 }
1069
1070 // This assumes that it starts in editable content.
trailingWhitespacePosition(EAffinity,bool considerNonCollapsibleWhitespace) const1071 Position Position::trailingWhitespacePosition(EAffinity, bool considerNonCollapsibleWhitespace) const
1072 {
1073 ASSERT(isEditablePosition(*this));
1074 if (isNull())
1075 return Position();
1076
1077 VisiblePosition v(*this);
1078 UChar c = v.characterAfter();
1079 // The space must not be in another paragraph and it must be editable.
1080 if (!isEndOfParagraph(v) && v.next(CannotCrossEditingBoundary).isNotNull())
1081 if (considerNonCollapsibleWhitespace ? (isSpaceOrNewline(c) || c == noBreakSpace) : isCollapsibleWhitespace(c))
1082 return *this;
1083
1084 return Position();
1085 }
1086
getInlineBoxAndOffset(EAffinity affinity,InlineBox * & inlineBox,int & caretOffset) const1087 void Position::getInlineBoxAndOffset(EAffinity affinity, InlineBox*& inlineBox, int& caretOffset) const
1088 {
1089 getInlineBoxAndOffset(affinity, primaryDirection(), inlineBox, caretOffset);
1090 }
1091
isNonTextLeafChild(RenderObject * object)1092 static bool isNonTextLeafChild(RenderObject* object)
1093 {
1094 if (object->firstChild())
1095 return false;
1096 if (object->isText())
1097 return false;
1098 return true;
1099 }
1100
searchAheadForBetterMatch(RenderObject * renderer)1101 static InlineTextBox* searchAheadForBetterMatch(RenderObject* renderer)
1102 {
1103 RenderBlock* container = renderer->containingBlock();
1104 RenderObject* next = renderer;
1105 while ((next = next->nextInPreOrder(container))) {
1106 if (next->isRenderBlock())
1107 return 0;
1108 if (next->isBR())
1109 return 0;
1110 if (isNonTextLeafChild(next))
1111 return 0;
1112 if (next->isText()) {
1113 InlineTextBox* match = 0;
1114 int minOffset = INT_MAX;
1115 for (InlineTextBox* box = toRenderText(next)->firstTextBox(); box; box = box->nextTextBox()) {
1116 int caretMinOffset = box->caretMinOffset();
1117 if (caretMinOffset < minOffset) {
1118 match = box;
1119 minOffset = caretMinOffset;
1120 }
1121 }
1122 if (match)
1123 return match;
1124 }
1125 }
1126 return 0;
1127 }
1128
downstreamIgnoringEditingBoundaries(Position position)1129 static Position downstreamIgnoringEditingBoundaries(Position position)
1130 {
1131 Position lastPosition;
1132 while (position != lastPosition) {
1133 lastPosition = position;
1134 position = position.downstream(CanCrossEditingBoundary);
1135 }
1136 return position;
1137 }
1138
upstreamIgnoringEditingBoundaries(Position position)1139 static Position upstreamIgnoringEditingBoundaries(Position position)
1140 {
1141 Position lastPosition;
1142 while (position != lastPosition) {
1143 lastPosition = position;
1144 position = position.upstream(CanCrossEditingBoundary);
1145 }
1146 return position;
1147 }
1148
getInlineBoxAndOffset(EAffinity affinity,TextDirection primaryDirection,InlineBox * & inlineBox,int & caretOffset) const1149 void Position::getInlineBoxAndOffset(EAffinity affinity, TextDirection primaryDirection, InlineBox*& inlineBox, int& caretOffset) const
1150 {
1151 caretOffset = deprecatedEditingOffset();
1152 RenderObject* renderer = deprecatedNode()->renderer();
1153
1154 if (!renderer->isText()) {
1155 inlineBox = 0;
1156 if (canHaveChildrenForEditing(deprecatedNode()) && renderer->isRenderBlockFlow() && hasRenderedNonAnonymousDescendantsWithHeight(renderer)) {
1157 // Try a visually equivalent position with possibly opposite editability. This helps in case |this| is in
1158 // an editable block but surrounded by non-editable positions. It acts to negate the logic at the beginning
1159 // of RenderObject::createVisiblePosition().
1160 Position equivalent = downstreamIgnoringEditingBoundaries(*this);
1161 if (equivalent == *this) {
1162 equivalent = upstreamIgnoringEditingBoundaries(*this);
1163 if (equivalent == *this || downstreamIgnoringEditingBoundaries(equivalent) == *this)
1164 return;
1165 }
1166
1167 equivalent.getInlineBoxAndOffset(UPSTREAM, primaryDirection, inlineBox, caretOffset);
1168 return;
1169 }
1170 if (renderer->isBox()) {
1171 inlineBox = toRenderBox(renderer)->inlineBoxWrapper();
1172 if (!inlineBox || (caretOffset > inlineBox->caretMinOffset() && caretOffset < inlineBox->caretMaxOffset()))
1173 return;
1174 }
1175 } else {
1176 RenderText* textRenderer = toRenderText(renderer);
1177
1178 InlineTextBox* box;
1179 InlineTextBox* candidate = 0;
1180
1181 for (box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
1182 int caretMinOffset = box->caretMinOffset();
1183 int caretMaxOffset = box->caretMaxOffset();
1184
1185 if (caretOffset < caretMinOffset || caretOffset > caretMaxOffset || (caretOffset == caretMaxOffset && box->isLineBreak()))
1186 continue;
1187
1188 if (caretOffset > caretMinOffset && caretOffset < caretMaxOffset) {
1189 inlineBox = box;
1190 return;
1191 }
1192
1193 if (((caretOffset == caretMaxOffset) ^ (affinity == DOWNSTREAM))
1194 || ((caretOffset == caretMinOffset) ^ (affinity == UPSTREAM))
1195 || (caretOffset == caretMaxOffset && box->nextLeafChild() && box->nextLeafChild()->isLineBreak()))
1196 break;
1197
1198 candidate = box;
1199 }
1200 if (candidate && candidate == textRenderer->lastTextBox() && affinity == DOWNSTREAM) {
1201 box = searchAheadForBetterMatch(textRenderer);
1202 if (box)
1203 caretOffset = box->caretMinOffset();
1204 }
1205 inlineBox = box ? box : candidate;
1206 }
1207
1208 if (!inlineBox)
1209 return;
1210
1211 unsigned char level = inlineBox->bidiLevel();
1212
1213 if (inlineBox->direction() == primaryDirection) {
1214 if (caretOffset == inlineBox->caretRightmostOffset()) {
1215 InlineBox* nextBox = inlineBox->nextLeafChild();
1216 if (!nextBox || nextBox->bidiLevel() >= level)
1217 return;
1218
1219 level = nextBox->bidiLevel();
1220 InlineBox* prevBox = inlineBox;
1221 do {
1222 prevBox = prevBox->prevLeafChild();
1223 } while (prevBox && prevBox->bidiLevel() > level);
1224
1225 if (prevBox && prevBox->bidiLevel() == level) // For example, abc FED 123 ^ CBA
1226 return;
1227
1228 // For example, abc 123 ^ CBA
1229 while (InlineBox* nextBox = inlineBox->nextLeafChild()) {
1230 if (nextBox->bidiLevel() < level)
1231 break;
1232 inlineBox = nextBox;
1233 }
1234 caretOffset = inlineBox->caretRightmostOffset();
1235 } else {
1236 InlineBox* prevBox = inlineBox->prevLeafChild();
1237 if (!prevBox || prevBox->bidiLevel() >= level)
1238 return;
1239
1240 level = prevBox->bidiLevel();
1241 InlineBox* nextBox = inlineBox;
1242 do {
1243 nextBox = nextBox->nextLeafChild();
1244 } while (nextBox && nextBox->bidiLevel() > level);
1245
1246 if (nextBox && nextBox->bidiLevel() == level)
1247 return;
1248
1249 while (InlineBox* prevBox = inlineBox->prevLeafChild()) {
1250 if (prevBox->bidiLevel() < level)
1251 break;
1252 inlineBox = prevBox;
1253 }
1254 caretOffset = inlineBox->caretLeftmostOffset();
1255 }
1256 return;
1257 }
1258
1259 if (caretOffset == inlineBox->caretLeftmostOffset()) {
1260 InlineBox* prevBox = inlineBox->prevLeafChildIgnoringLineBreak();
1261 if (!prevBox || prevBox->bidiLevel() < level) {
1262 // Left edge of a secondary run. Set to the right edge of the entire run.
1263 while (InlineBox* nextBox = inlineBox->nextLeafChildIgnoringLineBreak()) {
1264 if (nextBox->bidiLevel() < level)
1265 break;
1266 inlineBox = nextBox;
1267 }
1268 caretOffset = inlineBox->caretRightmostOffset();
1269 } else if (prevBox->bidiLevel() > level) {
1270 // Right edge of a "tertiary" run. Set to the left edge of that run.
1271 while (InlineBox* tertiaryBox = inlineBox->prevLeafChildIgnoringLineBreak()) {
1272 if (tertiaryBox->bidiLevel() <= level)
1273 break;
1274 inlineBox = tertiaryBox;
1275 }
1276 caretOffset = inlineBox->caretLeftmostOffset();
1277 }
1278 } else {
1279 InlineBox* nextBox = inlineBox->nextLeafChildIgnoringLineBreak();
1280 if (!nextBox || nextBox->bidiLevel() < level) {
1281 // Right edge of a secondary run. Set to the left edge of the entire run.
1282 while (InlineBox* prevBox = inlineBox->prevLeafChildIgnoringLineBreak()) {
1283 if (prevBox->bidiLevel() < level)
1284 break;
1285 inlineBox = prevBox;
1286 }
1287 caretOffset = inlineBox->caretLeftmostOffset();
1288 } else if (nextBox->bidiLevel() > level) {
1289 // Left edge of a "tertiary" run. Set to the right edge of that run.
1290 while (InlineBox* tertiaryBox = inlineBox->nextLeafChildIgnoringLineBreak()) {
1291 if (tertiaryBox->bidiLevel() <= level)
1292 break;
1293 inlineBox = tertiaryBox;
1294 }
1295 caretOffset = inlineBox->caretRightmostOffset();
1296 }
1297 }
1298 }
1299
primaryDirection() const1300 TextDirection Position::primaryDirection() const
1301 {
1302 TextDirection primaryDirection = LTR;
1303 for (const RenderObject* r = m_anchorNode->renderer(); r; r = r->parent()) {
1304 if (r->isRenderBlockFlow()) {
1305 primaryDirection = r->style()->direction();
1306 break;
1307 }
1308 }
1309
1310 return primaryDirection;
1311 }
1312
1313
debugPosition(const char * msg) const1314 void Position::debugPosition(const char* msg) const
1315 {
1316 if (isNull())
1317 fprintf(stderr, "Position [%s]: null\n", msg);
1318 else
1319 fprintf(stderr, "Position [%s]: %s [%p] at %d\n", msg, deprecatedNode()->nodeName().utf8().data(), deprecatedNode(), m_offset);
1320 }
1321
1322 #ifndef NDEBUG
1323
formatForDebugger(char * buffer,unsigned length) const1324 void Position::formatForDebugger(char* buffer, unsigned length) const
1325 {
1326 StringBuilder result;
1327
1328 if (isNull())
1329 result.appendLiteral("<null>");
1330 else {
1331 char s[1024];
1332 result.appendLiteral("offset ");
1333 result.appendNumber(m_offset);
1334 result.appendLiteral(" of ");
1335 deprecatedNode()->formatForDebugger(s, sizeof(s));
1336 result.append(s);
1337 }
1338
1339 strncpy(buffer, result.toString().utf8().data(), length - 1);
1340 }
1341
showAnchorTypeAndOffset() const1342 void Position::showAnchorTypeAndOffset() const
1343 {
1344 if (m_isLegacyEditingPosition)
1345 fputs("legacy, ", stderr);
1346 switch (anchorType()) {
1347 case PositionIsOffsetInAnchor:
1348 fputs("offset", stderr);
1349 break;
1350 case PositionIsBeforeChildren:
1351 fputs("beforeChildren", stderr);
1352 break;
1353 case PositionIsAfterChildren:
1354 fputs("afterChildren", stderr);
1355 break;
1356 case PositionIsBeforeAnchor:
1357 fputs("before", stderr);
1358 break;
1359 case PositionIsAfterAnchor:
1360 fputs("after", stderr);
1361 break;
1362 }
1363 fprintf(stderr, ", offset:%d\n", m_offset);
1364 }
1365
showTreeForThis() const1366 void Position::showTreeForThis() const
1367 {
1368 if (anchorNode()) {
1369 anchorNode()->showTreeForThis();
1370 showAnchorTypeAndOffset();
1371 }
1372 }
1373
1374 #endif
1375
1376
1377
1378 } // namespace WebCore
1379
1380 #ifndef NDEBUG
1381
showTree(const WebCore::Position & pos)1382 void showTree(const WebCore::Position& pos)
1383 {
1384 pos.showTreeForThis();
1385 }
1386
showTree(const WebCore::Position * pos)1387 void showTree(const WebCore::Position* pos)
1388 {
1389 if (pos)
1390 pos->showTreeForThis();
1391 }
1392
1393 #endif
1394