nsFrameSelection.h (54376B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef nsFrameSelection_h___ 8 #define nsFrameSelection_h___ 9 10 #include <stdint.h> 11 12 #include "WordMovementType.h" 13 #include "mozilla/Assertions.h" 14 #include "mozilla/Attributes.h" 15 #include "mozilla/CaretAssociationHint.h" 16 #include "mozilla/CompactPair.h" 17 #include "mozilla/EnumSet.h" 18 #include "mozilla/EventForwards.h" 19 #include "mozilla/Result.h" 20 #include "mozilla/TextRange.h" 21 #include "mozilla/UniquePtr.h" 22 #include "mozilla/dom/Element.h" 23 #include "mozilla/dom/Highlight.h" 24 #include "mozilla/dom/Selection.h" 25 #include "mozilla/intl/BidiEmbeddingLevel.h" 26 #include "nsBidiPresUtils.h" 27 #include "nsIContent.h" 28 #include "nsIFrame.h" 29 #include "nsISelectionController.h" 30 #include "nsISelectionListener.h" 31 #include "nsITableCellLayout.h" 32 33 class nsRange; 34 35 #define BIDI_LEVEL_UNDEFINED mozilla::intl::BidiEmbeddingLevel(0x80) 36 37 //---------------------------------------------------------------------- 38 39 // Selection interface 40 41 struct SelectionDetails { 42 SelectionDetails() 43 : mStart(), mEnd(), mSelectionType(mozilla::SelectionType::eInvalid) { 44 MOZ_COUNT_CTOR(SelectionDetails); 45 } 46 ~SelectionDetails() { 47 MOZ_COUNT_DTOR(SelectionDetails); 48 // Destroy the linked list without recursion. 49 auto next = std::move(mNext); 50 while (next) { 51 next = std::move(next->mNext); 52 } 53 } 54 55 int32_t mStart; 56 int32_t mEnd; 57 mozilla::SelectionType mSelectionType; 58 mozilla::dom::HighlightSelectionData mHighlightData; 59 mozilla::TextRangeStyle mTextRangeStyle; 60 mozilla::UniquePtr<SelectionDetails> mNext; 61 }; 62 63 struct SelectionCustomColors { 64 #ifdef NS_BUILD_REFCNT_LOGGING 65 MOZ_COUNTED_DEFAULT_CTOR(SelectionCustomColors) 66 MOZ_COUNTED_DTOR(SelectionCustomColors) 67 #endif 68 mozilla::Maybe<nscolor> mForegroundColor; 69 mozilla::Maybe<nscolor> mBackgroundColor; 70 mozilla::Maybe<nscolor> mAltForegroundColor; 71 mozilla::Maybe<nscolor> mAltBackgroundColor; 72 }; 73 74 namespace mozilla { 75 class PresShell; 76 77 /** PeekOffsetStruct is used to group various arguments (both input and output) 78 * that are passed to nsIFrame::PeekOffset(). See below for the description of 79 * individual arguments. 80 */ 81 enum class PeekOffsetOption : uint16_t { 82 // Whether to allow jumping across line boundaries. 83 // 84 // Used with: eSelectCharacter, eSelectWord. 85 JumpLines, 86 87 // Whether we should preserve or trim spaces at begin/end of content 88 PreserveSpaces, 89 90 // Whether to stop when reaching a scroll view boundary. 91 // 92 // Used with: eSelectCharacter, eSelectWord, eSelectLine. 93 StopAtScroller, 94 95 // Whether to stop when reaching a placeholder frame. 96 StopAtPlaceholder, 97 98 // Whether the peeking is done in response to a keyboard action. 99 // 100 // Used with: eSelectWord. 101 IsKeyboardSelect, 102 103 // Whether bidi caret behavior is visual (set) or logical (unset). 104 // 105 // Used with: eSelectCharacter, eSelectWord, eSelectBeginLine, eSelectEndLine. 106 Visual, 107 108 // Whether the selection is being extended or moved. 109 Extend, 110 111 // If true, the offset has to end up in an editable node, otherwise we'll keep 112 // searching. 113 ForceEditableRegion, 114 }; 115 116 using PeekOffsetOptions = EnumSet<PeekOffsetOption>; 117 118 struct MOZ_STACK_CLASS PeekOffsetStruct { 119 PeekOffsetStruct(nsSelectionAmount aAmount, nsDirection aDirection, 120 int32_t aStartOffset, nsPoint aDesiredCaretPos, 121 // Passing by value here is intentional because EnumSet 122 // is optimized as uint*_t in opt builds. 123 const PeekOffsetOptions aOptions, 124 EWordMovementType aWordMovementType = eDefaultBehavior, 125 const dom::Element* aAncestorLimiter = nullptr); 126 127 /** 128 * Return true if the ancestor limiter is not specified or if the content for 129 * aFrame is an inclusive descendant of mAncestorLimiter. 130 */ 131 [[nodiscard]] bool FrameContentIsInAncestorLimiter( 132 const nsIFrame* aFrame) const { 133 return !mAncestorLimiter || 134 (aFrame->GetContent() && 135 aFrame->GetContent()->IsInclusiveDescendantOf(mAncestorLimiter)); 136 } 137 138 // Note: Most arguments (input and output) are only used with certain values 139 // of mAmount. These values are indicated for each argument below. 140 // Arguments with no such indication are used with all values of mAmount. 141 142 /*** Input arguments ***/ 143 // Note: The value of some of the input arguments may be changed upon exit. 144 145 // The type of movement requested (by character, word, line, etc.) 146 nsSelectionAmount mAmount; 147 148 // eDirPrevious or eDirNext. 149 // 150 // Note for visual bidi movement: 151 // * eDirPrevious means 'left-then-up' if the containing block is LTR, 152 // 'right-then-up' if it is RTL. 153 // * eDirNext means 'right-then-down' if the containing block is LTR, 154 // 'left-then-down' if it is RTL. 155 // * Between paragraphs, eDirPrevious means "go to the visual end of 156 // the previous paragraph", and eDirNext means "go to the visual 157 // beginning of the next paragraph". 158 // 159 // Used with: eSelectCharacter, eSelectWord, eSelectLine, eSelectParagraph. 160 const nsDirection mDirection; 161 162 // Offset into the content of the current frame where the peek starts. 163 // 164 // Used with: eSelectCharacter, eSelectWord 165 int32_t mStartOffset; 166 167 // The desired inline coordinate for the caret (one of .x or .y will be used, 168 // depending on line's writing mode) 169 // 170 // Used with: eSelectLine. 171 const nsPoint mDesiredCaretPos; 172 173 // An enum that determines whether to prefer the start or end of a word or to 174 // use the default behavior, which is a combination of direction and the 175 // platform-based pref "layout.word_select.eat_space_to_next_word" 176 EWordMovementType mWordMovementType; 177 178 PeekOffsetOptions mOptions; 179 180 // The ancestor limiter element to peek offset. 181 const dom::Element* const mAncestorLimiter; 182 183 /*** Output arguments ***/ 184 185 // Content reached as a result of the peek. 186 nsCOMPtr<nsIContent> mResultContent; 187 188 // Frame reached as a result of the peek. 189 // 190 // Used with: eSelectCharacter, eSelectWord. 191 nsIFrame* mResultFrame; 192 193 // Offset into content reached as a result of the peek. 194 int32_t mContentOffset; 195 196 // When the result position is between two frames, indicates which of the two 197 // frames the caret should be painted in. false means "the end of the frame 198 // logically before the caret", true means "the beginning of the frame 199 // logically after the caret". 200 // 201 // Used with: eSelectLine, eSelectBeginLine, eSelectEndLine. 202 CaretAssociationHint mAttach; 203 }; 204 205 struct LimitersAndCaretData; 206 207 } // namespace mozilla 208 209 struct nsPrevNextBidiLevels { 210 void SetData(nsIFrame* aFrameBefore, nsIFrame* aFrameAfter, 211 mozilla::intl::BidiEmbeddingLevel aLevelBefore, 212 mozilla::intl::BidiEmbeddingLevel aLevelAfter) { 213 mFrameBefore = aFrameBefore; 214 mFrameAfter = aFrameAfter; 215 mLevelBefore = aLevelBefore; 216 mLevelAfter = aLevelAfter; 217 } 218 nsIFrame* mFrameBefore; 219 nsIFrame* mFrameAfter; 220 mozilla::intl::BidiEmbeddingLevel mLevelBefore; 221 mozilla::intl::BidiEmbeddingLevel mLevelAfter; 222 }; 223 224 namespace mozilla { 225 class SelectionChangeEventDispatcher; 226 namespace dom { 227 class Highlight; 228 class Selection; 229 enum class ClickSelectionType { NotApplicable, Double, Triple }; 230 } // namespace dom 231 232 /** 233 * Constants for places that want to handle table selections. These 234 * indicate what part of a table is being selected. 235 */ 236 enum class TableSelectionMode : uint32_t { 237 None, /* Nothing being selected; not valid in all cases. */ 238 Cell, /* A cell is being selected. */ 239 Row, /* A row is being selected. */ 240 Column, /* A column is being selected. */ 241 Table, /* A table (including cells and captions) is being selected. */ 242 AllCells, /* All the cells in a table are being selected. */ 243 }; 244 245 } // namespace mozilla 246 247 class nsFrameSelection final { 248 public: 249 friend std::ostream& operator<<(std::ostream&, const nsFrameSelection&); 250 251 using CaretAssociationHint = mozilla::CaretAssociationHint; 252 using Element = mozilla::dom::Element; 253 254 /*interfaces for addref and release and queryinterface*/ 255 256 NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(nsFrameSelection) 257 NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(nsFrameSelection) 258 259 enum class FocusMode { 260 kExtendSelection, /** Keep old anchor point. */ 261 kCollapseToNewPoint, /** Collapses the Selection to the new point. */ 262 kMultiRangeSelection, /** Keeps existing non-collapsed ranges and marks them 263 as generated. */ 264 }; 265 266 /** 267 * HandleClick will take the focus to the new frame at the new offset and 268 * will either extend the selection from the old anchor, or replace the old 269 * anchor. the old anchor and focus position may also be used to deselect 270 * things 271 * 272 * @param aNewfocus is the content that wants the focus 273 * 274 * @param aContentOffset is the content offset of the parent aNewFocus 275 * 276 * @param aContentOffsetEnd is the content offset of the parent aNewFocus and 277 * is specified different when you need to select to and include both start 278 * and end points 279 * 280 * @param aHint will tell the selection which direction geometrically to 281 * actually show the caret on. 1 = end of this line 0 = beginning of this line 282 */ 283 MOZ_CAN_RUN_SCRIPT nsresult HandleClick(nsIContent* aNewFocus, 284 uint32_t aContentOffset, 285 uint32_t aContentEndOffset, 286 FocusMode aFocusMode, 287 CaretAssociationHint aHint); 288 289 public: 290 /** 291 * Sets the type of the selection based on whether a selection is created 292 * by doubleclick, long tapping a word or tripleclick. 293 * 294 * @param aClickSelectionType ClickSelectionType::Double if the selection 295 * is created by doubleclick, 296 * ClickSelectionType::Triple if the selection 297 * is created by tripleclick. 298 */ 299 void SetClickSelectionType( 300 mozilla::dom::ClickSelectionType aClickSelectionType) { 301 mClickSelectionType = aClickSelectionType; 302 } 303 304 /** 305 * Return true if this is an instance for an independent selection. 306 * Currently, independent selection is created only in the text controls 307 * to manage selections in their native anonymous subtree. 308 */ 309 [[nodiscard]] bool IsIndependentSelection() const { 310 return !!GetIndependentSelectionRootElement(); 311 } 312 313 /** 314 * Returns true if the selection was created by doubleclick or 315 * long tap over a word. 316 */ 317 [[nodiscard]] bool IsDoubleClickSelection() const { 318 return mClickSelectionType == mozilla::dom::ClickSelectionType::Double; 319 } 320 321 /** 322 * Returns true if the selection was created by triple click 323 */ 324 [[nodiscard]] bool IsTripleClickSelection() const { 325 return mClickSelectionType == mozilla::dom::ClickSelectionType::Triple; 326 } 327 328 /** 329 * HandleDrag extends the selection to contain the frame closest to aPoint. 330 * 331 * @param aPresContext is the context to use when figuring out what frame 332 * contains the point. 333 * 334 * @param aFrame is the parent of all frames to use when searching for the 335 * closest frame to the point. 336 * 337 * @param aPoint is relative to aFrame 338 */ 339 MOZ_CAN_RUN_SCRIPT void HandleDrag(nsIFrame* aFrame, const nsPoint& aPoint); 340 341 /** 342 * HandleTableSelection will set selection to a table, cell, etc 343 * depending on information contained in aFlags 344 * 345 * @param aParentContent is the paretent of either a table or cell that user 346 * clicked or dragged the mouse in 347 * 348 * @param aContentOffset is the offset of the table or cell 349 * 350 * @param aTarget indicates what to select 351 * * TableSelectionMode::Cell 352 * We should select a cell (content points to the cell) 353 * * TableSelectionMode::Row 354 * We should select a row (content points to any cell in row) 355 * * TableSelectionMode::Column 356 * We should select a row (content points to any cell in column) 357 * * TableSelectionMode::Table 358 * We should select a table (content points to the table) 359 * * TableSelectionMode::AllCells 360 * We should select all cells (content points to any cell in table) 361 * 362 * @param aMouseEvent passed in so we can get where event occurred 363 * and what keys are pressed 364 */ 365 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 366 HandleTableSelection(nsINode* aParentContent, int32_t aContentOffset, 367 mozilla::TableSelectionMode aTarget, 368 mozilla::WidgetMouseEvent* aMouseEvent); 369 370 /** 371 * Add cell to the selection with `SelectionType::eNormal`. 372 * 373 * @param aCell [in] HTML td element. 374 */ 375 nsresult SelectCellElement(nsIContent* aCell); 376 377 public: 378 /** 379 * Remove cells from selection inside of the given cell range. 380 * 381 * @param aTable [in] HTML table element 382 * @param aStartRowIndex [in] row index where the cells range starts 383 * @param aStartColumnIndex [in] column index where the cells range starts 384 * @param aEndRowIndex [in] row index where the cells range ends 385 * @param aEndColumnIndex [in] column index where the cells range ends 386 */ 387 MOZ_CAN_RUN_SCRIPT nsresult RemoveCellsFromSelection( 388 nsIContent* aTable, int32_t aStartRowIndex, int32_t aStartColumnIndex, 389 int32_t aEndRowIndex, int32_t aEndColumnIndex); 390 391 /** 392 * Remove cells from selection outside of the given cell range. 393 * 394 * @param aTable [in] HTML table element 395 * @param aStartRowIndex [in] row index where the cells range starts 396 * @param aStartColumnIndex [in] column index where the cells range starts 397 * @param aEndRowIndex [in] row index where the cells range ends 398 * @param aEndColumnIndex [in] column index where the cells range ends 399 */ 400 MOZ_CAN_RUN_SCRIPT nsresult RestrictCellsToSelection( 401 nsIContent* aTable, int32_t aStartRowIndex, int32_t aStartColumnIndex, 402 int32_t aEndRowIndex, int32_t aEndColumnIndex); 403 404 /** 405 * StartAutoScrollTimer is responsible for scrolling frames so that 406 * aPoint is always visible, and for selecting any frame that contains 407 * aPoint. The timer will also reset itself to fire again if we have 408 * not scrolled to the end of the document. 409 * 410 * @param aFrame is the outermost frame to use when searching for 411 * the closest frame for the point, i.e. the frame that is capturing 412 * the mouse 413 * 414 * @param aPoint is relative to aFrame. 415 * 416 * @param aDelay is the timer's interval. 417 */ 418 MOZ_CAN_RUN_SCRIPT nsresult StartAutoScrollTimer(nsIFrame* aFrame, 419 const nsPoint& aPoint, 420 uint32_t aDelay); 421 422 /** 423 * Stops any active auto scroll timer. 424 */ 425 void StopAutoScrollTimer(); 426 427 /** 428 * Returns in frame coordinates the selection beginning and ending with the 429 * type of selection given 430 * 431 * @param aContent is the content asking 432 * @param aContentOffset is the starting content boundary 433 * @param aContentLength is the length of the content piece asking 434 * @param aIgnoreSelection is Yes, this won't return selection details about 435 * the normal selection. 436 */ 437 enum class IgnoreNormalSelection : bool { No, Yes }; 438 mozilla::UniquePtr<SelectionDetails> LookUpSelection( 439 nsIContent* aContent, int32_t aContentOffset, int32_t aContentLength, 440 IgnoreNormalSelection aIgnoreNormalSelection) const; 441 442 /** 443 * Sets the drag state to aState for resons of drag state. 444 * 445 * @param aState is the new state of drag 446 */ 447 MOZ_CAN_RUN_SCRIPT void SetDragState(bool aState); 448 449 /** 450 * Gets the drag state to aState for resons of drag state. 451 * 452 * @param aState will hold the state of drag 453 */ 454 [[nodiscard]] bool GetDragState() const { return mDragState; } 455 456 /** 457 * If we are in table cell selection mode. aka ctrl click in table cell 458 */ 459 [[nodiscard]] bool IsInTableSelectionMode() const { 460 return mTableSelection.mMode != mozilla::TableSelectionMode::None; 461 } 462 void ClearTableCellSelection() { 463 mTableSelection.mMode = mozilla::TableSelectionMode::None; 464 } 465 466 /** 467 * No query interface for selection. must use this method now. 468 * 469 * @param aSelectionType The selection type what you want. 470 */ 471 [[nodiscard]] mozilla::dom::Selection* GetSelection( 472 mozilla::SelectionType aSelectionType) const; 473 474 /** 475 * Convenience method to access the `eNormal` Selection. 476 */ 477 [[nodiscard]] mozilla::dom::Selection& NormalSelection() const { 478 return *GetSelection(mozilla::SelectionType::eNormal); 479 } 480 481 /** 482 * Returns the number of highlight selections. 483 */ 484 [[nodiscard]] size_t HighlightSelectionCount() const { 485 return mHighlightSelections.Length(); 486 } 487 488 /** 489 * Get a highlight selection by index. The index must be valid. 490 */ 491 [[nodiscard]] RefPtr<mozilla::dom::Selection> HighlightSelection( 492 size_t aIndex) const { 493 return mHighlightSelections[aIndex].second(); 494 } 495 496 /** 497 * @brief Adds a highlight selection for `aHighlight`. 498 */ 499 MOZ_CAN_RUN_SCRIPT void AddHighlightSelection( 500 nsAtom* aHighlightName, mozilla::dom::Highlight& aHighlight); 501 502 void RepaintHighlightSelection(nsAtom* aHighlightName); 503 504 /** 505 * @brief Removes the Highlight selection identified by `aHighlightName`. 506 */ 507 MOZ_CAN_RUN_SCRIPT void RemoveHighlightSelection(nsAtom* aHighlightName); 508 509 /** 510 * @brief Adds a new range to the highlight selection. 511 * 512 * If there is no highlight selection for the given highlight yet, it is 513 * created using |AddHighlightSelection|. 514 */ 515 MOZ_CAN_RUN_SCRIPT void AddHighlightSelectionRange( 516 nsAtom* aHighlightName, mozilla::dom::Highlight& aHighlight, 517 mozilla::dom::AbstractRange& aRange); 518 519 /** 520 * @brief Removes a range from a highlight selection. 521 */ 522 MOZ_CAN_RUN_SCRIPT void RemoveHighlightSelectionRange( 523 nsAtom* aHighlightName, mozilla::dom::AbstractRange& aRange); 524 /** 525 * ScrollSelectionIntoView scrolls a region of the selection, 526 * so that it is visible in the scrolled view. 527 * 528 * @param aSelectionType the selection to scroll into view. 529 * 530 * @param aRegion the region inside the selection to scroll into view. 531 * 532 * @param aFlags the scroll flags. Valid bits include: 533 * * SCROLL_SYNCHRONOUS: when set, scrolls the selection into view 534 * before returning. If not set, posts a request which is processed 535 * at some point after the method returns. 536 * * SCROLL_FIRST_ANCESTOR_ONLY: if set, only the first ancestor will be 537 * scrolled into view. 538 */ 539 MOZ_CAN_RUN_SCRIPT nsresult 540 ScrollSelectionIntoView(mozilla::SelectionType aSelectionType, 541 SelectionRegion aRegion, int16_t aFlags) const; 542 543 /** 544 * RepaintSelection repaints the selected frames that are inside the 545 * selection specified by aSelectionType. 546 * 547 * @param aSelectionType The selection type what you want to repaint. 548 */ 549 nsresult RepaintSelection(mozilla::SelectionType aSelectionType); 550 551 /** 552 * Return true if aContainerNode is in the selection limiter or the ancestor 553 * limiter if one of them is set. 554 * 555 * Note that this returns true when aContainerNode may be in the scope of 556 * an independent selection. Therefore, even if this returns `true`, 557 * aContainerNode may not be valid container node for a selection managed 558 * by this instance. 559 */ 560 [[nodiscard]] bool NodeIsInLimiters(const nsINode* aContainerNode) const; 561 562 [[nodiscard]] static bool NodeIsInLimiters( 563 const nsINode* aContainerNode, 564 const Element* aIndependentSelectionLimiterElement, 565 const Element* aSelectionAncestorLimiter); 566 567 /** 568 * GetFrameToPageSelect() returns a frame which is ancestor limit of 569 * per-page selection. The frame may not be scrollable. E.g., 570 * when selection ancestor limit is set to a frame of an editing host of 571 * contenteditable element and it's not scrollable. 572 */ 573 [[nodiscard]] nsIFrame* GetFrameToPageSelect() const; 574 575 /** 576 * This method moves caret (if aExtend is false) or expands selection (if 577 * aExtend is true). Then, scrolls aFrame one page. Finally, this may 578 * call ScrollSelectionIntoView() for making focus of selection visible 579 * but depending on aSelectionIntoView value. 580 * 581 * @param aForward if true, scroll forward if not scroll backward 582 * @param aExtend if true, extend selection to the new point 583 * @param aFrame the frame to scroll or container of per-page selection. 584 * if aExtend is true and selection may have ancestor limit, 585 * should set result of GetFrameToPageSelect(). 586 * @param aSelectionIntoView 587 * If IfChanged, this makes selection into view only when 588 * selection is modified by the call. 589 * If Yes, this makes selection into view always. 590 */ 591 enum class SelectionIntoView { IfChanged, Yes }; 592 MOZ_CAN_RUN_SCRIPT nsresult PageMove(bool aForward, bool aExtend, 593 nsIFrame* aFrame, 594 SelectionIntoView aSelectionIntoView); 595 596 void SetHint(CaretAssociationHint aHintRight) { mCaret.mHint = aHintRight; } 597 [[nodiscard]] CaretAssociationHint GetHint() const { return mCaret.mHint; } 598 599 void SetCaretBidiLevelAndMaybeSchedulePaint( 600 mozilla::intl::BidiEmbeddingLevel aLevel); 601 602 /** 603 * GetCaretBidiLevel gets the caret bidi level. 604 */ 605 [[nodiscard]] mozilla::intl::BidiEmbeddingLevel GetCaretBidiLevel() const; 606 607 /** 608 * UndefineCaretBidiLevel sets the caret bidi level to "undefined". 609 */ 610 void UndefineCaretBidiLevel(); 611 612 /** 613 * PhysicalMove will generally be called from the nsiselectioncontroller 614 * implementations. the effect being the selection will move one unit 615 * 'aAmount' in the given aDirection. 616 * @param aDirection the direction to move the selection 617 * @param aAmount amount of movement (char/line; word/page; eol/doc) 618 * @param aExtend continue selection 619 */ 620 MOZ_CAN_RUN_SCRIPT nsresult PhysicalMove(int16_t aDirection, int16_t aAmount, 621 bool aExtend); 622 623 /** 624 * CharacterMove will generally be called from the nsiselectioncontroller 625 * implementations. the effect being the selection will move one character 626 * left or right. 627 * @param aForward move forward in document. 628 * @param aExtend continue selection 629 */ 630 MOZ_CAN_RUN_SCRIPT nsresult CharacterMove(bool aForward, bool aExtend); 631 632 /** 633 * WordMove will generally be called from the nsiselectioncontroller 634 * implementations. the effect being the selection will move one word left or 635 * right. 636 * @param aForward move forward in document. 637 * @param aExtend continue selection 638 */ 639 MOZ_CAN_RUN_SCRIPT nsresult WordMove(bool aForward, bool aExtend); 640 641 /** 642 * LineMove will generally be called from the nsiselectioncontroller 643 * implementations. the effect being the selection will move one line up or 644 * down. 645 * @param aForward move forward in document. 646 * @param aExtend continue selection 647 */ 648 MOZ_CAN_RUN_SCRIPT nsresult LineMove(bool aForward, bool aExtend); 649 650 /** 651 * IntraLineMove will generally be called from the nsiselectioncontroller 652 * implementations. the effect being the selection will move to beginning or 653 * end of line 654 * @param aForward move forward in document. 655 * @param aExtend continue selection 656 */ 657 MOZ_CAN_RUN_SCRIPT nsresult IntraLineMove(bool aForward, bool aExtend); 658 659 /** 660 * CreateRangeExtendedToNextGraphemeClusterBoundary() returns range which is 661 * extended from normal selection range to start of next grapheme cluster 662 * boundary. 663 * 664 * @param aLimitersAndCaretData The data of limiters and additional 665 * caret data. 666 * @param aRange The range which you want to extend. 667 * @param aRangeDirection eDirNext if the start boundary of 668 * aRange is focus. Otherwise, i.e., if 669 * the start boundary is anchor, 670 * eDirPrevious. 671 */ 672 template <typename RangeType> 673 MOZ_CAN_RUN_SCRIPT static mozilla::Result<RefPtr<RangeType>, nsresult> 674 CreateRangeExtendedToNextGraphemeClusterBoundary( 675 mozilla::PresShell& aPresShell, 676 const mozilla::LimitersAndCaretData& aLimitersAndCaretData, 677 const mozilla::dom::AbstractRange& aRange, nsDirection aRangeDirection) { 678 return CreateRangeExtendedToSomewhere<RangeType>( 679 aPresShell, aLimitersAndCaretData, aRange, aRangeDirection, eDirNext, 680 eSelectCluster, eLogical); 681 } 682 683 /** 684 * CreateRangeExtendedToPreviousCharacterBoundary() returns range which is 685 * extended from normal selection range to start of previous character 686 * boundary. 687 * 688 * @param aLimitersAndCaretData The data of limiters and additional 689 * caret data. 690 * @param aRange The range which you want to extend. 691 * @param aRangeDirection eDirNext if the start boundary of 692 * aRange is focus. Otherwise, i.e., if 693 * the start boundary is anchor, 694 * eDirPrevious. 695 */ 696 template <typename RangeType> 697 MOZ_CAN_RUN_SCRIPT static mozilla::Result<RefPtr<RangeType>, nsresult> 698 CreateRangeExtendedToPreviousCharacterBoundary( 699 mozilla::PresShell& aPresShell, 700 const mozilla::LimitersAndCaretData& aLimitersAndCaretData, 701 const mozilla::dom::AbstractRange& aRange, nsDirection aRangeDirection) { 702 return CreateRangeExtendedToSomewhere<RangeType>( 703 aPresShell, aLimitersAndCaretData, aRange, aRangeDirection, 704 eDirPrevious, eSelectCharacter, eLogical); 705 } 706 707 /** 708 * CreateRangeExtendedToNextWordBoundary() returns range which is 709 * extended from normal selection range to start of next word boundary. 710 * 711 * @param aLimitersAndCaretData The data of limiters and additional 712 * caret data. 713 * @param aRange The range which you want to extend. 714 * @param aRangeDirection eDirNext if the start boundary of 715 * aRange is focus. Otherwise, i.e., if 716 * the start boundary is anchor, 717 * eDirPrevious. 718 */ 719 template <typename RangeType> 720 MOZ_CAN_RUN_SCRIPT static mozilla::Result<RefPtr<RangeType>, nsresult> 721 CreateRangeExtendedToNextWordBoundary( 722 mozilla::PresShell& aPresShell, 723 const mozilla::LimitersAndCaretData& aLimitersAndCaretData, 724 const mozilla::dom::AbstractRange& aRange, nsDirection aRangeDirection) { 725 return CreateRangeExtendedToSomewhere<RangeType>( 726 aPresShell, aLimitersAndCaretData, aRange, aRangeDirection, eDirNext, 727 eSelectWord, eLogical); 728 } 729 730 /** 731 * CreateRangeExtendedToPreviousWordBoundary() returns range which is 732 * extended from normal selection range to start of previous word boundary. 733 * 734 * @param aLimitersAndCaretData The data of limiters and additional 735 * caret data. 736 * @param aRange The range which you want to extend. 737 * @param aRangeDirection eDirNext if the start boundary of 738 * aRange is focus. Otherwise, i.e., if 739 * the start boundary is anchor, 740 * eDirPrevious. 741 */ 742 template <typename RangeType> 743 MOZ_CAN_RUN_SCRIPT static mozilla::Result<RefPtr<RangeType>, nsresult> 744 CreateRangeExtendedToPreviousWordBoundary( 745 mozilla::PresShell& aPresShell, 746 const mozilla::LimitersAndCaretData& aLimitersAndCaretData, 747 const mozilla::dom::AbstractRange& aRange, nsDirection aRangeDirection) { 748 return CreateRangeExtendedToSomewhere<RangeType>( 749 aPresShell, aLimitersAndCaretData, aRange, aRangeDirection, 750 eDirPrevious, eSelectWord, eLogical); 751 } 752 753 /** 754 * CreateRangeExtendedToPreviousHardLineBreak() returns range which is 755 * extended from normal selection range to previous hard line break. 756 * 757 * @param aLimitersAndCaretData The data of limiters and additional 758 * caret data. 759 * @param aRange The range which you want to extend. 760 * @param aRangeDirection eDirNext if the start boundary of 761 * aRange is focus. Otherwise, i.e., if 762 * the start boundary is anchor, 763 * eDirPrevious. 764 */ 765 template <typename RangeType> 766 MOZ_CAN_RUN_SCRIPT static mozilla::Result<RefPtr<RangeType>, nsresult> 767 CreateRangeExtendedToPreviousHardLineBreak( 768 mozilla::PresShell& aPresShell, 769 const mozilla::LimitersAndCaretData& aLimitersAndCaretData, 770 const mozilla::dom::AbstractRange& aRange, nsDirection aRangeDirection) { 771 return CreateRangeExtendedToSomewhere<RangeType>( 772 aPresShell, aLimitersAndCaretData, aRange, aRangeDirection, 773 eDirPrevious, eSelectBeginLine, eLogical); 774 } 775 776 /** 777 * CreateRangeExtendedToNextHardLineBreak() returns range which is extended 778 * from normal selection range to next hard line break. 779 * 780 * @param aLimitersAndCaretData The data of limiters and additional 781 * caret data. 782 * @param aRange The range which you want to extend. 783 * @param aRangeDirection eDirNext if the start boundary of 784 * aRange is focus. Otherwise, i.e., if 785 * the start boundary is anchor, 786 * eDirPrevious. 787 */ 788 template <typename RangeType> 789 MOZ_CAN_RUN_SCRIPT static mozilla::Result<RefPtr<RangeType>, nsresult> 790 CreateRangeExtendedToNextHardLineBreak( 791 mozilla::PresShell& aPresShell, 792 const mozilla::LimitersAndCaretData& aLimitersAndCaretData, 793 const mozilla::dom::AbstractRange& aRange, nsDirection aRangeDirection) { 794 return CreateRangeExtendedToSomewhere<RangeType>( 795 aPresShell, aLimitersAndCaretData, aRange, aRangeDirection, eDirNext, 796 eSelectEndLine, eLogical); 797 } 798 799 /** Sets/Gets The display selection enum. 800 */ 801 void SetDisplaySelection(int16_t aState) { mDisplaySelection = aState; } 802 [[nodiscard]] int16_t GetDisplaySelection() const { 803 return mDisplaySelection; 804 } 805 806 /** 807 * This method can be used to store the data received during a MouseDown 808 * event so that we can place the caret during the MouseUp event. 809 * 810 * @param aMouseEvent the event received by the selection MouseDown 811 * handling method. A nullptr value can be use to tell this method 812 * that any data is storing is no longer valid. 813 */ 814 void SetDelayedCaretData(mozilla::WidgetMouseEvent* aMouseEvent); 815 816 /** 817 * Get the delayed MouseDown event data necessary to place the 818 * caret during MouseUp processing. 819 * 820 * @return a pointer to the event received 821 * by the selection during MouseDown processing. It can be nullptr 822 * if the data is no longer valid. 823 */ 824 [[nodiscard]] bool HasDelayedCaretData() const { 825 return mDelayedMouseEvent.mIsValid; 826 } 827 [[nodiscard]] bool IsShiftDownInDelayedCaretData() const { 828 NS_ASSERTION(mDelayedMouseEvent.mIsValid, "No valid delayed caret data"); 829 return mDelayedMouseEvent.mIsShift; 830 } 831 [[nodiscard]] uint32_t GetClickCountInDelayedCaretData() const { 832 NS_ASSERTION(mDelayedMouseEvent.mIsValid, "No valid delayed caret data"); 833 return mDelayedMouseEvent.mClickCount; 834 } 835 836 [[nodiscard]] bool MouseDownRecorded() const { 837 return !GetDragState() && HasDelayedCaretData() && 838 GetClickCountInDelayedCaretData() < 2; 839 } 840 841 /** 842 * Returns the selection root element if and only if the instance is for an 843 * independent selection. Currently, this is a native anonymous `<div>` for 844 * a text control. 845 */ 846 [[nodiscard]] Element* GetIndependentSelectionRootElement() const { 847 return mLimiters.mIndependentSelectionRootElement; 848 } 849 850 /** 851 * Get the independent selection root parent which is usually a text control 852 * element which hosts the anonymous subtree managed by this frame selection. 853 */ 854 [[nodiscard]] Element* GetIndependentSelectionRootParentElement() const { 855 MOZ_DIAGNOSTIC_ASSERT(IsIndependentSelection()); 856 return Element::FromNodeOrNull( 857 mLimiters.mIndependentSelectionRootElement 858 ->GetClosestNativeAnonymousSubtreeRootParentOrHost()); 859 } 860 861 /** 862 * GetAncestorLimiter() returns the root of current selection ranges. This is 863 * typically the focused editing host unless it's the root element of the 864 * document. 865 */ 866 [[nodiscard]] Element* GetAncestorLimiter() const { 867 return mLimiters.mAncestorLimiter; 868 } 869 870 [[nodiscard]] Element* GetAncestorLimiterOrIndependentSelectionRootElement() 871 const { 872 return mLimiters.mAncestorLimiter 873 ? mLimiters.mAncestorLimiter 874 : mLimiters.mIndependentSelectionRootElement; 875 } 876 877 /** 878 * Set ancestor limiter. If aLimiter is not nullptr, this adjusts all 879 * selection ranges into the limiter element. Thus, calling this may run 880 * the selection listeners. 881 */ 882 MOZ_CAN_RUN_SCRIPT void SetAncestorLimiter(Element* aLimiter); 883 884 /** 885 * GetPrevNextBidiLevels will return the frames and associated Bidi levels of 886 * the characters logically before and after a (collapsed) selection. 887 * 888 * @param aNode is the node containing the selection 889 * @param aContentOffset is the offset of the selection in the node 890 * @param aJumpLines 891 * If true, look across line boundaries. 892 * If false, behave as if there were base-level frames at line edges. 893 * 894 * @return A struct holding the before/after frame and the before/after 895 * level. 896 * 897 * At the beginning and end of each line there is assumed to be a frame with 898 * Bidi level equal to the paragraph embedding level. 899 * 900 * In these cases the before frame and after frame respectively will be 901 * nullptr. 902 */ 903 [[nodiscard]] nsPrevNextBidiLevels GetPrevNextBidiLevels( 904 nsIContent* aNode, uint32_t aContentOffset, bool aJumpLines) const; 905 906 /** 907 * MaintainSelection will track the normal selection as being "sticky". 908 * Dragging or extending selection will never allow for a subset 909 * (or the whole) of the maintained selection to become unselected. 910 * Primary use: double click selecting then dragging on second click 911 * 912 * @param aAmount the initial amount of text selected (word, line or 913 * paragraph). For "line", use eSelectBeginLine. 914 */ 915 nsresult MaintainSelection(nsSelectionAmount aAmount = eSelectNoAmount); 916 917 MOZ_CAN_RUN_SCRIPT nsresult ConstrainFrameAndPointToAnchorSubtree( 918 nsIFrame* aFrame, const nsPoint& aPoint, nsIFrame** aRetFrame, 919 nsPoint& aRetPoint) const; 920 921 /** 922 * @param aPresShell is the parameter to be used for most of the other calls 923 * for callbacks etc 924 * 925 * @param aAccessibleCaretEnabled true if we should enable the accessible 926 * caret. 927 * 928 * @param aEditorRootAnonymousDiv if this instance is for an independent 929 * selection for a text control, specify this to the anonymous <div> element 930 * of the text control which contains only an editable Text and/or a <br>. 931 */ 932 nsFrameSelection(mozilla::PresShell* aPresShell, bool aAccessibleCaretEnabled, 933 Element* aEditorRootAnonymousDiv = nullptr); 934 935 /** 936 * @param aRequesterFuncName function name which wants to start the batch. 937 * This won't be stored nor exposed to selection listeners etc, used only for 938 * logging. 939 */ 940 void StartBatchChanges(const char* aRequesterFuncName); 941 942 /** 943 * @param aRequesterFuncName function name which wants to end the batch. 944 * This won't be stored nor exposed to selection listeners etc, used only for 945 * logging. 946 * @param aReasons potentially multiple of the reasons defined in 947 * nsISelectionListener.idl 948 */ 949 MOZ_CAN_RUN_SCRIPT void EndBatchChanges( 950 const char* aRequesterFuncName, 951 int16_t aReasons = nsISelectionListener::NO_REASON); 952 953 [[nodiscard]] mozilla::PresShell* GetPresShell() const { return mPresShell; } 954 955 void DisconnectFromPresShell(); 956 MOZ_CAN_RUN_SCRIPT nsresult ClearNormalSelection(); 957 958 // Table selection support. 959 static nsITableCellLayout* GetCellLayout(const nsIContent* aCellContent); 960 961 private: 962 ~nsFrameSelection(); 963 964 // TODO: in case an error is returned, it sometimes refers to a programming 965 // error, in other cases to runtime errors. This deserves to be cleaned up. 966 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 967 TakeFocus(nsIContent& aNewFocus, uint32_t aContentOffset, 968 uint32_t aContentEndOffset, CaretAssociationHint aHint, 969 FocusMode aFocusMode); 970 971 /** 972 * After moving the caret, its Bidi level is set according to the following 973 * rules: 974 * 975 * After moving over a character with left/right arrow, set to the Bidi level 976 * of the last moved over character. After Home and End, set to the paragraph 977 * embedding level. After up/down arrow, PageUp/Down, set to the lower level 978 * of the 2 surrounding characters. After mouse click, set to the level of the 979 * current frame. 980 * 981 * The following two methods use GetPrevNextBidiLevels to determine the new 982 * Bidi level. BidiLevelFromMove is called when the caret is moved in response 983 * to a keyboard event 984 * 985 * @param aPresShell is the presentation shell 986 * @param aNode is the content node 987 * @param aContentOffset is the new caret position, as an offset into aNode 988 * @param aAmount is the amount of the move that gave the caret its new 989 * position 990 * @param aHint is the hint indicating in what logical direction the caret 991 * moved 992 */ 993 void BidiLevelFromMove(mozilla::PresShell* aPresShell, nsIContent* aNode, 994 uint32_t aContentOffset, nsSelectionAmount aAmount, 995 CaretAssociationHint aHint); 996 /** 997 * BidiLevelFromClick is called when the caret is repositioned by clicking the 998 * mouse 999 * 1000 * @param aNode is the content node 1001 * @param aContentOffset is the new caret position, as an offset into aNode 1002 */ 1003 void BidiLevelFromClick(nsIContent* aNewFocus, uint32_t aContentOffset); 1004 1005 /** 1006 * @param aReasons potentially multiple of the reasons defined in 1007 * nsISelectionListener.idl. 1008 */ 1009 void SetChangeReasons(int16_t aReasons) { 1010 mSelectionChangeReasons = aReasons; 1011 } 1012 1013 /** 1014 * @param aReasons potentially multiple of the reasons defined in 1015 * nsISelectionListener.idl. 1016 */ 1017 void AddChangeReasons(int16_t aReasons) { 1018 mSelectionChangeReasons |= aReasons; 1019 } 1020 1021 /** 1022 * @return potentially multiple of the reasons defined in 1023 * nsISelectionListener.idl. 1024 */ 1025 [[nodiscard]] int16_t PopChangeReasons() { 1026 int16_t retval = mSelectionChangeReasons; 1027 mSelectionChangeReasons = nsISelectionListener::NO_REASON; 1028 return retval; 1029 } 1030 1031 [[nodiscard]] nsSelectionAmount GetCaretMoveAmount() { 1032 return mCaretMoveAmount; 1033 } 1034 1035 [[nodiscard]] bool IsUserSelectionReason() const { 1036 return (mSelectionChangeReasons & 1037 (nsISelectionListener::DRAG_REASON | 1038 nsISelectionListener::MOUSEDOWN_REASON | 1039 nsISelectionListener::MOUSEUP_REASON | 1040 nsISelectionListener::KEYPRESS_REASON)) != 1041 nsISelectionListener::NO_REASON; 1042 } 1043 1044 friend class mozilla::dom::Selection; 1045 friend class mozilla::SelectionChangeEventDispatcher; 1046 friend struct mozilla::AutoPrepareFocusRange; 1047 1048 /*HELPER METHODS*/ 1049 // Whether MoveCaret should use logical or visual movement, 1050 // or follow the bidi.edit.caret_movement_style preference. 1051 enum CaretMovementStyle { eLogical, eVisual, eUsePrefStyle }; 1052 enum class ExtendSelection : bool { No, Yes }; 1053 MOZ_CAN_RUN_SCRIPT nsresult MoveCaret(nsDirection aDirection, 1054 ExtendSelection aExtendSelection, 1055 nsSelectionAmount aAmount, 1056 CaretMovementStyle aMovementStyle); 1057 1058 /** 1059 * @brief Creates `PeekOffsetOptions` for caret move operations. 1060 * 1061 * @param aSelection The selection object. Must be non-null 1062 * @param aExtendSelection Whether the selection should be extended or not 1063 * @param aMovementStyle The `CaretMovementStyle` (logical or visual) 1064 * @return mozilla::Result<mozilla::PeekOffsetOptions, nsresult> 1065 */ 1066 [[nodiscard]] mozilla::Result<mozilla::PeekOffsetOptions, nsresult> 1067 CreatePeekOffsetOptionsForCaretMove(mozilla::dom::Selection* aSelection, 1068 ExtendSelection aExtendSelection, 1069 CaretMovementStyle aMovementStyle) const { 1070 MOZ_ASSERT(aSelection); 1071 return CreatePeekOffsetOptionsForCaretMove( 1072 mLimiters.mIndependentSelectionRootElement, 1073 static_cast<ForceEditableRegion>(aSelection->IsEditorSelection()), 1074 aExtendSelection, aMovementStyle); 1075 } 1076 1077 enum class ForceEditableRegion : bool { No, Yes }; 1078 [[nodiscard]] static mozilla::Result<mozilla::PeekOffsetOptions, nsresult> 1079 CreatePeekOffsetOptionsForCaretMove(const Element* aSelectionLimiter, 1080 ForceEditableRegion aForceEditableRegion, 1081 ExtendSelection aExtendSelection, 1082 CaretMovementStyle aMovementStyle); 1083 1084 /** 1085 * @brief Get the Ancestor Limiter for caret move operation. 1086 * 1087 * If the selection is an editor selection, the correct editing host is 1088 * identified and chosen as limiting element. 1089 * 1090 * @param aSelection The selection object. Must be non-null 1091 * @return The ancestor limiter, or nullptr. 1092 */ 1093 [[nodiscard]] mozilla::Result<Element*, nsresult> 1094 GetAncestorLimiterForCaretMove(mozilla::dom::Selection* aSelection) const; 1095 1096 /** 1097 * CreateRangeExtendedToSomewhere() is common method to implement 1098 * CreateRangeExtendedTo*(). This method creates a range extended from 1099 * aRange. 1100 * 1101 * @param aLimitersAndCaretData The data of limiters and additional 1102 * caret data. 1103 * @param aRange The range which you want to extend. 1104 * @param aRangeDirection eDirNext if the start boundary of 1105 * aRange is focus. Otherwise, i.e., if 1106 * the start boundary is anchor, 1107 * eDirPrevious. 1108 * @param aExtendDirection Whether you want to extend the range 1109 * backward or forward. 1110 * @param aAmount The amount which you want to extend. 1111 * @param aMovementStyle Whether visual or logical. 1112 */ 1113 template <typename RangeType> 1114 MOZ_CAN_RUN_SCRIPT static mozilla::Result<RefPtr<RangeType>, nsresult> 1115 CreateRangeExtendedToSomewhere( 1116 mozilla::PresShell& aPresShell, 1117 const mozilla::LimitersAndCaretData& aLimitersAndCaretData, 1118 const mozilla::dom::AbstractRange& aRange, nsDirection aRangeDirection, 1119 nsDirection aExtendDirection, const nsSelectionAmount aAmount, 1120 CaretMovementStyle aMovementStyle); 1121 1122 void InvalidateDesiredCaretPos(); // do not listen to mDesiredCaretPos.mValue 1123 // you must get another. 1124 1125 [[nodiscard]] bool IsBatching() const { return mBatching.mCounter > 0; } 1126 1127 enum class IsBatchingEnd : bool { No, Yes }; 1128 1129 // nsFrameSelection may get deleted when calling this, 1130 // so remember to use nsCOMPtr when needed. 1131 MOZ_CAN_RUN_SCRIPT nsresult 1132 NotifySelectionListeners(mozilla::SelectionType aSelectionType, 1133 IsBatchingEnd aEndBatching = IsBatchingEnd::No); 1134 1135 static nsresult GetCellIndexes(const nsIContent* aCell, int32_t& aRowIndex, 1136 int32_t& aColIndex); 1137 1138 [[nodiscard]] static nsIContent* GetFirstCellNodeInRange( 1139 const nsRange* aRange); 1140 // Returns non-null table if in same table, null otherwise 1141 [[nodiscard]] static nsIContent* IsInSameTable(const nsIContent* aContent1, 1142 const nsIContent* aContent2); 1143 // Might return null 1144 [[nodiscard]] static nsIContent* GetParentTable(const nsIContent* aCellNode); 1145 1146 ////////////BEGIN nsFrameSelection members 1147 1148 RefPtr<mozilla::dom::Selection> 1149 mDomSelections[sizeof(mozilla::kPresentSelectionTypes) / 1150 sizeof(mozilla::SelectionType)]; 1151 1152 nsTArray< 1153 mozilla::CompactPair<RefPtr<nsAtom>, RefPtr<mozilla::dom::Selection>>> 1154 mHighlightSelections; 1155 1156 struct TableSelection { 1157 // Get our first range, if its first selected node is a cell. If this does 1158 // not return null, then the first node in the returned range is a cell 1159 // (according to GetFirstCellNodeInRange). 1160 nsRange* GetFirstCellRange(const mozilla::dom::Selection& aNormalSelection); 1161 1162 // Get our next range, if its first selected node is a cell. If this does 1163 // not return null, then the first node in the returned range is a cell 1164 // (according to GetFirstCellNodeInRange). 1165 nsRange* GetNextCellRange(const mozilla::dom::Selection& aNormalSelection); 1166 1167 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 1168 HandleSelection(nsINode* aParentContent, int32_t aContentOffset, 1169 mozilla::TableSelectionMode aTarget, 1170 mozilla::WidgetMouseEvent* aMouseEvent, bool aDragState, 1171 mozilla::dom::Selection& aNormalSelection); 1172 1173 /** 1174 * @return the closest inclusive table cell ancestor 1175 * (https://dom.spec.whatwg.org/#concept-tree-inclusive-ancestor) of 1176 * aContent, if it is actively editable. 1177 */ 1178 [[nodiscard]] static nsINode* IsContentInActivelyEditableTableCell( 1179 nsPresContext* aContext, nsIContent* aContent); 1180 1181 // TODO: annotate this with `MOZ_CAN_RUN_SCRIPT` instead. 1182 MOZ_CAN_RUN_SCRIPT nsresult 1183 SelectBlockOfCells(nsIContent* aStartCell, nsIContent* aEndCell, 1184 mozilla::dom::Selection& aNormalSelection); 1185 1186 MOZ_CAN_RUN_SCRIPT nsresult SelectRowOrColumn( 1187 nsIContent* aCellContent, mozilla::dom::Selection& aNormalSelection); 1188 1189 MOZ_CAN_RUN_SCRIPT nsresult 1190 UnselectCells(const nsIContent* aTable, int32_t aStartRowIndex, 1191 int32_t aStartColumnIndex, int32_t aEndRowIndex, 1192 int32_t aEndColumnIndex, bool aRemoveOutsideOfCellRange, 1193 mozilla::dom::Selection& aNormalSelection); 1194 1195 nsCOMPtr<nsINode> 1196 mClosestInclusiveTableCellAncestor; // used to snap to table selection 1197 nsCOMPtr<nsIContent> mStartSelectedCell; 1198 nsCOMPtr<nsIContent> mEndSelectedCell; 1199 nsCOMPtr<nsIContent> mAppendStartSelectedCell; 1200 nsCOMPtr<nsIContent> mUnselectCellOnMouseUp; 1201 mozilla::TableSelectionMode mMode = mozilla::TableSelectionMode::None; 1202 int32_t mSelectedCellIndex = 0; 1203 bool mDragSelectingCells = false; 1204 1205 private: 1206 struct MOZ_STACK_CLASS FirstAndLastCell { 1207 nsCOMPtr<nsIContent> mFirst; 1208 nsCOMPtr<nsIContent> mLast; 1209 }; 1210 1211 [[nodiscard]] mozilla::Result<FirstAndLastCell, nsresult> 1212 FindFirstAndLastCellOfRowOrColumn(const nsIContent& aCellContent) const; 1213 1214 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult HandleDragSelecting( 1215 mozilla::TableSelectionMode aTarget, nsIContent* aChildContent, 1216 const mozilla::WidgetMouseEvent* aMouseEvent, 1217 mozilla::dom::Selection& aNormalSelection); 1218 1219 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult HandleMouseUpOrDown( 1220 mozilla::TableSelectionMode aTarget, bool aDragState, 1221 nsIContent* aChildContent, nsINode* aParentContent, 1222 int32_t aContentOffset, const mozilla::WidgetMouseEvent* aMouseEvent, 1223 mozilla::dom::Selection& aNormalSelection); 1224 1225 class MOZ_STACK_CLASS RowAndColumnRelation; 1226 }; 1227 1228 TableSelection mTableSelection; 1229 1230 struct MaintainedRange { 1231 /** 1232 * Ensure anchor and focus of aNormalSelection are ordered appropriately 1233 * relative to the maintained range. 1234 */ 1235 MOZ_CAN_RUN_SCRIPT void AdjustNormalSelection( 1236 const nsIContent* aContent, int32_t aOffset, 1237 mozilla::dom::Selection& aNormalSelection) const; 1238 1239 /** 1240 * @param aStopAtScroller If yes, this will 1241 * set `PeekOffsetOption::StopAtScroller`. 1242 */ 1243 enum class StopAtScroller : bool { No, Yes }; 1244 void AdjustContentOffsets(nsIFrame::ContentOffsets& aOffsets, 1245 StopAtScroller aStopAtScroller) const; 1246 1247 void MaintainAnchorFocusRange( 1248 const mozilla::dom::Selection& aNormalSelection, 1249 nsSelectionAmount aAmount); 1250 1251 RefPtr<nsRange> mRange; 1252 nsSelectionAmount mAmount = eSelectNoAmount; 1253 }; 1254 1255 MaintainedRange mMaintainedRange; 1256 1257 struct Batching { 1258 uint32_t mCounter = 0; 1259 }; 1260 1261 Batching mBatching; 1262 1263 struct Limiters { 1264 // The independent selection root element if and only if the 1265 // nsFrameSelection instance is for an independent selection. 1266 RefPtr<Element> mIndependentSelectionRootElement; 1267 // Limit selection navigation to a descendant of this element. 1268 // This is typically the focused editing host if set unless it's the root 1269 // element of the document. 1270 RefPtr<Element> mAncestorLimiter; 1271 }; 1272 1273 Limiters mLimiters; 1274 1275 mozilla::PresShell* mPresShell = nullptr; 1276 // Reasons for notifications of selection changing. 1277 // Can be multiple of the reasons defined in nsISelectionListener.idl. 1278 int16_t mSelectionChangeReasons = nsISelectionListener::NO_REASON; 1279 // For visual display purposes. 1280 int16_t mDisplaySelection = nsISelectionController::SELECTION_OFF; 1281 nsSelectionAmount mCaretMoveAmount = eSelectNoAmount; 1282 1283 struct Caret { 1284 // Hint to tell if the selection is at the end of this line or beginning of 1285 // next. 1286 CaretAssociationHint mHint = CaretAssociationHint::Before; 1287 mozilla::intl::BidiEmbeddingLevel mBidiLevel = BIDI_LEVEL_UNDEFINED; 1288 1289 [[nodiscard]] static bool IsVisualMovement( 1290 ExtendSelection aExtendSelection, CaretMovementStyle aMovementStyle); 1291 }; 1292 1293 Caret mCaret; 1294 1295 mozilla::intl::BidiEmbeddingLevel mKbdBidiLevel = 1296 mozilla::intl::BidiEmbeddingLevel::LTR(); 1297 1298 class DesiredCaretPos { 1299 public: 1300 // the position requested by the Key Handling for up down 1301 nsresult FetchPos(nsPoint& aDesiredCaretPos, 1302 const mozilla::PresShell& aPresShell, 1303 mozilla::dom::Selection& aNormalSelection) const; 1304 1305 void Set(const nsPoint& aPos); 1306 1307 void Invalidate(); 1308 1309 bool mIsSet = false; 1310 1311 private: 1312 nsPoint mValue; 1313 }; 1314 1315 DesiredCaretPos mDesiredCaretPos; 1316 1317 struct DelayedMouseEvent { 1318 bool mIsValid = false; 1319 // These values are not used since they are only valid when mIsValid is 1320 // true, and setting mIsValid always overrides these values. 1321 bool mIsShift = false; 1322 uint32_t mClickCount = 0; 1323 }; 1324 1325 DelayedMouseEvent mDelayedMouseEvent; 1326 1327 bool mDragState = false; // for drag purposes 1328 bool mAccessibleCaretEnabled = false; 1329 1330 // Records if a selection was created by doubleclicking or tripleclicking 1331 // a word. This information is needed later on to determine if a leading 1332 // or trailing whitespace needs to be removed as well to achieve 1333 // native behaviour on macOS. 1334 mozilla::dom::ClickSelectionType mClickSelectionType = 1335 mozilla::dom::ClickSelectionType::NotApplicable; 1336 }; 1337 1338 /** 1339 * Selection Batcher class that supports multiple FrameSelections. 1340 */ 1341 class MOZ_RAII AutoFrameSelectionBatcher final { 1342 public: 1343 MOZ_CAN_RUN_SCRIPT explicit AutoFrameSelectionBatcher( 1344 const char* aFunctionName, size_t aEstimatedSize = 1) 1345 : mFunctionName(aFunctionName) { 1346 mFrameSelections.SetCapacity(aEstimatedSize); 1347 } 1348 MOZ_CAN_RUN_SCRIPT ~AutoFrameSelectionBatcher() { 1349 for (const auto& frameSelection : mFrameSelections) { 1350 MOZ_KnownLive(frameSelection)->EndBatchChanges(mFunctionName); 1351 } 1352 } 1353 void AddFrameSelection(nsFrameSelection* aFrameSelection) { 1354 if (!aFrameSelection) { 1355 return; 1356 } 1357 aFrameSelection->StartBatchChanges(mFunctionName); 1358 mFrameSelections.AppendElement(aFrameSelection); 1359 } 1360 1361 private: 1362 const char* mFunctionName; 1363 AutoTArray<RefPtr<nsFrameSelection>, 1> mFrameSelections; 1364 }; 1365 1366 namespace mozilla { 1367 /** 1368 * A struct for sharing nsFrameSelection outside of its instance. 1369 */ 1370 struct LimitersAndCaretData { 1371 using Element = dom::Element; 1372 1373 LimitersAndCaretData() = default; 1374 explicit LimitersAndCaretData(const nsFrameSelection& aFrameSelection) 1375 : mIndependentSelectionRootElement( 1376 aFrameSelection.GetIndependentSelectionRootElement()), 1377 mAncestorLimiter(aFrameSelection.GetAncestorLimiter()), 1378 mCaretAssociationHint(aFrameSelection.GetHint()), 1379 mCaretBidiLevel(aFrameSelection.GetCaretBidiLevel()) {} 1380 1381 [[nodiscard]] bool NodeIsInLimiters(const nsINode* aContainerNode) const { 1382 return nsFrameSelection::NodeIsInLimiters( 1383 aContainerNode, mIndependentSelectionRootElement, mAncestorLimiter); 1384 } 1385 [[nodiscard]] bool RangeInLimiters(const dom::AbstractRange& aRange) const { 1386 return NodeIsInLimiters(aRange.GetStartContainer()) && 1387 (!aRange.IsPositionedAndSameContainer() || 1388 NodeIsInLimiters(aRange.GetEndContainer())); 1389 } 1390 1391 // nsFrameSelection::GetIndependentSelectionRootElement 1392 RefPtr<Element> mIndependentSelectionRootElement; 1393 // nsFrameSelection::GetAncestorLimiter 1394 RefPtr<Element> mAncestorLimiter; 1395 // nsFrameSelection::GetHint 1396 CaretAssociationHint mCaretAssociationHint = CaretAssociationHint::Before; 1397 // nsFrameSelection::GetCaretBidiLevel 1398 intl::BidiEmbeddingLevel mCaretBidiLevel; 1399 }; 1400 1401 } // namespace mozilla 1402 1403 #endif /* nsFrameSelection_h___ */