Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 /* |
| 2 * Copyright (C) 2004, 2005, 2006, 2009 Apple Inc. All rights reserved. | 2 * Copyright (C) 2004, 2005, 2006, 2009 Apple Inc. All rights reserved. |
| 3 * | 3 * |
| 4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
| 5 * modification, are permitted provided that the following conditions | 5 * modification, are permitted provided that the following conditions |
| 6 * are met: | 6 * are met: |
| 7 * 1. Redistributions of source code must retain the above copyright | 7 * 1. Redistributions of source code must retain the above copyright |
| 8 * notice, this list of conditions and the following disclaimer. | 8 * notice, this list of conditions and the following disclaimer. |
| 9 * 2. Redistributions in binary form must reproduce the above copyright | 9 * 2. Redistributions in binary form must reproduce the above copyright |
| 10 * notice, this list of conditions and the following disclaimer in the | 10 * notice, this list of conditions and the following disclaimer in the |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 37 #include "core/editing/htmlediting.h" | 37 #include "core/editing/htmlediting.h" |
| 38 #include "core/html/HTMLHtmlElement.h" | 38 #include "core/html/HTMLHtmlElement.h" |
| 39 #include "core/html/HTMLTableElement.h" | 39 #include "core/html/HTMLTableElement.h" |
| 40 #include "core/page/Frame.h" | 40 #include "core/page/Frame.h" |
| 41 #include "core/page/Settings.h" | 41 #include "core/page/Settings.h" |
| 42 #include "core/platform/Logging.h" | 42 #include "core/platform/Logging.h" |
| 43 #include "core/rendering/InlineIterator.h" | 43 #include "core/rendering/InlineIterator.h" |
| 44 #include "core/rendering/InlineTextBox.h" | 44 #include "core/rendering/InlineTextBox.h" |
| 45 #include "core/rendering/RenderBlock.h" | 45 #include "core/rendering/RenderBlock.h" |
| 46 #include "core/rendering/RenderInline.h" | 46 #include "core/rendering/RenderInline.h" |
| 47 #include "core/rendering/RenderText.h" | 47 #include "core/rendering/RenderTextFragment.h" |
| 48 #include "wtf/text/CString.h" | 48 #include "wtf/text/CString.h" |
| 49 #include "wtf/unicode/CharacterNames.h" | 49 #include "wtf/unicode/CharacterNames.h" |
| 50 | 50 |
| 51 namespace WebCore { | 51 namespace WebCore { |
| 52 | 52 |
| 53 using namespace HTMLNames; | 53 using namespace HTMLNames; |
| 54 | 54 |
| 55 static Node* nextRenderedEditable(Node* node) | 55 static Node* nextRenderedEditable(Node* node) |
| 56 { | 56 { |
| 57 while ((node = node->nextLeafNode())) { | 57 while ((node = node->nextLeafNode())) { |
| (...skipping 388 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 446 return !deprecatedNode()->parentNode() && m_offset <= 0; | 446 return !deprecatedNode()->parentNode() && m_offset <= 0; |
| 447 } | 447 } |
| 448 | 448 |
| 449 bool Position::atEndOfTree() const | 449 bool Position::atEndOfTree() const |
| 450 { | 450 { |
| 451 if (isNull()) | 451 if (isNull()) |
| 452 return true; | 452 return true; |
| 453 return !deprecatedNode()->parentNode() && m_offset >= lastOffsetForEditing(d eprecatedNode()); | 453 return !deprecatedNode()->parentNode() && m_offset >= lastOffsetForEditing(d eprecatedNode()); |
| 454 } | 454 } |
| 455 | 455 |
| 456 RenderObject* Position::rendererOfAnchorNode() const | |
| 457 { | |
| 458 if (!m_anchorNode) | |
| 459 return 0; | |
| 460 RenderObject* renderer = m_anchorNode->renderer(); | |
| 461 if (!renderer || !renderer->isText() || !toRenderText(renderer)->isTextFragm ent()) | |
| 462 return renderer; | |
| 463 if (m_offset >= static_cast<int>(toRenderTextFragment(renderer)->textStartOf fset())) | |
| 464 return renderer; | |
| 465 return toRenderTextFragment(renderer)->firstRenderTextInFirstLetter(); | |
| 466 } | |
| 467 | |
| 468 // Position::deprecatedOffsetInRendererOfAnchorNode() is replacement of usage of | |
| 469 // deprecatedEditingOffset() in FrameSelection::updateAppearance(). | |
| 470 int Position::deprecatedOffsetInRendererOfAnchorNode() const | |
| 471 { | |
| 472 switch (m_isLegacyEditingPosition ? PositionIsOffsetInAnchor : m_anchorType) { | |
| 473 case PositionIsBeforeChildren: | |
| 474 case PositionIsBeforeAnchor: | |
| 475 case PositionIsOffsetInAnchor: { | |
| 476 RenderObject* renderer = rendererOfAnchorNode(); | |
| 477 if (!renderer || !renderer->isText()) | |
| 478 return m_offset; | |
| 479 return m_offset - toRenderText(renderer)->textStartOffset(); | |
| 480 } | |
| 481 case PositionIsAfterChildren: | |
| 482 case PositionIsAfterAnchor: | |
| 483 return offsetForPositionAfterAnchor(); | |
| 484 } | |
| 485 ASSERT_NOT_REACHED(); | |
| 486 return m_offset; | |
| 487 } | |
| 488 | |
| 456 int Position::renderedOffset() const | 489 int Position::renderedOffset() const |
| 457 { | 490 { |
| 458 if (!deprecatedNode()->isTextNode()) | 491 RenderObject* renderer = rendererOfAnchorNode(); |
| 492 if (!renderer || !renderer->isText()) | |
| 459 return m_offset; | 493 return m_offset; |
| 460 | 494 |
| 461 if (!deprecatedNode()->renderer()) | 495 RenderText* textRenderer = toRenderText(renderer); |
| 462 return m_offset; | 496 int result = textRenderer->textStartOffset(); |
| 463 | |
| 464 int result = 0; | |
| 465 RenderText* textRenderer = toRenderText(deprecatedNode()->renderer()); | |
| 466 for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->next TextBox()) { | 497 for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->next TextBox()) { |
| 467 int start = box->start(); | 498 int start = box->start(); |
| 468 int end = box->start() + box->len(); | 499 int end = box->start() + box->len(); |
| 469 if (m_offset < start) | 500 if (m_offset < start) |
| 470 return result; | 501 return result; |
| 471 if (m_offset <= end) { | 502 if (m_offset <= end) { |
| 472 result += m_offset - start; | 503 result += m_offset - start; |
| 473 return result; | 504 return result; |
| 474 } | 505 } |
| 475 result += box->len(); | 506 result += box->len(); |
| (...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 585 Node* startNode = deprecatedNode(); | 616 Node* startNode = deprecatedNode(); |
| 586 if (!startNode) | 617 if (!startNode) |
| 587 return Position(); | 618 return Position(); |
| 588 | 619 |
| 589 // iterate backward from there, looking for a qualified position | 620 // iterate backward from there, looking for a qualified position |
| 590 Node* boundary = enclosingVisualBoundary(startNode); | 621 Node* boundary = enclosingVisualBoundary(startNode); |
| 591 // FIXME: PositionIterator should respect Before and After positions. | 622 // FIXME: PositionIterator should respect Before and After positions. |
| 592 PositionIterator lastVisible = m_anchorType == PositionIsAfterAnchor ? creat eLegacyEditingPosition(m_anchorNode.get(), caretMaxOffset(m_anchorNode.get())) : *this; | 623 PositionIterator lastVisible = m_anchorType == PositionIsAfterAnchor ? creat eLegacyEditingPosition(m_anchorNode.get(), caretMaxOffset(m_anchorNode.get())) : *this; |
| 593 PositionIterator currentPos = lastVisible; | 624 PositionIterator currentPos = lastVisible; |
| 594 bool startEditable = startNode->rendererIsEditable(); | 625 bool startEditable = startNode->rendererIsEditable(); |
| 626 RenderObject* startRenderer = rendererOfAnchorNode(); | |
| 595 Node* lastNode = startNode; | 627 Node* lastNode = startNode; |
| 628 RenderObject* lastRenderer = startRenderer; | |
| 596 bool boundaryCrossed = false; | 629 bool boundaryCrossed = false; |
| 597 for (; !currentPos.atStart(); currentPos.decrement()) { | 630 for (; !currentPos.atStart(); currentPos.decrement()) { |
| 598 Node* currentNode = currentPos.node(); | 631 Node* currentNode = currentPos.node(); |
| 632 RenderObject* currentRenderer = currentPos.renderer(); | |
| 599 | 633 |
| 600 // Don't check for an editability change if we haven't moved to a differ ent node, | 634 // Don't check for an editability change if we haven't moved to a differ ent node, |
| 601 // to avoid the expense of computing rendererIsEditable(). | 635 // to avoid the expense of computing rendererIsEditable(). |
| 602 if (currentNode != lastNode) { | 636 if (currentRenderer != lastRenderer) { |
| 603 // Don't change editability. | 637 // Don't change editability. |
| 604 bool currentEditable = currentNode->rendererIsEditable(); | 638 bool currentEditable = currentNode->rendererIsEditable(); |
| 605 if (startEditable != currentEditable) { | 639 if (startEditable != currentEditable) { |
| 606 if (rule == CannotCrossEditingBoundary) | 640 if (rule == CannotCrossEditingBoundary) |
| 607 break; | 641 break; |
| 608 boundaryCrossed = true; | 642 boundaryCrossed = true; |
| 609 } | 643 } |
| 610 lastNode = currentNode; | 644 lastNode = currentNode; |
| 645 lastRenderer = currentPos.renderer(); | |
| 611 } | 646 } |
| 612 | 647 |
| 613 // If we've moved to a position that is visually distinct, return the la st saved position. There | 648 // If we've moved to a position that is visually distinct, return the la st saved position. There |
| 614 // is code below that terminates early if we're *about* to move to a vis ually distinct position. | 649 // is code below that terminates early if we're *about* to move to a vis ually distinct position. |
| 615 if (endsOfNodeAreVisuallyDistinctPositions(currentNode) && currentNode ! = boundary) | 650 if (endsOfNodeAreVisuallyDistinctPositions(currentNode) && currentNode ! = boundary) |
| 616 return lastVisible; | 651 return lastVisible; |
| 617 | 652 |
| 618 // skip position in unrendered or invisible node | 653 // skip position in unrendered or invisible node |
| 619 RenderObject* renderer = currentNode->renderer(); | 654 if (!currentRenderer || currentRenderer->style()->visibility() != VISIBL E) |
| 620 if (!renderer || renderer->style()->visibility() != VISIBLE) | |
| 621 continue; | 655 continue; |
| 622 | 656 |
| 623 if (rule == CanCrossEditingBoundary && boundaryCrossed) { | 657 if (rule == CanCrossEditingBoundary && boundaryCrossed) { |
| 624 lastVisible = currentPos; | 658 lastVisible = currentPos; |
| 625 break; | 659 break; |
| 626 } | 660 } |
| 627 | 661 |
| 628 // track last visible streamer position | 662 // track last visible streamer position |
| 629 if (isStreamer(currentPos)) | 663 if (isStreamer(currentPos)) |
| 630 lastVisible = currentPos; | 664 lastVisible = currentPos; |
| 631 | 665 |
| 632 // Don't move past a position that is visually distinct. We could rely on code above to terminate and | 666 // Don't move past a position that is visually distinct. We could rely on code above to terminate and |
| 633 // return lastVisible on the next iteration, but we terminate early to a void doing a nodeIndex() call. | 667 // return lastVisible on the next iteration, but we terminate early to a void doing a nodeIndex() call. |
| 634 if (endsOfNodeAreVisuallyDistinctPositions(currentNode) && currentPos.at StartOfNode()) | 668 if (endsOfNodeAreVisuallyDistinctPositions(currentNode) && currentPos.at StartOfNode()) |
| 635 return lastVisible; | 669 return lastVisible; |
| 636 | 670 |
| 637 // Return position after tables and nodes which have content that can be ignored. | 671 // Return position after tables and nodes which have content that can be ignored. |
| 638 if (editingIgnoresContent(currentNode) || isTableElement(currentNode)) { | 672 if (editingIgnoresContent(currentNode) || isTableElement(currentNode)) { |
| 639 if (currentPos.atEndOfNode()) | 673 if (currentPos.atEndOfNode()) |
| 640 return positionAfterNode(currentNode); | 674 return positionAfterNode(currentNode); |
| 641 continue; | 675 continue; |
| 642 } | 676 } |
| 643 | 677 |
| 644 // return current position if it is in rendered text | 678 // return current position if it is in rendered text |
| 645 if (renderer->isText() && toRenderText(renderer)->firstTextBox()) { | 679 if (currentRenderer->isText() && toRenderText(currentRenderer)->firstTex tBox()) { |
| 646 if (currentNode != startNode) { | 680 RenderText* textRenderer = toRenderText(currentRenderer); |
| 647 // This assertion fires in layout tests in the case-transform.ht ml test because | 681 if (currentRenderer != startRenderer) |
| 648 // of a mix-up between offsets in the text in the DOM tree with text in the | 682 return createLegacyEditingPosition(currentNode, textRenderer->ca retMaxOffset() + textRenderer->textStartOffset()); |
| 649 // render tree which can have a different length due to case tra nsformation. | |
| 650 // Until we resolve that, disable this so we can run the layout tests! | |
| 651 //ASSERT(currentOffset >= renderer->caretMaxOffset()); | |
| 652 return createLegacyEditingPosition(currentNode, renderer->caretM axOffset()); | |
| 653 } | |
| 654 | 683 |
| 655 unsigned textOffset = currentPos.offsetInLeafNode(); | 684 unsigned textOffset = currentPos.offsetInLeafNode() - textRenderer-> textStartOffset(); |
| 656 RenderText* textRenderer = toRenderText(renderer); | |
| 657 InlineTextBox* lastTextBox = textRenderer->lastTextBox(); | 685 InlineTextBox* lastTextBox = textRenderer->lastTextBox(); |
| 658 for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = b ox->nextTextBox()) { | 686 for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = b ox->nextTextBox()) { |
| 659 if (textOffset <= box->start() + box->len()) { | 687 if (textOffset <= box->start() + box->len()) { |
| 660 if (textOffset > box->start()) | 688 if (textOffset > box->start()) |
| 661 return currentPos; | 689 return currentPos; |
| 662 continue; | 690 continue; |
| 663 } | 691 } |
| 664 | 692 |
| 665 if (box == lastTextBox || textOffset != box->start() + box->len( ) + 1) | 693 if (box == lastTextBox || textOffset != box->start() + box->len( ) + 1) |
| 666 continue; | 694 continue; |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 708 Node* startNode = deprecatedNode(); | 736 Node* startNode = deprecatedNode(); |
| 709 if (!startNode) | 737 if (!startNode) |
| 710 return Position(); | 738 return Position(); |
| 711 | 739 |
| 712 // iterate forward from there, looking for a qualified position | 740 // iterate forward from there, looking for a qualified position |
| 713 Node* boundary = enclosingVisualBoundary(startNode); | 741 Node* boundary = enclosingVisualBoundary(startNode); |
| 714 // FIXME: PositionIterator should respect Before and After positions. | 742 // FIXME: PositionIterator should respect Before and After positions. |
| 715 PositionIterator lastVisible = m_anchorType == PositionIsAfterAnchor ? creat eLegacyEditingPosition(m_anchorNode.get(), caretMaxOffset(m_anchorNode.get())) : *this; | 743 PositionIterator lastVisible = m_anchorType == PositionIsAfterAnchor ? creat eLegacyEditingPosition(m_anchorNode.get(), caretMaxOffset(m_anchorNode.get())) : *this; |
| 716 PositionIterator currentPos = lastVisible; | 744 PositionIterator currentPos = lastVisible; |
| 717 bool startEditable = startNode->rendererIsEditable(); | 745 bool startEditable = startNode->rendererIsEditable(); |
| 746 RenderObject* startRenderer = rendererOfAnchorNode(); | |
| 718 Node* lastNode = startNode; | 747 Node* lastNode = startNode; |
| 748 RenderObject* lastRenderer = startRenderer; | |
| 719 bool boundaryCrossed = false; | 749 bool boundaryCrossed = false; |
| 720 for (; !currentPos.atEnd(); currentPos.increment()) { | 750 for (; !currentPos.atEnd(); currentPos.increment()) { |
| 721 Node* currentNode = currentPos.node(); | 751 Node* currentNode = currentPos.node(); |
| 752 RenderObject* currentRenderer = currentPos.renderer(); | |
| 722 | 753 |
| 723 // Don't check for an editability change if we haven't moved to a differ ent node, | 754 // Don't check for an editability change if we haven't moved to a differ ent node, |
| 724 // to avoid the expense of computing rendererIsEditable(). | 755 // to avoid the expense of computing rendererIsEditable(). |
| 725 if (currentNode != lastNode) { | 756 if (currentRenderer!= lastRenderer) { |
| 726 // Don't change editability. | 757 // Don't change editability. |
| 727 bool currentEditable = currentNode->rendererIsEditable(); | 758 bool currentEditable = currentNode->rendererIsEditable(); |
| 728 if (startEditable != currentEditable) { | 759 if (startEditable != currentEditable) { |
| 729 if (rule == CannotCrossEditingBoundary) | 760 if (rule == CannotCrossEditingBoundary) |
| 730 break; | 761 break; |
| 731 boundaryCrossed = true; | 762 boundaryCrossed = true; |
| 732 } | 763 } |
| 733 | 764 |
| 734 lastNode = currentNode; | 765 lastNode = currentNode; |
| 766 lastRenderer = currentRenderer; | |
| 735 } | 767 } |
| 736 | 768 |
| 737 // stop before going above the body, up into the head | 769 // stop before going above the body, up into the head |
| 738 // return the last visible streamer position | 770 // return the last visible streamer position |
| 739 if (currentNode->hasTagName(bodyTag) && currentPos.atEndOfNode()) | 771 if (currentNode->hasTagName(bodyTag) && currentPos.atEndOfNode()) |
| 740 break; | 772 break; |
| 741 | 773 |
| 742 // Do not move to a visually distinct position. | 774 // Do not move to a visually distinct position. |
| 743 if (endsOfNodeAreVisuallyDistinctPositions(currentNode) && currentNode ! = boundary) | 775 if (endsOfNodeAreVisuallyDistinctPositions(currentNode) && currentNode ! = boundary) |
| 744 return lastVisible; | 776 return lastVisible; |
| 745 // Do not move past a visually disinct position. | 777 // Do not move past a visually disinct position. |
| 746 // Note: The first position after the last in a node whose ends are visu ally distinct | 778 // Note: The first position after the last in a node whose ends are visu ally distinct |
| 747 // positions will be [boundary->parentNode(), originalBlock->nodeIndex() + 1]. | 779 // positions will be [boundary->parentNode(), originalBlock->nodeIndex() + 1]. |
| 748 if (boundary && boundary->parentNode() == currentNode) | 780 if (boundary && boundary->parentNode() == currentNode) |
| 749 return lastVisible; | 781 return lastVisible; |
| 750 | 782 |
| 751 // skip position in unrendered or invisible node | 783 // skip position in unrendered or invisible node |
| 752 RenderObject* renderer = currentNode->renderer(); | 784 if (!currentRenderer || currentRenderer->style()->visibility() != VISIBL E) |
| 753 if (!renderer || renderer->style()->visibility() != VISIBLE) | |
| 754 continue; | 785 continue; |
| 755 | 786 |
| 756 if (rule == CanCrossEditingBoundary && boundaryCrossed) { | 787 if (rule == CanCrossEditingBoundary && boundaryCrossed) { |
| 757 lastVisible = currentPos; | 788 lastVisible = currentPos; |
| 758 break; | 789 break; |
| 759 } | 790 } |
| 760 | 791 |
| 761 // track last visible streamer position | 792 // track last visible streamer position |
| 762 if (isStreamer(currentPos)) | 793 if (isStreamer(currentPos)) |
| 763 lastVisible = currentPos; | 794 lastVisible = currentPos; |
| 764 | 795 |
| 765 // Return position before tables and nodes which have content that can b e ignored. | 796 // Return position before tables and nodes which have content that can b e ignored. |
| 766 if (editingIgnoresContent(currentNode) || isTableElement(currentNode)) { | 797 if (editingIgnoresContent(currentNode) || isTableElement(currentNode)) { |
| 767 if (currentPos.offsetInLeafNode() <= renderer->caretMinOffset()) | 798 if (currentPos.offsetInLeafNode() <= currentRenderer->caretMinOffset ()) |
| 768 return createLegacyEditingPosition(currentNode, renderer->caretM inOffset()); | 799 return createLegacyEditingPosition(currentNode, currentRenderer- >caretMinOffset()); |
| 769 continue; | 800 continue; |
| 770 } | 801 } |
| 771 | 802 |
| 772 // return current position if it is in rendered text | 803 // return current position if it is in rendered text |
| 773 if (renderer->isText() && toRenderText(renderer)->firstTextBox()) { | 804 if (currentRenderer->isText() && toRenderText(currentRenderer)->firstTex tBox()) { |
| 774 if (currentNode != startNode) { | 805 RenderText* textRenderer = toRenderText(currentRenderer); |
| 775 ASSERT(currentPos.atStartOfNode()); | 806 if (currentRenderer != startRenderer) |
| 776 return createLegacyEditingPosition(currentNode, renderer->caretM inOffset()); | 807 return createLegacyEditingPosition(currentNode, textRenderer->ca retMinOffset() + textRenderer->textStartOffset()); |
| 777 } | |
| 778 | 808 |
| 779 unsigned textOffset = currentPos.offsetInLeafNode(); | 809 unsigned textOffset = currentPos.offsetInLeafNode() - textRenderer-> textStartOffset(); |
| 780 RenderText* textRenderer = toRenderText(renderer); | |
| 781 InlineTextBox* lastTextBox = textRenderer->lastTextBox(); | 810 InlineTextBox* lastTextBox = textRenderer->lastTextBox(); |
| 782 for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = b ox->nextTextBox()) { | 811 for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = b ox->nextTextBox()) { |
| 783 if (textOffset <= box->end()) { | 812 if (textOffset <= box->end()) { |
| 784 if (textOffset >= box->start()) | 813 if (textOffset >= box->start()) |
| 785 return currentPos; | 814 return currentPos; |
| 786 continue; | 815 continue; |
| 787 } | 816 } |
| 788 | 817 |
| 789 if (box == lastTextBox || textOffset != box->start() + box->len( )) | 818 if (box == lastTextBox || textOffset != box->start() + box->len( )) |
| 790 continue; | 819 continue; |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 831 for (RenderObject *o = renderer->firstChild(); o && o != stop; o = o->nextIn PreOrder()) | 860 for (RenderObject *o = renderer->firstChild(); o && o != stop; o = o->nextIn PreOrder()) |
| 832 if (o->nonPseudoNode()) { | 861 if (o->nonPseudoNode()) { |
| 833 if ((o->isText() && boundingBoxLogicalHeight(o, toRenderText(o)->lin esBoundingBox())) | 862 if ((o->isText() && boundingBoxLogicalHeight(o, toRenderText(o)->lin esBoundingBox())) |
| 834 || (o->isBox() && toRenderBox(o)->pixelSnappedLogicalHeight()) | 863 || (o->isBox() && toRenderBox(o)->pixelSnappedLogicalHeight()) |
| 835 || (o->isRenderInline() && isEmptyInline(o) && boundingBoxLogica lHeight(o, toRenderInline(o)->linesBoundingBox()))) | 864 || (o->isRenderInline() && isEmptyInline(o) && boundingBoxLogica lHeight(o, toRenderInline(o)->linesBoundingBox()))) |
| 836 return true; | 865 return true; |
| 837 } | 866 } |
| 838 return false; | 867 return false; |
| 839 } | 868 } |
| 840 | 869 |
| 870 static bool rendererIsUserSelectNone(RenderObject* renderer) | |
| 871 { | |
| 872 return renderer && renderer->style()->userSelect() == SELECT_NONE && rendere r->style()->userModify() == READ_ONLY; | |
| 873 } | |
| 874 | |
| 875 // FIXME: Caller of nodeIsUserSelectNode() should consider first letter and | |
| 876 // remaining text have different user-select CSS property. | |
| 841 bool Position::nodeIsUserSelectNone(Node* node) | 877 bool Position::nodeIsUserSelectNone(Node* node) |
| 842 { | 878 { |
| 843 return node && node->renderer() && !node->renderer()->isSelectable(); | 879 return node && rendererIsUserSelectNone(node->renderer()); |
| 880 } | |
| 881 | |
| 882 ContainerNode* Position::findParent(const Node* node) | |
|
ojan
2013/09/23 23:01:45
This looks unused.
| |
| 883 { | |
| 884 return node->parentNode(); | |
| 844 } | 885 } |
| 845 | 886 |
| 846 bool Position::nodeIsUserSelectAll(const Node* node) | 887 bool Position::nodeIsUserSelectAll(const Node* node) |
| 847 { | 888 { |
| 848 return RuntimeEnabledFeatures::userSelectAllEnabled() && node && node->rende rer() && node->renderer()->style()->userSelect() == SELECT_ALL; | 889 return RuntimeEnabledFeatures::userSelectAllEnabled() && node && node->rende rer() && node->renderer()->style()->userSelect() == SELECT_ALL; |
| 849 } | 890 } |
| 850 | 891 |
| 851 Node* Position::rootUserSelectAllForNode(Node* node) | 892 Node* Position::rootUserSelectAllForNode(Node* node) |
| 852 { | 893 { |
| 853 if (!node || !nodeIsUserSelectAll(node)) | 894 if (!node || !nodeIsUserSelectAll(node)) |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 865 if (!nodeIsUserSelectAll(parent)) | 906 if (!nodeIsUserSelectAll(parent)) |
| 866 break; | 907 break; |
| 867 candidateRoot = parent; | 908 candidateRoot = parent; |
| 868 parent = candidateRoot->parentNode(); | 909 parent = candidateRoot->parentNode(); |
| 869 } | 910 } |
| 870 return candidateRoot; | 911 return candidateRoot; |
| 871 } | 912 } |
| 872 | 913 |
| 873 bool Position::isCandidate() const | 914 bool Position::isCandidate() const |
| 874 { | 915 { |
| 875 if (isNull()) | 916 RenderObject* renderer = rendererOfAnchorNode(); |
| 876 return false; | |
| 877 | |
| 878 RenderObject* renderer = deprecatedNode()->renderer(); | |
| 879 if (!renderer) | 917 if (!renderer) |
| 880 return false; | 918 return false; |
| 881 | 919 |
| 882 if (renderer->style()->visibility() != VISIBLE) | 920 if (renderer->style()->visibility() != VISIBLE) |
| 883 return false; | 921 return false; |
| 884 | 922 |
| 885 if (renderer->isBR()) | 923 if (renderer->isBR()) |
| 886 // FIXME: The condition should be m_anchorType == PositionIsBeforeAnchor , but for now we still need to support legacy positions. | 924 // FIXME: The condition should be m_anchorType == PositionIsBeforeAnchor , but for now we still need to support legacy positions. |
| 887 return !m_offset && m_anchorType != PositionIsAfterAnchor && !nodeIsUser SelectNone(deprecatedNode()->parentNode()); | 925 return !m_offset && m_anchorType != PositionIsAfterAnchor && !nodeIsUser SelectNone(deprecatedNode()->parentNode()); |
| 888 | 926 |
| 889 if (renderer->isText()) | 927 if (renderer->isText()) |
| 890 return !nodeIsUserSelectNone(deprecatedNode()) && inRenderedText(); | 928 return !rendererIsUserSelectNone(renderer) && inRenderedText(); |
| 891 | 929 |
| 892 if (isTableElement(deprecatedNode()) || editingIgnoresContent(deprecatedNode ())) | 930 if (isTableElement(deprecatedNode()) || editingIgnoresContent(deprecatedNode ())) |
| 893 return (atFirstEditingPositionForNode() || atLastEditingPositionForNode( )) && !nodeIsUserSelectNone(deprecatedNode()->parentNode()); | 931 return (atFirstEditingPositionForNode() || atLastEditingPositionForNode( )) && !nodeIsUserSelectNone(deprecatedNode()->parentNode()); |
| 894 | 932 |
| 895 if (isHTMLHtmlElement(m_anchorNode.get())) | 933 if (isHTMLHtmlElement(m_anchorNode.get())) |
| 896 return false; | 934 return false; |
| 897 | 935 |
| 898 if (renderer->isRenderBlockFlow()) { | 936 if (renderer->isRenderBlockFlow()) { |
| 899 if (toRenderBlock(renderer)->logicalHeight() || m_anchorNode->hasTagName (bodyTag)) { | 937 if (toRenderBlock(renderer)->logicalHeight() || m_anchorNode->hasTagName (bodyTag)) { |
| 900 if (!Position::hasRenderedNonAnonymousDescendantsWithHeight(renderer )) | 938 if (!Position::hasRenderedNonAnonymousDescendantsWithHeight(renderer )) |
| 901 return atFirstEditingPositionForNode() && !Position::nodeIsUserS electNone(deprecatedNode()); | 939 return atFirstEditingPositionForNode() && !Position::nodeIsUserS electNone(deprecatedNode()); |
| 902 return m_anchorNode->rendererIsEditable() && !Position::nodeIsUserSe lectNone(deprecatedNode()) && atEditingBoundary(); | 940 return m_anchorNode->rendererIsEditable() && !Position::nodeIsUserSe lectNone(deprecatedNode()) && atEditingBoundary(); |
| 903 } | 941 } |
| 904 } else { | 942 } else { |
| 905 Frame* frame = m_anchorNode->document().frame(); | 943 Frame* frame = m_anchorNode->document().frame(); |
| 906 bool caretBrowsing = frame->settings() && frame->settings()->caretBrowsi ngEnabled(); | 944 bool caretBrowsing = frame->settings() && frame->settings()->caretBrowsi ngEnabled(); |
| 907 return (caretBrowsing || m_anchorNode->rendererIsEditable()) && !Positio n::nodeIsUserSelectNone(deprecatedNode()) && atEditingBoundary(); | 945 return (caretBrowsing || m_anchorNode->rendererIsEditable()) && !Positio n::nodeIsUserSelectNone(deprecatedNode()) && atEditingBoundary(); |
| 908 } | 946 } |
| 909 | 947 |
| 910 return false; | 948 return false; |
| 911 } | 949 } |
| 912 | 950 |
| 913 bool Position::inRenderedText() const | 951 bool Position::inRenderedText() const |
| 914 { | 952 { |
| 915 if (isNull() || !deprecatedNode()->isTextNode()) | 953 RenderObject* renderer = rendererOfAnchorNode(); |
| 954 if (!renderer || !renderer->isText()) | |
| 916 return false; | 955 return false; |
| 917 | 956 |
| 918 RenderObject* renderer = deprecatedNode()->renderer(); | 957 RenderText* textRenderer = toRenderText(renderer); |
| 919 if (!renderer) | 958 int offset = m_offset - textRenderer->textStartOffset(); |
| 920 return false; | |
| 921 | |
| 922 RenderText *textRenderer = toRenderText(renderer); | |
| 923 for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->next TextBox()) { | 959 for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->next TextBox()) { |
| 924 if (m_offset < static_cast<int>(box->start()) && !textRenderer->contains ReversedText()) { | 960 if (offset < static_cast<int>(box->start()) && !textRenderer->containsRe versedText()) { |
| 925 // The offset we're looking for is before this node | 961 // The offset we're looking for is before this node |
| 926 // this means the offset must be in content that is | 962 // this means the offset must be in content that is |
| 927 // not rendered. Return false. | 963 // not rendered. Return false. |
| 928 return false; | 964 return false; |
| 929 } | 965 } |
| 930 if (box->containsCaretOffset(m_offset)) | 966 if (box->containsCaretOffset(offset)) { |
| 931 // Return false for offsets inside composed characters. | 967 // Return false for offsets inside composed characters. |
| 932 return m_offset == 0 || m_offset == textRenderer->nextOffset(textRen derer->previousOffset(m_offset)); | 968 return !offset || offset == textRenderer->nextOffset(textRenderer->p reviousOffset(offset)); |
| 969 } | |
| 933 } | 970 } |
| 934 | 971 |
| 935 return false; | 972 return false; |
| 936 } | 973 } |
| 937 | 974 |
| 938 bool Position::isRenderedCharacter() const | 975 bool Position::isRenderedCharacter() const |
| 939 { | 976 { |
| 940 if (isNull() || !deprecatedNode()->isTextNode()) | 977 RenderObject* renderer = rendererOfAnchorNode(); |
| 941 return false; | 978 if (!renderer || !renderer->isText()) |
| 942 | |
| 943 RenderObject* renderer = deprecatedNode()->renderer(); | |
| 944 if (!renderer) | |
| 945 return false; | 979 return false; |
| 946 | 980 |
| 947 RenderText* textRenderer = toRenderText(renderer); | 981 RenderText* textRenderer = toRenderText(renderer); |
| 982 int offset = m_offset - textRenderer->textStartOffset(); | |
| 948 for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->next TextBox()) { | 983 for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->next TextBox()) { |
| 949 if (m_offset < static_cast<int>(box->start()) && !textRenderer->contains ReversedText()) { | 984 if (offset < static_cast<int>(box->start()) && !textRenderer->containsRe versedText()) { |
| 950 // The offset we're looking for is before this node | 985 // The offset we're looking for is before this node |
| 951 // this means the offset must be in content that is | 986 // this means the offset must be in content that is |
| 952 // not rendered. Return false. | 987 // not rendered. Return false. |
| 953 return false; | 988 return false; |
| 954 } | 989 } |
| 955 if (m_offset >= static_cast<int>(box->start()) && m_offset < static_cast <int>(box->start() + box->len())) | 990 if (offset >= static_cast<int>(box->start()) && offset < static_cast<int >(box->start() + box->len())) |
| 956 return true; | 991 return true; |
| 957 } | 992 } |
| 958 | 993 |
| 959 return false; | 994 return false; |
| 960 } | 995 } |
| 961 | 996 |
| 962 bool Position::rendersInDifferentPosition(const Position &pos) const | 997 bool Position::rendersInDifferentPosition(const Position &pos) const |
| 963 { | 998 { |
| 964 if (isNull() || pos.isNull()) | 999 RenderObject* renderer = rendererOfAnchorNode(); |
| 965 return false; | |
| 966 | |
| 967 RenderObject* renderer = deprecatedNode()->renderer(); | |
| 968 if (!renderer) | 1000 if (!renderer) |
| 969 return false; | 1001 return false; |
| 970 | 1002 |
| 971 RenderObject* posRenderer = pos.deprecatedNode()->renderer(); | 1003 RenderObject* posRenderer = pos.rendererOfAnchorNode(); |
| 972 if (!posRenderer) | 1004 if (!posRenderer) |
| 973 return false; | 1005 return false; |
| 974 | 1006 |
| 975 if (renderer->style()->visibility() != VISIBLE || | 1007 if (renderer->style()->visibility() != VISIBLE || |
| 976 posRenderer->style()->visibility() != VISIBLE) | 1008 posRenderer->style()->visibility() != VISIBLE) |
| 977 return false; | 1009 return false; |
| 978 | 1010 |
| 979 if (deprecatedNode() == pos.deprecatedNode()) { | 1011 if (deprecatedNode() == pos.deprecatedNode()) { |
| 980 if (deprecatedNode()->hasTagName(brTag)) | 1012 if (deprecatedNode()->hasTagName(brTag)) |
| 981 return false; | 1013 return false; |
| (...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1142 while (position != lastPosition) { | 1174 while (position != lastPosition) { |
| 1143 lastPosition = position; | 1175 lastPosition = position; |
| 1144 position = position.upstream(CanCrossEditingBoundary); | 1176 position = position.upstream(CanCrossEditingBoundary); |
| 1145 } | 1177 } |
| 1146 return position; | 1178 return position; |
| 1147 } | 1179 } |
| 1148 | 1180 |
| 1149 void Position::getInlineBoxAndOffset(EAffinity affinity, TextDirection primaryDi rection, InlineBox*& inlineBox, int& caretOffset) const | 1181 void Position::getInlineBoxAndOffset(EAffinity affinity, TextDirection primaryDi rection, InlineBox*& inlineBox, int& caretOffset) const |
| 1150 { | 1182 { |
| 1151 caretOffset = deprecatedEditingOffset(); | 1183 caretOffset = deprecatedEditingOffset(); |
| 1152 RenderObject* renderer = deprecatedNode()->renderer(); | 1184 RenderObject* renderer = rendererOfAnchorNode(); |
| 1153 | 1185 |
| 1154 if (!renderer->isText()) { | 1186 if (!renderer->isText()) { |
| 1155 inlineBox = 0; | 1187 inlineBox = 0; |
| 1156 if (canHaveChildrenForEditing(deprecatedNode()) && renderer->isRenderBlo ckFlow() && hasRenderedNonAnonymousDescendantsWithHeight(renderer)) { | 1188 if (canHaveChildrenForEditing(deprecatedNode()) && renderer->isRenderBlo ckFlow() && hasRenderedNonAnonymousDescendantsWithHeight(renderer)) { |
| 1157 // Try a visually equivalent position with possibly opposite editabi lity. This helps in case |this| is in | 1189 // Try a visually equivalent position with possibly opposite editabi lity. This helps in case |this| is in |
| 1158 // an editable block but surrounded by non-editable positions. It ac ts to negate the logic at the beginning | 1190 // an editable block but surrounded by non-editable positions. It ac ts to negate the logic at the beginning |
| 1159 // of RenderObject::createVisiblePosition(). | 1191 // of RenderObject::createVisiblePosition(). |
| 1160 Position equivalent = downstreamIgnoringEditingBoundaries(*this); | 1192 Position equivalent = downstreamIgnoringEditingBoundaries(*this); |
| 1161 if (equivalent == *this) { | 1193 if (equivalent == *this) { |
| 1162 equivalent = upstreamIgnoringEditingBoundaries(*this); | 1194 equivalent = upstreamIgnoringEditingBoundaries(*this); |
| 1163 if (equivalent == *this || downstreamIgnoringEditingBoundaries(e quivalent) == *this) | 1195 if (equivalent == *this || downstreamIgnoringEditingBoundaries(e quivalent) == *this) |
| 1164 return; | 1196 return; |
| 1165 } | 1197 } |
| 1166 | 1198 |
| 1167 equivalent.getInlineBoxAndOffset(UPSTREAM, primaryDirection, inlineB ox, caretOffset); | 1199 equivalent.getInlineBoxAndOffset(UPSTREAM, primaryDirection, inlineB ox, caretOffset); |
| 1168 return; | 1200 return; |
| 1169 } | 1201 } |
| 1170 if (renderer->isBox()) { | 1202 if (renderer->isBox()) { |
| 1171 inlineBox = toRenderBox(renderer)->inlineBoxWrapper(); | 1203 inlineBox = toRenderBox(renderer)->inlineBoxWrapper(); |
| 1172 if (!inlineBox || (caretOffset > inlineBox->caretMinOffset() && care tOffset < inlineBox->caretMaxOffset())) | 1204 if (!inlineBox || (caretOffset > inlineBox->caretMinOffset() && care tOffset < inlineBox->caretMaxOffset())) |
| 1173 return; | 1205 return; |
| 1174 } | 1206 } |
| 1175 } else { | 1207 } else { |
| 1176 RenderText* textRenderer = toRenderText(renderer); | 1208 RenderText* textRenderer = toRenderText(renderer); |
| 1177 | 1209 |
| 1178 InlineTextBox* box; | 1210 InlineTextBox* box; |
| 1179 InlineTextBox* candidate = 0; | 1211 InlineTextBox* candidate = 0; |
| 1180 | 1212 |
| 1213 if (textRenderer->isTextFragment() && toRenderTextFragment(textRenderer) ->firstLetter()) { | |
| 1214 if (caretOffset) { | |
| 1215 caretOffset -= textRenderer->textStartOffset(); | |
| 1216 } else { | |
| 1217 textRenderer = toRenderTextFragment(textRenderer)->firstRenderTe xtInFirstLetter(); | |
| 1218 if (!textRenderer) | |
| 1219 return; | |
| 1220 } | |
| 1221 } | |
| 1222 | |
| 1181 for (box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) { | 1223 for (box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) { |
| 1182 int caretMinOffset = box->caretMinOffset(); | 1224 int caretMinOffset = box->caretMinOffset(); |
| 1183 int caretMaxOffset = box->caretMaxOffset(); | 1225 int caretMaxOffset = box->caretMaxOffset(); |
| 1184 | 1226 |
| 1185 if (caretOffset < caretMinOffset || caretOffset > caretMaxOffset || (caretOffset == caretMaxOffset && box->isLineBreak())) | 1227 if (caretOffset < caretMinOffset || caretOffset > caretMaxOffset || (caretOffset == caretMaxOffset && box->isLineBreak())) |
| 1186 continue; | 1228 continue; |
| 1187 | 1229 |
| 1188 if (caretOffset > caretMinOffset && caretOffset < caretMaxOffset) { | 1230 if (caretOffset > caretMinOffset && caretOffset < caretMaxOffset) { |
| 1189 inlineBox = box; | 1231 inlineBox = box; |
| 1190 return; | 1232 return; |
| (...skipping 193 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1384 pos.showTreeForThis(); | 1426 pos.showTreeForThis(); |
| 1385 } | 1427 } |
| 1386 | 1428 |
| 1387 void showTree(const WebCore::Position* pos) | 1429 void showTree(const WebCore::Position* pos) |
| 1388 { | 1430 { |
| 1389 if (pos) | 1431 if (pos) |
| 1390 pos->showTreeForThis(); | 1432 pos->showTreeForThis(); |
| 1391 } | 1433 } |
| 1392 | 1434 |
| 1393 #endif | 1435 #endif |
| OLD | NEW |