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