1 /* 2 * Copyright (C) 2004, 2005, 2006, 2007 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/editing/htmlediting.h" 28 29 #include "bindings/core/v8/ExceptionState.h" 30 #include "bindings/core/v8/ExceptionStatePlaceholder.h" 31 #include "core/HTMLElementFactory.h" 32 #include "core/HTMLNames.h" 33 #include "core/dom/Document.h" 34 #include "core/dom/NodeTraversal.h" 35 #include "core/dom/PositionIterator.h" 36 #include "core/dom/Range.h" 37 #include "core/dom/Text.h" 38 #include "core/dom/shadow/ShadowRoot.h" 39 #include "core/editing/Editor.h" 40 #include "core/editing/HTMLInterchange.h" 41 #include "core/editing/PlainTextRange.h" 42 #include "core/editing/TextIterator.h" 43 #include "core/editing/VisiblePosition.h" 44 #include "core/editing/VisibleSelection.h" 45 #include "core/editing/VisibleUnits.h" 46 #include "core/frame/LocalFrame.h" 47 #include "core/frame/UseCounter.h" 48 #include "core/html/HTMLBRElement.h" 49 #include "core/html/HTMLDivElement.h" 50 #include "core/html/HTMLLIElement.h" 51 #include "core/html/HTMLOListElement.h" 52 #include "core/html/HTMLParagraphElement.h" 53 #include "core/html/HTMLSpanElement.h" 54 #include "core/html/HTMLTableCellElement.h" 55 #include "core/html/HTMLUListElement.h" 56 #include "core/rendering/RenderObject.h" 57 #include "core/rendering/RenderTableCell.h" 58 #include "wtf/Assertions.h" 59 #include "wtf/StdLibExtras.h" 60 #include "wtf/text/StringBuilder.h" 61 62 namespace blink { 63 64 using namespace HTMLNames; 65 66 // Atomic means that the node has no children, or has children which are ignored for the 67 // purposes of editing. 68 bool isAtomicNode(const Node *node) 69 { 70 return node && (!node->hasChildren() || editingIgnoresContent(node)); 71 } 72 73 // Compare two positions, taking into account the possibility that one or both 74 // could be inside a shadow tree. Only works for non-null values. 75 int comparePositions(const Position& a, const Position& b) 76 { 77 ASSERT(a.isNotNull()); 78 ASSERT(b.isNotNull()); 79 TreeScope* commonScope = commonTreeScope(a.containerNode(), b.containerNode()); 80 81 ASSERT(commonScope); 82 if (!commonScope) 83 return 0; 84 85 Node* nodeA = commonScope->ancestorInThisScope(a.containerNode()); 86 ASSERT(nodeA); 87 bool hasDescendentA = nodeA != a.containerNode(); 88 int offsetA = hasDescendentA ? 0 : a.computeOffsetInContainerNode(); 89 90 Node* nodeB = commonScope->ancestorInThisScope(b.containerNode()); 91 ASSERT(nodeB); 92 bool hasDescendentB = nodeB != b.containerNode(); 93 int offsetB = hasDescendentB ? 0 : b.computeOffsetInContainerNode(); 94 95 int bias = 0; 96 if (nodeA == nodeB) { 97 if (hasDescendentA) 98 bias = -1; 99 else if (hasDescendentB) 100 bias = 1; 101 } 102 103 int result = Range::compareBoundaryPoints(nodeA, offsetA, nodeB, offsetB, IGNORE_EXCEPTION); 104 return result ? result : bias; 105 } 106 107 int comparePositions(const PositionWithAffinity& a, const PositionWithAffinity& b) 108 { 109 return comparePositions(a.position(), b.position()); 110 } 111 112 int comparePositions(const VisiblePosition& a, const VisiblePosition& b) 113 { 114 return comparePositions(a.deepEquivalent(), b.deepEquivalent()); 115 } 116 117 ContainerNode* highestEditableRoot(const Position& position, EditableType editableType) 118 { 119 if (position.isNull()) 120 return 0; 121 122 ContainerNode* highestRoot = editableRootForPosition(position, editableType); 123 if (!highestRoot) 124 return 0; 125 126 if (isHTMLBodyElement(*highestRoot)) 127 return highestRoot; 128 129 ContainerNode* node = highestRoot->parentNode(); 130 while (node) { 131 if (node->hasEditableStyle(editableType)) 132 highestRoot = node; 133 if (isHTMLBodyElement(*node)) 134 break; 135 node = node->parentNode(); 136 } 137 138 return highestRoot; 139 } 140 141 Element* lowestEditableAncestor(Node* node) 142 { 143 while (node) { 144 if (node->hasEditableStyle()) 145 return node->rootEditableElement(); 146 if (isHTMLBodyElement(*node)) 147 break; 148 node = node->parentNode(); 149 } 150 151 return 0; 152 } 153 154 bool isEditablePosition(const Position& p, EditableType editableType, EUpdateStyle updateStyle) 155 { 156 Node* node = p.parentAnchoredEquivalent().anchorNode(); 157 if (!node) 158 return false; 159 if (updateStyle == UpdateStyle) 160 node->document().updateLayoutIgnorePendingStylesheets(); 161 else 162 ASSERT(updateStyle == DoNotUpdateStyle); 163 164 if (isRenderedHTMLTableElement(node)) 165 node = node->parentNode(); 166 167 return node->hasEditableStyle(editableType); 168 } 169 170 bool isAtUnsplittableElement(const Position& pos) 171 { 172 Node* node = pos.deprecatedNode(); 173 return (node == editableRootForPosition(pos) || node == enclosingNodeOfType(pos, &isTableCell)); 174 } 175 176 177 bool isRichlyEditablePosition(const Position& p, EditableType editableType) 178 { 179 Node* node = p.deprecatedNode(); 180 if (!node) 181 return false; 182 183 if (isRenderedHTMLTableElement(node)) 184 node = node->parentNode(); 185 186 return node->rendererIsRichlyEditable(editableType); 187 } 188 189 Element* editableRootForPosition(const Position& p, EditableType editableType) 190 { 191 Node* node = p.containerNode(); 192 if (!node) 193 return 0; 194 195 if (isRenderedHTMLTableElement(node)) 196 node = node->parentNode(); 197 198 return node->rootEditableElement(editableType); 199 } 200 201 // Finds the enclosing element until which the tree can be split. 202 // When a user hits ENTER, he/she won't expect this element to be split into two. 203 // You may pass it as the second argument of splitTreeToNode. 204 Element* unsplittableElementForPosition(const Position& p) 205 { 206 // Since enclosingNodeOfType won't search beyond the highest root editable node, 207 // this code works even if the closest table cell was outside of the root editable node. 208 Element* enclosingCell = toElement(enclosingNodeOfType(p, &isTableCell)); 209 if (enclosingCell) 210 return enclosingCell; 211 212 return editableRootForPosition(p); 213 } 214 215 Position nextCandidate(const Position& position) 216 { 217 PositionIterator p = position; 218 while (!p.atEnd()) { 219 p.increment(); 220 if (p.isCandidate()) 221 return p; 222 } 223 return Position(); 224 } 225 226 Position nextVisuallyDistinctCandidate(const Position& position) 227 { 228 Position p = position; 229 Position downstreamStart = p.downstream(); 230 while (!p.atEndOfTree()) { 231 p = p.next(Character); 232 if (p.isCandidate() && p.downstream() != downstreamStart) 233 return p; 234 } 235 return Position(); 236 } 237 238 Position previousCandidate(const Position& position) 239 { 240 PositionIterator p = position; 241 while (!p.atStart()) { 242 p.decrement(); 243 if (p.isCandidate()) 244 return p; 245 } 246 return Position(); 247 } 248 249 Position previousVisuallyDistinctCandidate(const Position& position) 250 { 251 Position p = position; 252 Position downstreamStart = p.downstream(); 253 while (!p.atStartOfTree()) { 254 p = p.previous(Character); 255 if (p.isCandidate() && p.downstream() != downstreamStart) 256 return p; 257 } 258 return Position(); 259 } 260 261 VisiblePosition firstEditableVisiblePositionAfterPositionInRoot(const Position& position, ContainerNode* highestRoot) 262 { 263 // position falls before highestRoot. 264 if (comparePositions(position, firstPositionInNode(highestRoot)) == -1 && highestRoot->hasEditableStyle()) 265 return VisiblePosition(firstPositionInNode(highestRoot)); 266 267 Position editablePosition = position; 268 269 if (position.deprecatedNode()->treeScope() != highestRoot->treeScope()) { 270 Node* shadowAncestor = highestRoot->treeScope().ancestorInThisScope(editablePosition.deprecatedNode()); 271 if (!shadowAncestor) 272 return VisiblePosition(); 273 274 editablePosition = positionAfterNode(shadowAncestor); 275 } 276 277 while (editablePosition.deprecatedNode() && !isEditablePosition(editablePosition) && editablePosition.deprecatedNode()->isDescendantOf(highestRoot)) 278 editablePosition = isAtomicNode(editablePosition.deprecatedNode()) ? positionInParentAfterNode(*editablePosition.deprecatedNode()) : nextVisuallyDistinctCandidate(editablePosition); 279 280 if (editablePosition.deprecatedNode() && editablePosition.deprecatedNode() != highestRoot && !editablePosition.deprecatedNode()->isDescendantOf(highestRoot)) 281 return VisiblePosition(); 282 283 return VisiblePosition(editablePosition); 284 } 285 286 VisiblePosition lastEditableVisiblePositionBeforePositionInRoot(const Position& position, ContainerNode* highestRoot) 287 { 288 return VisiblePosition(lastEditablePositionBeforePositionInRoot(position, highestRoot)); 289 } 290 291 Position lastEditablePositionBeforePositionInRoot(const Position& position, Node* highestRoot) 292 { 293 // When position falls after highestRoot, the result is easy to compute. 294 if (comparePositions(position, lastPositionInNode(highestRoot)) == 1) 295 return lastPositionInNode(highestRoot); 296 297 Position editablePosition = position; 298 299 if (position.deprecatedNode()->treeScope() != highestRoot->treeScope()) { 300 Node* shadowAncestor = highestRoot->treeScope().ancestorInThisScope(editablePosition.deprecatedNode()); 301 if (!shadowAncestor) 302 return Position(); 303 304 editablePosition = firstPositionInOrBeforeNode(shadowAncestor); 305 } 306 307 while (editablePosition.deprecatedNode() && !isEditablePosition(editablePosition) && editablePosition.deprecatedNode()->isDescendantOf(highestRoot)) 308 editablePosition = isAtomicNode(editablePosition.deprecatedNode()) ? positionInParentBeforeNode(*editablePosition.deprecatedNode()) : previousVisuallyDistinctCandidate(editablePosition); 309 310 if (editablePosition.deprecatedNode() && editablePosition.deprecatedNode() != highestRoot && !editablePosition.deprecatedNode()->isDescendantOf(highestRoot)) 311 return Position(); 312 return editablePosition; 313 } 314 315 // FIXME: The method name, comment, and code say three different things here! 316 // Whether or not content before and after this node will collapse onto the same line as it. 317 bool isBlock(const Node* node) 318 { 319 return node && node->renderer() && !node->renderer()->isInline() && !node->renderer()->isRubyText(); 320 } 321 322 bool isInline(const Node* node) 323 { 324 return node && node->renderer() && node->renderer()->isInline(); 325 } 326 327 // FIXME: Deploy this in all of the places where enclosingBlockFlow/enclosingBlockFlowOrTableElement are used. 328 // FIXME: Pass a position to this function. The enclosing block of [table, x] for example, should be the 329 // block that contains the table and not the table, and this function should be the only one responsible for 330 // knowing about these kinds of special cases. 331 Element* enclosingBlock(Node* node, EditingBoundaryCrossingRule rule) 332 { 333 Node* enclosingNode = enclosingNodeOfType(firstPositionInOrBeforeNode(node), isBlock, rule); 334 return enclosingNode && enclosingNode->isElementNode() ? toElement(enclosingNode) : 0; 335 } 336 337 Element* enclosingBlockFlowElement(Node& node) 338 { 339 if (isBlockFlowElement(node)) 340 return &toElement(node); 341 342 for (Node* n = node.parentNode(); n; n = n->parentNode()) { 343 if (isBlockFlowElement(*n) || isHTMLBodyElement(*n)) 344 return toElement(n); 345 } 346 return 0; 347 } 348 349 bool inSameContainingBlockFlowElement(Node* a, Node* b) 350 { 351 return a && b && enclosingBlockFlowElement(*a) == enclosingBlockFlowElement(*b); 352 } 353 354 TextDirection directionOfEnclosingBlock(const Position& position) 355 { 356 Element* enclosingBlockElement = enclosingBlock(position.containerNode()); 357 if (!enclosingBlockElement) 358 return LTR; 359 RenderObject* renderer = enclosingBlockElement->renderer(); 360 return renderer ? renderer->style()->direction() : LTR; 361 } 362 363 // This method is used to create positions in the DOM. It returns the maximum valid offset 364 // in a node. It returns 1 for some elements even though they do not have children, which 365 // creates technically invalid DOM Positions. Be sure to call parentAnchoredEquivalent 366 // on a Position before using it to create a DOM Range, or an exception will be thrown. 367 int lastOffsetForEditing(const Node* node) 368 { 369 ASSERT(node); 370 if (!node) 371 return 0; 372 if (node->offsetInCharacters()) 373 return node->maxCharacterOffset(); 374 375 if (node->hasChildren()) 376 return node->countChildren(); 377 378 // NOTE: This should preempt the childNodeCount for, e.g., select nodes 379 if (editingIgnoresContent(node)) 380 return 1; 381 382 return 0; 383 } 384 385 String stringWithRebalancedWhitespace(const String& string, bool startIsStartOfParagraph, bool endIsEndOfParagraph) 386 { 387 unsigned length = string.length(); 388 389 StringBuilder rebalancedString; 390 rebalancedString.reserveCapacity(length); 391 392 bool previousCharacterWasSpace = false; 393 for (size_t i = 0; i < length; i++) { 394 UChar c = string[i]; 395 if (!isWhitespace(c)) { 396 rebalancedString.append(c); 397 previousCharacterWasSpace = false; 398 continue; 399 } 400 401 if (previousCharacterWasSpace || (!i && startIsStartOfParagraph) || (i + 1 == length && endIsEndOfParagraph)) { 402 rebalancedString.append(noBreakSpace); 403 previousCharacterWasSpace = false; 404 } else { 405 rebalancedString.append(' '); 406 previousCharacterWasSpace = true; 407 } 408 } 409 410 ASSERT(rebalancedString.length() == length); 411 412 return rebalancedString.toString(); 413 } 414 415 bool isTableStructureNode(const Node *node) 416 { 417 RenderObject* renderer = node->renderer(); 418 return (renderer && (renderer->isTableCell() || renderer->isTableRow() || renderer->isTableSection() || renderer->isRenderTableCol())); 419 } 420 421 const String& nonBreakingSpaceString() 422 { 423 DEFINE_STATIC_LOCAL(String, nonBreakingSpaceString, (&noBreakSpace, 1)); 424 return nonBreakingSpaceString; 425 } 426 427 // FIXME: need to dump this 428 bool isSpecialHTMLElement(const Node* n) 429 { 430 if (!n) 431 return false; 432 433 if (!n->isHTMLElement()) 434 return false; 435 436 if (n->isLink()) 437 return true; 438 439 RenderObject* renderer = n->renderer(); 440 if (!renderer) 441 return false; 442 443 if (renderer->style()->display() == TABLE || renderer->style()->display() == INLINE_TABLE) 444 return true; 445 446 if (renderer->style()->isFloating()) 447 return true; 448 449 return false; 450 } 451 452 static HTMLElement* firstInSpecialElement(const Position& pos) 453 { 454 Element* rootEditableElement = pos.containerNode()->rootEditableElement(); 455 for (Node* n = pos.deprecatedNode(); n && n->rootEditableElement() == rootEditableElement; n = n->parentNode()) { 456 if (isSpecialHTMLElement(n)) { 457 HTMLElement* specialElement = toHTMLElement(n); 458 VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM); 459 VisiblePosition firstInElement = VisiblePosition(firstPositionInOrBeforeNode(specialElement), DOWNSTREAM); 460 if (isRenderedTableElement(specialElement) && vPos == firstInElement.next()) 461 return specialElement; 462 if (vPos == firstInElement) 463 return specialElement; 464 } 465 } 466 return 0; 467 } 468 469 static HTMLElement* lastInSpecialElement(const Position& pos) 470 { 471 Element* rootEditableElement = pos.containerNode()->rootEditableElement(); 472 for (Node* n = pos.deprecatedNode(); n && n->rootEditableElement() == rootEditableElement; n = n->parentNode()) { 473 if (isSpecialHTMLElement(n)) { 474 HTMLElement* specialElement = toHTMLElement(n); 475 VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM); 476 VisiblePosition lastInElement = VisiblePosition(lastPositionInOrAfterNode(specialElement), DOWNSTREAM); 477 if (isRenderedTableElement(specialElement) && vPos == lastInElement.previous()) 478 return specialElement; 479 if (vPos == lastInElement) 480 return specialElement; 481 } 482 } 483 return 0; 484 } 485 486 Position positionBeforeContainingSpecialElement(const Position& pos, HTMLElement** containingSpecialElement) 487 { 488 HTMLElement* n = firstInSpecialElement(pos); 489 if (!n) 490 return pos; 491 Position result = positionInParentBeforeNode(*n); 492 if (result.isNull() || result.deprecatedNode()->rootEditableElement() != pos.deprecatedNode()->rootEditableElement()) 493 return pos; 494 if (containingSpecialElement) 495 *containingSpecialElement = n; 496 return result; 497 } 498 499 Position positionAfterContainingSpecialElement(const Position& pos, HTMLElement** containingSpecialElement) 500 { 501 HTMLElement* n = lastInSpecialElement(pos); 502 if (!n) 503 return pos; 504 Position result = positionInParentAfterNode(*n); 505 if (result.isNull() || result.deprecatedNode()->rootEditableElement() != pos.deprecatedNode()->rootEditableElement()) 506 return pos; 507 if (containingSpecialElement) 508 *containingSpecialElement = n; 509 return result; 510 } 511 512 Element* isFirstPositionAfterTable(const VisiblePosition& visiblePosition) 513 { 514 Position upstream(visiblePosition.deepEquivalent().upstream()); 515 if (isRenderedTableElement(upstream.deprecatedNode()) && upstream.atLastEditingPositionForNode()) 516 return toElement(upstream.deprecatedNode()); 517 518 return 0; 519 } 520 521 Element* isLastPositionBeforeTable(const VisiblePosition& visiblePosition) 522 { 523 Position downstream(visiblePosition.deepEquivalent().downstream()); 524 if (isRenderedTableElement(downstream.deprecatedNode()) && downstream.atFirstEditingPositionForNode()) 525 return toElement(downstream.deprecatedNode()); 526 527 return 0; 528 } 529 530 // Returns the visible position at the beginning of a node 531 VisiblePosition visiblePositionBeforeNode(Node& node) 532 { 533 if (node.hasChildren()) 534 return VisiblePosition(firstPositionInOrBeforeNode(&node), DOWNSTREAM); 535 ASSERT(node.parentNode()); 536 ASSERT(!node.parentNode()->isShadowRoot()); 537 return VisiblePosition(positionInParentBeforeNode(node)); 538 } 539 540 // Returns the visible position at the ending of a node 541 VisiblePosition visiblePositionAfterNode(Node& node) 542 { 543 if (node.hasChildren()) 544 return VisiblePosition(lastPositionInOrAfterNode(&node), DOWNSTREAM); 545 ASSERT(node.parentNode()); 546 ASSERT(!node.parentNode()->isShadowRoot()); 547 return VisiblePosition(positionInParentAfterNode(node)); 548 } 549 550 // Create a range object with two visible positions, start and end. 551 // create(Document*, const Position&, const Position&); will use deprecatedEditingOffset 552 // Use this function instead of create a regular range object (avoiding editing offset). 553 PassRefPtrWillBeRawPtr<Range> createRange(Document& document, const VisiblePosition& start, const VisiblePosition& end, ExceptionState& exceptionState) 554 { 555 RefPtrWillBeRawPtr<Range> selectedRange = Range::create(document); 556 selectedRange->setStart(start.deepEquivalent().containerNode(), start.deepEquivalent().computeOffsetInContainerNode(), exceptionState); 557 if (!exceptionState.hadException()) 558 selectedRange->setEnd(end.deepEquivalent().containerNode(), end.deepEquivalent().computeOffsetInContainerNode(), exceptionState); 559 return selectedRange.release(); 560 } 561 562 bool isHTMLListElement(Node* n) 563 { 564 return (n && (isHTMLUListElement(*n) || isHTMLOListElement(*n) || isHTMLDListElement(*n))); 565 } 566 567 bool isListItem(const Node* n) 568 { 569 return n && n->renderer() && n->renderer()->isListItem(); 570 } 571 572 Element* enclosingElementWithTag(const Position& p, const QualifiedName& tagName) 573 { 574 if (p.isNull()) 575 return 0; 576 577 ContainerNode* root = highestEditableRoot(p); 578 Element* ancestor = p.deprecatedNode()->isElementNode() ? toElement(p.deprecatedNode()) : p.deprecatedNode()->parentElement(); 579 for (; ancestor; ancestor = ancestor->parentElement()) { 580 if (root && !ancestor->hasEditableStyle()) 581 continue; 582 if (ancestor->hasTagName(tagName)) 583 return ancestor; 584 if (ancestor == root) 585 return 0; 586 } 587 588 return 0; 589 } 590 591 Node* enclosingNodeOfType(const Position& p, bool (*nodeIsOfType)(const Node*), EditingBoundaryCrossingRule rule) 592 { 593 // FIXME: support CanSkipCrossEditingBoundary 594 ASSERT(rule == CanCrossEditingBoundary || rule == CannotCrossEditingBoundary); 595 if (p.isNull()) 596 return 0; 597 598 ContainerNode* root = rule == CannotCrossEditingBoundary ? highestEditableRoot(p) : 0; 599 for (Node* n = p.deprecatedNode(); n; n = n->parentNode()) { 600 // Don't return a non-editable node if the input position was editable, since 601 // the callers from editing will no doubt want to perform editing inside the returned node. 602 if (root && !n->hasEditableStyle()) 603 continue; 604 if (nodeIsOfType(n)) 605 return n; 606 if (n == root) 607 return 0; 608 } 609 610 return 0; 611 } 612 613 Node* highestEnclosingNodeOfType(const Position& p, bool (*nodeIsOfType)(const Node*), EditingBoundaryCrossingRule rule, Node* stayWithin) 614 { 615 Node* highest = 0; 616 ContainerNode* root = rule == CannotCrossEditingBoundary ? highestEditableRoot(p) : 0; 617 for (Node* n = p.containerNode(); n && n != stayWithin; n = n->parentNode()) { 618 if (root && !n->hasEditableStyle()) 619 continue; 620 if (nodeIsOfType(n)) 621 highest = n; 622 if (n == root) 623 break; 624 } 625 626 return highest; 627 } 628 629 static bool hasARenderedDescendant(Node* node, Node* excludedNode) 630 { 631 for (Node* n = node->firstChild(); n;) { 632 if (n == excludedNode) { 633 n = NodeTraversal::nextSkippingChildren(*n, node); 634 continue; 635 } 636 if (n->renderer()) 637 return true; 638 n = NodeTraversal::next(*n, node); 639 } 640 return false; 641 } 642 643 Node* highestNodeToRemoveInPruning(Node* node, Node* excludeNode) 644 { 645 Node* previousNode = 0; 646 Element* rootEditableElement = node ? node->rootEditableElement() : 0; 647 for (; node; node = node->parentNode()) { 648 if (RenderObject* renderer = node->renderer()) { 649 if (!renderer->canHaveChildren() || hasARenderedDescendant(node, previousNode) || rootEditableElement == node || excludeNode == node) 650 return previousNode; 651 } 652 previousNode = node; 653 } 654 return 0; 655 } 656 657 Element* enclosingTableCell(const Position& p) 658 { 659 return toElement(enclosingNodeOfType(p, isTableCell)); 660 } 661 662 Element* enclosingAnchorElement(const Position& p) 663 { 664 if (p.isNull()) 665 return 0; 666 667 for (Element* ancestor = ElementTraversal::firstAncestorOrSelf(*p.deprecatedNode()); ancestor; ancestor = ElementTraversal::firstAncestor(*ancestor)) { 668 if (ancestor->isLink()) 669 return ancestor; 670 } 671 return 0; 672 } 673 674 HTMLElement* enclosingList(Node* node) 675 { 676 if (!node) 677 return 0; 678 679 ContainerNode* root = highestEditableRoot(firstPositionInOrBeforeNode(node)); 680 681 for (ContainerNode* n = node->parentNode(); n; n = n->parentNode()) { 682 if (isHTMLUListElement(*n) || isHTMLOListElement(*n)) 683 return toHTMLElement(n); 684 if (n == root) 685 return 0; 686 } 687 688 return 0; 689 } 690 691 Node* enclosingListChild(Node *node) 692 { 693 if (!node) 694 return 0; 695 // Check for a list item element, or for a node whose parent is a list element. Such a node 696 // will appear visually as a list item (but without a list marker) 697 ContainerNode* root = highestEditableRoot(firstPositionInOrBeforeNode(node)); 698 699 // FIXME: This function is inappropriately named if it starts with node instead of node->parentNode() 700 for (Node* n = node; n && n->parentNode(); n = n->parentNode()) { 701 if (isHTMLLIElement(*n) || (isHTMLListElement(n->parentNode()) && n != root)) 702 return n; 703 if (n == root || isTableCell(n)) 704 return 0; 705 } 706 707 return 0; 708 } 709 710 // FIXME: This method should not need to call isStartOfParagraph/isEndOfParagraph 711 Node* enclosingEmptyListItem(const VisiblePosition& visiblePos) 712 { 713 // Check that position is on a line by itself inside a list item 714 Node* listChildNode = enclosingListChild(visiblePos.deepEquivalent().deprecatedNode()); 715 if (!listChildNode || !isStartOfParagraph(visiblePos) || !isEndOfParagraph(visiblePos)) 716 return 0; 717 718 VisiblePosition firstInListChild(firstPositionInOrBeforeNode(listChildNode)); 719 VisiblePosition lastInListChild(lastPositionInOrAfterNode(listChildNode)); 720 721 if (firstInListChild != visiblePos || lastInListChild != visiblePos) 722 return 0; 723 724 return listChildNode; 725 } 726 727 HTMLElement* outermostEnclosingList(Node* node, HTMLElement* rootList) 728 { 729 HTMLElement* list = enclosingList(node); 730 if (!list) 731 return 0; 732 733 while (HTMLElement* nextList = enclosingList(list)) { 734 if (nextList == rootList) 735 break; 736 list = nextList; 737 } 738 739 return list; 740 } 741 742 bool canMergeLists(Element* firstList, Element* secondList) 743 { 744 if (!firstList || !secondList || !firstList->isHTMLElement() || !secondList->isHTMLElement()) 745 return false; 746 747 return firstList->hasTagName(secondList->tagQName()) // make sure the list types match (ol vs. ul) 748 && firstList->hasEditableStyle() && secondList->hasEditableStyle() // both lists are editable 749 && firstList->rootEditableElement() == secondList->rootEditableElement() // don't cross editing boundaries 750 && isVisiblyAdjacent(positionInParentAfterNode(*firstList), positionInParentBeforeNode(*secondList)); 751 // Make sure there is no visible content between this li and the previous list 752 } 753 754 bool isRenderedHTMLTableElement(const Node* node) 755 { 756 return isHTMLTableElement(*node) && node->renderer(); 757 } 758 759 bool isRenderedTableElement(const Node* node) 760 { 761 if (!node || !node->isElementNode()) 762 return false; 763 764 RenderObject* renderer = node->renderer(); 765 return (renderer && renderer->isTable()); 766 } 767 768 bool isTableCell(const Node* node) 769 { 770 ASSERT(node); 771 RenderObject* r = node->renderer(); 772 return r ? r->isTableCell() : isHTMLTableCellElement(*node); 773 } 774 775 bool isEmptyTableCell(const Node* node) 776 { 777 // Returns true IFF the passed in node is one of: 778 // .) a table cell with no children, 779 // .) a table cell with a single BR child, and which has no other child renderers, including :before and :after renderers 780 // .) the BR child of such a table cell 781 782 // Find rendered node 783 while (node && !node->renderer()) 784 node = node->parentNode(); 785 if (!node) 786 return false; 787 788 // Make sure the rendered node is a table cell or <br>. 789 // If it's a <br>, then the parent node has to be a table cell. 790 RenderObject* renderer = node->renderer(); 791 if (renderer->isBR()) { 792 renderer = renderer->parent(); 793 if (!renderer) 794 return false; 795 } 796 if (!renderer->isTableCell()) 797 return false; 798 799 // Check that the table cell contains no child renderers except for perhaps a single <br>. 800 RenderObject* childRenderer = toRenderTableCell(renderer)->firstChild(); 801 if (!childRenderer) 802 return true; 803 if (!childRenderer->isBR()) 804 return false; 805 return !childRenderer->nextSibling(); 806 } 807 808 PassRefPtrWillBeRawPtr<HTMLElement> createDefaultParagraphElement(Document& document) 809 { 810 switch (document.frame()->editor().defaultParagraphSeparator()) { 811 case EditorParagraphSeparatorIsDiv: 812 return HTMLDivElement::create(document); 813 case EditorParagraphSeparatorIsP: 814 return HTMLParagraphElement::create(document); 815 } 816 817 ASSERT_NOT_REACHED(); 818 return nullptr; 819 } 820 821 PassRefPtrWillBeRawPtr<HTMLBRElement> createBreakElement(Document& document) 822 { 823 return HTMLBRElement::create(document); 824 } 825 826 PassRefPtrWillBeRawPtr<HTMLOListElement> createOrderedListElement(Document& document) 827 { 828 return HTMLOListElement::create(document); 829 } 830 831 PassRefPtrWillBeRawPtr<HTMLUListElement> createUnorderedListElement(Document& document) 832 { 833 return HTMLUListElement::create(document); 834 } 835 836 PassRefPtrWillBeRawPtr<HTMLLIElement> createListItemElement(Document& document) 837 { 838 return HTMLLIElement::create(document); 839 } 840 841 PassRefPtrWillBeRawPtr<HTMLElement> createHTMLElement(Document& document, const QualifiedName& name) 842 { 843 return createHTMLElement(document, name.localName()); 844 } 845 846 PassRefPtrWillBeRawPtr<HTMLElement> createHTMLElement(Document& document, const AtomicString& tagName) 847 { 848 return HTMLElementFactory::createHTMLElement(tagName, document, 0, false); 849 } 850 851 bool isTabHTMLSpanElement(const Node* node) 852 { 853 if (!isHTMLSpanElement(node) || toHTMLSpanElement(node)->getAttribute(classAttr) != AppleTabSpanClass) 854 return false; 855 UseCounter::count(node->document(), UseCounter::EditingAppleTabSpanClass); 856 return true; 857 } 858 859 bool isTabHTMLSpanElementTextNode(const Node* node) 860 { 861 return node && node->isTextNode() && node->parentNode() && isTabHTMLSpanElement(node->parentNode()); 862 } 863 864 HTMLSpanElement* tabSpanElement(const Node* node) 865 { 866 return isTabHTMLSpanElementTextNode(node) ? toHTMLSpanElement(node->parentNode()) : 0; 867 } 868 869 PassRefPtrWillBeRawPtr<HTMLSpanElement> createTabSpanElement(Document& document, PassRefPtrWillBeRawPtr<Text> prpTabTextNode) 870 { 871 RefPtrWillBeRawPtr<Text> tabTextNode = prpTabTextNode; 872 873 // Make the span to hold the tab. 874 RefPtrWillBeRawPtr<HTMLSpanElement> spanElement = toHTMLSpanElement(document.createElement(spanTag, false).get()); 875 spanElement->setAttribute(classAttr, AppleTabSpanClass); 876 spanElement->setAttribute(styleAttr, "white-space:pre"); 877 878 // Add tab text to that span. 879 if (!tabTextNode) 880 tabTextNode = document.createEditingTextNode("\t"); 881 882 spanElement->appendChild(tabTextNode.release()); 883 884 return spanElement.release(); 885 } 886 887 PassRefPtrWillBeRawPtr<HTMLSpanElement> createTabSpanElement(Document& document, const String& tabText) 888 { 889 return createTabSpanElement(document, document.createTextNode(tabText)); 890 } 891 892 PassRefPtrWillBeRawPtr<HTMLSpanElement> createTabSpanElement(Document& document) 893 { 894 return createTabSpanElement(document, PassRefPtrWillBeRawPtr<Text>(nullptr)); 895 } 896 897 PassRefPtrWillBeRawPtr<HTMLBRElement> createBlockPlaceholderElement(Document& document) 898 { 899 return toHTMLBRElement(document.createElement(brTag, false).get()); 900 } 901 902 bool isNodeRendered(const Node *node) 903 { 904 if (!node) 905 return false; 906 907 RenderObject* renderer = node->renderer(); 908 if (!renderer) 909 return false; 910 911 return renderer->style()->visibility() == VISIBLE; 912 } 913 914 // return first preceding DOM position rendered at a different location, or "this" 915 static Position previousCharacterPosition(const Position& position, EAffinity affinity) 916 { 917 if (position.isNull()) 918 return Position(); 919 920 Element* fromRootEditableElement = position.anchorNode()->rootEditableElement(); 921 922 bool atStartOfLine = isStartOfLine(VisiblePosition(position, affinity)); 923 bool rendered = position.isCandidate(); 924 925 Position currentPos = position; 926 while (!currentPos.atStartOfTree()) { 927 currentPos = currentPos.previous(); 928 929 if (currentPos.anchorNode()->rootEditableElement() != fromRootEditableElement) 930 return position; 931 932 if (atStartOfLine || !rendered) { 933 if (currentPos.isCandidate()) 934 return currentPos; 935 } else if (position.rendersInDifferentPosition(currentPos)) { 936 return currentPos; 937 } 938 } 939 940 return position; 941 } 942 943 // This assumes that it starts in editable content. 944 Position leadingWhitespacePosition(const Position& position, EAffinity affinity, WhitespacePositionOption option) 945 { 946 ASSERT(isEditablePosition(position, ContentIsEditable, DoNotUpdateStyle)); 947 if (position.isNull()) 948 return Position(); 949 950 if (isHTMLBRElement(*position.upstream().anchorNode())) 951 return Position(); 952 953 Position prev = previousCharacterPosition(position, affinity); 954 if (prev != position && inSameContainingBlockFlowElement(prev.anchorNode(), position.anchorNode()) && prev.anchorNode()->isTextNode()) { 955 String string = toText(prev.anchorNode())->data(); 956 UChar previousCharacter = string[prev.deprecatedEditingOffset()]; 957 bool isSpace = option == ConsiderNonCollapsibleWhitespace ? (isSpaceOrNewline(previousCharacter) || previousCharacter == noBreakSpace) : isCollapsibleWhitespace(previousCharacter); 958 if (isSpace && isEditablePosition(prev)) 959 return prev; 960 } 961 962 return Position(); 963 } 964 965 // This assumes that it starts in editable content. 966 Position trailingWhitespacePosition(const Position& position, EAffinity, WhitespacePositionOption option) 967 { 968 ASSERT(isEditablePosition(position, ContentIsEditable, DoNotUpdateStyle)); 969 if (position.isNull()) 970 return Position(); 971 972 VisiblePosition visiblePosition(position); 973 UChar characterAfterVisiblePosition = visiblePosition.characterAfter(); 974 bool isSpace = option == ConsiderNonCollapsibleWhitespace ? (isSpaceOrNewline(characterAfterVisiblePosition) || characterAfterVisiblePosition == noBreakSpace) : isCollapsibleWhitespace(characterAfterVisiblePosition); 975 // The space must not be in another paragraph and it must be editable. 976 if (isSpace && !isEndOfParagraph(visiblePosition) && visiblePosition.next(CannotCrossEditingBoundary).isNotNull()) 977 return position; 978 return Position(); 979 } 980 981 unsigned numEnclosingMailBlockquotes(const Position& p) 982 { 983 unsigned num = 0; 984 for (Node* n = p.deprecatedNode(); n; n = n->parentNode()) 985 if (isMailHTMLBlockquoteElement(n)) 986 num++; 987 988 return num; 989 } 990 991 void updatePositionForNodeRemoval(Position& position, Node& node) 992 { 993 if (position.isNull()) 994 return; 995 switch (position.anchorType()) { 996 case Position::PositionIsBeforeChildren: 997 if (position.containerNode() == node) 998 position = positionInParentBeforeNode(node); 999 break; 1000 case Position::PositionIsAfterChildren: 1001 if (position.containerNode() == node) 1002 position = positionInParentAfterNode(node); 1003 break; 1004 case Position::PositionIsOffsetInAnchor: 1005 if (position.containerNode() == node.parentNode() && static_cast<unsigned>(position.offsetInContainerNode()) > node.nodeIndex()) 1006 position.moveToOffset(position.offsetInContainerNode() - 1); 1007 else if (node.containsIncludingShadowDOM(position.containerNode())) 1008 position = positionInParentBeforeNode(node); 1009 break; 1010 case Position::PositionIsAfterAnchor: 1011 if (node.containsIncludingShadowDOM(position.anchorNode())) 1012 position = positionInParentAfterNode(node); 1013 break; 1014 case Position::PositionIsBeforeAnchor: 1015 if (node.containsIncludingShadowDOM(position.anchorNode())) 1016 position = positionInParentBeforeNode(node); 1017 break; 1018 } 1019 } 1020 1021 bool isMailHTMLBlockquoteElement(const Node* node) 1022 { 1023 if (!node || !node->isHTMLElement()) 1024 return false; 1025 1026 const HTMLElement& element = toHTMLElement(*node); 1027 return element.hasTagName(blockquoteTag) && element.getAttribute("type") == "cite"; 1028 } 1029 1030 int caretMinOffset(const Node* n) 1031 { 1032 RenderObject* r = n->renderer(); 1033 ASSERT(!n->isCharacterDataNode() || !r || r->isText()); // FIXME: This was a runtime check that seemingly couldn't fail; changed it to an assertion for now. 1034 return r ? r->caretMinOffset() : 0; 1035 } 1036 1037 // If a node can contain candidates for VisiblePositions, return the offset of the last candidate, otherwise 1038 // return the number of children for container nodes and the length for unrendered text nodes. 1039 int caretMaxOffset(const Node* n) 1040 { 1041 // For rendered text nodes, return the last position that a caret could occupy. 1042 if (n->isTextNode() && n->renderer()) 1043 return n->renderer()->caretMaxOffset(); 1044 // For containers return the number of children. For others do the same as above. 1045 return lastOffsetForEditing(n); 1046 } 1047 1048 bool lineBreakExistsAtVisiblePosition(const VisiblePosition& visiblePosition) 1049 { 1050 return lineBreakExistsAtPosition(visiblePosition.deepEquivalent().downstream()); 1051 } 1052 1053 bool lineBreakExistsAtPosition(const Position& position) 1054 { 1055 if (position.isNull()) 1056 return false; 1057 1058 if (isHTMLBRElement(*position.anchorNode()) && position.atFirstEditingPositionForNode()) 1059 return true; 1060 1061 if (!position.anchorNode()->renderer()) 1062 return false; 1063 1064 if (!position.anchorNode()->isTextNode() || !position.anchorNode()->renderer()->style()->preserveNewline()) 1065 return false; 1066 1067 Text* textNode = toText(position.anchorNode()); 1068 unsigned offset = position.offsetInContainerNode(); 1069 return offset < textNode->length() && textNode->data()[offset] == '\n'; 1070 } 1071 1072 // Modifies selections that have an end point at the edge of a table 1073 // that contains the other endpoint so that they don't confuse 1074 // code that iterates over selected paragraphs. 1075 VisibleSelection selectionForParagraphIteration(const VisibleSelection& original) 1076 { 1077 VisibleSelection newSelection(original); 1078 VisiblePosition startOfSelection(newSelection.visibleStart()); 1079 VisiblePosition endOfSelection(newSelection.visibleEnd()); 1080 1081 // If the end of the selection to modify is just after a table, and 1082 // if the start of the selection is inside that table, then the last paragraph 1083 // that we'll want modify is the last one inside the table, not the table itself 1084 // (a table is itself a paragraph). 1085 if (Element* table = isFirstPositionAfterTable(endOfSelection)) 1086 if (startOfSelection.deepEquivalent().deprecatedNode()->isDescendantOf(table)) 1087 newSelection = VisibleSelection(startOfSelection, endOfSelection.previous(CannotCrossEditingBoundary)); 1088 1089 // If the start of the selection to modify is just before a table, 1090 // and if the end of the selection is inside that table, then the first paragraph 1091 // we'll want to modify is the first one inside the table, not the paragraph 1092 // containing the table itself. 1093 if (Element* table = isLastPositionBeforeTable(startOfSelection)) 1094 if (endOfSelection.deepEquivalent().deprecatedNode()->isDescendantOf(table)) 1095 newSelection = VisibleSelection(startOfSelection.next(CannotCrossEditingBoundary), endOfSelection); 1096 1097 return newSelection; 1098 } 1099 1100 // FIXME: indexForVisiblePosition and visiblePositionForIndex use TextIterators to convert between 1101 // VisiblePositions and indices. But TextIterator iteration using TextIteratorEmitsCharactersBetweenAllVisiblePositions 1102 // does not exactly match VisiblePosition iteration, so using them to preserve a selection during an editing 1103 // opertion is unreliable. TextIterator's TextIteratorEmitsCharactersBetweenAllVisiblePositions mode needs to be fixed, 1104 // or these functions need to be changed to iterate using actual VisiblePositions. 1105 // FIXME: Deploy these functions everywhere that TextIterators are used to convert between VisiblePositions and indices. 1106 int indexForVisiblePosition(const VisiblePosition& visiblePosition, RefPtrWillBeRawPtr<ContainerNode>& scope) 1107 { 1108 if (visiblePosition.isNull()) 1109 return 0; 1110 1111 Position p(visiblePosition.deepEquivalent()); 1112 Document& document = *p.document(); 1113 ShadowRoot* shadowRoot = p.anchorNode()->containingShadowRoot(); 1114 1115 if (shadowRoot) 1116 scope = shadowRoot; 1117 else 1118 scope = document.documentElement(); 1119 1120 RefPtrWillBeRawPtr<Range> range = Range::create(document, firstPositionInNode(scope.get()), p.parentAnchoredEquivalent()); 1121 1122 return TextIterator::rangeLength(range.get(), true); 1123 } 1124 1125 VisiblePosition visiblePositionForIndex(int index, ContainerNode* scope) 1126 { 1127 if (!scope) 1128 return VisiblePosition(); 1129 RefPtrWillBeRawPtr<Range> range = PlainTextRange(index).createRangeForSelection(*scope); 1130 // Check for an invalid index. Certain editing operations invalidate indices because 1131 // of problems with TextIteratorEmitsCharactersBetweenAllVisiblePositions. 1132 if (!range) 1133 return VisiblePosition(); 1134 return VisiblePosition(range->startPosition()); 1135 } 1136 1137 // Determines whether two positions are visibly next to each other (first then second) 1138 // while ignoring whitespaces and unrendered nodes 1139 bool isVisiblyAdjacent(const Position& first, const Position& second) 1140 { 1141 return VisiblePosition(first) == VisiblePosition(second.upstream()); 1142 } 1143 1144 // Determines whether a node is inside a range or visibly starts and ends at the boundaries of the range. 1145 // Call this function to determine whether a node is visibly fit inside selectedRange 1146 bool isNodeVisiblyContainedWithin(Node& node, const Range& selectedRange) 1147 { 1148 // If the node is inside the range, then it surely is contained within 1149 if (selectedRange.compareNode(&node, IGNORE_EXCEPTION) == Range::NODE_INSIDE) 1150 return true; 1151 1152 bool startIsVisuallySame = visiblePositionBeforeNode(node) == VisiblePosition(selectedRange.startPosition()); 1153 if (startIsVisuallySame && comparePositions(positionInParentAfterNode(node), selectedRange.endPosition()) < 0) 1154 return true; 1155 1156 bool endIsVisuallySame = visiblePositionAfterNode(node) == VisiblePosition(selectedRange.endPosition()); 1157 if (endIsVisuallySame && comparePositions(selectedRange.startPosition(), positionInParentBeforeNode(node)) < 0) 1158 return true; 1159 1160 return startIsVisuallySame && endIsVisuallySame; 1161 } 1162 1163 bool isRenderedAsNonInlineTableImageOrHR(const Node* node) 1164 { 1165 if (!node) 1166 return false; 1167 RenderObject* renderer = node->renderer(); 1168 return renderer && ((renderer->isTable() && !renderer->isInline()) || (renderer->isImage() && !renderer->isInline()) || renderer->isHR()); 1169 } 1170 1171 bool areIdenticalElements(const Node* first, const Node* second) 1172 { 1173 if (!first->isElementNode() || !second->isElementNode()) 1174 return false; 1175 1176 const Element* firstElement = toElement(first); 1177 const Element* secondElement = toElement(second); 1178 if (!firstElement->hasTagName(secondElement->tagQName())) 1179 return false; 1180 1181 return firstElement->hasEquivalentAttributes(secondElement); 1182 } 1183 1184 bool isNonTableCellHTMLBlockElement(const Node* node) 1185 { 1186 if (!node->isHTMLElement()) 1187 return false; 1188 1189 const HTMLElement& element = toHTMLElement(*node); 1190 return element.hasTagName(listingTag) 1191 || element.hasTagName(olTag) 1192 || element.hasTagName(preTag) 1193 || element.hasTagName(tableTag) 1194 || element.hasTagName(ulTag) 1195 || element.hasTagName(xmpTag) 1196 || element.hasTagName(h1Tag) 1197 || element.hasTagName(h2Tag) 1198 || element.hasTagName(h3Tag) 1199 || element.hasTagName(h4Tag) 1200 || element.hasTagName(h5Tag); 1201 } 1202 1203 bool isBlockFlowElement(const Node& node) 1204 { 1205 RenderObject* renderer = node.renderer(); 1206 return node.isElementNode() && renderer && renderer->isRenderBlockFlow(); 1207 } 1208 1209 Position adjustedSelectionStartForStyleComputation(const VisibleSelection& selection) 1210 { 1211 // This function is used by range style computations to avoid bugs like: 1212 // <rdar://problem/4017641> REGRESSION (Mail): you can only bold/unbold a selection starting from end of line once 1213 // It is important to skip certain irrelevant content at the start of the selection, so we do not wind up 1214 // with a spurious "mixed" style. 1215 1216 VisiblePosition visiblePosition(selection.start()); 1217 if (visiblePosition.isNull()) 1218 return Position(); 1219 1220 // if the selection is a caret, just return the position, since the style 1221 // behind us is relevant 1222 if (selection.isCaret()) 1223 return visiblePosition.deepEquivalent(); 1224 1225 // if the selection starts just before a paragraph break, skip over it 1226 if (isEndOfParagraph(visiblePosition)) 1227 return visiblePosition.next().deepEquivalent().downstream(); 1228 1229 // otherwise, make sure to be at the start of the first selected node, 1230 // instead of possibly at the end of the last node before the selection 1231 return visiblePosition.deepEquivalent().downstream(); 1232 } 1233 1234 } // namespace blink 1235