Backed out 2 changesets (bug 1943998) for causing wd failures @ phases.py CLOSED...
[gecko.git] / editor / spellchecker / FilteredContentIterator.cpp
blobf60a0df98910369ba9c90b769619c267b6cf697b
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"
8 #include <utility>
10 #include "mozilla/ContentIterator.h"
11 #include "mozilla/dom/AbstractRange.h"
12 #include "mozilla/Maybe.h"
13 #include "mozilla/mozalloc.h"
14 #include "nsAtom.h"
15 #include "nsComponentManagerUtils.h"
16 #include "nsComposeTxtSrvFilter.h"
17 #include "nsContentUtils.h"
18 #include "nsDebug.h"
19 #include "nsError.h"
20 #include "nsIContent.h"
21 #include "nsINode.h"
22 #include "nsISupports.h"
23 #include "nsISupportsUtils.h"
24 #include "nsRange.h"
26 namespace mozilla {
28 using namespace dom;
30 FilteredContentIterator::FilteredContentIterator(
31 UniquePtr<nsComposeTxtSrvFilter> aFilter)
32 : mCurrentIterator(nullptr),
33 mFilter(std::move(aFilter)),
34 mDidSkip(false),
35 mIsOutOfRange(false),
36 mDirection(eDirNotSet) {}
38 FilteredContentIterator::~FilteredContentIterator() {}
40 NS_IMPL_CYCLE_COLLECTION(FilteredContentIterator, mPostIterator, mPreIterator,
41 mRange)
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() {
98 MOZ_ASSERT(mRange);
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))) {
107 return 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;
118 } else {
119 mCurrentIterator = &mPostIterator;
120 mDirection = eBackward;
123 if (node) {
124 nsresult rv = mCurrentIterator->PositionAt(node);
125 if (NS_FAILED(rv)) {
126 mIsOutOfRange = true;
127 return rv;
130 return NS_OK;
133 void FilteredContentIterator::First() {
134 if (!mCurrentIterator) {
135 NS_ERROR("Missing iterator!");
137 return;
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()) {
151 return;
154 nsINode* currentNode = mCurrentIterator->GetCurrentNode();
156 bool didCross;
157 CheckAdvNode(currentNode, didCross, eForward);
160 void FilteredContentIterator::Last() {
161 if (!mCurrentIterator) {
162 NS_ERROR("Missing iterator!");
164 return;
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()) {
178 return;
181 nsINode* currentNode = mCurrentIterator->GetCurrentNode();
183 bool didCross;
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))) {
200 return false;
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()))) {
207 return false;
210 if (!aIsPreMode) {
211 MOZ_ASSERT(*offsetInParent != UINT32_MAX);
212 ++(*offsetInParent);
215 const Maybe<int32_t> startRes = nsContentUtils::ComparePoints(
216 aStartContainer, aStartOffset, parentContent, *offsetInParent);
217 if (MOZ_UNLIKELY(NS_WARN_IF(!startRes))) {
218 return false;
220 const Maybe<int32_t> endRes = nsContentUtils::ComparePoints(
221 aEndContainer, aEndOffset, parentContent, *offsetInParent);
222 if (MOZ_UNLIKELY(NS_WARN_IF(!endRes))) {
223 return false;
225 return *startRes <= 0 && *endRes >= 0;
228 static bool ContentIsInTraversalRange(nsRange* aRange, nsIContent* aNextContent,
229 bool aIsPreMode) {
230 // XXXbz we have a caller below (in AdvanceNode) who passes null for
231 // aNextContent!
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,
242 nsINode*& aNewNode,
243 eDirectionType aDir) {
244 nsCOMPtr<nsIContent> nextNode;
245 if (aDir == eForward) {
246 nextNode = aNode->GetNextSibling();
247 } else {
248 nextNode = aNode->GetPreviousSibling();
251 if (nextNode) {
252 // If we got here, that means we found the nxt/prv node
253 // make sure it is in our DOMRange
254 bool intersects =
255 ContentIsInTraversalRange(mRange, nextNode, aDir == eForward);
256 if (intersects) {
257 aNewNode = nextNode;
258 NS_ADDREF(aNewNode);
259 return NS_OK;
261 } else {
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.
269 bool intersects =
270 ContentIsInTraversalRange(mRange, nextNode, aDir == eForward);
271 if (intersects) {
272 // Now find the nxt/prv node after/before this node
273 nsresult rv = AdvanceNode(parent, aNewNode, aDir);
274 if (NS_SUCCEEDED(rv) && aNewNode) {
275 return NS_OK;
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) {
290 aDidSkip = false;
291 mIsOutOfRange = false;
293 if (aNode && mFilter) {
294 nsCOMPtr<nsINode> currentNode = aNode;
295 while (1) {
296 if (mFilter->Skip(aNode)) {
297 aDidSkip = true;
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) {
303 aNode = advNode;
304 } else {
305 return; // fell out of range
307 } else {
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!");
322 return;
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);
329 if (NS_FAILED(rv)) {
330 return;
334 mCurrentIterator->Next();
336 if (mCurrentIterator->IsDone()) {
337 return;
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!");
351 return;
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);
358 if (NS_FAILED(rv)) {
359 return;
363 mCurrentIterator->Prev();
365 if (mCurrentIterator->IsDone()) {
366 return;
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) {
378 return nullptr;
381 return mCurrentIterator->GetCurrentNode();
384 bool FilteredContentIterator::IsDone() {
385 if (mIsOutOfRange || !mCurrentIterator) {
386 return true;
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