|
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 } |