| 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/SelectionModifier.h" |
| 32 |
| 33 #include "core/editing/EditingUtilities.h" |
| 34 #include "core/editing/VisibleUnits.h" |
| 35 #include "core/layout/api/LineLayoutAPIShim.h" |
| 36 #include "core/layout/api/LineLayoutItem.h" |
| 37 #include "core/layout/line/InlineTextBox.h" |
| 38 #include "core/layout/line/RootInlineBox.h" |
| 39 |
| 40 namespace blink { |
| 41 |
| 42 namespace { |
| 43 |
| 44 template <typename Strategy> |
| 45 static PositionTemplate<Strategy> LeftVisuallyDistinctCandidate( |
| 46 const VisiblePositionTemplate<Strategy>& visible_position) { |
| 47 DCHECK(visible_position.IsValid()) << visible_position; |
| 48 const PositionTemplate<Strategy> deep_position = |
| 49 visible_position.DeepEquivalent(); |
| 50 PositionTemplate<Strategy> p = deep_position; |
| 51 |
| 52 if (p.IsNull()) |
| 53 return PositionTemplate<Strategy>(); |
| 54 |
| 55 const PositionTemplate<Strategy> downstream_start = |
| 56 MostForwardCaretPosition(p); |
| 57 const TextDirection primary_direction = PrimaryDirectionOf(*p.AnchorNode()); |
| 58 const TextAffinity affinity = visible_position.Affinity(); |
| 59 |
| 60 while (true) { |
| 61 InlineBoxPosition box_position = |
| 62 ComputeInlineBoxPosition(p, affinity, primary_direction); |
| 63 InlineBox* box = box_position.inline_box; |
| 64 int offset = box_position.offset_in_box; |
| 65 if (!box) { |
| 66 return primary_direction == TextDirection::kLtr |
| 67 ? PreviousVisuallyDistinctCandidate(deep_position) |
| 68 : NextVisuallyDistinctCandidate(deep_position); |
| 69 } |
| 70 LineLayoutItem line_layout_item = box->GetLineLayoutItem(); |
| 71 |
| 72 while (true) { |
| 73 if ((line_layout_item.IsAtomicInlineLevel() || line_layout_item.IsBR()) && |
| 74 offset == box->CaretRightmostOffset()) { |
| 75 return box->IsLeftToRightDirection() |
| 76 ? PreviousVisuallyDistinctCandidate(deep_position) |
| 77 : NextVisuallyDistinctCandidate(deep_position); |
| 78 } |
| 79 if (!line_layout_item.GetNode()) { |
| 80 box = box->PrevLeafChild(); |
| 81 if (!box) { |
| 82 return primary_direction == TextDirection::kLtr |
| 83 ? PreviousVisuallyDistinctCandidate(deep_position) |
| 84 : NextVisuallyDistinctCandidate(deep_position); |
| 85 } |
| 86 line_layout_item = box->GetLineLayoutItem(); |
| 87 offset = box->CaretRightmostOffset(); |
| 88 continue; |
| 89 } |
| 90 |
| 91 offset = |
| 92 box->IsLeftToRightDirection() |
| 93 ? PreviousGraphemeBoundaryOf(line_layout_item.GetNode(), offset) |
| 94 : NextGraphemeBoundaryOf(line_layout_item.GetNode(), offset); |
| 95 |
| 96 const int caret_min_offset = box->CaretMinOffset(); |
| 97 const int caret_max_offset = box->CaretMaxOffset(); |
| 98 |
| 99 if (offset > caret_min_offset && offset < caret_max_offset) |
| 100 break; |
| 101 |
| 102 if (box->IsLeftToRightDirection() ? offset < caret_min_offset |
| 103 : offset > caret_max_offset) { |
| 104 // Overshot to the left. |
| 105 InlineBox* const prev_box = box->PrevLeafChildIgnoringLineBreak(); |
| 106 if (!prev_box) { |
| 107 PositionTemplate<Strategy> position_on_left = |
| 108 primary_direction == TextDirection::kLtr |
| 109 ? PreviousVisuallyDistinctCandidate( |
| 110 visible_position.DeepEquivalent()) |
| 111 : NextVisuallyDistinctCandidate( |
| 112 visible_position.DeepEquivalent()); |
| 113 if (position_on_left.IsNull()) |
| 114 return PositionTemplate<Strategy>(); |
| 115 |
| 116 InlineBox* box_on_left = |
| 117 ComputeInlineBoxPosition(position_on_left, affinity, |
| 118 primary_direction) |
| 119 .inline_box; |
| 120 if (box_on_left && box_on_left->Root() == box->Root()) |
| 121 return PositionTemplate<Strategy>(); |
| 122 return position_on_left; |
| 123 } |
| 124 |
| 125 // Reposition at the other logical position corresponding to our |
| 126 // edge's visual position and go for another round. |
| 127 box = prev_box; |
| 128 line_layout_item = box->GetLineLayoutItem(); |
| 129 offset = prev_box->CaretRightmostOffset(); |
| 130 continue; |
| 131 } |
| 132 |
| 133 DCHECK_EQ(offset, box->CaretLeftmostOffset()); |
| 134 |
| 135 unsigned char level = box->BidiLevel(); |
| 136 InlineBox* prev_box = box->PrevLeafChild(); |
| 137 |
| 138 if (box->Direction() == primary_direction) { |
| 139 if (!prev_box) { |
| 140 InlineBox* logical_start = nullptr; |
| 141 if (primary_direction == TextDirection::kLtr |
| 142 ? box->Root().GetLogicalStartBoxWithNode(logical_start) |
| 143 : box->Root().GetLogicalEndBoxWithNode(logical_start)) { |
| 144 box = logical_start; |
| 145 line_layout_item = box->GetLineLayoutItem(); |
| 146 offset = primary_direction == TextDirection::kLtr |
| 147 ? box->CaretMinOffset() |
| 148 : box->CaretMaxOffset(); |
| 149 } |
| 150 break; |
| 151 } |
| 152 if (prev_box->BidiLevel() >= level) |
| 153 break; |
| 154 |
| 155 level = prev_box->BidiLevel(); |
| 156 |
| 157 InlineBox* next_box = box; |
| 158 do { |
| 159 next_box = next_box->NextLeafChild(); |
| 160 } while (next_box && next_box->BidiLevel() > level); |
| 161 |
| 162 if (next_box && next_box->BidiLevel() == level) |
| 163 break; |
| 164 |
| 165 box = prev_box; |
| 166 line_layout_item = box->GetLineLayoutItem(); |
| 167 offset = box->CaretRightmostOffset(); |
| 168 if (box->Direction() == primary_direction) |
| 169 break; |
| 170 continue; |
| 171 } |
| 172 |
| 173 while (prev_box && !prev_box->GetLineLayoutItem().GetNode()) |
| 174 prev_box = prev_box->PrevLeafChild(); |
| 175 |
| 176 if (prev_box) { |
| 177 box = prev_box; |
| 178 line_layout_item = box->GetLineLayoutItem(); |
| 179 offset = box->CaretRightmostOffset(); |
| 180 if (box->BidiLevel() > level) { |
| 181 do { |
| 182 prev_box = prev_box->PrevLeafChild(); |
| 183 } while (prev_box && prev_box->BidiLevel() > level); |
| 184 |
| 185 if (!prev_box || prev_box->BidiLevel() < level) |
| 186 continue; |
| 187 } |
| 188 } else { |
| 189 // Trailing edge of a secondary run. Set to the leading edge of |
| 190 // the entire run. |
| 191 while (true) { |
| 192 while (InlineBox* next_box = box->NextLeafChild()) { |
| 193 if (next_box->BidiLevel() < level) |
| 194 break; |
| 195 box = next_box; |
| 196 } |
| 197 if (box->BidiLevel() == level) |
| 198 break; |
| 199 level = box->BidiLevel(); |
| 200 while (InlineBox* prev_box = box->PrevLeafChild()) { |
| 201 if (prev_box->BidiLevel() < level) |
| 202 break; |
| 203 box = prev_box; |
| 204 } |
| 205 if (box->BidiLevel() == level) |
| 206 break; |
| 207 level = box->BidiLevel(); |
| 208 } |
| 209 line_layout_item = box->GetLineLayoutItem(); |
| 210 offset = primary_direction == TextDirection::kLtr |
| 211 ? box->CaretMinOffset() |
| 212 : box->CaretMaxOffset(); |
| 213 } |
| 214 break; |
| 215 } |
| 216 |
| 217 p = PositionTemplate<Strategy>::EditingPositionOf( |
| 218 line_layout_item.GetNode(), offset); |
| 219 |
| 220 if ((IsVisuallyEquivalentCandidate(p) && |
| 221 MostForwardCaretPosition(p) != downstream_start) || |
| 222 p.AtStartOfTree() || p.AtEndOfTree()) |
| 223 return p; |
| 224 |
| 225 DCHECK_NE(p, deep_position); |
| 226 } |
| 227 } |
| 228 |
| 229 template <typename Strategy> |
| 230 VisiblePositionTemplate<Strategy> LeftPositionOfAlgorithm( |
| 231 const VisiblePositionTemplate<Strategy>& visible_position) { |
| 232 DCHECK(visible_position.IsValid()) << visible_position; |
| 233 const PositionTemplate<Strategy> pos = |
| 234 LeftVisuallyDistinctCandidate(visible_position); |
| 235 // TODO(yosin) Why can't we move left from the last position in a tree? |
| 236 if (pos.AtStartOfTree() || pos.AtEndOfTree()) |
| 237 return VisiblePositionTemplate<Strategy>(); |
| 238 |
| 239 const VisiblePositionTemplate<Strategy> left = CreateVisiblePosition(pos); |
| 240 DCHECK_NE(left.DeepEquivalent(), visible_position.DeepEquivalent()); |
| 241 |
| 242 return DirectionOfEnclosingBlockOf(left.DeepEquivalent()) == |
| 243 TextDirection::kLtr |
| 244 ? HonorEditingBoundaryAtOrBefore(left, |
| 245 visible_position.DeepEquivalent()) |
| 246 : HonorEditingBoundaryAtOrAfter(left, |
| 247 visible_position.DeepEquivalent()); |
| 248 } |
| 249 |
| 250 template <typename Strategy> |
| 251 PositionTemplate<Strategy> RightVisuallyDistinctCandidate( |
| 252 const VisiblePositionTemplate<Strategy>& visible_position) { |
| 253 DCHECK(visible_position.IsValid()) << visible_position; |
| 254 const PositionTemplate<Strategy> deep_position = |
| 255 visible_position.DeepEquivalent(); |
| 256 PositionTemplate<Strategy> p = deep_position; |
| 257 if (p.IsNull()) |
| 258 return PositionTemplate<Strategy>(); |
| 259 |
| 260 const PositionTemplate<Strategy> downstream_start = |
| 261 MostForwardCaretPosition(p); |
| 262 const TextDirection primary_direction = PrimaryDirectionOf(*p.AnchorNode()); |
| 263 const TextAffinity affinity = visible_position.Affinity(); |
| 264 |
| 265 while (true) { |
| 266 InlineBoxPosition box_position = |
| 267 ComputeInlineBoxPosition(p, affinity, primary_direction); |
| 268 InlineBox* box = box_position.inline_box; |
| 269 int offset = box_position.offset_in_box; |
| 270 if (!box) { |
| 271 return primary_direction == TextDirection::kLtr |
| 272 ? NextVisuallyDistinctCandidate(deep_position) |
| 273 : PreviousVisuallyDistinctCandidate(deep_position); |
| 274 } |
| 275 LayoutObject* layout_object = |
| 276 LineLayoutAPIShim::LayoutObjectFrom(box->GetLineLayoutItem()); |
| 277 |
| 278 while (true) { |
| 279 if ((layout_object->IsAtomicInlineLevel() || layout_object->IsBR()) && |
| 280 offset == box->CaretLeftmostOffset()) { |
| 281 return box->IsLeftToRightDirection() |
| 282 ? NextVisuallyDistinctCandidate(deep_position) |
| 283 : PreviousVisuallyDistinctCandidate(deep_position); |
| 284 } |
| 285 if (!layout_object->GetNode()) { |
| 286 box = box->NextLeafChild(); |
| 287 if (!box) { |
| 288 return primary_direction == TextDirection::kLtr |
| 289 ? NextVisuallyDistinctCandidate(deep_position) |
| 290 : PreviousVisuallyDistinctCandidate(deep_position); |
| 291 } |
| 292 layout_object = |
| 293 LineLayoutAPIShim::LayoutObjectFrom(box->GetLineLayoutItem()); |
| 294 offset = box->CaretLeftmostOffset(); |
| 295 continue; |
| 296 } |
| 297 |
| 298 offset = |
| 299 box->IsLeftToRightDirection() |
| 300 ? NextGraphemeBoundaryOf(layout_object->GetNode(), offset) |
| 301 : PreviousGraphemeBoundaryOf(layout_object->GetNode(), offset); |
| 302 |
| 303 const int caret_min_offset = box->CaretMinOffset(); |
| 304 const int caret_max_offset = box->CaretMaxOffset(); |
| 305 |
| 306 if (offset > caret_min_offset && offset < caret_max_offset) |
| 307 break; |
| 308 |
| 309 if (box->IsLeftToRightDirection() ? offset > caret_max_offset |
| 310 : offset < caret_min_offset) { |
| 311 // Overshot to the right. |
| 312 InlineBox* const next_box = box->NextLeafChildIgnoringLineBreak(); |
| 313 if (!next_box) { |
| 314 PositionTemplate<Strategy> position_on_right = |
| 315 primary_direction == TextDirection::kLtr |
| 316 ? NextVisuallyDistinctCandidate(deep_position) |
| 317 : PreviousVisuallyDistinctCandidate(deep_position); |
| 318 if (position_on_right.IsNull()) |
| 319 return PositionTemplate<Strategy>(); |
| 320 |
| 321 InlineBox* box_on_right = |
| 322 ComputeInlineBoxPosition(position_on_right, affinity, |
| 323 primary_direction) |
| 324 .inline_box; |
| 325 if (box_on_right && box_on_right->Root() == box->Root()) |
| 326 return PositionTemplate<Strategy>(); |
| 327 return position_on_right; |
| 328 } |
| 329 |
| 330 // Reposition at the other logical position corresponding to our |
| 331 // edge's visual position and go for another round. |
| 332 box = next_box; |
| 333 layout_object = |
| 334 LineLayoutAPIShim::LayoutObjectFrom(box->GetLineLayoutItem()); |
| 335 offset = next_box->CaretLeftmostOffset(); |
| 336 continue; |
| 337 } |
| 338 |
| 339 DCHECK_EQ(offset, box->CaretRightmostOffset()); |
| 340 |
| 341 unsigned char level = box->BidiLevel(); |
| 342 InlineBox* next_box = box->NextLeafChild(); |
| 343 |
| 344 if (box->Direction() == primary_direction) { |
| 345 if (!next_box) { |
| 346 InlineBox* logical_end = nullptr; |
| 347 if (primary_direction == TextDirection::kLtr |
| 348 ? box->Root().GetLogicalEndBoxWithNode(logical_end) |
| 349 : box->Root().GetLogicalStartBoxWithNode(logical_end)) { |
| 350 box = logical_end; |
| 351 layout_object = |
| 352 LineLayoutAPIShim::LayoutObjectFrom(box->GetLineLayoutItem()); |
| 353 offset = primary_direction == TextDirection::kLtr |
| 354 ? box->CaretMaxOffset() |
| 355 : box->CaretMinOffset(); |
| 356 } |
| 357 break; |
| 358 } |
| 359 |
| 360 if (next_box->BidiLevel() >= level) |
| 361 break; |
| 362 |
| 363 level = next_box->BidiLevel(); |
| 364 |
| 365 InlineBox* prev_box = box; |
| 366 do { |
| 367 prev_box = prev_box->PrevLeafChild(); |
| 368 } while (prev_box && prev_box->BidiLevel() > level); |
| 369 |
| 370 // For example, abc FED 123 ^ CBA |
| 371 if (prev_box && prev_box->BidiLevel() == level) |
| 372 break; |
| 373 |
| 374 // For example, abc 123 ^ CBA or 123 ^ CBA abc |
| 375 box = next_box; |
| 376 layout_object = |
| 377 LineLayoutAPIShim::LayoutObjectFrom(box->GetLineLayoutItem()); |
| 378 offset = box->CaretLeftmostOffset(); |
| 379 if (box->Direction() == primary_direction) |
| 380 break; |
| 381 continue; |
| 382 } |
| 383 |
| 384 while (next_box && !next_box->GetLineLayoutItem().GetNode()) |
| 385 next_box = next_box->NextLeafChild(); |
| 386 |
| 387 if (next_box) { |
| 388 box = next_box; |
| 389 layout_object = |
| 390 LineLayoutAPIShim::LayoutObjectFrom(box->GetLineLayoutItem()); |
| 391 offset = box->CaretLeftmostOffset(); |
| 392 |
| 393 if (box->BidiLevel() > level) { |
| 394 do { |
| 395 next_box = next_box->NextLeafChild(); |
| 396 } while (next_box && next_box->BidiLevel() > level); |
| 397 |
| 398 if (!next_box || next_box->BidiLevel() < level) |
| 399 continue; |
| 400 } |
| 401 } else { |
| 402 // Trailing edge of a secondary run. Set to the leading edge of the |
| 403 // entire run. |
| 404 while (true) { |
| 405 while (InlineBox* prev_box = box->PrevLeafChild()) { |
| 406 if (prev_box->BidiLevel() < level) |
| 407 break; |
| 408 box = prev_box; |
| 409 } |
| 410 if (box->BidiLevel() == level) |
| 411 break; |
| 412 level = box->BidiLevel(); |
| 413 while (InlineBox* next_box = box->NextLeafChild()) { |
| 414 if (next_box->BidiLevel() < level) |
| 415 break; |
| 416 box = next_box; |
| 417 } |
| 418 if (box->BidiLevel() == level) |
| 419 break; |
| 420 level = box->BidiLevel(); |
| 421 } |
| 422 layout_object = |
| 423 LineLayoutAPIShim::LayoutObjectFrom(box->GetLineLayoutItem()); |
| 424 offset = primary_direction == TextDirection::kLtr |
| 425 ? box->CaretMaxOffset() |
| 426 : box->CaretMinOffset(); |
| 427 } |
| 428 break; |
| 429 } |
| 430 |
| 431 p = PositionTemplate<Strategy>::EditingPositionOf(layout_object->GetNode(), |
| 432 offset); |
| 433 |
| 434 if ((IsVisuallyEquivalentCandidate(p) && |
| 435 MostForwardCaretPosition(p) != downstream_start) || |
| 436 p.AtStartOfTree() || p.AtEndOfTree()) |
| 437 return p; |
| 438 |
| 439 DCHECK_NE(p, deep_position); |
| 440 } |
| 441 } |
| 442 |
| 443 template <typename Strategy> |
| 444 VisiblePositionTemplate<Strategy> RightPositionOfAlgorithm( |
| 445 const VisiblePositionTemplate<Strategy>& visible_position) { |
| 446 DCHECK(visible_position.IsValid()) << visible_position; |
| 447 const PositionTemplate<Strategy> pos = |
| 448 RightVisuallyDistinctCandidate(visible_position); |
| 449 // TODO(editing-dev): Why can't we move left from the last position in a tree? |
| 450 if (pos.AtStartOfTree() || pos.AtEndOfTree()) |
| 451 return VisiblePositionTemplate<Strategy>(); |
| 452 |
| 453 const VisiblePositionTemplate<Strategy> right = CreateVisiblePosition(pos); |
| 454 DCHECK_NE(right.DeepEquivalent(), visible_position.DeepEquivalent()); |
| 455 |
| 456 return DirectionOfEnclosingBlockOf(right.DeepEquivalent()) == |
| 457 TextDirection::kLtr |
| 458 ? HonorEditingBoundaryAtOrAfter(right, |
| 459 visible_position.DeepEquivalent()) |
| 460 : HonorEditingBoundaryAtOrBefore( |
| 461 right, visible_position.DeepEquivalent()); |
| 462 } |
| 463 |
| 464 } // namespace |
| 465 |
| 466 VisiblePosition LeftPositionOf(const VisiblePosition& visible_position) { |
| 467 return LeftPositionOfAlgorithm<EditingStrategy>(visible_position); |
| 468 } |
| 469 |
| 470 VisiblePositionInFlatTree LeftPositionOf( |
| 471 const VisiblePositionInFlatTree& visible_position) { |
| 472 return LeftPositionOfAlgorithm<EditingInFlatTreeStrategy>(visible_position); |
| 473 } |
| 474 |
| 475 VisiblePosition RightPositionOf(const VisiblePosition& visible_position) { |
| 476 return RightPositionOfAlgorithm<EditingStrategy>(visible_position); |
| 477 } |
| 478 |
| 479 VisiblePositionInFlatTree RightPositionOf( |
| 480 const VisiblePositionInFlatTree& visible_position) { |
| 481 return RightPositionOfAlgorithm<EditingInFlatTreeStrategy>(visible_position); |
| 482 } |
| 483 |
| 484 } // namespace blink |
| OLD | NEW |