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