Line data Source code
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/. */
5 :
6 : #include "mozilla/mozalloc.h"
7 : #include "nsComponentManagerUtils.h"
8 : #include "nsContentUtils.h"
9 : #include "nsDebug.h"
10 : #include "nsError.h"
11 : #include "nsFilteredContentIterator.h"
12 : #include "nsIAtom.h"
13 : #include "nsIContent.h"
14 : #include "nsIContentIterator.h"
15 : #include "nsIDOMNode.h"
16 : #include "nsINode.h"
17 : #include "nsISupportsBase.h"
18 : #include "nsISupportsUtils.h"
19 : #include "nsITextServicesFilter.h"
20 : #include "nsRange.h"
21 :
22 : //------------------------------------------------------------
23 0 : nsFilteredContentIterator::nsFilteredContentIterator(nsITextServicesFilter* aFilter) :
24 : mFilter(aFilter),
25 : mDidSkip(false),
26 : mIsOutOfRange(false),
27 0 : mDirection(eDirNotSet)
28 : {
29 0 : mIterator = do_CreateInstance("@mozilla.org/content/post-content-iterator;1");
30 0 : mPreIterator = do_CreateInstance("@mozilla.org/content/pre-content-iterator;1");
31 0 : }
32 :
33 : //------------------------------------------------------------
34 0 : nsFilteredContentIterator::~nsFilteredContentIterator()
35 : {
36 0 : }
37 :
38 : //------------------------------------------------------------
39 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFilteredContentIterator)
40 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFilteredContentIterator)
41 :
42 0 : NS_INTERFACE_MAP_BEGIN(nsFilteredContentIterator)
43 0 : NS_INTERFACE_MAP_ENTRY(nsIContentIterator)
44 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentIterator)
45 0 : NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(nsFilteredContentIterator)
46 0 : NS_INTERFACE_MAP_END
47 :
48 0 : NS_IMPL_CYCLE_COLLECTION(nsFilteredContentIterator,
49 : mCurrentIterator,
50 : mIterator,
51 : mPreIterator,
52 : mFilter,
53 : mRange)
54 :
55 : //------------------------------------------------------------
56 : nsresult
57 0 : nsFilteredContentIterator::Init(nsINode* aRoot)
58 : {
59 0 : NS_ENSURE_ARG_POINTER(aRoot);
60 0 : NS_ENSURE_TRUE(mPreIterator, NS_ERROR_FAILURE);
61 0 : NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
62 0 : mIsOutOfRange = false;
63 0 : mDirection = eForward;
64 0 : mCurrentIterator = mPreIterator;
65 :
66 0 : mRange = new nsRange(aRoot);
67 0 : nsCOMPtr<nsIDOMNode> domNode(do_QueryInterface(aRoot));
68 0 : if (domNode) {
69 0 : mRange->SelectNode(domNode);
70 : }
71 :
72 0 : nsresult rv = mPreIterator->Init(mRange);
73 0 : NS_ENSURE_SUCCESS(rv, rv);
74 0 : return mIterator->Init(mRange);
75 : }
76 :
77 : //------------------------------------------------------------
78 : nsresult
79 0 : nsFilteredContentIterator::Init(nsIDOMRange* aRange)
80 : {
81 0 : NS_ENSURE_TRUE(mPreIterator, NS_ERROR_FAILURE);
82 0 : NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE);
83 0 : NS_ENSURE_ARG_POINTER(aRange);
84 0 : mIsOutOfRange = false;
85 0 : mDirection = eForward;
86 0 : mCurrentIterator = mPreIterator;
87 :
88 0 : mRange = static_cast<nsRange*>(aRange)->CloneRange();
89 :
90 0 : nsresult rv = mPreIterator->Init(mRange);
91 0 : NS_ENSURE_SUCCESS(rv, rv);
92 0 : return mIterator->Init(mRange);
93 : }
94 :
95 : //------------------------------------------------------------
96 : nsresult
97 0 : nsFilteredContentIterator::SwitchDirections(bool aChangeToForward)
98 : {
99 0 : nsINode *node = mCurrentIterator->GetCurrentNode();
100 :
101 0 : if (aChangeToForward) {
102 0 : mCurrentIterator = mPreIterator;
103 0 : mDirection = eForward;
104 : } else {
105 0 : mCurrentIterator = mIterator;
106 0 : mDirection = eBackward;
107 : }
108 :
109 0 : if (node) {
110 0 : nsresult rv = mCurrentIterator->PositionAt(node);
111 0 : if (NS_FAILED(rv)) {
112 0 : mIsOutOfRange = true;
113 0 : return rv;
114 : }
115 : }
116 0 : return NS_OK;
117 : }
118 :
119 : //------------------------------------------------------------
120 : void
121 0 : nsFilteredContentIterator::First()
122 : {
123 0 : if (!mCurrentIterator) {
124 0 : NS_ERROR("Missing iterator!");
125 :
126 0 : return;
127 : }
128 :
129 : // If we are switching directions then
130 : // we need to switch how we process the nodes
131 0 : if (mDirection != eForward) {
132 0 : mCurrentIterator = mPreIterator;
133 0 : mDirection = eForward;
134 0 : mIsOutOfRange = false;
135 : }
136 :
137 0 : mCurrentIterator->First();
138 :
139 0 : if (mCurrentIterator->IsDone()) {
140 0 : return;
141 : }
142 :
143 0 : nsINode *currentNode = mCurrentIterator->GetCurrentNode();
144 0 : nsCOMPtr<nsIDOMNode> node(do_QueryInterface(currentNode));
145 :
146 : bool didCross;
147 0 : CheckAdvNode(node, didCross, eForward);
148 : }
149 :
150 : //------------------------------------------------------------
151 : void
152 0 : nsFilteredContentIterator::Last()
153 : {
154 0 : if (!mCurrentIterator) {
155 0 : NS_ERROR("Missing iterator!");
156 :
157 0 : return;
158 : }
159 :
160 : // If we are switching directions then
161 : // we need to switch how we process the nodes
162 0 : if (mDirection != eBackward) {
163 0 : mCurrentIterator = mIterator;
164 0 : mDirection = eBackward;
165 0 : mIsOutOfRange = false;
166 : }
167 :
168 0 : mCurrentIterator->Last();
169 :
170 0 : if (mCurrentIterator->IsDone()) {
171 0 : return;
172 : }
173 :
174 0 : nsINode *currentNode = mCurrentIterator->GetCurrentNode();
175 0 : nsCOMPtr<nsIDOMNode> node(do_QueryInterface(currentNode));
176 :
177 : bool didCross;
178 0 : CheckAdvNode(node, didCross, eBackward);
179 : }
180 :
181 : ///////////////////////////////////////////////////////////////////////////
182 : // ContentToParentOffset: returns the content node's parent and offset.
183 : //
184 : static void
185 0 : ContentToParentOffset(nsIContent *aContent, nsIDOMNode **aParent,
186 : int32_t *aOffset)
187 : {
188 0 : if (!aParent || !aOffset)
189 0 : return;
190 :
191 0 : *aParent = nullptr;
192 0 : *aOffset = 0;
193 :
194 0 : if (!aContent)
195 0 : return;
196 :
197 0 : nsIContent* parent = aContent->GetParent();
198 :
199 0 : if (!parent)
200 0 : return;
201 :
202 0 : *aOffset = parent->IndexOf(aContent);
203 :
204 0 : CallQueryInterface(parent, aParent);
205 : }
206 :
207 : ///////////////////////////////////////////////////////////////////////////
208 : // ContentIsInTraversalRange: returns true if content is visited during
209 : // the traversal of the range in the specified mode.
210 : //
211 : static bool
212 0 : ContentIsInTraversalRange(nsIContent *aContent, bool aIsPreMode,
213 : nsIDOMNode *aStartContainer, int32_t aStartOffset,
214 : nsIDOMNode *aEndContainer, int32_t aEndOffset)
215 : {
216 0 : NS_ENSURE_TRUE(aStartContainer && aEndContainer && aContent, false);
217 :
218 0 : nsCOMPtr<nsIDOMNode> parentNode;
219 0 : int32_t indx = 0;
220 :
221 0 : ContentToParentOffset(aContent, getter_AddRefs(parentNode), &indx);
222 :
223 0 : NS_ENSURE_TRUE(parentNode, false);
224 :
225 0 : if (!aIsPreMode)
226 0 : ++indx;
227 :
228 : int32_t startRes =
229 0 : nsContentUtils::ComparePoints(aStartContainer, aStartOffset,
230 0 : parentNode, indx);
231 0 : int32_t endRes = nsContentUtils::ComparePoints(aEndContainer, aEndOffset,
232 0 : parentNode, indx);
233 0 : return (startRes <= 0) && (endRes >= 0);
234 : }
235 :
236 : static bool
237 0 : ContentIsInTraversalRange(nsRange* aRange, nsIDOMNode* aNextNode, bool aIsPreMode)
238 : {
239 0 : nsCOMPtr<nsIContent> content(do_QueryInterface(aNextNode));
240 0 : NS_ENSURE_TRUE(content && aRange, false);
241 :
242 0 : nsCOMPtr<nsIDOMNode> sNode;
243 0 : nsCOMPtr<nsIDOMNode> eNode;
244 : int32_t sOffset;
245 : int32_t eOffset;
246 0 : aRange->GetStartContainer(getter_AddRefs(sNode));
247 0 : aRange->GetStartOffset(&sOffset);
248 0 : aRange->GetEndContainer(getter_AddRefs(eNode));
249 0 : aRange->GetEndOffset(&eOffset);
250 0 : return ContentIsInTraversalRange(content, aIsPreMode, sNode, sOffset, eNode, eOffset);
251 : }
252 :
253 : //------------------------------------------------------------
254 : // Helper function to advance to the next or previous node
255 : nsresult
256 0 : nsFilteredContentIterator::AdvanceNode(nsIDOMNode* aNode, nsIDOMNode*& aNewNode, eDirectionType aDir)
257 : {
258 0 : nsCOMPtr<nsIDOMNode> nextNode;
259 0 : if (aDir == eForward) {
260 0 : aNode->GetNextSibling(getter_AddRefs(nextNode));
261 : } else {
262 0 : aNode->GetPreviousSibling(getter_AddRefs(nextNode));
263 : }
264 :
265 0 : if (nextNode) {
266 : // If we got here, that means we found the nxt/prv node
267 : // make sure it is in our DOMRange
268 0 : bool intersects = ContentIsInTraversalRange(mRange, nextNode, aDir == eForward);
269 0 : if (intersects) {
270 0 : aNewNode = nextNode;
271 0 : NS_ADDREF(aNewNode);
272 0 : return NS_OK;
273 : }
274 : } else {
275 : // The next node was null so we need to walk up the parent(s)
276 0 : nsCOMPtr<nsIDOMNode> parent;
277 0 : aNode->GetParentNode(getter_AddRefs(parent));
278 0 : NS_ASSERTION(parent, "parent can't be nullptr");
279 :
280 : // Make sure the parent is in the DOMRange before going further
281 0 : bool intersects = ContentIsInTraversalRange(mRange, nextNode, aDir == eForward);
282 0 : if (intersects) {
283 : // Now find the nxt/prv node after/before this node
284 0 : nsresult rv = AdvanceNode(parent, aNewNode, aDir);
285 0 : if (NS_SUCCEEDED(rv) && aNewNode) {
286 0 : return NS_OK;
287 : }
288 : }
289 : }
290 :
291 : // if we get here it pretty much means
292 : // we went out of the DOM Range
293 0 : mIsOutOfRange = true;
294 :
295 0 : return NS_ERROR_FAILURE;
296 : }
297 :
298 : //------------------------------------------------------------
299 : // Helper function to see if the next/prev node should be skipped
300 : void
301 0 : nsFilteredContentIterator::CheckAdvNode(nsIDOMNode* aNode, bool& aDidSkip, eDirectionType aDir)
302 : {
303 0 : aDidSkip = false;
304 0 : mIsOutOfRange = false;
305 :
306 0 : if (aNode && mFilter) {
307 0 : nsCOMPtr<nsIDOMNode> currentNode = aNode;
308 : bool skipIt;
309 : while (1) {
310 0 : nsresult rv = mFilter->Skip(aNode, &skipIt);
311 0 : if (NS_SUCCEEDED(rv) && skipIt) {
312 0 : aDidSkip = true;
313 : // Get the next/prev node and then
314 : // see if we should skip that
315 0 : nsCOMPtr<nsIDOMNode> advNode;
316 0 : rv = AdvanceNode(aNode, *getter_AddRefs(advNode), aDir);
317 0 : if (NS_SUCCEEDED(rv) && advNode) {
318 0 : aNode = advNode;
319 : } else {
320 0 : return; // fell out of range
321 : }
322 : } else {
323 0 : if (aNode != currentNode) {
324 0 : nsCOMPtr<nsIContent> content(do_QueryInterface(aNode));
325 0 : mCurrentIterator->PositionAt(content);
326 : }
327 0 : return; // found something
328 : }
329 0 : }
330 : }
331 : }
332 :
333 : void
334 0 : nsFilteredContentIterator::Next()
335 : {
336 0 : if (mIsOutOfRange || !mCurrentIterator) {
337 0 : NS_ASSERTION(mCurrentIterator, "Missing iterator!");
338 :
339 0 : return;
340 : }
341 :
342 : // If we are switching directions then
343 : // we need to switch how we process the nodes
344 0 : if (mDirection != eForward) {
345 0 : nsresult rv = SwitchDirections(true);
346 0 : if (NS_FAILED(rv)) {
347 0 : return;
348 : }
349 : }
350 :
351 0 : mCurrentIterator->Next();
352 :
353 0 : if (mCurrentIterator->IsDone()) {
354 0 : return;
355 : }
356 :
357 : // If we can't get the current node then
358 : // don't check to see if we can skip it
359 0 : nsINode *currentNode = mCurrentIterator->GetCurrentNode();
360 :
361 0 : nsCOMPtr<nsIDOMNode> node(do_QueryInterface(currentNode));
362 0 : CheckAdvNode(node, mDidSkip, eForward);
363 : }
364 :
365 : void
366 0 : nsFilteredContentIterator::Prev()
367 : {
368 0 : if (mIsOutOfRange || !mCurrentIterator) {
369 0 : NS_ASSERTION(mCurrentIterator, "Missing iterator!");
370 :
371 0 : return;
372 : }
373 :
374 : // If we are switching directions then
375 : // we need to switch how we process the nodes
376 0 : if (mDirection != eBackward) {
377 0 : nsresult rv = SwitchDirections(false);
378 0 : if (NS_FAILED(rv)) {
379 0 : return;
380 : }
381 : }
382 :
383 0 : mCurrentIterator->Prev();
384 :
385 0 : if (mCurrentIterator->IsDone()) {
386 0 : return;
387 : }
388 :
389 : // If we can't get the current node then
390 : // don't check to see if we can skip it
391 0 : nsINode *currentNode = mCurrentIterator->GetCurrentNode();
392 :
393 0 : nsCOMPtr<nsIDOMNode> node(do_QueryInterface(currentNode));
394 0 : CheckAdvNode(node, mDidSkip, eBackward);
395 : }
396 :
397 : nsINode *
398 0 : nsFilteredContentIterator::GetCurrentNode()
399 : {
400 0 : if (mIsOutOfRange || !mCurrentIterator) {
401 0 : return nullptr;
402 : }
403 :
404 0 : return mCurrentIterator->GetCurrentNode();
405 : }
406 :
407 : bool
408 0 : nsFilteredContentIterator::IsDone()
409 : {
410 0 : if (mIsOutOfRange || !mCurrentIterator) {
411 0 : return true;
412 : }
413 :
414 0 : return mCurrentIterator->IsDone();
415 : }
416 :
417 : nsresult
418 0 : nsFilteredContentIterator::PositionAt(nsINode* aCurNode)
419 : {
420 0 : NS_ENSURE_TRUE(mCurrentIterator, NS_ERROR_FAILURE);
421 0 : mIsOutOfRange = false;
422 0 : return mCurrentIterator->PositionAt(aCurNode);
423 : }
|