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 "mozilla/Assertions.h" 11 #include "mozilla/Attributes.h" 12 #include "mozilla/EventForwards.h" 13 #include "mozilla/dom/Selection.h" 14 #include "mozilla/Result.h" 15 #include "mozilla/TextRange.h" 16 #include "mozilla/UniquePtr.h" 17 #include "nsIFrame.h" 18 #include "nsIContent.h" 19 #include "nsISelectionController.h" 20 #include "nsISelectionListener.h" 21 #include "nsITableCellLayout.h" 22 #include "WordMovementType.h" 23 #include "CaretAssociationHint.h" 24 #include "nsBidiPresUtils.h" 25 26 class nsRange; 27 28 #define BIDI_LEVEL_UNDEFINED 0x80 29 30 //---------------------------------------------------------------------- 31 32 // Selection interface 33 34 struct SelectionDetails { SelectionDetailsSelectionDetails35 SelectionDetails() 36 : mStart(), mEnd(), mSelectionType(mozilla::SelectionType::eInvalid) { 37 MOZ_COUNT_CTOR(SelectionDetails); 38 } 39 MOZ_COUNTED_DTOR(SelectionDetails) 40 41 int32_t mStart; 42 int32_t mEnd; 43 mozilla::SelectionType mSelectionType; 44 mozilla::TextRangeStyle mTextRangeStyle; 45 mozilla::UniquePtr<SelectionDetails> mNext; 46 }; 47 48 struct SelectionCustomColors { 49 #ifdef NS_BUILD_REFCNT_LOGGING 50 MOZ_COUNTED_DEFAULT_CTOR(SelectionCustomColors) 51 MOZ_COUNTED_DTOR(SelectionCustomColors) 52 #endif 53 mozilla::Maybe<nscolor> mForegroundColor; 54 mozilla::Maybe<nscolor> mBackgroundColor; 55 mozilla::Maybe<nscolor> mAltForegroundColor; 56 mozilla::Maybe<nscolor> mAltBackgroundColor; 57 }; 58 59 namespace mozilla { 60 class PresShell; 61 } // namespace mozilla 62 63 /** PeekOffsetStruct is used to group various arguments (both input and output) 64 * that are passed to nsFrame::PeekOffset(). See below for the description of 65 * individual arguments. 66 */ 67 struct MOZ_STACK_CLASS nsPeekOffsetStruct { 68 enum class ForceEditableRegion { 69 No, 70 Yes, 71 }; 72 73 nsPeekOffsetStruct( 74 nsSelectionAmount aAmount, nsDirection aDirection, int32_t aStartOffset, 75 nsPoint aDesiredCaretPos, bool aJumpLines, bool aScrollViewStop, 76 bool aIsKeyboardSelect, bool aVisual, bool aExtend, 77 ForceEditableRegion = ForceEditableRegion::No, 78 mozilla::EWordMovementType aWordMovementType = mozilla::eDefaultBehavior, 79 bool aTrimSpaces = true); 80 81 // Note: Most arguments (input and output) are only used with certain values 82 // of mAmount. These values are indicated for each argument below. 83 // Arguments with no such indication are used with all values of mAmount. 84 85 /*** Input arguments ***/ 86 // Note: The value of some of the input arguments may be changed upon exit. 87 88 // The type of movement requested (by character, word, line, etc.) 89 nsSelectionAmount mAmount; 90 91 // eDirPrevious or eDirNext. 92 // 93 // Note for visual bidi movement: 94 // * eDirPrevious means 'left-then-up' if the containing block is LTR, 95 // 'right-then-up' if it is RTL. 96 // * eDirNext means 'right-then-down' if the containing block is LTR, 97 // 'left-then-down' if it is RTL. 98 // * Between paragraphs, eDirPrevious means "go to the visual end of 99 // the previous paragraph", and eDirNext means "go to the visual 100 // beginning of the next paragraph". 101 // 102 // Used with: eSelectCharacter, eSelectWord, eSelectLine, eSelectParagraph. 103 const nsDirection mDirection; 104 105 // Offset into the content of the current frame where the peek starts. 106 // 107 // Used with: eSelectCharacter, eSelectWord 108 int32_t mStartOffset; 109 110 // The desired inline coordinate for the caret (one of .x or .y will be used, 111 // depending on line's writing mode) 112 // 113 // Used with: eSelectLine. 114 const nsPoint mDesiredCaretPos; 115 116 // An enum that determines whether to prefer the start or end of a word or to 117 // use the default beahvior, which is a combination of direction and the 118 // platform-based pref "layout.word_select.eat_space_to_next_word" 119 mozilla::EWordMovementType mWordMovementType; 120 121 // Whether to allow jumping across line boundaries. 122 // 123 // Used with: eSelectCharacter, eSelectWord. 124 const bool mJumpLines; 125 126 // mTrimSpaces: Whether we should trim spaces at begin/end of content 127 const bool mTrimSpaces; 128 129 // Whether to stop when reaching a scroll view boundary. 130 // 131 // Used with: eSelectCharacter, eSelectWord, eSelectLine. 132 const bool mScrollViewStop; 133 134 // Whether the peeking is done in response to a keyboard action. 135 // 136 // Used with: eSelectWord. 137 const bool mIsKeyboardSelect; 138 139 // Whether bidi caret behavior is visual (true) or logical (false). 140 // 141 // Used with: eSelectCharacter, eSelectWord, eSelectBeginLine, eSelectEndLine. 142 const bool mVisual; 143 144 // Whether the selection is being extended or moved. 145 const bool mExtend; 146 147 // If true, the offset has to end up in an editable node, otherwise we'll keep 148 // searching. 149 const bool mForceEditableRegion; 150 151 /*** Output arguments ***/ 152 153 // Content reached as a result of the peek. 154 nsCOMPtr<nsIContent> mResultContent; 155 156 // Frame reached as a result of the peek. 157 // 158 // Used with: eSelectCharacter, eSelectWord. 159 nsIFrame* mResultFrame; 160 161 // Offset into content reached as a result of the peek. 162 int32_t mContentOffset; 163 164 // When the result position is between two frames, indicates which of the two 165 // frames the caret should be painted in. false means "the end of the frame 166 // logically before the caret", true means "the beginning of the frame 167 // logically after the caret". 168 // 169 // Used with: eSelectLine, eSelectBeginLine, eSelectEndLine. 170 mozilla::CaretAssociationHint mAttach; 171 }; 172 173 struct nsPrevNextBidiLevels { SetDatansPrevNextBidiLevels174 void SetData(nsIFrame* aFrameBefore, nsIFrame* aFrameAfter, 175 nsBidiLevel aLevelBefore, nsBidiLevel aLevelAfter) { 176 mFrameBefore = aFrameBefore; 177 mFrameAfter = aFrameAfter; 178 mLevelBefore = aLevelBefore; 179 mLevelAfter = aLevelAfter; 180 } 181 nsIFrame* mFrameBefore; 182 nsIFrame* mFrameAfter; 183 nsBidiLevel mLevelBefore; 184 nsBidiLevel mLevelAfter; 185 }; 186 187 namespace mozilla { 188 class SelectionChangeEventDispatcher; 189 namespace dom { 190 class Selection; 191 } // namespace dom 192 193 /** 194 * Constants for places that want to handle table selections. These 195 * indicate what part of a table is being selected. 196 */ 197 enum class TableSelectionMode : uint32_t { 198 None, /* Nothing being selected; not valid in all cases. */ 199 Cell, /* A cell is being selected. */ 200 Row, /* A row is being selected. */ 201 Column, /* A column is being selected. */ 202 Table, /* A table (including cells and captions) is being selected. */ 203 AllCells, /* All the cells in a table are being selected. */ 204 }; 205 206 } // namespace mozilla 207 class nsIScrollableFrame; 208 209 class nsFrameSelection final { 210 public: 211 typedef mozilla::CaretAssociationHint CaretAssociateHint; 212 213 /*interfaces for addref and release and queryinterface*/ 214 215 NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(nsFrameSelection) 216 NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(nsFrameSelection) 217 218 enum class FocusMode { 219 kExtendSelection, /** Keep old anchor point. */ 220 kCollapseToNewPoint, /** Collapses the Selection to the new point. */ 221 kMultiRangeSelection, /** Keeps existing non-collapsed ranges and marks them 222 as generated. */ 223 }; 224 225 /** 226 * HandleClick will take the focus to the new frame at the new offset and 227 * will either extend the selection from the old anchor, or replace the old 228 * anchor. the old anchor and focus position may also be used to deselect 229 * things 230 * 231 * @param aNewfocus is the content that wants the focus 232 * 233 * @param aContentOffset is the content offset of the parent aNewFocus 234 * 235 * @param aContentOffsetEnd is the content offset of the parent aNewFocus and 236 * is specified different when you need to select to and include both start 237 * and end points 238 * 239 * @param aHint will tell the selection which direction geometrically to 240 * actually show the caret on. 1 = end of this line 0 = beginning of this line 241 */ 242 MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult HandleClick(nsIContent* aNewFocus, 243 uint32_t aContentOffset, 244 uint32_t aContentEndOffset, 245 FocusMode aFocusMode, 246 CaretAssociateHint aHint); 247 248 /** 249 * HandleDrag extends the selection to contain the frame closest to aPoint. 250 * 251 * @param aPresContext is the context to use when figuring out what frame 252 * contains the point. 253 * 254 * @param aFrame is the parent of all frames to use when searching for the 255 * closest frame to the point. 256 * 257 * @param aPoint is relative to aFrame 258 */ 259 // TODO: replace with `MOZ_CAN_RUN_SCRIPT`. 260 MOZ_CAN_RUN_SCRIPT_BOUNDARY void HandleDrag(nsIFrame* aFrame, 261 const nsPoint& aPoint); 262 263 /** 264 * HandleTableSelection will set selection to a table, cell, etc 265 * depending on information contained in aFlags 266 * 267 * @param aParentContent is the paretent of either a table or cell that user 268 * clicked or dragged the mouse in 269 * 270 * @param aContentOffset is the offset of the table or cell 271 * 272 * @param aTarget indicates what to select 273 * * TableSelectionMode::Cell 274 * We should select a cell (content points to the cell) 275 * * TableSelectionMode::Row 276 * We should select a row (content points to any cell in row) 277 * * TableSelectionMode::Column 278 * We should select a row (content points to any cell in column) 279 * * TableSelectionMode::Table 280 * We should select a table (content points to the table) 281 * * TableSelectionMode::AllCells 282 * We should select all cells (content points to any cell in table) 283 * 284 * @param aMouseEvent passed in so we can get where event occurred 285 * and what keys are pressed 286 */ 287 // TODO: replace with `MOZ_CAN_RUN_SCRIPT`. 288 [[nodiscard]] MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult 289 HandleTableSelection(nsINode* aParentContent, int32_t aContentOffset, 290 mozilla::TableSelectionMode aTarget, 291 mozilla::WidgetMouseEvent* aMouseEvent); 292 293 /** 294 * Add cell to the selection with `SelectionType::eNormal`. 295 * 296 * @param aCell [in] HTML td element. 297 */ 298 nsresult SelectCellElement(nsIContent* aCell); 299 300 public: 301 /** 302 * Remove cells from selection inside of the given cell range. 303 * 304 * @param aTable [in] HTML table element 305 * @param aStartRowIndex [in] row index where the cells range starts 306 * @param aStartColumnIndex [in] column index where the cells range starts 307 * @param aEndRowIndex [in] row index where the cells range ends 308 * @param aEndColumnIndex [in] column index where the cells range ends 309 */ 310 // TODO: annotate this with `MOZ_CAN_RUN_SCRIPT` instead. 311 MOZ_CAN_RUN_SCRIPT_BOUNDARY 312 nsresult RemoveCellsFromSelection(nsIContent* aTable, int32_t aStartRowIndex, 313 int32_t aStartColumnIndex, 314 int32_t aEndRowIndex, 315 int32_t aEndColumnIndex); 316 317 /** 318 * Remove cells from selection outside of the given cell range. 319 * 320 * @param aTable [in] HTML table element 321 * @param aStartRowIndex [in] row index where the cells range starts 322 * @param aStartColumnIndex [in] column index where the cells range starts 323 * @param aEndRowIndex [in] row index where the cells range ends 324 * @param aEndColumnIndex [in] column index where the cells range ends 325 */ 326 // TODO: annotate this with `MOZ_CAN_RUN_SCRIPT` instead. 327 MOZ_CAN_RUN_SCRIPT_BOUNDARY 328 nsresult RestrictCellsToSelection(nsIContent* aTable, int32_t aStartRowIndex, 329 int32_t aStartColumnIndex, 330 int32_t aEndRowIndex, 331 int32_t aEndColumnIndex); 332 333 /** 334 * StartAutoScrollTimer is responsible for scrolling frames so that 335 * aPoint is always visible, and for selecting any frame that contains 336 * aPoint. The timer will also reset itself to fire again if we have 337 * not scrolled to the end of the document. 338 * 339 * @param aFrame is the outermost frame to use when searching for 340 * the closest frame for the point, i.e. the frame that is capturing 341 * the mouse 342 * 343 * @param aPoint is relative to aFrame. 344 * 345 * @param aDelay is the timer's interval. 346 */ 347 MOZ_CAN_RUN_SCRIPT 348 nsresult StartAutoScrollTimer(nsIFrame* aFrame, const nsPoint& aPoint, 349 uint32_t aDelay); 350 351 /** 352 * Stops any active auto scroll timer. 353 */ 354 void StopAutoScrollTimer(); 355 356 /** 357 * Returns in frame coordinates the selection beginning and ending with the 358 * type of selection given 359 * 360 * @param aContent is the content asking 361 * @param aContentOffset is the starting content boundary 362 * @param aContentLength is the length of the content piece asking 363 * @param aSlowCheck will check using slow method with no shortcuts 364 */ 365 mozilla::UniquePtr<SelectionDetails> LookUpSelection(nsIContent* aContent, 366 int32_t aContentOffset, 367 int32_t aContentLength, 368 bool aSlowCheck) const; 369 370 /** 371 * Sets the drag state to aState for resons of drag state. 372 * 373 * @param aState is the new state of drag 374 */ 375 MOZ_CAN_RUN_SCRIPT_BOUNDARY 376 void SetDragState(bool aState); 377 378 /** 379 * Gets the drag state to aState for resons of drag state. 380 * 381 * @param aState will hold the state of drag 382 */ GetDragState()383 bool GetDragState() const { return mDragState; } 384 385 /** 386 * If we are in table cell selection mode. aka ctrl click in table cell 387 */ IsInTableSelectionMode()388 bool IsInTableSelectionMode() const { 389 return mTableSelection.mMode != mozilla::TableSelectionMode::None; 390 } ClearTableCellSelection()391 void ClearTableCellSelection() { 392 mTableSelection.mMode = mozilla::TableSelectionMode::None; 393 } 394 395 /** 396 * No query interface for selection. must use this method now. 397 * 398 * @param aSelectionType The selection type what you want. 399 */ 400 mozilla::dom::Selection* GetSelection( 401 mozilla::SelectionType aSelectionType) const; 402 403 /** 404 * ScrollSelectionIntoView scrolls a region of the selection, 405 * so that it is visible in the scrolled view. 406 * 407 * @param aSelectionType the selection to scroll into view. 408 * 409 * @param aRegion the region inside the selection to scroll into view. 410 * 411 * @param aFlags the scroll flags. Valid bits include: 412 * * SCROLL_SYNCHRONOUS: when set, scrolls the selection into view 413 * before returning. If not set, posts a request which is processed 414 * at some point after the method returns. 415 * * SCROLL_FIRST_ANCESTOR_ONLY: if set, only the first ancestor will be 416 * scrolled into view. 417 */ 418 // TODO: replace with `MOZ_CAN_RUN_SCRIPT`. 419 MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult 420 ScrollSelectionIntoView(mozilla::SelectionType aSelectionType, 421 SelectionRegion aRegion, int16_t aFlags) const; 422 423 /** 424 * RepaintSelection repaints the selected frames that are inside the 425 * selection specified by aSelectionType. 426 * 427 * @param aSelectionType The selection type what you want to repaint. 428 */ 429 nsresult RepaintSelection(mozilla::SelectionType aSelectionType); 430 431 bool IsValidSelectionPoint(nsINode* aNode) const; 432 433 /** 434 * Given a node and its child offset, return the nsIFrame and the offset into 435 * that frame. 436 * 437 * @param aNode input parameter for the node to look at 438 * @param aOffset offset into above node. 439 * @param aReturnOffset will contain offset into frame. 440 */ 441 static nsIFrame* GetFrameForNodeOffset(nsIContent* aNode, int32_t aOffset, 442 CaretAssociateHint aHint, 443 int32_t* aReturnOffset); 444 445 /** 446 * GetFrameToPageSelect() returns a frame which is ancestor limit of 447 * per-page selection. The frame may not be scrollable. E.g., 448 * when selection ancestor limit is set to a frame of an editing host of 449 * contenteditable element and it's not scrollable. 450 */ 451 nsIFrame* GetFrameToPageSelect() const; 452 453 /** 454 * This method moves caret (if aExtend is false) or expands selection (if 455 * aExtend is true). Then, scrolls aFrame one page. Finally, this may 456 * call ScrollSelectionIntoView() for making focus of selection visible 457 * but depending on aSelectionIntoView value. 458 * 459 * @param aForward if true, scroll forward if not scroll backward 460 * @param aExtend if true, extend selection to the new point 461 * @param aFrame the frame to scroll or container of per-page selection. 462 * if aExtend is true and selection may have ancestor limit, 463 * should set result of GetFrameToPageSelect(). 464 * @param aSelectionIntoView 465 * If IfChanged, this makes selection into view only when 466 * selection is modified by the call. 467 * If Yes, this makes selection into view always. 468 */ 469 enum class SelectionIntoView { IfChanged, Yes }; 470 MOZ_CAN_RUN_SCRIPT nsresult PageMove(bool aForward, bool aExtend, 471 nsIFrame* aFrame, 472 SelectionIntoView aSelectionIntoView); 473 SetHint(CaretAssociateHint aHintRight)474 void SetHint(CaretAssociateHint aHintRight) { mCaret.mHint = aHintRight; } GetHint()475 CaretAssociateHint GetHint() const { return mCaret.mHint; } 476 477 void SetCaretBidiLevelAndMaybeSchedulePaint(nsBidiLevel aLevel); 478 479 /** 480 * GetCaretBidiLevel gets the caret bidi level. 481 */ 482 nsBidiLevel GetCaretBidiLevel() const; 483 484 /** 485 * UndefineCaretBidiLevel sets the caret bidi level to "undefined". 486 */ 487 void UndefineCaretBidiLevel(); 488 489 /** 490 * PhysicalMove will generally be called from the nsiselectioncontroller 491 * implementations. the effect being the selection will move one unit 492 * 'aAmount' in the given aDirection. 493 * @param aDirection the direction to move the selection 494 * @param aAmount amount of movement (char/line; word/page; eol/doc) 495 * @param aExtend continue selection 496 */ 497 // TODO: replace with `MOZ_CAN_RUN_SCRIPT`. 498 MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult PhysicalMove(int16_t aDirection, 499 int16_t aAmount, 500 bool aExtend); 501 502 /** 503 * CharacterMove will generally be called from the nsiselectioncontroller 504 * implementations. the effect being the selection will move one character 505 * left or right. 506 * @param aForward move forward in document. 507 * @param aExtend continue selection 508 */ 509 // TODO: replace with `MOZ_CAN_RUN_SCRIPT`. 510 MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult CharacterMove(bool aForward, 511 bool aExtend); 512 513 /** 514 * WordMove will generally be called from the nsiselectioncontroller 515 * implementations. the effect being the selection will move one word left or 516 * right. 517 * @param aForward move forward in document. 518 * @param aExtend continue selection 519 */ 520 // TODO: replace with `MOZ_CAN_RUN_SCRIPT`. 521 MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult WordMove(bool aForward, bool aExtend); 522 523 /** 524 * LineMove will generally be called from the nsiselectioncontroller 525 * implementations. the effect being the selection will move one line up or 526 * down. 527 * @param aForward move forward in document. 528 * @param aExtend continue selection 529 */ 530 // TODO: replace with `MOZ_CAN_RUN_SCRIPT`. 531 MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult LineMove(bool aForward, bool aExtend); 532 533 /** 534 * IntraLineMove will generally be called from the nsiselectioncontroller 535 * implementations. the effect being the selection will move to beginning or 536 * end of line 537 * @param aForward move forward in document. 538 * @param aExtend continue selection 539 */ 540 // TODO: replace with `MOZ_CAN_RUN_SCRIPT`. 541 MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult IntraLineMove(bool aForward, 542 bool aExtend); 543 544 /** 545 * CreateRangeExtendedToNextGraphemeClusterBoundary() returns range which is 546 * extended from normal selection range to start of next grapheme cluster 547 * boundary. 548 */ 549 template <typename RangeType> 550 MOZ_CAN_RUN_SCRIPT mozilla::Result<RefPtr<RangeType>, nsresult> CreateRangeExtendedToNextGraphemeClusterBoundary()551 CreateRangeExtendedToNextGraphemeClusterBoundary() { 552 return CreateRangeExtendedToSomewhere<RangeType>(eDirNext, eSelectCluster, 553 eLogical); 554 } 555 556 /** 557 * CreateRangeExtendedToPreviousCharacterBoundary() returns range which is 558 * extended from normal selection range to start of previous character 559 * boundary. 560 */ 561 template <typename RangeType> 562 MOZ_CAN_RUN_SCRIPT mozilla::Result<RefPtr<RangeType>, nsresult> CreateRangeExtendedToPreviousCharacterBoundary()563 CreateRangeExtendedToPreviousCharacterBoundary() { 564 return CreateRangeExtendedToSomewhere<RangeType>( 565 eDirPrevious, eSelectCharacter, eLogical); 566 } 567 568 /** 569 * CreateRangeExtendedToNextWordBoundary() returns range which is 570 * extended from normal selection range to start of next word boundary. 571 */ 572 template <typename RangeType> 573 MOZ_CAN_RUN_SCRIPT mozilla::Result<RefPtr<RangeType>, nsresult> CreateRangeExtendedToNextWordBoundary()574 CreateRangeExtendedToNextWordBoundary() { 575 return CreateRangeExtendedToSomewhere<RangeType>(eDirNext, eSelectWord, 576 eLogical); 577 } 578 579 /** 580 * CreateRangeExtendedToPreviousWordBoundary() returns range which is 581 * extended from normal selection range to start of previous word boundary. 582 */ 583 template <typename RangeType> 584 MOZ_CAN_RUN_SCRIPT mozilla::Result<RefPtr<RangeType>, nsresult> CreateRangeExtendedToPreviousWordBoundary()585 CreateRangeExtendedToPreviousWordBoundary() { 586 return CreateRangeExtendedToSomewhere<RangeType>(eDirPrevious, eSelectWord, 587 eLogical); 588 } 589 590 /** 591 * CreateRangeExtendedToPreviousHardLineBreak() returns range which is 592 * extended from normal selection range to previous hard line break. 593 */ 594 template <typename RangeType> 595 MOZ_CAN_RUN_SCRIPT mozilla::Result<RefPtr<RangeType>, nsresult> CreateRangeExtendedToPreviousHardLineBreak()596 CreateRangeExtendedToPreviousHardLineBreak() { 597 return CreateRangeExtendedToSomewhere<RangeType>( 598 eDirPrevious, eSelectBeginLine, eLogical); 599 } 600 601 /** 602 * CreateRangeExtendedToNextHardLineBreak() returns range which is extended 603 * from normal selection range to next hard line break. 604 */ 605 template <typename RangeType> 606 MOZ_CAN_RUN_SCRIPT mozilla::Result<RefPtr<RangeType>, nsresult> CreateRangeExtendedToNextHardLineBreak()607 CreateRangeExtendedToNextHardLineBreak() { 608 return CreateRangeExtendedToSomewhere<RangeType>(eDirNext, eSelectEndLine, 609 eLogical); 610 } 611 612 /** Sets/Gets The display selection enum. 613 */ SetDisplaySelection(int16_t aState)614 void SetDisplaySelection(int16_t aState) { mDisplaySelection = aState; } GetDisplaySelection()615 int16_t GetDisplaySelection() const { return mDisplaySelection; } 616 617 /** 618 * This method can be used to store the data received during a MouseDown 619 * event so that we can place the caret during the MouseUp event. 620 * 621 * @param aMouseEvent the event received by the selection MouseDown 622 * handling method. A nullptr value can be use to tell this method 623 * that any data is storing is no longer valid. 624 */ 625 void SetDelayedCaretData(mozilla::WidgetMouseEvent* aMouseEvent); 626 627 /** 628 * Get the delayed MouseDown event data necessary to place the 629 * caret during MouseUp processing. 630 * 631 * @return a pointer to the event received 632 * by the selection during MouseDown processing. It can be nullptr 633 * if the data is no longer valid. 634 */ HasDelayedCaretData()635 bool HasDelayedCaretData() { return mDelayedMouseEvent.mIsValid; } IsShiftDownInDelayedCaretData()636 bool IsShiftDownInDelayedCaretData() { 637 NS_ASSERTION(mDelayedMouseEvent.mIsValid, "No valid delayed caret data"); 638 return mDelayedMouseEvent.mIsShift; 639 } GetClickCountInDelayedCaretData()640 uint32_t GetClickCountInDelayedCaretData() { 641 NS_ASSERTION(mDelayedMouseEvent.mIsValid, "No valid delayed caret data"); 642 return mDelayedMouseEvent.mClickCount; 643 } 644 MouseDownRecorded()645 bool MouseDownRecorded() { 646 return !GetDragState() && HasDelayedCaretData() && 647 GetClickCountInDelayedCaretData() < 2; 648 } 649 650 /** 651 * Get the content node that limits the selection 652 * 653 * When searching up a nodes for parents, as in a text edit field 654 * in an browser page, we must stop at this node else we reach into the 655 * parent page, which is very bad! 656 */ GetLimiter()657 nsIContent* GetLimiter() const { return mLimiters.mLimiter; } 658 GetAncestorLimiter()659 nsIContent* GetAncestorLimiter() const { return mLimiters.mAncestorLimiter; } 660 MOZ_CAN_RUN_SCRIPT_BOUNDARY void SetAncestorLimiter(nsIContent* aLimiter); 661 662 /** 663 * GetPrevNextBidiLevels will return the frames and associated Bidi levels of 664 * the characters logically before and after a (collapsed) selection. 665 * 666 * @param aNode is the node containing the selection 667 * @param aContentOffset is the offset of the selection in the node 668 * @param aJumpLines 669 * If true, look across line boundaries. 670 * If false, behave as if there were base-level frames at line edges. 671 * 672 * @return A struct holding the before/after frame and the before/after 673 * level. 674 * 675 * At the beginning and end of each line there is assumed to be a frame with 676 * Bidi level equal to the paragraph embedding level. 677 * 678 * In these cases the before frame and after frame respectively will be 679 * nullptr. 680 */ 681 nsPrevNextBidiLevels GetPrevNextBidiLevels(nsIContent* aNode, 682 uint32_t aContentOffset, 683 bool aJumpLines) const; 684 685 /** 686 * GetFrameFromLevel will scan in a given direction 687 * until it finds a frame with a Bidi level less than or equal to a given 688 * level. It will return the last frame before this. 689 * 690 * @param aPresContext is the context to use 691 * @param aFrameIn is the frame to start from 692 * @param aDirection is the direction to scan 693 * @param aBidiLevel is the level to search for 694 * @param aFrameOut will hold the frame returned 695 */ 696 nsresult GetFrameFromLevel(nsIFrame* aFrameIn, nsDirection aDirection, 697 nsBidiLevel aBidiLevel, 698 nsIFrame** aFrameOut) const; 699 700 /** 701 * MaintainSelection will track the normal selection as being "sticky". 702 * Dragging or extending selection will never allow for a subset 703 * (or the whole) of the maintained selection to become unselected. 704 * Primary use: double click selecting then dragging on second click 705 * 706 * @param aAmount the initial amount of text selected (word, line or 707 * paragraph). For "line", use eSelectBeginLine. 708 */ 709 nsresult MaintainSelection(nsSelectionAmount aAmount = eSelectNoAmount); 710 711 nsresult ConstrainFrameAndPointToAnchorSubtree(nsIFrame* aFrame, 712 const nsPoint& aPoint, 713 nsIFrame** aRetFrame, 714 nsPoint& aRetPoint) const; 715 716 /** 717 * @param aPresShell is the parameter to be used for most of the other calls 718 * for callbacks etc 719 * 720 * @param aLimiter limits the selection to nodes with aLimiter parents 721 * 722 * @param aAccessibleCaretEnabled true if we should enable the accessible 723 * caret. 724 */ 725 nsFrameSelection(mozilla::PresShell* aPresShell, nsIContent* aLimiter, 726 bool aAccessibleCaretEnabled); 727 728 void StartBatchChanges(); 729 730 MOZ_CAN_RUN_SCRIPT_BOUNDARY 731 /** 732 * @param aReasons potentially multiple of the reasons defined in 733 * nsISelectionListener.idl 734 */ 735 void EndBatchChanges(int16_t aReasons = nsISelectionListener::NO_REASON); 736 GetPresShell()737 mozilla::PresShell* GetPresShell() const { return mPresShell; } 738 739 void DisconnectFromPresShell(); 740 nsresult ClearNormalSelection(); 741 742 // Table selection support. 743 static nsITableCellLayout* GetCellLayout(nsIContent* aCellContent); 744 745 private: 746 ~nsFrameSelection(); 747 748 MOZ_CAN_RUN_SCRIPT 749 nsresult TakeFocus(nsIContent* aNewFocus, uint32_t aContentOffset, 750 uint32_t aContentEndOffset, CaretAssociateHint aHint, 751 FocusMode aFocusMode); 752 753 void BidiLevelFromMove(mozilla::PresShell* aPresShell, nsIContent* aNode, 754 uint32_t aContentOffset, nsSelectionAmount aAmount, 755 CaretAssociateHint aHint); 756 void BidiLevelFromClick(nsIContent* aNewFocus, uint32_t aContentOffset); 757 static nsPrevNextBidiLevels GetPrevNextBidiLevels(nsIContent* aNode, 758 uint32_t aContentOffset, 759 CaretAssociateHint aHint, 760 bool aJumpLines); 761 762 /** 763 * @param aReasons potentially multiple of the reasons defined in 764 * nsISelectionListener.idl. 765 */ SetChangeReasons(int16_t aReasons)766 void SetChangeReasons(int16_t aReasons) { 767 mSelectionChangeReasons = aReasons; 768 } 769 770 /** 771 * @param aReasons potentially multiple of the reasons defined in 772 * nsISelectionListener.idl. 773 */ AddChangeReasons(int16_t aReasons)774 void AddChangeReasons(int16_t aReasons) { 775 mSelectionChangeReasons |= aReasons; 776 } 777 778 /** 779 * @return potentially multiple of the reasons defined in 780 * nsISelectionListener.idl. 781 */ PopChangeReasons()782 int16_t PopChangeReasons() { 783 int16_t retval = mSelectionChangeReasons; 784 mSelectionChangeReasons = nsISelectionListener::NO_REASON; 785 return retval; 786 } 787 IsUserSelectionReason()788 bool IsUserSelectionReason() const { 789 return (mSelectionChangeReasons & 790 (nsISelectionListener::DRAG_REASON | 791 nsISelectionListener::MOUSEDOWN_REASON | 792 nsISelectionListener::MOUSEUP_REASON | 793 nsISelectionListener::KEYPRESS_REASON)) != 794 nsISelectionListener::NO_REASON; 795 } 796 797 friend class mozilla::dom::Selection; 798 friend class mozilla::SelectionChangeEventDispatcher; 799 friend struct mozilla::AutoPrepareFocusRange; 800 801 /*HELPER METHODS*/ 802 // Whether MoveCaret should use logical or visual movement, 803 // or follow the bidi.edit.caret_movement_style preference. 804 enum CaretMovementStyle { eLogical, eVisual, eUsePrefStyle }; 805 MOZ_CAN_RUN_SCRIPT nsresult MoveCaret(nsDirection aDirection, 806 bool aContinueSelection, 807 nsSelectionAmount aAmount, 808 CaretMovementStyle aMovementStyle); 809 810 /** 811 * PeekOffsetForCaretMove() only peek offset for caret move. I.e., won't 812 * change selection ranges nor bidi information. 813 */ 814 mozilla::Result<nsPeekOffsetStruct, nsresult> PeekOffsetForCaretMove( 815 nsDirection aDirection, bool aContinueSelection, 816 const nsSelectionAmount aAmount, CaretMovementStyle aMovementStyle, 817 const nsPoint& aDesiredCaretPos) const; 818 819 /** 820 * CreateRangeExtendedToSomewhere() is common method to implement 821 * CreateRangeExtendedTo*(). This method creates a range extended from 822 * normal selection range. 823 */ 824 template <typename RangeType> 825 MOZ_CAN_RUN_SCRIPT mozilla::Result<RefPtr<RangeType>, nsresult> 826 CreateRangeExtendedToSomewhere(nsDirection aDirection, 827 const nsSelectionAmount aAmount, 828 CaretMovementStyle aMovementStyle); 829 830 /** 831 * IsIntraLineCaretMove() is a helper method for PeekOffsetForCaretMove() 832 * and CreateRangeExtendedToSomwhereFromNormalSelection(). This returns 833 * whether aAmount is intra line move or is crossing hard line break. 834 * This returns error if aMount is not supported by the methods. 835 */ IsIntraLineCaretMove(nsSelectionAmount aAmount)836 static mozilla::Result<bool, nsresult> IsIntraLineCaretMove( 837 nsSelectionAmount aAmount) { 838 switch (aAmount) { 839 case eSelectCharacter: 840 case eSelectCluster: 841 case eSelectWord: 842 case eSelectWordNoSpace: 843 case eSelectBeginLine: 844 case eSelectEndLine: 845 return true; 846 case eSelectLine: 847 return false; 848 default: 849 return mozilla::Err(NS_ERROR_FAILURE); 850 } 851 } 852 853 void InvalidateDesiredCaretPos(); // do not listen to mDesiredCaretPos.mValue 854 // you must get another. 855 IsBatching()856 bool IsBatching() const { return mBatching.mCounter > 0; } 857 SetChangesDuringBatchingFlag()858 void SetChangesDuringBatchingFlag() { 859 MOZ_ASSERT(mBatching.mCounter > 0); 860 861 mBatching.mChangesDuringBatching = true; 862 } 863 864 // nsFrameSelection may get deleted when calling this, 865 // so remember to use nsCOMPtr when needed. 866 MOZ_CAN_RUN_SCRIPT 867 nsresult NotifySelectionListeners(mozilla::SelectionType aSelectionType); 868 869 static nsresult GetCellIndexes(nsIContent* aCell, int32_t& aRowIndex, 870 int32_t& aColIndex); 871 872 static nsIContent* GetFirstCellNodeInRange(const nsRange* aRange); 873 // Returns non-null table if in same table, null otherwise 874 static nsIContent* IsInSameTable(nsIContent* aContent1, 875 nsIContent* aContent2); 876 // Might return null 877 static nsIContent* GetParentTable(nsIContent* aCellNode); 878 879 ////////////BEGIN nsFrameSelection members 880 881 RefPtr<mozilla::dom::Selection> 882 mDomSelections[sizeof(mozilla::kPresentSelectionTypes) / 883 sizeof(mozilla::SelectionType)]; 884 885 struct TableSelection { 886 // Get our first range, if its first selected node is a cell. If this does 887 // not return null, then the first node in the returned range is a cell 888 // (according to GetFirstCellNodeInRange). 889 nsRange* GetFirstCellRange(const mozilla::dom::Selection& aNormalSelection); 890 891 // Get our next range, if its first selected node is a cell. If this does 892 // not return null, then the first node in the returned range is a cell 893 // (according to GetFirstCellNodeInRange). 894 nsRange* GetNextCellRange(const mozilla::dom::Selection& aNormalSelection); 895 896 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 897 HandleSelection(nsINode* aParentContent, int32_t aContentOffset, 898 mozilla::TableSelectionMode aTarget, 899 mozilla::WidgetMouseEvent* aMouseEvent, bool aDragState, 900 mozilla::dom::Selection& aNormalSelection); 901 902 /** 903 * @return the closest inclusive table cell ancestor 904 * (https://dom.spec.whatwg.org/#concept-tree-inclusive-ancestor) of 905 * aContent, if it is actively editable. 906 */ 907 static nsINode* IsContentInActivelyEditableTableCell( 908 nsPresContext* aContext, nsIContent* aContent); 909 910 // TODO: annotate this with `MOZ_CAN_RUN_SCRIPT` instead. 911 MOZ_CAN_RUN_SCRIPT_BOUNDARY 912 nsresult SelectBlockOfCells(nsIContent* aStartCell, nsIContent* aEndCell, 913 mozilla::dom::Selection& aNormalSelection); 914 915 nsresult SelectRowOrColumn(nsIContent* aCellContent, 916 mozilla::dom::Selection& aNormalSelection); 917 918 MOZ_CAN_RUN_SCRIPT nsresult 919 UnselectCells(nsIContent* aTable, int32_t aStartRowIndex, 920 int32_t aStartColumnIndex, int32_t aEndRowIndex, 921 int32_t aEndColumnIndex, bool aRemoveOutsideOfCellRange, 922 mozilla::dom::Selection& aNormalSelection); 923 924 nsCOMPtr<nsINode> 925 mClosestInclusiveTableCellAncestor; // used to snap to table selection 926 nsCOMPtr<nsIContent> mStartSelectedCell; 927 nsCOMPtr<nsIContent> mEndSelectedCell; 928 nsCOMPtr<nsIContent> mAppendStartSelectedCell; 929 nsCOMPtr<nsIContent> mUnselectCellOnMouseUp; 930 mozilla::TableSelectionMode mMode = mozilla::TableSelectionMode::None; 931 int32_t mSelectedCellIndex = 0; 932 bool mDragSelectingCells = false; 933 }; 934 935 TableSelection mTableSelection; 936 937 struct MaintainedRange { 938 /** 939 * @return true iff the point (aContent, aOffset) is inside and not at the 940 * boundaries of mRange. 941 */ 942 MOZ_CAN_RUN_SCRIPT bool AdjustNormalSelection( 943 const nsIContent* aContent, int32_t aOffset, 944 mozilla::dom::Selection& aNormalSelection) const; 945 946 /** 947 * @param aScrollViewStop see `nsPeekOffsetStruct::mScrollViewStop`. 948 */ 949 void AdjustContentOffsets(nsIFrame::ContentOffsets& aOffsets, 950 bool aScrollViewStop) const; 951 952 void MaintainAnchorFocusRange( 953 const mozilla::dom::Selection& aNormalSelection, 954 nsSelectionAmount aAmount); 955 956 RefPtr<nsRange> mRange; 957 nsSelectionAmount mAmount = eSelectNoAmount; 958 }; 959 960 MaintainedRange mMaintainedRange; 961 962 struct Batching { 963 uint32_t mCounter = 0; 964 bool mChangesDuringBatching = false; 965 }; 966 967 Batching mBatching; 968 969 struct Limiters { 970 // Limit selection navigation to a child of this node. 971 nsCOMPtr<nsIContent> mLimiter; 972 // Limit selection navigation to a descendant of this node. 973 nsCOMPtr<nsIContent> mAncestorLimiter; 974 }; 975 976 Limiters mLimiters; 977 978 mozilla::PresShell* mPresShell = nullptr; 979 // Reasons for notifications of selection changing. 980 // Can be multiple of the reasons defined in nsISelectionListener.idl. 981 int16_t mSelectionChangeReasons = nsISelectionListener::NO_REASON; 982 // For visual display purposes. 983 int16_t mDisplaySelection = nsISelectionController::SELECTION_OFF; 984 985 struct Caret { 986 // Hint to tell if the selection is at the end of this line or beginning of 987 // next. 988 CaretAssociateHint mHint = mozilla::CARET_ASSOCIATE_BEFORE; 989 nsBidiLevel mBidiLevel = BIDI_LEVEL_UNDEFINED; 990 int8_t mMovementStyle = 0; 991 IsVisualMovementCaret992 bool IsVisualMovement(bool aContinueSelection, 993 CaretMovementStyle aMovementStyle) const { 994 return aMovementStyle == eVisual || 995 (aMovementStyle == eUsePrefStyle && 996 (mMovementStyle == 1 || 997 (mMovementStyle == 2 && !aContinueSelection))); 998 } 999 }; 1000 1001 Caret mCaret; 1002 1003 nsBidiLevel mKbdBidiLevel = NSBIDI_LTR; 1004 1005 class DesiredCaretPos { 1006 public: 1007 // the position requested by the Key Handling for up down 1008 nsresult FetchPos(nsPoint& aDesiredCaretPos, 1009 const mozilla::PresShell& aPresShell, 1010 mozilla::dom::Selection& aNormalSelection) const; 1011 1012 void Set(const nsPoint& aPos); 1013 1014 void Invalidate(); 1015 1016 bool mIsSet = false; 1017 1018 private: 1019 nsPoint mValue; 1020 }; 1021 1022 DesiredCaretPos mDesiredCaretPos; 1023 1024 struct DelayedMouseEvent { 1025 bool mIsValid = false; 1026 // These values are not used since they are only valid when mIsValid is 1027 // true, and setting mIsValid always overrides these values. 1028 bool mIsShift = false; 1029 uint32_t mClickCount = 0; 1030 }; 1031 1032 DelayedMouseEvent mDelayedMouseEvent; 1033 1034 bool mDragState = false; // for drag purposes 1035 bool mAccessibleCaretEnabled = false; 1036 }; 1037 1038 #endif /* nsFrameSelection_h___ */ 1039