| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) | 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) |
| 3 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights | 3 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights |
| 4 * reserved. | 4 * reserved. |
| 5 * | 5 * |
| 6 * This library is free software; you can redistribute it and/or | 6 * This library is free software; you can redistribute it and/or |
| 7 * modify it under the terms of the GNU Library General Public | 7 * modify it under the terms of the GNU Library General Public |
| 8 * License as published by the Free Software Foundation; either | 8 * License as published by the Free Software Foundation; either |
| 9 * version 2 of the License, or (at your option) any later version. | 9 * version 2 of the License, or (at your option) any later version. |
| 10 * | 10 * |
| 11 * This library is distributed in the hope that it will be useful, | 11 * This library is distributed in the hope that it will be useful, |
| 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of | 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 14 * Library General Public License for more details. | 14 * Library General Public License for more details. |
| 15 * | 15 * |
| 16 * You should have received a copy of the GNU Library General Public License | 16 * You should have received a copy of the GNU Library General Public License |
| 17 * along with this library; see the file COPYING.LIB. If not, write to | 17 * along with this library; see the file COPYING.LIB. If not, write to |
| 18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | 18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| 19 * Boston, MA 02110-1301, USA. | 19 * Boston, MA 02110-1301, USA. |
| 20 */ | 20 */ |
| 21 | 21 |
| 22 #include "core/editing/LayoutSelection.h" | 22 #include "core/editing/LayoutSelection.h" |
| 23 | 23 |
| 24 #include "core/dom/Document.h" | 24 #include "core/dom/Document.h" |
| 25 #include "core/editing/EditingUtilities.h" | 25 #include "core/editing/EditingUtilities.h" |
| 26 #include "core/editing/FrameSelection.h" | 26 #include "core/editing/FrameSelection.h" |
| 27 #include "core/editing/VisiblePosition.h" | 27 #include "core/editing/VisiblePosition.h" |
| 28 #include "core/editing/VisibleUnits.h" | 28 #include "core/editing/VisibleUnits.h" |
| 29 #include "core/html/TextControlElement.h" | 29 #include "core/html/TextControlElement.h" |
| 30 #include "core/layout/LayoutBlock.h" |
| 31 #include "core/layout/LayoutObject.h" |
| 30 #include "core/layout/LayoutView.h" | 32 #include "core/layout/LayoutView.h" |
| 31 #include "core/paint/PaintLayer.h" | 33 #include "core/paint/PaintLayer.h" |
| 32 | 34 |
| 33 namespace blink { | 35 namespace blink { |
| 34 | 36 |
| 35 SelectionPaintRange::SelectionPaintRange(LayoutObject* start_layout_object, | 37 SelectionPaintRange::SelectionPaintRange(LayoutObject* start_layout_object, |
| 36 int start_offset, | 38 int start_offset, |
| 37 LayoutObject* end_layout_object, | 39 LayoutObject* end_layout_object, |
| 38 int end_offset) | 40 int end_offset) |
| 39 : start_layout_object_(start_layout_object), | 41 : start_layout_object_(start_layout_object), |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 102 | 104 |
| 103 current_ = nullptr; | 105 current_ = nullptr; |
| 104 return *this; | 106 return *this; |
| 105 } | 107 } |
| 106 | 108 |
| 107 LayoutSelection::LayoutSelection(FrameSelection& frame_selection) | 109 LayoutSelection::LayoutSelection(FrameSelection& frame_selection) |
| 108 : frame_selection_(&frame_selection), | 110 : frame_selection_(&frame_selection), |
| 109 has_pending_selection_(false), | 111 has_pending_selection_(false), |
| 110 paint_range_(SelectionPaintRange()) {} | 112 paint_range_(SelectionPaintRange()) {} |
| 111 | 113 |
| 114 static bool IsSelectionAtomicAndVisible(LayoutObject* layout_object) { |
| 115 if (!layout_object) |
| 116 return false; |
| 117 // return true;/* |
| 118 if (layout_object->CanBeSelectionLeaf()) |
| 119 return true; |
| 120 if (layout_object->IsImage() || layout_object->IsLayoutEmbeddedContent()) |
| 121 return true; |
| 122 Node* node = layout_object->GetNode(); |
| 123 if (!node || !node->IsHTMLElement()) |
| 124 return false; |
| 125 if (isHTMLTableElement(node)) |
| 126 return true; |
| 127 if (IsHTMLFormControlElement(ToHTMLElement(*node)) || |
| 128 isHTMLLegendElement(ToHTMLElement(*node)) || |
| 129 isHTMLImageElement(ToHTMLElement(*node)) || |
| 130 isHTMLMeterElement(ToHTMLElement(*node)) || |
| 131 isHTMLProgressElement(ToHTMLElement(*node))) |
| 132 return true; |
| 133 return false; //*/ |
| 134 } |
| 135 |
| 136 static Vector<LayoutObject*> GetAncestors(LayoutObject* layout_object) { |
| 137 Vector<LayoutObject*> ancestors; |
| 138 for (LayoutObject* runner = layout_object; runner;) { |
| 139 ancestors.push_back(runner); |
| 140 LayoutObject* next = runner->Parent(); |
| 141 if (runner == next) |
| 142 break; |
| 143 runner = next; |
| 144 } |
| 145 return std::move(ancestors); |
| 146 } |
| 147 |
| 148 static Optional<int> compare(LayoutObject* a, LayoutObject* b) { |
| 149 if (a == b) |
| 150 return {0}; |
| 151 const Vector<LayoutObject*>& a_ancestors = GetAncestors(a); |
| 152 const Vector<LayoutObject*>& b_ancestors = GetAncestors(b); |
| 153 if (a_ancestors[a_ancestors.size() - 1] != |
| 154 b_ancestors[b_ancestors.size() - 1]) |
| 155 return {}; |
| 156 LayoutObject* recentAncestor = a_ancestors[a_ancestors.size() - 1]; |
| 157 size_t i = 0; |
| 158 for (; i < std::min(a_ancestors.size(), b_ancestors.size()); ++i) { |
| 159 if (a_ancestors[a_ancestors.size() - 1 - i] != |
| 160 b_ancestors[b_ancestors.size() - 1 - i]) |
| 161 break; |
| 162 recentAncestor = a_ancestors[a_ancestors.size() - 1 - i]; |
| 163 } |
| 164 |
| 165 if (i == a_ancestors.size()) |
| 166 return {-1}; // a is ancestor of b |
| 167 if (i == b_ancestors.size()) |
| 168 return {1}; // b is ancestor of a |
| 169 LayoutObject* const a_child_of_RA = a_ancestors[a_ancestors.size() - 1 - i]; |
| 170 LayoutObject* const b_child_of_RA = b_ancestors[b_ancestors.size() - 1 - i]; |
| 171 for (LayoutObject* runner = a_child_of_RA; runner; |
| 172 runner = runner->NextSibling()) { |
| 173 if (runner == b_child_of_RA) |
| 174 return {-1}; |
| 175 } |
| 176 return {1}; |
| 177 } |
| 178 |
| 179 struct SelectionPosition { |
| 180 STACK_ALLOCATED(); |
| 181 |
| 182 SelectionPosition() : SelectionPosition(nullptr, -1) {} |
| 183 SelectionPosition(LayoutObject* layout_object, int offset) |
| 184 : layout_object_(layout_object), offset_(offset) {} |
| 185 |
| 186 SelectionPosition(const PositionInFlatTree& position) : SelectionPosition() { |
| 187 if (position.IsNull()) |
| 188 return; |
| 189 layout_object_ = position.AnchorNode()->GetLayoutObject(); |
| 190 offset_ = position.ComputeEditingOffset(); |
| 191 } |
| 192 |
| 193 bool IsNull() const { return !layout_object_; } |
| 194 PositionInFlatTree ToPositionInFlatTree() const { |
| 195 if (IsNull()) |
| 196 return PositionInFlatTree(); |
| 197 return PositionInFlatTree(layout_object_->GetNode(), offset_); |
| 198 } |
| 199 |
| 200 LayoutObject* layout_object_; |
| 201 int offset_; |
| 202 |
| 203 bool operator==(const SelectionPosition& other) const { |
| 204 return layout_object_ == other.layout_object_ && offset_ == other.offset_; |
| 205 } |
| 206 bool operator!=(const SelectionPosition& other) const { |
| 207 return !operator==(other); |
| 208 } |
| 209 }; |
| 210 |
| 211 Node* ComputeNodeAfterPosition(const PositionInFlatTree& position) { |
| 212 if (!position.AnchorNode()) |
| 213 return 0; |
| 214 |
| 215 switch (position.AnchorType()) { |
| 216 case PositionAnchorType::kBeforeChildren: { |
| 217 if (Node* first_child = |
| 218 FlatTreeTraversal::FirstChild(*position.AnchorNode())) |
| 219 return first_child; |
| 220 FlatTreeTraversal::NextSkippingChildren(*position.AnchorNode()); |
| 221 } |
| 222 case PositionAnchorType::kAfterChildren: |
| 223 return FlatTreeTraversal::NextSkippingChildren(*position.AnchorNode()); |
| 224 case PositionAnchorType::kOffsetInAnchor: { |
| 225 if (position.AnchorNode()->IsCharacterDataNode()) |
| 226 return FlatTreeTraversal::Next(*position.AnchorNode()); |
| 227 if (Node* child_at = FlatTreeTraversal::ChildAt( |
| 228 *position.AnchorNode(), position.OffsetInContainerNode())) |
| 229 return child_at; |
| 230 return FlatTreeTraversal::Next(*position.AnchorNode()); |
| 231 } |
| 232 case PositionAnchorType::kBeforeAnchor: |
| 233 return position.AnchorNode(); |
| 234 case PositionAnchorType::kAfterAnchor: |
| 235 return FlatTreeTraversal::NextSkippingChildren(*position.AnchorNode()); |
| 236 } |
| 237 NOTREACHED(); |
| 238 return 0; |
| 239 } |
| 240 |
| 241 static SelectionPosition FirstLayoutPosition(const PositionInFlatTree& start) { |
| 242 if (start.AnchorNode()->IsTextNode() && |
| 243 start.AnchorNode()->GetLayoutObject()) { |
| 244 return start; |
| 245 } |
| 246 |
| 247 LayoutObject* first_layout_object = nullptr; |
| 248 for (Node* runner = ComputeNodeAfterPosition(start); runner; |
| 249 runner = FlatTreeTraversal::Next(*runner)) { |
| 250 if ((first_layout_object = runner->GetLayoutObject())) |
| 251 break; |
| 252 } |
| 253 if (!first_layout_object) |
| 254 return {}; |
| 255 |
| 256 for (LayoutObject* runner = first_layout_object; runner; |
| 257 runner = runner->NextInPreOrder()) { |
| 258 if (!IsSelectionAtomicAndVisible(runner)) |
| 259 continue; |
| 260 |
| 261 return {runner, 0}; |
| 262 } |
| 263 return {}; |
| 264 } |
| 265 |
| 266 // Traverse FlatTree parent first backward. |
| 267 // It looks mirror of Next(). |
| 268 static Node* Previous(const Node& node) { |
| 269 if (FlatTreeTraversal::FirstChild(node)) |
| 270 return FlatTreeTraversal::LastWithin(node); |
| 271 return FlatTreeTraversal::PreviousSkippingChildren(node); |
| 272 } |
| 273 |
| 274 // Traverse FlatTree parent first backward. |
| 275 // It looks mirror of Next(). |
| 276 static LayoutObject* Previous(const LayoutObject& layout_object) { |
| 277 if (LayoutObject* last_child = layout_object.SlowLastChild()) |
| 278 return last_child; |
| 279 if (LayoutObject* previous_sibling = layout_object.PreviousSibling()) |
| 280 return previous_sibling; |
| 281 for (LayoutObject* ancestor = layout_object.Parent(); ancestor;) { |
| 282 LayoutObject* ancestor_prev_sib = ancestor->PreviousSibling(); |
| 283 if (ancestor_prev_sib) |
| 284 return ancestor_prev_sib; |
| 285 LayoutObject* parent = layout_object.Parent(); |
| 286 // LayoutTests/paint/invalidation/text-selection-rect-in-overflow-2.html |
| 287 // makes infinite self-parent loop. Strange. |
| 288 if (parent == ancestor) |
| 289 return nullptr; |
| 290 ancestor = parent; |
| 291 } |
| 292 return nullptr; |
| 293 } |
| 294 |
| 295 static Node* ComputeNodeBeforePosition(const PositionInFlatTree& position) { |
| 296 if (!position.AnchorNode()) |
| 297 return nullptr; |
| 298 switch (position.AnchorType()) { |
| 299 case PositionAnchorType::kBeforeChildren: |
| 300 return Previous(*position.AnchorNode()); |
| 301 case PositionAnchorType::kAfterChildren: { |
| 302 if (Node* last_child = |
| 303 FlatTreeTraversal::LastChild(*position.AnchorNode())) |
| 304 return last_child; |
| 305 return Previous(*position.AnchorNode()); |
| 306 } |
| 307 case PositionAnchorType::kOffsetInAnchor: { |
| 308 if (position.AnchorNode()->IsCharacterDataNode()) |
| 309 return Previous(*position.AnchorNode()); |
| 310 if (position.OffsetInContainerNode() == 0) |
| 311 return Previous(*position.AnchorNode()); |
| 312 Node* child_before_offset = FlatTreeTraversal::ChildAt( |
| 313 *position.AnchorNode(), position.OffsetInContainerNode() - 1); |
| 314 return child_before_offset; |
| 315 } |
| 316 case PositionAnchorType::kBeforeAnchor: |
| 317 return Previous(*position.AnchorNode()); |
| 318 case PositionAnchorType::kAfterAnchor: |
| 319 return position.AnchorNode(); |
| 320 } |
| 321 NOTREACHED(); |
| 322 return 0; |
| 323 } |
| 324 |
| 325 static SelectionPosition LastLayoutPosition(const PositionInFlatTree& end) { |
| 326 if (end.AnchorNode()->IsTextNode() && end.AnchorNode()->GetLayoutObject()) { |
| 327 return end; |
| 328 } |
| 329 |
| 330 LayoutObject* last_layout_object = nullptr; |
| 331 for (Node* runner = ComputeNodeBeforePosition(end); runner; |
| 332 runner = Previous(*runner)) { |
| 333 if ((last_layout_object = runner->GetLayoutObject())) |
| 334 break; |
| 335 } |
| 336 if (!last_layout_object) |
| 337 return {}; |
| 338 |
| 339 for (LayoutObject* runner = last_layout_object; runner; |
| 340 runner = Previous(*runner)) { |
| 341 if (!IsSelectionAtomicAndVisible(runner)) |
| 342 continue; |
| 343 if (Node* node = runner->GetNode()) { |
| 344 if (node->IsTextNode()) { |
| 345 return {runner, (int)ToText(runner->GetNode())->data().length()}; |
| 346 } |
| 347 } |
| 348 |
| 349 return {runner, 1}; |
| 350 } |
| 351 return PositionInFlatTree(); |
| 352 } |
| 353 |
| 354 static SelectionPosition ComputeStartRespectingGranularity( |
| 355 const PositionInFlatTree passed_start, |
| 356 TextGranularity granularity) { |
| 357 DCHECK(passed_start.IsNotNull()); |
| 358 |
| 359 switch (granularity) { |
| 360 case kCharacterGranularity: |
| 361 // Don't do any expansion. |
| 362 return FirstLayoutPosition(passed_start); |
| 363 case kWordGranularity: { |
| 364 // General case: Select the word the caret is positioned inside of. |
| 365 // If the caret is on the word boundary, select the word according to |
| 366 // |wordSide|. |
| 367 // Edge case: If the caret is after the last word in a soft-wrapped line |
| 368 // or the last word in the document, select that last word |
| 369 // (LeftWordIfOnBoundary). |
| 370 // Edge case: If the caret is after the last word in a paragraph, select |
| 371 // from the the end of the last word to the line break (also |
| 372 // RightWordIfOnBoundary); |
| 373 const VisiblePositionInFlatTree& visible_start = |
| 374 CreateVisiblePosition(passed_start); |
| 375 if (IsEndOfEditableOrNonEditableContent(visible_start) || |
| 376 (IsEndOfLine(visible_start) && !IsStartOfLine(visible_start) && |
| 377 !IsEndOfParagraph(visible_start))) { |
| 378 return StartOfWord(visible_start, kLeftWordIfOnBoundary) |
| 379 .DeepEquivalent(); |
| 380 } |
| 381 return StartOfWord(visible_start, kRightWordIfOnBoundary) |
| 382 .DeepEquivalent(); |
| 383 } |
| 384 case kLineGranularity: |
| 385 return StartOfLine(CreateVisiblePosition(passed_start)).DeepEquivalent(); |
| 386 case kParagraphGranularity: { |
| 387 const VisiblePositionInFlatTree pos = CreateVisiblePosition(passed_start); |
| 388 if (IsStartOfLine(pos) && IsEndOfEditableOrNonEditableContent(pos)) |
| 389 return StartOfParagraph(PreviousPositionOf(pos)).DeepEquivalent(); |
| 390 return StartOfParagraph(pos).DeepEquivalent(); |
| 391 } |
| 392 default: |
| 393 break; |
| 394 } |
| 395 |
| 396 NOTREACHED(); |
| 397 return PositionInFlatTree(); |
| 398 } |
| 399 |
| 400 static SelectionPosition ComputeEndRespectingGranularity( |
| 401 const PositionInFlatTree& start, |
| 402 const PositionInFlatTree& passed_end, |
| 403 TextGranularity granularity) { |
| 404 DCHECK(passed_end.IsNotNull()); |
| 405 |
| 406 switch (granularity) { |
| 407 case kCharacterGranularity: |
| 408 // Don't do any expansion. |
| 409 return LastLayoutPosition(passed_end); |
| 410 case kWordGranularity: { |
| 411 // General case: Select the word the caret is positioned inside of. |
| 412 // If the caret is on the word boundary, select the word according to |
| 413 // |wordSide|. |
| 414 // Edge case: If the caret is after the last word in a soft-wrapped line |
| 415 // or the last word in the document, select that last word |
| 416 // (|LeftWordIfOnBoundary|). |
| 417 // Edge case: If the caret is after the last word in a paragraph, select |
| 418 // from the the end of the last word to the line break (also |
| 419 // |RightWordIfOnBoundary|); |
| 420 const VisiblePositionInFlatTree& original_end = |
| 421 CreateVisiblePosition(passed_end); |
| 422 EWordSide side = kRightWordIfOnBoundary; |
| 423 if (IsEndOfEditableOrNonEditableContent(original_end) || |
| 424 (IsEndOfLine(original_end) && !IsStartOfLine(original_end) && |
| 425 !IsEndOfParagraph(original_end))) |
| 426 side = kLeftWordIfOnBoundary; |
| 427 |
| 428 const VisiblePositionInFlatTree& word_end = EndOfWord(original_end, side); |
| 429 if (!IsEndOfParagraph(original_end)) |
| 430 return word_end.DeepEquivalent(); |
| 431 if (IsEmptyTableCell(start.AnchorNode())) |
| 432 return word_end.DeepEquivalent(); |
| 433 |
| 434 // Select the paragraph break (the space from the end of a paragraph |
| 435 // to the start of the next one) to match TextEdit. |
| 436 const VisiblePositionInFlatTree& end = NextPositionOf(word_end); |
| 437 Element* const table = TableElementJustBefore(end); |
| 438 if (!table) { |
| 439 if (end.IsNull()) |
| 440 return word_end.DeepEquivalent(); |
| 441 return end.DeepEquivalent(); |
| 442 } |
| 443 |
| 444 if (!IsEnclosingBlock(table)) |
| 445 return word_end.DeepEquivalent(); |
| 446 |
| 447 // The paragraph break after the last paragraph in the last cell |
| 448 // of a block table ends at the start of the paragraph after the |
| 449 // table. |
| 450 const VisiblePositionInFlatTree next = |
| 451 NextPositionOf(end, kCannotCrossEditingBoundary); |
| 452 if (next.IsNull()) |
| 453 return word_end.DeepEquivalent(); |
| 454 return next.DeepEquivalent(); |
| 455 } |
| 456 case kLineGranularity: { |
| 457 const VisiblePositionInFlatTree& end = |
| 458 EndOfLine(CreateVisiblePosition(passed_end)); |
| 459 if (!IsEndOfParagraph(end)) |
| 460 return end.DeepEquivalent(); |
| 461 // If the end of this line is at the end of a paragraph, include the |
| 462 // space after the end of the line in the selection. |
| 463 const VisiblePositionInFlatTree& next = NextPositionOf(end); |
| 464 if (next.IsNull()) |
| 465 return end.DeepEquivalent(); |
| 466 return next.DeepEquivalent(); |
| 467 } |
| 468 case kParagraphGranularity: { |
| 469 const VisiblePositionInFlatTree& visible_paragraph_end = |
| 470 EndOfParagraph(CreateVisiblePosition(passed_end)); |
| 471 |
| 472 // Include the "paragraph break" (the space from the end of this |
| 473 // paragraph to the start of the next one) in the selection. |
| 474 const VisiblePositionInFlatTree& end = |
| 475 NextPositionOf(visible_paragraph_end); |
| 476 |
| 477 Element* const table = TableElementJustBefore(end); |
| 478 if (!table) { |
| 479 if (end.IsNull()) |
| 480 return visible_paragraph_end.DeepEquivalent(); |
| 481 return end.DeepEquivalent(); |
| 482 } |
| 483 |
| 484 if (!IsEnclosingBlock(table)) { |
| 485 // There is no paragraph break after the last paragraph in the |
| 486 // last cell of an inline table. |
| 487 return visible_paragraph_end.DeepEquivalent(); |
| 488 } |
| 489 |
| 490 // The paragraph break after the last paragraph in the last cell of |
| 491 // a block table ends at the start of the paragraph after the table, |
| 492 // not at the position just after the table. |
| 493 const VisiblePositionInFlatTree& next = |
| 494 NextPositionOf(end, kCannotCrossEditingBoundary); |
| 495 if (next.IsNull()) |
| 496 return visible_paragraph_end.DeepEquivalent(); |
| 497 return next.DeepEquivalent(); |
| 498 } |
| 499 default: |
| 500 break; |
| 501 } |
| 502 NOTREACHED(); |
| 503 return PositionInFlatTree(); |
| 504 } |
| 505 |
| 112 static bool ShouldShowBlockCursor(const FrameSelection& frame_selection, | 506 static bool ShouldShowBlockCursor(const FrameSelection& frame_selection, |
| 113 const VisibleSelectionInFlatTree& selection) { | 507 const SelectionPosition& start, |
| 508 const SelectionPosition& end) { |
| 114 if (!frame_selection.ShouldShowBlockCursor()) | 509 if (!frame_selection.ShouldShowBlockCursor()) |
| 115 return false; | 510 return false; |
| 116 if (selection.GetSelectionType() != SelectionType::kCaretSelection) | 511 if (start != end) |
| 117 return false; | 512 return false; |
| 118 if (IsLogicalEndOfLine(selection.VisibleEnd())) | 513 if (IsLogicalEndOfLine(CreateVisiblePosition(start.ToPositionInFlatTree()))) |
| 119 return false; | 514 return false; |
| 120 return true; | 515 return true; |
| 121 } | 516 } |
| 122 | 517 |
| 123 static VisibleSelectionInFlatTree CalcSelection( | 518 #define MYDEBUG |
| 519 |
| 520 #ifdef MYDEBUG |
| 521 static VisibleSelectionInFlatTree createFromDOM( |
| 522 const SelectionInDOMTree& selection_) { |
| 523 SelectionInFlatTree::Builder builder; |
| 524 const PositionInFlatTree& base = ToPositionInFlatTree(selection_.Base()); |
| 525 const PositionInFlatTree& extent = ToPositionInFlatTree(selection_.Extent()); |
| 526 if (base.IsNotNull() && extent.IsNotNull()) |
| 527 builder.SetBaseAndExtent(base, extent); |
| 528 else if (base.IsNotNull()) |
| 529 builder.Collapse(base); |
| 530 else if (extent.IsNotNull()) |
| 531 builder.Collapse(extent); |
| 532 builder.SetAffinity(selection_.Affinity()) |
| 533 .SetHasTrailingWhitespace(selection_.HasTrailingWhitespace()) |
| 534 .SetGranularity(selection_.Granularity()) |
| 535 .SetIsDirectional(selection_.IsDirectional()); |
| 536 return CreateVisibleSelection(builder.Build()); |
| 537 } |
| 538 #endif |
| 539 |
| 540 static std::pair<SelectionPosition, SelectionPosition> CalcSelection( |
| 124 const FrameSelection& frame_selection) { | 541 const FrameSelection& frame_selection) { |
| 542 const SelectionInDOMTree& selection_in_dom = |
| 543 frame_selection.GetSelectionInDOMTree(); |
| 544 #ifdef MYDEBUG |
| 125 const VisibleSelectionInFlatTree& original_selection = | 545 const VisibleSelectionInFlatTree& original_selection = |
| 126 frame_selection.ComputeVisibleSelectionInFlatTree(); | 546 createFromDOM(selection_in_dom); |
| 127 | 547 const SelectionPosition original_start = original_selection.Start(); |
| 128 if (!ShouldShowBlockCursor(frame_selection, original_selection)) | 548 const SelectionPosition original_end = original_selection.End(); |
| 129 return original_selection; | 549 DCHECK(original_start.IsNull() || original_end.IsNull() || |
| 130 | 550 original_selection.IsNone() || true); |
| 131 const PositionInFlatTree end_position = NextPositionOf( | 551 #endif |
| 132 original_selection.Start(), PositionMoveType::kGraphemeCluster); | 552 |
| 133 return CreateVisibleSelection( | 553 const PositionInFlatTree& base = |
| 134 SelectionInFlatTree::Builder() | 554 ToPositionInFlatTree(selection_in_dom.Base()); |
| 135 .SetBaseAndExtent(original_selection.Start(), end_position) | 555 const PositionInFlatTree& extent = |
| 136 .Build()); | 556 ToPositionInFlatTree(selection_in_dom.Extent()); |
| 557 const bool is_base_first = base <= extent; |
| 558 const PositionInFlatTree& start = is_base_first ? base : extent; |
| 559 const PositionInFlatTree& end = is_base_first ? extent : base; |
| 560 DCHECK_LE(start, end); |
| 561 if (start.IsNull() || end.IsNull()) |
| 562 return {PositionInFlatTree(), PositionInFlatTree()}; |
| 563 |
| 564 SelectionPosition start_mod_granularity = start; |
| 565 SelectionPosition end_mod_granularity = end; |
| 566 if (selection_in_dom.Granularity() != kCharacterGranularity || start != end) { |
| 567 start_mod_granularity = ComputeStartRespectingGranularity( |
| 568 start, selection_in_dom.Granularity()); |
| 569 end_mod_granularity = ComputeEndRespectingGranularity( |
| 570 start, end, selection_in_dom.Granularity()); |
| 571 } |
| 572 // #define USE_ORIGINAL |
| 573 #ifdef USE_ORIGINAL |
| 574 start_mod_granularity = original_start; |
| 575 end_mod_granularity = original_end; |
| 576 #else |
| 577 if (start_mod_granularity.IsNull() || end_mod_granularity.IsNull()) |
| 578 return {PositionInFlatTree(), PositionInFlatTree()}; |
| 579 Optional<int> comp = compare(start_mod_granularity.layout_object_, |
| 580 end_mod_granularity.layout_object_); |
| 581 DCHECK(comp.has_value()); |
| 582 if (comp.value() > 0) |
| 583 end_mod_granularity = start_mod_granularity; |
| 584 #endif |
| 585 // DCHECK_LE(start_mod_granularity, end_mod_granularity); |
| 586 /*const PositionInFlatTree& start_most_forward = |
| 587 MostForwardCaretPosition(start_mod_granularity); const PositionInFlatTree& |
| 588 end_most_backward = MostBackwardCaretPosition(end_mod_granularity);*/ |
| 589 if (!ShouldShowBlockCursor(frame_selection, start_mod_granularity, |
| 590 end_mod_granularity)) |
| 591 return {start_mod_granularity, end_mod_granularity}; |
| 592 |
| 593 const PositionInFlatTree end_position = |
| 594 NextPositionOf(start, PositionMoveType::kGraphemeCluster); |
| 595 return {start, end_position}; |
| 137 } | 596 } |
| 138 | 597 |
| 139 // Objects each have a single selection rect to examine. | 598 // Objects each have a single selection rect to examine. |
| 140 using SelectedObjectMap = HashMap<LayoutObject*, SelectionState>; | 599 using SelectedObjectMap = HashMap<LayoutObject*, SelectionState>; |
| 141 // Blocks contain selected objects and fill gaps between them, either on the | 600 // Blocks contain selected objects and fill gaps between them, either on the |
| 142 // left, right, or in between lines and blocks. | 601 // left, right, or in between lines and blocks. |
| 143 // In order to get the visual rect right, we have to examine left, middle, and | 602 // In order to get the visual rect right, we have to examine left, middle, and |
| 144 // right rects individually, since otherwise the union of those rects might | 603 // right rects individually, since otherwise the union of those rects might |
| 145 // remain the same even when changes have occurred. | 604 // remain the same even when changes have occurred. |
| 146 using SelectedBlockMap = HashMap<LayoutBlock*, SelectionState>; | 605 using SelectedBlockMap = HashMap<LayoutBlock*, SelectionState>; |
| (...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 290 paint_range_ = SelectionPaintRange(); | 749 paint_range_ = SelectionPaintRange(); |
| 291 } | 750 } |
| 292 | 751 |
| 293 static SelectionPaintRange CalcSelectionPaintRange( | 752 static SelectionPaintRange CalcSelectionPaintRange( |
| 294 const FrameSelection& frame_selection) { | 753 const FrameSelection& frame_selection) { |
| 295 const SelectionInDOMTree& selection_in_dom = | 754 const SelectionInDOMTree& selection_in_dom = |
| 296 frame_selection.GetSelectionInDOMTree(); | 755 frame_selection.GetSelectionInDOMTree(); |
| 297 if (selection_in_dom.IsNone()) | 756 if (selection_in_dom.IsNone()) |
| 298 return SelectionPaintRange(); | 757 return SelectionPaintRange(); |
| 299 | 758 |
| 300 const VisibleSelectionInFlatTree& selection = CalcSelection(frame_selection); | 759 const std::pair<SelectionPosition, SelectionPosition>& selection = |
| 301 if (!selection.IsRange() || frame_selection.IsHidden()) | 760 CalcSelection(frame_selection); |
| 761 const SelectionPosition& start_pos = selection.first; |
| 762 const SelectionPosition& end_pos = selection.second; |
| 763 if (start_pos == end_pos || frame_selection.IsHidden()) |
| 302 return SelectionPaintRange(); | 764 return SelectionPaintRange(); |
| 303 | 765 |
| 304 DCHECK(!selection.IsNone()); | 766 // DCHECK_LE(start_pos, end_pos); |
| 305 const PositionInFlatTree start_pos = selection.Start(); | 767 LayoutObject* start_layout_object = start_pos.layout_object_; |
| 306 const PositionInFlatTree end_pos = selection.End(); | 768 LayoutObject* end_layout_object = end_pos.layout_object_; |
| 307 DCHECK_LE(start_pos, end_pos); | |
| 308 LayoutObject* start_layout_object = start_pos.AnchorNode()->GetLayoutObject(); | |
| 309 LayoutObject* end_layout_object = end_pos.AnchorNode()->GetLayoutObject(); | |
| 310 DCHECK(start_layout_object); | 769 DCHECK(start_layout_object); |
| 311 DCHECK(end_layout_object); | 770 DCHECK(end_layout_object); |
| 312 DCHECK(start_layout_object->View() == end_layout_object->View()); | 771 DCHECK(start_layout_object->View() == end_layout_object->View()); |
| 313 | 772 |
| 314 return SelectionPaintRange(start_layout_object, | 773 return SelectionPaintRange(start_layout_object, start_pos.offset_, |
| 315 start_pos.ComputeEditingOffset(), | 774 end_layout_object, end_pos.offset_); |
| 316 end_layout_object, end_pos.ComputeEditingOffset()); | |
| 317 } | 775 } |
| 318 | 776 |
| 319 void LayoutSelection::Commit() { | 777 void LayoutSelection::Commit() { |
| 320 if (!HasPendingSelection()) | 778 if (!HasPendingSelection()) |
| 321 return; | 779 return; |
| 322 has_pending_selection_ = false; | 780 has_pending_selection_ = false; |
| 323 | 781 |
| 324 const SelectionPaintRange& new_range = | 782 const SelectionPaintRange& new_range = |
| 325 CalcSelectionPaintRange(*frame_selection_); | 783 CalcSelectionPaintRange(*frame_selection_); |
| 326 if (new_range.IsNull()) { | 784 if (new_range.IsNull()) { |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 378 | 836 |
| 379 runner->SetShouldInvalidateSelection(); | 837 runner->SetShouldInvalidateSelection(); |
| 380 } | 838 } |
| 381 } | 839 } |
| 382 | 840 |
| 383 DEFINE_TRACE(LayoutSelection) { | 841 DEFINE_TRACE(LayoutSelection) { |
| 384 visitor->Trace(frame_selection_); | 842 visitor->Trace(frame_selection_); |
| 385 } | 843 } |
| 386 | 844 |
| 387 } // namespace blink | 845 } // namespace blink |
| OLD | NEW |