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/. */
7 #ifndef nsFrameSelection_h___
8 #define nsFrameSelection_h___
11 #include "mozilla/intl/BidiEmbeddingLevel.h"
12 #include "mozilla/Assertions.h"
13 #include "mozilla/Attributes.h"
14 #include "mozilla/CompactPair.h"
15 #include "mozilla/EnumSet.h"
16 #include "mozilla/EventForwards.h"
17 #include "mozilla/dom/Highlight.h"
18 #include "mozilla/dom/Selection.h"
19 #include "mozilla/Result.h"
20 #include "mozilla/TextRange.h"
21 #include "mozilla/UniquePtr.h"
23 #include "nsIContent.h"
24 #include "nsISelectionController.h"
25 #include "nsISelectionListener.h"
26 #include "nsITableCellLayout.h"
27 #include "WordMovementType.h"
28 #include "CaretAssociationHint.h"
29 #include "nsBidiPresUtils.h"
33 #define BIDI_LEVEL_UNDEFINED mozilla::intl::BidiEmbeddingLevel(0x80)
35 //----------------------------------------------------------------------
37 // Selection interface
39 struct SelectionDetails
{
41 : mStart(), mEnd(), mSelectionType(mozilla::SelectionType::eInvalid
) {
42 MOZ_COUNT_CTOR(SelectionDetails
);
44 MOZ_COUNTED_DTOR(SelectionDetails
)
48 mozilla::SelectionType mSelectionType
;
49 mozilla::dom::HighlightSelectionData mHighlightData
;
50 mozilla::TextRangeStyle mTextRangeStyle
;
51 mozilla::UniquePtr
<SelectionDetails
> mNext
;
54 struct SelectionCustomColors
{
55 #ifdef NS_BUILD_REFCNT_LOGGING
56 MOZ_COUNTED_DEFAULT_CTOR(SelectionCustomColors
)
57 MOZ_COUNTED_DTOR(SelectionCustomColors
)
59 mozilla::Maybe
<nscolor
> mForegroundColor
;
60 mozilla::Maybe
<nscolor
> mBackgroundColor
;
61 mozilla::Maybe
<nscolor
> mAltForegroundColor
;
62 mozilla::Maybe
<nscolor
> mAltBackgroundColor
;
67 } // namespace mozilla
69 /** PeekOffsetStruct is used to group various arguments (both input and output)
70 * that are passed to nsIFrame::PeekOffset(). See below for the description of
71 * individual arguments.
76 enum class PeekOffsetOption
: uint16_t {
77 // Whether to allow jumping across line boundaries.
79 // Used with: eSelectCharacter, eSelectWord.
82 // Whether we should preserve or trim spaces at begin/end of content
85 // Whether to stop when reaching a scroll view boundary.
87 // Used with: eSelectCharacter, eSelectWord, eSelectLine.
90 // Whether to stop when reaching a placeholder frame.
93 // Whether the peeking is done in response to a keyboard action.
95 // Used with: eSelectWord.
98 // Whether bidi caret behavior is visual (set) or logical (unset).
100 // Used with: eSelectCharacter, eSelectWord, eSelectBeginLine, eSelectEndLine.
103 // Whether the selection is being extended or moved.
106 // If true, the offset has to end up in an editable node, otherwise we'll keep
111 using PeekOffsetOptions
= EnumSet
<PeekOffsetOption
>;
113 struct MOZ_STACK_CLASS PeekOffsetStruct
{
114 PeekOffsetStruct(nsSelectionAmount aAmount
, nsDirection aDirection
,
115 int32_t aStartOffset
, nsPoint aDesiredCaretPos
,
116 // Passing by value here is intentional because EnumSet
117 // is optimized as uint*_t in opt builds.
118 const PeekOffsetOptions aOptions
,
119 EWordMovementType aWordMovementType
= eDefaultBehavior
);
121 // Note: Most arguments (input and output) are only used with certain values
122 // of mAmount. These values are indicated for each argument below.
123 // Arguments with no such indication are used with all values of mAmount.
125 /*** Input arguments ***/
126 // Note: The value of some of the input arguments may be changed upon exit.
128 // The type of movement requested (by character, word, line, etc.)
129 nsSelectionAmount mAmount
;
131 // eDirPrevious or eDirNext.
133 // Note for visual bidi movement:
134 // * eDirPrevious means 'left-then-up' if the containing block is LTR,
135 // 'right-then-up' if it is RTL.
136 // * eDirNext means 'right-then-down' if the containing block is LTR,
137 // 'left-then-down' if it is RTL.
138 // * Between paragraphs, eDirPrevious means "go to the visual end of
139 // the previous paragraph", and eDirNext means "go to the visual
140 // beginning of the next paragraph".
142 // Used with: eSelectCharacter, eSelectWord, eSelectLine, eSelectParagraph.
143 const nsDirection mDirection
;
145 // Offset into the content of the current frame where the peek starts.
147 // Used with: eSelectCharacter, eSelectWord
148 int32_t mStartOffset
;
150 // The desired inline coordinate for the caret (one of .x or .y will be used,
151 // depending on line's writing mode)
153 // Used with: eSelectLine.
154 const nsPoint mDesiredCaretPos
;
156 // An enum that determines whether to prefer the start or end of a word or to
157 // use the default behavior, which is a combination of direction and the
158 // platform-based pref "layout.word_select.eat_space_to_next_word"
159 EWordMovementType mWordMovementType
;
161 PeekOffsetOptions mOptions
;
163 /*** Output arguments ***/
165 // Content reached as a result of the peek.
166 nsCOMPtr
<nsIContent
> mResultContent
;
168 // Frame reached as a result of the peek.
170 // Used with: eSelectCharacter, eSelectWord.
171 nsIFrame
* mResultFrame
;
173 // Offset into content reached as a result of the peek.
174 int32_t mContentOffset
;
176 // When the result position is between two frames, indicates which of the two
177 // frames the caret should be painted in. false means "the end of the frame
178 // logically before the caret", true means "the beginning of the frame
179 // logically after the caret".
181 // Used with: eSelectLine, eSelectBeginLine, eSelectEndLine.
182 CaretAssociationHint mAttach
;
185 } // namespace mozilla
187 struct nsPrevNextBidiLevels
{
188 void SetData(nsIFrame
* aFrameBefore
, nsIFrame
* aFrameAfter
,
189 mozilla::intl::BidiEmbeddingLevel aLevelBefore
,
190 mozilla::intl::BidiEmbeddingLevel aLevelAfter
) {
191 mFrameBefore
= aFrameBefore
;
192 mFrameAfter
= aFrameAfter
;
193 mLevelBefore
= aLevelBefore
;
194 mLevelAfter
= aLevelAfter
;
196 nsIFrame
* mFrameBefore
;
197 nsIFrame
* mFrameAfter
;
198 mozilla::intl::BidiEmbeddingLevel mLevelBefore
;
199 mozilla::intl::BidiEmbeddingLevel mLevelAfter
;
203 class SelectionChangeEventDispatcher
;
210 * Constants for places that want to handle table selections. These
211 * indicate what part of a table is being selected.
213 enum class TableSelectionMode
: uint32_t {
214 None
, /* Nothing being selected; not valid in all cases. */
215 Cell
, /* A cell is being selected. */
216 Row
, /* A row is being selected. */
217 Column
, /* A column is being selected. */
218 Table
, /* A table (including cells and captions) is being selected. */
219 AllCells
, /* All the cells in a table are being selected. */
222 } // namespace mozilla
223 class nsIScrollableFrame
;
225 class nsFrameSelection final
{
227 typedef mozilla::CaretAssociationHint CaretAssociateHint
;
229 /*interfaces for addref and release and queryinterface*/
231 NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(nsFrameSelection
)
232 NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(nsFrameSelection
)
234 enum class FocusMode
{
235 kExtendSelection
, /** Keep old anchor point. */
236 kCollapseToNewPoint
, /** Collapses the Selection to the new point. */
237 kMultiRangeSelection
, /** Keeps existing non-collapsed ranges and marks them
242 * HandleClick will take the focus to the new frame at the new offset and
243 * will either extend the selection from the old anchor, or replace the old
244 * anchor. the old anchor and focus position may also be used to deselect
247 * @param aNewfocus is the content that wants the focus
249 * @param aContentOffset is the content offset of the parent aNewFocus
251 * @param aContentOffsetEnd is the content offset of the parent aNewFocus and
252 * is specified different when you need to select to and include both start
255 * @param aHint will tell the selection which direction geometrically to
256 * actually show the caret on. 1 = end of this line 0 = beginning of this line
258 MOZ_CAN_RUN_SCRIPT nsresult
HandleClick(nsIContent
* aNewFocus
,
259 uint32_t aContentOffset
,
260 uint32_t aContentEndOffset
,
261 FocusMode aFocusMode
,
262 CaretAssociateHint aHint
);
266 * Sets flag to true if a selection is created by doubleclick or
267 * long tapping a word.
269 * @param aIsDoubleClickSelection True if the selection is created by
270 * doubleclick or long tap over a word.
272 void SetIsDoubleClickSelection(bool aIsDoubleClickSelection
) {
273 mIsDoubleClickSelection
= aIsDoubleClickSelection
;
277 * Returns true if the selection was created by doubleclick or
278 * long tap over a word.
280 [[nodiscard
]] bool IsDoubleClickSelection() const {
281 return mIsDoubleClickSelection
;
285 * HandleDrag extends the selection to contain the frame closest to aPoint.
287 * @param aPresContext is the context to use when figuring out what frame
288 * contains the point.
290 * @param aFrame is the parent of all frames to use when searching for the
291 * closest frame to the point.
293 * @param aPoint is relative to aFrame
295 MOZ_CAN_RUN_SCRIPT
void HandleDrag(nsIFrame
* aFrame
, const nsPoint
& aPoint
);
298 * HandleTableSelection will set selection to a table, cell, etc
299 * depending on information contained in aFlags
301 * @param aParentContent is the paretent of either a table or cell that user
302 * clicked or dragged the mouse in
304 * @param aContentOffset is the offset of the table or cell
306 * @param aTarget indicates what to select
307 * * TableSelectionMode::Cell
308 * We should select a cell (content points to the cell)
309 * * TableSelectionMode::Row
310 * We should select a row (content points to any cell in row)
311 * * TableSelectionMode::Column
312 * We should select a row (content points to any cell in column)
313 * * TableSelectionMode::Table
314 * We should select a table (content points to the table)
315 * * TableSelectionMode::AllCells
316 * We should select all cells (content points to any cell in table)
318 * @param aMouseEvent passed in so we can get where event occurred
319 * and what keys are pressed
321 // TODO: replace with `MOZ_CAN_RUN_SCRIPT`.
322 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult
323 HandleTableSelection(nsINode
* aParentContent
, int32_t aContentOffset
,
324 mozilla::TableSelectionMode aTarget
,
325 mozilla::WidgetMouseEvent
* aMouseEvent
);
328 * Add cell to the selection with `SelectionType::eNormal`.
330 * @param aCell [in] HTML td element.
332 nsresult
SelectCellElement(nsIContent
* aCell
);
336 * Remove cells from selection inside of the given cell range.
338 * @param aTable [in] HTML table element
339 * @param aStartRowIndex [in] row index where the cells range starts
340 * @param aStartColumnIndex [in] column index where the cells range starts
341 * @param aEndRowIndex [in] row index where the cells range ends
342 * @param aEndColumnIndex [in] column index where the cells range ends
344 // TODO: annotate this with `MOZ_CAN_RUN_SCRIPT` instead.
345 MOZ_CAN_RUN_SCRIPT_BOUNDARY
346 nsresult
RemoveCellsFromSelection(nsIContent
* aTable
, int32_t aStartRowIndex
,
347 int32_t aStartColumnIndex
,
348 int32_t aEndRowIndex
,
349 int32_t aEndColumnIndex
);
352 * Remove cells from selection outside of the given cell range.
354 * @param aTable [in] HTML table element
355 * @param aStartRowIndex [in] row index where the cells range starts
356 * @param aStartColumnIndex [in] column index where the cells range starts
357 * @param aEndRowIndex [in] row index where the cells range ends
358 * @param aEndColumnIndex [in] column index where the cells range ends
360 // TODO: annotate this with `MOZ_CAN_RUN_SCRIPT` instead.
361 MOZ_CAN_RUN_SCRIPT_BOUNDARY
362 nsresult
RestrictCellsToSelection(nsIContent
* aTable
, int32_t aStartRowIndex
,
363 int32_t aStartColumnIndex
,
364 int32_t aEndRowIndex
,
365 int32_t aEndColumnIndex
);
368 * StartAutoScrollTimer is responsible for scrolling frames so that
369 * aPoint is always visible, and for selecting any frame that contains
370 * aPoint. The timer will also reset itself to fire again if we have
371 * not scrolled to the end of the document.
373 * @param aFrame is the outermost frame to use when searching for
374 * the closest frame for the point, i.e. the frame that is capturing
377 * @param aPoint is relative to aFrame.
379 * @param aDelay is the timer's interval.
382 nsresult
StartAutoScrollTimer(nsIFrame
* aFrame
, const nsPoint
& aPoint
,
386 * Stops any active auto scroll timer.
388 void StopAutoScrollTimer();
391 * Returns in frame coordinates the selection beginning and ending with the
392 * type of selection given
394 * @param aContent is the content asking
395 * @param aContentOffset is the starting content boundary
396 * @param aContentLength is the length of the content piece asking
397 * @param aSlowCheck will check using slow method with no shortcuts
399 mozilla::UniquePtr
<SelectionDetails
> LookUpSelection(nsIContent
* aContent
,
400 int32_t aContentOffset
,
401 int32_t aContentLength
,
402 bool aSlowCheck
) const;
405 * Sets the drag state to aState for resons of drag state.
407 * @param aState is the new state of drag
410 void SetDragState(bool aState
);
413 * Gets the drag state to aState for resons of drag state.
415 * @param aState will hold the state of drag
417 bool GetDragState() const { return mDragState
; }
420 * If we are in table cell selection mode. aka ctrl click in table cell
422 bool IsInTableSelectionMode() const {
423 return mTableSelection
.mMode
!= mozilla::TableSelectionMode::None
;
425 void ClearTableCellSelection() {
426 mTableSelection
.mMode
= mozilla::TableSelectionMode::None
;
430 * No query interface for selection. must use this method now.
432 * @param aSelectionType The selection type what you want.
434 mozilla::dom::Selection
* GetSelection(
435 mozilla::SelectionType aSelectionType
) const;
438 * @brief Adds a highlight selection for `aHighlight`.
440 MOZ_CAN_RUN_SCRIPT
void AddHighlightSelection(
441 nsAtom
* aHighlightName
, mozilla::dom::Highlight
& aHighlight
);
443 * @brief Removes the Highlight selection identified by `aHighlightName`.
445 MOZ_CAN_RUN_SCRIPT
void RemoveHighlightSelection(nsAtom
* aHighlightName
);
448 * @brief Adds a new range to the highlight selection.
450 * If there is no highlight selection for the given highlight yet, it is
451 * created using |AddHighlightSelection|.
453 MOZ_CAN_RUN_SCRIPT
void AddHighlightSelectionRange(
454 nsAtom
* aHighlightName
, mozilla::dom::Highlight
& aHighlight
,
455 mozilla::dom::AbstractRange
& aRange
);
458 * @brief Removes a range from a highlight selection.
460 MOZ_CAN_RUN_SCRIPT
void RemoveHighlightSelectionRange(
461 nsAtom
* aHighlightName
, mozilla::dom::AbstractRange
& aRange
);
463 * ScrollSelectionIntoView scrolls a region of the selection,
464 * so that it is visible in the scrolled view.
466 * @param aSelectionType the selection to scroll into view.
468 * @param aRegion the region inside the selection to scroll into view.
470 * @param aFlags the scroll flags. Valid bits include:
471 * * SCROLL_SYNCHRONOUS: when set, scrolls the selection into view
472 * before returning. If not set, posts a request which is processed
473 * at some point after the method returns.
474 * * SCROLL_FIRST_ANCESTOR_ONLY: if set, only the first ancestor will be
475 * scrolled into view.
477 // TODO: replace with `MOZ_CAN_RUN_SCRIPT`.
478 MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult
479 ScrollSelectionIntoView(mozilla::SelectionType aSelectionType
,
480 SelectionRegion aRegion
, int16_t aFlags
) const;
483 * RepaintSelection repaints the selected frames that are inside the
484 * selection specified by aSelectionType.
486 * @param aSelectionType The selection type what you want to repaint.
488 nsresult
RepaintSelection(mozilla::SelectionType aSelectionType
);
490 bool IsValidSelectionPoint(nsINode
* aNode
) const;
492 static bool AdjustFrameForLineStart(nsIFrame
*& aFrame
, int32_t& aFrameOffset
);
495 * Given a node and its child offset, return the nsIFrame and the offset into
498 * @param aNode input parameter for the node to look at
499 * TODO: Make this `const nsIContent*` for `ContentEventHandler`.
500 * @param aOffset offset into above node.
501 * @param aReturnOffset will contain offset into frame.
503 static nsIFrame
* GetFrameForNodeOffset(nsIContent
* aNode
, int32_t aOffset
,
504 CaretAssociateHint aHint
,
505 int32_t* aReturnOffset
);
508 * GetFrameToPageSelect() returns a frame which is ancestor limit of
509 * per-page selection. The frame may not be scrollable. E.g.,
510 * when selection ancestor limit is set to a frame of an editing host of
511 * contenteditable element and it's not scrollable.
513 nsIFrame
* GetFrameToPageSelect() const;
516 * This method moves caret (if aExtend is false) or expands selection (if
517 * aExtend is true). Then, scrolls aFrame one page. Finally, this may
518 * call ScrollSelectionIntoView() for making focus of selection visible
519 * but depending on aSelectionIntoView value.
521 * @param aForward if true, scroll forward if not scroll backward
522 * @param aExtend if true, extend selection to the new point
523 * @param aFrame the frame to scroll or container of per-page selection.
524 * if aExtend is true and selection may have ancestor limit,
525 * should set result of GetFrameToPageSelect().
526 * @param aSelectionIntoView
527 * If IfChanged, this makes selection into view only when
528 * selection is modified by the call.
529 * If Yes, this makes selection into view always.
531 enum class SelectionIntoView
{ IfChanged
, Yes
};
532 MOZ_CAN_RUN_SCRIPT nsresult
PageMove(bool aForward
, bool aExtend
,
534 SelectionIntoView aSelectionIntoView
);
536 void SetHint(CaretAssociateHint aHintRight
) { mCaret
.mHint
= aHintRight
; }
537 CaretAssociateHint
GetHint() const { return mCaret
.mHint
; }
539 void SetCaretBidiLevelAndMaybeSchedulePaint(
540 mozilla::intl::BidiEmbeddingLevel aLevel
);
543 * GetCaretBidiLevel gets the caret bidi level.
545 mozilla::intl::BidiEmbeddingLevel
GetCaretBidiLevel() const;
548 * UndefineCaretBidiLevel sets the caret bidi level to "undefined".
550 void UndefineCaretBidiLevel();
553 * PhysicalMove will generally be called from the nsiselectioncontroller
554 * implementations. the effect being the selection will move one unit
555 * 'aAmount' in the given aDirection.
556 * @param aDirection the direction to move the selection
557 * @param aAmount amount of movement (char/line; word/page; eol/doc)
558 * @param aExtend continue selection
560 // TODO: replace with `MOZ_CAN_RUN_SCRIPT`.
561 MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult
PhysicalMove(int16_t aDirection
,
566 * CharacterMove will generally be called from the nsiselectioncontroller
567 * implementations. the effect being the selection will move one character
569 * @param aForward move forward in document.
570 * @param aExtend continue selection
572 // TODO: replace with `MOZ_CAN_RUN_SCRIPT`.
573 MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult
CharacterMove(bool aForward
,
577 * WordMove will generally be called from the nsiselectioncontroller
578 * implementations. the effect being the selection will move one word left or
580 * @param aForward move forward in document.
581 * @param aExtend continue selection
583 // TODO: replace with `MOZ_CAN_RUN_SCRIPT`.
584 MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult
WordMove(bool aForward
, bool aExtend
);
587 * LineMove will generally be called from the nsiselectioncontroller
588 * implementations. the effect being the selection will move one line up or
590 * @param aForward move forward in document.
591 * @param aExtend continue selection
593 // TODO: replace with `MOZ_CAN_RUN_SCRIPT`.
594 MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult
LineMove(bool aForward
, bool aExtend
);
597 * IntraLineMove will generally be called from the nsiselectioncontroller
598 * implementations. the effect being the selection will move to beginning or
600 * @param aForward move forward in document.
601 * @param aExtend continue selection
603 // TODO: replace with `MOZ_CAN_RUN_SCRIPT`.
604 MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult
IntraLineMove(bool aForward
,
608 * CreateRangeExtendedToNextGraphemeClusterBoundary() returns range which is
609 * extended from normal selection range to start of next grapheme cluster
612 template <typename RangeType
>
613 MOZ_CAN_RUN_SCRIPT
mozilla::Result
<RefPtr
<RangeType
>, nsresult
>
614 CreateRangeExtendedToNextGraphemeClusterBoundary() {
615 return CreateRangeExtendedToSomewhere
<RangeType
>(eDirNext
, eSelectCluster
,
620 * CreateRangeExtendedToPreviousCharacterBoundary() returns range which is
621 * extended from normal selection range to start of previous character
624 template <typename RangeType
>
625 MOZ_CAN_RUN_SCRIPT
mozilla::Result
<RefPtr
<RangeType
>, nsresult
>
626 CreateRangeExtendedToPreviousCharacterBoundary() {
627 return CreateRangeExtendedToSomewhere
<RangeType
>(
628 eDirPrevious
, eSelectCharacter
, eLogical
);
632 * CreateRangeExtendedToNextWordBoundary() returns range which is
633 * extended from normal selection range to start of next word boundary.
635 template <typename RangeType
>
636 MOZ_CAN_RUN_SCRIPT
mozilla::Result
<RefPtr
<RangeType
>, nsresult
>
637 CreateRangeExtendedToNextWordBoundary() {
638 return CreateRangeExtendedToSomewhere
<RangeType
>(eDirNext
, eSelectWord
,
643 * CreateRangeExtendedToPreviousWordBoundary() returns range which is
644 * extended from normal selection range to start of previous word boundary.
646 template <typename RangeType
>
647 MOZ_CAN_RUN_SCRIPT
mozilla::Result
<RefPtr
<RangeType
>, nsresult
>
648 CreateRangeExtendedToPreviousWordBoundary() {
649 return CreateRangeExtendedToSomewhere
<RangeType
>(eDirPrevious
, eSelectWord
,
654 * CreateRangeExtendedToPreviousHardLineBreak() returns range which is
655 * extended from normal selection range to previous hard line break.
657 template <typename RangeType
>
658 MOZ_CAN_RUN_SCRIPT
mozilla::Result
<RefPtr
<RangeType
>, nsresult
>
659 CreateRangeExtendedToPreviousHardLineBreak() {
660 return CreateRangeExtendedToSomewhere
<RangeType
>(
661 eDirPrevious
, eSelectBeginLine
, eLogical
);
665 * CreateRangeExtendedToNextHardLineBreak() returns range which is extended
666 * from normal selection range to next hard line break.
668 template <typename RangeType
>
669 MOZ_CAN_RUN_SCRIPT
mozilla::Result
<RefPtr
<RangeType
>, nsresult
>
670 CreateRangeExtendedToNextHardLineBreak() {
671 return CreateRangeExtendedToSomewhere
<RangeType
>(eDirNext
, eSelectEndLine
,
675 /** Sets/Gets The display selection enum.
677 void SetDisplaySelection(int16_t aState
) { mDisplaySelection
= aState
; }
678 int16_t GetDisplaySelection() const { return mDisplaySelection
; }
681 * This method can be used to store the data received during a MouseDown
682 * event so that we can place the caret during the MouseUp event.
684 * @param aMouseEvent the event received by the selection MouseDown
685 * handling method. A nullptr value can be use to tell this method
686 * that any data is storing is no longer valid.
688 void SetDelayedCaretData(mozilla::WidgetMouseEvent
* aMouseEvent
);
691 * Get the delayed MouseDown event data necessary to place the
692 * caret during MouseUp processing.
694 * @return a pointer to the event received
695 * by the selection during MouseDown processing. It can be nullptr
696 * if the data is no longer valid.
698 bool HasDelayedCaretData() const { return mDelayedMouseEvent
.mIsValid
; }
699 bool IsShiftDownInDelayedCaretData() const {
700 NS_ASSERTION(mDelayedMouseEvent
.mIsValid
, "No valid delayed caret data");
701 return mDelayedMouseEvent
.mIsShift
;
703 uint32_t GetClickCountInDelayedCaretData() const {
704 NS_ASSERTION(mDelayedMouseEvent
.mIsValid
, "No valid delayed caret data");
705 return mDelayedMouseEvent
.mClickCount
;
708 bool MouseDownRecorded() const {
709 return !GetDragState() && HasDelayedCaretData() &&
710 GetClickCountInDelayedCaretData() < 2;
714 * Get the content node that limits the selection
716 * When searching up a nodes for parents, as in a text edit field
717 * in an browser page, we must stop at this node else we reach into the
718 * parent page, which is very bad!
720 nsIContent
* GetLimiter() const { return mLimiters
.mLimiter
; }
722 nsIContent
* GetAncestorLimiter() const { return mLimiters
.mAncestorLimiter
; }
723 MOZ_CAN_RUN_SCRIPT_BOUNDARY
void SetAncestorLimiter(nsIContent
* aLimiter
);
726 * GetPrevNextBidiLevels will return the frames and associated Bidi levels of
727 * the characters logically before and after a (collapsed) selection.
729 * @param aNode is the node containing the selection
730 * @param aContentOffset is the offset of the selection in the node
732 * If true, look across line boundaries.
733 * If false, behave as if there were base-level frames at line edges.
735 * @return A struct holding the before/after frame and the before/after
738 * At the beginning and end of each line there is assumed to be a frame with
739 * Bidi level equal to the paragraph embedding level.
741 * In these cases the before frame and after frame respectively will be
744 nsPrevNextBidiLevels
GetPrevNextBidiLevels(nsIContent
* aNode
,
745 uint32_t aContentOffset
,
746 bool aJumpLines
) const;
749 * GetFrameFromLevel will scan in a given direction
750 * until it finds a frame with a Bidi level less than or equal to a given
751 * level. It will return the last frame before this.
753 * @param aPresContext is the context to use
754 * @param aFrameIn is the frame to start from
755 * @param aDirection is the direction to scan
756 * @param aBidiLevel is the level to search for
757 * @param aFrameOut will hold the frame returned
759 nsresult
GetFrameFromLevel(nsIFrame
* aFrameIn
, nsDirection aDirection
,
760 mozilla::intl::BidiEmbeddingLevel aBidiLevel
,
761 nsIFrame
** aFrameOut
) const;
764 * MaintainSelection will track the normal selection as being "sticky".
765 * Dragging or extending selection will never allow for a subset
766 * (or the whole) of the maintained selection to become unselected.
767 * Primary use: double click selecting then dragging on second click
769 * @param aAmount the initial amount of text selected (word, line or
770 * paragraph). For "line", use eSelectBeginLine.
772 nsresult
MaintainSelection(nsSelectionAmount aAmount
= eSelectNoAmount
);
774 MOZ_CAN_RUN_SCRIPT nsresult
ConstrainFrameAndPointToAnchorSubtree(
775 nsIFrame
* aFrame
, const nsPoint
& aPoint
, nsIFrame
** aRetFrame
,
776 nsPoint
& aRetPoint
) const;
779 * @param aPresShell is the parameter to be used for most of the other calls
782 * @param aLimiter limits the selection to nodes with aLimiter parents
784 * @param aAccessibleCaretEnabled true if we should enable the accessible
787 nsFrameSelection(mozilla::PresShell
* aPresShell
, nsIContent
* aLimiter
,
788 bool aAccessibleCaretEnabled
);
791 * @param aRequesterFuncName function name which wants to start the batch.
792 * This won't be stored nor exposed to selection listeners etc, used only for
795 void StartBatchChanges(const char* aRequesterFuncName
);
798 * @param aRequesterFuncName function name which wants to end the batch.
799 * This won't be stored nor exposed to selection listeners etc, used only for
801 * @param aReasons potentially multiple of the reasons defined in
802 * nsISelectionListener.idl
804 MOZ_CAN_RUN_SCRIPT_BOUNDARY
void EndBatchChanges(
805 const char* aRequesterFuncName
,
806 int16_t aReasons
= nsISelectionListener::NO_REASON
);
808 mozilla::PresShell
* GetPresShell() const { return mPresShell
; }
810 void DisconnectFromPresShell();
811 MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult
ClearNormalSelection();
813 // Table selection support.
814 static nsITableCellLayout
* GetCellLayout(const nsIContent
* aCellContent
);
819 // TODO: in case an error is returned, it sometimes refers to a programming
820 // error, in other cases to runtime errors. This deserves to be cleaned up.
821 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT nsresult
822 TakeFocus(nsIContent
& aNewFocus
, uint32_t aContentOffset
,
823 uint32_t aContentEndOffset
, CaretAssociateHint aHint
,
824 FocusMode aFocusMode
);
827 * After moving the caret, its Bidi level is set according to the following
830 * After moving over a character with left/right arrow, set to the Bidi level
831 * of the last moved over character. After Home and End, set to the paragraph
832 * embedding level. After up/down arrow, PageUp/Down, set to the lower level
833 * of the 2 surrounding characters. After mouse click, set to the level of the
836 * The following two methods use GetPrevNextBidiLevels to determine the new
837 * Bidi level. BidiLevelFromMove is called when the caret is moved in response
838 * to a keyboard event
840 * @param aPresShell is the presentation shell
841 * @param aNode is the content node
842 * @param aContentOffset is the new caret position, as an offset into aNode
843 * @param aAmount is the amount of the move that gave the caret its new
845 * @param aHint is the hint indicating in what logical direction the caret
848 void BidiLevelFromMove(mozilla::PresShell
* aPresShell
, nsIContent
* aNode
,
849 uint32_t aContentOffset
, nsSelectionAmount aAmount
,
850 CaretAssociateHint aHint
);
852 * BidiLevelFromClick is called when the caret is repositioned by clicking the
855 * @param aNode is the content node
856 * @param aContentOffset is the new caret position, as an offset into aNode
858 void BidiLevelFromClick(nsIContent
* aNewFocus
, uint32_t aContentOffset
);
860 static nsPrevNextBidiLevels
GetPrevNextBidiLevels(nsIContent
* aNode
,
861 uint32_t aContentOffset
,
862 CaretAssociateHint aHint
,
866 * @param aReasons potentially multiple of the reasons defined in
867 * nsISelectionListener.idl.
869 void SetChangeReasons(int16_t aReasons
) {
870 mSelectionChangeReasons
= aReasons
;
874 * @param aReasons potentially multiple of the reasons defined in
875 * nsISelectionListener.idl.
877 void AddChangeReasons(int16_t aReasons
) {
878 mSelectionChangeReasons
|= aReasons
;
882 * @return potentially multiple of the reasons defined in
883 * nsISelectionListener.idl.
885 int16_t PopChangeReasons() {
886 int16_t retval
= mSelectionChangeReasons
;
887 mSelectionChangeReasons
= nsISelectionListener::NO_REASON
;
891 nsSelectionAmount
GetCaretMoveAmount() { return mCaretMoveAmount
; }
893 bool IsUserSelectionReason() const {
894 return (mSelectionChangeReasons
&
895 (nsISelectionListener::DRAG_REASON
|
896 nsISelectionListener::MOUSEDOWN_REASON
|
897 nsISelectionListener::MOUSEUP_REASON
|
898 nsISelectionListener::KEYPRESS_REASON
)) !=
899 nsISelectionListener::NO_REASON
;
902 friend class mozilla::dom::Selection
;
903 friend class mozilla::SelectionChangeEventDispatcher
;
904 friend struct mozilla::AutoPrepareFocusRange
;
907 // Whether MoveCaret should use logical or visual movement,
908 // or follow the bidi.edit.caret_movement_style preference.
909 enum CaretMovementStyle
{ eLogical
, eVisual
, eUsePrefStyle
};
910 MOZ_CAN_RUN_SCRIPT nsresult
MoveCaret(nsDirection aDirection
,
911 bool aContinueSelection
,
912 nsSelectionAmount aAmount
,
913 CaretMovementStyle aMovementStyle
);
916 * PeekOffsetForCaretMove() only peek offset for caret move. I.e., won't
917 * change selection ranges nor bidi information.
919 mozilla::Result
<mozilla::PeekOffsetStruct
, nsresult
> PeekOffsetForCaretMove(
920 nsDirection aDirection
, bool aContinueSelection
,
921 const nsSelectionAmount aAmount
, CaretMovementStyle aMovementStyle
,
922 const nsPoint
& aDesiredCaretPos
) const;
925 * CreateRangeExtendedToSomewhere() is common method to implement
926 * CreateRangeExtendedTo*(). This method creates a range extended from
927 * normal selection range.
929 template <typename RangeType
>
930 MOZ_CAN_RUN_SCRIPT
mozilla::Result
<RefPtr
<RangeType
>, nsresult
>
931 CreateRangeExtendedToSomewhere(nsDirection aDirection
,
932 const nsSelectionAmount aAmount
,
933 CaretMovementStyle aMovementStyle
);
936 * IsIntraLineCaretMove() is a helper method for PeekOffsetForCaretMove()
937 * and CreateRangeExtendedToSomwhereFromNormalSelection(). This returns
938 * whether aAmount is intra line move or is crossing hard line break.
939 * This returns error if aMount is not supported by the methods.
941 static mozilla::Result
<bool, nsresult
> IsIntraLineCaretMove(
942 nsSelectionAmount aAmount
) {
944 case eSelectCharacter
:
947 case eSelectWordNoSpace
:
948 case eSelectBeginLine
:
954 return mozilla::Err(NS_ERROR_FAILURE
);
958 void InvalidateDesiredCaretPos(); // do not listen to mDesiredCaretPos.mValue
959 // you must get another.
961 bool IsBatching() const { return mBatching
.mCounter
> 0; }
963 void SetChangesDuringBatchingFlag() {
964 MOZ_ASSERT(mBatching
.mCounter
> 0);
966 mBatching
.mChangesDuringBatching
= true;
969 // nsFrameSelection may get deleted when calling this,
970 // so remember to use nsCOMPtr when needed.
972 nsresult
NotifySelectionListeners(mozilla::SelectionType aSelectionType
);
974 static nsresult
GetCellIndexes(const nsIContent
* aCell
, int32_t& aRowIndex
,
977 static nsIContent
* GetFirstCellNodeInRange(const nsRange
* aRange
);
978 // Returns non-null table if in same table, null otherwise
979 static nsIContent
* IsInSameTable(const nsIContent
* aContent1
,
980 const nsIContent
* aContent2
);
982 static nsIContent
* GetParentTable(const nsIContent
* aCellNode
);
984 ////////////BEGIN nsFrameSelection members
986 RefPtr
<mozilla::dom::Selection
>
987 mDomSelections
[sizeof(mozilla::kPresentSelectionTypes
) /
988 sizeof(mozilla::SelectionType
)];
991 mozilla::CompactPair
<RefPtr
<nsAtom
>, RefPtr
<mozilla::dom::Selection
>>>
992 mHighlightSelections
;
994 struct TableSelection
{
995 // Get our first range, if its first selected node is a cell. If this does
996 // not return null, then the first node in the returned range is a cell
997 // (according to GetFirstCellNodeInRange).
998 nsRange
* GetFirstCellRange(const mozilla::dom::Selection
& aNormalSelection
);
1000 // Get our next range, if its first selected node is a cell. If this does
1001 // not return null, then the first node in the returned range is a cell
1002 // (according to GetFirstCellNodeInRange).
1003 nsRange
* GetNextCellRange(const mozilla::dom::Selection
& aNormalSelection
);
1005 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT nsresult
1006 HandleSelection(nsINode
* aParentContent
, int32_t aContentOffset
,
1007 mozilla::TableSelectionMode aTarget
,
1008 mozilla::WidgetMouseEvent
* aMouseEvent
, bool aDragState
,
1009 mozilla::dom::Selection
& aNormalSelection
);
1012 * @return the closest inclusive table cell ancestor
1013 * (https://dom.spec.whatwg.org/#concept-tree-inclusive-ancestor) of
1014 * aContent, if it is actively editable.
1016 static nsINode
* IsContentInActivelyEditableTableCell(
1017 nsPresContext
* aContext
, nsIContent
* aContent
);
1019 // TODO: annotate this with `MOZ_CAN_RUN_SCRIPT` instead.
1020 MOZ_CAN_RUN_SCRIPT_BOUNDARY
1021 nsresult
SelectBlockOfCells(nsIContent
* aStartCell
, nsIContent
* aEndCell
,
1022 mozilla::dom::Selection
& aNormalSelection
);
1024 nsresult
SelectRowOrColumn(nsIContent
* aCellContent
,
1025 mozilla::dom::Selection
& aNormalSelection
);
1027 MOZ_CAN_RUN_SCRIPT nsresult
1028 UnselectCells(const nsIContent
* aTable
, int32_t aStartRowIndex
,
1029 int32_t aStartColumnIndex
, int32_t aEndRowIndex
,
1030 int32_t aEndColumnIndex
, bool aRemoveOutsideOfCellRange
,
1031 mozilla::dom::Selection
& aNormalSelection
);
1034 mClosestInclusiveTableCellAncestor
; // used to snap to table selection
1035 nsCOMPtr
<nsIContent
> mStartSelectedCell
;
1036 nsCOMPtr
<nsIContent
> mEndSelectedCell
;
1037 nsCOMPtr
<nsIContent
> mAppendStartSelectedCell
;
1038 nsCOMPtr
<nsIContent
> mUnselectCellOnMouseUp
;
1039 mozilla::TableSelectionMode mMode
= mozilla::TableSelectionMode::None
;
1040 int32_t mSelectedCellIndex
= 0;
1041 bool mDragSelectingCells
= false;
1044 struct MOZ_STACK_CLASS FirstAndLastCell
{
1045 nsCOMPtr
<nsIContent
> mFirst
;
1046 nsCOMPtr
<nsIContent
> mLast
;
1049 mozilla::Result
<FirstAndLastCell
, nsresult
>
1050 FindFirstAndLastCellOfRowOrColumn(const nsIContent
& aCellContent
) const;
1052 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult
HandleDragSelecting(
1053 mozilla::TableSelectionMode aTarget
, nsIContent
* aChildContent
,
1054 const mozilla::WidgetMouseEvent
* aMouseEvent
,
1055 mozilla::dom::Selection
& aNormalSelection
);
1057 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT nsresult
HandleMouseUpOrDown(
1058 mozilla::TableSelectionMode aTarget
, bool aDragState
,
1059 nsIContent
* aChildContent
, nsINode
* aParentContent
,
1060 int32_t aContentOffset
, const mozilla::WidgetMouseEvent
* aMouseEvent
,
1061 mozilla::dom::Selection
& aNormalSelection
);
1063 class MOZ_STACK_CLASS RowAndColumnRelation
;
1066 TableSelection mTableSelection
;
1068 struct MaintainedRange
{
1070 * Ensure anchor and focus of aNormalSelection are ordered appropriately
1071 * relative to the maintained range.
1073 MOZ_CAN_RUN_SCRIPT
void AdjustNormalSelection(
1074 const nsIContent
* aContent
, int32_t aOffset
,
1075 mozilla::dom::Selection
& aNormalSelection
) const;
1078 * @param aStopAtScroller If yes, this will
1079 * set `PeekOffsetOption::StopAtScroller`.
1081 enum class StopAtScroller
: bool { No
, Yes
};
1082 void AdjustContentOffsets(nsIFrame::ContentOffsets
& aOffsets
,
1083 StopAtScroller aStopAtScroller
) const;
1085 void MaintainAnchorFocusRange(
1086 const mozilla::dom::Selection
& aNormalSelection
,
1087 nsSelectionAmount aAmount
);
1089 RefPtr
<nsRange
> mRange
;
1090 nsSelectionAmount mAmount
= eSelectNoAmount
;
1093 MaintainedRange mMaintainedRange
;
1096 uint32_t mCounter
= 0;
1097 bool mChangesDuringBatching
= false;
1103 // Limit selection navigation to a child of this node.
1104 nsCOMPtr
<nsIContent
> mLimiter
;
1105 // Limit selection navigation to a descendant of this node.
1106 nsCOMPtr
<nsIContent
> mAncestorLimiter
;
1111 mozilla::PresShell
* mPresShell
= nullptr;
1112 // Reasons for notifications of selection changing.
1113 // Can be multiple of the reasons defined in nsISelectionListener.idl.
1114 int16_t mSelectionChangeReasons
= nsISelectionListener::NO_REASON
;
1115 // For visual display purposes.
1116 int16_t mDisplaySelection
= nsISelectionController::SELECTION_OFF
;
1117 nsSelectionAmount mCaretMoveAmount
= eSelectNoAmount
;
1120 // Hint to tell if the selection is at the end of this line or beginning of
1122 CaretAssociateHint mHint
= mozilla::CARET_ASSOCIATE_BEFORE
;
1123 mozilla::intl::BidiEmbeddingLevel mBidiLevel
= BIDI_LEVEL_UNDEFINED
;
1125 bool IsVisualMovement(bool aContinueSelection
,
1126 CaretMovementStyle aMovementStyle
) const;
1131 mozilla::intl::BidiEmbeddingLevel mKbdBidiLevel
=
1132 mozilla::intl::BidiEmbeddingLevel::LTR();
1134 class DesiredCaretPos
{
1136 // the position requested by the Key Handling for up down
1137 nsresult
FetchPos(nsPoint
& aDesiredCaretPos
,
1138 const mozilla::PresShell
& aPresShell
,
1139 mozilla::dom::Selection
& aNormalSelection
) const;
1141 void Set(const nsPoint
& aPos
);
1145 bool mIsSet
= false;
1151 DesiredCaretPos mDesiredCaretPos
;
1153 struct DelayedMouseEvent
{
1154 bool mIsValid
= false;
1155 // These values are not used since they are only valid when mIsValid is
1156 // true, and setting mIsValid always overrides these values.
1157 bool mIsShift
= false;
1158 uint32_t mClickCount
= 0;
1161 DelayedMouseEvent mDelayedMouseEvent
;
1163 bool mDragState
= false; // for drag purposes
1164 bool mAccessibleCaretEnabled
= false;
1166 // Records if a selection was created by doubleclicking a word.
1167 // This information is needed later on to determine if a leading
1168 // or trailing whitespace needs to be removed as well to achieve
1169 // native behaviour on macOS.
1170 bool mIsDoubleClickSelection
{false};
1174 * Selection Batcher class that supports multiple FrameSelections.
1176 class MOZ_STACK_CLASS AutoFrameSelectionBatcher
{
1178 explicit AutoFrameSelectionBatcher(const char* aFunctionName
,
1179 size_t aEstimatedSize
= 1)
1180 : mFunctionName(aFunctionName
) {
1181 mFrameSelections
.SetCapacity(aEstimatedSize
);
1183 ~AutoFrameSelectionBatcher() {
1184 for (const auto& frameSelection
: mFrameSelections
) {
1185 frameSelection
->EndBatchChanges(mFunctionName
);
1188 void AddFrameSelection(nsFrameSelection
* aFrameSelection
) {
1189 if (!aFrameSelection
) {
1192 aFrameSelection
->StartBatchChanges(mFunctionName
);
1193 mFrameSelections
.AppendElement(aFrameSelection
);
1197 const char* mFunctionName
;
1198 AutoTArray
<RefPtr
<nsFrameSelection
>, 1> mFrameSelections
;
1201 #endif /* nsFrameSelection_h___ */