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