Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/base/Selection.cpp
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
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
/*
8
 * Implementation of mozilla::dom::Selection
9
 */
10
11
#include "mozilla/dom/Selection.h"
12
13
#include "mozilla/AsyncEventDispatcher.h"
14
#include "mozilla/Attributes.h"
15
#include "mozilla/AutoCopyListener.h"
16
#include "mozilla/AutoRestore.h"
17
#include "mozilla/dom/Element.h"
18
#include "mozilla/dom/SelectionBinding.h"
19
#include "mozilla/dom/ShadowRoot.h"
20
#include "mozilla/ErrorResult.h"
21
#include "mozilla/EventStates.h"
22
#include "mozilla/HTMLEditor.h"
23
#include "mozilla/RangeBoundary.h"
24
#include "mozilla/Telemetry.h"
25
26
#include "nsCOMPtr.h"
27
#include "nsString.h"
28
#include "nsFrameSelection.h"
29
#include "nsISelectionListener.h"
30
#include "nsContentCID.h"
31
#include "nsDeviceContext.h"
32
#include "nsIContent.h"
33
#include "nsRange.h"
34
#include "nsITableCellLayout.h"
35
#include "nsTArray.h"
36
#include "nsTableWrapperFrame.h"
37
#include "nsTableCellFrame.h"
38
#include "nsIScrollableFrame.h"
39
#include "nsCCUncollectableMarker.h"
40
#include "nsIContentIterator.h"
41
#include "nsIDocumentEncoder.h"
42
#include "nsTextFragment.h"
43
#include <algorithm>
44
#include "nsContentUtils.h"
45
46
#include "nsGkAtoms.h"
47
#include "nsLayoutUtils.h"
48
#include "nsBidiPresUtils.h"
49
#include "nsTextFrame.h"
50
51
#include "nsContentUtils.h"
52
#include "nsThreadUtils.h"
53
54
#include "nsPresContext.h"
55
#include "nsIPresShell.h"
56
#include "nsCaret.h"
57
58
#include "nsITimer.h"
59
#include "nsIDocument.h"
60
#include "nsINamed.h"
61
62
#include "nsISelectionController.h" //for the enums
63
#include "nsCopySupport.h"
64
#include "nsIClipboard.h"
65
#include "nsIFrameInlines.h"
66
#include "nsRefreshDriver.h"
67
#include "nsIBidiKeyboard.h"
68
69
#include "nsError.h"
70
#include "nsViewManager.h"
71
72
#include "nsFocusManager.h"
73
#include "nsPIDOMWindow.h"
74
75
using namespace mozilla;
76
using namespace mozilla::dom;
77
78
//#define DEBUG_TABLE 1
79
80
static bool IsValidSelectionPoint(nsFrameSelection *aFrameSel, nsINode *aNode);
81
82
#ifdef PRINT_RANGE
83
static void printRange(nsRange *aDomRange);
84
#define DEBUG_OUT_RANGE(x)  printRange(x)
85
#else
86
#define DEBUG_OUT_RANGE(x)
87
#endif // PRINT_RANGE
88
89
/******************************************************************************
90
 * Utility methods defined in nsISelectionController.idl
91
 ******************************************************************************/
92
93
namespace mozilla {
94
95
const char*
96
ToChar(SelectionType aSelectionType)
97
{
98
  switch (aSelectionType) {
99
    case SelectionType::eInvalid:
100
      return "SelectionType::eInvalid";
101
    case SelectionType::eNone:
102
      return "SelectionType::eNone";
103
    case SelectionType::eNormal:
104
      return "SelectionType::eNormal";
105
    case SelectionType::eSpellCheck:
106
      return "SelectionType::eSpellCheck";
107
    case SelectionType::eIMERawClause:
108
      return "SelectionType::eIMERawClause";
109
    case SelectionType::eIMESelectedRawClause:
110
      return "SelectionType::eIMESelectedRawClause";
111
    case SelectionType::eIMEConvertedClause:
112
      return "SelectionType::eIMEConvertedClause";
113
    case SelectionType::eIMESelectedClause:
114
      return "SelectionType::eIMESelectedClause";
115
    case SelectionType::eAccessibility:
116
      return "SelectionType::eAccessibility";
117
    case SelectionType::eFind:
118
      return "SelectionType::eFind";
119
    case SelectionType::eURLSecondary:
120
      return "SelectionType::eURLSecondary";
121
    case SelectionType::eURLStrikeout:
122
      return "SelectionType::eURLStrikeout";
123
    default:
124
      return "Invalid SelectionType";
125
  }
126
}
127
128
} // namespace mozilla
129
130
//#define DEBUG_SELECTION // uncomment for printf describing every collapse and extend.
131
//#define DEBUG_NAVIGATION
132
133
134
//#define DEBUG_TABLE_SELECTION 1
135
136
struct CachedOffsetForFrame {
137
  CachedOffsetForFrame()
138
  : mCachedFrameOffset(0, 0) // nsPoint ctor
139
  , mLastCaretFrame(nullptr)
140
  , mLastContentOffset(0)
141
  , mCanCacheFrameOffset(false)
142
0
  {}
143
144
  nsPoint      mCachedFrameOffset;      // cached frame offset
145
  nsIFrame*    mLastCaretFrame;         // store the frame the caret was last drawn in.
146
  int32_t      mLastContentOffset;      // store last content offset
147
  bool mCanCacheFrameOffset;    // cached frame offset is valid?
148
};
149
150
class nsAutoScrollTimer final : public nsITimerCallback
151
                              , public nsINamed
152
{
153
public:
154
155
  NS_DECL_ISUPPORTS
156
157
  nsAutoScrollTimer()
158
  : mFrameSelection(0), mSelection(0), mPresContext(0), mPoint(0,0), mDelay(30)
159
0
  {
160
0
  }
161
162
  // aPoint is relative to aPresContext's root frame
163
  nsresult Start(nsPresContext *aPresContext, nsPoint &aPoint)
164
0
  {
165
0
    mPoint = aPoint;
166
0
167
0
    // Store the presentation context. The timer will be
168
0
    // stopped by the selection if the prescontext is destroyed.
169
0
    mPresContext = aPresContext;
170
0
171
0
    mContent = nsIPresShell::GetCapturingContent();
172
0
173
0
    if (!mTimer) {
174
0
      mTimer = NS_NewTimer(
175
0
        mPresContext->Document()->EventTargetFor(TaskCategory::Other));
176
0
177
0
      if (!mTimer) {
178
0
        return NS_ERROR_OUT_OF_MEMORY;
179
0
      }
180
0
    }
181
0
182
0
    return mTimer->InitWithCallback(this, mDelay, nsITimer::TYPE_ONE_SHOT);
183
0
  }
184
185
  nsresult Stop()
186
0
  {
187
0
    if (mTimer) {
188
0
      mTimer->Cancel();
189
0
      mTimer = nullptr;
190
0
    }
191
0
192
0
    mContent = nullptr;
193
0
    return NS_OK;
194
0
  }
195
196
  nsresult Init(nsFrameSelection* aFrameSelection, Selection* aSelection)
197
0
  {
198
0
    mFrameSelection = aFrameSelection;
199
0
    mSelection = aSelection;
200
0
    return NS_OK;
201
0
  }
202
203
  nsresult SetDelay(uint32_t aDelay)
204
0
  {
205
0
    mDelay = aDelay;
206
0
    return NS_OK;
207
0
  }
208
209
  NS_IMETHOD Notify(nsITimer *timer) override
210
0
  {
211
0
    if (mSelection && mPresContext)
212
0
    {
213
0
      AutoWeakFrame frame =
214
0
        mContent ? mPresContext->GetPrimaryFrameFor(mContent) : nullptr;
215
0
      if (!frame) {
216
0
        return NS_OK;
217
0
      }
218
0
      mContent = nullptr;
219
0
220
0
      nsPoint pt = mPoint -
221
0
        frame->GetOffsetTo(mPresContext->PresShell()->GetRootFrame());
222
0
      RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
223
0
      frameSelection->HandleDrag(frame, pt);
224
0
      if (!frame.IsAlive()) {
225
0
        return NS_OK;
226
0
      }
227
0
228
0
      NS_ASSERTION(frame->PresContext() == mPresContext, "document mismatch?");
229
0
      mSelection->DoAutoScroll(frame, pt);
230
0
    }
231
0
    return NS_OK;
232
0
  }
233
234
  NS_IMETHOD GetName(nsACString& aName) override
235
0
  {
236
0
    aName.AssignLiteral("nsAutoScrollTimer");
237
0
    return NS_OK;
238
0
  }
239
240
protected:
241
  virtual ~nsAutoScrollTimer()
242
0
  {
243
0
    if (mTimer) {
244
0
      mTimer->Cancel();
245
0
    }
246
0
  }
247
248
private:
249
  nsFrameSelection *mFrameSelection;
250
  Selection* mSelection;
251
  nsPresContext *mPresContext;
252
  // relative to mPresContext's root frame
253
  nsPoint mPoint;
254
  nsCOMPtr<nsITimer> mTimer;
255
  nsCOMPtr<nsIContent> mContent;
256
  uint32_t mDelay;
257
};
258
259
NS_IMPL_ISUPPORTS(nsAutoScrollTimer, nsITimerCallback, nsINamed)
260
261
/*
262
The limiter is used specifically for the text areas and textfields
263
In that case it is the DIV tag that is anonymously created for the text
264
areas/fields.  Text nodes and BR nodes fall beneath it.  In the case of a
265
BR node the limiter will be the parent and the offset will point before or
266
after the BR node.  In the case of the text node the parent content is
267
the text node itself and the offset will be the exact character position.
268
The offset is not important to check for validity.  Simply look at the
269
passed in content.  If it equals the limiter then the selection point is valid.
270
If its parent it the limiter then the point is also valid.  In the case of
271
NO limiter all points are valid since you are in a topmost iframe. (browser
272
or composer)
273
*/
274
bool
275
IsValidSelectionPoint(nsFrameSelection *aFrameSel, nsINode *aNode)
276
0
{
277
0
  if (!aFrameSel || !aNode)
278
0
    return false;
279
0
280
0
  nsIContent *limiter = aFrameSel->GetLimiter();
281
0
  if (limiter && limiter != aNode && limiter != aNode->GetParent()) {
282
0
    //if newfocus == the limiter. that's ok. but if not there and not parent bad
283
0
    return false; //not in the right content. tLimiter said so
284
0
  }
285
0
286
0
  limiter = aFrameSel->GetAncestorLimiter();
287
0
  return !limiter || nsContentUtils::ContentIsDescendantOf(aNode, limiter);
288
0
}
289
290
namespace mozilla {
291
struct MOZ_RAII AutoPrepareFocusRange
292
{
293
  AutoPrepareFocusRange(Selection* aSelection,
294
                        bool aContinueSelection,
295
                        bool aMultipleSelection
296
                        MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
297
  {
298
    MOZ_GUARD_OBJECT_NOTIFIER_INIT;
299
300
    if (aSelection->mRanges.Length() <= 1) {
301
      return;
302
    }
303
304
    if (aSelection->mFrameSelection->IsUserSelectionReason()) {
305
      mUserSelect.emplace(aSelection);
306
    }
307
    bool userSelection = aSelection->mUserInitiated;
308
309
    nsTArray<RangeData>& ranges = aSelection->mRanges;
310
    if (!userSelection ||
311
        (!aContinueSelection && aMultipleSelection)) {
312
      // Scripted command or the user is starting a new explicit multi-range
313
      // selection.
314
      for (RangeData& entry : ranges) {
315
        entry.mRange->SetIsGenerated(false);
316
      }
317
      return;
318
    }
319
320
    int16_t reason = aSelection->mFrameSelection->mSelectionChangeReason;
321
    bool isAnchorRelativeOp = (reason & (nsISelectionListener::DRAG_REASON |
322
                                         nsISelectionListener::MOUSEDOWN_REASON |
323
                                         nsISelectionListener::MOUSEUP_REASON |
324
                                         nsISelectionListener::COLLAPSETOSTART_REASON));
325
    if (!isAnchorRelativeOp) {
326
      return;
327
    }
328
329
    // This operation is against the anchor but our current mAnchorFocusRange
330
    // represents the focus in a multi-range selection.  The anchor from a user
331
    // perspective is the most distant generated range on the opposite side.
332
    // Find that range and make it the mAnchorFocusRange.
333
    const size_t len = ranges.Length();
334
    size_t newAnchorFocusIndex = size_t(-1);
335
    if (aSelection->GetDirection() == eDirNext) {
336
      for (size_t i = 0; i < len; ++i) {
337
        if (ranges[i].mRange->IsGenerated()) {
338
          newAnchorFocusIndex = i;
339
          break;
340
        }
341
      }
342
    } else {
343
      size_t i = len;
344
      while (i--) {
345
        if (ranges[i].mRange->IsGenerated()) {
346
          newAnchorFocusIndex = i;
347
          break;
348
        }
349
      }
350
    }
351
352
    if (newAnchorFocusIndex == size_t(-1)) {
353
      // There are no generated ranges - that's fine.
354
      return;
355
    }
356
357
    // Setup the new mAnchorFocusRange and mark the old one as generated.
358
    if (aSelection->mAnchorFocusRange) {
359
      aSelection->mAnchorFocusRange->SetIsGenerated(true);
360
    }
361
    nsRange* range = ranges[newAnchorFocusIndex].mRange;
362
    range->SetIsGenerated(false);
363
    aSelection->mAnchorFocusRange = range;
364
365
    // Remove all generated ranges (including the old mAnchorFocusRange).
366
    RefPtr<nsPresContext> presContext = aSelection->GetPresContext();
367
    size_t i = len;
368
    while (i--) {
369
      range = aSelection->mRanges[i].mRange;
370
      if (range->IsGenerated()) {
371
        range->SetSelection(nullptr);
372
        aSelection->SelectFrames(presContext, range, false);
373
        aSelection->mRanges.RemoveElementAt(i);
374
      }
375
    }
376
    if (aSelection->mFrameSelection) {
377
      aSelection->mFrameSelection->InvalidateDesiredPos();
378
    }
379
  }
380
381
  Maybe<Selection::AutoUserInitiated> mUserSelect;
382
  MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
383
};
384
385
} // namespace mozilla
386
387
388
#ifdef PRINT_RANGE
389
void printRange(nsRange *aDomRange)
390
{
391
  if (!aDomRange)
392
  {
393
    printf("NULL Range\n");
394
  }
395
  nsINode* startNode = aDomRange->GetStartContainer();
396
  nsINode* endNode = aDomRange->GetEndContainer();
397
  int32_t startOffset = aDomRange->StartOffset();
398
  int32_t endOffset = aDomRange->EndOffset();
399
400
  printf("range: 0x%lx\t start: 0x%lx %ld, \t end: 0x%lx,%ld\n",
401
         (unsigned long)aDomRange,
402
         (unsigned long)startNode, (long)startOffset,
403
         (unsigned long)endNode, (long)endOffset);
404
405
}
406
#endif /* PRINT_RANGE */
407
408
void
409
Selection::Stringify(nsAString& aResult)
410
0
{
411
0
  // We need FlushType::Frames here to make sure frames have been created for
412
0
  // the selected content.  Use mFrameSelection->GetShell() which returns
413
0
  // null if the Selection has been disconnected (the shell is Destroyed).
414
0
  nsCOMPtr<nsIPresShell> shell =
415
0
    mFrameSelection ? mFrameSelection->GetShell() : nullptr;
416
0
  if (!shell) {
417
0
    aResult.Truncate();
418
0
    return;
419
0
  }
420
0
  shell->FlushPendingNotifications(FlushType::Frames);
421
0
422
0
  IgnoredErrorResult rv;
423
0
  ToStringWithFormat(NS_LITERAL_STRING("text/plain"),
424
0
                     nsIDocumentEncoder::SkipInvisibleContent,
425
0
                     0, aResult, rv);
426
0
  if (rv.Failed()) {
427
0
    aResult.Truncate();
428
0
  }
429
0
}
430
431
void
432
Selection::ToStringWithFormat(const nsAString& aFormatType, uint32_t aFlags,
433
                              int32_t aWrapCol, nsAString& aReturn,
434
                              ErrorResult& aRv)
435
0
{
436
0
  nsresult rv = NS_OK;
437
0
  NS_ConvertUTF8toUTF16 formatType( NS_DOC_ENCODER_CONTRACTID_BASE );
438
0
  formatType.Append(aFormatType);
439
0
  nsCOMPtr<nsIDocumentEncoder> encoder =
440
0
           do_CreateInstance(NS_ConvertUTF16toUTF8(formatType).get(), &rv);
441
0
  if (NS_FAILED(rv)) {
442
0
    aRv.Throw(rv);
443
0
    return;
444
0
  }
445
0
446
0
  nsIPresShell* shell = GetPresShell();
447
0
  if (!shell) {
448
0
    aRv.Throw(NS_ERROR_FAILURE);
449
0
    return;
450
0
  }
451
0
452
0
  nsIDocument *doc = shell->GetDocument();
453
0
454
0
  // Flags should always include OutputSelectionOnly if we're coming from here:
455
0
  aFlags |= nsIDocumentEncoder::OutputSelectionOnly;
456
0
  nsAutoString readstring;
457
0
  readstring.Assign(aFormatType);
458
0
  rv = encoder->Init(doc, readstring, aFlags);
459
0
  if (NS_FAILED(rv)) {
460
0
    aRv.Throw(rv);
461
0
    return;
462
0
  }
463
0
464
0
  encoder->SetSelection(this);
465
0
  if (aWrapCol != 0)
466
0
    encoder->SetWrapColumn(aWrapCol);
467
0
468
0
  rv = encoder->EncodeToString(aReturn);
469
0
  if (NS_FAILED(rv)) {
470
0
    aRv.Throw(rv);
471
0
  }
472
0
}
473
474
void
475
Selection::SetInterlinePosition(bool aHintRight, ErrorResult& aRv)
476
0
{
477
0
  if (!mFrameSelection) {
478
0
    aRv.Throw(NS_ERROR_NOT_INITIALIZED); // Can't do selection
479
0
    return;
480
0
  }
481
0
  mFrameSelection->SetHint(aHintRight ? CARET_ASSOCIATE_AFTER : CARET_ASSOCIATE_BEFORE);
482
0
}
483
484
bool
485
Selection::GetInterlinePosition(ErrorResult& aRv)
486
0
{
487
0
  if (!mFrameSelection) {
488
0
    aRv.Throw(NS_ERROR_NOT_INITIALIZED); // Can't do selection
489
0
    return false;
490
0
  }
491
0
  return mFrameSelection->GetHint() == CARET_ASSOCIATE_AFTER;
492
0
}
493
494
Nullable<int16_t>
495
Selection::GetCaretBidiLevel(mozilla::ErrorResult& aRv) const
496
0
{
497
0
  if (!mFrameSelection) {
498
0
    aRv.Throw(NS_ERROR_NOT_INITIALIZED);
499
0
    return Nullable<int16_t>();
500
0
  }
501
0
  nsBidiLevel caretBidiLevel = mFrameSelection->GetCaretBidiLevel();
502
0
  return (caretBidiLevel & BIDI_LEVEL_UNDEFINED) ?
503
0
    Nullable<int16_t>() : Nullable<int16_t>(caretBidiLevel);
504
0
}
505
506
void
507
Selection::SetCaretBidiLevel(const Nullable<int16_t>& aCaretBidiLevel, mozilla::ErrorResult& aRv)
508
0
{
509
0
  if (!mFrameSelection) {
510
0
    aRv.Throw(NS_ERROR_NOT_INITIALIZED);
511
0
    return;
512
0
  }
513
0
  if (aCaretBidiLevel.IsNull()) {
514
0
    mFrameSelection->UndefineCaretBidiLevel();
515
0
  } else {
516
0
    mFrameSelection->SetCaretBidiLevel(aCaretBidiLevel.Value());
517
0
  }
518
0
}
519
520
nsresult
521
Selection::GetTableCellLocationFromRange(nsRange* aRange,
522
                                         TableSelection* aSelectionType,
523
                                         int32_t* aRow, int32_t* aCol)
524
0
{
525
0
  if (!aRange || !aSelectionType || !aRow || !aCol)
526
0
    return NS_ERROR_NULL_POINTER;
527
0
528
0
  *aSelectionType = TableSelection::None;
529
0
  *aRow = 0;
530
0
  *aCol = 0;
531
0
532
0
  // Must have access to frame selection to get cell info
533
0
  if (!mFrameSelection) return NS_OK;
534
0
535
0
  nsresult result = GetTableSelectionType(aRange, aSelectionType);
536
0
  if (NS_FAILED(result)) return result;
537
0
538
0
  // Don't fail if range does not point to a single table cell,
539
0
  //  let aSelectionType tell user if we don't have a cell
540
0
  if (*aSelectionType  != TableSelection::Cell) {
541
0
    return NS_OK;
542
0
  }
543
0
544
0
  // Get the child content (the cell) pointed to by starting node of range
545
0
  // We do minimal checking since GetTableSelectionType assures
546
0
  //   us that this really is a table cell
547
0
  nsCOMPtr<nsIContent> child = aRange->GetChildAtStartOffset();
548
0
  if (!child)
549
0
    return NS_ERROR_FAILURE;
550
0
551
0
  // GetCellLayout depends on current frame, we need flush frame to get
552
0
  // nsITableCellLayout
553
0
  nsCOMPtr<nsIPresShell> presShell = mFrameSelection->GetShell();
554
0
  if (presShell) {
555
0
    presShell->FlushPendingNotifications(FlushType::Frames);
556
0
557
0
    // Since calling FlushPendingNotifications, so check whether disconnected.
558
0
    if (!mFrameSelection || !mFrameSelection->GetShell()) {
559
0
      return NS_ERROR_FAILURE;
560
0
    }
561
0
  }
562
0
563
0
  //Note: This is a non-ref-counted pointer to the frame
564
0
  nsITableCellLayout *cellLayout = mFrameSelection->GetCellLayout(child);
565
0
  if (!cellLayout)
566
0
    return NS_ERROR_FAILURE;
567
0
568
0
  return cellLayout->GetCellIndexes(*aRow, *aCol);
569
0
}
570
571
nsresult
572
Selection::AddTableCellRange(nsRange* aRange, bool* aDidAddRange,
573
                             int32_t* aOutIndex)
574
0
{
575
0
  if (!aDidAddRange || !aOutIndex)
576
0
    return NS_ERROR_NULL_POINTER;
577
0
578
0
  *aDidAddRange = false;
579
0
  *aOutIndex = -1;
580
0
581
0
  if (!mFrameSelection)
582
0
    return NS_OK;
583
0
584
0
  if (!aRange)
585
0
    return NS_ERROR_NULL_POINTER;
586
0
587
0
  nsresult result;
588
0
589
0
  // Get if we are adding a cell selection and the row, col of cell if we are
590
0
  int32_t newRow, newCol;
591
0
  TableSelection tableMode;
592
0
  result = GetTableCellLocationFromRange(aRange, &tableMode, &newRow, &newCol);
593
0
  if (NS_FAILED(result)) return result;
594
0
595
0
  // If not adding a cell range, we are done here
596
0
  if (tableMode != TableSelection::Cell)
597
0
  {
598
0
    mFrameSelection->mSelectingTableCellMode = tableMode;
599
0
    // Don't fail if range isn't a selected cell, aDidAddRange tells caller if we didn't proceed
600
0
    return NS_OK;
601
0
  }
602
0
603
0
  // Set frame selection mode only if not already set to a table mode
604
0
  //  so we don't lose the select row and column flags (not detected by getTableCellLocation)
605
0
  if (mFrameSelection->mSelectingTableCellMode == TableSelection::None)
606
0
    mFrameSelection->mSelectingTableCellMode = tableMode;
607
0
608
0
  *aDidAddRange = true;
609
0
  return AddItem(aRange, aOutIndex);
610
0
}
611
612
//TODO: Figure out TableSelection::Column and TableSelection::AllCells
613
nsresult
614
Selection::GetTableSelectionType(nsRange* aRange,
615
                                 TableSelection* aTableSelectionType)
616
0
{
617
0
  if (!aRange || !aTableSelectionType)
618
0
    return NS_ERROR_NULL_POINTER;
619
0
620
0
  *aTableSelectionType = TableSelection::None;
621
0
622
0
  // Must have access to frame selection to get cell info
623
0
  if(!mFrameSelection) return NS_OK;
624
0
625
0
  nsINode* startNode = aRange->GetStartContainer();
626
0
  if (!startNode) return NS_ERROR_FAILURE;
627
0
628
0
  nsINode* endNode = aRange->GetEndContainer();
629
0
  if (!endNode) return NS_ERROR_FAILURE;
630
0
631
0
  // Not a single selected node
632
0
  if (startNode != endNode) return NS_OK;
633
0
634
0
  nsIContent* child = aRange->GetChildAtStartOffset();
635
0
636
0
  // Not a single selected node
637
0
  if (!child || child->GetNextSibling() != aRange->GetChildAtEndOffset()) {
638
0
    return NS_OK;
639
0
  }
640
0
641
0
  nsIContent* startContent = static_cast<nsIContent*>(startNode);
642
0
  if (!(startNode->IsElement() && startContent->IsHTMLElement())) {
643
0
    // Implies a check for being an element; if we ever make this work
644
0
    // for non-HTML, need to keep checking for elements.
645
0
    return NS_OK;
646
0
  }
647
0
648
0
  if (startContent->IsHTMLElement(nsGkAtoms::tr))
649
0
  {
650
0
    *aTableSelectionType = TableSelection::Cell;
651
0
  }
652
0
  else //check to see if we are selecting a table or row (column and all cells not done yet)
653
0
  {
654
0
    if (child->IsHTMLElement(nsGkAtoms::table))
655
0
      *aTableSelectionType = TableSelection::Table;
656
0
    else if (child->IsHTMLElement(nsGkAtoms::tr))
657
0
      *aTableSelectionType = TableSelection::Row;
658
0
  }
659
0
660
0
  return NS_OK;
661
0
}
662
663
Selection::Selection()
664
  : mCachedOffsetForFrame(nullptr)
665
  , mDirection(eDirNext)
666
  , mSelectionType(SelectionType::eNormal)
667
  , mCustomColors(nullptr)
668
  , mSelectionChangeBlockerCount(0)
669
  , mUserInitiated(false)
670
  , mCalledByJS(false)
671
  , mNotifyAutoCopy(false)
672
0
{
673
0
}
674
675
Selection::Selection(nsFrameSelection* aList)
676
  : mFrameSelection(aList)
677
  , mCachedOffsetForFrame(nullptr)
678
  , mDirection(eDirNext)
679
  , mSelectionType(SelectionType::eNormal)
680
  , mCustomColors(nullptr)
681
  , mSelectionChangeBlockerCount(0)
682
  , mUserInitiated(false)
683
  , mCalledByJS(false)
684
  , mNotifyAutoCopy(false)
685
0
{
686
0
}
687
688
Selection::~Selection()
689
0
{
690
0
  SetAnchorFocusRange(-1);
691
0
692
0
  uint32_t count = mRanges.Length();
693
0
  for (uint32_t i = 0; i < count; ++i) {
694
0
    mRanges[i].mRange->SetSelection(nullptr);
695
0
  }
696
0
697
0
  if (mAutoScrollTimer) {
698
0
    mAutoScrollTimer->Stop();
699
0
    mAutoScrollTimer = nullptr;
700
0
  }
701
0
702
0
  mScrollEvent.Revoke();
703
0
704
0
  if (mCachedOffsetForFrame) {
705
0
    delete mCachedOffsetForFrame;
706
0
    mCachedOffsetForFrame = nullptr;
707
0
  }
708
0
}
709
710
nsIDocument*
711
Selection::GetParentObject() const
712
0
{
713
0
  nsIPresShell* shell = GetPresShell();
714
0
  if (shell) {
715
0
    return shell->GetDocument();
716
0
  }
717
0
  return nullptr;
718
0
}
719
720
DocGroup*
721
Selection::GetDocGroup() const
722
0
{
723
0
  nsIPresShell* shell = GetPresShell();
724
0
  if (!shell) {
725
0
    return nullptr;
726
0
  }
727
0
728
0
  nsIDocument* doc = shell->GetDocument();
729
0
  return doc ? doc->GetDocGroup() : nullptr;
730
0
}
731
732
NS_IMPL_CYCLE_COLLECTION_CLASS(Selection)
733
734
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Selection)
735
0
  // Unlink the selection listeners *before* we do RemoveAllRanges since
736
0
  // we don't want to notify the listeners during JS GC (they could be
737
0
  // in JS!).
738
0
  tmp->mNotifyAutoCopy = false;
739
0
  tmp->StopNotifyingAccessibleCaretEventHub();
740
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelectionChangeEventDispatcher)
741
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelectionListeners)
742
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mCachedRange)
743
0
  tmp->RemoveAllRanges(IgnoreErrors());
744
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrameSelection)
745
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
746
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
747
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Selection)
748
0
  {
749
0
    uint32_t i, count = tmp->mRanges.Length();
750
0
    for (i = 0; i < count; ++i) {
751
0
      NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRanges[i].mRange)
752
0
    }
753
0
  }
754
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnchorFocusRange)
755
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCachedRange)
756
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameSelection)
757
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelectionChangeEventDispatcher)
758
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelectionListeners)
759
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
760
NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(Selection)
761
762
// QueryInterface implementation for Selection
763
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Selection)
764
0
  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
765
0
  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
766
0
  NS_INTERFACE_MAP_ENTRY(nsISupports)
767
0
NS_INTERFACE_MAP_END
768
769
NS_IMPL_MAIN_THREAD_ONLY_CYCLE_COLLECTING_ADDREF(Selection)
770
NS_IMPL_MAIN_THREAD_ONLY_CYCLE_COLLECTING_RELEASE(Selection)
771
772
const RangeBoundary&
773
Selection::AnchorRef()
774
0
{
775
0
  if (!mAnchorFocusRange) {
776
0
    static RangeBoundary sEmpty;
777
0
    return sEmpty;
778
0
  }
779
0
780
0
  if (GetDirection() == eDirNext) {
781
0
    return mAnchorFocusRange->StartRef();
782
0
  }
783
0
784
0
  return mAnchorFocusRange->EndRef();
785
0
}
786
787
const RangeBoundary&
788
Selection::FocusRef()
789
0
{
790
0
  if (!mAnchorFocusRange) {
791
0
    static RangeBoundary sEmpty;
792
0
    return sEmpty;
793
0
  }
794
0
795
0
  if (GetDirection() == eDirNext){
796
0
    return mAnchorFocusRange->EndRef();
797
0
  }
798
0
799
0
  return mAnchorFocusRange->StartRef();
800
0
}
801
802
void
803
Selection::SetAnchorFocusRange(int32_t indx)
804
0
{
805
0
  if (indx >= (int32_t)mRanges.Length())
806
0
    return;
807
0
  if (indx < 0) //release all
808
0
  {
809
0
    mAnchorFocusRange = nullptr;
810
0
  }
811
0
  else{
812
0
    mAnchorFocusRange = mRanges[indx].mRange;
813
0
  }
814
0
}
815
816
static nsresult
817
CompareToRangeStart(nsINode* aCompareNode, int32_t aCompareOffset,
818
                    nsRange* aRange, int32_t* aCmp)
819
0
{
820
0
  nsINode* start = aRange->GetStartContainer();
821
0
  NS_ENSURE_STATE(aCompareNode && start);
822
0
  // If the nodes that we're comparing are not in the same document or in the
823
0
  // same subtree, assume that aCompareNode will fall at the end of the ranges.
824
0
  if (aCompareNode->GetComposedDoc() != start->GetComposedDoc() ||
825
0
      !start->GetComposedDoc() ||
826
0
      aCompareNode->SubtreeRoot() != start->SubtreeRoot()) {
827
0
    *aCmp = 1;
828
0
  } else {
829
0
    *aCmp = nsContentUtils::ComparePoints(aCompareNode, aCompareOffset,
830
0
                                          start, aRange->StartOffset());
831
0
  }
832
0
  return NS_OK;
833
0
}
834
835
static nsresult
836
CompareToRangeEnd(nsINode* aCompareNode, int32_t aCompareOffset,
837
                  nsRange* aRange, int32_t* aCmp)
838
0
{
839
0
  nsINode* end = aRange->GetEndContainer();
840
0
  NS_ENSURE_STATE(aCompareNode && end);
841
0
  // If the nodes that we're comparing are not in the same document or in the
842
0
  // same subtree, assume that aCompareNode will fall at the end of the ranges.
843
0
  if (aCompareNode->GetComposedDoc() != end->GetComposedDoc() ||
844
0
      !end->GetComposedDoc() ||
845
0
      aCompareNode->SubtreeRoot() != end->SubtreeRoot()) {
846
0
    *aCmp = 1;
847
0
  } else {
848
0
    *aCmp = nsContentUtils::ComparePoints(aCompareNode, aCompareOffset,
849
0
                                          end, aRange->EndOffset());
850
0
  }
851
0
  return NS_OK;
852
0
}
853
854
// Selection::FindInsertionPoint
855
//
856
//    Binary searches the given sorted array of ranges for the insertion point
857
//    for the given node/offset. The given comparator is used, and the index
858
//    where the point should appear in the array is placed in *aInsertionPoint.
859
//
860
//    If there is an item in the array equal to the input point, we will return
861
//    the index of this item.
862
863
nsresult
864
Selection::FindInsertionPoint(
865
    nsTArray<RangeData>* aElementArray,
866
    nsINode* aPointNode, int32_t aPointOffset,
867
    nsresult (*aComparator)(nsINode*,int32_t,nsRange*,int32_t*),
868
    int32_t* aPoint)
869
0
{
870
0
  *aPoint = 0;
871
0
  int32_t beginSearch = 0;
872
0
  int32_t endSearch = aElementArray->Length(); // one beyond what to check
873
0
874
0
  if (endSearch) {
875
0
    int32_t center = endSearch - 1; // Check last index, then binary search
876
0
    do {
877
0
      nsRange* range = (*aElementArray)[center].mRange;
878
0
879
0
      int32_t cmp;
880
0
      nsresult rv = aComparator(aPointNode, aPointOffset, range, &cmp);
881
0
      NS_ENSURE_SUCCESS(rv, rv);
882
0
883
0
      if (cmp < 0) {        // point < cur
884
0
        endSearch = center;
885
0
      } else if (cmp > 0) { // point > cur
886
0
        beginSearch = center + 1;
887
0
      } else {              // found match, done
888
0
        beginSearch = center;
889
0
        break;
890
0
      }
891
0
      center = (endSearch - beginSearch) / 2 + beginSearch;
892
0
    } while (endSearch - beginSearch > 0);
893
0
  }
894
0
895
0
  *aPoint = beginSearch;
896
0
  return NS_OK;
897
0
}
898
899
// Selection::SubtractRange
900
//
901
//    A helper function that subtracts aSubtract from aRange, and adds
902
//    1 or 2 RangeData objects representing the remaining non-overlapping
903
//    difference to aOutput. It is assumed that the caller has checked that
904
//    aRange and aSubtract do indeed overlap
905
906
nsresult
907
Selection::SubtractRange(RangeData* aRange, nsRange* aSubtract,
908
                         nsTArray<RangeData>* aOutput)
909
0
{
910
0
  nsRange* range = aRange->mRange;
911
0
912
0
  // First we want to compare to the range start
913
0
  int32_t cmp;
914
0
  nsresult rv = CompareToRangeStart(range->GetStartContainer(),
915
0
                                    range->StartOffset(),
916
0
                                    aSubtract, &cmp);
917
0
  NS_ENSURE_SUCCESS(rv, rv);
918
0
919
0
  // Also, make a comparison to the range end
920
0
  int32_t cmp2;
921
0
  rv = CompareToRangeEnd(range->GetEndContainer(),
922
0
                         range->EndOffset(),
923
0
                         aSubtract, &cmp2);
924
0
  NS_ENSURE_SUCCESS(rv, rv);
925
0
926
0
  // If the existing range left overlaps the new range (aSubtract) then
927
0
  // cmp < 0, and cmp2 < 0
928
0
  // If it right overlaps the new range then cmp > 0 and cmp2 > 0
929
0
  // If it fully contains the new range, then cmp < 0 and cmp2 > 0
930
0
931
0
  if (cmp2 > 0) {
932
0
    // We need to add a new RangeData to the output, running from
933
0
    // the end of aSubtract to the end of range
934
0
    RefPtr<nsRange> postOverlap = new nsRange(aSubtract->GetEndContainer());
935
0
    rv = postOverlap->SetStartAndEnd(
936
0
                        aSubtract->GetEndContainer(), aSubtract->EndOffset(),
937
0
                        range->GetEndContainer(), range->EndOffset());
938
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
939
0
      return rv;
940
0
    }
941
0
    if (!postOverlap->Collapsed()) {
942
0
      if (!aOutput->InsertElementAt(0, RangeData(postOverlap)))
943
0
        return NS_ERROR_OUT_OF_MEMORY;
944
0
      (*aOutput)[0].mTextRangeStyle = aRange->mTextRangeStyle;
945
0
    }
946
0
  }
947
0
948
0
  if (cmp < 0) {
949
0
    // We need to add a new RangeData to the output, running from
950
0
    // the start of the range to the start of aSubtract
951
0
    RefPtr<nsRange> preOverlap = new nsRange(range->GetStartContainer());
952
0
    rv = preOverlap->SetStartAndEnd(range->GetStartContainer(),
953
0
                                    range->StartOffset(),
954
0
                                    aSubtract->GetStartContainer(),
955
0
                                    aSubtract->StartOffset());
956
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
957
0
      return rv;
958
0
    }
959
0
    if (!preOverlap->Collapsed()) {
960
0
      if (!aOutput->InsertElementAt(0, RangeData(preOverlap)))
961
0
        return NS_ERROR_OUT_OF_MEMORY;
962
0
      (*aOutput)[0].mTextRangeStyle = aRange->mTextRangeStyle;
963
0
    }
964
0
  }
965
0
966
0
  return NS_OK;
967
0
}
968
969
void
970
Selection::UserSelectRangesToAdd(nsRange* aItem, nsTArray<RefPtr<nsRange>>& aRangesToAdd)
971
0
{
972
0
  aItem->ExcludeNonSelectableNodes(&aRangesToAdd);
973
0
  if (aRangesToAdd.IsEmpty()) {
974
0
    ErrorResult err;
975
0
    nsINode* node = aItem->GetStartContainer(err);
976
0
    if (node && node->IsContent() && node->AsContent()->GetEditingHost()) {
977
0
      // A contenteditable node with user-select:none, for example.
978
0
      // Allow it to have a collapsed selection (for the caret).
979
0
      aItem->Collapse(GetDirection() == eDirPrevious);
980
0
      aRangesToAdd.AppendElement(aItem);
981
0
    }
982
0
  }
983
0
}
984
985
nsresult
986
Selection::AddItem(nsRange* aItem, int32_t* aOutIndex, bool aNoStartSelect)
987
0
{
988
0
  if (!aItem)
989
0
    return NS_ERROR_NULL_POINTER;
990
0
  if (!aItem->IsPositioned())
991
0
    return NS_ERROR_UNEXPECTED;
992
0
993
0
  NS_ASSERTION(aOutIndex, "aOutIndex can't be null");
994
0
995
0
  if (mUserInitiated) {
996
0
    AutoTArray<RefPtr<nsRange>, 4> rangesToAdd;
997
0
    *aOutIndex = int32_t(mRanges.Length()) - 1;
998
0
999
0
    nsIDocument* doc = GetParentObject();
1000
0
    bool selectEventsEnabled =
1001
0
      nsFrameSelection::sSelectionEventsEnabled ||
1002
0
      (doc && nsContentUtils::IsSystemPrincipal(doc->NodePrincipal()));
1003
0
1004
0
    if (!aNoStartSelect &&
1005
0
        mSelectionType == SelectionType::eNormal &&
1006
0
        selectEventsEnabled && IsCollapsed() &&
1007
0
        !IsBlockingSelectionChangeEvents()) {
1008
0
      // First, we generate the ranges to add with a scratch range, which is a
1009
0
      // clone of the original range passed in. We do this seperately, because the
1010
0
      // selectstart event could have caused the world to change, and required
1011
0
      // ranges to be re-generated
1012
0
      RefPtr<nsRange> scratchRange = aItem->CloneRange();
1013
0
      UserSelectRangesToAdd(scratchRange, rangesToAdd);
1014
0
      bool newRangesNonEmpty = rangesToAdd.Length() > 1 ||
1015
0
        (rangesToAdd.Length() == 1 && !rangesToAdd[0]->Collapsed());
1016
0
1017
0
      MOZ_ASSERT(!newRangesNonEmpty || nsContentUtils::IsSafeToRunScript());
1018
0
      if (newRangesNonEmpty && nsContentUtils::IsSafeToRunScript()) {
1019
0
        // We consider a selection to be starting if we are currently collapsed,
1020
0
        // and the selection is becoming uncollapsed, and this is caused by a user
1021
0
        // initiated event.
1022
0
        bool defaultAction = true;
1023
0
1024
0
        // The spec currently doesn't say that we should dispatch this event
1025
0
        // on text controls, so for now we only support doing that under a
1026
0
        // pref, disabled by default.
1027
0
        // See https://github.com/w3c/selection-api/issues/53.
1028
0
        bool dispatchEvent = true;
1029
0
        nsCOMPtr<nsINode> target = aItem->GetStartContainer();
1030
0
        if (nsFrameSelection::sSelectionEventsOnTextControlsEnabled) {
1031
0
          // Get the first element which isn't in a native anonymous subtree
1032
0
          while (target && target->IsInNativeAnonymousSubtree()) {
1033
0
            target = target->GetParent();
1034
0
          }
1035
0
        } else {
1036
0
          if (target->IsInNativeAnonymousSubtree()) {
1037
0
            // This is a selection under a text control, so don't dispatch the
1038
0
            // event.
1039
0
            dispatchEvent = false;
1040
0
          }
1041
0
        }
1042
0
1043
0
        if (dispatchEvent) {
1044
0
          nsContentUtils::DispatchTrustedEvent(GetParentObject(), target,
1045
0
                                               NS_LITERAL_STRING("selectstart"),
1046
0
                                               CanBubble::eYes, Cancelable::eYes,
1047
0
                                               &defaultAction);
1048
0
1049
0
          if (!defaultAction) {
1050
0
            return NS_OK;
1051
0
          }
1052
0
1053
0
          // As we just dispatched an event to the DOM, something could have
1054
0
          // changed under our feet. Re-generate the rangesToAdd array, and ensure
1055
0
          // that the range we are about to add is still valid.
1056
0
          if (!aItem->IsPositioned()) {
1057
0
            return NS_ERROR_UNEXPECTED;
1058
0
          }
1059
0
        }
1060
0
      }
1061
0
1062
0
      // The scratch ranges we generated may be invalid now, throw them out
1063
0
      rangesToAdd.ClearAndRetainStorage();
1064
0
    }
1065
0
1066
0
    // Generate the ranges to add
1067
0
    UserSelectRangesToAdd(aItem, rangesToAdd);
1068
0
    size_t newAnchorFocusIndex =
1069
0
      GetDirection() == eDirPrevious ? 0 : rangesToAdd.Length() - 1;
1070
0
    for (size_t i = 0; i < rangesToAdd.Length(); ++i) {
1071
0
      int32_t index;
1072
0
      nsresult rv = AddItemInternal(rangesToAdd[i], &index);
1073
0
      NS_ENSURE_SUCCESS(rv, rv);
1074
0
      if (i == newAnchorFocusIndex) {
1075
0
        *aOutIndex = index;
1076
0
        rangesToAdd[i]->SetIsGenerated(false);
1077
0
      } else {
1078
0
        rangesToAdd[i]->SetIsGenerated(true);
1079
0
      }
1080
0
    }
1081
0
    return NS_OK;
1082
0
  }
1083
0
  return AddItemInternal(aItem, aOutIndex);
1084
0
}
1085
1086
nsresult
1087
Selection::AddItemInternal(nsRange* aItem, int32_t* aOutIndex)
1088
0
{
1089
0
  MOZ_ASSERT(aItem);
1090
0
  MOZ_ASSERT(aItem->IsPositioned());
1091
0
  MOZ_ASSERT(aOutIndex);
1092
0
1093
0
  *aOutIndex = -1;
1094
0
1095
0
  // a common case is that we have no ranges yet
1096
0
  if (mRanges.Length() == 0) {
1097
0
    if (!mRanges.AppendElement(RangeData(aItem)))
1098
0
      return NS_ERROR_OUT_OF_MEMORY;
1099
0
    aItem->SetSelection(this);
1100
0
1101
0
    *aOutIndex = 0;
1102
0
    return NS_OK;
1103
0
  }
1104
0
1105
0
  int32_t startIndex, endIndex;
1106
0
  nsresult rv = GetIndicesForInterval(aItem->GetStartContainer(),
1107
0
                                      aItem->StartOffset(),
1108
0
                                      aItem->GetEndContainer(),
1109
0
                                      aItem->EndOffset(), false,
1110
0
                                      &startIndex, &endIndex);
1111
0
  NS_ENSURE_SUCCESS(rv, rv);
1112
0
1113
0
  if (endIndex == -1) {
1114
0
    // All ranges start after the given range. We can insert our range at
1115
0
    // position 0, knowing there are no overlaps (handled below)
1116
0
    startIndex = 0;
1117
0
    endIndex = 0;
1118
0
  } else if (startIndex == -1) {
1119
0
    // All ranges end before the given range. We can insert our range at
1120
0
    // the end of the array, knowing there are no overlaps (handled below)
1121
0
    startIndex = mRanges.Length();
1122
0
    endIndex = startIndex;
1123
0
  }
1124
0
1125
0
  // If the range is already contained in mRanges, silently succeed
1126
0
  bool sameRange = EqualsRangeAtPoint(aItem->GetStartContainer(),
1127
0
                                      aItem->StartOffset(),
1128
0
                                      aItem->GetEndContainer(),
1129
0
                                      aItem->EndOffset(), startIndex);
1130
0
  if (sameRange) {
1131
0
    *aOutIndex = startIndex;
1132
0
    return NS_OK;
1133
0
  }
1134
0
1135
0
  if (startIndex == endIndex) {
1136
0
    // The new range doesn't overlap any existing ranges
1137
0
    if (!mRanges.InsertElementAt(startIndex, RangeData(aItem)))
1138
0
      return NS_ERROR_OUT_OF_MEMORY;
1139
0
    aItem->SetSelection(this);
1140
0
    *aOutIndex = startIndex;
1141
0
    return NS_OK;
1142
0
  }
1143
0
1144
0
  // We now know that at least 1 existing range overlaps with the range that
1145
0
  // we are trying to add. In fact, the only ranges of interest are those at
1146
0
  // the two end points, startIndex and endIndex - 1 (which may point to the
1147
0
  // same range) as these may partially overlap the new range. Any ranges
1148
0
  // between these indices are fully overlapped by the new range, and so can be
1149
0
  // removed
1150
0
  nsTArray<RangeData> overlaps;
1151
0
  if (!overlaps.InsertElementAt(0, mRanges[startIndex]))
1152
0
    return NS_ERROR_OUT_OF_MEMORY;
1153
0
1154
0
  if (endIndex - 1 != startIndex) {
1155
0
    if (!overlaps.InsertElementAt(1, mRanges[endIndex - 1]))
1156
0
      return NS_ERROR_OUT_OF_MEMORY;
1157
0
  }
1158
0
1159
0
  // Remove all the overlapping ranges
1160
0
  for (int32_t i = startIndex; i < endIndex; ++i) {
1161
0
    mRanges[i].mRange->SetSelection(nullptr);
1162
0
  }
1163
0
  mRanges.RemoveElementsAt(startIndex, endIndex - startIndex);
1164
0
1165
0
  nsTArray<RangeData> temp;
1166
0
  for (int32_t i = overlaps.Length() - 1; i >= 0; i--) {
1167
0
    nsresult rv = SubtractRange(&overlaps[i], aItem, &temp);
1168
0
    NS_ENSURE_SUCCESS(rv, rv);
1169
0
  }
1170
0
1171
0
  // Insert the new element into our "leftovers" array
1172
0
  int32_t insertionPoint;
1173
0
  rv = FindInsertionPoint(&temp, aItem->GetStartContainer(),
1174
0
                          aItem->StartOffset(), CompareToRangeStart,
1175
0
                          &insertionPoint);
1176
0
  NS_ENSURE_SUCCESS(rv, rv);
1177
0
1178
0
  if (!temp.InsertElementAt(insertionPoint, RangeData(aItem)))
1179
0
    return NS_ERROR_OUT_OF_MEMORY;
1180
0
1181
0
  // Merge the leftovers back in to mRanges
1182
0
  if (!mRanges.InsertElementsAt(startIndex, temp))
1183
0
    return NS_ERROR_OUT_OF_MEMORY;
1184
0
1185
0
  for (uint32_t i = 0; i < temp.Length(); ++i) {
1186
0
    temp[i].mRange->SetSelection(this);
1187
0
  }
1188
0
1189
0
  *aOutIndex = startIndex + insertionPoint;
1190
0
  return NS_OK;
1191
0
}
1192
1193
nsresult
1194
Selection::RemoveItem(nsRange* aItem)
1195
0
{
1196
0
  if (!aItem)
1197
0
    return NS_ERROR_NULL_POINTER;
1198
0
1199
0
  // Find the range's index & remove it. We could use FindInsertionPoint to
1200
0
  // get O(log n) time, but that requires many expensive DOM comparisons.
1201
0
  // For even several thousand items, this is probably faster because the
1202
0
  // comparisons are so fast.
1203
0
  int32_t idx = -1;
1204
0
  uint32_t i;
1205
0
  for (i = 0; i < mRanges.Length(); i ++) {
1206
0
    if (mRanges[i].mRange == aItem) {
1207
0
      idx = (int32_t)i;
1208
0
      break;
1209
0
    }
1210
0
  }
1211
0
  if (idx < 0)
1212
0
    return NS_ERROR_DOM_NOT_FOUND_ERR;
1213
0
1214
0
  mRanges.RemoveElementAt(idx);
1215
0
  aItem->SetSelection(nullptr);
1216
0
  return NS_OK;
1217
0
}
1218
1219
nsresult
1220
Selection::RemoveCollapsedRanges()
1221
0
{
1222
0
  uint32_t i = 0;
1223
0
  while (i < mRanges.Length()) {
1224
0
    if (mRanges[i].mRange->Collapsed()) {
1225
0
      nsresult rv = RemoveItem(mRanges[i].mRange);
1226
0
      NS_ENSURE_SUCCESS(rv, rv);
1227
0
    } else {
1228
0
      ++i;
1229
0
    }
1230
0
  }
1231
0
  return NS_OK;
1232
0
}
1233
1234
nsresult
1235
Selection::Clear(nsPresContext* aPresContext)
1236
0
{
1237
0
  SetAnchorFocusRange(-1);
1238
0
1239
0
  for (uint32_t i = 0; i < mRanges.Length(); ++i) {
1240
0
    mRanges[i].mRange->SetSelection(nullptr);
1241
0
    SelectFrames(aPresContext, mRanges[i].mRange, false);
1242
0
  }
1243
0
  mRanges.Clear();
1244
0
1245
0
  // Reset direction so for more dependable table selection range handling
1246
0
  SetDirection(eDirNext);
1247
0
1248
0
  // If this was an ATTENTION selection, change it back to normal now
1249
0
  if (mFrameSelection &&
1250
0
      mFrameSelection->GetDisplaySelection() ==
1251
0
      nsISelectionController::SELECTION_ATTENTION) {
1252
0
    mFrameSelection->SetDisplaySelection(nsISelectionController::SELECTION_ON);
1253
0
  }
1254
0
1255
0
  return NS_OK;
1256
0
}
1257
1258
// RangeMatches*Point
1259
//
1260
//    Compares the range beginning or ending point, and returns true if it
1261
//    exactly matches the given DOM point.
1262
1263
static inline bool
1264
RangeMatchesBeginPoint(nsRange* aRange, nsINode* aNode, int32_t aOffset)
1265
0
{
1266
0
  return aRange->GetStartContainer() == aNode &&
1267
0
         static_cast<int32_t>(aRange->StartOffset()) == aOffset;
1268
0
}
1269
1270
static inline bool
1271
RangeMatchesEndPoint(nsRange* aRange, nsINode* aNode, int32_t aOffset)
1272
0
{
1273
0
  return aRange->GetEndContainer() == aNode &&
1274
0
         static_cast<int32_t>(aRange->EndOffset()) == aOffset;
1275
0
}
1276
1277
// Selection::EqualsRangeAtPoint
1278
//
1279
//    Utility method for checking equivalence of two ranges.
1280
1281
bool
1282
Selection::EqualsRangeAtPoint(
1283
    nsINode* aBeginNode, int32_t aBeginOffset,
1284
    nsINode* aEndNode, int32_t aEndOffset,
1285
    int32_t aRangeIndex)
1286
0
{
1287
0
  if (aRangeIndex >=0 && aRangeIndex < (int32_t) mRanges.Length()) {
1288
0
    nsRange* range = mRanges[aRangeIndex].mRange;
1289
0
    if (RangeMatchesBeginPoint(range, aBeginNode, aBeginOffset) &&
1290
0
        RangeMatchesEndPoint(range, aEndNode, aEndOffset))
1291
0
      return true;
1292
0
  }
1293
0
  return false;
1294
0
}
1295
1296
void
1297
Selection::GetRangesForInterval(nsINode& aBeginNode, int32_t aBeginOffset,
1298
                                nsINode& aEndNode, int32_t aEndOffset,
1299
                                bool aAllowAdjacent,
1300
                                nsTArray<RefPtr<nsRange>>& aReturn,
1301
                                mozilla::ErrorResult& aRv)
1302
0
{
1303
0
  nsTArray<nsRange*> results;
1304
0
  nsresult rv = GetRangesForIntervalArray(&aBeginNode, aBeginOffset,
1305
0
                                          &aEndNode, aEndOffset,
1306
0
                                          aAllowAdjacent, &results);
1307
0
  if (NS_FAILED(rv)) {
1308
0
    aRv.Throw(rv);
1309
0
    return;
1310
0
  }
1311
0
1312
0
  aReturn.SetLength(results.Length());
1313
0
  for (uint32_t i = 0; i < results.Length(); ++i) {
1314
0
    aReturn[i] = results[i]; // AddRefs
1315
0
  }
1316
0
}
1317
1318
nsresult
1319
Selection::GetRangesForIntervalArray(nsINode* aBeginNode, int32_t aBeginOffset,
1320
                                     nsINode* aEndNode, int32_t aEndOffset,
1321
                                     bool aAllowAdjacent,
1322
                                     nsTArray<nsRange*>* aRanges)
1323
0
{
1324
0
  aRanges->Clear();
1325
0
  int32_t startIndex, endIndex;
1326
0
  nsresult res = GetIndicesForInterval(aBeginNode, aBeginOffset,
1327
0
                                       aEndNode, aEndOffset, aAllowAdjacent,
1328
0
                                       &startIndex, &endIndex);
1329
0
  NS_ENSURE_SUCCESS(res, res);
1330
0
1331
0
  if (startIndex == -1 || endIndex == -1)
1332
0
    return NS_OK;
1333
0
1334
0
  for (int32_t i = startIndex; i < endIndex; i++) {
1335
0
    if (!aRanges->AppendElement(mRanges[i].mRange))
1336
0
      return NS_ERROR_OUT_OF_MEMORY;
1337
0
  }
1338
0
1339
0
  return NS_OK;
1340
0
}
1341
1342
// Selection::GetIndicesForInterval
1343
//
1344
//    Works on the same principle as GetRangesForIntervalArray above, however
1345
//    instead this returns the indices into mRanges between which the
1346
//    overlapping ranges lie.
1347
1348
nsresult
1349
Selection::GetIndicesForInterval(nsINode* aBeginNode, int32_t aBeginOffset,
1350
                                 nsINode* aEndNode, int32_t aEndOffset,
1351
                                 bool aAllowAdjacent,
1352
                                 int32_t* aStartIndex, int32_t* aEndIndex)
1353
0
{
1354
0
  int32_t startIndex;
1355
0
  int32_t endIndex;
1356
0
1357
0
  if (!aStartIndex)
1358
0
    aStartIndex = &startIndex;
1359
0
  if (!aEndIndex)
1360
0
    aEndIndex = &endIndex;
1361
0
1362
0
  *aStartIndex = -1;
1363
0
  *aEndIndex = -1;
1364
0
1365
0
  if (mRanges.Length() == 0)
1366
0
    return NS_OK;
1367
0
1368
0
  bool intervalIsCollapsed = aBeginNode == aEndNode &&
1369
0
    aBeginOffset == aEndOffset;
1370
0
1371
0
  // Ranges that end before the given interval and begin after the given
1372
0
  // interval can be discarded
1373
0
  int32_t endsBeforeIndex;
1374
0
  if (NS_FAILED(FindInsertionPoint(&mRanges, aEndNode, aEndOffset,
1375
0
                                   &CompareToRangeStart,
1376
0
                                   &endsBeforeIndex))) {
1377
0
    return NS_OK;
1378
0
  }
1379
0
1380
0
  if (endsBeforeIndex == 0) {
1381
0
    nsRange* endRange = mRanges[endsBeforeIndex].mRange;
1382
0
1383
0
    // If the interval is strictly before the range at index 0, we can optimize
1384
0
    // by returning now - all ranges start after the given interval
1385
0
    if (!RangeMatchesBeginPoint(endRange, aEndNode, aEndOffset))
1386
0
      return NS_OK;
1387
0
1388
0
    // We now know that the start point of mRanges[0].mRange equals the end of
1389
0
    // the interval. Thus, when aAllowadjacent is true, the caller is always
1390
0
    // interested in this range. However, when excluding adjacencies, we must
1391
0
    // remember to include the range when both it and the given interval are
1392
0
    // collapsed to the same point
1393
0
    if (!aAllowAdjacent && !(endRange->Collapsed() && intervalIsCollapsed))
1394
0
      return NS_OK;
1395
0
  }
1396
0
  *aEndIndex = endsBeforeIndex;
1397
0
1398
0
  int32_t beginsAfterIndex;
1399
0
  if (NS_FAILED(FindInsertionPoint(&mRanges, aBeginNode, aBeginOffset,
1400
0
                                   &CompareToRangeEnd,
1401
0
                                   &beginsAfterIndex))) {
1402
0
    return NS_OK;
1403
0
  }
1404
0
  if (beginsAfterIndex == (int32_t) mRanges.Length())
1405
0
    return NS_OK; // optimization: all ranges are strictly before us
1406
0
1407
0
  if (aAllowAdjacent) {
1408
0
    // At this point, one of the following holds:
1409
0
    //   endsBeforeIndex == mRanges.Length(),
1410
0
    //   endsBeforeIndex points to a range whose start point does not equal the
1411
0
    //     given interval's start point
1412
0
    //   endsBeforeIndex points to a range whose start point equals the given
1413
0
    //     interval's start point
1414
0
    // In the final case, there can be two such ranges, a collapsed range, and
1415
0
    // an adjacent range (they will appear in mRanges in that order). For this
1416
0
    // final case, we need to increment endsBeforeIndex, until one of the
1417
0
    // first two possibilites hold
1418
0
    while (endsBeforeIndex < (int32_t) mRanges.Length()) {
1419
0
      nsRange* endRange = mRanges[endsBeforeIndex].mRange;
1420
0
      if (!RangeMatchesBeginPoint(endRange, aEndNode, aEndOffset))
1421
0
        break;
1422
0
      endsBeforeIndex++;
1423
0
    }
1424
0
1425
0
    // Likewise, one of the following holds:
1426
0
    //   beginsAfterIndex == 0,
1427
0
    //   beginsAfterIndex points to a range whose end point does not equal
1428
0
    //     the given interval's end point
1429
0
    //   beginsOnOrAfter points to a range whose end point equals the given
1430
0
    //     interval's end point
1431
0
    // In the final case, there can be two such ranges, an adjacent range, and
1432
0
    // a collapsed range (they will appear in mRanges in that order). For this
1433
0
    // final case, we only need to take action if both those ranges exist, and
1434
0
    // we are pointing to the collapsed range - we need to point to the
1435
0
    // adjacent range
1436
0
    nsRange* beginRange = mRanges[beginsAfterIndex].mRange;
1437
0
    if (beginsAfterIndex > 0 && beginRange->Collapsed() &&
1438
0
        RangeMatchesEndPoint(beginRange, aBeginNode, aBeginOffset)) {
1439
0
      beginRange = mRanges[beginsAfterIndex - 1].mRange;
1440
0
      if (RangeMatchesEndPoint(beginRange, aBeginNode, aBeginOffset))
1441
0
        beginsAfterIndex--;
1442
0
    }
1443
0
  } else {
1444
0
    // See above for the possibilities at this point. The only case where we
1445
0
    // need to take action is when the range at beginsAfterIndex ends on
1446
0
    // the given interval's start point, but that range isn't collapsed (a
1447
0
    // collapsed range should be included in the returned results).
1448
0
    nsRange* beginRange = mRanges[beginsAfterIndex].mRange;
1449
0
    if (RangeMatchesEndPoint(beginRange, aBeginNode, aBeginOffset) &&
1450
0
        !beginRange->Collapsed())
1451
0
      beginsAfterIndex++;
1452
0
1453
0
    // Again, see above for the meaning of endsBeforeIndex at this point.
1454
0
    // In particular, endsBeforeIndex may point to a collaped range which
1455
0
    // represents the point at the end of the interval - this range should be
1456
0
    // included
1457
0
    if (endsBeforeIndex < (int32_t) mRanges.Length()) {
1458
0
      nsRange* endRange = mRanges[endsBeforeIndex].mRange;
1459
0
      if (RangeMatchesBeginPoint(endRange, aEndNode, aEndOffset) &&
1460
0
          endRange->Collapsed())
1461
0
        endsBeforeIndex++;
1462
0
     }
1463
0
  }
1464
0
1465
0
  NS_ASSERTION(beginsAfterIndex <= endsBeforeIndex,
1466
0
               "Is mRanges not ordered?");
1467
0
  NS_ENSURE_STATE(beginsAfterIndex <= endsBeforeIndex);
1468
0
1469
0
  *aStartIndex = beginsAfterIndex;
1470
0
  *aEndIndex = endsBeforeIndex;
1471
0
  return NS_OK;
1472
0
}
1473
1474
nsresult
1475
Selection::GetPrimaryFrameForAnchorNode(nsIFrame** aReturnFrame)
1476
0
{
1477
0
  if (!aReturnFrame)
1478
0
    return NS_ERROR_NULL_POINTER;
1479
0
1480
0
  int32_t frameOffset = 0;
1481
0
  *aReturnFrame = 0;
1482
0
  nsCOMPtr<nsIContent> content = do_QueryInterface(GetAnchorNode());
1483
0
  if (content && mFrameSelection)
1484
0
  {
1485
0
    *aReturnFrame = mFrameSelection->
1486
0
      GetFrameForNodeOffset(content, AnchorOffset(),
1487
0
                            mFrameSelection->GetHint(), &frameOffset);
1488
0
    if (*aReturnFrame)
1489
0
      return NS_OK;
1490
0
  }
1491
0
  return NS_ERROR_FAILURE;
1492
0
}
1493
1494
nsresult
1495
Selection::GetPrimaryFrameForFocusNode(nsIFrame** aReturnFrame,
1496
                                       int32_t* aOffsetUsed,
1497
                                       bool aVisual)
1498
0
{
1499
0
  if (!aReturnFrame) {
1500
0
    return NS_ERROR_NULL_POINTER;
1501
0
  }
1502
0
1503
0
  *aReturnFrame = nullptr;
1504
0
  nsINode* focusNode = GetFocusNode();
1505
0
  if (!focusNode || !focusNode->IsContent() || !mFrameSelection) {
1506
0
    return NS_ERROR_FAILURE;
1507
0
  }
1508
0
1509
0
  nsCOMPtr<nsIContent> content = focusNode->AsContent();
1510
0
  int32_t frameOffset = 0;
1511
0
  if (!aOffsetUsed) {
1512
0
    aOffsetUsed = &frameOffset;
1513
0
  }
1514
0
1515
0
  nsresult rv =
1516
0
    GetPrimaryOrCaretFrameForNodeOffset(content, FocusOffset(), aReturnFrame,
1517
0
                                        aOffsetUsed, aVisual);
1518
0
  if (NS_SUCCEEDED(rv)) {
1519
0
    return rv;
1520
0
  }
1521
0
1522
0
  // If content is whitespace only, we promote focus node to parent because
1523
0
  // whitespace only node might have no frame.
1524
0
1525
0
  if (!content->TextIsOnlyWhitespace()) {
1526
0
    return NS_ERROR_FAILURE;
1527
0
  }
1528
0
1529
0
  nsCOMPtr<nsIContent> parent = content->GetParent();
1530
0
  if (NS_WARN_IF(!parent)) {
1531
0
    return NS_ERROR_FAILURE;
1532
0
  }
1533
0
  int32_t offset = parent->ComputeIndexOf(content);
1534
0
1535
0
  return GetPrimaryOrCaretFrameForNodeOffset(parent, offset, aReturnFrame,
1536
0
                                             aOffsetUsed, aVisual);
1537
0
}
1538
1539
nsresult
1540
Selection::GetPrimaryOrCaretFrameForNodeOffset(nsIContent* aContent,
1541
                                               uint32_t aOffset,
1542
                                               nsIFrame** aReturnFrame,
1543
                                               int32_t* aOffsetUsed,
1544
                                               bool aVisual) const
1545
0
{
1546
0
  MOZ_ASSERT(aReturnFrame);
1547
0
  MOZ_ASSERT(aOffsetUsed);
1548
0
1549
0
  *aReturnFrame = nullptr;
1550
0
1551
0
  if (!mFrameSelection) {
1552
0
    return NS_ERROR_FAILURE;
1553
0
  }
1554
0
1555
0
  CaretAssociationHint hint = mFrameSelection->GetHint();
1556
0
1557
0
  if (aVisual) {
1558
0
    nsBidiLevel caretBidiLevel = mFrameSelection->GetCaretBidiLevel();
1559
0
1560
0
    return nsCaret::GetCaretFrameForNodeOffset(mFrameSelection,
1561
0
                                               aContent, aOffset, hint,
1562
0
                                               caretBidiLevel, aReturnFrame,
1563
0
                                               aOffsetUsed);
1564
0
  }
1565
0
1566
0
  *aReturnFrame =
1567
0
    mFrameSelection->GetFrameForNodeOffset(aContent, aOffset,
1568
0
                                           hint, aOffsetUsed);
1569
0
  if (!*aReturnFrame) {
1570
0
    return NS_ERROR_FAILURE;
1571
0
  }
1572
0
1573
0
  return NS_OK;
1574
0
}
1575
1576
void
1577
Selection::SelectFramesForContent(nsIContent* aContent,
1578
                                  bool aSelected)
1579
0
{
1580
0
  nsIFrame* frame = aContent->GetPrimaryFrame();
1581
0
  if (!frame) {
1582
0
    return;
1583
0
  }
1584
0
  // The frame could be an SVG text frame, in which case we don't treat it
1585
0
  // as a text frame.
1586
0
  if (frame->IsTextFrame()) {
1587
0
    nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
1588
0
    textFrame->SetSelectedRange(0, aContent->GetText()->GetLength(),
1589
0
                                aSelected, mSelectionType);
1590
0
  } else {
1591
0
    frame->InvalidateFrameSubtree();  // frame continuations?
1592
0
  }
1593
0
}
1594
1595
//select all content children of aContent
1596
nsresult
1597
Selection::SelectAllFramesForContent(nsIContentIterator* aInnerIter,
1598
                                     nsIContent* aContent,
1599
                                     bool aSelected)
1600
0
{
1601
0
  // If aContent doesn't have children, we should avoid to use the content
1602
0
  // iterator for performance reason.
1603
0
  if (!aContent->HasChildren()) {
1604
0
    SelectFramesForContent(aContent, aSelected);
1605
0
    return NS_OK;
1606
0
  }
1607
0
1608
0
  if (NS_WARN_IF(NS_FAILED(aInnerIter->Init(aContent)))) {
1609
0
    return NS_ERROR_FAILURE;
1610
0
  }
1611
0
1612
0
  for (; !aInnerIter->IsDone(); aInnerIter->Next()) {
1613
0
    nsINode* node = aInnerIter->GetCurrentNode();
1614
0
    MOZ_ASSERT(node);
1615
0
    nsIContent* innercontent = node->IsContent() ? node->AsContent() : nullptr;
1616
0
    SelectFramesForContent(innercontent, aSelected);
1617
0
  }
1618
0
1619
0
  return NS_OK;
1620
0
}
1621
1622
/**
1623
 * The idea of this helper method is to select or deselect "top to bottom",
1624
 * traversing through the frames
1625
 */
1626
nsresult
1627
Selection::SelectFrames(nsPresContext* aPresContext, nsRange* aRange,
1628
                        bool aSelect)
1629
0
{
1630
0
  if (!mFrameSelection || !aPresContext || !aPresContext->GetPresShell()) {
1631
0
    // nothing to do
1632
0
    return NS_OK;
1633
0
  }
1634
0
  MOZ_ASSERT(aRange && aRange->IsPositioned());
1635
0
1636
0
  if (mFrameSelection->GetTableCellSelection()) {
1637
0
    nsINode* node = aRange->GetCommonAncestor();
1638
0
    nsIFrame* frame = node->IsContent() ? node->AsContent()->GetPrimaryFrame()
1639
0
                                : aPresContext->PresShell()->GetRootFrame();
1640
0
    if (frame) {
1641
0
      frame->InvalidateFrameSubtree();
1642
0
    }
1643
0
    return NS_OK;
1644
0
  }
1645
0
1646
0
1647
0
  // Loop through the content iterator for each content node; for each text
1648
0
  // node, call SetSelected on it:
1649
0
  nsINode* startNode = aRange->GetStartContainer();
1650
0
  nsIContent* startContent =
1651
0
    startNode->IsContent() ? startNode->AsContent() : nullptr;
1652
0
  if (!startContent) {
1653
0
    // Don't warn, bug 1055722
1654
0
    // XXX The range can start from a document node and such range can be
1655
0
    //     added to Selection with JS.  Therefore, even in such cases,
1656
0
    //     shouldn't we handle selection in the range?
1657
0
    return NS_ERROR_UNEXPECTED;
1658
0
  }
1659
0
1660
0
  // We must call first one explicitly
1661
0
  bool isFirstContentTextNode = startContent->IsText();
1662
0
  nsINode* endNode = aRange->GetEndContainer();
1663
0
  if (isFirstContentTextNode) {
1664
0
    nsIFrame* frame = startContent->GetPrimaryFrame();
1665
0
    // The frame could be an SVG text frame, in which case we don't treat it
1666
0
    // as a text frame.
1667
0
    if (frame) {
1668
0
      if (frame->IsTextFrame()) {
1669
0
        nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
1670
0
        uint32_t startOffset = aRange->StartOffset();
1671
0
        uint32_t endOffset;
1672
0
        if (endNode == startContent) {
1673
0
          endOffset = aRange->EndOffset();
1674
0
        } else {
1675
0
          endOffset = startContent->Length();
1676
0
        }
1677
0
        textFrame->SetSelectedRange(startOffset, endOffset, aSelect,
1678
0
                                    mSelectionType);
1679
0
      } else {
1680
0
        frame->InvalidateFrameSubtree();
1681
0
      }
1682
0
    }
1683
0
  }
1684
0
1685
0
  // If the range is in a node and the node is a leaf node, we don't need to
1686
0
  // walk the subtree.
1687
0
  if (aRange->Collapsed() ||
1688
0
      (startNode == endNode && !startNode->HasChildren())) {
1689
0
    if (!isFirstContentTextNode) {
1690
0
      SelectFramesForContent(startContent, aSelect);
1691
0
    }
1692
0
    return NS_OK;
1693
0
  }
1694
0
1695
0
  nsCOMPtr<nsIContentIterator> iter = NS_NewContentSubtreeIterator();
1696
0
  iter->Init(aRange);
1697
0
  if (isFirstContentTextNode && !iter->IsDone() &&
1698
0
      iter->GetCurrentNode() == startNode) {
1699
0
    iter->Next(); // first content has already been handled.
1700
0
  }
1701
0
  nsCOMPtr<nsIContentIterator> inneriter = NS_NewContentIterator();
1702
0
  for (; !iter->IsDone(); iter->Next()) {
1703
0
    nsINode* node = iter->GetCurrentNode();
1704
0
    MOZ_ASSERT(node);
1705
0
    nsIContent* content = node->IsContent() ? node->AsContent() : nullptr;
1706
0
    SelectAllFramesForContent(inneriter, content, aSelect);
1707
0
  }
1708
0
1709
0
  // We must now do the last one if it is not the same as the first
1710
0
  if (endNode != startNode) {
1711
0
    nsIContent* endContent =
1712
0
      endNode->IsContent() ? endNode->AsContent() : nullptr;
1713
0
    // XXX The range can end at a document node and such range can be
1714
0
    //     added to Selection with JS.  Therefore, even in such cases,
1715
0
    //     shouldn't we handle selection in the range?
1716
0
    if (NS_WARN_IF(!endContent)) {
1717
0
      return NS_ERROR_UNEXPECTED;
1718
0
    }
1719
0
    if (endContent->IsText()) {
1720
0
      nsIFrame* frame = endContent->GetPrimaryFrame();
1721
0
      // The frame could be an SVG text frame, in which case we'll ignore it.
1722
0
      if (frame && frame->IsTextFrame()) {
1723
0
        nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
1724
0
        textFrame->SetSelectedRange(0, aRange->EndOffset(), aSelect,
1725
0
                                    mSelectionType);
1726
0
      }
1727
0
    }
1728
0
  }
1729
0
  return NS_OK;
1730
0
}
1731
1732
1733
// Selection::LookUpSelection
1734
//
1735
//    This function is called when a node wants to know where the selection is
1736
//    over itself.
1737
//
1738
//    Usually, this is called when we already know there is a selection over
1739
//    the node in question, and we only need to find the boundaries of it on
1740
//    that node. This is when slowCheck is false--a strict test is not needed.
1741
//    Other times, the caller has no idea, and wants us to test everything,
1742
//    so we are supposed to determine whether there is a selection over the
1743
//    node at all.
1744
//
1745
//    A previous version of this code used this flag to do less work when
1746
//    inclusion was already known (slowCheck=false). However, our tree
1747
//    structure allows us to quickly determine ranges overlapping the node,
1748
//    so we just ignore the slowCheck flag and do the full test every time.
1749
//
1750
//    PERFORMANCE: a common case is that we are doing a fast check with exactly
1751
//    one range in the selection. In this case, this function is slower than
1752
//    brute force because of the overhead of checking the tree. We can optimize
1753
//    this case to make it faster by doing the same thing the previous version
1754
//    of this function did in the case of 1 range. This would also mean that
1755
//    the aSlowCheck flag would have meaning again.
1756
1757
UniquePtr<SelectionDetails>
1758
Selection::LookUpSelection(nsIContent* aContent, int32_t aContentOffset,
1759
                           int32_t aContentLength,
1760
                           UniquePtr<SelectionDetails> aDetailsHead,
1761
                           SelectionType aSelectionType,
1762
                           bool aSlowCheck)
1763
0
{
1764
0
  if (!aContent) {
1765
0
    return aDetailsHead;
1766
0
  }
1767
0
1768
0
  // it is common to have no ranges, to optimize that
1769
0
  if (mRanges.Length() == 0) {
1770
0
    return aDetailsHead;
1771
0
  }
1772
0
1773
0
  nsTArray<nsRange*> overlappingRanges;
1774
0
  nsresult rv = GetRangesForIntervalArray(aContent, aContentOffset,
1775
0
                                          aContent, aContentOffset + aContentLength,
1776
0
                                          false,
1777
0
                                          &overlappingRanges);
1778
0
  if (NS_FAILED(rv)) {
1779
0
    return aDetailsHead;
1780
0
  }
1781
0
1782
0
  if (overlappingRanges.Length() == 0) {
1783
0
    return aDetailsHead;
1784
0
  }
1785
0
1786
0
  UniquePtr<SelectionDetails> detailsHead = std::move(aDetailsHead);
1787
0
1788
0
  for (uint32_t i = 0; i < overlappingRanges.Length(); i++) {
1789
0
    nsRange* range = overlappingRanges[i];
1790
0
    nsINode* startNode = range->GetStartContainer();
1791
0
    nsINode* endNode = range->GetEndContainer();
1792
0
    int32_t startOffset = range->StartOffset();
1793
0
    int32_t endOffset = range->EndOffset();
1794
0
1795
0
    int32_t start = -1, end = -1;
1796
0
    if (startNode == aContent && endNode == aContent) {
1797
0
      if (startOffset < (aContentOffset + aContentLength)  &&
1798
0
          endOffset > aContentOffset) {
1799
0
        // this range is totally inside the requested content range
1800
0
        start = std::max(0, startOffset - aContentOffset);
1801
0
        end = std::min(aContentLength, endOffset - aContentOffset);
1802
0
      }
1803
0
      // otherwise, range is inside the requested node, but does not intersect
1804
0
      // the requested content range, so ignore it
1805
0
    } else if (startNode == aContent) {
1806
0
      if (startOffset < (aContentOffset + aContentLength)) {
1807
0
        // the beginning of the range is inside the requested node, but the
1808
0
        // end is outside, select everything from there to the end
1809
0
        start = std::max(0, startOffset - aContentOffset);
1810
0
        end = aContentLength;
1811
0
      }
1812
0
    } else if (endNode == aContent) {
1813
0
      if (endOffset > aContentOffset) {
1814
0
        // the end of the range is inside the requested node, but the beginning
1815
0
        // is outside, select everything from the beginning to there
1816
0
        start = 0;
1817
0
        end = std::min(aContentLength, endOffset - aContentOffset);
1818
0
      }
1819
0
    } else {
1820
0
      // this range does not begin or end in the requested node, but since
1821
0
      // GetRangesForInterval returned this range, we know it overlaps.
1822
0
      // Therefore, this node is enclosed in the range, and we select all
1823
0
      // of it.
1824
0
      start = 0;
1825
0
      end = aContentLength;
1826
0
    }
1827
0
    if (start < 0)
1828
0
      continue; // the ranges do not overlap the input range
1829
0
1830
0
    auto newHead = MakeUnique<SelectionDetails>();
1831
0
1832
0
    newHead->mNext = std::move(detailsHead);
1833
0
    newHead->mStart = start;
1834
0
    newHead->mEnd = end;
1835
0
    newHead->mSelectionType = aSelectionType;
1836
0
    RangeData *rd = FindRangeData(range);
1837
0
    if (rd) {
1838
0
      newHead->mTextRangeStyle = rd->mTextRangeStyle;
1839
0
    }
1840
0
    detailsHead = std::move(newHead);
1841
0
  }
1842
0
  return detailsHead;
1843
0
}
1844
1845
NS_IMETHODIMP
1846
Selection::Repaint(nsPresContext* aPresContext)
1847
0
{
1848
0
  int32_t arrCount = (int32_t)mRanges.Length();
1849
0
1850
0
  if (arrCount < 1)
1851
0
    return NS_OK;
1852
0
1853
0
  int32_t i;
1854
0
1855
0
  for (i = 0; i < arrCount; i++)
1856
0
  {
1857
0
    nsresult rv = SelectFrames(aPresContext, mRanges[i].mRange, true);
1858
0
1859
0
    if (NS_FAILED(rv)) {
1860
0
      return rv;
1861
0
    }
1862
0
  }
1863
0
1864
0
  return NS_OK;
1865
0
}
1866
1867
void
1868
Selection::SetCanCacheFrameOffset(bool aCanCacheFrameOffset)
1869
0
{
1870
0
  if (!mCachedOffsetForFrame) {
1871
0
    mCachedOffsetForFrame = new CachedOffsetForFrame;
1872
0
  }
1873
0
1874
0
  mCachedOffsetForFrame->mCanCacheFrameOffset = aCanCacheFrameOffset;
1875
0
1876
0
  // clean up cached frame when turn off cache
1877
0
  // fix bug 207936
1878
0
  if (!aCanCacheFrameOffset) {
1879
0
    mCachedOffsetForFrame->mLastCaretFrame = nullptr;
1880
0
  }
1881
0
}
1882
1883
nsresult
1884
Selection::GetCachedFrameOffset(nsIFrame* aFrame, int32_t inOffset,
1885
                                nsPoint& aPoint)
1886
0
{
1887
0
  if (!mCachedOffsetForFrame) {
1888
0
    mCachedOffsetForFrame = new CachedOffsetForFrame;
1889
0
  }
1890
0
1891
0
  nsresult rv = NS_OK;
1892
0
  if (mCachedOffsetForFrame->mCanCacheFrameOffset &&
1893
0
      mCachedOffsetForFrame->mLastCaretFrame &&
1894
0
      (aFrame == mCachedOffsetForFrame->mLastCaretFrame) &&
1895
0
      (inOffset == mCachedOffsetForFrame->mLastContentOffset))
1896
0
  {
1897
0
     // get cached frame offset
1898
0
     aPoint = mCachedOffsetForFrame->mCachedFrameOffset;
1899
0
  }
1900
0
  else
1901
0
  {
1902
0
     // Recalculate frame offset and cache it. Don't cache a frame offset if
1903
0
     // GetPointFromOffset fails, though.
1904
0
     rv = aFrame->GetPointFromOffset(inOffset, &aPoint);
1905
0
     if (NS_SUCCEEDED(rv) && mCachedOffsetForFrame->mCanCacheFrameOffset) {
1906
0
       mCachedOffsetForFrame->mCachedFrameOffset = aPoint;
1907
0
       mCachedOffsetForFrame->mLastCaretFrame = aFrame;
1908
0
       mCachedOffsetForFrame->mLastContentOffset = inOffset;
1909
0
     }
1910
0
  }
1911
0
1912
0
  return rv;
1913
0
}
1914
1915
nsIContent*
1916
Selection::GetAncestorLimiter() const
1917
0
{
1918
0
  if (mFrameSelection) {
1919
0
    return mFrameSelection->GetAncestorLimiter();
1920
0
  }
1921
0
  return nullptr;
1922
0
}
1923
1924
void
1925
Selection::SetAncestorLimiter(nsIContent* aLimiter)
1926
0
{
1927
0
  if (mFrameSelection) {
1928
0
    RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
1929
0
    frameSelection->SetAncestorLimiter(aLimiter);
1930
0
  }
1931
0
}
1932
1933
RangeData*
1934
Selection::FindRangeData(nsRange* aRange)
1935
0
{
1936
0
  NS_ENSURE_TRUE(aRange, nullptr);
1937
0
  for (uint32_t i = 0; i < mRanges.Length(); i++) {
1938
0
    if (mRanges[i].mRange == aRange)
1939
0
      return &mRanges[i];
1940
0
  }
1941
0
  return nullptr;
1942
0
}
1943
1944
nsresult
1945
Selection::SetTextRangeStyle(nsRange* aRange,
1946
                             const TextRangeStyle& aTextRangeStyle)
1947
0
{
1948
0
  NS_ENSURE_ARG_POINTER(aRange);
1949
0
  RangeData *rd = FindRangeData(aRange);
1950
0
  if (rd) {
1951
0
    rd->mTextRangeStyle = aTextRangeStyle;
1952
0
  }
1953
0
  return NS_OK;
1954
0
}
1955
1956
nsresult
1957
Selection::StartAutoScrollTimer(nsIFrame* aFrame, const nsPoint& aPoint,
1958
                                uint32_t aDelay)
1959
0
{
1960
0
  MOZ_ASSERT(aFrame, "Need a frame");
1961
0
1962
0
  nsresult result;
1963
0
  if (!mFrameSelection) {
1964
0
    return NS_OK;//nothing to do
1965
0
  }
1966
0
1967
0
  if (!mAutoScrollTimer) {
1968
0
    mAutoScrollTimer = new nsAutoScrollTimer();
1969
0
1970
0
    result = mAutoScrollTimer->Init(mFrameSelection, this);
1971
0
1972
0
    if (NS_FAILED(result)) {
1973
0
      return result;
1974
0
    }
1975
0
  }
1976
0
1977
0
  result = mAutoScrollTimer->SetDelay(aDelay);
1978
0
1979
0
  if (NS_FAILED(result)) {
1980
0
    return result;
1981
0
  }
1982
0
1983
0
  return DoAutoScroll(aFrame, aPoint);
1984
0
}
1985
1986
nsresult
1987
Selection::StopAutoScrollTimer()
1988
0
{
1989
0
  if (mAutoScrollTimer) {
1990
0
    return mAutoScrollTimer->Stop();
1991
0
  }
1992
0
  return NS_OK;
1993
0
}
1994
1995
nsresult
1996
Selection::DoAutoScroll(nsIFrame* aFrame, nsPoint aPoint)
1997
0
{
1998
0
  MOZ_ASSERT(aFrame, "Need a frame");
1999
0
2000
0
  if (mAutoScrollTimer) {
2001
0
    (void)mAutoScrollTimer->Stop();
2002
0
  }
2003
0
2004
0
  nsPresContext* presContext = aFrame->PresContext();
2005
0
  nsCOMPtr<nsIPresShell> shell = presContext->PresShell();
2006
0
  nsRootPresContext* rootPC = presContext->GetRootPresContext();
2007
0
  if (!rootPC)
2008
0
    return NS_OK;
2009
0
  nsIFrame* rootmostFrame = rootPC->PresShell()->GetRootFrame();
2010
0
  AutoWeakFrame weakRootFrame(rootmostFrame);
2011
0
  AutoWeakFrame weakFrame(aFrame);
2012
0
  // Get the point relative to the root most frame because the scroll we are
2013
0
  // about to do will change the coordinates of aFrame.
2014
0
  nsPoint globalPoint = aPoint + aFrame->GetOffsetToCrossDoc(rootmostFrame);
2015
0
2016
0
  bool done = false;
2017
0
  bool didScroll;
2018
0
  while (true) {
2019
0
    didScroll = shell->ScrollFrameRectIntoView(
2020
0
                  aFrame, nsRect(aPoint, nsSize(0, 0)),
2021
0
                  nsIPresShell::ScrollAxis(), nsIPresShell::ScrollAxis(),
2022
0
                  0);
2023
0
    if (!weakFrame || !weakRootFrame) {
2024
0
      return NS_OK;
2025
0
    }
2026
0
    if (!didScroll && !done) {
2027
0
      // If aPoint is at the screen edge then try to scroll anyway, once.
2028
0
      RefPtr<nsDeviceContext> dx = shell->GetViewManager()->GetDeviceContext();
2029
0
      nsRect screen;
2030
0
      dx->GetRect(screen);
2031
0
      nsPoint screenPoint = globalPoint +
2032
0
                            rootmostFrame->GetScreenRectInAppUnits().TopLeft();
2033
0
      nscoord onePx = AppUnitsPerCSSPixel();
2034
0
      nscoord scrollAmount = 10 * onePx;
2035
0
      if (std::abs(screen.x - screenPoint.x) <= onePx) {
2036
0
        aPoint.x -= scrollAmount;
2037
0
      } else if (std::abs(screen.XMost() - screenPoint.x) <= onePx) {
2038
0
        aPoint.x += scrollAmount;
2039
0
      } else if (std::abs(screen.y - screenPoint.y) <= onePx) {
2040
0
        aPoint.y -= scrollAmount;
2041
0
      } else if (std::abs(screen.YMost() - screenPoint.y) <= onePx) {
2042
0
        aPoint.y += scrollAmount;
2043
0
      } else {
2044
0
        break;
2045
0
      }
2046
0
      done = true;
2047
0
      continue;
2048
0
    }
2049
0
    break;
2050
0
  }
2051
0
2052
0
  // Start the AutoScroll timer if necessary.
2053
0
  if (didScroll && mAutoScrollTimer) {
2054
0
    nsPoint presContextPoint = globalPoint -
2055
0
      shell->GetRootFrame()->GetOffsetToCrossDoc(rootmostFrame);
2056
0
    mAutoScrollTimer->Start(presContext, presContextPoint);
2057
0
  }
2058
0
2059
0
  return NS_OK;
2060
0
}
2061
2062
2063
void
2064
Selection::RemoveAllRanges(ErrorResult& aRv)
2065
0
{
2066
0
  if (!mFrameSelection)
2067
0
    return; // nothing to do
2068
0
  RefPtr<nsPresContext>  presContext = GetPresContext();
2069
0
  nsresult  result = Clear(presContext);
2070
0
  if (NS_FAILED(result)) {
2071
0
    aRv.Throw(result);
2072
0
    return;
2073
0
  }
2074
0
2075
0
  // Turn off signal for table selection
2076
0
  RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
2077
0
  frameSelection->ClearTableCellSelection();
2078
0
2079
0
  // Be aware, this instance may be destroyed after this call.
2080
0
  // XXX Why doesn't this call Selection::NotifySelectionListener() directly?
2081
0
  result = frameSelection->NotifySelectionListeners(GetType());
2082
0
2083
0
  // Also need to notify the frames!
2084
0
  // PresShell::CharacterDataChanged should do that on DocumentChanged
2085
0
  if (NS_FAILED(result)) {
2086
0
    aRv.Throw(result);
2087
0
  }
2088
0
}
2089
2090
nsresult
2091
Selection::RemoveAllRangesTemporarily()
2092
0
{
2093
0
  if (!mCachedRange) {
2094
0
    // Look for a range which isn't referred by other than this instance.
2095
0
    // If there is, it'll be released by calling Clear().  So, we can reuse it
2096
0
    // when we need to create a range.
2097
0
    for (auto& rangeData : mRanges) {
2098
0
      auto& range = rangeData.mRange;
2099
0
      if (range->GetRefCount() == 1 ||
2100
0
          (range->GetRefCount() == 2 && range == mAnchorFocusRange)) {
2101
0
        mCachedRange = range;
2102
0
        break;
2103
0
      }
2104
0
    }
2105
0
  }
2106
0
2107
0
  // Then, remove all ranges.
2108
0
  ErrorResult result;
2109
0
  RemoveAllRanges(result);
2110
0
  if (result.Failed()) {
2111
0
    mCachedRange = nullptr;
2112
0
  } else if (mCachedRange) {
2113
0
    // To save the computing cost to keep valid DOM point against DOM tree
2114
0
    // changes, we should clear the range temporarily.
2115
0
    mCachedRange->ResetTemporarily();
2116
0
  }
2117
0
  return result.StealNSResult();
2118
0
}
2119
2120
void
2121
Selection::AddRangeJS(nsRange& aRange, ErrorResult& aRv)
2122
0
{
2123
0
  AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
2124
0
  mCalledByJS = true;
2125
0
  AddRange(aRange, aRv);
2126
0
}
2127
2128
void
2129
Selection::AddRange(nsRange& aRange, ErrorResult& aRv)
2130
0
{
2131
0
  RefPtr<nsIDocument> document(GetParentObject());
2132
0
  return AddRangeInternal(aRange, document, aRv);
2133
0
}
2134
2135
void
2136
Selection::AddRangeInternal(nsRange& aRange, nsIDocument* aDocument,
2137
                            ErrorResult& aRv)
2138
0
{
2139
0
  // If the given range is part of another Selection, we need to clone the
2140
0
  // range first.
2141
0
  RefPtr<nsRange> range;
2142
0
  if (aRange.IsInSelection() && aRange.GetSelection() != this) {
2143
0
    // Because of performance reason, when there is a cached range, let's use
2144
0
    // it.  Otherwise, clone the range.
2145
0
    if (mCachedRange) {
2146
0
      range = std::move(mCachedRange);
2147
0
      nsresult rv = range->SetStartAndEnd(aRange.StartRef().AsRaw(),
2148
0
                                          aRange.EndRef().AsRaw());
2149
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
2150
0
        return;
2151
0
      }
2152
0
    } else {
2153
0
      range = aRange.CloneRange();
2154
0
    }
2155
0
  } else {
2156
0
    range = &aRange;
2157
0
  }
2158
0
2159
0
  nsINode* rangeRoot = range->GetRoot();
2160
0
  if (aDocument != rangeRoot && (!rangeRoot ||
2161
0
                                 aDocument != rangeRoot->GetComposedDoc())) {
2162
0
    // http://w3c.github.io/selection-api/#dom-selection-addrange
2163
0
    // "...  if the root of the range's boundary points are the document
2164
0
    // associated with context object. Otherwise, this method must do nothing."
2165
0
    return;
2166
0
  }
2167
0
2168
0
  // If a range is being added, we don't need cached range because Collapse()
2169
0
  // won't use it.
2170
0
  mCachedRange = nullptr;
2171
0
2172
0
  // AddTableCellRange might flush frame.
2173
0
  RefPtr<Selection> kungFuDeathGrip(this);
2174
0
2175
0
  // This inserts a table cell range in proper document order
2176
0
  // and returns NS_OK if range doesn't contain just one table cell
2177
0
  bool didAddRange;
2178
0
  int32_t rangeIndex;
2179
0
  nsresult result = AddTableCellRange(range, &didAddRange, &rangeIndex);
2180
0
  if (NS_FAILED(result)) {
2181
0
    aRv.Throw(result);
2182
0
    return;
2183
0
  }
2184
0
2185
0
  if (!didAddRange) {
2186
0
    result = AddItem(range, &rangeIndex);
2187
0
    if (NS_FAILED(result)) {
2188
0
      aRv.Throw(result);
2189
0
      return;
2190
0
    }
2191
0
  }
2192
0
2193
0
  if (rangeIndex < 0) {
2194
0
    return;
2195
0
  }
2196
0
2197
0
  SetAnchorFocusRange(rangeIndex);
2198
0
2199
0
  // Make sure the caret appears on the next line, if at a newline
2200
0
  if (mSelectionType == SelectionType::eNormal) {
2201
0
    SetInterlinePosition(true, IgnoreErrors());
2202
0
  }
2203
0
2204
0
  RefPtr<nsPresContext>  presContext = GetPresContext();
2205
0
  SelectFrames(presContext, range, true);
2206
0
2207
0
  if (!mFrameSelection)
2208
0
    return;//nothing to do
2209
0
2210
0
  // Be aware, this instance may be destroyed after this call.
2211
0
  // XXX Why doesn't this call Selection::NotifySelectionListener() directly?
2212
0
  RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
2213
0
  result = frameSelection->NotifySelectionListeners(GetType());
2214
0
  if (NS_FAILED(result)) {
2215
0
    aRv.Throw(result);
2216
0
  }
2217
0
}
2218
2219
// Selection::RemoveRange
2220
//
2221
//    Removes the given range from the selection. The tricky part is updating
2222
//    the flags on the frames that indicate whether they have a selection or
2223
//    not. There could be several selection ranges on the frame, and clearing
2224
//    the bit would cause the selection to not be drawn, even when there is
2225
//    another range on the frame (bug 346185).
2226
//
2227
//    We therefore find any ranges that intersect the same nodes as the range
2228
//    being removed, and cause them to set the selected bits back on their
2229
//    selected frames after we've cleared the bit from ours.
2230
2231
void
2232
Selection::RemoveRange(nsRange& aRange, ErrorResult& aRv)
2233
0
{
2234
0
  nsresult rv = RemoveItem(&aRange);
2235
0
  if (NS_FAILED(rv)) {
2236
0
    aRv.Throw(rv);
2237
0
    return;
2238
0
  }
2239
0
2240
0
  nsINode* beginNode = aRange.GetStartContainer();
2241
0
  nsINode* endNode = aRange.GetEndContainer();
2242
0
2243
0
  if (!beginNode || !endNode) {
2244
0
    // Detached range; nothing else to do here.
2245
0
    return;
2246
0
  }
2247
0
2248
0
  // find out the length of the end node, so we can select all of it
2249
0
  int32_t beginOffset, endOffset;
2250
0
  if (endNode->IsText()) {
2251
0
    // Get the length of the text. We can't just use the offset because
2252
0
    // another range could be touching this text node but not intersect our
2253
0
    // range.
2254
0
    beginOffset = 0;
2255
0
    endOffset = endNode->AsText()->TextLength();
2256
0
  } else {
2257
0
    // For non-text nodes, the given offsets should be sufficient.
2258
0
    beginOffset = aRange.StartOffset();
2259
0
    endOffset = aRange.EndOffset();
2260
0
  }
2261
0
2262
0
  // clear the selected bit from the removed range's frames
2263
0
  RefPtr<nsPresContext>  presContext = GetPresContext();
2264
0
  SelectFrames(presContext, &aRange, false);
2265
0
2266
0
  // add back the selected bit for each range touching our nodes
2267
0
  nsTArray<nsRange*> affectedRanges;
2268
0
  rv = GetRangesForIntervalArray(beginNode, beginOffset,
2269
0
                                 endNode, endOffset,
2270
0
                                 true, &affectedRanges);
2271
0
  if (NS_FAILED(rv)) {
2272
0
    aRv.Throw(rv);
2273
0
    return;
2274
0
  }
2275
0
  for (uint32_t i = 0; i < affectedRanges.Length(); i++) {
2276
0
    SelectFrames(presContext, affectedRanges[i], true);
2277
0
  }
2278
0
2279
0
  int32_t cnt = mRanges.Length();
2280
0
  if (&aRange == mAnchorFocusRange) {
2281
0
    // Reset anchor to LAST range or clear it if there are no ranges.
2282
0
    SetAnchorFocusRange(cnt - 1);
2283
0
2284
0
    // When the selection is user-created it makes sense to scroll the range
2285
0
    // into view. The spell-check selection, however, is created and destroyed
2286
0
    // in the background. We don't want to scroll in this case or the view
2287
0
    // might appear to be moving randomly (bug 337871).
2288
0
    if (mSelectionType != SelectionType::eSpellCheck && cnt > 0) {
2289
0
      ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION);
2290
0
    }
2291
0
  }
2292
0
2293
0
  if (!mFrameSelection)
2294
0
    return;//nothing to do
2295
0
2296
0
  // Be aware, this instance may be destroyed after this call.
2297
0
  // XXX Why doesn't this call Selection::NotifySelectionListener() directly?
2298
0
  RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
2299
0
  rv = frameSelection->NotifySelectionListeners(GetType());
2300
0
  if (NS_FAILED(rv)) {
2301
0
    aRv.Throw(rv);
2302
0
  }
2303
0
}
2304
2305
2306
2307
/*
2308
 * Collapse sets the whole selection to be one point.
2309
 */
2310
void
2311
Selection::CollapseJS(nsINode* aContainer, uint32_t aOffset, ErrorResult& aRv)
2312
0
{
2313
0
  AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
2314
0
  mCalledByJS = true;
2315
0
  if (!aContainer) {
2316
0
    RemoveAllRanges(aRv);
2317
0
    return;
2318
0
  }
2319
0
  Collapse(RawRangeBoundary(aContainer, aOffset), aRv);
2320
0
}
2321
2322
void
2323
Selection::Collapse(const RawRangeBoundary& aPoint, ErrorResult& aRv)
2324
0
{
2325
0
  if (!mFrameSelection) {
2326
0
    aRv.Throw(NS_ERROR_NOT_INITIALIZED); // Can't do selection
2327
0
    return;
2328
0
  }
2329
0
2330
0
  if (!aPoint.IsSet()) {
2331
0
    aRv.Throw(NS_ERROR_INVALID_ARG);
2332
0
    return;
2333
0
  }
2334
0
2335
0
  if (aPoint.Container()->NodeType() == nsINode::DOCUMENT_TYPE_NODE) {
2336
0
    aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
2337
0
    return;
2338
0
  }
2339
0
2340
0
  // RawRangeBoundary::IsSetAndValid() checks if the point actually refers
2341
0
  // a child of the container when IsSet() is true.  If its offset hasn't been
2342
0
  // computed yet, this just checks it with its mRef.  So, we can avoid
2343
0
  // computing offset here.
2344
0
  if (!aPoint.IsSetAndValid()) {
2345
0
    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
2346
0
    return;
2347
0
  }
2348
0
2349
0
  if (!HasSameRoot(*aPoint.Container())) {
2350
0
    // Return with no error
2351
0
    return;
2352
0
  }
2353
0
2354
0
  RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
2355
0
  frameSelection->InvalidateDesiredPos();
2356
0
  if (!IsValidSelectionPoint(frameSelection, aPoint.Container())) {
2357
0
    aRv.Throw(NS_ERROR_FAILURE);
2358
0
    return;
2359
0
  }
2360
0
  nsresult result;
2361
0
2362
0
  RefPtr<nsPresContext> presContext = GetPresContext();
2363
0
  if (!presContext ||
2364
0
      presContext->Document() != aPoint.Container()->OwnerDoc()) {
2365
0
    aRv.Throw(NS_ERROR_FAILURE);
2366
0
    return;
2367
0
  }
2368
0
2369
0
  // Cache current range is if there is because it may be reusable.
2370
0
  RefPtr<nsRange> oldRange = !mRanges.IsEmpty() ? mRanges[0].mRange : nullptr;
2371
0
2372
0
  // Delete all of the current ranges
2373
0
  Clear(presContext);
2374
0
2375
0
  // Turn off signal for table selection
2376
0
  frameSelection->ClearTableCellSelection();
2377
0
2378
0
  // Hack to display the caret on the right line (bug 1237236).
2379
0
  if (frameSelection->GetHint() != CARET_ASSOCIATE_AFTER &&
2380
0
      aPoint.Container()->IsContent()) {
2381
0
    int32_t frameOffset;
2382
0
    nsTextFrame* f =
2383
0
      do_QueryFrame(nsCaret::GetFrameAndOffset(this, aPoint.Container(),
2384
0
                                               aPoint.Offset(), &frameOffset));
2385
0
    if (f && f->IsAtEndOfLine() && f->HasSignificantTerminalNewline()) {
2386
0
      // RawRangeBounary::Offset() causes computing offset if it's not been
2387
0
      // done yet.  However, it's called only when the container is a text
2388
0
      // node.  In such case, offset has always been set since it cannot have
2389
0
      // any children.  So, this doesn't cause computing offset with expensive
2390
0
      // method, nsINode::ComputeIndexOf().
2391
0
      if ((aPoint.Container()->AsContent() == f->GetContent() &&
2392
0
           f->GetContentEnd() == static_cast<int32_t>(aPoint.Offset())) ||
2393
0
          (aPoint.Container() == f->GetContent()->GetParentNode() &&
2394
0
           f->GetContent() == aPoint.GetPreviousSiblingOfChildAtOffset())) {
2395
0
        frameSelection->SetHint(CARET_ASSOCIATE_AFTER);
2396
0
      }
2397
0
    }
2398
0
  }
2399
0
2400
0
  RefPtr<nsRange> range;
2401
0
  // If the old range isn't referred by anybody other than this method,
2402
0
  // we should reuse it for reducing the recreation cost.
2403
0
  if (oldRange && oldRange->GetRefCount() == 1) {
2404
0
    range = std::move(oldRange);
2405
0
  } else if (mCachedRange) {
2406
0
    range = std::move(mCachedRange);
2407
0
  } else {
2408
0
    range = new nsRange(aPoint.Container());
2409
0
  }
2410
0
  result = range->CollapseTo(aPoint);
2411
0
  if (NS_FAILED(result)) {
2412
0
    aRv.Throw(result);
2413
0
    return;
2414
0
  }
2415
0
2416
#ifdef DEBUG_SELECTION
2417
  nsCOMPtr<nsIContent> content = do_QueryInterface(aPoint.Container());
2418
  nsCOMPtr<nsIDocument> doc = do_QueryInterface(aPoint.Container());
2419
  printf ("Sel. Collapse to %p %s %d\n", container.get(),
2420
          content ? nsAtomCString(content->NodeInfo()->NameAtom()).get()
2421
                  : (doc ? "DOCUMENT" : "???"),
2422
          aPoint.Offset());
2423
#endif
2424
2425
0
  int32_t rangeIndex = -1;
2426
0
  result = AddItem(range, &rangeIndex);
2427
0
  if (NS_FAILED(result)) {
2428
0
    aRv.Throw(result);
2429
0
    return;
2430
0
  }
2431
0
  SetAnchorFocusRange(0);
2432
0
  SelectFrames(presContext, range, true);
2433
0
2434
0
  // Be aware, this instance may be destroyed after this call.
2435
0
  // XXX Why doesn't this call Selection::NotifySelectionListener() directly?
2436
0
  result = frameSelection->NotifySelectionListeners(GetType());
2437
0
  if (NS_FAILED(result)) {
2438
0
    aRv.Throw(result);
2439
0
  }
2440
0
}
2441
2442
/*
2443
 * Sets the whole selection to be one point
2444
 * at the start of the current selection
2445
 */
2446
void
2447
Selection::CollapseToStartJS(ErrorResult& aRv)
2448
0
{
2449
0
  AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
2450
0
  mCalledByJS = true;
2451
0
  CollapseToStart(aRv);
2452
0
}
2453
2454
void
2455
Selection::CollapseToStart(ErrorResult& aRv)
2456
0
{
2457
0
  if (RangeCount() == 0) {
2458
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
2459
0
    return;
2460
0
  }
2461
0
2462
0
  // Get the first range
2463
0
  nsRange* firstRange = mRanges[0].mRange;
2464
0
  if (!firstRange) {
2465
0
    aRv.Throw(NS_ERROR_FAILURE);
2466
0
    return;
2467
0
  }
2468
0
2469
0
  if (mFrameSelection) {
2470
0
    int16_t reason = mFrameSelection->PopReason() | nsISelectionListener::COLLAPSETOSTART_REASON;
2471
0
    mFrameSelection->PostReason(reason);
2472
0
  }
2473
0
  nsINode* container = firstRange->GetStartContainer();
2474
0
  if (!container) {
2475
0
    aRv.Throw(NS_ERROR_FAILURE);
2476
0
    return;
2477
0
  }
2478
0
  Collapse(*container, firstRange->StartOffset(), aRv);
2479
0
}
2480
2481
/*
2482
 * Sets the whole selection to be one point
2483
 * at the end of the current selection
2484
 */
2485
void
2486
Selection::CollapseToEndJS(ErrorResult& aRv)
2487
0
{
2488
0
  AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
2489
0
  mCalledByJS = true;
2490
0
  CollapseToEnd(aRv);
2491
0
}
2492
2493
void
2494
Selection::CollapseToEnd(ErrorResult& aRv)
2495
0
{
2496
0
  uint32_t cnt = RangeCount();
2497
0
  if (cnt == 0) {
2498
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
2499
0
    return;
2500
0
  }
2501
0
2502
0
  // Get the last range
2503
0
  nsRange* lastRange = mRanges[cnt - 1].mRange;
2504
0
  if (!lastRange) {
2505
0
    aRv.Throw(NS_ERROR_FAILURE);
2506
0
    return;
2507
0
  }
2508
0
2509
0
  if (mFrameSelection) {
2510
0
    int16_t reason = mFrameSelection->PopReason() | nsISelectionListener::COLLAPSETOEND_REASON;
2511
0
    mFrameSelection->PostReason(reason);
2512
0
  }
2513
0
  nsINode* container = lastRange->GetEndContainer();
2514
0
  if (!container) {
2515
0
    aRv.Throw(NS_ERROR_FAILURE);
2516
0
    return;
2517
0
  }
2518
0
  Collapse(*container, lastRange->EndOffset(), aRv);
2519
0
}
2520
2521
void
2522
Selection::GetType(nsAString& aOutType) const
2523
0
{
2524
0
  if (!RangeCount()) {
2525
0
    aOutType.AssignLiteral("None");
2526
0
  } else if (IsCollapsed()) {
2527
0
    aOutType.AssignLiteral("Caret");
2528
0
  } else {
2529
0
    aOutType.AssignLiteral("Range");
2530
0
  }
2531
0
}
2532
2533
nsRange*
2534
Selection::GetRangeAt(uint32_t aIndex, ErrorResult& aRv)
2535
0
{
2536
0
  nsRange* range = GetRangeAt(aIndex);
2537
0
  if (!range) {
2538
0
    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
2539
0
    return nullptr;
2540
0
  }
2541
0
2542
0
  return range;
2543
0
}
2544
2545
nsRange*
2546
Selection::GetRangeAt(int32_t aIndex) const
2547
0
{
2548
0
  RangeData empty(nullptr);
2549
0
  return mRanges.SafeElementAt(aIndex, empty).mRange;
2550
0
}
2551
2552
/*
2553
utility function
2554
*/
2555
nsresult
2556
Selection::SetAnchorFocusToRange(nsRange* aRange)
2557
0
{
2558
0
  NS_ENSURE_STATE(mAnchorFocusRange);
2559
0
2560
0
  bool collapsed = IsCollapsed();
2561
0
2562
0
  nsresult res = RemoveItem(mAnchorFocusRange);
2563
0
  if (NS_FAILED(res))
2564
0
    return res;
2565
0
2566
0
  int32_t aOutIndex = -1;
2567
0
  res = AddItem(aRange, &aOutIndex, !collapsed);
2568
0
  if (NS_FAILED(res))
2569
0
    return res;
2570
0
  SetAnchorFocusRange(aOutIndex);
2571
0
2572
0
  return NS_OK;
2573
0
}
2574
2575
void
2576
Selection::ReplaceAnchorFocusRange(nsRange* aRange)
2577
0
{
2578
0
  NS_ENSURE_TRUE_VOID(mAnchorFocusRange);
2579
0
  RefPtr<nsPresContext> presContext = GetPresContext();
2580
0
  if (presContext) {
2581
0
    SelectFrames(presContext, mAnchorFocusRange, false);
2582
0
    SetAnchorFocusToRange(aRange);
2583
0
    SelectFrames(presContext, mAnchorFocusRange, true);
2584
0
  }
2585
0
}
2586
2587
void
2588
Selection::AdjustAnchorFocusForMultiRange(nsDirection aDirection)
2589
0
{
2590
0
  if (aDirection == mDirection) {
2591
0
    return;
2592
0
  }
2593
0
  SetDirection(aDirection);
2594
0
2595
0
  if (RangeCount() <= 1) {
2596
0
    return;
2597
0
  }
2598
0
2599
0
  nsRange* firstRange = GetRangeAt(0);
2600
0
  nsRange* lastRange = GetRangeAt(RangeCount() - 1);
2601
0
2602
0
  if (mDirection == eDirPrevious) {
2603
0
    firstRange->SetIsGenerated(false);
2604
0
    lastRange->SetIsGenerated(true);
2605
0
    SetAnchorFocusRange(0);
2606
0
  } else { // aDir == eDirNext
2607
0
    firstRange->SetIsGenerated(true);
2608
0
    lastRange->SetIsGenerated(false);
2609
0
    SetAnchorFocusRange(RangeCount() - 1);
2610
0
  }
2611
0
}
2612
2613
/*
2614
Notes which might come in handy for extend:
2615
2616
We can tell the direction of the selection by asking for the anchors selection
2617
if the begin is less than the end then we know the selection is to the "right".
2618
else it is a backwards selection.
2619
a = anchor
2620
1 = old cursor
2621
2 = new cursor
2622
2623
  if (a <= 1 && 1 <=2)    a,1,2  or (a1,2)
2624
  if (a < 2 && 1 > 2)     a,2,1
2625
  if (1 < a && a <2)      1,a,2
2626
  if (a > 2 && 2 >1)      1,2,a
2627
  if (2 < a && a <1)      2,a,1
2628
  if (a > 1 && 1 >2)      2,1,a
2629
then execute
2630
a  1  2 select from 1 to 2
2631
a  2  1 deselect from 2 to 1
2632
1  a  2 deselect from 1 to a select from a to 2
2633
1  2  a deselect from 1 to 2
2634
2  1  a = continue selection from 2 to 1
2635
*/
2636
2637
2638
/*
2639
 * Extend extends the selection away from the anchor.
2640
 * We don't need to know the direction, because we always change the focus.
2641
 */
2642
void
2643
Selection::ExtendJS(nsINode& aContainer, uint32_t aOffset, ErrorResult& aRv)
2644
0
{
2645
0
  AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
2646
0
  mCalledByJS = true;
2647
0
  Extend(aContainer, aOffset, aRv);
2648
0
}
2649
2650
nsresult
2651
Selection::Extend(nsINode* aContainer, int32_t aOffset)
2652
0
{
2653
0
  if (!aContainer) {
2654
0
    return NS_ERROR_INVALID_ARG;
2655
0
  }
2656
0
2657
0
  ErrorResult result;
2658
0
  Extend(*aContainer, static_cast<uint32_t>(aOffset), result);
2659
0
  return result.StealNSResult();
2660
0
}
2661
2662
void
2663
Selection::Extend(nsINode& aContainer, uint32_t aOffset, ErrorResult& aRv)
2664
0
{
2665
0
  // First, find the range containing the old focus point:
2666
0
  if (!mAnchorFocusRange) {
2667
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
2668
0
    return;
2669
0
  }
2670
0
2671
0
  if (!mFrameSelection) {
2672
0
    aRv.Throw(NS_ERROR_NOT_INITIALIZED); // Can't do selection
2673
0
    return;
2674
0
  }
2675
0
2676
0
  if (!HasSameRoot(aContainer)) {
2677
0
    // Return with no error
2678
0
    return;
2679
0
  }
2680
0
2681
0
  nsresult res;
2682
0
  if (!IsValidSelectionPoint(mFrameSelection, &aContainer)) {
2683
0
    aRv.Throw(NS_ERROR_FAILURE);
2684
0
    return;
2685
0
  }
2686
0
2687
0
  RefPtr<nsPresContext> presContext = GetPresContext();
2688
0
  if (!presContext || presContext->Document() != aContainer.OwnerDoc()) {
2689
0
    aRv.Throw(NS_ERROR_FAILURE);
2690
0
    return;
2691
0
  }
2692
0
2693
#ifdef DEBUG_SELECTION
2694
  nsDirection oldDirection = GetDirection();
2695
#endif
2696
0
  nsINode* anchorNode = GetAnchorNode();
2697
0
  nsINode* focusNode = GetFocusNode();
2698
0
  uint32_t anchorOffset = AnchorOffset();
2699
0
  uint32_t focusOffset = FocusOffset();
2700
0
2701
0
  RefPtr<nsRange> range = mAnchorFocusRange->CloneRange();
2702
0
2703
0
  nsINode* startNode = range->GetStartContainer();
2704
0
  nsINode* endNode = range->GetEndContainer();
2705
0
  int32_t startOffset = range->StartOffset();
2706
0
  int32_t endOffset = range->EndOffset();
2707
0
2708
0
  //compare anchor to old cursor.
2709
0
2710
0
  // We pass |disconnected| to the following ComparePoints calls in order
2711
0
  // to avoid assertions. ComparePoints returns 1 in the disconnected case
2712
0
  // and we can end up in various cases below, but it is assumed that in
2713
0
  // any of the cases we end up, the nsRange implementation will collapse
2714
0
  // the range to the new point because we can not make a valid range with
2715
0
  // a disconnected point. This means that whatever range is currently
2716
0
  // selected will be cleared.
2717
0
  bool disconnected = false;
2718
0
  bool shouldClearRange = false;
2719
0
  int32_t result1 = nsContentUtils::ComparePoints(anchorNode, anchorOffset,
2720
0
                                                  focusNode, focusOffset,
2721
0
                                                  &disconnected);
2722
0
  //compare old cursor to new cursor
2723
0
  shouldClearRange |= disconnected;
2724
0
  int32_t result2 = nsContentUtils::ComparePoints(focusNode, focusOffset,
2725
0
                                                  &aContainer, aOffset,
2726
0
                                                  &disconnected);
2727
0
  //compare anchor to new cursor
2728
0
  shouldClearRange |= disconnected;
2729
0
  int32_t result3 = nsContentUtils::ComparePoints(anchorNode, anchorOffset,
2730
0
                                                  &aContainer, aOffset,
2731
0
                                                  &disconnected);
2732
0
2733
0
  // If the points are disconnected, the range will be collapsed below,
2734
0
  // resulting in a range that selects nothing.
2735
0
  if (shouldClearRange) {
2736
0
    // Repaint the current range with the selection removed.
2737
0
    SelectFrames(presContext, range, false);
2738
0
  }
2739
0
2740
0
  RefPtr<nsRange> difRange = new nsRange(&aContainer);
2741
0
  if ((result1 == 0 && result3 < 0) || (result1 <= 0 && result2 < 0)){//a1,2  a,1,2
2742
0
    //select from 1 to 2 unless they are collapsed
2743
0
    range->SetEnd(aContainer, aOffset, aRv);
2744
0
    if (aRv.Failed()) {
2745
0
      return;
2746
0
    }
2747
0
    SetDirection(eDirNext);
2748
0
    res = difRange->SetStartAndEnd(focusNode, focusOffset,
2749
0
                                   range->GetEndContainer(),
2750
0
                                   range->EndOffset());
2751
0
    if (NS_FAILED(res)) {
2752
0
      aRv.Throw(res);
2753
0
      return;
2754
0
    }
2755
0
    SelectFrames(presContext, difRange , true);
2756
0
    res = SetAnchorFocusToRange(range);
2757
0
    if (NS_FAILED(res)) {
2758
0
      aRv.Throw(res);
2759
0
      return;
2760
0
    }
2761
0
  }
2762
0
  else if (result1 == 0 && result3 > 0){//2, a1
2763
0
    //select from 2 to 1a
2764
0
    SetDirection(eDirPrevious);
2765
0
    range->SetStart(aContainer, aOffset, aRv);
2766
0
    if (aRv.Failed()) {
2767
0
      return;
2768
0
    }
2769
0
    SelectFrames(presContext, range, true);
2770
0
    res = SetAnchorFocusToRange(range);
2771
0
    if (NS_FAILED(res)) {
2772
0
      aRv.Throw(res);
2773
0
      return;
2774
0
    }
2775
0
  }
2776
0
  else if (result3 <= 0 && result2 >= 0) {//a,2,1 or a2,1 or a,21 or a21
2777
0
    //deselect from 2 to 1
2778
0
    res = difRange->SetStartAndEnd(&aContainer, aOffset,
2779
0
                                   focusNode, focusOffset);
2780
0
    if (NS_FAILED(res)) {
2781
0
      aRv.Throw(res);
2782
0
      return;
2783
0
    }
2784
0
2785
0
    range->SetEnd(aContainer, aOffset, aRv);
2786
0
    if (aRv.Failed()) {
2787
0
      return;
2788
0
    }
2789
0
    res = SetAnchorFocusToRange(range);
2790
0
    if (NS_FAILED(res)) {
2791
0
      aRv.Throw(res);
2792
0
      return;
2793
0
    }
2794
0
    SelectFrames(presContext, difRange, false); // deselect now
2795
0
    difRange->SetEnd(range->GetEndContainer(), range->EndOffset());
2796
0
    SelectFrames(presContext, difRange, true); // must reselect last node
2797
0
                                               // maybe more
2798
0
  }
2799
0
  else if (result1 >= 0 && result3 <= 0) {//1,a,2 or 1a,2 or 1,a2 or 1a2
2800
0
    if (GetDirection() == eDirPrevious){
2801
0
      res = range->SetStart(endNode, endOffset);
2802
0
      if (NS_FAILED(res)) {
2803
0
        aRv.Throw(res);
2804
0
        return;
2805
0
      }
2806
0
    }
2807
0
    SetDirection(eDirNext);
2808
0
    range->SetEnd(aContainer, aOffset, aRv);
2809
0
    if (aRv.Failed()) {
2810
0
      return;
2811
0
    }
2812
0
    if (focusNode != anchorNode || focusOffset != anchorOffset) {//if collapsed diff dont do anything
2813
0
      res = difRange->SetStart(focusNode, focusOffset);
2814
0
      nsresult tmp = difRange->SetEnd(anchorNode, anchorOffset);
2815
0
      if (NS_FAILED(tmp)) {
2816
0
        res = tmp;
2817
0
      }
2818
0
      if (NS_FAILED(res)) {
2819
0
        aRv.Throw(res);
2820
0
        return;
2821
0
      }
2822
0
      res = SetAnchorFocusToRange(range);
2823
0
      if (NS_FAILED(res)) {
2824
0
        aRv.Throw(res);
2825
0
        return;
2826
0
      }
2827
0
      //deselect from 1 to a
2828
0
      SelectFrames(presContext, difRange , false);
2829
0
    }
2830
0
    else
2831
0
    {
2832
0
      res = SetAnchorFocusToRange(range);
2833
0
      if (NS_FAILED(res)) {
2834
0
        aRv.Throw(res);
2835
0
        return;
2836
0
      }
2837
0
    }
2838
0
    //select from a to 2
2839
0
    SelectFrames(presContext, range , true);
2840
0
  }
2841
0
  else if (result2 <= 0 && result3 >= 0) {//1,2,a or 12,a or 1,2a or 12a
2842
0
    //deselect from 1 to 2
2843
0
    res = difRange->SetStartAndEnd(focusNode, focusOffset,
2844
0
                                   &aContainer, aOffset);
2845
0
    if (NS_FAILED(res)) {
2846
0
      aRv.Throw(res);
2847
0
      return;
2848
0
    }
2849
0
    SetDirection(eDirPrevious);
2850
0
    range->SetStart(aContainer, aOffset, aRv);
2851
0
    if (aRv.Failed()) {
2852
0
      return;
2853
0
    }
2854
0
2855
0
    res = SetAnchorFocusToRange(range);
2856
0
    if (NS_FAILED(res)) {
2857
0
      aRv.Throw(res);
2858
0
      return;
2859
0
    }
2860
0
    SelectFrames(presContext, difRange , false);
2861
0
    difRange->SetStart(range->GetStartContainer(), range->StartOffset());
2862
0
    SelectFrames(presContext, difRange, true); // must reselect last node
2863
0
  }
2864
0
  else if (result3 >= 0 && result1 <= 0) {//2,a,1 or 2a,1 or 2,a1 or 2a1
2865
0
    if (GetDirection() == eDirNext){
2866
0
      range->SetEnd(startNode, startOffset);
2867
0
    }
2868
0
    SetDirection(eDirPrevious);
2869
0
    range->SetStart(aContainer, aOffset, aRv);
2870
0
    if (aRv.Failed()) {
2871
0
      return;
2872
0
    }
2873
0
    //deselect from a to 1
2874
0
    if (focusNode != anchorNode || focusOffset!= anchorOffset) {//if collapsed diff dont do anything
2875
0
      res = difRange->SetStartAndEnd(anchorNode, anchorOffset,
2876
0
                                     focusNode, focusOffset);
2877
0
      nsresult tmp = SetAnchorFocusToRange(range);
2878
0
      if (NS_FAILED(tmp)) {
2879
0
        res = tmp;
2880
0
      }
2881
0
      if (NS_FAILED(res)) {
2882
0
        aRv.Throw(res);
2883
0
        return;
2884
0
      }
2885
0
      SelectFrames(presContext, difRange, false);
2886
0
    }
2887
0
    else
2888
0
    {
2889
0
      res = SetAnchorFocusToRange(range);
2890
0
      if (NS_FAILED(res)) {
2891
0
        aRv.Throw(res);
2892
0
        return;
2893
0
      }
2894
0
    }
2895
0
    //select from 2 to a
2896
0
    SelectFrames(presContext, range , true);
2897
0
  }
2898
0
  else if (result2 >= 0 && result1 >= 0) {//2,1,a or 21,a or 2,1a or 21a
2899
0
    //select from 2 to 1
2900
0
    range->SetStart(aContainer, aOffset, aRv);
2901
0
    if (aRv.Failed()) {
2902
0
      return;
2903
0
    }
2904
0
    SetDirection(eDirPrevious);
2905
0
    res = difRange->SetStartAndEnd(
2906
0
                      range->GetStartContainer(), range->StartOffset(),
2907
0
                      focusNode, focusOffset);
2908
0
    if (NS_FAILED(res)) {
2909
0
      aRv.Throw(res);
2910
0
      return;
2911
0
    }
2912
0
2913
0
    SelectFrames(presContext, difRange, true);
2914
0
    res = SetAnchorFocusToRange(range);
2915
0
    if (NS_FAILED(res)) {
2916
0
      aRv.Throw(res);
2917
0
      return;
2918
0
    }
2919
0
  }
2920
0
2921
0
  if (mRanges.Length() > 1) {
2922
0
    for (size_t i = 0; i < mRanges.Length(); ++i) {
2923
0
      nsRange* range = mRanges[i].mRange;
2924
0
      MOZ_ASSERT(range->IsInSelection());
2925
0
      SelectFrames(presContext, range, range->IsInSelection());
2926
0
    }
2927
0
  }
2928
0
2929
0
  DEBUG_OUT_RANGE(range);
2930
#ifdef DEBUG_SELECTION
2931
  if (GetDirection() != oldDirection) {
2932
    printf("    direction changed to %s\n",
2933
           GetDirection() == eDirNext? "eDirNext":"eDirPrevious");
2934
  }
2935
  nsCOMPtr<nsIContent> content = do_QueryInterface(&aContainer);
2936
  printf ("Sel. Extend to %p %s %d\n", content.get(),
2937
          nsAtomCString(content->NodeInfo()->NameAtom()).get(), aOffset);
2938
#endif
2939
2940
0
  // Be aware, this instance may be destroyed after this call.
2941
0
  // XXX Why doesn't this call Selection::NotifySelectionListener() directly?
2942
0
  RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
2943
0
  res = frameSelection->NotifySelectionListeners(GetType());
2944
0
  if (NS_FAILED(res)) {
2945
0
    aRv.Throw(res);
2946
0
  }
2947
0
}
2948
2949
void
2950
Selection::SelectAllChildrenJS(nsINode& aNode, ErrorResult& aRv)
2951
0
{
2952
0
  AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
2953
0
  mCalledByJS = true;
2954
0
  SelectAllChildren(aNode, aRv);
2955
0
}
2956
2957
void
2958
Selection::SelectAllChildren(nsINode& aNode, ErrorResult& aRv)
2959
0
{
2960
0
  if (aNode.NodeType() == nsINode::DOCUMENT_TYPE_NODE) {
2961
0
    aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
2962
0
    return;
2963
0
  }
2964
0
2965
0
  if (!HasSameRoot(aNode)) {
2966
0
    // Return with no error
2967
0
    return;
2968
0
  }
2969
0
2970
0
  if (mFrameSelection) {
2971
0
    mFrameSelection->PostReason(nsISelectionListener::SELECTALL_REASON);
2972
0
  }
2973
0
  SelectionBatcher batch(this);
2974
0
2975
0
  Collapse(aNode, 0, aRv);
2976
0
  if (aRv.Failed()) {
2977
0
    return;
2978
0
  }
2979
0
2980
0
  Extend(aNode, aNode.GetChildCount(), aRv);
2981
0
}
2982
2983
bool
2984
Selection::ContainsNode(nsINode& aNode, bool aAllowPartial, ErrorResult& aRv)
2985
0
{
2986
0
  nsresult rv;
2987
0
  if (mRanges.Length() == 0) {
2988
0
    return false;
2989
0
  }
2990
0
2991
0
  // XXXbz this duplicates the GetNodeLength code in nsRange.cpp
2992
0
  uint32_t nodeLength;
2993
0
  auto* nodeAsCharData = CharacterData::FromNode(aNode);
2994
0
  if (nodeAsCharData) {
2995
0
    nodeLength = nodeAsCharData->TextLength();
2996
0
  } else {
2997
0
    nodeLength = aNode.GetChildCount();
2998
0
  }
2999
0
3000
0
  nsTArray<nsRange*> overlappingRanges;
3001
0
  rv = GetRangesForIntervalArray(&aNode, 0, &aNode, nodeLength,
3002
0
                                 false, &overlappingRanges);
3003
0
  if (NS_FAILED(rv)) {
3004
0
    aRv.Throw(rv);
3005
0
    return false;
3006
0
  }
3007
0
  if (overlappingRanges.Length() == 0)
3008
0
    return false; // no ranges overlap
3009
0
3010
0
  // if the caller said partial intersections are OK, we're done
3011
0
  if (aAllowPartial) {
3012
0
    return true;
3013
0
  }
3014
0
3015
0
  // text nodes always count as inside
3016
0
  if (nodeAsCharData) {
3017
0
    return true;
3018
0
  }
3019
0
3020
0
  // The caller wants to know if the node is entirely within the given range,
3021
0
  // so we have to check all intersecting ranges.
3022
0
  for (uint32_t i = 0; i < overlappingRanges.Length(); i++) {
3023
0
    bool nodeStartsBeforeRange, nodeEndsAfterRange;
3024
0
    if (NS_SUCCEEDED(nsRange::CompareNodeToRange(&aNode, overlappingRanges[i],
3025
0
                                                 &nodeStartsBeforeRange,
3026
0
                                                 &nodeEndsAfterRange))) {
3027
0
      if (!nodeStartsBeforeRange && !nodeEndsAfterRange) {
3028
0
        return true;
3029
0
      }
3030
0
    }
3031
0
  }
3032
0
  return false;
3033
0
}
3034
3035
class PointInRectChecker : public nsLayoutUtils::RectCallback {
3036
public:
3037
  explicit PointInRectChecker(const nsPoint& aPoint)
3038
    : mPoint(aPoint)
3039
    , mMatchFound(false)
3040
0
  {
3041
0
  }
3042
3043
  void AddRect(const nsRect& aRect) override
3044
0
  {
3045
0
    mMatchFound = mMatchFound || aRect.Contains(mPoint);
3046
0
  }
3047
3048
  bool MatchFound()
3049
0
  {
3050
0
    return mMatchFound;
3051
0
  }
3052
3053
private:
3054
  nsPoint mPoint;
3055
  bool mMatchFound;
3056
};
3057
3058
bool
3059
Selection::ContainsPoint(const nsPoint& aPoint)
3060
0
{
3061
0
  if (IsCollapsed()) {
3062
0
    return false;
3063
0
  }
3064
0
  PointInRectChecker checker(aPoint);
3065
0
  for (uint32_t i = 0; i < RangeCount(); i++) {
3066
0
    nsRange* range = GetRangeAt(i);
3067
0
    nsRange::CollectClientRectsAndText(&checker, nullptr, range,
3068
0
                                       range->GetStartContainer(),
3069
0
                                       range->StartOffset(),
3070
0
                                       range->GetEndContainer(),
3071
0
                                       range->EndOffset(),
3072
0
                                       true, false);
3073
0
    if (checker.MatchFound()) {
3074
0
      return true;
3075
0
    }
3076
0
  }
3077
0
  return false;
3078
0
}
3079
3080
nsPresContext*
3081
Selection::GetPresContext() const
3082
0
{
3083
0
  nsIPresShell *shell = GetPresShell();
3084
0
  if (!shell) {
3085
0
    return nullptr;
3086
0
  }
3087
0
3088
0
  return shell->GetPresContext();
3089
0
}
3090
3091
nsIPresShell*
3092
Selection::GetPresShell() const
3093
0
{
3094
0
  if (!mFrameSelection)
3095
0
    return nullptr;//nothing to do
3096
0
3097
0
  return mFrameSelection->GetShell();
3098
0
}
3099
3100
nsIDocument*
3101
Selection::GetDocument() const
3102
0
{
3103
0
  nsIPresShell* presShell = GetPresShell();
3104
0
  return presShell ? presShell->GetDocument() : nullptr;
3105
0
}
3106
3107
nsPIDOMWindowOuter*
3108
Selection::GetWindow() const
3109
0
{
3110
0
  nsIDocument* document = GetDocument();
3111
0
  return document ? document->GetWindow() : nullptr;
3112
0
}
3113
3114
HTMLEditor*
3115
Selection::GetHTMLEditor() const
3116
0
{
3117
0
  nsPresContext* presContext = GetPresContext();
3118
0
  if (!presContext) {
3119
0
    return nullptr;
3120
0
  }
3121
0
  return nsContentUtils::GetHTMLEditor(presContext);
3122
0
}
3123
3124
nsIFrame *
3125
Selection::GetSelectionAnchorGeometry(SelectionRegion aRegion, nsRect* aRect)
3126
0
{
3127
0
  if (!mFrameSelection)
3128
0
    return nullptr;  // nothing to do
3129
0
3130
0
  NS_ENSURE_TRUE(aRect, nullptr);
3131
0
3132
0
  aRect->SetRect(0, 0, 0, 0);
3133
0
3134
0
  switch (aRegion) {
3135
0
    case nsISelectionController::SELECTION_ANCHOR_REGION:
3136
0
    case nsISelectionController::SELECTION_FOCUS_REGION:
3137
0
      return GetSelectionEndPointGeometry(aRegion, aRect);
3138
0
    case nsISelectionController::SELECTION_WHOLE_SELECTION:
3139
0
      break;
3140
0
    default:
3141
0
      return nullptr;
3142
0
  }
3143
0
3144
0
  NS_ASSERTION(aRegion == nsISelectionController::SELECTION_WHOLE_SELECTION,
3145
0
    "should only be SELECTION_WHOLE_SELECTION here");
3146
0
3147
0
  nsRect anchorRect;
3148
0
  nsIFrame* anchorFrame = GetSelectionEndPointGeometry(
3149
0
    nsISelectionController::SELECTION_ANCHOR_REGION, &anchorRect);
3150
0
  if (!anchorFrame)
3151
0
    return nullptr;
3152
0
3153
0
  nsRect focusRect;
3154
0
  nsIFrame* focusFrame = GetSelectionEndPointGeometry(
3155
0
    nsISelectionController::SELECTION_FOCUS_REGION, &focusRect);
3156
0
  if (!focusFrame)
3157
0
    return nullptr;
3158
0
3159
0
  NS_ASSERTION(anchorFrame->PresContext() == focusFrame->PresContext(),
3160
0
    "points of selection in different documents?");
3161
0
  // make focusRect relative to anchorFrame
3162
0
  focusRect += focusFrame->GetOffsetTo(anchorFrame);
3163
0
3164
0
  aRect->UnionRectEdges(anchorRect, focusRect);
3165
0
  return anchorFrame;
3166
0
}
3167
3168
nsIFrame *
3169
Selection::GetSelectionEndPointGeometry(SelectionRegion aRegion, nsRect* aRect)
3170
0
{
3171
0
  if (!mFrameSelection)
3172
0
    return nullptr;  // nothing to do
3173
0
3174
0
  NS_ENSURE_TRUE(aRect, nullptr);
3175
0
3176
0
  aRect->SetRect(0, 0, 0, 0);
3177
0
3178
0
  nsINode    *node       = nullptr;
3179
0
  uint32_t    nodeOffset = 0;
3180
0
  nsIFrame   *frame      = nullptr;
3181
0
3182
0
  switch (aRegion) {
3183
0
    case nsISelectionController::SELECTION_ANCHOR_REGION:
3184
0
      node       = GetAnchorNode();
3185
0
      nodeOffset = AnchorOffset();
3186
0
      break;
3187
0
    case nsISelectionController::SELECTION_FOCUS_REGION:
3188
0
      node       = GetFocusNode();
3189
0
      nodeOffset = FocusOffset();
3190
0
      break;
3191
0
    default:
3192
0
      return nullptr;
3193
0
  }
3194
0
3195
0
  if (!node)
3196
0
    return nullptr;
3197
0
3198
0
  nsCOMPtr<nsIContent> content = do_QueryInterface(node);
3199
0
  NS_ENSURE_TRUE(content.get(), nullptr);
3200
0
  int32_t frameOffset = 0;
3201
0
  frame = mFrameSelection->GetFrameForNodeOffset(content, nodeOffset,
3202
0
                                                 mFrameSelection->GetHint(),
3203
0
                                                 &frameOffset);
3204
0
  if (!frame)
3205
0
    return nullptr;
3206
0
3207
0
  // Figure out what node type we have, then get the
3208
0
  // appropriate rect for it's nodeOffset.
3209
0
  bool isText = node->IsText();
3210
0
3211
0
  nsPoint pt(0, 0);
3212
0
  if (isText) {
3213
0
    nsIFrame* childFrame = nullptr;
3214
0
    frameOffset = 0;
3215
0
    nsresult rv =
3216
0
      frame->GetChildFrameContainingOffset(nodeOffset,
3217
0
                                           mFrameSelection->GetHint(),
3218
0
                                           &frameOffset, &childFrame);
3219
0
    if (NS_FAILED(rv))
3220
0
      return nullptr;
3221
0
    if (!childFrame)
3222
0
      return nullptr;
3223
0
3224
0
    frame = childFrame;
3225
0
3226
0
    // Get the x coordinate of the offset into the text frame.
3227
0
    rv = GetCachedFrameOffset(frame, nodeOffset, pt);
3228
0
    if (NS_FAILED(rv))
3229
0
      return nullptr;
3230
0
  }
3231
0
3232
0
  // Return the rect relative to the frame, with zero width.
3233
0
  if (isText) {
3234
0
    aRect->x = pt.x;
3235
0
  } else if (mFrameSelection->GetHint() == CARET_ASSOCIATE_BEFORE) {
3236
0
    // It's the frame's right edge we're interested in.
3237
0
    aRect->x = frame->GetRect().Width();
3238
0
  }
3239
0
  aRect->SetHeight(frame->GetRect().Height());
3240
0
3241
0
  return frame;
3242
0
}
3243
3244
NS_IMETHODIMP
3245
Selection::ScrollSelectionIntoViewEvent::Run()
3246
0
{
3247
0
  if (!mSelection)
3248
0
    return NS_OK;  // event revoked
3249
0
3250
0
  int32_t flags = Selection::SCROLL_DO_FLUSH |
3251
0
                  Selection::SCROLL_SYNCHRONOUS;
3252
0
3253
0
  Selection* sel = mSelection; // workaround to satisfy static analysis
3254
0
  RefPtr<Selection> kungFuDeathGrip(sel);
3255
0
  mSelection->mScrollEvent.Forget();
3256
0
  mSelection->ScrollIntoView(mRegion, mVerticalScroll,
3257
0
                             mHorizontalScroll, mFlags | flags);
3258
0
  return NS_OK;
3259
0
}
3260
3261
nsresult
3262
Selection::PostScrollSelectionIntoViewEvent(
3263
                                         SelectionRegion aRegion,
3264
                                         int32_t aFlags,
3265
                                         nsIPresShell::ScrollAxis aVertical,
3266
                                         nsIPresShell::ScrollAxis aHorizontal)
3267
0
{
3268
0
  // If we've already posted an event, revoke it and place a new one at the
3269
0
  // end of the queue to make sure that any new pending reflow events are
3270
0
  // processed before we scroll. This will insure that we scroll to the
3271
0
  // correct place on screen.
3272
0
  mScrollEvent.Revoke();
3273
0
  nsPresContext* presContext = GetPresContext();
3274
0
  NS_ENSURE_STATE(presContext);
3275
0
  nsRefreshDriver* refreshDriver = presContext->RefreshDriver();
3276
0
  NS_ENSURE_STATE(refreshDriver);
3277
0
3278
0
  mScrollEvent =
3279
0
    new ScrollSelectionIntoViewEvent(this, aRegion, aVertical, aHorizontal,
3280
0
                                     aFlags);
3281
0
  refreshDriver->AddEarlyRunner(mScrollEvent.get());
3282
0
  return NS_OK;
3283
0
}
3284
3285
void
3286
Selection::ScrollIntoView(int16_t aRegion, bool aIsSynchronous,
3287
                          int16_t aVPercent, int16_t aHPercent,
3288
                          ErrorResult& aRv)
3289
0
{
3290
0
  int32_t flags = aIsSynchronous ? Selection::SCROLL_SYNCHRONOUS : 0;
3291
0
  nsresult rv = ScrollIntoView(aRegion,
3292
0
                               nsIPresShell::ScrollAxis(aVPercent),
3293
0
                               nsIPresShell::ScrollAxis(aHPercent),
3294
0
                               flags);
3295
0
  if (NS_FAILED(rv)) {
3296
0
    aRv.Throw(rv);
3297
0
  }
3298
0
}
3299
3300
nsresult
3301
Selection::ScrollIntoView(SelectionRegion aRegion,
3302
                          nsIPresShell::ScrollAxis aVertical,
3303
                          nsIPresShell::ScrollAxis aHorizontal,
3304
                          int32_t aFlags)
3305
0
{
3306
0
  if (!mFrameSelection)
3307
0
    return NS_OK;//nothing to do
3308
0
3309
0
  nsIPresShell* presShell = mFrameSelection->GetShell();
3310
0
  if (!presShell)
3311
0
    return NS_OK;
3312
0
3313
0
  if (mFrameSelection->GetBatching())
3314
0
    return NS_OK;
3315
0
3316
0
  if (!(aFlags & Selection::SCROLL_SYNCHRONOUS))
3317
0
    return PostScrollSelectionIntoViewEvent(aRegion, aFlags,
3318
0
      aVertical, aHorizontal);
3319
0
3320
0
  // From this point on, the presShell may get destroyed by the calls below, so
3321
0
  // hold on to it using a strong reference to ensure the safety of the
3322
0
  // accesses to frame pointers in the callees.
3323
0
  nsCOMPtr<nsIPresShell> kungFuDeathGrip(presShell);
3324
0
3325
0
  // Now that text frame character offsets are always valid (though not
3326
0
  // necessarily correct), the worst that will happen if we don't flush here
3327
0
  // is that some callers might scroll to the wrong place.  Those should
3328
0
  // either manually flush if they're in a safe position for it or use the
3329
0
  // async version of this method.
3330
0
  if (aFlags & Selection::SCROLL_DO_FLUSH) {
3331
0
    presShell->FlushPendingNotifications(FlushType::Layout);
3332
0
3333
0
    // Reget the presshell, since it might have been Destroy'ed.
3334
0
    presShell = mFrameSelection ? mFrameSelection->GetShell() : nullptr;
3335
0
    if (!presShell)
3336
0
      return NS_OK;
3337
0
  }
3338
0
3339
0
  //
3340
0
  // Scroll the selection region into view.
3341
0
  //
3342
0
3343
0
  nsRect rect;
3344
0
  nsIFrame* frame = GetSelectionAnchorGeometry(aRegion, &rect);
3345
0
  if (!frame)
3346
0
    return NS_ERROR_FAILURE;
3347
0
3348
0
  // Scroll vertically to get the caret into view, but only if the container
3349
0
  // is perceived to be scrollable in that direction (i.e. there is a visible
3350
0
  // vertical scrollbar or the scroll range is at least one device pixel)
3351
0
  aVertical.mOnlyIfPerceivedScrollableDirection = true;
3352
0
3353
0
  uint32_t flags = 0;
3354
0
  if (aFlags & Selection::SCROLL_FIRST_ANCESTOR_ONLY) {
3355
0
    flags |= nsIPresShell::SCROLL_FIRST_ANCESTOR_ONLY;
3356
0
  }
3357
0
  if (aFlags & Selection::SCROLL_OVERFLOW_HIDDEN) {
3358
0
    flags |= nsIPresShell::SCROLL_OVERFLOW_HIDDEN;
3359
0
  }
3360
0
3361
0
  presShell->ScrollFrameRectIntoView(frame, rect, aVertical, aHorizontal,
3362
0
    flags);
3363
0
  return NS_OK;
3364
0
}
3365
3366
void
3367
Selection::AddSelectionListener(nsISelectionListener* aNewListener)
3368
0
{
3369
0
  MOZ_ASSERT(aNewListener);
3370
0
  mSelectionListeners.AppendElement(aNewListener); // AddRefs
3371
0
}
3372
3373
void
3374
Selection::RemoveSelectionListener(nsISelectionListener* aListenerToRemove)
3375
0
{
3376
0
  mSelectionListeners.RemoveElement(aListenerToRemove); // Releases
3377
0
}
3378
3379
Element*
3380
Selection::GetCommonEditingHostForAllRanges()
3381
0
{
3382
0
  Element* editingHost = nullptr;
3383
0
  for (RangeData& rangeData : mRanges) {
3384
0
    nsRange* range = rangeData.mRange;
3385
0
    MOZ_ASSERT(range);
3386
0
    nsINode* commonAncestorNode = range->GetCommonAncestor();
3387
0
    if (!commonAncestorNode || !commonAncestorNode->IsContent()) {
3388
0
      return nullptr;
3389
0
    }
3390
0
    nsIContent* commonAncestor = commonAncestorNode->AsContent();
3391
0
    Element* foundEditingHost = commonAncestor->GetEditingHost();
3392
0
    // Even when common ancestor is a non-editable element in a contenteditable
3393
0
    // element, we don't need to move focus to the contenteditable element
3394
0
    // because Chromium doesn't set focus to it.
3395
0
    if (!foundEditingHost) {
3396
0
      return nullptr;
3397
0
    }
3398
0
    if (!editingHost) {
3399
0
      editingHost = foundEditingHost;
3400
0
      continue;
3401
0
    }
3402
0
    if (editingHost == foundEditingHost) {
3403
0
      continue;
3404
0
    }
3405
0
    if (nsContentUtils::ContentIsDescendantOf(foundEditingHost, editingHost)) {
3406
0
      continue;
3407
0
    }
3408
0
    if (nsContentUtils::ContentIsDescendantOf(editingHost, foundEditingHost)) {
3409
0
      editingHost = foundEditingHost;
3410
0
      continue;
3411
0
    }
3412
0
    // editingHost and foundEditingHost are not a descendant of the other.
3413
0
    // So, there is no common editing host.
3414
0
    return nullptr;
3415
0
  }
3416
0
  return editingHost;
3417
0
}
3418
3419
nsresult
3420
Selection::NotifySelectionListeners(bool aCalledByJS)
3421
0
{
3422
0
  AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
3423
0
  mCalledByJS = aCalledByJS;
3424
0
  return NotifySelectionListeners();
3425
0
}
3426
3427
nsresult
3428
Selection::NotifySelectionListeners()
3429
0
{
3430
0
  if (!mFrameSelection)
3431
0
    return NS_OK;//nothing to do
3432
0
3433
0
  // Our internal code should not move focus with using this class while
3434
0
  // this moves focus nor from selection listeners.
3435
0
  AutoRestore<bool> calledByJSRestorer(mCalledByJS);
3436
0
  mCalledByJS = false;
3437
0
3438
0
  // When normal selection is changed by Selection API, we need to move focus
3439
0
  // if common ancestor of all ranges are in an editing host.  Note that we
3440
0
  // don't need to move focus *to* the other focusable node because other
3441
0
  // browsers don't do it either.
3442
0
  if (mSelectionType == SelectionType::eNormal &&
3443
0
      calledByJSRestorer.SavedValue()) {
3444
0
    nsPIDOMWindowOuter* window = GetWindow();
3445
0
    nsIDocument* document = GetDocument();
3446
0
    // If the document is in design mode or doesn't have contenteditable
3447
0
    // element, we don't need to move focus.
3448
0
    if (window && document && !document->HasFlag(NODE_IS_EDITABLE) &&
3449
0
        GetHTMLEditor()) {
3450
0
      RefPtr<Element> newEditingHost = GetCommonEditingHostForAllRanges();
3451
0
      nsFocusManager* fm = nsFocusManager::GetFocusManager();
3452
0
      nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
3453
0
      nsIContent* focusedContent =
3454
0
        nsFocusManager::GetFocusedDescendant(window,
3455
0
                                             nsFocusManager::eOnlyCurrentWindow,
3456
0
                                             getter_AddRefs(focusedWindow));
3457
0
      nsCOMPtr<Element> focusedElement = do_QueryInterface(focusedContent);
3458
0
      // When all selected ranges are in an editing host, it should take focus.
3459
0
      // But otherwise, we shouldn't move focus since Chromium doesn't move
3460
0
      // focus but only selection range is updated.
3461
0
      if (newEditingHost && newEditingHost != focusedElement) {
3462
0
        MOZ_ASSERT(!newEditingHost->IsInNativeAnonymousSubtree());
3463
0
        // Note that don't steal focus from focused window if the window doesn't
3464
0
        // have focus and if the window isn't focused window, shouldn't be
3465
0
        // scrolled to the new focused element.
3466
0
        uint32_t flags = nsIFocusManager::FLAG_NOSWITCHFRAME;
3467
0
        if (focusedWindow != fm->GetFocusedWindow()) {
3468
0
          flags |= nsIFocusManager::FLAG_NOSCROLL;
3469
0
        }
3470
0
        fm->SetFocus(newEditingHost, flags);
3471
0
      }
3472
0
    }
3473
0
  }
3474
0
3475
0
  RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
3476
0
  if (frameSelection->GetBatching()) {
3477
0
    frameSelection->SetDirty();
3478
0
    return NS_OK;
3479
0
  }
3480
0
  if (mSelectionListeners.IsEmpty()) {
3481
0
    // If there are no selection listeners, we're done!
3482
0
    return NS_OK;
3483
0
  }
3484
0
3485
0
  nsCOMPtr<nsIDocument> doc;
3486
0
  nsIPresShell* ps = GetPresShell();
3487
0
  if (ps) {
3488
0
    doc = ps->GetDocument();
3489
0
  }
3490
0
3491
0
  // We've notified all selection listeners even when some of them are removed
3492
0
  // (and may be destroyed) during notifying one of them.  Therefore, we should
3493
0
  // copy all listeners to the local variable first.
3494
0
  AutoTArray<nsCOMPtr<nsISelectionListener>, 5>
3495
0
    selectionListeners(mSelectionListeners);
3496
0
3497
0
  int16_t reason = frameSelection->PopReason();
3498
0
3499
0
  if (mNotifyAutoCopy) {
3500
0
    AutoCopyListener::OnSelectionChange(doc, *this, reason);
3501
0
  }
3502
0
3503
0
  if (mAccessibleCaretEventHub) {
3504
0
    RefPtr<AccessibleCaretEventHub> hub(mAccessibleCaretEventHub);
3505
0
    hub->OnSelectionChange(doc, this, reason);
3506
0
  }
3507
0
3508
0
  if (mSelectionChangeEventDispatcher) {
3509
0
    RefPtr<SelectionChangeEventDispatcher> dispatcher(
3510
0
                                             mSelectionChangeEventDispatcher);
3511
0
    dispatcher->OnSelectionChange(doc, this, reason);
3512
0
  }
3513
0
  for (auto& listener : selectionListeners) {
3514
0
    listener->NotifySelectionChanged(doc, this, reason);
3515
0
  }
3516
0
  return NS_OK;
3517
0
}
3518
3519
void
3520
Selection::StartBatchChanges()
3521
0
{
3522
0
  if (mFrameSelection) {
3523
0
    RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
3524
0
    frameSelection->StartBatchChanges();
3525
0
  }
3526
0
}
3527
3528
void
3529
Selection::EndBatchChanges(int16_t aReason)
3530
0
{
3531
0
  if (mFrameSelection) {
3532
0
    RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
3533
0
    frameSelection->EndBatchChanges(aReason);
3534
0
  }
3535
0
}
3536
3537
void
3538
Selection::AddSelectionChangeBlocker()
3539
0
{
3540
0
  mSelectionChangeBlockerCount++;
3541
0
}
3542
3543
void
3544
Selection::RemoveSelectionChangeBlocker()
3545
0
{
3546
0
  MOZ_ASSERT(mSelectionChangeBlockerCount > 0,
3547
0
             "mSelectionChangeBlockerCount has an invalid value - "
3548
0
             "maybe you have a mismatched RemoveSelectionChangeBlocker?");
3549
0
  mSelectionChangeBlockerCount--;
3550
0
}
3551
3552
bool
3553
Selection::IsBlockingSelectionChangeEvents() const
3554
0
{
3555
0
  return mSelectionChangeBlockerCount > 0;
3556
0
}
3557
3558
void
3559
Selection::DeleteFromDocument(ErrorResult& aRv)
3560
0
{
3561
0
  if (!mFrameSelection)
3562
0
    return;//nothing to do
3563
0
  RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
3564
0
  nsresult rv = frameSelection->DeleteFromDocument();
3565
0
  if (NS_FAILED(rv)) {
3566
0
    aRv.Throw(rv);
3567
0
  }
3568
0
}
3569
3570
void
3571
Selection::Modify(const nsAString& aAlter, const nsAString& aDirection,
3572
                  const nsAString& aGranularity, ErrorResult& aRv)
3573
0
{
3574
0
  // Silently exit if there's no selection or no focus node.
3575
0
  if (!mFrameSelection || !GetAnchorFocusRange() || !GetFocusNode()) {
3576
0
    return;
3577
0
  }
3578
0
3579
0
  if (!aAlter.LowerCaseEqualsLiteral("move") &&
3580
0
      !aAlter.LowerCaseEqualsLiteral("extend")) {
3581
0
    aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
3582
0
    return;
3583
0
  }
3584
0
3585
0
  if (!aDirection.LowerCaseEqualsLiteral("forward") &&
3586
0
      !aDirection.LowerCaseEqualsLiteral("backward") &&
3587
0
      !aDirection.LowerCaseEqualsLiteral("left") &&
3588
0
      !aDirection.LowerCaseEqualsLiteral("right")) {
3589
0
    aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
3590
0
    return;
3591
0
  }
3592
0
3593
0
  // Line moves are always visual.
3594
0
  bool visual  = aDirection.LowerCaseEqualsLiteral("left") ||
3595
0
                   aDirection.LowerCaseEqualsLiteral("right") ||
3596
0
                   aGranularity.LowerCaseEqualsLiteral("line");
3597
0
3598
0
  bool forward = aDirection.LowerCaseEqualsLiteral("forward") ||
3599
0
                   aDirection.LowerCaseEqualsLiteral("right");
3600
0
3601
0
  bool extend  = aAlter.LowerCaseEqualsLiteral("extend");
3602
0
3603
0
  nsSelectionAmount amount;
3604
0
  if (aGranularity.LowerCaseEqualsLiteral("character")) {
3605
0
    amount = eSelectCluster;
3606
0
  } else if (aGranularity.LowerCaseEqualsLiteral("word")) {
3607
0
    amount = eSelectWordNoSpace;
3608
0
  } else if (aGranularity.LowerCaseEqualsLiteral("line")) {
3609
0
    amount = eSelectLine;
3610
0
  } else if (aGranularity.LowerCaseEqualsLiteral("lineboundary")) {
3611
0
    amount = forward ? eSelectEndLine : eSelectBeginLine;
3612
0
  } else if (aGranularity.LowerCaseEqualsLiteral("sentence") ||
3613
0
             aGranularity.LowerCaseEqualsLiteral("sentenceboundary") ||
3614
0
             aGranularity.LowerCaseEqualsLiteral("paragraph") ||
3615
0
             aGranularity.LowerCaseEqualsLiteral("paragraphboundary") ||
3616
0
             aGranularity.LowerCaseEqualsLiteral("documentboundary")) {
3617
0
    aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
3618
0
    return;
3619
0
  } else {
3620
0
    aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
3621
0
    return;
3622
0
  }
3623
0
3624
0
  // If the anchor doesn't equal the focus and we try to move without first
3625
0
  // collapsing the selection, MoveCaret will collapse the selection and quit.
3626
0
  // To avoid this, we need to collapse the selection first.
3627
0
  nsresult rv = NS_OK;
3628
0
  if (!extend) {
3629
0
    nsINode* focusNode = GetFocusNode();
3630
0
    // We should have checked earlier that there was a focus node.
3631
0
    if (!focusNode) {
3632
0
      aRv.Throw(NS_ERROR_UNEXPECTED);
3633
0
      return;
3634
0
    }
3635
0
    uint32_t focusOffset = FocusOffset();
3636
0
    Collapse(focusNode, focusOffset);
3637
0
  }
3638
0
3639
0
  // If the paragraph direction of the focused frame is right-to-left,
3640
0
  // we may have to swap the direction of movement.
3641
0
  nsIFrame *frame;
3642
0
  int32_t offset;
3643
0
  rv = GetPrimaryFrameForFocusNode(&frame, &offset, visual);
3644
0
  if (NS_SUCCEEDED(rv) && frame) {
3645
0
    nsBidiDirection paraDir = nsBidiPresUtils::ParagraphDirection(frame);
3646
0
3647
0
    if (paraDir == NSBIDI_RTL && visual) {
3648
0
      if (amount == eSelectBeginLine) {
3649
0
        amount = eSelectEndLine;
3650
0
        forward = !forward;
3651
0
      } else if (amount == eSelectEndLine) {
3652
0
        amount = eSelectBeginLine;
3653
0
        forward = !forward;
3654
0
      }
3655
0
    }
3656
0
  }
3657
0
3658
0
  // MoveCaret will return an error if it can't move in the specified
3659
0
  // direction, but we just ignore this error unless it's a line move, in which
3660
0
  // case we call nsISelectionController::CompleteMove to move the cursor to
3661
0
  // the beginning/end of the line.
3662
0
  RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
3663
0
  rv = frameSelection->MoveCaret(forward ? eDirNext : eDirPrevious,
3664
0
                                 extend, amount,
3665
0
                                 visual ? nsFrameSelection::eVisual
3666
0
                                        : nsFrameSelection::eLogical);
3667
0
3668
0
  if (aGranularity.LowerCaseEqualsLiteral("line") && NS_FAILED(rv)) {
3669
0
    nsCOMPtr<nsISelectionController> shell =
3670
0
      do_QueryInterface(frameSelection->GetShell());
3671
0
    if (!shell)
3672
0
      return;
3673
0
    shell->CompleteMove(forward, extend);
3674
0
  }
3675
0
}
3676
3677
void
3678
Selection::SetBaseAndExtentJS(nsINode& aAnchorNode,
3679
                              uint32_t aAnchorOffset,
3680
                              nsINode& aFocusNode,
3681
                              uint32_t aFocusOffset,
3682
                              ErrorResult& aRv)
3683
0
{
3684
0
  AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
3685
0
  mCalledByJS = true;
3686
0
  SetBaseAndExtent(aAnchorNode, aAnchorOffset,
3687
0
                   aFocusNode, aFocusOffset, aRv);
3688
0
}
3689
3690
void
3691
Selection::SetBaseAndExtent(nsINode& aAnchorNode, uint32_t aAnchorOffset,
3692
                            nsINode& aFocusNode, uint32_t aFocusOffset,
3693
                            ErrorResult& aRv)
3694
0
{
3695
0
  if (!mFrameSelection) {
3696
0
    return;
3697
0
  }
3698
0
3699
0
  if (!HasSameRoot(aAnchorNode) ||
3700
0
      !HasSameRoot(aFocusNode)) {
3701
0
    // Return with no error
3702
0
    return;
3703
0
  }
3704
0
3705
0
  SelectionBatcher batch(this);
3706
0
3707
0
  int32_t relativePosition =
3708
0
    nsContentUtils::ComparePoints(&aAnchorNode, aAnchorOffset,
3709
0
                                  &aFocusNode, aFocusOffset);
3710
0
  nsINode* start = &aAnchorNode;
3711
0
  nsINode* end = &aFocusNode;
3712
0
  uint32_t startOffset = aAnchorOffset;
3713
0
  uint32_t endOffset = aFocusOffset;
3714
0
  if (relativePosition > 0) {
3715
0
    start = &aFocusNode;
3716
0
    end = &aAnchorNode;
3717
0
    startOffset = aFocusOffset;
3718
0
    endOffset = aAnchorOffset;
3719
0
  }
3720
0
3721
0
  // If there is cached range, we should reuse it for saving the allocation
3722
0
  // const (and some other cost in nsRange::DoSetRange().
3723
0
  RefPtr<nsRange> newRange = std::move(mCachedRange);
3724
0
3725
0
  nsresult rv = NS_OK;
3726
0
  if (newRange) {
3727
0
    rv = newRange->SetStartAndEnd(start, startOffset, end, endOffset);
3728
0
  } else {
3729
0
    rv = nsRange::CreateRange(start, startOffset, end, endOffset,
3730
0
                              getter_AddRefs(newRange));
3731
0
  }
3732
0
3733
0
  // nsRange::SetStartAndEnd() and nsRange::CreateRange() returns
3734
0
  // IndexSizeError if any offset is out of bounds.
3735
0
  if (NS_FAILED(rv)) {
3736
0
    aRv.Throw(rv);
3737
0
    return;
3738
0
  }
3739
0
3740
0
  RemoveAllRanges(aRv);
3741
0
  if (aRv.Failed()) {
3742
0
    return;
3743
0
  }
3744
0
3745
0
  AddRange(*newRange, aRv);
3746
0
  if (aRv.Failed()) {
3747
0
    return;
3748
0
  }
3749
0
3750
0
  SetDirection(relativePosition > 0 ? eDirPrevious : eDirNext);
3751
0
}
3752
3753
/** SelectionLanguageChange modifies the cursor Bidi level after a change in keyboard direction
3754
 *  @param aLangRTL is true if the new language is right-to-left or false if the new language is left-to-right
3755
 */
3756
nsresult
3757
Selection::SelectionLanguageChange(bool aLangRTL)
3758
0
{
3759
0
  if (!mFrameSelection)
3760
0
    return NS_ERROR_NOT_INITIALIZED; // Can't do selection
3761
0
3762
0
  RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
3763
0
3764
0
  // if the direction of the language hasn't changed, nothing to do
3765
0
  nsBidiLevel kbdBidiLevel = aLangRTL ? NSBIDI_RTL : NSBIDI_LTR;
3766
0
  if (kbdBidiLevel == frameSelection->mKbdBidiLevel) {
3767
0
    return NS_OK;
3768
0
  }
3769
0
3770
0
  frameSelection->mKbdBidiLevel = kbdBidiLevel;
3771
0
3772
0
  nsresult result;
3773
0
  nsIFrame *focusFrame = 0;
3774
0
3775
0
  result = GetPrimaryFrameForFocusNode(&focusFrame, nullptr, false);
3776
0
  if (NS_FAILED(result)) {
3777
0
    return result;
3778
0
  }
3779
0
  if (!focusFrame) {
3780
0
    return NS_ERROR_FAILURE;
3781
0
  }
3782
0
3783
0
  int32_t frameStart, frameEnd;
3784
0
  focusFrame->GetOffsets(frameStart, frameEnd);
3785
0
  RefPtr<nsPresContext> context = GetPresContext();
3786
0
  nsBidiLevel levelBefore, levelAfter;
3787
0
  if (!context) {
3788
0
    return NS_ERROR_FAILURE;
3789
0
  }
3790
0
3791
0
  nsBidiLevel level = focusFrame->GetEmbeddingLevel();
3792
0
  int32_t focusOffset = static_cast<int32_t>(FocusOffset());
3793
0
  if ((focusOffset != frameStart) && (focusOffset != frameEnd))
3794
0
    // the cursor is not at a frame boundary, so the level of both the characters (logically) before and after the cursor
3795
0
    //  is equal to the frame level
3796
0
    levelBefore = levelAfter = level;
3797
0
  else {
3798
0
    // the cursor is at a frame boundary, so use GetPrevNextBidiLevels to find the level of the characters
3799
0
    //  before and after the cursor
3800
0
    nsCOMPtr<nsIContent> focusContent = do_QueryInterface(GetFocusNode());
3801
0
    nsPrevNextBidiLevels levels = frameSelection->
3802
0
      GetPrevNextBidiLevels(focusContent, focusOffset, false);
3803
0
3804
0
    levelBefore = levels.mLevelBefore;
3805
0
    levelAfter = levels.mLevelAfter;
3806
0
  }
3807
0
3808
0
  if (IS_SAME_DIRECTION(levelBefore, levelAfter)) {
3809
0
    // if cursor is between two characters with the same orientation, changing the keyboard language
3810
0
    //  must toggle the cursor level between the level of the character with the lowest level
3811
0
    //  (if the new language corresponds to the orientation of that character) and this level plus 1
3812
0
    //  (if the new language corresponds to the opposite orientation)
3813
0
    if ((level != levelBefore) && (level != levelAfter))
3814
0
      level = std::min(levelBefore, levelAfter);
3815
0
    if (IS_SAME_DIRECTION(level, kbdBidiLevel))
3816
0
      frameSelection->SetCaretBidiLevel(level);
3817
0
    else
3818
0
      frameSelection->SetCaretBidiLevel(level + 1);
3819
0
  }
3820
0
  else {
3821
0
    // if cursor is between characters with opposite orientations, changing the keyboard language must change
3822
0
    //  the cursor level to that of the adjacent character with the orientation corresponding to the new language.
3823
0
    if (IS_SAME_DIRECTION(levelBefore, kbdBidiLevel))
3824
0
      frameSelection->SetCaretBidiLevel(levelBefore);
3825
0
    else
3826
0
      frameSelection->SetCaretBidiLevel(levelAfter);
3827
0
  }
3828
0
3829
0
  // The caret might have moved, so invalidate the desired position
3830
0
  // for future usages of up-arrow or down-arrow
3831
0
  frameSelection->InvalidateDesiredPos();
3832
0
3833
0
  return NS_OK;
3834
0
}
3835
3836
void
3837
Selection::SetColors(const nsAString& aForegroundColor,
3838
                     const nsAString& aBackgroundColor,
3839
                     const nsAString& aAltForegroundColor,
3840
                     const nsAString& aAltBackgroundColor,
3841
                     ErrorResult& aRv)
3842
0
{
3843
0
  if (mSelectionType != SelectionType::eFind) {
3844
0
    aRv.Throw(NS_ERROR_FAILURE);
3845
0
    return;
3846
0
  }
3847
0
3848
0
  mCustomColors.reset(new SelectionCustomColors);
3849
0
3850
0
  NS_NAMED_LITERAL_STRING(currentColorStr, "currentColor");
3851
0
  NS_NAMED_LITERAL_STRING(transparentStr, "transparent");
3852
0
3853
0
  if (!aForegroundColor.Equals(currentColorStr)) {
3854
0
    nscolor foregroundColor;
3855
0
    nsAttrValue aForegroundColorValue;
3856
0
    aForegroundColorValue.ParseColor(aForegroundColor);
3857
0
    if (!aForegroundColorValue.GetColorValue(foregroundColor)) {
3858
0
      aRv.Throw(NS_ERROR_INVALID_ARG);
3859
0
      return;
3860
0
    }
3861
0
    mCustomColors->mForegroundColor = Some(foregroundColor);
3862
0
  } else {
3863
0
    mCustomColors->mForegroundColor = Nothing();
3864
0
  }
3865
0
3866
0
  if (!aBackgroundColor.Equals(transparentStr)) {
3867
0
    nscolor backgroundColor;
3868
0
    nsAttrValue aBackgroundColorValue;
3869
0
    aBackgroundColorValue.ParseColor(aBackgroundColor);
3870
0
    if (!aBackgroundColorValue.GetColorValue(backgroundColor)) {
3871
0
      aRv.Throw(NS_ERROR_INVALID_ARG);
3872
0
      return;
3873
0
    }
3874
0
    mCustomColors->mBackgroundColor = Some(backgroundColor);
3875
0
  } else {
3876
0
    mCustomColors->mBackgroundColor = Nothing();
3877
0
  }
3878
0
3879
0
  if (!aAltForegroundColor.Equals(currentColorStr)) {
3880
0
    nscolor altForegroundColor;
3881
0
    nsAttrValue aAltForegroundColorValue;
3882
0
    aAltForegroundColorValue.ParseColor(aAltForegroundColor);
3883
0
    if (!aAltForegroundColorValue.GetColorValue(altForegroundColor)) {
3884
0
      aRv.Throw(NS_ERROR_INVALID_ARG);
3885
0
      return;
3886
0
    }
3887
0
    mCustomColors->mAltForegroundColor = Some(altForegroundColor);
3888
0
  } else {
3889
0
    mCustomColors->mAltForegroundColor = Nothing();
3890
0
  }
3891
0
3892
0
  if (!aAltBackgroundColor.Equals(transparentStr)) {
3893
0
    nscolor altBackgroundColor;
3894
0
    nsAttrValue aAltBackgroundColorValue;
3895
0
    aAltBackgroundColorValue.ParseColor(aAltBackgroundColor);
3896
0
    if (!aAltBackgroundColorValue.GetColorValue(altBackgroundColor)) {
3897
0
      aRv.Throw(NS_ERROR_INVALID_ARG);
3898
0
      return;
3899
0
    }
3900
0
    mCustomColors->mAltBackgroundColor = Some(altBackgroundColor);
3901
0
  } else {
3902
0
    mCustomColors->mAltBackgroundColor = Nothing();
3903
0
  }
3904
0
}
3905
3906
void
3907
Selection::ResetColors(ErrorResult& aRv)
3908
0
{
3909
0
  mCustomColors = nullptr;
3910
0
}
3911
3912
JSObject*
3913
Selection::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
3914
0
{
3915
0
  return mozilla::dom::Selection_Binding::Wrap(aCx, this, aGivenProto);
3916
0
}
3917
3918
// AutoHideSelectionChanges
3919
AutoHideSelectionChanges::AutoHideSelectionChanges(const nsFrameSelection* aFrame)
3920
  : AutoHideSelectionChanges(
3921
      aFrame ? aFrame->GetSelection(SelectionType::eNormal) : nullptr)
3922
0
{}
3923
3924
bool
3925
Selection::HasSameRoot(nsINode& aNode)
3926
0
{
3927
0
  nsINode* root = aNode.SubtreeRoot();
3928
0
  nsIDocument* doc = GetParentObject();
3929
0
  return doc == root || (root && doc == root->GetComposedDoc());
3930
0
}