1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/base/src/nsRange.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,335 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +/* 1.10 + * Implementation of the DOM nsIDOMRange object. 1.11 + */ 1.12 + 1.13 +#ifndef nsRange_h___ 1.14 +#define nsRange_h___ 1.15 + 1.16 +#include "nsIDOMRange.h" 1.17 +#include "nsCOMPtr.h" 1.18 +#include "nsINode.h" 1.19 +#include "nsIDocument.h" 1.20 +#include "nsIDOMNode.h" 1.21 +#include "prmon.h" 1.22 +#include "nsStubMutationObserver.h" 1.23 +#include "nsWrapperCache.h" 1.24 +#include "mozilla/Attributes.h" 1.25 + 1.26 +namespace mozilla { 1.27 +class ErrorResult; 1.28 +namespace dom { 1.29 +class DocumentFragment; 1.30 +class DOMRect; 1.31 +class DOMRectList; 1.32 +} 1.33 +} 1.34 + 1.35 +class nsRange MOZ_FINAL : public nsIDOMRange, 1.36 + public nsStubMutationObserver, 1.37 + public nsWrapperCache 1.38 +{ 1.39 + typedef mozilla::ErrorResult ErrorResult; 1.40 + typedef mozilla::dom::DOMRect DOMRect; 1.41 + typedef mozilla::dom::DOMRectList DOMRectList; 1.42 + 1.43 +public: 1.44 + nsRange(nsINode* aNode) 1.45 + : mRoot(nullptr) 1.46 + , mStartOffset(0) 1.47 + , mEndOffset(0) 1.48 + , mIsPositioned(false) 1.49 + , mIsDetached(false) 1.50 + , mMaySpanAnonymousSubtrees(false) 1.51 + , mInSelection(false) 1.52 + , mStartOffsetWasIncremented(false) 1.53 + , mEndOffsetWasIncremented(false) 1.54 + , mEnableGravitationOnElementRemoval(true) 1.55 +#ifdef DEBUG 1.56 + , mAssertNextInsertOrAppendIndex(-1) 1.57 + , mAssertNextInsertOrAppendNode(nullptr) 1.58 +#endif 1.59 + { 1.60 + SetIsDOMBinding(); 1.61 + MOZ_ASSERT(aNode, "range isn't in a document!"); 1.62 + mOwner = aNode->OwnerDoc(); 1.63 + } 1.64 + virtual ~nsRange(); 1.65 + 1.66 + static nsresult CreateRange(nsIDOMNode* aStartParent, int32_t aStartOffset, 1.67 + nsIDOMNode* aEndParent, int32_t aEndOffset, 1.68 + nsRange** aRange); 1.69 + static nsresult CreateRange(nsIDOMNode* aStartParent, int32_t aStartOffset, 1.70 + nsIDOMNode* aEndParent, int32_t aEndOffset, 1.71 + nsIDOMRange** aRange); 1.72 + static nsresult CreateRange(nsINode* aStartParent, int32_t aStartOffset, 1.73 + nsINode* aEndParent, int32_t aEndOffset, 1.74 + nsRange** aRange); 1.75 + 1.76 + NS_DECL_CYCLE_COLLECTING_ISUPPORTS 1.77 + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsRange, nsIDOMRange) 1.78 + 1.79 + /** 1.80 + * The DOM Range spec requires that when a node is removed from its parent, 1.81 + * and the node's subtree contains the start or end point of a range, that 1.82 + * start or end point is moved up to where the node was removed from its 1.83 + * parent. 1.84 + * For some internal uses of Ranges it's useful to disable that behavior, 1.85 + * so that a range of children within a single parent is preserved even if 1.86 + * that parent is removed from the document tree. 1.87 + */ 1.88 + void SetEnableGravitationOnElementRemoval(bool aEnable) 1.89 + { 1.90 + mEnableGravitationOnElementRemoval = aEnable; 1.91 + } 1.92 + 1.93 + // nsIDOMRange interface 1.94 + NS_DECL_NSIDOMRANGE 1.95 + 1.96 + nsINode* GetRoot() const 1.97 + { 1.98 + return mRoot; 1.99 + } 1.100 + 1.101 + nsINode* GetStartParent() const 1.102 + { 1.103 + return mStartParent; 1.104 + } 1.105 + 1.106 + nsINode* GetEndParent() const 1.107 + { 1.108 + return mEndParent; 1.109 + } 1.110 + 1.111 + int32_t StartOffset() const 1.112 + { 1.113 + return mStartOffset; 1.114 + } 1.115 + 1.116 + int32_t EndOffset() const 1.117 + { 1.118 + return mEndOffset; 1.119 + } 1.120 + 1.121 + bool IsPositioned() const 1.122 + { 1.123 + return mIsPositioned; 1.124 + } 1.125 + 1.126 + void SetMaySpanAnonymousSubtrees(bool aMaySpanAnonymousSubtrees) 1.127 + { 1.128 + mMaySpanAnonymousSubtrees = aMaySpanAnonymousSubtrees; 1.129 + } 1.130 + 1.131 + /** 1.132 + * Return true iff this range is part of at least one Selection object 1.133 + * and isn't detached. 1.134 + */ 1.135 + bool IsInSelection() const 1.136 + { 1.137 + return mInSelection; 1.138 + } 1.139 + 1.140 + /** 1.141 + * Called when the range is added/removed from a Selection. 1.142 + */ 1.143 + void SetInSelection(bool aInSelection) 1.144 + { 1.145 + if (mInSelection == aInSelection) { 1.146 + return; 1.147 + } 1.148 + mInSelection = aInSelection; 1.149 + nsINode* commonAncestor = GetCommonAncestor(); 1.150 + NS_ASSERTION(commonAncestor, "unexpected disconnected nodes"); 1.151 + if (mInSelection) { 1.152 + RegisterCommonAncestor(commonAncestor); 1.153 + } else { 1.154 + UnregisterCommonAncestor(commonAncestor); 1.155 + } 1.156 + } 1.157 + 1.158 + nsINode* GetCommonAncestor() const; 1.159 + void Reset(); 1.160 + nsresult SetStart(nsINode* aParent, int32_t aOffset); 1.161 + nsresult SetEnd(nsINode* aParent, int32_t aOffset); 1.162 + already_AddRefed<nsRange> CloneRange() const; 1.163 + 1.164 + nsresult Set(nsINode* aStartParent, int32_t aStartOffset, 1.165 + nsINode* aEndParent, int32_t aEndOffset) 1.166 + { 1.167 + // If this starts being hot, we may be able to optimize this a bit, 1.168 + // but for now just set start and end separately. 1.169 + nsresult rv = SetStart(aStartParent, aStartOffset); 1.170 + NS_ENSURE_SUCCESS(rv, rv); 1.171 + 1.172 + return SetEnd(aEndParent, aEndOffset); 1.173 + } 1.174 + 1.175 + NS_IMETHOD GetUsedFontFaces(nsIDOMFontFaceList** aResult); 1.176 + 1.177 + // nsIMutationObserver methods 1.178 + NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED 1.179 + NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED 1.180 + NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED 1.181 + NS_DECL_NSIMUTATIONOBSERVER_PARENTCHAINCHANGED 1.182 + NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED 1.183 + 1.184 + // WebIDL 1.185 + static already_AddRefed<nsRange> 1.186 + Constructor(const mozilla::dom::GlobalObject& global, 1.187 + mozilla::ErrorResult& aRv); 1.188 + 1.189 + bool Collapsed() const 1.190 + { 1.191 + return mIsPositioned && mStartParent == mEndParent && 1.192 + mStartOffset == mEndOffset; 1.193 + } 1.194 + already_AddRefed<mozilla::dom::DocumentFragment> 1.195 + CreateContextualFragment(const nsAString& aString, ErrorResult& aError); 1.196 + already_AddRefed<mozilla::dom::DocumentFragment> 1.197 + CloneContents(ErrorResult& aErr); 1.198 + int16_t CompareBoundaryPoints(uint16_t aHow, nsRange& aOther, 1.199 + ErrorResult& aErr); 1.200 + int16_t ComparePoint(nsINode& aParent, uint32_t aOffset, ErrorResult& aErr); 1.201 + void DeleteContents(ErrorResult& aRv); 1.202 + already_AddRefed<mozilla::dom::DocumentFragment> 1.203 + ExtractContents(ErrorResult& aErr); 1.204 + nsINode* GetCommonAncestorContainer(ErrorResult& aRv) const; 1.205 + nsINode* GetStartContainer(ErrorResult& aRv) const; 1.206 + uint32_t GetStartOffset(ErrorResult& aRv) const; 1.207 + nsINode* GetEndContainer(ErrorResult& aRv) const; 1.208 + uint32_t GetEndOffset(ErrorResult& aRv) const; 1.209 + void InsertNode(nsINode& aNode, ErrorResult& aErr); 1.210 + bool IntersectsNode(nsINode& aNode, ErrorResult& aRv); 1.211 + bool IsPointInRange(nsINode& aParent, uint32_t aOffset, ErrorResult& aErr); 1.212 + void SelectNode(nsINode& aNode, ErrorResult& aErr); 1.213 + void SelectNodeContents(nsINode& aNode, ErrorResult& aErr); 1.214 + void SetEnd(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr); 1.215 + void SetEndAfter(nsINode& aNode, ErrorResult& aErr); 1.216 + void SetEndBefore(nsINode& aNode, ErrorResult& aErr); 1.217 + void SetStart(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr); 1.218 + void SetStartAfter(nsINode& aNode, ErrorResult& aErr); 1.219 + void SetStartBefore(nsINode& aNode, ErrorResult& aErr); 1.220 + void SurroundContents(nsINode& aNode, ErrorResult& aErr); 1.221 + already_AddRefed<DOMRect> GetBoundingClientRect(); 1.222 + already_AddRefed<DOMRectList> GetClientRects(); 1.223 + 1.224 + nsINode* GetParentObject() const { return mOwner; } 1.225 + virtual JSObject* WrapObject(JSContext* cx) MOZ_OVERRIDE MOZ_FINAL; 1.226 + 1.227 +private: 1.228 + // no copy's or assigns 1.229 + nsRange(const nsRange&); 1.230 + nsRange& operator=(const nsRange&); 1.231 + 1.232 + /** 1.233 + * Cut or delete the range's contents. 1.234 + * 1.235 + * @param aFragment nsIDOMDocumentFragment containing the nodes. 1.236 + * May be null to indicate the caller doesn't want a fragment. 1.237 + */ 1.238 + nsresult CutContents(mozilla::dom::DocumentFragment** frag); 1.239 + 1.240 + static nsresult CloneParentsBetween(nsINode* aAncestor, 1.241 + nsINode* aNode, 1.242 + nsINode** aClosestAncestor, 1.243 + nsINode** aFarthestAncestor); 1.244 + 1.245 +public: 1.246 +/****************************************************************************** 1.247 + * Utility routine to detect if a content node starts before a range and/or 1.248 + * ends after a range. If neither it is contained inside the range. 1.249 + * 1.250 + * XXX - callers responsibility to ensure node in same doc as range! 1.251 + * 1.252 + *****************************************************************************/ 1.253 + static nsresult CompareNodeToRange(nsINode* aNode, nsRange* aRange, 1.254 + bool *outNodeBefore, 1.255 + bool *outNodeAfter); 1.256 + 1.257 + static bool IsNodeSelected(nsINode* aNode, uint32_t aStartOffset, 1.258 + uint32_t aEndOffset); 1.259 + 1.260 + typedef nsTHashtable<nsPtrHashKey<nsRange> > RangeHashTable; 1.261 +protected: 1.262 + void RegisterCommonAncestor(nsINode* aNode); 1.263 + void UnregisterCommonAncestor(nsINode* aNode); 1.264 + nsINode* IsValidBoundary(nsINode* aNode); 1.265 + 1.266 + // CharacterDataChanged set aNotInsertedYet to true to disable an assertion 1.267 + // and suppress re-registering a range common ancestor node since 1.268 + // the new text node of a splitText hasn't been inserted yet. 1.269 + // CharacterDataChanged does the re-registering when needed. 1.270 + void DoSetRange(nsINode* aStartN, int32_t aStartOffset, 1.271 + nsINode* aEndN, int32_t aEndOffset, 1.272 + nsINode* aRoot, bool aNotInsertedYet = false); 1.273 + 1.274 + /** 1.275 + * For a range for which IsInSelection() is true, return the common 1.276 + * ancestor for the range. This method uses the selection bits and 1.277 + * nsGkAtoms::range property on the nodes to quickly find the ancestor. 1.278 + * That is, it's a faster version of GetCommonAncestor that only works 1.279 + * for ranges in a Selection. The method will assert and the behavior 1.280 + * is undefined if called on a range where IsInSelection() is false. 1.281 + */ 1.282 + nsINode* GetRegisteredCommonAncestor(); 1.283 + 1.284 + struct MOZ_STACK_CLASS AutoInvalidateSelection 1.285 + { 1.286 + AutoInvalidateSelection(nsRange* aRange) : mRange(aRange) 1.287 + { 1.288 +#ifdef DEBUG 1.289 + mWasInSelection = mRange->IsInSelection(); 1.290 +#endif 1.291 + if (!mRange->IsInSelection() || mIsNested) { 1.292 + return; 1.293 + } 1.294 + mIsNested = true; 1.295 + mCommonAncestor = mRange->GetRegisteredCommonAncestor(); 1.296 + } 1.297 + ~AutoInvalidateSelection(); 1.298 + nsRange* mRange; 1.299 + nsRefPtr<nsINode> mCommonAncestor; 1.300 +#ifdef DEBUG 1.301 + bool mWasInSelection; 1.302 +#endif 1.303 + static bool mIsNested; 1.304 + }; 1.305 + 1.306 + nsCOMPtr<nsIDocument> mOwner; 1.307 + nsCOMPtr<nsINode> mRoot; 1.308 + nsCOMPtr<nsINode> mStartParent; 1.309 + nsCOMPtr<nsINode> mEndParent; 1.310 + int32_t mStartOffset; 1.311 + int32_t mEndOffset; 1.312 + 1.313 + bool mIsPositioned; 1.314 + bool mIsDetached; 1.315 + bool mMaySpanAnonymousSubtrees; 1.316 + bool mInSelection; 1.317 + bool mStartOffsetWasIncremented; 1.318 + bool mEndOffsetWasIncremented; 1.319 + bool mEnableGravitationOnElementRemoval; 1.320 +#ifdef DEBUG 1.321 + int32_t mAssertNextInsertOrAppendIndex; 1.322 + nsINode* mAssertNextInsertOrAppendNode; 1.323 +#endif 1.324 +}; 1.325 + 1.326 +inline nsISupports* 1.327 +ToCanonicalSupports(nsRange* aRange) 1.328 +{ 1.329 + return static_cast<nsIDOMRange*>(aRange); 1.330 +} 1.331 + 1.332 +inline nsISupports* 1.333 +ToSupports(nsRange* aRange) 1.334 +{ 1.335 + return static_cast<nsIDOMRange*>(aRange); 1.336 +} 1.337 + 1.338 +#endif /* nsRange_h___ */