Coverage Report

Created: 2018-09-25 14:53

/work/obj-fuzz/dist/include/mozilla/EditorDOMPoint.h
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* This Source Code Form is subject to the terms of the Mozilla Public
3
 * License, v. 2.0. If a copy of the MPL was not distributed with this
4
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6
7
#ifndef mozilla_EditorDOMPoint_h
8
#define mozilla_EditorDOMPoint_h
9
10
#include "mozilla/Assertions.h"
11
#include "mozilla/Attributes.h"
12
#include "mozilla/RangeBoundary.h"
13
#include "mozilla/dom/Element.h"
14
#include "mozilla/dom/Text.h"
15
#include "nsAtom.h"
16
#include "nsCOMPtr.h"
17
#include "nsGkAtoms.h"
18
#include "nsIContent.h"
19
#include "nsINode.h"
20
21
namespace mozilla {
22
23
template<typename ParentType, typename ChildType>
24
class EditorDOMPointBase;
25
26
/**
27
 * EditorDOMPoint and EditorRawDOMPoint are simple classes which refers
28
 * a point in the DOM tree at creating the instance or initializing the
29
 * instance with calling Set().
30
 *
31
 * EditorDOMPoint refers container node (and child node if it's already set)
32
 * with nsCOMPtr.  EditorRawDOMPoint refers them with raw pointer.
33
 * So, EditorRawDOMPoint is useful when you access the nodes only before
34
 * changing DOM tree since increasing refcount may appear in micro benchmark
35
 * if it's in a hot path.  On the other hand, if you need to refer them even
36
 * after changing DOM tree, you must use EditorDOMPoint.
37
 *
38
 * When initializing an instance only with child node or offset,  the instance
39
 * starts to refer the child node or offset in the container.  In this case,
40
 * the other information hasn't been initialized due to performance reason.
41
 * When you retrieve the other information with calling Offset() or
42
 * GetChild(), the other information is computed with the current DOM tree.
43
 * Therefore, e.g., in the following case, the other information may be
44
 * different:
45
 *
46
 * EditorDOMPoint pointA(container1, childNode1);
47
 * EditorDOMPoint pointB(container1, childNode1);
48
 * Unused << pointA.Offset(); // The offset is computed now.
49
 * container1->RemoveChild(childNode1->GetPreviousSibling());
50
 * Unused << pointB.Offset(); // Now, pointB.Offset() equals pointA.Offset() - 1
51
 *
52
 * similarly:
53
 *
54
 * EditorDOMPoint pointA(container1, 5);
55
 * EditorDOMPoint pointB(container1, 5);
56
 * Unused << pointA.GetChild(); // The child is computed now.
57
 * container1->RemoveChild(childNode1->GetFirstChild());
58
 * Unused << pointB.GetChild(); // Now, pointB.GetChild() equals
59
 *                              // pointA.GetChild()->GetPreviousSibling().
60
 *
61
 * So, when you initialize an instance only with one information, you need to
62
 * be careful when you access the other information after changing the DOM tree.
63
 * When you need to lock the child node or offset and recompute the other
64
 * information with new DOM tree, you can use
65
 * AutoEditorDOMPointOffsetInvalidator and AutoEditorDOMPointChildInvalidator.
66
 */
67
68
typedef EditorDOMPointBase<nsCOMPtr<nsINode>,
69
                           nsCOMPtr<nsIContent>> EditorDOMPoint;
70
typedef EditorDOMPointBase<nsINode*, nsIContent*> EditorRawDOMPoint;
71
72
template<typename ParentType, typename ChildType>
73
class EditorDOMPointBase final
74
{
75
  typedef EditorDOMPointBase<ParentType, ChildType> SelfType;
76
77
public:
78
  EditorDOMPointBase()
79
    : mParent(nullptr)
80
    , mChild(nullptr)
81
    , mIsChildInitialized(false)
82
0
  {
83
0
  }
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsINode*, nsIContent*>::EditorDOMPointBase()
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> >::EditorDOMPointBase()
84
85
  EditorDOMPointBase(nsINode* aContainer,
86
                     int32_t aOffset)
87
    : mParent(aContainer)
88
    , mChild(nullptr)
89
    , mOffset(mozilla::Some(aOffset))
90
    , mIsChildInitialized(false)
91
0
  {
92
0
    NS_WARNING_ASSERTION(!mParent || mOffset.value() <= mParent->Length(),
93
0
      "The offset is larger than the length of aContainer or negative");
94
0
    if (!mParent) {
95
0
      mOffset.reset();
96
0
    }
97
0
  }
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsINode*, nsIContent*>::EditorDOMPointBase(nsINode*, int)
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> >::EditorDOMPointBase(nsINode*, int)
98
99
  /**
100
   * Different from RangeBoundary, aPointedNode should be a child node
101
   * which you want to refer.
102
   */
103
  explicit EditorDOMPointBase(nsINode* aPointedNode)
104
    : mParent(aPointedNode && aPointedNode->IsContent() ?
105
                aPointedNode->GetParentNode() : nullptr)
106
    , mChild(aPointedNode && aPointedNode->IsContent() ?
107
                aPointedNode->AsContent() : nullptr)
108
    , mIsChildInitialized(false)
109
0
  {
110
0
    mIsChildInitialized = aPointedNode && mChild;
111
0
    NS_WARNING_ASSERTION(IsSet(),
112
0
      "The child is nullptr or doesn't have its parent");
113
0
    NS_WARNING_ASSERTION(mChild && mChild->GetParentNode() == mParent,
114
0
      "Initializing RangeBoundary with invalid value");
115
0
  }
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsINode*, nsIContent*>::EditorDOMPointBase(nsINode*)
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> >::EditorDOMPointBase(nsINode*)
116
117
  EditorDOMPointBase(nsINode* aContainer,
118
                     nsIContent* aPointedNode,
119
                     int32_t aOffset)
120
    : mParent(aContainer)
121
    , mChild(aPointedNode)
122
    , mOffset(mozilla::Some(aOffset))
123
    , mIsChildInitialized(true)
124
0
  {
125
0
    MOZ_RELEASE_ASSERT(aContainer,
126
0
      "This constructor shouldn't be used when pointing nowhere");
127
0
    MOZ_ASSERT(mOffset.value() <= mParent->Length());
128
0
    MOZ_ASSERT(mChild || mParent->Length() == mOffset.value());
129
0
    MOZ_ASSERT(!mChild || mParent == mChild->GetParentNode());
130
0
    MOZ_ASSERT(mParent->GetChildAt_Deprecated(mOffset.value()) == mChild);
131
0
  }
132
133
  template<typename PT, typename CT>
134
  explicit EditorDOMPointBase(const RangeBoundaryBase<PT, CT>& aOther)
135
    : mParent(aOther.mParent)
136
    , mChild(aOther.mRef ? aOther.mRef->GetNextSibling() :
137
                           (aOther.mParent ? aOther.mParent->GetFirstChild() :
138
                                             nullptr))
139
    , mOffset(aOther.mOffset)
140
    , mIsChildInitialized(aOther.mRef ||
141
                          (aOther.mOffset.isSome() && !aOther.mOffset.value()))
142
0
  {
143
0
  }
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsINode*, nsIContent*>::EditorDOMPointBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> >(mozilla::RangeBoundaryBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> > const&)
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> >::EditorDOMPointBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> >(mozilla::RangeBoundaryBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> > const&)
144
145
  template<typename PT, typename CT>
146
  MOZ_IMPLICIT EditorDOMPointBase(const EditorDOMPointBase<PT, CT>& aOther)
147
    : mParent(aOther.mParent)
148
    , mChild(aOther.mChild)
149
    , mOffset(aOther.mOffset)
150
    , mIsChildInitialized(aOther.mIsChildInitialized)
151
0
  {
152
0
  }
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> >::EditorDOMPointBase<nsINode*, nsIContent*>(mozilla::EditorDOMPointBase<nsINode*, nsIContent*> const&)
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsINode*, nsIContent*>::EditorDOMPointBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> >(mozilla::EditorDOMPointBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> > const&)
153
154
  /**
155
   * GetContainer() returns the container node at the point.
156
   * GetContainerAs*() returns the container node as specific type.
157
   */
158
  nsINode*
159
  GetContainer() const
160
0
  {
161
0
    return mParent;
162
0
  }
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> >::GetContainer() const
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsINode*, nsIContent*>::GetContainer() const
163
164
  nsIContent*
165
  GetContainerAsContent() const
166
0
  {
167
0
    return mParent && mParent->IsContent() ? mParent->AsContent() : nullptr;
168
0
  }
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> >::GetContainerAsContent() const
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsINode*, nsIContent*>::GetContainerAsContent() const
169
170
  dom::Element*
171
  GetContainerAsElement() const
172
0
  {
173
0
    return Element::FromNodeOrNull(mParent);
174
0
  }
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> >::GetContainerAsElement() const
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsINode*, nsIContent*>::GetContainerAsElement() const
175
176
  dom::Text*
177
  GetContainerAsText() const
178
0
  {
179
0
    return mParent ? mParent->GetAsText() : nullptr;
180
0
  }
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsINode*, nsIContent*>::GetContainerAsText() const
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> >::GetContainerAsText() const
181
182
  /**
183
   * CanContainerHaveChildren() returns true if the container node can have
184
   * child nodes.  Otherwise, e.g., when the container is a text node, returns
185
   * false.
186
   */
187
  bool
188
  CanContainerHaveChildren() const
189
0
  {
190
0
    return mParent && mParent->IsContainerNode();
191
0
  }
192
193
  /**
194
   * IsInDataNode() returns true if the container node is a data node including
195
   * text node.
196
   */
197
  bool
198
  IsInDataNode() const
199
0
  {
200
0
    return mParent && mParent->IsCharacterData();
201
0
  }
202
203
  /**
204
   * IsInTextNode() returns true if the container node is a text node.
205
   */
206
  bool
207
  IsInTextNode() const
208
0
  {
209
0
    return mParent && mParent->IsText();
210
0
  }
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsINode*, nsIContent*>::IsInTextNode() const
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> >::IsInTextNode() const
211
212
  /**
213
   * IsInNativeAnonymousSubtree() returns true if the container is in
214
   * native anonymous subtree.
215
   */
216
  bool
217
  IsInNativeAnonymousSubtree() const
218
0
  {
219
0
    return mParent && mParent->IsInNativeAnonymousSubtree();
220
0
  }
221
222
  /**
223
   * IsContainerHTMLElement() returns true if the container node is an HTML
224
   * element node and its node name is aTag.
225
   */
226
  bool
227
  IsContainerHTMLElement(nsAtom* aTag) const
228
0
  {
229
0
    return mParent && mParent->IsHTMLElement(aTag);
230
0
  }
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsINode*, nsIContent*>::IsContainerHTMLElement(nsAtom*) const
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> >::IsContainerHTMLElement(nsAtom*) const
231
232
  /**
233
   * IsContainerAnyOfHTMLElements() returns true if the container node is an
234
   * HTML element node and its node name is one of the arguments.
235
   */
236
  template<typename First, typename... Args>
237
  bool
238
  IsContainerAnyOfHTMLElements(First aFirst, Args... aArgs) const
239
  {
240
    return mParent && mParent->IsAnyOfHTMLElements(aFirst, aArgs...);
241
  }
242
243
  /**
244
   * GetChild() returns a child node which is pointed by the instance.
245
   * If mChild hasn't been initialized yet, this computes the child node
246
   * from mParent and mOffset with *current* DOM tree.
247
   */
248
  nsIContent*
249
  GetChild() const
250
0
  {
251
0
    if (!mParent || !mParent->IsContainerNode()) {
252
0
      return nullptr;
253
0
    }
254
0
    if (mIsChildInitialized) {
255
0
      return mChild;
256
0
    }
257
0
    // Fix child node now.
258
0
    const_cast<SelfType*>(this)->EnsureChild();
259
0
    return mChild;
260
0
  }
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> >::GetChild() const
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsINode*, nsIContent*>::GetChild() const
261
262
  /**
263
   * GetNextSiblingOfChild() returns next sibling of the child node.
264
   * If this refers after the last child or the container cannot have children,
265
   * this returns nullptr with warning.
266
   * If mChild hasn't been initialized yet, this computes the child node
267
   * from mParent and mOffset with *current* DOM tree.
268
   */
269
  nsIContent*
270
  GetNextSiblingOfChild() const
271
  {
272
    if (NS_WARN_IF(!mParent) || NS_WARN_IF(!mParent->IsContainerNode())) {
273
      return nullptr;
274
    }
275
    if (mIsChildInitialized) {
276
      return mChild ? mChild->GetNextSibling() : nullptr;
277
    }
278
    MOZ_ASSERT(mOffset.isSome());
279
    if (NS_WARN_IF(mOffset.value() > mParent->Length())) {
280
      // If this has been set only offset and now the offset is invalid,
281
      // let's just return nullptr.
282
      return nullptr;
283
    }
284
    // Fix child node now.
285
    const_cast<SelfType*>(this)->EnsureChild();
286
    return mChild ? mChild->GetNextSibling() : nullptr;
287
  }
288
289
  /**
290
   * GetPreviousSiblingOfChild() returns previous sibling of a child
291
   * at offset.  If this refers the first child or the container cannot have
292
   * children, this returns nullptr with warning.
293
   * If mChild hasn't been initialized yet, this computes the child node
294
   * from mParent and mOffset with *current* DOM tree.
295
   */
296
  nsIContent*
297
  GetPreviousSiblingOfChild() const
298
0
  {
299
0
    if (NS_WARN_IF(!mParent) || NS_WARN_IF(!mParent->IsContainerNode())) {
300
0
      return nullptr;
301
0
    }
302
0
    if (mIsChildInitialized) {
303
0
      return mChild ? mChild->GetPreviousSibling() : mParent->GetLastChild();
304
0
    }
305
0
    MOZ_ASSERT(mOffset.isSome());
306
0
    if (NS_WARN_IF(mOffset.value() > mParent->Length())) {
307
0
      // If this has been set only offset and now the offset is invalid,
308
0
      // let's just return nullptr.
309
0
      return nullptr;
310
0
    }
311
0
    // Fix child node now.
312
0
    const_cast<SelfType*>(this)->EnsureChild();
313
0
    return mChild ? mChild->GetPreviousSibling() : mParent->GetLastChild();
314
0
  }
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsINode*, nsIContent*>::GetPreviousSiblingOfChild() const
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> >::GetPreviousSiblingOfChild() const
315
316
  uint32_t
317
  Offset() const
318
0
  {
319
0
    if (mOffset.isSome()) {
320
0
      MOZ_ASSERT(mOffset.isSome());
321
0
      return mOffset.value();
322
0
    }
323
0
    if (!mParent) {
324
0
      MOZ_ASSERT(!mChild);
325
0
      return 0;
326
0
    }
327
0
    MOZ_ASSERT(mParent->IsContainerNode(),
328
0
      "If the container cannot have children, mOffset.isSome() should be true");
329
0
    if (!mChild) {
330
0
      // We're referring after the last child.  Fix offset now.
331
0
      const_cast<SelfType*>(this)->mOffset = mozilla::Some(mParent->Length());
332
0
      return mOffset.value();
333
0
    }
334
0
    MOZ_ASSERT(mChild->GetParentNode() == mParent);
335
0
    // Fix offset now.
336
0
    if (mChild == mParent->GetFirstChild()) {
337
0
      const_cast<SelfType*>(this)->mOffset = mozilla::Some(0);
338
0
    } else {
339
0
      const_cast<SelfType*>(this)->mOffset =
340
0
        mozilla::Some(mParent->ComputeIndexOf(mChild));
341
0
    }
342
0
    return mOffset.value();
343
0
  }
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> >::Offset() const
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsINode*, nsIContent*>::Offset() const
344
345
  /**
346
   * Set() sets a point to aOffset or aChild.
347
   * If it's set with aOffset, mChild is invalidated.  If it's set with aChild,
348
   * mOffset may be invalidated.
349
   */
350
  void
351
  Set(nsINode* aContainer, int32_t aOffset)
352
0
  {
353
0
    mParent = aContainer;
354
0
    mChild = nullptr;
355
0
    mOffset = mozilla::Some(aOffset);
356
0
    mIsChildInitialized = false;
357
0
    NS_ASSERTION(!mParent || mOffset.value() <= mParent->Length(),
358
0
      "The offset is out of bounds");
359
0
  }
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> >::Set(nsINode*, int)
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsINode*, nsIContent*>::Set(nsINode*, int)
360
  void
361
  Set(const nsINode* aChild)
362
0
  {
363
0
    MOZ_ASSERT(aChild);
364
0
    if (NS_WARN_IF(!aChild->IsContent())) {
365
0
      Clear();
366
0
      return;
367
0
    }
368
0
    mParent = aChild->GetParentNode();
369
0
    mChild = const_cast<nsIContent*>(aChild->AsContent());
370
0
    mOffset.reset();
371
0
    mIsChildInitialized = true;
372
0
  }
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> >::Set(nsINode const*)
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsINode*, nsIContent*>::Set(nsINode const*)
373
374
  /**
375
   * SetToEndOf() sets this to the end of aContainer.  Then, mChild is always
376
   * nullptr but marked as initialized and mOffset is always set.
377
   */
378
  void
379
  SetToEndOf(const nsINode* aContainer)
380
0
  {
381
0
    MOZ_ASSERT(aContainer);
382
0
    mParent = const_cast<nsINode*>(aContainer);
383
0
    mChild = nullptr;
384
0
    mOffset = mozilla::Some(mParent->Length());
385
0
    mIsChildInitialized = true;
386
0
  }
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> >::SetToEndOf(nsINode const*)
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsINode*, nsIContent*>::SetToEndOf(nsINode const*)
387
388
  /**
389
   * SetAfter() sets mChild to next sibling of aChild.
390
   */
391
  void
392
  SetAfter(const nsINode* aChild)
393
0
  {
394
0
    MOZ_ASSERT(aChild);
395
0
    nsIContent* nextSibling = aChild->GetNextSibling();
396
0
    if (nextSibling) {
397
0
      Set(nextSibling);
398
0
      return;
399
0
    }
400
0
    nsINode* parentNode = aChild->GetParentNode();
401
0
    if (NS_WARN_IF(!parentNode)) {
402
0
      Clear();
403
0
      return;
404
0
    }
405
0
    SetToEndOf(parentNode);
406
0
  }
407
408
  /**
409
   * Clear() makes the instance not point anywhere.
410
   */
411
  void
412
  Clear()
413
0
  {
414
0
    mParent = nullptr;
415
0
    mChild = nullptr;
416
0
    mOffset.reset();
417
0
    mIsChildInitialized = false;
418
0
  }
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> >::Clear()
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsINode*, nsIContent*>::Clear()
419
420
  /**
421
   * AdvanceOffset() tries to refer next sibling of mChild and/of next offset.
422
   * If the container can have children and there is no next sibling or the
423
   * offset reached the length of the container, this outputs warning and does
424
   * nothing.  So, callers need to check if there is next sibling which you
425
   * need to refer.
426
   *
427
   * @return            true if there is a next DOM point to refer.
428
   */
429
  bool
430
  AdvanceOffset()
431
0
  {
432
0
    if (NS_WARN_IF(!mParent)) {
433
0
      return false;
434
0
    }
435
0
    // If only mOffset is available, just compute the offset.
436
0
    if ((mOffset.isSome() && !mIsChildInitialized) ||
437
0
        !mParent->IsContainerNode()) {
438
0
      MOZ_ASSERT(mOffset.isSome());
439
0
      MOZ_ASSERT(!mChild);
440
0
      if (NS_WARN_IF(mOffset.value() >= mParent->Length())) {
441
0
        // We're already referring the start of the container.
442
0
        return false;
443
0
      }
444
0
      mOffset = mozilla::Some(mOffset.value() + 1);
445
0
      return true;
446
0
    }
447
0
448
0
    MOZ_ASSERT(mIsChildInitialized);
449
0
    MOZ_ASSERT(!mOffset.isSome() || mOffset.isSome());
450
0
    if (NS_WARN_IF(!mParent->HasChildren()) ||
451
0
        NS_WARN_IF(!mChild) ||
452
0
        NS_WARN_IF(mOffset.isSome() && mOffset.value() >= mParent->Length())) {
453
0
      // We're already referring the end of the container (or outside).
454
0
      return false;
455
0
    }
456
0
457
0
    if (mOffset.isSome()) {
458
0
      MOZ_ASSERT(mOffset.isSome());
459
0
      mOffset = mozilla::Some(mOffset.value() + 1);
460
0
    }
461
0
    mChild = mChild->GetNextSibling();
462
0
    return true;
463
0
  }
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsINode*, nsIContent*>::AdvanceOffset()
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> >::AdvanceOffset()
464
465
  /**
466
   * RewindOffset() tries to refer previous sibling of mChild and/or previous
467
   * offset.  If the container can have children and there is no next previous
468
   * or the offset is 0, this outputs warning and does nothing.  So, callers
469
   * need to check if there is previous sibling which you need to refer.
470
   *
471
   * @return            true if there is a previous DOM point to refer.
472
   */
473
  bool
474
  RewindOffset()
475
0
  {
476
0
    if (NS_WARN_IF(!mParent)) {
477
0
      return false;
478
0
    }
479
0
    // If only mOffset is available, just compute the offset.
480
0
    if ((mOffset.isSome() && !mIsChildInitialized) ||
481
0
        !mParent->IsContainerNode()) {
482
0
      MOZ_ASSERT(mOffset.isSome());
483
0
      MOZ_ASSERT(!mChild);
484
0
      if (NS_WARN_IF(!mOffset.value()) ||
485
0
          NS_WARN_IF(mOffset.value() > mParent->Length())) {
486
0
        // We're already referring the start of the container or
487
0
        // the offset is invalid since perhaps, the offset was set before
488
0
        // the last DOM tree change.
489
0
        return false;
490
0
      }
491
0
      mOffset = mozilla::Some(mOffset.value() - 1);
492
0
      return true;
493
0
    }
494
0
495
0
    MOZ_ASSERT(mIsChildInitialized);
496
0
    MOZ_ASSERT(!mOffset.isSome() || mOffset.isSome());
497
0
    if (NS_WARN_IF(!mParent->HasChildren()) ||
498
0
        NS_WARN_IF(mChild && !mChild->GetPreviousSibling()) ||
499
0
        NS_WARN_IF(mOffset.isSome() && !mOffset.value())) {
500
0
      // We're already referring the start of the container (or the child has
501
0
      // been moved from the container?).
502
0
      return false;
503
0
    }
504
0
505
0
    nsIContent* previousSibling =
506
0
        mChild ? mChild->GetPreviousSibling() : mParent->GetLastChild();
507
0
    if (NS_WARN_IF(!previousSibling)) {
508
0
      // We're already referring the first child of the container.
509
0
      return false;
510
0
    }
511
0
512
0
    if (mOffset.isSome()) {
513
0
      mOffset = mozilla::Some(mOffset.value() - 1);
514
0
    }
515
0
    mChild = previousSibling;
516
0
    return true;
517
0
  }
518
519
  /**
520
   * GetNonAnonymousSubtreePoint() returns a DOM point which is NOT in
521
   * native-anonymous subtree.  If the instance isn't in native-anonymous
522
   * subtree, this returns same point.  Otherwise, climbs up until finding
523
   * non-native-anonymous parent and returns the point of it.  I.e.,
524
   * container is parent of the found non-anonymous-native node.
525
   */
526
  EditorRawDOMPoint
527
  GetNonAnonymousSubtreePoint() const
528
0
  {
529
0
    if (NS_WARN_IF(!IsSet())) {
530
0
      return EditorRawDOMPoint();
531
0
    }
532
0
    if (!IsInNativeAnonymousSubtree()) {
533
0
      return EditorRawDOMPoint(*this);
534
0
    }
535
0
    nsINode* parent;
536
0
    for (parent = mParent->GetParentNode();
537
0
         parent && parent->IsInNativeAnonymousSubtree();
538
0
         parent = mParent->GetParentNode()) {
539
0
    }
540
0
    if (!parent) {
541
0
      return EditorRawDOMPoint();
542
0
    }
543
0
    return EditorRawDOMPoint(parent);
544
0
  }
545
546
  bool
547
  IsSet() const
548
0
  {
549
0
    return mParent && (mIsChildInitialized || mOffset.isSome());
550
0
  }
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> >::IsSet() const
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsINode*, nsIContent*>::IsSet() const
551
552
  bool
553
  IsSetAndValid() const
554
0
  {
555
0
    if (!IsSet()) {
556
0
      return false;
557
0
    }
558
0
559
0
    if (mChild && mChild->GetParentNode() != mParent) {
560
0
      return false;
561
0
    }
562
0
    if (mOffset.isSome() && mOffset.value() > mParent->Length()) {
563
0
      return false;
564
0
    }
565
0
    return true;
566
0
  }
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsINode*, nsIContent*>::IsSetAndValid() const
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> >::IsSetAndValid() const
567
568
  bool
569
  IsStartOfContainer() const
570
0
  {
571
0
    // If we're referring the first point in the container:
572
0
    //   If mParent is not a container like a text node, mOffset is 0.
573
0
    //   If mChild is initialized and it's first child of mParent.
574
0
    //   If mChild isn't initialized and the offset is 0.
575
0
    if (NS_WARN_IF(!mParent)) {
576
0
      return false;
577
0
    }
578
0
    if (!mParent->IsContainerNode()) {
579
0
      return !mOffset.value();
580
0
    }
581
0
    if (mIsChildInitialized) {
582
0
      if (mParent->GetFirstChild() == mChild) {
583
0
        NS_WARNING_ASSERTION(!mOffset.isSome() || !mOffset.value(),
584
0
          "If mOffset was initialized, it should be 0");
585
0
        return true;
586
0
      }
587
0
      NS_WARNING_ASSERTION(!mOffset.isSome() ||
588
0
                           mParent->GetChildAt_Deprecated(mOffset.value()) == mChild,
589
0
        "If mOffset and mChild are mismatched");
590
0
      return false;
591
0
    }
592
0
    MOZ_ASSERT(mOffset.isSome());
593
0
    return !mOffset.value();
594
0
  }
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsINode*, nsIContent*>::IsStartOfContainer() const
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> >::IsStartOfContainer() const
595
596
  bool
597
  IsEndOfContainer() const
598
0
  {
599
0
    // If we're referring after the last point of the container:
600
0
    //   If mParent is not a container like text node, mOffset is same as the
601
0
    //   length of the container.
602
0
    //   If mChild is initialized and it's nullptr.
603
0
    //   If mChild isn't initialized and mOffset is same as the length of the
604
0
    //   container.
605
0
    if (NS_WARN_IF(!mParent)) {
606
0
      return false;
607
0
    }
608
0
    if (!mParent->IsContainerNode()) {
609
0
      return mOffset.value() == mParent->Length();
610
0
    }
611
0
    if (mIsChildInitialized) {
612
0
      if (!mChild) {
613
0
        NS_WARNING_ASSERTION(!mOffset.isSome() ||
614
0
                             mOffset.value() == mParent->Length(),
615
0
          "If mOffset was initialized, it should be length of the container");
616
0
        return true;
617
0
      }
618
0
      NS_WARNING_ASSERTION(!mOffset.isSome() ||
619
0
                           mParent->GetChildAt_Deprecated(mOffset.value()) == mChild,
620
0
        "If mOffset and mChild are mismatched");
621
0
      return false;
622
0
    }
623
0
    MOZ_ASSERT(mOffset.isSome());
624
0
    return mOffset.value() == mParent->Length();
625
0
  }
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> >::IsEndOfContainer() const
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsINode*, nsIContent*>::IsEndOfContainer() const
626
627
  bool
628
  IsBRElementAtEndOfContainer() const
629
0
  {
630
0
    if (NS_WARN_IF(!mParent)) {
631
0
      return false;
632
0
    }
633
0
    if (!mParent->IsContainerNode()) {
634
0
      return false;
635
0
    }
636
0
    const_cast<SelfType*>(this)->EnsureChild();
637
0
    if (!mChild ||
638
0
        mChild->GetNextSibling()) {
639
0
      return false;
640
0
    }
641
0
    return mChild->IsHTMLElement(nsGkAtoms::br);
642
0
  }
643
644
  template<typename A, typename B>
645
  EditorDOMPointBase& operator=(const RangeBoundaryBase<A,B>& aOther)
646
0
  {
647
0
    mParent = aOther.mParent;
648
0
    mChild =
649
0
      aOther.mRef ? aOther.mRef->GetNextSibling() :
650
0
                    (aOther.mParent && aOther.mParent->IsContainerNode() ?
651
0
                       aOther.mParent->GetFirstChild() : nullptr);
652
0
    mOffset = aOther.mOffset;
653
0
    mIsChildInitialized =
654
0
      aOther.mRef ||
655
0
      (aOther.mParent && !aOther.mParent->IsContainerNode()) ||
656
0
      (aOther.mOffset.isSome() && !aOther.mOffset.value());
657
0
    return *this;
658
0
  }
659
660
  template<typename A, typename B>
661
  EditorDOMPointBase& operator=(const EditorDOMPointBase<A, B>& aOther)
662
0
  {
663
0
    mParent = aOther.mParent;
664
0
    mChild = aOther.mChild;
665
0
    mOffset = aOther.mOffset;
666
0
    mIsChildInitialized = aOther.mIsChildInitialized;
667
0
    return *this;
668
0
  }
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> >& mozilla::EditorDOMPointBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> >::operator=<nsINode*, nsIContent*>(mozilla::EditorDOMPointBase<nsINode*, nsIContent*> const&)
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsINode*, nsIContent*>& mozilla::EditorDOMPointBase<nsINode*, nsIContent*>::operator=<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> >(mozilla::EditorDOMPointBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> > const&)
669
670
  template<typename A, typename B>
671
  bool operator==(const EditorDOMPointBase<A, B>& aOther) const
672
0
  {
673
0
    if (mParent != aOther.mParent) {
674
0
      return false;
675
0
    }
676
0
677
0
    if (mOffset.isSome() && aOther.mOffset.isSome()) {
678
0
      // If both mOffset are set, we need to compare both mRef too because
679
0
      // the relation of mRef and mOffset have already broken by DOM tree
680
0
      // changes.
681
0
      if (mOffset != aOther.mOffset) {
682
0
        return false;
683
0
      }
684
0
      if (mChild == aOther.mChild) {
685
0
        return true;
686
0
      }
687
0
      if (NS_WARN_IF(mIsChildInitialized && aOther.mIsChildInitialized)) {
688
0
        // In this case, relation between mChild and mOffset of one of or both
689
0
        // of them doesn't match with current DOM tree since the DOM tree might
690
0
        // have been changed after computing mChild or mOffset.
691
0
        return false;
692
0
      }
693
0
      // If one of mChild hasn't been computed yet, we should compare them only
694
0
      // with mOffset.  Perhaps, we shouldn't copy mChild from non-nullptr one
695
0
      // to the other since if we copy it here, it may be unexpected behavior
696
0
      // for some callers.
697
0
      return true;
698
0
    }
699
0
700
0
    MOZ_ASSERT(mIsChildInitialized || aOther.mIsChildInitialized);
701
0
702
0
    if (mOffset.isSome() && !mIsChildInitialized &&
703
0
        !aOther.mOffset.isSome() && aOther.mIsChildInitialized) {
704
0
      // If this has only mOffset and the other has only mChild, this needs to
705
0
      // compute mChild now.
706
0
      const_cast<SelfType*>(this)->EnsureChild();
707
0
      return mChild == aOther.mChild;
708
0
    }
709
0
710
0
    if (!mOffset.isSome() && mIsChildInitialized &&
711
0
        aOther.mOffset.isSome() && !aOther.mIsChildInitialized) {
712
0
      // If this has only mChild and the other has only mOffset, the other needs
713
0
      // to compute mChild now.
714
0
      const_cast<EditorDOMPointBase<A, B>&>(aOther).EnsureChild();
715
0
      return mChild == aOther.mChild;
716
0
    }
717
0
718
0
    // If mOffset of one of them hasn't been computed from mChild yet, we should
719
0
    // compare only with mChild.  Perhaps, we shouldn't copy mOffset from being
720
0
    // some one to not being some one since if we copy it here, it may be
721
0
    // unexpected behavior for some callers.
722
0
    return mChild == aOther.mChild;
723
0
  }
Unexecuted instantiation: bool mozilla::EditorDOMPointBase<nsINode*, nsIContent*>::operator==<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> >(mozilla::EditorDOMPointBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> > const&) const
Unexecuted instantiation: bool mozilla::EditorDOMPointBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> >::operator==<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> >(mozilla::EditorDOMPointBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> > const&) const
Unexecuted instantiation: bool mozilla::EditorDOMPointBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> >::operator==<nsINode*, nsIContent*>(mozilla::EditorDOMPointBase<nsINode*, nsIContent*> const&) const
Unexecuted instantiation: bool mozilla::EditorDOMPointBase<nsINode*, nsIContent*>::operator==<nsINode*, nsIContent*>(mozilla::EditorDOMPointBase<nsINode*, nsIContent*> const&) const
724
725
  template<typename A, typename B>
726
  bool operator!=(const EditorDOMPointBase<A, B>& aOther) const
727
0
  {
728
0
    return !(*this == aOther);
729
0
  }
Unexecuted instantiation: bool mozilla::EditorDOMPointBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> >::operator!=<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> >(mozilla::EditorDOMPointBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> > const&) const
Unexecuted instantiation: bool mozilla::EditorDOMPointBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> >::operator!=<nsINode*, nsIContent*>(mozilla::EditorDOMPointBase<nsINode*, nsIContent*> const&) const
730
731
  /**
732
   * This operator should be used if API of other modules take RawRangeBoundary,
733
   * e.g., methods of Selection and nsRange.
734
   */
735
  operator const RawRangeBoundary() const
736
0
  {
737
0
    if (!IsSet() ||
738
0
        NS_WARN_IF(!mIsChildInitialized && !mOffset.isSome())) {
739
0
      return RawRangeBoundary();
740
0
    }
741
0
    if (!mParent->IsContainerNode()) {
742
0
      // If the container is a data node like a text node, we need to create
743
0
      // RangeBoundaryBase instance only with mOffset because mChild is always
744
0
      // nullptr.
745
0
      return RawRangeBoundary(mParent, mOffset.value());
746
0
    }
747
0
    if (mIsChildInitialized && mOffset.isSome()) {
748
0
      // If we've already set both child and offset, we should create
749
0
      // RangeBoundary with offset after validation.
750
#ifdef DEBUG
751
      if (mChild) {
752
        MOZ_ASSERT(mParent == mChild->GetParentNode());
753
        MOZ_ASSERT(mParent->GetChildAt_Deprecated(mOffset.value()) == mChild);
754
      } else {
755
        MOZ_ASSERT(mParent->Length() == mOffset.value());
756
      }
757
#endif // #ifdef DEBUG
758
      return RawRangeBoundary(mParent,  mOffset.value());
759
0
    }
760
0
    // Otherwise, we should create RangeBoundaryBase only with available
761
0
    // information.
762
0
    if (mOffset.isSome()) {
763
0
      return RawRangeBoundary(mParent, mOffset.value());
764
0
    }
765
0
    if (mChild) {
766
0
      return RawRangeBoundary(mParent, mChild->GetPreviousSibling());
767
0
    }
768
0
    return RawRangeBoundary(mParent, mParent->GetLastChild());
769
0
  }
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsINode*, nsIContent*>::operator mozilla::RangeBoundaryBase<nsINode*, nsIContent*> const() const
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> >::operator mozilla::RangeBoundaryBase<nsINode*, nsIContent*> const() const
770
771
private:
772
  void
773
  EnsureChild()
774
0
  {
775
0
    if (mIsChildInitialized) {
776
0
      return;
777
0
    }
778
0
    if (!mParent) {
779
0
      MOZ_ASSERT(!mOffset.isSome());
780
0
      return;
781
0
    }
782
0
    MOZ_ASSERT(mOffset.isSome());
783
0
    MOZ_ASSERT(mOffset.value() <= mParent->Length());
784
0
    mIsChildInitialized = true;
785
0
    if (!mParent->IsContainerNode()) {
786
0
      return;
787
0
    }
788
0
    mChild = mParent->GetChildAt_Deprecated(mOffset.value());
789
0
    MOZ_ASSERT(mChild || mOffset.value() == mParent->Length());
790
0
  }
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent> >::EnsureChild()
Unexecuted instantiation: mozilla::EditorDOMPointBase<nsINode*, nsIContent*>::EnsureChild()
791
792
  ParentType mParent;
793
  ChildType mChild;
794
795
  mozilla::Maybe<uint32_t> mOffset;
796
797
  bool mIsChildInitialized;
798
799
  template<typename PT, typename CT>
800
  friend class EditorDOMPointBase;
801
802
  friend void ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback&,
803
                                          EditorDOMPoint&, const char*,
804
                                          uint32_t);
805
  friend void ImplCycleCollectionUnlink(EditorDOMPoint&);
806
};
807
808
inline void
809
ImplCycleCollectionUnlink(EditorDOMPoint& aField)
810
0
{
811
0
  ImplCycleCollectionUnlink(aField.mParent);
812
0
  ImplCycleCollectionUnlink(aField.mChild);
813
0
}
814
815
inline void
816
ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
817
                            EditorDOMPoint& aField,
818
                            const char* aName,
819
                            uint32_t aFlags)
820
0
{
821
0
  ImplCycleCollectionTraverse(aCallback, aField.mParent, "mParent", 0);
822
0
  ImplCycleCollectionTraverse(aCallback, aField.mChild, "mChild", 0);
823
0
}
824
825
/**
826
 * AutoEditorDOMPointOffsetInvalidator is useful if DOM tree will be changed
827
 * when EditorDOMPoint instance is available and keeps referring same child
828
 * node.
829
 *
830
 * This class automatically guarantees that given EditorDOMPoint instance
831
 * stores the child node and invalidates its offset when the instance is
832
 * destroyed.  Additionally, users of this class can invalidate the offset
833
 * manually when they need.
834
 */
835
class MOZ_STACK_CLASS AutoEditorDOMPointOffsetInvalidator final
836
{
837
public:
838
  explicit AutoEditorDOMPointOffsetInvalidator(EditorDOMPoint& aPoint)
839
    : mPoint(aPoint)
840
    , mCanceled(false)
841
0
  {
842
0
    MOZ_ASSERT(aPoint.IsSetAndValid());
843
0
    MOZ_ASSERT(mPoint.CanContainerHaveChildren());
844
0
    mChild = mPoint.GetChild();
845
0
  }
846
847
  ~AutoEditorDOMPointOffsetInvalidator()
848
0
  {
849
0
    if (!mCanceled) {
850
0
      InvalidateOffset();
851
0
    }
852
0
  }
853
854
  /**
855
   * Manually, invalidate offset of the given point.
856
   */
857
  void InvalidateOffset()
858
0
  {
859
0
    if (mChild) {
860
0
      mPoint.Set(mChild);
861
0
    } else {
862
0
      // If the point referred after the last child, let's keep referring
863
0
      // after current last node of the old container.
864
0
      mPoint.SetToEndOf(mPoint.GetContainer());
865
0
    }
866
0
  }
867
868
  /**
869
   * After calling Cancel(), mPoint won't be modified by the destructor.
870
   */
871
  void Cancel()
872
0
  {
873
0
    mCanceled = true;
874
0
  }
875
876
private:
877
  EditorDOMPoint& mPoint;
878
  // Needs to store child node by ourselves because EditorDOMPoint stores
879
  // child node with mRef which is previous sibling of current child node.
880
  // Therefore, we cannot keep referring it if it's first child.
881
  nsCOMPtr<nsIContent> mChild;
882
883
  bool mCanceled;
884
885
  AutoEditorDOMPointOffsetInvalidator() = delete;
886
  AutoEditorDOMPointOffsetInvalidator(
887
    const AutoEditorDOMPointOffsetInvalidator& aOther) = delete;
888
  const AutoEditorDOMPointOffsetInvalidator& operator=(
889
    const AutoEditorDOMPointOffsetInvalidator& aOther) = delete;
890
};
891
892
/**
893
 * AutoEditorDOMPointChildInvalidator is useful if DOM tree will be changed
894
 * when EditorDOMPoint instance is available and keeps referring same container
895
 * and offset in it.
896
 *
897
 * This class automatically guarantees that given EditorDOMPoint instance
898
 * stores offset and invalidates its child node when the instance is destroyed.
899
 * Additionally, users of this class can invalidate the child manually when
900
 * they need.
901
 */
902
class MOZ_STACK_CLASS AutoEditorDOMPointChildInvalidator final
903
{
904
public:
905
  explicit AutoEditorDOMPointChildInvalidator(EditorDOMPoint& aPoint)
906
    : mPoint(aPoint)
907
    , mCanceled(false)
908
0
  {
909
0
    MOZ_ASSERT(aPoint.IsSetAndValid());
910
0
    Unused << mPoint.Offset();
911
0
  }
912
913
  ~AutoEditorDOMPointChildInvalidator()
914
0
  {
915
0
    if (!mCanceled) {
916
0
      InvalidateChild();
917
0
    }
918
0
  }
919
920
  /**
921
   * Manually, invalidate child of the given point.
922
   */
923
  void InvalidateChild()
924
0
  {
925
0
    mPoint.Set(mPoint.GetContainer(), mPoint.Offset());
926
0
  }
927
928
  /**
929
   * After calling Cancel(), mPoint won't be modified by the destructor.
930
   */
931
  void Cancel()
932
0
  {
933
0
    mCanceled = true;
934
0
  }
935
936
private:
937
  EditorDOMPoint& mPoint;
938
939
  bool mCanceled;
940
941
  AutoEditorDOMPointChildInvalidator() = delete;
942
  AutoEditorDOMPointChildInvalidator(
943
    const AutoEditorDOMPointChildInvalidator& aOther) = delete;
944
  const AutoEditorDOMPointChildInvalidator& operator=(
945
    const AutoEditorDOMPointChildInvalidator& aOther) = delete;
946
};
947
948
} // namespace mozilla
949
950
#endif // #ifndef mozilla_EditorDOMPoint_h