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/v8/ExceptionState.h" 30 #include "bindings/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/HTMLTableCellElement.h" 54 #include "core/html/HTMLUListElement.h" 55 #include "core/rendering/RenderObject.h" 56 #include "core/rendering/RenderTableCell.h" 57 #include "wtf/Assertions.h" 58 #include "wtf/StdLibExtras.h" 59 #include "wtf/text/StringBuilder.h" 60 61 namespace WebCore { 62 63 using namespace HTMLNames; 64 65 // Atomic means that the node has no children, or has children which are ignored for the 66 // purposes of editing. 67 bool isAtomicNode(const Node *node) 68 { 69 return node && (!node->hasChildren() || editingIgnoresContent(node)); 70 } 71 72 // Compare two positions, taking into account the possibility that one or both 73 // could be inside a shadow tree. Only works for non-null values. 74 int comparePositions(const Position& a, const Position& b) 75 { 76 ASSERT(a.isNotNull()); 77 ASSERT(b.isNotNull()); 78 TreeScope* commonScope = commonTreeScope(a.containerNode(), b.containerNode()); 79 80 ASSERT(commonScope); 81 if (!commonScope) 82 return 0; 83 84 Node* nodeA = commonScope->ancestorInThisScope(a.containerNode()); 85 ASSERT(nodeA); 86 bool hasDescendentA = nodeA != a.containerNode(); 87 int offsetA = hasDescendentA ? 0 : a.computeOffsetInContainerNode(); 88 89 Node* nodeB = commonScope->ancestorInThisScope(b.containerNode()); 90 ASSERT(nodeB); 91 bool hasDescendentB = nodeB != b.containerNode(); 92 int offsetB = hasDescendentB ? 0 : b.computeOffsetInContainerNode(); 93 94 int bias = 0; 95 if (nodeA == nodeB) { 96 if (hasDescendentA) 97 bias = -1; 98 else if (hasDescendentB) 99 bias = 1; 100 } 101 102 int result = Range::compareBoundaryPoints(nodeA, offsetA, nodeB, offsetB, IGNORE_EXCEPTION); 103 return result ? result : bias; 104 } 105 106 int comparePositions(const PositionWithAffinity& a, const PositionWithAffinity& b) 107 { 108 return comparePositions(a.position(), b.position()); 109 } 110 111 int comparePositions(const VisiblePosition& a, const VisiblePosition& b) 112 { 113 return comparePositions(a.deepEquivalent(), b.deepEquivalent()); 114 } 115 116 Node* highestEditableRoot(const Position& position, EditableType editableType) 117 { 118 Node* node = position.deprecatedNode(); 119 if (!node) 120 return 0; 121 122 Node* highestRoot = editableRootForPosition(position, editableType); 123 if (!highestRoot) 124 return 0; 125 126 if (isHTMLBodyElement(*highestRoot)) 127 return highestRoot; 128 129 node = highestRoot->parentNode(); 130 while (node) { 131 if (node->rendererIsEditable(editableType)) 132 highestRoot = node; 133 if (isHTMLBodyElement(*node)) 134 break; 135 node = node->parentNode(); 136 } 137 138 return highestRoot; 139 } 140 141 Node* lowestEditableAncestor(Node* node) 142 { 143 while (node) { 144 if (node->rendererIsEditable()) 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 (isRenderedTableElement(node)) 165 node = node->parentNode(); 166 167 return node->rendererIsEditable(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 (isRenderedTableElement(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 (isRenderedTableElement(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, Node* highestRoot) 262 { 263 // position falls before highestRoot. 264 if (comparePositions(position, firstPositionInNode(highestRoot)) == -1 && highestRoot->rendererIsEditable()) 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, Node* 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 TextDirection directionOfEnclosingBlock(const Position& position) 338 { 339 Node* enclosingBlockNode = enclosingBlock(position.containerNode()); 340 if (!enclosingBlockNode) 341 return LTR; 342 RenderObject* renderer = enclosingBlockNode->renderer(); 343 return renderer ? renderer->style()->direction() : LTR; 344 } 345 346 // This method is used to create positions in the DOM. It returns the maximum valid offset 347 // in a node. It returns 1 for some elements even though they do not have children, which 348 // creates technically invalid DOM Positions. Be sure to call parentAnchoredEquivalent 349 // on a Position before using it to create a DOM Range, or an exception will be thrown. 350 int lastOffsetForEditing(const Node* node) 351 { 352 ASSERT(node); 353 if (!node) 354 return 0; 355 if (node->offsetInCharacters()) 356 return node->maxCharacterOffset(); 357 358 if (node->hasChildren()) 359 return node->countChildren(); 360 361 // NOTE: This should preempt the childNodeCount for, e.g., select nodes 362 if (editingIgnoresContent(node)) 363 return 1; 364 365 return 0; 366 } 367 368 String stringWithRebalancedWhitespace(const String& string, bool startIsStartOfParagraph, bool endIsEndOfParagraph) 369 { 370 unsigned length = string.length(); 371 372 StringBuilder rebalancedString; 373 rebalancedString.reserveCapacity(length); 374 375 bool previousCharacterWasSpace = false; 376 for (size_t i = 0; i < length; i++) { 377 UChar c = string[i]; 378 if (!isWhitespace(c)) { 379 rebalancedString.append(c); 380 previousCharacterWasSpace = false; 381 continue; 382 } 383 384 if (previousCharacterWasSpace || (!i && startIsStartOfParagraph) || (i + 1 == length && endIsEndOfParagraph)) { 385 rebalancedString.append(noBreakSpace); 386 previousCharacterWasSpace = false; 387 } else { 388 rebalancedString.append(' '); 389 previousCharacterWasSpace = true; 390 } 391 } 392 393 ASSERT(rebalancedString.length() == length); 394 395 return rebalancedString.toString(); 396 } 397 398 bool isTableStructureNode(const Node *node) 399 { 400 RenderObject* renderer = node->renderer(); 401 return (renderer && (renderer->isTableCell() || renderer->isTableRow() || renderer->isTableSection() || renderer->isRenderTableCol())); 402 } 403 404 const String& nonBreakingSpaceString() 405 { 406 DEFINE_STATIC_LOCAL(String, nonBreakingSpaceString, (&noBreakSpace, 1)); 407 return nonBreakingSpaceString; 408 } 409 410 // FIXME: need to dump this 411 bool isSpecialElement(const Node *n) 412 { 413 if (!n) 414 return false; 415 416 if (!n->isHTMLElement()) 417 return false; 418 419 if (n->isLink()) 420 return true; 421 422 RenderObject* renderer = n->renderer(); 423 if (!renderer) 424 return false; 425 426 if (renderer->style()->display() == TABLE || renderer->style()->display() == INLINE_TABLE) 427 return true; 428 429 if (renderer->style()->isFloating()) 430 return true; 431 432 return false; 433 } 434 435 static Node* firstInSpecialElement(const Position& pos) 436 { 437 Node* rootEditableElement = pos.containerNode()->rootEditableElement(); 438 for (Node* n = pos.deprecatedNode(); n && n->rootEditableElement() == rootEditableElement; n = n->parentNode()) 439 if (isSpecialElement(n)) { 440 VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM); 441 VisiblePosition firstInElement = VisiblePosition(firstPositionInOrBeforeNode(n), DOWNSTREAM); 442 if (isRenderedTable(n) && vPos == firstInElement.next()) 443 return n; 444 if (vPos == firstInElement) 445 return n; 446 } 447 return 0; 448 } 449 450 static Node* lastInSpecialElement(const Position& pos) 451 { 452 Node* rootEditableElement = pos.containerNode()->rootEditableElement(); 453 for (Node* n = pos.deprecatedNode(); n && n->rootEditableElement() == rootEditableElement; n = n->parentNode()) 454 if (isSpecialElement(n)) { 455 VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM); 456 VisiblePosition lastInElement = VisiblePosition(lastPositionInOrAfterNode(n), DOWNSTREAM); 457 if (isRenderedTable(n) && vPos == lastInElement.previous()) 458 return n; 459 if (vPos == lastInElement) 460 return n; 461 } 462 return 0; 463 } 464 465 Position positionBeforeContainingSpecialElement(const Position& pos, Node** containingSpecialElement) 466 { 467 Node* n = firstInSpecialElement(pos); 468 if (!n) 469 return pos; 470 Position result = positionInParentBeforeNode(*n); 471 if (result.isNull() || result.deprecatedNode()->rootEditableElement() != pos.deprecatedNode()->rootEditableElement()) 472 return pos; 473 if (containingSpecialElement) 474 *containingSpecialElement = n; 475 return result; 476 } 477 478 Position positionAfterContainingSpecialElement(const Position& pos, Node **containingSpecialElement) 479 { 480 Node* n = lastInSpecialElement(pos); 481 if (!n) 482 return pos; 483 Position result = positionInParentAfterNode(*n); 484 if (result.isNull() || result.deprecatedNode()->rootEditableElement() != pos.deprecatedNode()->rootEditableElement()) 485 return pos; 486 if (containingSpecialElement) 487 *containingSpecialElement = n; 488 return result; 489 } 490 491 Node* isFirstPositionAfterTable(const VisiblePosition& visiblePosition) 492 { 493 Position upstream(visiblePosition.deepEquivalent().upstream()); 494 if (isRenderedTable(upstream.deprecatedNode()) && upstream.atLastEditingPositionForNode()) 495 return upstream.deprecatedNode(); 496 497 return 0; 498 } 499 500 Node* isLastPositionBeforeTable(const VisiblePosition& visiblePosition) 501 { 502 Position downstream(visiblePosition.deepEquivalent().downstream()); 503 if (isRenderedTable(downstream.deprecatedNode()) && downstream.atFirstEditingPositionForNode()) 504 return downstream.deprecatedNode(); 505 506 return 0; 507 } 508 509 // Returns the visible position at the beginning of a node 510 VisiblePosition visiblePositionBeforeNode(Node& node) 511 { 512 if (node.hasChildren()) 513 return VisiblePosition(firstPositionInOrBeforeNode(&node), DOWNSTREAM); 514 ASSERT(node.parentNode()); 515 ASSERT(!node.parentNode()->isShadowRoot()); 516 return VisiblePosition(positionInParentBeforeNode(node)); 517 } 518 519 // Returns the visible position at the ending of a node 520 VisiblePosition visiblePositionAfterNode(Node& node) 521 { 522 if (node.hasChildren()) 523 return VisiblePosition(lastPositionInOrAfterNode(&node), DOWNSTREAM); 524 ASSERT(node.parentNode()); 525 ASSERT(!node.parentNode()->isShadowRoot()); 526 return VisiblePosition(positionInParentAfterNode(node)); 527 } 528 529 // Create a range object with two visible positions, start and end. 530 // create(Document*, const Position&, const Position&); will use deprecatedEditingOffset 531 // Use this function instead of create a regular range object (avoiding editing offset). 532 PassRefPtrWillBeRawPtr<Range> createRange(Document& document, const VisiblePosition& start, const VisiblePosition& end, ExceptionState& exceptionState) 533 { 534 RefPtrWillBeRawPtr<Range> selectedRange = Range::create(document); 535 selectedRange->setStart(start.deepEquivalent().containerNode(), start.deepEquivalent().computeOffsetInContainerNode(), exceptionState); 536 if (!exceptionState.hadException()) 537 selectedRange->setEnd(end.deepEquivalent().containerNode(), end.deepEquivalent().computeOffsetInContainerNode(), exceptionState); 538 return selectedRange.release(); 539 } 540 541 bool isListElement(Node* n) 542 { 543 return (n && (isHTMLUListElement(*n) || isHTMLOListElement(*n) || isHTMLDListElement(*n))); 544 } 545 546 bool isListItem(const Node* n) 547 { 548 return n && n->renderer() && n->renderer()->isListItem(); 549 } 550 551 Node* enclosingNodeWithTag(const Position& p, const QualifiedName& tagName) 552 { 553 if (p.isNull()) 554 return 0; 555 556 Node* root = highestEditableRoot(p); 557 for (Node* n = p.deprecatedNode(); n; n = n->parentNode()) { 558 if (root && !n->rendererIsEditable()) 559 continue; 560 if (n->hasTagName(tagName)) 561 return n; 562 if (n == root) 563 return 0; 564 } 565 566 return 0; 567 } 568 569 Node* enclosingNodeOfType(const Position& p, bool (*nodeIsOfType)(const Node*), EditingBoundaryCrossingRule rule) 570 { 571 // FIXME: support CanSkipCrossEditingBoundary 572 ASSERT(rule == CanCrossEditingBoundary || rule == CannotCrossEditingBoundary); 573 if (p.isNull()) 574 return 0; 575 576 Node* root = rule == CannotCrossEditingBoundary ? highestEditableRoot(p) : 0; 577 for (Node* n = p.deprecatedNode(); n; n = n->parentNode()) { 578 // Don't return a non-editable node if the input position was editable, since 579 // the callers from editing will no doubt want to perform editing inside the returned node. 580 if (root && !n->rendererIsEditable()) 581 continue; 582 if (nodeIsOfType(n)) 583 return n; 584 if (n == root) 585 return 0; 586 } 587 588 return 0; 589 } 590 591 Node* highestEnclosingNodeOfType(const Position& p, bool (*nodeIsOfType)(const Node*), EditingBoundaryCrossingRule rule, Node* stayWithin) 592 { 593 Node* highest = 0; 594 Node* root = rule == CannotCrossEditingBoundary ? highestEditableRoot(p) : 0; 595 for (Node* n = p.containerNode(); n && n != stayWithin; n = n->parentNode()) { 596 if (root && !n->rendererIsEditable()) 597 continue; 598 if (nodeIsOfType(n)) 599 highest = n; 600 if (n == root) 601 break; 602 } 603 604 return highest; 605 } 606 607 static bool hasARenderedDescendant(Node* node, Node* excludedNode) 608 { 609 for (Node* n = node->firstChild(); n;) { 610 if (n == excludedNode) { 611 n = NodeTraversal::nextSkippingChildren(*n, node); 612 continue; 613 } 614 if (n->renderer()) 615 return true; 616 n = NodeTraversal::next(*n, node); 617 } 618 return false; 619 } 620 621 Node* highestNodeToRemoveInPruning(Node* node, Node* excludeNode) 622 { 623 Node* previousNode = 0; 624 Node* rootEditableElement = node ? node->rootEditableElement() : 0; 625 for (; node; node = node->parentNode()) { 626 if (RenderObject* renderer = node->renderer()) { 627 if (!renderer->canHaveChildren() || hasARenderedDescendant(node, previousNode) || rootEditableElement == node || excludeNode == node) 628 return previousNode; 629 } 630 previousNode = node; 631 } 632 return 0; 633 } 634 635 Node* enclosingTableCell(const Position& p) 636 { 637 return toElement(enclosingNodeOfType(p, isTableCell)); 638 } 639 640 Element* enclosingAnchorElement(const Position& p) 641 { 642 if (p.isNull()) 643 return 0; 644 645 Node* node = p.deprecatedNode(); 646 while (node && !(node->isElementNode() && node->isLink())) 647 node = node->parentNode(); 648 return toElement(node); 649 } 650 651 HTMLElement* enclosingList(Node* node) 652 { 653 if (!node) 654 return 0; 655 656 Node* root = highestEditableRoot(firstPositionInOrBeforeNode(node)); 657 658 for (ContainerNode* n = node->parentNode(); n; n = n->parentNode()) { 659 if (isHTMLUListElement(*n) || isHTMLOListElement(*n)) 660 return toHTMLElement(n); 661 if (n == root) 662 return 0; 663 } 664 665 return 0; 666 } 667 668 Node* enclosingListChild(Node *node) 669 { 670 if (!node) 671 return 0; 672 // Check for a list item element, or for a node whose parent is a list element. Such a node 673 // will appear visually as a list item (but without a list marker) 674 Node* root = highestEditableRoot(firstPositionInOrBeforeNode(node)); 675 676 // FIXME: This function is inappropriately named if it starts with node instead of node->parentNode() 677 for (Node* n = node; n && n->parentNode(); n = n->parentNode()) { 678 if (isHTMLLIElement(*n) || (isListElement(n->parentNode()) && n != root)) 679 return n; 680 if (n == root || isTableCell(n)) 681 return 0; 682 } 683 684 return 0; 685 } 686 687 // FIXME: This method should not need to call isStartOfParagraph/isEndOfParagraph 688 Node* enclosingEmptyListItem(const VisiblePosition& visiblePos) 689 { 690 // Check that position is on a line by itself inside a list item 691 Node* listChildNode = enclosingListChild(visiblePos.deepEquivalent().deprecatedNode()); 692 if (!listChildNode || !isStartOfParagraph(visiblePos) || !isEndOfParagraph(visiblePos)) 693 return 0; 694 695 VisiblePosition firstInListChild(firstPositionInOrBeforeNode(listChildNode)); 696 VisiblePosition lastInListChild(lastPositionInOrAfterNode(listChildNode)); 697 698 if (firstInListChild != visiblePos || lastInListChild != visiblePos) 699 return 0; 700 701 return listChildNode; 702 } 703 704 HTMLElement* outermostEnclosingList(Node* node, Node* rootList) 705 { 706 HTMLElement* list = enclosingList(node); 707 if (!list) 708 return 0; 709 710 while (HTMLElement* nextList = enclosingList(list)) { 711 if (nextList == rootList) 712 break; 713 list = nextList; 714 } 715 716 return list; 717 } 718 719 bool canMergeLists(Element* firstList, Element* secondList) 720 { 721 if (!firstList || !secondList || !firstList->isHTMLElement() || !secondList->isHTMLElement()) 722 return false; 723 724 return firstList->hasTagName(secondList->tagQName()) // make sure the list types match (ol vs. ul) 725 && firstList->rendererIsEditable() && secondList->rendererIsEditable() // both lists are editable 726 && firstList->rootEditableElement() == secondList->rootEditableElement() // don't cross editing boundaries 727 && isVisiblyAdjacent(positionInParentAfterNode(*firstList), positionInParentBeforeNode(*secondList)); 728 // Make sure there is no visible content between this li and the previous list 729 } 730 731 bool isRenderedTableElement(const Node* node) 732 { 733 return isHTMLTableElement(*node) && node->renderer(); 734 } 735 736 bool isRenderedTable(const Node* node) 737 { 738 if (!node || !node->isElementNode()) 739 return false; 740 741 RenderObject* renderer = node->renderer(); 742 return (renderer && renderer->isTable()); 743 } 744 745 bool isTableCell(const Node* node) 746 { 747 ASSERT(node); 748 RenderObject* r = node->renderer(); 749 return r ? r->isTableCell() : isHTMLTableCellElement(*node); 750 } 751 752 bool isEmptyTableCell(const Node* node) 753 { 754 // Returns true IFF the passed in node is one of: 755 // .) a table cell with no children, 756 // .) a table cell with a single BR child, and which has no other child renderers, including :before and :after renderers 757 // .) the BR child of such a table cell 758 759 // Find rendered node 760 while (node && !node->renderer()) 761 node = node->parentNode(); 762 if (!node) 763 return false; 764 765 // Make sure the rendered node is a table cell or <br>. 766 // If it's a <br>, then the parent node has to be a table cell. 767 RenderObject* renderer = node->renderer(); 768 if (renderer->isBR()) { 769 renderer = renderer->parent(); 770 if (!renderer) 771 return false; 772 } 773 if (!renderer->isTableCell()) 774 return false; 775 776 // Check that the table cell contains no child renderers except for perhaps a single <br>. 777 RenderObject* childRenderer = toRenderTableCell(renderer)->firstChild(); 778 if (!childRenderer) 779 return true; 780 if (!childRenderer->isBR()) 781 return false; 782 return !childRenderer->nextSibling(); 783 } 784 785 PassRefPtrWillBeRawPtr<HTMLElement> createDefaultParagraphElement(Document& document) 786 { 787 switch (document.frame()->editor().defaultParagraphSeparator()) { 788 case EditorParagraphSeparatorIsDiv: 789 return HTMLDivElement::create(document); 790 case EditorParagraphSeparatorIsP: 791 return HTMLParagraphElement::create(document); 792 } 793 794 ASSERT_NOT_REACHED(); 795 return nullptr; 796 } 797 798 PassRefPtrWillBeRawPtr<HTMLElement> createBreakElement(Document& document) 799 { 800 return HTMLBRElement::create(document); 801 } 802 803 PassRefPtrWillBeRawPtr<HTMLElement> createOrderedListElement(Document& document) 804 { 805 return HTMLOListElement::create(document); 806 } 807 808 PassRefPtrWillBeRawPtr<HTMLElement> createUnorderedListElement(Document& document) 809 { 810 return HTMLUListElement::create(document); 811 } 812 813 PassRefPtrWillBeRawPtr<HTMLElement> createListItemElement(Document& document) 814 { 815 return HTMLLIElement::create(document); 816 } 817 818 PassRefPtrWillBeRawPtr<HTMLElement> createHTMLElement(Document& document, const QualifiedName& name) 819 { 820 return createHTMLElement(document, name.localName()); 821 } 822 823 PassRefPtrWillBeRawPtr<HTMLElement> createHTMLElement(Document& document, const AtomicString& tagName) 824 { 825 return HTMLElementFactory::createHTMLElement(tagName, document, 0, false); 826 } 827 828 bool isTabSpanNode(const Node* node) 829 { 830 if (!isHTMLSpanElement(node) || toElement(node)->getAttribute(classAttr) != AppleTabSpanClass) 831 return false; 832 UseCounter::count(node->document(), UseCounter::EditingAppleTabSpanClass); 833 return true; 834 } 835 836 bool isTabSpanTextNode(const Node* node) 837 { 838 return node && node->isTextNode() && node->parentNode() && isTabSpanNode(node->parentNode()); 839 } 840 841 Node* tabSpanNode(const Node* node) 842 { 843 return isTabSpanTextNode(node) ? node->parentNode() : 0; 844 } 845 846 PassRefPtrWillBeRawPtr<Element> createTabSpanElement(Document& document, PassRefPtrWillBeRawPtr<Node> prpTabTextNode) 847 { 848 RefPtrWillBeRawPtr<Node> tabTextNode = prpTabTextNode; 849 850 // Make the span to hold the tab. 851 RefPtrWillBeRawPtr<Element> spanElement = document.createElement(spanTag, false); 852 spanElement->setAttribute(classAttr, AppleTabSpanClass); 853 spanElement->setAttribute(styleAttr, "white-space:pre"); 854 855 // Add tab text to that span. 856 if (!tabTextNode) 857 tabTextNode = document.createEditingTextNode("\t"); 858 859 spanElement->appendChild(tabTextNode.release()); 860 861 return spanElement.release(); 862 } 863 864 PassRefPtrWillBeRawPtr<Element> createTabSpanElement(Document& document, const String& tabText) 865 { 866 return createTabSpanElement(document, document.createTextNode(tabText)); 867 } 868 869 PassRefPtrWillBeRawPtr<Element> createTabSpanElement(Document& document) 870 { 871 return createTabSpanElement(document, PassRefPtrWillBeRawPtr<Node>(nullptr)); 872 } 873 874 bool isNodeRendered(const Node *node) 875 { 876 if (!node) 877 return false; 878 879 RenderObject* renderer = node->renderer(); 880 if (!renderer) 881 return false; 882 883 return renderer->style()->visibility() == VISIBLE; 884 } 885 886 unsigned numEnclosingMailBlockquotes(const Position& p) 887 { 888 unsigned num = 0; 889 for (Node* n = p.deprecatedNode(); n; n = n->parentNode()) 890 if (isMailBlockquote(n)) 891 num++; 892 893 return num; 894 } 895 896 void updatePositionForNodeRemoval(Position& position, Node& node) 897 { 898 if (position.isNull()) 899 return; 900 switch (position.anchorType()) { 901 case Position::PositionIsBeforeChildren: 902 if (position.containerNode() == node) 903 position = positionInParentBeforeNode(node); 904 break; 905 case Position::PositionIsAfterChildren: 906 if (position.containerNode() == node) 907 position = positionInParentAfterNode(node); 908 break; 909 case Position::PositionIsOffsetInAnchor: 910 if (position.containerNode() == node.parentNode() && static_cast<unsigned>(position.offsetInContainerNode()) > node.nodeIndex()) 911 position.moveToOffset(position.offsetInContainerNode() - 1); 912 else if (node.containsIncludingShadowDOM(position.containerNode())) 913 position = positionInParentBeforeNode(node); 914 break; 915 case Position::PositionIsAfterAnchor: 916 if (node.containsIncludingShadowDOM(position.anchorNode())) 917 position = positionInParentAfterNode(node); 918 break; 919 case Position::PositionIsBeforeAnchor: 920 if (node.containsIncludingShadowDOM(position.anchorNode())) 921 position = positionInParentBeforeNode(node); 922 break; 923 } 924 } 925 926 bool isMailBlockquote(const Node *node) 927 { 928 if (!node || !node->hasTagName(blockquoteTag)) 929 return false; 930 931 return toElement(node)->getAttribute("type") == "cite"; 932 } 933 934 int caretMinOffset(const Node* n) 935 { 936 RenderObject* r = n->renderer(); 937 ASSERT(!n->isCharacterDataNode() || !r || r->isText()); // FIXME: This was a runtime check that seemingly couldn't fail; changed it to an assertion for now. 938 return r ? r->caretMinOffset() : 0; 939 } 940 941 // If a node can contain candidates for VisiblePositions, return the offset of the last candidate, otherwise 942 // return the number of children for container nodes and the length for unrendered text nodes. 943 int caretMaxOffset(const Node* n) 944 { 945 // For rendered text nodes, return the last position that a caret could occupy. 946 if (n->isTextNode() && n->renderer()) 947 return n->renderer()->caretMaxOffset(); 948 // For containers return the number of children. For others do the same as above. 949 return lastOffsetForEditing(n); 950 } 951 952 bool lineBreakExistsAtVisiblePosition(const VisiblePosition& visiblePosition) 953 { 954 return lineBreakExistsAtPosition(visiblePosition.deepEquivalent().downstream()); 955 } 956 957 bool lineBreakExistsAtPosition(const Position& position) 958 { 959 if (position.isNull()) 960 return false; 961 962 if (isHTMLBRElement(*position.anchorNode()) && position.atFirstEditingPositionForNode()) 963 return true; 964 965 if (!position.anchorNode()->renderer()) 966 return false; 967 968 if (!position.anchorNode()->isTextNode() || !position.anchorNode()->renderer()->style()->preserveNewline()) 969 return false; 970 971 Text* textNode = toText(position.anchorNode()); 972 unsigned offset = position.offsetInContainerNode(); 973 return offset < textNode->length() && textNode->data()[offset] == '\n'; 974 } 975 976 // Modifies selections that have an end point at the edge of a table 977 // that contains the other endpoint so that they don't confuse 978 // code that iterates over selected paragraphs. 979 VisibleSelection selectionForParagraphIteration(const VisibleSelection& original) 980 { 981 VisibleSelection newSelection(original); 982 VisiblePosition startOfSelection(newSelection.visibleStart()); 983 VisiblePosition endOfSelection(newSelection.visibleEnd()); 984 985 // If the end of the selection to modify is just after a table, and 986 // if the start of the selection is inside that table, then the last paragraph 987 // that we'll want modify is the last one inside the table, not the table itself 988 // (a table is itself a paragraph). 989 if (Node* table = isFirstPositionAfterTable(endOfSelection)) 990 if (startOfSelection.deepEquivalent().deprecatedNode()->isDescendantOf(table)) 991 newSelection = VisibleSelection(startOfSelection, endOfSelection.previous(CannotCrossEditingBoundary)); 992 993 // If the start of the selection to modify is just before a table, 994 // and if the end of the selection is inside that table, then the first paragraph 995 // we'll want to modify is the first one inside the table, not the paragraph 996 // containing the table itself. 997 if (Node* table = isLastPositionBeforeTable(startOfSelection)) 998 if (endOfSelection.deepEquivalent().deprecatedNode()->isDescendantOf(table)) 999 newSelection = VisibleSelection(startOfSelection.next(CannotCrossEditingBoundary), endOfSelection); 1000 1001 return newSelection; 1002 } 1003 1004 // FIXME: indexForVisiblePosition and visiblePositionForIndex use TextIterators to convert between 1005 // VisiblePositions and indices. But TextIterator iteration using TextIteratorEmitsCharactersBetweenAllVisiblePositions 1006 // does not exactly match VisiblePosition iteration, so using them to preserve a selection during an editing 1007 // opertion is unreliable. TextIterator's TextIteratorEmitsCharactersBetweenAllVisiblePositions mode needs to be fixed, 1008 // or these functions need to be changed to iterate using actual VisiblePositions. 1009 // FIXME: Deploy these functions everywhere that TextIterators are used to convert between VisiblePositions and indices. 1010 int indexForVisiblePosition(const VisiblePosition& visiblePosition, RefPtrWillBeRawPtr<ContainerNode>& scope) 1011 { 1012 if (visiblePosition.isNull()) 1013 return 0; 1014 1015 Position p(visiblePosition.deepEquivalent()); 1016 Document& document = *p.document(); 1017 ShadowRoot* shadowRoot = p.anchorNode()->containingShadowRoot(); 1018 1019 if (shadowRoot) 1020 scope = shadowRoot; 1021 else 1022 scope = document.documentElement(); 1023 1024 RefPtrWillBeRawPtr<Range> range = Range::create(document, firstPositionInNode(scope.get()), p.parentAnchoredEquivalent()); 1025 1026 return TextIterator::rangeLength(range.get(), true); 1027 } 1028 1029 VisiblePosition visiblePositionForIndex(int index, ContainerNode* scope) 1030 { 1031 if (!scope) 1032 return VisiblePosition(); 1033 RefPtrWillBeRawPtr<Range> range = PlainTextRange(index).createRangeForSelection(*scope); 1034 // Check for an invalid index. Certain editing operations invalidate indices because 1035 // of problems with TextIteratorEmitsCharactersBetweenAllVisiblePositions. 1036 if (!range) 1037 return VisiblePosition(); 1038 return VisiblePosition(range->startPosition()); 1039 } 1040 1041 // Determines whether two positions are visibly next to each other (first then second) 1042 // while ignoring whitespaces and unrendered nodes 1043 bool isVisiblyAdjacent(const Position& first, const Position& second) 1044 { 1045 return VisiblePosition(first) == VisiblePosition(second.upstream()); 1046 } 1047 1048 // Determines whether a node is inside a range or visibly starts and ends at the boundaries of the range. 1049 // Call this function to determine whether a node is visibly fit inside selectedRange 1050 bool isNodeVisiblyContainedWithin(Node& node, const Range& selectedRange) 1051 { 1052 // If the node is inside the range, then it surely is contained within 1053 if (selectedRange.compareNode(&node, IGNORE_EXCEPTION) == Range::NODE_INSIDE) 1054 return true; 1055 1056 bool startIsVisuallySame = visiblePositionBeforeNode(node) == VisiblePosition(selectedRange.startPosition()); 1057 if (startIsVisuallySame && comparePositions(positionInParentAfterNode(node), selectedRange.endPosition()) < 0) 1058 return true; 1059 1060 bool endIsVisuallySame = visiblePositionAfterNode(node) == VisiblePosition(selectedRange.endPosition()); 1061 if (endIsVisuallySame && comparePositions(selectedRange.startPosition(), positionInParentBeforeNode(node)) < 0) 1062 return true; 1063 1064 return startIsVisuallySame && endIsVisuallySame; 1065 } 1066 1067 bool isRenderedAsNonInlineTableImageOrHR(const Node* node) 1068 { 1069 if (!node) 1070 return false; 1071 RenderObject* renderer = node->renderer(); 1072 return renderer && ((renderer->isTable() && !renderer->isInline()) || (renderer->isImage() && !renderer->isInline()) || renderer->isHR()); 1073 } 1074 1075 bool areIdenticalElements(const Node* first, const Node* second) 1076 { 1077 if (!first->isElementNode() || !second->isElementNode()) 1078 return false; 1079 1080 const Element* firstElement = toElement(first); 1081 const Element* secondElement = toElement(second); 1082 if (!firstElement->hasTagName(secondElement->tagQName())) 1083 return false; 1084 1085 return firstElement->hasEquivalentAttributes(secondElement); 1086 } 1087 1088 bool isNonTableCellHTMLBlockElement(const Node* node) 1089 { 1090 return node->hasTagName(listingTag) 1091 || node->hasTagName(olTag) 1092 || node->hasTagName(preTag) 1093 || node->hasTagName(tableTag) 1094 || node->hasTagName(ulTag) 1095 || node->hasTagName(xmpTag) 1096 || node->hasTagName(h1Tag) 1097 || node->hasTagName(h2Tag) 1098 || node->hasTagName(h3Tag) 1099 || node->hasTagName(h4Tag) 1100 || node->hasTagName(h5Tag); 1101 } 1102 1103 Position adjustedSelectionStartForStyleComputation(const VisibleSelection& selection) 1104 { 1105 // This function is used by range style computations to avoid bugs like: 1106 // <rdar://problem/4017641> REGRESSION (Mail): you can only bold/unbold a selection starting from end of line once 1107 // It is important to skip certain irrelevant content at the start of the selection, so we do not wind up 1108 // with a spurious "mixed" style. 1109 1110 VisiblePosition visiblePosition(selection.start()); 1111 if (visiblePosition.isNull()) 1112 return Position(); 1113 1114 // if the selection is a caret, just return the position, since the style 1115 // behind us is relevant 1116 if (selection.isCaret()) 1117 return visiblePosition.deepEquivalent(); 1118 1119 // if the selection starts just before a paragraph break, skip over it 1120 if (isEndOfParagraph(visiblePosition)) 1121 return visiblePosition.next().deepEquivalent().downstream(); 1122 1123 // otherwise, make sure to be at the start of the first selected node, 1124 // instead of possibly at the end of the last node before the selection 1125 return visiblePosition.deepEquivalent().downstream(); 1126 } 1127 1128 } // namespace WebCore 1129