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