Backed out changeset 496886cb30a5 (bug 1867152) for bc failures on browser_user_input...
[gecko.git] / layout / generic / nsFrameSelection.h
blobc57b143905b3c7965abc7f41951e068c0e98b8bb
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___
10 #include <stdint.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"
22 #include "nsIFrame.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"
31 class nsRange;
33 #define BIDI_LEVEL_UNDEFINED mozilla::intl::BidiEmbeddingLevel(0x80)
35 //----------------------------------------------------------------------
37 // Selection interface
39 struct SelectionDetails {
40 SelectionDetails()
41 : mStart(), mEnd(), mSelectionType(mozilla::SelectionType::eInvalid) {
42 MOZ_COUNT_CTOR(SelectionDetails);
44 MOZ_COUNTED_DTOR(SelectionDetails)
46 int32_t mStart;
47 int32_t mEnd;
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)
58 #endif
59 mozilla::Maybe<nscolor> mForegroundColor;
60 mozilla::Maybe<nscolor> mBackgroundColor;
61 mozilla::Maybe<nscolor> mAltForegroundColor;
62 mozilla::Maybe<nscolor> mAltBackgroundColor;
65 namespace mozilla {
66 class PresShell;
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.
74 namespace mozilla {
76 enum class PeekOffsetOption : uint16_t {
77 // Whether to allow jumping across line boundaries.
79 // Used with: eSelectCharacter, eSelectWord.
80 JumpLines,
82 // Whether we should preserve or trim spaces at begin/end of content
83 PreserveSpaces,
85 // Whether to stop when reaching a scroll view boundary.
87 // Used with: eSelectCharacter, eSelectWord, eSelectLine.
88 StopAtScroller,
90 // Whether to stop when reaching a placeholder frame.
91 StopAtPlaceholder,
93 // Whether the peeking is done in response to a keyboard action.
95 // Used with: eSelectWord.
96 IsKeyboardSelect,
98 // Whether bidi caret behavior is visual (set) or logical (unset).
100 // Used with: eSelectCharacter, eSelectWord, eSelectBeginLine, eSelectEndLine.
101 Visual,
103 // Whether the selection is being extended or moved.
104 Extend,
106 // If true, the offset has to end up in an editable node, otherwise we'll keep
107 // searching.
108 ForceEditableRegion,
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;
202 namespace mozilla {
203 class SelectionChangeEventDispatcher;
204 namespace dom {
205 class Highlight;
206 class Selection;
207 } // namespace dom
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 {
226 public:
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
238 as generated. */
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
245 * things
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
253 * and end points
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);
264 public:
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);
334 public:
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
375 * the mouse
377 * @param aPoint is relative to aFrame.
379 * @param aDelay is the timer's interval.
381 MOZ_CAN_RUN_SCRIPT
382 nsresult StartAutoScrollTimer(nsIFrame* aFrame, const nsPoint& aPoint,
383 uint32_t aDelay);
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
409 MOZ_CAN_RUN_SCRIPT
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
496 * that frame.
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,
533 nsIFrame* aFrame,
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,
562 int16_t aAmount,
563 bool aExtend);
566 * CharacterMove will generally be called from the nsiselectioncontroller
567 * implementations. the effect being the selection will move one character
568 * left or right.
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,
574 bool aExtend);
577 * WordMove will generally be called from the nsiselectioncontroller
578 * implementations. the effect being the selection will move one word left or
579 * right.
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
589 * down.
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
599 * end of line
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,
605 bool aExtend);
608 * CreateRangeExtendedToNextGraphemeClusterBoundary() returns range which is
609 * extended from normal selection range to start of next grapheme cluster
610 * boundary.
612 template <typename RangeType>
613 MOZ_CAN_RUN_SCRIPT mozilla::Result<RefPtr<RangeType>, nsresult>
614 CreateRangeExtendedToNextGraphemeClusterBoundary() {
615 return CreateRangeExtendedToSomewhere<RangeType>(eDirNext, eSelectCluster,
616 eLogical);
620 * CreateRangeExtendedToPreviousCharacterBoundary() returns range which is
621 * extended from normal selection range to start of previous character
622 * boundary.
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,
639 eLogical);
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,
650 eLogical);
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,
672 eLogical);
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
731 * @param aJumpLines
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
736 * level.
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
742 * nullptr.
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
780 * for callbacks etc
782 * @param aLimiter limits the selection to nodes with aLimiter parents
784 * @param aAccessibleCaretEnabled true if we should enable the accessible
785 * caret.
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
793 * logging.
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
800 * logging.
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);
816 private:
817 ~nsFrameSelection();
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
828 * rules:
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
834 * current frame.
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
844 * position
845 * @param aHint is the hint indicating in what logical direction the caret
846 * moved
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
853 * mouse
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,
863 bool aJumpLines);
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;
888 return retval;
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;
906 /*HELPER METHODS*/
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) {
943 switch (aAmount) {
944 case eSelectCharacter:
945 case eSelectCluster:
946 case eSelectWord:
947 case eSelectWordNoSpace:
948 case eSelectBeginLine:
949 case eSelectEndLine:
950 return true;
951 case eSelectLine:
952 return false;
953 default:
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.
971 MOZ_CAN_RUN_SCRIPT
972 nsresult NotifySelectionListeners(mozilla::SelectionType aSelectionType);
974 static nsresult GetCellIndexes(const nsIContent* aCell, int32_t& aRowIndex,
975 int32_t& aColIndex);
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);
981 // Might return null
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)];
990 nsTArray<
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);
1033 nsCOMPtr<nsINode>
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;
1043 private:
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;
1095 struct Batching {
1096 uint32_t mCounter = 0;
1097 bool mChangesDuringBatching = false;
1100 Batching mBatching;
1102 struct Limiters {
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;
1109 Limiters mLimiters;
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;
1119 struct Caret {
1120 // Hint to tell if the selection is at the end of this line or beginning of
1121 // next.
1122 CaretAssociateHint mHint = mozilla::CARET_ASSOCIATE_BEFORE;
1123 mozilla::intl::BidiEmbeddingLevel mBidiLevel = BIDI_LEVEL_UNDEFINED;
1125 bool IsVisualMovement(bool aContinueSelection,
1126 CaretMovementStyle aMovementStyle) const;
1129 Caret mCaret;
1131 mozilla::intl::BidiEmbeddingLevel mKbdBidiLevel =
1132 mozilla::intl::BidiEmbeddingLevel::LTR();
1134 class DesiredCaretPos {
1135 public:
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);
1143 void Invalidate();
1145 bool mIsSet = false;
1147 private:
1148 nsPoint mValue;
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 {
1177 public:
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) {
1190 return;
1192 aFrameSelection->StartBatchChanges(mFunctionName);
1193 mFrameSelections.AppendElement(aFrameSelection);
1196 private:
1197 const char* mFunctionName;
1198 AutoTArray<RefPtr<nsFrameSelection>, 1> mFrameSelections;
1201 #endif /* nsFrameSelection_h___ */