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