/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "nsFilteredContentIterator.h" #include "nsIContentIterator.h" #include "nsComponentManagerUtils.h" #include "nsIContent.h" #include "nsString.h" #include "nsIEnumerator.h" #include "nsTextServicesDocument.h" #include "nsIDOMNode.h" #include "nsIDOMRange.h" //------------------------------------------------------------ nsFilteredContentIterator::nsFilteredContentIterator(nsITextServicesFilter* aFilter) : mFilter(aFilter), mDidSkip(PR_FALSE), mIsOutOfRange(PR_FALSE), mDirection(eDirNotSet) { mIterator = do_CreateInstance("@mozilla.org/content/post-content-iterator;1"); mPreIterator = do_CreateInstance("@mozilla.org/content/pre-content-iterator;1"); } //------------------------------------------------------------ nsFilteredContentIterator::~nsFilteredContentIterator() { } //------------------------------------------------------------ NS_IMPL_ISUPPORTS1(nsFilteredContentIterator, nsIContentIterator) //------------------------------------------------------------ nsresult nsFilteredContentIterator::Init(nsIContent* aRoot) { NS_ENSURE_TRUE(mPreIterator, NS_ERROR_FAILURE); NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE); mIsOutOfRange = PR_FALSE; mDirection = eForward; mCurrentIterator = mPreIterator; nsresult rv; mRange = do_CreateInstance("@mozilla.org/content/range;1", &rv); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr domRange(do_QueryInterface(mRange)); nsCOMPtr domNode(do_QueryInterface(aRoot)); if (domRange && domNode) { domRange->SelectNode(domNode); } rv = mPreIterator->Init(domRange); NS_ENSURE_SUCCESS(rv, rv); return mIterator->Init(domRange); } //------------------------------------------------------------ nsresult nsFilteredContentIterator::Init(nsIDOMRange* aRange) { NS_ENSURE_TRUE(mPreIterator, NS_ERROR_FAILURE); NS_ENSURE_TRUE(mIterator, NS_ERROR_FAILURE); NS_ENSURE_ARG_POINTER(aRange); mIsOutOfRange = PR_FALSE; mDirection = eForward; mCurrentIterator = mPreIterator; nsCOMPtr domRange; nsresult rv = aRange->CloneRange(getter_AddRefs(domRange)); NS_ENSURE_SUCCESS(rv, rv); mRange = do_QueryInterface(domRange); rv = mPreIterator->Init(domRange); NS_ENSURE_SUCCESS(rv, rv); return mIterator->Init(domRange); } //------------------------------------------------------------ nsresult nsFilteredContentIterator::SwitchDirections(PRPackedBool aChangeToForward) { nsIContent *node = mCurrentIterator->GetCurrentNode(); if (aChangeToForward) { mCurrentIterator = mPreIterator; mDirection = eForward; } else { mCurrentIterator = mIterator; mDirection = eBackward; } if (node) { nsresult rv = mCurrentIterator->PositionAt(node); if (NS_FAILED(rv)) { mIsOutOfRange = PR_TRUE; return rv; } } return NS_OK; } //------------------------------------------------------------ void nsFilteredContentIterator::First() { if (!mCurrentIterator) { NS_ERROR("Missing iterator!"); return; } // If we are switching directions then // we need to switch how we process the nodes if (mDirection != eForward) { mCurrentIterator = mPreIterator; mDirection = eForward; mIsOutOfRange = PR_FALSE; } mCurrentIterator->First(); if (mCurrentIterator->IsDone()) { return; } nsIContent *currentContent = mCurrentIterator->GetCurrentNode(); nsCOMPtr node(do_QueryInterface(currentContent)); PRPackedBool didCross; CheckAdvNode(node, didCross, eForward); } //------------------------------------------------------------ void nsFilteredContentIterator::Last() { if (!mCurrentIterator) { NS_ERROR("Missing iterator!"); return; } // If we are switching directions then // we need to switch how we process the nodes if (mDirection != eBackward) { mCurrentIterator = mIterator; mDirection = eBackward; mIsOutOfRange = PR_FALSE; } mCurrentIterator->Last(); if (mCurrentIterator->IsDone()) { return; } nsIContent *currentContent = mCurrentIterator->GetCurrentNode(); nsCOMPtr node(do_QueryInterface(currentContent)); PRPackedBool didCross; CheckAdvNode(node, didCross, eBackward); } /////////////////////////////////////////////////////////////////////////// // ContentToParentOffset: returns the content node's parent and offset. // static void ContentToParentOffset(nsIContent *aContent, nsIDOMNode **aParent, PRInt32 *aOffset) { if (!aParent || !aOffset) return; *aParent = nsnull; *aOffset = 0; if (!aContent) return; nsIContent* parent = aContent->GetParent(); if (!parent) return; *aOffset = parent->IndexOf(aContent); CallQueryInterface(parent, aParent); } /////////////////////////////////////////////////////////////////////////// // ContentIsInTraversalRange: returns true if content is visited during // the traversal of the range in the specified mode. // static PRBool ContentIsInTraversalRange(nsIContent *aContent, PRBool aIsPreMode, nsIDOMNode *aStartNode, PRInt32 aStartOffset, nsIDOMNode *aEndNode, PRInt32 aEndOffset) { if (!aStartNode || !aEndNode || !aContent) return PR_FALSE; nsCOMPtr parentNode; PRInt32 indx = 0; ContentToParentOffset(aContent, getter_AddRefs(parentNode), &indx); if (!parentNode) return PR_FALSE; if (!aIsPreMode) ++indx; PRInt32 startRes; PRInt32 endRes; nsresult rv = nsTextServicesDocument::ComparePoints(aStartNode, aStartOffset, parentNode, indx, &startRes); if (NS_FAILED(rv)) return PR_FALSE; rv = nsTextServicesDocument::ComparePoints(aEndNode, aEndOffset, parentNode, indx, &endRes); if (NS_FAILED(rv)) return PR_FALSE; return (startRes <= 0) && (endRes >= 0); } static PRBool ContentIsInTraversalRange(nsIDOMNSRange *aRange, nsIDOMNode* aNextNode, PRBool aIsPreMode) { nsCOMPtr content(do_QueryInterface(aNextNode)); nsCOMPtr range(do_QueryInterface(aRange)); if (!content || !range) return PR_FALSE; nsCOMPtr sNode; nsCOMPtr eNode; PRInt32 sOffset; PRInt32 eOffset; range->GetStartContainer(getter_AddRefs(sNode)); range->GetStartOffset(&sOffset); range->GetEndContainer(getter_AddRefs(eNode)); range->GetEndOffset(&eOffset); return ContentIsInTraversalRange(content, aIsPreMode, sNode, sOffset, eNode, eOffset); } //------------------------------------------------------------ // Helper function to advance to the next or previous node nsresult nsFilteredContentIterator::AdvanceNode(nsIDOMNode* aNode, nsIDOMNode*& aNewNode, eDirectionType aDir) { nsCOMPtr nextNode; if (aDir == eForward) { aNode->GetNextSibling(getter_AddRefs(nextNode)); } else { aNode->GetPreviousSibling(getter_AddRefs(nextNode)); } if (nextNode) { // If we got here, that means we found the nxt/prv node // make sure it is in our DOMRange PRBool intersects = ContentIsInTraversalRange(mRange, nextNode, aDir == eForward); if (intersects) { aNewNode = nextNode; NS_ADDREF(aNewNode); return NS_OK; } } else { // The next node was null so we need to walk up the parent(s) nsCOMPtr parent; aNode->GetParentNode(getter_AddRefs(parent)); NS_ASSERTION(parent, "parent can't be NULL"); // Make sure the parent is in the DOMRange before going further PRBool intersects = ContentIsInTraversalRange(mRange, nextNode, aDir == eForward); if (intersects) { // Now find the nxt/prv node after/before this node nsresult rv = AdvanceNode(parent, aNewNode, aDir); if (NS_SUCCEEDED(rv) && aNewNode) { return NS_OK; } } } // if we get here it pretty much means // we went out of the DOM Range mIsOutOfRange = PR_TRUE; return NS_ERROR_FAILURE; } //------------------------------------------------------------ // Helper function to see if the next/prev node should be skipped void nsFilteredContentIterator::CheckAdvNode(nsIDOMNode* aNode, PRPackedBool& aDidSkip, eDirectionType aDir) { aDidSkip = PR_FALSE; mIsOutOfRange = PR_FALSE; if (aNode && mFilter) { nsCOMPtr currentNode = aNode; PRBool skipIt; while (1) { nsresult rv = mFilter->Skip(aNode, &skipIt); if (NS_SUCCEEDED(rv) && skipIt) { aDidSkip = PR_TRUE; // Get the next/prev node and then // see if we should skip that nsCOMPtr advNode; rv = AdvanceNode(aNode, *getter_AddRefs(advNode), aDir); if (NS_SUCCEEDED(rv) && advNode) { aNode = advNode; } else { return; // fell out of range } } else { if (aNode != currentNode) { nsCOMPtr content(do_QueryInterface(aNode)); mCurrentIterator->PositionAt(content); } return; // found something } } } } void nsFilteredContentIterator::Next() { if (mIsOutOfRange || !mCurrentIterator) { NS_ASSERTION(mCurrentIterator, "Missing iterator!"); return; } // If we are switching directions then // we need to switch how we process the nodes if (mDirection != eForward) { nsresult rv = SwitchDirections(PR_TRUE); if (NS_FAILED(rv)) { return; } } mCurrentIterator->Next(); if (mCurrentIterator->IsDone()) { return; } // If we can't get the current node then // don't check to see if we can skip it nsIContent *currentContent = mCurrentIterator->GetCurrentNode(); nsCOMPtr node(do_QueryInterface(currentContent)); CheckAdvNode(node, mDidSkip, eForward); } void nsFilteredContentIterator::Prev() { if (mIsOutOfRange || !mCurrentIterator) { NS_ASSERTION(mCurrentIterator, "Missing iterator!"); return; } // If we are switching directions then // we need to switch how we process the nodes if (mDirection != eBackward) { nsresult rv = SwitchDirections(PR_FALSE); if (NS_FAILED(rv)) { return; } } mCurrentIterator->Prev(); if (mCurrentIterator->IsDone()) { return; } // If we can't get the current node then // don't check to see if we can skip it nsIContent *currentContent = mCurrentIterator->GetCurrentNode(); nsCOMPtr node(do_QueryInterface(currentContent)); CheckAdvNode(node, mDidSkip, eBackward); } nsIContent * nsFilteredContentIterator::GetCurrentNode() { if (mIsOutOfRange || !mCurrentIterator) { return nsnull; } return mCurrentIterator->GetCurrentNode(); } PRBool nsFilteredContentIterator::IsDone() { if (mIsOutOfRange || !mCurrentIterator) { return PR_TRUE; } return mCurrentIterator->IsDone(); } nsresult nsFilteredContentIterator::PositionAt(nsIContent* aCurNode) { NS_ENSURE_TRUE(mCurrentIterator, NS_ERROR_FAILURE); mIsOutOfRange = PR_FALSE; return mCurrentIterator->PositionAt(aCurNode); }