Thu, 15 Jan 2015 21:03:48 +0100
Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 /*
7 * Implementation of the DOM nsIDOMRange object.
8 */
10 #include "nscore.h"
11 #include "nsRange.h"
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"
38 using namespace mozilla;
39 using namespace mozilla::dom;
41 JSObject*
42 nsRange::WrapObject(JSContext* aCx)
43 {
44 return RangeBinding::Wrap(aCx, this);
45 }
47 /******************************************************
48 * stack based utilty class for managing monitor
49 ******************************************************/
51 static void InvalidateAllFrames(nsINode* aNode)
52 {
53 NS_PRECONDITION(aNode, "bad arg");
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 }
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!
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.
97 if (!aRange || !aRange->IsPositioned())
98 return NS_ERROR_UNEXPECTED;
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 }
116 nsINode* rangeStartParent = aRange->GetStartParent();
117 nsINode* rangeEndParent = aRange->GetEndParent();
118 int32_t rangeStartOffset = aRange->StartOffset();
119 int32_t rangeEndOffset = aRange->EndOffset();
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);
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 }
138 struct FindSelectedRangeData
139 {
140 nsINode* mNode;
141 nsRange* mResult;
142 uint32_t mStartOffset;
143 uint32_t mEndOffset;
144 };
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 }
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 }
180 /* static */ bool
181 nsRange::IsNodeSelected(nsINode* aNode, uint32_t aStartOffset,
182 uint32_t aEndOffset)
183 {
184 NS_PRECONDITION(aNode, "bad arg");
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 }
201 /******************************************************
202 * constructor/destructor
203 ******************************************************/
205 nsRange::~nsRange()
206 {
207 NS_ASSERTION(!IsInSelection(), "deleting nsRange that is in use");
209 // Maybe we can remove Detach() -- bug 702948.
210 Telemetry::Accumulate(Telemetry::DOM_RANGE_DETACHED, mIsDetached);
212 // we want the side effects (releases and list removals)
213 DoSetRange(nullptr, 0, nullptr, 0, nullptr);
214 }
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);
225 nsresult rv = CreateRange(startDomNode, aStartOffset, endDomNode, aEndOffset,
226 aRange);
228 return rv;
230 }
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;
241 nsCOMPtr<nsINode> startParent = do_QueryInterface(aStartParent);
242 NS_ENSURE_ARG_POINTER(startParent);
244 nsRefPtr<nsRange> range = new nsRange(startParent);
246 nsresult rv = range->SetStart(startParent, aStartOffset);
247 NS_ENSURE_SUCCESS(rv, rv);
249 rv = range->SetEnd(aEndParent, aEndOffset);
250 NS_ENSURE_SUCCESS(rv, rv);
252 range.forget(aRange);
253 return NS_OK;
254 }
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 }
269 /******************************************************
270 * nsISupports
271 ******************************************************/
273 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsRange)
274 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(nsRange,
275 DoSetRange(nullptr, 0, nullptr, 0, nullptr))
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
285 NS_IMPL_CYCLE_COLLECTION_CLASS(nsRange)
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
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
301 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsRange)
302 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
303 NS_IMPL_CYCLE_COLLECTION_TRACE_END
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 }
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 }
347 void
348 nsRange::RegisterCommonAncestor(nsINode* aNode)
349 {
350 NS_PRECONDITION(aNode, "bad arg");
351 NS_ASSERTION(IsInSelection(), "registering range not in selection");
353 MarkDescendants(aNode);
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 }
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");
375 if (ranges->Count() == 1) {
376 aNode->ClearCommonAncestorForRangeInSelection();
377 aNode->DeleteProperty(nsGkAtoms::range);
378 UnmarkDescendants(aNode);
379 } else {
380 ranges->RemoveEntry(this);
381 }
382 }
384 /******************************************************
385 * nsIMutationObserver implementation
386 ******************************************************/
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");
397 nsINode* newRoot = nullptr;
398 nsINode* newStartNode = nullptr;
399 nsINode* newEndNode = nullptr;
400 uint32_t newStartOffset = 0;
401 uint32_t newEndOffset = 0;
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 }
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 }
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 }
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;
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 }
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 }
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 }
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");
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 }
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 }
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");
594 nsINode* container = NODE_FROM(aContainer, aDocument);
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 }
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 }
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?");
635 nsINode* container = NODE_FROM(aContainer, aDocument);
636 bool gravitateStart = false;
637 bool gravitateEnd = false;
638 bool didCheckStartParentDescendant = false;
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 }
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 }
661 if (!mEnableGravitationOnElementRemoval) {
662 // Do not gravitate.
663 return;
664 }
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 }
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 }
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 }
718 ErrorResult rv;
719 *aResult = IsPointInRange(*parent, aOffset, rv);
720 return rv.ErrorCode();
721 }
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 }
733 return compareResult == 0;
734 }
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);
744 ErrorResult rv;
745 *aResult = ComparePoint(*parent, aOffset, rv);
746 return rv.ErrorCode();
747 }
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 }
758 if (!nsContentUtils::ContentIsDescendantOf(&aParent, mRoot)) {
759 aRv.Throw(NS_ERROR_DOM_WRONG_DOCUMENT_ERR);
760 return 0;
761 }
763 if (aParent.NodeType() == nsIDOMNode::DOCUMENT_TYPE_NODE) {
764 aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
765 return 0;
766 }
768 if (aOffset > aParent.Length()) {
769 aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
770 return 0;
771 }
773 int32_t cmp;
774 if ((cmp = nsContentUtils::ComparePoints(&aParent, aOffset,
775 mStartParent, mStartOffset)) <= 0) {
777 return cmp;
778 }
779 if (nsContentUtils::ComparePoints(mEndParent, mEndOffset,
780 &aParent, aOffset) == -1) {
781 return 1;
782 }
784 return 0;
785 }
787 NS_IMETHODIMP
788 nsRange::IntersectsNode(nsIDOMNode* aNode, bool* aResult)
789 {
790 *aResult = false;
792 nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
793 // TODO: This should throw a TypeError.
794 NS_ENSURE_ARG(node);
796 ErrorResult rv;
797 *aResult = IntersectsNode(*node, rv);
798 return rv.ErrorCode();
799 }
801 bool
802 nsRange::IntersectsNode(nsINode& aNode, ErrorResult& aRv)
803 {
804 if (!mIsPositioned) {
805 aRv.Throw(NS_ERROR_NOT_INITIALIZED);
806 return false;
807 }
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 }
817 // Step 5.
818 int32_t nodeIndex = parent->IndexOf(&aNode);
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;
830 // Step 2.
831 if (disconnected) {
832 result = false;
833 }
834 return result;
835 }
837 /******************************************************
838 * Private helper routines
839 ******************************************************/
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");
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 }
906 // This needs to be the last thing this function does. See comment
907 // in ParentChainChanged.
908 mRoot = aRoot;
909 }
911 static int32_t
912 IndexOf(nsINode* aChild)
913 {
914 nsINode* parent = aChild->GetParentNode();
916 return parent ? parent->IndexOf(aChild) : -1;
917 }
919 nsINode*
920 nsRange::GetCommonAncestor() const
921 {
922 return mIsPositioned ?
923 nsContentUtils::GetCommonAncestor(mStartParent, mEndParent) :
924 nullptr;
925 }
927 void
928 nsRange::Reset()
929 {
930 DoSetRange(nullptr, 0, nullptr, 0, nullptr);
931 }
933 /******************************************************
934 * public functionality
935 ******************************************************/
937 NS_IMETHODIMP
938 nsRange::GetStartContainer(nsIDOMNode** aStartParent)
939 {
940 if (!mIsPositioned)
941 return NS_ERROR_NOT_INITIALIZED;
943 return CallQueryInterface(mStartParent, aStartParent);
944 }
946 nsINode*
947 nsRange::GetStartContainer(ErrorResult& aRv) const
948 {
949 if (!mIsPositioned) {
950 aRv.Throw(NS_ERROR_NOT_INITIALIZED);
951 return nullptr;
952 }
954 return mStartParent;
955 }
957 NS_IMETHODIMP
958 nsRange::GetStartOffset(int32_t* aStartOffset)
959 {
960 if (!mIsPositioned)
961 return NS_ERROR_NOT_INITIALIZED;
963 *aStartOffset = mStartOffset;
965 return NS_OK;
966 }
968 uint32_t
969 nsRange::GetStartOffset(ErrorResult& aRv) const
970 {
971 if (!mIsPositioned) {
972 aRv.Throw(NS_ERROR_NOT_INITIALIZED);
973 return 0;
974 }
976 return mStartOffset;
977 }
979 NS_IMETHODIMP
980 nsRange::GetEndContainer(nsIDOMNode** aEndParent)
981 {
982 if (!mIsPositioned)
983 return NS_ERROR_NOT_INITIALIZED;
985 return CallQueryInterface(mEndParent, aEndParent);
986 }
988 nsINode*
989 nsRange::GetEndContainer(ErrorResult& aRv) const
990 {
991 if (!mIsPositioned) {
992 aRv.Throw(NS_ERROR_NOT_INITIALIZED);
993 return nullptr;
994 }
996 return mEndParent;
997 }
999 NS_IMETHODIMP
1000 nsRange::GetEndOffset(int32_t* aEndOffset)
1001 {
1002 if (!mIsPositioned)
1003 return NS_ERROR_NOT_INITIALIZED;
1005 *aEndOffset = mEndOffset;
1007 return NS_OK;
1008 }
1010 uint32_t
1011 nsRange::GetEndOffset(ErrorResult& aRv) const
1012 {
1013 if (!mIsPositioned) {
1014 aRv.Throw(NS_ERROR_NOT_INITIALIZED);
1015 return 0;
1016 }
1018 return mEndOffset;
1019 }
1021 NS_IMETHODIMP
1022 nsRange::GetCollapsed(bool* aIsCollapsed)
1023 {
1024 if (!mIsPositioned)
1025 return NS_ERROR_NOT_INITIALIZED;
1027 *aIsCollapsed = Collapsed();
1029 return NS_OK;
1030 }
1032 nsINode*
1033 nsRange::GetCommonAncestorContainer(ErrorResult& aRv) const
1034 {
1035 if (!mIsPositioned) {
1036 aRv.Throw(NS_ERROR_NOT_INITIALIZED);
1037 return nullptr;
1038 }
1040 return nsContentUtils::GetCommonAncestor(mStartParent, mEndParent);
1041 }
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 }
1054 return rv.ErrorCode();
1055 }
1057 nsINode*
1058 nsRange::IsValidBoundary(nsINode* aNode)
1059 {
1060 if (!aNode) {
1061 return nullptr;
1062 }
1064 if (aNode->IsNodeOfType(nsINode::eCONTENT)) {
1065 nsIContent* content = static_cast<nsIContent*>(aNode);
1066 if (content->Tag() == nsGkAtoms::documentTypeNodeName) {
1067 return nullptr;
1068 }
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 }
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 }
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 }
1093 root = aNode->SubtreeRoot();
1095 NS_ASSERTION(!root->IsNodeOfType(nsINode::eDOCUMENT),
1096 "GetCurrentDoc should have returned a doc");
1098 // We allow this because of backward compatibility.
1099 return root;
1100 }
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 }
1110 AutoInvalidateSelection atEndOfBlock(this);
1111 aRv = SetStart(&aNode, aOffset);
1112 }
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 }
1122 ErrorResult rv;
1123 SetStart(*parent, aOffset, rv);
1124 return rv.ErrorCode();
1125 }
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);
1133 if (aOffset < 0 || uint32_t(aOffset) > aParent->Length()) {
1134 return NS_ERROR_DOM_INDEX_SIZE_ERR;
1135 }
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);
1144 return NS_OK;
1145 }
1147 DoSetRange(aParent, aOffset, mEndParent, mEndOffset, mRoot);
1149 return NS_OK;
1150 }
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 }
1160 AutoInvalidateSelection atEndOfBlock(this);
1161 aRv = SetStart(aNode.GetParentNode(), IndexOf(&aNode));
1162 }
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 }
1172 ErrorResult rv;
1173 SetStartBefore(*sibling, rv);
1174 return rv.ErrorCode();
1175 }
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 }
1185 AutoInvalidateSelection atEndOfBlock(this);
1186 aRv = SetStart(aNode.GetParentNode(), IndexOf(&aNode) + 1);
1187 }
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 }
1197 ErrorResult rv;
1198 SetStartAfter(*sibling, rv);
1199 return rv.ErrorCode();
1200 }
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 }
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 }
1221 ErrorResult rv;
1222 SetEnd(*parent, aOffset, rv);
1223 return rv.ErrorCode();
1224 }
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);
1232 if (aOffset < 0 || uint32_t(aOffset) > aParent->Length()) {
1233 return NS_ERROR_DOM_INDEX_SIZE_ERR;
1234 }
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);
1243 return NS_OK;
1244 }
1246 DoSetRange(mStartParent, mStartOffset, aParent, aOffset, mRoot);
1248 return NS_OK;
1249 }
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 }
1259 AutoInvalidateSelection atEndOfBlock(this);
1260 aRv = SetEnd(aNode.GetParentNode(), IndexOf(&aNode));
1261 }
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 }
1271 ErrorResult rv;
1272 SetEndBefore(*sibling, rv);
1273 return rv.ErrorCode();
1274 }
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 }
1284 AutoInvalidateSelection atEndOfBlock(this);
1285 aRv = SetEnd(aNode.GetParentNode(), IndexOf(&aNode) + 1);
1286 }
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 }
1296 ErrorResult rv;
1297 SetEndAfter(*sibling, rv);
1298 return rv.ErrorCode();
1299 }
1301 NS_IMETHODIMP
1302 nsRange::Collapse(bool aToStart)
1303 {
1304 if (!mIsPositioned)
1305 return NS_ERROR_NOT_INITIALIZED;
1307 AutoInvalidateSelection atEndOfBlock(this);
1308 if (aToStart)
1309 DoSetRange(mStartParent, mStartOffset, mStartParent, mStartOffset, mRoot);
1310 else
1311 DoSetRange(mEndParent, mEndOffset, mEndParent, mEndOffset, mRoot);
1313 return NS_OK;
1314 }
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);
1322 ErrorResult rv;
1323 SelectNode(*node, rv);
1324 return rv.ErrorCode();
1325 }
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 }
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 }
1342 int32_t index = parent->IndexOf(&aNode);
1343 if (index < 0) {
1344 aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
1345 return;
1346 }
1348 AutoInvalidateSelection atEndOfBlock(this);
1349 DoSetRange(parent, index, parent, index + 1, newRoot);
1350 }
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);
1358 ErrorResult rv;
1359 SelectNodeContents(*node, rv);
1360 return rv.ErrorCode();
1361 }
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 }
1371 nsINode* newRoot = IsValidBoundary(&aNode);
1372 if (!newRoot) {
1373 aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
1374 return;
1375 }
1377 AutoInvalidateSelection atEndOfBlock(this);
1378 DoSetRange(&aNode, 0, &aNode, aNode.Length(), newRoot);
1379 }
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.
1394 class MOZ_STACK_CLASS RangeSubtreeIterator
1395 {
1396 private:
1398 enum RangeSubtreeIterState { eDone=0,
1399 eUseStart,
1400 eUseIterator,
1401 eUseEnd };
1403 nsCOMPtr<nsIContentIterator> mIter;
1404 RangeSubtreeIterState mIterState;
1406 nsCOMPtr<nsINode> mStart;
1407 nsCOMPtr<nsINode> mEnd;
1409 public:
1411 RangeSubtreeIterator()
1412 : mIterState(eDone)
1413 {
1414 }
1415 ~RangeSubtreeIterator()
1416 {
1417 }
1419 nsresult Init(nsRange *aRange);
1420 already_AddRefed<nsINode> GetCurrentNode();
1421 void First();
1422 void Last();
1423 void Next();
1424 void Prev();
1426 bool IsDone()
1427 {
1428 return mIterState == eDone;
1429 }
1430 };
1432 nsresult
1433 RangeSubtreeIterator::Init(nsRange *aRange)
1434 {
1435 mIterState = eDone;
1436 if (aRange->Collapsed()) {
1437 return NS_OK;
1438 }
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.
1444 ErrorResult rv;
1445 nsCOMPtr<nsINode> node = aRange->GetStartContainer(rv);
1446 if (!node) return NS_ERROR_FAILURE;
1448 nsCOMPtr<nsIDOMCharacterData> startData = do_QueryInterface(node);
1449 if (startData || (node->IsElement() &&
1450 node->AsElement()->GetChildCount() == aRange->GetStartOffset(rv))) {
1451 mStart = node;
1452 }
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.
1458 node = aRange->GetEndContainer(rv);
1459 if (!node) return NS_ERROR_FAILURE;
1461 nsCOMPtr<nsIDOMCharacterData> endData = do_QueryInterface(node);
1462 if (endData || (node->IsElement() && aRange->GetEndOffset(rv) == 0)) {
1463 mEnd = node;
1464 }
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!
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!
1479 mIter = NS_NewContentSubtreeIterator();
1481 nsresult res = mIter->Init(aRange);
1482 if (NS_FAILED(res)) return res;
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.
1490 mIter = nullptr;
1491 }
1492 }
1494 // Initialize the iterator by calling First().
1495 // Note that we are ignoring the return value on purpose!
1497 First();
1499 return NS_OK;
1500 }
1502 already_AddRefed<nsINode>
1503 RangeSubtreeIterator::GetCurrentNode()
1504 {
1505 nsCOMPtr<nsINode> node;
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 }
1515 return node.forget();
1516 }
1518 void
1519 RangeSubtreeIterator::First()
1520 {
1521 if (mStart)
1522 mIterState = eUseStart;
1523 else if (mIter)
1524 {
1525 mIter->First();
1527 mIterState = eUseIterator;
1528 }
1529 else if (mEnd)
1530 mIterState = eUseEnd;
1531 else
1532 mIterState = eDone;
1533 }
1535 void
1536 RangeSubtreeIterator::Last()
1537 {
1538 if (mEnd)
1539 mIterState = eUseEnd;
1540 else if (mIter)
1541 {
1542 mIter->Last();
1544 mIterState = eUseIterator;
1545 }
1546 else if (mStart)
1547 mIterState = eUseStart;
1548 else
1549 mIterState = eDone;
1550 }
1552 void
1553 RangeSubtreeIterator::Next()
1554 {
1555 if (mIterState == eUseStart)
1556 {
1557 if (mIter)
1558 {
1559 mIter->First();
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();
1572 if (mIter->IsDone())
1573 {
1574 if (mEnd)
1575 mIterState = eUseEnd;
1576 else
1577 mIterState = eDone;
1578 }
1579 }
1580 else
1581 mIterState = eDone;
1582 }
1584 void
1585 RangeSubtreeIterator::Prev()
1586 {
1587 if (mIterState == eUseEnd)
1588 {
1589 if (mIter)
1590 {
1591 mIter->Last();
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();
1604 if (mIter->IsDone())
1605 {
1606 if (mStart)
1607 mIterState = eUseStart;
1608 else
1609 mIterState = eDone;
1610 }
1611 }
1612 else
1613 mIterState = eDone;
1614 }
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.
1627 static nsresult
1628 CollapseRangeAfterDelete(nsRange* aRange)
1629 {
1630 NS_ENSURE_ARG_POINTER(aRange);
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.
1647 return NS_OK;
1648 }
1650 // aRange isn't collapsed so figure out the appropriate place to collapse!
1651 // First get both end points and their common ancestor.
1653 ErrorResult rv;
1654 nsCOMPtr<nsINode> commonAncestor = aRange->GetCommonAncestorContainer(rv);
1655 if (rv.Failed()) return rv.ErrorCode();
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();
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!
1667 if (startContainer == commonAncestor)
1668 return aRange->Collapse(true);
1669 if (endContainer == commonAncestor)
1670 return aRange->Collapse(false);
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.
1676 nsCOMPtr<nsINode> nodeToSelect(startContainer);
1678 while (nodeToSelect)
1679 {
1680 nsCOMPtr<nsINode> parent = nodeToSelect->GetParentNode();
1681 if (parent == commonAncestor)
1682 break; // We found the nodeToSelect!
1684 nodeToSelect = parent;
1685 }
1687 if (!nodeToSelect)
1688 return NS_ERROR_FAILURE; // This should never happen!
1690 aRange->SelectNode(*nodeToSelect, rv);
1691 if (rv.Failed()) return rv.ErrorCode();
1693 return aRange->Collapse(false);
1694 }
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());
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 }
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 }
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 }
1744 nsresult res = nsRange::CompareNodeToRange(node, aRange, &before, &after);
1746 return NS_SUCCEEDED(res) && !before && !after;
1747 }
1749 nsresult
1750 nsRange::CutContents(DocumentFragment** aFragment)
1751 {
1752 if (aFragment) {
1753 *aFragment = nullptr;
1754 }
1756 nsCOMPtr<nsIDocument> doc = mStartParent->OwnerDoc();
1758 ErrorResult res;
1759 nsCOMPtr<nsINode> commonAncestor = GetCommonAncestorContainer(res);
1760 NS_ENSURE_SUCCESS(res.ErrorCode(), res.ErrorCode());
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();
1769 // Batch possible DOMSubtreeModified events.
1770 mozAutoSubtreeModified subtree(mRoot ? mRoot->OwnerDoc(): nullptr, nullptr);
1772 // Save the range end points locally to avoid interference
1773 // of Range gravity during our edits!
1775 nsCOMPtr<nsINode> startContainer = mStartParent;
1776 int32_t startOffset = mStartOffset;
1777 nsCOMPtr<nsINode> endContainer = mEndParent;
1778 int32_t endOffset = mEndOffset;
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();
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 }
1798 // Create and initialize a subtree iterator that will give
1799 // us all the subtrees within the range.
1801 RangeSubtreeIterator iter;
1803 nsresult rv = iter.Init(this);
1804 if (NS_FAILED(rv)) return rv;
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 }
1816 // We delete backwards to avoid iterator problems!
1818 iter.Last();
1820 bool handled = false;
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.
1826 while (!iter.IsDone())
1827 {
1828 nsCOMPtr<nsINode> nodeToResult;
1829 nsCOMPtr<nsINode> node = iter.GetCurrentNode();
1831 // Before we delete anything, advance the iterator to the
1832 // next subtree.
1834 iter.Prev();
1836 handled = false;
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.
1844 nsCOMPtr<nsIDOMCharacterData> charData(do_QueryInterface(node));
1846 if (charData)
1847 {
1848 uint32_t dataLength = 0;
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.
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 }
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 }
1878 handled = true;
1879 }
1880 else
1881 {
1882 // Delete or extract everything after startOffset.
1884 rv = charData->GetLength(&dataLength);
1885 NS_ENSURE_SUCCESS(rv, rv);
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 }
1898 handled = true;
1899 }
1900 }
1901 else if (node == endContainer)
1902 {
1903 // Delete or extract everything before endOffset.
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 }
1920 handled = true;
1921 }
1922 }
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 }
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 }
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);
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);
1961 nsCOMPtr<nsINode> parentCounterNode = node;
1962 while (parentCounterNode && parentCounterNode != commonAncestor)
1963 {
1964 ++parentCount;
1965 parentCounterNode = parentCounterNode->GetParentNode();
1966 NS_ENSURE_STATE(parentCounterNode);
1967 }
1968 }
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);
1977 if (farthestAncestor)
1978 {
1979 nsCOMPtr<nsINode> n = do_QueryInterface(commonCloneAncestor);
1980 rv = PrependChild(n, farthestAncestor);
1981 NS_ENSURE_SUCCESS(rv, rv);
1982 }
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 }
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 }
2016 rv = CollapseRangeAfterDelete(this);
2017 if (NS_SUCCEEDED(rv) && aFragment) {
2018 NS_ADDREF(*aFragment = retval);
2019 }
2020 return rv;
2021 }
2023 NS_IMETHODIMP
2024 nsRange::DeleteContents()
2025 {
2026 return CutContents(nullptr);
2027 }
2029 void
2030 nsRange::DeleteContents(ErrorResult& aRv)
2031 {
2032 aRv = CutContents(nullptr);
2033 }
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 }
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 }
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);
2060 ErrorResult rv;
2061 *aCmpRet = CompareBoundaryPoints(aHow, *otherRange, rv);
2062 return rv.ErrorCode();
2063 }
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 }
2074 nsINode *ourNode, *otherNode;
2075 int32_t ourOffset, otherOffset;
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 }
2108 if (mRoot != aOtherRange.GetRoot()) {
2109 rv.Throw(NS_ERROR_DOM_WRONG_DOCUMENT_ERR);
2110 return 0;
2111 }
2113 return nsContentUtils::ComparePoints(ourNode, ourOffset,
2114 otherNode, otherOffset);
2115 }
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));
2125 *aClosestAncestor = nullptr;
2126 *aFarthestAncestor = nullptr;
2128 if (aAncestor == aNode)
2129 return NS_OK;
2131 nsCOMPtr<nsINode> firstParent, lastParent;
2132 nsCOMPtr<nsINode> parent = aNode->GetParentNode();
2134 while(parent && parent != aAncestor)
2135 {
2136 ErrorResult rv;
2137 nsCOMPtr<nsINode> clone = parent->CloneNode(false, rv);
2139 if (rv.Failed()) {
2140 return rv.ErrorCode();
2141 }
2142 if (!clone) {
2143 return NS_ERROR_FAILURE;
2144 }
2146 if (! firstParent) {
2147 firstParent = lastParent = clone;
2148 } else {
2149 clone->AppendChild(*lastParent, rv);
2150 if (rv.Failed()) return rv.ErrorCode();
2152 lastParent = clone;
2153 }
2155 parent = parent->GetParentNode();
2156 }
2158 *aClosestAncestor = firstParent;
2159 NS_IF_ADDREF(*aClosestAncestor);
2161 *aFarthestAncestor = lastParent;
2162 NS_IF_ADDREF(*aFarthestAncestor);
2164 return NS_OK;
2165 }
2167 NS_IMETHODIMP
2168 nsRange::CloneContents(nsIDOMDocumentFragment** aReturn)
2169 {
2170 ErrorResult rv;
2171 *aReturn = CloneContents(rv).take();
2172 return rv.ErrorCode();
2173 }
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!");
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 }
2188 // Create a new document fragment in the context of this document,
2189 // which might be null
2192 nsRefPtr<DocumentFragment> clonedFrag =
2193 new DocumentFragment(doc->NodeInfoManager());
2195 nsCOMPtr<nsINode> commonCloneAncestor = clonedFrag.get();
2197 // Create and initialize a subtree iterator that will give
2198 // us all the subtrees within the range.
2200 RangeSubtreeIterator iter;
2202 aRv = iter.Init(this);
2203 if (aRv.Failed()) {
2204 return nullptr;
2205 }
2207 if (iter.IsDone())
2208 {
2209 // There's nothing to add to the doc frag, we must be done!
2210 return clonedFrag.forget();
2211 }
2213 iter.First();
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.
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())));
2234 // Clone the current subtree!
2236 nsCOMPtr<nsINode> clone = node->CloneNode(deepClone, aRv);
2237 if (aRv.Failed()) {
2238 return nullptr;
2239 }
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.
2247 nsCOMPtr<nsIDOMCharacterData> charData(do_QueryInterface(clone));
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.
2256 uint32_t dataLength = 0;
2257 aRv = charData->GetLength(&dataLength);
2258 if (aRv.Failed()) {
2259 return nullptr;
2260 }
2262 if (dataLength > (uint32_t)mEndOffset)
2263 {
2264 aRv = charData->DeleteData(mEndOffset, dataLength - mEndOffset);
2265 if (aRv.Failed()) {
2266 return nullptr;
2267 }
2268 }
2269 }
2271 if (node == mStartParent)
2272 {
2273 // We don't need any data before mStartOffset, so just
2274 // delete it!
2276 if (mStartOffset > 0)
2277 {
2278 aRv = charData->DeleteData(0, mStartOffset);
2279 if (aRv.Failed()) {
2280 return nullptr;
2281 }
2282 }
2283 }
2284 }
2286 // Clone the parent hierarchy between commonAncestor and node.
2288 nsCOMPtr<nsINode> closestAncestor, farthestAncestor;
2290 aRv = CloneParentsBetween(commonAncestor, node,
2291 getter_AddRefs(closestAncestor),
2292 getter_AddRefs(farthestAncestor));
2294 if (aRv.Failed()) {
2295 return nullptr;
2296 }
2298 // Hook the parent hierarchy/context of the subtree into the clone tree.
2300 if (farthestAncestor)
2301 {
2302 commonCloneAncestor->AppendChild(*farthestAncestor, aRv);
2304 if (aRv.Failed()) {
2305 return nullptr;
2306 }
2307 }
2309 // Place the cloned subtree into the cloned doc frag tree!
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.
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.
2324 commonCloneAncestor->AppendChild(*cloneNode, aRv);
2325 }
2326 if (aRv.Failed()) {
2327 return nullptr;
2328 }
2330 // Get the next subtree to be processed. The idea here is to setup
2331 // the parameters for the next iteration of the loop.
2333 iter.Next();
2335 if (iter.IsDone())
2336 break; // We must be done!
2338 nsCOMPtr<nsINode> nextNode = iter.GetCurrentNode();
2339 if (!nextNode) {
2340 aRv.Throw(NS_ERROR_FAILURE);
2341 return nullptr;
2342 }
2344 // Get node and nextNode's common parent.
2345 commonAncestor = nsContentUtils::GetCommonAncestor(node, nextNode);
2347 if (!commonAncestor) {
2348 aRv.Throw(NS_ERROR_FAILURE);
2349 return nullptr;
2350 }
2352 // Find the equivalent of commonAncestor in the cloned tree!
2354 while (node && node != commonAncestor)
2355 {
2356 node = node->GetParentNode();
2357 if (aRv.Failed()) {
2358 return nullptr;
2359 }
2361 if (!node) {
2362 aRv.Throw(NS_ERROR_FAILURE);
2363 return nullptr;
2364 }
2366 cloneNode = cloneNode->GetParentNode();
2367 if (!cloneNode) {
2368 aRv.Throw(NS_ERROR_FAILURE);
2369 return nullptr;
2370 }
2371 }
2373 commonCloneAncestor = cloneNode;
2374 }
2376 return clonedFrag.forget();
2377 }
2379 already_AddRefed<nsRange>
2380 nsRange::CloneRange() const
2381 {
2382 nsRefPtr<nsRange> range = new nsRange(mOwner);
2384 range->SetMaySpanAnonymousSubtrees(mMaySpanAnonymousSubtrees);
2386 range->DoSetRange(mStartParent, mStartOffset, mEndParent, mEndOffset, mRoot);
2388 return range.forget();
2389 }
2391 NS_IMETHODIMP
2392 nsRange::CloneRange(nsIDOMRange** aReturn)
2393 {
2394 *aReturn = CloneRange().take();
2395 return NS_OK;
2396 }
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 }
2406 ErrorResult rv;
2407 InsertNode(*node, rv);
2408 return rv.ErrorCode();
2409 }
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 }
2419 int32_t tStartOffset = StartOffset();
2421 nsCOMPtr<nsINode> tStartContainer = GetStartContainer(aRv);
2422 if (aRv.Failed()) {
2423 return;
2424 }
2426 // This is the node we'll be inserting before, and its parent
2427 nsCOMPtr<nsINode> referenceNode;
2428 nsCOMPtr<nsINode> referenceParentNode = tStartContainer;
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 }
2439 nsCOMPtr<nsIDOMText> secondPart;
2440 aRv = startTextNode->SplitText(tStartOffset, getter_AddRefs(secondPart));
2441 if (aRv.Failed()) {
2442 return;
2443 }
2445 referenceNode = do_QueryInterface(secondPart);
2446 } else {
2447 aRv = tStartContainer->AsDOMNode()->GetChildNodes(getter_AddRefs(tChildList));
2448 if (aRv.Failed()) {
2449 return;
2450 }
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 }
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;
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 }
2475 newOffset = length;
2476 }
2478 if (aNode.NodeType() == nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
2479 newOffset += aNode.GetChildCount();
2480 } else {
2481 newOffset++;
2482 }
2484 // Now actually insert the node
2485 nsCOMPtr<nsINode> tResultNode;
2486 tResultNode = referenceParentNode->InsertBefore(aNode, referenceNode, aRv);
2487 if (aRv.Failed()) {
2488 return;
2489 }
2491 if (Collapsed()) {
2492 aRv = SetEnd(referenceParentNode, newOffset);
2493 }
2494 }
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 }
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 }
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 }
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 }
2551 // Extract the contents within the range.
2553 nsRefPtr<DocumentFragment> docFrag = ExtractContents(aRv);
2555 if (aRv.Failed()) {
2556 return;
2557 }
2559 if (!docFrag) {
2560 aRv.Throw(NS_ERROR_FAILURE);
2561 return;
2562 }
2564 // Spec says we need to remove all of aNewParent's
2565 // children prior to insertion.
2567 nsCOMPtr<nsINodeList> children = aNewParent.ChildNodes();
2568 if (!children) {
2569 aRv.Throw(NS_ERROR_FAILURE);
2570 return;
2571 }
2573 uint32_t numChildren = children->Length();
2575 while (numChildren)
2576 {
2577 nsCOMPtr<nsINode> child = children->Item(--numChildren);
2578 if (!child) {
2579 aRv.Throw(NS_ERROR_FAILURE);
2580 return;
2581 }
2583 aNewParent.RemoveChild(*child, aRv);
2584 if (aRv.Failed()) {
2585 return;
2586 }
2587 }
2589 // Insert aNewParent at the range's start point.
2591 InsertNode(aNewParent, aRv);
2592 if (aRv.Failed()) {
2593 return;
2594 }
2596 // Append the content we extracted under aNewParent.
2597 aNewParent.AppendChild(*docFrag, aRv);
2598 if (aRv.Failed()) {
2599 return;
2600 }
2602 // Select aNewParent, and its contents.
2604 SelectNode(aNewParent, aRv);
2605 }
2607 NS_IMETHODIMP
2608 nsRange::ToString(nsAString& aReturn)
2609 {
2610 // clear the string
2611 aReturn.Truncate();
2613 // If we're unpositioned, return the empty string
2614 if (!mIsPositioned) {
2615 return NS_OK;
2616 }
2618 #ifdef DEBUG_range
2619 printf("Range dump: -----------------------\n");
2620 #endif /* DEBUG */
2622 // effeciency hack for simple case
2623 if (mStartParent == mEndParent)
2624 {
2625 nsCOMPtr<nsIDOMText> textNode( do_QueryInterface(mStartParent) );
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 */
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 }
2643 /* complex case: mStartParent != mEndParent, or mStartParent not a text node
2644 revisit - there are potential optimizations here and also tradeoffs.
2645 */
2647 nsCOMPtr<nsIContentIterator> iter = NS_NewContentIterator();
2648 nsresult rv = iter->Init(this);
2649 NS_ENSURE_SUCCESS(rv, rv);
2651 nsString tempString;
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();
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 }
2685 iter->Next();
2686 }
2688 #ifdef DEBUG_range
2689 printf("End Range dump: -----------------------\n");
2690 #endif /* DEBUG */
2691 return NS_OK;
2692 }
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 }
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 }
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 }
2723 return nsContentUtils::CreateContextualFragment(mStartParent, aFragment,
2724 false, aRv);
2725 }
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);
2734 point += aFrame->GetOffsetTo(aRelativeTo);
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");
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 }
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 }
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;
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 }
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;
2804 // Flush out layout so our frames are up to date.
2805 if (!aStartParent->IsInDoc()) {
2806 return;
2807 }
2809 aStartParent->OwnerDoc()->FlushPendingNotifications(Flush_Layout);
2811 // Recheck whether we're still in the document
2812 if (!aStartParent->IsInDoc()) {
2813 return;
2814 }
2816 RangeSubtreeIterator iter;
2818 nsresult rv = iter.Init(aRange);
2819 if (NS_FAILED(rv)) return;
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 }
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 }
2862 nsIFrame* frame = content->GetPrimaryFrame();
2863 if (frame) {
2864 nsLayoutUtils::GetAllInFlowRects(frame,
2865 nsLayoutUtils::GetContainingBlockForClientRect(frame), aCollector);
2866 }
2867 } while (!iter.IsDone());
2868 }
2870 NS_IMETHODIMP
2871 nsRange::GetBoundingClientRect(nsIDOMClientRect** aResult)
2872 {
2873 *aResult = GetBoundingClientRect().take();
2874 return NS_OK;
2875 }
2877 already_AddRefed<DOMRect>
2878 nsRange::GetBoundingClientRect()
2879 {
2880 nsRefPtr<DOMRect> rect = new DOMRect(ToSupports(this));
2881 if (!mStartParent) {
2882 return rect.forget();
2883 }
2885 nsLayoutUtils::RectAccumulator accumulator;
2886 CollectClientRects(&accumulator, this, mStartParent, mStartOffset,
2887 mEndParent, mEndOffset);
2889 nsRect r = accumulator.mResultRect.IsEmpty() ? accumulator.mFirstRect :
2890 accumulator.mResultRect;
2891 rect->SetLayoutRect(r);
2892 return rect.forget();
2893 }
2895 NS_IMETHODIMP
2896 nsRange::GetClientRects(nsIDOMClientRectList** aResult)
2897 {
2898 *aResult = GetClientRects().take();
2899 return NS_OK;
2900 }
2902 already_AddRefed<DOMRectList>
2903 nsRange::GetClientRects()
2904 {
2905 if (!mStartParent) {
2906 return nullptr;
2907 }
2909 nsRefPtr<DOMRectList> rectList =
2910 new DOMRectList(static_cast<nsIDOMRange*>(this));
2912 nsLayoutUtils::RectListBuilder builder(rectList);
2914 CollectClientRects(&builder, this, mStartParent, mStartOffset,
2915 mEndParent, mEndOffset);
2916 return rectList.forget();
2917 }
2919 NS_IMETHODIMP
2920 nsRange::GetUsedFontFaces(nsIDOMFontFaceList** aResult)
2921 {
2922 *aResult = nullptr;
2924 NS_ENSURE_TRUE(mStartParent, NS_ERROR_UNEXPECTED);
2926 nsCOMPtr<nsINode> startContainer = do_QueryInterface(mStartParent);
2927 nsCOMPtr<nsINode> endContainer = do_QueryInterface(mEndParent);
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);
2934 // Recheck whether we're still in the document
2935 NS_ENSURE_TRUE(mStartParent->IsInDoc(), NS_ERROR_UNEXPECTED);
2937 nsRefPtr<nsFontFaceList> fontFaceList = new nsFontFaceList();
2939 RangeSubtreeIterator iter;
2940 nsresult rv = iter.Init(this);
2941 NS_ENSURE_SUCCESS(rv, rv);
2943 while (!iter.IsDone()) {
2944 // only collect anything if the range is not collapsed
2945 nsCOMPtr<nsINode> node = iter.GetCurrentNode();
2946 iter.Next();
2948 nsCOMPtr<nsIContent> content = do_QueryInterface(node);
2949 if (!content) {
2950 continue;
2951 }
2952 nsIFrame* frame = content->GetPrimaryFrame();
2953 if (!frame) {
2954 continue;
2955 }
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 }
2972 nsLayoutUtils::GetFontFacesForFrames(frame, fontFaceList);
2973 }
2975 fontFaceList.forget(aResult);
2976 return NS_OK;
2977 }
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 }
2997 /* static */ bool nsRange::AutoInvalidateSelection::mIsNested;
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 }
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 }
3024 return window->GetDoc()->CreateRange(aRv);
3025 }