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 147 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
158 | 158 |
159 Position CanonicalPositionOf(const Position& position) { | 159 Position CanonicalPositionOf(const Position& position) { |
160 return CanonicalPosition(position); | 160 return CanonicalPosition(position); |
161 } | 161 } |
162 | 162 |
163 PositionInFlatTree CanonicalPositionOf(const PositionInFlatTree& position) { | 163 PositionInFlatTree CanonicalPositionOf(const PositionInFlatTree& position) { |
164 return CanonicalPosition(position); | 164 return CanonicalPosition(position); |
165 } | 165 } |
166 | 166 |
167 template <typename Strategy> | 167 template <typename Strategy> |
168 static PositionWithAffinityTemplate<Strategy> HonorEditingBoundaryAtOrBefore( | 168 static PositionWithAffinityTemplate<Strategy> |
| 169 HonorEditingBoundaryAtOrBeforeTemplate( |
169 const PositionWithAffinityTemplate<Strategy>& pos, | 170 const PositionWithAffinityTemplate<Strategy>& pos, |
170 const PositionTemplate<Strategy>& anchor) { | 171 const PositionTemplate<Strategy>& anchor) { |
171 if (pos.IsNull()) | 172 if (pos.IsNull()) |
172 return pos; | 173 return pos; |
173 | 174 |
174 ContainerNode* highest_root = HighestEditableRoot(anchor); | 175 ContainerNode* highest_root = HighestEditableRoot(anchor); |
175 | 176 |
176 // Return empty position if |pos| is not somewhere inside the editable | 177 // Return empty position if |pos| is not somewhere inside the editable |
177 // region containing this position | 178 // region containing this position |
178 if (highest_root && !pos.AnchorNode()->IsDescendantOf(highest_root)) | 179 if (highest_root && !pos.AnchorNode()->IsDescendantOf(highest_root)) |
(...skipping 12 matching lines...) Expand all Loading... |
191 // TODO(yosin) Move to the previous non-editable region. | 192 // TODO(yosin) Move to the previous non-editable region. |
192 if (!highest_root) | 193 if (!highest_root) |
193 return PositionWithAffinityTemplate<Strategy>(); | 194 return PositionWithAffinityTemplate<Strategy>(); |
194 | 195 |
195 // Return the last position before |pos| that is in the same editable region | 196 // Return the last position before |pos| that is in the same editable region |
196 // as this position | 197 // as this position |
197 return LastEditablePositionBeforePositionInRoot(pos.GetPosition(), | 198 return LastEditablePositionBeforePositionInRoot(pos.GetPosition(), |
198 *highest_root); | 199 *highest_root); |
199 } | 200 } |
200 | 201 |
| 202 PositionWithAffinity HonorEditingBoundaryAtOrBefore( |
| 203 const PositionWithAffinity& pos, |
| 204 const Position& anchor) { |
| 205 return HonorEditingBoundaryAtOrBeforeTemplate(pos, anchor); |
| 206 } |
| 207 |
| 208 PositionInFlatTreeWithAffinity HonorEditingBoundaryAtOrBefore( |
| 209 const PositionInFlatTreeWithAffinity& pos, |
| 210 const PositionInFlatTree& anchor) { |
| 211 return HonorEditingBoundaryAtOrBeforeTemplate(pos, anchor); |
| 212 } |
| 213 |
201 template <typename Strategy> | 214 template <typename Strategy> |
202 VisiblePositionTemplate<Strategy> HonorEditingBoundaryAtOrBeforeAlgorithm( | 215 VisiblePositionTemplate<Strategy> HonorEditingBoundaryAtOrBeforeAlgorithm( |
203 const VisiblePositionTemplate<Strategy>& pos, | 216 const VisiblePositionTemplate<Strategy>& pos, |
204 const PositionTemplate<Strategy>& anchor) { | 217 const PositionTemplate<Strategy>& anchor) { |
205 DCHECK(pos.IsValid()) << pos; | 218 DCHECK(pos.IsValid()) << pos; |
206 return CreateVisiblePosition( | 219 return CreateVisiblePosition( |
207 HonorEditingBoundaryAtOrBefore(pos.ToPositionWithAffinity(), anchor)); | 220 HonorEditingBoundaryAtOrBefore(pos.ToPositionWithAffinity(), anchor)); |
208 } | 221 } |
209 | 222 |
210 VisiblePosition HonorEditingBoundaryAtOrBefore( | 223 VisiblePosition HonorEditingBoundaryAtOrBefore( |
211 const VisiblePosition& visiblePosition, | 224 const VisiblePosition& visiblePosition, |
212 const Position& anchor) { | 225 const Position& anchor) { |
213 return HonorEditingBoundaryAtOrBeforeAlgorithm(visiblePosition, anchor); | 226 return HonorEditingBoundaryAtOrBeforeAlgorithm(visiblePosition, anchor); |
214 } | 227 } |
215 | 228 |
216 VisiblePositionInFlatTree HonorEditingBoundaryAtOrBefore( | 229 VisiblePositionInFlatTree HonorEditingBoundaryAtOrBefore( |
217 const VisiblePositionInFlatTree& visiblePosition, | 230 const VisiblePositionInFlatTree& visiblePosition, |
218 const PositionInFlatTree& anchor) { | 231 const PositionInFlatTree& anchor) { |
219 return HonorEditingBoundaryAtOrBeforeAlgorithm(visiblePosition, anchor); | 232 return HonorEditingBoundaryAtOrBeforeAlgorithm(visiblePosition, anchor); |
220 } | 233 } |
221 | 234 |
222 template <typename Strategy> | 235 template <typename Strategy> |
223 static VisiblePositionTemplate<Strategy> HonorEditingBoundaryAtOrAfter( | 236 static VisiblePositionTemplate<Strategy> HonorEditingBoundaryAtOrAfterTemplate( |
224 const VisiblePositionTemplate<Strategy>& pos, | 237 const VisiblePositionTemplate<Strategy>& pos, |
225 const PositionTemplate<Strategy>& anchor) { | 238 const PositionTemplate<Strategy>& anchor) { |
226 DCHECK(pos.IsValid()) << pos; | 239 DCHECK(pos.IsValid()) << pos; |
227 if (pos.IsNull()) | 240 if (pos.IsNull()) |
228 return pos; | 241 return pos; |
229 | 242 |
230 ContainerNode* highest_root = HighestEditableRoot(anchor); | 243 ContainerNode* highest_root = HighestEditableRoot(anchor); |
231 | 244 |
232 // Return empty position if |pos| is not somewhere inside the editable | 245 // Return empty position if |pos| is not somewhere inside the editable |
233 // region containing this position | 246 // region containing this position |
(...skipping 14 matching lines...) Expand all Loading... |
248 // TODO(yosin) Move to the next non-editable region. | 261 // TODO(yosin) Move to the next non-editable region. |
249 if (!highest_root) | 262 if (!highest_root) |
250 return VisiblePositionTemplate<Strategy>(); | 263 return VisiblePositionTemplate<Strategy>(); |
251 | 264 |
252 // Return the next position after |pos| that is in the same editable region | 265 // Return the next position after |pos| that is in the same editable region |
253 // as this position | 266 // as this position |
254 return FirstEditableVisiblePositionAfterPositionInRoot(pos.DeepEquivalent(), | 267 return FirstEditableVisiblePositionAfterPositionInRoot(pos.DeepEquivalent(), |
255 *highest_root); | 268 *highest_root); |
256 } | 269 } |
257 | 270 |
258 static bool HasEditableStyle(const Node& node, EditableType editable_type) { | 271 VisiblePosition HonorEditingBoundaryAtOrAfter(const VisiblePosition& pos, |
259 if (editable_type == kHasEditableAXRole) { | 272 const Position& anchor) { |
260 if (AXObjectCache* cache = node.GetDocument().ExistingAXObjectCache()) { | 273 return HonorEditingBoundaryAtOrAfterTemplate(pos, anchor); |
261 if (cache->RootAXEditableElement(&node)) | |
262 return true; | |
263 } | |
264 } | |
265 | |
266 return HasEditableStyle(node); | |
267 } | 274 } |
268 | 275 |
269 static Element* RootEditableElement(const Node& node, | 276 VisiblePositionInFlatTree HonorEditingBoundaryAtOrAfter( |
270 EditableType editable_type) { | 277 const VisiblePositionInFlatTree& pos, |
271 if (editable_type == kHasEditableAXRole) { | 278 const PositionInFlatTree& anchor) { |
272 if (AXObjectCache* cache = node.GetDocument().ExistingAXObjectCache()) | 279 return HonorEditingBoundaryAtOrAfterTemplate(pos, anchor); |
273 return const_cast<Element*>(cache->RootAXEditableElement(&node)); | |
274 } | |
275 | |
276 return RootEditableElement(node); | |
277 } | |
278 | |
279 static Element* RootAXEditableElementOf(const Position& position) { | |
280 Node* node = position.ComputeContainerNode(); | |
281 if (!node) | |
282 return 0; | |
283 | |
284 if (IsDisplayInsideTable(node)) | |
285 node = node->parentNode(); | |
286 | |
287 return RootEditableElement(*node, kHasEditableAXRole); | |
288 } | |
289 | |
290 static bool HasAXEditableStyle(const Node& node) { | |
291 return HasEditableStyle(node, kHasEditableAXRole); | |
292 } | |
293 | |
294 static ContainerNode* HighestEditableRoot(const Position& position, | |
295 EditableType editable_type) { | |
296 if (editable_type == kHasEditableAXRole) | |
297 return HighestEditableRoot(position, RootAXEditableElementOf, | |
298 HasAXEditableStyle); | |
299 | |
300 return HighestEditableRoot(position); | |
301 } | |
302 | |
303 static Node* PreviousLeafWithSameEditability(Node* node, | |
304 EditableType editable_type) { | |
305 bool editable = HasEditableStyle(*node, editable_type); | |
306 node = PreviousAtomicLeafNode(*node); | |
307 while (node) { | |
308 if (editable == HasEditableStyle(*node, editable_type)) | |
309 return node; | |
310 node = PreviousAtomicLeafNode(*node); | |
311 } | |
312 return 0; | |
313 } | |
314 | |
315 static Node* NextLeafWithSameEditability( | |
316 Node* node, | |
317 EditableType editable_type = kContentIsEditable) { | |
318 if (!node) | |
319 return 0; | |
320 | |
321 bool editable = HasEditableStyle(*node, editable_type); | |
322 node = NextAtomicLeafNode(*node); | |
323 while (node) { | |
324 if (editable == HasEditableStyle(*node, editable_type)) | |
325 return node; | |
326 node = NextAtomicLeafNode(*node); | |
327 } | |
328 return 0; | |
329 } | |
330 | |
331 // FIXME: consolidate with code in previousLinePosition. | |
332 Position PreviousRootInlineBoxCandidatePosition( | |
333 Node* node, | |
334 const VisiblePosition& visible_position, | |
335 EditableType editable_type) { | |
336 DCHECK(visible_position.IsValid()) << visible_position; | |
337 ContainerNode* highest_root = | |
338 HighestEditableRoot(visible_position.DeepEquivalent(), editable_type); | |
339 Node* previous_node = PreviousLeafWithSameEditability(node, editable_type); | |
340 | |
341 while (previous_node && | |
342 (!previous_node->GetLayoutObject() || | |
343 InSameLine( | |
344 CreateVisiblePosition(FirstPositionInOrBeforeNode(previous_node)), | |
345 visible_position))) | |
346 previous_node = | |
347 PreviousLeafWithSameEditability(previous_node, editable_type); | |
348 | |
349 while (previous_node && !previous_node->IsShadowRoot()) { | |
350 if (HighestEditableRoot(FirstPositionInOrBeforeNode(previous_node), | |
351 editable_type) != highest_root) | |
352 break; | |
353 | |
354 Position pos = isHTMLBRElement(*previous_node) | |
355 ? Position::BeforeNode(previous_node) | |
356 : Position::EditingPositionOf( | |
357 previous_node, CaretMaxOffset(previous_node)); | |
358 | |
359 if (IsVisuallyEquivalentCandidate(pos)) | |
360 return pos; | |
361 | |
362 previous_node = | |
363 PreviousLeafWithSameEditability(previous_node, editable_type); | |
364 } | |
365 return Position(); | |
366 } | |
367 | |
368 Position NextRootInlineBoxCandidatePosition( | |
369 Node* node, | |
370 const VisiblePosition& visible_position, | |
371 EditableType editable_type) { | |
372 DCHECK(visible_position.IsValid()) << visible_position; | |
373 ContainerNode* highest_root = | |
374 HighestEditableRoot(visible_position.DeepEquivalent(), editable_type); | |
375 Node* next_node = NextLeafWithSameEditability(node, editable_type); | |
376 while (next_node && (!next_node->GetLayoutObject() || | |
377 InSameLine(CreateVisiblePosition( | |
378 FirstPositionInOrBeforeNode(next_node)), | |
379 visible_position))) | |
380 next_node = NextLeafWithSameEditability(next_node, kContentIsEditable); | |
381 | |
382 while (next_node && !next_node->IsShadowRoot()) { | |
383 if (HighestEditableRoot(FirstPositionInOrBeforeNode(next_node), | |
384 editable_type) != highest_root) | |
385 break; | |
386 | |
387 Position pos; | |
388 pos = Position::EditingPositionOf(next_node, CaretMinOffset(next_node)); | |
389 | |
390 if (IsVisuallyEquivalentCandidate(pos)) | |
391 return pos; | |
392 | |
393 next_node = NextLeafWithSameEditability(next_node, editable_type); | |
394 } | |
395 return Position(); | |
396 } | 280 } |
397 | 281 |
398 template <typename Strategy> | 282 template <typename Strategy> |
399 static ContainerNode* NonShadowBoundaryParentNode(Node* node) { | 283 static ContainerNode* NonShadowBoundaryParentNode(Node* node) { |
400 ContainerNode* parent = Strategy::Parent(*node); | 284 ContainerNode* parent = Strategy::Parent(*node); |
401 return parent && !parent->IsShadowRoot() ? parent : nullptr; | 285 return parent && !parent->IsShadowRoot() ? parent : nullptr; |
402 } | 286 } |
403 | 287 |
404 template <typename Strategy> | 288 template <typename Strategy> |
405 static Node* ParentEditingBoundary(const PositionTemplate<Strategy>& position) { | 289 static Node* ParentEditingBoundary(const PositionTemplate<Strategy>& position) { |
(...skipping 396 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
802 | 686 |
803 VisiblePosition NextWordPosition(const VisiblePosition& c) { | 687 VisiblePosition NextWordPosition(const VisiblePosition& c) { |
804 DCHECK(c.IsValid()) << c; | 688 DCHECK(c.IsValid()) << c; |
805 VisiblePosition next = CreateVisiblePosition( | 689 VisiblePosition next = CreateVisiblePosition( |
806 NextBoundary(c, NextWordPositionBoundary), VP_UPSTREAM_IF_POSSIBLE); | 690 NextBoundary(c, NextWordPositionBoundary), VP_UPSTREAM_IF_POSSIBLE); |
807 return HonorEditingBoundaryAtOrAfter(next, c.DeepEquivalent()); | 691 return HonorEditingBoundaryAtOrAfter(next, c.DeepEquivalent()); |
808 } | 692 } |
809 | 693 |
810 // --------- | 694 // --------- |
811 | 695 |
812 enum LineEndpointComputationMode { kUseLogicalOrdering, kUseInlineBoxOrdering }; | |
813 template <typename Strategy> | |
814 static PositionWithAffinityTemplate<Strategy> StartPositionForLine( | |
815 const PositionWithAffinityTemplate<Strategy>& c, | |
816 LineEndpointComputationMode mode) { | |
817 if (c.IsNull()) | |
818 return PositionWithAffinityTemplate<Strategy>(); | |
819 | |
820 RootInlineBox* root_box = | |
821 RenderedPosition(c.GetPosition(), c.Affinity()).RootBox(); | |
822 if (!root_box) { | |
823 // There are VisiblePositions at offset 0 in blocks without | |
824 // RootInlineBoxes, like empty editable blocks and bordered blocks. | |
825 PositionTemplate<Strategy> p = c.GetPosition(); | |
826 if (p.AnchorNode()->GetLayoutObject() && | |
827 p.AnchorNode()->GetLayoutObject()->IsLayoutBlock() && | |
828 !p.ComputeEditingOffset()) | |
829 return c; | |
830 | |
831 return PositionWithAffinityTemplate<Strategy>(); | |
832 } | |
833 | |
834 Node* start_node; | |
835 InlineBox* start_box; | |
836 if (mode == kUseLogicalOrdering) { | |
837 start_node = root_box->GetLogicalStartBoxWithNode(start_box); | |
838 if (!start_node) | |
839 return PositionWithAffinityTemplate<Strategy>(); | |
840 } else { | |
841 // Generated content (e.g. list markers and CSS :before and :after | |
842 // pseudoelements) have no corresponding DOM element, and so cannot be | |
843 // represented by a VisiblePosition. Use whatever follows instead. | |
844 start_box = root_box->FirstLeafChild(); | |
845 while (true) { | |
846 if (!start_box) | |
847 return PositionWithAffinityTemplate<Strategy>(); | |
848 | |
849 start_node = start_box->GetLineLayoutItem().NonPseudoNode(); | |
850 if (start_node) | |
851 break; | |
852 | |
853 start_box = start_box->NextLeafChild(); | |
854 } | |
855 } | |
856 | |
857 return PositionWithAffinityTemplate<Strategy>( | |
858 start_node->IsTextNode() | |
859 ? PositionTemplate<Strategy>(ToText(start_node), | |
860 ToInlineTextBox(start_box)->Start()) | |
861 : PositionTemplate<Strategy>::BeforeNode(start_node)); | |
862 } | |
863 | |
864 template <typename Strategy> | |
865 static PositionWithAffinityTemplate<Strategy> StartOfLineAlgorithm( | |
866 const PositionWithAffinityTemplate<Strategy>& c) { | |
867 // TODO: this is the current behavior that might need to be fixed. | |
868 // Please refer to https://bugs.webkit.org/show_bug.cgi?id=49107 for detail. | |
869 PositionWithAffinityTemplate<Strategy> vis_pos = | |
870 StartPositionForLine(c, kUseInlineBoxOrdering); | |
871 return HonorEditingBoundaryAtOrBefore(vis_pos, c.GetPosition()); | |
872 } | |
873 | |
874 static PositionWithAffinity StartOfLine( | |
875 const PositionWithAffinity& current_position) { | |
876 return StartOfLineAlgorithm<EditingStrategy>(current_position); | |
877 } | |
878 | |
879 static PositionInFlatTreeWithAffinity StartOfLine( | |
880 const PositionInFlatTreeWithAffinity& current_position) { | |
881 return StartOfLineAlgorithm<EditingInFlatTreeStrategy>(current_position); | |
882 } | |
883 | |
884 // FIXME: Rename this function to reflect the fact it ignores bidi levels. | |
885 VisiblePosition StartOfLine(const VisiblePosition& current_position) { | |
886 DCHECK(current_position.IsValid()) << current_position; | |
887 return CreateVisiblePosition( | |
888 StartOfLine(current_position.ToPositionWithAffinity())); | |
889 } | |
890 | |
891 VisiblePositionInFlatTree StartOfLine( | |
892 const VisiblePositionInFlatTree& current_position) { | |
893 DCHECK(current_position.IsValid()) << current_position; | |
894 return CreateVisiblePosition( | |
895 StartOfLine(current_position.ToPositionWithAffinity())); | |
896 } | |
897 | |
898 template <typename Strategy> | |
899 static PositionWithAffinityTemplate<Strategy> LogicalStartOfLineAlgorithm( | |
900 const PositionWithAffinityTemplate<Strategy>& c) { | |
901 // TODO: this is the current behavior that might need to be fixed. | |
902 // Please refer to https://bugs.webkit.org/show_bug.cgi?id=49107 for detail. | |
903 PositionWithAffinityTemplate<Strategy> vis_pos = | |
904 StartPositionForLine(c, kUseLogicalOrdering); | |
905 | |
906 if (ContainerNode* editable_root = HighestEditableRoot(c.GetPosition())) { | |
907 if (!editable_root->contains(vis_pos.GetPosition().ComputeContainerNode())) | |
908 return PositionWithAffinityTemplate<Strategy>( | |
909 PositionTemplate<Strategy>::FirstPositionInNode(editable_root)); | |
910 } | |
911 | |
912 return HonorEditingBoundaryAtOrBefore(vis_pos, c.GetPosition()); | |
913 } | |
914 | |
915 VisiblePosition LogicalStartOfLine(const VisiblePosition& current_position) { | |
916 DCHECK(current_position.IsValid()) << current_position; | |
917 return CreateVisiblePosition(LogicalStartOfLineAlgorithm<EditingStrategy>( | |
918 current_position.ToPositionWithAffinity())); | |
919 } | |
920 | |
921 VisiblePositionInFlatTree LogicalStartOfLine( | |
922 const VisiblePositionInFlatTree& current_position) { | |
923 DCHECK(current_position.IsValid()) << current_position; | |
924 return CreateVisiblePosition( | |
925 LogicalStartOfLineAlgorithm<EditingInFlatTreeStrategy>( | |
926 current_position.ToPositionWithAffinity())); | |
927 } | |
928 | |
929 template <typename Strategy> | |
930 static VisiblePositionTemplate<Strategy> EndPositionForLine( | |
931 const VisiblePositionTemplate<Strategy>& c, | |
932 LineEndpointComputationMode mode) { | |
933 DCHECK(c.IsValid()) << c; | |
934 if (c.IsNull()) | |
935 return VisiblePositionTemplate<Strategy>(); | |
936 | |
937 RootInlineBox* root_box = RenderedPosition(c).RootBox(); | |
938 if (!root_box) { | |
939 // There are VisiblePositions at offset 0 in blocks without | |
940 // RootInlineBoxes, like empty editable blocks and bordered blocks. | |
941 const PositionTemplate<Strategy> p = c.DeepEquivalent(); | |
942 if (p.AnchorNode()->GetLayoutObject() && | |
943 p.AnchorNode()->GetLayoutObject()->IsLayoutBlock() && | |
944 !p.ComputeEditingOffset()) | |
945 return c; | |
946 return VisiblePositionTemplate<Strategy>(); | |
947 } | |
948 | |
949 Node* end_node; | |
950 InlineBox* end_box; | |
951 if (mode == kUseLogicalOrdering) { | |
952 end_node = root_box->GetLogicalEndBoxWithNode(end_box); | |
953 if (!end_node) | |
954 return VisiblePositionTemplate<Strategy>(); | |
955 } else { | |
956 // Generated content (e.g. list markers and CSS :before and :after | |
957 // pseudo elements) have no corresponding DOM element, and so cannot be | |
958 // represented by a VisiblePosition. Use whatever precedes instead. | |
959 end_box = root_box->LastLeafChild(); | |
960 while (true) { | |
961 if (!end_box) | |
962 return VisiblePositionTemplate<Strategy>(); | |
963 | |
964 end_node = end_box->GetLineLayoutItem().NonPseudoNode(); | |
965 if (end_node) | |
966 break; | |
967 | |
968 end_box = end_box->PrevLeafChild(); | |
969 } | |
970 } | |
971 | |
972 PositionTemplate<Strategy> pos; | |
973 if (isHTMLBRElement(*end_node)) { | |
974 pos = PositionTemplate<Strategy>::BeforeNode(end_node); | |
975 } else if (end_box->IsInlineTextBox() && end_node->IsTextNode()) { | |
976 InlineTextBox* end_text_box = ToInlineTextBox(end_box); | |
977 int end_offset = end_text_box->Start(); | |
978 if (!end_text_box->IsLineBreak()) | |
979 end_offset += end_text_box->Len(); | |
980 pos = PositionTemplate<Strategy>(ToText(end_node), end_offset); | |
981 } else { | |
982 pos = PositionTemplate<Strategy>::AfterNode(end_node); | |
983 } | |
984 | |
985 return CreateVisiblePosition(pos, VP_UPSTREAM_IF_POSSIBLE); | |
986 } | |
987 | |
988 // TODO(yosin) Rename this function to reflect the fact it ignores bidi levels. | |
989 template <typename Strategy> | |
990 static VisiblePositionTemplate<Strategy> EndOfLineAlgorithm( | |
991 const VisiblePositionTemplate<Strategy>& current_position) { | |
992 DCHECK(current_position.IsValid()) << current_position; | |
993 // TODO(yosin) this is the current behavior that might need to be fixed. | |
994 // Please refer to https://bugs.webkit.org/show_bug.cgi?id=49107 for detail. | |
995 VisiblePositionTemplate<Strategy> vis_pos = | |
996 EndPositionForLine(current_position, kUseInlineBoxOrdering); | |
997 | |
998 // Make sure the end of line is at the same line as the given input | |
999 // position. Else use the previous position to obtain end of line. This | |
1000 // condition happens when the input position is before the space character | |
1001 // at the end of a soft-wrapped non-editable line. In this scenario, | |
1002 // |endPositionForLine()| would incorrectly hand back a position in the next | |
1003 // line instead. This fix is to account for the discrepancy between lines | |
1004 // with "webkit-line-break:after-white-space" style versus lines without | |
1005 // that style, which would break before a space by default. | |
1006 if (!InSameLine(current_position, vis_pos)) { | |
1007 vis_pos = PreviousPositionOf(current_position); | |
1008 if (vis_pos.IsNull()) | |
1009 return VisiblePositionTemplate<Strategy>(); | |
1010 vis_pos = EndPositionForLine(vis_pos, kUseInlineBoxOrdering); | |
1011 } | |
1012 | |
1013 return HonorEditingBoundaryAtOrAfter(vis_pos, | |
1014 current_position.DeepEquivalent()); | |
1015 } | |
1016 | |
1017 // TODO(yosin) Rename this function to reflect the fact it ignores bidi levels. | |
1018 VisiblePosition EndOfLine(const VisiblePosition& current_position) { | |
1019 return EndOfLineAlgorithm<EditingStrategy>(current_position); | |
1020 } | |
1021 | |
1022 VisiblePositionInFlatTree EndOfLine( | |
1023 const VisiblePositionInFlatTree& current_position) { | |
1024 return EndOfLineAlgorithm<EditingInFlatTreeStrategy>(current_position); | |
1025 } | |
1026 | |
1027 template <typename Strategy> | |
1028 static bool InSameLogicalLine(const VisiblePositionTemplate<Strategy>& a, | |
1029 const VisiblePositionTemplate<Strategy>& b) { | |
1030 DCHECK(a.IsValid()) << a; | |
1031 DCHECK(b.IsValid()) << b; | |
1032 return a.IsNotNull() && LogicalStartOfLine(a).DeepEquivalent() == | |
1033 LogicalStartOfLine(b).DeepEquivalent(); | |
1034 } | |
1035 | |
1036 template <typename Strategy> | |
1037 VisiblePositionTemplate<Strategy> LogicalEndOfLineAlgorithm( | |
1038 const VisiblePositionTemplate<Strategy>& current_position) { | |
1039 DCHECK(current_position.IsValid()) << current_position; | |
1040 // TODO(yosin) this is the current behavior that might need to be fixed. | |
1041 // Please refer to https://bugs.webkit.org/show_bug.cgi?id=49107 for detail. | |
1042 VisiblePositionTemplate<Strategy> vis_pos = | |
1043 EndPositionForLine(current_position, kUseLogicalOrdering); | |
1044 | |
1045 // Make sure the end of line is at the same line as the given input | |
1046 // position. For a wrapping line, the logical end position for the | |
1047 // not-last-2-lines might incorrectly hand back the logical beginning of the | |
1048 // next line. For example, | |
1049 // <div contenteditable dir="rtl" style="line-break:before-white-space">xyz | |
1050 // a xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz </div> | |
1051 // In this case, use the previous position of the computed logical end | |
1052 // position. | |
1053 if (!InSameLogicalLine(current_position, vis_pos)) | |
1054 vis_pos = PreviousPositionOf(vis_pos); | |
1055 | |
1056 if (ContainerNode* editable_root = | |
1057 HighestEditableRoot(current_position.DeepEquivalent())) { | |
1058 if (!editable_root->contains( | |
1059 vis_pos.DeepEquivalent().ComputeContainerNode())) | |
1060 return CreateVisiblePosition( | |
1061 PositionTemplate<Strategy>::LastPositionInNode(editable_root)); | |
1062 } | |
1063 | |
1064 return HonorEditingBoundaryAtOrAfter(vis_pos, | |
1065 current_position.DeepEquivalent()); | |
1066 } | |
1067 | |
1068 VisiblePosition LogicalEndOfLine(const VisiblePosition& current_position) { | |
1069 return LogicalEndOfLineAlgorithm<EditingStrategy>(current_position); | |
1070 } | |
1071 | |
1072 VisiblePositionInFlatTree LogicalEndOfLine( | |
1073 const VisiblePositionInFlatTree& current_position) { | |
1074 return LogicalEndOfLineAlgorithm<EditingInFlatTreeStrategy>(current_position); | |
1075 } | |
1076 | |
1077 template <typename Strategy> | |
1078 bool InSameLineAlgorithm( | |
1079 const PositionWithAffinityTemplate<Strategy>& position1, | |
1080 const PositionWithAffinityTemplate<Strategy>& position2) { | |
1081 if (position1.IsNull() || position2.IsNull()) | |
1082 return false; | |
1083 DCHECK_EQ(position1.GetDocument(), position2.GetDocument()); | |
1084 DCHECK(!position1.GetDocument()->NeedsLayoutTreeUpdate()); | |
1085 | |
1086 PositionWithAffinityTemplate<Strategy> start_of_line1 = | |
1087 StartOfLine(position1); | |
1088 PositionWithAffinityTemplate<Strategy> start_of_line2 = | |
1089 StartOfLine(position2); | |
1090 if (start_of_line1 == start_of_line2) | |
1091 return true; | |
1092 PositionTemplate<Strategy> canonicalized1 = | |
1093 CanonicalPositionOf(start_of_line1.GetPosition()); | |
1094 if (canonicalized1 == start_of_line2.GetPosition()) | |
1095 return true; | |
1096 return canonicalized1 == CanonicalPositionOf(start_of_line2.GetPosition()); | |
1097 } | |
1098 | |
1099 bool InSameLine(const PositionWithAffinity& a, const PositionWithAffinity& b) { | |
1100 return InSameLineAlgorithm<EditingStrategy>(a, b); | |
1101 } | |
1102 | |
1103 bool InSameLine(const PositionInFlatTreeWithAffinity& position1, | |
1104 const PositionInFlatTreeWithAffinity& position2) { | |
1105 return InSameLineAlgorithm<EditingInFlatTreeStrategy>(position1, position2); | |
1106 } | |
1107 | |
1108 bool InSameLine(const VisiblePosition& position1, | |
1109 const VisiblePosition& position2) { | |
1110 DCHECK(position1.IsValid()) << position1; | |
1111 DCHECK(position2.IsValid()) << position2; | |
1112 return InSameLine(position1.ToPositionWithAffinity(), | |
1113 position2.ToPositionWithAffinity()); | |
1114 } | |
1115 | |
1116 bool InSameLine(const VisiblePositionInFlatTree& position1, | |
1117 const VisiblePositionInFlatTree& position2) { | |
1118 DCHECK(position1.IsValid()) << position1; | |
1119 DCHECK(position2.IsValid()) << position2; | |
1120 return InSameLine(position1.ToPositionWithAffinity(), | |
1121 position2.ToPositionWithAffinity()); | |
1122 } | |
1123 | |
1124 template <typename Strategy> | |
1125 bool IsStartOfLineAlgorithm(const VisiblePositionTemplate<Strategy>& p) { | |
1126 DCHECK(p.IsValid()) << p; | |
1127 return p.IsNotNull() && p.DeepEquivalent() == StartOfLine(p).DeepEquivalent(); | |
1128 } | |
1129 | |
1130 bool IsStartOfLine(const VisiblePosition& p) { | |
1131 return IsStartOfLineAlgorithm<EditingStrategy>(p); | |
1132 } | |
1133 | |
1134 bool IsStartOfLine(const VisiblePositionInFlatTree& p) { | |
1135 return IsStartOfLineAlgorithm<EditingInFlatTreeStrategy>(p); | |
1136 } | |
1137 | |
1138 template <typename Strategy> | |
1139 bool IsEndOfLineAlgorithm(const VisiblePositionTemplate<Strategy>& p) { | |
1140 DCHECK(p.IsValid()) << p; | |
1141 return p.IsNotNull() && p.DeepEquivalent() == EndOfLine(p).DeepEquivalent(); | |
1142 } | |
1143 | |
1144 bool IsEndOfLine(const VisiblePosition& p) { | |
1145 return IsEndOfLineAlgorithm<EditingStrategy>(p); | |
1146 } | |
1147 | |
1148 bool IsEndOfLine(const VisiblePositionInFlatTree& p) { | |
1149 return IsEndOfLineAlgorithm<EditingInFlatTreeStrategy>(p); | |
1150 } | |
1151 | |
1152 template <typename Strategy> | |
1153 static bool IsLogicalEndOfLineAlgorithm( | |
1154 const VisiblePositionTemplate<Strategy>& p) { | |
1155 DCHECK(p.IsValid()) << p; | |
1156 return p.IsNotNull() && | |
1157 p.DeepEquivalent() == LogicalEndOfLine(p).DeepEquivalent(); | |
1158 } | |
1159 | |
1160 bool IsLogicalEndOfLine(const VisiblePosition& p) { | |
1161 return IsLogicalEndOfLineAlgorithm<EditingStrategy>(p); | |
1162 } | |
1163 | |
1164 bool IsLogicalEndOfLine(const VisiblePositionInFlatTree& p) { | |
1165 return IsLogicalEndOfLineAlgorithm<EditingInFlatTreeStrategy>(p); | |
1166 } | |
1167 | |
1168 static inline LayoutPoint AbsoluteLineDirectionPointToLocalPointInBlock( | |
1169 RootInlineBox* root, | |
1170 LayoutUnit line_direction_point) { | |
1171 DCHECK(root); | |
1172 LineLayoutBlockFlow containing_block = root->Block(); | |
1173 FloatPoint absolute_block_point = | |
1174 containing_block.LocalToAbsolute(FloatPoint()); | |
1175 if (containing_block.HasOverflowClip()) | |
1176 absolute_block_point -= FloatSize(containing_block.ScrolledContentOffset()); | |
1177 | |
1178 if (root->Block().IsHorizontalWritingMode()) | |
1179 return LayoutPoint( | |
1180 LayoutUnit(line_direction_point - absolute_block_point.X()), | |
1181 root->BlockDirectionPointInLine()); | |
1182 | |
1183 return LayoutPoint( | |
1184 root->BlockDirectionPointInLine(), | |
1185 LayoutUnit(line_direction_point - absolute_block_point.Y())); | |
1186 } | |
1187 | |
1188 VisiblePosition PreviousLinePosition(const VisiblePosition& visible_position, | |
1189 LayoutUnit line_direction_point, | |
1190 EditableType editable_type) { | |
1191 DCHECK(visible_position.IsValid()) << visible_position; | |
1192 | |
1193 Position p = visible_position.DeepEquivalent(); | |
1194 Node* node = p.AnchorNode(); | |
1195 | |
1196 if (!node) | |
1197 return VisiblePosition(); | |
1198 | |
1199 LayoutObject* layout_object = node->GetLayoutObject(); | |
1200 if (!layout_object) | |
1201 return VisiblePosition(); | |
1202 | |
1203 RootInlineBox* root = 0; | |
1204 InlineBox* box = ComputeInlineBoxPosition(visible_position).inline_box; | |
1205 if (box) { | |
1206 root = box->Root().PrevRootBox(); | |
1207 // We want to skip zero height boxes. | |
1208 // This could happen in case it is a TrailingFloatsRootInlineBox. | |
1209 if (!root || !root->LogicalHeight() || !root->FirstLeafChild()) | |
1210 root = 0; | |
1211 } | |
1212 | |
1213 if (!root) { | |
1214 Position position = PreviousRootInlineBoxCandidatePosition( | |
1215 node, visible_position, editable_type); | |
1216 if (position.IsNotNull()) { | |
1217 RenderedPosition rendered_position((CreateVisiblePosition(position))); | |
1218 root = rendered_position.RootBox(); | |
1219 if (!root) | |
1220 return CreateVisiblePosition(position); | |
1221 } | |
1222 } | |
1223 | |
1224 if (root) { | |
1225 // FIXME: Can be wrong for multi-column layout and with transforms. | |
1226 LayoutPoint point_in_line = AbsoluteLineDirectionPointToLocalPointInBlock( | |
1227 root, line_direction_point); | |
1228 LineLayoutItem line_layout_item = | |
1229 root->ClosestLeafChildForPoint(point_in_line, IsEditablePosition(p)) | |
1230 ->GetLineLayoutItem(); | |
1231 Node* node = line_layout_item.GetNode(); | |
1232 if (node && EditingIgnoresContent(*node)) | |
1233 return VisiblePosition::InParentBeforeNode(*node); | |
1234 return CreateVisiblePosition( | |
1235 line_layout_item.PositionForPoint(point_in_line)); | |
1236 } | |
1237 | |
1238 // Could not find a previous line. This means we must already be on the first | |
1239 // line. Move to the start of the content in this block, which effectively | |
1240 // moves us to the start of the line we're on. | |
1241 Element* root_element = HasEditableStyle(*node, editable_type) | |
1242 ? RootEditableElement(*node, editable_type) | |
1243 : node->GetDocument().documentElement(); | |
1244 if (!root_element) | |
1245 return VisiblePosition(); | |
1246 return VisiblePosition::FirstPositionInNode(root_element); | |
1247 } | |
1248 | |
1249 VisiblePosition NextLinePosition(const VisiblePosition& visible_position, | |
1250 LayoutUnit line_direction_point, | |
1251 EditableType editable_type) { | |
1252 DCHECK(visible_position.IsValid()) << visible_position; | |
1253 | |
1254 Position p = visible_position.DeepEquivalent(); | |
1255 Node* node = p.AnchorNode(); | |
1256 | |
1257 if (!node) | |
1258 return VisiblePosition(); | |
1259 | |
1260 LayoutObject* layout_object = node->GetLayoutObject(); | |
1261 if (!layout_object) | |
1262 return VisiblePosition(); | |
1263 | |
1264 RootInlineBox* root = 0; | |
1265 InlineBox* box = ComputeInlineBoxPosition(visible_position).inline_box; | |
1266 if (box) { | |
1267 root = box->Root().NextRootBox(); | |
1268 // We want to skip zero height boxes. | |
1269 // This could happen in case it is a TrailingFloatsRootInlineBox. | |
1270 if (!root || !root->LogicalHeight() || !root->FirstLeafChild()) | |
1271 root = 0; | |
1272 } | |
1273 | |
1274 if (!root) { | |
1275 // FIXME: We need do the same in previousLinePosition. | |
1276 Node* child = NodeTraversal::ChildAt(*node, p.ComputeEditingOffset()); | |
1277 node = child ? child : &NodeTraversal::LastWithinOrSelf(*node); | |
1278 Position position = NextRootInlineBoxCandidatePosition( | |
1279 node, visible_position, editable_type); | |
1280 if (position.IsNotNull()) { | |
1281 RenderedPosition rendered_position((CreateVisiblePosition(position))); | |
1282 root = rendered_position.RootBox(); | |
1283 if (!root) | |
1284 return CreateVisiblePosition(position); | |
1285 } | |
1286 } | |
1287 | |
1288 if (root) { | |
1289 // FIXME: Can be wrong for multi-column layout and with transforms. | |
1290 LayoutPoint point_in_line = AbsoluteLineDirectionPointToLocalPointInBlock( | |
1291 root, line_direction_point); | |
1292 LineLayoutItem line_layout_item = | |
1293 root->ClosestLeafChildForPoint(point_in_line, IsEditablePosition(p)) | |
1294 ->GetLineLayoutItem(); | |
1295 Node* node = line_layout_item.GetNode(); | |
1296 if (node && EditingIgnoresContent(*node)) | |
1297 return VisiblePosition::InParentBeforeNode(*node); | |
1298 return CreateVisiblePosition( | |
1299 line_layout_item.PositionForPoint(point_in_line)); | |
1300 } | |
1301 | |
1302 // Could not find a next line. This means we must already be on the last line. | |
1303 // Move to the end of the content in this block, which effectively moves us | |
1304 // to the end of the line we're on. | |
1305 Element* root_element = HasEditableStyle(*node, editable_type) | |
1306 ? RootEditableElement(*node, editable_type) | |
1307 : node->GetDocument().documentElement(); | |
1308 if (!root_element) | |
1309 return VisiblePosition(); | |
1310 return VisiblePosition::LastPositionInNode(root_element); | |
1311 } | |
1312 | |
1313 // --------- | |
1314 | |
1315 static unsigned StartSentenceBoundary(const UChar* characters, | 696 static unsigned StartSentenceBoundary(const UChar* characters, |
1316 unsigned length, | 697 unsigned length, |
1317 unsigned, | 698 unsigned, |
1318 BoundarySearchContextAvailability, | 699 BoundarySearchContextAvailability, |
1319 bool&) { | 700 bool&) { |
1320 TextBreakIterator* iterator = SentenceBreakIterator(characters, length); | 701 TextBreakIterator* iterator = SentenceBreakIterator(characters, length); |
1321 // FIXME: The following function can return -1; we don't handle that. | 702 // FIXME: The following function can return -1; we don't handle that. |
1322 return iterator->preceding(length); | 703 return iterator->preceding(length); |
1323 } | 704 } |
1324 | 705 |
(...skipping 2431 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3756 | 3137 |
3757 IntRect ComputeTextRect(const EphemeralRangeInFlatTree& range) { | 3138 IntRect ComputeTextRect(const EphemeralRangeInFlatTree& range) { |
3758 return EnclosingIntRect(ComputeTextRectTemplate(range)); | 3139 return EnclosingIntRect(ComputeTextRectTemplate(range)); |
3759 } | 3140 } |
3760 | 3141 |
3761 FloatRect ComputeTextFloatRect(const EphemeralRange& range) { | 3142 FloatRect ComputeTextFloatRect(const EphemeralRange& range) { |
3762 return ComputeTextRectTemplate(range); | 3143 return ComputeTextRectTemplate(range); |
3763 } | 3144 } |
3764 | 3145 |
3765 } // namespace blink | 3146 } // namespace blink |
OLD | NEW |