1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "FilteredContentIterator.h"
10 #include "mozilla/ContentIterator.h"
11 #include "mozilla/dom/AbstractRange.h"
12 #include "mozilla/Maybe.h"
13 #include "mozilla/mozalloc.h"
15 #include "nsComponentManagerUtils.h"
16 #include "nsComposeTxtSrvFilter.h"
17 #include "nsContentUtils.h"
20 #include "nsIContent.h"
22 #include "nsISupports.h"
23 #include "nsISupportsUtils.h"
30 FilteredContentIterator::FilteredContentIterator(
31 UniquePtr
<nsComposeTxtSrvFilter
> aFilter
)
32 : mCurrentIterator(nullptr),
33 mFilter(std::move(aFilter
)),
36 mDirection(eDirNotSet
) {}
38 FilteredContentIterator::~FilteredContentIterator() {}
40 NS_IMPL_CYCLE_COLLECTION(FilteredContentIterator
, mPostIterator
, mPreIterator
,
43 nsresult
FilteredContentIterator::Init(nsINode
* aRoot
) {
44 NS_ENSURE_ARG_POINTER(aRoot
);
45 mIsOutOfRange
= false;
46 mDirection
= eForward
;
47 mCurrentIterator
= &mPreIterator
;
49 mRange
= nsRange::Create(aRoot
);
50 mRange
->SelectNode(*aRoot
, IgnoreErrors());
52 nsresult rv
= mPreIterator
.Init(mRange
);
53 NS_ENSURE_SUCCESS(rv
, rv
);
54 return mPostIterator
.Init(mRange
);
57 nsresult
FilteredContentIterator::Init(const AbstractRange
* aAbstractRange
) {
58 if (NS_WARN_IF(!aAbstractRange
)) {
59 return NS_ERROR_INVALID_ARG
;
62 if (NS_WARN_IF(!aAbstractRange
->IsPositioned())) {
63 return NS_ERROR_INVALID_ARG
;
66 mRange
= nsRange::Create(aAbstractRange
, IgnoreErrors());
67 if (NS_WARN_IF(!mRange
)) {
68 return NS_ERROR_FAILURE
;
70 return InitWithRange();
73 nsresult
FilteredContentIterator::Init(nsINode
* aStartContainer
,
74 uint32_t aStartOffset
,
75 nsINode
* aEndContainer
,
76 uint32_t aEndOffset
) {
77 return Init(RawRangeBoundary(aStartContainer
, aStartOffset
),
78 RawRangeBoundary(aEndContainer
, aEndOffset
));
81 nsresult
FilteredContentIterator::Init(const RawRangeBoundary
& aStartBoundary
,
82 const RawRangeBoundary
& aEndBoundary
) {
83 RefPtr
<nsRange
> range
=
84 nsRange::Create(aStartBoundary
, aEndBoundary
, IgnoreErrors());
85 if (NS_WARN_IF(!range
) || NS_WARN_IF(!range
->IsPositioned())) {
86 return NS_ERROR_INVALID_ARG
;
89 MOZ_ASSERT(range
->StartRef() == aStartBoundary
);
90 MOZ_ASSERT(range
->EndRef() == aEndBoundary
);
92 mRange
= std::move(range
);
94 return InitWithRange();
97 nsresult
FilteredContentIterator::InitWithRange() {
99 MOZ_ASSERT(mRange
->IsPositioned());
101 mIsOutOfRange
= false;
102 mDirection
= eForward
;
103 mCurrentIterator
= &mPreIterator
;
105 nsresult rv
= mPreIterator
.Init(mRange
);
106 if (NS_WARN_IF(NS_FAILED(rv
))) {
109 return mPostIterator
.Init(mRange
);
112 nsresult
FilteredContentIterator::SwitchDirections(bool aChangeToForward
) {
113 nsINode
* node
= mCurrentIterator
->GetCurrentNode();
115 if (aChangeToForward
) {
116 mCurrentIterator
= &mPreIterator
;
117 mDirection
= eForward
;
119 mCurrentIterator
= &mPostIterator
;
120 mDirection
= eBackward
;
124 nsresult rv
= mCurrentIterator
->PositionAt(node
);
126 mIsOutOfRange
= true;
133 void FilteredContentIterator::First() {
134 if (!mCurrentIterator
) {
135 NS_ERROR("Missing iterator!");
140 // If we are switching directions then
141 // we need to switch how we process the nodes
142 if (mDirection
!= eForward
) {
143 mCurrentIterator
= &mPreIterator
;
144 mDirection
= eForward
;
145 mIsOutOfRange
= false;
148 mCurrentIterator
->First();
150 if (mCurrentIterator
->IsDone()) {
154 nsINode
* currentNode
= mCurrentIterator
->GetCurrentNode();
157 CheckAdvNode(currentNode
, didCross
, eForward
);
160 void FilteredContentIterator::Last() {
161 if (!mCurrentIterator
) {
162 NS_ERROR("Missing iterator!");
167 // If we are switching directions then
168 // we need to switch how we process the nodes
169 if (mDirection
!= eBackward
) {
170 mCurrentIterator
= &mPostIterator
;
171 mDirection
= eBackward
;
172 mIsOutOfRange
= false;
175 mCurrentIterator
->Last();
177 if (mCurrentIterator
->IsDone()) {
181 nsINode
* currentNode
= mCurrentIterator
->GetCurrentNode();
184 CheckAdvNode(currentNode
, didCross
, eBackward
);
187 ///////////////////////////////////////////////////////////////////////////
188 // ContentIsInTraversalRange: returns true if content is visited during
189 // the traversal of the range in the specified mode.
191 static bool ContentIsInTraversalRange(nsIContent
* aContent
, bool aIsPreMode
,
192 nsINode
* aStartContainer
,
193 int32_t aStartOffset
,
194 nsINode
* aEndContainer
,
195 int32_t aEndOffset
) {
196 NS_ENSURE_TRUE(aStartContainer
&& aEndContainer
&& aContent
, false);
198 nsIContent
* parentContent
= aContent
->GetParent();
199 if (MOZ_UNLIKELY(NS_WARN_IF(!parentContent
))) {
202 Maybe
<uint32_t> offsetInParent
= parentContent
->ComputeIndexOf(aContent
);
203 NS_WARNING_ASSERTION(
204 offsetInParent
.isSome(),
205 "Content is not in the parent, is this called during a DOM mutation?");
206 if (MOZ_UNLIKELY(NS_WARN_IF(offsetInParent
.isNothing()))) {
211 MOZ_ASSERT(*offsetInParent
!= UINT32_MAX
);
215 const Maybe
<int32_t> startRes
= nsContentUtils::ComparePoints(
216 aStartContainer
, aStartOffset
, parentContent
, *offsetInParent
);
217 if (MOZ_UNLIKELY(NS_WARN_IF(!startRes
))) {
220 const Maybe
<int32_t> endRes
= nsContentUtils::ComparePoints(
221 aEndContainer
, aEndOffset
, parentContent
, *offsetInParent
);
222 if (MOZ_UNLIKELY(NS_WARN_IF(!endRes
))) {
225 return *startRes
<= 0 && *endRes
>= 0;
228 static bool ContentIsInTraversalRange(nsRange
* aRange
, nsIContent
* aNextContent
,
230 // XXXbz we have a caller below (in AdvanceNode) who passes null for
232 NS_ENSURE_TRUE(aNextContent
&& aRange
, false);
234 return ContentIsInTraversalRange(
235 aNextContent
, aIsPreMode
, aRange
->GetStartContainer(),
236 static_cast<int32_t>(aRange
->StartOffset()), aRange
->GetEndContainer(),
237 static_cast<int32_t>(aRange
->EndOffset()));
240 // Helper function to advance to the next or previous node
241 nsresult
FilteredContentIterator::AdvanceNode(nsINode
* aNode
,
243 eDirectionType aDir
) {
244 nsCOMPtr
<nsIContent
> nextNode
;
245 if (aDir
== eForward
) {
246 nextNode
= aNode
->GetNextSibling();
248 nextNode
= aNode
->GetPreviousSibling();
252 // If we got here, that means we found the nxt/prv node
253 // make sure it is in our DOMRange
255 ContentIsInTraversalRange(mRange
, nextNode
, aDir
== eForward
);
262 // The next node was null so we need to walk up the parent(s)
263 nsCOMPtr
<nsINode
> parent
= aNode
->GetParentNode();
264 NS_ASSERTION(parent
, "parent can't be nullptr");
266 // Make sure the parent is in the DOMRange before going further
267 // XXXbz why are we passing nextNode, not the parent??? If this gets fixed,
268 // then ContentIsInTraversalRange can stop null-checking its second arg.
270 ContentIsInTraversalRange(mRange
, nextNode
, aDir
== eForward
);
272 // Now find the nxt/prv node after/before this node
273 nsresult rv
= AdvanceNode(parent
, aNewNode
, aDir
);
274 if (NS_SUCCEEDED(rv
) && aNewNode
) {
280 // if we get here it pretty much means
281 // we went out of the DOM Range
282 mIsOutOfRange
= true;
284 return NS_ERROR_FAILURE
;
287 // Helper function to see if the next/prev node should be skipped
288 void FilteredContentIterator::CheckAdvNode(nsINode
* aNode
, bool& aDidSkip
,
289 eDirectionType aDir
) {
291 mIsOutOfRange
= false;
293 if (aNode
&& mFilter
) {
294 nsCOMPtr
<nsINode
> currentNode
= aNode
;
296 if (mFilter
->Skip(aNode
)) {
298 // Get the next/prev node and then
299 // see if we should skip that
300 nsCOMPtr
<nsINode
> advNode
;
301 nsresult rv
= AdvanceNode(aNode
, *getter_AddRefs(advNode
), aDir
);
302 if (NS_SUCCEEDED(rv
) && advNode
) {
305 return; // fell out of range
308 if (aNode
!= currentNode
) {
309 nsCOMPtr
<nsIContent
> content(do_QueryInterface(aNode
));
310 Unused
<< mCurrentIterator
->PositionAt(content
);
312 return; // found something
318 void FilteredContentIterator::Next() {
319 if (mIsOutOfRange
|| !mCurrentIterator
) {
320 NS_ASSERTION(mCurrentIterator
, "Missing iterator!");
325 // If we are switching directions then
326 // we need to switch how we process the nodes
327 if (mDirection
!= eForward
) {
328 nsresult rv
= SwitchDirections(true);
334 mCurrentIterator
->Next();
336 if (mCurrentIterator
->IsDone()) {
340 // If we can't get the current node then
341 // don't check to see if we can skip it
342 nsINode
* currentNode
= mCurrentIterator
->GetCurrentNode();
344 CheckAdvNode(currentNode
, mDidSkip
, eForward
);
347 void FilteredContentIterator::Prev() {
348 if (mIsOutOfRange
|| !mCurrentIterator
) {
349 NS_ASSERTION(mCurrentIterator
, "Missing iterator!");
354 // If we are switching directions then
355 // we need to switch how we process the nodes
356 if (mDirection
!= eBackward
) {
357 nsresult rv
= SwitchDirections(false);
363 mCurrentIterator
->Prev();
365 if (mCurrentIterator
->IsDone()) {
369 // If we can't get the current node then
370 // don't check to see if we can skip it
371 nsINode
* currentNode
= mCurrentIterator
->GetCurrentNode();
373 CheckAdvNode(currentNode
, mDidSkip
, eBackward
);
376 nsINode
* FilteredContentIterator::GetCurrentNode() {
377 if (mIsOutOfRange
|| !mCurrentIterator
) {
381 return mCurrentIterator
->GetCurrentNode();
384 bool FilteredContentIterator::IsDone() {
385 if (mIsOutOfRange
|| !mCurrentIterator
) {
389 return mCurrentIterator
->IsDone();
392 nsresult
FilteredContentIterator::PositionAt(nsINode
* aCurNode
) {
393 NS_ENSURE_TRUE(mCurrentIterator
, NS_ERROR_FAILURE
);
394 mIsOutOfRange
= false;
395 return mCurrentIterator
->PositionAt(aCurNode
);
398 } // namespace mozilla