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