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 nsIDOMRange object.
9  */
10 
11 #ifndef nsRange_h___
12 #define nsRange_h___
13 
14 #include "nsIDOMRange.h"
15 #include "nsCOMPtr.h"
16 #include "nsINode.h"
17 #include "nsIDocument.h"
18 #include "nsIDOMNode.h"
19 #include "nsLayoutUtils.h"
20 #include "prmon.h"
21 #include "nsStubMutationObserver.h"
22 #include "nsWrapperCache.h"
23 #include "mozilla/Attributes.h"
24 #include "mozilla/GuardObjects.h"
25 #include "mozilla/LinkedList.h"
26 #include "mozilla/RangeBoundary.h"
27 
28 namespace mozilla {
29 class ErrorResult;
30 namespace dom {
31 struct ClientRectsAndTexts;
32 class DocGroup;
33 class DocumentFragment;
34 class DOMRect;
35 class DOMRectList;
36 class InspectorFontFace;
37 class Selection;
38 }  // namespace dom
39 }  // namespace mozilla
40 
41 class nsRange final : public nsIDOMRange,
42                       public nsStubMutationObserver,
43                       public nsWrapperCache,
44                       // For linking together selection-associated ranges.
45                       public mozilla::LinkedListElement<nsRange> {
46   typedef mozilla::ErrorResult ErrorResult;
47   typedef mozilla::dom::DocGroup DocGroup;
48   typedef mozilla::dom::DOMRect DOMRect;
49   typedef mozilla::dom::DOMRectList DOMRectList;
50   typedef mozilla::RangeBoundary RangeBoundary;
51   typedef mozilla::RawRangeBoundary RawRangeBoundary;
52 
53   virtual ~nsRange();
54 
55  public:
56   explicit nsRange(nsINode* aNode);
57 
58   static nsresult CreateRange(nsIDOMNode* aStartContainer,
59                               uint32_t aStartOffset, nsIDOMNode* aEndContainer,
60                               uint32_t aEndOffset, nsRange** aRange);
61   static nsresult CreateRange(nsIDOMNode* aStartContainer,
62                               uint32_t aStartOffset, nsIDOMNode* aEndContainer,
63                               uint32_t aEndOffset, nsIDOMRange** aRange);
64   static nsresult CreateRange(nsINode* aStartContainer, uint32_t aStartOffset,
65                               nsINode* aEndContainer, uint32_t aEndOffset,
66                               nsRange** aRange);
67   static nsresult CreateRange(const RawRangeBoundary& aStart,
68                               const RawRangeBoundary& aEnd, nsRange** aRange);
69 
70   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsRange,nsIDOMRange)71   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsRange, nsIDOMRange)
72 
73   nsrefcnt GetRefCount() const { return mRefCnt; }
74 
75   // nsIDOMRange interface
76   NS_DECL_NSIDOMRANGE
77 
GetRoot()78   nsINode* GetRoot() const { return mRoot; }
79 
StartRef()80   const RangeBoundary& StartRef() const { return mStart; }
81 
GetStartContainer()82   nsINode* GetStartContainer() const { return mStart.Container(); }
83 
EndRef()84   const RangeBoundary& EndRef() const { return mEnd; }
85 
GetEndContainer()86   nsINode* GetEndContainer() const { return mEnd.Container(); }
87 
StartOffset()88   uint32_t StartOffset() const {
89     return static_cast<uint32_t>(mStart.Offset());
90   }
91 
EndOffset()92   uint32_t EndOffset() const { return static_cast<uint32_t>(mEnd.Offset()); }
93 
GetChildAtStartOffset()94   nsIContent* GetChildAtStartOffset() const {
95     return mStart.GetChildAtOffset();
96   }
97 
GetChildAtEndOffset()98   nsIContent* GetChildAtEndOffset() const { return mEnd.GetChildAtOffset(); }
99 
IsPositioned()100   bool IsPositioned() const { return mIsPositioned; }
101 
SetMaySpanAnonymousSubtrees(bool aMaySpanAnonymousSubtrees)102   void SetMaySpanAnonymousSubtrees(bool aMaySpanAnonymousSubtrees) {
103     mMaySpanAnonymousSubtrees = aMaySpanAnonymousSubtrees;
104   }
105 
106   /**
107    * Return true iff this range is part of a Selection object
108    * and isn't detached.
109    */
IsInSelection()110   bool IsInSelection() const { return !!mSelection; }
111 
112   /**
113    * Called when the range is added/removed from a Selection.
114    */
115   void SetSelection(mozilla::dom::Selection* aSelection);
116 
117   /**
118    * Return true if this range was generated.
119    * @see SetIsGenerated
120    */
IsGenerated()121   bool IsGenerated() const { return mIsGenerated; }
122 
123   /**
124    * Mark this range as being generated or not.
125    * Currently it is used for marking ranges that are created when splitting up
126    * a range to exclude a -moz-user-select:none region.
127    * @see Selection::AddItem
128    * @see ExcludeNonSelectableNodes
129    */
SetIsGenerated(bool aIsGenerated)130   void SetIsGenerated(bool aIsGenerated) { mIsGenerated = aIsGenerated; }
131 
132   nsINode* GetCommonAncestor() const;
133   void Reset();
134 
135   /**
136    * SetStart() and SetEnd() sets start point or end point separately.
137    * However, this is expensive especially when it's a range of Selection.
138    * When you set both start and end of a range, you should use
139    * SetStartAndEnd() instead.
140    */
SetStart(nsINode * aContainer,uint32_t aOffset)141   nsresult SetStart(nsINode* aContainer, uint32_t aOffset) {
142     ErrorResult error;
143     SetStart(RawRangeBoundary(aContainer, aOffset), error);
144     return error.StealNSResult();
145   }
SetEnd(nsINode * aContainer,uint32_t aOffset)146   nsresult SetEnd(nsINode* aContainer, uint32_t aOffset) {
147     ErrorResult error;
148     SetEnd(RawRangeBoundary(aContainer, aOffset), error);
149     return error.StealNSResult();
150   }
151 
152   already_AddRefed<nsRange> CloneRange() const;
153 
154   /**
155    * SetStartAndEnd() works similar to call both SetStart() and SetEnd().
156    * Different from calls them separately, this does nothing if either
157    * the start point or the end point is invalid point.
158    * If the specified start point is after the end point, the range will be
159    * collapsed at the end point.  Similarly, if they are in different root,
160    * the range will be collapsed at the end point.
161    */
SetStartAndEnd(nsINode * aStartContainer,uint32_t aStartOffset,nsINode * aEndContainer,uint32_t aEndOffset)162   nsresult SetStartAndEnd(nsINode* aStartContainer, uint32_t aStartOffset,
163                           nsINode* aEndContainer, uint32_t aEndOffset) {
164     return SetStartAndEnd(RawRangeBoundary(aStartContainer, aStartOffset),
165                           RawRangeBoundary(aEndContainer, aEndOffset));
166   }
167   nsresult SetStartAndEnd(const RawRangeBoundary& aStart,
168                           const RawRangeBoundary& aEnd);
169 
170   /**
171    * Adds all nodes between |aStartContent| and |aEndContent| to the range.
172    * The start offset will be set before |aStartContent|,
173    * while the end offset will be set immediately after |aEndContent|.
174    *
175    * Caller must guarantee both nodes are non null and
176    * children of |aContainer| and that |aEndContent| is after |aStartContent|.
177    */
178   void SelectNodesInContainer(nsINode* aContainer, nsIContent* aStartContent,
179                               nsIContent* aEndContent);
180 
181   /**
182    * CollapseTo() works similar to call both SetStart() and SetEnd() with
183    * same node and offset.  This just calls SetStartAndParent() to set
184    * collapsed range at aContainer and aOffset.
185    */
CollapseTo(nsINode * aContainer,uint32_t aOffset)186   nsresult CollapseTo(nsINode* aContainer, uint32_t aOffset) {
187     return CollapseTo(RawRangeBoundary(aContainer, aOffset));
188   }
CollapseTo(const RawRangeBoundary & aPoint)189   nsresult CollapseTo(const RawRangeBoundary& aPoint) {
190     return SetStartAndEnd(aPoint, aPoint);
191   }
192 
193   /**
194    * Retrieves node and offset for setting start or end of a range to
195    * before or after aNode.
196    */
GetContainerAndOffsetAfter(nsINode * aNode,uint32_t * aOffset)197   static nsINode* GetContainerAndOffsetAfter(nsINode* aNode,
198                                              uint32_t* aOffset) {
199     MOZ_ASSERT(aNode);
200     MOZ_ASSERT(aOffset);
201     *aOffset = 0;
202     nsINode* parentNode = aNode->GetParentNode();
203     if (!parentNode) {
204       return nullptr;
205     }
206     int32_t indexInParent = parentNode->ComputeIndexOf(aNode);
207     if (NS_WARN_IF(indexInParent < 0)) {
208       return nullptr;
209     }
210     *aOffset = static_cast<uint32_t>(indexInParent) + 1;
211     return parentNode;
212   }
GetContainerAndOffsetBefore(nsINode * aNode,uint32_t * aOffset)213   static nsINode* GetContainerAndOffsetBefore(nsINode* aNode,
214                                               uint32_t* aOffset) {
215     MOZ_ASSERT(aNode);
216     MOZ_ASSERT(aOffset);
217     *aOffset = 0;
218     nsINode* parentNode = aNode->GetParentNode();
219     if (!parentNode) {
220       return nullptr;
221     }
222     int32_t indexInParent = parentNode->ComputeIndexOf(aNode);
223     if (NS_WARN_IF(indexInParent < 0)) {
224       return nullptr;
225     }
226     *aOffset = static_cast<uint32_t>(indexInParent);
227     return parentNode;
228   }
229 
230   // aMaxRanges is the maximum number of text ranges to record for each face
231   // (pass 0 to just get the list of faces, without recording exact ranges
232   // where each face was used).
233   nsresult GetUsedFontFaces(
234       nsTArray<nsAutoPtr<mozilla::dom::InspectorFontFace>>& aResult,
235       uint32_t aMaxRanges);
236 
237   // nsIMutationObserver methods
238   NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED
239   NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
240   NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
241   NS_DECL_NSIMUTATIONOBSERVER_PARENTCHAINCHANGED
242   NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
243 
244   // WebIDL
245   static already_AddRefed<nsRange> Constructor(
246       const mozilla::dom::GlobalObject& global, mozilla::ErrorResult& aRv);
247 
Collapsed()248   bool Collapsed() const {
249     return mIsPositioned && mStart.Container() == mEnd.Container() &&
250            mStart.Offset() == mEnd.Offset();
251   }
252   already_AddRefed<mozilla::dom::DocumentFragment> CreateContextualFragment(
253       const nsAString& aString, ErrorResult& aError);
254   already_AddRefed<mozilla::dom::DocumentFragment> CloneContents(
255       ErrorResult& aErr);
256   int16_t CompareBoundaryPoints(uint16_t aHow, nsRange& aOther,
257                                 ErrorResult& aErr);
ComparePoint(nsINode & aContainer,uint32_t aOffset,ErrorResult & aErr)258   int16_t ComparePoint(nsINode& aContainer, uint32_t aOffset,
259                        ErrorResult& aErr) {
260     return ComparePoint(RawRangeBoundary(&aContainer, aOffset), aErr);
261   }
262   int16_t ComparePoint(const RawRangeBoundary& aPoint, ErrorResult& aErr);
263   void DeleteContents(ErrorResult& aRv);
264   already_AddRefed<mozilla::dom::DocumentFragment> ExtractContents(
265       ErrorResult& aErr);
266   nsINode* GetCommonAncestorContainer(ErrorResult& aRv) const;
267   nsINode* GetStartContainer(ErrorResult& aRv) const;
268   uint32_t GetStartOffset(ErrorResult& aRv) const;
269   nsINode* GetEndContainer(ErrorResult& aRv) const;
270   uint32_t GetEndOffset(ErrorResult& aRv) const;
271   void InsertNode(nsINode& aNode, ErrorResult& aErr);
272   bool IntersectsNode(nsINode& aNode, ErrorResult& aRv);
IsPointInRange(nsINode & aContainer,uint32_t aOffset,ErrorResult & aErr)273   bool IsPointInRange(nsINode& aContainer, uint32_t aOffset,
274                       ErrorResult& aErr) {
275     return IsPointInRange(RawRangeBoundary(&aContainer, aOffset), aErr);
276   }
277   bool IsPointInRange(const RawRangeBoundary& aPoint, ErrorResult& aErr);
278 
279   // *JS() methods are mapped to Range.*() of DOM.
280   // They may move focus only when the range represents normal selection.
281   // These methods shouldn't be used from internal.
282   void CollapseJS(bool aToStart);
283   void SelectNodeJS(nsINode& aNode, ErrorResult& aErr);
284   void SelectNodeContentsJS(nsINode& aNode, ErrorResult& aErr);
285   void SetEndJS(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr);
286   void SetEndAfterJS(nsINode& aNode, ErrorResult& aErr);
287   void SetEndBeforeJS(nsINode& aNode, ErrorResult& aErr);
288   void SetStartJS(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr);
289   void SetStartAfterJS(nsINode& aNode, ErrorResult& aErr);
290   void SetStartBeforeJS(nsINode& aNode, ErrorResult& aErr);
291 
292   void SurroundContents(nsINode& aNode, ErrorResult& aErr);
293   already_AddRefed<DOMRect> GetBoundingClientRect(bool aClampToEdge = true,
294                                                   bool aFlushLayout = true);
295   already_AddRefed<DOMRectList> GetClientRects(bool aClampToEdge = true,
296                                                bool aFlushLayout = true);
297   void GetClientRectsAndTexts(mozilla::dom::ClientRectsAndTexts& aResult,
298                               ErrorResult& aErr);
299 
300   // Following methods should be used for internal use instead of *JS().
301   void SelectNode(nsINode& aNode, ErrorResult& aErr);
302   void SelectNodeContents(nsINode& aNode, ErrorResult& aErr);
303   void SetEnd(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr);
304   void SetEnd(const RawRangeBoundary& aPoint, ErrorResult& aErr);
305   void SetEndAfter(nsINode& aNode, ErrorResult& aErr);
306   void SetEndBefore(nsINode& aNode, ErrorResult& aErr);
307   void SetStart(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr);
308   void SetStart(const RawRangeBoundary& aPoint, ErrorResult& aErr);
309   void SetStartAfter(nsINode& aNode, ErrorResult& aErr);
310   void SetStartBefore(nsINode& aNode, ErrorResult& aErr);
311 
312   static void GetInnerTextNoFlush(mozilla::dom::DOMString& aValue,
313                                   mozilla::ErrorResult& aError,
314                                   nsIContent* aContainer);
315 
GetParentObject()316   nsINode* GetParentObject() const { return mOwner; }
317   JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) final;
318   DocGroup* GetDocGroup() const;
319 
320  private:
321   // no copy's or assigns
322   nsRange(const nsRange&);
323   nsRange& operator=(const nsRange&);
324 
325   /**
326    * Cut or delete the range's contents.
327    *
328    * @param aFragment nsIDOMDocumentFragment containing the nodes.
329    *                  May be null to indicate the caller doesn't want a
330    * fragment.
331    */
332   nsresult CutContents(mozilla::dom::DocumentFragment** frag);
333 
334   static nsresult CloneParentsBetween(nsINode* aAncestor, nsINode* aNode,
335                                       nsINode** aClosestAncestor,
336                                       nsINode** aFarthestAncestor);
337 
338  public:
339   /**
340    * Compute the root node of aNode for initializing range classes.
341    * When aNode is in an anonymous subtree, this returns the shadow root or
342    * binding parent.  Otherwise, the root node of the document or document
343    * fragment.  If this returns nullptr, that means aNode can be neither the
344    * start container nor end container of any range.
345    */
ComputeRootNode(nsINode * aNode)346   static nsINode* ComputeRootNode(nsINode* aNode) {
347     return ComputeRootNode(aNode, false);
348   }
349 
350   /**
351    * Return true if aStartContainer/aStartOffset and aEndContainer/aEndOffset
352    * are valid start and end points for a range.  Otherwise, return false.
353    */
354   static bool IsValidPoints(nsINode* aStartContainer, uint32_t aStartOffset,
355                             nsINode* aEndContainer, uint32_t aEndOffset);
356 
357   /******************************************************************************
358    *  Utility routine to detect if a content node starts before a range and/or
359    *  ends after a range.  If neither it is contained inside the range.
360    *
361    *  XXX - callers responsibility to ensure node in same doc as range!
362    *
363    *****************************************************************************/
364   static nsresult CompareNodeToRange(nsINode* aNode, nsRange* aRange,
365                                      bool* outNodeBefore, bool* outNodeAfter);
366 
367   /**
368    * Return true if any part of (aNode, aStartOffset) .. (aNode, aEndOffset)
369    * overlaps any nsRange in aNode's GetNextRangeCommonAncestor ranges (i.e.
370    * where aNode is a descendant of a range's common ancestor node).
371    * If a nsRange starts in (aNode, aEndOffset) or if it ends in
372    * (aNode, aStartOffset) then it is non-overlapping and the result is false
373    * for that nsRange.  Collapsed ranges always counts as non-overlapping.
374    */
375   static bool IsNodeSelected(nsINode* aNode, uint32_t aStartOffset,
376                              uint32_t aEndOffset);
377 
378   /**
379    * This helper function gets rects and correlated text for the given range.
380    * @param aTextList optional where nullptr = don't retrieve text
381    */
382   static void CollectClientRectsAndText(
383       nsLayoutUtils::RectCallback* aCollector,
384       mozilla::dom::Sequence<nsString>* aTextList, nsRange* aRange,
385       nsINode* aStartContainer, uint32_t aStartOffset, nsINode* aEndContainer,
386       uint32_t aEndOffset, bool aClampToEdge, bool aFlushLayout);
387 
388   /**
389    * Scan this range for -moz-user-select:none nodes and split it up into
390    * multiple ranges to exclude those nodes.  The resulting ranges are put
391    * in aOutRanges.  If no -moz-user-select:none node is found in the range
392    * then |this| is unmodified and is the only range in aOutRanges.
393    * Otherwise, |this| will be modified so that it ends before the first
394    * -moz-user-select:none node and additional ranges may also be created.
395    * If all nodes in the range are -moz-user-select:none then aOutRanges
396    * will be empty.
397    * @param aOutRanges the resulting set of ranges
398    */
399   void ExcludeNonSelectableNodes(nsTArray<RefPtr<nsRange>>* aOutRanges);
400 
401   /**
402    * Notify the selection listeners after a range has been modified.
403    */
404   void NotifySelectionListenersAfterRangeSet();
405 
406   typedef nsTHashtable<nsPtrHashKey<nsRange>> RangeHashTable;
407 
408  protected:
409   void RegisterCommonAncestor(nsINode* aNode);
410   void UnregisterCommonAncestor(nsINode* aNode, bool aIsUnlinking);
IsValidBoundary(nsINode * aNode)411   nsINode* IsValidBoundary(nsINode* aNode) const {
412     return ComputeRootNode(aNode, mMaySpanAnonymousSubtrees);
413   }
414 
415   /**
416    * XXX nsRange should accept 0 - UINT32_MAX as offset.  However, users of
417    *     nsRange treat offset as int32_t.  Additionally, some other internal
418    *     APIs like nsINode::ComputeIndexOf() use int32_t.  Therefore,
419    *     nsRange should accept only 0 - INT32_MAX as valid offset for now.
420    */
IsValidOffset(uint32_t aOffset)421   static bool IsValidOffset(uint32_t aOffset) { return aOffset <= INT32_MAX; }
422   static bool IsValidOffset(nsINode* aNode, uint32_t aOffset);
423 
424   static nsINode* ComputeRootNode(nsINode* aNode,
425                                   bool aMaySpanAnonymousSubtrees);
426 
427   // CharacterDataChanged set aNotInsertedYet to true to disable an assertion
428   // and suppress re-registering a range common ancestor node since
429   // the new text node of a splitText hasn't been inserted yet.
430   // CharacterDataChanged does the re-registering when needed.
431   void DoSetRange(const RawRangeBoundary& lowerBound,
432                   const RawRangeBoundary& upperBound, nsINode* aRoot,
433                   bool aNotInsertedYet = false);
434 
435   /**
436    * For a range for which IsInSelection() is true, return the common ancestor
437    * for the range, which we had to compute when the common ancestor changed or
438    * IsInSelection became true, so we could register with it.  That is, it's a
439    * faster version of GetCommonAncestor that only works for ranges in a
440    * Selection.  The method will assert and the behavior is undefined if called
441    * on a range where IsInSelection() is false.
442    */
443   nsINode* GetRegisteredCommonAncestor();
444 
445   // Helper to IsNodeSelected.
446   static bool IsNodeInSortedRanges(nsINode* aNode, uint32_t aStartOffset,
447                                    uint32_t aEndOffset,
448                                    const nsTArray<const nsRange*>& aRanges,
449                                    size_t aRangeStart, size_t aRangeEnd);
450 
451   // Assume that this is guaranteed that this is held by the caller when
452   // this is used.  (Note that we cannot use AutoRestore for mCalledByJS
453   // due to a bit field.)
454   class MOZ_RAII AutoCalledByJSRestore final {
455    private:
456     nsRange& mRange;
457     bool mOldValue;
458     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
459 
460    public:
AutoCalledByJSRestore(nsRange & aRange MOZ_GUARD_OBJECT_NOTIFIER_PARAM)461     explicit AutoCalledByJSRestore(
462         nsRange& aRange MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
463         : mRange(aRange), mOldValue(aRange.mCalledByJS) {
464       MOZ_GUARD_OBJECT_NOTIFIER_INIT;
465     }
~AutoCalledByJSRestore()466     ~AutoCalledByJSRestore() { mRange.mCalledByJS = mOldValue; }
SavedValue()467     bool SavedValue() const { return mOldValue; }
468   };
469 
470   struct MOZ_STACK_CLASS AutoInvalidateSelection {
AutoInvalidateSelectionAutoInvalidateSelection471     explicit AutoInvalidateSelection(nsRange* aRange) : mRange(aRange) {
472       if (!mRange->IsInSelection() || sIsNested) {
473         return;
474       }
475       sIsNested = true;
476       mCommonAncestor = mRange->GetRegisteredCommonAncestor();
477     }
478     ~AutoInvalidateSelection();
479     nsRange* mRange;
480     RefPtr<nsINode> mCommonAncestor;
481     static bool sIsNested;
482   };
483 
484   nsCOMPtr<nsIDocument> mOwner;
485   nsCOMPtr<nsINode> mRoot;
486   // mRegisteredCommonAncestor is only non-null when the range
487   // IsInSelection().  It's kept alive via mStartContainer/mEndContainer,
488   // because we update it any time those could become disconnected from it.
489   nsINode* MOZ_NON_OWNING_REF mRegisteredCommonAncestor;
490   RefPtr<mozilla::dom::Selection> mSelection;
491 
492   // These raw pointers are used to remember a child that is about
493   // to be inserted between a CharacterData call and a subsequent
494   // ContentInserted or ContentAppended call. It is safe to store
495   // these refs because the caller is guaranteed to trigger both
496   // notifications while holding a strong reference to the new child.
497   nsIContent* MOZ_NON_OWNING_REF mNextStartRef;
498   nsIContent* MOZ_NON_OWNING_REF mNextEndRef;
499 
500   RangeBoundary mStart;
501   RangeBoundary mEnd;
502 
503   bool mIsPositioned : 1;
504   bool mMaySpanAnonymousSubtrees : 1;
505   bool mIsGenerated : 1;
506   bool mCalledByJS : 1;
507 };
508 
ToCanonicalSupports(nsRange * aRange)509 inline nsISupports* ToCanonicalSupports(nsRange* aRange) {
510   return static_cast<nsIDOMRange*>(aRange);
511 }
512 
ToSupports(nsRange * aRange)513 inline nsISupports* ToSupports(nsRange* aRange) {
514   return static_cast<nsIDOMRange*>(aRange);
515 }
516 
517 #endif /* nsRange_h___ */
518