| |
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/. */ |
| |
5 |
| |
6 /* |
| |
7 * Implementation of the DOM nsIDOMRange object. |
| |
8 */ |
| |
9 |
| |
10 #include "nscore.h" |
| |
11 #include "nsRange.h" |
| |
12 |
| |
13 #include "nsString.h" |
| |
14 #include "nsReadableUtils.h" |
| |
15 #include "nsIDOMNode.h" |
| |
16 #include "nsIDOMDocumentFragment.h" |
| |
17 #include "nsIContent.h" |
| |
18 #include "nsIDocument.h" |
| |
19 #include "nsIDOMText.h" |
| |
20 #include "nsError.h" |
| |
21 #include "nsIContentIterator.h" |
| |
22 #include "nsIDOMNodeList.h" |
| |
23 #include "nsGkAtoms.h" |
| |
24 #include "nsContentUtils.h" |
| |
25 #include "nsGenericDOMDataNode.h" |
| |
26 #include "nsLayoutUtils.h" |
| |
27 #include "nsTextFrame.h" |
| |
28 #include "nsFontFaceList.h" |
| |
29 #include "mozilla/dom/DocumentFragment.h" |
| |
30 #include "mozilla/dom/DocumentType.h" |
| |
31 #include "mozilla/dom/RangeBinding.h" |
| |
32 #include "mozilla/dom/DOMRect.h" |
| |
33 #include "mozilla/dom/ShadowRoot.h" |
| |
34 #include "mozilla/Telemetry.h" |
| |
35 #include "mozilla/Likely.h" |
| |
36 #include "nsCSSFrameConstructor.h" |
| |
37 |
| |
38 using namespace mozilla; |
| |
39 using namespace mozilla::dom; |
| |
40 |
| |
41 JSObject* |
| |
42 nsRange::WrapObject(JSContext* aCx) |
| |
43 { |
| |
44 return RangeBinding::Wrap(aCx, this); |
| |
45 } |
| |
46 |
| |
47 /****************************************************** |
| |
48 * stack based utilty class for managing monitor |
| |
49 ******************************************************/ |
| |
50 |
| |
51 static void InvalidateAllFrames(nsINode* aNode) |
| |
52 { |
| |
53 NS_PRECONDITION(aNode, "bad arg"); |
| |
54 |
| |
55 nsIFrame* frame = nullptr; |
| |
56 switch (aNode->NodeType()) { |
| |
57 case nsIDOMNode::TEXT_NODE: |
| |
58 case nsIDOMNode::ELEMENT_NODE: |
| |
59 { |
| |
60 nsIContent* content = static_cast<nsIContent*>(aNode); |
| |
61 frame = content->GetPrimaryFrame(); |
| |
62 break; |
| |
63 } |
| |
64 case nsIDOMNode::DOCUMENT_NODE: |
| |
65 { |
| |
66 nsIDocument* doc = static_cast<nsIDocument*>(aNode); |
| |
67 nsIPresShell* shell = doc ? doc->GetShell() : nullptr; |
| |
68 frame = shell ? shell->GetRootFrame() : nullptr; |
| |
69 break; |
| |
70 } |
| |
71 } |
| |
72 for (nsIFrame* f = frame; f; f = f->GetNextContinuation()) { |
| |
73 f->InvalidateFrameSubtree(); |
| |
74 } |
| |
75 } |
| |
76 |
| |
77 // Utility routine to detect if a content node is completely contained in a range |
| |
78 // If outNodeBefore is returned true, then the node starts before the range does. |
| |
79 // If outNodeAfter is returned true, then the node ends after the range does. |
| |
80 // Note that both of the above might be true. |
| |
81 // If neither are true, the node is contained inside of the range. |
| |
82 // XXX - callers responsibility to ensure node in same doc as range! |
| |
83 |
| |
84 // static |
| |
85 nsresult |
| |
86 nsRange::CompareNodeToRange(nsINode* aNode, nsRange* aRange, |
| |
87 bool *outNodeBefore, bool *outNodeAfter) |
| |
88 { |
| |
89 NS_ENSURE_STATE(aNode); |
| |
90 // create a pair of dom points that expresses location of node: |
| |
91 // NODE(start), NODE(end) |
| |
92 // Let incoming range be: |
| |
93 // {RANGE(start), RANGE(end)} |
| |
94 // if (RANGE(start) <= NODE(start)) and (RANGE(end) => NODE(end)) |
| |
95 // then the Node is contained (completely) by the Range. |
| |
96 |
| |
97 if (!aRange || !aRange->IsPositioned()) |
| |
98 return NS_ERROR_UNEXPECTED; |
| |
99 |
| |
100 // gather up the dom point info |
| |
101 int32_t nodeStart, nodeEnd; |
| |
102 nsINode* parent = aNode->GetParentNode(); |
| |
103 if (!parent) { |
| |
104 // can't make a parent/offset pair to represent start or |
| |
105 // end of the root node, because it has no parent. |
| |
106 // so instead represent it by (node,0) and (node,numChildren) |
| |
107 parent = aNode; |
| |
108 nodeStart = 0; |
| |
109 nodeEnd = aNode->GetChildCount(); |
| |
110 } |
| |
111 else { |
| |
112 nodeStart = parent->IndexOf(aNode); |
| |
113 nodeEnd = nodeStart + 1; |
| |
114 } |
| |
115 |
| |
116 nsINode* rangeStartParent = aRange->GetStartParent(); |
| |
117 nsINode* rangeEndParent = aRange->GetEndParent(); |
| |
118 int32_t rangeStartOffset = aRange->StartOffset(); |
| |
119 int32_t rangeEndOffset = aRange->EndOffset(); |
| |
120 |
| |
121 // is RANGE(start) <= NODE(start) ? |
| |
122 bool disconnected = false; |
| |
123 *outNodeBefore = nsContentUtils::ComparePoints(rangeStartParent, |
| |
124 rangeStartOffset, |
| |
125 parent, nodeStart, |
| |
126 &disconnected) > 0; |
| |
127 NS_ENSURE_TRUE(!disconnected, NS_ERROR_DOM_WRONG_DOCUMENT_ERR); |
| |
128 |
| |
129 // is RANGE(end) >= NODE(end) ? |
| |
130 *outNodeAfter = nsContentUtils::ComparePoints(rangeEndParent, |
| |
131 rangeEndOffset, |
| |
132 parent, nodeEnd, |
| |
133 &disconnected) < 0; |
| |
134 NS_ENSURE_TRUE(!disconnected, NS_ERROR_DOM_WRONG_DOCUMENT_ERR); |
| |
135 return NS_OK; |
| |
136 } |
| |
137 |
| |
138 struct FindSelectedRangeData |
| |
139 { |
| |
140 nsINode* mNode; |
| |
141 nsRange* mResult; |
| |
142 uint32_t mStartOffset; |
| |
143 uint32_t mEndOffset; |
| |
144 }; |
| |
145 |
| |
146 static PLDHashOperator |
| |
147 FindSelectedRange(nsPtrHashKey<nsRange>* aEntry, void* userArg) |
| |
148 { |
| |
149 nsRange* range = aEntry->GetKey(); |
| |
150 if (range->IsInSelection() && !range->Collapsed()) { |
| |
151 FindSelectedRangeData* data = static_cast<FindSelectedRangeData*>(userArg); |
| |
152 int32_t cmp = nsContentUtils::ComparePoints(data->mNode, data->mEndOffset, |
| |
153 range->GetStartParent(), |
| |
154 range->StartOffset()); |
| |
155 if (cmp == 1) { |
| |
156 cmp = nsContentUtils::ComparePoints(data->mNode, data->mStartOffset, |
| |
157 range->GetEndParent(), |
| |
158 range->EndOffset()); |
| |
159 if (cmp == -1) { |
| |
160 data->mResult = range; |
| |
161 return PL_DHASH_STOP; |
| |
162 } |
| |
163 } |
| |
164 } |
| |
165 return PL_DHASH_NEXT; |
| |
166 } |
| |
167 |
| |
168 static nsINode* |
| |
169 GetNextRangeCommonAncestor(nsINode* aNode) |
| |
170 { |
| |
171 while (aNode && !aNode->IsCommonAncestorForRangeInSelection()) { |
| |
172 if (!aNode->IsDescendantOfCommonAncestorForRangeInSelection()) { |
| |
173 return nullptr; |
| |
174 } |
| |
175 aNode = aNode->GetParentNode(); |
| |
176 } |
| |
177 return aNode; |
| |
178 } |
| |
179 |
| |
180 /* static */ bool |
| |
181 nsRange::IsNodeSelected(nsINode* aNode, uint32_t aStartOffset, |
| |
182 uint32_t aEndOffset) |
| |
183 { |
| |
184 NS_PRECONDITION(aNode, "bad arg"); |
| |
185 |
| |
186 FindSelectedRangeData data = { aNode, nullptr, aStartOffset, aEndOffset }; |
| |
187 nsINode* n = GetNextRangeCommonAncestor(aNode); |
| |
188 NS_ASSERTION(n || !aNode->IsSelectionDescendant(), |
| |
189 "orphan selection descendant"); |
| |
190 for (; n; n = GetNextRangeCommonAncestor(n->GetParentNode())) { |
| |
191 RangeHashTable* ranges = |
| |
192 static_cast<RangeHashTable*>(n->GetProperty(nsGkAtoms::range)); |
| |
193 ranges->EnumerateEntries(FindSelectedRange, &data); |
| |
194 if (data.mResult) { |
| |
195 return true; |
| |
196 } |
| |
197 } |
| |
198 return false; |
| |
199 } |
| |
200 |
| |
201 /****************************************************** |
| |
202 * constructor/destructor |
| |
203 ******************************************************/ |
| |
204 |
| |
205 nsRange::~nsRange() |
| |
206 { |
| |
207 NS_ASSERTION(!IsInSelection(), "deleting nsRange that is in use"); |
| |
208 |
| |
209 // Maybe we can remove Detach() -- bug 702948. |
| |
210 Telemetry::Accumulate(Telemetry::DOM_RANGE_DETACHED, mIsDetached); |
| |
211 |
| |
212 // we want the side effects (releases and list removals) |
| |
213 DoSetRange(nullptr, 0, nullptr, 0, nullptr); |
| |
214 } |
| |
215 |
| |
216 /* static */ |
| |
217 nsresult |
| |
218 nsRange::CreateRange(nsINode* aStartParent, int32_t aStartOffset, |
| |
219 nsINode* aEndParent, int32_t aEndOffset, |
| |
220 nsRange** aRange) |
| |
221 { |
| |
222 nsCOMPtr<nsIDOMNode> startDomNode = do_QueryInterface(aStartParent); |
| |
223 nsCOMPtr<nsIDOMNode> endDomNode = do_QueryInterface(aEndParent); |
| |
224 |
| |
225 nsresult rv = CreateRange(startDomNode, aStartOffset, endDomNode, aEndOffset, |
| |
226 aRange); |
| |
227 |
| |
228 return rv; |
| |
229 |
| |
230 } |
| |
231 |
| |
232 /* static */ |
| |
233 nsresult |
| |
234 nsRange::CreateRange(nsIDOMNode* aStartParent, int32_t aStartOffset, |
| |
235 nsIDOMNode* aEndParent, int32_t aEndOffset, |
| |
236 nsRange** aRange) |
| |
237 { |
| |
238 MOZ_ASSERT(aRange); |
| |
239 *aRange = nullptr; |
| |
240 |
| |
241 nsCOMPtr<nsINode> startParent = do_QueryInterface(aStartParent); |
| |
242 NS_ENSURE_ARG_POINTER(startParent); |
| |
243 |
| |
244 nsRefPtr<nsRange> range = new nsRange(startParent); |
| |
245 |
| |
246 nsresult rv = range->SetStart(startParent, aStartOffset); |
| |
247 NS_ENSURE_SUCCESS(rv, rv); |
| |
248 |
| |
249 rv = range->SetEnd(aEndParent, aEndOffset); |
| |
250 NS_ENSURE_SUCCESS(rv, rv); |
| |
251 |
| |
252 range.forget(aRange); |
| |
253 return NS_OK; |
| |
254 } |
| |
255 |
| |
256 /* static */ |
| |
257 nsresult |
| |
258 nsRange::CreateRange(nsIDOMNode* aStartParent, int32_t aStartOffset, |
| |
259 nsIDOMNode* aEndParent, int32_t aEndOffset, |
| |
260 nsIDOMRange** aRange) |
| |
261 { |
| |
262 nsRefPtr<nsRange> range; |
| |
263 nsresult rv = nsRange::CreateRange(aStartParent, aStartOffset, aEndParent, |
| |
264 aEndOffset, getter_AddRefs(range)); |
| |
265 range.forget(aRange); |
| |
266 return rv; |
| |
267 } |
| |
268 |
| |
269 /****************************************************** |
| |
270 * nsISupports |
| |
271 ******************************************************/ |
| |
272 |
| |
273 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsRange) |
| |
274 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(nsRange, |
| |
275 DoSetRange(nullptr, 0, nullptr, 0, nullptr)) |
| |
276 |
| |
277 // QueryInterface implementation for nsRange |
| |
278 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsRange) |
| |
279 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY |
| |
280 NS_INTERFACE_MAP_ENTRY(nsIDOMRange) |
| |
281 NS_INTERFACE_MAP_ENTRY(nsIMutationObserver) |
| |
282 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMRange) |
| |
283 NS_INTERFACE_MAP_END |
| |
284 |
| |
285 NS_IMPL_CYCLE_COLLECTION_CLASS(nsRange) |
| |
286 |
| |
287 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsRange) |
| |
288 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER |
| |
289 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner); |
| |
290 tmp->Reset(); |
| |
291 NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
| |
292 |
| |
293 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsRange) |
| |
294 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner) |
| |
295 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStartParent) |
| |
296 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEndParent) |
| |
297 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot) |
| |
298 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS |
| |
299 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
| |
300 |
| |
301 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsRange) |
| |
302 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER |
| |
303 NS_IMPL_CYCLE_COLLECTION_TRACE_END |
| |
304 |
| |
305 static void MarkDescendants(nsINode* aNode) |
| |
306 { |
| |
307 // Set NodeIsDescendantOfCommonAncestorForRangeInSelection on aNode's |
| |
308 // descendants unless aNode is already marked as a range common ancestor |
| |
309 // or a descendant of one, in which case all of our descendants have the |
| |
310 // bit set already. |
| |
311 if (!aNode->IsSelectionDescendant()) { |
| |
312 // don't set the Descendant bit on |aNode| itself |
| |
313 nsINode* node = aNode->GetNextNode(aNode); |
| |
314 while (node) { |
| |
315 node->SetDescendantOfCommonAncestorForRangeInSelection(); |
| |
316 if (!node->IsCommonAncestorForRangeInSelection()) { |
| |
317 node = node->GetNextNode(aNode); |
| |
318 } else { |
| |
319 // optimize: skip this sub-tree since it's marked already. |
| |
320 node = node->GetNextNonChildNode(aNode); |
| |
321 } |
| |
322 } |
| |
323 } |
| |
324 } |
| |
325 |
| |
326 static void UnmarkDescendants(nsINode* aNode) |
| |
327 { |
| |
328 // Unset NodeIsDescendantOfCommonAncestorForRangeInSelection on aNode's |
| |
329 // descendants unless aNode is a descendant of another range common ancestor. |
| |
330 // Also, exclude descendants of range common ancestors (but not the common |
| |
331 // ancestor itself). |
| |
332 if (!aNode->IsDescendantOfCommonAncestorForRangeInSelection()) { |
| |
333 // we know |aNode| doesn't have any bit set |
| |
334 nsINode* node = aNode->GetNextNode(aNode); |
| |
335 while (node) { |
| |
336 node->ClearDescendantOfCommonAncestorForRangeInSelection(); |
| |
337 if (!node->IsCommonAncestorForRangeInSelection()) { |
| |
338 node = node->GetNextNode(aNode); |
| |
339 } else { |
| |
340 // We found an ancestor of an overlapping range, skip its descendants. |
| |
341 node = node->GetNextNonChildNode(aNode); |
| |
342 } |
| |
343 } |
| |
344 } |
| |
345 } |
| |
346 |
| |
347 void |
| |
348 nsRange::RegisterCommonAncestor(nsINode* aNode) |
| |
349 { |
| |
350 NS_PRECONDITION(aNode, "bad arg"); |
| |
351 NS_ASSERTION(IsInSelection(), "registering range not in selection"); |
| |
352 |
| |
353 MarkDescendants(aNode); |
| |
354 |
| |
355 RangeHashTable* ranges = |
| |
356 static_cast<RangeHashTable*>(aNode->GetProperty(nsGkAtoms::range)); |
| |
357 if (!ranges) { |
| |
358 ranges = new RangeHashTable; |
| |
359 aNode->SetProperty(nsGkAtoms::range, ranges, |
| |
360 nsINode::DeleteProperty<nsRange::RangeHashTable>, true); |
| |
361 } |
| |
362 ranges->PutEntry(this); |
| |
363 aNode->SetCommonAncestorForRangeInSelection(); |
| |
364 } |
| |
365 |
| |
366 void |
| |
367 nsRange::UnregisterCommonAncestor(nsINode* aNode) |
| |
368 { |
| |
369 NS_PRECONDITION(aNode, "bad arg"); |
| |
370 NS_ASSERTION(aNode->IsCommonAncestorForRangeInSelection(), "wrong node"); |
| |
371 RangeHashTable* ranges = |
| |
372 static_cast<RangeHashTable*>(aNode->GetProperty(nsGkAtoms::range)); |
| |
373 NS_ASSERTION(ranges->GetEntry(this), "unknown range"); |
| |
374 |
| |
375 if (ranges->Count() == 1) { |
| |
376 aNode->ClearCommonAncestorForRangeInSelection(); |
| |
377 aNode->DeleteProperty(nsGkAtoms::range); |
| |
378 UnmarkDescendants(aNode); |
| |
379 } else { |
| |
380 ranges->RemoveEntry(this); |
| |
381 } |
| |
382 } |
| |
383 |
| |
384 /****************************************************** |
| |
385 * nsIMutationObserver implementation |
| |
386 ******************************************************/ |
| |
387 |
| |
388 void |
| |
389 nsRange::CharacterDataChanged(nsIDocument* aDocument, |
| |
390 nsIContent* aContent, |
| |
391 CharacterDataChangeInfo* aInfo) |
| |
392 { |
| |
393 MOZ_ASSERT(mAssertNextInsertOrAppendIndex == -1, |
| |
394 "splitText failed to notify insert/append?"); |
| |
395 NS_ASSERTION(mIsPositioned, "shouldn't be notified if not positioned"); |
| |
396 |
| |
397 nsINode* newRoot = nullptr; |
| |
398 nsINode* newStartNode = nullptr; |
| |
399 nsINode* newEndNode = nullptr; |
| |
400 uint32_t newStartOffset = 0; |
| |
401 uint32_t newEndOffset = 0; |
| |
402 |
| |
403 if (aInfo->mDetails && |
| |
404 aInfo->mDetails->mType == CharacterDataChangeInfo::Details::eSplit) { |
| |
405 // If the splitted text node is immediately before a range boundary point |
| |
406 // that refers to a child index (i.e. its parent is the boundary container) |
| |
407 // then we need to increment the corresponding offset to account for the new |
| |
408 // text node that will be inserted. If so, we need to prevent the next |
| |
409 // ContentInserted or ContentAppended for this range from incrementing it |
| |
410 // again (when the new text node is notified). |
| |
411 nsINode* parentNode = aContent->GetParentNode(); |
| |
412 int32_t index = -1; |
| |
413 if (parentNode == mEndParent && mEndOffset > 0 && |
| |
414 (index = parentNode->IndexOf(aContent)) + 1 == mEndOffset) { |
| |
415 ++mEndOffset; |
| |
416 mEndOffsetWasIncremented = true; |
| |
417 } |
| |
418 if (parentNode == mStartParent && mStartOffset > 0 && |
| |
419 (index != -1 ? index : parentNode->IndexOf(aContent)) + 1 == mStartOffset) { |
| |
420 ++mStartOffset; |
| |
421 mStartOffsetWasIncremented = true; |
| |
422 } |
| |
423 #ifdef DEBUG |
| |
424 if (mStartOffsetWasIncremented || mEndOffsetWasIncremented) { |
| |
425 mAssertNextInsertOrAppendIndex = |
| |
426 (mStartOffsetWasIncremented ? mStartOffset : mEndOffset) - 1; |
| |
427 mAssertNextInsertOrAppendNode = aInfo->mDetails->mNextSibling; |
| |
428 } |
| |
429 #endif |
| |
430 } |
| |
431 |
| |
432 // If the changed node contains our start boundary and the change starts |
| |
433 // before the boundary we'll need to adjust the offset. |
| |
434 if (aContent == mStartParent && |
| |
435 aInfo->mChangeStart < static_cast<uint32_t>(mStartOffset)) { |
| |
436 if (aInfo->mDetails) { |
| |
437 // splitText(), aInfo->mDetails->mNextSibling is the new text node |
| |
438 NS_ASSERTION(aInfo->mDetails->mType == |
| |
439 CharacterDataChangeInfo::Details::eSplit, |
| |
440 "only a split can start before the end"); |
| |
441 NS_ASSERTION(static_cast<uint32_t>(mStartOffset) <= aInfo->mChangeEnd + 1, |
| |
442 "mStartOffset is beyond the end of this node"); |
| |
443 newStartOffset = static_cast<uint32_t>(mStartOffset) - aInfo->mChangeStart; |
| |
444 newStartNode = aInfo->mDetails->mNextSibling; |
| |
445 if (MOZ_UNLIKELY(aContent == mRoot)) { |
| |
446 newRoot = IsValidBoundary(newStartNode); |
| |
447 } |
| |
448 |
| |
449 bool isCommonAncestor = IsInSelection() && mStartParent == mEndParent; |
| |
450 if (isCommonAncestor) { |
| |
451 UnregisterCommonAncestor(mStartParent); |
| |
452 RegisterCommonAncestor(newStartNode); |
| |
453 } |
| |
454 if (mStartParent->IsDescendantOfCommonAncestorForRangeInSelection()) { |
| |
455 newStartNode->SetDescendantOfCommonAncestorForRangeInSelection(); |
| |
456 } |
| |
457 } else { |
| |
458 // If boundary is inside changed text, position it before change |
| |
459 // else adjust start offset for the change in length. |
| |
460 mStartOffset = static_cast<uint32_t>(mStartOffset) <= aInfo->mChangeEnd ? |
| |
461 aInfo->mChangeStart : |
| |
462 mStartOffset + aInfo->mChangeStart - aInfo->mChangeEnd + |
| |
463 aInfo->mReplaceLength; |
| |
464 } |
| |
465 } |
| |
466 |
| |
467 // Do the same thing for the end boundary, except for splitText of a node |
| |
468 // with no parent then only switch to the new node if the start boundary |
| |
469 // did so too (otherwise the range would end up with disconnected nodes). |
| |
470 if (aContent == mEndParent && |
| |
471 aInfo->mChangeStart < static_cast<uint32_t>(mEndOffset)) { |
| |
472 if (aInfo->mDetails && (aContent->GetParentNode() || newStartNode)) { |
| |
473 // splitText(), aInfo->mDetails->mNextSibling is the new text node |
| |
474 NS_ASSERTION(aInfo->mDetails->mType == |
| |
475 CharacterDataChangeInfo::Details::eSplit, |
| |
476 "only a split can start before the end"); |
| |
477 NS_ASSERTION(static_cast<uint32_t>(mEndOffset) <= aInfo->mChangeEnd + 1, |
| |
478 "mEndOffset is beyond the end of this node"); |
| |
479 newEndOffset = static_cast<uint32_t>(mEndOffset) - aInfo->mChangeStart; |
| |
480 newEndNode = aInfo->mDetails->mNextSibling; |
| |
481 |
| |
482 bool isCommonAncestor = IsInSelection() && mStartParent == mEndParent; |
| |
483 if (isCommonAncestor && !newStartNode) { |
| |
484 // The split occurs inside the range. |
| |
485 UnregisterCommonAncestor(mStartParent); |
| |
486 RegisterCommonAncestor(mStartParent->GetParentNode()); |
| |
487 newEndNode->SetDescendantOfCommonAncestorForRangeInSelection(); |
| |
488 } else if (mEndParent->IsDescendantOfCommonAncestorForRangeInSelection()) { |
| |
489 newEndNode->SetDescendantOfCommonAncestorForRangeInSelection(); |
| |
490 } |
| |
491 } else { |
| |
492 mEndOffset = static_cast<uint32_t>(mEndOffset) <= aInfo->mChangeEnd ? |
| |
493 aInfo->mChangeStart : |
| |
494 mEndOffset + aInfo->mChangeStart - aInfo->mChangeEnd + |
| |
495 aInfo->mReplaceLength; |
| |
496 } |
| |
497 } |
| |
498 |
| |
499 if (aInfo->mDetails && |
| |
500 aInfo->mDetails->mType == CharacterDataChangeInfo::Details::eMerge) { |
| |
501 // normalize(), aInfo->mDetails->mNextSibling is the merged text node |
| |
502 // that will be removed |
| |
503 nsIContent* removed = aInfo->mDetails->mNextSibling; |
| |
504 if (removed == mStartParent) { |
| |
505 newStartOffset = static_cast<uint32_t>(mStartOffset) + aInfo->mChangeStart; |
| |
506 newStartNode = aContent; |
| |
507 if (MOZ_UNLIKELY(removed == mRoot)) { |
| |
508 newRoot = IsValidBoundary(newStartNode); |
| |
509 } |
| |
510 } |
| |
511 if (removed == mEndParent) { |
| |
512 newEndOffset = static_cast<uint32_t>(mEndOffset) + aInfo->mChangeStart; |
| |
513 newEndNode = aContent; |
| |
514 if (MOZ_UNLIKELY(removed == mRoot)) { |
| |
515 newRoot = IsValidBoundary(newEndNode); |
| |
516 } |
| |
517 } |
| |
518 // When the removed text node's parent is one of our boundary nodes we may |
| |
519 // need to adjust the offset to account for the removed node. However, |
| |
520 // there will also be a ContentRemoved notification later so the only cases |
| |
521 // we need to handle here is when the removed node is the text node after |
| |
522 // the boundary. (The m*Offset > 0 check is an optimization - a boundary |
| |
523 // point before the first child is never affected by normalize().) |
| |
524 nsINode* parentNode = aContent->GetParentNode(); |
| |
525 if (parentNode == mStartParent && mStartOffset > 0 && |
| |
526 uint32_t(mStartOffset) < parentNode->GetChildCount() && |
| |
527 removed == parentNode->GetChildAt(mStartOffset)) { |
| |
528 newStartNode = aContent; |
| |
529 newStartOffset = aInfo->mChangeStart; |
| |
530 } |
| |
531 if (parentNode == mEndParent && mEndOffset > 0 && |
| |
532 uint32_t(mEndOffset) < parentNode->GetChildCount() && |
| |
533 removed == parentNode->GetChildAt(mEndOffset)) { |
| |
534 newEndNode = aContent; |
| |
535 newEndOffset = aInfo->mChangeEnd; |
| |
536 } |
| |
537 } |
| |
538 |
| |
539 if (newStartNode || newEndNode) { |
| |
540 if (!newStartNode) { |
| |
541 newStartNode = mStartParent; |
| |
542 newStartOffset = mStartOffset; |
| |
543 } |
| |
544 if (!newEndNode) { |
| |
545 newEndNode = mEndParent; |
| |
546 newEndOffset = mEndOffset; |
| |
547 } |
| |
548 DoSetRange(newStartNode, newStartOffset, newEndNode, newEndOffset, |
| |
549 newRoot ? newRoot : mRoot.get(), |
| |
550 !newEndNode->GetParentNode() || !newStartNode->GetParentNode()); |
| |
551 } |
| |
552 } |
| |
553 |
| |
554 void |
| |
555 nsRange::ContentAppended(nsIDocument* aDocument, |
| |
556 nsIContent* aContainer, |
| |
557 nsIContent* aFirstNewContent, |
| |
558 int32_t aNewIndexInContainer) |
| |
559 { |
| |
560 NS_ASSERTION(mIsPositioned, "shouldn't be notified if not positioned"); |
| |
561 |
| |
562 nsINode* container = NODE_FROM(aContainer, aDocument); |
| |
563 if (container->IsSelectionDescendant() && IsInSelection()) { |
| |
564 nsINode* child = aFirstNewContent; |
| |
565 while (child) { |
| |
566 if (!child->IsDescendantOfCommonAncestorForRangeInSelection()) { |
| |
567 MarkDescendants(child); |
| |
568 child->SetDescendantOfCommonAncestorForRangeInSelection(); |
| |
569 } |
| |
570 child = child->GetNextSibling(); |
| |
571 } |
| |
572 } |
| |
573 |
| |
574 if (mStartOffsetWasIncremented || mEndOffsetWasIncremented) { |
| |
575 MOZ_ASSERT(mAssertNextInsertOrAppendIndex == aNewIndexInContainer); |
| |
576 MOZ_ASSERT(mAssertNextInsertOrAppendNode == aFirstNewContent); |
| |
577 MOZ_ASSERT(aFirstNewContent->IsNodeOfType(nsINode::eDATA_NODE)); |
| |
578 mStartOffsetWasIncremented = mEndOffsetWasIncremented = false; |
| |
579 #ifdef DEBUG |
| |
580 mAssertNextInsertOrAppendIndex = -1; |
| |
581 mAssertNextInsertOrAppendNode = nullptr; |
| |
582 #endif |
| |
583 } |
| |
584 } |
| |
585 |
| |
586 void |
| |
587 nsRange::ContentInserted(nsIDocument* aDocument, |
| |
588 nsIContent* aContainer, |
| |
589 nsIContent* aChild, |
| |
590 int32_t aIndexInContainer) |
| |
591 { |
| |
592 NS_ASSERTION(mIsPositioned, "shouldn't be notified if not positioned"); |
| |
593 |
| |
594 nsINode* container = NODE_FROM(aContainer, aDocument); |
| |
595 |
| |
596 // Adjust position if a sibling was inserted. |
| |
597 if (container == mStartParent && aIndexInContainer < mStartOffset && |
| |
598 !mStartOffsetWasIncremented) { |
| |
599 ++mStartOffset; |
| |
600 } |
| |
601 if (container == mEndParent && aIndexInContainer < mEndOffset && |
| |
602 !mEndOffsetWasIncremented) { |
| |
603 ++mEndOffset; |
| |
604 } |
| |
605 if (container->IsSelectionDescendant() && |
| |
606 !aChild->IsDescendantOfCommonAncestorForRangeInSelection()) { |
| |
607 MarkDescendants(aChild); |
| |
608 aChild->SetDescendantOfCommonAncestorForRangeInSelection(); |
| |
609 } |
| |
610 |
| |
611 if (mStartOffsetWasIncremented || mEndOffsetWasIncremented) { |
| |
612 MOZ_ASSERT(mAssertNextInsertOrAppendIndex == aIndexInContainer); |
| |
613 MOZ_ASSERT(mAssertNextInsertOrAppendNode == aChild); |
| |
614 MOZ_ASSERT(aChild->IsNodeOfType(nsINode::eDATA_NODE)); |
| |
615 mStartOffsetWasIncremented = mEndOffsetWasIncremented = false; |
| |
616 #ifdef DEBUG |
| |
617 mAssertNextInsertOrAppendIndex = -1; |
| |
618 mAssertNextInsertOrAppendNode = nullptr; |
| |
619 #endif |
| |
620 } |
| |
621 } |
| |
622 |
| |
623 void |
| |
624 nsRange::ContentRemoved(nsIDocument* aDocument, |
| |
625 nsIContent* aContainer, |
| |
626 nsIContent* aChild, |
| |
627 int32_t aIndexInContainer, |
| |
628 nsIContent* aPreviousSibling) |
| |
629 { |
| |
630 NS_ASSERTION(mIsPositioned, "shouldn't be notified if not positioned"); |
| |
631 MOZ_ASSERT(!mStartOffsetWasIncremented && !mEndOffsetWasIncremented && |
| |
632 mAssertNextInsertOrAppendIndex == -1, |
| |
633 "splitText failed to notify insert/append?"); |
| |
634 |
| |
635 nsINode* container = NODE_FROM(aContainer, aDocument); |
| |
636 bool gravitateStart = false; |
| |
637 bool gravitateEnd = false; |
| |
638 bool didCheckStartParentDescendant = false; |
| |
639 |
| |
640 // Adjust position if a sibling was removed... |
| |
641 if (container == mStartParent) { |
| |
642 if (aIndexInContainer < mStartOffset) { |
| |
643 --mStartOffset; |
| |
644 } |
| |
645 } else { // ...or gravitate if an ancestor was removed. |
| |
646 didCheckStartParentDescendant = true; |
| |
647 gravitateStart = nsContentUtils::ContentIsDescendantOf(mStartParent, aChild); |
| |
648 } |
| |
649 |
| |
650 // Do same thing for end boundry. |
| |
651 if (container == mEndParent) { |
| |
652 if (aIndexInContainer < mEndOffset) { |
| |
653 --mEndOffset; |
| |
654 } |
| |
655 } else if (didCheckStartParentDescendant && mStartParent == mEndParent) { |
| |
656 gravitateEnd = gravitateStart; |
| |
657 } else { |
| |
658 gravitateEnd = nsContentUtils::ContentIsDescendantOf(mEndParent, aChild); |
| |
659 } |
| |
660 |
| |
661 if (!mEnableGravitationOnElementRemoval) { |
| |
662 // Do not gravitate. |
| |
663 return; |
| |
664 } |
| |
665 |
| |
666 if (gravitateStart || gravitateEnd) { |
| |
667 DoSetRange(gravitateStart ? container : mStartParent.get(), |
| |
668 gravitateStart ? aIndexInContainer : mStartOffset, |
| |
669 gravitateEnd ? container : mEndParent.get(), |
| |
670 gravitateEnd ? aIndexInContainer : mEndOffset, |
| |
671 mRoot); |
| |
672 } |
| |
673 if (container->IsSelectionDescendant() && |
| |
674 aChild->IsDescendantOfCommonAncestorForRangeInSelection()) { |
| |
675 aChild->ClearDescendantOfCommonAncestorForRangeInSelection(); |
| |
676 UnmarkDescendants(aChild); |
| |
677 } |
| |
678 } |
| |
679 |
| |
680 void |
| |
681 nsRange::ParentChainChanged(nsIContent *aContent) |
| |
682 { |
| |
683 MOZ_ASSERT(!mStartOffsetWasIncremented && !mEndOffsetWasIncremented && |
| |
684 mAssertNextInsertOrAppendIndex == -1, |
| |
685 "splitText failed to notify insert/append?"); |
| |
686 NS_ASSERTION(mRoot == aContent, "Wrong ParentChainChanged notification?"); |
| |
687 nsINode* newRoot = IsValidBoundary(mStartParent); |
| |
688 NS_ASSERTION(newRoot, "No valid boundary or root found!"); |
| |
689 if (newRoot != IsValidBoundary(mEndParent)) { |
| |
690 // Sometimes ordering involved in cycle collection can lead to our |
| |
691 // start parent and/or end parent being disconnected from our root |
| |
692 // without our getting a ContentRemoved notification. |
| |
693 // See bug 846096 for more details. |
| |
694 NS_ASSERTION(mEndParent->IsInNativeAnonymousSubtree(), |
| |
695 "This special case should happen only with " |
| |
696 "native-anonymous content"); |
| |
697 // When that happens, bail out and set pointers to null; since we're |
| |
698 // in cycle collection and unreachable it shouldn't matter. |
| |
699 Reset(); |
| |
700 return; |
| |
701 } |
| |
702 // This is safe without holding a strong ref to self as long as the change |
| |
703 // of mRoot is the last thing in DoSetRange. |
| |
704 DoSetRange(mStartParent, mStartOffset, mEndParent, mEndOffset, newRoot); |
| |
705 } |
| |
706 |
| |
707 /****************************************************** |
| |
708 * Utilities for comparing points: API from nsIDOMRange |
| |
709 ******************************************************/ |
| |
710 NS_IMETHODIMP |
| |
711 nsRange::IsPointInRange(nsIDOMNode* aParent, int32_t aOffset, bool* aResult) |
| |
712 { |
| |
713 nsCOMPtr<nsINode> parent = do_QueryInterface(aParent); |
| |
714 if (!parent) { |
| |
715 return NS_ERROR_DOM_NOT_OBJECT_ERR; |
| |
716 } |
| |
717 |
| |
718 ErrorResult rv; |
| |
719 *aResult = IsPointInRange(*parent, aOffset, rv); |
| |
720 return rv.ErrorCode(); |
| |
721 } |
| |
722 |
| |
723 bool |
| |
724 nsRange::IsPointInRange(nsINode& aParent, uint32_t aOffset, ErrorResult& aRv) |
| |
725 { |
| |
726 uint16_t compareResult = ComparePoint(aParent, aOffset, aRv); |
| |
727 // If the node isn't in the range's document, it clearly isn't in the range. |
| |
728 if (aRv.ErrorCode() == NS_ERROR_DOM_WRONG_DOCUMENT_ERR) { |
| |
729 aRv = NS_OK; |
| |
730 return false; |
| |
731 } |
| |
732 |
| |
733 return compareResult == 0; |
| |
734 } |
| |
735 |
| |
736 // returns -1 if point is before range, 0 if point is in range, |
| |
737 // 1 if point is after range. |
| |
738 NS_IMETHODIMP |
| |
739 nsRange::ComparePoint(nsIDOMNode* aParent, int32_t aOffset, int16_t* aResult) |
| |
740 { |
| |
741 nsCOMPtr<nsINode> parent = do_QueryInterface(aParent); |
| |
742 NS_ENSURE_TRUE(parent, NS_ERROR_DOM_HIERARCHY_REQUEST_ERR); |
| |
743 |
| |
744 ErrorResult rv; |
| |
745 *aResult = ComparePoint(*parent, aOffset, rv); |
| |
746 return rv.ErrorCode(); |
| |
747 } |
| |
748 |
| |
749 int16_t |
| |
750 nsRange::ComparePoint(nsINode& aParent, uint32_t aOffset, ErrorResult& aRv) |
| |
751 { |
| |
752 // our range is in a good state? |
| |
753 if (!mIsPositioned) { |
| |
754 aRv.Throw(NS_ERROR_NOT_INITIALIZED); |
| |
755 return 0; |
| |
756 } |
| |
757 |
| |
758 if (!nsContentUtils::ContentIsDescendantOf(&aParent, mRoot)) { |
| |
759 aRv.Throw(NS_ERROR_DOM_WRONG_DOCUMENT_ERR); |
| |
760 return 0; |
| |
761 } |
| |
762 |
| |
763 if (aParent.NodeType() == nsIDOMNode::DOCUMENT_TYPE_NODE) { |
| |
764 aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR); |
| |
765 return 0; |
| |
766 } |
| |
767 |
| |
768 if (aOffset > aParent.Length()) { |
| |
769 aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); |
| |
770 return 0; |
| |
771 } |
| |
772 |
| |
773 int32_t cmp; |
| |
774 if ((cmp = nsContentUtils::ComparePoints(&aParent, aOffset, |
| |
775 mStartParent, mStartOffset)) <= 0) { |
| |
776 |
| |
777 return cmp; |
| |
778 } |
| |
779 if (nsContentUtils::ComparePoints(mEndParent, mEndOffset, |
| |
780 &aParent, aOffset) == -1) { |
| |
781 return 1; |
| |
782 } |
| |
783 |
| |
784 return 0; |
| |
785 } |
| |
786 |
| |
787 NS_IMETHODIMP |
| |
788 nsRange::IntersectsNode(nsIDOMNode* aNode, bool* aResult) |
| |
789 { |
| |
790 *aResult = false; |
| |
791 |
| |
792 nsCOMPtr<nsINode> node = do_QueryInterface(aNode); |
| |
793 // TODO: This should throw a TypeError. |
| |
794 NS_ENSURE_ARG(node); |
| |
795 |
| |
796 ErrorResult rv; |
| |
797 *aResult = IntersectsNode(*node, rv); |
| |
798 return rv.ErrorCode(); |
| |
799 } |
| |
800 |
| |
801 bool |
| |
802 nsRange::IntersectsNode(nsINode& aNode, ErrorResult& aRv) |
| |
803 { |
| |
804 if (!mIsPositioned) { |
| |
805 aRv.Throw(NS_ERROR_NOT_INITIALIZED); |
| |
806 return false; |
| |
807 } |
| |
808 |
| |
809 // Step 3. |
| |
810 nsINode* parent = aNode.GetParentNode(); |
| |
811 if (!parent) { |
| |
812 // Steps 2 and 4. |
| |
813 // |parent| is null, so |node|'s root is |node| itself. |
| |
814 return GetRoot() == &aNode; |
| |
815 } |
| |
816 |
| |
817 // Step 5. |
| |
818 int32_t nodeIndex = parent->IndexOf(&aNode); |
| |
819 |
| |
820 // Steps 6-7. |
| |
821 // Note: if disconnected is true, ComparePoints returns 1. |
| |
822 bool disconnected = false; |
| |
823 bool result = nsContentUtils::ComparePoints(mStartParent, mStartOffset, |
| |
824 parent, nodeIndex + 1, |
| |
825 &disconnected) < 0 && |
| |
826 nsContentUtils::ComparePoints(parent, nodeIndex, |
| |
827 mEndParent, mEndOffset, |
| |
828 &disconnected) < 0; |
| |
829 |
| |
830 // Step 2. |
| |
831 if (disconnected) { |
| |
832 result = false; |
| |
833 } |
| |
834 return result; |
| |
835 } |
| |
836 |
| |
837 /****************************************************** |
| |
838 * Private helper routines |
| |
839 ******************************************************/ |
| |
840 |
| |
841 // It's important that all setting of the range start/end points |
| |
842 // go through this function, which will do all the right voodoo |
| |
843 // for content notification of range ownership. |
| |
844 // Calling DoSetRange with either parent argument null will collapse |
| |
845 // the range to have both endpoints point to the other node |
| |
846 void |
| |
847 nsRange::DoSetRange(nsINode* aStartN, int32_t aStartOffset, |
| |
848 nsINode* aEndN, int32_t aEndOffset, |
| |
849 nsINode* aRoot, bool aNotInsertedYet) |
| |
850 { |
| |
851 NS_PRECONDITION((aStartN && aEndN && aRoot) || |
| |
852 (!aStartN && !aEndN && !aRoot), |
| |
853 "Set all or none"); |
| |
854 NS_PRECONDITION(!aRoot || aNotInsertedYet || |
| |
855 (nsContentUtils::ContentIsDescendantOf(aStartN, aRoot) && |
| |
856 nsContentUtils::ContentIsDescendantOf(aEndN, aRoot) && |
| |
857 aRoot == IsValidBoundary(aStartN) && |
| |
858 aRoot == IsValidBoundary(aEndN)), |
| |
859 "Wrong root"); |
| |
860 NS_PRECONDITION(!aRoot || |
| |
861 (aStartN->IsNodeOfType(nsINode::eCONTENT) && |
| |
862 aEndN->IsNodeOfType(nsINode::eCONTENT) && |
| |
863 aRoot == |
| |
864 static_cast<nsIContent*>(aStartN)->GetBindingParent() && |
| |
865 aRoot == |
| |
866 static_cast<nsIContent*>(aEndN)->GetBindingParent()) || |
| |
867 (!aRoot->GetParentNode() && |
| |
868 (aRoot->IsNodeOfType(nsINode::eDOCUMENT) || |
| |
869 aRoot->IsNodeOfType(nsINode::eATTRIBUTE) || |
| |
870 aRoot->IsNodeOfType(nsINode::eDOCUMENT_FRAGMENT) || |
| |
871 /*For backward compatibility*/ |
| |
872 aRoot->IsNodeOfType(nsINode::eCONTENT))), |
| |
873 "Bad root"); |
| |
874 |
| |
875 if (mRoot != aRoot) { |
| |
876 if (mRoot) { |
| |
877 mRoot->RemoveMutationObserver(this); |
| |
878 } |
| |
879 if (aRoot) { |
| |
880 aRoot->AddMutationObserver(this); |
| |
881 } |
| |
882 } |
| |
883 bool checkCommonAncestor = (mStartParent != aStartN || mEndParent != aEndN) && |
| |
884 IsInSelection() && !aNotInsertedYet; |
| |
885 nsINode* oldCommonAncestor = checkCommonAncestor ? GetCommonAncestor() : nullptr; |
| |
886 mStartParent = aStartN; |
| |
887 mStartOffset = aStartOffset; |
| |
888 mEndParent = aEndN; |
| |
889 mEndOffset = aEndOffset; |
| |
890 mIsPositioned = !!mStartParent; |
| |
891 if (checkCommonAncestor) { |
| |
892 nsINode* newCommonAncestor = GetCommonAncestor(); |
| |
893 if (newCommonAncestor != oldCommonAncestor) { |
| |
894 if (oldCommonAncestor) { |
| |
895 UnregisterCommonAncestor(oldCommonAncestor); |
| |
896 } |
| |
897 if (newCommonAncestor) { |
| |
898 RegisterCommonAncestor(newCommonAncestor); |
| |
899 } else { |
| |
900 NS_ASSERTION(!mIsPositioned, "unexpected disconnected nodes"); |
| |
901 mInSelection = false; |
| |
902 } |
| |
903 } |
| |
904 } |
| |
905 |
| |
906 // This needs to be the last thing this function does. See comment |
| |
907 // in ParentChainChanged. |
| |
908 mRoot = aRoot; |
| |
909 } |
| |
910 |
| |
911 static int32_t |
| |
912 IndexOf(nsINode* aChild) |
| |
913 { |
| |
914 nsINode* parent = aChild->GetParentNode(); |
| |
915 |
| |
916 return parent ? parent->IndexOf(aChild) : -1; |
| |
917 } |
| |
918 |
| |
919 nsINode* |
| |
920 nsRange::GetCommonAncestor() const |
| |
921 { |
| |
922 return mIsPositioned ? |
| |
923 nsContentUtils::GetCommonAncestor(mStartParent, mEndParent) : |
| |
924 nullptr; |
| |
925 } |
| |
926 |
| |
927 void |
| |
928 nsRange::Reset() |
| |
929 { |
| |
930 DoSetRange(nullptr, 0, nullptr, 0, nullptr); |
| |
931 } |
| |
932 |
| |
933 /****************************************************** |
| |
934 * public functionality |
| |
935 ******************************************************/ |
| |
936 |
| |
937 NS_IMETHODIMP |
| |
938 nsRange::GetStartContainer(nsIDOMNode** aStartParent) |
| |
939 { |
| |
940 if (!mIsPositioned) |
| |
941 return NS_ERROR_NOT_INITIALIZED; |
| |
942 |
| |
943 return CallQueryInterface(mStartParent, aStartParent); |
| |
944 } |
| |
945 |
| |
946 nsINode* |
| |
947 nsRange::GetStartContainer(ErrorResult& aRv) const |
| |
948 { |
| |
949 if (!mIsPositioned) { |
| |
950 aRv.Throw(NS_ERROR_NOT_INITIALIZED); |
| |
951 return nullptr; |
| |
952 } |
| |
953 |
| |
954 return mStartParent; |
| |
955 } |
| |
956 |
| |
957 NS_IMETHODIMP |
| |
958 nsRange::GetStartOffset(int32_t* aStartOffset) |
| |
959 { |
| |
960 if (!mIsPositioned) |
| |
961 return NS_ERROR_NOT_INITIALIZED; |
| |
962 |
| |
963 *aStartOffset = mStartOffset; |
| |
964 |
| |
965 return NS_OK; |
| |
966 } |
| |
967 |
| |
968 uint32_t |
| |
969 nsRange::GetStartOffset(ErrorResult& aRv) const |
| |
970 { |
| |
971 if (!mIsPositioned) { |
| |
972 aRv.Throw(NS_ERROR_NOT_INITIALIZED); |
| |
973 return 0; |
| |
974 } |
| |
975 |
| |
976 return mStartOffset; |
| |
977 } |
| |
978 |
| |
979 NS_IMETHODIMP |
| |
980 nsRange::GetEndContainer(nsIDOMNode** aEndParent) |
| |
981 { |
| |
982 if (!mIsPositioned) |
| |
983 return NS_ERROR_NOT_INITIALIZED; |
| |
984 |
| |
985 return CallQueryInterface(mEndParent, aEndParent); |
| |
986 } |
| |
987 |
| |
988 nsINode* |
| |
989 nsRange::GetEndContainer(ErrorResult& aRv) const |
| |
990 { |
| |
991 if (!mIsPositioned) { |
| |
992 aRv.Throw(NS_ERROR_NOT_INITIALIZED); |
| |
993 return nullptr; |
| |
994 } |
| |
995 |
| |
996 return mEndParent; |
| |
997 } |
| |
998 |
| |
999 NS_IMETHODIMP |
| |
1000 nsRange::GetEndOffset(int32_t* aEndOffset) |
| |
1001 { |
| |
1002 if (!mIsPositioned) |
| |
1003 return NS_ERROR_NOT_INITIALIZED; |
| |
1004 |
| |
1005 *aEndOffset = mEndOffset; |
| |
1006 |
| |
1007 return NS_OK; |
| |
1008 } |
| |
1009 |
| |
1010 uint32_t |
| |
1011 nsRange::GetEndOffset(ErrorResult& aRv) const |
| |
1012 { |
| |
1013 if (!mIsPositioned) { |
| |
1014 aRv.Throw(NS_ERROR_NOT_INITIALIZED); |
| |
1015 return 0; |
| |
1016 } |
| |
1017 |
| |
1018 return mEndOffset; |
| |
1019 } |
| |
1020 |
| |
1021 NS_IMETHODIMP |
| |
1022 nsRange::GetCollapsed(bool* aIsCollapsed) |
| |
1023 { |
| |
1024 if (!mIsPositioned) |
| |
1025 return NS_ERROR_NOT_INITIALIZED; |
| |
1026 |
| |
1027 *aIsCollapsed = Collapsed(); |
| |
1028 |
| |
1029 return NS_OK; |
| |
1030 } |
| |
1031 |
| |
1032 nsINode* |
| |
1033 nsRange::GetCommonAncestorContainer(ErrorResult& aRv) const |
| |
1034 { |
| |
1035 if (!mIsPositioned) { |
| |
1036 aRv.Throw(NS_ERROR_NOT_INITIALIZED); |
| |
1037 return nullptr; |
| |
1038 } |
| |
1039 |
| |
1040 return nsContentUtils::GetCommonAncestor(mStartParent, mEndParent); |
| |
1041 } |
| |
1042 |
| |
1043 NS_IMETHODIMP |
| |
1044 nsRange::GetCommonAncestorContainer(nsIDOMNode** aCommonParent) |
| |
1045 { |
| |
1046 ErrorResult rv; |
| |
1047 nsINode* commonAncestor = GetCommonAncestorContainer(rv); |
| |
1048 if (commonAncestor) { |
| |
1049 NS_ADDREF(*aCommonParent = commonAncestor->AsDOMNode()); |
| |
1050 } else { |
| |
1051 *aCommonParent = nullptr; |
| |
1052 } |
| |
1053 |
| |
1054 return rv.ErrorCode(); |
| |
1055 } |
| |
1056 |
| |
1057 nsINode* |
| |
1058 nsRange::IsValidBoundary(nsINode* aNode) |
| |
1059 { |
| |
1060 if (!aNode) { |
| |
1061 return nullptr; |
| |
1062 } |
| |
1063 |
| |
1064 if (aNode->IsNodeOfType(nsINode::eCONTENT)) { |
| |
1065 nsIContent* content = static_cast<nsIContent*>(aNode); |
| |
1066 if (content->Tag() == nsGkAtoms::documentTypeNodeName) { |
| |
1067 return nullptr; |
| |
1068 } |
| |
1069 |
| |
1070 if (!mMaySpanAnonymousSubtrees) { |
| |
1071 // If the node is in a shadow tree then the ShadowRoot is the root. |
| |
1072 ShadowRoot* containingShadow = content->GetContainingShadow(); |
| |
1073 if (containingShadow) { |
| |
1074 return containingShadow; |
| |
1075 } |
| |
1076 |
| |
1077 // If the node has a binding parent, that should be the root. |
| |
1078 // XXXbz maybe only for native anonymous content? |
| |
1079 nsINode* root = content->GetBindingParent(); |
| |
1080 if (root) { |
| |
1081 return root; |
| |
1082 } |
| |
1083 } |
| |
1084 } |
| |
1085 |
| |
1086 // Elements etc. must be in document or in document fragment, |
| |
1087 // text nodes in document, in document fragment or in attribute. |
| |
1088 nsINode* root = aNode->GetCurrentDoc(); |
| |
1089 if (root) { |
| |
1090 return root; |
| |
1091 } |
| |
1092 |
| |
1093 root = aNode->SubtreeRoot(); |
| |
1094 |
| |
1095 NS_ASSERTION(!root->IsNodeOfType(nsINode::eDOCUMENT), |
| |
1096 "GetCurrentDoc should have returned a doc"); |
| |
1097 |
| |
1098 // We allow this because of backward compatibility. |
| |
1099 return root; |
| |
1100 } |
| |
1101 |
| |
1102 void |
| |
1103 nsRange::SetStart(nsINode& aNode, uint32_t aOffset, ErrorResult& aRv) |
| |
1104 { |
| |
1105 if (!nsContentUtils::CanCallerAccess(&aNode)) { |
| |
1106 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); |
| |
1107 return; |
| |
1108 } |
| |
1109 |
| |
1110 AutoInvalidateSelection atEndOfBlock(this); |
| |
1111 aRv = SetStart(&aNode, aOffset); |
| |
1112 } |
| |
1113 |
| |
1114 NS_IMETHODIMP |
| |
1115 nsRange::SetStart(nsIDOMNode* aParent, int32_t aOffset) |
| |
1116 { |
| |
1117 nsCOMPtr<nsINode> parent = do_QueryInterface(aParent); |
| |
1118 if (!parent) { |
| |
1119 return NS_ERROR_DOM_NOT_OBJECT_ERR; |
| |
1120 } |
| |
1121 |
| |
1122 ErrorResult rv; |
| |
1123 SetStart(*parent, aOffset, rv); |
| |
1124 return rv.ErrorCode(); |
| |
1125 } |
| |
1126 |
| |
1127 /* virtual */ nsresult |
| |
1128 nsRange::SetStart(nsINode* aParent, int32_t aOffset) |
| |
1129 { |
| |
1130 nsINode* newRoot = IsValidBoundary(aParent); |
| |
1131 NS_ENSURE_TRUE(newRoot, NS_ERROR_DOM_INVALID_NODE_TYPE_ERR); |
| |
1132 |
| |
1133 if (aOffset < 0 || uint32_t(aOffset) > aParent->Length()) { |
| |
1134 return NS_ERROR_DOM_INDEX_SIZE_ERR; |
| |
1135 } |
| |
1136 |
| |
1137 // Collapse if not positioned yet, if positioned in another doc or |
| |
1138 // if the new start is after end. |
| |
1139 if (!mIsPositioned || newRoot != mRoot || |
| |
1140 nsContentUtils::ComparePoints(aParent, aOffset, |
| |
1141 mEndParent, mEndOffset) == 1) { |
| |
1142 DoSetRange(aParent, aOffset, aParent, aOffset, newRoot); |
| |
1143 |
| |
1144 return NS_OK; |
| |
1145 } |
| |
1146 |
| |
1147 DoSetRange(aParent, aOffset, mEndParent, mEndOffset, mRoot); |
| |
1148 |
| |
1149 return NS_OK; |
| |
1150 } |
| |
1151 |
| |
1152 void |
| |
1153 nsRange::SetStartBefore(nsINode& aNode, ErrorResult& aRv) |
| |
1154 { |
| |
1155 if (!nsContentUtils::CanCallerAccess(&aNode)) { |
| |
1156 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); |
| |
1157 return; |
| |
1158 } |
| |
1159 |
| |
1160 AutoInvalidateSelection atEndOfBlock(this); |
| |
1161 aRv = SetStart(aNode.GetParentNode(), IndexOf(&aNode)); |
| |
1162 } |
| |
1163 |
| |
1164 NS_IMETHODIMP |
| |
1165 nsRange::SetStartBefore(nsIDOMNode* aSibling) |
| |
1166 { |
| |
1167 nsCOMPtr<nsINode> sibling = do_QueryInterface(aSibling); |
| |
1168 if (!sibling) { |
| |
1169 return NS_ERROR_DOM_NOT_OBJECT_ERR; |
| |
1170 } |
| |
1171 |
| |
1172 ErrorResult rv; |
| |
1173 SetStartBefore(*sibling, rv); |
| |
1174 return rv.ErrorCode(); |
| |
1175 } |
| |
1176 |
| |
1177 void |
| |
1178 nsRange::SetStartAfter(nsINode& aNode, ErrorResult& aRv) |
| |
1179 { |
| |
1180 if (!nsContentUtils::CanCallerAccess(&aNode)) { |
| |
1181 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); |
| |
1182 return; |
| |
1183 } |
| |
1184 |
| |
1185 AutoInvalidateSelection atEndOfBlock(this); |
| |
1186 aRv = SetStart(aNode.GetParentNode(), IndexOf(&aNode) + 1); |
| |
1187 } |
| |
1188 |
| |
1189 NS_IMETHODIMP |
| |
1190 nsRange::SetStartAfter(nsIDOMNode* aSibling) |
| |
1191 { |
| |
1192 nsCOMPtr<nsINode> sibling = do_QueryInterface(aSibling); |
| |
1193 if (!sibling) { |
| |
1194 return NS_ERROR_DOM_NOT_OBJECT_ERR; |
| |
1195 } |
| |
1196 |
| |
1197 ErrorResult rv; |
| |
1198 SetStartAfter(*sibling, rv); |
| |
1199 return rv.ErrorCode(); |
| |
1200 } |
| |
1201 |
| |
1202 void |
| |
1203 nsRange::SetEnd(nsINode& aNode, uint32_t aOffset, ErrorResult& aRv) |
| |
1204 { |
| |
1205 if (!nsContentUtils::CanCallerAccess(&aNode)) { |
| |
1206 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); |
| |
1207 return; |
| |
1208 } |
| |
1209 AutoInvalidateSelection atEndOfBlock(this); |
| |
1210 aRv = SetEnd(&aNode, aOffset); |
| |
1211 } |
| |
1212 |
| |
1213 NS_IMETHODIMP |
| |
1214 nsRange::SetEnd(nsIDOMNode* aParent, int32_t aOffset) |
| |
1215 { |
| |
1216 nsCOMPtr<nsINode> parent = do_QueryInterface(aParent); |
| |
1217 if (!parent) { |
| |
1218 return NS_ERROR_DOM_NOT_OBJECT_ERR; |
| |
1219 } |
| |
1220 |
| |
1221 ErrorResult rv; |
| |
1222 SetEnd(*parent, aOffset, rv); |
| |
1223 return rv.ErrorCode(); |
| |
1224 } |
| |
1225 |
| |
1226 /* virtual */ nsresult |
| |
1227 nsRange::SetEnd(nsINode* aParent, int32_t aOffset) |
| |
1228 { |
| |
1229 nsINode* newRoot = IsValidBoundary(aParent); |
| |
1230 NS_ENSURE_TRUE(newRoot, NS_ERROR_DOM_INVALID_NODE_TYPE_ERR); |
| |
1231 |
| |
1232 if (aOffset < 0 || uint32_t(aOffset) > aParent->Length()) { |
| |
1233 return NS_ERROR_DOM_INDEX_SIZE_ERR; |
| |
1234 } |
| |
1235 |
| |
1236 // Collapse if not positioned yet, if positioned in another doc or |
| |
1237 // if the new end is before start. |
| |
1238 if (!mIsPositioned || newRoot != mRoot || |
| |
1239 nsContentUtils::ComparePoints(mStartParent, mStartOffset, |
| |
1240 aParent, aOffset) == 1) { |
| |
1241 DoSetRange(aParent, aOffset, aParent, aOffset, newRoot); |
| |
1242 |
| |
1243 return NS_OK; |
| |
1244 } |
| |
1245 |
| |
1246 DoSetRange(mStartParent, mStartOffset, aParent, aOffset, mRoot); |
| |
1247 |
| |
1248 return NS_OK; |
| |
1249 } |
| |
1250 |
| |
1251 void |
| |
1252 nsRange::SetEndBefore(nsINode& aNode, ErrorResult& aRv) |
| |
1253 { |
| |
1254 if (!nsContentUtils::CanCallerAccess(&aNode)) { |
| |
1255 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); |
| |
1256 return; |
| |
1257 } |
| |
1258 |
| |
1259 AutoInvalidateSelection atEndOfBlock(this); |
| |
1260 aRv = SetEnd(aNode.GetParentNode(), IndexOf(&aNode)); |
| |
1261 } |
| |
1262 |
| |
1263 NS_IMETHODIMP |
| |
1264 nsRange::SetEndBefore(nsIDOMNode* aSibling) |
| |
1265 { |
| |
1266 nsCOMPtr<nsINode> sibling = do_QueryInterface(aSibling); |
| |
1267 if (!sibling) { |
| |
1268 return NS_ERROR_DOM_NOT_OBJECT_ERR; |
| |
1269 } |
| |
1270 |
| |
1271 ErrorResult rv; |
| |
1272 SetEndBefore(*sibling, rv); |
| |
1273 return rv.ErrorCode(); |
| |
1274 } |
| |
1275 |
| |
1276 void |
| |
1277 nsRange::SetEndAfter(nsINode& aNode, ErrorResult& aRv) |
| |
1278 { |
| |
1279 if (!nsContentUtils::CanCallerAccess(&aNode)) { |
| |
1280 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); |
| |
1281 return; |
| |
1282 } |
| |
1283 |
| |
1284 AutoInvalidateSelection atEndOfBlock(this); |
| |
1285 aRv = SetEnd(aNode.GetParentNode(), IndexOf(&aNode) + 1); |
| |
1286 } |
| |
1287 |
| |
1288 NS_IMETHODIMP |
| |
1289 nsRange::SetEndAfter(nsIDOMNode* aSibling) |
| |
1290 { |
| |
1291 nsCOMPtr<nsINode> sibling = do_QueryInterface(aSibling); |
| |
1292 if (!sibling) { |
| |
1293 return NS_ERROR_DOM_NOT_OBJECT_ERR; |
| |
1294 } |
| |
1295 |
| |
1296 ErrorResult rv; |
| |
1297 SetEndAfter(*sibling, rv); |
| |
1298 return rv.ErrorCode(); |
| |
1299 } |
| |
1300 |
| |
1301 NS_IMETHODIMP |
| |
1302 nsRange::Collapse(bool aToStart) |
| |
1303 { |
| |
1304 if (!mIsPositioned) |
| |
1305 return NS_ERROR_NOT_INITIALIZED; |
| |
1306 |
| |
1307 AutoInvalidateSelection atEndOfBlock(this); |
| |
1308 if (aToStart) |
| |
1309 DoSetRange(mStartParent, mStartOffset, mStartParent, mStartOffset, mRoot); |
| |
1310 else |
| |
1311 DoSetRange(mEndParent, mEndOffset, mEndParent, mEndOffset, mRoot); |
| |
1312 |
| |
1313 return NS_OK; |
| |
1314 } |
| |
1315 |
| |
1316 NS_IMETHODIMP |
| |
1317 nsRange::SelectNode(nsIDOMNode* aN) |
| |
1318 { |
| |
1319 nsCOMPtr<nsINode> node = do_QueryInterface(aN); |
| |
1320 NS_ENSURE_TRUE(node, NS_ERROR_DOM_INVALID_NODE_TYPE_ERR); |
| |
1321 |
| |
1322 ErrorResult rv; |
| |
1323 SelectNode(*node, rv); |
| |
1324 return rv.ErrorCode(); |
| |
1325 } |
| |
1326 |
| |
1327 void |
| |
1328 nsRange::SelectNode(nsINode& aNode, ErrorResult& aRv) |
| |
1329 { |
| |
1330 if (!nsContentUtils::CanCallerAccess(&aNode)) { |
| |
1331 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); |
| |
1332 return; |
| |
1333 } |
| |
1334 |
| |
1335 nsINode* parent = aNode.GetParentNode(); |
| |
1336 nsINode* newRoot = IsValidBoundary(parent); |
| |
1337 if (!newRoot) { |
| |
1338 aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR); |
| |
1339 return; |
| |
1340 } |
| |
1341 |
| |
1342 int32_t index = parent->IndexOf(&aNode); |
| |
1343 if (index < 0) { |
| |
1344 aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR); |
| |
1345 return; |
| |
1346 } |
| |
1347 |
| |
1348 AutoInvalidateSelection atEndOfBlock(this); |
| |
1349 DoSetRange(parent, index, parent, index + 1, newRoot); |
| |
1350 } |
| |
1351 |
| |
1352 NS_IMETHODIMP |
| |
1353 nsRange::SelectNodeContents(nsIDOMNode* aN) |
| |
1354 { |
| |
1355 nsCOMPtr<nsINode> node = do_QueryInterface(aN); |
| |
1356 NS_ENSURE_TRUE(node, NS_ERROR_DOM_INVALID_NODE_TYPE_ERR); |
| |
1357 |
| |
1358 ErrorResult rv; |
| |
1359 SelectNodeContents(*node, rv); |
| |
1360 return rv.ErrorCode(); |
| |
1361 } |
| |
1362 |
| |
1363 void |
| |
1364 nsRange::SelectNodeContents(nsINode& aNode, ErrorResult& aRv) |
| |
1365 { |
| |
1366 if (!nsContentUtils::CanCallerAccess(&aNode)) { |
| |
1367 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); |
| |
1368 return; |
| |
1369 } |
| |
1370 |
| |
1371 nsINode* newRoot = IsValidBoundary(&aNode); |
| |
1372 if (!newRoot) { |
| |
1373 aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR); |
| |
1374 return; |
| |
1375 } |
| |
1376 |
| |
1377 AutoInvalidateSelection atEndOfBlock(this); |
| |
1378 DoSetRange(&aNode, 0, &aNode, aNode.Length(), newRoot); |
| |
1379 } |
| |
1380 |
| |
1381 // The Subtree Content Iterator only returns subtrees that are |
| |
1382 // completely within a given range. It doesn't return a CharacterData |
| |
1383 // node that contains either the start or end point of the range., |
| |
1384 // nor does it return element nodes when nothing in the element is selected. |
| |
1385 // We need an iterator that will also include these start/end points |
| |
1386 // so that our methods/algorithms aren't cluttered with special |
| |
1387 // case code that tries to include these points while iterating. |
| |
1388 // |
| |
1389 // The RangeSubtreeIterator class mimics the nsIContentIterator |
| |
1390 // methods we need, so should the Content Iterator support the |
| |
1391 // start/end points in the future, we can switchover relatively |
| |
1392 // easy. |
| |
1393 |
| |
1394 class MOZ_STACK_CLASS RangeSubtreeIterator |
| |
1395 { |
| |
1396 private: |
| |
1397 |
| |
1398 enum RangeSubtreeIterState { eDone=0, |
| |
1399 eUseStart, |
| |
1400 eUseIterator, |
| |
1401 eUseEnd }; |
| |
1402 |
| |
1403 nsCOMPtr<nsIContentIterator> mIter; |
| |
1404 RangeSubtreeIterState mIterState; |
| |
1405 |
| |
1406 nsCOMPtr<nsINode> mStart; |
| |
1407 nsCOMPtr<nsINode> mEnd; |
| |
1408 |
| |
1409 public: |
| |
1410 |
| |
1411 RangeSubtreeIterator() |
| |
1412 : mIterState(eDone) |
| |
1413 { |
| |
1414 } |
| |
1415 ~RangeSubtreeIterator() |
| |
1416 { |
| |
1417 } |
| |
1418 |
| |
1419 nsresult Init(nsRange *aRange); |
| |
1420 already_AddRefed<nsINode> GetCurrentNode(); |
| |
1421 void First(); |
| |
1422 void Last(); |
| |
1423 void Next(); |
| |
1424 void Prev(); |
| |
1425 |
| |
1426 bool IsDone() |
| |
1427 { |
| |
1428 return mIterState == eDone; |
| |
1429 } |
| |
1430 }; |
| |
1431 |
| |
1432 nsresult |
| |
1433 RangeSubtreeIterator::Init(nsRange *aRange) |
| |
1434 { |
| |
1435 mIterState = eDone; |
| |
1436 if (aRange->Collapsed()) { |
| |
1437 return NS_OK; |
| |
1438 } |
| |
1439 |
| |
1440 // Grab the start point of the range and QI it to |
| |
1441 // a CharacterData pointer. If it is CharacterData store |
| |
1442 // a pointer to the node. |
| |
1443 |
| |
1444 ErrorResult rv; |
| |
1445 nsCOMPtr<nsINode> node = aRange->GetStartContainer(rv); |
| |
1446 if (!node) return NS_ERROR_FAILURE; |
| |
1447 |
| |
1448 nsCOMPtr<nsIDOMCharacterData> startData = do_QueryInterface(node); |
| |
1449 if (startData || (node->IsElement() && |
| |
1450 node->AsElement()->GetChildCount() == aRange->GetStartOffset(rv))) { |
| |
1451 mStart = node; |
| |
1452 } |
| |
1453 |
| |
1454 // Grab the end point of the range and QI it to |
| |
1455 // a CharacterData pointer. If it is CharacterData store |
| |
1456 // a pointer to the node. |
| |
1457 |
| |
1458 node = aRange->GetEndContainer(rv); |
| |
1459 if (!node) return NS_ERROR_FAILURE; |
| |
1460 |
| |
1461 nsCOMPtr<nsIDOMCharacterData> endData = do_QueryInterface(node); |
| |
1462 if (endData || (node->IsElement() && aRange->GetEndOffset(rv) == 0)) { |
| |
1463 mEnd = node; |
| |
1464 } |
| |
1465 |
| |
1466 if (mStart && mStart == mEnd) |
| |
1467 { |
| |
1468 // The range starts and stops in the same CharacterData |
| |
1469 // node. Null out the end pointer so we only visit the |
| |
1470 // node once! |
| |
1471 |
| |
1472 mEnd = nullptr; |
| |
1473 } |
| |
1474 else |
| |
1475 { |
| |
1476 // Now create a Content Subtree Iterator to be used |
| |
1477 // for the subtrees between the end points! |
| |
1478 |
| |
1479 mIter = NS_NewContentSubtreeIterator(); |
| |
1480 |
| |
1481 nsresult res = mIter->Init(aRange); |
| |
1482 if (NS_FAILED(res)) return res; |
| |
1483 |
| |
1484 if (mIter->IsDone()) |
| |
1485 { |
| |
1486 // The subtree iterator thinks there's nothing |
| |
1487 // to iterate over, so just free it up so we |
| |
1488 // don't accidentally call into it. |
| |
1489 |
| |
1490 mIter = nullptr; |
| |
1491 } |
| |
1492 } |
| |
1493 |
| |
1494 // Initialize the iterator by calling First(). |
| |
1495 // Note that we are ignoring the return value on purpose! |
| |
1496 |
| |
1497 First(); |
| |
1498 |
| |
1499 return NS_OK; |
| |
1500 } |
| |
1501 |
| |
1502 already_AddRefed<nsINode> |
| |
1503 RangeSubtreeIterator::GetCurrentNode() |
| |
1504 { |
| |
1505 nsCOMPtr<nsINode> node; |
| |
1506 |
| |
1507 if (mIterState == eUseStart && mStart) { |
| |
1508 node = mStart; |
| |
1509 } else if (mIterState == eUseEnd && mEnd) { |
| |
1510 node = mEnd; |
| |
1511 } else if (mIterState == eUseIterator && mIter) { |
| |
1512 node = mIter->GetCurrentNode(); |
| |
1513 } |
| |
1514 |
| |
1515 return node.forget(); |
| |
1516 } |
| |
1517 |
| |
1518 void |
| |
1519 RangeSubtreeIterator::First() |
| |
1520 { |
| |
1521 if (mStart) |
| |
1522 mIterState = eUseStart; |
| |
1523 else if (mIter) |
| |
1524 { |
| |
1525 mIter->First(); |
| |
1526 |
| |
1527 mIterState = eUseIterator; |
| |
1528 } |
| |
1529 else if (mEnd) |
| |
1530 mIterState = eUseEnd; |
| |
1531 else |
| |
1532 mIterState = eDone; |
| |
1533 } |
| |
1534 |
| |
1535 void |
| |
1536 RangeSubtreeIterator::Last() |
| |
1537 { |
| |
1538 if (mEnd) |
| |
1539 mIterState = eUseEnd; |
| |
1540 else if (mIter) |
| |
1541 { |
| |
1542 mIter->Last(); |
| |
1543 |
| |
1544 mIterState = eUseIterator; |
| |
1545 } |
| |
1546 else if (mStart) |
| |
1547 mIterState = eUseStart; |
| |
1548 else |
| |
1549 mIterState = eDone; |
| |
1550 } |
| |
1551 |
| |
1552 void |
| |
1553 RangeSubtreeIterator::Next() |
| |
1554 { |
| |
1555 if (mIterState == eUseStart) |
| |
1556 { |
| |
1557 if (mIter) |
| |
1558 { |
| |
1559 mIter->First(); |
| |
1560 |
| |
1561 mIterState = eUseIterator; |
| |
1562 } |
| |
1563 else if (mEnd) |
| |
1564 mIterState = eUseEnd; |
| |
1565 else |
| |
1566 mIterState = eDone; |
| |
1567 } |
| |
1568 else if (mIterState == eUseIterator) |
| |
1569 { |
| |
1570 mIter->Next(); |
| |
1571 |
| |
1572 if (mIter->IsDone()) |
| |
1573 { |
| |
1574 if (mEnd) |
| |
1575 mIterState = eUseEnd; |
| |
1576 else |
| |
1577 mIterState = eDone; |
| |
1578 } |
| |
1579 } |
| |
1580 else |
| |
1581 mIterState = eDone; |
| |
1582 } |
| |
1583 |
| |
1584 void |
| |
1585 RangeSubtreeIterator::Prev() |
| |
1586 { |
| |
1587 if (mIterState == eUseEnd) |
| |
1588 { |
| |
1589 if (mIter) |
| |
1590 { |
| |
1591 mIter->Last(); |
| |
1592 |
| |
1593 mIterState = eUseIterator; |
| |
1594 } |
| |
1595 else if (mStart) |
| |
1596 mIterState = eUseStart; |
| |
1597 else |
| |
1598 mIterState = eDone; |
| |
1599 } |
| |
1600 else if (mIterState == eUseIterator) |
| |
1601 { |
| |
1602 mIter->Prev(); |
| |
1603 |
| |
1604 if (mIter->IsDone()) |
| |
1605 { |
| |
1606 if (mStart) |
| |
1607 mIterState = eUseStart; |
| |
1608 else |
| |
1609 mIterState = eDone; |
| |
1610 } |
| |
1611 } |
| |
1612 else |
| |
1613 mIterState = eDone; |
| |
1614 } |
| |
1615 |
| |
1616 |
| |
1617 // CollapseRangeAfterDelete() is a utility method that is used by |
| |
1618 // DeleteContents() and ExtractContents() to collapse the range |
| |
1619 // in the correct place, under the range's root container (the |
| |
1620 // range end points common container) as outlined by the Range spec: |
| |
1621 // |
| |
1622 // http://www.w3.org/TR/2000/REC-DOM-Level-2-Traversal-Range-20001113/ranges.html |
| |
1623 // The assumption made by this method is that the delete or extract |
| |
1624 // has been done already, and left the range in a state where there is |
| |
1625 // no content between the 2 end points. |
| |
1626 |
| |
1627 static nsresult |
| |
1628 CollapseRangeAfterDelete(nsRange* aRange) |
| |
1629 { |
| |
1630 NS_ENSURE_ARG_POINTER(aRange); |
| |
1631 |
| |
1632 // Check if range gravity took care of collapsing the range for us! |
| |
1633 if (aRange->Collapsed()) |
| |
1634 { |
| |
1635 // aRange is collapsed so there's nothing for us to do. |
| |
1636 // |
| |
1637 // There are 2 possible scenarios here: |
| |
1638 // |
| |
1639 // 1. aRange could've been collapsed prior to the delete/extract, |
| |
1640 // which would've resulted in nothing being removed, so aRange |
| |
1641 // is already where it should be. |
| |
1642 // |
| |
1643 // 2. Prior to the delete/extract, aRange's start and end were in |
| |
1644 // the same container which would mean everything between them |
| |
1645 // was removed, causing range gravity to collapse the range. |
| |
1646 |
| |
1647 return NS_OK; |
| |
1648 } |
| |
1649 |
| |
1650 // aRange isn't collapsed so figure out the appropriate place to collapse! |
| |
1651 // First get both end points and their common ancestor. |
| |
1652 |
| |
1653 ErrorResult rv; |
| |
1654 nsCOMPtr<nsINode> commonAncestor = aRange->GetCommonAncestorContainer(rv); |
| |
1655 if (rv.Failed()) return rv.ErrorCode(); |
| |
1656 |
| |
1657 nsCOMPtr<nsINode> startContainer = aRange->GetStartContainer(rv); |
| |
1658 if (rv.Failed()) return rv.ErrorCode(); |
| |
1659 nsCOMPtr<nsINode> endContainer = aRange->GetEndContainer(rv); |
| |
1660 if (rv.Failed()) return rv.ErrorCode(); |
| |
1661 |
| |
1662 // Collapse to one of the end points if they are already in the |
| |
1663 // commonAncestor. This should work ok since this method is called |
| |
1664 // immediately after a delete or extract that leaves no content |
| |
1665 // between the 2 end points! |
| |
1666 |
| |
1667 if (startContainer == commonAncestor) |
| |
1668 return aRange->Collapse(true); |
| |
1669 if (endContainer == commonAncestor) |
| |
1670 return aRange->Collapse(false); |
| |
1671 |
| |
1672 // End points are at differing levels. We want to collapse to the |
| |
1673 // point that is between the 2 subtrees that contain each point, |
| |
1674 // under the common ancestor. |
| |
1675 |
| |
1676 nsCOMPtr<nsINode> nodeToSelect(startContainer); |
| |
1677 |
| |
1678 while (nodeToSelect) |
| |
1679 { |
| |
1680 nsCOMPtr<nsINode> parent = nodeToSelect->GetParentNode(); |
| |
1681 if (parent == commonAncestor) |
| |
1682 break; // We found the nodeToSelect! |
| |
1683 |
| |
1684 nodeToSelect = parent; |
| |
1685 } |
| |
1686 |
| |
1687 if (!nodeToSelect) |
| |
1688 return NS_ERROR_FAILURE; // This should never happen! |
| |
1689 |
| |
1690 aRange->SelectNode(*nodeToSelect, rv); |
| |
1691 if (rv.Failed()) return rv.ErrorCode(); |
| |
1692 |
| |
1693 return aRange->Collapse(false); |
| |
1694 } |
| |
1695 |
| |
1696 /** |
| |
1697 * Split a data node into two parts. |
| |
1698 * |
| |
1699 * @param aStartNode The original node we are trying to split. |
| |
1700 * @param aStartIndex The index at which to split. |
| |
1701 * @param aEndNode The second node. |
| |
1702 * @param aCloneAfterOriginal Set false if the original node should be the |
| |
1703 * latter one after split. |
| |
1704 */ |
| |
1705 static nsresult SplitDataNode(nsIDOMCharacterData* aStartNode, |
| |
1706 uint32_t aStartIndex, |
| |
1707 nsIDOMCharacterData** aEndNode, |
| |
1708 bool aCloneAfterOriginal = true) |
| |
1709 { |
| |
1710 nsresult rv; |
| |
1711 nsCOMPtr<nsINode> node = do_QueryInterface(aStartNode); |
| |
1712 NS_ENSURE_STATE(node && node->IsNodeOfType(nsINode::eDATA_NODE)); |
| |
1713 nsGenericDOMDataNode* dataNode = static_cast<nsGenericDOMDataNode*>(node.get()); |
| |
1714 |
| |
1715 nsCOMPtr<nsIContent> newData; |
| |
1716 rv = dataNode->SplitData(aStartIndex, getter_AddRefs(newData), |
| |
1717 aCloneAfterOriginal); |
| |
1718 NS_ENSURE_SUCCESS(rv, rv); |
| |
1719 return CallQueryInterface(newData, aEndNode); |
| |
1720 } |
| |
1721 |
| |
1722 NS_IMETHODIMP |
| |
1723 PrependChild(nsINode* aParent, nsINode* aChild) |
| |
1724 { |
| |
1725 nsCOMPtr<nsINode> first = aParent->GetFirstChild(); |
| |
1726 ErrorResult rv; |
| |
1727 aParent->InsertBefore(*aChild, first, rv); |
| |
1728 return rv.ErrorCode(); |
| |
1729 } |
| |
1730 |
| |
1731 // Helper function for CutContents, making sure that the current node wasn't |
| |
1732 // removed by mutation events (bug 766426) |
| |
1733 static bool |
| |
1734 ValidateCurrentNode(nsRange* aRange, RangeSubtreeIterator& aIter) |
| |
1735 { |
| |
1736 bool before, after; |
| |
1737 nsCOMPtr<nsINode> node = aIter.GetCurrentNode(); |
| |
1738 if (!node) { |
| |
1739 // We don't have to worry that the node was removed if it doesn't exist, |
| |
1740 // e.g., the iterator is done. |
| |
1741 return true; |
| |
1742 } |
| |
1743 |
| |
1744 nsresult res = nsRange::CompareNodeToRange(node, aRange, &before, &after); |
| |
1745 |
| |
1746 return NS_SUCCEEDED(res) && !before && !after; |
| |
1747 } |
| |
1748 |
| |
1749 nsresult |
| |
1750 nsRange::CutContents(DocumentFragment** aFragment) |
| |
1751 { |
| |
1752 if (aFragment) { |
| |
1753 *aFragment = nullptr; |
| |
1754 } |
| |
1755 |
| |
1756 nsCOMPtr<nsIDocument> doc = mStartParent->OwnerDoc(); |
| |
1757 |
| |
1758 ErrorResult res; |
| |
1759 nsCOMPtr<nsINode> commonAncestor = GetCommonAncestorContainer(res); |
| |
1760 NS_ENSURE_SUCCESS(res.ErrorCode(), res.ErrorCode()); |
| |
1761 |
| |
1762 // If aFragment isn't null, create a temporary fragment to hold our return. |
| |
1763 nsRefPtr<DocumentFragment> retval; |
| |
1764 if (aFragment) { |
| |
1765 retval = new DocumentFragment(doc->NodeInfoManager()); |
| |
1766 } |
| |
1767 nsCOMPtr<nsINode> commonCloneAncestor = retval.get(); |
| |
1768 |
| |
1769 // Batch possible DOMSubtreeModified events. |
| |
1770 mozAutoSubtreeModified subtree(mRoot ? mRoot->OwnerDoc(): nullptr, nullptr); |
| |
1771 |
| |
1772 // Save the range end points locally to avoid interference |
| |
1773 // of Range gravity during our edits! |
| |
1774 |
| |
1775 nsCOMPtr<nsINode> startContainer = mStartParent; |
| |
1776 int32_t startOffset = mStartOffset; |
| |
1777 nsCOMPtr<nsINode> endContainer = mEndParent; |
| |
1778 int32_t endOffset = mEndOffset; |
| |
1779 |
| |
1780 if (retval) { |
| |
1781 // For extractContents(), abort early if there's a doctype (bug 719533). |
| |
1782 // This can happen only if the common ancestor is a document, in which case |
| |
1783 // we just need to find its doctype child and check if that's in the range. |
| |
1784 nsCOMPtr<nsIDocument> commonAncestorDocument = do_QueryInterface(commonAncestor); |
| |
1785 if (commonAncestorDocument) { |
| |
1786 nsRefPtr<DocumentType> doctype = commonAncestorDocument->GetDoctype(); |
| |
1787 |
| |
1788 if (doctype && |
| |
1789 nsContentUtils::ComparePoints(startContainer, startOffset, |
| |
1790 doctype, 0) < 0 && |
| |
1791 nsContentUtils::ComparePoints(doctype, 0, |
| |
1792 endContainer, endOffset) < 0) { |
| |
1793 return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR; |
| |
1794 } |
| |
1795 } |
| |
1796 } |
| |
1797 |
| |
1798 // Create and initialize a subtree iterator that will give |
| |
1799 // us all the subtrees within the range. |
| |
1800 |
| |
1801 RangeSubtreeIterator iter; |
| |
1802 |
| |
1803 nsresult rv = iter.Init(this); |
| |
1804 if (NS_FAILED(rv)) return rv; |
| |
1805 |
| |
1806 if (iter.IsDone()) |
| |
1807 { |
| |
1808 // There's nothing for us to delete. |
| |
1809 rv = CollapseRangeAfterDelete(this); |
| |
1810 if (NS_SUCCEEDED(rv) && aFragment) { |
| |
1811 NS_ADDREF(*aFragment = retval); |
| |
1812 } |
| |
1813 return rv; |
| |
1814 } |
| |
1815 |
| |
1816 // We delete backwards to avoid iterator problems! |
| |
1817 |
| |
1818 iter.Last(); |
| |
1819 |
| |
1820 bool handled = false; |
| |
1821 |
| |
1822 // With the exception of text nodes that contain one of the range |
| |
1823 // end points, the subtree iterator should only give us back subtrees |
| |
1824 // that are completely contained between the range's end points. |
| |
1825 |
| |
1826 while (!iter.IsDone()) |
| |
1827 { |
| |
1828 nsCOMPtr<nsINode> nodeToResult; |
| |
1829 nsCOMPtr<nsINode> node = iter.GetCurrentNode(); |
| |
1830 |
| |
1831 // Before we delete anything, advance the iterator to the |
| |
1832 // next subtree. |
| |
1833 |
| |
1834 iter.Prev(); |
| |
1835 |
| |
1836 handled = false; |
| |
1837 |
| |
1838 // If it's CharacterData, make sure we might need to delete |
| |
1839 // part of the data, instead of removing the whole node. |
| |
1840 // |
| |
1841 // XXX_kin: We need to also handle ProcessingInstruction |
| |
1842 // XXX_kin: according to the spec. |
| |
1843 |
| |
1844 nsCOMPtr<nsIDOMCharacterData> charData(do_QueryInterface(node)); |
| |
1845 |
| |
1846 if (charData) |
| |
1847 { |
| |
1848 uint32_t dataLength = 0; |
| |
1849 |
| |
1850 if (node == startContainer) |
| |
1851 { |
| |
1852 if (node == endContainer) |
| |
1853 { |
| |
1854 // This range is completely contained within a single text node. |
| |
1855 // Delete or extract the data between startOffset and endOffset. |
| |
1856 |
| |
1857 if (endOffset > startOffset) |
| |
1858 { |
| |
1859 if (retval) { |
| |
1860 nsAutoString cutValue; |
| |
1861 rv = charData->SubstringData(startOffset, endOffset - startOffset, |
| |
1862 cutValue); |
| |
1863 NS_ENSURE_SUCCESS(rv, rv); |
| |
1864 nsCOMPtr<nsIDOMNode> clone; |
| |
1865 rv = charData->CloneNode(false, 1, getter_AddRefs(clone)); |
| |
1866 NS_ENSURE_SUCCESS(rv, rv); |
| |
1867 clone->SetNodeValue(cutValue); |
| |
1868 nodeToResult = do_QueryInterface(clone); |
| |
1869 } |
| |
1870 |
| |
1871 nsMutationGuard guard; |
| |
1872 rv = charData->DeleteData(startOffset, endOffset - startOffset); |
| |
1873 NS_ENSURE_SUCCESS(rv, rv); |
| |
1874 NS_ENSURE_STATE(!guard.Mutated(0) || |
| |
1875 ValidateCurrentNode(this, iter)); |
| |
1876 } |
| |
1877 |
| |
1878 handled = true; |
| |
1879 } |
| |
1880 else |
| |
1881 { |
| |
1882 // Delete or extract everything after startOffset. |
| |
1883 |
| |
1884 rv = charData->GetLength(&dataLength); |
| |
1885 NS_ENSURE_SUCCESS(rv, rv); |
| |
1886 |
| |
1887 if (dataLength >= (uint32_t)startOffset) |
| |
1888 { |
| |
1889 nsMutationGuard guard; |
| |
1890 nsCOMPtr<nsIDOMCharacterData> cutNode; |
| |
1891 rv = SplitDataNode(charData, startOffset, getter_AddRefs(cutNode)); |
| |
1892 NS_ENSURE_SUCCESS(rv, rv); |
| |
1893 NS_ENSURE_STATE(!guard.Mutated(1) || |
| |
1894 ValidateCurrentNode(this, iter)); |
| |
1895 nodeToResult = do_QueryInterface(cutNode); |
| |
1896 } |
| |
1897 |
| |
1898 handled = true; |
| |
1899 } |
| |
1900 } |
| |
1901 else if (node == endContainer) |
| |
1902 { |
| |
1903 // Delete or extract everything before endOffset. |
| |
1904 |
| |
1905 if (endOffset >= 0) |
| |
1906 { |
| |
1907 nsMutationGuard guard; |
| |
1908 nsCOMPtr<nsIDOMCharacterData> cutNode; |
| |
1909 /* The Range spec clearly states clones get cut and original nodes |
| |
1910 remain behind, so use false as the last parameter. |
| |
1911 */ |
| |
1912 rv = SplitDataNode(charData, endOffset, getter_AddRefs(cutNode), |
| |
1913 false); |
| |
1914 NS_ENSURE_SUCCESS(rv, rv); |
| |
1915 NS_ENSURE_STATE(!guard.Mutated(1) || |
| |
1916 ValidateCurrentNode(this, iter)); |
| |
1917 nodeToResult = do_QueryInterface(cutNode); |
| |
1918 } |
| |
1919 |
| |
1920 handled = true; |
| |
1921 } |
| |
1922 } |
| |
1923 |
| |
1924 if (!handled && (node == endContainer || node == startContainer)) |
| |
1925 { |
| |
1926 if (node && node->IsElement() && |
| |
1927 ((node == endContainer && endOffset == 0) || |
| |
1928 (node == startContainer && |
| |
1929 int32_t(node->AsElement()->GetChildCount()) == startOffset))) |
| |
1930 { |
| |
1931 if (retval) { |
| |
1932 ErrorResult rv; |
| |
1933 nodeToResult = node->CloneNode(false, rv); |
| |
1934 NS_ENSURE_SUCCESS(rv.ErrorCode(), rv.ErrorCode()); |
| |
1935 } |
| |
1936 handled = true; |
| |
1937 } |
| |
1938 } |
| |
1939 |
| |
1940 if (!handled) |
| |
1941 { |
| |
1942 // node was not handled above, so it must be completely contained |
| |
1943 // within the range. Just remove it from the tree! |
| |
1944 nodeToResult = node; |
| |
1945 } |
| |
1946 |
| |
1947 uint32_t parentCount = 0; |
| |
1948 // Set the result to document fragment if we have 'retval'. |
| |
1949 if (retval) { |
| |
1950 nsCOMPtr<nsINode> oldCommonAncestor = commonAncestor; |
| |
1951 if (!iter.IsDone()) { |
| |
1952 // Setup the parameters for the next iteration of the loop. |
| |
1953 nsCOMPtr<nsINode> prevNode = iter.GetCurrentNode(); |
| |
1954 NS_ENSURE_STATE(prevNode); |
| |
1955 |
| |
1956 // Get node's and prevNode's common parent. Do this before moving |
| |
1957 // nodes from original DOM to result fragment. |
| |
1958 commonAncestor = nsContentUtils::GetCommonAncestor(node, prevNode); |
| |
1959 NS_ENSURE_STATE(commonAncestor); |
| |
1960 |
| |
1961 nsCOMPtr<nsINode> parentCounterNode = node; |
| |
1962 while (parentCounterNode && parentCounterNode != commonAncestor) |
| |
1963 { |
| |
1964 ++parentCount; |
| |
1965 parentCounterNode = parentCounterNode->GetParentNode(); |
| |
1966 NS_ENSURE_STATE(parentCounterNode); |
| |
1967 } |
| |
1968 } |
| |
1969 |
| |
1970 // Clone the parent hierarchy between commonAncestor and node. |
| |
1971 nsCOMPtr<nsINode> closestAncestor, farthestAncestor; |
| |
1972 rv = CloneParentsBetween(oldCommonAncestor, node, |
| |
1973 getter_AddRefs(closestAncestor), |
| |
1974 getter_AddRefs(farthestAncestor)); |
| |
1975 NS_ENSURE_SUCCESS(rv, rv); |
| |
1976 |
| |
1977 if (farthestAncestor) |
| |
1978 { |
| |
1979 nsCOMPtr<nsINode> n = do_QueryInterface(commonCloneAncestor); |
| |
1980 rv = PrependChild(n, farthestAncestor); |
| |
1981 NS_ENSURE_SUCCESS(rv, rv); |
| |
1982 } |
| |
1983 |
| |
1984 nsMutationGuard guard; |
| |
1985 nsCOMPtr<nsINode> parent = nodeToResult->GetParentNode(); |
| |
1986 rv = closestAncestor ? PrependChild(closestAncestor, nodeToResult) |
| |
1987 : PrependChild(commonCloneAncestor, nodeToResult); |
| |
1988 NS_ENSURE_SUCCESS(rv, rv); |
| |
1989 NS_ENSURE_STATE(!guard.Mutated(parent ? 2 : 1) || |
| |
1990 ValidateCurrentNode(this, iter)); |
| |
1991 } else if (nodeToResult) { |
| |
1992 nsMutationGuard guard; |
| |
1993 nsCOMPtr<nsINode> node = nodeToResult; |
| |
1994 nsINode* parent = node->GetParentNode(); |
| |
1995 if (parent) { |
| |
1996 mozilla::ErrorResult error; |
| |
1997 parent->RemoveChild(*node, error); |
| |
1998 NS_ENSURE_FALSE(error.Failed(), error.ErrorCode()); |
| |
1999 } |
| |
2000 NS_ENSURE_STATE(!guard.Mutated(1) || |
| |
2001 ValidateCurrentNode(this, iter)); |
| |
2002 } |
| |
2003 |
| |
2004 if (!iter.IsDone() && retval) { |
| |
2005 // Find the equivalent of commonAncestor in the cloned tree. |
| |
2006 nsCOMPtr<nsINode> newCloneAncestor = nodeToResult; |
| |
2007 for (uint32_t i = parentCount; i; --i) |
| |
2008 { |
| |
2009 newCloneAncestor = newCloneAncestor->GetParentNode(); |
| |
2010 NS_ENSURE_STATE(newCloneAncestor); |
| |
2011 } |
| |
2012 commonCloneAncestor = newCloneAncestor; |
| |
2013 } |
| |
2014 } |
| |
2015 |
| |
2016 rv = CollapseRangeAfterDelete(this); |
| |
2017 if (NS_SUCCEEDED(rv) && aFragment) { |
| |
2018 NS_ADDREF(*aFragment = retval); |
| |
2019 } |
| |
2020 return rv; |
| |
2021 } |
| |
2022 |
| |
2023 NS_IMETHODIMP |
| |
2024 nsRange::DeleteContents() |
| |
2025 { |
| |
2026 return CutContents(nullptr); |
| |
2027 } |
| |
2028 |
| |
2029 void |
| |
2030 nsRange::DeleteContents(ErrorResult& aRv) |
| |
2031 { |
| |
2032 aRv = CutContents(nullptr); |
| |
2033 } |
| |
2034 |
| |
2035 NS_IMETHODIMP |
| |
2036 nsRange::ExtractContents(nsIDOMDocumentFragment** aReturn) |
| |
2037 { |
| |
2038 NS_ENSURE_ARG_POINTER(aReturn); |
| |
2039 nsRefPtr<DocumentFragment> fragment; |
| |
2040 nsresult rv = CutContents(getter_AddRefs(fragment)); |
| |
2041 fragment.forget(aReturn); |
| |
2042 return rv; |
| |
2043 } |
| |
2044 |
| |
2045 already_AddRefed<DocumentFragment> |
| |
2046 nsRange::ExtractContents(ErrorResult& rv) |
| |
2047 { |
| |
2048 nsRefPtr<DocumentFragment> fragment; |
| |
2049 rv = CutContents(getter_AddRefs(fragment)); |
| |
2050 return fragment.forget(); |
| |
2051 } |
| |
2052 |
| |
2053 NS_IMETHODIMP |
| |
2054 nsRange::CompareBoundaryPoints(uint16_t aHow, nsIDOMRange* aOtherRange, |
| |
2055 int16_t* aCmpRet) |
| |
2056 { |
| |
2057 nsRange* otherRange = static_cast<nsRange*>(aOtherRange); |
| |
2058 NS_ENSURE_TRUE(otherRange, NS_ERROR_NULL_POINTER); |
| |
2059 |
| |
2060 ErrorResult rv; |
| |
2061 *aCmpRet = CompareBoundaryPoints(aHow, *otherRange, rv); |
| |
2062 return rv.ErrorCode(); |
| |
2063 } |
| |
2064 |
| |
2065 int16_t |
| |
2066 nsRange::CompareBoundaryPoints(uint16_t aHow, nsRange& aOtherRange, |
| |
2067 ErrorResult& rv) |
| |
2068 { |
| |
2069 if (!mIsPositioned || !aOtherRange.IsPositioned()) { |
| |
2070 rv.Throw(NS_ERROR_NOT_INITIALIZED); |
| |
2071 return 0; |
| |
2072 } |
| |
2073 |
| |
2074 nsINode *ourNode, *otherNode; |
| |
2075 int32_t ourOffset, otherOffset; |
| |
2076 |
| |
2077 switch (aHow) { |
| |
2078 case nsIDOMRange::START_TO_START: |
| |
2079 ourNode = mStartParent; |
| |
2080 ourOffset = mStartOffset; |
| |
2081 otherNode = aOtherRange.GetStartParent(); |
| |
2082 otherOffset = aOtherRange.StartOffset(); |
| |
2083 break; |
| |
2084 case nsIDOMRange::START_TO_END: |
| |
2085 ourNode = mEndParent; |
| |
2086 ourOffset = mEndOffset; |
| |
2087 otherNode = aOtherRange.GetStartParent(); |
| |
2088 otherOffset = aOtherRange.StartOffset(); |
| |
2089 break; |
| |
2090 case nsIDOMRange::END_TO_START: |
| |
2091 ourNode = mStartParent; |
| |
2092 ourOffset = mStartOffset; |
| |
2093 otherNode = aOtherRange.GetEndParent(); |
| |
2094 otherOffset = aOtherRange.EndOffset(); |
| |
2095 break; |
| |
2096 case nsIDOMRange::END_TO_END: |
| |
2097 ourNode = mEndParent; |
| |
2098 ourOffset = mEndOffset; |
| |
2099 otherNode = aOtherRange.GetEndParent(); |
| |
2100 otherOffset = aOtherRange.EndOffset(); |
| |
2101 break; |
| |
2102 default: |
| |
2103 // We were passed an illegal value |
| |
2104 rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); |
| |
2105 return 0; |
| |
2106 } |
| |
2107 |
| |
2108 if (mRoot != aOtherRange.GetRoot()) { |
| |
2109 rv.Throw(NS_ERROR_DOM_WRONG_DOCUMENT_ERR); |
| |
2110 return 0; |
| |
2111 } |
| |
2112 |
| |
2113 return nsContentUtils::ComparePoints(ourNode, ourOffset, |
| |
2114 otherNode, otherOffset); |
| |
2115 } |
| |
2116 |
| |
2117 /* static */ nsresult |
| |
2118 nsRange::CloneParentsBetween(nsINode *aAncestor, |
| |
2119 nsINode *aNode, |
| |
2120 nsINode **aClosestAncestor, |
| |
2121 nsINode **aFarthestAncestor) |
| |
2122 { |
| |
2123 NS_ENSURE_ARG_POINTER((aAncestor && aNode && aClosestAncestor && aFarthestAncestor)); |
| |
2124 |
| |
2125 *aClosestAncestor = nullptr; |
| |
2126 *aFarthestAncestor = nullptr; |
| |
2127 |
| |
2128 if (aAncestor == aNode) |
| |
2129 return NS_OK; |
| |
2130 |
| |
2131 nsCOMPtr<nsINode> firstParent, lastParent; |
| |
2132 nsCOMPtr<nsINode> parent = aNode->GetParentNode(); |
| |
2133 |
| |
2134 while(parent && parent != aAncestor) |
| |
2135 { |
| |
2136 ErrorResult rv; |
| |
2137 nsCOMPtr<nsINode> clone = parent->CloneNode(false, rv); |
| |
2138 |
| |
2139 if (rv.Failed()) { |
| |
2140 return rv.ErrorCode(); |
| |
2141 } |
| |
2142 if (!clone) { |
| |
2143 return NS_ERROR_FAILURE; |
| |
2144 } |
| |
2145 |
| |
2146 if (! firstParent) { |
| |
2147 firstParent = lastParent = clone; |
| |
2148 } else { |
| |
2149 clone->AppendChild(*lastParent, rv); |
| |
2150 if (rv.Failed()) return rv.ErrorCode(); |
| |
2151 |
| |
2152 lastParent = clone; |
| |
2153 } |
| |
2154 |
| |
2155 parent = parent->GetParentNode(); |
| |
2156 } |
| |
2157 |
| |
2158 *aClosestAncestor = firstParent; |
| |
2159 NS_IF_ADDREF(*aClosestAncestor); |
| |
2160 |
| |
2161 *aFarthestAncestor = lastParent; |
| |
2162 NS_IF_ADDREF(*aFarthestAncestor); |
| |
2163 |
| |
2164 return NS_OK; |
| |
2165 } |
| |
2166 |
| |
2167 NS_IMETHODIMP |
| |
2168 nsRange::CloneContents(nsIDOMDocumentFragment** aReturn) |
| |
2169 { |
| |
2170 ErrorResult rv; |
| |
2171 *aReturn = CloneContents(rv).take(); |
| |
2172 return rv.ErrorCode(); |
| |
2173 } |
| |
2174 |
| |
2175 already_AddRefed<DocumentFragment> |
| |
2176 nsRange::CloneContents(ErrorResult& aRv) |
| |
2177 { |
| |
2178 nsCOMPtr<nsINode> commonAncestor = GetCommonAncestorContainer(aRv); |
| |
2179 MOZ_ASSERT(!aRv.Failed(), "GetCommonAncestorContainer() shouldn't fail!"); |
| |
2180 |
| |
2181 nsCOMPtr<nsIDocument> doc = mStartParent->OwnerDoc(); |
| |
2182 NS_ASSERTION(doc, "CloneContents needs a document to continue."); |
| |
2183 if (!doc) { |
| |
2184 aRv.Throw(NS_ERROR_FAILURE); |
| |
2185 return nullptr; |
| |
2186 } |
| |
2187 |
| |
2188 // Create a new document fragment in the context of this document, |
| |
2189 // which might be null |
| |
2190 |
| |
2191 |
| |
2192 nsRefPtr<DocumentFragment> clonedFrag = |
| |
2193 new DocumentFragment(doc->NodeInfoManager()); |
| |
2194 |
| |
2195 nsCOMPtr<nsINode> commonCloneAncestor = clonedFrag.get(); |
| |
2196 |
| |
2197 // Create and initialize a subtree iterator that will give |
| |
2198 // us all the subtrees within the range. |
| |
2199 |
| |
2200 RangeSubtreeIterator iter; |
| |
2201 |
| |
2202 aRv = iter.Init(this); |
| |
2203 if (aRv.Failed()) { |
| |
2204 return nullptr; |
| |
2205 } |
| |
2206 |
| |
2207 if (iter.IsDone()) |
| |
2208 { |
| |
2209 // There's nothing to add to the doc frag, we must be done! |
| |
2210 return clonedFrag.forget(); |
| |
2211 } |
| |
2212 |
| |
2213 iter.First(); |
| |
2214 |
| |
2215 // With the exception of text nodes that contain one of the range |
| |
2216 // end points and elements which don't have any content selected the subtree |
| |
2217 // iterator should only give us back subtrees that are completely contained |
| |
2218 // between the range's end points. |
| |
2219 // |
| |
2220 // Unfortunately these subtrees don't contain the parent hierarchy/context |
| |
2221 // that the Range spec requires us to return. This loop clones the |
| |
2222 // parent hierarchy, adds a cloned version of the subtree, to it, then |
| |
2223 // correctly places this new subtree into the doc fragment. |
| |
2224 |
| |
2225 while (!iter.IsDone()) |
| |
2226 { |
| |
2227 nsCOMPtr<nsINode> node = iter.GetCurrentNode(); |
| |
2228 bool deepClone = !node->IsElement() || |
| |
2229 (!(node == mEndParent && mEndOffset == 0) && |
| |
2230 !(node == mStartParent && |
| |
2231 mStartOffset == |
| |
2232 int32_t(node->AsElement()->GetChildCount()))); |
| |
2233 |
| |
2234 // Clone the current subtree! |
| |
2235 |
| |
2236 nsCOMPtr<nsINode> clone = node->CloneNode(deepClone, aRv); |
| |
2237 if (aRv.Failed()) { |
| |
2238 return nullptr; |
| |
2239 } |
| |
2240 |
| |
2241 // If it's CharacterData, make sure we only clone what |
| |
2242 // is in the range. |
| |
2243 // |
| |
2244 // XXX_kin: We need to also handle ProcessingInstruction |
| |
2245 // XXX_kin: according to the spec. |
| |
2246 |
| |
2247 nsCOMPtr<nsIDOMCharacterData> charData(do_QueryInterface(clone)); |
| |
2248 |
| |
2249 if (charData) |
| |
2250 { |
| |
2251 if (node == mEndParent) |
| |
2252 { |
| |
2253 // We only need the data before mEndOffset, so get rid of any |
| |
2254 // data after it. |
| |
2255 |
| |
2256 uint32_t dataLength = 0; |
| |
2257 aRv = charData->GetLength(&dataLength); |
| |
2258 if (aRv.Failed()) { |
| |
2259 return nullptr; |
| |
2260 } |
| |
2261 |
| |
2262 if (dataLength > (uint32_t)mEndOffset) |
| |
2263 { |
| |
2264 aRv = charData->DeleteData(mEndOffset, dataLength - mEndOffset); |
| |
2265 if (aRv.Failed()) { |
| |
2266 return nullptr; |
| |
2267 } |
| |
2268 } |
| |
2269 } |
| |
2270 |
| |
2271 if (node == mStartParent) |
| |
2272 { |
| |
2273 // We don't need any data before mStartOffset, so just |
| |
2274 // delete it! |
| |
2275 |
| |
2276 if (mStartOffset > 0) |
| |
2277 { |
| |
2278 aRv = charData->DeleteData(0, mStartOffset); |
| |
2279 if (aRv.Failed()) { |
| |
2280 return nullptr; |
| |
2281 } |
| |
2282 } |
| |
2283 } |
| |
2284 } |
| |
2285 |
| |
2286 // Clone the parent hierarchy between commonAncestor and node. |
| |
2287 |
| |
2288 nsCOMPtr<nsINode> closestAncestor, farthestAncestor; |
| |
2289 |
| |
2290 aRv = CloneParentsBetween(commonAncestor, node, |
| |
2291 getter_AddRefs(closestAncestor), |
| |
2292 getter_AddRefs(farthestAncestor)); |
| |
2293 |
| |
2294 if (aRv.Failed()) { |
| |
2295 return nullptr; |
| |
2296 } |
| |
2297 |
| |
2298 // Hook the parent hierarchy/context of the subtree into the clone tree. |
| |
2299 |
| |
2300 if (farthestAncestor) |
| |
2301 { |
| |
2302 commonCloneAncestor->AppendChild(*farthestAncestor, aRv); |
| |
2303 |
| |
2304 if (aRv.Failed()) { |
| |
2305 return nullptr; |
| |
2306 } |
| |
2307 } |
| |
2308 |
| |
2309 // Place the cloned subtree into the cloned doc frag tree! |
| |
2310 |
| |
2311 nsCOMPtr<nsINode> cloneNode = do_QueryInterface(clone); |
| |
2312 if (closestAncestor) |
| |
2313 { |
| |
2314 // Append the subtree under closestAncestor since it is the |
| |
2315 // immediate parent of the subtree. |
| |
2316 |
| |
2317 closestAncestor->AppendChild(*cloneNode, aRv); |
| |
2318 } |
| |
2319 else |
| |
2320 { |
| |
2321 // If we get here, there is no missing parent hierarchy between |
| |
2322 // commonAncestor and node, so just append clone to commonCloneAncestor. |
| |
2323 |
| |
2324 commonCloneAncestor->AppendChild(*cloneNode, aRv); |
| |
2325 } |
| |
2326 if (aRv.Failed()) { |
| |
2327 return nullptr; |
| |
2328 } |
| |
2329 |
| |
2330 // Get the next subtree to be processed. The idea here is to setup |
| |
2331 // the parameters for the next iteration of the loop. |
| |
2332 |
| |
2333 iter.Next(); |
| |
2334 |
| |
2335 if (iter.IsDone()) |
| |
2336 break; // We must be done! |
| |
2337 |
| |
2338 nsCOMPtr<nsINode> nextNode = iter.GetCurrentNode(); |
| |
2339 if (!nextNode) { |
| |
2340 aRv.Throw(NS_ERROR_FAILURE); |
| |
2341 return nullptr; |
| |
2342 } |
| |
2343 |
| |
2344 // Get node and nextNode's common parent. |
| |
2345 commonAncestor = nsContentUtils::GetCommonAncestor(node, nextNode); |
| |
2346 |
| |
2347 if (!commonAncestor) { |
| |
2348 aRv.Throw(NS_ERROR_FAILURE); |
| |
2349 return nullptr; |
| |
2350 } |
| |
2351 |
| |
2352 // Find the equivalent of commonAncestor in the cloned tree! |
| |
2353 |
| |
2354 while (node && node != commonAncestor) |
| |
2355 { |
| |
2356 node = node->GetParentNode(); |
| |
2357 if (aRv.Failed()) { |
| |
2358 return nullptr; |
| |
2359 } |
| |
2360 |
| |
2361 if (!node) { |
| |
2362 aRv.Throw(NS_ERROR_FAILURE); |
| |
2363 return nullptr; |
| |
2364 } |
| |
2365 |
| |
2366 cloneNode = cloneNode->GetParentNode(); |
| |
2367 if (!cloneNode) { |
| |
2368 aRv.Throw(NS_ERROR_FAILURE); |
| |
2369 return nullptr; |
| |
2370 } |
| |
2371 } |
| |
2372 |
| |
2373 commonCloneAncestor = cloneNode; |
| |
2374 } |
| |
2375 |
| |
2376 return clonedFrag.forget(); |
| |
2377 } |
| |
2378 |
| |
2379 already_AddRefed<nsRange> |
| |
2380 nsRange::CloneRange() const |
| |
2381 { |
| |
2382 nsRefPtr<nsRange> range = new nsRange(mOwner); |
| |
2383 |
| |
2384 range->SetMaySpanAnonymousSubtrees(mMaySpanAnonymousSubtrees); |
| |
2385 |
| |
2386 range->DoSetRange(mStartParent, mStartOffset, mEndParent, mEndOffset, mRoot); |
| |
2387 |
| |
2388 return range.forget(); |
| |
2389 } |
| |
2390 |
| |
2391 NS_IMETHODIMP |
| |
2392 nsRange::CloneRange(nsIDOMRange** aReturn) |
| |
2393 { |
| |
2394 *aReturn = CloneRange().take(); |
| |
2395 return NS_OK; |
| |
2396 } |
| |
2397 |
| |
2398 NS_IMETHODIMP |
| |
2399 nsRange::InsertNode(nsIDOMNode* aNode) |
| |
2400 { |
| |
2401 nsCOMPtr<nsINode> node = do_QueryInterface(aNode); |
| |
2402 if (!node) { |
| |
2403 return NS_ERROR_DOM_NOT_OBJECT_ERR; |
| |
2404 } |
| |
2405 |
| |
2406 ErrorResult rv; |
| |
2407 InsertNode(*node, rv); |
| |
2408 return rv.ErrorCode(); |
| |
2409 } |
| |
2410 |
| |
2411 void |
| |
2412 nsRange::InsertNode(nsINode& aNode, ErrorResult& aRv) |
| |
2413 { |
| |
2414 if (!nsContentUtils::CanCallerAccess(&aNode)) { |
| |
2415 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); |
| |
2416 return; |
| |
2417 } |
| |
2418 |
| |
2419 int32_t tStartOffset = StartOffset(); |
| |
2420 |
| |
2421 nsCOMPtr<nsINode> tStartContainer = GetStartContainer(aRv); |
| |
2422 if (aRv.Failed()) { |
| |
2423 return; |
| |
2424 } |
| |
2425 |
| |
2426 // This is the node we'll be inserting before, and its parent |
| |
2427 nsCOMPtr<nsINode> referenceNode; |
| |
2428 nsCOMPtr<nsINode> referenceParentNode = tStartContainer; |
| |
2429 |
| |
2430 nsCOMPtr<nsIDOMText> startTextNode(do_QueryInterface(tStartContainer)); |
| |
2431 nsCOMPtr<nsIDOMNodeList> tChildList; |
| |
2432 if (startTextNode) { |
| |
2433 referenceParentNode = tStartContainer->GetParentNode(); |
| |
2434 if (!referenceParentNode) { |
| |
2435 aRv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR); |
| |
2436 return; |
| |
2437 } |
| |
2438 |
| |
2439 nsCOMPtr<nsIDOMText> secondPart; |
| |
2440 aRv = startTextNode->SplitText(tStartOffset, getter_AddRefs(secondPart)); |
| |
2441 if (aRv.Failed()) { |
| |
2442 return; |
| |
2443 } |
| |
2444 |
| |
2445 referenceNode = do_QueryInterface(secondPart); |
| |
2446 } else { |
| |
2447 aRv = tStartContainer->AsDOMNode()->GetChildNodes(getter_AddRefs(tChildList)); |
| |
2448 if (aRv.Failed()) { |
| |
2449 return; |
| |
2450 } |
| |
2451 |
| |
2452 // find the insertion point in the DOM and insert the Node |
| |
2453 nsCOMPtr<nsIDOMNode> q; |
| |
2454 aRv = tChildList->Item(tStartOffset, getter_AddRefs(q)); |
| |
2455 referenceNode = do_QueryInterface(q); |
| |
2456 if (aRv.Failed()) { |
| |
2457 return; |
| |
2458 } |
| |
2459 } |
| |
2460 |
| |
2461 // We might need to update the end to include the new node (bug 433662). |
| |
2462 // Ideally we'd only do this if needed, but it's tricky to know when it's |
| |
2463 // needed in advance (bug 765799). |
| |
2464 int32_t newOffset; |
| |
2465 |
| |
2466 if (referenceNode) { |
| |
2467 newOffset = IndexOf(referenceNode); |
| |
2468 } else { |
| |
2469 uint32_t length; |
| |
2470 aRv = tChildList->GetLength(&length); |
| |
2471 if (aRv.Failed()) { |
| |
2472 return; |
| |
2473 } |
| |
2474 |
| |
2475 newOffset = length; |
| |
2476 } |
| |
2477 |
| |
2478 if (aNode.NodeType() == nsIDOMNode::DOCUMENT_FRAGMENT_NODE) { |
| |
2479 newOffset += aNode.GetChildCount(); |
| |
2480 } else { |
| |
2481 newOffset++; |
| |
2482 } |
| |
2483 |
| |
2484 // Now actually insert the node |
| |
2485 nsCOMPtr<nsINode> tResultNode; |
| |
2486 tResultNode = referenceParentNode->InsertBefore(aNode, referenceNode, aRv); |
| |
2487 if (aRv.Failed()) { |
| |
2488 return; |
| |
2489 } |
| |
2490 |
| |
2491 if (Collapsed()) { |
| |
2492 aRv = SetEnd(referenceParentNode, newOffset); |
| |
2493 } |
| |
2494 } |
| |
2495 |
| |
2496 NS_IMETHODIMP |
| |
2497 nsRange::SurroundContents(nsIDOMNode* aNewParent) |
| |
2498 { |
| |
2499 nsCOMPtr<nsINode> node = do_QueryInterface(aNewParent); |
| |
2500 if (!node) { |
| |
2501 return NS_ERROR_DOM_NOT_OBJECT_ERR; |
| |
2502 } |
| |
2503 ErrorResult rv; |
| |
2504 SurroundContents(*node, rv); |
| |
2505 return rv.ErrorCode(); |
| |
2506 } |
| |
2507 |
| |
2508 void |
| |
2509 nsRange::SurroundContents(nsINode& aNewParent, ErrorResult& aRv) |
| |
2510 { |
| |
2511 if (!nsContentUtils::CanCallerAccess(&aNewParent)) { |
| |
2512 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); |
| |
2513 return; |
| |
2514 } |
| |
2515 |
| |
2516 if (!mRoot) { |
| |
2517 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); |
| |
2518 return; |
| |
2519 } |
| |
2520 // INVALID_STATE_ERROR: Raised if the Range partially selects a non-text |
| |
2521 // node. |
| |
2522 if (mStartParent != mEndParent) { |
| |
2523 bool startIsText = mStartParent->IsNodeOfType(nsINode::eTEXT); |
| |
2524 bool endIsText = mEndParent->IsNodeOfType(nsINode::eTEXT); |
| |
2525 nsINode* startGrandParent = mStartParent->GetParentNode(); |
| |
2526 nsINode* endGrandParent = mEndParent->GetParentNode(); |
| |
2527 if (!((startIsText && endIsText && |
| |
2528 startGrandParent && |
| |
2529 startGrandParent == endGrandParent) || |
| |
2530 (startIsText && |
| |
2531 startGrandParent && |
| |
2532 startGrandParent == mEndParent) || |
| |
2533 (endIsText && |
| |
2534 endGrandParent && |
| |
2535 endGrandParent == mStartParent))) { |
| |
2536 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); |
| |
2537 return; |
| |
2538 } |
| |
2539 } |
| |
2540 |
| |
2541 // INVALID_NODE_TYPE_ERROR if aNewParent is something that can't be inserted |
| |
2542 // (Document, DocumentType, DocumentFragment) |
| |
2543 uint16_t nodeType = aNewParent.NodeType(); |
| |
2544 if (nodeType == nsIDOMNode::DOCUMENT_NODE || |
| |
2545 nodeType == nsIDOMNode::DOCUMENT_TYPE_NODE || |
| |
2546 nodeType == nsIDOMNode::DOCUMENT_FRAGMENT_NODE) { |
| |
2547 aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR); |
| |
2548 return; |
| |
2549 } |
| |
2550 |
| |
2551 // Extract the contents within the range. |
| |
2552 |
| |
2553 nsRefPtr<DocumentFragment> docFrag = ExtractContents(aRv); |
| |
2554 |
| |
2555 if (aRv.Failed()) { |
| |
2556 return; |
| |
2557 } |
| |
2558 |
| |
2559 if (!docFrag) { |
| |
2560 aRv.Throw(NS_ERROR_FAILURE); |
| |
2561 return; |
| |
2562 } |
| |
2563 |
| |
2564 // Spec says we need to remove all of aNewParent's |
| |
2565 // children prior to insertion. |
| |
2566 |
| |
2567 nsCOMPtr<nsINodeList> children = aNewParent.ChildNodes(); |
| |
2568 if (!children) { |
| |
2569 aRv.Throw(NS_ERROR_FAILURE); |
| |
2570 return; |
| |
2571 } |
| |
2572 |
| |
2573 uint32_t numChildren = children->Length(); |
| |
2574 |
| |
2575 while (numChildren) |
| |
2576 { |
| |
2577 nsCOMPtr<nsINode> child = children->Item(--numChildren); |
| |
2578 if (!child) { |
| |
2579 aRv.Throw(NS_ERROR_FAILURE); |
| |
2580 return; |
| |
2581 } |
| |
2582 |
| |
2583 aNewParent.RemoveChild(*child, aRv); |
| |
2584 if (aRv.Failed()) { |
| |
2585 return; |
| |
2586 } |
| |
2587 } |
| |
2588 |
| |
2589 // Insert aNewParent at the range's start point. |
| |
2590 |
| |
2591 InsertNode(aNewParent, aRv); |
| |
2592 if (aRv.Failed()) { |
| |
2593 return; |
| |
2594 } |
| |
2595 |
| |
2596 // Append the content we extracted under aNewParent. |
| |
2597 aNewParent.AppendChild(*docFrag, aRv); |
| |
2598 if (aRv.Failed()) { |
| |
2599 return; |
| |
2600 } |
| |
2601 |
| |
2602 // Select aNewParent, and its contents. |
| |
2603 |
| |
2604 SelectNode(aNewParent, aRv); |
| |
2605 } |
| |
2606 |
| |
2607 NS_IMETHODIMP |
| |
2608 nsRange::ToString(nsAString& aReturn) |
| |
2609 { |
| |
2610 // clear the string |
| |
2611 aReturn.Truncate(); |
| |
2612 |
| |
2613 // If we're unpositioned, return the empty string |
| |
2614 if (!mIsPositioned) { |
| |
2615 return NS_OK; |
| |
2616 } |
| |
2617 |
| |
2618 #ifdef DEBUG_range |
| |
2619 printf("Range dump: -----------------------\n"); |
| |
2620 #endif /* DEBUG */ |
| |
2621 |
| |
2622 // effeciency hack for simple case |
| |
2623 if (mStartParent == mEndParent) |
| |
2624 { |
| |
2625 nsCOMPtr<nsIDOMText> textNode( do_QueryInterface(mStartParent) ); |
| |
2626 |
| |
2627 if (textNode) |
| |
2628 { |
| |
2629 #ifdef DEBUG_range |
| |
2630 // If debug, dump it: |
| |
2631 nsCOMPtr<nsIContent> cN (do_QueryInterface(mStartParent)); |
| |
2632 if (cN) cN->List(stdout); |
| |
2633 printf("End Range dump: -----------------------\n"); |
| |
2634 #endif /* DEBUG */ |
| |
2635 |
| |
2636 // grab the text |
| |
2637 if (NS_FAILED(textNode->SubstringData(mStartOffset,mEndOffset-mStartOffset,aReturn))) |
| |
2638 return NS_ERROR_UNEXPECTED; |
| |
2639 return NS_OK; |
| |
2640 } |
| |
2641 } |
| |
2642 |
| |
2643 /* complex case: mStartParent != mEndParent, or mStartParent not a text node |
| |
2644 revisit - there are potential optimizations here and also tradeoffs. |
| |
2645 */ |
| |
2646 |
| |
2647 nsCOMPtr<nsIContentIterator> iter = NS_NewContentIterator(); |
| |
2648 nsresult rv = iter->Init(this); |
| |
2649 NS_ENSURE_SUCCESS(rv, rv); |
| |
2650 |
| |
2651 nsString tempString; |
| |
2652 |
| |
2653 // loop through the content iterator, which returns nodes in the range in |
| |
2654 // close tag order, and grab the text from any text node |
| |
2655 while (!iter->IsDone()) |
| |
2656 { |
| |
2657 nsINode *n = iter->GetCurrentNode(); |
| |
2658 |
| |
2659 #ifdef DEBUG_range |
| |
2660 // If debug, dump it: |
| |
2661 n->List(stdout); |
| |
2662 #endif /* DEBUG */ |
| |
2663 nsCOMPtr<nsIDOMText> textNode(do_QueryInterface(n)); |
| |
2664 if (textNode) // if it's a text node, get the text |
| |
2665 { |
| |
2666 if (n == mStartParent) // only include text past start offset |
| |
2667 { |
| |
2668 uint32_t strLength; |
| |
2669 textNode->GetLength(&strLength); |
| |
2670 textNode->SubstringData(mStartOffset,strLength-mStartOffset,tempString); |
| |
2671 aReturn += tempString; |
| |
2672 } |
| |
2673 else if (n == mEndParent) // only include text before end offset |
| |
2674 { |
| |
2675 textNode->SubstringData(0,mEndOffset,tempString); |
| |
2676 aReturn += tempString; |
| |
2677 } |
| |
2678 else // grab the whole kit-n-kaboodle |
| |
2679 { |
| |
2680 textNode->GetData(tempString); |
| |
2681 aReturn += tempString; |
| |
2682 } |
| |
2683 } |
| |
2684 |
| |
2685 iter->Next(); |
| |
2686 } |
| |
2687 |
| |
2688 #ifdef DEBUG_range |
| |
2689 printf("End Range dump: -----------------------\n"); |
| |
2690 #endif /* DEBUG */ |
| |
2691 return NS_OK; |
| |
2692 } |
| |
2693 |
| |
2694 |
| |
2695 |
| |
2696 NS_IMETHODIMP |
| |
2697 nsRange::Detach() |
| |
2698 { |
| |
2699 // No-op, but still set mIsDetached for telemetry (bug 702948) |
| |
2700 mIsDetached = true; |
| |
2701 return NS_OK; |
| |
2702 } |
| |
2703 |
| |
2704 NS_IMETHODIMP |
| |
2705 nsRange::CreateContextualFragment(const nsAString& aFragment, |
| |
2706 nsIDOMDocumentFragment** aReturn) |
| |
2707 { |
| |
2708 if (mIsPositioned) { |
| |
2709 return nsContentUtils::CreateContextualFragment(mStartParent, aFragment, |
| |
2710 false, aReturn); |
| |
2711 } |
| |
2712 return NS_ERROR_FAILURE; |
| |
2713 } |
| |
2714 |
| |
2715 already_AddRefed<DocumentFragment> |
| |
2716 nsRange::CreateContextualFragment(const nsAString& aFragment, ErrorResult& aRv) |
| |
2717 { |
| |
2718 if (!mIsPositioned) { |
| |
2719 aRv.Throw(NS_ERROR_FAILURE); |
| |
2720 return nullptr; |
| |
2721 } |
| |
2722 |
| |
2723 return nsContentUtils::CreateContextualFragment(mStartParent, aFragment, |
| |
2724 false, aRv); |
| |
2725 } |
| |
2726 |
| |
2727 static void ExtractRectFromOffset(nsIFrame* aFrame, |
| |
2728 const nsIFrame* aRelativeTo, |
| |
2729 const int32_t aOffset, nsRect* aR, bool aKeepLeft) |
| |
2730 { |
| |
2731 nsPoint point; |
| |
2732 aFrame->GetPointFromOffset(aOffset, &point); |
| |
2733 |
| |
2734 point += aFrame->GetOffsetTo(aRelativeTo); |
| |
2735 |
| |
2736 //given a point.x, extract left or right portion of rect aR |
| |
2737 //point.x has to be within this rect |
| |
2738 NS_ASSERTION(aR->x <= point.x && point.x <= aR->XMost(), |
| |
2739 "point.x should not be outside of rect r"); |
| |
2740 |
| |
2741 if (aKeepLeft) { |
| |
2742 aR->width = point.x - aR->x; |
| |
2743 } else { |
| |
2744 aR->width = aR->XMost() - point.x; |
| |
2745 aR->x = point.x; |
| |
2746 } |
| |
2747 } |
| |
2748 |
| |
2749 static nsTextFrame* |
| |
2750 GetTextFrameForContent(nsIContent* aContent) |
| |
2751 { |
| |
2752 nsIPresShell* presShell = aContent->OwnerDoc()->GetShell(); |
| |
2753 if (presShell) { |
| |
2754 presShell->FrameConstructor()->EnsureFrameForTextNode( |
| |
2755 static_cast<nsGenericDOMDataNode*>(aContent)); |
| |
2756 aContent->OwnerDoc()->FlushPendingNotifications(Flush_Layout); |
| |
2757 nsIFrame* frame = aContent->GetPrimaryFrame(); |
| |
2758 if (frame && frame->GetType() == nsGkAtoms::textFrame) { |
| |
2759 return static_cast<nsTextFrame*>(frame); |
| |
2760 } |
| |
2761 } |
| |
2762 return nullptr; |
| |
2763 } |
| |
2764 |
| |
2765 static nsresult GetPartialTextRect(nsLayoutUtils::RectCallback* aCallback, |
| |
2766 nsIContent* aContent, int32_t aStartOffset, int32_t aEndOffset) |
| |
2767 { |
| |
2768 nsTextFrame* textFrame = GetTextFrameForContent(aContent); |
| |
2769 if (textFrame) { |
| |
2770 nsIFrame* relativeTo = nsLayoutUtils::GetContainingBlockForClientRect(textFrame); |
| |
2771 for (nsTextFrame* f = textFrame; f; f = static_cast<nsTextFrame*>(f->GetNextContinuation())) { |
| |
2772 int32_t fstart = f->GetContentOffset(), fend = f->GetContentEnd(); |
| |
2773 if (fend <= aStartOffset || fstart >= aEndOffset) |
| |
2774 continue; |
| |
2775 |
| |
2776 // overlapping with the offset we want |
| |
2777 f->EnsureTextRun(nsTextFrame::eInflated); |
| |
2778 NS_ENSURE_TRUE(f->GetTextRun(nsTextFrame::eInflated), NS_ERROR_OUT_OF_MEMORY); |
| |
2779 bool rtl = f->GetTextRun(nsTextFrame::eInflated)->IsRightToLeft(); |
| |
2780 nsRect r(f->GetOffsetTo(relativeTo), f->GetSize()); |
| |
2781 if (fstart < aStartOffset) { |
| |
2782 // aStartOffset is within this frame |
| |
2783 ExtractRectFromOffset(f, relativeTo, aStartOffset, &r, rtl); |
| |
2784 } |
| |
2785 if (fend > aEndOffset) { |
| |
2786 // aEndOffset is in the middle of this frame |
| |
2787 ExtractRectFromOffset(f, relativeTo, aEndOffset, &r, !rtl); |
| |
2788 } |
| |
2789 aCallback->AddRect(r); |
| |
2790 } |
| |
2791 } |
| |
2792 return NS_OK; |
| |
2793 } |
| |
2794 |
| |
2795 static void CollectClientRects(nsLayoutUtils::RectCallback* aCollector, |
| |
2796 nsRange* aRange, |
| |
2797 nsINode* aStartParent, int32_t aStartOffset, |
| |
2798 nsINode* aEndParent, int32_t aEndOffset) |
| |
2799 { |
| |
2800 // Hold strong pointers across the flush |
| |
2801 nsCOMPtr<nsINode> startContainer = aStartParent; |
| |
2802 nsCOMPtr<nsINode> endContainer = aEndParent; |
| |
2803 |
| |
2804 // Flush out layout so our frames are up to date. |
| |
2805 if (!aStartParent->IsInDoc()) { |
| |
2806 return; |
| |
2807 } |
| |
2808 |
| |
2809 aStartParent->OwnerDoc()->FlushPendingNotifications(Flush_Layout); |
| |
2810 |
| |
2811 // Recheck whether we're still in the document |
| |
2812 if (!aStartParent->IsInDoc()) { |
| |
2813 return; |
| |
2814 } |
| |
2815 |
| |
2816 RangeSubtreeIterator iter; |
| |
2817 |
| |
2818 nsresult rv = iter.Init(aRange); |
| |
2819 if (NS_FAILED(rv)) return; |
| |
2820 |
| |
2821 if (iter.IsDone()) { |
| |
2822 // the range is collapsed, only continue if the cursor is in a text node |
| |
2823 nsCOMPtr<nsIContent> content = do_QueryInterface(aStartParent); |
| |
2824 if (content && content->IsNodeOfType(nsINode::eTEXT)) { |
| |
2825 nsTextFrame* textFrame = GetTextFrameForContent(content); |
| |
2826 if (textFrame) { |
| |
2827 int32_t outOffset; |
| |
2828 nsIFrame* outFrame; |
| |
2829 textFrame->GetChildFrameContainingOffset(aStartOffset, false, |
| |
2830 &outOffset, &outFrame); |
| |
2831 if (outFrame) { |
| |
2832 nsIFrame* relativeTo = |
| |
2833 nsLayoutUtils::GetContainingBlockForClientRect(outFrame); |
| |
2834 nsRect r(outFrame->GetOffsetTo(relativeTo), outFrame->GetSize()); |
| |
2835 ExtractRectFromOffset(outFrame, relativeTo, aStartOffset, &r, false); |
| |
2836 r.width = 0; |
| |
2837 aCollector->AddRect(r); |
| |
2838 } |
| |
2839 } |
| |
2840 } |
| |
2841 return; |
| |
2842 } |
| |
2843 |
| |
2844 do { |
| |
2845 nsCOMPtr<nsINode> node = iter.GetCurrentNode(); |
| |
2846 iter.Next(); |
| |
2847 nsCOMPtr<nsIContent> content = do_QueryInterface(node); |
| |
2848 if (!content) |
| |
2849 continue; |
| |
2850 if (content->IsNodeOfType(nsINode::eTEXT)) { |
| |
2851 if (node == startContainer) { |
| |
2852 int32_t offset = startContainer == endContainer ? |
| |
2853 aEndOffset : content->GetText()->GetLength(); |
| |
2854 GetPartialTextRect(aCollector, content, aStartOffset, offset); |
| |
2855 continue; |
| |
2856 } else if (node == endContainer) { |
| |
2857 GetPartialTextRect(aCollector, content, 0, aEndOffset); |
| |
2858 continue; |
| |
2859 } |
| |
2860 } |
| |
2861 |
| |
2862 nsIFrame* frame = content->GetPrimaryFrame(); |
| |
2863 if (frame) { |
| |
2864 nsLayoutUtils::GetAllInFlowRects(frame, |
| |
2865 nsLayoutUtils::GetContainingBlockForClientRect(frame), aCollector); |
| |
2866 } |
| |
2867 } while (!iter.IsDone()); |
| |
2868 } |
| |
2869 |
| |
2870 NS_IMETHODIMP |
| |
2871 nsRange::GetBoundingClientRect(nsIDOMClientRect** aResult) |
| |
2872 { |
| |
2873 *aResult = GetBoundingClientRect().take(); |
| |
2874 return NS_OK; |
| |
2875 } |
| |
2876 |
| |
2877 already_AddRefed<DOMRect> |
| |
2878 nsRange::GetBoundingClientRect() |
| |
2879 { |
| |
2880 nsRefPtr<DOMRect> rect = new DOMRect(ToSupports(this)); |
| |
2881 if (!mStartParent) { |
| |
2882 return rect.forget(); |
| |
2883 } |
| |
2884 |
| |
2885 nsLayoutUtils::RectAccumulator accumulator; |
| |
2886 CollectClientRects(&accumulator, this, mStartParent, mStartOffset, |
| |
2887 mEndParent, mEndOffset); |
| |
2888 |
| |
2889 nsRect r = accumulator.mResultRect.IsEmpty() ? accumulator.mFirstRect : |
| |
2890 accumulator.mResultRect; |
| |
2891 rect->SetLayoutRect(r); |
| |
2892 return rect.forget(); |
| |
2893 } |
| |
2894 |
| |
2895 NS_IMETHODIMP |
| |
2896 nsRange::GetClientRects(nsIDOMClientRectList** aResult) |
| |
2897 { |
| |
2898 *aResult = GetClientRects().take(); |
| |
2899 return NS_OK; |
| |
2900 } |
| |
2901 |
| |
2902 already_AddRefed<DOMRectList> |
| |
2903 nsRange::GetClientRects() |
| |
2904 { |
| |
2905 if (!mStartParent) { |
| |
2906 return nullptr; |
| |
2907 } |
| |
2908 |
| |
2909 nsRefPtr<DOMRectList> rectList = |
| |
2910 new DOMRectList(static_cast<nsIDOMRange*>(this)); |
| |
2911 |
| |
2912 nsLayoutUtils::RectListBuilder builder(rectList); |
| |
2913 |
| |
2914 CollectClientRects(&builder, this, mStartParent, mStartOffset, |
| |
2915 mEndParent, mEndOffset); |
| |
2916 return rectList.forget(); |
| |
2917 } |
| |
2918 |
| |
2919 NS_IMETHODIMP |
| |
2920 nsRange::GetUsedFontFaces(nsIDOMFontFaceList** aResult) |
| |
2921 { |
| |
2922 *aResult = nullptr; |
| |
2923 |
| |
2924 NS_ENSURE_TRUE(mStartParent, NS_ERROR_UNEXPECTED); |
| |
2925 |
| |
2926 nsCOMPtr<nsINode> startContainer = do_QueryInterface(mStartParent); |
| |
2927 nsCOMPtr<nsINode> endContainer = do_QueryInterface(mEndParent); |
| |
2928 |
| |
2929 // Flush out layout so our frames are up to date. |
| |
2930 nsIDocument* doc = mStartParent->OwnerDoc(); |
| |
2931 NS_ENSURE_TRUE(doc, NS_ERROR_UNEXPECTED); |
| |
2932 doc->FlushPendingNotifications(Flush_Frames); |
| |
2933 |
| |
2934 // Recheck whether we're still in the document |
| |
2935 NS_ENSURE_TRUE(mStartParent->IsInDoc(), NS_ERROR_UNEXPECTED); |
| |
2936 |
| |
2937 nsRefPtr<nsFontFaceList> fontFaceList = new nsFontFaceList(); |
| |
2938 |
| |
2939 RangeSubtreeIterator iter; |
| |
2940 nsresult rv = iter.Init(this); |
| |
2941 NS_ENSURE_SUCCESS(rv, rv); |
| |
2942 |
| |
2943 while (!iter.IsDone()) { |
| |
2944 // only collect anything if the range is not collapsed |
| |
2945 nsCOMPtr<nsINode> node = iter.GetCurrentNode(); |
| |
2946 iter.Next(); |
| |
2947 |
| |
2948 nsCOMPtr<nsIContent> content = do_QueryInterface(node); |
| |
2949 if (!content) { |
| |
2950 continue; |
| |
2951 } |
| |
2952 nsIFrame* frame = content->GetPrimaryFrame(); |
| |
2953 if (!frame) { |
| |
2954 continue; |
| |
2955 } |
| |
2956 |
| |
2957 if (content->IsNodeOfType(nsINode::eTEXT)) { |
| |
2958 if (node == startContainer) { |
| |
2959 int32_t offset = startContainer == endContainer ? |
| |
2960 mEndOffset : content->GetText()->GetLength(); |
| |
2961 nsLayoutUtils::GetFontFacesForText(frame, mStartOffset, offset, |
| |
2962 true, fontFaceList); |
| |
2963 continue; |
| |
2964 } |
| |
2965 if (node == endContainer) { |
| |
2966 nsLayoutUtils::GetFontFacesForText(frame, 0, mEndOffset, |
| |
2967 true, fontFaceList); |
| |
2968 continue; |
| |
2969 } |
| |
2970 } |
| |
2971 |
| |
2972 nsLayoutUtils::GetFontFacesForFrames(frame, fontFaceList); |
| |
2973 } |
| |
2974 |
| |
2975 fontFaceList.forget(aResult); |
| |
2976 return NS_OK; |
| |
2977 } |
| |
2978 |
| |
2979 nsINode* |
| |
2980 nsRange::GetRegisteredCommonAncestor() |
| |
2981 { |
| |
2982 NS_ASSERTION(IsInSelection(), |
| |
2983 "GetRegisteredCommonAncestor only valid for range in selection"); |
| |
2984 nsINode* ancestor = GetNextRangeCommonAncestor(mStartParent); |
| |
2985 while (ancestor) { |
| |
2986 RangeHashTable* ranges = |
| |
2987 static_cast<RangeHashTable*>(ancestor->GetProperty(nsGkAtoms::range)); |
| |
2988 if (ranges->GetEntry(this)) { |
| |
2989 break; |
| |
2990 } |
| |
2991 ancestor = GetNextRangeCommonAncestor(ancestor->GetParentNode()); |
| |
2992 } |
| |
2993 NS_ASSERTION(ancestor, "can't find common ancestor for selected range"); |
| |
2994 return ancestor; |
| |
2995 } |
| |
2996 |
| |
2997 /* static */ bool nsRange::AutoInvalidateSelection::mIsNested; |
| |
2998 |
| |
2999 nsRange::AutoInvalidateSelection::~AutoInvalidateSelection() |
| |
3000 { |
| |
3001 NS_ASSERTION(mWasInSelection == mRange->IsInSelection(), |
| |
3002 "Range got unselected in AutoInvalidateSelection block"); |
| |
3003 if (!mCommonAncestor) { |
| |
3004 return; |
| |
3005 } |
| |
3006 mIsNested = false; |
| |
3007 ::InvalidateAllFrames(mCommonAncestor); |
| |
3008 nsINode* commonAncestor = mRange->GetRegisteredCommonAncestor(); |
| |
3009 if (commonAncestor != mCommonAncestor) { |
| |
3010 ::InvalidateAllFrames(commonAncestor); |
| |
3011 } |
| |
3012 } |
| |
3013 |
| |
3014 /* static */ already_AddRefed<nsRange> |
| |
3015 nsRange::Constructor(const GlobalObject& aGlobal, |
| |
3016 ErrorResult& aRv) |
| |
3017 { |
| |
3018 nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports()); |
| |
3019 if (!window || !window->GetDoc()) { |
| |
3020 aRv.Throw(NS_ERROR_FAILURE); |
| |
3021 return nullptr; |
| |
3022 } |
| |
3023 |
| |
3024 return window->GetDoc()->CreateRange(aRv); |
| |
3025 } |