Coverage Report

Created: 2018-09-25 14:53

/work/obj-fuzz/dist/include/mozilla/dom/Selection.h
Line
Count
Source (jump to first uncovered line)
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 file,
5
 * You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#ifndef mozilla_Selection_h__
8
#define mozilla_Selection_h__
9
10
#include "nsIWeakReference.h"
11
12
#include "mozilla/AccessibleCaretEventHub.h"
13
#include "mozilla/AutoRestore.h"
14
#include "mozilla/RangeBoundary.h"
15
#include "mozilla/SelectionChangeEventDispatcher.h"
16
#include "mozilla/TextRange.h"
17
#include "mozilla/UniquePtr.h"
18
#include "mozilla/WeakPtr.h"
19
#include "nsDirection.h"
20
#include "nsIPresShell.h"  // For ScrollAxis
21
#include "nsISelectionController.h"
22
#include "nsISelectionListener.h"
23
#include "nsRange.h"
24
#include "nsTArrayForwardDeclare.h"
25
#include "nsThreadUtils.h"
26
#include "nsWrapperCache.h"
27
28
struct CachedOffsetForFrame;
29
class nsAutoScrollTimer;
30
class nsIContentIterator;
31
class nsIDocument;
32
class nsIFrame;
33
class nsFrameSelection;
34
class nsPIDOMWindowOuter;
35
struct SelectionDetails;
36
struct SelectionCustomColors;
37
class nsCopySupport;
38
class nsHTMLCopyEncoder;
39
40
namespace mozilla {
41
class ErrorResult;
42
class HTMLEditor;
43
enum class TableSelection : uint32_t;
44
struct AutoPrepareFocusRange;
45
namespace dom {
46
class DocGroup;
47
} // namespace dom
48
} // namespace mozilla
49
50
struct RangeData
51
{
52
  explicit RangeData(nsRange* aRange)
53
    : mRange(aRange)
54
0
  {}
55
56
  RefPtr<nsRange> mRange;
57
  mozilla::TextRangeStyle mTextRangeStyle;
58
};
59
60
namespace mozilla {
61
namespace dom {
62
63
// Note, the ownership of mozilla::dom::Selection depends on which way the
64
// object is created. When nsFrameSelection has created Selection,
65
// addreffing/releasing the Selection object is aggregated to nsFrameSelection.
66
// Otherwise normal addref/release is used.  This ensures that nsFrameSelection
67
// is never deleted before its Selections.
68
class Selection final : public nsSupportsWeakReference,
69
                        public nsWrapperCache,
70
                        public SupportsWeakPtr<Selection>
71
{
72
protected:
73
  virtual ~Selection();
74
75
public:
76
  Selection();
77
  explicit Selection(nsFrameSelection *aList);
78
79
  MOZ_DECLARE_WEAKREFERENCE_TYPENAME(Selection)
80
81
  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
82
  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Selection)
83
84
  // match this up with EndbatchChanges. will stop ui updates while multiple
85
  // selection methods are called
86
  void StartBatchChanges();
87
88
  // match this up with StartBatchChanges
89
  void EndBatchChanges(int16_t aReason = nsISelectionListener::NO_REASON);
90
91
  /**
92
   * NotifyAutoCopy() starts to notify AutoCopyListener of selection changes.
93
   */
94
  void NotifyAutoCopy()
95
0
  {
96
0
    mNotifyAutoCopy = true;
97
0
  }
98
99
  /**
100
   * MaybeNotifyAccessibleCaretEventHub() starts to notify
101
   * AccessibleCaretEventHub of selection change if aPresShell has it.
102
   */
103
  void MaybeNotifyAccessibleCaretEventHub(nsIPresShell* aPresShell)
104
0
  {
105
0
    if (!mAccessibleCaretEventHub && aPresShell) {
106
0
      mAccessibleCaretEventHub = aPresShell->GetAccessibleCaretEventHub();
107
0
    }
108
0
  }
109
110
  /**
111
   * StopNotifyingAccessibleCaretEventHub() stops notifying
112
   * AccessibleCaretEventHub of selection change.
113
   */
114
  void StopNotifyingAccessibleCaretEventHub()
115
0
  {
116
0
    mAccessibleCaretEventHub = nullptr;
117
0
  }
118
119
  /**
120
   * EnableSelectionChangeEvent() starts to notify
121
   * SelectionChangeEventDispatcher of selection change to dispatch a
122
   * selectionchange event at every selection change.
123
   */
124
  void EnableSelectionChangeEvent()
125
0
  {
126
0
    if (!mSelectionChangeEventDispatcher) {
127
0
      mSelectionChangeEventDispatcher = new SelectionChangeEventDispatcher();
128
0
    }
129
0
  }
130
131
  nsIDocument* GetParentObject() const;
132
  DocGroup* GetDocGroup() const;
133
134
  // utility methods for scrolling the selection into view
135
  nsPresContext* GetPresContext() const;
136
  nsIPresShell* GetPresShell() const;
137
0
  nsFrameSelection* GetFrameSelection() const { return mFrameSelection; }
138
  // Returns a rect containing the selection region, and frame that that
139
  // position is relative to. For SELECTION_ANCHOR_REGION or
140
  // SELECTION_FOCUS_REGION the rect is a zero-width rectangle. For
141
  // SELECTION_WHOLE_SELECTION the rect contains both the anchor and focus
142
  // region rects.
143
  nsIFrame*     GetSelectionAnchorGeometry(SelectionRegion aRegion, nsRect *aRect);
144
  // Returns the position of the region (SELECTION_ANCHOR_REGION or
145
  // SELECTION_FOCUS_REGION only), and frame that that position is relative to.
146
  // The 'position' is a zero-width rectangle.
147
  nsIFrame*     GetSelectionEndPointGeometry(SelectionRegion aRegion, nsRect *aRect);
148
149
  nsresult      PostScrollSelectionIntoViewEvent(
150
                                        SelectionRegion aRegion,
151
                                        int32_t aFlags,
152
                                        nsIPresShell::ScrollAxis aVertical,
153
                                        nsIPresShell::ScrollAxis aHorizontal);
154
  enum {
155
    SCROLL_SYNCHRONOUS = 1<<1,
156
    SCROLL_FIRST_ANCESTOR_ONLY = 1<<2,
157
    SCROLL_DO_FLUSH = 1<<3,  // only matters if SCROLL_SYNCHRONOUS is passed too
158
    SCROLL_OVERFLOW_HIDDEN = 1<<5,
159
    SCROLL_FOR_CARET_MOVE = 1<<6
160
  };
161
  // If aFlags doesn't contain SCROLL_SYNCHRONOUS, then we'll flush when
162
  // the scroll event fires so we make sure to scroll to the right place.
163
  // Otherwise, if SCROLL_DO_FLUSH is also in aFlags, then this method will
164
  // flush layout and you MUST hold a strong ref on 'this' for the duration
165
  // of this call.  This might destroy arbitrary layout objects.
166
  nsresult      ScrollIntoView(SelectionRegion aRegion,
167
                               nsIPresShell::ScrollAxis aVertical =
168
                                 nsIPresShell::ScrollAxis(),
169
                               nsIPresShell::ScrollAxis aHorizontal =
170
                                 nsIPresShell::ScrollAxis(),
171
                               int32_t aFlags = 0);
172
  nsresult      SubtractRange(RangeData* aRange, nsRange* aSubtract,
173
                              nsTArray<RangeData>* aOutput);
174
  /**
175
   * AddItem adds aRange to this Selection.  If mUserInitiated is true,
176
   * then aRange is first scanned for -moz-user-select:none nodes and split up
177
   * into multiple ranges to exclude those before adding the resulting ranges
178
   * to this Selection.
179
   */
180
  nsresult      AddItem(nsRange* aRange, int32_t* aOutIndex, bool aNoStartSelect = false);
181
  nsresult      RemoveItem(nsRange* aRange);
182
  nsresult      RemoveCollapsedRanges();
183
  nsresult      Clear(nsPresContext* aPresContext);
184
  nsresult      Collapse(nsINode* aContainer, int32_t aOffset)
185
0
  {
186
0
    if (!aContainer) {
187
0
      return NS_ERROR_INVALID_ARG;
188
0
    }
189
0
    return Collapse(RawRangeBoundary(aContainer, aOffset));
190
0
  }
191
  nsresult      Collapse(const RawRangeBoundary& aPoint)
192
0
  {
193
0
    ErrorResult result;
194
0
    Collapse(aPoint, result);
195
0
    return result.StealNSResult();
196
0
  }
197
198
  MOZ_CAN_RUN_SCRIPT_BOUNDARY
199
  nsresult      Extend(nsINode* aContainer, int32_t aOffset);
200
201
  nsRange*      GetRangeAt(int32_t aIndex) const;
202
203
  // Get the anchor-to-focus range if we don't care which end is
204
  // anchor and which end is focus.
205
0
  const nsRange* GetAnchorFocusRange() const {
206
0
    return mAnchorFocusRange;
207
0
  }
208
209
0
  nsDirection  GetDirection(){return mDirection;}
210
0
  void         SetDirection(nsDirection aDir){mDirection = aDir;}
211
  nsresult     SetAnchorFocusToRange(nsRange *aRange);
212
  void         ReplaceAnchorFocusRange(nsRange *aRange);
213
  void         AdjustAnchorFocusForMultiRange(nsDirection aDirection);
214
215
  nsresult GetPrimaryFrameForAnchorNode(nsIFrame** aReturnFrame);
216
  nsresult GetPrimaryFrameForFocusNode(nsIFrame** aReturnFrame,
217
                                       int32_t* aOffset, bool aVisual);
218
219
  UniquePtr<SelectionDetails> LookUpSelection(
220
    nsIContent* aContent,
221
    int32_t aContentOffset,
222
    int32_t aContentLength,
223
    UniquePtr<SelectionDetails> aDetailsHead,
224
    SelectionType aSelectionType,
225
    bool aSlowCheck);
226
227
  NS_IMETHOD   Repaint(nsPresContext* aPresContext);
228
229
  // Note: StartAutoScrollTimer might destroy arbitrary frames etc.
230
  nsresult     StartAutoScrollTimer(nsIFrame* aFrame,
231
                                    const nsPoint& aPoint,
232
                                    uint32_t aDelay);
233
234
  nsresult     StopAutoScrollTimer();
235
236
  JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
237
238
  // WebIDL methods
239
  nsINode* GetAnchorNode()
240
0
  {
241
0
    const RangeBoundary& anchor = AnchorRef();
242
0
    return anchor.IsSet() ? anchor.Container() : nullptr;
243
0
  }
244
  uint32_t AnchorOffset()
245
0
  {
246
0
    const RangeBoundary& anchor = AnchorRef();
247
0
    return anchor.IsSet() ? anchor.Offset() : 0;
248
0
  }
249
  nsINode* GetFocusNode()
250
0
  {
251
0
    const RangeBoundary& focus = FocusRef();
252
0
    return focus.IsSet() ? focus.Container() : nullptr;
253
0
  }
254
  uint32_t FocusOffset()
255
0
  {
256
0
    const RangeBoundary& focus = FocusRef();
257
0
    return focus.IsSet() ? focus.Offset() : 0;
258
0
  }
259
260
  nsIContent* GetChildAtAnchorOffset()
261
0
  {
262
0
    const RangeBoundary& anchor = AnchorRef();
263
0
    return anchor.IsSet() ? anchor.GetChildAtOffset() : nullptr;
264
0
  }
265
  nsIContent* GetChildAtFocusOffset()
266
0
  {
267
0
    const RangeBoundary& focus = FocusRef();
268
0
    return focus.IsSet() ? focus.GetChildAtOffset() : nullptr;
269
0
  }
270
271
  const RangeBoundary& AnchorRef();
272
  const RangeBoundary& FocusRef();
273
274
  /*
275
   * IsCollapsed -- is the whole selection just one point, or unset?
276
   */
277
  bool IsCollapsed() const
278
0
  {
279
0
    uint32_t cnt = mRanges.Length();
280
0
    if (cnt == 0) {
281
0
      return true;
282
0
    }
283
0
284
0
    if (cnt != 1) {
285
0
      return false;
286
0
    }
287
0
288
0
    return mRanges[0].mRange->Collapsed();
289
0
  }
290
291
  // *JS() methods are mapped to Selection.*().
292
  // They may move focus only when the range represents normal selection.
293
  // These methods shouldn't be used by non-JS callers.
294
  void CollapseJS(nsINode* aContainer, uint32_t aOffset,
295
                  mozilla::ErrorResult& aRv);
296
  void CollapseToStartJS(mozilla::ErrorResult& aRv);
297
  void CollapseToEndJS(mozilla::ErrorResult& aRv);
298
299
  MOZ_CAN_RUN_SCRIPT_BOUNDARY
300
  void ExtendJS(nsINode& aContainer, uint32_t aOffset,
301
                mozilla::ErrorResult& aRv);
302
303
  void SelectAllChildrenJS(nsINode& aNode, mozilla::ErrorResult& aRv);
304
305
  /**
306
   * Deletes this selection from document the nodes belong to.
307
   */
308
  void DeleteFromDocument(mozilla::ErrorResult& aRv);
309
310
  uint32_t RangeCount() const
311
0
  {
312
0
    return mRanges.Length();
313
0
  }
314
315
  void GetType(nsAString& aOutType) const;
316
317
  nsRange* GetRangeAt(uint32_t aIndex, mozilla::ErrorResult& aRv);
318
  void AddRangeJS(nsRange& aRange, mozilla::ErrorResult& aRv);
319
320
  MOZ_CAN_RUN_SCRIPT_BOUNDARY
321
  void RemoveRange(nsRange& aRange, mozilla::ErrorResult& aRv);
322
323
  MOZ_CAN_RUN_SCRIPT_BOUNDARY void RemoveAllRanges(mozilla::ErrorResult& aRv);
324
325
  /**
326
   * RemoveAllRangesTemporarily() is useful if the caller will add one or more
327
   * ranges later.  This tries to cache a removing range if it's possible.
328
   * If a range is not referred by anything else this selection, the range
329
   * can be reused later.  Otherwise, this works as same as RemoveAllRanges().
330
   */
331
  nsresult RemoveAllRangesTemporarily();
332
333
  void Stringify(nsAString& aResult);
334
335
  /**
336
   * Indicates whether the node is part of the selection. If partlyContained
337
   * is true, the function returns true when some part of the node
338
   * is part of the selection. If partlyContained is false, the
339
   * function only returns true when the entire node is part of the selection.
340
   */
341
  bool ContainsNode(nsINode& aNode, bool aPartlyContained, mozilla::ErrorResult& aRv);
342
343
  /**
344
   * Check to see if the given point is contained within the selection area. In
345
   * particular, this iterates through all the rects that make up the selection,
346
   * not just the bounding box, and checks to see if the given point is contained
347
   * in any one of them.
348
   * @param aPoint The point to check, relative to the root frame.
349
   */
350
  bool ContainsPoint(const nsPoint& aPoint);
351
352
  /**
353
   * Modifies the selection.  Note that the parameters are case-insensitive.
354
   *
355
   * @param alter can be one of { "move", "extend" }
356
   *   - "move" collapses the selection to the end of the selection and
357
   *      applies the movement direction/granularity to the collapsed
358
   *      selection.
359
   *   - "extend" leaves the start of the selection unchanged, and applies
360
   *      movement direction/granularity to the end of the selection.
361
   * @param direction can be one of { "forward", "backward", "left", "right" }
362
   * @param granularity can be one of { "character", "word",
363
   *                                    "line", "lineboundary" }
364
   *
365
   * @throws NS_ERROR_NOT_IMPLEMENTED if the granularity is "sentence",
366
   * "sentenceboundary", "paragraph", "paragraphboundary", or
367
   * "documentboundary".  Throws NS_ERROR_INVALID_ARG if alter, direction,
368
   * or granularity has an unrecognized value.
369
   */
370
  void Modify(const nsAString& aAlter, const nsAString& aDirection,
371
              const nsAString& aGranularity, mozilla::ErrorResult& aRv);
372
373
  void SetBaseAndExtentJS(nsINode& aAnchorNode, uint32_t aAnchorOffset,
374
                          nsINode& aFocusNode, uint32_t aFocusOffset,
375
                          mozilla::ErrorResult& aRv);
376
377
  bool GetInterlinePosition(mozilla::ErrorResult& aRv);
378
  void SetInterlinePosition(bool aValue, mozilla::ErrorResult& aRv);
379
380
  Nullable<int16_t> GetCaretBidiLevel(mozilla::ErrorResult& aRv) const;
381
  void SetCaretBidiLevel(const Nullable<int16_t>& aCaretBidiLevel, mozilla::ErrorResult& aRv);
382
383
  void ToStringWithFormat(const nsAString& aFormatType,
384
                          uint32_t aFlags,
385
                          int32_t aWrapColumn,
386
                          nsAString& aReturn,
387
                          mozilla::ErrorResult& aRv);
388
  void AddSelectionListener(nsISelectionListener* aListener);
389
  void RemoveSelectionListener(nsISelectionListener* aListener);
390
391
  RawSelectionType RawType() const
392
0
  {
393
0
    return ToRawSelectionType(mSelectionType);
394
0
  }
395
0
  SelectionType Type() const { return mSelectionType; }
396
397
  void GetRangesForInterval(nsINode& aBeginNode, int32_t aBeginOffset,
398
                            nsINode& aEndNode, int32_t aEndOffset,
399
                            bool aAllowAdjacent,
400
                            nsTArray<RefPtr<nsRange>>& aReturn,
401
                            mozilla::ErrorResult& aRv);
402
403
  void ScrollIntoView(int16_t aRegion, bool aIsSynchronous,
404
                      int16_t aVPercent, int16_t aHPercent,
405
                      mozilla::ErrorResult& aRv);
406
407
  void SetColors(const nsAString& aForeColor, const nsAString& aBackColor,
408
                 const nsAString& aAltForeColor, const nsAString& aAltBackColor,
409
                 mozilla::ErrorResult& aRv);
410
411
  void ResetColors(mozilla::ErrorResult& aRv);
412
413
  /**
414
   * Non-JS callers should use the following
415
   * collapse/collapseToStart/extend/etc methods, instead of the *JS
416
   * versions that bindings call.
417
   */
418
419
  /**
420
   * Collapses the selection to a single point, at the specified offset
421
   * in the given node. When the selection is collapsed, and the content
422
   * is focused and editable, the caret will blink there.
423
   * @param aContainer The given node where the selection will be set
424
   * @param offset      Where in given dom node to place the selection (the offset into the given node)
425
   */
426
  void Collapse(nsINode& aContainer, uint32_t aOffset, ErrorResult& aRv)
427
0
  {
428
0
    Collapse(RawRangeBoundary(&aContainer, aOffset), aRv);
429
0
  }
430
431
  MOZ_CAN_RUN_SCRIPT_BOUNDARY
432
  void Collapse(const RawRangeBoundary& aPoint, ErrorResult& aRv);
433
434
  /**
435
   * Collapses the whole selection to a single point at the start
436
   * of the current selection (irrespective of direction).  If content
437
   * is focused and editable, the caret will blink there.
438
   */
439
  void CollapseToStart(mozilla::ErrorResult& aRv);
440
441
  /**
442
   * Collapses the whole selection to a single point at the end
443
   * of the current selection (irrespective of direction).  If content
444
   * is focused and editable, the caret will blink there.
445
   */
446
  void CollapseToEnd(mozilla::ErrorResult& aRv);
447
448
  /**
449
   * Extends the selection by moving the selection end to the specified node and
450
   * offset, preserving the selection begin position. The new selection end
451
   * result will always be from the anchorNode to the new focusNode, regardless
452
   * of direction.
453
   *
454
   * @param aContainer The node where the selection will be extended to
455
   * @param aOffset    Where in aContainer to place the offset of the new selection end.
456
   */
457
  MOZ_CAN_RUN_SCRIPT_BOUNDARY
458
  void Extend(nsINode& aContainer, uint32_t aOffset, ErrorResult& aRv);
459
460
  MOZ_CAN_RUN_SCRIPT_BOUNDARY
461
  void AddRange(nsRange& aRange, mozilla::ErrorResult& aRv);
462
463
  /**
464
   * Adds all children of the specified node to the selection.
465
   * @param aNode the parent of the children to be added to the selection.
466
   */
467
  MOZ_CAN_RUN_SCRIPT_BOUNDARY
468
  void SelectAllChildren(nsINode& aNode, mozilla::ErrorResult& aRv);
469
470
  void SetBaseAndExtent(nsINode& aAnchorNode, uint32_t aAnchorOffset,
471
                        nsINode& aFocusNode, uint32_t aFocusOffset,
472
                        mozilla::ErrorResult& aRv);
473
474
  void AddSelectionChangeBlocker();
475
  void RemoveSelectionChangeBlocker();
476
  bool IsBlockingSelectionChangeEvents() const;
477
478
  /**
479
   * Set the painting style for the range. The range must be a range in
480
   * the selection. The textRangeStyle will be used by text frame
481
   * when it is painting the selection.
482
   */
483
  nsresult SetTextRangeStyle(nsRange* aRange,
484
                             const TextRangeStyle& aTextRangeStyle);
485
486
  // Methods to manipulate our mFrameSelection's ancestor limiter.
487
  nsIContent* GetAncestorLimiter() const;
488
  void SetAncestorLimiter(nsIContent* aLimiter);
489
490
  /*
491
   * Frame Offset cache can be used just during calling nsEditor::EndPlaceHolderTransaction.
492
   * EndPlaceHolderTransaction will give rise to reflow/refreshing view/scroll, and call times
493
   * of nsTextFrame::GetPointFromOffset whose return value is to be cached.
494
   * see bugs 35296 and 199412
495
   */
496
  void SetCanCacheFrameOffset(bool aCanCacheFrameOffset);
497
498
  // Selection::GetRangesForIntervalArray
499
  //
500
  //    Fills a nsTArray with the ranges overlapping the range specified by
501
  //    the given endpoints. Ranges in the selection exactly adjacent to the
502
  //    input range are not returned unless aAllowAdjacent is set.
503
  //
504
  //    For example, if the following ranges were in the selection
505
  //    (assume everything is within the same node)
506
  //
507
  //    Start Offset: 0 2 7 9
508
  //      End Offset: 2 5 9 10
509
  //
510
  //    and passed aBeginOffset of 2 and aEndOffset of 9, then with
511
  //    aAllowAdjacent set, all the ranges should be returned. If
512
  //    aAllowAdjacent was false, the ranges [2, 5] and [7, 9] only
513
  //    should be returned
514
  //
515
  //    Now that overlapping ranges are disallowed, there can be a maximum of
516
  //    2 adjacent ranges
517
  nsresult
518
  GetRangesForIntervalArray(nsINode* aBeginNode, int32_t aBeginOffset,
519
                            nsINode* aEndNode, int32_t aEndOffset,
520
                            bool aAllowAdjacent,
521
                            nsTArray<nsRange*>* aRanges);
522
523
  /**
524
   * Modifies the cursor Bidi level after a change in keyboard direction
525
   * @param langRTL is true if the new language is right-to-left or
526
   *                false if the new language is left-to-right.
527
   */
528
  nsresult SelectionLanguageChange(bool aLangRTL);
529
530
private:
531
  friend class ::nsAutoScrollTimer;
532
533
  // Note: DoAutoScroll might destroy arbitrary frames etc.
534
  nsresult DoAutoScroll(nsIFrame* aFrame, nsPoint aPoint);
535
536
  // We are not allowed to be in nodes whose root is not our document
537
  bool HasSameRoot(nsINode& aNode);
538
539
  // XXX Please don't add additional uses of this method, it's only for
540
  // XXX supporting broken code (bug 1245883) in the following classes:
541
  friend class ::nsCopySupport;
542
  friend class ::nsHTMLCopyEncoder;
543
  MOZ_CAN_RUN_SCRIPT
544
  void AddRangeInternal(nsRange& aRange, nsIDocument* aDocument, ErrorResult&);
545
546
  // This is helper method for GetPrimaryFrameForFocusNode.
547
  // If aVisual is true, this returns caret frame.
548
  // If false, this returns primary frame.
549
  nsresult GetPrimaryOrCaretFrameForNodeOffset(nsIContent* aContent,
550
                                               uint32_t aOffset,
551
                                               nsIFrame** aReturnFrame,
552
                                               int32_t* aOffsetUsed,
553
                                               bool aVisual) const;
554
555
  // Get the cached value for nsTextFrame::GetPointFromOffset.
556
  nsresult GetCachedFrameOffset(nsIFrame* aFrame, int32_t inOffset,
557
                                nsPoint& aPoint);
558
559
public:
560
0
  SelectionType GetType() const { return mSelectionType; }
561
  void SetType(SelectionType aSelectionType)
562
0
  {
563
0
    mSelectionType = aSelectionType;
564
0
  }
565
566
0
  SelectionCustomColors* GetCustomColors() const { return mCustomColors.get(); }
567
568
  MOZ_CAN_RUN_SCRIPT nsresult NotifySelectionListeners(bool aCalledByJS);
569
  MOZ_CAN_RUN_SCRIPT nsresult NotifySelectionListeners();
570
571
  friend struct AutoUserInitiated;
572
  struct MOZ_RAII AutoUserInitiated
573
  {
574
    explicit AutoUserInitiated(Selection* aSelection
575
                               MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
576
      : mSavedValue(aSelection->mUserInitiated)
577
0
    {
578
0
      MOZ_GUARD_OBJECT_NOTIFIER_INIT;
579
0
      aSelection->mUserInitiated = true;
580
0
    }
581
    AutoRestore<bool> mSavedValue;
582
    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
583
  };
584
585
private:
586
  friend struct mozilla::AutoPrepareFocusRange;
587
  class ScrollSelectionIntoViewEvent;
588
  friend class ScrollSelectionIntoViewEvent;
589
590
  class ScrollSelectionIntoViewEvent : public Runnable {
591
  public:
592
    NS_DECL_NSIRUNNABLE
593
    ScrollSelectionIntoViewEvent(Selection* aSelection,
594
                                 SelectionRegion aRegion,
595
                                 nsIPresShell::ScrollAxis aVertical,
596
                                 nsIPresShell::ScrollAxis aHorizontal,
597
                                 int32_t aFlags)
598
      : Runnable("dom::Selection::ScrollSelectionIntoViewEvent")
599
      , mSelection(aSelection)
600
      , mRegion(aRegion)
601
      , mVerticalScroll(aVertical)
602
      , mHorizontalScroll(aHorizontal)
603
      , mFlags(aFlags)
604
0
    {
605
0
      NS_ASSERTION(aSelection, "null parameter");
606
0
    }
607
0
    void Revoke() { mSelection = nullptr; }
608
  private:
609
    Selection *mSelection;
610
    SelectionRegion mRegion;
611
    nsIPresShell::ScrollAxis mVerticalScroll;
612
    nsIPresShell::ScrollAxis mHorizontalScroll;
613
    int32_t mFlags;
614
  };
615
616
  /**
617
   * Set mAnchorFocusRange to mRanges[aIndex] if aIndex is a valid index.
618
   * Set mAnchorFocusRange to nullptr if aIndex is negative.
619
   * Otherwise, i.e., if aIndex is positive but out of bounds of mRanges, do
620
   * nothing.
621
   */
622
  void SetAnchorFocusRange(int32_t aIndex);
623
  void SelectFramesForContent(nsIContent* aContent, bool aSelected);
624
  nsresult SelectAllFramesForContent(nsIContentIterator* aInnerIter,
625
                                     nsIContent *aContent,
626
                                     bool aSelected);
627
  nsresult SelectFrames(nsPresContext* aPresContext,
628
                        nsRange* aRange,
629
                        bool aSelect);
630
631
  /**
632
   * Test whether the supplied range points to a single table element.
633
   * Result is one of the TableSelection constants. "None" means
634
   * a table element isn't selected.
635
   */
636
  nsresult GetTableSelectionType(nsRange* aRange,
637
                                 TableSelection* aTableSelectionType);
638
  nsresult GetTableCellLocationFromRange(nsRange* aRange,
639
                                         TableSelection* aSelectionType,
640
                                         int32_t* aRow,
641
                                         int32_t* aCol);
642
  nsresult AddTableCellRange(nsRange* aRange,
643
                             bool* aDidAddRange,
644
                             int32_t* aOutIndex);
645
646
  nsresult FindInsertionPoint(
647
      nsTArray<RangeData>* aElementArray,
648
      nsINode* aPointNode, int32_t aPointOffset,
649
      nsresult (*aComparator)(nsINode*,int32_t,nsRange*,int32_t*),
650
      int32_t* aPoint);
651
  bool EqualsRangeAtPoint(nsINode* aBeginNode, int32_t aBeginOffset,
652
                            nsINode* aEndNode, int32_t aEndOffset,
653
                            int32_t aRangeIndex);
654
  nsresult GetIndicesForInterval(nsINode* aBeginNode, int32_t aBeginOffset,
655
                                 nsINode* aEndNode, int32_t aEndOffset,
656
                                 bool aAllowAdjacent,
657
                                 int32_t* aStartIndex, int32_t* aEndIndex);
658
  RangeData* FindRangeData(nsRange* aRange);
659
660
  void UserSelectRangesToAdd(nsRange* aItem, nsTArray<RefPtr<nsRange> >& rangesToAdd);
661
662
  /**
663
   * Helper method for AddItem.
664
   */
665
  nsresult AddItemInternal(nsRange* aRange, int32_t* aOutIndex);
666
667
  nsIDocument* GetDocument() const;
668
  nsPIDOMWindowOuter* GetWindow() const;
669
  HTMLEditor* GetHTMLEditor() const;
670
671
  /**
672
   * GetCommonEditingHostForAllRanges() returns common editing host of all
673
   * ranges if there is. If at least one of the ranges is in non-editable
674
   * element, returns nullptr.  See following examples for the detail:
675
   *
676
   *  <div id="a" contenteditable>
677
   *    an[cestor
678
   *    <div id="b" contenteditable="false">
679
   *      non-editable
680
   *      <div id="c" contenteditable>
681
   *        desc]endant
682
   *  in this case, this returns div#a because div#c is also in div#a.
683
   *
684
   *  <div id="a" contenteditable>
685
   *    an[ce]stor
686
   *    <div id="b" contenteditable="false">
687
   *      non-editable
688
   *      <div id="c" contenteditable>
689
   *        de[sc]endant
690
   *  in this case, this returns div#a because second range is also in div#a
691
   *  and common ancestor of the range (i.e., div#c) is editable.
692
   *
693
   *  <div id="a" contenteditable>
694
   *    an[ce]stor
695
   *    <div id="b" contenteditable="false">
696
   *      [non]-editable
697
   *      <div id="c" contenteditable>
698
   *        de[sc]endant
699
   *  in this case, this returns nullptr because the second range is in
700
   *  non-editable area.
701
   */
702
  Element* GetCommonEditingHostForAllRanges();
703
704
  // These are the ranges inside this selection. They are kept sorted in order
705
  // of DOM start position.
706
  //
707
  // This data structure is sorted by the range beginnings. As the ranges are
708
  // disjoint, it is also implicitly sorted by the range endings. This allows
709
  // us to perform binary searches when searching for existence of a range,
710
  // giving us O(log n) search time.
711
  //
712
  // Inserting a new range requires finding the overlapping interval, requiring
713
  // two binary searches plus up to an additional 6 DOM comparisons. If this
714
  // proves to be a performance concern, then an interval tree may be a
715
  // possible solution, allowing the calculation of the overlap interval in
716
  // O(log n) time, though this would require rebalancing and other overhead.
717
  AutoTArray<RangeData, 1> mRanges;
718
719
  RefPtr<nsRange> mAnchorFocusRange;
720
  // mCachedRange is set by RemoveAllRangesTemporarily() and used by
721
  // Collapse() and SetBaseAndExtent().  If there is a range which will be
722
  // released by Clear(), RemoveAllRangesTemporarily() stores it with this.
723
  // If Collapse() is called without existing ranges, it'll reuse this range
724
  // for saving the creation cost.
725
  // Note that while the range is cached by this, we keep the range being
726
  // a mutation observer because it is not so cheap to register the range
727
  // as a mutation observer again.  On the other hand, we make it not
728
  // positioned because it is not so cheap to keep valid DOM point against
729
  // mutations.  This does not cause any problems because we will set new
730
  // DOM point when we treat it as a range of Selection again.
731
  RefPtr<nsRange> mCachedRange;
732
  RefPtr<nsFrameSelection> mFrameSelection;
733
  RefPtr<AccessibleCaretEventHub> mAccessibleCaretEventHub;
734
  RefPtr<SelectionChangeEventDispatcher> mSelectionChangeEventDispatcher;
735
  RefPtr<nsAutoScrollTimer> mAutoScrollTimer;
736
  nsTArray<nsCOMPtr<nsISelectionListener>> mSelectionListeners;
737
  nsRevocableEventPtr<ScrollSelectionIntoViewEvent> mScrollEvent;
738
  CachedOffsetForFrame* mCachedOffsetForFrame;
739
  nsDirection mDirection;
740
  SelectionType mSelectionType;
741
  UniquePtr<SelectionCustomColors> mCustomColors;
742
743
  // Non-zero if we don't want any changes we make to the selection to be
744
  // visible to content. If non-zero, content won't be notified about changes.
745
  uint32_t mSelectionChangeBlockerCount;
746
747
  /**
748
   * True if the current selection operation was initiated by user action.
749
   * It determines whether we exclude -moz-user-select:none nodes or not,
750
   * as well as whether selectstart events will be fired.
751
   */
752
  bool mUserInitiated;
753
754
  /**
755
   * When the selection change is caused by a call of Selection API,
756
   * mCalledByJS is true.  Otherwise, false.
757
   */
758
  bool mCalledByJS;
759
760
  /**
761
   * true if AutoCopyListner::OnSelectionChange() should be called.
762
   */
763
  bool mNotifyAutoCopy;
764
};
765
766
// Stack-class to turn on/off selection batching.
767
class MOZ_STACK_CLASS SelectionBatcher final
768
{
769
private:
770
  RefPtr<Selection> mSelection;
771
public:
772
  explicit SelectionBatcher(Selection* aSelection)
773
0
  {
774
0
    mSelection = aSelection;
775
0
    if (mSelection) {
776
0
      mSelection->StartBatchChanges();
777
0
    }
778
0
  }
779
780
  ~SelectionBatcher()
781
0
  {
782
0
    if (mSelection) {
783
0
      mSelection->EndBatchChanges();
784
0
    }
785
0
  }
786
};
787
788
class MOZ_RAII AutoHideSelectionChanges final
789
{
790
private:
791
  RefPtr<Selection> mSelection;
792
  MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
793
public:
794
  explicit AutoHideSelectionChanges(const nsFrameSelection* aFrame);
795
796
  explicit AutoHideSelectionChanges(Selection* aSelection
797
                                    MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
798
    : mSelection(aSelection)
799
0
  {
800
0
    MOZ_GUARD_OBJECT_NOTIFIER_INIT;
801
0
    mSelection = aSelection;
802
0
    if (mSelection) {
803
0
      mSelection->AddSelectionChangeBlocker();
804
0
    }
805
0
  }
806
807
  ~AutoHideSelectionChanges()
808
0
  {
809
0
    if (mSelection) {
810
0
      mSelection->RemoveSelectionChangeBlocker();
811
0
    }
812
0
  }
813
};
814
815
} // namespace dom
816
817
inline bool
818
IsValidRawSelectionType(RawSelectionType aRawSelectionType)
819
0
{
820
0
  return aRawSelectionType >= nsISelectionController::SELECTION_NONE &&
821
0
         aRawSelectionType <= nsISelectionController::SELECTION_URLSTRIKEOUT;
822
0
}
823
824
inline SelectionType
825
ToSelectionType(RawSelectionType aRawSelectionType)
826
0
{
827
0
  if (!IsValidRawSelectionType(aRawSelectionType)) {
828
0
    return SelectionType::eInvalid;
829
0
  }
830
0
  return static_cast<SelectionType>(aRawSelectionType);
831
0
}
832
833
inline RawSelectionType
834
ToRawSelectionType(SelectionType aSelectionType)
835
0
{
836
0
  MOZ_ASSERT(aSelectionType != SelectionType::eInvalid);
837
0
  return static_cast<RawSelectionType>(aSelectionType);
838
0
}
839
840
inline RawSelectionType
841
ToRawSelectionType(TextRangeType aTextRangeType)
842
0
{
843
0
  return ToRawSelectionType(ToSelectionType(aTextRangeType));
844
0
}
845
846
inline SelectionTypeMask
847
ToSelectionTypeMask(SelectionType aSelectionType)
848
18
{
849
18
  MOZ_ASSERT(aSelectionType != SelectionType::eInvalid);
850
18
  return aSelectionType == SelectionType::eNone ? 0 :
851
18
           (1 << (static_cast<uint8_t>(aSelectionType) - 1));
852
18
}
853
854
} // namespace mozilla
855
856
#endif // mozilla_Selection_h__