Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : /*
8 : * Implementation of the DOM nsIDOMRange object.
9 : */
10 :
11 : #include "nscore.h"
12 : #include "nsRange.h"
13 :
14 : #include "nsString.h"
15 : #include "nsReadableUtils.h"
16 : #include "nsIDOMNode.h"
17 : #include "nsIDOMDocumentFragment.h"
18 : #include "nsIContent.h"
19 : #include "nsIDocument.h"
20 : #include "nsIDOMText.h"
21 : #include "nsError.h"
22 : #include "nsIContentIterator.h"
23 : #include "nsIDOMNodeList.h"
24 : #include "nsGkAtoms.h"
25 : #include "nsContentUtils.h"
26 : #include "nsGenericDOMDataNode.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/DOMStringList.h"
34 : #include "mozilla/dom/ShadowRoot.h"
35 : #include "mozilla/dom/Selection.h"
36 : #include "mozilla/Telemetry.h"
37 : #include "mozilla/Likely.h"
38 : #include "nsCSSFrameConstructor.h"
39 : #include "nsStyleStruct.h"
40 : #include "nsStyleStructInlines.h"
41 : #include "nsComputedDOMStyle.h"
42 :
43 : using namespace mozilla;
44 : using namespace mozilla::dom;
45 :
46 : JSObject*
47 2 : nsRange::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
48 : {
49 2 : return RangeBinding::Wrap(aCx, this, aGivenProto);
50 : }
51 :
52 : /******************************************************
53 : * stack based utilty class for managing monitor
54 : ******************************************************/
55 :
56 0 : static void InvalidateAllFrames(nsINode* aNode)
57 : {
58 0 : NS_PRECONDITION(aNode, "bad arg");
59 :
60 0 : nsIFrame* frame = nullptr;
61 0 : switch (aNode->NodeType()) {
62 : case nsIDOMNode::TEXT_NODE:
63 : case nsIDOMNode::ELEMENT_NODE:
64 : {
65 0 : nsIContent* content = static_cast<nsIContent*>(aNode);
66 0 : frame = content->GetPrimaryFrame();
67 0 : break;
68 : }
69 : case nsIDOMNode::DOCUMENT_NODE:
70 : {
71 0 : nsIDocument* doc = static_cast<nsIDocument*>(aNode);
72 0 : nsIPresShell* shell = doc ? doc->GetShell() : nullptr;
73 0 : frame = shell ? shell->GetRootFrame() : nullptr;
74 0 : break;
75 : }
76 : }
77 0 : for (nsIFrame* f = frame; f; f = f->GetNextContinuation()) {
78 0 : f->InvalidateFrameSubtree();
79 : }
80 0 : }
81 :
82 : // Utility routine to detect if a content node is completely contained in a range
83 : // If outNodeBefore is returned true, then the node starts before the range does.
84 : // If outNodeAfter is returned true, then the node ends after the range does.
85 : // Note that both of the above might be true.
86 : // If neither are true, the node is contained inside of the range.
87 : // XXX - callers responsibility to ensure node in same doc as range!
88 :
89 : // static
90 : nsresult
91 0 : nsRange::CompareNodeToRange(nsINode* aNode, nsRange* aRange,
92 : bool *outNodeBefore, bool *outNodeAfter)
93 : {
94 0 : NS_ENSURE_STATE(aNode);
95 : // create a pair of dom points that expresses location of node:
96 : // NODE(start), NODE(end)
97 : // Let incoming range be:
98 : // {RANGE(start), RANGE(end)}
99 : // if (RANGE(start) <= NODE(start)) and (RANGE(end) => NODE(end))
100 : // then the Node is contained (completely) by the Range.
101 :
102 0 : if (!aRange || !aRange->IsPositioned())
103 0 : return NS_ERROR_UNEXPECTED;
104 :
105 : // gather up the dom point info
106 : int32_t nodeStart, nodeEnd;
107 0 : nsINode* parent = aNode->GetParentNode();
108 0 : if (!parent) {
109 : // can't make a parent/offset pair to represent start or
110 : // end of the root node, because it has no parent.
111 : // so instead represent it by (node,0) and (node,numChildren)
112 0 : parent = aNode;
113 0 : nodeStart = 0;
114 0 : nodeEnd = aNode->GetChildCount();
115 : }
116 : else {
117 0 : nodeStart = parent->IndexOf(aNode);
118 0 : nodeEnd = nodeStart + 1;
119 : }
120 :
121 0 : nsINode* rangeStartContainer = aRange->GetStartContainer();
122 0 : nsINode* rangeEndContainer = aRange->GetEndContainer();
123 0 : int32_t rangeStartOffset = aRange->StartOffset();
124 0 : int32_t rangeEndOffset = aRange->EndOffset();
125 :
126 : // is RANGE(start) <= NODE(start) ?
127 0 : bool disconnected = false;
128 0 : *outNodeBefore = nsContentUtils::ComparePoints(rangeStartContainer,
129 : rangeStartOffset,
130 : parent, nodeStart,
131 0 : &disconnected) > 0;
132 0 : NS_ENSURE_TRUE(!disconnected, NS_ERROR_DOM_WRONG_DOCUMENT_ERR);
133 :
134 : // is RANGE(end) >= NODE(end) ?
135 0 : *outNodeAfter = nsContentUtils::ComparePoints(rangeEndContainer,
136 : rangeEndOffset,
137 : parent, nodeEnd,
138 0 : &disconnected) < 0;
139 0 : NS_ENSURE_TRUE(!disconnected, NS_ERROR_DOM_WRONG_DOCUMENT_ERR);
140 0 : return NS_OK;
141 : }
142 :
143 : static nsINode*
144 4 : GetNextRangeCommonAncestor(nsINode* aNode)
145 : {
146 4 : while (aNode && !aNode->IsCommonAncestorForRangeInSelection()) {
147 2 : if (!aNode->IsDescendantOfCommonAncestorForRangeInSelection()) {
148 2 : return nullptr;
149 : }
150 0 : aNode = aNode->GetParentNode();
151 : }
152 2 : return aNode;
153 : }
154 :
155 : /**
156 : * A Comparator suitable for mozilla::BinarySearchIf for searching a collection
157 : * of nsRange* for an overlap of (mNode, mStartOffset) .. (mNode, mEndOffset).
158 : */
159 : struct IsItemInRangeComparator
160 : {
161 : nsINode* mNode;
162 : uint32_t mStartOffset;
163 : uint32_t mEndOffset;
164 :
165 2 : int operator()(const nsRange* const aRange) const
166 : {
167 2 : int32_t cmp = nsContentUtils::ComparePoints(mNode, mEndOffset,
168 : aRange->GetStartContainer(),
169 2 : aRange->StartOffset());
170 2 : if (cmp == 1) {
171 2 : cmp = nsContentUtils::ComparePoints(mNode, mStartOffset,
172 : aRange->GetEndContainer(),
173 2 : aRange->EndOffset());
174 2 : if (cmp == -1) {
175 2 : return 0;
176 : }
177 0 : return 1;
178 : }
179 0 : return -1;
180 : }
181 : };
182 :
183 : /* static */ bool
184 2 : nsRange::IsNodeSelected(nsINode* aNode, uint32_t aStartOffset,
185 : uint32_t aEndOffset)
186 : {
187 2 : NS_PRECONDITION(aNode, "bad arg");
188 :
189 2 : nsINode* n = GetNextRangeCommonAncestor(aNode);
190 2 : NS_ASSERTION(n || !aNode->IsSelectionDescendant(),
191 : "orphan selection descendant");
192 :
193 : // Collect the potential ranges and their selection objects.
194 4 : RangeHashTable ancestorSelectionRanges;
195 4 : nsTHashtable<nsPtrHashKey<Selection>> ancestorSelections;
196 2 : uint32_t maxRangeCount = 0;
197 6 : for (; n; n = GetNextRangeCommonAncestor(n->GetParentNode())) {
198 : RangeHashTable* ranges =
199 2 : static_cast<RangeHashTable*>(n->GetProperty(nsGkAtoms::range));
200 6 : for (auto iter = ranges->ConstIter(); !iter.Done(); iter.Next()) {
201 4 : nsRange* range = iter.Get()->GetKey();
202 4 : if (range->IsInSelection() && !range->Collapsed()) {
203 2 : ancestorSelectionRanges.PutEntry(range);
204 2 : Selection* selection = range->mSelection;
205 2 : ancestorSelections.PutEntry(selection);
206 2 : maxRangeCount = std::max(maxRangeCount, selection->RangeCount());
207 : }
208 : }
209 : }
210 :
211 2 : if (!ancestorSelectionRanges.IsEmpty()) {
212 2 : nsTArray<const nsRange*> sortedRanges(maxRangeCount);
213 2 : for (auto iter = ancestorSelections.ConstIter(); !iter.Done(); iter.Next()) {
214 2 : Selection* selection = iter.Get()->GetKey();
215 : // Sort the found ranges for |selection| in document order
216 : // (Selection::GetRangeAt returns its ranges ordered).
217 4 : for (uint32_t i = 0, len = selection->RangeCount(); i < len; ++i) {
218 2 : nsRange* range = selection->GetRangeAt(i);
219 2 : if (ancestorSelectionRanges.Contains(range)) {
220 2 : sortedRanges.AppendElement(range);
221 : }
222 : }
223 2 : MOZ_ASSERT(!sortedRanges.IsEmpty());
224 : // Binary search the now sorted ranges.
225 2 : IsItemInRangeComparator comparator = { aNode, aStartOffset, aEndOffset };
226 : size_t unused;
227 2 : if (mozilla::BinarySearchIf(sortedRanges, 0, sortedRanges.Length(), comparator, &unused)) {
228 2 : return true;
229 : }
230 0 : sortedRanges.ClearAndRetainStorage();
231 : }
232 : }
233 0 : return false;
234 : }
235 :
236 : /******************************************************
237 : * constructor/destructor
238 : ******************************************************/
239 :
240 30 : nsRange::~nsRange()
241 : {
242 10 : NS_ASSERTION(!IsInSelection(), "deleting nsRange that is in use");
243 :
244 : // we want the side effects (releases and list removals)
245 10 : DoSetRange(nullptr, 0, nullptr, 0, nullptr);
246 30 : }
247 :
248 15 : nsRange::nsRange(nsINode* aNode)
249 : : mRoot(nullptr)
250 : , mStartOffset(0)
251 : , mEndOffset(0)
252 : , mIsPositioned(false)
253 : , mMaySpanAnonymousSubtrees(false)
254 : , mIsGenerated(false)
255 : , mStartOffsetWasIncremented(false)
256 : , mEndOffsetWasIncremented(false)
257 : , mCalledByJS(false)
258 : #ifdef DEBUG
259 : , mAssertNextInsertOrAppendIndex(-1)
260 15 : , mAssertNextInsertOrAppendNode(nullptr)
261 : #endif
262 : {
263 15 : MOZ_ASSERT(aNode, "range isn't in a document!");
264 15 : mOwner = aNode->OwnerDoc();
265 15 : }
266 :
267 : /* static */
268 : nsresult
269 0 : nsRange::CreateRange(nsINode* aStartContainer, int32_t aStartOffset,
270 : nsINode* aEndParent, int32_t aEndOffset,
271 : nsRange** aRange)
272 : {
273 0 : MOZ_ASSERT(aRange);
274 0 : *aRange = nullptr;
275 :
276 0 : RefPtr<nsRange> range = new nsRange(aStartContainer);
277 0 : nsresult rv = range->SetStartAndEnd(aStartContainer, aStartOffset,
278 0 : aEndParent, aEndOffset);
279 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
280 0 : return rv;
281 : }
282 0 : range.forget(aRange);
283 0 : return NS_OK;
284 : }
285 :
286 : /* static */
287 : nsresult
288 0 : nsRange::CreateRange(nsIDOMNode* aStartContainer, int32_t aStartOffset,
289 : nsIDOMNode* aEndParent, int32_t aEndOffset,
290 : nsRange** aRange)
291 : {
292 0 : nsCOMPtr<nsINode> startContainer = do_QueryInterface(aStartContainer);
293 0 : nsCOMPtr<nsINode> endContainer = do_QueryInterface(aEndParent);
294 0 : return CreateRange(startContainer, aStartOffset,
295 0 : endContainer, aEndOffset, aRange);
296 : }
297 :
298 : /* static */
299 : nsresult
300 0 : nsRange::CreateRange(nsIDOMNode* aStartContainer, int32_t aStartOffset,
301 : nsIDOMNode* aEndParent, int32_t aEndOffset,
302 : nsIDOMRange** aRange)
303 : {
304 0 : RefPtr<nsRange> range;
305 0 : nsresult rv = nsRange::CreateRange(aStartContainer, aStartOffset, aEndParent,
306 0 : aEndOffset, getter_AddRefs(range));
307 0 : range.forget(aRange);
308 0 : return rv;
309 : }
310 :
311 : /******************************************************
312 : * nsISupports
313 : ******************************************************/
314 :
315 57 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsRange)
316 57 : NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(nsRange,
317 : DoSetRange(nullptr, 0, nullptr, 0, nullptr))
318 :
319 : // QueryInterface implementation for nsRange
320 93 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsRange)
321 2 : NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
322 0 : NS_INTERFACE_MAP_ENTRY(nsIDOMRange)
323 0 : NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
324 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMRange)
325 0 : NS_INTERFACE_MAP_END
326 :
327 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsRange)
328 :
329 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsRange)
330 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
331 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner);
332 0 : tmp->Reset();
333 :
334 : // This needs to be unlinked after Reset() is called, as it controls
335 : // the result of IsInSelection() which is used by tmp->Reset().
336 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelection);
337 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
338 :
339 2 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsRange)
340 2 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
341 2 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStartContainer)
342 2 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEndContainer)
343 2 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot)
344 2 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelection)
345 2 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
346 :
347 14 : NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsRange)
348 14 : NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
349 14 : NS_IMPL_CYCLE_COLLECTION_TRACE_END
350 :
351 17 : static void MarkDescendants(nsINode* aNode)
352 : {
353 : // Set NodeIsDescendantOfCommonAncestorForRangeInSelection on aNode's
354 : // descendants unless aNode is already marked as a range common ancestor
355 : // or a descendant of one, in which case all of our descendants have the
356 : // bit set already.
357 17 : if (!aNode->IsSelectionDescendant()) {
358 : // don't set the Descendant bit on |aNode| itself
359 13 : nsINode* node = aNode->GetNextNode(aNode);
360 31 : while (node) {
361 9 : node->SetDescendantOfCommonAncestorForRangeInSelection();
362 9 : if (!node->IsCommonAncestorForRangeInSelection()) {
363 9 : node = node->GetNextNode(aNode);
364 : } else {
365 : // optimize: skip this sub-tree since it's marked already.
366 0 : node = node->GetNextNonChildNode(aNode);
367 : }
368 : }
369 : }
370 17 : }
371 :
372 11 : static void UnmarkDescendants(nsINode* aNode)
373 : {
374 : // Unset NodeIsDescendantOfCommonAncestorForRangeInSelection on aNode's
375 : // descendants unless aNode is a descendant of another range common ancestor.
376 : // Also, exclude descendants of range common ancestors (but not the common
377 : // ancestor itself).
378 11 : if (!aNode->IsDescendantOfCommonAncestorForRangeInSelection()) {
379 : // we know |aNode| doesn't have any bit set
380 11 : nsINode* node = aNode->GetNextNode(aNode);
381 29 : while (node) {
382 9 : node->ClearDescendantOfCommonAncestorForRangeInSelection();
383 9 : if (!node->IsCommonAncestorForRangeInSelection()) {
384 8 : node = node->GetNextNode(aNode);
385 : } else {
386 : // We found an ancestor of an overlapping range, skip its descendants.
387 1 : node = node->GetNextNonChildNode(aNode);
388 : }
389 : }
390 : }
391 11 : }
392 :
393 : void
394 17 : nsRange::RegisterCommonAncestor(nsINode* aNode)
395 : {
396 17 : NS_PRECONDITION(aNode, "bad arg");
397 17 : NS_ASSERTION(IsInSelection(), "registering range not in selection");
398 :
399 17 : MarkDescendants(aNode);
400 :
401 : RangeHashTable* ranges =
402 17 : static_cast<RangeHashTable*>(aNode->GetProperty(nsGkAtoms::range));
403 17 : if (!ranges) {
404 14 : ranges = new RangeHashTable;
405 : aNode->SetProperty(nsGkAtoms::range, ranges,
406 14 : nsINode::DeleteProperty<nsRange::RangeHashTable>, true);
407 : }
408 17 : ranges->PutEntry(this);
409 17 : aNode->SetCommonAncestorForRangeInSelection();
410 17 : }
411 :
412 : void
413 13 : nsRange::UnregisterCommonAncestor(nsINode* aNode)
414 : {
415 13 : NS_PRECONDITION(aNode, "bad arg");
416 13 : NS_ASSERTION(aNode->IsCommonAncestorForRangeInSelection(), "wrong node");
417 : RangeHashTable* ranges =
418 13 : static_cast<RangeHashTable*>(aNode->GetProperty(nsGkAtoms::range));
419 13 : NS_ASSERTION(ranges->GetEntry(this), "unknown range");
420 :
421 13 : if (ranges->Count() == 1) {
422 11 : aNode->ClearCommonAncestorForRangeInSelection();
423 11 : aNode->DeleteProperty(nsGkAtoms::range);
424 11 : UnmarkDescendants(aNode);
425 : } else {
426 2 : ranges->RemoveEntry(this);
427 : }
428 13 : }
429 :
430 : /******************************************************
431 : * nsIMutationObserver implementation
432 : ******************************************************/
433 :
434 : void
435 0 : nsRange::CharacterDataChanged(nsIDocument* aDocument,
436 : nsIContent* aContent,
437 : CharacterDataChangeInfo* aInfo)
438 : {
439 0 : MOZ_ASSERT(mAssertNextInsertOrAppendIndex == -1,
440 : "splitText failed to notify insert/append?");
441 0 : NS_ASSERTION(mIsPositioned, "shouldn't be notified if not positioned");
442 :
443 0 : nsINode* newRoot = nullptr;
444 0 : nsINode* newStartNode = nullptr;
445 0 : nsINode* newEndNode = nullptr;
446 0 : uint32_t newStartOffset = 0;
447 0 : uint32_t newEndOffset = 0;
448 :
449 0 : if (aInfo->mDetails &&
450 0 : aInfo->mDetails->mType == CharacterDataChangeInfo::Details::eSplit) {
451 : // If the splitted text node is immediately before a range boundary point
452 : // that refers to a child index (i.e. its parent is the boundary container)
453 : // then we need to increment the corresponding offset to account for the new
454 : // text node that will be inserted. If so, we need to prevent the next
455 : // ContentInserted or ContentAppended for this range from incrementing it
456 : // again (when the new text node is notified).
457 0 : nsINode* parentNode = aContent->GetParentNode();
458 0 : int32_t index = -1;
459 0 : if (parentNode == mEndContainer && mEndOffset > 0 &&
460 0 : (index = parentNode->IndexOf(aContent)) + 1 == mEndOffset) {
461 0 : ++mEndOffset;
462 0 : mEndOffsetWasIncremented = true;
463 : }
464 0 : if (parentNode == mStartContainer && mStartOffset > 0 &&
465 0 : (index != -1 ? index : parentNode->IndexOf(aContent)) + 1 == mStartOffset) {
466 0 : ++mStartOffset;
467 0 : mStartOffsetWasIncremented = true;
468 : }
469 : #ifdef DEBUG
470 0 : if (mStartOffsetWasIncremented || mEndOffsetWasIncremented) {
471 0 : mAssertNextInsertOrAppendIndex =
472 0 : (mStartOffsetWasIncremented ? mStartOffset : mEndOffset) - 1;
473 0 : mAssertNextInsertOrAppendNode = aInfo->mDetails->mNextSibling;
474 : }
475 : #endif
476 : }
477 :
478 : // If the changed node contains our start boundary and the change starts
479 : // before the boundary we'll need to adjust the offset.
480 0 : if (aContent == mStartContainer &&
481 0 : aInfo->mChangeStart < static_cast<uint32_t>(mStartOffset)) {
482 0 : if (aInfo->mDetails) {
483 : // splitText(), aInfo->mDetails->mNextSibling is the new text node
484 0 : NS_ASSERTION(aInfo->mDetails->mType ==
485 : CharacterDataChangeInfo::Details::eSplit,
486 : "only a split can start before the end");
487 0 : NS_ASSERTION(static_cast<uint32_t>(mStartOffset) <= aInfo->mChangeEnd + 1,
488 : "mStartOffset is beyond the end of this node");
489 0 : newStartOffset = static_cast<uint32_t>(mStartOffset) - aInfo->mChangeStart;
490 0 : newStartNode = aInfo->mDetails->mNextSibling;
491 0 : if (MOZ_UNLIKELY(aContent == mRoot)) {
492 0 : newRoot = IsValidBoundary(newStartNode);
493 : }
494 :
495 : bool isCommonAncestor =
496 0 : IsInSelection() && mStartContainer == mEndContainer;
497 0 : if (isCommonAncestor) {
498 0 : UnregisterCommonAncestor(mStartContainer);
499 0 : RegisterCommonAncestor(newStartNode);
500 : }
501 0 : if (mStartContainer->IsDescendantOfCommonAncestorForRangeInSelection()) {
502 0 : newStartNode->SetDescendantOfCommonAncestorForRangeInSelection();
503 : }
504 : } else {
505 : // If boundary is inside changed text, position it before change
506 : // else adjust start offset for the change in length.
507 0 : mStartOffset = static_cast<uint32_t>(mStartOffset) <= aInfo->mChangeEnd ?
508 0 : aInfo->mChangeStart :
509 0 : mStartOffset + aInfo->mChangeStart - aInfo->mChangeEnd +
510 0 : aInfo->mReplaceLength;
511 : }
512 : }
513 :
514 : // Do the same thing for the end boundary, except for splitText of a node
515 : // with no parent then only switch to the new node if the start boundary
516 : // did so too (otherwise the range would end up with disconnected nodes).
517 0 : if (aContent == mEndContainer &&
518 0 : aInfo->mChangeStart < static_cast<uint32_t>(mEndOffset)) {
519 0 : if (aInfo->mDetails && (aContent->GetParentNode() || newStartNode)) {
520 : // splitText(), aInfo->mDetails->mNextSibling is the new text node
521 0 : NS_ASSERTION(aInfo->mDetails->mType ==
522 : CharacterDataChangeInfo::Details::eSplit,
523 : "only a split can start before the end");
524 0 : NS_ASSERTION(static_cast<uint32_t>(mEndOffset) <= aInfo->mChangeEnd + 1,
525 : "mEndOffset is beyond the end of this node");
526 0 : newEndOffset = static_cast<uint32_t>(mEndOffset) - aInfo->mChangeStart;
527 0 : newEndNode = aInfo->mDetails->mNextSibling;
528 :
529 : bool isCommonAncestor =
530 0 : IsInSelection() && mStartContainer == mEndContainer;
531 0 : if (isCommonAncestor && !newStartNode) {
532 : // The split occurs inside the range.
533 0 : UnregisterCommonAncestor(mStartContainer);
534 0 : RegisterCommonAncestor(mStartContainer->GetParentNode());
535 0 : newEndNode->SetDescendantOfCommonAncestorForRangeInSelection();
536 0 : } else if (mEndContainer->
537 0 : IsDescendantOfCommonAncestorForRangeInSelection()) {
538 0 : newEndNode->SetDescendantOfCommonAncestorForRangeInSelection();
539 : }
540 : } else {
541 0 : mEndOffset = static_cast<uint32_t>(mEndOffset) <= aInfo->mChangeEnd ?
542 0 : aInfo->mChangeStart :
543 0 : mEndOffset + aInfo->mChangeStart - aInfo->mChangeEnd +
544 0 : aInfo->mReplaceLength;
545 : }
546 : }
547 :
548 0 : if (aInfo->mDetails &&
549 0 : aInfo->mDetails->mType == CharacterDataChangeInfo::Details::eMerge) {
550 : // normalize(), aInfo->mDetails->mNextSibling is the merged text node
551 : // that will be removed
552 0 : nsIContent* removed = aInfo->mDetails->mNextSibling;
553 0 : if (removed == mStartContainer) {
554 0 : newStartOffset = static_cast<uint32_t>(mStartOffset) + aInfo->mChangeStart;
555 0 : newStartNode = aContent;
556 0 : if (MOZ_UNLIKELY(removed == mRoot)) {
557 0 : newRoot = IsValidBoundary(newStartNode);
558 : }
559 : }
560 0 : if (removed == mEndContainer) {
561 0 : newEndOffset = static_cast<uint32_t>(mEndOffset) + aInfo->mChangeStart;
562 0 : newEndNode = aContent;
563 0 : if (MOZ_UNLIKELY(removed == mRoot)) {
564 0 : newRoot = IsValidBoundary(newEndNode);
565 : }
566 : }
567 : // When the removed text node's parent is one of our boundary nodes we may
568 : // need to adjust the offset to account for the removed node. However,
569 : // there will also be a ContentRemoved notification later so the only cases
570 : // we need to handle here is when the removed node is the text node after
571 : // the boundary. (The m*Offset > 0 check is an optimization - a boundary
572 : // point before the first child is never affected by normalize().)
573 0 : nsINode* parentNode = aContent->GetParentNode();
574 0 : if (parentNode == mStartContainer && mStartOffset > 0 &&
575 0 : uint32_t(mStartOffset) < parentNode->GetChildCount() &&
576 0 : removed == parentNode->GetChildAt(mStartOffset)) {
577 0 : newStartNode = aContent;
578 0 : newStartOffset = aInfo->mChangeStart;
579 : }
580 0 : if (parentNode == mEndContainer && mEndOffset > 0 &&
581 0 : uint32_t(mEndOffset) < parentNode->GetChildCount() &&
582 0 : removed == parentNode->GetChildAt(mEndOffset)) {
583 0 : newEndNode = aContent;
584 0 : newEndOffset = aInfo->mChangeEnd;
585 : }
586 : }
587 :
588 0 : if (newStartNode || newEndNode) {
589 0 : if (!newStartNode) {
590 0 : newStartNode = mStartContainer;
591 0 : newStartOffset = mStartOffset;
592 : }
593 0 : if (!newEndNode) {
594 0 : newEndNode = mEndContainer;
595 0 : newEndOffset = mEndOffset;
596 : }
597 0 : DoSetRange(newStartNode, newStartOffset, newEndNode, newEndOffset,
598 0 : newRoot ? newRoot : mRoot.get(),
599 0 : !newEndNode->GetParentNode() || !newStartNode->GetParentNode());
600 : }
601 0 : }
602 :
603 : void
604 0 : nsRange::ContentAppended(nsIDocument* aDocument,
605 : nsIContent* aContainer,
606 : nsIContent* aFirstNewContent,
607 : int32_t aNewIndexInContainer)
608 : {
609 0 : NS_ASSERTION(mIsPositioned, "shouldn't be notified if not positioned");
610 :
611 0 : nsINode* container = NODE_FROM(aContainer, aDocument);
612 0 : if (container->IsSelectionDescendant() && IsInSelection()) {
613 0 : nsINode* child = aFirstNewContent;
614 0 : while (child) {
615 0 : if (!child->IsDescendantOfCommonAncestorForRangeInSelection()) {
616 0 : MarkDescendants(child);
617 0 : child->SetDescendantOfCommonAncestorForRangeInSelection();
618 : }
619 0 : child = child->GetNextSibling();
620 : }
621 : }
622 :
623 0 : if (mStartOffsetWasIncremented || mEndOffsetWasIncremented) {
624 0 : MOZ_ASSERT(mAssertNextInsertOrAppendIndex == aNewIndexInContainer);
625 0 : MOZ_ASSERT(mAssertNextInsertOrAppendNode == aFirstNewContent);
626 0 : MOZ_ASSERT(aFirstNewContent->IsNodeOfType(nsINode::eDATA_NODE));
627 0 : mStartOffsetWasIncremented = mEndOffsetWasIncremented = false;
628 : #ifdef DEBUG
629 0 : mAssertNextInsertOrAppendIndex = -1;
630 0 : mAssertNextInsertOrAppendNode = nullptr;
631 : #endif
632 : }
633 0 : }
634 :
635 : void
636 0 : nsRange::ContentInserted(nsIDocument* aDocument,
637 : nsIContent* aContainer,
638 : nsIContent* aChild,
639 : int32_t aIndexInContainer)
640 : {
641 0 : NS_ASSERTION(mIsPositioned, "shouldn't be notified if not positioned");
642 :
643 0 : nsINode* container = NODE_FROM(aContainer, aDocument);
644 :
645 : // Adjust position if a sibling was inserted.
646 0 : if (container == mStartContainer && aIndexInContainer < mStartOffset &&
647 0 : !mStartOffsetWasIncremented) {
648 0 : ++mStartOffset;
649 : }
650 0 : if (container == mEndContainer && aIndexInContainer < mEndOffset &&
651 0 : !mEndOffsetWasIncremented) {
652 0 : ++mEndOffset;
653 : }
654 0 : if (container->IsSelectionDescendant() &&
655 0 : !aChild->IsDescendantOfCommonAncestorForRangeInSelection()) {
656 0 : MarkDescendants(aChild);
657 0 : aChild->SetDescendantOfCommonAncestorForRangeInSelection();
658 : }
659 :
660 0 : if (mStartOffsetWasIncremented || mEndOffsetWasIncremented) {
661 0 : MOZ_ASSERT(mAssertNextInsertOrAppendIndex == aIndexInContainer);
662 0 : MOZ_ASSERT(mAssertNextInsertOrAppendNode == aChild);
663 0 : MOZ_ASSERT(aChild->IsNodeOfType(nsINode::eDATA_NODE));
664 0 : mStartOffsetWasIncremented = mEndOffsetWasIncremented = false;
665 : #ifdef DEBUG
666 0 : mAssertNextInsertOrAppendIndex = -1;
667 0 : mAssertNextInsertOrAppendNode = nullptr;
668 : #endif
669 : }
670 0 : }
671 :
672 : void
673 0 : nsRange::ContentRemoved(nsIDocument* aDocument,
674 : nsIContent* aContainer,
675 : nsIContent* aChild,
676 : int32_t aIndexInContainer,
677 : nsIContent* aPreviousSibling)
678 : {
679 0 : NS_ASSERTION(mIsPositioned, "shouldn't be notified if not positioned");
680 0 : MOZ_ASSERT(!mStartOffsetWasIncremented && !mEndOffsetWasIncremented &&
681 : mAssertNextInsertOrAppendIndex == -1,
682 : "splitText failed to notify insert/append?");
683 :
684 0 : nsINode* container = NODE_FROM(aContainer, aDocument);
685 0 : bool gravitateStart = false;
686 0 : bool gravitateEnd = false;
687 0 : bool didCheckStartParentDescendant = false;
688 :
689 : // Adjust position if a sibling was removed...
690 0 : if (container == mStartContainer) {
691 0 : if (aIndexInContainer < mStartOffset) {
692 0 : --mStartOffset;
693 : }
694 : } else { // ...or gravitate if an ancestor was removed.
695 0 : didCheckStartParentDescendant = true;
696 : gravitateStart =
697 0 : nsContentUtils::ContentIsDescendantOf(mStartContainer, aChild);
698 : }
699 :
700 : // Do same thing for end boundry.
701 0 : if (container == mEndContainer) {
702 0 : if (aIndexInContainer < mEndOffset) {
703 0 : --mEndOffset;
704 : }
705 0 : } else if (didCheckStartParentDescendant &&
706 0 : mStartContainer == mEndContainer) {
707 0 : gravitateEnd = gravitateStart;
708 : } else {
709 0 : gravitateEnd = nsContentUtils::ContentIsDescendantOf(mEndContainer, aChild);
710 : }
711 :
712 0 : if (gravitateStart || gravitateEnd) {
713 0 : DoSetRange(gravitateStart ? container : mStartContainer.get(),
714 : gravitateStart ? aIndexInContainer : mStartOffset,
715 0 : gravitateEnd ? container : mEndContainer.get(),
716 : gravitateEnd ? aIndexInContainer : mEndOffset,
717 0 : mRoot);
718 : }
719 0 : if (container->IsSelectionDescendant() &&
720 0 : aChild->IsDescendantOfCommonAncestorForRangeInSelection()) {
721 0 : aChild->ClearDescendantOfCommonAncestorForRangeInSelection();
722 0 : UnmarkDescendants(aChild);
723 : }
724 0 : }
725 :
726 : void
727 0 : nsRange::ParentChainChanged(nsIContent *aContent)
728 : {
729 0 : MOZ_ASSERT(!mStartOffsetWasIncremented && !mEndOffsetWasIncremented &&
730 : mAssertNextInsertOrAppendIndex == -1,
731 : "splitText failed to notify insert/append?");
732 0 : NS_ASSERTION(mRoot == aContent, "Wrong ParentChainChanged notification?");
733 0 : nsINode* newRoot = IsValidBoundary(mStartContainer);
734 0 : NS_ASSERTION(newRoot, "No valid boundary or root found!");
735 0 : if (newRoot != IsValidBoundary(mEndContainer)) {
736 : // Sometimes ordering involved in cycle collection can lead to our
737 : // start parent and/or end parent being disconnected from our root
738 : // without our getting a ContentRemoved notification.
739 : // See bug 846096 for more details.
740 0 : NS_ASSERTION(mEndContainer->IsInNativeAnonymousSubtree(),
741 : "This special case should happen only with "
742 : "native-anonymous content");
743 : // When that happens, bail out and set pointers to null; since we're
744 : // in cycle collection and unreachable it shouldn't matter.
745 0 : Reset();
746 0 : return;
747 : }
748 : // This is safe without holding a strong ref to self as long as the change
749 : // of mRoot is the last thing in DoSetRange.
750 0 : DoSetRange(mStartContainer, mStartOffset, mEndContainer, mEndOffset, newRoot);
751 : }
752 :
753 : /******************************************************
754 : * Utilities for comparing points: API from nsIDOMRange
755 : ******************************************************/
756 : NS_IMETHODIMP
757 0 : nsRange::IsPointInRange(nsIDOMNode* aContainer, int32_t aOffset, bool* aResult)
758 : {
759 0 : nsCOMPtr<nsINode> container = do_QueryInterface(aContainer);
760 0 : if (!container) {
761 0 : return NS_ERROR_DOM_NOT_OBJECT_ERR;
762 : }
763 :
764 0 : ErrorResult rv;
765 0 : *aResult = IsPointInRange(*container, aOffset, rv);
766 0 : return rv.StealNSResult();
767 : }
768 :
769 : bool
770 0 : nsRange::IsPointInRange(nsINode& aContainer, uint32_t aOffset, ErrorResult& aRv)
771 : {
772 0 : uint16_t compareResult = ComparePoint(aContainer, aOffset, aRv);
773 : // If the node isn't in the range's document, it clearly isn't in the range.
774 0 : if (aRv.ErrorCodeIs(NS_ERROR_DOM_WRONG_DOCUMENT_ERR)) {
775 0 : aRv.SuppressException();
776 0 : return false;
777 : }
778 :
779 0 : return compareResult == 0;
780 : }
781 :
782 : // returns -1 if point is before range, 0 if point is in range,
783 : // 1 if point is after range.
784 : NS_IMETHODIMP
785 0 : nsRange::ComparePoint(nsIDOMNode* aContainer, int32_t aOffset, int16_t* aResult)
786 : {
787 0 : nsCOMPtr<nsINode> container = do_QueryInterface(aContainer);
788 0 : NS_ENSURE_TRUE(container, NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
789 :
790 0 : ErrorResult rv;
791 0 : *aResult = ComparePoint(*container, aOffset, rv);
792 0 : return rv.StealNSResult();
793 : }
794 :
795 : int16_t
796 0 : nsRange::ComparePoint(nsINode& aContainer, uint32_t aOffset, ErrorResult& aRv)
797 : {
798 : // our range is in a good state?
799 0 : if (!mIsPositioned) {
800 0 : aRv.Throw(NS_ERROR_NOT_INITIALIZED);
801 0 : return 0;
802 : }
803 :
804 0 : if (!nsContentUtils::ContentIsDescendantOf(&aContainer, mRoot)) {
805 0 : aRv.Throw(NS_ERROR_DOM_WRONG_DOCUMENT_ERR);
806 0 : return 0;
807 : }
808 :
809 0 : if (aContainer.NodeType() == nsIDOMNode::DOCUMENT_TYPE_NODE) {
810 0 : aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
811 0 : return 0;
812 : }
813 :
814 0 : if (aOffset > aContainer.Length()) {
815 0 : aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
816 0 : return 0;
817 : }
818 :
819 : int32_t cmp;
820 0 : if ((cmp = nsContentUtils::ComparePoints(&aContainer, aOffset,
821 : mStartContainer,
822 : mStartOffset)) <= 0) {
823 :
824 0 : return cmp;
825 : }
826 0 : if (nsContentUtils::ComparePoints(mEndContainer, mEndOffset,
827 : &aContainer, aOffset) == -1) {
828 0 : return 1;
829 : }
830 :
831 0 : return 0;
832 : }
833 :
834 : NS_IMETHODIMP
835 0 : nsRange::IntersectsNode(nsIDOMNode* aNode, bool* aResult)
836 : {
837 0 : *aResult = false;
838 :
839 0 : nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
840 : // TODO: This should throw a TypeError.
841 0 : NS_ENSURE_ARG(node);
842 :
843 0 : ErrorResult rv;
844 0 : *aResult = IntersectsNode(*node, rv);
845 0 : return rv.StealNSResult();
846 : }
847 :
848 : bool
849 0 : nsRange::IntersectsNode(nsINode& aNode, ErrorResult& aRv)
850 : {
851 0 : if (!mIsPositioned) {
852 0 : aRv.Throw(NS_ERROR_NOT_INITIALIZED);
853 0 : return false;
854 : }
855 :
856 : // Step 3.
857 0 : nsINode* parent = aNode.GetParentNode();
858 0 : if (!parent) {
859 : // Steps 2 and 4.
860 : // |parent| is null, so |node|'s root is |node| itself.
861 0 : return GetRoot() == &aNode;
862 : }
863 :
864 : // Step 5.
865 0 : int32_t nodeIndex = parent->IndexOf(&aNode);
866 :
867 : // Steps 6-7.
868 : // Note: if disconnected is true, ComparePoints returns 1.
869 0 : bool disconnected = false;
870 0 : bool result = nsContentUtils::ComparePoints(mStartContainer, mStartOffset,
871 : parent, nodeIndex + 1,
872 0 : &disconnected) < 0 &&
873 0 : nsContentUtils::ComparePoints(parent, nodeIndex,
874 : mEndContainer, mEndOffset,
875 0 : &disconnected) < 0;
876 :
877 : // Step 2.
878 0 : if (disconnected) {
879 0 : result = false;
880 : }
881 0 : return result;
882 : }
883 :
884 : /******************************************************
885 : * Private helper routines
886 : ******************************************************/
887 :
888 : // It's important that all setting of the range start/end points
889 : // go through this function, which will do all the right voodoo
890 : // for content notification of range ownership.
891 : // Calling DoSetRange with either parent argument null will collapse
892 : // the range to have both endpoints point to the other node
893 : void
894 41 : nsRange::DoSetRange(nsINode* aStartN, int32_t aStartOffset,
895 : nsINode* aEndN, int32_t aEndOffset,
896 : nsINode* aRoot, bool aNotInsertedYet)
897 : {
898 41 : NS_PRECONDITION((aStartN && aEndN && aRoot) ||
899 : (!aStartN && !aEndN && !aRoot),
900 : "Set all or none");
901 41 : NS_PRECONDITION(!aRoot || aNotInsertedYet ||
902 : (nsContentUtils::ContentIsDescendantOf(aStartN, aRoot) &&
903 : nsContentUtils::ContentIsDescendantOf(aEndN, aRoot) &&
904 : aRoot == IsValidBoundary(aStartN) &&
905 : aRoot == IsValidBoundary(aEndN)),
906 : "Wrong root");
907 41 : NS_PRECONDITION(!aRoot ||
908 : (aStartN->IsNodeOfType(nsINode::eCONTENT) &&
909 : aEndN->IsNodeOfType(nsINode::eCONTENT) &&
910 : aRoot ==
911 : static_cast<nsIContent*>(aStartN)->GetBindingParent() &&
912 : aRoot ==
913 : static_cast<nsIContent*>(aEndN)->GetBindingParent()) ||
914 : (!aRoot->GetParentNode() &&
915 : (aRoot->IsNodeOfType(nsINode::eDOCUMENT) ||
916 : aRoot->IsNodeOfType(nsINode::eATTRIBUTE) ||
917 : aRoot->IsNodeOfType(nsINode::eDOCUMENT_FRAGMENT) ||
918 : /*For backward compatibility*/
919 : aRoot->IsNodeOfType(nsINode::eCONTENT))),
920 : "Bad root");
921 :
922 41 : if (mRoot != aRoot) {
923 27 : if (mRoot) {
924 12 : mRoot->RemoveMutationObserver(this);
925 : }
926 27 : if (aRoot) {
927 17 : aRoot->AddMutationObserver(this);
928 : }
929 : }
930 : bool checkCommonAncestor =
931 82 : (mStartContainer != aStartN || mEndContainer != aEndN) &&
932 68 : IsInSelection() && !aNotInsertedYet;
933 41 : nsINode* oldCommonAncestor = checkCommonAncestor ? GetCommonAncestor() : nullptr;
934 41 : mStartContainer = aStartN;
935 41 : mStartOffset = aStartOffset;
936 41 : mEndContainer = aEndN;
937 41 : mEndOffset = aEndOffset;
938 41 : mIsPositioned = !!mStartContainer;
939 41 : if (checkCommonAncestor) {
940 0 : nsINode* newCommonAncestor = GetCommonAncestor();
941 0 : if (newCommonAncestor != oldCommonAncestor) {
942 0 : if (oldCommonAncestor) {
943 0 : UnregisterCommonAncestor(oldCommonAncestor);
944 : }
945 0 : if (newCommonAncestor) {
946 0 : RegisterCommonAncestor(newCommonAncestor);
947 : } else {
948 0 : NS_ASSERTION(!mIsPositioned, "unexpected disconnected nodes");
949 0 : mSelection = nullptr;
950 : }
951 : }
952 : }
953 :
954 : // This needs to be the last thing this function does, other than notifying
955 : // selection listeners. See comment in ParentChainChanged.
956 41 : mRoot = aRoot;
957 :
958 : // Notify any selection listeners. This has to occur last because otherwise the world
959 : // could be observed by a selection listener while the range was in an invalid state.
960 41 : if (mSelection) {
961 : // Our internal code should not move focus with using this instance while
962 : // it's calling Selection::NotifySelectionListeners() which may move focus
963 : // or calls selection listeners. So, let's set mCalledByJS to false here
964 : // since non-*JS() methods don't set it to false.
965 0 : AutoCalledByJSRestore calledByJSRestorer(*this);
966 0 : mCalledByJS = false;
967 : // Be aware, this range may be modified or stop being a range for selection
968 : // after this call. Additionally, the selection instance may have gone.
969 0 : RefPtr<Selection> selection = mSelection;
970 0 : selection->NotifySelectionListeners(calledByJSRestorer.SavedValue());
971 : }
972 41 : }
973 :
974 : static int32_t
975 0 : IndexOf(nsINode* aChild)
976 : {
977 0 : nsINode* parent = aChild->GetParentNode();
978 :
979 0 : return parent ? parent->IndexOf(aChild) : -1;
980 : }
981 :
982 : void
983 30 : nsRange::SetSelection(mozilla::dom::Selection* aSelection)
984 : {
985 30 : if (mSelection == aSelection) {
986 0 : return;
987 : }
988 : // At least one of aSelection and mSelection must be null
989 : // aSelection will be null when we are removing from a selection
990 : // and a range can't be in more than one selection at a time,
991 : // thus mSelection must be null too.
992 30 : MOZ_ASSERT(!aSelection || !mSelection);
993 :
994 30 : mSelection = aSelection;
995 30 : nsINode* commonAncestor = GetCommonAncestor();
996 30 : NS_ASSERTION(commonAncestor, "unexpected disconnected nodes");
997 30 : if (mSelection) {
998 17 : RegisterCommonAncestor(commonAncestor);
999 : } else {
1000 13 : UnregisterCommonAncestor(commonAncestor);
1001 : }
1002 : }
1003 :
1004 : nsINode*
1005 30 : nsRange::GetCommonAncestor() const
1006 : {
1007 60 : return mIsPositioned ?
1008 30 : nsContentUtils::GetCommonAncestor(mStartContainer, mEndContainer) :
1009 30 : nullptr;
1010 : }
1011 :
1012 : void
1013 0 : nsRange::Reset()
1014 : {
1015 0 : DoSetRange(nullptr, 0, nullptr, 0, nullptr);
1016 0 : }
1017 :
1018 : /******************************************************
1019 : * public functionality
1020 : ******************************************************/
1021 :
1022 : NS_IMETHODIMP
1023 0 : nsRange::GetStartContainer(nsIDOMNode** aStartContainer)
1024 : {
1025 0 : if (!mIsPositioned)
1026 0 : return NS_ERROR_NOT_INITIALIZED;
1027 :
1028 0 : return CallQueryInterface(mStartContainer, aStartContainer);
1029 : }
1030 :
1031 : nsINode*
1032 18 : nsRange::GetStartContainer(ErrorResult& aRv) const
1033 : {
1034 18 : if (!mIsPositioned) {
1035 0 : aRv.Throw(NS_ERROR_NOT_INITIALIZED);
1036 0 : return nullptr;
1037 : }
1038 :
1039 18 : return mStartContainer;
1040 : }
1041 :
1042 : NS_IMETHODIMP
1043 0 : nsRange::GetStartOffset(int32_t* aStartOffset)
1044 : {
1045 0 : if (!mIsPositioned)
1046 0 : return NS_ERROR_NOT_INITIALIZED;
1047 :
1048 0 : *aStartOffset = mStartOffset;
1049 :
1050 0 : return NS_OK;
1051 : }
1052 :
1053 : uint32_t
1054 18 : nsRange::GetStartOffset(ErrorResult& aRv) const
1055 : {
1056 18 : if (!mIsPositioned) {
1057 0 : aRv.Throw(NS_ERROR_NOT_INITIALIZED);
1058 0 : return 0;
1059 : }
1060 :
1061 18 : return mStartOffset;
1062 : }
1063 :
1064 : NS_IMETHODIMP
1065 0 : nsRange::GetEndContainer(nsIDOMNode** aEndContainer)
1066 : {
1067 0 : if (!mIsPositioned)
1068 0 : return NS_ERROR_NOT_INITIALIZED;
1069 :
1070 0 : return CallQueryInterface(mEndContainer, aEndContainer);
1071 : }
1072 :
1073 : nsINode*
1074 18 : nsRange::GetEndContainer(ErrorResult& aRv) const
1075 : {
1076 18 : if (!mIsPositioned) {
1077 0 : aRv.Throw(NS_ERROR_NOT_INITIALIZED);
1078 0 : return nullptr;
1079 : }
1080 :
1081 18 : return mEndContainer;
1082 : }
1083 :
1084 : NS_IMETHODIMP
1085 0 : nsRange::GetEndOffset(int32_t* aEndOffset)
1086 : {
1087 0 : if (!mIsPositioned)
1088 0 : return NS_ERROR_NOT_INITIALIZED;
1089 :
1090 0 : *aEndOffset = mEndOffset;
1091 :
1092 0 : return NS_OK;
1093 : }
1094 :
1095 : uint32_t
1096 17 : nsRange::GetEndOffset(ErrorResult& aRv) const
1097 : {
1098 17 : if (!mIsPositioned) {
1099 0 : aRv.Throw(NS_ERROR_NOT_INITIALIZED);
1100 0 : return 0;
1101 : }
1102 :
1103 17 : return mEndOffset;
1104 : }
1105 :
1106 : NS_IMETHODIMP
1107 0 : nsRange::GetCollapsed(bool* aIsCollapsed)
1108 : {
1109 0 : if (!mIsPositioned)
1110 0 : return NS_ERROR_NOT_INITIALIZED;
1111 :
1112 0 : *aIsCollapsed = Collapsed();
1113 :
1114 0 : return NS_OK;
1115 : }
1116 :
1117 : nsINode*
1118 0 : nsRange::GetCommonAncestorContainer(ErrorResult& aRv) const
1119 : {
1120 0 : if (!mIsPositioned) {
1121 0 : aRv.Throw(NS_ERROR_NOT_INITIALIZED);
1122 0 : return nullptr;
1123 : }
1124 :
1125 0 : return nsContentUtils::GetCommonAncestor(mStartContainer, mEndContainer);
1126 : }
1127 :
1128 : NS_IMETHODIMP
1129 0 : nsRange::GetCommonAncestorContainer(nsIDOMNode** aCommonParent)
1130 : {
1131 0 : ErrorResult rv;
1132 0 : nsINode* commonAncestor = GetCommonAncestorContainer(rv);
1133 0 : if (commonAncestor) {
1134 0 : NS_ADDREF(*aCommonParent = commonAncestor->AsDOMNode());
1135 : } else {
1136 0 : *aCommonParent = nullptr;
1137 : }
1138 :
1139 0 : return rv.StealNSResult();
1140 : }
1141 :
1142 : /* static */
1143 : bool
1144 34 : nsRange::IsValidOffset(nsINode* aNode, int32_t aOffset)
1145 : {
1146 34 : return aNode &&
1147 68 : aOffset >= 0 &&
1148 68 : static_cast<size_t>(aOffset) <= aNode->Length();
1149 : }
1150 :
1151 : nsINode*
1152 63 : nsRange::IsValidBoundary(nsINode* aNode)
1153 : {
1154 63 : if (!aNode) {
1155 0 : return nullptr;
1156 : }
1157 :
1158 63 : if (aNode->IsNodeOfType(nsINode::eCONTENT)) {
1159 57 : if (aNode->NodeInfo()->NameAtom() == nsGkAtoms::documentTypeNodeName) {
1160 0 : return nullptr;
1161 : }
1162 :
1163 57 : nsIContent* content = static_cast<nsIContent*>(aNode);
1164 :
1165 57 : if (!mMaySpanAnonymousSubtrees) {
1166 : // If the node is in a shadow tree then the ShadowRoot is the root.
1167 57 : ShadowRoot* containingShadow = content->GetContainingShadow();
1168 57 : if (containingShadow) {
1169 0 : return containingShadow;
1170 : }
1171 :
1172 : // If the node has a binding parent, that should be the root.
1173 : // XXXbz maybe only for native anonymous content?
1174 57 : nsINode* root = content->GetBindingParent();
1175 57 : if (root) {
1176 45 : return root;
1177 : }
1178 : }
1179 : }
1180 :
1181 : // Elements etc. must be in document or in document fragment,
1182 : // text nodes in document, in document fragment or in attribute.
1183 18 : nsINode* root = aNode->GetUncomposedDoc();
1184 18 : if (root) {
1185 18 : return root;
1186 : }
1187 :
1188 0 : root = aNode->SubtreeRoot();
1189 :
1190 0 : NS_ASSERTION(!root->IsNodeOfType(nsINode::eDOCUMENT),
1191 : "GetUncomposedDoc should have returned a doc");
1192 :
1193 : // We allow this because of backward compatibility.
1194 0 : return root;
1195 : }
1196 :
1197 : void
1198 2 : nsRange::SetStartJS(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr)
1199 : {
1200 4 : AutoCalledByJSRestore calledByJSRestorer(*this);
1201 2 : mCalledByJS = true;
1202 2 : SetStart(aNode, aOffset, aErr);
1203 2 : }
1204 :
1205 : void
1206 2 : nsRange::SetStart(nsINode& aNode, uint32_t aOffset, ErrorResult& aRv)
1207 : {
1208 4 : if (!nsContentUtils::LegacyIsCallerNativeCode() &&
1209 2 : !nsContentUtils::CanCallerAccess(&aNode)) {
1210 0 : aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
1211 0 : return;
1212 : }
1213 :
1214 4 : AutoInvalidateSelection atEndOfBlock(this);
1215 2 : aRv = SetStart(&aNode, aOffset);
1216 : }
1217 :
1218 : NS_IMETHODIMP
1219 0 : nsRange::SetStart(nsIDOMNode* aContainer, int32_t aOffset)
1220 : {
1221 0 : nsCOMPtr<nsINode> container = do_QueryInterface(aContainer);
1222 0 : if (!container) {
1223 0 : return NS_ERROR_DOM_NOT_OBJECT_ERR;
1224 : }
1225 :
1226 0 : ErrorResult rv;
1227 0 : SetStart(*container, aOffset, rv);
1228 0 : return rv.StealNSResult();
1229 : }
1230 :
1231 : /* virtual */ nsresult
1232 2 : nsRange::SetStart(nsINode* aContainer, int32_t aOffset)
1233 : {
1234 2 : nsINode* newRoot = IsValidBoundary(aContainer);
1235 2 : if (!newRoot) {
1236 0 : return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR;
1237 : }
1238 :
1239 2 : if (!IsValidOffset(aContainer, aOffset)) {
1240 0 : return NS_ERROR_DOM_INDEX_SIZE_ERR;
1241 : }
1242 :
1243 : // Collapse if not positioned yet, if positioned in another doc or
1244 : // if the new start is after end.
1245 2 : if (!mIsPositioned || newRoot != mRoot ||
1246 0 : nsContentUtils::ComparePoints(aContainer, aOffset,
1247 : mEndContainer, mEndOffset) == 1) {
1248 2 : DoSetRange(aContainer, aOffset, aContainer, aOffset, newRoot);
1249 :
1250 2 : return NS_OK;
1251 : }
1252 :
1253 0 : DoSetRange(aContainer, aOffset, mEndContainer, mEndOffset, mRoot);
1254 :
1255 0 : return NS_OK;
1256 : }
1257 :
1258 : void
1259 0 : nsRange::SetStartBeforeJS(nsINode& aNode, ErrorResult& aErr)
1260 : {
1261 0 : AutoCalledByJSRestore calledByJSRestorer(*this);
1262 0 : mCalledByJS = true;
1263 0 : SetStartBefore(aNode, aErr);
1264 0 : }
1265 :
1266 : void
1267 0 : nsRange::SetStartBefore(nsINode& aNode, ErrorResult& aRv)
1268 : {
1269 0 : if (!nsContentUtils::LegacyIsCallerNativeCode() &&
1270 0 : !nsContentUtils::CanCallerAccess(&aNode)) {
1271 0 : aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
1272 0 : return;
1273 : }
1274 :
1275 0 : AutoInvalidateSelection atEndOfBlock(this);
1276 0 : int32_t offset = -1;
1277 0 : nsINode* container = GetContainerAndOffsetBefore(&aNode, &offset);
1278 0 : aRv = SetStart(container, offset);
1279 : }
1280 :
1281 : NS_IMETHODIMP
1282 0 : nsRange::SetStartBefore(nsIDOMNode* aSibling)
1283 : {
1284 0 : nsCOMPtr<nsINode> sibling = do_QueryInterface(aSibling);
1285 0 : if (!sibling) {
1286 0 : return NS_ERROR_DOM_NOT_OBJECT_ERR;
1287 : }
1288 :
1289 0 : ErrorResult rv;
1290 0 : SetStartBefore(*sibling, rv);
1291 0 : return rv.StealNSResult();
1292 : }
1293 :
1294 : void
1295 0 : nsRange::SetStartAfterJS(nsINode& aNode, ErrorResult& aErr)
1296 : {
1297 0 : AutoCalledByJSRestore calledByJSRestorer(*this);
1298 0 : mCalledByJS = true;
1299 0 : SetStartAfter(aNode, aErr);
1300 0 : }
1301 :
1302 : void
1303 0 : nsRange::SetStartAfter(nsINode& aNode, ErrorResult& aRv)
1304 : {
1305 0 : if (!nsContentUtils::LegacyIsCallerNativeCode() &&
1306 0 : !nsContentUtils::CanCallerAccess(&aNode)) {
1307 0 : aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
1308 0 : return;
1309 : }
1310 :
1311 0 : AutoInvalidateSelection atEndOfBlock(this);
1312 0 : int32_t offset = -1;
1313 0 : nsINode* container = GetContainerAndOffsetAfter(&aNode, &offset);
1314 0 : aRv = SetStart(container, offset);
1315 : }
1316 :
1317 : NS_IMETHODIMP
1318 0 : nsRange::SetStartAfter(nsIDOMNode* aSibling)
1319 : {
1320 0 : nsCOMPtr<nsINode> sibling = do_QueryInterface(aSibling);
1321 0 : if (!sibling) {
1322 0 : return NS_ERROR_DOM_NOT_OBJECT_ERR;
1323 : }
1324 :
1325 0 : ErrorResult rv;
1326 0 : SetStartAfter(*sibling, rv);
1327 0 : return rv.StealNSResult();
1328 : }
1329 :
1330 : void
1331 2 : nsRange::SetEndJS(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr)
1332 : {
1333 4 : AutoCalledByJSRestore calledByJSRestorer(*this);
1334 2 : mCalledByJS = true;
1335 2 : SetEnd(aNode, aOffset, aErr);
1336 2 : }
1337 :
1338 : void
1339 2 : nsRange::SetEnd(nsINode& aNode, uint32_t aOffset, ErrorResult& aRv)
1340 : {
1341 4 : if (!nsContentUtils::LegacyIsCallerNativeCode() &&
1342 2 : !nsContentUtils::CanCallerAccess(&aNode)) {
1343 0 : aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
1344 0 : return;
1345 : }
1346 4 : AutoInvalidateSelection atEndOfBlock(this);
1347 2 : aRv = SetEnd(&aNode, aOffset);
1348 : }
1349 :
1350 : NS_IMETHODIMP
1351 0 : nsRange::SetEnd(nsIDOMNode* aContainer, int32_t aOffset)
1352 : {
1353 0 : nsCOMPtr<nsINode> container = do_QueryInterface(aContainer);
1354 0 : if (!container) {
1355 0 : return NS_ERROR_DOM_NOT_OBJECT_ERR;
1356 : }
1357 :
1358 0 : ErrorResult rv;
1359 0 : SetEnd(*container, aOffset, rv);
1360 0 : return rv.StealNSResult();
1361 : }
1362 :
1363 : /* virtual */ nsresult
1364 2 : nsRange::SetEnd(nsINode* aContainer, int32_t aOffset)
1365 : {
1366 2 : nsINode* newRoot = IsValidBoundary(aContainer);
1367 2 : if (!newRoot) {
1368 0 : return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR;
1369 : }
1370 :
1371 2 : if (!IsValidOffset(aContainer, aOffset)) {
1372 0 : return NS_ERROR_DOM_INDEX_SIZE_ERR;
1373 : }
1374 :
1375 : // Collapse if not positioned yet, if positioned in another doc or
1376 : // if the new end is before start.
1377 4 : if (!mIsPositioned || newRoot != mRoot ||
1378 2 : nsContentUtils::ComparePoints(mStartContainer, mStartOffset,
1379 : aContainer, aOffset) == 1) {
1380 0 : DoSetRange(aContainer, aOffset, aContainer, aOffset, newRoot);
1381 :
1382 0 : return NS_OK;
1383 : }
1384 :
1385 2 : DoSetRange(mStartContainer, mStartOffset, aContainer, aOffset, mRoot);
1386 :
1387 2 : return NS_OK;
1388 : }
1389 :
1390 : nsresult
1391 15 : nsRange::SetStartAndEnd(nsINode* aStartContainer, int32_t aStartOffset,
1392 : nsINode* aEndContainer, int32_t aEndOffset)
1393 : {
1394 15 : if (NS_WARN_IF(!aStartContainer) || NS_WARN_IF(!aEndContainer)) {
1395 0 : return NS_ERROR_INVALID_ARG;
1396 : }
1397 :
1398 15 : nsINode* newStartRoot = IsValidBoundary(aStartContainer);
1399 15 : if (!newStartRoot) {
1400 0 : return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR;
1401 : }
1402 15 : if (!IsValidOffset(aStartContainer, aStartOffset)) {
1403 0 : return NS_ERROR_DOM_INDEX_SIZE_ERR;
1404 : }
1405 :
1406 15 : if (aStartContainer == aEndContainer) {
1407 15 : if (!IsValidOffset(aEndContainer, aEndOffset)) {
1408 0 : return NS_ERROR_DOM_INDEX_SIZE_ERR;
1409 : }
1410 : // If the end offset is less than the start offset, this should be
1411 : // collapsed at the end offset.
1412 15 : if (aStartOffset > aEndOffset) {
1413 : DoSetRange(aEndContainer, aEndOffset,
1414 0 : aEndContainer, aEndOffset, newStartRoot);
1415 : } else {
1416 : DoSetRange(aStartContainer, aStartOffset,
1417 15 : aEndContainer, aEndOffset, newStartRoot);
1418 : }
1419 15 : return NS_OK;
1420 : }
1421 :
1422 0 : nsINode* newEndRoot = IsValidBoundary(aEndContainer);
1423 0 : if (!newEndRoot) {
1424 0 : return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR;
1425 : }
1426 0 : if (!IsValidOffset(aEndContainer, aEndOffset)) {
1427 0 : return NS_ERROR_DOM_INDEX_SIZE_ERR;
1428 : }
1429 :
1430 : // If they have different root, this should be collapsed at the end point.
1431 0 : if (newStartRoot != newEndRoot) {
1432 : DoSetRange(aEndContainer, aEndOffset,
1433 0 : aEndContainer, aEndOffset, newEndRoot);
1434 0 : return NS_OK;
1435 : }
1436 :
1437 : // If the end point is before the start point, this should be collapsed at
1438 : // the end point.
1439 0 : if (nsContentUtils::ComparePoints(aStartContainer, aStartOffset,
1440 : aEndContainer, aEndOffset) == 1) {
1441 : DoSetRange(aEndContainer, aEndOffset,
1442 0 : aEndContainer, aEndOffset, newEndRoot);
1443 0 : return NS_OK;
1444 : }
1445 :
1446 : // Otherwise, set the range as specified.
1447 : DoSetRange(aStartContainer, aStartOffset,
1448 0 : aEndContainer, aEndOffset, newStartRoot);
1449 0 : return NS_OK;
1450 : }
1451 :
1452 : void
1453 0 : nsRange::SetEndBeforeJS(nsINode& aNode, ErrorResult& aErr)
1454 : {
1455 0 : AutoCalledByJSRestore calledByJSRestorer(*this);
1456 0 : mCalledByJS = true;
1457 0 : SetEndBefore(aNode, aErr);
1458 0 : }
1459 :
1460 : void
1461 0 : nsRange::SetEndBefore(nsINode& aNode, ErrorResult& aRv)
1462 : {
1463 0 : if (!nsContentUtils::LegacyIsCallerNativeCode() &&
1464 0 : !nsContentUtils::CanCallerAccess(&aNode)) {
1465 0 : aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
1466 0 : return;
1467 : }
1468 :
1469 0 : AutoInvalidateSelection atEndOfBlock(this);
1470 0 : int32_t offset = -1;
1471 0 : nsINode* container = GetContainerAndOffsetBefore(&aNode, &offset);
1472 0 : aRv = SetEnd(container, offset);
1473 : }
1474 :
1475 : NS_IMETHODIMP
1476 0 : nsRange::SetEndBefore(nsIDOMNode* aSibling)
1477 : {
1478 0 : nsCOMPtr<nsINode> sibling = do_QueryInterface(aSibling);
1479 0 : if (!sibling) {
1480 0 : return NS_ERROR_DOM_NOT_OBJECT_ERR;
1481 : }
1482 :
1483 0 : ErrorResult rv;
1484 0 : SetEndBefore(*sibling, rv);
1485 0 : return rv.StealNSResult();
1486 : }
1487 :
1488 : void
1489 0 : nsRange::SetEndAfterJS(nsINode& aNode, ErrorResult& aErr)
1490 : {
1491 0 : AutoCalledByJSRestore calledByJSRestorer(*this);
1492 0 : mCalledByJS = true;
1493 0 : SetEndAfter(aNode, aErr);
1494 0 : }
1495 :
1496 : void
1497 0 : nsRange::SetEndAfter(nsINode& aNode, ErrorResult& aRv)
1498 : {
1499 0 : if (!nsContentUtils::LegacyIsCallerNativeCode() &&
1500 0 : !nsContentUtils::CanCallerAccess(&aNode)) {
1501 0 : aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
1502 0 : return;
1503 : }
1504 :
1505 0 : AutoInvalidateSelection atEndOfBlock(this);
1506 0 : int32_t offset = -1;
1507 0 : nsINode* container = GetContainerAndOffsetAfter(&aNode, &offset);
1508 0 : aRv = SetEnd(container, offset);
1509 : }
1510 :
1511 : NS_IMETHODIMP
1512 0 : nsRange::SetEndAfter(nsIDOMNode* aSibling)
1513 : {
1514 0 : nsCOMPtr<nsINode> sibling = do_QueryInterface(aSibling);
1515 0 : if (!sibling) {
1516 0 : return NS_ERROR_DOM_NOT_OBJECT_ERR;
1517 : }
1518 :
1519 0 : ErrorResult rv;
1520 0 : SetEndAfter(*sibling, rv);
1521 0 : return rv.StealNSResult();
1522 : }
1523 :
1524 : NS_IMETHODIMP
1525 0 : nsRange::Collapse(bool aToStart)
1526 : {
1527 0 : if (!mIsPositioned)
1528 0 : return NS_ERROR_NOT_INITIALIZED;
1529 :
1530 0 : AutoInvalidateSelection atEndOfBlock(this);
1531 0 : if (aToStart) {
1532 0 : DoSetRange(mStartContainer, mStartOffset,
1533 0 : mStartContainer, mStartOffset, mRoot);
1534 : } else {
1535 0 : DoSetRange(mEndContainer, mEndOffset,
1536 0 : mEndContainer, mEndOffset, mRoot);
1537 : }
1538 :
1539 0 : return NS_OK;
1540 : }
1541 :
1542 : void
1543 0 : nsRange::CollapseJS(bool aToStart)
1544 : {
1545 0 : AutoCalledByJSRestore calledByJSRestorer(*this);
1546 0 : mCalledByJS = true;
1547 0 : Unused << Collapse(aToStart);
1548 0 : }
1549 :
1550 : NS_IMETHODIMP
1551 0 : nsRange::SelectNode(nsIDOMNode* aN)
1552 : {
1553 0 : nsCOMPtr<nsINode> node = do_QueryInterface(aN);
1554 0 : NS_ENSURE_TRUE(node, NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
1555 :
1556 0 : ErrorResult rv;
1557 0 : SelectNode(*node, rv);
1558 0 : return rv.StealNSResult();
1559 : }
1560 :
1561 : void
1562 0 : nsRange::SelectNodeJS(nsINode& aNode, ErrorResult& aErr)
1563 : {
1564 0 : AutoCalledByJSRestore calledByJSRestorer(*this);
1565 0 : mCalledByJS = true;
1566 0 : SelectNode(aNode, aErr);
1567 0 : }
1568 :
1569 : void
1570 0 : nsRange::SelectNode(nsINode& aNode, ErrorResult& aRv)
1571 : {
1572 0 : if (!nsContentUtils::LegacyIsCallerNativeCode() &&
1573 0 : !nsContentUtils::CanCallerAccess(&aNode)) {
1574 0 : aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
1575 0 : return;
1576 : }
1577 :
1578 0 : nsINode* container = aNode.GetParentNode();
1579 0 : nsINode* newRoot = IsValidBoundary(container);
1580 0 : if (!newRoot) {
1581 0 : aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
1582 0 : return;
1583 : }
1584 :
1585 0 : int32_t index = container->IndexOf(&aNode);
1586 0 : if (index < 0) {
1587 0 : aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
1588 0 : return;
1589 : }
1590 :
1591 0 : AutoInvalidateSelection atEndOfBlock(this);
1592 0 : DoSetRange(container, index, container, index + 1, newRoot);
1593 : }
1594 :
1595 : NS_IMETHODIMP
1596 2 : nsRange::SelectNodeContents(nsIDOMNode* aN)
1597 : {
1598 4 : nsCOMPtr<nsINode> node = do_QueryInterface(aN);
1599 2 : NS_ENSURE_TRUE(node, NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
1600 :
1601 4 : ErrorResult rv;
1602 2 : SelectNodeContents(*node, rv);
1603 2 : return rv.StealNSResult();
1604 : }
1605 :
1606 : void
1607 0 : nsRange::SelectNodeContentsJS(nsINode& aNode, ErrorResult& aErr)
1608 : {
1609 0 : AutoCalledByJSRestore calledByJSRestorer(*this);
1610 0 : mCalledByJS = true;
1611 0 : SelectNodeContents(aNode, aErr);
1612 0 : }
1613 :
1614 : void
1615 2 : nsRange::SelectNodeContents(nsINode& aNode, ErrorResult& aRv)
1616 : {
1617 2 : if (!nsContentUtils::LegacyIsCallerNativeCode() &&
1618 0 : !nsContentUtils::CanCallerAccess(&aNode)) {
1619 0 : aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
1620 0 : return;
1621 : }
1622 :
1623 2 : nsINode* newRoot = IsValidBoundary(&aNode);
1624 2 : if (!newRoot) {
1625 0 : aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
1626 0 : return;
1627 : }
1628 :
1629 4 : AutoInvalidateSelection atEndOfBlock(this);
1630 2 : DoSetRange(&aNode, 0, &aNode, aNode.Length(), newRoot);
1631 : }
1632 :
1633 : // The Subtree Content Iterator only returns subtrees that are
1634 : // completely within a given range. It doesn't return a CharacterData
1635 : // node that contains either the start or end point of the range.,
1636 : // nor does it return element nodes when nothing in the element is selected.
1637 : // We need an iterator that will also include these start/end points
1638 : // so that our methods/algorithms aren't cluttered with special
1639 : // case code that tries to include these points while iterating.
1640 : //
1641 : // The RangeSubtreeIterator class mimics the nsIContentIterator
1642 : // methods we need, so should the Content Iterator support the
1643 : // start/end points in the future, we can switchover relatively
1644 : // easy.
1645 :
1646 : class MOZ_STACK_CLASS RangeSubtreeIterator
1647 : {
1648 : private:
1649 :
1650 : enum RangeSubtreeIterState { eDone=0,
1651 : eUseStart,
1652 : eUseIterator,
1653 : eUseEnd };
1654 :
1655 : nsCOMPtr<nsIContentIterator> mIter;
1656 : RangeSubtreeIterState mIterState;
1657 :
1658 : nsCOMPtr<nsINode> mStart;
1659 : nsCOMPtr<nsINode> mEnd;
1660 :
1661 : public:
1662 :
1663 0 : RangeSubtreeIterator()
1664 0 : : mIterState(eDone)
1665 : {
1666 0 : }
1667 0 : ~RangeSubtreeIterator()
1668 0 : {
1669 0 : }
1670 :
1671 : nsresult Init(nsRange *aRange);
1672 : already_AddRefed<nsINode> GetCurrentNode();
1673 : void First();
1674 : void Last();
1675 : void Next();
1676 : void Prev();
1677 :
1678 0 : bool IsDone()
1679 : {
1680 0 : return mIterState == eDone;
1681 : }
1682 : };
1683 :
1684 : nsresult
1685 0 : RangeSubtreeIterator::Init(nsRange *aRange)
1686 : {
1687 0 : mIterState = eDone;
1688 0 : if (aRange->Collapsed()) {
1689 0 : return NS_OK;
1690 : }
1691 :
1692 : // Grab the start point of the range and QI it to
1693 : // a CharacterData pointer. If it is CharacterData store
1694 : // a pointer to the node.
1695 :
1696 0 : ErrorResult rv;
1697 0 : nsCOMPtr<nsINode> node = aRange->GetStartContainer(rv);
1698 0 : if (!node) return NS_ERROR_FAILURE;
1699 :
1700 0 : nsCOMPtr<nsIDOMCharacterData> startData = do_QueryInterface(node);
1701 0 : if (startData || (node->IsElement() &&
1702 0 : node->AsElement()->GetChildCount() == aRange->GetStartOffset(rv))) {
1703 0 : mStart = node;
1704 : }
1705 :
1706 : // Grab the end point of the range and QI it to
1707 : // a CharacterData pointer. If it is CharacterData store
1708 : // a pointer to the node.
1709 :
1710 0 : node = aRange->GetEndContainer(rv);
1711 0 : if (!node) return NS_ERROR_FAILURE;
1712 :
1713 0 : nsCOMPtr<nsIDOMCharacterData> endData = do_QueryInterface(node);
1714 0 : if (endData || (node->IsElement() && aRange->GetEndOffset(rv) == 0)) {
1715 0 : mEnd = node;
1716 : }
1717 :
1718 0 : if (mStart && mStart == mEnd)
1719 : {
1720 : // The range starts and stops in the same CharacterData
1721 : // node. Null out the end pointer so we only visit the
1722 : // node once!
1723 :
1724 0 : mEnd = nullptr;
1725 : }
1726 : else
1727 : {
1728 : // Now create a Content Subtree Iterator to be used
1729 : // for the subtrees between the end points!
1730 :
1731 0 : mIter = NS_NewContentSubtreeIterator();
1732 :
1733 0 : nsresult res = mIter->Init(aRange);
1734 0 : if (NS_FAILED(res)) return res;
1735 :
1736 0 : if (mIter->IsDone())
1737 : {
1738 : // The subtree iterator thinks there's nothing
1739 : // to iterate over, so just free it up so we
1740 : // don't accidentally call into it.
1741 :
1742 0 : mIter = nullptr;
1743 : }
1744 : }
1745 :
1746 : // Initialize the iterator by calling First().
1747 : // Note that we are ignoring the return value on purpose!
1748 :
1749 0 : First();
1750 :
1751 0 : return NS_OK;
1752 : }
1753 :
1754 : already_AddRefed<nsINode>
1755 0 : RangeSubtreeIterator::GetCurrentNode()
1756 : {
1757 0 : nsCOMPtr<nsINode> node;
1758 :
1759 0 : if (mIterState == eUseStart && mStart) {
1760 0 : node = mStart;
1761 0 : } else if (mIterState == eUseEnd && mEnd) {
1762 0 : node = mEnd;
1763 0 : } else if (mIterState == eUseIterator && mIter) {
1764 0 : node = mIter->GetCurrentNode();
1765 : }
1766 :
1767 0 : return node.forget();
1768 : }
1769 :
1770 : void
1771 0 : RangeSubtreeIterator::First()
1772 : {
1773 0 : if (mStart)
1774 0 : mIterState = eUseStart;
1775 0 : else if (mIter)
1776 : {
1777 0 : mIter->First();
1778 :
1779 0 : mIterState = eUseIterator;
1780 : }
1781 0 : else if (mEnd)
1782 0 : mIterState = eUseEnd;
1783 : else
1784 0 : mIterState = eDone;
1785 0 : }
1786 :
1787 : void
1788 0 : RangeSubtreeIterator::Last()
1789 : {
1790 0 : if (mEnd)
1791 0 : mIterState = eUseEnd;
1792 0 : else if (mIter)
1793 : {
1794 0 : mIter->Last();
1795 :
1796 0 : mIterState = eUseIterator;
1797 : }
1798 0 : else if (mStart)
1799 0 : mIterState = eUseStart;
1800 : else
1801 0 : mIterState = eDone;
1802 0 : }
1803 :
1804 : void
1805 0 : RangeSubtreeIterator::Next()
1806 : {
1807 0 : if (mIterState == eUseStart)
1808 : {
1809 0 : if (mIter)
1810 : {
1811 0 : mIter->First();
1812 :
1813 0 : mIterState = eUseIterator;
1814 : }
1815 0 : else if (mEnd)
1816 0 : mIterState = eUseEnd;
1817 : else
1818 0 : mIterState = eDone;
1819 : }
1820 0 : else if (mIterState == eUseIterator)
1821 : {
1822 0 : mIter->Next();
1823 :
1824 0 : if (mIter->IsDone())
1825 : {
1826 0 : if (mEnd)
1827 0 : mIterState = eUseEnd;
1828 : else
1829 0 : mIterState = eDone;
1830 : }
1831 : }
1832 : else
1833 0 : mIterState = eDone;
1834 0 : }
1835 :
1836 : void
1837 0 : RangeSubtreeIterator::Prev()
1838 : {
1839 0 : if (mIterState == eUseEnd)
1840 : {
1841 0 : if (mIter)
1842 : {
1843 0 : mIter->Last();
1844 :
1845 0 : mIterState = eUseIterator;
1846 : }
1847 0 : else if (mStart)
1848 0 : mIterState = eUseStart;
1849 : else
1850 0 : mIterState = eDone;
1851 : }
1852 0 : else if (mIterState == eUseIterator)
1853 : {
1854 0 : mIter->Prev();
1855 :
1856 0 : if (mIter->IsDone())
1857 : {
1858 0 : if (mStart)
1859 0 : mIterState = eUseStart;
1860 : else
1861 0 : mIterState = eDone;
1862 : }
1863 : }
1864 : else
1865 0 : mIterState = eDone;
1866 0 : }
1867 :
1868 :
1869 : // CollapseRangeAfterDelete() is a utility method that is used by
1870 : // DeleteContents() and ExtractContents() to collapse the range
1871 : // in the correct place, under the range's root container (the
1872 : // range end points common container) as outlined by the Range spec:
1873 : //
1874 : // http://www.w3.org/TR/2000/REC-DOM-Level-2-Traversal-Range-20001113/ranges.html
1875 : // The assumption made by this method is that the delete or extract
1876 : // has been done already, and left the range in a state where there is
1877 : // no content between the 2 end points.
1878 :
1879 : static nsresult
1880 0 : CollapseRangeAfterDelete(nsRange* aRange)
1881 : {
1882 0 : NS_ENSURE_ARG_POINTER(aRange);
1883 :
1884 : // Check if range gravity took care of collapsing the range for us!
1885 0 : if (aRange->Collapsed())
1886 : {
1887 : // aRange is collapsed so there's nothing for us to do.
1888 : //
1889 : // There are 2 possible scenarios here:
1890 : //
1891 : // 1. aRange could've been collapsed prior to the delete/extract,
1892 : // which would've resulted in nothing being removed, so aRange
1893 : // is already where it should be.
1894 : //
1895 : // 2. Prior to the delete/extract, aRange's start and end were in
1896 : // the same container which would mean everything between them
1897 : // was removed, causing range gravity to collapse the range.
1898 :
1899 0 : return NS_OK;
1900 : }
1901 :
1902 : // aRange isn't collapsed so figure out the appropriate place to collapse!
1903 : // First get both end points and their common ancestor.
1904 :
1905 0 : ErrorResult rv;
1906 0 : nsCOMPtr<nsINode> commonAncestor = aRange->GetCommonAncestorContainer(rv);
1907 0 : if (rv.Failed()) return rv.StealNSResult();
1908 :
1909 0 : nsCOMPtr<nsINode> startContainer = aRange->GetStartContainer(rv);
1910 0 : if (rv.Failed()) return rv.StealNSResult();
1911 0 : nsCOMPtr<nsINode> endContainer = aRange->GetEndContainer(rv);
1912 0 : if (rv.Failed()) return rv.StealNSResult();
1913 :
1914 : // Collapse to one of the end points if they are already in the
1915 : // commonAncestor. This should work ok since this method is called
1916 : // immediately after a delete or extract that leaves no content
1917 : // between the 2 end points!
1918 :
1919 0 : if (startContainer == commonAncestor)
1920 0 : return aRange->Collapse(true);
1921 0 : if (endContainer == commonAncestor)
1922 0 : return aRange->Collapse(false);
1923 :
1924 : // End points are at differing levels. We want to collapse to the
1925 : // point that is between the 2 subtrees that contain each point,
1926 : // under the common ancestor.
1927 :
1928 0 : nsCOMPtr<nsINode> nodeToSelect(startContainer);
1929 :
1930 0 : while (nodeToSelect)
1931 : {
1932 0 : nsCOMPtr<nsINode> parent = nodeToSelect->GetParentNode();
1933 0 : if (parent == commonAncestor)
1934 0 : break; // We found the nodeToSelect!
1935 :
1936 0 : nodeToSelect = parent;
1937 : }
1938 :
1939 0 : if (!nodeToSelect)
1940 0 : return NS_ERROR_FAILURE; // This should never happen!
1941 :
1942 0 : aRange->SelectNode(*nodeToSelect, rv);
1943 0 : if (rv.Failed()) return rv.StealNSResult();
1944 :
1945 0 : return aRange->Collapse(false);
1946 : }
1947 :
1948 : /**
1949 : * Split a data node into two parts.
1950 : *
1951 : * @param aStartContainer The original node we are trying to split.
1952 : * @param aStartOffset The offset at which to split.
1953 : * @param aEndContainer The second node.
1954 : * @param aCloneAfterOriginal Set false if the original node should be the
1955 : * latter one after split.
1956 : */
1957 0 : static nsresult SplitDataNode(nsIDOMCharacterData* aStartContainer,
1958 : uint32_t aStartOffset,
1959 : nsIDOMCharacterData** aEndContainer,
1960 : bool aCloneAfterOriginal = true)
1961 : {
1962 : nsresult rv;
1963 0 : nsCOMPtr<nsINode> node = do_QueryInterface(aStartContainer);
1964 0 : NS_ENSURE_STATE(node && node->IsNodeOfType(nsINode::eDATA_NODE));
1965 0 : nsGenericDOMDataNode* dataNode = static_cast<nsGenericDOMDataNode*>(node.get());
1966 :
1967 0 : nsCOMPtr<nsIContent> newData;
1968 0 : rv = dataNode->SplitData(aStartOffset, getter_AddRefs(newData),
1969 0 : aCloneAfterOriginal);
1970 0 : NS_ENSURE_SUCCESS(rv, rv);
1971 0 : return CallQueryInterface(newData, aEndContainer);
1972 : }
1973 :
1974 : NS_IMETHODIMP
1975 0 : PrependChild(nsINode* aContainer, nsINode* aChild)
1976 : {
1977 0 : nsCOMPtr<nsINode> first = aContainer->GetFirstChild();
1978 0 : ErrorResult rv;
1979 0 : aContainer->InsertBefore(*aChild, first, rv);
1980 0 : return rv.StealNSResult();
1981 : }
1982 :
1983 : // Helper function for CutContents, making sure that the current node wasn't
1984 : // removed by mutation events (bug 766426)
1985 : static bool
1986 0 : ValidateCurrentNode(nsRange* aRange, RangeSubtreeIterator& aIter)
1987 : {
1988 : bool before, after;
1989 0 : nsCOMPtr<nsINode> node = aIter.GetCurrentNode();
1990 0 : if (!node) {
1991 : // We don't have to worry that the node was removed if it doesn't exist,
1992 : // e.g., the iterator is done.
1993 0 : return true;
1994 : }
1995 :
1996 0 : nsresult res = nsRange::CompareNodeToRange(node, aRange, &before, &after);
1997 0 : NS_ENSURE_SUCCESS(res, false);
1998 :
1999 0 : if (before || after) {
2000 0 : nsCOMPtr<nsIDOMCharacterData> charData = do_QueryInterface(node);
2001 0 : if (charData) {
2002 : // If we're dealing with the start/end container which is a character
2003 : // node, pretend that the node is in the range.
2004 0 : if (before && node == aRange->GetStartContainer()) {
2005 0 : before = false;
2006 : }
2007 0 : if (after && node == aRange->GetEndContainer()) {
2008 0 : after = false;
2009 : }
2010 : }
2011 : }
2012 :
2013 0 : return !before && !after;
2014 : }
2015 :
2016 : nsresult
2017 0 : nsRange::CutContents(DocumentFragment** aFragment)
2018 : {
2019 0 : if (aFragment) {
2020 0 : *aFragment = nullptr;
2021 : }
2022 :
2023 0 : nsCOMPtr<nsIDocument> doc = mStartContainer->OwnerDoc();
2024 :
2025 0 : ErrorResult res;
2026 0 : nsCOMPtr<nsINode> commonAncestor = GetCommonAncestorContainer(res);
2027 0 : NS_ENSURE_TRUE(!res.Failed(), res.StealNSResult());
2028 :
2029 : // If aFragment isn't null, create a temporary fragment to hold our return.
2030 0 : RefPtr<DocumentFragment> retval;
2031 0 : if (aFragment) {
2032 0 : retval = new DocumentFragment(doc->NodeInfoManager());
2033 : }
2034 0 : nsCOMPtr<nsINode> commonCloneAncestor = retval.get();
2035 :
2036 : // Batch possible DOMSubtreeModified events.
2037 0 : mozAutoSubtreeModified subtree(mRoot ? mRoot->OwnerDoc(): nullptr, nullptr);
2038 :
2039 : // Save the range end points locally to avoid interference
2040 : // of Range gravity during our edits!
2041 :
2042 0 : nsCOMPtr<nsINode> startContainer = mStartContainer;
2043 0 : int32_t startOffset = mStartOffset;
2044 0 : nsCOMPtr<nsINode> endContainer = mEndContainer;
2045 0 : int32_t endOffset = mEndOffset;
2046 :
2047 0 : if (retval) {
2048 : // For extractContents(), abort early if there's a doctype (bug 719533).
2049 : // This can happen only if the common ancestor is a document, in which case
2050 : // we just need to find its doctype child and check if that's in the range.
2051 0 : nsCOMPtr<nsIDocument> commonAncestorDocument = do_QueryInterface(commonAncestor);
2052 0 : if (commonAncestorDocument) {
2053 0 : RefPtr<DocumentType> doctype = commonAncestorDocument->GetDoctype();
2054 :
2055 0 : if (doctype &&
2056 0 : nsContentUtils::ComparePoints(startContainer, startOffset,
2057 0 : doctype, 0) < 0 &&
2058 0 : nsContentUtils::ComparePoints(doctype, 0,
2059 0 : endContainer, endOffset) < 0) {
2060 0 : return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
2061 : }
2062 : }
2063 : }
2064 :
2065 : // Create and initialize a subtree iterator that will give
2066 : // us all the subtrees within the range.
2067 :
2068 0 : RangeSubtreeIterator iter;
2069 :
2070 0 : nsresult rv = iter.Init(this);
2071 0 : if (NS_FAILED(rv)) return rv;
2072 :
2073 0 : if (iter.IsDone())
2074 : {
2075 : // There's nothing for us to delete.
2076 0 : rv = CollapseRangeAfterDelete(this);
2077 0 : if (NS_SUCCEEDED(rv) && aFragment) {
2078 0 : retval.forget(aFragment);
2079 : }
2080 0 : return rv;
2081 : }
2082 :
2083 : // We delete backwards to avoid iterator problems!
2084 :
2085 0 : iter.Last();
2086 :
2087 0 : bool handled = false;
2088 :
2089 : // With the exception of text nodes that contain one of the range
2090 : // end points, the subtree iterator should only give us back subtrees
2091 : // that are completely contained between the range's end points.
2092 :
2093 0 : while (!iter.IsDone())
2094 : {
2095 0 : nsCOMPtr<nsINode> nodeToResult;
2096 0 : nsCOMPtr<nsINode> node = iter.GetCurrentNode();
2097 :
2098 : // Before we delete anything, advance the iterator to the
2099 : // next subtree.
2100 :
2101 0 : iter.Prev();
2102 :
2103 0 : handled = false;
2104 :
2105 : // If it's CharacterData, make sure we might need to delete
2106 : // part of the data, instead of removing the whole node.
2107 : //
2108 : // XXX_kin: We need to also handle ProcessingInstruction
2109 : // XXX_kin: according to the spec.
2110 :
2111 0 : nsCOMPtr<nsIDOMCharacterData> charData(do_QueryInterface(node));
2112 :
2113 0 : if (charData)
2114 : {
2115 0 : uint32_t dataLength = 0;
2116 :
2117 0 : if (node == startContainer)
2118 : {
2119 0 : if (node == endContainer)
2120 : {
2121 : // This range is completely contained within a single text node.
2122 : // Delete or extract the data between startOffset and endOffset.
2123 :
2124 0 : if (endOffset > startOffset)
2125 : {
2126 0 : if (retval) {
2127 0 : nsAutoString cutValue;
2128 0 : rv = charData->SubstringData(startOffset, endOffset - startOffset,
2129 0 : cutValue);
2130 0 : NS_ENSURE_SUCCESS(rv, rv);
2131 0 : nsCOMPtr<nsIDOMNode> clone;
2132 0 : rv = charData->CloneNode(false, 1, getter_AddRefs(clone));
2133 0 : NS_ENSURE_SUCCESS(rv, rv);
2134 0 : clone->SetNodeValue(cutValue);
2135 0 : nodeToResult = do_QueryInterface(clone);
2136 : }
2137 :
2138 0 : nsMutationGuard guard;
2139 0 : rv = charData->DeleteData(startOffset, endOffset - startOffset);
2140 0 : NS_ENSURE_SUCCESS(rv, rv);
2141 0 : NS_ENSURE_STATE(!guard.Mutated(0) ||
2142 : ValidateCurrentNode(this, iter));
2143 : }
2144 :
2145 0 : handled = true;
2146 : }
2147 : else
2148 : {
2149 : // Delete or extract everything after startOffset.
2150 :
2151 0 : rv = charData->GetLength(&dataLength);
2152 0 : NS_ENSURE_SUCCESS(rv, rv);
2153 :
2154 0 : if (dataLength >= (uint32_t)startOffset)
2155 : {
2156 0 : nsMutationGuard guard;
2157 0 : nsCOMPtr<nsIDOMCharacterData> cutNode;
2158 0 : rv = SplitDataNode(charData, startOffset, getter_AddRefs(cutNode));
2159 0 : NS_ENSURE_SUCCESS(rv, rv);
2160 0 : NS_ENSURE_STATE(!guard.Mutated(1) ||
2161 : ValidateCurrentNode(this, iter));
2162 0 : nodeToResult = do_QueryInterface(cutNode);
2163 : }
2164 :
2165 0 : handled = true;
2166 : }
2167 : }
2168 0 : else if (node == endContainer)
2169 : {
2170 : // Delete or extract everything before endOffset.
2171 :
2172 0 : if (endOffset >= 0)
2173 : {
2174 0 : nsMutationGuard guard;
2175 0 : nsCOMPtr<nsIDOMCharacterData> cutNode;
2176 : /* The Range spec clearly states clones get cut and original nodes
2177 : remain behind, so use false as the last parameter.
2178 : */
2179 0 : rv = SplitDataNode(charData, endOffset, getter_AddRefs(cutNode),
2180 0 : false);
2181 0 : NS_ENSURE_SUCCESS(rv, rv);
2182 0 : NS_ENSURE_STATE(!guard.Mutated(1) ||
2183 : ValidateCurrentNode(this, iter));
2184 0 : nodeToResult = do_QueryInterface(cutNode);
2185 : }
2186 :
2187 0 : handled = true;
2188 : }
2189 : }
2190 :
2191 0 : if (!handled && (node == endContainer || node == startContainer))
2192 : {
2193 0 : if (node && node->IsElement() &&
2194 0 : ((node == endContainer && endOffset == 0) ||
2195 0 : (node == startContainer &&
2196 0 : int32_t(node->AsElement()->GetChildCount()) == startOffset)))
2197 : {
2198 0 : if (retval) {
2199 0 : ErrorResult rv;
2200 0 : nodeToResult = node->CloneNode(false, rv);
2201 0 : NS_ENSURE_TRUE(!rv.Failed(), rv.StealNSResult());
2202 : }
2203 0 : handled = true;
2204 : }
2205 : }
2206 :
2207 0 : if (!handled)
2208 : {
2209 : // node was not handled above, so it must be completely contained
2210 : // within the range. Just remove it from the tree!
2211 0 : nodeToResult = node;
2212 : }
2213 :
2214 0 : uint32_t parentCount = 0;
2215 : // Set the result to document fragment if we have 'retval'.
2216 0 : if (retval) {
2217 0 : nsCOMPtr<nsINode> oldCommonAncestor = commonAncestor;
2218 0 : if (!iter.IsDone()) {
2219 : // Setup the parameters for the next iteration of the loop.
2220 0 : nsCOMPtr<nsINode> prevNode = iter.GetCurrentNode();
2221 0 : NS_ENSURE_STATE(prevNode);
2222 :
2223 : // Get node's and prevNode's common parent. Do this before moving
2224 : // nodes from original DOM to result fragment.
2225 0 : commonAncestor = nsContentUtils::GetCommonAncestor(node, prevNode);
2226 0 : NS_ENSURE_STATE(commonAncestor);
2227 :
2228 0 : nsCOMPtr<nsINode> parentCounterNode = node;
2229 0 : while (parentCounterNode && parentCounterNode != commonAncestor)
2230 : {
2231 0 : ++parentCount;
2232 0 : parentCounterNode = parentCounterNode->GetParentNode();
2233 0 : NS_ENSURE_STATE(parentCounterNode);
2234 : }
2235 : }
2236 :
2237 : // Clone the parent hierarchy between commonAncestor and node.
2238 0 : nsCOMPtr<nsINode> closestAncestor, farthestAncestor;
2239 0 : rv = CloneParentsBetween(oldCommonAncestor, node,
2240 0 : getter_AddRefs(closestAncestor),
2241 0 : getter_AddRefs(farthestAncestor));
2242 0 : NS_ENSURE_SUCCESS(rv, rv);
2243 :
2244 0 : if (farthestAncestor)
2245 : {
2246 0 : nsCOMPtr<nsINode> n = do_QueryInterface(commonCloneAncestor);
2247 0 : rv = PrependChild(n, farthestAncestor);
2248 0 : NS_ENSURE_SUCCESS(rv, rv);
2249 : }
2250 :
2251 0 : nsMutationGuard guard;
2252 0 : nsCOMPtr<nsINode> parent = nodeToResult->GetParentNode();
2253 0 : rv = closestAncestor ? PrependChild(closestAncestor, nodeToResult)
2254 0 : : PrependChild(commonCloneAncestor, nodeToResult);
2255 0 : NS_ENSURE_SUCCESS(rv, rv);
2256 0 : NS_ENSURE_STATE(!guard.Mutated(parent ? 2 : 1) ||
2257 : ValidateCurrentNode(this, iter));
2258 0 : } else if (nodeToResult) {
2259 0 : nsMutationGuard guard;
2260 0 : nsCOMPtr<nsINode> node = nodeToResult;
2261 0 : nsCOMPtr<nsINode> parent = node->GetParentNode();
2262 0 : if (parent) {
2263 0 : mozilla::ErrorResult error;
2264 0 : parent->RemoveChild(*node, error);
2265 0 : NS_ENSURE_FALSE(error.Failed(), error.StealNSResult());
2266 : }
2267 0 : NS_ENSURE_STATE(!guard.Mutated(1) ||
2268 : ValidateCurrentNode(this, iter));
2269 : }
2270 :
2271 0 : if (!iter.IsDone() && retval) {
2272 : // Find the equivalent of commonAncestor in the cloned tree.
2273 0 : nsCOMPtr<nsINode> newCloneAncestor = nodeToResult;
2274 0 : for (uint32_t i = parentCount; i; --i)
2275 : {
2276 0 : newCloneAncestor = newCloneAncestor->GetParentNode();
2277 0 : NS_ENSURE_STATE(newCloneAncestor);
2278 : }
2279 0 : commonCloneAncestor = newCloneAncestor;
2280 : }
2281 : }
2282 :
2283 0 : rv = CollapseRangeAfterDelete(this);
2284 0 : if (NS_SUCCEEDED(rv) && aFragment) {
2285 0 : retval.forget(aFragment);
2286 : }
2287 0 : return rv;
2288 : }
2289 :
2290 : NS_IMETHODIMP
2291 0 : nsRange::DeleteContents()
2292 : {
2293 0 : return CutContents(nullptr);
2294 : }
2295 :
2296 : void
2297 0 : nsRange::DeleteContents(ErrorResult& aRv)
2298 : {
2299 0 : aRv = CutContents(nullptr);
2300 0 : }
2301 :
2302 : NS_IMETHODIMP
2303 0 : nsRange::ExtractContents(nsIDOMDocumentFragment** aReturn)
2304 : {
2305 0 : NS_ENSURE_ARG_POINTER(aReturn);
2306 0 : RefPtr<DocumentFragment> fragment;
2307 0 : nsresult rv = CutContents(getter_AddRefs(fragment));
2308 0 : fragment.forget(aReturn);
2309 0 : return rv;
2310 : }
2311 :
2312 : already_AddRefed<DocumentFragment>
2313 0 : nsRange::ExtractContents(ErrorResult& rv)
2314 : {
2315 0 : RefPtr<DocumentFragment> fragment;
2316 0 : rv = CutContents(getter_AddRefs(fragment));
2317 0 : return fragment.forget();
2318 : }
2319 :
2320 : NS_IMETHODIMP
2321 0 : nsRange::CompareBoundaryPoints(uint16_t aHow, nsIDOMRange* aOtherRange,
2322 : int16_t* aCmpRet)
2323 : {
2324 0 : nsRange* otherRange = static_cast<nsRange*>(aOtherRange);
2325 0 : NS_ENSURE_TRUE(otherRange, NS_ERROR_NULL_POINTER);
2326 :
2327 0 : ErrorResult rv;
2328 0 : *aCmpRet = CompareBoundaryPoints(aHow, *otherRange, rv);
2329 0 : return rv.StealNSResult();
2330 : }
2331 :
2332 : int16_t
2333 0 : nsRange::CompareBoundaryPoints(uint16_t aHow, nsRange& aOtherRange,
2334 : ErrorResult& rv)
2335 : {
2336 0 : if (!mIsPositioned || !aOtherRange.IsPositioned()) {
2337 0 : rv.Throw(NS_ERROR_NOT_INITIALIZED);
2338 0 : return 0;
2339 : }
2340 :
2341 : nsINode *ourNode, *otherNode;
2342 : int32_t ourOffset, otherOffset;
2343 :
2344 0 : switch (aHow) {
2345 : case nsIDOMRange::START_TO_START:
2346 0 : ourNode = mStartContainer;
2347 0 : ourOffset = mStartOffset;
2348 0 : otherNode = aOtherRange.GetStartContainer();
2349 0 : otherOffset = aOtherRange.StartOffset();
2350 0 : break;
2351 : case nsIDOMRange::START_TO_END:
2352 0 : ourNode = mEndContainer;
2353 0 : ourOffset = mEndOffset;
2354 0 : otherNode = aOtherRange.GetStartContainer();
2355 0 : otherOffset = aOtherRange.StartOffset();
2356 0 : break;
2357 : case nsIDOMRange::END_TO_START:
2358 0 : ourNode = mStartContainer;
2359 0 : ourOffset = mStartOffset;
2360 0 : otherNode = aOtherRange.GetEndContainer();
2361 0 : otherOffset = aOtherRange.EndOffset();
2362 0 : break;
2363 : case nsIDOMRange::END_TO_END:
2364 0 : ourNode = mEndContainer;
2365 0 : ourOffset = mEndOffset;
2366 0 : otherNode = aOtherRange.GetEndContainer();
2367 0 : otherOffset = aOtherRange.EndOffset();
2368 0 : break;
2369 : default:
2370 : // We were passed an illegal value
2371 0 : rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
2372 0 : return 0;
2373 : }
2374 :
2375 0 : if (mRoot != aOtherRange.GetRoot()) {
2376 0 : rv.Throw(NS_ERROR_DOM_WRONG_DOCUMENT_ERR);
2377 0 : return 0;
2378 : }
2379 :
2380 0 : return nsContentUtils::ComparePoints(ourNode, ourOffset,
2381 0 : otherNode, otherOffset);
2382 : }
2383 :
2384 : /* static */ nsresult
2385 0 : nsRange::CloneParentsBetween(nsINode *aAncestor,
2386 : nsINode *aNode,
2387 : nsINode **aClosestAncestor,
2388 : nsINode **aFarthestAncestor)
2389 : {
2390 0 : NS_ENSURE_ARG_POINTER((aAncestor && aNode && aClosestAncestor && aFarthestAncestor));
2391 :
2392 0 : *aClosestAncestor = nullptr;
2393 0 : *aFarthestAncestor = nullptr;
2394 :
2395 0 : if (aAncestor == aNode)
2396 0 : return NS_OK;
2397 :
2398 0 : nsCOMPtr<nsINode> firstParent, lastParent;
2399 0 : nsCOMPtr<nsINode> parent = aNode->GetParentNode();
2400 :
2401 0 : while(parent && parent != aAncestor)
2402 : {
2403 0 : ErrorResult rv;
2404 0 : nsCOMPtr<nsINode> clone = parent->CloneNode(false, rv);
2405 :
2406 0 : if (rv.Failed()) {
2407 0 : return rv.StealNSResult();
2408 : }
2409 0 : if (!clone) {
2410 0 : return NS_ERROR_FAILURE;
2411 : }
2412 :
2413 0 : if (! firstParent) {
2414 0 : firstParent = lastParent = clone;
2415 : } else {
2416 0 : clone->AppendChild(*lastParent, rv);
2417 0 : if (rv.Failed()) return rv.StealNSResult();
2418 :
2419 0 : lastParent = clone;
2420 : }
2421 :
2422 0 : parent = parent->GetParentNode();
2423 : }
2424 :
2425 0 : *aClosestAncestor = firstParent;
2426 0 : NS_IF_ADDREF(*aClosestAncestor);
2427 :
2428 0 : *aFarthestAncestor = lastParent;
2429 0 : NS_IF_ADDREF(*aFarthestAncestor);
2430 :
2431 0 : return NS_OK;
2432 : }
2433 :
2434 : NS_IMETHODIMP
2435 0 : nsRange::CloneContents(nsIDOMDocumentFragment** aReturn)
2436 : {
2437 0 : ErrorResult rv;
2438 0 : *aReturn = CloneContents(rv).take();
2439 0 : return rv.StealNSResult();
2440 : }
2441 :
2442 : already_AddRefed<DocumentFragment>
2443 0 : nsRange::CloneContents(ErrorResult& aRv)
2444 : {
2445 0 : nsCOMPtr<nsINode> commonAncestor = GetCommonAncestorContainer(aRv);
2446 0 : MOZ_ASSERT(!aRv.Failed(), "GetCommonAncestorContainer() shouldn't fail!");
2447 :
2448 0 : nsCOMPtr<nsIDocument> doc = mStartContainer->OwnerDoc();
2449 0 : NS_ASSERTION(doc, "CloneContents needs a document to continue.");
2450 0 : if (!doc) {
2451 0 : aRv.Throw(NS_ERROR_FAILURE);
2452 0 : return nullptr;
2453 : }
2454 :
2455 : // Create a new document fragment in the context of this document,
2456 : // which might be null
2457 :
2458 :
2459 : RefPtr<DocumentFragment> clonedFrag =
2460 0 : new DocumentFragment(doc->NodeInfoManager());
2461 :
2462 0 : nsCOMPtr<nsINode> commonCloneAncestor = clonedFrag.get();
2463 :
2464 : // Create and initialize a subtree iterator that will give
2465 : // us all the subtrees within the range.
2466 :
2467 0 : RangeSubtreeIterator iter;
2468 :
2469 0 : aRv = iter.Init(this);
2470 0 : if (aRv.Failed()) {
2471 0 : return nullptr;
2472 : }
2473 :
2474 0 : if (iter.IsDone())
2475 : {
2476 : // There's nothing to add to the doc frag, we must be done!
2477 0 : return clonedFrag.forget();
2478 : }
2479 :
2480 0 : iter.First();
2481 :
2482 : // With the exception of text nodes that contain one of the range
2483 : // end points and elements which don't have any content selected the subtree
2484 : // iterator should only give us back subtrees that are completely contained
2485 : // between the range's end points.
2486 : //
2487 : // Unfortunately these subtrees don't contain the parent hierarchy/context
2488 : // that the Range spec requires us to return. This loop clones the
2489 : // parent hierarchy, adds a cloned version of the subtree, to it, then
2490 : // correctly places this new subtree into the doc fragment.
2491 :
2492 0 : while (!iter.IsDone())
2493 : {
2494 0 : nsCOMPtr<nsINode> node = iter.GetCurrentNode();
2495 0 : bool deepClone = !node->IsElement() ||
2496 0 : (!(node == mEndContainer && mEndOffset == 0) &&
2497 0 : !(node == mStartContainer &&
2498 0 : mStartOffset ==
2499 0 : int32_t(node->AsElement()->GetChildCount())));
2500 :
2501 : // Clone the current subtree!
2502 :
2503 0 : nsCOMPtr<nsINode> clone = node->CloneNode(deepClone, aRv);
2504 0 : if (aRv.Failed()) {
2505 0 : return nullptr;
2506 : }
2507 :
2508 : // If it's CharacterData, make sure we only clone what
2509 : // is in the range.
2510 : //
2511 : // XXX_kin: We need to also handle ProcessingInstruction
2512 : // XXX_kin: according to the spec.
2513 :
2514 0 : nsCOMPtr<nsIDOMCharacterData> charData(do_QueryInterface(clone));
2515 :
2516 0 : if (charData)
2517 : {
2518 0 : if (node == mEndContainer) {
2519 : // We only need the data before mEndOffset, so get rid of any
2520 : // data after it.
2521 :
2522 0 : uint32_t dataLength = 0;
2523 0 : aRv = charData->GetLength(&dataLength);
2524 0 : if (aRv.Failed()) {
2525 0 : return nullptr;
2526 : }
2527 :
2528 0 : if (dataLength > (uint32_t)mEndOffset)
2529 : {
2530 0 : aRv = charData->DeleteData(mEndOffset, dataLength - mEndOffset);
2531 0 : if (aRv.Failed()) {
2532 0 : return nullptr;
2533 : }
2534 : }
2535 : }
2536 :
2537 0 : if (node == mStartContainer) {
2538 : // We don't need any data before mStartOffset, so just
2539 : // delete it!
2540 :
2541 0 : if (mStartOffset > 0)
2542 : {
2543 0 : aRv = charData->DeleteData(0, mStartOffset);
2544 0 : if (aRv.Failed()) {
2545 0 : return nullptr;
2546 : }
2547 : }
2548 : }
2549 : }
2550 :
2551 : // Clone the parent hierarchy between commonAncestor and node.
2552 :
2553 0 : nsCOMPtr<nsINode> closestAncestor, farthestAncestor;
2554 :
2555 0 : aRv = CloneParentsBetween(commonAncestor, node,
2556 0 : getter_AddRefs(closestAncestor),
2557 0 : getter_AddRefs(farthestAncestor));
2558 :
2559 0 : if (aRv.Failed()) {
2560 0 : return nullptr;
2561 : }
2562 :
2563 : // Hook the parent hierarchy/context of the subtree into the clone tree.
2564 :
2565 0 : if (farthestAncestor)
2566 : {
2567 0 : commonCloneAncestor->AppendChild(*farthestAncestor, aRv);
2568 :
2569 0 : if (aRv.Failed()) {
2570 0 : return nullptr;
2571 : }
2572 : }
2573 :
2574 : // Place the cloned subtree into the cloned doc frag tree!
2575 :
2576 0 : nsCOMPtr<nsINode> cloneNode = do_QueryInterface(clone);
2577 0 : if (closestAncestor)
2578 : {
2579 : // Append the subtree under closestAncestor since it is the
2580 : // immediate parent of the subtree.
2581 :
2582 0 : closestAncestor->AppendChild(*cloneNode, aRv);
2583 : }
2584 : else
2585 : {
2586 : // If we get here, there is no missing parent hierarchy between
2587 : // commonAncestor and node, so just append clone to commonCloneAncestor.
2588 :
2589 0 : commonCloneAncestor->AppendChild(*cloneNode, aRv);
2590 : }
2591 0 : if (aRv.Failed()) {
2592 0 : return nullptr;
2593 : }
2594 :
2595 : // Get the next subtree to be processed. The idea here is to setup
2596 : // the parameters for the next iteration of the loop.
2597 :
2598 0 : iter.Next();
2599 :
2600 0 : if (iter.IsDone())
2601 0 : break; // We must be done!
2602 :
2603 0 : nsCOMPtr<nsINode> nextNode = iter.GetCurrentNode();
2604 0 : if (!nextNode) {
2605 0 : aRv.Throw(NS_ERROR_FAILURE);
2606 0 : return nullptr;
2607 : }
2608 :
2609 : // Get node and nextNode's common parent.
2610 0 : commonAncestor = nsContentUtils::GetCommonAncestor(node, nextNode);
2611 :
2612 0 : if (!commonAncestor) {
2613 0 : aRv.Throw(NS_ERROR_FAILURE);
2614 0 : return nullptr;
2615 : }
2616 :
2617 : // Find the equivalent of commonAncestor in the cloned tree!
2618 :
2619 0 : while (node && node != commonAncestor)
2620 : {
2621 0 : node = node->GetParentNode();
2622 0 : if (aRv.Failed()) {
2623 0 : return nullptr;
2624 : }
2625 :
2626 0 : if (!node) {
2627 0 : aRv.Throw(NS_ERROR_FAILURE);
2628 0 : return nullptr;
2629 : }
2630 :
2631 0 : cloneNode = cloneNode->GetParentNode();
2632 0 : if (!cloneNode) {
2633 0 : aRv.Throw(NS_ERROR_FAILURE);
2634 0 : return nullptr;
2635 : }
2636 : }
2637 :
2638 0 : commonCloneAncestor = cloneNode;
2639 : }
2640 :
2641 0 : return clonedFrag.forget();
2642 : }
2643 :
2644 : already_AddRefed<nsRange>
2645 0 : nsRange::CloneRange() const
2646 : {
2647 0 : RefPtr<nsRange> range = new nsRange(mOwner);
2648 :
2649 0 : range->SetMaySpanAnonymousSubtrees(mMaySpanAnonymousSubtrees);
2650 :
2651 0 : range->DoSetRange(mStartContainer, mStartOffset,
2652 0 : mEndContainer, mEndOffset, mRoot);
2653 :
2654 0 : return range.forget();
2655 : }
2656 :
2657 : NS_IMETHODIMP
2658 0 : nsRange::CloneRange(nsIDOMRange** aReturn)
2659 : {
2660 0 : *aReturn = CloneRange().take();
2661 0 : return NS_OK;
2662 : }
2663 :
2664 : NS_IMETHODIMP
2665 0 : nsRange::InsertNode(nsIDOMNode* aNode)
2666 : {
2667 0 : nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
2668 0 : if (!node) {
2669 0 : return NS_ERROR_DOM_NOT_OBJECT_ERR;
2670 : }
2671 :
2672 0 : ErrorResult rv;
2673 0 : InsertNode(*node, rv);
2674 0 : return rv.StealNSResult();
2675 : }
2676 :
2677 : void
2678 0 : nsRange::InsertNode(nsINode& aNode, ErrorResult& aRv)
2679 : {
2680 0 : if (!nsContentUtils::LegacyIsCallerNativeCode() &&
2681 0 : !nsContentUtils::CanCallerAccess(&aNode)) {
2682 0 : aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
2683 0 : return;
2684 : }
2685 :
2686 0 : int32_t tStartOffset = StartOffset();
2687 :
2688 0 : nsCOMPtr<nsINode> tStartContainer = GetStartContainer(aRv);
2689 0 : if (aRv.Failed()) {
2690 0 : return;
2691 : }
2692 :
2693 0 : if (&aNode == tStartContainer) {
2694 0 : aRv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
2695 0 : return;
2696 : }
2697 :
2698 : // This is the node we'll be inserting before, and its parent
2699 0 : nsCOMPtr<nsINode> referenceNode;
2700 0 : nsCOMPtr<nsINode> referenceParentNode = tStartContainer;
2701 :
2702 0 : nsCOMPtr<nsIDOMText> startTextNode(do_QueryInterface(tStartContainer));
2703 0 : nsCOMPtr<nsIDOMNodeList> tChildList;
2704 0 : if (startTextNode) {
2705 0 : referenceParentNode = tStartContainer->GetParentNode();
2706 0 : if (!referenceParentNode) {
2707 0 : aRv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
2708 0 : return;
2709 : }
2710 :
2711 0 : referenceParentNode->EnsurePreInsertionValidity(aNode, tStartContainer,
2712 0 : aRv);
2713 0 : if (aRv.Failed()) {
2714 0 : return;
2715 : }
2716 :
2717 0 : nsCOMPtr<nsIDOMText> secondPart;
2718 0 : aRv = startTextNode->SplitText(tStartOffset, getter_AddRefs(secondPart));
2719 0 : if (aRv.Failed()) {
2720 0 : return;
2721 : }
2722 :
2723 0 : referenceNode = do_QueryInterface(secondPart);
2724 : } else {
2725 0 : aRv = tStartContainer->AsDOMNode()->GetChildNodes(getter_AddRefs(tChildList));
2726 0 : if (aRv.Failed()) {
2727 0 : return;
2728 : }
2729 :
2730 : // find the insertion point in the DOM and insert the Node
2731 0 : nsCOMPtr<nsIDOMNode> q;
2732 0 : aRv = tChildList->Item(tStartOffset, getter_AddRefs(q));
2733 0 : referenceNode = do_QueryInterface(q);
2734 0 : if (aRv.Failed()) {
2735 0 : return;
2736 : }
2737 :
2738 0 : tStartContainer->EnsurePreInsertionValidity(aNode, referenceNode, aRv);
2739 0 : if (aRv.Failed()) {
2740 0 : return;
2741 : }
2742 : }
2743 :
2744 : // We might need to update the end to include the new node (bug 433662).
2745 : // Ideally we'd only do this if needed, but it's tricky to know when it's
2746 : // needed in advance (bug 765799).
2747 : int32_t newOffset;
2748 :
2749 0 : if (referenceNode) {
2750 0 : newOffset = IndexOf(referenceNode);
2751 : } else {
2752 : uint32_t length;
2753 0 : aRv = tChildList->GetLength(&length);
2754 0 : if (aRv.Failed()) {
2755 0 : return;
2756 : }
2757 :
2758 0 : newOffset = length;
2759 : }
2760 :
2761 0 : if (aNode.NodeType() == nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
2762 0 : newOffset += aNode.GetChildCount();
2763 : } else {
2764 0 : newOffset++;
2765 : }
2766 :
2767 : // Now actually insert the node
2768 0 : nsCOMPtr<nsINode> tResultNode;
2769 0 : tResultNode = referenceParentNode->InsertBefore(aNode, referenceNode, aRv);
2770 0 : if (aRv.Failed()) {
2771 0 : return;
2772 : }
2773 :
2774 0 : if (Collapsed()) {
2775 0 : aRv = SetEnd(referenceParentNode, newOffset);
2776 : }
2777 : }
2778 :
2779 : NS_IMETHODIMP
2780 0 : nsRange::SurroundContents(nsIDOMNode* aNewParent)
2781 : {
2782 0 : nsCOMPtr<nsINode> node = do_QueryInterface(aNewParent);
2783 0 : if (!node) {
2784 0 : return NS_ERROR_DOM_NOT_OBJECT_ERR;
2785 : }
2786 0 : ErrorResult rv;
2787 0 : SurroundContents(*node, rv);
2788 0 : return rv.StealNSResult();
2789 : }
2790 :
2791 : void
2792 0 : nsRange::SurroundContents(nsINode& aNewParent, ErrorResult& aRv)
2793 : {
2794 0 : if (!nsContentUtils::LegacyIsCallerNativeCode() &&
2795 0 : !nsContentUtils::CanCallerAccess(&aNewParent)) {
2796 0 : aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
2797 0 : return;
2798 : }
2799 :
2800 0 : if (!mRoot) {
2801 0 : aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
2802 0 : return;
2803 : }
2804 : // INVALID_STATE_ERROR: Raised if the Range partially selects a non-text
2805 : // node.
2806 0 : if (mStartContainer != mEndContainer) {
2807 0 : bool startIsText = mStartContainer->IsNodeOfType(nsINode::eTEXT);
2808 0 : bool endIsText = mEndContainer->IsNodeOfType(nsINode::eTEXT);
2809 0 : nsINode* startGrandParent = mStartContainer->GetParentNode();
2810 0 : nsINode* endGrandParent = mEndContainer->GetParentNode();
2811 0 : if (!((startIsText && endIsText &&
2812 0 : startGrandParent &&
2813 0 : startGrandParent == endGrandParent) ||
2814 0 : (startIsText &&
2815 0 : startGrandParent &&
2816 0 : startGrandParent == mEndContainer) ||
2817 0 : (endIsText &&
2818 0 : endGrandParent &&
2819 0 : endGrandParent == mStartContainer))) {
2820 0 : aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
2821 0 : return;
2822 : }
2823 : }
2824 :
2825 : // INVALID_NODE_TYPE_ERROR if aNewParent is something that can't be inserted
2826 : // (Document, DocumentType, DocumentFragment)
2827 0 : uint16_t nodeType = aNewParent.NodeType();
2828 0 : if (nodeType == nsIDOMNode::DOCUMENT_NODE ||
2829 0 : nodeType == nsIDOMNode::DOCUMENT_TYPE_NODE ||
2830 : nodeType == nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
2831 0 : aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
2832 0 : return;
2833 : }
2834 :
2835 : // Extract the contents within the range.
2836 :
2837 0 : RefPtr<DocumentFragment> docFrag = ExtractContents(aRv);
2838 :
2839 0 : if (aRv.Failed()) {
2840 0 : return;
2841 : }
2842 :
2843 0 : if (!docFrag) {
2844 0 : aRv.Throw(NS_ERROR_FAILURE);
2845 0 : return;
2846 : }
2847 :
2848 : // Spec says we need to remove all of aNewParent's
2849 : // children prior to insertion.
2850 :
2851 0 : nsCOMPtr<nsINodeList> children = aNewParent.ChildNodes();
2852 0 : if (!children) {
2853 0 : aRv.Throw(NS_ERROR_FAILURE);
2854 0 : return;
2855 : }
2856 :
2857 0 : uint32_t numChildren = children->Length();
2858 :
2859 0 : while (numChildren)
2860 : {
2861 0 : nsCOMPtr<nsINode> child = children->Item(--numChildren);
2862 0 : if (!child) {
2863 0 : aRv.Throw(NS_ERROR_FAILURE);
2864 0 : return;
2865 : }
2866 :
2867 0 : aNewParent.RemoveChild(*child, aRv);
2868 0 : if (aRv.Failed()) {
2869 0 : return;
2870 : }
2871 : }
2872 :
2873 : // Insert aNewParent at the range's start point.
2874 :
2875 0 : InsertNode(aNewParent, aRv);
2876 0 : if (aRv.Failed()) {
2877 0 : return;
2878 : }
2879 :
2880 : // Append the content we extracted under aNewParent.
2881 0 : aNewParent.AppendChild(*docFrag, aRv);
2882 0 : if (aRv.Failed()) {
2883 0 : return;
2884 : }
2885 :
2886 : // Select aNewParent, and its contents.
2887 :
2888 0 : SelectNode(aNewParent, aRv);
2889 : }
2890 :
2891 : NS_IMETHODIMP
2892 0 : nsRange::ToString(nsAString& aReturn)
2893 : {
2894 : // clear the string
2895 0 : aReturn.Truncate();
2896 :
2897 : // If we're unpositioned, return the empty string
2898 0 : if (!mIsPositioned) {
2899 0 : return NS_OK;
2900 : }
2901 :
2902 : #ifdef DEBUG_range
2903 : printf("Range dump: -----------------------\n");
2904 : #endif /* DEBUG */
2905 :
2906 : // effeciency hack for simple case
2907 0 : if (mStartContainer == mEndContainer) {
2908 0 : nsCOMPtr<nsIDOMText> textNode = do_QueryInterface(mStartContainer);
2909 :
2910 0 : if (textNode)
2911 : {
2912 : #ifdef DEBUG_range
2913 : // If debug, dump it:
2914 : nsCOMPtr<nsIContent> cN = do_QueryInterface(mStartContainer);
2915 : if (cN) cN->List(stdout);
2916 : printf("End Range dump: -----------------------\n");
2917 : #endif /* DEBUG */
2918 :
2919 : // grab the text
2920 0 : if (NS_FAILED(textNode->SubstringData(mStartOffset,mEndOffset-mStartOffset,aReturn)))
2921 0 : return NS_ERROR_UNEXPECTED;
2922 0 : return NS_OK;
2923 : }
2924 : }
2925 :
2926 : /* complex case: mStartContainer != mEndContainer, or mStartParent not a text
2927 : node revisit - there are potential optimizations here and also tradeoffs.
2928 : */
2929 :
2930 0 : nsCOMPtr<nsIContentIterator> iter = NS_NewContentIterator();
2931 0 : nsresult rv = iter->Init(this);
2932 0 : NS_ENSURE_SUCCESS(rv, rv);
2933 :
2934 0 : nsString tempString;
2935 :
2936 : // loop through the content iterator, which returns nodes in the range in
2937 : // close tag order, and grab the text from any text node
2938 0 : while (!iter->IsDone())
2939 : {
2940 0 : nsINode *n = iter->GetCurrentNode();
2941 :
2942 : #ifdef DEBUG_range
2943 : // If debug, dump it:
2944 : n->List(stdout);
2945 : #endif /* DEBUG */
2946 0 : nsCOMPtr<nsIDOMText> textNode(do_QueryInterface(n));
2947 0 : if (textNode) // if it's a text node, get the text
2948 : {
2949 0 : if (n == mStartContainer) { // only include text past start offset
2950 : uint32_t strLength;
2951 0 : textNode->GetLength(&strLength);
2952 0 : textNode->SubstringData(mStartOffset,strLength-mStartOffset,tempString);
2953 0 : aReturn += tempString;
2954 0 : } else if (n == mEndContainer) { // only include text before end offset
2955 0 : textNode->SubstringData(0,mEndOffset,tempString);
2956 0 : aReturn += tempString;
2957 : } else { // grab the whole kit-n-kaboodle
2958 0 : textNode->GetData(tempString);
2959 0 : aReturn += tempString;
2960 : }
2961 : }
2962 :
2963 0 : iter->Next();
2964 : }
2965 :
2966 : #ifdef DEBUG_range
2967 : printf("End Range dump: -----------------------\n");
2968 : #endif /* DEBUG */
2969 0 : return NS_OK;
2970 : }
2971 :
2972 :
2973 :
2974 : NS_IMETHODIMP
2975 0 : nsRange::Detach()
2976 : {
2977 0 : return NS_OK;
2978 : }
2979 :
2980 : NS_IMETHODIMP
2981 0 : nsRange::CreateContextualFragment(const nsAString& aFragment,
2982 : nsIDOMDocumentFragment** aReturn)
2983 : {
2984 0 : if (mIsPositioned) {
2985 0 : return nsContentUtils::CreateContextualFragment(mStartContainer, aFragment,
2986 0 : false, aReturn);
2987 : }
2988 0 : return NS_ERROR_FAILURE;
2989 : }
2990 :
2991 : already_AddRefed<DocumentFragment>
2992 0 : nsRange::CreateContextualFragment(const nsAString& aFragment, ErrorResult& aRv)
2993 : {
2994 0 : if (!mIsPositioned) {
2995 0 : aRv.Throw(NS_ERROR_FAILURE);
2996 0 : return nullptr;
2997 : }
2998 :
2999 : return nsContentUtils::CreateContextualFragment(mStartContainer, aFragment,
3000 0 : false, aRv);
3001 : }
3002 :
3003 0 : static void ExtractRectFromOffset(nsIFrame* aFrame,
3004 : const int32_t aOffset, nsRect* aR, bool aKeepLeft,
3005 : bool aClampToEdge)
3006 : {
3007 0 : nsPoint point;
3008 0 : aFrame->GetPointFromOffset(aOffset, &point);
3009 :
3010 0 : if (!aClampToEdge && !aR->Contains(point)) {
3011 0 : aR->width = 0;
3012 0 : aR->x = point.x;
3013 0 : return;
3014 : }
3015 :
3016 0 : if (aClampToEdge) {
3017 0 : point = aR->ClampPoint(point);
3018 : }
3019 :
3020 0 : if (aKeepLeft) {
3021 0 : aR->width = point.x - aR->x;
3022 : } else {
3023 0 : aR->width = aR->XMost() - point.x;
3024 0 : aR->x = point.x;
3025 : }
3026 : }
3027 :
3028 : static nsTextFrame*
3029 0 : GetTextFrameForContent(nsIContent* aContent, bool aFlushLayout)
3030 : {
3031 0 : nsIPresShell* presShell = aContent->OwnerDoc()->GetShell();
3032 0 : if (presShell) {
3033 0 : presShell->FrameConstructor()->EnsureFrameForTextNode(
3034 0 : static_cast<nsGenericDOMDataNode*>(aContent));
3035 :
3036 0 : if (aFlushLayout) {
3037 0 : aContent->OwnerDoc()->FlushPendingNotifications(FlushType::Layout);
3038 : }
3039 :
3040 0 : nsIFrame* frame = aContent->GetPrimaryFrame();
3041 0 : if (frame && frame->IsTextFrame()) {
3042 0 : return static_cast<nsTextFrame*>(frame);
3043 : }
3044 : }
3045 0 : return nullptr;
3046 : }
3047 :
3048 0 : static nsresult GetPartialTextRect(nsLayoutUtils::RectCallback* aCallback,
3049 : Sequence<nsString>* aTextList,
3050 : nsIContent* aContent, int32_t aStartOffset,
3051 : int32_t aEndOffset, bool aClampToEdge,
3052 : bool aFlushLayout)
3053 : {
3054 0 : nsTextFrame* textFrame = GetTextFrameForContent(aContent, aFlushLayout);
3055 0 : if (textFrame) {
3056 0 : nsIFrame* relativeTo = nsLayoutUtils::GetContainingBlockForClientRect(textFrame);
3057 0 : for (nsTextFrame* f = textFrame; f; f = static_cast<nsTextFrame*>(f->GetNextContinuation())) {
3058 0 : int32_t fstart = f->GetContentOffset(), fend = f->GetContentEnd();
3059 0 : if (fend <= aStartOffset || fstart >= aEndOffset)
3060 0 : continue;
3061 :
3062 : // Calculate the text content offsets we'll need if text is requested.
3063 0 : int32_t textContentStart = fstart;
3064 0 : int32_t textContentEnd = fend;
3065 :
3066 : // overlapping with the offset we want
3067 0 : f->EnsureTextRun(nsTextFrame::eInflated);
3068 0 : NS_ENSURE_TRUE(f->GetTextRun(nsTextFrame::eInflated), NS_ERROR_OUT_OF_MEMORY);
3069 0 : bool rtl = f->GetTextRun(nsTextFrame::eInflated)->IsRightToLeft();
3070 0 : nsRect r = f->GetRectRelativeToSelf();
3071 0 : if (fstart < aStartOffset) {
3072 : // aStartOffset is within this frame
3073 0 : ExtractRectFromOffset(f, aStartOffset, &r, rtl, aClampToEdge);
3074 0 : textContentStart = aStartOffset;
3075 : }
3076 0 : if (fend > aEndOffset) {
3077 : // aEndOffset is in the middle of this frame
3078 0 : ExtractRectFromOffset(f, aEndOffset, &r, !rtl, aClampToEdge);
3079 0 : textContentEnd = aEndOffset;
3080 : }
3081 0 : r = nsLayoutUtils::TransformFrameRectToAncestor(f, r, relativeTo);
3082 0 : aCallback->AddRect(r);
3083 :
3084 : // Finally capture the text, if requested.
3085 0 : if (aTextList) {
3086 : nsIFrame::RenderedText renderedText = f->GetRenderedText(
3087 : textContentStart,
3088 : textContentEnd,
3089 : nsIFrame::TextOffsetType::OFFSETS_IN_CONTENT_TEXT,
3090 0 : nsIFrame::TrailingWhitespace::DONT_TRIM_TRAILING_WHITESPACE);
3091 :
3092 0 : aTextList->AppendElement(renderedText.mString, fallible);
3093 : }
3094 : }
3095 : }
3096 0 : return NS_OK;
3097 : }
3098 :
3099 : /* static */ void
3100 0 : nsRange::CollectClientRectsAndText(nsLayoutUtils::RectCallback* aCollector,
3101 : Sequence<nsString>* aTextList,
3102 : nsRange* aRange,
3103 : nsINode* aStartContainer,
3104 : int32_t aStartOffset,
3105 : nsINode* aEndContainer,
3106 : int32_t aEndOffset,
3107 : bool aClampToEdge, bool aFlushLayout)
3108 : {
3109 : // Hold strong pointers across the flush
3110 0 : nsCOMPtr<nsINode> startContainer = aStartContainer;
3111 0 : nsCOMPtr<nsINode> endContainer = aEndContainer;
3112 :
3113 : // Flush out layout so our frames are up to date.
3114 0 : if (!aStartContainer->IsInUncomposedDoc()) {
3115 0 : return;
3116 : }
3117 :
3118 0 : if (aFlushLayout) {
3119 0 : aStartContainer->OwnerDoc()->FlushPendingNotifications(FlushType::Layout);
3120 : // Recheck whether we're still in the document
3121 0 : if (!aStartContainer->IsInUncomposedDoc()) {
3122 0 : return;
3123 : }
3124 : }
3125 :
3126 0 : RangeSubtreeIterator iter;
3127 :
3128 0 : nsresult rv = iter.Init(aRange);
3129 0 : if (NS_FAILED(rv)) return;
3130 :
3131 0 : if (iter.IsDone()) {
3132 : // the range is collapsed, only continue if the cursor is in a text node
3133 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(aStartContainer);
3134 0 : if (content && content->IsNodeOfType(nsINode::eTEXT)) {
3135 0 : nsTextFrame* textFrame = GetTextFrameForContent(content, aFlushLayout);
3136 0 : if (textFrame) {
3137 : int32_t outOffset;
3138 : nsIFrame* outFrame;
3139 : textFrame->GetChildFrameContainingOffset(aStartOffset, false,
3140 0 : &outOffset, &outFrame);
3141 0 : if (outFrame) {
3142 : nsIFrame* relativeTo =
3143 0 : nsLayoutUtils::GetContainingBlockForClientRect(outFrame);
3144 0 : nsRect r = outFrame->GetRectRelativeToSelf();
3145 0 : ExtractRectFromOffset(outFrame, aStartOffset, &r, false, aClampToEdge);
3146 0 : r.width = 0;
3147 0 : r = nsLayoutUtils::TransformFrameRectToAncestor(outFrame, r, relativeTo);
3148 0 : aCollector->AddRect(r);
3149 : }
3150 : }
3151 : }
3152 0 : return;
3153 : }
3154 :
3155 0 : do {
3156 0 : nsCOMPtr<nsINode> node = iter.GetCurrentNode();
3157 0 : iter.Next();
3158 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(node);
3159 0 : if (!content)
3160 0 : continue;
3161 0 : if (content->IsNodeOfType(nsINode::eTEXT)) {
3162 0 : if (node == startContainer) {
3163 0 : int32_t offset = startContainer == endContainer ?
3164 0 : aEndOffset : content->GetText()->GetLength();
3165 0 : GetPartialTextRect(aCollector, aTextList, content, aStartOffset, offset,
3166 0 : aClampToEdge, aFlushLayout);
3167 0 : continue;
3168 0 : } else if (node == endContainer) {
3169 0 : GetPartialTextRect(aCollector, aTextList, content, 0, aEndOffset,
3170 0 : aClampToEdge, aFlushLayout);
3171 0 : continue;
3172 : }
3173 : }
3174 :
3175 0 : nsIFrame* frame = content->GetPrimaryFrame();
3176 0 : if (frame) {
3177 0 : nsLayoutUtils::GetAllInFlowRectsAndTexts(frame,
3178 : nsLayoutUtils::GetContainingBlockForClientRect(frame), aCollector,
3179 : aTextList,
3180 0 : nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS);
3181 : }
3182 0 : } while (!iter.IsDone());
3183 : }
3184 :
3185 : NS_IMETHODIMP
3186 0 : nsRange::GetBoundingClientRect(nsIDOMClientRect** aResult)
3187 : {
3188 0 : *aResult = GetBoundingClientRect(true).take();
3189 0 : return NS_OK;
3190 : }
3191 :
3192 : already_AddRefed<DOMRect>
3193 0 : nsRange::GetBoundingClientRect(bool aClampToEdge, bool aFlushLayout)
3194 : {
3195 0 : RefPtr<DOMRect> rect = new DOMRect(ToSupports(this));
3196 0 : if (!mStartContainer) {
3197 0 : return rect.forget();
3198 : }
3199 :
3200 0 : nsLayoutUtils::RectAccumulator accumulator;
3201 0 : CollectClientRectsAndText(&accumulator, nullptr, this, mStartContainer,
3202 0 : mStartOffset, mEndContainer, mEndOffset, aClampToEdge, aFlushLayout);
3203 :
3204 0 : nsRect r = accumulator.mResultRect.IsEmpty() ? accumulator.mFirstRect :
3205 0 : accumulator.mResultRect;
3206 0 : rect->SetLayoutRect(r);
3207 0 : return rect.forget();
3208 : }
3209 :
3210 : NS_IMETHODIMP
3211 0 : nsRange::GetClientRects(nsIDOMClientRectList** aResult)
3212 : {
3213 0 : *aResult = GetClientRects(true).take();
3214 0 : return NS_OK;
3215 : }
3216 :
3217 : already_AddRefed<DOMRectList>
3218 0 : nsRange::GetClientRects(bool aClampToEdge, bool aFlushLayout)
3219 : {
3220 0 : if (!mStartContainer) {
3221 0 : return nullptr;
3222 : }
3223 :
3224 : RefPtr<DOMRectList> rectList =
3225 0 : new DOMRectList(static_cast<nsIDOMRange*>(this));
3226 :
3227 0 : nsLayoutUtils::RectListBuilder builder(rectList);
3228 :
3229 0 : CollectClientRectsAndText(&builder, nullptr, this, mStartContainer,
3230 0 : mStartOffset, mEndContainer, mEndOffset, aClampToEdge, aFlushLayout);
3231 0 : return rectList.forget();
3232 : }
3233 :
3234 : void
3235 0 : nsRange::GetClientRectsAndTexts(
3236 : mozilla::dom::ClientRectsAndTexts& aResult,
3237 : ErrorResult& aErr)
3238 : {
3239 0 : if (!mStartContainer) {
3240 0 : return;
3241 : }
3242 :
3243 0 : aResult.mRectList = new DOMRectList(static_cast<nsIDOMRange*>(this));
3244 :
3245 0 : nsLayoutUtils::RectListBuilder builder(aResult.mRectList);
3246 :
3247 0 : CollectClientRectsAndText(&builder, &aResult.mTextList, this,
3248 0 : mStartContainer, mStartOffset, mEndContainer, mEndOffset, true, true);
3249 : }
3250 :
3251 : NS_IMETHODIMP
3252 0 : nsRange::GetUsedFontFaces(nsIDOMFontFaceList** aResult)
3253 : {
3254 0 : *aResult = nullptr;
3255 :
3256 0 : NS_ENSURE_TRUE(mStartContainer, NS_ERROR_UNEXPECTED);
3257 :
3258 0 : nsCOMPtr<nsINode> startContainer = do_QueryInterface(mStartContainer);
3259 0 : nsCOMPtr<nsINode> endContainer = do_QueryInterface(mEndContainer);
3260 :
3261 : // Flush out layout so our frames are up to date.
3262 0 : nsIDocument* doc = mStartContainer->OwnerDoc();
3263 0 : NS_ENSURE_TRUE(doc, NS_ERROR_UNEXPECTED);
3264 0 : doc->FlushPendingNotifications(FlushType::Frames);
3265 :
3266 : // Recheck whether we're still in the document
3267 0 : NS_ENSURE_TRUE(mStartContainer->IsInUncomposedDoc(), NS_ERROR_UNEXPECTED);
3268 :
3269 0 : RefPtr<nsFontFaceList> fontFaceList = new nsFontFaceList();
3270 :
3271 0 : RangeSubtreeIterator iter;
3272 0 : nsresult rv = iter.Init(this);
3273 0 : NS_ENSURE_SUCCESS(rv, rv);
3274 :
3275 0 : while (!iter.IsDone()) {
3276 : // only collect anything if the range is not collapsed
3277 0 : nsCOMPtr<nsINode> node = iter.GetCurrentNode();
3278 0 : iter.Next();
3279 :
3280 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(node);
3281 0 : if (!content) {
3282 0 : continue;
3283 : }
3284 0 : nsIFrame* frame = content->GetPrimaryFrame();
3285 0 : if (!frame) {
3286 0 : continue;
3287 : }
3288 :
3289 0 : if (content->IsNodeOfType(nsINode::eTEXT)) {
3290 0 : if (node == startContainer) {
3291 0 : int32_t offset = startContainer == endContainer ?
3292 0 : mEndOffset : content->GetText()->GetLength();
3293 0 : nsLayoutUtils::GetFontFacesForText(frame, mStartOffset, offset,
3294 0 : true, fontFaceList);
3295 0 : continue;
3296 : }
3297 0 : if (node == endContainer) {
3298 0 : nsLayoutUtils::GetFontFacesForText(frame, 0, mEndOffset,
3299 0 : true, fontFaceList);
3300 0 : continue;
3301 : }
3302 : }
3303 :
3304 0 : nsLayoutUtils::GetFontFacesForFrames(frame, fontFaceList);
3305 : }
3306 :
3307 0 : fontFaceList.forget(aResult);
3308 0 : return NS_OK;
3309 : }
3310 :
3311 : nsINode*
3312 0 : nsRange::GetRegisteredCommonAncestor()
3313 : {
3314 0 : NS_ASSERTION(IsInSelection(),
3315 : "GetRegisteredCommonAncestor only valid for range in selection");
3316 0 : nsINode* ancestor = GetNextRangeCommonAncestor(mStartContainer);
3317 0 : while (ancestor) {
3318 : RangeHashTable* ranges =
3319 0 : static_cast<RangeHashTable*>(ancestor->GetProperty(nsGkAtoms::range));
3320 0 : if (ranges->GetEntry(this)) {
3321 0 : break;
3322 : }
3323 0 : ancestor = GetNextRangeCommonAncestor(ancestor->GetParentNode());
3324 : }
3325 0 : NS_ASSERTION(ancestor, "can't find common ancestor for selected range");
3326 0 : return ancestor;
3327 : }
3328 :
3329 : /* static */ bool nsRange::AutoInvalidateSelection::mIsNested;
3330 :
3331 12 : nsRange::AutoInvalidateSelection::~AutoInvalidateSelection()
3332 : {
3333 6 : NS_ASSERTION(mWasInSelection == mRange->IsInSelection(),
3334 : "Range got unselected in AutoInvalidateSelection block");
3335 6 : if (!mCommonAncestor) {
3336 6 : return;
3337 : }
3338 0 : mIsNested = false;
3339 0 : ::InvalidateAllFrames(mCommonAncestor);
3340 0 : nsINode* commonAncestor = mRange->GetRegisteredCommonAncestor();
3341 0 : if (commonAncestor && commonAncestor != mCommonAncestor) {
3342 0 : ::InvalidateAllFrames(commonAncestor);
3343 : }
3344 6 : }
3345 :
3346 : /* static */ already_AddRefed<nsRange>
3347 0 : nsRange::Constructor(const GlobalObject& aGlobal,
3348 : ErrorResult& aRv)
3349 : {
3350 0 : nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal.GetAsSupports());
3351 0 : if (!window || !window->GetDoc()) {
3352 0 : aRv.Throw(NS_ERROR_FAILURE);
3353 0 : return nullptr;
3354 : }
3355 :
3356 0 : return window->GetDoc()->CreateRange(aRv);
3357 : }
3358 :
3359 0 : static bool ExcludeIfNextToNonSelectable(nsIContent* aContent)
3360 : {
3361 0 : return aContent->IsNodeOfType(nsINode::eTEXT) &&
3362 0 : aContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE);
3363 : }
3364 :
3365 : void
3366 0 : nsRange::ExcludeNonSelectableNodes(nsTArray<RefPtr<nsRange>>* aOutRanges)
3367 : {
3368 0 : MOZ_ASSERT(mIsPositioned);
3369 0 : MOZ_ASSERT(mEndContainer);
3370 0 : MOZ_ASSERT(mStartContainer);
3371 :
3372 0 : nsRange* range = this;
3373 0 : RefPtr<nsRange> newRange;
3374 0 : while (range) {
3375 0 : nsCOMPtr<nsIContentIterator> iter = NS_NewPreContentIterator();
3376 0 : nsresult rv = iter->Init(range);
3377 0 : if (NS_FAILED(rv)) {
3378 0 : return;
3379 : }
3380 :
3381 0 : bool added = false;
3382 0 : bool seenSelectable = false;
3383 : // |firstNonSelectableContent| is the first node in a consecutive sequence
3384 : // of non-IsSelectable nodes. When we find a selectable node after such
3385 : // a sequence we'll end the last nsRange, create a new one and restart
3386 : // the outer loop.
3387 0 : nsIContent* firstNonSelectableContent = nullptr;
3388 : while (true) {
3389 0 : ErrorResult err;
3390 0 : nsINode* node = iter->GetCurrentNode();
3391 0 : iter->Next();
3392 0 : bool selectable = true;
3393 : nsIContent* content =
3394 0 : node && node->IsContent() ? node->AsContent() : nullptr;
3395 0 : if (content) {
3396 0 : if (firstNonSelectableContent && ExcludeIfNextToNonSelectable(content)) {
3397 : // Ignorable whitespace next to a sequence of non-selectable nodes
3398 : // counts as non-selectable (bug 1216001).
3399 0 : selectable = false;
3400 : }
3401 0 : if (selectable) {
3402 0 : nsIFrame* frame = content->GetPrimaryFrame();
3403 0 : for (nsIContent* p = content; !frame && (p = p->GetParent()); ) {
3404 0 : frame = p->GetPrimaryFrame();
3405 : }
3406 0 : if (frame) {
3407 0 : selectable = frame->IsSelectable(nullptr);
3408 : }
3409 : }
3410 : }
3411 :
3412 0 : if (!selectable) {
3413 0 : if (!firstNonSelectableContent) {
3414 0 : firstNonSelectableContent = content;
3415 : }
3416 0 : if (iter->IsDone() && seenSelectable) {
3417 : // The tail end of the initial range is non-selectable - truncate the
3418 : // current range before the first non-selectable node.
3419 0 : range->SetEndBefore(*firstNonSelectableContent, err);
3420 : }
3421 0 : } else if (firstNonSelectableContent) {
3422 0 : if (range == this && !seenSelectable) {
3423 : // This is the initial range and all its nodes until now are
3424 : // non-selectable so just trim them from the start.
3425 0 : range->SetStartBefore(*node, err);
3426 0 : if (err.Failed()) {
3427 0 : return;
3428 : }
3429 0 : break; // restart the same range with a new iterator
3430 : } else {
3431 : // Save the end point before truncating the range.
3432 0 : nsINode* endContainer = range->mEndContainer;
3433 0 : int32_t endOffset = range->mEndOffset;
3434 :
3435 : // Truncate the current range before the first non-selectable node.
3436 0 : range->SetEndBefore(*firstNonSelectableContent, err);
3437 :
3438 : // Store it in the result (strong ref) - do this before creating
3439 : // a new range in |newRange| below so we don't drop the last ref
3440 : // to the range created in the previous iteration.
3441 0 : if (!added && !err.Failed()) {
3442 0 : aOutRanges->AppendElement(range);
3443 : }
3444 :
3445 : // Create a new range for the remainder.
3446 0 : nsINode* startContainer = node;
3447 0 : int32_t startOffset = 0;
3448 : // Don't start *inside* a node with independent selection though
3449 : // (e.g. <input>).
3450 0 : if (content && content->HasIndependentSelection()) {
3451 0 : nsINode* parent = startContainer->GetParent();
3452 0 : if (parent) {
3453 0 : startOffset = parent->IndexOf(startContainer);
3454 0 : startContainer = parent;
3455 : }
3456 : }
3457 0 : rv = CreateRange(startContainer, startOffset, endContainer, endOffset,
3458 0 : getter_AddRefs(newRange));
3459 0 : if (NS_FAILED(rv) || newRange->Collapsed()) {
3460 0 : newRange = nullptr;
3461 : }
3462 0 : range = newRange;
3463 0 : break; // create a new iterator for the new range, if any
3464 : }
3465 : } else {
3466 0 : seenSelectable = true;
3467 0 : if (!added) {
3468 0 : added = true;
3469 0 : aOutRanges->AppendElement(range);
3470 : }
3471 : }
3472 0 : if (iter->IsDone()) {
3473 0 : return;
3474 : }
3475 0 : }
3476 : }
3477 : }
3478 :
3479 : struct InnerTextAccumulator
3480 : {
3481 0 : explicit InnerTextAccumulator(mozilla::dom::DOMString& aValue)
3482 0 : : mString(aValue.AsAString()), mRequiredLineBreakCount(0) {}
3483 0 : void FlushLineBreaks()
3484 : {
3485 0 : while (mRequiredLineBreakCount > 0) {
3486 : // Required line breaks at the start of the text are suppressed.
3487 0 : if (!mString.IsEmpty()) {
3488 0 : mString.Append('\n');
3489 : }
3490 0 : --mRequiredLineBreakCount;
3491 : }
3492 0 : }
3493 0 : void Append(char aCh)
3494 : {
3495 0 : Append(nsAutoString(aCh));
3496 0 : }
3497 0 : void Append(const nsAString& aString)
3498 : {
3499 0 : if (aString.IsEmpty()) {
3500 0 : return;
3501 : }
3502 0 : FlushLineBreaks();
3503 0 : mString.Append(aString);
3504 : }
3505 0 : void AddRequiredLineBreakCount(int8_t aCount)
3506 : {
3507 0 : mRequiredLineBreakCount = std::max(mRequiredLineBreakCount, aCount);
3508 0 : }
3509 :
3510 : nsAString& mString;
3511 : int8_t mRequiredLineBreakCount;
3512 : };
3513 :
3514 : static bool
3515 0 : IsVisibleAndNotInReplacedElement(nsIFrame* aFrame)
3516 : {
3517 0 : if (!aFrame || !aFrame->StyleVisibility()->IsVisible()) {
3518 0 : return false;
3519 : }
3520 0 : for (nsIFrame* f = aFrame->GetParent(); f; f = f->GetParent()) {
3521 0 : if (f->IsFrameOfType(nsIFrame::eReplaced) &&
3522 0 : !f->GetContent()->IsHTMLElement(nsGkAtoms::button) &&
3523 0 : !f->GetContent()->IsHTMLElement(nsGkAtoms::select)) {
3524 0 : return false;
3525 : }
3526 : }
3527 0 : return true;
3528 : }
3529 :
3530 : static bool
3531 0 : ElementIsVisibleNoFlush(Element* aElement)
3532 : {
3533 0 : if (!aElement) {
3534 0 : return false;
3535 : }
3536 : RefPtr<nsStyleContext> sc =
3537 0 : nsComputedDOMStyle::GetStyleContextNoFlush(aElement, nullptr, nullptr);
3538 0 : return sc && sc->StyleVisibility()->IsVisible();
3539 : }
3540 :
3541 : static void
3542 0 : AppendTransformedText(InnerTextAccumulator& aResult,
3543 : nsGenericDOMDataNode* aTextNode,
3544 : int32_t aStart, int32_t aEnd)
3545 : {
3546 0 : nsIFrame* frame = aTextNode->GetPrimaryFrame();
3547 0 : if (!IsVisibleAndNotInReplacedElement(frame)) {
3548 0 : return;
3549 : }
3550 0 : nsIFrame::RenderedText text = frame->GetRenderedText(aStart, aEnd);
3551 0 : aResult.Append(text.mString);
3552 : }
3553 :
3554 : /**
3555 : * States for tree traversal. AT_NODE means that we are about to enter
3556 : * the current DOM node. AFTER_NODE means that we have just finished traversing
3557 : * the children of the current DOM node and are about to apply any
3558 : * "after processing the node's children" steps before we finish visiting
3559 : * the node.
3560 : */
3561 : enum TreeTraversalState {
3562 : AT_NODE,
3563 : AFTER_NODE
3564 : };
3565 :
3566 : static int8_t
3567 0 : GetRequiredInnerTextLineBreakCount(nsIFrame* aFrame)
3568 : {
3569 0 : if (aFrame->GetContent()->IsHTMLElement(nsGkAtoms::p)) {
3570 0 : return 2;
3571 : }
3572 0 : const nsStyleDisplay* styleDisplay = aFrame->StyleDisplay();
3573 0 : if (styleDisplay->IsBlockOutside(aFrame) ||
3574 0 : styleDisplay->mDisplay == StyleDisplay::TableCaption) {
3575 0 : return 1;
3576 : }
3577 0 : return 0;
3578 : }
3579 :
3580 : static bool
3581 0 : IsLastCellOfRow(nsIFrame* aFrame)
3582 : {
3583 0 : LayoutFrameType type = aFrame->Type();
3584 0 : if (type != LayoutFrameType::TableCell &&
3585 : type != LayoutFrameType::BCTableCell) {
3586 0 : return true;
3587 : }
3588 0 : for (nsIFrame* c = aFrame; c; c = c->GetNextContinuation()) {
3589 0 : if (c->GetNextSibling()) {
3590 0 : return false;
3591 : }
3592 : }
3593 0 : return true;
3594 : }
3595 :
3596 : static bool
3597 0 : IsLastRowOfRowGroup(nsIFrame* aFrame)
3598 : {
3599 0 : if (!aFrame->IsTableRowFrame()) {
3600 0 : return true;
3601 : }
3602 0 : for (nsIFrame* c = aFrame; c; c = c->GetNextContinuation()) {
3603 0 : if (c->GetNextSibling()) {
3604 0 : return false;
3605 : }
3606 : }
3607 0 : return true;
3608 : }
3609 :
3610 : static bool
3611 0 : IsLastNonemptyRowGroupOfTable(nsIFrame* aFrame)
3612 : {
3613 0 : if (!aFrame->IsTableRowGroupFrame()) {
3614 0 : return true;
3615 : }
3616 0 : for (nsIFrame* c = aFrame; c; c = c->GetNextContinuation()) {
3617 0 : for (nsIFrame* next = c->GetNextSibling(); next; next = next->GetNextSibling()) {
3618 0 : if (next->PrincipalChildList().FirstChild()) {
3619 0 : return false;
3620 : }
3621 : }
3622 : }
3623 0 : return true;
3624 : }
3625 :
3626 : void
3627 0 : nsRange::GetInnerTextNoFlush(DOMString& aValue, ErrorResult& aError,
3628 : nsIContent* aStartContainer, uint32_t aStartOffset,
3629 : nsIContent* aEndContainer, uint32_t aEndOffset)
3630 : {
3631 0 : InnerTextAccumulator result(aValue);
3632 0 : nsIContent* currentNode = aStartContainer;
3633 0 : TreeTraversalState currentState = AFTER_NODE;
3634 0 : if (aStartContainer->IsNodeOfType(nsINode::eTEXT)) {
3635 0 : auto t = static_cast<nsGenericDOMDataNode*>(aStartContainer);
3636 0 : if (aStartContainer == aEndContainer) {
3637 0 : AppendTransformedText(result, t, aStartOffset, aEndOffset);
3638 0 : return;
3639 : }
3640 0 : AppendTransformedText(result, t, aStartOffset, t->TextLength());
3641 : } else {
3642 0 : if (uint32_t(aStartOffset) < aStartContainer->GetChildCount()) {
3643 0 : currentNode = aStartContainer->GetChildAt(aStartOffset);
3644 0 : currentState = AT_NODE;
3645 : }
3646 : }
3647 :
3648 0 : nsIContent* endNode = aEndContainer;
3649 0 : TreeTraversalState endState = AFTER_NODE;
3650 0 : if (aEndContainer->IsNodeOfType(nsINode::eTEXT)) {
3651 0 : endState = AT_NODE;
3652 : } else {
3653 0 : if (uint32_t(aEndOffset) < aEndContainer->GetChildCount()) {
3654 0 : endNode = aEndContainer->GetChildAt(aEndOffset);
3655 0 : endState = AT_NODE;
3656 : }
3657 : }
3658 :
3659 0 : while (currentNode != endNode || currentState != endState) {
3660 0 : nsIFrame* f = currentNode->GetPrimaryFrame();
3661 0 : bool isVisibleAndNotReplaced = IsVisibleAndNotInReplacedElement(f);
3662 0 : if (currentState == AT_NODE) {
3663 0 : bool isText = currentNode->IsNodeOfType(nsINode::eTEXT);
3664 0 : if (isText && currentNode->GetParent()->IsHTMLElement(nsGkAtoms::rp) &&
3665 0 : ElementIsVisibleNoFlush(currentNode->GetParent()->AsElement())) {
3666 0 : nsAutoString str;
3667 0 : currentNode->GetTextContent(str, aError);
3668 0 : result.Append(str);
3669 0 : } else if (isVisibleAndNotReplaced) {
3670 0 : result.AddRequiredLineBreakCount(GetRequiredInnerTextLineBreakCount(f));
3671 0 : if (isText) {
3672 0 : nsIFrame::RenderedText text = f->GetRenderedText();
3673 0 : result.Append(text.mString);
3674 : }
3675 : }
3676 0 : nsIContent* child = currentNode->GetFirstChild();
3677 0 : if (child) {
3678 0 : currentNode = child;
3679 0 : continue;
3680 : }
3681 0 : currentState = AFTER_NODE;
3682 : }
3683 0 : if (currentNode == endNode && currentState == endState) {
3684 0 : break;
3685 : }
3686 0 : if (isVisibleAndNotReplaced) {
3687 0 : if (currentNode->IsHTMLElement(nsGkAtoms::br)) {
3688 0 : result.Append('\n');
3689 : }
3690 0 : switch (f->StyleDisplay()->mDisplay) {
3691 : case StyleDisplay::TableCell:
3692 0 : if (!IsLastCellOfRow(f)) {
3693 0 : result.Append('\t');
3694 : }
3695 0 : break;
3696 : case StyleDisplay::TableRow:
3697 0 : if (!IsLastRowOfRowGroup(f) ||
3698 0 : !IsLastNonemptyRowGroupOfTable(f->GetParent())) {
3699 0 : result.Append('\n');
3700 : }
3701 0 : break;
3702 : default:
3703 0 : break; // Do nothing
3704 : }
3705 0 : result.AddRequiredLineBreakCount(GetRequiredInnerTextLineBreakCount(f));
3706 : }
3707 0 : nsIContent* next = currentNode->GetNextSibling();
3708 0 : if (next) {
3709 0 : currentNode = next;
3710 0 : currentState = AT_NODE;
3711 : } else {
3712 0 : currentNode = currentNode->GetParent();
3713 : }
3714 : }
3715 :
3716 0 : if (aEndContainer->IsNodeOfType(nsINode::eTEXT)) {
3717 0 : nsGenericDOMDataNode* t = static_cast<nsGenericDOMDataNode*>(aEndContainer);
3718 0 : AppendTransformedText(result, t, 0, aEndOffset);
3719 : }
3720 : // Do not flush trailing line breaks! Required breaks at the end of the text
3721 : // are suppressed.
3722 : }
|