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