OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights | 2 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights |
3 * reserved. | 3 * reserved. |
4 * | 4 * |
5 * Redistribution and use in source and binary forms, with or without | 5 * Redistribution and use in source and binary forms, with or without |
6 * modification, are permitted provided that the following conditions | 6 * modification, are permitted provided that the following conditions |
7 * are met: | 7 * are met: |
8 * 1. Redistributions of source code must retain the above copyright | 8 * 1. Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * 2. Redistributions in binary form must reproduce the above copyright | 10 * 2. Redistributions in binary form must reproduce the above copyright |
(...skipping 798 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
809 // TODO(xiaochengh): |sentenceStart > range.startPosition()| is possible, | 809 // TODO(xiaochengh): |sentenceStart > range.startPosition()| is possible, |
810 // which would trigger a DCHECK in EphemeralRange's constructor if we return | 810 // which would trigger a DCHECK in EphemeralRange's constructor if we return |
811 // it directly. However, this shouldn't happen and needs to be fixed. | 811 // it directly. However, this shouldn't happen and needs to be fixed. |
812 return ExpandEndToSentenceBoundary(EphemeralRange( | 812 return ExpandEndToSentenceBoundary(EphemeralRange( |
813 sentence_start.IsNotNull() && sentence_start < range.StartPosition() | 813 sentence_start.IsNotNull() && sentence_start < range.StartPosition() |
814 ? sentence_start | 814 ? sentence_start |
815 : range.StartPosition(), | 815 : range.StartPosition(), |
816 range.EndPosition())); | 816 range.EndPosition())); |
817 } | 817 } |
818 | 818 |
819 static bool NodeIsUserSelectAll(const Node* node) { | |
820 return node && node->GetLayoutObject() && | |
821 node->GetLayoutObject()->Style()->UserSelect() == EUserSelect::kAll; | |
822 } | |
823 | |
824 template <typename Strategy> | |
825 PositionTemplate<Strategy> StartOfParagraphAlgorithm( | |
826 const PositionTemplate<Strategy>& position, | |
827 EditingBoundaryCrossingRule boundary_crossing_rule) { | |
828 Node* const start_node = position.AnchorNode(); | |
829 | |
830 if (!start_node) | |
831 return PositionTemplate<Strategy>(); | |
832 | |
833 if (IsRenderedAsNonInlineTableImageOrHR(start_node)) | |
834 return PositionTemplate<Strategy>::BeforeNode(start_node); | |
835 | |
836 Element* const start_block = EnclosingBlock( | |
837 PositionTemplate<Strategy>::FirstPositionInOrBeforeNode(start_node), | |
838 kCannotCrossEditingBoundary); | |
839 ContainerNode* const highest_root = HighestEditableRoot(position); | |
840 const bool start_node_is_editable = HasEditableStyle(*start_node); | |
841 | |
842 Node* candidate_node = start_node; | |
843 PositionAnchorType candidate_type = position.AnchorType(); | |
844 int candidate_offset = position.ComputeEditingOffset(); | |
845 | |
846 Node* previous_node_iterator = start_node; | |
847 while (previous_node_iterator) { | |
848 if (boundary_crossing_rule == kCannotCrossEditingBoundary && | |
849 !NodeIsUserSelectAll(previous_node_iterator) && | |
850 HasEditableStyle(*previous_node_iterator) != start_node_is_editable) | |
851 break; | |
852 if (boundary_crossing_rule == kCanSkipOverEditingBoundary) { | |
853 while (previous_node_iterator && | |
854 HasEditableStyle(*previous_node_iterator) != | |
855 start_node_is_editable) { | |
856 previous_node_iterator = | |
857 Strategy::PreviousPostOrder(*previous_node_iterator, start_block); | |
858 } | |
859 if (!previous_node_iterator || | |
860 !previous_node_iterator->IsDescendantOf(highest_root)) | |
861 break; | |
862 } | |
863 | |
864 const LayoutItem layout_item = | |
865 LayoutItem(previous_node_iterator->GetLayoutObject()); | |
866 if (layout_item.IsNull()) { | |
867 previous_node_iterator = | |
868 Strategy::PreviousPostOrder(*previous_node_iterator, start_block); | |
869 continue; | |
870 } | |
871 const ComputedStyle& style = layout_item.StyleRef(); | |
872 if (style.Visibility() != EVisibility::kVisible) { | |
873 previous_node_iterator = | |
874 Strategy::PreviousPostOrder(*previous_node_iterator, start_block); | |
875 continue; | |
876 } | |
877 | |
878 if (layout_item.IsBR() || IsEnclosingBlock(previous_node_iterator)) | |
879 break; | |
880 | |
881 if (layout_item.IsText() && | |
882 ToLayoutText(previous_node_iterator->GetLayoutObject()) | |
883 ->ResolvedTextLength()) { | |
884 SECURITY_DCHECK(previous_node_iterator->IsTextNode()); | |
885 if (style.PreserveNewline()) { | |
886 LayoutText* text = | |
887 ToLayoutText(previous_node_iterator->GetLayoutObject()); | |
888 int index = text->TextLength(); | |
889 if (previous_node_iterator == start_node && candidate_offset < index) | |
890 index = max(0, candidate_offset); | |
891 while (--index >= 0) { | |
892 if ((*text)[index] == '\n') | |
893 return PositionTemplate<Strategy>(ToText(previous_node_iterator), | |
894 index + 1); | |
895 } | |
896 } | |
897 candidate_node = previous_node_iterator; | |
898 candidate_type = PositionAnchorType::kOffsetInAnchor; | |
899 candidate_offset = 0; | |
900 previous_node_iterator = | |
901 Strategy::PreviousPostOrder(*previous_node_iterator, start_block); | |
902 } else if (EditingIgnoresContent(*previous_node_iterator) || | |
903 IsDisplayInsideTable(previous_node_iterator)) { | |
904 candidate_node = previous_node_iterator; | |
905 candidate_type = PositionAnchorType::kBeforeAnchor; | |
906 previous_node_iterator = previous_node_iterator->previousSibling() | |
907 ? previous_node_iterator->previousSibling() | |
908 : Strategy::PreviousPostOrder( | |
909 *previous_node_iterator, start_block); | |
910 } else { | |
911 previous_node_iterator = | |
912 Strategy::PreviousPostOrder(*previous_node_iterator, start_block); | |
913 } | |
914 } | |
915 | |
916 if (candidate_type == PositionAnchorType::kOffsetInAnchor) | |
917 return PositionTemplate<Strategy>(candidate_node, candidate_offset); | |
918 | |
919 return PositionTemplate<Strategy>(candidate_node, candidate_type); | |
920 } | |
921 | |
922 template <typename Strategy> | |
923 VisiblePositionTemplate<Strategy> StartOfParagraphAlgorithm( | |
924 const VisiblePositionTemplate<Strategy>& visible_position, | |
925 EditingBoundaryCrossingRule boundary_crossing_rule) { | |
926 DCHECK(visible_position.IsValid()) << visible_position; | |
927 return CreateVisiblePosition(StartOfParagraphAlgorithm( | |
928 visible_position.DeepEquivalent(), boundary_crossing_rule)); | |
929 } | |
930 | |
931 VisiblePosition StartOfParagraph( | |
932 const VisiblePosition& c, | |
933 EditingBoundaryCrossingRule boundary_crossing_rule) { | |
934 return StartOfParagraphAlgorithm<EditingStrategy>(c, boundary_crossing_rule); | |
935 } | |
936 | |
937 VisiblePositionInFlatTree StartOfParagraph( | |
938 const VisiblePositionInFlatTree& c, | |
939 EditingBoundaryCrossingRule boundary_crossing_rule) { | |
940 return StartOfParagraphAlgorithm<EditingInFlatTreeStrategy>( | |
941 c, boundary_crossing_rule); | |
942 } | |
943 | |
944 template <typename Strategy> | |
945 static PositionTemplate<Strategy> EndOfParagraphAlgorithm( | |
946 const PositionTemplate<Strategy>& position, | |
947 EditingBoundaryCrossingRule boundary_crossing_rule) { | |
948 Node* const start_node = position.AnchorNode(); | |
949 | |
950 if (!start_node) | |
951 return PositionTemplate<Strategy>(); | |
952 | |
953 if (IsRenderedAsNonInlineTableImageOrHR(start_node)) | |
954 return PositionTemplate<Strategy>::AfterNode(start_node); | |
955 | |
956 Element* const start_block = EnclosingBlock( | |
957 PositionTemplate<Strategy>::FirstPositionInOrBeforeNode(start_node), | |
958 kCannotCrossEditingBoundary); | |
959 ContainerNode* const highest_root = HighestEditableRoot(position); | |
960 const bool start_node_is_editable = HasEditableStyle(*start_node); | |
961 | |
962 Node* candidate_node = start_node; | |
963 PositionAnchorType candidate_type = position.AnchorType(); | |
964 int candidate_offset = position.ComputeEditingOffset(); | |
965 | |
966 Node* next_node_iterator = start_node; | |
967 while (next_node_iterator) { | |
968 if (boundary_crossing_rule == kCannotCrossEditingBoundary && | |
969 !NodeIsUserSelectAll(next_node_iterator) && | |
970 HasEditableStyle(*next_node_iterator) != start_node_is_editable) | |
971 break; | |
972 if (boundary_crossing_rule == kCanSkipOverEditingBoundary) { | |
973 while (next_node_iterator && | |
974 HasEditableStyle(*next_node_iterator) != start_node_is_editable) | |
975 next_node_iterator = Strategy::Next(*next_node_iterator, start_block); | |
976 if (!next_node_iterator || | |
977 !next_node_iterator->IsDescendantOf(highest_root)) | |
978 break; | |
979 } | |
980 | |
981 LayoutObject* const layout_object = next_node_iterator->GetLayoutObject(); | |
982 if (!layout_object) { | |
983 next_node_iterator = Strategy::Next(*next_node_iterator, start_block); | |
984 continue; | |
985 } | |
986 const ComputedStyle& style = layout_object->StyleRef(); | |
987 if (style.Visibility() != EVisibility::kVisible) { | |
988 next_node_iterator = Strategy::Next(*next_node_iterator, start_block); | |
989 continue; | |
990 } | |
991 | |
992 if (layout_object->IsBR() || IsEnclosingBlock(next_node_iterator)) | |
993 break; | |
994 | |
995 // FIXME: We avoid returning a position where the layoutObject can't accept | |
996 // the caret. | |
997 if (layout_object->IsText() && | |
998 ToLayoutText(layout_object)->ResolvedTextLength()) { | |
999 SECURITY_DCHECK(next_node_iterator->IsTextNode()); | |
1000 LayoutText* const text = ToLayoutText(layout_object); | |
1001 if (style.PreserveNewline()) { | |
1002 const int length = ToLayoutText(layout_object)->TextLength(); | |
1003 for (int i = (next_node_iterator == start_node ? candidate_offset : 0); | |
1004 i < length; ++i) { | |
1005 if ((*text)[i] == '\n') | |
1006 return PositionTemplate<Strategy>(ToText(next_node_iterator), | |
1007 i + text->TextStartOffset()); | |
1008 } | |
1009 } | |
1010 | |
1011 candidate_node = next_node_iterator; | |
1012 candidate_type = PositionAnchorType::kOffsetInAnchor; | |
1013 candidate_offset = | |
1014 layout_object->CaretMaxOffset() + text->TextStartOffset(); | |
1015 next_node_iterator = Strategy::Next(*next_node_iterator, start_block); | |
1016 } else if (EditingIgnoresContent(*next_node_iterator) || | |
1017 IsDisplayInsideTable(next_node_iterator)) { | |
1018 candidate_node = next_node_iterator; | |
1019 candidate_type = PositionAnchorType::kAfterAnchor; | |
1020 next_node_iterator = | |
1021 Strategy::NextSkippingChildren(*next_node_iterator, start_block); | |
1022 } else { | |
1023 next_node_iterator = Strategy::Next(*next_node_iterator, start_block); | |
1024 } | |
1025 } | |
1026 | |
1027 if (candidate_type == PositionAnchorType::kOffsetInAnchor) | |
1028 return PositionTemplate<Strategy>(candidate_node, candidate_offset); | |
1029 | |
1030 return PositionTemplate<Strategy>(candidate_node, candidate_type); | |
1031 } | |
1032 | |
1033 template <typename Strategy> | |
1034 static VisiblePositionTemplate<Strategy> EndOfParagraphAlgorithm( | |
1035 const VisiblePositionTemplate<Strategy>& visible_position, | |
1036 EditingBoundaryCrossingRule boundary_crossing_rule) { | |
1037 DCHECK(visible_position.IsValid()) << visible_position; | |
1038 return CreateVisiblePosition(EndOfParagraphAlgorithm( | |
1039 visible_position.DeepEquivalent(), boundary_crossing_rule)); | |
1040 } | |
1041 | |
1042 VisiblePosition EndOfParagraph( | |
1043 const VisiblePosition& c, | |
1044 EditingBoundaryCrossingRule boundary_crossing_rule) { | |
1045 return EndOfParagraphAlgorithm<EditingStrategy>(c, boundary_crossing_rule); | |
1046 } | |
1047 | |
1048 VisiblePositionInFlatTree EndOfParagraph( | |
1049 const VisiblePositionInFlatTree& c, | |
1050 EditingBoundaryCrossingRule boundary_crossing_rule) { | |
1051 return EndOfParagraphAlgorithm<EditingInFlatTreeStrategy>( | |
1052 c, boundary_crossing_rule); | |
1053 } | |
1054 | |
1055 // FIXME: isStartOfParagraph(startOfNextParagraph(pos)) is not always true | |
1056 VisiblePosition StartOfNextParagraph(const VisiblePosition& visible_position) { | |
1057 DCHECK(visible_position.IsValid()) << visible_position; | |
1058 VisiblePosition paragraph_end( | |
1059 EndOfParagraph(visible_position, kCanSkipOverEditingBoundary)); | |
1060 VisiblePosition after_paragraph_end( | |
1061 NextPositionOf(paragraph_end, kCannotCrossEditingBoundary)); | |
1062 // The position after the last position in the last cell of a table | |
1063 // is not the start of the next paragraph. | |
1064 if (TableElementJustBefore(after_paragraph_end)) | |
1065 return NextPositionOf(after_paragraph_end, kCannotCrossEditingBoundary); | |
1066 return after_paragraph_end; | |
1067 } | |
1068 | |
1069 // FIXME: isStartOfParagraph(startOfNextParagraph(pos)) is not always true | |
1070 bool InSameParagraph(const VisiblePosition& a, | |
1071 const VisiblePosition& b, | |
1072 EditingBoundaryCrossingRule boundary_crossing_rule) { | |
1073 DCHECK(a.IsValid()) << a; | |
1074 DCHECK(b.IsValid()) << b; | |
1075 return a.IsNotNull() && | |
1076 StartOfParagraph(a, boundary_crossing_rule).DeepEquivalent() == | |
1077 StartOfParagraph(b, boundary_crossing_rule).DeepEquivalent(); | |
1078 } | |
1079 | |
1080 template <typename Strategy> | |
1081 static bool IsStartOfParagraphAlgorithm( | |
1082 const VisiblePositionTemplate<Strategy>& pos, | |
1083 EditingBoundaryCrossingRule boundary_crossing_rule) { | |
1084 DCHECK(pos.IsValid()) << pos; | |
1085 return pos.IsNotNull() && | |
1086 pos.DeepEquivalent() == | |
1087 StartOfParagraph(pos, boundary_crossing_rule).DeepEquivalent(); | |
1088 } | |
1089 | |
1090 bool IsStartOfParagraph(const VisiblePosition& pos, | |
1091 EditingBoundaryCrossingRule boundary_crossing_rule) { | |
1092 return IsStartOfParagraphAlgorithm<EditingStrategy>(pos, | |
1093 boundary_crossing_rule); | |
1094 } | |
1095 | |
1096 bool IsStartOfParagraph(const VisiblePositionInFlatTree& pos, | |
1097 EditingBoundaryCrossingRule boundary_crossing_rule) { | |
1098 return IsStartOfParagraphAlgorithm<EditingInFlatTreeStrategy>( | |
1099 pos, boundary_crossing_rule); | |
1100 } | |
1101 | |
1102 template <typename Strategy> | |
1103 static bool IsEndOfParagraphAlgorithm( | |
1104 const VisiblePositionTemplate<Strategy>& pos, | |
1105 EditingBoundaryCrossingRule boundary_crossing_rule) { | |
1106 DCHECK(pos.IsValid()) << pos; | |
1107 return pos.IsNotNull() && | |
1108 pos.DeepEquivalent() == | |
1109 EndOfParagraph(pos, boundary_crossing_rule).DeepEquivalent(); | |
1110 } | |
1111 | |
1112 bool IsEndOfParagraph(const VisiblePosition& pos, | |
1113 EditingBoundaryCrossingRule boundary_crossing_rule) { | |
1114 return IsEndOfParagraphAlgorithm<EditingStrategy>(pos, | |
1115 boundary_crossing_rule); | |
1116 } | |
1117 | |
1118 bool IsEndOfParagraph(const VisiblePositionInFlatTree& pos, | |
1119 EditingBoundaryCrossingRule boundary_crossing_rule) { | |
1120 return IsEndOfParagraphAlgorithm<EditingInFlatTreeStrategy>( | |
1121 pos, boundary_crossing_rule); | |
1122 } | |
1123 | |
1124 VisiblePosition PreviousParagraphPosition(const VisiblePosition& p, | |
1125 LayoutUnit x) { | |
1126 DCHECK(p.IsValid()) << p; | |
1127 VisiblePosition pos = p; | |
1128 do { | |
1129 VisiblePosition n = PreviousLinePosition(pos, x); | |
1130 if (n.IsNull() || n.DeepEquivalent() == pos.DeepEquivalent()) | |
1131 break; | |
1132 pos = n; | |
1133 } while (InSameParagraph(p, pos)); | |
1134 return pos; | |
1135 } | |
1136 | |
1137 VisiblePosition NextParagraphPosition(const VisiblePosition& p, LayoutUnit x) { | |
1138 DCHECK(p.IsValid()) << p; | |
1139 VisiblePosition pos = p; | |
1140 do { | |
1141 VisiblePosition n = NextLinePosition(pos, x); | |
1142 if (n.IsNull() || n.DeepEquivalent() == pos.DeepEquivalent()) | |
1143 break; | |
1144 pos = n; | |
1145 } while (InSameParagraph(p, pos)); | |
1146 return pos; | |
1147 } | |
1148 | |
1149 EphemeralRange ExpandToParagraphBoundary(const EphemeralRange& range) { | |
1150 const VisiblePosition& start = CreateVisiblePosition(range.StartPosition()); | |
1151 DCHECK(start.IsNotNull()) << range.StartPosition(); | |
1152 const Position& paragraph_start = StartOfParagraph(start).DeepEquivalent(); | |
1153 DCHECK(paragraph_start.IsNotNull()) << range.StartPosition(); | |
1154 | |
1155 const VisiblePosition& end = CreateVisiblePosition(range.EndPosition()); | |
1156 DCHECK(end.IsNotNull()) << range.EndPosition(); | |
1157 const Position& paragraph_end = EndOfParagraph(end).DeepEquivalent(); | |
1158 DCHECK(paragraph_end.IsNotNull()) << range.EndPosition(); | |
1159 | |
1160 // TODO(xiaochengh): There are some cases (crbug.com/640112) where we get | |
1161 // |paragraphStart > paragraphEnd|, which is the reason we cannot directly | |
1162 // return |EphemeralRange(paragraphStart, paragraphEnd)|. This is not | |
1163 // desired, though. We should do more investigation to ensure that why | |
1164 // |paragraphStart <= paragraphEnd| is violated. | |
1165 const Position& result_start = | |
1166 paragraph_start.IsNotNull() && paragraph_start <= range.StartPosition() | |
1167 ? paragraph_start | |
1168 : range.StartPosition(); | |
1169 const Position& result_end = | |
1170 paragraph_end.IsNotNull() && paragraph_end >= range.EndPosition() | |
1171 ? paragraph_end | |
1172 : range.EndPosition(); | |
1173 return EphemeralRange(result_start, result_end); | |
1174 } | |
1175 | |
1176 // --------- | 819 // --------- |
1177 | 820 |
1178 VisiblePosition StartOfBlock(const VisiblePosition& visible_position, | 821 VisiblePosition StartOfBlock(const VisiblePosition& visible_position, |
1179 EditingBoundaryCrossingRule rule) { | 822 EditingBoundaryCrossingRule rule) { |
1180 DCHECK(visible_position.IsValid()) << visible_position; | 823 DCHECK(visible_position.IsValid()) << visible_position; |
1181 Position position = visible_position.DeepEquivalent(); | 824 Position position = visible_position.DeepEquivalent(); |
1182 Element* start_block = | 825 Element* start_block = |
1183 position.ComputeContainerNode() | 826 position.ComputeContainerNode() |
1184 ? EnclosingBlock(position.ComputeContainerNode(), rule) | 827 ? EnclosingBlock(position.ComputeContainerNode(), rule) |
1185 : 0; | 828 : 0; |
(...skipping 1951 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3137 | 2780 |
3138 IntRect ComputeTextRect(const EphemeralRangeInFlatTree& range) { | 2781 IntRect ComputeTextRect(const EphemeralRangeInFlatTree& range) { |
3139 return EnclosingIntRect(ComputeTextRectTemplate(range)); | 2782 return EnclosingIntRect(ComputeTextRectTemplate(range)); |
3140 } | 2783 } |
3141 | 2784 |
3142 FloatRect ComputeTextFloatRect(const EphemeralRange& range) { | 2785 FloatRect ComputeTextFloatRect(const EphemeralRange& range) { |
3143 return ComputeTextRectTemplate(range); | 2786 return ComputeTextRectTemplate(range); |
3144 } | 2787 } |
3145 | 2788 |
3146 } // namespace blink | 2789 } // namespace blink |
OLD | NEW |