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