michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: #include "nsCOMPtr.h" michael@0: #include "nsGkAtoms.h" michael@0: michael@0: #include "nsFrameTraversal.h" michael@0: #include "nsFrameList.h" michael@0: #include "nsPlaceholderFrame.h" michael@0: michael@0: michael@0: class nsFrameIterator : public nsIFrameEnumerator michael@0: { michael@0: public: michael@0: typedef nsIFrame::ChildListID ChildListID; michael@0: michael@0: NS_DECL_ISUPPORTS michael@0: michael@0: virtual ~nsFrameIterator() {} michael@0: michael@0: virtual void First() MOZ_OVERRIDE; michael@0: virtual void Next() MOZ_OVERRIDE; michael@0: virtual nsIFrame* CurrentItem() MOZ_OVERRIDE; michael@0: virtual bool IsDone() MOZ_OVERRIDE; michael@0: michael@0: virtual void Last() MOZ_OVERRIDE; michael@0: virtual void Prev() MOZ_OVERRIDE; michael@0: michael@0: nsFrameIterator(nsPresContext* aPresContext, nsIFrame *aStart, michael@0: nsIteratorType aType, bool aLockScroll, bool aFollowOOFs); michael@0: michael@0: protected: michael@0: void setCurrent(nsIFrame *aFrame){mCurrent = aFrame;} michael@0: nsIFrame *getCurrent(){return mCurrent;} michael@0: nsIFrame *getStart(){return mStart;} michael@0: nsIFrame *getLast(){return mLast;} michael@0: void setLast(nsIFrame *aFrame){mLast = aFrame;} michael@0: int8_t getOffEdge(){return mOffEdge;} michael@0: void setOffEdge(int8_t aOffEdge){mOffEdge = aOffEdge;} michael@0: michael@0: /* michael@0: Our own versions of the standard frame tree navigation michael@0: methods, which, if the iterator is following out-of-flows, michael@0: apply the following rules for placeholder frames: michael@0: michael@0: - If a frame HAS a placeholder frame, getting its parent michael@0: gets the placeholder's parent. michael@0: michael@0: - If a frame's first child or next/prev sibling IS a michael@0: placeholder frame, then we instead return the real frame. michael@0: michael@0: - If a frame HAS a placeholder frame, getting its next/prev michael@0: sibling gets the placeholder frame's next/prev sibling. michael@0: michael@0: These are all applied recursively to support multiple levels of michael@0: placeholders. michael@0: */ michael@0: michael@0: nsIFrame* GetParentFrame(nsIFrame* aFrame); michael@0: // like GetParentFrame but returns null once a popup frame is reached michael@0: nsIFrame* GetParentFrameNotPopup(nsIFrame* aFrame); michael@0: michael@0: nsIFrame* GetFirstChild(nsIFrame* aFrame); michael@0: nsIFrame* GetLastChild(nsIFrame* aFrame); michael@0: michael@0: nsIFrame* GetNextSibling(nsIFrame* aFrame); michael@0: nsIFrame* GetPrevSibling(nsIFrame* aFrame); michael@0: michael@0: /* michael@0: These methods are overridden by the bidi visual iterator to have the michael@0: semantics of "get first child in visual order", "get last child in visual michael@0: order", "get next sibling in visual order" and "get previous sibling in visual michael@0: order". michael@0: */ michael@0: michael@0: virtual nsIFrame* GetFirstChildInner(nsIFrame* aFrame); michael@0: virtual nsIFrame* GetLastChildInner(nsIFrame* aFrame); michael@0: michael@0: virtual nsIFrame* GetNextSiblingInner(nsIFrame* aFrame); michael@0: virtual nsIFrame* GetPrevSiblingInner(nsIFrame* aFrame); michael@0: michael@0: nsIFrame* GetPlaceholderFrame(nsIFrame* aFrame); michael@0: bool IsPopupFrame(nsIFrame* aFrame); michael@0: michael@0: nsPresContext* const mPresContext; michael@0: const bool mLockScroll; michael@0: const bool mFollowOOFs; michael@0: const nsIteratorType mType; michael@0: michael@0: private: michael@0: nsIFrame* const mStart; michael@0: nsIFrame* mCurrent; michael@0: nsIFrame* mLast; //the last one that was in current; michael@0: int8_t mOffEdge; //0= no -1 to far prev, 1 to far next; michael@0: }; michael@0: michael@0: michael@0: michael@0: // Bidi visual iterator michael@0: class nsVisualIterator: public nsFrameIterator michael@0: { michael@0: public: michael@0: nsVisualIterator(nsPresContext* aPresContext, nsIFrame *aStart, michael@0: nsIteratorType aType, bool aLockScroll, bool aFollowOOFs) : michael@0: nsFrameIterator(aPresContext, aStart, aType, aLockScroll, aFollowOOFs) {} michael@0: michael@0: protected: michael@0: nsIFrame* GetFirstChildInner(nsIFrame* aFrame) MOZ_OVERRIDE; michael@0: nsIFrame* GetLastChildInner(nsIFrame* aFrame) MOZ_OVERRIDE; michael@0: michael@0: nsIFrame* GetNextSiblingInner(nsIFrame* aFrame) MOZ_OVERRIDE; michael@0: nsIFrame* GetPrevSiblingInner(nsIFrame* aFrame) MOZ_OVERRIDE; michael@0: }; michael@0: michael@0: /************IMPLEMENTATIONS**************/ michael@0: michael@0: nsresult NS_CreateFrameTraversal(nsIFrameTraversal** aResult) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aResult); michael@0: *aResult = nullptr; michael@0: michael@0: nsCOMPtr t(new nsFrameTraversal()); michael@0: michael@0: *aResult = t; michael@0: NS_ADDREF(*aResult); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: NS_NewFrameTraversal(nsIFrameEnumerator **aEnumerator, michael@0: nsPresContext* aPresContext, michael@0: nsIFrame *aStart, michael@0: nsIteratorType aType, michael@0: bool aVisual, michael@0: bool aLockInScrollView, michael@0: bool aFollowOOFs) michael@0: { michael@0: if (!aEnumerator || !aStart) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: if (aFollowOOFs) { michael@0: aStart = nsPlaceholderFrame::GetRealFrameFor(aStart); michael@0: } michael@0: michael@0: nsCOMPtr trav; michael@0: if (aVisual) { michael@0: trav = new nsVisualIterator(aPresContext, aStart, aType, michael@0: aLockInScrollView, aFollowOOFs); michael@0: } else { michael@0: trav = new nsFrameIterator(aPresContext, aStart, aType, michael@0: aLockInScrollView, aFollowOOFs); michael@0: } michael@0: trav.forget(aEnumerator); michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: nsFrameTraversal::nsFrameTraversal() michael@0: { michael@0: } michael@0: michael@0: nsFrameTraversal::~nsFrameTraversal() michael@0: { michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(nsFrameTraversal,nsIFrameTraversal) michael@0: michael@0: NS_IMETHODIMP michael@0: nsFrameTraversal::NewFrameTraversal(nsIFrameEnumerator **aEnumerator, michael@0: nsPresContext* aPresContext, michael@0: nsIFrame *aStart, michael@0: int32_t aType, michael@0: bool aVisual, michael@0: bool aLockInScrollView, michael@0: bool aFollowOOFs) michael@0: { michael@0: return NS_NewFrameTraversal(aEnumerator, aPresContext, aStart, michael@0: static_cast(aType), michael@0: aVisual, aLockInScrollView, aFollowOOFs); michael@0: } michael@0: michael@0: // nsFrameIterator implementation michael@0: michael@0: NS_IMPL_ISUPPORTS(nsFrameIterator, nsIFrameEnumerator) michael@0: michael@0: nsFrameIterator::nsFrameIterator(nsPresContext* aPresContext, nsIFrame *aStart, michael@0: nsIteratorType aType, bool aLockInScrollView, michael@0: bool aFollowOOFs) michael@0: : mPresContext(aPresContext), michael@0: mLockScroll(aLockInScrollView), michael@0: mFollowOOFs(aFollowOOFs), michael@0: mType(aType), michael@0: mStart(aStart), michael@0: mCurrent(aStart), michael@0: mLast(aStart), michael@0: mOffEdge(0) michael@0: { michael@0: MOZ_ASSERT(!aFollowOOFs || aStart->GetType() != nsGkAtoms::placeholderFrame, michael@0: "Caller should have resolved placeholder frame"); michael@0: } michael@0: michael@0: michael@0: michael@0: nsIFrame* michael@0: nsFrameIterator::CurrentItem() michael@0: { michael@0: if (mOffEdge) michael@0: return nullptr; michael@0: michael@0: return mCurrent; michael@0: } michael@0: michael@0: michael@0: michael@0: bool michael@0: nsFrameIterator::IsDone() michael@0: { michael@0: return mOffEdge != 0; michael@0: } michael@0: michael@0: void michael@0: nsFrameIterator::First() michael@0: { michael@0: mCurrent = mStart; michael@0: } michael@0: michael@0: static bool michael@0: IsRootFrame(nsIFrame* aFrame) michael@0: { michael@0: nsIAtom* atom = aFrame->GetType(); michael@0: return (atom == nsGkAtoms::canvasFrame) || michael@0: (atom == nsGkAtoms::rootFrame); michael@0: } michael@0: michael@0: void michael@0: nsFrameIterator::Last() michael@0: { michael@0: nsIFrame* result; michael@0: nsIFrame* parent = getCurrent(); michael@0: // If the current frame is a popup, don't move farther up the tree. michael@0: // Otherwise, get the nearest root frame or popup. michael@0: if (parent->GetType() != nsGkAtoms::menuPopupFrame) { michael@0: while (!IsRootFrame(parent) && (result = GetParentFrameNotPopup(parent))) michael@0: parent = result; michael@0: } michael@0: michael@0: while ((result = GetLastChild(parent))) { michael@0: parent = result; michael@0: } michael@0: michael@0: setCurrent(parent); michael@0: if (!parent) michael@0: setOffEdge(1); michael@0: } michael@0: michael@0: void michael@0: nsFrameIterator::Next() michael@0: { michael@0: // recursive-oid method to get next frame michael@0: nsIFrame *result = nullptr; michael@0: nsIFrame *parent = getCurrent(); michael@0: if (!parent) michael@0: parent = getLast(); michael@0: michael@0: if (mType == eLeaf) { michael@0: // Drill down to first leaf michael@0: while ((result = GetFirstChild(parent))) { michael@0: parent = result; michael@0: } michael@0: } else if (mType == ePreOrder) { michael@0: result = GetFirstChild(parent); michael@0: if (result) michael@0: parent = result; michael@0: } michael@0: michael@0: if (parent != getCurrent()) { michael@0: result = parent; michael@0: } else { michael@0: while (parent) { michael@0: result = GetNextSibling(parent); michael@0: if (result) { michael@0: if (mType != ePreOrder) { michael@0: parent = result; michael@0: while ((result = GetFirstChild(parent))) { michael@0: parent = result; michael@0: } michael@0: result = parent; michael@0: } michael@0: break; michael@0: } michael@0: else { michael@0: result = GetParentFrameNotPopup(parent); michael@0: if (!result || IsRootFrame(result) || michael@0: (mLockScroll && result->GetType() == nsGkAtoms::scrollFrame)) { michael@0: result = nullptr; michael@0: break; michael@0: } michael@0: if (mType == ePostOrder) michael@0: break; michael@0: parent = result; michael@0: } michael@0: } michael@0: } michael@0: michael@0: setCurrent(result); michael@0: if (!result) { michael@0: setOffEdge(1); michael@0: setLast(parent); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsFrameIterator::Prev() michael@0: { michael@0: // recursive-oid method to get prev frame michael@0: nsIFrame *result = nullptr; michael@0: nsIFrame *parent = getCurrent(); michael@0: if (!parent) michael@0: parent = getLast(); michael@0: michael@0: if (mType == eLeaf) { michael@0: // Drill down to last leaf michael@0: while ((result = GetLastChild(parent))) { michael@0: parent = result; michael@0: } michael@0: } else if (mType == ePostOrder) { michael@0: result = GetLastChild(parent); michael@0: if (result) michael@0: parent = result; michael@0: } michael@0: michael@0: if (parent != getCurrent()) { michael@0: result = parent; michael@0: } else { michael@0: while (parent) { michael@0: result = GetPrevSibling(parent); michael@0: if (result) { michael@0: if (mType != ePostOrder) { michael@0: parent = result; michael@0: while ((result = GetLastChild(parent))) { michael@0: parent = result; michael@0: } michael@0: result = parent; michael@0: } michael@0: break; michael@0: } else { michael@0: result = GetParentFrameNotPopup(parent); michael@0: if (!result || IsRootFrame(result) || michael@0: (mLockScroll && result->GetType() == nsGkAtoms::scrollFrame)) { michael@0: result = nullptr; michael@0: break; michael@0: } michael@0: if (mType == ePreOrder) michael@0: break; michael@0: parent = result; michael@0: } michael@0: } michael@0: } michael@0: michael@0: setCurrent(result); michael@0: if (!result) { michael@0: setOffEdge(-1); michael@0: setLast(parent); michael@0: } michael@0: } michael@0: michael@0: nsIFrame* michael@0: nsFrameIterator::GetParentFrame(nsIFrame* aFrame) michael@0: { michael@0: if (mFollowOOFs) michael@0: aFrame = GetPlaceholderFrame(aFrame); michael@0: if (aFrame) michael@0: return aFrame->GetParent(); michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: nsIFrame* michael@0: nsFrameIterator::GetParentFrameNotPopup(nsIFrame* aFrame) michael@0: { michael@0: if (mFollowOOFs) michael@0: aFrame = GetPlaceholderFrame(aFrame); michael@0: if (aFrame) { michael@0: nsIFrame* parent = aFrame->GetParent(); michael@0: if (!IsPopupFrame(parent)) michael@0: return parent; michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: nsIFrame* michael@0: nsFrameIterator::GetFirstChild(nsIFrame* aFrame) michael@0: { michael@0: nsIFrame* result = GetFirstChildInner(aFrame); michael@0: if (mLockScroll && result && result->GetType() == nsGkAtoms::scrollFrame) michael@0: return nullptr; michael@0: if (result && mFollowOOFs) { michael@0: result = nsPlaceholderFrame::GetRealFrameFor(result); michael@0: michael@0: if (IsPopupFrame(result)) michael@0: result = GetNextSibling(result); michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: nsIFrame* michael@0: nsFrameIterator::GetLastChild(nsIFrame* aFrame) michael@0: { michael@0: nsIFrame* result = GetLastChildInner(aFrame); michael@0: if (mLockScroll && result && result->GetType() == nsGkAtoms::scrollFrame) michael@0: return nullptr; michael@0: if (result && mFollowOOFs) { michael@0: result = nsPlaceholderFrame::GetRealFrameFor(result); michael@0: michael@0: if (IsPopupFrame(result)) michael@0: result = GetPrevSibling(result); michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: nsIFrame* michael@0: nsFrameIterator::GetNextSibling(nsIFrame* aFrame) michael@0: { michael@0: nsIFrame* result = nullptr; michael@0: if (mFollowOOFs) michael@0: aFrame = GetPlaceholderFrame(aFrame); michael@0: if (aFrame) { michael@0: result = GetNextSiblingInner(aFrame); michael@0: if (result && mFollowOOFs) michael@0: result = nsPlaceholderFrame::GetRealFrameFor(result); michael@0: } michael@0: michael@0: if (mFollowOOFs && IsPopupFrame(result)) michael@0: result = GetNextSibling(result); michael@0: michael@0: return result; michael@0: } michael@0: michael@0: nsIFrame* michael@0: nsFrameIterator::GetPrevSibling(nsIFrame* aFrame) michael@0: { michael@0: nsIFrame* result = nullptr; michael@0: if (mFollowOOFs) michael@0: aFrame = GetPlaceholderFrame(aFrame); michael@0: if (aFrame) { michael@0: result = GetPrevSiblingInner(aFrame); michael@0: if (result && mFollowOOFs) michael@0: result = nsPlaceholderFrame::GetRealFrameFor(result); michael@0: } michael@0: michael@0: if (mFollowOOFs && IsPopupFrame(result)) michael@0: result = GetPrevSibling(result); michael@0: michael@0: return result; michael@0: } michael@0: michael@0: nsIFrame* michael@0: nsFrameIterator::GetFirstChildInner(nsIFrame* aFrame) { michael@0: return aFrame->GetFirstPrincipalChild(); michael@0: } michael@0: michael@0: nsIFrame* michael@0: nsFrameIterator::GetLastChildInner(nsIFrame* aFrame) { michael@0: return aFrame->PrincipalChildList().LastChild(); michael@0: } michael@0: michael@0: nsIFrame* michael@0: nsFrameIterator::GetNextSiblingInner(nsIFrame* aFrame) { michael@0: return aFrame->GetNextSibling(); michael@0: } michael@0: michael@0: nsIFrame* michael@0: nsFrameIterator::GetPrevSiblingInner(nsIFrame* aFrame) { michael@0: return aFrame->GetPrevSibling(); michael@0: } michael@0: michael@0: michael@0: nsIFrame* michael@0: nsFrameIterator::GetPlaceholderFrame(nsIFrame* aFrame) michael@0: { michael@0: nsIFrame* result = aFrame; michael@0: nsIPresShell *presShell = mPresContext->GetPresShell(); michael@0: if (presShell) { michael@0: nsIFrame* placeholder = presShell->GetPlaceholderFrameFor(aFrame); michael@0: if (placeholder) michael@0: result = placeholder; michael@0: } michael@0: michael@0: if (result != aFrame) michael@0: result = GetPlaceholderFrame(result); michael@0: michael@0: return result; michael@0: } michael@0: michael@0: bool michael@0: nsFrameIterator::IsPopupFrame(nsIFrame* aFrame) michael@0: { michael@0: return (aFrame && michael@0: aFrame->StyleDisplay()->mDisplay == NS_STYLE_DISPLAY_POPUP); michael@0: } michael@0: michael@0: // nsVisualIterator implementation michael@0: michael@0: nsIFrame* michael@0: nsVisualIterator::GetFirstChildInner(nsIFrame* aFrame) { michael@0: return aFrame->PrincipalChildList().GetNextVisualFor(nullptr); michael@0: } michael@0: michael@0: nsIFrame* michael@0: nsVisualIterator::GetLastChildInner(nsIFrame* aFrame) { michael@0: return aFrame->PrincipalChildList().GetPrevVisualFor(nullptr); michael@0: } michael@0: michael@0: nsIFrame* michael@0: nsVisualIterator::GetNextSiblingInner(nsIFrame* aFrame) { michael@0: nsIFrame* parent = GetParentFrame(aFrame); michael@0: if (!parent) michael@0: return nullptr; michael@0: return parent->PrincipalChildList().GetNextVisualFor(aFrame); michael@0: } michael@0: michael@0: nsIFrame* michael@0: nsVisualIterator::GetPrevSiblingInner(nsIFrame* aFrame) { michael@0: nsIFrame* parent = GetParentFrame(aFrame); michael@0: if (!parent) michael@0: return nullptr; michael@0: return parent->PrincipalChildList().GetPrevVisualFor(aFrame); michael@0: }