OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) | 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) |
3 * (C) 1999 Antti Koivisto (koivisto@kde.org) | 3 * (C) 1999 Antti Koivisto (koivisto@kde.org) |
4 * (C) 2001 Dirk Mueller (mueller@kde.org) | 4 * (C) 2001 Dirk Mueller (mueller@kde.org) |
5 * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. | 5 * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. |
6 * (C) 2006 Alexey Proskuryakov (ap@nypop.com) | 6 * (C) 2006 Alexey Proskuryakov (ap@nypop.com) |
7 * | 7 * |
8 * This library is free software; you can redistribute it and/or | 8 * This library is free software; you can redistribute it and/or |
9 * modify it under the terms of the GNU Library General Public | 9 * modify it under the terms of the GNU Library General Public |
10 * License as published by the Free Software Foundation; either | 10 * License as published by the Free Software Foundation; either |
(...skipping 12 matching lines...) Expand all Loading... | |
23 */ | 23 */ |
24 | 24 |
25 #include "config.h" | 25 #include "config.h" |
26 #include "core/html/HTMLTextFormControlElement.h" | 26 #include "core/html/HTMLTextFormControlElement.h" |
27 | 27 |
28 #include "bindings/core/v8/ExceptionState.h" | 28 #include "bindings/core/v8/ExceptionState.h" |
29 #include "bindings/core/v8/ExceptionStatePlaceholder.h" | 29 #include "bindings/core/v8/ExceptionStatePlaceholder.h" |
30 #include "core/HTMLNames.h" | 30 #include "core/HTMLNames.h" |
31 #include "core/accessibility/AXObjectCache.h" | 31 #include "core/accessibility/AXObjectCache.h" |
32 #include "core/dom/Document.h" | 32 #include "core/dom/Document.h" |
33 #include "core/dom/NodeList.h" | |
33 #include "core/dom/NodeTraversal.h" | 34 #include "core/dom/NodeTraversal.h" |
34 #include "core/dom/Text.h" | 35 #include "core/dom/Text.h" |
35 #include "core/dom/shadow/ShadowRoot.h" | 36 #include "core/dom/shadow/ShadowRoot.h" |
36 #include "core/editing/FrameSelection.h" | 37 #include "core/editing/FrameSelection.h" |
37 #include "core/editing/TextIterator.h" | 38 #include "core/editing/TextIterator.h" |
38 #include "core/events/Event.h" | 39 #include "core/events/Event.h" |
39 #include "core/frame/LocalFrame.h" | 40 #include "core/frame/LocalFrame.h" |
40 #include "core/frame/UseCounter.h" | 41 #include "core/frame/UseCounter.h" |
41 #include "core/html/HTMLBRElement.h" | 42 #include "core/html/HTMLBRElement.h" |
42 #include "core/html/shadow/ShadowElementNames.h" | 43 #include "core/html/shadow/ShadowElementNames.h" |
43 #include "core/rendering/RenderBlock.h" | 44 #include "core/rendering/RenderBlock.h" |
44 #include "core/rendering/RenderTheme.h" | 45 #include "core/rendering/RenderTheme.h" |
45 #include "platform/heap/Handle.h" | 46 #include "platform/heap/Handle.h" |
47 #include "platform/text/TextBoundaries.h" | |
46 #include "wtf/text/StringBuilder.h" | 48 #include "wtf/text/StringBuilder.h" |
47 | 49 |
48 namespace WebCore { | 50 namespace WebCore { |
49 | 51 |
50 using namespace HTMLNames; | 52 using namespace HTMLNames; |
51 | 53 |
52 HTMLTextFormControlElement::HTMLTextFormControlElement(const QualifiedName& tagN ame, Document& doc, HTMLFormElement* form) | 54 HTMLTextFormControlElement::HTMLTextFormControlElement(const QualifiedName& tagN ame, Document& doc, HTMLFormElement* form) |
53 : HTMLFormControlElementWithState(tagName, doc, form) | 55 : HTMLFormControlElementWithState(tagName, doc, form) |
54 , m_lastChangeWasUserEdit(false) | 56 , m_lastChangeWasUserEdit(false) |
55 , m_cachedSelectionStart(0) | 57 , m_cachedSelectionStart(0) |
(...skipping 596 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
652 } | 654 } |
653 | 655 |
654 return "ltr"; | 656 return "ltr"; |
655 } | 657 } |
656 | 658 |
657 HTMLElement* HTMLTextFormControlElement::innerEditorElement() const | 659 HTMLElement* HTMLTextFormControlElement::innerEditorElement() const |
658 { | 660 { |
659 return toHTMLElement(userAgentShadowRoot()->getElementById(ShadowElementName s::innerEditor())); | 661 return toHTMLElement(userAgentShadowRoot()->getElementById(ShadowElementName s::innerEditor())); |
660 } | 662 } |
661 | 663 |
664 static Position innerNodePosition(const Position& innerPosition) | |
665 { | |
666 HTMLElement* element = toHTMLElement(innerPosition.anchorNode()); | |
667 RefPtrWillBeRawPtr<NodeList> childNodes = element->childNodes(); | |
668 if (!childNodes->length()) | |
669 return Position(element, 0, Position::PositionIsOffsetInAnchor); | |
670 | |
671 unsigned offset = 0; | |
672 | |
673 switch (innerPosition.anchorType()) { | |
674 case Position::PositionIsOffsetInAnchor: | |
675 { | |
676 offset = std::max(0, std::min(innerPosition.computeOffsetInContainer Node(), (int)childNodes->length())); | |
677 break; | |
678 } | |
679 case Position::PositionIsAfterChildren: | |
680 { | |
681 offset = childNodes->length(); | |
682 break; | |
683 } | |
684 default: | |
685 break; | |
686 } | |
687 | |
688 if (offset == childNodes->length()) { | |
689 return Position(childNodes->item(childNodes->length() - 1), Position::Po sitionIsAfterAnchor); | |
690 } | |
691 | |
692 Node* node = childNodes->item(offset); | |
693 if (node->isTextNode()) | |
694 return Position(toText(node), 0); | |
695 | |
696 return Position(node, Position::PositionIsBeforeAnchor); | |
697 } | |
698 | |
699 static Position findWordBoundary(const HTMLElement* innerText, const Position& s tartPosition, const Position endPosition, bool findStart) | |
yosin_UTC9
2014/07/03 04:05:10
nit: |innerText| => |innerEditor|
yoichio
2014/07/03 04:32:19
Done.
| |
700 { | |
701 Vector<UChar> characters; | |
702 Vector<unsigned> lengthList; | |
703 Vector<Text*> textList; | |
704 // Trace text nodes. | |
705 for (Node* node = startPosition.anchorNode(); node; node = NodeTraversal::ne xt(*node, innerText)) { | |
706 bool isStartNode = node == startPosition.anchorNode(); | |
707 bool isEndNode = node == endPosition.anchorNode(); | |
708 if (node->isTextNode()) { | |
709 Text* text = toText(node); | |
710 const unsigned start = isStartNode ? startPosition.computeOffsetInCo ntainerNode() : 0; | |
711 const unsigned end = isEndNode ? endPosition.computeOffsetInContaine rNode() : text->data().length(); | |
712 for (unsigned offset = start; offset < end; ++offset) | |
713 characters.append(text->data()[offset]); | |
714 const unsigned length = end - start; | |
715 | |
716 lengthList.append(length); | |
717 textList.append(text); | |
718 } | |
719 | |
720 if (isEndNode) | |
721 break; | |
722 } | |
723 | |
724 if (!characters.size()) | |
725 return startPosition; | |
726 | |
727 int start, end; | |
728 if (!findStart && characters.data()[0] == '\n') { | |
729 // findWordBoundary("\ntext", 0, &start, &end) assigns 1 to |end| but we expect 0 at the case. | |
730 start = 0; | |
731 end = 0; | |
732 } else { | |
733 findWordBoundary(characters.data(), characters.size(), findStart ? chara cters.size() : 0, &start, &end); | |
734 } | |
735 ASSERT(start >= 0); | |
736 ASSERT(end >= 0); | |
737 unsigned restOffset = findStart ? start : end; | |
738 // Find position. | |
739 for (unsigned i = 0; i < lengthList.size(); ++i) { | |
740 if (restOffset <= lengthList[i]) | |
741 return Position(textList[i], (textList[i] == startPosition.anchorNod e()) ? restOffset + startPosition.computeOffsetInContainerNode() : restOffset); | |
742 restOffset -= lengthList[i]; | |
743 } | |
744 | |
745 ASSERT_NOT_REACHED(); | |
746 return Position(); | |
747 } | |
748 | |
749 Position HTMLTextFormControlElement::startOfWord(const Position& position) | |
750 { | |
751 const HTMLTextFormControlElement* textForm = enclosingTextFormControl(positi on); | |
752 ASSERT(textForm); | |
753 HTMLElement* innerText = textForm->innerEditorElement(); | |
754 | |
755 const Position startPosition = startOfSentence(position); | |
756 if (startPosition == position) | |
757 return position; | |
758 const Position endPosition = (position.anchorNode() == innerText) ? innerNod ePosition(position): position; | |
759 | |
760 return findWordBoundary(innerText, startPosition, endPosition, true); | |
761 } | |
762 | |
763 Position HTMLTextFormControlElement::endOfWord(const Position& position) | |
764 { | |
765 const HTMLTextFormControlElement* textForm = enclosingTextFormControl(positi on); | |
766 ASSERT(textForm); | |
767 HTMLElement* innerText = textForm->innerEditorElement(); | |
768 | |
769 | |
770 const Position endPosition = endOfSentence(position); | |
771 if (endPosition == position) | |
772 return position; | |
773 const Position startPosition = (position.anchorNode() == innerText) ? innerN odePosition(position) : position; | |
774 | |
775 return findWordBoundary(innerText, startPosition, endPosition, false); | |
776 } | |
777 | |
778 static int findLastLineBreak(const Text& text, int offset) | |
779 { | |
780 for (; offset >= 0; offset--) { | |
yosin_UTC9
2014/07/03 04:05:10
nit: |--offset|
yoichio
2014/07/03 04:32:19
Done.
| |
781 if (text.data()[offset] == '\n') | |
782 return offset; | |
783 } | |
784 return -1; | |
785 } | |
786 | |
787 static Position endOfPrevious(const Node& node, HTMLElement* innerText) | |
788 { | |
789 Node* prev = NodeTraversal::previous(node, innerText); | |
790 if (!prev) | |
791 return Position(); | |
792 | |
793 if (prev->hasTagName(brTag)) | |
794 return Position(prev, Position::PositionIsAfterAnchor); | |
795 | |
796 if (prev->isTextNode()) { | |
797 Text* textNode = toText(prev); | |
798 return Position(textNode, textNode->length()); | |
799 } | |
800 | |
801 return Position(); | |
802 } | |
803 | |
804 static Position previousIfPositionIsAfterLineBreak(const Position& position, HTM LElement* innerText) | |
805 { | |
806 if (position.isNull()) | |
807 return Position(); | |
808 | |
809 // Move back if position is just after line break. | |
810 if (isHTMLBRElement(*position.anchorNode())) { | |
811 switch (position.anchorType()) { | |
812 case Position::PositionIsAfterAnchor: | |
813 return Position(position.anchorNode(), Position::PositionIsBeforeAnc hor); | |
814 case Position::PositionIsBeforeAnchor: | |
815 return previousIfPositionIsAfterLineBreak(endOfPrevious(*position.an chorNode(), innerText), innerText); | |
816 default: | |
817 ASSERT_NOT_REACHED(); | |
818 } | |
819 } else if (position.anchorNode()->isTextNode()) { | |
820 Text* textNode = toText(position.anchorNode()); | |
821 unsigned offset = position.offsetInContainerNode(); | |
822 if (textNode->length() == 0 || offset <= 0) { | |
823 return previousIfPositionIsAfterLineBreak(endOfPrevious(*position.an chorNode(), innerText), innerText); | |
824 } | |
825 | |
826 if (offset <= textNode->length() && textNode->data()[offset - 1] == '\n' ) { | |
827 return Position(textNode, offset - 1); | |
828 } | |
829 } | |
830 | |
831 return position; | |
832 } | |
833 | |
834 static inline Position startOfInnerText(const HTMLTextFormControlElement* textFo rm) | |
yosin_UTC9
2014/07/03 04:05:10
nit: |textForm| => |textFormControl|
yoichio
2014/07/03 04:32:19
Done.
| |
835 { | |
836 return Position(textForm->innerEditorElement(), 0, Position::PositionIsOffse tInAnchor); | |
837 } | |
838 | |
839 Position HTMLTextFormControlElement::startOfSentence(const Position& position) | |
840 { | |
841 HTMLTextFormControlElement* textForm = enclosingTextFormControl(position); | |
yosin_UTC9
2014/07/03 04:05:10
nit: |textForm| => |textFormControl|
yoichio
2014/07/03 04:32:19
Done.
| |
842 ASSERT(textForm); | |
843 | |
844 HTMLElement* innerText = textForm->innerEditorElement(); | |
845 if (!innerText->childNodes()->length()) | |
846 return startOfInnerText(textForm); | |
847 | |
848 const Position innerPosition = position.anchorNode() == innerText ? innerNod ePosition(position): position; | |
849 const Position pivotPosition = previousIfPositionIsAfterLineBreak(innerPosit ion, innerText); | |
850 if (pivotPosition.isNull()) | |
851 return startOfInnerText(textForm); | |
852 | |
853 for (Node* node = pivotPosition.anchorNode(); node; node = NodeTraversal::pr evious(*node, innerText)) { | |
854 bool isPivotNode = (node == pivotPosition.anchorNode()); | |
855 | |
856 if (node->isTextNode()) { | |
857 Text* textNode = toText(node); | |
858 int lastLineBreak = findLastLineBreak(*textNode, isPivotNode ? pivot Position.offsetInContainerNode() - 1 : (int)textNode->length() - 1); | |
859 if (lastLineBreak >= 0) | |
860 return Position(textNode, lastLineBreak + 1); | |
861 } else if (isHTMLBRElement(node) && (!isPivotNode || pivotPosition.ancho rType() == Position::PositionIsAfterAnchor)) { | |
862 return Position(node, Position::PositionIsAfterAnchor); | |
863 } | |
864 } | |
865 return startOfInnerText(textForm); | |
866 } | |
867 | |
868 static int findFirstLineBreak(const Text& text, int offset) | |
869 { | |
870 for (; (unsigned)offset < text.length(); offset++) { | |
871 if (text.data()[offset] == '\n') | |
872 return offset; | |
873 } | |
874 return -1; | |
875 } | |
876 | |
877 static Position endOfInnerText(const HTMLTextFormControlElement* textForm) | |
yosin_UTC9
2014/07/03 04:05:10
nit: |textForm| => |textFormControl|
yoichio
2014/07/03 04:32:19
Done.
| |
878 { | |
879 HTMLElement* innerText = textForm->innerEditorElement(); | |
yosin_UTC9
2014/07/03 04:05:10
nit: |innerText|=>|innerEditor|
yoichio
2014/07/03 04:32:19
Done.
| |
880 return Position(innerText, innerText->childNodes()->length(), Position::Posi tionIsOffsetInAnchor); | |
881 } | |
882 | |
883 Position HTMLTextFormControlElement::endOfSentence(const Position& position) | |
884 { | |
885 HTMLTextFormControlElement* textForm = enclosingTextFormControl(position); | |
886 ASSERT(textForm); | |
887 | |
888 HTMLElement* innerText = textForm->innerEditorElement(); | |
889 if (!innerText->childNodes()->length()) | |
890 return startOfInnerText(textForm); | |
891 | |
892 const Position pivotPosition = position.anchorNode() == innerText ? innerNod ePosition(position) : position; | |
893 if (pivotPosition.isNull()) | |
894 return startOfInnerText(textForm); | |
895 | |
896 for (Node* node = pivotPosition.anchorNode(); node; node = NodeTraversal::ne xt(*node, innerText)) { | |
897 bool isPivotNode = (node == pivotPosition.anchorNode()); | |
898 | |
899 if (node->isTextNode()) { | |
900 Text* textNode = toText(node); | |
901 int firstLineBreak = findFirstLineBreak(*textNode, isPivotNode ? piv otPosition.offsetInContainerNode() : 0); | |
902 if (firstLineBreak >= 0) | |
903 return Position(textNode, firstLineBreak + 1); | |
904 } else if (isHTMLBRElement(node)) { | |
905 return Position(node, Position::PositionIsAfterAnchor); | |
906 } | |
907 } | |
908 return endOfInnerText(textForm); | |
909 } | |
910 | |
662 } // namespace Webcore | 911 } // namespace Webcore |
OLD | NEW |