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