Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/base/nsRange.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 the DOM Range object.
9
 */
10
11
#include "nscore.h"
12
#include "nsRange.h"
13
14
#include "nsString.h"
15
#include "nsReadableUtils.h"
16
#include "nsIContent.h"
17
#include "nsIDocument.h"
18
#include "nsError.h"
19
#include "nsIContentIterator.h"
20
#include "nsINodeList.h"
21
#include "nsGkAtoms.h"
22
#include "nsContentUtils.h"
23
#include "nsTextFrame.h"
24
#include "mozilla/dom/CharacterData.h"
25
#include "mozilla/dom/DocumentFragment.h"
26
#include "mozilla/dom/DocumentType.h"
27
#include "mozilla/dom/RangeBinding.h"
28
#include "mozilla/dom/DOMRect.h"
29
#include "mozilla/dom/DOMStringList.h"
30
#include "mozilla/dom/ShadowRoot.h"
31
#include "mozilla/dom/Selection.h"
32
#include "mozilla/dom/Text.h"
33
#include "mozilla/Telemetry.h"
34
#include "mozilla/Likely.h"
35
#include "nsCSSFrameConstructor.h"
36
#include "nsStyleStruct.h"
37
#include "nsStyleStructInlines.h"
38
#include "nsComputedDOMStyle.h"
39
#include "mozilla/dom/InspectorFontFace.h"
40
41
using namespace mozilla;
42
using namespace mozilla::dom;
43
44
JSObject*
45
nsRange::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
46
0
{
47
0
  return Range_Binding::Wrap(aCx, this, aGivenProto);
48
0
}
49
50
DocGroup*
51
nsRange::GetDocGroup() const
52
0
{
53
0
  return mOwner ? mOwner->GetDocGroup() : nullptr;
54
0
}
55
56
/******************************************************
57
 * stack based utilty class for managing monitor
58
 ******************************************************/
59
60
static void InvalidateAllFrames(nsINode* aNode)
61
0
{
62
0
  MOZ_ASSERT(aNode, "bad arg");
63
0
64
0
  nsIFrame* frame = nullptr;
65
0
  switch (aNode->NodeType()) {
66
0
    case nsINode::TEXT_NODE:
67
0
    case nsINode::ELEMENT_NODE:
68
0
    {
69
0
      nsIContent* content = static_cast<nsIContent*>(aNode);
70
0
      frame = content->GetPrimaryFrame();
71
0
      break;
72
0
    }
73
0
    case nsINode::DOCUMENT_NODE:
74
0
    {
75
0
      nsIDocument* doc = static_cast<nsIDocument*>(aNode);
76
0
      nsIPresShell* shell = doc ? doc->GetShell() : nullptr;
77
0
      frame = shell ? shell->GetRootFrame() : nullptr;
78
0
      break;
79
0
    }
80
0
  }
81
0
  for (nsIFrame* f = frame; f; f = f->GetNextContinuation()) {
82
0
    f->InvalidateFrameSubtree();
83
0
  }
84
0
}
85
86
// Utility routine to detect if a content node is completely contained in a range
87
// If outNodeBefore is returned true, then the node starts before the range does.
88
// If outNodeAfter is returned true, then the node ends after the range does.
89
// Note that both of the above might be true.
90
// If neither are true, the node is contained inside of the range.
91
// XXX - callers responsibility to ensure node in same doc as range!
92
93
// static
94
nsresult
95
nsRange::CompareNodeToRange(nsINode* aNode, nsRange* aRange,
96
                            bool *outNodeBefore, bool *outNodeAfter)
97
0
{
98
0
  NS_ENSURE_STATE(aNode);
99
0
  // create a pair of dom points that expresses location of node:
100
0
  //     NODE(start), NODE(end)
101
0
  // Let incoming range be:
102
0
  //    {RANGE(start), RANGE(end)}
103
0
  // if (RANGE(start) <= NODE(start))  and (RANGE(end) => NODE(end))
104
0
  // then the Node is contained (completely) by the Range.
105
0
106
0
  if (!aRange || !aRange->IsPositioned())
107
0
    return NS_ERROR_UNEXPECTED;
108
0
109
0
  // gather up the dom point info
110
0
  int32_t nodeStart, nodeEnd;
111
0
  nsINode* parent = aNode->GetParentNode();
112
0
  if (!parent) {
113
0
    // can't make a parent/offset pair to represent start or
114
0
    // end of the root node, because it has no parent.
115
0
    // so instead represent it by (node,0) and (node,numChildren)
116
0
    parent = aNode;
117
0
    nodeStart = 0;
118
0
    uint32_t childCount = aNode->GetChildCount();
119
0
    MOZ_ASSERT(childCount <= INT32_MAX,
120
0
               "There shouldn't be over INT32_MAX children");
121
0
    nodeEnd = static_cast<int32_t>(childCount);
122
0
  }
123
0
  else {
124
0
    nodeStart = parent->ComputeIndexOf(aNode);
125
0
    nodeEnd = nodeStart + 1;
126
0
    MOZ_ASSERT(nodeStart < nodeEnd, "nodeStart shouldn't be INT32_MAX");
127
0
  }
128
0
129
0
  nsINode* rangeStartContainer = aRange->GetStartContainer();
130
0
  nsINode* rangeEndContainer = aRange->GetEndContainer();
131
0
  uint32_t rangeStartOffset = aRange->StartOffset();
132
0
  uint32_t rangeEndOffset = aRange->EndOffset();
133
0
134
0
  // is RANGE(start) <= NODE(start) ?
135
0
  bool disconnected = false;
136
0
  *outNodeBefore =
137
0
    nsContentUtils::ComparePoints(rangeStartContainer,
138
0
                                  static_cast<int32_t>(rangeStartOffset),
139
0
                                  parent, nodeStart,
140
0
                                  &disconnected) > 0;
141
0
  NS_ENSURE_TRUE(!disconnected, NS_ERROR_DOM_WRONG_DOCUMENT_ERR);
142
0
143
0
  // is RANGE(end) >= NODE(end) ?
144
0
  *outNodeAfter =
145
0
    nsContentUtils::ComparePoints(rangeEndContainer,
146
0
                                  static_cast<int32_t>(rangeEndOffset),
147
0
                                  parent, nodeEnd,
148
0
                                  &disconnected) < 0;
149
0
  NS_ENSURE_TRUE(!disconnected, NS_ERROR_DOM_WRONG_DOCUMENT_ERR);
150
0
  return NS_OK;
151
0
}
152
153
static nsINode*
154
GetNextRangeCommonAncestor(nsINode* aNode)
155
0
{
156
0
  while (aNode && !aNode->IsCommonAncestorForRangeInSelection()) {
157
0
    if (!aNode->IsDescendantOfCommonAncestorForRangeInSelection()) {
158
0
      return nullptr;
159
0
    }
160
0
    aNode = aNode->GetParentNode();
161
0
  }
162
0
  return aNode;
163
0
}
164
165
/**
166
 * A Comparator suitable for mozilla::BinarySearchIf for searching a collection
167
 * of nsRange* for an overlap of (mNode, mStartOffset) .. (mNode, mEndOffset).
168
 */
169
struct IsItemInRangeComparator
170
{
171
  nsINode* mNode;
172
  uint32_t mStartOffset;
173
  uint32_t mEndOffset;
174
175
  int operator()(const nsRange* const aRange) const
176
0
  {
177
0
    int32_t cmp =
178
0
      nsContentUtils::ComparePoints(
179
0
        mNode, static_cast<int32_t>(mEndOffset),
180
0
        aRange->GetStartContainer(),
181
0
        static_cast<int32_t>(aRange->StartOffset()));
182
0
    if (cmp == 1) {
183
0
      cmp =
184
0
        nsContentUtils::ComparePoints(
185
0
          mNode, static_cast<int32_t>(mStartOffset),
186
0
          aRange->GetEndContainer(),
187
0
          static_cast<int32_t>(aRange->EndOffset()));
188
0
      if (cmp == -1) {
189
0
        return 0;
190
0
      }
191
0
      return 1;
192
0
    }
193
0
    return -1;
194
0
  }
195
};
196
197
/* static */ bool
198
nsRange::IsNodeSelected(nsINode* aNode, uint32_t aStartOffset,
199
                        uint32_t aEndOffset)
200
0
{
201
0
  MOZ_ASSERT(aNode, "bad arg");
202
0
203
0
  nsINode* n = GetNextRangeCommonAncestor(aNode);
204
0
  NS_ASSERTION(n || !aNode->IsSelectionDescendant(),
205
0
               "orphan selection descendant");
206
0
207
0
  // Collect the selection objects for potential ranges.
208
0
  nsTHashtable<nsPtrHashKey<Selection>> ancestorSelections;
209
0
  Selection* prevSelection = nullptr;
210
0
  uint32_t maxRangeCount = 0;
211
0
  for (; n; n = GetNextRangeCommonAncestor(n->GetParentNode())) {
212
0
    LinkedList<nsRange>* ranges = n->GetExistingCommonAncestorRanges();
213
0
    if (!ranges) {
214
0
      continue;
215
0
    }
216
0
    for (nsRange* range : *ranges) {
217
0
      MOZ_ASSERT(range->IsInSelection(),
218
0
                 "Why is this range registeed with a node?");
219
0
      // Looks like that IsInSelection() assert fails sometimes...
220
0
      if (range->IsInSelection()) {
221
0
        Selection* selection = range->mSelection;
222
0
        if (prevSelection != selection) {
223
0
          prevSelection = selection;
224
0
          ancestorSelections.PutEntry(selection);
225
0
        }
226
0
        maxRangeCount = std::max(maxRangeCount, selection->RangeCount());
227
0
      }
228
0
    }
229
0
  }
230
0
231
0
  IsItemInRangeComparator comparator = { aNode, aStartOffset, aEndOffset };
232
0
  if (!ancestorSelections.IsEmpty()) {
233
0
    for (auto iter = ancestorSelections.ConstIter(); !iter.Done(); iter.Next()) {
234
0
      Selection* selection = iter.Get()->GetKey();
235
0
      // Binary search the sorted ranges in this selection.
236
0
      // (Selection::GetRangeAt returns its ranges ordered).
237
0
      size_t low = 0;
238
0
      size_t high = selection->RangeCount();
239
0
240
0
      while (high != low) {
241
0
        size_t middle = low + (high - low) / 2;
242
0
243
0
        const nsRange* const range = selection->GetRangeAt(middle);
244
0
        int result = comparator(range);
245
0
        if (result == 0) {
246
0
          if (!range->Collapsed())
247
0
            return true;
248
0
249
0
          const nsRange* middlePlus1;
250
0
          const nsRange* middleMinus1;
251
0
          // if node end > start of middle+1, result = 1
252
0
          if (middle + 1 < high &&
253
0
              (middlePlus1 = selection->GetRangeAt(middle + 1)) &&
254
0
              nsContentUtils::ComparePoints(
255
0
                aNode, static_cast<int32_t>(aEndOffset),
256
0
                middlePlus1->GetStartContainer(),
257
0
                static_cast<int32_t>(middlePlus1->StartOffset())) > 0) {
258
0
              result = 1;
259
0
          // if node start < end of middle - 1, result = -1
260
0
          } else if (middle >= 1 &&
261
0
              (middleMinus1 = selection->GetRangeAt(middle - 1)) &&
262
0
              nsContentUtils::ComparePoints(
263
0
                aNode, static_cast<int32_t>(aStartOffset),
264
0
                middleMinus1->GetEndContainer(),
265
0
                static_cast<int32_t>(middleMinus1->EndOffset())) < 0) {
266
0
            result = -1;
267
0
          } else {
268
0
            break;
269
0
          }
270
0
        }
271
0
272
0
        if (result < 0) {
273
0
          high = middle;
274
0
        } else {
275
0
          low = middle + 1;
276
0
        }
277
0
      }
278
0
    }
279
0
  }
280
0
  return false;
281
0
}
282
283
/******************************************************
284
 * constructor/destructor
285
 ******************************************************/
286
287
nsRange::~nsRange()
288
0
{
289
0
  NS_ASSERTION(!IsInSelection(), "deleting nsRange that is in use");
290
0
291
0
  // we want the side effects (releases and list removals)
292
0
  DoSetRange(RawRangeBoundary(), RawRangeBoundary(), nullptr);
293
0
}
294
295
nsRange::nsRange(nsINode* aNode)
296
  : mRoot(nullptr)
297
  , mRegisteredCommonAncestor(nullptr)
298
  , mNextStartRef(nullptr)
299
  , mNextEndRef(nullptr)
300
  , mIsPositioned(false)
301
  , mIsGenerated(false)
302
  , mCalledByJS(false)
303
0
{
304
0
  MOZ_ASSERT(aNode, "range isn't in a document!");
305
0
  mOwner = aNode->OwnerDoc();
306
0
}
307
308
/* static */
309
nsresult
310
nsRange::CreateRange(nsINode* aStartContainer, uint32_t aStartOffset,
311
                     nsINode* aEndParent, uint32_t aEndOffset,
312
                     nsRange** aRange)
313
0
{
314
0
  MOZ_ASSERT(aRange);
315
0
  *aRange = nullptr;
316
0
317
0
  RefPtr<nsRange> range = new nsRange(aStartContainer);
318
0
  nsresult rv = range->SetStartAndEnd(aStartContainer, aStartOffset,
319
0
                                      aEndParent, aEndOffset);
320
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
321
0
    return rv;
322
0
  }
323
0
  range.forget(aRange);
324
0
  return NS_OK;
325
0
}
326
327
/* static */
328
nsresult
329
nsRange::CreateRange(const RawRangeBoundary& aStart,
330
                     const RawRangeBoundary& aEnd,
331
                     nsRange** aRange)
332
0
{
333
0
  RefPtr<nsRange> range = new nsRange(aStart.Container());
334
0
  nsresult rv = range->SetStartAndEnd(aStart, aEnd);
335
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
336
0
    return rv;
337
0
  }
338
0
  range.forget(aRange);
339
0
  return NS_OK;
340
0
}
341
342
/******************************************************
343
 * nsISupports
344
 ******************************************************/
345
346
NS_IMPL_MAIN_THREAD_ONLY_CYCLE_COLLECTING_ADDREF(nsRange)
347
NS_IMPL_MAIN_THREAD_ONLY_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(
348
  nsRange, DoSetRange(RawRangeBoundary(), RawRangeBoundary(), nullptr))
349
350
// QueryInterface implementation for nsRange
351
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsRange)
352
0
  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
353
0
  NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
354
0
  NS_INTERFACE_MAP_ENTRY(nsISupports)
355
0
NS_INTERFACE_MAP_END
356
357
NS_IMPL_CYCLE_COLLECTION_CLASS(nsRange)
358
359
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsRange)
360
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
361
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner);
362
0
363
0
  // We _could_ just rely on Reset() to UnregisterCommonAncestor(),
364
0
  // but it wouldn't know we're calling it from Unlink and so would do
365
0
  // more work than it really needs to.
366
0
  if (tmp->mRegisteredCommonAncestor) {
367
0
    tmp->UnregisterCommonAncestor(tmp->mRegisteredCommonAncestor, true);
368
0
  }
369
0
370
0
  tmp->Reset();
371
0
372
0
  // This needs to be unlinked after Reset() is called, as it controls
373
0
  // the result of IsInSelection() which is used by tmp->Reset().
374
0
  MOZ_DIAGNOSTIC_ASSERT(!tmp->isInList(),
375
0
                        "Shouldn't be registered now that we're unlinking");
376
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelection);
377
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
378
379
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsRange)
380
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
381
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStart)
382
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEnd)
383
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot)
384
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelection)
385
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
386
387
0
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsRange)
388
0
  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
389
0
NS_IMPL_CYCLE_COLLECTION_TRACE_END
390
391
static void MarkDescendants(nsINode* aNode)
392
0
{
393
0
  // Set NodeIsDescendantOfCommonAncestorForRangeInSelection on aNode's
394
0
  // descendants unless aNode is already marked as a range common ancestor
395
0
  // or a descendant of one, in which case all of our descendants have the
396
0
  // bit set already.
397
0
  if (!aNode->IsSelectionDescendant()) {
398
0
    // don't set the Descendant bit on |aNode| itself
399
0
    nsINode* node = aNode->GetNextNode(aNode);
400
0
    while (node) {
401
0
      node->SetDescendantOfCommonAncestorForRangeInSelection();
402
0
      if (!node->IsCommonAncestorForRangeInSelection()) {
403
0
        node = node->GetNextNode(aNode);
404
0
      } else {
405
0
        // optimize: skip this sub-tree since it's marked already.
406
0
        node = node->GetNextNonChildNode(aNode);
407
0
      }
408
0
    }
409
0
  }
410
0
}
411
412
static void UnmarkDescendants(nsINode* aNode)
413
0
{
414
0
  // Unset NodeIsDescendantOfCommonAncestorForRangeInSelection on aNode's
415
0
  // descendants unless aNode is a descendant of another range common ancestor.
416
0
  // Also, exclude descendants of range common ancestors (but not the common
417
0
  // ancestor itself).
418
0
  if (!aNode->IsDescendantOfCommonAncestorForRangeInSelection()) {
419
0
    // we know |aNode| doesn't have any bit set
420
0
    nsINode* node = aNode->GetNextNode(aNode);
421
0
    while (node) {
422
0
      node->ClearDescendantOfCommonAncestorForRangeInSelection();
423
0
      if (!node->IsCommonAncestorForRangeInSelection()) {
424
0
        node = node->GetNextNode(aNode);
425
0
      } else {
426
0
        // We found an ancestor of an overlapping range, skip its descendants.
427
0
        node = node->GetNextNonChildNode(aNode);
428
0
      }
429
0
    }
430
0
  }
431
0
}
432
433
void
434
nsRange::RegisterCommonAncestor(nsINode* aNode)
435
0
{
436
0
  MOZ_ASSERT(aNode, "bad arg");
437
0
438
0
  MOZ_DIAGNOSTIC_ASSERT(IsInSelection(), "registering range not in selection");
439
0
440
0
  mRegisteredCommonAncestor = aNode;
441
0
442
0
  MarkDescendants(aNode);
443
0
444
0
  UniquePtr<LinkedList<nsRange>>& ranges = aNode->GetCommonAncestorRangesPtr();
445
0
  if (!ranges) {
446
0
    ranges = MakeUnique<LinkedList<nsRange>>();
447
0
  }
448
0
449
0
  MOZ_DIAGNOSTIC_ASSERT(!isInList());
450
0
  ranges->insertBack(this);
451
0
  aNode->SetCommonAncestorForRangeInSelection();
452
0
}
453
454
void
455
nsRange::UnregisterCommonAncestor(nsINode* aNode, bool aIsUnlinking)
456
0
{
457
0
  MOZ_ASSERT(aNode, "bad arg");
458
0
  NS_ASSERTION(aNode->IsCommonAncestorForRangeInSelection(), "wrong node");
459
0
  MOZ_DIAGNOSTIC_ASSERT(aNode == mRegisteredCommonAncestor, "wrong node");
460
0
  LinkedList<nsRange>* ranges = aNode->GetExistingCommonAncestorRanges();
461
0
  MOZ_ASSERT(ranges);
462
0
463
0
  mRegisteredCommonAncestor = nullptr;
464
0
465
#ifdef DEBUG
466
  bool found = false;
467
  for (nsRange* range : *ranges) {
468
    if (range == this) {
469
      found = true;
470
      break;
471
    }
472
  }
473
  MOZ_ASSERT(found,
474
             "We should be in the list on our registered common ancestor");
475
#endif // DEBUG
476
477
0
  remove();
478
0
479
0
  // We don't want to waste time unmarking flags on nodes that are
480
0
  // being unlinked anyway.
481
0
  if (!aIsUnlinking && ranges->isEmpty()) {
482
0
    aNode->ClearCommonAncestorForRangeInSelection();
483
0
    UnmarkDescendants(aNode);
484
0
  }
485
0
}
486
487
/******************************************************
488
 * nsIMutationObserver implementation
489
 ******************************************************/
490
void
491
nsRange::CharacterDataChanged(nsIContent* aContent,
492
                              const CharacterDataChangeInfo& aInfo)
493
0
{
494
0
  // If this is called when this is not positioned, it means that this range
495
0
  // will be initialized again or destroyed soon.  See Selection::mCachedRange.
496
0
  if (!mIsPositioned) {
497
0
    MOZ_ASSERT(mRoot);
498
0
    return;
499
0
  }
500
0
501
0
  MOZ_ASSERT(!mNextEndRef);
502
0
  MOZ_ASSERT(!mNextStartRef);
503
0
504
0
  nsINode* newRoot = nullptr;
505
0
  RawRangeBoundary newStart;
506
0
  RawRangeBoundary newEnd;
507
0
508
0
  if (aInfo.mDetails &&
509
0
      aInfo.mDetails->mType == CharacterDataChangeInfo::Details::eSplit) {
510
0
    // If the splitted text node is immediately before a range boundary point
511
0
    // that refers to a child index (i.e. its parent is the boundary container)
512
0
    // then we need to adjust the corresponding boundary to account for the new
513
0
    // text node that will be inserted. However, because the new sibling hasn't
514
0
    // been inserted yet, that would result in an invalid boundary. Therefore,
515
0
    // we store the new child in mNext*Ref to make sure we adjust the boundary
516
0
    // in the next ContentInserted or ContentAppended call.
517
0
    nsINode* parentNode = aContent->GetParentNode();
518
0
    if (parentNode == mEnd.Container()) {
519
0
      if (aContent == mEnd.Ref()) {
520
0
        MOZ_ASSERT(aInfo.mDetails->mNextSibling);
521
0
        mNextEndRef = aInfo.mDetails->mNextSibling;
522
0
      }
523
0
    }
524
0
525
0
    if (parentNode == mStart.Container()) {
526
0
      if (aContent == mStart.Ref()) {
527
0
        MOZ_ASSERT(aInfo.mDetails->mNextSibling);
528
0
        mNextStartRef = aInfo.mDetails->mNextSibling;
529
0
      }
530
0
    }
531
0
  }
532
0
533
0
  // If the changed node contains our start boundary and the change starts
534
0
  // before the boundary we'll need to adjust the offset.
535
0
  if (aContent == mStart.Container() && aInfo.mChangeStart < mStart.Offset()) {
536
0
    if (aInfo.mDetails) {
537
0
      // splitText(), aInfo->mDetails->mNextSibling is the new text node
538
0
      NS_ASSERTION(aInfo.mDetails->mType ==
539
0
                   CharacterDataChangeInfo::Details::eSplit,
540
0
                   "only a split can start before the end");
541
0
      NS_ASSERTION(mStart.Offset() <= aInfo.mChangeEnd + 1,
542
0
                   "mStart.Offset() is beyond the end of this node");
543
0
      int32_t newStartOffset = mStart.Offset() - aInfo.mChangeStart;
544
0
      newStart.Set(aInfo.mDetails->mNextSibling, newStartOffset);
545
0
      if (MOZ_UNLIKELY(aContent == mRoot)) {
546
0
        newRoot = IsValidBoundary(newStart.Container());
547
0
      }
548
0
549
0
      bool isCommonAncestor =
550
0
        IsInSelection() && mStart.Container() == mEnd.Container();
551
0
      if (isCommonAncestor) {
552
0
        UnregisterCommonAncestor(mStart.Container(), false);
553
0
        RegisterCommonAncestor(newStart.Container());
554
0
      }
555
0
      if (mStart.Container()->IsDescendantOfCommonAncestorForRangeInSelection()) {
556
0
        newStart.Container()->SetDescendantOfCommonAncestorForRangeInSelection();
557
0
      }
558
0
    } else {
559
0
      // If boundary is inside changed text, position it before change
560
0
      // else adjust start offset for the change in length.
561
0
      int32_t newStartOffset = mStart.Offset() <= aInfo.mChangeEnd ?
562
0
        aInfo.mChangeStart :
563
0
        mStart.Offset() + aInfo.mChangeStart - aInfo.mChangeEnd +
564
0
          aInfo.mReplaceLength;
565
0
      newStart.Set(mStart.Container(), newStartOffset);
566
0
    }
567
0
  }
568
0
569
0
  // Do the same thing for the end boundary, except for splitText of a node
570
0
  // with no parent then only switch to the new node if the start boundary
571
0
  // did so too (otherwise the range would end up with disconnected nodes).
572
0
  if (aContent == mEnd.Container() && aInfo.mChangeStart < mEnd.Offset()) {
573
0
    if (aInfo.mDetails && (aContent->GetParentNode() || newStart.Container())) {
574
0
      // splitText(), aInfo.mDetails->mNextSibling is the new text node
575
0
      NS_ASSERTION(aInfo.mDetails->mType ==
576
0
                   CharacterDataChangeInfo::Details::eSplit,
577
0
                   "only a split can start before the end");
578
0
      NS_ASSERTION(mEnd.Offset() <= aInfo.mChangeEnd + 1,
579
0
                   "mEnd.Offset() is beyond the end of this node");
580
0
      newEnd.Set(aInfo.mDetails->mNextSibling, mEnd.Offset() - aInfo.mChangeStart);
581
0
582
0
      bool isCommonAncestor =
583
0
        IsInSelection() && mStart.Container() == mEnd.Container();
584
0
      if (isCommonAncestor && !newStart.Container()) {
585
0
        // The split occurs inside the range.
586
0
        UnregisterCommonAncestor(mStart.Container(), false);
587
0
        RegisterCommonAncestor(mStart.Container()->GetParentNode());
588
0
        newEnd.Container()->SetDescendantOfCommonAncestorForRangeInSelection();
589
0
      } else if (mEnd.Container()->
590
0
                   IsDescendantOfCommonAncestorForRangeInSelection()) {
591
0
        newEnd.Container()->SetDescendantOfCommonAncestorForRangeInSelection();
592
0
      }
593
0
    } else {
594
0
      int32_t newEndOffset = mEnd.Offset() <= aInfo.mChangeEnd ?
595
0
        aInfo.mChangeStart :
596
0
        mEnd.Offset() + aInfo.mChangeStart - aInfo.mChangeEnd +
597
0
          aInfo.mReplaceLength;
598
0
      newEnd.Set(mEnd.Container(), newEndOffset);
599
0
    }
600
0
  }
601
0
602
0
  if (aInfo.mDetails &&
603
0
      aInfo.mDetails->mType == CharacterDataChangeInfo::Details::eMerge) {
604
0
    // normalize(), aInfo.mDetails->mNextSibling is the merged text node
605
0
    // that will be removed
606
0
    nsIContent* removed = aInfo.mDetails->mNextSibling;
607
0
    if (removed == mStart.Container()) {
608
0
      newStart.Set(aContent, mStart.Offset() + aInfo.mChangeStart);
609
0
      if (MOZ_UNLIKELY(removed == mRoot)) {
610
0
        newRoot = IsValidBoundary(newStart.Container());
611
0
      }
612
0
    }
613
0
    if (removed == mEnd.Container()) {
614
0
      newEnd.Set(aContent, mEnd.Offset() + aInfo.mChangeStart);
615
0
      if (MOZ_UNLIKELY(removed == mRoot)) {
616
0
        newRoot = IsValidBoundary(newEnd.Container());
617
0
      }
618
0
    }
619
0
    // When the removed text node's parent is one of our boundary nodes we may
620
0
    // need to adjust the offset to account for the removed node. However,
621
0
    // there will also be a ContentRemoved notification later so the only cases
622
0
    // we need to handle here is when the removed node is the text node after
623
0
    // the boundary.  (The m*Offset > 0 check is an optimization - a boundary
624
0
    // point before the first child is never affected by normalize().)
625
0
    nsINode* parentNode = aContent->GetParentNode();
626
0
    if (parentNode == mStart.Container() && mStart.Offset() > 0 &&
627
0
        mStart.Offset() < parentNode->GetChildCount() &&
628
0
        removed == mStart.GetChildAtOffset()) {
629
0
      newStart.Set(aContent, aInfo.mChangeStart);
630
0
    }
631
0
    if (parentNode == mEnd.Container() && mEnd.Offset() > 0 &&
632
0
        mEnd.Offset() < parentNode->GetChildCount() &&
633
0
        removed == mEnd.GetChildAtOffset()) {
634
0
      newEnd.Set(aContent, aInfo.mChangeEnd);
635
0
    }
636
0
  }
637
0
638
0
  if (newStart.IsSet() || newEnd.IsSet()) {
639
0
    if (!newStart.IsSet()) {
640
0
      newStart = mStart;
641
0
    }
642
0
    if (!newEnd.IsSet()) {
643
0
      newEnd = mEnd;
644
0
    }
645
0
    DoSetRange(newStart, newEnd,
646
0
               newRoot ? newRoot : mRoot.get(),
647
0
               !newEnd.Container()->GetParentNode() || !newStart.Container()->GetParentNode());
648
0
  }
649
0
}
650
651
void
652
nsRange::ContentAppended(nsIContent*  aFirstNewContent)
653
0
{
654
0
  // If this is called when this is not positioned, it means that this range
655
0
  // will be initialized again or destroyed soon.  See Selection::mCachedRange.
656
0
  if (!mIsPositioned) {
657
0
    MOZ_ASSERT(mRoot);
658
0
    return;
659
0
  }
660
0
661
0
  nsINode* container = aFirstNewContent->GetParentNode();
662
0
  MOZ_ASSERT(container);
663
0
  if (container->IsSelectionDescendant() && IsInSelection()) {
664
0
    nsINode* child = aFirstNewContent;
665
0
    while (child) {
666
0
      if (!child->IsDescendantOfCommonAncestorForRangeInSelection()) {
667
0
        MarkDescendants(child);
668
0
        child->SetDescendantOfCommonAncestorForRangeInSelection();
669
0
      }
670
0
      child = child->GetNextSibling();
671
0
    }
672
0
  }
673
0
674
0
  if (mNextStartRef || mNextEndRef) {
675
0
    // A splitText has occurred, if any mNext*Ref was set, we need to adjust
676
0
    // the range boundaries.
677
0
    if (mNextStartRef) {
678
0
      mStart.SetAfterRef(mStart.Container(), mNextStartRef);
679
0
      MOZ_ASSERT(mNextStartRef == aFirstNewContent);
680
0
      mNextStartRef = nullptr;
681
0
    }
682
0
    if (mNextEndRef) {
683
0
      mEnd.SetAfterRef(mEnd.Container(), mNextEndRef);
684
0
      MOZ_ASSERT(mNextEndRef == aFirstNewContent);
685
0
      mNextEndRef = nullptr;
686
0
    }
687
0
    DoSetRange(mStart.AsRaw(), mEnd.AsRaw(), mRoot, true);
688
0
  }
689
0
}
690
691
void
692
nsRange::ContentInserted(nsIContent* aChild)
693
0
{
694
0
  // If this is called when this is not positioned, it means that this range
695
0
  // will be initialized again or destroyed soon.  See Selection::mCachedRange.
696
0
  if (!mIsPositioned) {
697
0
    MOZ_ASSERT(mRoot);
698
0
    return;
699
0
  }
700
0
701
0
  bool updateBoundaries = false;
702
0
  nsINode* container = aChild->GetParentNode();
703
0
  MOZ_ASSERT(container);
704
0
  RawRangeBoundary newStart(mStart);
705
0
  RawRangeBoundary newEnd(mEnd);
706
0
  MOZ_ASSERT(aChild->GetParentNode() == container);
707
0
708
0
  // Invalidate boundary offsets if a child that may have moved them was
709
0
  // inserted.
710
0
  if (container == mStart.Container()) {
711
0
    newStart.InvalidateOffset();
712
0
    updateBoundaries = true;
713
0
  }
714
0
715
0
  if (container == mEnd.Container()) {
716
0
    newEnd.InvalidateOffset();
717
0
    updateBoundaries = true;
718
0
  }
719
0
720
0
  if (container->IsSelectionDescendant() &&
721
0
      !aChild->IsDescendantOfCommonAncestorForRangeInSelection()) {
722
0
    MarkDescendants(aChild);
723
0
    aChild->SetDescendantOfCommonAncestorForRangeInSelection();
724
0
  }
725
0
726
0
  if (mNextStartRef || mNextEndRef) {
727
0
    if (mNextStartRef) {
728
0
      newStart.SetAfterRef(mStart.Container(), mNextStartRef);
729
0
      MOZ_ASSERT(mNextStartRef == aChild);
730
0
      mNextStartRef = nullptr;
731
0
    }
732
0
    if (mNextEndRef) {
733
0
      newEnd.SetAfterRef(mEnd.Container(), mNextEndRef);
734
0
      MOZ_ASSERT(mNextEndRef == aChild);
735
0
      mNextEndRef = nullptr;
736
0
    }
737
0
738
0
    updateBoundaries = true;
739
0
  }
740
0
741
0
  if (updateBoundaries) {
742
0
    DoSetRange(newStart, newEnd, mRoot);
743
0
  }
744
0
}
745
746
void
747
nsRange::ContentRemoved(nsIContent* aChild, nsIContent* aPreviousSibling)
748
0
{
749
0
  // If this is called when this is not positioned, it means that this range
750
0
  // will be initialized again or destroyed soon.  See Selection::mCachedRange.
751
0
  if (!mIsPositioned) {
752
0
    MOZ_ASSERT(mRoot);
753
0
    return;
754
0
  }
755
0
756
0
  nsINode* container = aChild->GetParentNode();
757
0
  MOZ_ASSERT(container);
758
0
759
0
  RawRangeBoundary newStart;
760
0
  RawRangeBoundary newEnd;
761
0
  Maybe<bool> gravitateStart;
762
0
  bool gravitateEnd;
763
0
764
0
  // Adjust position if a sibling was removed...
765
0
  if (container == mStart.Container()) {
766
0
    // We're only interested if our boundary reference was removed, otherwise
767
0
    // we can just invalidate the offset.
768
0
    if (aChild == mStart.Ref()) {
769
0
      newStart.SetAfterRef(container, aPreviousSibling);
770
0
    } else {
771
0
      newStart = mStart;
772
0
      newStart.InvalidateOffset();
773
0
    }
774
0
  } else {
775
0
    gravitateStart = Some(nsContentUtils::ContentIsDescendantOf(mStart.Container(), aChild));
776
0
    if (gravitateStart.value()) {
777
0
      newStart.SetAfterRef(container, aPreviousSibling);
778
0
    }
779
0
  }
780
0
781
0
  // Do same thing for end boundry.
782
0
  if (container == mEnd.Container()) {
783
0
    if (aChild == mEnd.Ref()) {
784
0
      newEnd.SetAfterRef(container, aPreviousSibling);
785
0
    } else {
786
0
      newEnd = mEnd;
787
0
      newEnd.InvalidateOffset();
788
0
    }
789
0
  } else {
790
0
    if (mStart.Container() == mEnd.Container() && gravitateStart.isSome()) {
791
0
      gravitateEnd = gravitateStart.value();
792
0
    } else {
793
0
      gravitateEnd = nsContentUtils::ContentIsDescendantOf(mEnd.Container(), aChild);
794
0
    }
795
0
    if (gravitateEnd) {
796
0
      newEnd.SetAfterRef(container, aPreviousSibling);
797
0
    }
798
0
  }
799
0
800
0
  if (newStart.IsSet() || newEnd.IsSet()) {
801
0
    DoSetRange(newStart.IsSet() ? newStart : mStart.AsRaw(),
802
0
               newEnd.IsSet() ? newEnd : mEnd.AsRaw(), mRoot);
803
0
  }
804
0
805
0
  MOZ_ASSERT(mStart.Ref() != aChild);
806
0
  MOZ_ASSERT(mEnd.Ref() != aChild);
807
0
808
0
  if (container->IsSelectionDescendant() &&
809
0
      aChild->IsDescendantOfCommonAncestorForRangeInSelection()) {
810
0
    aChild->ClearDescendantOfCommonAncestorForRangeInSelection();
811
0
    UnmarkDescendants(aChild);
812
0
  }
813
0
}
814
815
void
816
nsRange::ParentChainChanged(nsIContent *aContent)
817
0
{
818
0
  NS_ASSERTION(mRoot == aContent, "Wrong ParentChainChanged notification?");
819
0
  nsINode* newRoot = IsValidBoundary(mStart.Container());
820
0
  NS_ASSERTION(newRoot, "No valid boundary or root found!");
821
0
  if (newRoot != IsValidBoundary(mEnd.Container())) {
822
0
    // Sometimes ordering involved in cycle collection can lead to our
823
0
    // start parent and/or end parent being disconnected from our root
824
0
    // without our getting a ContentRemoved notification.
825
0
    // See bug 846096 for more details.
826
0
    NS_ASSERTION(mEnd.Container()->IsInNativeAnonymousSubtree(),
827
0
                 "This special case should happen only with "
828
0
                 "native-anonymous content");
829
0
    // When that happens, bail out and set pointers to null; since we're
830
0
    // in cycle collection and unreachable it shouldn't matter.
831
0
    Reset();
832
0
    return;
833
0
  }
834
0
  // This is safe without holding a strong ref to self as long as the change
835
0
  // of mRoot is the last thing in DoSetRange.
836
0
  DoSetRange(mStart.AsRaw(), mEnd.AsRaw(), newRoot);
837
0
}
838
839
bool
840
nsRange::IsPointInRange(const RawRangeBoundary& aPoint, ErrorResult& aRv)
841
0
{
842
0
  uint16_t compareResult = ComparePoint(aPoint, aRv);
843
0
  // If the node isn't in the range's document, it clearly isn't in the range.
844
0
  if (aRv.ErrorCodeIs(NS_ERROR_DOM_WRONG_DOCUMENT_ERR)) {
845
0
    aRv.SuppressException();
846
0
    return false;
847
0
  }
848
0
849
0
  return compareResult == 0;
850
0
}
851
852
int16_t
853
nsRange::ComparePoint(const RawRangeBoundary& aPoint, ErrorResult& aRv)
854
0
{
855
0
  if (NS_WARN_IF(!aPoint.IsSet())) {
856
0
    // FYI: Shouldn't reach this case if it's called by JS.  Therefore, it's
857
0
    //      okay to warn.
858
0
    aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
859
0
    return 0;
860
0
  }
861
0
862
0
  // our range is in a good state?
863
0
  if (!mIsPositioned) {
864
0
    aRv.Throw(NS_ERROR_NOT_INITIALIZED);
865
0
    return 0;
866
0
  }
867
0
868
0
  if (!nsContentUtils::ContentIsDescendantOf(aPoint.Container(), mRoot)) {
869
0
    aRv.Throw(NS_ERROR_DOM_WRONG_DOCUMENT_ERR);
870
0
    return 0;
871
0
  }
872
0
873
0
  if (aPoint.Container()->NodeType() == nsINode::DOCUMENT_TYPE_NODE) {
874
0
    aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
875
0
    return 0;
876
0
  }
877
0
878
0
  if (aPoint.Offset() > aPoint.Container()->Length()) {
879
0
    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
880
0
    return 0;
881
0
  }
882
0
883
0
  int32_t cmp = nsContentUtils::ComparePoints(aPoint, mStart.AsRaw());
884
0
  if (cmp <= 0) {
885
0
    return cmp;
886
0
  }
887
0
  if (nsContentUtils::ComparePoints(mEnd.AsRaw(), aPoint) == -1) {
888
0
    return 1;
889
0
  }
890
0
891
0
  return 0;
892
0
}
893
894
bool
895
nsRange::IntersectsNode(nsINode& aNode, ErrorResult& aRv)
896
0
{
897
0
  if (!mIsPositioned) {
898
0
    aRv.Throw(NS_ERROR_NOT_INITIALIZED);
899
0
    return false;
900
0
  }
901
0
902
0
  // Step 3.
903
0
  nsINode* parent = aNode.GetParentNode();
904
0
  if (!parent) {
905
0
    // Steps 2 and 4.
906
0
    // |parent| is null, so |node|'s root is |node| itself.
907
0
    return GetRoot() == &aNode;
908
0
  }
909
0
910
0
  // Step 5.
911
0
  int32_t nodeIndex = parent->ComputeIndexOf(&aNode);
912
0
913
0
  // Steps 6-7.
914
0
  // Note: if disconnected is true, ComparePoints returns 1.
915
0
  bool disconnected = false;
916
0
  bool result = nsContentUtils::ComparePoints(mStart.Container(), mStart.Offset(),
917
0
                                             parent, nodeIndex + 1,
918
0
                                             &disconnected) < 0 &&
919
0
               nsContentUtils::ComparePoints(parent, nodeIndex,
920
0
                                             mEnd.Container(), mEnd.Offset(),
921
0
                                             &disconnected) < 0;
922
0
923
0
  // Step 2.
924
0
  if (disconnected) {
925
0
    result = false;
926
0
  }
927
0
  return result;
928
0
}
929
930
void
931
nsRange::NotifySelectionListenersAfterRangeSet()
932
0
{
933
0
  if (mSelection) {
934
0
    // Our internal code should not move focus with using this instance while
935
0
    // it's calling Selection::NotifySelectionListeners() which may move focus
936
0
    // or calls selection listeners.  So, let's set mCalledByJS to false here
937
0
    // since non-*JS() methods don't set it to false.
938
0
    AutoCalledByJSRestore calledByJSRestorer(*this);
939
0
    mCalledByJS = false;
940
0
    // Be aware, this range may be modified or stop being a range for selection
941
0
    // after this call.  Additionally, the selection instance may have gone.
942
0
    RefPtr<Selection> selection = mSelection;
943
0
    selection->NotifySelectionListeners(calledByJSRestorer.SavedValue());
944
0
  }
945
0
}
946
947
/******************************************************
948
 * Private helper routines
949
 ******************************************************/
950
951
// It's important that all setting of the range start/end points
952
// go through this function, which will do all the right voodoo
953
// for content notification of range ownership.
954
// Calling DoSetRange with either parent argument null will collapse
955
// the range to have both endpoints point to the other node
956
void
957
nsRange::DoSetRange(const RawRangeBoundary& aStart,
958
                    const RawRangeBoundary& aEnd,
959
                    nsINode* aRoot, bool aNotInsertedYet)
960
0
{
961
0
  MOZ_ASSERT((aStart.IsSet() && aEnd.IsSet() && aRoot) ||
962
0
             (!aStart.IsSet() && !aEnd.IsSet()),
963
0
             "Set all or none");
964
0
965
0
  MOZ_ASSERT(!aRoot || (!aStart.IsSet() && !aEnd.IsSet()) || aNotInsertedYet ||
966
0
             (nsContentUtils::ContentIsDescendantOf(aStart.Container(), aRoot) &&
967
0
              nsContentUtils::ContentIsDescendantOf(aEnd.Container(), aRoot) &&
968
0
              aRoot == IsValidBoundary(aStart.Container()) &&
969
0
              aRoot == IsValidBoundary(aEnd.Container())),
970
0
             "Wrong root");
971
0
972
0
  MOZ_ASSERT(!aRoot || (!aStart.IsSet() && !aEnd.IsSet()) ||
973
0
             (aStart.Container()->IsContent() &&
974
0
              aEnd.Container()->IsContent() &&
975
0
              aRoot ==
976
0
               static_cast<nsIContent*>(aStart.Container())->GetBindingParent() &&
977
0
              aRoot ==
978
0
               static_cast<nsIContent*>(aEnd.Container())->GetBindingParent()) ||
979
0
             (!aRoot->GetParentNode() &&
980
0
              (aRoot->IsDocument() ||
981
0
               aRoot->IsAttr() ||
982
0
               aRoot->IsDocumentFragment() ||
983
0
                /*For backward compatibility*/
984
0
               aRoot->IsContent())),
985
0
             "Bad root");
986
0
987
0
  if (mRoot != aRoot) {
988
0
    if (mRoot) {
989
0
      mRoot->RemoveMutationObserver(this);
990
0
    }
991
0
    if (aRoot) {
992
0
      aRoot->AddMutationObserver(this);
993
0
    }
994
0
  }
995
0
  bool checkCommonAncestor =
996
0
    (mStart.Container() != aStart.Container() || mEnd.Container() != aEnd.Container()) &&
997
0
    IsInSelection() && !aNotInsertedYet;
998
0
999
0
  // GetCommonAncestor is unreliable while we're unlinking (could
1000
0
  // return null if our start/end have already been unlinked), so make
1001
0
  // sure to not use it here to determine our "old" current ancestor.
1002
0
  mStart = aStart;
1003
0
  mEnd = aEnd;
1004
0
1005
0
  mIsPositioned = !!mStart.Container();
1006
0
  if (checkCommonAncestor) {
1007
0
    nsINode* oldCommonAncestor = mRegisteredCommonAncestor;
1008
0
    nsINode* newCommonAncestor = GetCommonAncestor();
1009
0
    if (newCommonAncestor != oldCommonAncestor) {
1010
0
      if (oldCommonAncestor) {
1011
0
        UnregisterCommonAncestor(oldCommonAncestor, false);
1012
0
      }
1013
0
      if (newCommonAncestor) {
1014
0
        RegisterCommonAncestor(newCommonAncestor);
1015
0
      } else {
1016
0
        NS_ASSERTION(!mIsPositioned, "unexpected disconnected nodes");
1017
0
        mSelection = nullptr;
1018
0
        MOZ_DIAGNOSTIC_ASSERT(!mRegisteredCommonAncestor,
1019
0
                              "How can we have a registered common ancestor when we "
1020
0
                              "didn't register ourselves?");
1021
0
        MOZ_DIAGNOSTIC_ASSERT(!isInList(),
1022
0
                              "Shouldn't be registered if we have no "
1023
0
                              "mRegisteredCommonAncestor");
1024
0
      }
1025
0
    }
1026
0
  }
1027
0
1028
0
  // This needs to be the last thing this function does, other than notifying
1029
0
  // selection listeners. See comment in ParentChainChanged.
1030
0
  mRoot = aRoot;
1031
0
1032
0
  // Notify any selection listeners. This has to occur last because otherwise the world
1033
0
  // could be observed by a selection listener while the range was in an invalid state.
1034
0
  // So we run it off of a script runner to ensure it runs after the mutation observers
1035
0
  // have finished running.
1036
0
  if (mSelection) {
1037
0
    nsContentUtils::AddScriptRunner(NewRunnableMethod(
1038
0
                                    "NotifySelectionListenersAfterRangeSet",
1039
0
                                    this,
1040
0
                                    &nsRange::NotifySelectionListenersAfterRangeSet));
1041
0
  }
1042
0
}
1043
1044
static int32_t
1045
IndexOf(nsINode* aChild)
1046
0
{
1047
0
  nsINode* parent = aChild->GetParentNode();
1048
0
1049
0
  return parent ? parent->ComputeIndexOf(aChild) : -1;
1050
0
}
1051
1052
void
1053
nsRange::SetSelection(mozilla::dom::Selection* aSelection)
1054
0
{
1055
0
  if (mSelection == aSelection) {
1056
0
    return;
1057
0
  }
1058
0
1059
0
  // At least one of aSelection and mSelection must be null
1060
0
  // aSelection will be null when we are removing from a selection
1061
0
  // and a range can't be in more than one selection at a time,
1062
0
  // thus mSelection must be null too.
1063
0
  MOZ_ASSERT(!aSelection || !mSelection);
1064
0
1065
0
  // Extra step in case our parent failed to ensure the above
1066
0
  // invariant.
1067
0
  if (aSelection && mSelection) {
1068
0
    mSelection->RemoveRange(*this, IgnoreErrors());
1069
0
  }
1070
0
1071
0
  mSelection = aSelection;
1072
0
  if (mSelection) {
1073
0
    nsINode* commonAncestor = GetCommonAncestor();
1074
0
    NS_ASSERTION(commonAncestor, "unexpected disconnected nodes");
1075
0
    RegisterCommonAncestor(commonAncestor);
1076
0
  } else {
1077
0
    UnregisterCommonAncestor(mRegisteredCommonAncestor, false);
1078
0
    MOZ_DIAGNOSTIC_ASSERT(!mRegisteredCommonAncestor,
1079
0
                          "How can we have a registered common ancestor when we "
1080
0
                          "just unregistered?");
1081
0
    MOZ_DIAGNOSTIC_ASSERT(!isInList(),
1082
0
                          "Shouldn't be registered if we have no "
1083
0
                          "mRegisteredCommonAncestor after unregistering");
1084
0
  }
1085
0
}
1086
1087
nsINode*
1088
nsRange::GetCommonAncestor() const
1089
0
{
1090
0
  return mIsPositioned ?
1091
0
    nsContentUtils::GetCommonAncestor(mStart.Container(), mEnd.Container()) :
1092
0
    nullptr;
1093
0
}
1094
1095
void
1096
nsRange::Reset()
1097
0
{
1098
0
  DoSetRange(RawRangeBoundary(), RawRangeBoundary(), nullptr);
1099
0
}
1100
1101
/******************************************************
1102
 * public functionality
1103
 ******************************************************/
1104
1105
nsINode*
1106
nsRange::GetStartContainer(ErrorResult& aRv) const
1107
0
{
1108
0
  if (!mIsPositioned) {
1109
0
    aRv.Throw(NS_ERROR_NOT_INITIALIZED);
1110
0
    return nullptr;
1111
0
  }
1112
0
1113
0
  return mStart.Container();
1114
0
}
1115
1116
uint32_t
1117
nsRange::GetStartOffset(ErrorResult& aRv) const
1118
0
{
1119
0
  if (!mIsPositioned) {
1120
0
    aRv.Throw(NS_ERROR_NOT_INITIALIZED);
1121
0
    return 0;
1122
0
  }
1123
0
1124
0
  return mStart.Offset();
1125
0
}
1126
1127
nsINode*
1128
nsRange::GetEndContainer(ErrorResult& aRv) const
1129
0
{
1130
0
  if (!mIsPositioned) {
1131
0
    aRv.Throw(NS_ERROR_NOT_INITIALIZED);
1132
0
    return nullptr;
1133
0
  }
1134
0
1135
0
  return mEnd.Container();
1136
0
}
1137
1138
uint32_t
1139
nsRange::GetEndOffset(ErrorResult& aRv) const
1140
0
{
1141
0
  if (!mIsPositioned) {
1142
0
    aRv.Throw(NS_ERROR_NOT_INITIALIZED);
1143
0
    return 0;
1144
0
  }
1145
0
1146
0
  return mEnd.Offset();
1147
0
}
1148
1149
nsINode*
1150
nsRange::GetCommonAncestorContainer(ErrorResult& aRv) const
1151
0
{
1152
0
  if (!mIsPositioned) {
1153
0
    aRv.Throw(NS_ERROR_NOT_INITIALIZED);
1154
0
    return nullptr;
1155
0
  }
1156
0
1157
0
  return nsContentUtils::GetCommonAncestor(mStart.Container(), mEnd.Container());
1158
0
}
1159
1160
/* static */
1161
bool
1162
nsRange::IsValidOffset(nsINode* aNode, uint32_t aOffset)
1163
0
{
1164
0
  return aNode &&
1165
0
         IsValidOffset(aOffset) &&
1166
0
         static_cast<size_t>(aOffset) <= aNode->Length();
1167
0
}
1168
1169
/* static */
1170
nsINode*
1171
nsRange::ComputeRootNode(nsINode* aNode)
1172
0
{
1173
0
  if (!aNode) {
1174
0
    return nullptr;
1175
0
  }
1176
0
1177
0
  if (aNode->IsContent()) {
1178
0
    if (aNode->NodeInfo()->NameAtom() == nsGkAtoms::documentTypeNodeName) {
1179
0
      return nullptr;
1180
0
    }
1181
0
1182
0
    nsIContent* content = aNode->AsContent();
1183
0
1184
0
    // If the node is in a shadow tree then the ShadowRoot is the root.
1185
0
    if (ShadowRoot* containingShadow = content->GetContainingShadow()) {
1186
0
      return containingShadow;
1187
0
    }
1188
0
1189
0
    // If the node has a binding parent, that should be the root.
1190
0
    // XXXbz maybe only for native anonymous content?
1191
0
    if (nsINode* root = content->GetBindingParent()) {
1192
0
      return root;
1193
0
    }
1194
0
  }
1195
0
1196
0
  // Elements etc. must be in document or in document fragment,
1197
0
  // text nodes in document, in document fragment or in attribute.
1198
0
  if (nsINode* root = aNode->GetUncomposedDoc()) {
1199
0
    return root;
1200
0
  }
1201
0
1202
0
  NS_ASSERTION(!aNode->SubtreeRoot()->IsDocument(),
1203
0
               "GetUncomposedDoc should have returned a doc");
1204
0
1205
0
  // We allow this because of backward compatibility.
1206
0
  return aNode->SubtreeRoot();
1207
0
}
1208
1209
/* static */
1210
bool
1211
nsRange::IsValidPoints(nsINode* aStartContainer, uint32_t aStartOffset,
1212
                       nsINode* aEndContainer, uint32_t aEndOffset)
1213
0
{
1214
0
  // Use NS_WARN_IF() only for the cases where the arguments are unexpected.
1215
0
  if (NS_WARN_IF(!aStartContainer) || NS_WARN_IF(!aEndContainer) ||
1216
0
      NS_WARN_IF(!IsValidOffset(aStartContainer, aStartOffset)) ||
1217
0
      NS_WARN_IF(!IsValidOffset(aEndContainer, aEndOffset))) {
1218
0
    return false;
1219
0
  }
1220
0
1221
0
  // Otherwise, don't use NS_WARN_IF() for preventing to make console messy.
1222
0
  // Instead, check one by one since it is easier to catch the error reason
1223
0
  // with debugger.
1224
0
1225
0
  if (ComputeRootNode(aStartContainer) != ComputeRootNode(aEndContainer)) {
1226
0
    return false;
1227
0
  }
1228
0
1229
0
  bool disconnected = false;
1230
0
  int32_t order =
1231
0
    nsContentUtils::ComparePoints(aStartContainer,
1232
0
                                    static_cast<int32_t>(aStartOffset),
1233
0
                                    aEndContainer,
1234
0
                                    static_cast<int32_t>(aEndOffset),
1235
0
                                    &disconnected);
1236
0
  // FYI: disconnected should be false unless |order| is 1.
1237
0
  if (order == 1 || NS_WARN_IF(disconnected)) {
1238
0
    return false;
1239
0
  }
1240
0
1241
0
  return true;
1242
0
}
1243
1244
void
1245
nsRange::SetStartJS(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr)
1246
0
{
1247
0
  AutoCalledByJSRestore calledByJSRestorer(*this);
1248
0
  mCalledByJS = true;
1249
0
  SetStart(aNode, aOffset, aErr);
1250
0
}
1251
1252
void
1253
nsRange::SetStart(nsINode& aNode, uint32_t aOffset, ErrorResult& aRv)
1254
0
{
1255
0
 if (!nsContentUtils::LegacyIsCallerNativeCode() &&
1256
0
     !nsContentUtils::CanCallerAccess(&aNode)) {
1257
0
    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
1258
0
    return;
1259
0
  }
1260
0
1261
0
  AutoInvalidateSelection atEndOfBlock(this);
1262
0
  SetStart(RawRangeBoundary(&aNode, aOffset), aRv);
1263
0
}
1264
1265
void
1266
nsRange::SetStart(const RawRangeBoundary& aPoint, ErrorResult& aRv)
1267
0
{
1268
0
  nsINode* newRoot = IsValidBoundary(aPoint.Container());
1269
0
  if (!newRoot) {
1270
0
    aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
1271
0
    return;
1272
0
  }
1273
0
1274
0
  if (!aPoint.IsSetAndValid()) {
1275
0
    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
1276
0
    return;
1277
0
  }
1278
0
1279
0
  // Collapse if not positioned yet, if positioned in another doc or
1280
0
  // if the new start is after end.
1281
0
  if (!mIsPositioned || newRoot != mRoot ||
1282
0
      nsContentUtils::ComparePoints(aPoint, mEnd.AsRaw()) == 1) {
1283
0
    DoSetRange(aPoint, aPoint, newRoot);
1284
0
    return;
1285
0
  }
1286
0
1287
0
  DoSetRange(aPoint, mEnd.AsRaw(), mRoot);
1288
0
}
1289
1290
void
1291
nsRange::SetStartBeforeJS(nsINode& aNode, ErrorResult& aErr)
1292
0
{
1293
0
  AutoCalledByJSRestore calledByJSRestorer(*this);
1294
0
  mCalledByJS = true;
1295
0
  SetStartBefore(aNode, aErr);
1296
0
}
1297
1298
void
1299
nsRange::SetStartBefore(nsINode& aNode, ErrorResult& aRv)
1300
0
{
1301
0
  if (!nsContentUtils::LegacyIsCallerNativeCode() &&
1302
0
      !nsContentUtils::CanCallerAccess(&aNode)) {
1303
0
    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
1304
0
    return;
1305
0
  }
1306
0
1307
0
  AutoInvalidateSelection atEndOfBlock(this);
1308
0
  // If the node is being removed from its parent, GetContainerAndOffsetBefore()
1309
0
  // returns nullptr.  Then, SetStart() will throw
1310
0
  // NS_ERROR_DOM_INVALID_NODE_TYPE_ERR.
1311
0
  uint32_t offset = UINT32_MAX;
1312
0
  nsINode* container = GetContainerAndOffsetBefore(&aNode, &offset);
1313
0
  aRv = SetStart(container, offset);
1314
0
}
1315
1316
void
1317
nsRange::SetStartAfterJS(nsINode& aNode, ErrorResult& aErr)
1318
0
{
1319
0
  AutoCalledByJSRestore calledByJSRestorer(*this);
1320
0
  mCalledByJS = true;
1321
0
  SetStartAfter(aNode, aErr);
1322
0
}
1323
1324
void
1325
nsRange::SetStartAfter(nsINode& aNode, ErrorResult& aRv)
1326
0
{
1327
0
  if (!nsContentUtils::LegacyIsCallerNativeCode() &&
1328
0
      !nsContentUtils::CanCallerAccess(&aNode)) {
1329
0
    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
1330
0
    return;
1331
0
  }
1332
0
1333
0
  AutoInvalidateSelection atEndOfBlock(this);
1334
0
  // If the node is being removed from its parent, GetContainerAndOffsetAfter()
1335
0
  // returns nullptr.  Then, SetStart() will throw
1336
0
  // NS_ERROR_DOM_INVALID_NODE_TYPE_ERR.
1337
0
  uint32_t offset = UINT32_MAX;
1338
0
  nsINode* container = GetContainerAndOffsetAfter(&aNode, &offset);
1339
0
  aRv = SetStart(container, offset);
1340
0
}
1341
1342
void
1343
nsRange::SetEndJS(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr)
1344
0
{
1345
0
  AutoCalledByJSRestore calledByJSRestorer(*this);
1346
0
  mCalledByJS = true;
1347
0
  SetEnd(aNode, aOffset, aErr);
1348
0
}
1349
1350
void
1351
nsRange::SetEnd(nsINode& aNode, uint32_t aOffset, ErrorResult& aRv)
1352
0
{
1353
0
 if (!nsContentUtils::LegacyIsCallerNativeCode() &&
1354
0
     !nsContentUtils::CanCallerAccess(&aNode)) {
1355
0
    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
1356
0
    return;
1357
0
  }
1358
0
  AutoInvalidateSelection atEndOfBlock(this);
1359
0
  SetEnd(RawRangeBoundary(&aNode, aOffset), aRv);
1360
0
}
1361
1362
void
1363
nsRange::SetEnd(const RawRangeBoundary& aPoint, ErrorResult& aRv)
1364
0
{
1365
0
  nsINode* newRoot = IsValidBoundary(aPoint.Container());
1366
0
  if (!newRoot) {
1367
0
    aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
1368
0
    return;
1369
0
  }
1370
0
1371
0
  if (!aPoint.IsSetAndValid()) {
1372
0
    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
1373
0
    return;
1374
0
  }
1375
0
1376
0
  // Collapse if not positioned yet, if positioned in another doc or
1377
0
  // if the new end is before start.
1378
0
  if (!mIsPositioned || newRoot != mRoot ||
1379
0
      nsContentUtils::ComparePoints(mStart.AsRaw(), aPoint) == 1) {
1380
0
    DoSetRange(aPoint, aPoint, newRoot);
1381
0
    return;
1382
0
  }
1383
0
1384
0
  DoSetRange(mStart.AsRaw(), aPoint, mRoot);
1385
0
}
1386
1387
void
1388
nsRange::SelectNodesInContainer(nsINode* aContainer,
1389
                                nsIContent* aStartContent,
1390
                                nsIContent* aEndContent)
1391
0
{
1392
0
  MOZ_ASSERT(aContainer);
1393
0
  MOZ_ASSERT(aContainer->ComputeIndexOf(aStartContent) <=
1394
0
               aContainer->ComputeIndexOf(aEndContent));
1395
0
  MOZ_ASSERT(aStartContent && aContainer->ComputeIndexOf(aStartContent) != -1);
1396
0
  MOZ_ASSERT(aEndContent && aContainer->ComputeIndexOf(aEndContent) != -1);
1397
0
1398
0
  nsINode* newRoot = ComputeRootNode(aContainer);
1399
0
  MOZ_ASSERT(newRoot);
1400
0
  if (!newRoot) {
1401
0
    return;
1402
0
  }
1403
0
1404
0
  RawRangeBoundary start(aContainer, aStartContent->GetPreviousSibling());
1405
0
  RawRangeBoundary end(aContainer, aEndContent);
1406
0
  DoSetRange(start, end, newRoot);
1407
0
}
1408
1409
nsresult
1410
nsRange::SetStartAndEnd(const RawRangeBoundary& aStart,
1411
                        const RawRangeBoundary& aEnd)
1412
0
{
1413
0
  if (NS_WARN_IF(!aStart.IsSet()) || NS_WARN_IF(!aEnd.IsSet())) {
1414
0
    return NS_ERROR_INVALID_ARG;
1415
0
  }
1416
0
1417
0
  nsINode* newStartRoot = IsValidBoundary(aStart.Container());
1418
0
  if (!newStartRoot) {
1419
0
    return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR;
1420
0
  }
1421
0
  if (!aStart.IsSetAndValid()) {
1422
0
    return NS_ERROR_DOM_INDEX_SIZE_ERR;
1423
0
  }
1424
0
1425
0
  if (aStart.Container() == aEnd.Container()) {
1426
0
    if (!aEnd.IsSetAndValid()) {
1427
0
      return NS_ERROR_DOM_INDEX_SIZE_ERR;
1428
0
    }
1429
0
    // XXX: Offsets - handle this more efficiently.
1430
0
    // If the end offset is less than the start offset, this should be
1431
0
    // collapsed at the end offset.
1432
0
    if (aStart.Offset() > aEnd.Offset()) {
1433
0
      DoSetRange(aEnd, aEnd, newStartRoot);
1434
0
    } else {
1435
0
      DoSetRange(aStart, aEnd, newStartRoot);
1436
0
    }
1437
0
    return NS_OK;
1438
0
  }
1439
0
1440
0
  nsINode* newEndRoot = IsValidBoundary(aEnd.Container());
1441
0
  if (!newEndRoot) {
1442
0
    return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR;
1443
0
  }
1444
0
  if (!aEnd.IsSetAndValid()) {
1445
0
    return NS_ERROR_DOM_INDEX_SIZE_ERR;
1446
0
  }
1447
0
1448
0
  // If they have different root, this should be collapsed at the end point.
1449
0
  if (newStartRoot != newEndRoot) {
1450
0
    DoSetRange(aEnd, aEnd, newEndRoot);
1451
0
    return NS_OK;
1452
0
  }
1453
0
1454
0
  // If the end point is before the start point, this should be collapsed at
1455
0
  // the end point.
1456
0
  if (nsContentUtils::ComparePoints(aStart, aEnd) == 1) {
1457
0
    DoSetRange(aEnd, aEnd, newEndRoot);
1458
0
    return NS_OK;
1459
0
  }
1460
0
1461
0
  // Otherwise, set the range as specified.
1462
0
  DoSetRange(aStart, aEnd, newStartRoot);
1463
0
  return NS_OK;
1464
0
}
1465
1466
void
1467
nsRange::SetEndBeforeJS(nsINode& aNode, ErrorResult& aErr)
1468
0
{
1469
0
  AutoCalledByJSRestore calledByJSRestorer(*this);
1470
0
  mCalledByJS = true;
1471
0
  SetEndBefore(aNode, aErr);
1472
0
}
1473
1474
void
1475
nsRange::SetEndBefore(nsINode& aNode, ErrorResult& aRv)
1476
0
{
1477
0
  if (!nsContentUtils::LegacyIsCallerNativeCode() &&
1478
0
      !nsContentUtils::CanCallerAccess(&aNode)) {
1479
0
    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
1480
0
    return;
1481
0
  }
1482
0
1483
0
  AutoInvalidateSelection atEndOfBlock(this);
1484
0
  // If the node is being removed from its parent, GetContainerAndOffsetBefore()
1485
0
  // returns nullptr.  Then, SetEnd() will throw
1486
0
  // NS_ERROR_DOM_INVALID_NODE_TYPE_ERR.
1487
0
  uint32_t offset = UINT32_MAX;
1488
0
  nsINode* container = GetContainerAndOffsetBefore(&aNode, &offset);
1489
0
  aRv = SetEnd(container, offset);
1490
0
}
1491
1492
void
1493
nsRange::SetEndAfterJS(nsINode& aNode, ErrorResult& aErr)
1494
0
{
1495
0
  AutoCalledByJSRestore calledByJSRestorer(*this);
1496
0
  mCalledByJS = true;
1497
0
  SetEndAfter(aNode, aErr);
1498
0
}
1499
1500
void
1501
nsRange::SetEndAfter(nsINode& aNode, ErrorResult& aRv)
1502
0
{
1503
0
  if (!nsContentUtils::LegacyIsCallerNativeCode() &&
1504
0
      !nsContentUtils::CanCallerAccess(&aNode)) {
1505
0
    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
1506
0
    return;
1507
0
  }
1508
0
1509
0
  AutoInvalidateSelection atEndOfBlock(this);
1510
0
  // If the node is being removed from its parent, GetContainerAndOffsetAfter()
1511
0
  // returns nullptr.  Then, SetEnd() will throw
1512
0
  // NS_ERROR_DOM_INVALID_NODE_TYPE_ERR.
1513
0
  uint32_t offset = UINT32_MAX;
1514
0
  nsINode* container = GetContainerAndOffsetAfter(&aNode, &offset);
1515
0
  aRv = SetEnd(container, offset);
1516
0
}
1517
1518
void
1519
nsRange::Collapse(bool aToStart)
1520
0
{
1521
0
  if (!mIsPositioned)
1522
0
    return;
1523
0
1524
0
  AutoInvalidateSelection atEndOfBlock(this);
1525
0
  if (aToStart) {
1526
0
    DoSetRange(mStart.AsRaw(), mStart.AsRaw(), mRoot);
1527
0
  } else {
1528
0
    DoSetRange(mEnd.AsRaw(), mEnd.AsRaw(), mRoot);
1529
0
  }
1530
0
}
1531
1532
void
1533
nsRange::CollapseJS(bool aToStart)
1534
0
{
1535
0
  AutoCalledByJSRestore calledByJSRestorer(*this);
1536
0
  mCalledByJS = true;
1537
0
  Collapse(aToStart);
1538
0
}
1539
1540
void
1541
nsRange::SelectNodeJS(nsINode& aNode, ErrorResult& aErr)
1542
0
{
1543
0
  AutoCalledByJSRestore calledByJSRestorer(*this);
1544
0
  mCalledByJS = true;
1545
0
  SelectNode(aNode, aErr);
1546
0
}
1547
1548
void
1549
nsRange::SelectNode(nsINode& aNode, ErrorResult& aRv)
1550
0
{
1551
0
  if (!nsContentUtils::LegacyIsCallerNativeCode() &&
1552
0
      !nsContentUtils::CanCallerAccess(&aNode)) {
1553
0
    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
1554
0
    return;
1555
0
  }
1556
0
1557
0
  nsINode* container = aNode.GetParentNode();
1558
0
  nsINode* newRoot = IsValidBoundary(container);
1559
0
  if (!newRoot) {
1560
0
    aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
1561
0
    return;
1562
0
  }
1563
0
1564
0
  int32_t index = container->ComputeIndexOf(&aNode);
1565
0
  // MOZ_ASSERT(index != -1);
1566
0
  // We need to compute the index here unfortunately, because, while we have
1567
0
  // support for XBL, |container| may be the node's binding parent without
1568
0
  // actually containing it.
1569
0
  if (NS_WARN_IF(index < 0) ||
1570
0
      !IsValidOffset(static_cast<uint32_t>(index)) ||
1571
0
      !IsValidOffset(static_cast<uint32_t>(index) + 1)) {
1572
0
    aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
1573
0
    return;
1574
0
  }
1575
0
1576
0
  AutoInvalidateSelection atEndOfBlock(this);
1577
0
  DoSetRange(RawRangeBoundary(container, index),
1578
0
             RawRangeBoundary(container, index + 1), newRoot);
1579
0
}
1580
1581
void
1582
nsRange::SelectNodeContentsJS(nsINode& aNode, ErrorResult& aErr)
1583
0
{
1584
0
  AutoCalledByJSRestore calledByJSRestorer(*this);
1585
0
  mCalledByJS = true;
1586
0
  SelectNodeContents(aNode, aErr);
1587
0
}
1588
1589
void
1590
nsRange::SelectNodeContents(nsINode& aNode, ErrorResult& aRv)
1591
0
{
1592
0
  if (!nsContentUtils::LegacyIsCallerNativeCode() &&
1593
0
      !nsContentUtils::CanCallerAccess(&aNode)) {
1594
0
    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
1595
0
    return;
1596
0
  }
1597
0
1598
0
  nsINode* newRoot = IsValidBoundary(&aNode);
1599
0
  if (!newRoot) {
1600
0
    aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
1601
0
    return;
1602
0
  }
1603
0
1604
0
  AutoInvalidateSelection atEndOfBlock(this);
1605
0
  DoSetRange(RawRangeBoundary(&aNode, 0),
1606
0
             RawRangeBoundary(&aNode, aNode.Length()), newRoot);
1607
0
}
1608
1609
// The Subtree Content Iterator only returns subtrees that are
1610
// completely within a given range. It doesn't return a CharacterData
1611
// node that contains either the start or end point of the range.,
1612
// nor does it return element nodes when nothing in the element is selected.
1613
// We need an iterator that will also include these start/end points
1614
// so that our methods/algorithms aren't cluttered with special
1615
// case code that tries to include these points while iterating.
1616
//
1617
// The RangeSubtreeIterator class mimics the nsIContentIterator
1618
// methods we need, so should the Content Iterator support the
1619
// start/end points in the future, we can switchover relatively
1620
// easy.
1621
1622
class MOZ_STACK_CLASS RangeSubtreeIterator
1623
{
1624
private:
1625
1626
  enum RangeSubtreeIterState { eDone=0,
1627
                               eUseStart,
1628
                               eUseIterator,
1629
                               eUseEnd };
1630
1631
  nsCOMPtr<nsIContentIterator>  mIter;
1632
  RangeSubtreeIterState         mIterState;
1633
1634
  nsCOMPtr<nsINode> mStart;
1635
  nsCOMPtr<nsINode> mEnd;
1636
1637
public:
1638
1639
  RangeSubtreeIterator()
1640
    : mIterState(eDone)
1641
0
  {
1642
0
  }
1643
  ~RangeSubtreeIterator()
1644
0
  {
1645
0
  }
1646
1647
  nsresult Init(nsRange *aRange);
1648
  already_AddRefed<nsINode> GetCurrentNode();
1649
  void First();
1650
  void Last();
1651
  void Next();
1652
  void Prev();
1653
1654
  bool IsDone()
1655
0
  {
1656
0
    return mIterState == eDone;
1657
0
  }
1658
};
1659
1660
nsresult
1661
RangeSubtreeIterator::Init(nsRange *aRange)
1662
0
{
1663
0
  mIterState = eDone;
1664
0
  if (aRange->Collapsed()) {
1665
0
    return NS_OK;
1666
0
  }
1667
0
1668
0
  // Grab the start point of the range and QI it to
1669
0
  // a CharacterData pointer. If it is CharacterData store
1670
0
  // a pointer to the node.
1671
0
1672
0
  ErrorResult rv;
1673
0
  nsCOMPtr<nsINode> node = aRange->GetStartContainer(rv);
1674
0
  if (!node) return NS_ERROR_FAILURE;
1675
0
1676
0
  if (node->IsCharacterData() ||
1677
0
      (node->IsElement() &&
1678
0
       node->AsElement()->GetChildCount() == aRange->GetStartOffset(rv))) {
1679
0
    mStart = node;
1680
0
  }
1681
0
1682
0
  // Grab the end point of the range and QI it to
1683
0
  // a CharacterData pointer. If it is CharacterData store
1684
0
  // a pointer to the node.
1685
0
1686
0
  node = aRange->GetEndContainer(rv);
1687
0
  if (!node) return NS_ERROR_FAILURE;
1688
0
1689
0
  if (node->IsCharacterData() ||
1690
0
      (node->IsElement() && aRange->GetEndOffset(rv) == 0)) {
1691
0
    mEnd = node;
1692
0
  }
1693
0
1694
0
  if (mStart && mStart == mEnd)
1695
0
  {
1696
0
    // The range starts and stops in the same CharacterData
1697
0
    // node. Null out the end pointer so we only visit the
1698
0
    // node once!
1699
0
1700
0
    mEnd = nullptr;
1701
0
  }
1702
0
  else
1703
0
  {
1704
0
    // Now create a Content Subtree Iterator to be used
1705
0
    // for the subtrees between the end points!
1706
0
1707
0
    mIter = NS_NewContentSubtreeIterator();
1708
0
1709
0
    nsresult res = mIter->Init(aRange);
1710
0
    if (NS_FAILED(res)) return res;
1711
0
1712
0
    if (mIter->IsDone())
1713
0
    {
1714
0
      // The subtree iterator thinks there's nothing
1715
0
      // to iterate over, so just free it up so we
1716
0
      // don't accidentally call into it.
1717
0
1718
0
      mIter = nullptr;
1719
0
    }
1720
0
  }
1721
0
1722
0
  // Initialize the iterator by calling First().
1723
0
  // Note that we are ignoring the return value on purpose!
1724
0
1725
0
  First();
1726
0
1727
0
  return NS_OK;
1728
0
}
1729
1730
already_AddRefed<nsINode>
1731
RangeSubtreeIterator::GetCurrentNode()
1732
0
{
1733
0
  nsCOMPtr<nsINode> node;
1734
0
1735
0
  if (mIterState == eUseStart && mStart) {
1736
0
    node = mStart;
1737
0
  } else if (mIterState == eUseEnd && mEnd) {
1738
0
    node = mEnd;
1739
0
  } else if (mIterState == eUseIterator && mIter) {
1740
0
    node = mIter->GetCurrentNode();
1741
0
  }
1742
0
1743
0
  return node.forget();
1744
0
}
1745
1746
void
1747
RangeSubtreeIterator::First()
1748
0
{
1749
0
  if (mStart)
1750
0
    mIterState = eUseStart;
1751
0
  else if (mIter)
1752
0
  {
1753
0
    mIter->First();
1754
0
1755
0
    mIterState = eUseIterator;
1756
0
  }
1757
0
  else if (mEnd)
1758
0
    mIterState = eUseEnd;
1759
0
  else
1760
0
    mIterState = eDone;
1761
0
}
1762
1763
void
1764
RangeSubtreeIterator::Last()
1765
0
{
1766
0
  if (mEnd)
1767
0
    mIterState = eUseEnd;
1768
0
  else if (mIter)
1769
0
  {
1770
0
    mIter->Last();
1771
0
1772
0
    mIterState = eUseIterator;
1773
0
  }
1774
0
  else if (mStart)
1775
0
    mIterState = eUseStart;
1776
0
  else
1777
0
    mIterState = eDone;
1778
0
}
1779
1780
void
1781
RangeSubtreeIterator::Next()
1782
0
{
1783
0
  if (mIterState == eUseStart)
1784
0
  {
1785
0
    if (mIter)
1786
0
    {
1787
0
      mIter->First();
1788
0
1789
0
      mIterState = eUseIterator;
1790
0
    }
1791
0
    else if (mEnd)
1792
0
      mIterState = eUseEnd;
1793
0
    else
1794
0
      mIterState = eDone;
1795
0
  }
1796
0
  else if (mIterState == eUseIterator)
1797
0
  {
1798
0
    mIter->Next();
1799
0
1800
0
    if (mIter->IsDone())
1801
0
    {
1802
0
      if (mEnd)
1803
0
        mIterState = eUseEnd;
1804
0
      else
1805
0
        mIterState = eDone;
1806
0
    }
1807
0
  }
1808
0
  else
1809
0
    mIterState = eDone;
1810
0
}
1811
1812
void
1813
RangeSubtreeIterator::Prev()
1814
0
{
1815
0
  if (mIterState == eUseEnd)
1816
0
  {
1817
0
    if (mIter)
1818
0
    {
1819
0
      mIter->Last();
1820
0
1821
0
      mIterState = eUseIterator;
1822
0
    }
1823
0
    else if (mStart)
1824
0
      mIterState = eUseStart;
1825
0
    else
1826
0
      mIterState = eDone;
1827
0
  }
1828
0
  else if (mIterState == eUseIterator)
1829
0
  {
1830
0
    mIter->Prev();
1831
0
1832
0
    if (mIter->IsDone())
1833
0
    {
1834
0
      if (mStart)
1835
0
        mIterState = eUseStart;
1836
0
      else
1837
0
        mIterState = eDone;
1838
0
    }
1839
0
  }
1840
0
  else
1841
0
    mIterState = eDone;
1842
0
}
1843
1844
1845
// CollapseRangeAfterDelete() is a utility method that is used by
1846
// DeleteContents() and ExtractContents() to collapse the range
1847
// in the correct place, under the range's root container (the
1848
// range end points common container) as outlined by the Range spec:
1849
//
1850
// http://www.w3.org/TR/2000/REC-DOM-Level-2-Traversal-Range-20001113/ranges.html
1851
// The assumption made by this method is that the delete or extract
1852
// has been done already, and left the range in a state where there is
1853
// no content between the 2 end points.
1854
1855
static nsresult
1856
CollapseRangeAfterDelete(nsRange* aRange)
1857
0
{
1858
0
  NS_ENSURE_ARG_POINTER(aRange);
1859
0
1860
0
  // Check if range gravity took care of collapsing the range for us!
1861
0
  if (aRange->Collapsed())
1862
0
  {
1863
0
    // aRange is collapsed so there's nothing for us to do.
1864
0
    //
1865
0
    // There are 2 possible scenarios here:
1866
0
    //
1867
0
    // 1. aRange could've been collapsed prior to the delete/extract,
1868
0
    //    which would've resulted in nothing being removed, so aRange
1869
0
    //    is already where it should be.
1870
0
    //
1871
0
    // 2. Prior to the delete/extract, aRange's start and end were in
1872
0
    //    the same container which would mean everything between them
1873
0
    //    was removed, causing range gravity to collapse the range.
1874
0
1875
0
    return NS_OK;
1876
0
  }
1877
0
1878
0
  // aRange isn't collapsed so figure out the appropriate place to collapse!
1879
0
  // First get both end points and their common ancestor.
1880
0
1881
0
  ErrorResult rv;
1882
0
  nsCOMPtr<nsINode> commonAncestor = aRange->GetCommonAncestorContainer(rv);
1883
0
  if (rv.Failed()) return rv.StealNSResult();
1884
0
1885
0
  nsCOMPtr<nsINode> startContainer = aRange->GetStartContainer(rv);
1886
0
  if (rv.Failed()) return rv.StealNSResult();
1887
0
  nsCOMPtr<nsINode> endContainer = aRange->GetEndContainer(rv);
1888
0
  if (rv.Failed()) return rv.StealNSResult();
1889
0
1890
0
  // Collapse to one of the end points if they are already in the
1891
0
  // commonAncestor. This should work ok since this method is called
1892
0
  // immediately after a delete or extract that leaves no content
1893
0
  // between the 2 end points!
1894
0
1895
0
  if (startContainer == commonAncestor) {
1896
0
    aRange->Collapse(true);
1897
0
    return NS_OK;
1898
0
  }
1899
0
  if (endContainer == commonAncestor) {
1900
0
    aRange->Collapse(false);
1901
0
    return NS_OK;
1902
0
  }
1903
0
1904
0
  // End points are at differing levels. We want to collapse to the
1905
0
  // point that is between the 2 subtrees that contain each point,
1906
0
  // under the common ancestor.
1907
0
1908
0
  nsCOMPtr<nsINode> nodeToSelect(startContainer);
1909
0
1910
0
  while (nodeToSelect)
1911
0
  {
1912
0
    nsCOMPtr<nsINode> parent = nodeToSelect->GetParentNode();
1913
0
    if (parent == commonAncestor)
1914
0
      break; // We found the nodeToSelect!
1915
0
1916
0
    nodeToSelect = parent;
1917
0
  }
1918
0
1919
0
  if (!nodeToSelect)
1920
0
    return NS_ERROR_FAILURE; // This should never happen!
1921
0
1922
0
  aRange->SelectNode(*nodeToSelect, rv);
1923
0
  if (rv.Failed()) return rv.StealNSResult();
1924
0
1925
0
  aRange->Collapse(false);
1926
0
  return NS_OK;
1927
0
}
1928
1929
NS_IMETHODIMP
1930
PrependChild(nsINode* aContainer, nsINode* aChild)
1931
0
{
1932
0
  nsCOMPtr<nsINode> first = aContainer->GetFirstChild();
1933
0
  ErrorResult rv;
1934
0
  aContainer->InsertBefore(*aChild, first, rv);
1935
0
  return rv.StealNSResult();
1936
0
}
1937
1938
// Helper function for CutContents, making sure that the current node wasn't
1939
// removed by mutation events (bug 766426)
1940
static bool
1941
ValidateCurrentNode(nsRange* aRange, RangeSubtreeIterator& aIter)
1942
0
{
1943
0
  bool before, after;
1944
0
  nsCOMPtr<nsINode> node = aIter.GetCurrentNode();
1945
0
  if (!node) {
1946
0
    // We don't have to worry that the node was removed if it doesn't exist,
1947
0
    // e.g., the iterator is done.
1948
0
    return true;
1949
0
  }
1950
0
1951
0
  nsresult res = nsRange::CompareNodeToRange(node, aRange, &before, &after);
1952
0
  NS_ENSURE_SUCCESS(res, false);
1953
0
1954
0
  if (before || after) {
1955
0
    if (node->IsCharacterData()) {
1956
0
      // If we're dealing with the start/end container which is a character
1957
0
      // node, pretend that the node is in the range.
1958
0
      if (before && node == aRange->GetStartContainer()) {
1959
0
        before = false;
1960
0
      }
1961
0
      if (after && node == aRange->GetEndContainer()) {
1962
0
        after = false;
1963
0
      }
1964
0
    }
1965
0
  }
1966
0
1967
0
  return !before && !after;
1968
0
}
1969
1970
nsresult
1971
nsRange::CutContents(DocumentFragment** aFragment)
1972
0
{
1973
0
  if (aFragment) {
1974
0
    *aFragment = nullptr;
1975
0
  }
1976
0
1977
0
  nsCOMPtr<nsIDocument> doc = mStart.Container()->OwnerDoc();
1978
0
1979
0
  ErrorResult res;
1980
0
  nsCOMPtr<nsINode> commonAncestor = GetCommonAncestorContainer(res);
1981
0
  NS_ENSURE_TRUE(!res.Failed(), res.StealNSResult());
1982
0
1983
0
  // If aFragment isn't null, create a temporary fragment to hold our return.
1984
0
  RefPtr<DocumentFragment> retval;
1985
0
  if (aFragment) {
1986
0
    retval = new DocumentFragment(doc->NodeInfoManager());
1987
0
  }
1988
0
  nsCOMPtr<nsINode> commonCloneAncestor = retval.get();
1989
0
1990
0
  // Batch possible DOMSubtreeModified events.
1991
0
  mozAutoSubtreeModified subtree(mRoot ? mRoot->OwnerDoc(): nullptr, nullptr);
1992
0
1993
0
  // Save the range end points locally to avoid interference
1994
0
  // of Range gravity during our edits!
1995
0
1996
0
  nsCOMPtr<nsINode> startContainer = mStart.Container();
1997
0
  uint32_t startOffset = mStart.Offset();
1998
0
  nsCOMPtr<nsINode> endContainer = mEnd.Container();
1999
0
  uint32_t endOffset = mEnd.Offset();
2000
0
2001
0
  if (retval) {
2002
0
    // For extractContents(), abort early if there's a doctype (bug 719533).
2003
0
    // This can happen only if the common ancestor is a document, in which case
2004
0
    // we just need to find its doctype child and check if that's in the range.
2005
0
    nsCOMPtr<nsIDocument> commonAncestorDocument = do_QueryInterface(commonAncestor);
2006
0
    if (commonAncestorDocument) {
2007
0
      RefPtr<DocumentType> doctype = commonAncestorDocument->GetDoctype();
2008
0
2009
0
      if (doctype &&
2010
0
          nsContentUtils::ComparePoints(startContainer,
2011
0
                                        static_cast<int32_t>(startOffset),
2012
0
                                        doctype, 0) < 0 &&
2013
0
          nsContentUtils::ComparePoints(doctype, 0,
2014
0
                                        endContainer,
2015
0
                                        static_cast<int32_t>(endOffset)) < 0) {
2016
0
        return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
2017
0
      }
2018
0
    }
2019
0
  }
2020
0
2021
0
  // Create and initialize a subtree iterator that will give
2022
0
  // us all the subtrees within the range.
2023
0
2024
0
  RangeSubtreeIterator iter;
2025
0
2026
0
  nsresult rv = iter.Init(this);
2027
0
  if (NS_FAILED(rv)) return rv;
2028
0
2029
0
  if (iter.IsDone())
2030
0
  {
2031
0
    // There's nothing for us to delete.
2032
0
    rv = CollapseRangeAfterDelete(this);
2033
0
    if (NS_SUCCEEDED(rv) && aFragment) {
2034
0
      retval.forget(aFragment);
2035
0
    }
2036
0
    return rv;
2037
0
  }
2038
0
2039
0
  iter.First();
2040
0
2041
0
  bool handled = false;
2042
0
2043
0
  // With the exception of text nodes that contain one of the range
2044
0
  // end points, the subtree iterator should only give us back subtrees
2045
0
  // that are completely contained between the range's end points.
2046
0
2047
0
  while (!iter.IsDone())
2048
0
  {
2049
0
    nsCOMPtr<nsINode> nodeToResult;
2050
0
    nsCOMPtr<nsINode> node = iter.GetCurrentNode();
2051
0
2052
0
    // Before we delete anything, advance the iterator to the next node that's
2053
0
    // not a descendant of this one.  XXX It's a bit silly to iterate through
2054
0
    // the descendants only to throw them out, we should use an iterator that
2055
0
    // skips the descendants to begin with.
2056
0
2057
0
    iter.Next();
2058
0
    nsCOMPtr<nsINode> nextNode = iter.GetCurrentNode();
2059
0
    while (nextNode && nsContentUtils::ContentIsDescendantOf(nextNode, node)) {
2060
0
      iter.Next();
2061
0
      nextNode = iter.GetCurrentNode();
2062
0
    }
2063
0
2064
0
    handled = false;
2065
0
2066
0
    // If it's CharacterData, make sure we might need to delete
2067
0
    // part of the data, instead of removing the whole node.
2068
0
    //
2069
0
    // XXX_kin: We need to also handle ProcessingInstruction
2070
0
    // XXX_kin: according to the spec.
2071
0
2072
0
    if (auto charData = CharacterData::FromNode(node)) {
2073
0
      uint32_t dataLength = 0;
2074
0
2075
0
      if (node == startContainer) {
2076
0
        if (node == endContainer) {
2077
0
          // This range is completely contained within a single text node.
2078
0
          // Delete or extract the data between startOffset and endOffset.
2079
0
2080
0
          if (endOffset > startOffset) {
2081
0
            if (retval) {
2082
0
              nsAutoString cutValue;
2083
0
              ErrorResult err;
2084
0
              charData->SubstringData(startOffset, endOffset - startOffset,
2085
0
                                      cutValue, err);
2086
0
              if (NS_WARN_IF(err.Failed())) {
2087
0
                return err.StealNSResult();
2088
0
              }
2089
0
              nsCOMPtr<nsINode> clone = node->CloneNode(false, err);
2090
0
              if (NS_WARN_IF(err.Failed())) {
2091
0
                return err.StealNSResult();
2092
0
              }
2093
0
              clone->SetNodeValue(cutValue, err);
2094
0
              if (NS_WARN_IF(err.Failed())) {
2095
0
                return err.StealNSResult();
2096
0
              }
2097
0
              nodeToResult = clone;
2098
0
            }
2099
0
2100
0
            nsMutationGuard guard;
2101
0
            ErrorResult err;
2102
0
            charData->DeleteData(startOffset, endOffset - startOffset, err);
2103
0
            if (NS_WARN_IF(err.Failed())) {
2104
0
              return err.StealNSResult();
2105
0
            }
2106
0
            NS_ENSURE_STATE(!guard.Mutated(0) ||
2107
0
                            ValidateCurrentNode(this, iter));
2108
0
          }
2109
0
2110
0
          handled = true;
2111
0
        }
2112
0
        else  {
2113
0
          // Delete or extract everything after startOffset.
2114
0
2115
0
          dataLength = charData->Length();
2116
0
2117
0
          if (dataLength >= startOffset) {
2118
0
            if (retval) {
2119
0
              nsAutoString cutValue;
2120
0
              ErrorResult err;
2121
0
              charData->SubstringData(startOffset, dataLength, cutValue, err);
2122
0
              if (NS_WARN_IF(err.Failed())) {
2123
0
                return err.StealNSResult();
2124
0
              }
2125
0
              nsCOMPtr<nsINode> clone = node->CloneNode(false, err);
2126
0
              if (NS_WARN_IF(err.Failed())) {
2127
0
                return err.StealNSResult();
2128
0
              }
2129
0
              clone->SetNodeValue(cutValue, err);
2130
0
              if (NS_WARN_IF(err.Failed())) {
2131
0
                return err.StealNSResult();
2132
0
              }
2133
0
              nodeToResult = clone;
2134
0
            }
2135
0
2136
0
            nsMutationGuard guard;
2137
0
            ErrorResult err;
2138
0
            charData->DeleteData(startOffset, dataLength, err);
2139
0
            if (NS_WARN_IF(err.Failed())) {
2140
0
              return err.StealNSResult();
2141
0
            }
2142
0
            NS_ENSURE_SUCCESS(rv, rv);
2143
0
            NS_ENSURE_STATE(!guard.Mutated(0) ||
2144
0
                            ValidateCurrentNode(this, iter));
2145
0
          }
2146
0
2147
0
          handled = true;
2148
0
        }
2149
0
      }
2150
0
      else if (node == endContainer) {
2151
0
        // Delete or extract everything before endOffset.
2152
0
        if (retval) {
2153
0
          nsAutoString cutValue;
2154
0
          ErrorResult err;
2155
0
          charData->SubstringData(0, endOffset, cutValue, err);
2156
0
          if (NS_WARN_IF(err.Failed())) {
2157
0
            return err.StealNSResult();
2158
0
          }
2159
0
          nsCOMPtr<nsINode> clone = node->CloneNode(false, err);
2160
0
          if (NS_WARN_IF(err.Failed())) {
2161
0
            return err.StealNSResult();
2162
0
          }
2163
0
          clone->SetNodeValue(cutValue, err);
2164
0
          if (NS_WARN_IF(err.Failed())) {
2165
0
            return err.StealNSResult();
2166
0
          }
2167
0
          nodeToResult = clone;
2168
0
        }
2169
0
2170
0
        nsMutationGuard guard;
2171
0
        ErrorResult err;
2172
0
        charData->DeleteData(0, endOffset, err);
2173
0
        if (NS_WARN_IF(err.Failed())) {
2174
0
          return err.StealNSResult();
2175
0
        }
2176
0
        NS_ENSURE_STATE(!guard.Mutated(0) ||
2177
0
                        ValidateCurrentNode(this, iter));
2178
0
        handled = true;
2179
0
      }
2180
0
    }
2181
0
2182
0
    if (!handled && (node == endContainer || node == startContainer)) {
2183
0
      if (node && node->IsElement() &&
2184
0
          ((node == endContainer && endOffset == 0) ||
2185
0
           (node == startContainer &&
2186
0
            node->AsElement()->GetChildCount() == startOffset))) {
2187
0
        if (retval) {
2188
0
          ErrorResult rv;
2189
0
          nodeToResult = node->CloneNode(false, rv);
2190
0
          NS_ENSURE_TRUE(!rv.Failed(), rv.StealNSResult());
2191
0
        }
2192
0
        handled = true;
2193
0
      }
2194
0
    }
2195
0
2196
0
    if (!handled) {
2197
0
      // node was not handled above, so it must be completely contained
2198
0
      // within the range. Just remove it from the tree!
2199
0
      nodeToResult = node;
2200
0
    }
2201
0
2202
0
    uint32_t parentCount = 0;
2203
0
    // Set the result to document fragment if we have 'retval'.
2204
0
    if (retval) {
2205
0
      nsCOMPtr<nsINode> oldCommonAncestor = commonAncestor;
2206
0
      if (!iter.IsDone()) {
2207
0
        // Setup the parameters for the next iteration of the loop.
2208
0
        NS_ENSURE_STATE(nextNode);
2209
0
2210
0
        // Get node's and nextNode's common parent. Do this before moving
2211
0
        // nodes from original DOM to result fragment.
2212
0
        commonAncestor = nsContentUtils::GetCommonAncestor(node, nextNode);
2213
0
        NS_ENSURE_STATE(commonAncestor);
2214
0
2215
0
        nsCOMPtr<nsINode> parentCounterNode = node;
2216
0
        while (parentCounterNode && parentCounterNode != commonAncestor) {
2217
0
          ++parentCount;
2218
0
          parentCounterNode = parentCounterNode->GetParentNode();
2219
0
          NS_ENSURE_STATE(parentCounterNode);
2220
0
        }
2221
0
      }
2222
0
2223
0
      // Clone the parent hierarchy between commonAncestor and node.
2224
0
      nsCOMPtr<nsINode> closestAncestor, farthestAncestor;
2225
0
      rv = CloneParentsBetween(oldCommonAncestor, node,
2226
0
                               getter_AddRefs(closestAncestor),
2227
0
                               getter_AddRefs(farthestAncestor));
2228
0
      NS_ENSURE_SUCCESS(rv, rv);
2229
0
2230
0
      ErrorResult res;
2231
0
      if (farthestAncestor) {
2232
0
        commonCloneAncestor->AppendChild(*farthestAncestor, res);
2233
0
        res.WouldReportJSException();
2234
0
        if (NS_WARN_IF(res.Failed())) {
2235
0
          return res.StealNSResult();
2236
0
        }
2237
0
      }
2238
0
2239
0
      nsMutationGuard guard;
2240
0
      nsCOMPtr<nsINode> parent = nodeToResult->GetParentNode();
2241
0
      if (closestAncestor) {
2242
0
        closestAncestor->AppendChild(*nodeToResult, res);
2243
0
      } else {
2244
0
        commonCloneAncestor->AppendChild(*nodeToResult, res);
2245
0
      }
2246
0
      res.WouldReportJSException();
2247
0
      if (NS_WARN_IF(res.Failed())) {
2248
0
        return res.StealNSResult();
2249
0
      }
2250
0
      NS_ENSURE_STATE(!guard.Mutated(parent ? 2 : 1) ||
2251
0
                      ValidateCurrentNode(this, iter));
2252
0
    } else if (nodeToResult) {
2253
0
      nsMutationGuard guard;
2254
0
      nsCOMPtr<nsINode> node = nodeToResult;
2255
0
      nsCOMPtr<nsINode> parent = node->GetParentNode();
2256
0
      if (parent) {
2257
0
        mozilla::ErrorResult error;
2258
0
        parent->RemoveChild(*node, error);
2259
0
        NS_ENSURE_FALSE(error.Failed(), error.StealNSResult());
2260
0
      }
2261
0
      NS_ENSURE_STATE(!guard.Mutated(1) ||
2262
0
                      ValidateCurrentNode(this, iter));
2263
0
    }
2264
0
2265
0
    if (!iter.IsDone() && retval) {
2266
0
      // Find the equivalent of commonAncestor in the cloned tree.
2267
0
      nsCOMPtr<nsINode> newCloneAncestor = nodeToResult;
2268
0
      for (uint32_t i = parentCount; i; --i) {
2269
0
        newCloneAncestor = newCloneAncestor->GetParentNode();
2270
0
        NS_ENSURE_STATE(newCloneAncestor);
2271
0
      }
2272
0
      commonCloneAncestor = newCloneAncestor;
2273
0
    }
2274
0
  }
2275
0
2276
0
  rv = CollapseRangeAfterDelete(this);
2277
0
  if (NS_SUCCEEDED(rv) && aFragment) {
2278
0
    retval.forget(aFragment);
2279
0
  }
2280
0
  return rv;
2281
0
}
2282
2283
void
2284
nsRange::DeleteContents(ErrorResult& aRv)
2285
0
{
2286
0
  aRv = CutContents(nullptr);
2287
0
}
2288
2289
already_AddRefed<DocumentFragment>
2290
nsRange::ExtractContents(ErrorResult& rv)
2291
0
{
2292
0
  RefPtr<DocumentFragment> fragment;
2293
0
  rv = CutContents(getter_AddRefs(fragment));
2294
0
  return fragment.forget();
2295
0
}
2296
2297
int16_t
2298
nsRange::CompareBoundaryPoints(uint16_t aHow, nsRange& aOtherRange,
2299
                               ErrorResult& rv)
2300
0
{
2301
0
  if (!mIsPositioned || !aOtherRange.IsPositioned()) {
2302
0
    rv.Throw(NS_ERROR_NOT_INITIALIZED);
2303
0
    return 0;
2304
0
  }
2305
0
2306
0
  nsINode *ourNode, *otherNode;
2307
0
  uint32_t ourOffset, otherOffset;
2308
0
2309
0
  switch (aHow) {
2310
0
    case Range_Binding::START_TO_START:
2311
0
      ourNode = mStart.Container();
2312
0
      ourOffset = mStart.Offset();
2313
0
      otherNode = aOtherRange.GetStartContainer();
2314
0
      otherOffset = aOtherRange.StartOffset();
2315
0
      break;
2316
0
    case Range_Binding::START_TO_END:
2317
0
      ourNode = mEnd.Container();
2318
0
      ourOffset = mEnd.Offset();
2319
0
      otherNode = aOtherRange.GetStartContainer();
2320
0
      otherOffset = aOtherRange.StartOffset();
2321
0
      break;
2322
0
    case Range_Binding::END_TO_START:
2323
0
      ourNode = mStart.Container();
2324
0
      ourOffset = mStart.Offset();
2325
0
      otherNode = aOtherRange.GetEndContainer();
2326
0
      otherOffset = aOtherRange.EndOffset();
2327
0
      break;
2328
0
    case Range_Binding::END_TO_END:
2329
0
      ourNode = mEnd.Container();
2330
0
      ourOffset = mEnd.Offset();
2331
0
      otherNode = aOtherRange.GetEndContainer();
2332
0
      otherOffset = aOtherRange.EndOffset();
2333
0
      break;
2334
0
    default:
2335
0
      // We were passed an illegal value
2336
0
      rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
2337
0
      return 0;
2338
0
  }
2339
0
2340
0
  if (mRoot != aOtherRange.GetRoot()) {
2341
0
    rv.Throw(NS_ERROR_DOM_WRONG_DOCUMENT_ERR);
2342
0
    return 0;
2343
0
  }
2344
0
2345
0
  return nsContentUtils::ComparePoints(ourNode,
2346
0
                                       static_cast<int32_t>(ourOffset),
2347
0
                                       otherNode,
2348
0
                                       static_cast<int32_t>(otherOffset));
2349
0
}
2350
2351
/* static */ nsresult
2352
nsRange::CloneParentsBetween(nsINode *aAncestor,
2353
                             nsINode *aNode,
2354
                             nsINode **aClosestAncestor,
2355
                             nsINode **aFarthestAncestor)
2356
0
{
2357
0
  NS_ENSURE_ARG_POINTER((aAncestor && aNode && aClosestAncestor && aFarthestAncestor));
2358
0
2359
0
  *aClosestAncestor  = nullptr;
2360
0
  *aFarthestAncestor = nullptr;
2361
0
2362
0
  if (aAncestor == aNode)
2363
0
    return NS_OK;
2364
0
2365
0
  AutoTArray<nsCOMPtr<nsINode>, 16> parentStack;
2366
0
2367
0
  nsCOMPtr<nsINode> parent = aNode->GetParentNode();
2368
0
  while(parent && parent != aAncestor)
2369
0
  {
2370
0
    parentStack.AppendElement(parent);
2371
0
    parent = parent->GetParentNode();
2372
0
  }
2373
0
2374
0
  nsCOMPtr<nsINode> firstParent;
2375
0
  nsCOMPtr<nsINode> lastParent;
2376
0
  for (int32_t i = parentStack.Length() - 1; i >= 0; i--) {
2377
0
    ErrorResult rv;
2378
0
    nsCOMPtr<nsINode> clone = parentStack[i]->CloneNode(false, rv);
2379
0
2380
0
    if (rv.Failed()) {
2381
0
      return rv.StealNSResult();
2382
0
    }
2383
0
    if (!clone) {
2384
0
      return NS_ERROR_FAILURE;
2385
0
    }
2386
0
2387
0
    if (!lastParent) {
2388
0
      lastParent = clone;
2389
0
    } else {
2390
0
      firstParent->AppendChild(*clone, rv);
2391
0
      if (rv.Failed()) {
2392
0
        return rv.StealNSResult();
2393
0
      }
2394
0
    }
2395
0
2396
0
    firstParent = clone;
2397
0
  }
2398
0
2399
0
  firstParent.forget(aClosestAncestor);
2400
0
  lastParent.forget(aFarthestAncestor);
2401
0
2402
0
  return NS_OK;
2403
0
}
2404
2405
already_AddRefed<DocumentFragment>
2406
nsRange::CloneContents(ErrorResult& aRv)
2407
0
{
2408
0
  nsCOMPtr<nsINode> commonAncestor = GetCommonAncestorContainer(aRv);
2409
0
  MOZ_ASSERT(!aRv.Failed(), "GetCommonAncestorContainer() shouldn't fail!");
2410
0
2411
0
  nsCOMPtr<nsIDocument> doc = mStart.Container()->OwnerDoc();
2412
0
  NS_ASSERTION(doc, "CloneContents needs a document to continue.");
2413
0
  if (!doc) {
2414
0
    aRv.Throw(NS_ERROR_FAILURE);
2415
0
    return nullptr;
2416
0
  }
2417
0
2418
0
  // Create a new document fragment in the context of this document,
2419
0
  // which might be null
2420
0
2421
0
2422
0
  RefPtr<DocumentFragment> clonedFrag =
2423
0
    new DocumentFragment(doc->NodeInfoManager());
2424
0
2425
0
  nsCOMPtr<nsINode> commonCloneAncestor = clonedFrag.get();
2426
0
2427
0
  // Create and initialize a subtree iterator that will give
2428
0
  // us all the subtrees within the range.
2429
0
2430
0
  RangeSubtreeIterator iter;
2431
0
2432
0
  aRv = iter.Init(this);
2433
0
  if (aRv.Failed()) {
2434
0
    return nullptr;
2435
0
  }
2436
0
2437
0
  if (iter.IsDone())
2438
0
  {
2439
0
    // There's nothing to add to the doc frag, we must be done!
2440
0
    return clonedFrag.forget();
2441
0
  }
2442
0
2443
0
  iter.First();
2444
0
2445
0
  // With the exception of text nodes that contain one of the range
2446
0
  // end points and elements which don't have any content selected the subtree
2447
0
  // iterator should only give us back subtrees that are completely contained
2448
0
  // between the range's end points.
2449
0
  //
2450
0
  // Unfortunately these subtrees don't contain the parent hierarchy/context
2451
0
  // that the Range spec requires us to return. This loop clones the
2452
0
  // parent hierarchy, adds a cloned version of the subtree, to it, then
2453
0
  // correctly places this new subtree into the doc fragment.
2454
0
2455
0
  while (!iter.IsDone())
2456
0
  {
2457
0
    nsCOMPtr<nsINode> node = iter.GetCurrentNode();
2458
0
    bool deepClone = !node->IsElement() ||
2459
0
                       (!(node == mEnd.Container() && mEnd.Offset() == 0) &&
2460
0
                        !(node == mStart.Container() &&
2461
0
                          mStart.Offset() == node->AsElement()->GetChildCount()));
2462
0
2463
0
    // Clone the current subtree!
2464
0
2465
0
    nsCOMPtr<nsINode> clone = node->CloneNode(deepClone, aRv);
2466
0
    if (aRv.Failed()) {
2467
0
      return nullptr;
2468
0
    }
2469
0
2470
0
    // If it's CharacterData, make sure we only clone what
2471
0
    // is in the range.
2472
0
    //
2473
0
    // XXX_kin: We need to also handle ProcessingInstruction
2474
0
    // XXX_kin: according to the spec.
2475
0
2476
0
    if (auto charData = CharacterData::FromNode(clone))
2477
0
    {
2478
0
      if (node == mEnd.Container()) {
2479
0
        // We only need the data before mEndOffset, so get rid of any
2480
0
        // data after it.
2481
0
2482
0
        uint32_t dataLength = charData->Length();
2483
0
        if (dataLength > (uint32_t)mEnd.Offset())
2484
0
        {
2485
0
          charData->DeleteData(mEnd.Offset(), dataLength - mEnd.Offset(), aRv);
2486
0
          if (aRv.Failed()) {
2487
0
            return nullptr;
2488
0
          }
2489
0
        }
2490
0
      }
2491
0
2492
0
      if (node == mStart.Container()) {
2493
0
        // We don't need any data before mStartOffset, so just
2494
0
        // delete it!
2495
0
2496
0
        if (mStart.Offset() > 0)
2497
0
        {
2498
0
          charData->DeleteData(0, mStart.Offset(), aRv);
2499
0
          if (aRv.Failed()) {
2500
0
            return nullptr;
2501
0
          }
2502
0
        }
2503
0
      }
2504
0
    }
2505
0
2506
0
    // Clone the parent hierarchy between commonAncestor and node.
2507
0
2508
0
    nsCOMPtr<nsINode> closestAncestor, farthestAncestor;
2509
0
2510
0
    aRv = CloneParentsBetween(commonAncestor, node,
2511
0
                              getter_AddRefs(closestAncestor),
2512
0
                              getter_AddRefs(farthestAncestor));
2513
0
2514
0
    if (aRv.Failed()) {
2515
0
      return nullptr;
2516
0
    }
2517
0
2518
0
    // Hook the parent hierarchy/context of the subtree into the clone tree.
2519
0
2520
0
    if (farthestAncestor)
2521
0
    {
2522
0
      commonCloneAncestor->AppendChild(*farthestAncestor, aRv);
2523
0
2524
0
      if (aRv.Failed()) {
2525
0
        return nullptr;
2526
0
      }
2527
0
    }
2528
0
2529
0
    // Place the cloned subtree into the cloned doc frag tree!
2530
0
2531
0
    nsCOMPtr<nsINode> cloneNode = do_QueryInterface(clone);
2532
0
    if (closestAncestor)
2533
0
    {
2534
0
      // Append the subtree under closestAncestor since it is the
2535
0
      // immediate parent of the subtree.
2536
0
2537
0
      closestAncestor->AppendChild(*cloneNode, aRv);
2538
0
    }
2539
0
    else
2540
0
    {
2541
0
      // If we get here, there is no missing parent hierarchy between
2542
0
      // commonAncestor and node, so just append clone to commonCloneAncestor.
2543
0
2544
0
      commonCloneAncestor->AppendChild(*cloneNode, aRv);
2545
0
    }
2546
0
    if (aRv.Failed()) {
2547
0
      return nullptr;
2548
0
    }
2549
0
2550
0
    // Get the next subtree to be processed. The idea here is to setup
2551
0
    // the parameters for the next iteration of the loop.
2552
0
2553
0
    iter.Next();
2554
0
2555
0
    if (iter.IsDone())
2556
0
      break; // We must be done!
2557
0
2558
0
    nsCOMPtr<nsINode> nextNode = iter.GetCurrentNode();
2559
0
    if (!nextNode) {
2560
0
      aRv.Throw(NS_ERROR_FAILURE);
2561
0
      return nullptr;
2562
0
    }
2563
0
2564
0
    // Get node and nextNode's common parent.
2565
0
    commonAncestor = nsContentUtils::GetCommonAncestor(node, nextNode);
2566
0
2567
0
    if (!commonAncestor) {
2568
0
      aRv.Throw(NS_ERROR_FAILURE);
2569
0
      return nullptr;
2570
0
    }
2571
0
2572
0
    // Find the equivalent of commonAncestor in the cloned tree!
2573
0
2574
0
    while (node && node != commonAncestor)
2575
0
    {
2576
0
      node = node->GetParentNode();
2577
0
      if (aRv.Failed()) {
2578
0
        return nullptr;
2579
0
      }
2580
0
2581
0
      if (!node) {
2582
0
        aRv.Throw(NS_ERROR_FAILURE);
2583
0
        return nullptr;
2584
0
      }
2585
0
2586
0
      cloneNode = cloneNode->GetParentNode();
2587
0
      if (!cloneNode) {
2588
0
        aRv.Throw(NS_ERROR_FAILURE);
2589
0
        return nullptr;
2590
0
      }
2591
0
    }
2592
0
2593
0
    commonCloneAncestor = cloneNode;
2594
0
  }
2595
0
2596
0
  return clonedFrag.forget();
2597
0
}
2598
2599
already_AddRefed<nsRange>
2600
nsRange::CloneRange() const
2601
0
{
2602
0
  RefPtr<nsRange> range = new nsRange(mOwner);
2603
0
2604
0
  range->DoSetRange(mStart.AsRaw(), mEnd.AsRaw(), mRoot);
2605
0
2606
0
  return range.forget();
2607
0
}
2608
2609
void
2610
nsRange::InsertNode(nsINode& aNode, ErrorResult& aRv)
2611
0
{
2612
0
  if (!nsContentUtils::LegacyIsCallerNativeCode() &&
2613
0
      !nsContentUtils::CanCallerAccess(&aNode)) {
2614
0
    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
2615
0
    return;
2616
0
  }
2617
0
2618
0
  uint32_t tStartOffset = StartOffset();
2619
0
2620
0
  nsCOMPtr<nsINode> tStartContainer = GetStartContainer(aRv);
2621
0
  if (aRv.Failed()) {
2622
0
    return;
2623
0
  }
2624
0
2625
0
  if (&aNode == tStartContainer) {
2626
0
    aRv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
2627
0
    return;
2628
0
  }
2629
0
2630
0
  // This is the node we'll be inserting before, and its parent
2631
0
  nsCOMPtr<nsINode> referenceNode;
2632
0
  nsCOMPtr<nsINode> referenceParentNode = tStartContainer;
2633
0
2634
0
  RefPtr<Text> startTextNode =
2635
0
    tStartContainer ? tStartContainer->GetAsText() : nullptr;
2636
0
  nsCOMPtr<nsINodeList> tChildList;
2637
0
  if (startTextNode) {
2638
0
    referenceParentNode = tStartContainer->GetParentNode();
2639
0
    if (!referenceParentNode) {
2640
0
      aRv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
2641
0
      return;
2642
0
    }
2643
0
2644
0
    referenceParentNode->EnsurePreInsertionValidity(aNode, tStartContainer,
2645
0
                                                    aRv);
2646
0
    if (aRv.Failed()) {
2647
0
      return;
2648
0
    }
2649
0
2650
0
    RefPtr<Text> secondPart = startTextNode->SplitText(tStartOffset, aRv);
2651
0
    if (aRv.Failed()) {
2652
0
      return;
2653
0
    }
2654
0
2655
0
    referenceNode = do_QueryInterface(secondPart);
2656
0
  } else {
2657
0
    tChildList = tStartContainer->ChildNodes();
2658
0
2659
0
    // find the insertion point in the DOM and insert the Node
2660
0
    referenceNode = tChildList->Item(tStartOffset);
2661
0
2662
0
    tStartContainer->EnsurePreInsertionValidity(aNode, referenceNode, aRv);
2663
0
    if (aRv.Failed()) {
2664
0
      return;
2665
0
    }
2666
0
  }
2667
0
2668
0
  // We might need to update the end to include the new node (bug 433662).
2669
0
  // Ideally we'd only do this if needed, but it's tricky to know when it's
2670
0
  // needed in advance (bug 765799).
2671
0
  uint32_t newOffset;
2672
0
2673
0
  if (referenceNode) {
2674
0
    int32_t indexInParent = IndexOf(referenceNode);
2675
0
    if (NS_WARN_IF(indexInParent < 0)) {
2676
0
      aRv.Throw(NS_ERROR_FAILURE);
2677
0
      return;
2678
0
    }
2679
0
    newOffset = static_cast<uint32_t>(indexInParent);
2680
0
  } else {
2681
0
    newOffset = tChildList->Length();
2682
0
  }
2683
0
2684
0
  if (aNode.NodeType() == nsINode::DOCUMENT_FRAGMENT_NODE) {
2685
0
    newOffset += aNode.GetChildCount();
2686
0
  } else {
2687
0
    newOffset++;
2688
0
  }
2689
0
2690
0
  // Now actually insert the node
2691
0
  nsCOMPtr<nsINode> tResultNode;
2692
0
  tResultNode = referenceParentNode->InsertBefore(aNode, referenceNode, aRv);
2693
0
  if (aRv.Failed()) {
2694
0
    return;
2695
0
  }
2696
0
2697
0
  if (Collapsed()) {
2698
0
    aRv = SetEnd(referenceParentNode, newOffset);
2699
0
  }
2700
0
}
2701
2702
void
2703
nsRange::SurroundContents(nsINode& aNewParent, ErrorResult& aRv)
2704
0
{
2705
0
  if (!nsContentUtils::LegacyIsCallerNativeCode() &&
2706
0
      !nsContentUtils::CanCallerAccess(&aNewParent)) {
2707
0
    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
2708
0
    return;
2709
0
  }
2710
0
2711
0
  if (!mRoot) {
2712
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
2713
0
    return;
2714
0
  }
2715
0
  // INVALID_STATE_ERROR: Raised if the Range partially selects a non-text
2716
0
  // node.
2717
0
  if (mStart.Container() != mEnd.Container()) {
2718
0
    bool startIsText = mStart.Container()->IsText();
2719
0
    bool endIsText = mEnd.Container()->IsText();
2720
0
    nsINode* startGrandParent = mStart.Container()->GetParentNode();
2721
0
    nsINode* endGrandParent = mEnd.Container()->GetParentNode();
2722
0
    if (!((startIsText && endIsText &&
2723
0
           startGrandParent &&
2724
0
           startGrandParent == endGrandParent) ||
2725
0
          (startIsText &&
2726
0
           startGrandParent &&
2727
0
           startGrandParent == mEnd.Container()) ||
2728
0
          (endIsText &&
2729
0
           endGrandParent &&
2730
0
           endGrandParent == mStart.Container()))) {
2731
0
      aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
2732
0
      return;
2733
0
    }
2734
0
  }
2735
0
2736
0
  // INVALID_NODE_TYPE_ERROR if aNewParent is something that can't be inserted
2737
0
  // (Document, DocumentType, DocumentFragment)
2738
0
  uint16_t nodeType = aNewParent.NodeType();
2739
0
  if (nodeType == nsINode::DOCUMENT_NODE ||
2740
0
      nodeType == nsINode::DOCUMENT_TYPE_NODE ||
2741
0
      nodeType == nsINode::DOCUMENT_FRAGMENT_NODE) {
2742
0
    aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
2743
0
    return;
2744
0
  }
2745
0
2746
0
  // Extract the contents within the range.
2747
0
2748
0
  RefPtr<DocumentFragment> docFrag = ExtractContents(aRv);
2749
0
2750
0
  if (aRv.Failed()) {
2751
0
    return;
2752
0
  }
2753
0
2754
0
  if (!docFrag) {
2755
0
    aRv.Throw(NS_ERROR_FAILURE);
2756
0
    return;
2757
0
  }
2758
0
2759
0
  // Spec says we need to remove all of aNewParent's
2760
0
  // children prior to insertion.
2761
0
2762
0
  nsCOMPtr<nsINodeList> children = aNewParent.ChildNodes();
2763
0
  if (!children) {
2764
0
    aRv.Throw(NS_ERROR_FAILURE);
2765
0
    return;
2766
0
  }
2767
0
2768
0
  uint32_t numChildren = children->Length();
2769
0
2770
0
  while (numChildren)
2771
0
  {
2772
0
    nsCOMPtr<nsINode> child = children->Item(--numChildren);
2773
0
    if (!child) {
2774
0
      aRv.Throw(NS_ERROR_FAILURE);
2775
0
      return;
2776
0
    }
2777
0
2778
0
    aNewParent.RemoveChild(*child, aRv);
2779
0
    if (aRv.Failed()) {
2780
0
      return;
2781
0
    }
2782
0
  }
2783
0
2784
0
  // Insert aNewParent at the range's start point.
2785
0
2786
0
  InsertNode(aNewParent, aRv);
2787
0
  if (aRv.Failed()) {
2788
0
    return;
2789
0
  }
2790
0
2791
0
  // Append the content we extracted under aNewParent.
2792
0
  aNewParent.AppendChild(*docFrag, aRv);
2793
0
  if (aRv.Failed()) {
2794
0
    return;
2795
0
  }
2796
0
2797
0
  // Select aNewParent, and its contents.
2798
0
2799
0
  SelectNode(aNewParent, aRv);
2800
0
}
2801
2802
void
2803
nsRange::ToString(nsAString& aReturn, ErrorResult& aErr)
2804
0
{
2805
0
  // clear the string
2806
0
  aReturn.Truncate();
2807
0
2808
0
  // If we're unpositioned, return the empty string
2809
0
  if (!mIsPositioned) {
2810
0
    return;
2811
0
  }
2812
0
2813
#ifdef DEBUG_range
2814
      printf("Range dump: -----------------------\n");
2815
#endif /* DEBUG */
2816
2817
0
  // effeciency hack for simple case
2818
0
  if (mStart.Container() == mEnd.Container()) {
2819
0
    Text* textNode = mStart.Container() ? mStart.Container()->GetAsText() : nullptr;
2820
0
2821
0
    if (textNode)
2822
0
    {
2823
#ifdef DEBUG_range
2824
      // If debug, dump it:
2825
      textNode->List(stdout);
2826
      printf("End Range dump: -----------------------\n");
2827
#endif /* DEBUG */
2828
2829
0
      // grab the text
2830
0
      textNode->SubstringData(mStart.Offset(), mEnd.Offset() - mStart.Offset(),
2831
0
                              aReturn, aErr);
2832
0
      return;
2833
0
    }
2834
0
  }
2835
0
2836
0
  /* complex case: mStart.Container() != mEnd.Container(), or mStartParent not a text
2837
0
     node revisit - there are potential optimizations here and also tradeoffs.
2838
0
  */
2839
0
2840
0
  nsCOMPtr<nsIContentIterator> iter = NS_NewContentIterator();
2841
0
  nsresult rv = iter->Init(this);
2842
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
2843
0
    aErr.Throw(rv);
2844
0
    return;
2845
0
  }
2846
0
2847
0
  nsString tempString;
2848
0
2849
0
  // loop through the content iterator, which returns nodes in the range in
2850
0
  // close tag order, and grab the text from any text node
2851
0
  while (!iter->IsDone())
2852
0
  {
2853
0
    nsINode *n = iter->GetCurrentNode();
2854
0
2855
#ifdef DEBUG_range
2856
    // If debug, dump it:
2857
    n->List(stdout);
2858
#endif /* DEBUG */
2859
    Text* textNode = n->GetAsText();
2860
0
    if (textNode) // if it's a text node, get the text
2861
0
    {
2862
0
      if (n == mStart.Container()) { // only include text past start offset
2863
0
        uint32_t strLength = textNode->Length();
2864
0
        textNode->SubstringData(mStart.Offset(), strLength-mStart.Offset(),
2865
0
                                tempString, IgnoreErrors());
2866
0
        aReturn += tempString;
2867
0
      } else if (n == mEnd.Container()) { // only include text before end offset
2868
0
        textNode->SubstringData(0, mEnd.Offset(), tempString, IgnoreErrors());
2869
0
        aReturn += tempString;
2870
0
      } else { // grab the whole kit-n-kaboodle
2871
0
        textNode->GetData(tempString);
2872
0
        aReturn += tempString;
2873
0
      }
2874
0
    }
2875
0
2876
0
    iter->Next();
2877
0
  }
2878
0
2879
#ifdef DEBUG_range
2880
  printf("End Range dump: -----------------------\n");
2881
#endif /* DEBUG */
2882
}
2883
2884
void
2885
nsRange::Detach()
2886
0
{
2887
0
}
2888
2889
already_AddRefed<DocumentFragment>
2890
nsRange::CreateContextualFragment(const nsAString& aFragment, ErrorResult& aRv)
2891
0
{
2892
0
  if (!mIsPositioned) {
2893
0
    aRv.Throw(NS_ERROR_FAILURE);
2894
0
    return nullptr;
2895
0
  }
2896
0
2897
0
  return nsContentUtils::CreateContextualFragment(mStart.Container(), aFragment,
2898
0
                                                  false, aRv);
2899
0
}
2900
2901
static void ExtractRectFromOffset(nsIFrame* aFrame,
2902
                                  const int32_t aOffset, nsRect* aR,
2903
                                  bool aFlushToOriginEdge, bool aClampToEdge)
2904
0
{
2905
0
  MOZ_ASSERT(aFrame);
2906
0
  MOZ_ASSERT(aR);
2907
0
2908
0
  nsPoint point;
2909
0
  aFrame->GetPointFromOffset(aOffset, &point);
2910
0
2911
0
  // Determine if aFrame has a vertical writing mode, which will change our math
2912
0
  // on the output rect.
2913
0
  bool isVertical = aFrame->GetWritingMode().IsVertical();
2914
0
2915
0
  if (!aClampToEdge && !aR->Contains(point)) {
2916
0
    // If point is outside aR, and we aren't clamping, output an empty rect
2917
0
    // with origin at the point.
2918
0
    if (isVertical) {
2919
0
      aR->SetHeight(0);
2920
0
      aR->y = point.y;
2921
0
    } else {
2922
0
      aR->SetWidth(0);
2923
0
      aR->x = point.x;
2924
0
    }
2925
0
    return;
2926
0
  }
2927
0
2928
0
  if (aClampToEdge) {
2929
0
    point = aR->ClampPoint(point);
2930
0
  }
2931
0
2932
0
  // point is within aR, and now we'll modify aR to output a rect that has point
2933
0
  // on one edge. But which edge?
2934
0
  if (aFlushToOriginEdge) {
2935
0
    // The output rect should be flush to the edge of aR that contains the origin.
2936
0
    if (isVertical) {
2937
0
      aR->SetHeight(point.y - aR->y);
2938
0
    } else {
2939
0
      aR->SetWidth(point.x - aR->x);
2940
0
    }
2941
0
  } else {
2942
0
    // The output rect should be flush to the edge of aR opposite the origin.
2943
0
    if (isVertical) {
2944
0
      aR->SetHeight(aR->YMost() - point.y);
2945
0
      aR->y = point.y;
2946
0
    } else {
2947
0
      aR->SetWidth(aR->XMost() - point.x);
2948
0
      aR->x = point.x;
2949
0
    }
2950
0
  }
2951
0
}
2952
2953
static nsTextFrame*
2954
GetTextFrameForContent(nsIContent* aContent, bool aFlushLayout)
2955
0
{
2956
0
  nsIDocument* doc = aContent->OwnerDoc();
2957
0
  nsIPresShell* presShell = doc->GetShell();
2958
0
  if (!presShell) {
2959
0
    return nullptr;
2960
0
  }
2961
0
2962
0
  const bool frameWillBeUnsuppressed =
2963
0
    presShell->FrameConstructor()->EnsureFrameForTextNodeIsCreatedAfterFlush(
2964
0
      static_cast<CharacterData*>(aContent));
2965
0
  if (aFlushLayout) {
2966
0
    doc->FlushPendingNotifications(FlushType::Layout);
2967
0
  } else if (frameWillBeUnsuppressed) {
2968
0
    doc->FlushPendingNotifications(FlushType::Frames);
2969
0
  }
2970
0
2971
0
  nsIFrame* frame = aContent->GetPrimaryFrame();
2972
0
  if (!frame || !frame->IsTextFrame()) {
2973
0
    return nullptr;
2974
0
  }
2975
0
  return static_cast<nsTextFrame*>(frame);
2976
0
}
2977
2978
static nsresult GetPartialTextRect(nsLayoutUtils::RectCallback* aCallback,
2979
                                   Sequence<nsString>* aTextList,
2980
                                   nsIContent* aContent, int32_t aStartOffset,
2981
                                   int32_t aEndOffset, bool aClampToEdge,
2982
                                   bool aFlushLayout)
2983
0
{
2984
0
  nsTextFrame* textFrame = GetTextFrameForContent(aContent, aFlushLayout);
2985
0
  if (textFrame) {
2986
0
    nsIFrame* relativeTo = nsLayoutUtils::GetContainingBlockForClientRect(textFrame);
2987
0
    for (nsTextFrame* f = textFrame; f; f = static_cast<nsTextFrame*>(f->GetNextContinuation())) {
2988
0
      int32_t fstart = f->GetContentOffset(), fend = f->GetContentEnd();
2989
0
      if (fend <= aStartOffset || fstart >= aEndOffset)
2990
0
        continue;
2991
0
2992
0
      // Calculate the text content offsets we'll need if text is requested.
2993
0
      int32_t textContentStart = fstart;
2994
0
      int32_t textContentEnd = fend;
2995
0
2996
0
      // overlapping with the offset we want
2997
0
      f->EnsureTextRun(nsTextFrame::eInflated);
2998
0
      NS_ENSURE_TRUE(f->GetTextRun(nsTextFrame::eInflated), NS_ERROR_OUT_OF_MEMORY);
2999
0
      bool rtl = f->GetTextRun(nsTextFrame::eInflated)->IsRightToLeft();
3000
0
      nsRect r = f->GetRectRelativeToSelf();
3001
0
      if (fstart < aStartOffset) {
3002
0
        // aStartOffset is within this frame
3003
0
        ExtractRectFromOffset(f, aStartOffset, &r, rtl, aClampToEdge);
3004
0
        textContentStart = aStartOffset;
3005
0
      }
3006
0
      if (fend > aEndOffset) {
3007
0
        // aEndOffset is in the middle of this frame
3008
0
        ExtractRectFromOffset(f, aEndOffset, &r, !rtl, aClampToEdge);
3009
0
        textContentEnd = aEndOffset;
3010
0
      }
3011
0
      r = nsLayoutUtils::TransformFrameRectToAncestor(f, r, relativeTo);
3012
0
      aCallback->AddRect(r);
3013
0
3014
0
      // Finally capture the text, if requested.
3015
0
      if (aTextList) {
3016
0
        nsIFrame::RenderedText renderedText = f->GetRenderedText(
3017
0
          textContentStart,
3018
0
          textContentEnd,
3019
0
          nsIFrame::TextOffsetType::OFFSETS_IN_CONTENT_TEXT,
3020
0
          nsIFrame::TrailingWhitespace::DONT_TRIM_TRAILING_WHITESPACE);
3021
0
3022
0
        aTextList->AppendElement(renderedText.mString, fallible);
3023
0
      }
3024
0
    }
3025
0
  }
3026
0
  return NS_OK;
3027
0
}
3028
3029
/* static */ void
3030
nsRange::CollectClientRectsAndText(nsLayoutUtils::RectCallback* aCollector,
3031
                                   Sequence<nsString>* aTextList,
3032
                                   nsRange* aRange,
3033
                                   nsINode* aStartContainer,
3034
                                   uint32_t aStartOffset,
3035
                                   nsINode* aEndContainer,
3036
                                   uint32_t aEndOffset,
3037
                                   bool aClampToEdge, bool aFlushLayout)
3038
0
{
3039
0
  // Currently, this method is called with start of end offset of nsRange.
3040
0
  // So, they must be between 0 - INT32_MAX.
3041
0
  MOZ_ASSERT(IsValidOffset(aStartOffset));
3042
0
  MOZ_ASSERT(IsValidOffset(aEndOffset));
3043
0
3044
0
  // Hold strong pointers across the flush
3045
0
  nsCOMPtr<nsINode> startContainer = aStartContainer;
3046
0
  nsCOMPtr<nsINode> endContainer = aEndContainer;
3047
0
3048
0
  // Flush out layout so our frames are up to date.
3049
0
  if (!aStartContainer->IsInComposedDoc()) {
3050
0
    return;
3051
0
  }
3052
0
3053
0
  if (aFlushLayout) {
3054
0
    aStartContainer->OwnerDoc()->FlushPendingNotifications(FlushType::Layout);
3055
0
    // Recheck whether we're still in the document
3056
0
    if (!aStartContainer->IsInComposedDoc()) {
3057
0
      return;
3058
0
    }
3059
0
  }
3060
0
3061
0
  RangeSubtreeIterator iter;
3062
0
3063
0
  nsresult rv = iter.Init(aRange);
3064
0
  if (NS_FAILED(rv)) return;
3065
0
3066
0
  if (iter.IsDone()) {
3067
0
    // the range is collapsed, only continue if the cursor is in a text node
3068
0
    if (aStartContainer->IsText()) {
3069
0
      nsTextFrame* textFrame = GetTextFrameForContent(aStartContainer->AsText(), aFlushLayout);
3070
0
      if (textFrame) {
3071
0
        int32_t outOffset;
3072
0
        nsIFrame* outFrame;
3073
0
        textFrame->GetChildFrameContainingOffset(
3074
0
                     static_cast<int32_t>(aStartOffset), false,
3075
0
                     &outOffset, &outFrame);
3076
0
        if (outFrame) {
3077
0
           nsIFrame* relativeTo =
3078
0
             nsLayoutUtils::GetContainingBlockForClientRect(outFrame);
3079
0
           nsRect r = outFrame->GetRectRelativeToSelf();
3080
0
           ExtractRectFromOffset(outFrame, static_cast<int32_t>(aStartOffset),
3081
0
                                 &r, false, aClampToEdge);
3082
0
           r.SetWidth(0);
3083
0
           r = nsLayoutUtils::TransformFrameRectToAncestor(outFrame, r, relativeTo);
3084
0
           aCollector->AddRect(r);
3085
0
        }
3086
0
      }
3087
0
    }
3088
0
    return;
3089
0
  }
3090
0
3091
0
  do {
3092
0
    nsCOMPtr<nsINode> node = iter.GetCurrentNode();
3093
0
    iter.Next();
3094
0
    nsCOMPtr<nsIContent> content = do_QueryInterface(node);
3095
0
    if (!content)
3096
0
      continue;
3097
0
    if (content->IsText()) {
3098
0
       if (node == startContainer) {
3099
0
         int32_t offset = startContainer == endContainer ?
3100
0
           static_cast<int32_t>(aEndOffset) : content->GetText()->GetLength();
3101
0
         GetPartialTextRect(aCollector, aTextList, content,
3102
0
                            static_cast<int32_t>(aStartOffset), offset,
3103
0
                            aClampToEdge, aFlushLayout);
3104
0
         continue;
3105
0
       } else if (node == endContainer) {
3106
0
         GetPartialTextRect(aCollector, aTextList, content,
3107
0
                            0, static_cast<int32_t>(aEndOffset),
3108
0
                            aClampToEdge, aFlushLayout);
3109
0
         continue;
3110
0
       }
3111
0
    }
3112
0
3113
0
    nsIFrame* frame = content->GetPrimaryFrame();
3114
0
    if (frame) {
3115
0
      nsLayoutUtils::GetAllInFlowRectsAndTexts(frame,
3116
0
        nsLayoutUtils::GetContainingBlockForClientRect(frame), aCollector,
3117
0
        aTextList,
3118
0
        nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS);
3119
0
    }
3120
0
  } while (!iter.IsDone());
3121
0
}
3122
3123
already_AddRefed<DOMRect>
3124
nsRange::GetBoundingClientRect(bool aClampToEdge, bool aFlushLayout)
3125
0
{
3126
0
  RefPtr<DOMRect> rect = new DOMRect(ToSupports(this));
3127
0
  if (!mStart.Container()) {
3128
0
    return rect.forget();
3129
0
  }
3130
0
3131
0
  nsLayoutUtils::RectAccumulator accumulator;
3132
0
  CollectClientRectsAndText(&accumulator, nullptr, this, mStart.Container(),
3133
0
    mStart.Offset(), mEnd.Container(), mEnd.Offset(), aClampToEdge, aFlushLayout);
3134
0
3135
0
  nsRect r = accumulator.mResultRect.IsEmpty() ? accumulator.mFirstRect :
3136
0
    accumulator.mResultRect;
3137
0
  rect->SetLayoutRect(r);
3138
0
  return rect.forget();
3139
0
}
3140
3141
already_AddRefed<DOMRectList>
3142
nsRange::GetClientRects(bool aClampToEdge, bool aFlushLayout)
3143
0
{
3144
0
  if (!mStart.Container()) {
3145
0
    return nullptr;
3146
0
  }
3147
0
3148
0
  RefPtr<DOMRectList> rectList = new DOMRectList(this);
3149
0
3150
0
  nsLayoutUtils::RectListBuilder builder(rectList);
3151
0
3152
0
  CollectClientRectsAndText(&builder, nullptr, this, mStart.Container(),
3153
0
    mStart.Offset(), mEnd.Container(), mEnd.Offset(), aClampToEdge, aFlushLayout);
3154
0
  return rectList.forget();
3155
0
}
3156
3157
void
3158
nsRange::GetClientRectsAndTexts(
3159
  mozilla::dom::ClientRectsAndTexts& aResult,
3160
  ErrorResult& aErr)
3161
0
{
3162
0
  if (!mStart.Container()) {
3163
0
    return;
3164
0
  }
3165
0
3166
0
  aResult.mRectList = new DOMRectList(this);
3167
0
3168
0
  nsLayoutUtils::RectListBuilder builder(aResult.mRectList);
3169
0
3170
0
  CollectClientRectsAndText(&builder, &aResult.mTextList, this,
3171
0
    mStart.Container(), mStart.Offset(), mEnd.Container(), mEnd.Offset(), true, true);
3172
0
}
3173
3174
nsresult
3175
nsRange::GetUsedFontFaces(nsTArray<nsAutoPtr<InspectorFontFace>>& aResult,
3176
                          uint32_t aMaxRanges, bool aSkipCollapsedWhitespace)
3177
0
{
3178
0
  NS_ENSURE_TRUE(mStart.Container(), NS_ERROR_UNEXPECTED);
3179
0
3180
0
  nsCOMPtr<nsINode> startContainer = mStart.Container();
3181
0
  nsCOMPtr<nsINode> endContainer = mEnd.Container();
3182
0
3183
0
  // Flush out layout so our frames are up to date.
3184
0
  nsIDocument* doc = mStart.Container()->OwnerDoc();
3185
0
  NS_ENSURE_TRUE(doc, NS_ERROR_UNEXPECTED);
3186
0
  doc->FlushPendingNotifications(FlushType::Frames);
3187
0
3188
0
  // Recheck whether we're still in the document
3189
0
  NS_ENSURE_TRUE(mStart.Container()->IsInComposedDoc(), NS_ERROR_UNEXPECTED);
3190
0
3191
0
  // A table to map gfxFontEntry objects to InspectorFontFace objects.
3192
0
  // (We hold on to the InspectorFontFaces strongly due to the nsAutoPtrs
3193
0
  // in the nsClassHashtable, until we move them out into aResult at the end
3194
0
  // of the function.)
3195
0
  nsLayoutUtils::UsedFontFaceTable fontFaces;
3196
0
3197
0
  RangeSubtreeIterator iter;
3198
0
  nsresult rv = iter.Init(this);
3199
0
  NS_ENSURE_SUCCESS(rv, rv);
3200
0
3201
0
  while (!iter.IsDone()) {
3202
0
    // only collect anything if the range is not collapsed
3203
0
    nsCOMPtr<nsINode> node = iter.GetCurrentNode();
3204
0
    iter.Next();
3205
0
3206
0
    nsCOMPtr<nsIContent> content = do_QueryInterface(node);
3207
0
    if (!content) {
3208
0
      continue;
3209
0
    }
3210
0
    nsIFrame* frame = content->GetPrimaryFrame();
3211
0
    if (!frame) {
3212
0
      continue;
3213
0
    }
3214
0
3215
0
    if (content->IsText()) {
3216
0
       if (node == startContainer) {
3217
0
         int32_t offset = startContainer == endContainer ?
3218
0
           mEnd.Offset() : content->GetText()->GetLength();
3219
0
         nsLayoutUtils::GetFontFacesForText(frame, mStart.Offset(), offset,
3220
0
                                            true, fontFaces, aMaxRanges,
3221
0
                                            aSkipCollapsedWhitespace);
3222
0
         continue;
3223
0
       }
3224
0
       if (node == endContainer) {
3225
0
         nsLayoutUtils::GetFontFacesForText(frame, 0, mEnd.Offset(),
3226
0
                                            true, fontFaces, aMaxRanges,
3227
0
                                            aSkipCollapsedWhitespace);
3228
0
         continue;
3229
0
       }
3230
0
    }
3231
0
3232
0
    nsLayoutUtils::GetFontFacesForFrames(frame, fontFaces, aMaxRanges,
3233
0
                                         aSkipCollapsedWhitespace);
3234
0
  }
3235
0
3236
0
  // Take ownership of the InspectorFontFaces in the table and move them into
3237
0
  // the aResult outparam.
3238
0
  for (auto iter = fontFaces.Iter(); !iter.Done(); iter.Next()) {
3239
0
    aResult.AppendElement(std::move(iter.Data()));
3240
0
  }
3241
0
3242
0
  return NS_OK;
3243
0
}
3244
3245
nsINode*
3246
nsRange::GetRegisteredCommonAncestor()
3247
0
{
3248
0
  MOZ_ASSERT(IsInSelection(),
3249
0
             "GetRegisteredCommonAncestor only valid for range in selection");
3250
0
  MOZ_ASSERT(mRegisteredCommonAncestor);
3251
0
  return mRegisteredCommonAncestor;
3252
0
}
3253
3254
/* static */ bool nsRange::AutoInvalidateSelection::sIsNested;
3255
3256
nsRange::AutoInvalidateSelection::~AutoInvalidateSelection()
3257
0
{
3258
0
  if (!mCommonAncestor) {
3259
0
    return;
3260
0
  }
3261
0
  sIsNested = false;
3262
0
  ::InvalidateAllFrames(mCommonAncestor);
3263
0
3264
0
  // Our range might not be in a selection anymore, because one of our selection
3265
0
  // listeners might have gone ahead and run script of various sorts that messed
3266
0
  // with selections, ranges, etc.  But if it still is, we should check whether
3267
0
  // we have a different common ancestor now, and if so invalidate its subtree
3268
0
  // so it paints the selection it's in now.
3269
0
  if (mRange->IsInSelection()) {
3270
0
    nsINode* commonAncestor = mRange->GetRegisteredCommonAncestor();
3271
0
    // XXXbz can commonAncestor really be null here?  I wouldn't think so!  If
3272
0
    // it _were_, then in a debug build GetRegisteredCommonAncestor() would have
3273
0
    // fatally asserted.
3274
0
    if (commonAncestor && commonAncestor != mCommonAncestor) {
3275
0
      ::InvalidateAllFrames(commonAncestor);
3276
0
    }
3277
0
  }
3278
0
}
3279
3280
/* static */ already_AddRefed<nsRange>
3281
nsRange::Constructor(const GlobalObject& aGlobal,
3282
                     ErrorResult& aRv)
3283
0
{
3284
0
  nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal.GetAsSupports());
3285
0
  if (!window || !window->GetDoc()) {
3286
0
    aRv.Throw(NS_ERROR_FAILURE);
3287
0
    return nullptr;
3288
0
  }
3289
0
3290
0
  return window->GetDoc()->CreateRange(aRv);
3291
0
}
3292
3293
static bool ExcludeIfNextToNonSelectable(nsIContent* aContent)
3294
0
{
3295
0
  return aContent->IsText() &&
3296
0
    aContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE);
3297
0
}
3298
3299
void
3300
nsRange::ExcludeNonSelectableNodes(nsTArray<RefPtr<nsRange>>* aOutRanges)
3301
0
{
3302
0
  MOZ_ASSERT(mIsPositioned);
3303
0
  MOZ_ASSERT(mEnd.Container());
3304
0
  MOZ_ASSERT(mStart.Container());
3305
0
3306
0
  nsRange* range = this;
3307
0
  RefPtr<nsRange> newRange;
3308
0
  while (range) {
3309
0
    nsCOMPtr<nsIContentIterator> iter = NS_NewPreContentIterator();
3310
0
    nsresult rv = iter->Init(range);
3311
0
    if (NS_FAILED(rv)) {
3312
0
      return;
3313
0
    }
3314
0
3315
0
    bool added = false;
3316
0
    bool seenSelectable = false;
3317
0
    // |firstNonSelectableContent| is the first node in a consecutive sequence
3318
0
    // of non-IsSelectable nodes.  When we find a selectable node after such
3319
0
    // a sequence we'll end the last nsRange, create a new one and restart
3320
0
    // the outer loop.
3321
0
    nsIContent* firstNonSelectableContent = nullptr;
3322
0
    while (true) {
3323
0
      ErrorResult err;
3324
0
      nsINode* node = iter->GetCurrentNode();
3325
0
      iter->Next();
3326
0
      bool selectable = true;
3327
0
      nsIContent* content =
3328
0
        node && node->IsContent() ? node->AsContent() : nullptr;
3329
0
      if (content) {
3330
0
        if (firstNonSelectableContent && ExcludeIfNextToNonSelectable(content)) {
3331
0
          // Ignorable whitespace next to a sequence of non-selectable nodes
3332
0
          // counts as non-selectable (bug 1216001).
3333
0
          selectable = false;
3334
0
        }
3335
0
        if (selectable) {
3336
0
          nsIFrame* frame = content->GetPrimaryFrame();
3337
0
          for (nsIContent* p = content; !frame && (p = p->GetParent()); ) {
3338
0
            frame = p->GetPrimaryFrame();
3339
0
          }
3340
0
          if (frame) {
3341
0
            selectable = frame->IsSelectable(nullptr);
3342
0
          }
3343
0
        }
3344
0
      }
3345
0
3346
0
      if (!selectable) {
3347
0
        if (!firstNonSelectableContent) {
3348
0
          firstNonSelectableContent = content;
3349
0
        }
3350
0
        if (iter->IsDone() && seenSelectable) {
3351
0
          // The tail end of the initial range is non-selectable - truncate the
3352
0
          // current range before the first non-selectable node.
3353
0
          range->SetEndBefore(*firstNonSelectableContent, err);
3354
0
        }
3355
0
      } else if (firstNonSelectableContent) {
3356
0
        if (range == this && !seenSelectable) {
3357
0
          // This is the initial range and all its nodes until now are
3358
0
          // non-selectable so just trim them from the start.
3359
0
          range->SetStartBefore(*node, err);
3360
0
          if (err.Failed()) {
3361
0
            return;
3362
0
          }
3363
0
          break; // restart the same range with a new iterator
3364
0
        } else {
3365
0
          // Save the end point before truncating the range.
3366
0
          nsINode* endContainer = range->mEnd.Container();
3367
0
          int32_t endOffset = range->mEnd.Offset();
3368
0
3369
0
          // Truncate the current range before the first non-selectable node.
3370
0
          range->SetEndBefore(*firstNonSelectableContent, err);
3371
0
3372
0
          // Store it in the result (strong ref) - do this before creating
3373
0
          // a new range in |newRange| below so we don't drop the last ref
3374
0
          // to the range created in the previous iteration.
3375
0
          if (!added && !err.Failed()) {
3376
0
            aOutRanges->AppendElement(range);
3377
0
          }
3378
0
3379
0
          // Create a new range for the remainder.
3380
0
          nsINode* startContainer = node;
3381
0
          int32_t startOffset = 0;
3382
0
          // Don't start *inside* a node with independent selection though
3383
0
          // (e.g. <input>).
3384
0
          if (content && content->HasIndependentSelection()) {
3385
0
            nsINode* parent = startContainer->GetParent();
3386
0
            if (parent) {
3387
0
              startOffset = parent->ComputeIndexOf(startContainer);
3388
0
              startContainer = parent;
3389
0
            }
3390
0
          }
3391
0
          rv = CreateRange(startContainer, startOffset, endContainer, endOffset,
3392
0
                           getter_AddRefs(newRange));
3393
0
          if (NS_FAILED(rv) || newRange->Collapsed()) {
3394
0
            newRange = nullptr;
3395
0
          }
3396
0
          range = newRange;
3397
0
          break; // create a new iterator for the new range, if any
3398
0
        }
3399
0
      } else {
3400
0
        seenSelectable = true;
3401
0
        if (!added) {
3402
0
          added = true;
3403
0
          aOutRanges->AppendElement(range);
3404
0
        }
3405
0
      }
3406
0
      if (iter->IsDone()) {
3407
0
        return;
3408
0
      }
3409
0
    }
3410
0
  }
3411
0
}
3412
3413
struct InnerTextAccumulator
3414
{
3415
  explicit InnerTextAccumulator(mozilla::dom::DOMString& aValue)
3416
0
    : mString(aValue.AsAString()), mRequiredLineBreakCount(0) {}
3417
  void FlushLineBreaks()
3418
0
  {
3419
0
    while (mRequiredLineBreakCount > 0) {
3420
0
      // Required line breaks at the start of the text are suppressed.
3421
0
      if (!mString.IsEmpty()) {
3422
0
        mString.Append('\n');
3423
0
      }
3424
0
      --mRequiredLineBreakCount;
3425
0
    }
3426
0
  }
3427
  void Append(char aCh)
3428
0
  {
3429
0
    Append(nsAutoString(aCh));
3430
0
  }
3431
  void Append(const nsAString& aString)
3432
0
  {
3433
0
    if (aString.IsEmpty()) {
3434
0
      return;
3435
0
    }
3436
0
    FlushLineBreaks();
3437
0
    mString.Append(aString);
3438
0
  }
3439
  void AddRequiredLineBreakCount(int8_t aCount)
3440
0
  {
3441
0
    mRequiredLineBreakCount = std::max(mRequiredLineBreakCount, aCount);
3442
0
  }
3443
3444
  nsAString& mString;
3445
  int8_t mRequiredLineBreakCount;
3446
};
3447
3448
static bool
3449
IsVisibleAndNotInReplacedElement(nsIFrame* aFrame)
3450
0
{
3451
0
  if (!aFrame || !aFrame->StyleVisibility()->IsVisible()) {
3452
0
    return false;
3453
0
  }
3454
0
  for (nsIFrame* f = aFrame->GetParent(); f; f = f->GetParent()) {
3455
0
    if (f->IsFrameOfType(nsIFrame::eReplaced) &&
3456
0
        !f->GetContent()->IsHTMLElement(nsGkAtoms::button) &&
3457
0
        !f->GetContent()->IsHTMLElement(nsGkAtoms::select)) {
3458
0
      return false;
3459
0
    }
3460
0
  }
3461
0
  return true;
3462
0
}
3463
3464
static bool
3465
ElementIsVisibleNoFlush(Element* aElement)
3466
0
{
3467
0
  if (!aElement) {
3468
0
    return false;
3469
0
  }
3470
0
  RefPtr<ComputedStyle> sc =
3471
0
    nsComputedDOMStyle::GetComputedStyleNoFlush(aElement, nullptr);
3472
0
  return sc && sc->StyleVisibility()->IsVisible();
3473
0
}
3474
3475
static void
3476
AppendTransformedText(InnerTextAccumulator& aResult, nsIContent* aContainer)
3477
0
{
3478
0
  auto textNode = static_cast<CharacterData*>(aContainer);
3479
0
3480
0
  nsIFrame* frame = textNode->GetPrimaryFrame();
3481
0
  if (!IsVisibleAndNotInReplacedElement(frame)) {
3482
0
    return;
3483
0
  }
3484
0
3485
0
  nsIFrame::RenderedText text =
3486
0
    frame->GetRenderedText(0, aContainer->GetChildCount());
3487
0
  aResult.Append(text.mString);
3488
0
}
3489
3490
/**
3491
 * States for tree traversal. AT_NODE means that we are about to enter
3492
 * the current DOM node. AFTER_NODE means that we have just finished traversing
3493
 * the children of the current DOM node and are about to apply any
3494
 * "after processing the node's children" steps before we finish visiting
3495
 * the node.
3496
 */
3497
enum TreeTraversalState {
3498
  AT_NODE,
3499
  AFTER_NODE
3500
};
3501
3502
static int8_t
3503
GetRequiredInnerTextLineBreakCount(nsIFrame* aFrame)
3504
0
{
3505
0
  if (aFrame->GetContent()->IsHTMLElement(nsGkAtoms::p)) {
3506
0
    return 2;
3507
0
  }
3508
0
  const nsStyleDisplay* styleDisplay = aFrame->StyleDisplay();
3509
0
  if (styleDisplay->IsBlockOutside(aFrame) ||
3510
0
      styleDisplay->mDisplay == StyleDisplay::TableCaption) {
3511
0
    return 1;
3512
0
  }
3513
0
  return 0;
3514
0
}
3515
3516
static bool
3517
IsLastCellOfRow(nsIFrame* aFrame)
3518
0
{
3519
0
  LayoutFrameType type = aFrame->Type();
3520
0
  if (type != LayoutFrameType::TableCell &&
3521
0
      type != LayoutFrameType::BCTableCell) {
3522
0
    return true;
3523
0
  }
3524
0
  for (nsIFrame* c = aFrame; c; c = c->GetNextContinuation()) {
3525
0
    if (c->GetNextSibling()) {
3526
0
      return false;
3527
0
    }
3528
0
  }
3529
0
  return true;
3530
0
}
3531
3532
static bool
3533
IsLastRowOfRowGroup(nsIFrame* aFrame)
3534
0
{
3535
0
  if (!aFrame->IsTableRowFrame()) {
3536
0
    return true;
3537
0
  }
3538
0
  for (nsIFrame* c = aFrame; c; c = c->GetNextContinuation()) {
3539
0
    if (c->GetNextSibling()) {
3540
0
      return false;
3541
0
    }
3542
0
  }
3543
0
  return true;
3544
0
}
3545
3546
static bool
3547
IsLastNonemptyRowGroupOfTable(nsIFrame* aFrame)
3548
0
{
3549
0
  if (!aFrame->IsTableRowGroupFrame()) {
3550
0
    return true;
3551
0
  }
3552
0
  for (nsIFrame* c = aFrame; c; c = c->GetNextContinuation()) {
3553
0
    for (nsIFrame* next = c->GetNextSibling(); next; next = next->GetNextSibling()) {
3554
0
      if (next->PrincipalChildList().FirstChild()) {
3555
0
        return false;
3556
0
      }
3557
0
    }
3558
0
  }
3559
0
  return true;
3560
0
}
3561
3562
void
3563
nsRange::GetInnerTextNoFlush(DOMString& aValue, ErrorResult& aError,
3564
                             nsIContent* aContainer)
3565
0
{
3566
0
  InnerTextAccumulator result(aValue);
3567
0
3568
0
  if (aContainer->IsText()) {
3569
0
    AppendTransformedText(result, aContainer);
3570
0
    return;
3571
0
  }
3572
0
3573
0
  nsIContent* currentNode = aContainer;
3574
0
  TreeTraversalState currentState = AFTER_NODE;
3575
0
3576
0
  nsIContent* endNode = aContainer;
3577
0
  TreeTraversalState endState = AFTER_NODE;
3578
0
3579
0
  nsIContent* firstChild = aContainer->GetFirstChild();
3580
0
  if (firstChild) {
3581
0
    currentNode = firstChild;
3582
0
    currentState = AT_NODE;
3583
0
  }
3584
0
3585
0
  while (currentNode != endNode || currentState != endState) {
3586
0
    nsIFrame* f = currentNode->GetPrimaryFrame();
3587
0
    bool isVisibleAndNotReplaced = IsVisibleAndNotInReplacedElement(f);
3588
0
    if (currentState == AT_NODE) {
3589
0
      bool isText = currentNode->IsText();
3590
0
      if (isText && currentNode->GetParent()->IsHTMLElement(nsGkAtoms::rp) &&
3591
0
          ElementIsVisibleNoFlush(currentNode->GetParent()->AsElement())) {
3592
0
        nsAutoString str;
3593
0
        currentNode->GetTextContent(str, aError);
3594
0
        result.Append(str);
3595
0
      } else if (isVisibleAndNotReplaced) {
3596
0
        result.AddRequiredLineBreakCount(GetRequiredInnerTextLineBreakCount(f));
3597
0
        if (isText) {
3598
0
          nsIFrame::RenderedText text = f->GetRenderedText();
3599
0
          result.Append(text.mString);
3600
0
        }
3601
0
      }
3602
0
      nsIContent* child = currentNode->GetFirstChild();
3603
0
      if (child) {
3604
0
        currentNode = child;
3605
0
        continue;
3606
0
      }
3607
0
      currentState = AFTER_NODE;
3608
0
    }
3609
0
    if (currentNode == endNode && currentState == endState) {
3610
0
      break;
3611
0
    }
3612
0
    if (isVisibleAndNotReplaced) {
3613
0
      if (currentNode->IsHTMLElement(nsGkAtoms::br)) {
3614
0
        result.Append('\n');
3615
0
      }
3616
0
      switch (f->StyleDisplay()->mDisplay) {
3617
0
      case StyleDisplay::TableCell:
3618
0
        if (!IsLastCellOfRow(f)) {
3619
0
          result.Append('\t');
3620
0
        }
3621
0
        break;
3622
0
      case StyleDisplay::TableRow:
3623
0
        if (!IsLastRowOfRowGroup(f) ||
3624
0
            !IsLastNonemptyRowGroupOfTable(f->GetParent())) {
3625
0
          result.Append('\n');
3626
0
        }
3627
0
        break;
3628
0
      default:
3629
0
        break; // Do nothing
3630
0
      }
3631
0
      result.AddRequiredLineBreakCount(GetRequiredInnerTextLineBreakCount(f));
3632
0
    }
3633
0
    nsIContent* next = currentNode->GetNextSibling();
3634
0
    if (next) {
3635
0
      currentNode = next;
3636
0
      currentState = AT_NODE;
3637
0
    } else {
3638
0
      currentNode = currentNode->GetParent();
3639
0
    }
3640
0
  }
3641
0
3642
0
  // Do not flush trailing line breaks! Required breaks at the end of the text
3643
0
  // are suppressed.
3644
0
}