gecko-dev/layout/base/nsFrameTraversal.cpp

808 строки
19 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: NPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Netscape 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/NPL/
*
* 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 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 NPL, 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 NPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsCOMPtr.h"
#include "nsLayoutAtoms.h"
#include "nsFrameTraversal.h"
#include "nsFrameList.h"
#include "nsPlaceholderFrame.h"
class nsFrameIterator: public nsIBidirectionalEnumerator
{
public:
NS_DECL_ISUPPORTS
NS_IMETHOD First();
NS_IMETHOD Last();
NS_IMETHOD Next()=0;
NS_IMETHOD Prev()=0;
NS_IMETHOD CurrentItem(nsISupports **aItem);
NS_IMETHOD IsDone();//what does this mean??off edge? yes
nsFrameIterator();
protected:
void setCurrent(nsIFrame *aFrame){mCurrent = aFrame;}
nsIFrame *getCurrent(){return mCurrent;}
void setStart(nsIFrame *aFrame){mStart = aFrame;}
nsIFrame *getStart(){return mStart;}
nsIFrame *getLast(){return mLast;}
void setLast(nsIFrame *aFrame){mLast = aFrame;}
PRInt8 getOffEdge(){return mOffEdge;}
void setOffEdge(PRInt8 aOffEdge){mOffEdge = aOffEdge;}
private:
nsIFrame *mStart;
nsIFrame *mCurrent;
nsIFrame *mLast; //the last one that was in current;
PRInt8 mOffEdge; //0= no -1 to far prev, 1 to far next;
};
/*
class nsFastFrameIterator: public nsFrameIterator
{
nsFastFrameIterator(nsIFrame *start);
private :
virtual nsresult Next();
virtual nsresult Prev();
}
*/
class nsLeafIterator: public nsFrameIterator
{
public:
nsLeafIterator(nsIPresContext* aPresContext, nsIFrame *start);
void SetExtensive(PRBool aExtensive) {mExtensive = aExtensive;}
PRBool GetExtensive(){return mExtensive;}
void SetLockInScrollView(PRBool aLockScroll){mLockScroll = aLockScroll;}
private :
NS_IMETHOD Next();
NS_IMETHOD Prev();
nsIPresContext* mPresContext;
PRPackedBool mExtensive;
PRBool mLockScroll;
};
class nsFocusIterator : public nsFrameIterator
{
public:
nsFocusIterator(nsIPresContext* aPresContext, nsIFrame* aStart);
private:
NS_IMETHOD Next();
NS_IMETHOD Prev();
NS_IMETHOD Last();
/*
Our own versions of the standard frame tree navigation
methods, which apply the following rules for placeholder
frames:
- If a frame HAS a placeholder frame, getting its parent
gets the placeholder's parent.
- If a frame's first child or next/prev sibling IS a
placeholder frame, then we instead return the real frame.
- If a frame HAS a placeholder frame, getting its next/prev
sibling gets the placeholder frame's next/prev sibling.
These are all applied recursively to support multiple levels of
placeholders.
*/
nsIFrame* GetParentFrame(nsIFrame* aFrame);
nsIFrame* GetFirstChild(nsIFrame* aFrame);
nsIFrame* GetNextSibling(nsIFrame* aFrame);
nsIFrame* GetPrevSibling(nsIFrame* aFrame);
nsIFrame* GetRealFrame(nsIFrame* aFrame);
nsIFrame* GetPlaceholderFrame(nsIFrame* aFrame);
PRBool IsPopupFrame(nsIFrame* aFrame);
nsIPresContext* mPresContext;
};
#ifdef IBMBIDI // Simon
class nsVisualIterator: public nsFrameIterator
{
public:
nsVisualIterator(nsIPresContext* aPresContext, nsIFrame *start);
private :
NS_IMETHOD Next();
NS_IMETHOD Prev();
nsIPresContext* mPresContext;
};
#endif
/************IMPLEMENTATIONS**************/
nsresult NS_CreateFrameTraversal(nsIFrameTraversal** aResult)
{
NS_ENSURE_ARG_POINTER(aResult);
*aResult = nsnull;
nsCOMPtr<nsIFrameTraversal> t(new nsFrameTraversal());
if (!t)
return NS_ERROR_OUT_OF_MEMORY;
*aResult = t;
NS_ADDREF(*aResult);
return NS_OK;
}
nsresult
NS_NewFrameTraversal(nsIBidirectionalEnumerator **aEnumerator,
nsTraversalType aType,
nsIPresContext* aPresContext,
nsIFrame *aStart,
PRBool aLockInScrollView)
{
if (!aEnumerator || !aStart)
return NS_ERROR_NULL_POINTER;
switch(aType)
{
case LEAF: {
nsLeafIterator *trav = new nsLeafIterator(aPresContext, aStart);
if (!trav)
return NS_ERROR_OUT_OF_MEMORY;
trav->SetLockInScrollView(aLockInScrollView);
*aEnumerator = NS_STATIC_CAST(nsIBidirectionalEnumerator*, trav);
NS_ADDREF(trav);
trav->SetExtensive(PR_FALSE);
}
break;
case EXTENSIVE:{
nsLeafIterator *trav = new nsLeafIterator(aPresContext, aStart);
if (!trav)
return NS_ERROR_OUT_OF_MEMORY;
*aEnumerator = NS_STATIC_CAST(nsIBidirectionalEnumerator*, trav);
NS_ADDREF(trav);
trav->SetExtensive(PR_TRUE);
}
break;
case FOCUS: {
nsFocusIterator *trav = new nsFocusIterator(aPresContext, aStart);
if (!trav)
return NS_ERROR_OUT_OF_MEMORY;
*aEnumerator = NS_STATIC_CAST(nsIBidirectionalEnumerator*, trav);
NS_ADDREF(trav);
}
break;
#ifdef IBMBIDI
case VISUAL:{
nsVisualIterator *trav = new nsVisualIterator(aPresContext, aStart);
if (!trav)
return NS_ERROR_OUT_OF_MEMORY;
*aEnumerator = NS_STATIC_CAST(nsIBidirectionalEnumerator*, trav);
NS_ADDREF(trav);
}
break;
#endif
#if 0
case FASTEST:{
nsFastestTraversal *trav = new nsFastestTraversal(aStart);
if (!trav)
return NS_ERROR_NOMEMORY;
*aEnumerator = NS_STATIC_CAST(nsIBidirectionalEnumerator*, trav);
NS_ADDREF(trav);
}
#endif
default:
return NS_ERROR_NOT_IMPLEMENTED;
break;
}
return NS_OK;
}
nsFrameTraversal::nsFrameTraversal()
{
}
nsFrameTraversal::~nsFrameTraversal()
{
}
NS_IMPL_ISUPPORTS1(nsFrameTraversal,nsIFrameTraversal);
NS_IMETHODIMP
nsFrameTraversal::NewFrameTraversal(nsIBidirectionalEnumerator **aEnumerator,
PRUint32 aType,
nsIPresContext* aPresContext,
nsIFrame *aStart)
{
return NS_NewFrameTraversal(aEnumerator, NS_STATIC_CAST(nsTraversalType,
aType),
aPresContext, aStart,PR_FALSE);
}
/*********nsFrameIterator************/
NS_IMPL_ISUPPORTS2(nsFrameIterator, nsIEnumerator, nsIBidirectionalEnumerator)
nsFrameIterator::nsFrameIterator()
{
mOffEdge = 0;
mLast = nsnull;
mCurrent = nsnull;
mStart = nsnull;
}
NS_IMETHODIMP
nsFrameIterator::CurrentItem(nsISupports **aItem)
{
if (!aItem)
return NS_ERROR_NULL_POINTER;
*aItem = mCurrent;
if (mOffEdge)
return NS_ENUMERATOR_FALSE;
return NS_OK;
}
NS_IMETHODIMP
nsFrameIterator::IsDone()//what does this mean??off edge? yes
{
if (mOffEdge != 0)
return NS_OK;
return NS_ENUMERATOR_FALSE;
}
NS_IMETHODIMP
nsFrameIterator::First()
{
mCurrent = mStart;
return NS_OK;
}
NS_IMETHODIMP
nsFrameIterator::Last()
{
return NS_ERROR_FAILURE;
}
/*********LEAFITERATOR**********/
nsLeafIterator::nsLeafIterator(nsIPresContext* aPresContext, nsIFrame *aStart)
: mPresContext(aPresContext)
{
setStart(aStart);
setCurrent(aStart);
setLast(aStart);
SetLockInScrollView(PR_FALSE);
}
static PRBool
IsRootFrame(nsIFrame* aFrame)
{
nsCOMPtr<nsIAtom>atom;
aFrame->GetFrameType(getter_AddRefs(atom));
return (atom.get() == nsLayoutAtoms::canvasFrame) ||
(atom.get() == nsLayoutAtoms::rootFrame);
}
NS_IMETHODIMP
nsLeafIterator::Next()
{
//recursive-oid method to get next frame
nsIFrame *result = nsnull;
nsIFrame *parent = getCurrent();
if (!parent)
parent = getLast();
if (!mExtensive)
{
while(NS_SUCCEEDED(parent->FirstChild(mPresContext, nsnull,&result)) && result)
{
parent = result;
}
}
if (parent != getCurrent())
{
result = parent;
}
else {
while(parent && !IsRootFrame(parent)) {
result = parent->GetNextSibling();
if (result) {
parent = result;
while(NS_SUCCEEDED(parent->FirstChild(mPresContext, nsnull,&result)) && result)
{
parent = result;
}
result = parent;
break;
}
else
{
result = parent->GetParent();
if (!result || IsRootFrame(result)) {
result = nsnull;
break;
}
else
{
parent = result;
// check if FrameType of result is TextInputFrame
if (mLockScroll) //lock the traversal when we hit a scroll frame
{
nsCOMPtr<nsIAtom> atom;
nsresult res = result->GetFrameType(getter_AddRefs(atom));
if ( NS_SUCCEEDED(res) && atom ) {
if ( atom.get() == nsLayoutAtoms::scrollFrame ) return NS_ERROR_FAILURE;
}
}
if (mExtensive)
break;
}
}
}
}
setCurrent(result);
if (!result)
setOffEdge(1);
return NS_OK;
}
NS_IMETHODIMP
nsLeafIterator::Prev()
{
//recursive-oid method to get prev frame
nsIFrame *result;
nsIFrame *parent = getCurrent();
if (!parent)
parent = getLast();
while (parent){
nsIFrame *grandParent = parent->GetParent();
if (grandParent)
{
// check if FrameType of grandParent is TextInputFrame
if (mLockScroll) //lock the traversal when we hit a scroll frame
{
nsCOMPtr<nsIAtom> atom;
nsresult res = grandParent->GetFrameType(getter_AddRefs(atom));
if ( NS_SUCCEEDED(res) && atom )
{
#ifdef DEBUG_skamio
nsAutoString aString;
res = atom->ToString(aString);
if ( NS_SUCCEEDED(res) ) {
printf("%s:%d\n", __FILE__, __LINE__);
printf("FrameType: %s\n", NS_ConvertUCS2toUTF8(aString).get());
}
#endif
if ( atom.get() == nsLayoutAtoms::scrollFrame )
return NS_ERROR_FAILURE;
}
}
if (NS_SUCCEEDED(grandParent->FirstChild(mPresContext, nsnull,&result)))
{
nsFrameList list(result);
result = list.GetPrevSiblingFor(parent);
if (result)
{
parent = result;
while(NS_SUCCEEDED(parent->FirstChild(mPresContext, nsnull,&result)) && result)
{
parent = result;
while ((result = parent->GetNextSibling()) != nsnull)
{
parent = result;
}
}
result = parent;
break;
}
else if (!(result = parent->GetParent()))
{
result = nsnull;
break;
}
else
{
parent = result;
if (mExtensive)
break;
}
}
}
else
{
setLast(parent);
result = nsnull;
break;
}
}
setCurrent(result);
if (!result)
setOffEdge(-1);
return NS_OK;
}
nsFocusIterator::nsFocusIterator(nsIPresContext* aPresContext, nsIFrame* aStart)
: mPresContext(aPresContext)
{
nsIFrame* start = aStart;
if (aStart)
start = GetRealFrame(aStart);
setStart(start);
setCurrent(start);
setLast(start);
}
nsIFrame*
nsFocusIterator::GetPlaceholderFrame(nsIFrame* aFrame)
{
nsIFrame* result = aFrame;
nsCOMPtr<nsIPresShell> presShell;
mPresContext->GetShell(getter_AddRefs(presShell));
if (presShell) {
nsIFrame* placeholder = 0;
presShell->GetPlaceholderFrameFor(aFrame, &placeholder);
if (placeholder)
result = placeholder;
}
if (result != aFrame)
result = GetPlaceholderFrame(result);
return result;
}
nsIFrame*
nsFocusIterator::GetRealFrame(nsIFrame* aFrame)
{
nsIFrame* result = aFrame;
// See if it's a placeholder frame for a floater.
if (aFrame) {
nsCOMPtr<nsIAtom> frameType;
aFrame->GetFrameType(getter_AddRefs(frameType));
PRBool isPlaceholder = (nsLayoutAtoms::placeholderFrame == frameType.get());
if (isPlaceholder) {
// Get the out-of-flow frame that the placeholder points to.
// This is the real floater that we should examine.
result = NS_STATIC_CAST(nsPlaceholderFrame*,aFrame)->GetOutOfFlowFrame();
NS_ASSERTION(result, "No out of flow frame found for placeholder!\n");
}
if (result != aFrame)
result = GetRealFrame(result);
}
return result;
}
PRBool
nsFocusIterator::IsPopupFrame(nsIFrame* aFrame)
{
return (aFrame->GetStyleDisplay()->mDisplay == NS_STYLE_DISPLAY_POPUP);
}
nsIFrame*
nsFocusIterator::GetParentFrame(nsIFrame* aFrame)
{
nsIFrame* placeholder = GetPlaceholderFrame(aFrame);
if (placeholder)
return placeholder->GetParent();
return nsnull;
}
nsIFrame*
nsFocusIterator::GetFirstChild(nsIFrame* aFrame)
{
nsIFrame* result = 0;
aFrame->FirstChild(mPresContext, nsnull, &result);
if (result)
result = GetRealFrame(result);
if (result && IsPopupFrame(result))
result = GetNextSibling(result);
return result;
}
nsIFrame*
nsFocusIterator::GetNextSibling(nsIFrame* aFrame)
{
nsIFrame* result = nsnull;
nsIFrame* placeholder = GetPlaceholderFrame(aFrame);
if (placeholder) {
result = placeholder->GetNextSibling();
if (result)
result = GetRealFrame(result);
}
if (result && IsPopupFrame(result))
result = GetNextSibling(result);
return result;
}
nsIFrame*
nsFocusIterator::GetPrevSibling(nsIFrame* aFrame)
{
nsIFrame* result = 0;
nsIFrame* placeholder = GetPlaceholderFrame(aFrame);
if (placeholder) {
nsIFrame* parent = GetParentFrame(placeholder);
if (parent) {
nsIFrame* child = 0;
parent->FirstChild(mPresContext, nsnull, &child);
nsFrameList list(child);
result = list.GetPrevSiblingFor(placeholder);
result = GetRealFrame(result);
}
}
if (result && IsPopupFrame(result))
result = GetPrevSibling(result);
return result;
}
NS_IMETHODIMP
nsFocusIterator::Next()
{
nsIFrame* result = 0;
nsIFrame* parent = getCurrent();
if (!parent)
parent = getLast();
if ((result = GetFirstChild(parent)))
parent = result;
result = parent;
if (result == getCurrent()) {
while (result && !IsRootFrame(result)) {
if ((parent = GetNextSibling(result))) {
result = parent;
break;
} else {
parent = result;
result = GetParentFrame(parent);
}
}
if (!result || IsRootFrame(result)) {
result = 0;
setLast(parent);
}
}
setCurrent(result);
if (!result)
setOffEdge(1);
return NS_OK;
}
NS_IMETHODIMP
nsFocusIterator::Prev()
{
nsIFrame *result = 0;
nsIFrame *parent = getCurrent();
if (!parent)
parent = getLast();
if (parent) {
if ((result = GetPrevSibling(parent))) {
parent = result;
while ((result = GetFirstChild(parent))) {
parent = result;
while ((result = GetNextSibling(parent)))
parent = result;
}
result = parent;
} else if (!(result = GetParentFrame(parent))) {
result = 0;
setLast(parent);
}
}
setCurrent(result);
if (!result)
setOffEdge(-1);
return NS_OK;
}
NS_IMETHODIMP
nsFocusIterator::Last()
{
nsIFrame* result;
nsIFrame* parent = getCurrent();
while (!IsRootFrame(parent) && (result = GetParentFrame(parent)))
parent = result;
while ((result = GetFirstChild(parent))) {
parent = result;
while ((result = GetNextSibling(parent)))
parent = result;
}
setCurrent(parent);
if (!parent)
setOffEdge(1);
return NS_OK;
}
#ifdef IBMBIDI
/*********VISUALITERATOR**********/
nsVisualIterator::nsVisualIterator(nsIPresContext* aPresContext, nsIFrame *aStart)
: mPresContext(aPresContext)
{
setStart(aStart);
setCurrent(aStart);
setLast(aStart);
}
NS_IMETHODIMP
nsVisualIterator::Next()
{
//recursive-oid method to get next frame
nsIFrame *result = nsnull;
nsIFrame *parent = getCurrent();
if (!parent)
parent = getLast();
while(NS_SUCCEEDED(parent->FirstChild(mPresContext, nsnull,&result)) && result)
{
parent = result;
}
if (parent != getCurrent())
{
result = parent;
}
else {
while(parent && !IsRootFrame(parent)) {
nsIFrame *grandParent = parent->GetParent();
if (grandParent &&
NS_SUCCEEDED(grandParent->FirstChild(mPresContext, nsnull,&result))){
nsFrameList list(result);
result = list.GetNextVisualFor(parent);
if (result){
parent = result;
while(NS_SUCCEEDED(parent->FirstChild(mPresContext, nsnull,&result)) && result) {
parent = result;
}
result = parent;
break;
}
else if (!(result = parent->GetParent()) || IsRootFrame(result)) {
result = nsnull;
break;
}
else
{
parent = result;
}
}
else{
setLast(parent);
result = nsnull;
break;
}
}
}
setCurrent(result);
if (!result)
setOffEdge(-1);
return NS_OK;
}
NS_IMETHODIMP
nsVisualIterator::Prev()
{
//recursive-oid method to get prev frame
nsIFrame *result;
nsIFrame *parent = getCurrent();
if (!parent)
parent = getLast();
while(parent){
nsIFrame *grandParent = parent->GetParent();
if (grandParent &&
NS_SUCCEEDED(grandParent->FirstChild(mPresContext, nsnull,&result))){
nsFrameList list(result);
result = list.GetPrevVisualFor(parent);
if (result){
parent = result;
while(NS_SUCCEEDED(parent->FirstChild(mPresContext, nsnull,&result)) && result){
parent = result;
while ((result = parent->GetNextSibling()) != nsnull) {
parent = result;
}
}
result = parent;
break;
}
else if (!(result = parent->GetParent())) {
break;
}
else
{
parent = result;
}
}
else{
setLast(parent);
result = nsnull;
break;
}
}
setCurrent(result);
if (!result)
setOffEdge(-1);
return NS_OK;
}
#endif