| 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 |