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