Thu, 15 Jan 2015 21:03:48 +0100
Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | /* |
michael@0 | 7 | * Implementation of the DOM nsIDOMRange object. |
michael@0 | 8 | */ |
michael@0 | 9 | |
michael@0 | 10 | #ifndef nsRange_h___ |
michael@0 | 11 | #define nsRange_h___ |
michael@0 | 12 | |
michael@0 | 13 | #include "nsIDOMRange.h" |
michael@0 | 14 | #include "nsCOMPtr.h" |
michael@0 | 15 | #include "nsINode.h" |
michael@0 | 16 | #include "nsIDocument.h" |
michael@0 | 17 | #include "nsIDOMNode.h" |
michael@0 | 18 | #include "prmon.h" |
michael@0 | 19 | #include "nsStubMutationObserver.h" |
michael@0 | 20 | #include "nsWrapperCache.h" |
michael@0 | 21 | #include "mozilla/Attributes.h" |
michael@0 | 22 | |
michael@0 | 23 | namespace mozilla { |
michael@0 | 24 | class ErrorResult; |
michael@0 | 25 | namespace dom { |
michael@0 | 26 | class DocumentFragment; |
michael@0 | 27 | class DOMRect; |
michael@0 | 28 | class DOMRectList; |
michael@0 | 29 | } |
michael@0 | 30 | } |
michael@0 | 31 | |
michael@0 | 32 | class nsRange MOZ_FINAL : public nsIDOMRange, |
michael@0 | 33 | public nsStubMutationObserver, |
michael@0 | 34 | public nsWrapperCache |
michael@0 | 35 | { |
michael@0 | 36 | typedef mozilla::ErrorResult ErrorResult; |
michael@0 | 37 | typedef mozilla::dom::DOMRect DOMRect; |
michael@0 | 38 | typedef mozilla::dom::DOMRectList DOMRectList; |
michael@0 | 39 | |
michael@0 | 40 | public: |
michael@0 | 41 | nsRange(nsINode* aNode) |
michael@0 | 42 | : mRoot(nullptr) |
michael@0 | 43 | , mStartOffset(0) |
michael@0 | 44 | , mEndOffset(0) |
michael@0 | 45 | , mIsPositioned(false) |
michael@0 | 46 | , mIsDetached(false) |
michael@0 | 47 | , mMaySpanAnonymousSubtrees(false) |
michael@0 | 48 | , mInSelection(false) |
michael@0 | 49 | , mStartOffsetWasIncremented(false) |
michael@0 | 50 | , mEndOffsetWasIncremented(false) |
michael@0 | 51 | , mEnableGravitationOnElementRemoval(true) |
michael@0 | 52 | #ifdef DEBUG |
michael@0 | 53 | , mAssertNextInsertOrAppendIndex(-1) |
michael@0 | 54 | , mAssertNextInsertOrAppendNode(nullptr) |
michael@0 | 55 | #endif |
michael@0 | 56 | { |
michael@0 | 57 | SetIsDOMBinding(); |
michael@0 | 58 | MOZ_ASSERT(aNode, "range isn't in a document!"); |
michael@0 | 59 | mOwner = aNode->OwnerDoc(); |
michael@0 | 60 | } |
michael@0 | 61 | virtual ~nsRange(); |
michael@0 | 62 | |
michael@0 | 63 | static nsresult CreateRange(nsIDOMNode* aStartParent, int32_t aStartOffset, |
michael@0 | 64 | nsIDOMNode* aEndParent, int32_t aEndOffset, |
michael@0 | 65 | nsRange** aRange); |
michael@0 | 66 | static nsresult CreateRange(nsIDOMNode* aStartParent, int32_t aStartOffset, |
michael@0 | 67 | nsIDOMNode* aEndParent, int32_t aEndOffset, |
michael@0 | 68 | nsIDOMRange** aRange); |
michael@0 | 69 | static nsresult CreateRange(nsINode* aStartParent, int32_t aStartOffset, |
michael@0 | 70 | nsINode* aEndParent, int32_t aEndOffset, |
michael@0 | 71 | nsRange** aRange); |
michael@0 | 72 | |
michael@0 | 73 | NS_DECL_CYCLE_COLLECTING_ISUPPORTS |
michael@0 | 74 | NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsRange, nsIDOMRange) |
michael@0 | 75 | |
michael@0 | 76 | /** |
michael@0 | 77 | * The DOM Range spec requires that when a node is removed from its parent, |
michael@0 | 78 | * and the node's subtree contains the start or end point of a range, that |
michael@0 | 79 | * start or end point is moved up to where the node was removed from its |
michael@0 | 80 | * parent. |
michael@0 | 81 | * For some internal uses of Ranges it's useful to disable that behavior, |
michael@0 | 82 | * so that a range of children within a single parent is preserved even if |
michael@0 | 83 | * that parent is removed from the document tree. |
michael@0 | 84 | */ |
michael@0 | 85 | void SetEnableGravitationOnElementRemoval(bool aEnable) |
michael@0 | 86 | { |
michael@0 | 87 | mEnableGravitationOnElementRemoval = aEnable; |
michael@0 | 88 | } |
michael@0 | 89 | |
michael@0 | 90 | // nsIDOMRange interface |
michael@0 | 91 | NS_DECL_NSIDOMRANGE |
michael@0 | 92 | |
michael@0 | 93 | nsINode* GetRoot() const |
michael@0 | 94 | { |
michael@0 | 95 | return mRoot; |
michael@0 | 96 | } |
michael@0 | 97 | |
michael@0 | 98 | nsINode* GetStartParent() const |
michael@0 | 99 | { |
michael@0 | 100 | return mStartParent; |
michael@0 | 101 | } |
michael@0 | 102 | |
michael@0 | 103 | nsINode* GetEndParent() const |
michael@0 | 104 | { |
michael@0 | 105 | return mEndParent; |
michael@0 | 106 | } |
michael@0 | 107 | |
michael@0 | 108 | int32_t StartOffset() const |
michael@0 | 109 | { |
michael@0 | 110 | return mStartOffset; |
michael@0 | 111 | } |
michael@0 | 112 | |
michael@0 | 113 | int32_t EndOffset() const |
michael@0 | 114 | { |
michael@0 | 115 | return mEndOffset; |
michael@0 | 116 | } |
michael@0 | 117 | |
michael@0 | 118 | bool IsPositioned() const |
michael@0 | 119 | { |
michael@0 | 120 | return mIsPositioned; |
michael@0 | 121 | } |
michael@0 | 122 | |
michael@0 | 123 | void SetMaySpanAnonymousSubtrees(bool aMaySpanAnonymousSubtrees) |
michael@0 | 124 | { |
michael@0 | 125 | mMaySpanAnonymousSubtrees = aMaySpanAnonymousSubtrees; |
michael@0 | 126 | } |
michael@0 | 127 | |
michael@0 | 128 | /** |
michael@0 | 129 | * Return true iff this range is part of at least one Selection object |
michael@0 | 130 | * and isn't detached. |
michael@0 | 131 | */ |
michael@0 | 132 | bool IsInSelection() const |
michael@0 | 133 | { |
michael@0 | 134 | return mInSelection; |
michael@0 | 135 | } |
michael@0 | 136 | |
michael@0 | 137 | /** |
michael@0 | 138 | * Called when the range is added/removed from a Selection. |
michael@0 | 139 | */ |
michael@0 | 140 | void SetInSelection(bool aInSelection) |
michael@0 | 141 | { |
michael@0 | 142 | if (mInSelection == aInSelection) { |
michael@0 | 143 | return; |
michael@0 | 144 | } |
michael@0 | 145 | mInSelection = aInSelection; |
michael@0 | 146 | nsINode* commonAncestor = GetCommonAncestor(); |
michael@0 | 147 | NS_ASSERTION(commonAncestor, "unexpected disconnected nodes"); |
michael@0 | 148 | if (mInSelection) { |
michael@0 | 149 | RegisterCommonAncestor(commonAncestor); |
michael@0 | 150 | } else { |
michael@0 | 151 | UnregisterCommonAncestor(commonAncestor); |
michael@0 | 152 | } |
michael@0 | 153 | } |
michael@0 | 154 | |
michael@0 | 155 | nsINode* GetCommonAncestor() const; |
michael@0 | 156 | void Reset(); |
michael@0 | 157 | nsresult SetStart(nsINode* aParent, int32_t aOffset); |
michael@0 | 158 | nsresult SetEnd(nsINode* aParent, int32_t aOffset); |
michael@0 | 159 | already_AddRefed<nsRange> CloneRange() const; |
michael@0 | 160 | |
michael@0 | 161 | nsresult Set(nsINode* aStartParent, int32_t aStartOffset, |
michael@0 | 162 | nsINode* aEndParent, int32_t aEndOffset) |
michael@0 | 163 | { |
michael@0 | 164 | // If this starts being hot, we may be able to optimize this a bit, |
michael@0 | 165 | // but for now just set start and end separately. |
michael@0 | 166 | nsresult rv = SetStart(aStartParent, aStartOffset); |
michael@0 | 167 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 168 | |
michael@0 | 169 | return SetEnd(aEndParent, aEndOffset); |
michael@0 | 170 | } |
michael@0 | 171 | |
michael@0 | 172 | NS_IMETHOD GetUsedFontFaces(nsIDOMFontFaceList** aResult); |
michael@0 | 173 | |
michael@0 | 174 | // nsIMutationObserver methods |
michael@0 | 175 | NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED |
michael@0 | 176 | NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED |
michael@0 | 177 | NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED |
michael@0 | 178 | NS_DECL_NSIMUTATIONOBSERVER_PARENTCHAINCHANGED |
michael@0 | 179 | NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED |
michael@0 | 180 | |
michael@0 | 181 | // WebIDL |
michael@0 | 182 | static already_AddRefed<nsRange> |
michael@0 | 183 | Constructor(const mozilla::dom::GlobalObject& global, |
michael@0 | 184 | mozilla::ErrorResult& aRv); |
michael@0 | 185 | |
michael@0 | 186 | bool Collapsed() const |
michael@0 | 187 | { |
michael@0 | 188 | return mIsPositioned && mStartParent == mEndParent && |
michael@0 | 189 | mStartOffset == mEndOffset; |
michael@0 | 190 | } |
michael@0 | 191 | already_AddRefed<mozilla::dom::DocumentFragment> |
michael@0 | 192 | CreateContextualFragment(const nsAString& aString, ErrorResult& aError); |
michael@0 | 193 | already_AddRefed<mozilla::dom::DocumentFragment> |
michael@0 | 194 | CloneContents(ErrorResult& aErr); |
michael@0 | 195 | int16_t CompareBoundaryPoints(uint16_t aHow, nsRange& aOther, |
michael@0 | 196 | ErrorResult& aErr); |
michael@0 | 197 | int16_t ComparePoint(nsINode& aParent, uint32_t aOffset, ErrorResult& aErr); |
michael@0 | 198 | void DeleteContents(ErrorResult& aRv); |
michael@0 | 199 | already_AddRefed<mozilla::dom::DocumentFragment> |
michael@0 | 200 | ExtractContents(ErrorResult& aErr); |
michael@0 | 201 | nsINode* GetCommonAncestorContainer(ErrorResult& aRv) const; |
michael@0 | 202 | nsINode* GetStartContainer(ErrorResult& aRv) const; |
michael@0 | 203 | uint32_t GetStartOffset(ErrorResult& aRv) const; |
michael@0 | 204 | nsINode* GetEndContainer(ErrorResult& aRv) const; |
michael@0 | 205 | uint32_t GetEndOffset(ErrorResult& aRv) const; |
michael@0 | 206 | void InsertNode(nsINode& aNode, ErrorResult& aErr); |
michael@0 | 207 | bool IntersectsNode(nsINode& aNode, ErrorResult& aRv); |
michael@0 | 208 | bool IsPointInRange(nsINode& aParent, uint32_t aOffset, ErrorResult& aErr); |
michael@0 | 209 | void SelectNode(nsINode& aNode, ErrorResult& aErr); |
michael@0 | 210 | void SelectNodeContents(nsINode& aNode, ErrorResult& aErr); |
michael@0 | 211 | void SetEnd(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr); |
michael@0 | 212 | void SetEndAfter(nsINode& aNode, ErrorResult& aErr); |
michael@0 | 213 | void SetEndBefore(nsINode& aNode, ErrorResult& aErr); |
michael@0 | 214 | void SetStart(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr); |
michael@0 | 215 | void SetStartAfter(nsINode& aNode, ErrorResult& aErr); |
michael@0 | 216 | void SetStartBefore(nsINode& aNode, ErrorResult& aErr); |
michael@0 | 217 | void SurroundContents(nsINode& aNode, ErrorResult& aErr); |
michael@0 | 218 | already_AddRefed<DOMRect> GetBoundingClientRect(); |
michael@0 | 219 | already_AddRefed<DOMRectList> GetClientRects(); |
michael@0 | 220 | |
michael@0 | 221 | nsINode* GetParentObject() const { return mOwner; } |
michael@0 | 222 | virtual JSObject* WrapObject(JSContext* cx) MOZ_OVERRIDE MOZ_FINAL; |
michael@0 | 223 | |
michael@0 | 224 | private: |
michael@0 | 225 | // no copy's or assigns |
michael@0 | 226 | nsRange(const nsRange&); |
michael@0 | 227 | nsRange& operator=(const nsRange&); |
michael@0 | 228 | |
michael@0 | 229 | /** |
michael@0 | 230 | * Cut or delete the range's contents. |
michael@0 | 231 | * |
michael@0 | 232 | * @param aFragment nsIDOMDocumentFragment containing the nodes. |
michael@0 | 233 | * May be null to indicate the caller doesn't want a fragment. |
michael@0 | 234 | */ |
michael@0 | 235 | nsresult CutContents(mozilla::dom::DocumentFragment** frag); |
michael@0 | 236 | |
michael@0 | 237 | static nsresult CloneParentsBetween(nsINode* aAncestor, |
michael@0 | 238 | nsINode* aNode, |
michael@0 | 239 | nsINode** aClosestAncestor, |
michael@0 | 240 | nsINode** aFarthestAncestor); |
michael@0 | 241 | |
michael@0 | 242 | public: |
michael@0 | 243 | /****************************************************************************** |
michael@0 | 244 | * Utility routine to detect if a content node starts before a range and/or |
michael@0 | 245 | * ends after a range. If neither it is contained inside the range. |
michael@0 | 246 | * |
michael@0 | 247 | * XXX - callers responsibility to ensure node in same doc as range! |
michael@0 | 248 | * |
michael@0 | 249 | *****************************************************************************/ |
michael@0 | 250 | static nsresult CompareNodeToRange(nsINode* aNode, nsRange* aRange, |
michael@0 | 251 | bool *outNodeBefore, |
michael@0 | 252 | bool *outNodeAfter); |
michael@0 | 253 | |
michael@0 | 254 | static bool IsNodeSelected(nsINode* aNode, uint32_t aStartOffset, |
michael@0 | 255 | uint32_t aEndOffset); |
michael@0 | 256 | |
michael@0 | 257 | typedef nsTHashtable<nsPtrHashKey<nsRange> > RangeHashTable; |
michael@0 | 258 | protected: |
michael@0 | 259 | void RegisterCommonAncestor(nsINode* aNode); |
michael@0 | 260 | void UnregisterCommonAncestor(nsINode* aNode); |
michael@0 | 261 | nsINode* IsValidBoundary(nsINode* aNode); |
michael@0 | 262 | |
michael@0 | 263 | // CharacterDataChanged set aNotInsertedYet to true to disable an assertion |
michael@0 | 264 | // and suppress re-registering a range common ancestor node since |
michael@0 | 265 | // the new text node of a splitText hasn't been inserted yet. |
michael@0 | 266 | // CharacterDataChanged does the re-registering when needed. |
michael@0 | 267 | void DoSetRange(nsINode* aStartN, int32_t aStartOffset, |
michael@0 | 268 | nsINode* aEndN, int32_t aEndOffset, |
michael@0 | 269 | nsINode* aRoot, bool aNotInsertedYet = false); |
michael@0 | 270 | |
michael@0 | 271 | /** |
michael@0 | 272 | * For a range for which IsInSelection() is true, return the common |
michael@0 | 273 | * ancestor for the range. This method uses the selection bits and |
michael@0 | 274 | * nsGkAtoms::range property on the nodes to quickly find the ancestor. |
michael@0 | 275 | * That is, it's a faster version of GetCommonAncestor that only works |
michael@0 | 276 | * for ranges in a Selection. The method will assert and the behavior |
michael@0 | 277 | * is undefined if called on a range where IsInSelection() is false. |
michael@0 | 278 | */ |
michael@0 | 279 | nsINode* GetRegisteredCommonAncestor(); |
michael@0 | 280 | |
michael@0 | 281 | struct MOZ_STACK_CLASS AutoInvalidateSelection |
michael@0 | 282 | { |
michael@0 | 283 | AutoInvalidateSelection(nsRange* aRange) : mRange(aRange) |
michael@0 | 284 | { |
michael@0 | 285 | #ifdef DEBUG |
michael@0 | 286 | mWasInSelection = mRange->IsInSelection(); |
michael@0 | 287 | #endif |
michael@0 | 288 | if (!mRange->IsInSelection() || mIsNested) { |
michael@0 | 289 | return; |
michael@0 | 290 | } |
michael@0 | 291 | mIsNested = true; |
michael@0 | 292 | mCommonAncestor = mRange->GetRegisteredCommonAncestor(); |
michael@0 | 293 | } |
michael@0 | 294 | ~AutoInvalidateSelection(); |
michael@0 | 295 | nsRange* mRange; |
michael@0 | 296 | nsRefPtr<nsINode> mCommonAncestor; |
michael@0 | 297 | #ifdef DEBUG |
michael@0 | 298 | bool mWasInSelection; |
michael@0 | 299 | #endif |
michael@0 | 300 | static bool mIsNested; |
michael@0 | 301 | }; |
michael@0 | 302 | |
michael@0 | 303 | nsCOMPtr<nsIDocument> mOwner; |
michael@0 | 304 | nsCOMPtr<nsINode> mRoot; |
michael@0 | 305 | nsCOMPtr<nsINode> mStartParent; |
michael@0 | 306 | nsCOMPtr<nsINode> mEndParent; |
michael@0 | 307 | int32_t mStartOffset; |
michael@0 | 308 | int32_t mEndOffset; |
michael@0 | 309 | |
michael@0 | 310 | bool mIsPositioned; |
michael@0 | 311 | bool mIsDetached; |
michael@0 | 312 | bool mMaySpanAnonymousSubtrees; |
michael@0 | 313 | bool mInSelection; |
michael@0 | 314 | bool mStartOffsetWasIncremented; |
michael@0 | 315 | bool mEndOffsetWasIncremented; |
michael@0 | 316 | bool mEnableGravitationOnElementRemoval; |
michael@0 | 317 | #ifdef DEBUG |
michael@0 | 318 | int32_t mAssertNextInsertOrAppendIndex; |
michael@0 | 319 | nsINode* mAssertNextInsertOrAppendNode; |
michael@0 | 320 | #endif |
michael@0 | 321 | }; |
michael@0 | 322 | |
michael@0 | 323 | inline nsISupports* |
michael@0 | 324 | ToCanonicalSupports(nsRange* aRange) |
michael@0 | 325 | { |
michael@0 | 326 | return static_cast<nsIDOMRange*>(aRange); |
michael@0 | 327 | } |
michael@0 | 328 | |
michael@0 | 329 | inline nsISupports* |
michael@0 | 330 | ToSupports(nsRange* aRange) |
michael@0 | 331 | { |
michael@0 | 332 | return static_cast<nsIDOMRange*>(aRange); |
michael@0 | 333 | } |
michael@0 | 334 | |
michael@0 | 335 | #endif /* nsRange_h___ */ |