LCOV - code coverage report
Current view: top level - dom/base - nsRange.h (source / functions) Hit Total Coverage
Test: output.info Lines: 38 63 60.3 %
Date: 2017-07-14 16:53:18 Functions: 20 28 71.4 %
Legend: Lines: hit not hit

          Line data    Source code
       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             : 
      26             : namespace mozilla {
      27             : class ErrorResult;
      28             : namespace dom {
      29             : struct ClientRectsAndTexts;
      30             : class DocumentFragment;
      31             : class DOMRect;
      32             : class DOMRectList;
      33             : class Selection;
      34             : } // namespace dom
      35             : } // namespace mozilla
      36             : 
      37             : class nsRange final : public nsIDOMRange,
      38             :                       public nsStubMutationObserver,
      39             :                       public nsWrapperCache
      40             : {
      41             :   typedef mozilla::ErrorResult ErrorResult;
      42             :   typedef mozilla::dom::DOMRect DOMRect;
      43             :   typedef mozilla::dom::DOMRectList DOMRectList;
      44             : 
      45             :   virtual ~nsRange();
      46             : 
      47             : public:
      48             :   explicit nsRange(nsINode* aNode);
      49             : 
      50             :   static nsresult CreateRange(nsIDOMNode* aStartContainer, int32_t aStartOffset,
      51             :                               nsIDOMNode* aEndContainer, int32_t aEndOffset,
      52             :                               nsRange** aRange);
      53             :   static nsresult CreateRange(nsIDOMNode* aStartContainer, int32_t aStartOffset,
      54             :                               nsIDOMNode* aEndContainer, int32_t aEndOffset,
      55             :                               nsIDOMRange** aRange);
      56             :   static nsresult CreateRange(nsINode* aStartContainer, int32_t aStartOffset,
      57             :                               nsINode* aEndContainer, int32_t aEndOffset,
      58             :                               nsRange** aRange);
      59             : 
      60             :   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
      61         257 :   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsRange, nsIDOMRange)
      62             : 
      63           4 :   nsrefcnt GetRefCount() const
      64             :   {
      65           4 :     return mRefCnt;
      66             :   }
      67             : 
      68             :   // nsIDOMRange interface
      69             :   NS_DECL_NSIDOMRANGE
      70             : 
      71          10 :   nsINode* GetRoot() const
      72             :   {
      73          10 :     return mRoot;
      74             :   }
      75             : 
      76          58 :   nsINode* GetStartContainer() const
      77             :   {
      78          58 :     return mStartContainer;
      79             :   }
      80             : 
      81          58 :   nsINode* GetEndContainer() const
      82             :   {
      83          58 :     return mEndContainer;
      84             :   }
      85             : 
      86          35 :   int32_t StartOffset() const
      87             :   {
      88          35 :     return mStartOffset;
      89             :   }
      90             : 
      91          35 :   int32_t EndOffset() const
      92             :   {
      93          35 :     return mEndOffset;
      94             :   }
      95             : 
      96          66 :   bool IsPositioned() const
      97             :   {
      98          66 :     return mIsPositioned;
      99             :   }
     100             : 
     101           0 :   void SetMaySpanAnonymousSubtrees(bool aMaySpanAnonymousSubtrees)
     102             :   {
     103           0 :     mMaySpanAnonymousSubtrees = aMaySpanAnonymousSubtrees;
     104           0 :   }
     105             : 
     106             :   /**
     107             :    * Return true iff this range is part of a Selection object
     108             :    * and isn't detached.
     109             :    */
     110          76 :   bool IsInSelection() const
     111             :   {
     112          76 :     return !!mSelection;
     113             :   }
     114             : 
     115             :   /**
     116             :    * Called when the range is added/removed from a Selection.
     117             :    */
     118             :   void SetSelection(mozilla::dom::Selection* aSelection);
     119             : 
     120             :   /**
     121             :    * Return true if this range was generated.
     122             :    * @see SetIsGenerated
     123             :    */
     124           0 :   bool IsGenerated() const
     125             :   {
     126           0 :     return mIsGenerated;
     127             :   }
     128             : 
     129             :   /**
     130             :    * Mark this range as being generated or not.
     131             :    * Currently it is used for marking ranges that are created when splitting up
     132             :    * a range to exclude a -moz-user-select:none region.
     133             :    * @see Selection::AddItem
     134             :    * @see ExcludeNonSelectableNodes
     135             :    */
     136           0 :   void SetIsGenerated(bool aIsGenerated)
     137             :   {
     138           0 :     mIsGenerated = aIsGenerated;
     139           0 :   }
     140             : 
     141             :   nsINode* GetCommonAncestor() const;
     142             :   void Reset();
     143             : 
     144             :   /**
     145             :    * SetStart() and SetEnd() sets start point or end point separately.
     146             :    * However, this is expensive especially when it's a range of Selection.
     147             :    * When you set both start and end of a range, you should use
     148             :    * SetStartAndEnd() instead.
     149             :    */
     150             :   nsresult SetStart(nsINode* aContainer, int32_t aOffset);
     151             :   nsresult SetEnd(nsINode* aContainer, int32_t aOffset);
     152             : 
     153             :   already_AddRefed<nsRange> CloneRange() const;
     154             : 
     155             :   /**
     156             :    * SetStartAndEnd() works similar to call both SetStart() and SetEnd().
     157             :    * Different from calls them separately, this does nothing if either
     158             :    * the start point or the end point is invalid point.
     159             :    * If the specified start point is after the end point, the range will be
     160             :    * collapsed at the end point.  Similarly, if they are in different root,
     161             :    * the range will be collapsed at the end point.
     162             :    */
     163             :   nsresult SetStartAndEnd(nsINode* aStartContainer, int32_t aStartOffset,
     164             :                           nsINode* aEndContainer, int32_t aEndOffset);
     165             : 
     166             :   /**
     167             :    * CollapseTo() works similar to call both SetStart() and SetEnd() with
     168             :    * same node and offset.  This just calls SetStartAndParent() to set
     169             :    * collapsed range at aContainer and aOffset.
     170             :    */
     171           9 :   nsresult CollapseTo(nsINode* aContainer, int32_t aOffset)
     172             :   {
     173           9 :     return SetStartAndEnd(aContainer, aOffset, aContainer, aOffset);
     174             :   }
     175             : 
     176             :   /**
     177             :    * Retrieves node and offset for setting start or end of a range to
     178             :    * before or after aNode.
     179             :    */
     180           0 :   static nsINode* GetContainerAndOffsetAfter(nsINode* aNode, int32_t* aOffset)
     181             :   {
     182           0 :     MOZ_ASSERT(aNode);
     183           0 :     MOZ_ASSERT(aOffset);
     184           0 :     nsINode* parentNode = aNode->GetParentNode();
     185           0 :     *aOffset = parentNode ? parentNode->IndexOf(aNode) : -1;
     186           0 :     if (*aOffset >= 0) {
     187           0 :       (*aOffset)++;
     188             :     }
     189           0 :     return parentNode;
     190             :   }
     191           0 :   static nsINode* GetContainerAndOffsetBefore(nsINode* aNode, int32_t* aOffset)
     192             :   {
     193           0 :     MOZ_ASSERT(aNode);
     194           0 :     MOZ_ASSERT(aOffset);
     195           0 :     nsINode* parentNode = aNode->GetParentNode();
     196           0 :     *aOffset = parentNode ? parentNode->IndexOf(aNode) : -1;
     197           0 :     return parentNode;
     198             :   }
     199             : 
     200             :   NS_IMETHOD GetUsedFontFaces(nsIDOMFontFaceList** aResult);
     201             : 
     202             :   // nsIMutationObserver methods
     203             :   NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED
     204             :   NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
     205             :   NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
     206             :   NS_DECL_NSIMUTATIONOBSERVER_PARENTCHAINCHANGED
     207             :   NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
     208             : 
     209             :   // WebIDL
     210             :   static already_AddRefed<nsRange>
     211             :   Constructor(const mozilla::dom::GlobalObject& global,
     212             :               mozilla::ErrorResult& aRv);
     213             : 
     214          46 :   bool Collapsed() const
     215             :   {
     216          92 :     return mIsPositioned && mStartContainer == mEndContainer &&
     217          92 :            mStartOffset == mEndOffset;
     218             :   }
     219             :   already_AddRefed<mozilla::dom::DocumentFragment>
     220             :   CreateContextualFragment(const nsAString& aString, ErrorResult& aError);
     221             :   already_AddRefed<mozilla::dom::DocumentFragment>
     222             :   CloneContents(ErrorResult& aErr);
     223             :   int16_t CompareBoundaryPoints(uint16_t aHow, nsRange& aOther,
     224             :                                 ErrorResult& aErr);
     225             :   int16_t ComparePoint(nsINode& aContainer, uint32_t aOffset,
     226             :                        ErrorResult& aErr);
     227             :   void DeleteContents(ErrorResult& aRv);
     228             :   already_AddRefed<mozilla::dom::DocumentFragment>
     229             :     ExtractContents(ErrorResult& aErr);
     230             :   nsINode* GetCommonAncestorContainer(ErrorResult& aRv) const;
     231             :   nsINode* GetStartContainer(ErrorResult& aRv) const;
     232             :   uint32_t GetStartOffset(ErrorResult& aRv) const;
     233             :   nsINode* GetEndContainer(ErrorResult& aRv) const;
     234             :   uint32_t GetEndOffset(ErrorResult& aRv) const;
     235             :   void InsertNode(nsINode& aNode, ErrorResult& aErr);
     236             :   bool IntersectsNode(nsINode& aNode, ErrorResult& aRv);
     237             :   bool IsPointInRange(nsINode& aContainer, uint32_t aOffset, ErrorResult& aErr);
     238             : 
     239             :   // *JS() methods are mapped to Range.*() of DOM.
     240             :   // They may move focus only when the range represents normal selection.
     241             :   // These methods shouldn't be used from internal.
     242             :   void CollapseJS(bool aToStart);
     243             :   void SelectNodeJS(nsINode& aNode, ErrorResult& aErr);
     244             :   void SelectNodeContentsJS(nsINode& aNode, ErrorResult& aErr);
     245             :   void SetEndJS(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr);
     246             :   void SetEndAfterJS(nsINode& aNode, ErrorResult& aErr);
     247             :   void SetEndBeforeJS(nsINode& aNode, ErrorResult& aErr);
     248             :   void SetStartJS(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr);
     249             :   void SetStartAfterJS(nsINode& aNode, ErrorResult& aErr);
     250             :   void SetStartBeforeJS(nsINode& aNode, ErrorResult& aErr);
     251             : 
     252             :   void SurroundContents(nsINode& aNode, ErrorResult& aErr);
     253             :   already_AddRefed<DOMRect> GetBoundingClientRect(bool aClampToEdge = true,
     254             :                                                   bool aFlushLayout = true);
     255             :   already_AddRefed<DOMRectList> GetClientRects(bool aClampToEdge = true,
     256             :                                                bool aFlushLayout = true);
     257             :   void GetClientRectsAndTexts(
     258             :     mozilla::dom::ClientRectsAndTexts& aResult,
     259             :     ErrorResult& aErr);
     260             : 
     261             :   // Following methods should be used for internal use instead of *JS().
     262             :   void SelectNode(nsINode& aNode, ErrorResult& aErr);
     263             :   void SelectNodeContents(nsINode& aNode, ErrorResult& aErr);
     264             :   void SetEnd(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr);
     265             :   void SetEndAfter(nsINode& aNode, ErrorResult& aErr);
     266             :   void SetEndBefore(nsINode& aNode, ErrorResult& aErr);
     267             :   void SetStart(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr);
     268             :   void SetStartAfter(nsINode& aNode, ErrorResult& aErr);
     269             :   void SetStartBefore(nsINode& aNode, ErrorResult& aErr);
     270             : 
     271             :   static void GetInnerTextNoFlush(mozilla::dom::DOMString& aValue,
     272             :                                   mozilla::ErrorResult& aError,
     273             :                                   nsIContent* aStartContainer,
     274             :                                   uint32_t aStartOffset,
     275             :                                   nsIContent* aEndContainer,
     276             :                                   uint32_t aEndOffset);
     277             : 
     278           2 :   nsINode* GetParentObject() const { return mOwner; }
     279             :   virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override final;
     280             : 
     281             : private:
     282             :   // no copy's or assigns
     283             :   nsRange(const nsRange&);
     284             :   nsRange& operator=(const nsRange&);
     285             : 
     286             :   /**
     287             :    * Cut or delete the range's contents.
     288             :    *
     289             :    * @param aFragment nsIDOMDocumentFragment containing the nodes.
     290             :    *                  May be null to indicate the caller doesn't want a fragment.
     291             :    */
     292             :   nsresult CutContents(mozilla::dom::DocumentFragment** frag);
     293             : 
     294             :   static nsresult CloneParentsBetween(nsINode* aAncestor,
     295             :                                       nsINode* aNode,
     296             :                                       nsINode** aClosestAncestor,
     297             :                                       nsINode** aFarthestAncestor);
     298             : 
     299             : public:
     300             : /******************************************************************************
     301             :  *  Utility routine to detect if a content node starts before a range and/or
     302             :  *  ends after a range.  If neither it is contained inside the range.
     303             :  *
     304             :  *  XXX - callers responsibility to ensure node in same doc as range!
     305             :  *
     306             :  *****************************************************************************/
     307             :   static nsresult CompareNodeToRange(nsINode* aNode, nsRange* aRange,
     308             :                                      bool *outNodeBefore,
     309             :                                      bool *outNodeAfter);
     310             : 
     311             :   /**
     312             :    * Return true if any part of (aNode, aStartOffset) .. (aNode, aEndOffset)
     313             :    * overlaps any nsRange in aNode's GetNextRangeCommonAncestor ranges (i.e.
     314             :    * where aNode is a descendant of a range's common ancestor node).
     315             :    * If a nsRange starts in (aNode, aEndOffset) or if it ends in
     316             :    * (aNode, aStartOffset) then it is non-overlapping and the result is false
     317             :    * for that nsRange.  Collapsed ranges always counts as non-overlapping.
     318             :    */
     319             :   static bool IsNodeSelected(nsINode* aNode, uint32_t aStartOffset,
     320             :                              uint32_t aEndOffset);
     321             : 
     322             :   /**
     323             :    * This helper function gets rects and correlated text for the given range.
     324             :    * @param aTextList optional where nullptr = don't retrieve text
     325             :    */
     326             :   static void CollectClientRectsAndText(nsLayoutUtils::RectCallback* aCollector,
     327             :                                         mozilla::dom::Sequence<nsString>* aTextList,
     328             :                                         nsRange* aRange,
     329             :                                         nsINode* aStartContainer,
     330             :                                         int32_t aStartOffset,
     331             :                                         nsINode* aEndContainer,
     332             :                                         int32_t aEndOffset,
     333             :                                         bool aClampToEdge, bool aFlushLayout);
     334             : 
     335             :   /**
     336             :    * Scan this range for -moz-user-select:none nodes and split it up into
     337             :    * multiple ranges to exclude those nodes.  The resulting ranges are put
     338             :    * in aOutRanges.  If no -moz-user-select:none node is found in the range
     339             :    * then |this| is unmodified and is the only range in aOutRanges.
     340             :    * Otherwise, |this| will be modified so that it ends before the first
     341             :    * -moz-user-select:none node and additional ranges may also be created.
     342             :    * If all nodes in the range are -moz-user-select:none then aOutRanges
     343             :    * will be empty.
     344             :    * @param aOutRanges the resulting set of ranges
     345             :    */
     346             :   void ExcludeNonSelectableNodes(nsTArray<RefPtr<nsRange>>* aOutRanges);
     347             : 
     348             :   typedef nsTHashtable<nsPtrHashKey<nsRange> > RangeHashTable;
     349             : protected:
     350             :   void RegisterCommonAncestor(nsINode* aNode);
     351             :   void UnregisterCommonAncestor(nsINode* aNode);
     352             :   nsINode* IsValidBoundary(nsINode* aNode);
     353             :   static bool IsValidOffset(nsINode* aNode, int32_t aOffset);
     354             : 
     355             :   // CharacterDataChanged set aNotInsertedYet to true to disable an assertion
     356             :   // and suppress re-registering a range common ancestor node since
     357             :   // the new text node of a splitText hasn't been inserted yet.
     358             :   // CharacterDataChanged does the re-registering when needed.
     359             :   void DoSetRange(nsINode* aStartN, int32_t aStartOffset,
     360             :                   nsINode* aEndN, int32_t aEndOffset,
     361             :                   nsINode* aRoot, bool aNotInsertedYet = false);
     362             : 
     363             :   /**
     364             :    * For a range for which IsInSelection() is true, return the common
     365             :    * ancestor for the range.  This method uses the selection bits and
     366             :    * nsGkAtoms::range property on the nodes to quickly find the ancestor.
     367             :    * That is, it's a faster version of GetCommonAncestor that only works
     368             :    * for ranges in a Selection.  The method will assert and the behavior
     369             :    * is undefined if called on a range where IsInSelection() is false.
     370             :    */
     371             :   nsINode* GetRegisteredCommonAncestor();
     372             : 
     373             :   // Helper to IsNodeSelected.
     374             :   static bool IsNodeInSortedRanges(nsINode* aNode,
     375             :                                    uint32_t aStartOffset,
     376             :                                    uint32_t aEndOffset,
     377             :                                    const nsTArray<const nsRange*>& aRanges,
     378             :                                    size_t aRangeStart,
     379             :                                    size_t aRangeEnd);
     380             : 
     381             :   // Assume that this is guaranteed that this is held by the caller when
     382             :   // this is used.  (Note that we cannot use AutoRestore for mCalledByJS
     383             :   // due to a bit field.)
     384             :   class MOZ_RAII AutoCalledByJSRestore final
     385             :   {
     386             :   private:
     387             :     nsRange& mRange;
     388             :     bool mOldValue;
     389             :     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
     390             : 
     391             :   public:
     392           4 :     explicit AutoCalledByJSRestore(nsRange& aRange
     393             :                                    MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
     394           4 :       : mRange(aRange)
     395           4 :       , mOldValue(aRange.mCalledByJS)
     396             :     {
     397           4 :       MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     398           4 :     }
     399           4 :     ~AutoCalledByJSRestore()
     400           4 :     {
     401           4 :       mRange.mCalledByJS = mOldValue;
     402           4 :     }
     403           0 :     bool SavedValue() const { return mOldValue; }
     404             :   };
     405             : 
     406             :   struct MOZ_STACK_CLASS AutoInvalidateSelection
     407             :   {
     408           6 :     explicit AutoInvalidateSelection(nsRange* aRange) : mRange(aRange)
     409             :     {
     410             : #ifdef DEBUG
     411           6 :       mWasInSelection = mRange->IsInSelection();
     412             : #endif
     413           6 :       if (!mRange->IsInSelection() || mIsNested) {
     414           6 :         return;
     415             :       }
     416           0 :       mIsNested = true;
     417           0 :       mCommonAncestor = mRange->GetRegisteredCommonAncestor();
     418             :     }
     419             :     ~AutoInvalidateSelection();
     420             :     nsRange* mRange;
     421             :     RefPtr<nsINode> mCommonAncestor;
     422             : #ifdef DEBUG
     423             :     bool mWasInSelection;
     424             : #endif
     425             :     static bool mIsNested;
     426             :   };
     427             : 
     428             :   nsCOMPtr<nsIDocument> mOwner;
     429             :   nsCOMPtr<nsINode> mRoot;
     430             :   nsCOMPtr<nsINode> mStartContainer;
     431             :   nsCOMPtr<nsINode> mEndContainer;
     432             :   RefPtr<mozilla::dom::Selection> mSelection;
     433             :   int32_t mStartOffset;
     434             :   int32_t mEndOffset;
     435             : 
     436             :   bool mIsPositioned : 1;
     437             :   bool mMaySpanAnonymousSubtrees : 1;
     438             :   bool mIsGenerated : 1;
     439             :   bool mStartOffsetWasIncremented : 1;
     440             :   bool mEndOffsetWasIncremented : 1;
     441             :   bool mCalledByJS : 1;
     442             : #ifdef DEBUG
     443             :   int32_t  mAssertNextInsertOrAppendIndex;
     444             :   nsINode* mAssertNextInsertOrAppendNode;
     445             : #endif
     446             : };
     447             : 
     448             : inline nsISupports*
     449             : ToCanonicalSupports(nsRange* aRange)
     450             : {
     451             :   return static_cast<nsIDOMRange*>(aRange);
     452             : }
     453             : 
     454             : inline nsISupports*
     455           2 : ToSupports(nsRange* aRange)
     456             : {
     457           2 :   return static_cast<nsIDOMRange*>(aRange);
     458             : }
     459             : 
     460             : #endif /* nsRange_h___ */

Generated by: LCOV version 1.13