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