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