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