Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 /* | |
| 2 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights | |
|
Xiaocheng
2017/05/31 18:12:21
We should use the Chromium Authors ... copyright n
yosin_UTC9
2017/06/01 01:09:16
No, we should use original copyright header.
Diss
tkent
2017/06/01 01:15:58
yeah, I agree about this case.
| |
| 3 * reserved. | |
| 4 * | |
| 5 * Redistribution and use in source and binary forms, with or without | |
| 6 * modification, are permitted provided that the following conditions | |
| 7 * are met: | |
| 8 * 1. Redistributions of source code must retain the above copyright | |
| 9 * notice, this list of conditions and the following disclaimer. | |
| 10 * 2. Redistributions in binary form must reproduce the above copyright | |
| 11 * notice, this list of conditions and the following disclaimer in the | |
| 12 * documentation and/or other materials provided with the distribution. | |
| 13 * | |
| 14 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY | |
| 15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
| 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR | |
| 18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
| 19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
| 20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
| 21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | |
| 22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 25 */ | |
| 26 | |
| 27 #include "core/editing/VisibleUnits.h" | |
| 28 | |
| 29 #include "core/editing/EditingUtilities.h" | |
| 30 #include "core/editing/RenderedPosition.h" | |
| 31 #include "core/layout/api/LineLayoutBlockFlow.h" | |
| 32 #include "core/layout/line/InlineTextBox.h" | |
| 33 #include "core/layout/line/RootInlineBox.h" | |
| 34 | |
| 35 namespace blink { | |
| 36 | |
| 37 namespace { | |
| 38 | |
| 39 bool HasEditableStyle(const Node& node, EditableType editable_type) { | |
| 40 if (editable_type == kHasEditableAXRole) { | |
| 41 if (AXObjectCache* cache = node.GetDocument().ExistingAXObjectCache()) { | |
| 42 if (cache->RootAXEditableElement(&node)) | |
| 43 return true; | |
| 44 } | |
| 45 } | |
| 46 | |
| 47 return HasEditableStyle(node); | |
| 48 } | |
| 49 | |
| 50 Element* RootEditableElement(const Node& node, EditableType editable_type) { | |
| 51 if (editable_type == kHasEditableAXRole) { | |
| 52 if (AXObjectCache* cache = node.GetDocument().ExistingAXObjectCache()) | |
| 53 return const_cast<Element*>(cache->RootAXEditableElement(&node)); | |
| 54 } | |
| 55 | |
| 56 return RootEditableElement(node); | |
| 57 } | |
| 58 | |
| 59 Element* RootAXEditableElementOf(const Position& position) { | |
| 60 Node* node = position.ComputeContainerNode(); | |
| 61 if (!node) | |
| 62 return nullptr; | |
| 63 | |
| 64 if (IsDisplayInsideTable(node)) | |
| 65 node = node->parentNode(); | |
| 66 | |
| 67 return RootEditableElement(*node, kHasEditableAXRole); | |
| 68 } | |
| 69 | |
| 70 bool HasAXEditableStyle(const Node& node) { | |
| 71 return HasEditableStyle(node, kHasEditableAXRole); | |
| 72 } | |
| 73 | |
| 74 ContainerNode* HighestEditableRoot(const Position& position, | |
| 75 EditableType editable_type) { | |
| 76 if (editable_type == kHasEditableAXRole) { | |
| 77 return HighestEditableRoot(position, RootAXEditableElementOf, | |
| 78 HasAXEditableStyle); | |
| 79 } | |
| 80 | |
| 81 return HighestEditableRoot(position); | |
| 82 } | |
| 83 | |
| 84 Node* PreviousLeafWithSameEditability(Node* node, EditableType editable_type) { | |
| 85 bool editable = HasEditableStyle(*node, editable_type); | |
| 86 node = PreviousAtomicLeafNode(*node); | |
| 87 while (node) { | |
| 88 if (editable == HasEditableStyle(*node, editable_type)) | |
| 89 return node; | |
| 90 node = PreviousAtomicLeafNode(*node); | |
| 91 } | |
| 92 return nullptr; | |
| 93 } | |
| 94 | |
| 95 Node* NextLeafWithSameEditability( | |
| 96 Node* node, | |
| 97 EditableType editable_type = kContentIsEditable) { | |
| 98 if (!node) | |
| 99 return nullptr; | |
| 100 | |
| 101 bool editable = HasEditableStyle(*node, editable_type); | |
| 102 node = NextAtomicLeafNode(*node); | |
| 103 while (node) { | |
| 104 if (editable == HasEditableStyle(*node, editable_type)) | |
| 105 return node; | |
| 106 node = NextAtomicLeafNode(*node); | |
| 107 } | |
| 108 return nullptr; | |
| 109 } | |
| 110 | |
| 111 enum LineEndpointComputationMode { kUseLogicalOrdering, kUseInlineBoxOrdering }; | |
| 112 template <typename Strategy> | |
| 113 PositionWithAffinityTemplate<Strategy> StartPositionForLine( | |
| 114 const PositionWithAffinityTemplate<Strategy>& c, | |
| 115 LineEndpointComputationMode mode) { | |
| 116 if (c.IsNull()) | |
| 117 return PositionWithAffinityTemplate<Strategy>(); | |
| 118 | |
| 119 RootInlineBox* root_box = | |
| 120 RenderedPosition(c.GetPosition(), c.Affinity()).RootBox(); | |
| 121 if (!root_box) { | |
| 122 // There are VisiblePositions at offset 0 in blocks without | |
| 123 // RootInlineBoxes, like empty editable blocks and bordered blocks. | |
| 124 PositionTemplate<Strategy> p = c.GetPosition(); | |
| 125 if (p.AnchorNode()->GetLayoutObject() && | |
| 126 p.AnchorNode()->GetLayoutObject()->IsLayoutBlock() && | |
| 127 !p.ComputeEditingOffset()) | |
| 128 return c; | |
| 129 | |
| 130 return PositionWithAffinityTemplate<Strategy>(); | |
| 131 } | |
| 132 | |
| 133 Node* start_node; | |
| 134 InlineBox* start_box; | |
| 135 if (mode == kUseLogicalOrdering) { | |
| 136 start_node = root_box->GetLogicalStartBoxWithNode(start_box); | |
| 137 if (!start_node) | |
| 138 return PositionWithAffinityTemplate<Strategy>(); | |
| 139 } else { | |
| 140 // Generated content (e.g. list markers and CSS :before and :after | |
| 141 // pseudoelements) have no corresponding DOM element, and so cannot be | |
| 142 // represented by a VisiblePosition. Use whatever follows instead. | |
| 143 start_box = root_box->FirstLeafChild(); | |
| 144 while (true) { | |
| 145 if (!start_box) | |
| 146 return PositionWithAffinityTemplate<Strategy>(); | |
| 147 | |
| 148 start_node = start_box->GetLineLayoutItem().NonPseudoNode(); | |
| 149 if (start_node) | |
| 150 break; | |
| 151 | |
| 152 start_box = start_box->NextLeafChild(); | |
| 153 } | |
| 154 } | |
| 155 | |
| 156 return PositionWithAffinityTemplate<Strategy>( | |
| 157 start_node->IsTextNode() | |
| 158 ? PositionTemplate<Strategy>(ToText(start_node), | |
| 159 ToInlineTextBox(start_box)->Start()) | |
| 160 : PositionTemplate<Strategy>::BeforeNode(start_node)); | |
| 161 } | |
| 162 | |
| 163 template <typename Strategy> | |
| 164 PositionWithAffinityTemplate<Strategy> StartOfLineAlgorithm( | |
| 165 const PositionWithAffinityTemplate<Strategy>& c) { | |
| 166 // TODO: this is the current behavior that might need to be fixed. | |
| 167 // Please refer to https://bugs.webkit.org/show_bug.cgi?id=49107 for detail. | |
| 168 PositionWithAffinityTemplate<Strategy> vis_pos = | |
| 169 StartPositionForLine(c, kUseInlineBoxOrdering); | |
| 170 return HonorEditingBoundaryAtOrBefore(vis_pos, c.GetPosition()); | |
| 171 } | |
| 172 | |
| 173 PositionWithAffinity StartOfLine(const PositionWithAffinity& current_position) { | |
| 174 return StartOfLineAlgorithm<EditingStrategy>(current_position); | |
| 175 } | |
| 176 | |
| 177 PositionInFlatTreeWithAffinity StartOfLine( | |
| 178 const PositionInFlatTreeWithAffinity& current_position) { | |
| 179 return StartOfLineAlgorithm<EditingInFlatTreeStrategy>(current_position); | |
| 180 } | |
| 181 | |
| 182 LayoutPoint AbsoluteLineDirectionPointToLocalPointInBlock( | |
| 183 RootInlineBox* root, | |
| 184 LayoutUnit line_direction_point) { | |
| 185 DCHECK(root); | |
| 186 LineLayoutBlockFlow containing_block = root->Block(); | |
| 187 FloatPoint absolute_block_point = | |
| 188 containing_block.LocalToAbsolute(FloatPoint()); | |
| 189 if (containing_block.HasOverflowClip()) | |
| 190 absolute_block_point -= FloatSize(containing_block.ScrolledContentOffset()); | |
| 191 | |
| 192 if (root->Block().IsHorizontalWritingMode()) { | |
| 193 return LayoutPoint( | |
| 194 LayoutUnit(line_direction_point - absolute_block_point.X()), | |
| 195 root->BlockDirectionPointInLine()); | |
| 196 } | |
| 197 | |
| 198 return LayoutPoint( | |
| 199 root->BlockDirectionPointInLine(), | |
| 200 LayoutUnit(line_direction_point - absolute_block_point.Y())); | |
| 201 } | |
| 202 | |
| 203 } // namespace | |
| 204 | |
| 205 // FIXME: consolidate with code in previousLinePosition. | |
| 206 Position PreviousRootInlineBoxCandidatePosition( | |
| 207 Node* node, | |
| 208 const VisiblePosition& visible_position, | |
| 209 EditableType editable_type) { | |
| 210 DCHECK(visible_position.IsValid()) << visible_position; | |
| 211 ContainerNode* highest_root = | |
| 212 HighestEditableRoot(visible_position.DeepEquivalent(), editable_type); | |
| 213 Node* previous_node = PreviousLeafWithSameEditability(node, editable_type); | |
| 214 | |
| 215 while (previous_node && | |
| 216 (!previous_node->GetLayoutObject() || | |
| 217 InSameLine( | |
| 218 CreateVisiblePosition(FirstPositionInOrBeforeNode(previous_node)), | |
| 219 visible_position))) { | |
| 220 previous_node = | |
| 221 PreviousLeafWithSameEditability(previous_node, editable_type); | |
| 222 } | |
| 223 | |
| 224 while (previous_node && !previous_node->IsShadowRoot()) { | |
| 225 if (HighestEditableRoot(FirstPositionInOrBeforeNode(previous_node), | |
| 226 editable_type) != highest_root) | |
| 227 break; | |
| 228 | |
| 229 Position pos = isHTMLBRElement(*previous_node) | |
| 230 ? Position::BeforeNode(previous_node) | |
| 231 : Position::EditingPositionOf( | |
| 232 previous_node, CaretMaxOffset(previous_node)); | |
| 233 | |
| 234 if (IsVisuallyEquivalentCandidate(pos)) | |
| 235 return pos; | |
| 236 | |
| 237 previous_node = | |
| 238 PreviousLeafWithSameEditability(previous_node, editable_type); | |
| 239 } | |
| 240 return Position(); | |
| 241 } | |
| 242 | |
| 243 Position NextRootInlineBoxCandidatePosition( | |
| 244 Node* node, | |
| 245 const VisiblePosition& visible_position, | |
| 246 EditableType editable_type) { | |
| 247 DCHECK(visible_position.IsValid()) << visible_position; | |
| 248 ContainerNode* highest_root = | |
| 249 HighestEditableRoot(visible_position.DeepEquivalent(), editable_type); | |
| 250 Node* next_node = NextLeafWithSameEditability(node, editable_type); | |
| 251 while (next_node && (!next_node->GetLayoutObject() || | |
| 252 InSameLine(CreateVisiblePosition( | |
| 253 FirstPositionInOrBeforeNode(next_node)), | |
| 254 visible_position))) | |
| 255 next_node = NextLeafWithSameEditability(next_node, kContentIsEditable); | |
| 256 | |
| 257 while (next_node && !next_node->IsShadowRoot()) { | |
| 258 if (HighestEditableRoot(FirstPositionInOrBeforeNode(next_node), | |
| 259 editable_type) != highest_root) | |
| 260 break; | |
| 261 | |
| 262 Position pos; | |
| 263 pos = Position::EditingPositionOf(next_node, CaretMinOffset(next_node)); | |
| 264 | |
| 265 if (IsVisuallyEquivalentCandidate(pos)) | |
| 266 return pos; | |
| 267 | |
| 268 next_node = NextLeafWithSameEditability(next_node, editable_type); | |
| 269 } | |
| 270 return Position(); | |
| 271 } | |
| 272 | |
| 273 // FIXME: Rename this function to reflect the fact it ignores bidi levels. | |
| 274 VisiblePosition StartOfLine(const VisiblePosition& current_position) { | |
| 275 DCHECK(current_position.IsValid()) << current_position; | |
| 276 return CreateVisiblePosition( | |
| 277 StartOfLine(current_position.ToPositionWithAffinity())); | |
| 278 } | |
| 279 | |
| 280 VisiblePositionInFlatTree StartOfLine( | |
| 281 const VisiblePositionInFlatTree& current_position) { | |
| 282 DCHECK(current_position.IsValid()) << current_position; | |
| 283 return CreateVisiblePosition( | |
| 284 StartOfLine(current_position.ToPositionWithAffinity())); | |
| 285 } | |
| 286 | |
| 287 template <typename Strategy> | |
| 288 static PositionWithAffinityTemplate<Strategy> LogicalStartOfLineAlgorithm( | |
| 289 const PositionWithAffinityTemplate<Strategy>& c) { | |
| 290 // TODO: this is the current behavior that might need to be fixed. | |
| 291 // Please refer to https://bugs.webkit.org/show_bug.cgi?id=49107 for detail. | |
| 292 PositionWithAffinityTemplate<Strategy> vis_pos = | |
| 293 StartPositionForLine(c, kUseLogicalOrdering); | |
| 294 | |
| 295 if (ContainerNode* editable_root = HighestEditableRoot(c.GetPosition())) { | |
| 296 if (!editable_root->contains( | |
| 297 vis_pos.GetPosition().ComputeContainerNode())) { | |
| 298 return PositionWithAffinityTemplate<Strategy>( | |
| 299 PositionTemplate<Strategy>::FirstPositionInNode(editable_root)); | |
| 300 } | |
| 301 } | |
| 302 | |
| 303 return HonorEditingBoundaryAtOrBefore(vis_pos, c.GetPosition()); | |
| 304 } | |
| 305 | |
| 306 VisiblePosition LogicalStartOfLine(const VisiblePosition& current_position) { | |
| 307 DCHECK(current_position.IsValid()) << current_position; | |
| 308 return CreateVisiblePosition(LogicalStartOfLineAlgorithm<EditingStrategy>( | |
| 309 current_position.ToPositionWithAffinity())); | |
| 310 } | |
| 311 | |
| 312 VisiblePositionInFlatTree LogicalStartOfLine( | |
| 313 const VisiblePositionInFlatTree& current_position) { | |
| 314 DCHECK(current_position.IsValid()) << current_position; | |
| 315 return CreateVisiblePosition( | |
| 316 LogicalStartOfLineAlgorithm<EditingInFlatTreeStrategy>( | |
| 317 current_position.ToPositionWithAffinity())); | |
| 318 } | |
| 319 | |
| 320 template <typename Strategy> | |
| 321 static VisiblePositionTemplate<Strategy> EndPositionForLine( | |
| 322 const VisiblePositionTemplate<Strategy>& c, | |
| 323 LineEndpointComputationMode mode) { | |
| 324 DCHECK(c.IsValid()) << c; | |
| 325 if (c.IsNull()) | |
| 326 return VisiblePositionTemplate<Strategy>(); | |
| 327 | |
| 328 RootInlineBox* root_box = RenderedPosition(c).RootBox(); | |
| 329 if (!root_box) { | |
| 330 // There are VisiblePositions at offset 0 in blocks without | |
| 331 // RootInlineBoxes, like empty editable blocks and bordered blocks. | |
| 332 const PositionTemplate<Strategy> p = c.DeepEquivalent(); | |
| 333 if (p.AnchorNode()->GetLayoutObject() && | |
| 334 p.AnchorNode()->GetLayoutObject()->IsLayoutBlock() && | |
| 335 !p.ComputeEditingOffset()) | |
| 336 return c; | |
| 337 return VisiblePositionTemplate<Strategy>(); | |
| 338 } | |
| 339 | |
| 340 Node* end_node; | |
| 341 InlineBox* end_box; | |
| 342 if (mode == kUseLogicalOrdering) { | |
| 343 end_node = root_box->GetLogicalEndBoxWithNode(end_box); | |
| 344 if (!end_node) | |
| 345 return VisiblePositionTemplate<Strategy>(); | |
| 346 } else { | |
| 347 // Generated content (e.g. list markers and CSS :before and :after | |
| 348 // pseudo elements) have no corresponding DOM element, and so cannot be | |
| 349 // represented by a VisiblePosition. Use whatever precedes instead. | |
| 350 end_box = root_box->LastLeafChild(); | |
| 351 while (true) { | |
| 352 if (!end_box) | |
| 353 return VisiblePositionTemplate<Strategy>(); | |
| 354 | |
| 355 end_node = end_box->GetLineLayoutItem().NonPseudoNode(); | |
| 356 if (end_node) | |
| 357 break; | |
| 358 | |
| 359 end_box = end_box->PrevLeafChild(); | |
| 360 } | |
| 361 } | |
| 362 | |
| 363 PositionTemplate<Strategy> pos; | |
| 364 if (isHTMLBRElement(*end_node)) { | |
| 365 pos = PositionTemplate<Strategy>::BeforeNode(end_node); | |
| 366 } else if (end_box->IsInlineTextBox() && end_node->IsTextNode()) { | |
| 367 InlineTextBox* end_text_box = ToInlineTextBox(end_box); | |
| 368 int end_offset = end_text_box->Start(); | |
| 369 if (!end_text_box->IsLineBreak()) | |
| 370 end_offset += end_text_box->Len(); | |
| 371 pos = PositionTemplate<Strategy>(ToText(end_node), end_offset); | |
| 372 } else { | |
| 373 pos = PositionTemplate<Strategy>::AfterNode(end_node); | |
| 374 } | |
| 375 | |
| 376 return CreateVisiblePosition(pos, VP_UPSTREAM_IF_POSSIBLE); | |
| 377 } | |
| 378 | |
| 379 // TODO(yosin) Rename this function to reflect the fact it ignores bidi levels. | |
| 380 template <typename Strategy> | |
| 381 static VisiblePositionTemplate<Strategy> EndOfLineAlgorithm( | |
| 382 const VisiblePositionTemplate<Strategy>& current_position) { | |
| 383 DCHECK(current_position.IsValid()) << current_position; | |
| 384 // TODO(yosin) this is the current behavior that might need to be fixed. | |
| 385 // Please refer to https://bugs.webkit.org/show_bug.cgi?id=49107 for detail. | |
| 386 VisiblePositionTemplate<Strategy> vis_pos = | |
| 387 EndPositionForLine(current_position, kUseInlineBoxOrdering); | |
| 388 | |
| 389 // Make sure the end of line is at the same line as the given input | |
| 390 // position. Else use the previous position to obtain end of line. This | |
| 391 // condition happens when the input position is before the space character | |
| 392 // at the end of a soft-wrapped non-editable line. In this scenario, | |
| 393 // |endPositionForLine()| would incorrectly hand back a position in the next | |
| 394 // line instead. This fix is to account for the discrepancy between lines | |
| 395 // with "webkit-line-break:after-white-space" style versus lines without | |
| 396 // that style, which would break before a space by default. | |
| 397 if (!InSameLine(current_position, vis_pos)) { | |
| 398 vis_pos = PreviousPositionOf(current_position); | |
| 399 if (vis_pos.IsNull()) | |
| 400 return VisiblePositionTemplate<Strategy>(); | |
| 401 vis_pos = EndPositionForLine(vis_pos, kUseInlineBoxOrdering); | |
| 402 } | |
| 403 | |
| 404 return HonorEditingBoundaryAtOrAfter(vis_pos, | |
| 405 current_position.DeepEquivalent()); | |
| 406 } | |
| 407 | |
| 408 // TODO(yosin) Rename this function to reflect the fact it ignores bidi levels. | |
| 409 VisiblePosition EndOfLine(const VisiblePosition& current_position) { | |
| 410 return EndOfLineAlgorithm<EditingStrategy>(current_position); | |
| 411 } | |
| 412 | |
| 413 VisiblePositionInFlatTree EndOfLine( | |
| 414 const VisiblePositionInFlatTree& current_position) { | |
| 415 return EndOfLineAlgorithm<EditingInFlatTreeStrategy>(current_position); | |
| 416 } | |
| 417 | |
| 418 template <typename Strategy> | |
| 419 static bool InSameLogicalLine(const VisiblePositionTemplate<Strategy>& a, | |
| 420 const VisiblePositionTemplate<Strategy>& b) { | |
| 421 DCHECK(a.IsValid()) << a; | |
| 422 DCHECK(b.IsValid()) << b; | |
| 423 return a.IsNotNull() && LogicalStartOfLine(a).DeepEquivalent() == | |
| 424 LogicalStartOfLine(b).DeepEquivalent(); | |
| 425 } | |
| 426 | |
| 427 template <typename Strategy> | |
| 428 static VisiblePositionTemplate<Strategy> LogicalEndOfLineAlgorithm( | |
| 429 const VisiblePositionTemplate<Strategy>& current_position) { | |
| 430 DCHECK(current_position.IsValid()) << current_position; | |
| 431 // TODO(yosin) this is the current behavior that might need to be fixed. | |
| 432 // Please refer to https://bugs.webkit.org/show_bug.cgi?id=49107 for detail. | |
| 433 VisiblePositionTemplate<Strategy> vis_pos = | |
| 434 EndPositionForLine(current_position, kUseLogicalOrdering); | |
| 435 | |
| 436 // Make sure the end of line is at the same line as the given input | |
| 437 // position. For a wrapping line, the logical end position for the | |
| 438 // not-last-2-lines might incorrectly hand back the logical beginning of the | |
| 439 // next line. For example, | |
| 440 // <div contenteditable dir="rtl" style="line-break:before-white-space">xyz | |
| 441 // a xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz </div> | |
| 442 // In this case, use the previous position of the computed logical end | |
| 443 // position. | |
| 444 if (!InSameLogicalLine(current_position, vis_pos)) | |
| 445 vis_pos = PreviousPositionOf(vis_pos); | |
| 446 | |
| 447 if (ContainerNode* editable_root = | |
| 448 HighestEditableRoot(current_position.DeepEquivalent())) { | |
| 449 if (!editable_root->contains( | |
| 450 vis_pos.DeepEquivalent().ComputeContainerNode())) { | |
| 451 return CreateVisiblePosition( | |
| 452 PositionTemplate<Strategy>::LastPositionInNode(editable_root)); | |
| 453 } | |
| 454 } | |
| 455 | |
| 456 return HonorEditingBoundaryAtOrAfter(vis_pos, | |
| 457 current_position.DeepEquivalent()); | |
| 458 } | |
| 459 | |
| 460 VisiblePosition LogicalEndOfLine(const VisiblePosition& current_position) { | |
| 461 return LogicalEndOfLineAlgorithm<EditingStrategy>(current_position); | |
| 462 } | |
| 463 | |
| 464 VisiblePositionInFlatTree LogicalEndOfLine( | |
| 465 const VisiblePositionInFlatTree& current_position) { | |
| 466 return LogicalEndOfLineAlgorithm<EditingInFlatTreeStrategy>(current_position); | |
| 467 } | |
| 468 | |
| 469 template <typename Strategy> | |
| 470 static bool InSameLineAlgorithm( | |
| 471 const PositionWithAffinityTemplate<Strategy>& position1, | |
| 472 const PositionWithAffinityTemplate<Strategy>& position2) { | |
| 473 if (position1.IsNull() || position2.IsNull()) | |
| 474 return false; | |
| 475 DCHECK_EQ(position1.GetDocument(), position2.GetDocument()); | |
| 476 DCHECK(!position1.GetDocument()->NeedsLayoutTreeUpdate()); | |
| 477 | |
| 478 PositionWithAffinityTemplate<Strategy> start_of_line1 = | |
| 479 StartOfLine(position1); | |
| 480 PositionWithAffinityTemplate<Strategy> start_of_line2 = | |
| 481 StartOfLine(position2); | |
| 482 if (start_of_line1 == start_of_line2) | |
| 483 return true; | |
| 484 PositionTemplate<Strategy> canonicalized1 = | |
| 485 CanonicalPositionOf(start_of_line1.GetPosition()); | |
| 486 if (canonicalized1 == start_of_line2.GetPosition()) | |
| 487 return true; | |
| 488 return canonicalized1 == CanonicalPositionOf(start_of_line2.GetPosition()); | |
| 489 } | |
| 490 | |
| 491 bool InSameLine(const PositionWithAffinity& a, const PositionWithAffinity& b) { | |
| 492 return InSameLineAlgorithm<EditingStrategy>(a, b); | |
| 493 } | |
| 494 | |
| 495 bool InSameLine(const PositionInFlatTreeWithAffinity& position1, | |
| 496 const PositionInFlatTreeWithAffinity& position2) { | |
| 497 return InSameLineAlgorithm<EditingInFlatTreeStrategy>(position1, position2); | |
| 498 } | |
| 499 | |
| 500 bool InSameLine(const VisiblePosition& position1, | |
| 501 const VisiblePosition& position2) { | |
| 502 DCHECK(position1.IsValid()) << position1; | |
| 503 DCHECK(position2.IsValid()) << position2; | |
| 504 return InSameLine(position1.ToPositionWithAffinity(), | |
| 505 position2.ToPositionWithAffinity()); | |
| 506 } | |
| 507 | |
| 508 bool InSameLine(const VisiblePositionInFlatTree& position1, | |
| 509 const VisiblePositionInFlatTree& position2) { | |
| 510 DCHECK(position1.IsValid()) << position1; | |
| 511 DCHECK(position2.IsValid()) << position2; | |
| 512 return InSameLine(position1.ToPositionWithAffinity(), | |
| 513 position2.ToPositionWithAffinity()); | |
| 514 } | |
| 515 | |
| 516 template <typename Strategy> | |
| 517 static bool IsStartOfLineAlgorithm(const VisiblePositionTemplate<Strategy>& p) { | |
| 518 DCHECK(p.IsValid()) << p; | |
| 519 return p.IsNotNull() && p.DeepEquivalent() == StartOfLine(p).DeepEquivalent(); | |
| 520 } | |
| 521 | |
| 522 bool IsStartOfLine(const VisiblePosition& p) { | |
| 523 return IsStartOfLineAlgorithm<EditingStrategy>(p); | |
| 524 } | |
| 525 | |
| 526 bool IsStartOfLine(const VisiblePositionInFlatTree& p) { | |
| 527 return IsStartOfLineAlgorithm<EditingInFlatTreeStrategy>(p); | |
| 528 } | |
| 529 | |
| 530 template <typename Strategy> | |
| 531 static bool IsEndOfLineAlgorithm(const VisiblePositionTemplate<Strategy>& p) { | |
| 532 DCHECK(p.IsValid()) << p; | |
| 533 return p.IsNotNull() && p.DeepEquivalent() == EndOfLine(p).DeepEquivalent(); | |
| 534 } | |
| 535 | |
| 536 bool IsEndOfLine(const VisiblePosition& p) { | |
| 537 return IsEndOfLineAlgorithm<EditingStrategy>(p); | |
| 538 } | |
| 539 | |
| 540 bool IsEndOfLine(const VisiblePositionInFlatTree& p) { | |
| 541 return IsEndOfLineAlgorithm<EditingInFlatTreeStrategy>(p); | |
| 542 } | |
| 543 | |
| 544 template <typename Strategy> | |
| 545 static bool IsLogicalEndOfLineAlgorithm( | |
| 546 const VisiblePositionTemplate<Strategy>& p) { | |
| 547 DCHECK(p.IsValid()) << p; | |
| 548 return p.IsNotNull() && | |
| 549 p.DeepEquivalent() == LogicalEndOfLine(p).DeepEquivalent(); | |
| 550 } | |
| 551 | |
| 552 bool IsLogicalEndOfLine(const VisiblePosition& p) { | |
| 553 return IsLogicalEndOfLineAlgorithm<EditingStrategy>(p); | |
| 554 } | |
| 555 | |
| 556 bool IsLogicalEndOfLine(const VisiblePositionInFlatTree& p) { | |
| 557 return IsLogicalEndOfLineAlgorithm<EditingInFlatTreeStrategy>(p); | |
| 558 } | |
| 559 | |
| 560 VisiblePosition PreviousLinePosition(const VisiblePosition& visible_position, | |
| 561 LayoutUnit line_direction_point, | |
| 562 EditableType editable_type) { | |
| 563 DCHECK(visible_position.IsValid()) << visible_position; | |
| 564 | |
| 565 Position p = visible_position.DeepEquivalent(); | |
| 566 Node* node = p.AnchorNode(); | |
| 567 | |
| 568 if (!node) | |
| 569 return VisiblePosition(); | |
| 570 | |
| 571 LayoutObject* layout_object = node->GetLayoutObject(); | |
| 572 if (!layout_object) | |
| 573 return VisiblePosition(); | |
| 574 | |
| 575 RootInlineBox* root = nullptr; | |
| 576 InlineBox* box = ComputeInlineBoxPosition(visible_position).inline_box; | |
| 577 if (box) { | |
| 578 root = box->Root().PrevRootBox(); | |
| 579 // We want to skip zero height boxes. | |
| 580 // This could happen in case it is a TrailingFloatsRootInlineBox. | |
| 581 if (!root || !root->LogicalHeight() || !root->FirstLeafChild()) | |
| 582 root = nullptr; | |
| 583 } | |
| 584 | |
| 585 if (!root) { | |
| 586 Position position = PreviousRootInlineBoxCandidatePosition( | |
| 587 node, visible_position, editable_type); | |
| 588 if (position.IsNotNull()) { | |
| 589 RenderedPosition rendered_position((CreateVisiblePosition(position))); | |
| 590 root = rendered_position.RootBox(); | |
| 591 if (!root) | |
| 592 return CreateVisiblePosition(position); | |
| 593 } | |
| 594 } | |
| 595 | |
| 596 if (root) { | |
| 597 // FIXME: Can be wrong for multi-column layout and with transforms. | |
| 598 LayoutPoint point_in_line = AbsoluteLineDirectionPointToLocalPointInBlock( | |
| 599 root, line_direction_point); | |
| 600 LineLayoutItem line_layout_item = | |
| 601 root->ClosestLeafChildForPoint(point_in_line, IsEditablePosition(p)) | |
| 602 ->GetLineLayoutItem(); | |
| 603 Node* node = line_layout_item.GetNode(); | |
| 604 if (node && EditingIgnoresContent(*node)) | |
| 605 return VisiblePosition::InParentBeforeNode(*node); | |
| 606 return CreateVisiblePosition( | |
| 607 line_layout_item.PositionForPoint(point_in_line)); | |
| 608 } | |
| 609 | |
| 610 // Could not find a previous line. This means we must already be on the first | |
| 611 // line. Move to the start of the content in this block, which effectively | |
| 612 // moves us to the start of the line we're on. | |
| 613 Element* root_element = HasEditableStyle(*node, editable_type) | |
| 614 ? RootEditableElement(*node, editable_type) | |
| 615 : node->GetDocument().documentElement(); | |
| 616 if (!root_element) | |
| 617 return VisiblePosition(); | |
| 618 return VisiblePosition::FirstPositionInNode(root_element); | |
| 619 } | |
| 620 | |
| 621 VisiblePosition NextLinePosition(const VisiblePosition& visible_position, | |
| 622 LayoutUnit line_direction_point, | |
| 623 EditableType editable_type) { | |
| 624 DCHECK(visible_position.IsValid()) << visible_position; | |
| 625 | |
| 626 Position p = visible_position.DeepEquivalent(); | |
| 627 Node* node = p.AnchorNode(); | |
| 628 | |
| 629 if (!node) | |
| 630 return VisiblePosition(); | |
| 631 | |
| 632 LayoutObject* layout_object = node->GetLayoutObject(); | |
| 633 if (!layout_object) | |
| 634 return VisiblePosition(); | |
| 635 | |
| 636 RootInlineBox* root = nullptr; | |
| 637 InlineBox* box = ComputeInlineBoxPosition(visible_position).inline_box; | |
| 638 if (box) { | |
| 639 root = box->Root().NextRootBox(); | |
| 640 // We want to skip zero height boxes. | |
| 641 // This could happen in case it is a TrailingFloatsRootInlineBox. | |
| 642 if (!root || !root->LogicalHeight() || !root->FirstLeafChild()) | |
| 643 root = nullptr; | |
| 644 } | |
| 645 | |
| 646 if (!root) { | |
| 647 // FIXME: We need do the same in previousLinePosition. | |
| 648 Node* child = NodeTraversal::ChildAt(*node, p.ComputeEditingOffset()); | |
| 649 node = child ? child : &NodeTraversal::LastWithinOrSelf(*node); | |
| 650 Position position = NextRootInlineBoxCandidatePosition( | |
| 651 node, visible_position, editable_type); | |
| 652 if (position.IsNotNull()) { | |
| 653 RenderedPosition rendered_position((CreateVisiblePosition(position))); | |
| 654 root = rendered_position.RootBox(); | |
| 655 if (!root) | |
| 656 return CreateVisiblePosition(position); | |
| 657 } | |
| 658 } | |
| 659 | |
| 660 if (root) { | |
| 661 // FIXME: Can be wrong for multi-column layout and with transforms. | |
| 662 LayoutPoint point_in_line = AbsoluteLineDirectionPointToLocalPointInBlock( | |
| 663 root, line_direction_point); | |
| 664 LineLayoutItem line_layout_item = | |
| 665 root->ClosestLeafChildForPoint(point_in_line, IsEditablePosition(p)) | |
| 666 ->GetLineLayoutItem(); | |
| 667 Node* node = line_layout_item.GetNode(); | |
| 668 if (node && EditingIgnoresContent(*node)) | |
| 669 return VisiblePosition::InParentBeforeNode(*node); | |
| 670 return CreateVisiblePosition( | |
| 671 line_layout_item.PositionForPoint(point_in_line)); | |
| 672 } | |
| 673 | |
| 674 // Could not find a next line. This means we must already be on the last line. | |
| 675 // Move to the end of the content in this block, which effectively moves us | |
| 676 // to the end of the line we're on. | |
| 677 Element* root_element = HasEditableStyle(*node, editable_type) | |
| 678 ? RootEditableElement(*node, editable_type) | |
| 679 : node->GetDocument().documentElement(); | |
| 680 if (!root_element) | |
| 681 return VisiblePosition(); | |
| 682 return VisiblePosition::LastPositionInNode(root_element); | |
| 683 } | |
| 684 | |
| 685 } // namespace blink | |
| OLD | NEW |