/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * 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 Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): */ #include "nsCOMPtr.h" #include "nsHTMLParts.h" #include "nsIPresContext.h" #include "nsIStyleContext.h" #include "nsIReflowCommand.h" #include "nsIDeviceContext.h" #include "nsPageFrame.h" #include "nsViewsCID.h" #include "nsIServiceManager.h" #include "nsIView.h" #include "nsIViewManager.h" #include "nsHTMLContainerFrame.h" #include "nsHTMLIIDs.h" #include "nsCSSRendering.h" #include "nsIScrollableView.h" #include "nsWidgetsCID.h" #include "nsGfxScrollFrame.h" #include "nsLayoutAtoms.h" #include "nsIXMLContent.h" #include "nsXULAtoms.h" #include "nsHTMLAtoms.h" #include "nsINameSpaceManager.h" #include "nsISupportsArray.h" #include "nsIDocument.h" #include "nsIFontMetrics.h" #include "nsIDocumentObserver.h" #include "nsIDocument.h" #include "nsIScrollPositionListener.h" //#include "nsBoxFrame.h" #include "nsIElementFactory.h" #include "nsBoxLayoutState.h" #include "nsINodeInfo.h" static NS_DEFINE_IID(kWidgetCID, NS_CHILD_CID); static NS_DEFINE_IID(kScrollingViewCID, NS_SCROLLING_VIEW_CID); static NS_DEFINE_IID(kViewCID, NS_VIEW_CID); static NS_DEFINE_IID(kIViewIID, NS_IVIEW_IID); static NS_DEFINE_IID(kScrollViewIID, NS_ISCROLLABLEVIEW_IID); static NS_DEFINE_IID(kIAnonymousContentCreatorIID, NS_IANONYMOUS_CONTENT_CREATOR_IID); static NS_DEFINE_IID(kIScrollableFrameIID, NS_ISCROLLABLE_FRAME_IID); //---------------------------------------------------------------------- class nsGfxScrollFrameInner : public nsIDocumentObserver, public nsIScrollPositionListener { NS_DECL_ISUPPORTS public: nsGfxScrollFrameInner(nsGfxScrollFrame* aOuter); virtual ~nsGfxScrollFrameInner(); // nsIScrollPositionListener NS_IMETHOD ScrollPositionWillChange(nsIScrollableView* aScrollable, nscoord aX, nscoord aY); NS_IMETHOD ScrollPositionDidChange(nsIScrollableView* aScrollable, nscoord aX, nscoord aY); // nsIDocumentObserver NS_IMETHOD BeginUpdate(nsIDocument *aDocument) { return NS_OK; } NS_IMETHOD EndUpdate(nsIDocument *aDocument) { return NS_OK; } NS_IMETHOD BeginLoad(nsIDocument *aDocument) { return NS_OK; } NS_IMETHOD EndLoad(nsIDocument *aDocument) { return NS_OK; } NS_IMETHOD BeginReflow(nsIDocument *aDocument, nsIPresShell* aShell) { return NS_OK; } NS_IMETHOD EndReflow(nsIDocument *aDocument, nsIPresShell* aShell) { return NS_OK; } NS_IMETHOD ContentChanged(nsIDocument* aDoc, nsIContent* aContent, nsISupports* aSubContent) { return NS_OK; } NS_IMETHOD ContentStatesChanged(nsIDocument* aDocument, nsIContent* aContent1, nsIContent* aContent2) { return NS_OK; } NS_IMETHOD AttributeChanged(nsIDocument *aDocument, nsIContent* aContent, PRInt32 aNameSpaceID, nsIAtom* aAttribute, PRInt32 aHint); NS_IMETHOD ContentAppended(nsIDocument *aDocument, nsIContent* aContainer, PRInt32 aNewIndexInContainer) { return NS_OK; } NS_IMETHOD ContentInserted(nsIDocument *aDocument, nsIContent* aContainer, nsIContent* aChild, PRInt32 aIndexInContainer) { return NS_OK; } NS_IMETHOD ContentReplaced(nsIDocument *aDocument, nsIContent* aContainer, nsIContent* aOldChild, nsIContent* aNewChild, PRInt32 aIndexInContainer) { return NS_OK; } NS_IMETHOD ContentRemoved(nsIDocument *aDocument, nsIContent* aContainer, nsIContent* aChild, PRInt32 aIndexInContainer) { return NS_OK; } NS_IMETHOD StyleSheetAdded(nsIDocument *aDocument, nsIStyleSheet* aStyleSheet) { return NS_OK; } NS_IMETHOD StyleSheetRemoved(nsIDocument *aDocument, nsIStyleSheet* aStyleSheet) { return NS_OK; } NS_IMETHOD StyleSheetDisabledStateChanged(nsIDocument *aDocument, nsIStyleSheet* aStyleSheet, PRBool aDisabled) { return NS_OK; } NS_IMETHOD StyleRuleChanged(nsIDocument *aDocument, nsIStyleSheet* aStyleSheet, nsIStyleRule* aStyleRule, PRInt32 aHint) { return NS_OK; } NS_IMETHOD StyleRuleAdded(nsIDocument *aDocument, nsIStyleSheet* aStyleSheet, nsIStyleRule* aStyleRule) { return NS_OK; } NS_IMETHOD StyleRuleRemoved(nsIDocument *aDocument, nsIStyleSheet* aStyleSheet, nsIStyleRule* aStyleRule) { return NS_OK; } NS_IMETHOD DocumentWillBeDestroyed(nsIDocument *aDocument) { mDocument = nsnull; return NS_OK; } PRBool SetAttribute(nsIBox* aBox, nsIAtom* aAtom, nscoord aSize, PRBool aReflow=PR_TRUE); PRInt32 GetIntegerAttribute(nsIBox* aFrame, nsIAtom* atom, PRInt32 defaultValue); nsresult Layout(nsBoxLayoutState& aState); nsresult LayoutBox(nsBoxLayoutState& aState, nsIBox* aBox, const nsRect& aRect); PRBool AddRemoveScrollbar (PRBool& aHasScrollbar, nscoord& aXY, nscoord& aSize, nscoord aSbSize, PRBool aOnRightOrBottom, PRBool aAdd); PRBool AddRemoveScrollbar(nsBoxLayoutState& aState, nsRect& aScrollAreaSize, PRBool aOnTop, PRBool aHorizontal, PRBool aAdd); PRBool AddHorizontalScrollbar (nsBoxLayoutState& aState, nsRect& aScrollAreaSize, PRBool aOnBottom); PRBool AddVerticalScrollbar (nsBoxLayoutState& aState, nsRect& aScrollAreaSize, PRBool aOnRight); PRBool RemoveHorizontalScrollbar(nsBoxLayoutState& aState, nsRect& aScrollAreaSize, PRBool aOnBottom); PRBool RemoveVerticalScrollbar (nsBoxLayoutState& aState, nsRect& aScrollAreaSize, PRBool aOnRight); nsIScrollableView* GetScrollableView(nsIPresContext* aPresContext); void GetScrolledContentSize(nsSize& aSize); void ScrollbarChanged(nsIPresContext* aPresContext, nscoord aX, nscoord aY); void SetScrollbarVisibility(nsIBox* aScrollbar, PRBool aVisible); nsIBox* mHScrollbarBox; nsIBox* mVScrollbarBox; nsIBox* mScrollAreaBox; nscoord mOnePixel; nsCOMPtr mDocument; nsGfxScrollFrame* mOuter; nsIScrollableView* mScrollableView; nsSize mMaxElementSize; PRBool mNeverHasVerticalScrollbar; PRBool mNeverHasHorizontalScrollbar; PRBool mHasVerticalScrollbar; PRBool mHasHorizontalScrollbar; PRBool mFirstPass; PRBool mIsRoot; PRBool mNeverReflowed; }; NS_IMPL_ISUPPORTS2(nsGfxScrollFrameInner, nsIDocumentObserver, nsIScrollPositionListener) nsresult NS_NewGfxScrollFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame, nsIDocument* aDocument, PRBool aIsRoot) { NS_PRECONDITION(aNewFrame, "null OUT ptr"); if (nsnull == aNewFrame) { return NS_ERROR_NULL_POINTER; } nsGfxScrollFrame* it = new (aPresShell) nsGfxScrollFrame(aPresShell, aDocument, aIsRoot); if (nsnull == it) { return NS_ERROR_OUT_OF_MEMORY; } *aNewFrame = it; return NS_OK; } nsGfxScrollFrame::nsGfxScrollFrame(nsIPresShell* aShell, nsIDocument* aDocument, PRBool aIsRoot):nsBoxFrame(aShell, aIsRoot) { mInner = new nsGfxScrollFrameInner(this); mInner->AddRef(); mInner->mDocument = aDocument; mPresContext = nsnull; mInner->mIsRoot = PR_FALSE; mInner->mNeverReflowed = PR_TRUE; SetLayoutManager(nsnull); } nsGfxScrollFrame::~nsGfxScrollFrame() { mInner->mOuter = nsnull; mInner->Release(); mPresContext = nsnull; } /** * Set the view that we are scrolling within the scrolling view. */ NS_IMETHODIMP nsGfxScrollFrame::SetScrolledFrame(nsIPresContext* aPresContext, nsIFrame *aScrolledFrame) { NS_ERROR("Not implemented!"); /* mFrames.DestroyFrame(aPresContext, mInner->mScrollAreaBox); mInner->mScrollAreaBox = aScrolledFrame; mFrames.InsertFrame(nsnull, nsnull, mInner->mScrollAreaBox); */ return NS_OK; } /** * Get the view that we are scrolling within the scrolling view. * @result child view */ NS_IMETHODIMP nsGfxScrollFrame::GetScrolledFrame(nsIPresContext* aPresContext, nsIFrame *&aScrolledFrame) const { nsIBox* child = nsnull; mInner->mScrollAreaBox->GetChildBox(&child); child->GetFrame(&aScrolledFrame); return NS_OK; } /** * Gets the size of the area that lies inside the scrollbars but clips the scrolled frame */ NS_IMETHODIMP nsGfxScrollFrame::GetClipSize(nsIPresContext* aPresContext, nscoord *aWidth, nscoord *aHeight) const { nsRect rect; mInner->mScrollAreaBox->GetBounds(rect); *aWidth = rect.width; *aHeight = rect.height; return NS_OK; } /** * Get information about whether the vertical and horizontal scrollbars * are currently visible */ NS_IMETHODIMP nsGfxScrollFrame::GetScrollbarVisibility(nsIPresContext* aPresContext, PRBool *aVerticalVisible, PRBool *aHorizontalVisible) const { *aVerticalVisible = mInner->mHasVerticalScrollbar; *aHorizontalVisible = mInner->mHasHorizontalScrollbar; return NS_OK; } NS_IMETHODIMP nsGfxScrollFrame::GetScrollPosition(nsIPresContext* aContext, nscoord &aX, nscoord& aY) const { nsIScrollableView* s = mInner->GetScrollableView(aContext); return s->GetScrollPosition(aX, aY); } NS_IMETHODIMP nsGfxScrollFrame::ScrollTo(nsIPresContext* aContext, nscoord aX, nscoord aY, PRUint32 aFlags) { nsIScrollableView* s = mInner->GetScrollableView(aContext); return s->ScrollTo(aX, aY, aFlags); } /** * Query whether scroll bars should be displayed all the time, never or * only when necessary. * @return current scrollbar selection */ NS_IMETHODIMP nsGfxScrollFrame::GetScrollPreference(nsScrollPref* aScrollPreference) const { const nsStyleDisplay* styleDisplay = nsnull; GetStyleData(eStyleStruct_Display, (const nsStyleStruct*&)styleDisplay); switch (styleDisplay->mOverflow) { case NS_STYLE_OVERFLOW_SCROLL: *aScrollPreference = AlwaysScroll; break; case NS_STYLE_OVERFLOW_SCROLLBARS_HORIZONTAL: *aScrollPreference = AlwaysScrollHorizontal; break; case NS_STYLE_OVERFLOW_SCROLLBARS_VERTICAL: *aScrollPreference = AlwaysScrollVertical; break; case NS_STYLE_OVERFLOW_AUTO: *aScrollPreference = Auto; break; default: *aScrollPreference = NeverScroll; } return NS_OK; } /** * Gets the size of the area that lies inside the scrollbars but clips the scrolled frame */ NS_IMETHODIMP nsGfxScrollFrame::GetScrollbarSizes(nsIPresContext* aPresContext, nscoord *aVbarWidth, nscoord *aHbarHeight) const { nsBoxLayoutState state(aPresContext); nsSize hs; mInner->mHScrollbarBox->GetPrefSize(state, hs); *aHbarHeight = hs.height; nsSize vs; mInner->mVScrollbarBox->GetPrefSize(state, vs); *aVbarWidth = vs.width; return NS_OK; } NS_IMETHODIMP nsGfxScrollFrame::SetScrollbarVisibility(nsIPresContext* aPresContext, PRBool aVerticalVisible, PRBool aHorizontalVisible) { mInner->mNeverHasVerticalScrollbar = !aVerticalVisible; mInner->mNeverHasHorizontalScrollbar = !aHorizontalVisible; return NS_OK; } nsresult NS_CreateAnonymousNode(nsIContent* aParent, nsIAtom* aTag, PRInt32 aNameSpaceId, nsCOMPtr& aNewNode); NS_IMETHODIMP nsGfxScrollFrame::CreateAnonymousContent(nsIPresContext* aPresContext, nsISupportsArray& aAnonymousChildren) { // create horzontal scrollbar nsresult rv; NS_WITH_SERVICE(nsIElementFactory, elementFactory, NS_ELEMENT_FACTORY_PROGID_PREFIX "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", &rv); if (!elementFactory) return NS_ERROR_FAILURE; nsCOMPtr nodeInfoManager; mInner->mDocument->GetNodeInfoManager(*getter_AddRefs(nodeInfoManager)); NS_ENSURE_TRUE(nodeInfoManager, NS_ERROR_FAILURE); nsCOMPtr nodeInfo; nodeInfoManager->GetNodeInfo(NS_ConvertASCIItoUCS2("scrollbar"), nsString(), NS_ConvertASCIItoUCS2("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"), *getter_AddRefs(nodeInfo)); nsCOMPtr content; elementFactory->CreateInstanceByTag(nodeInfo, getter_AddRefs(content)); content->SetAttribute(kNameSpaceID_None, nsHTMLAtoms::align, NS_ConvertToString("horizontal"), PR_FALSE); content->SetAttribute(kNameSpaceID_None, nsXULAtoms::collapsed, NS_ConvertToString("true"), PR_FALSE); aAnonymousChildren.AppendElement(content); // create vertical scrollbar content = nsnull; elementFactory->CreateInstanceByTag(nodeInfo, getter_AddRefs(content)); content->SetAttribute(kNameSpaceID_None, nsHTMLAtoms::align, NS_ConvertToString("vertical"), PR_FALSE); content->SetAttribute(kNameSpaceID_None, nsXULAtoms::collapsed, NS_ConvertToString("true"), PR_FALSE); aAnonymousChildren.AppendElement(content); // XXX For GFX never have scrollbars // mScrollableView->SetScrollPreference(nsScrollPreference_kNeverScroll); return NS_OK; } NS_IMETHODIMP nsGfxScrollFrame::Destroy(nsIPresContext* aPresContext) { return nsBoxFrame::Destroy(aPresContext); } NS_IMETHODIMP nsGfxScrollFrame::Init(nsIPresContext* aPresContext, nsIContent* aContent, nsIFrame* aParent, nsIStyleContext* aStyleContext, nsIFrame* aPrevInFlow) { mPresContext = aPresContext; nsresult rv = nsBoxFrame::Init(aPresContext, aContent, aParent, aStyleContext, aPrevInFlow); mInner->mDocument->AddObserver(mInner); return rv; } NS_IMETHODIMP nsGfxScrollFrame::SetInitialChildList(nsIPresContext* aPresContext, nsIAtom* aListName, nsIFrame* aChildList) { nsresult rv = nsBoxFrame::SetInitialChildList(aPresContext, aListName, aChildList); // get scroll area GetChildBox(&mInner->mScrollAreaBox); // horizontal scrollbar mInner->mScrollAreaBox->GetNextBox(&mInner->mHScrollbarBox); // vertical scrollbar mInner->mHScrollbarBox->GetNextBox(&mInner->mVScrollbarBox); // listen for scroll events. mInner->GetScrollableView(aPresContext)->AddScrollPositionListener(mInner); return rv; } NS_IMETHODIMP nsGfxScrollFrame::AppendFrames(nsIPresContext* aPresContext, nsIPresShell& aPresShell, nsIAtom* aListName, nsIFrame* aFrameList) { nsIFrame* frame; mInner->mScrollAreaBox->GetFrame(&frame); return frame->AppendFrames(aPresContext, aPresShell, aListName, aFrameList); } NS_IMETHODIMP nsGfxScrollFrame::InsertFrames(nsIPresContext* aPresContext, nsIPresShell& aPresShell, nsIAtom* aListName, nsIFrame* aPrevFrame, nsIFrame* aFrameList) { nsIFrame* frame; mInner->mScrollAreaBox->GetFrame(&frame); return frame->InsertFrames(aPresContext, aPresShell, aListName, aPrevFrame, aFrameList); } NS_IMETHODIMP nsGfxScrollFrame::RemoveFrame(nsIPresContext* aPresContext, nsIPresShell& aPresShell, nsIAtom* aListName, nsIFrame* aOldFrame) { nsIFrame* frame; mInner->mScrollAreaBox->GetFrame(&frame); return frame->RemoveFrame (aPresContext, aPresShell, aListName, aOldFrame); } NS_IMETHODIMP nsGfxScrollFrame::ReplaceFrame(nsIPresContext* aPresContext, nsIPresShell& aPresShell, nsIAtom* aListName, nsIFrame* aOldFrame, nsIFrame* aNewFrame) { nsIFrame* frame; mInner->mScrollAreaBox->GetFrame(&frame); return frame->ReplaceFrame (aPresContext, aPresShell, aListName, aOldFrame, aNewFrame); } NS_IMETHODIMP nsGfxScrollFrame::GetPadding(nsMargin& aMargin) { aMargin.SizeTo(0,0,0,0); return NS_OK; } NS_IMETHODIMP nsGfxScrollFrame::Paint(nsIPresContext* aPresContext, nsIRenderingContext& aRenderingContext, const nsRect& aDirtyRect, nsFramePaintLayer aWhichLayer) { // Paint our children return nsBoxFrame::Paint(aPresContext, aRenderingContext, aDirtyRect, aWhichLayer); } NS_IMETHODIMP nsGfxScrollFrame::GetContentAndOffsetsFromPoint(nsIPresContext* aCX, const nsPoint& aPoint, nsIContent ** aNewContent, PRInt32& aContentOffset, PRInt32& aContentOffsetEnd, PRBool& aBeginFrameContent) { if (! mInner) return NS_ERROR_NULL_POINTER; nsIFrame* frame = nsnull; mInner->mScrollAreaBox->GetFrame(&frame); return frame->GetContentAndOffsetsFromPoint(aCX, aPoint, aNewContent, aContentOffset, aContentOffsetEnd, aBeginFrameContent); } PRIntn nsGfxScrollFrame::GetSkipSides() const { return 0; } NS_IMETHODIMP nsGfxScrollFrame::GetFrameType(nsIAtom** aType) const { NS_PRECONDITION(nsnull != aType, "null OUT parameter pointer"); *aType = nsLayoutAtoms::scrollFrame; NS_ADDREF(*aType); return NS_OK; } NS_IMETHODIMP nsGfxScrollFrame::GetAscent(nsBoxLayoutState& aState, nscoord& aAscent) { aAscent = 0; nsresult rv = mInner->mScrollAreaBox->GetAscent(aState, aAscent); nsMargin m(0,0,0,0); GetBorderAndPadding(m); aAscent += m.top; GetMargin(m); aAscent += m.top; GetInset(m); aAscent += m.top; return rv; } NS_IMETHODIMP nsGfxScrollFrame::GetPrefSize(nsBoxLayoutState& aState, nsSize& aSize) { PropagateDebug(aState); nsIFrame* frame = nsnull; GetFrame(&frame); const nsStyleDisplay* styleDisplay = nsnull; frame->GetStyleData(eStyleStruct_Display, (const nsStyleStruct*&)styleDisplay); nsresult rv = mInner->mScrollAreaBox->GetPrefSize(aState, aSize); nsBox::AddMargin(mInner->mScrollAreaBox, aSize); if (styleDisplay->mOverflow == NS_STYLE_OVERFLOW_SCROLL || styleDisplay->mOverflow == NS_STYLE_OVERFLOW_SCROLLBARS_VERTICAL) { nsSize vSize(0,0); mInner->mVScrollbarBox->GetPrefSize(aState, vSize); nsBox::AddMargin(mInner->mVScrollbarBox, vSize); aSize.width += vSize.width; } if (styleDisplay->mOverflow == NS_STYLE_OVERFLOW_SCROLL || styleDisplay->mOverflow == NS_STYLE_OVERFLOW_SCROLLBARS_HORIZONTAL) { nsSize hSize(0,0); mInner->mHScrollbarBox->GetPrefSize(aState, hSize); nsBox::AddMargin(mInner->mHScrollbarBox, hSize); aSize.height += hSize.height; } AddBorderAndPadding(aSize); AddInset(aSize); nsIBox::AddCSSPrefSize(aState, this, aSize); return rv; } NS_IMETHODIMP nsGfxScrollFrame::GetMinSize(nsBoxLayoutState& aState, nsSize& aSize) { PropagateDebug(aState); nsIFrame* frame = nsnull; GetFrame(&frame); const nsStyleDisplay* styleDisplay = nsnull; frame->GetStyleData(eStyleStruct_Display, (const nsStyleStruct*&)styleDisplay); nsresult rv = mInner->mScrollAreaBox->GetMinSize(aState, aSize); if (styleDisplay->mOverflow == NS_STYLE_OVERFLOW_SCROLL || styleDisplay->mOverflow == NS_STYLE_OVERFLOW_SCROLLBARS_VERTICAL) { nsSize vSize(0,0); mInner->mVScrollbarBox->GetMinSize(aState, vSize); AddMargin(mInner->mVScrollbarBox, vSize); aSize.width += vSize.width; if (aSize.height < vSize.height) aSize.height = vSize.height; } if (styleDisplay->mOverflow == NS_STYLE_OVERFLOW_SCROLL || styleDisplay->mOverflow == NS_STYLE_OVERFLOW_SCROLLBARS_HORIZONTAL) { nsSize hSize(0,0); mInner->mHScrollbarBox->GetMinSize(aState, hSize); AddMargin(mInner->mHScrollbarBox, hSize); aSize.height += hSize.height; if (aSize.width < hSize.width) aSize.width = hSize.width; } AddBorderAndPadding(aSize); AddInset(aSize); nsIBox::AddCSSMinSize(aState, this, aSize); return rv; } NS_IMETHODIMP nsGfxScrollFrame::GetMaxSize(nsBoxLayoutState& aState, nsSize& aSize) { PropagateDebug(aState); aSize.width = NS_INTRINSICSIZE; aSize.height = NS_INTRINSICSIZE; AddBorderAndPadding(aSize); AddInset(aSize); nsIBox::AddCSSMaxSize(aState, this, aSize); return NS_OK; } NS_IMETHODIMP nsGfxScrollFrame::Reflow(nsIPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { DO_GLOBAL_REFLOW_COUNT("nsGfxScrollFrame", aReflowState.reason); // if there is a max element request then set it to -1 so we can see if it gets set if (aDesiredSize.maxElementSize) { aDesiredSize.maxElementSize->width = -1; aDesiredSize.maxElementSize->height = -1; } nsresult rv = nsBoxFrame::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus); // if it was set then cache it. Otherwise set it. if (aDesiredSize.maxElementSize) { nsSize* size = aDesiredSize.maxElementSize; // if not set then use the cached size. If set then set it. if (size->width == -1) size->width = mInner->mMaxElementSize.width; else mInner->mMaxElementSize.width = size->width; if (size->height == -1) size->height = mInner->mMaxElementSize.height; else mInner->mMaxElementSize.height = size->height; // make sure we add in our scrollbar. nsBoxLayoutState state(aPresContext, aReflowState, aDesiredSize); const nsStyleDisplay* styleDisplay = nsnull; nsIFrame* frame = nsnull; GetFrame(&frame); frame->GetStyleData(eStyleStruct_Display, (const nsStyleStruct*&)styleDisplay); if (mInner->mHasVerticalScrollbar || styleDisplay->mOverflow == NS_STYLE_OVERFLOW_SCROLL || styleDisplay->mOverflow == NS_STYLE_OVERFLOW_SCROLLBARS_VERTICAL) { nsSize vSize(0,0); mInner->mVScrollbarBox->GetMinSize(state, vSize); AddMargin(mInner->mVScrollbarBox, vSize); size->width += vSize.width; nsMargin border; GetBorderAndPadding(border); nsMargin inset; GetInset(inset); border += inset; size->width += border.left + border.right + inset.left + inset.right; } } return rv; } NS_IMETHODIMP_(nsrefcnt) nsGfxScrollFrame::AddRef(void) { return NS_OK; } NS_IMETHODIMP_(nsrefcnt) nsGfxScrollFrame::Release(void) { return NS_OK; } #ifdef NS_DEBUG NS_IMETHODIMP nsGfxScrollFrame::GetFrameName(nsString& aResult) const { return MakeFrameName("GfxScroll", aResult); } #endif NS_INTERFACE_MAP_BEGIN(nsGfxScrollFrame) NS_INTERFACE_MAP_ENTRY(nsIAnonymousContentCreator) #ifdef NS_DEBUG NS_INTERFACE_MAP_ENTRY(nsIFrameDebug) #endif NS_INTERFACE_MAP_ENTRY(nsIScrollableFrame) NS_INTERFACE_MAP_END_INHERITING(nsBoxFrame) //-------------------- Inner ---------------------- nsGfxScrollFrameInner::nsGfxScrollFrameInner(nsGfxScrollFrame* aOuter):mHScrollbarBox(nsnull), mVScrollbarBox(nsnull), mScrollAreaBox(nsnull), mOnePixel(20), mHasVerticalScrollbar(PR_FALSE), mHasHorizontalScrollbar(PR_FALSE) { NS_INIT_REFCNT(); mOuter = aOuter; mMaxElementSize.width = 0; mMaxElementSize.height = 0; mFirstPass = PR_FALSE; mNeverHasVerticalScrollbar = PR_FALSE; mNeverHasHorizontalScrollbar = PR_FALSE; } NS_IMETHODIMP nsGfxScrollFrameInner::ScrollPositionWillChange(nsIScrollableView* aScrollable, nscoord aX, nscoord aY) { // Do nothing. return NS_OK; } /** * Called if something externally moves the scroll area * This can happen if the user pages up down or uses arrow keys * So what we need to do up adjust the scrollbars to match. */ NS_IMETHODIMP nsGfxScrollFrameInner::ScrollPositionDidChange(nsIScrollableView* aScrollable, nscoord aX, nscoord aY) { SetAttribute(mVScrollbarBox, nsXULAtoms::curpos, aY); SetAttribute(mHScrollbarBox, nsXULAtoms::curpos, aX); return NS_OK; } NS_IMETHODIMP nsGfxScrollFrameInner::AttributeChanged(nsIDocument *aDocument, nsIContent* aContent, PRInt32 aNameSpaceID, nsIAtom* aAttribute, PRInt32 aHint) { if (mHScrollbarBox && mVScrollbarBox) { nsIFrame* hframe = nsnull; mHScrollbarBox->GetFrame(&hframe); nsIFrame* vframe = nsnull; mVScrollbarBox->GetFrame(&vframe); nsCOMPtr vcontent; nsCOMPtr hcontent; hframe->GetContent(getter_AddRefs(hcontent)); vframe->GetContent(getter_AddRefs(vcontent)); if (hcontent.get() == aContent || vcontent.get() == aContent) { nscoord x = 0; nscoord y = 0; nsAutoString value; if (NS_CONTENT_ATTR_HAS_VALUE == hcontent->GetAttribute(kNameSpaceID_None, nsXULAtoms::curpos, value)) { PRInt32 error; // convert it to an integer x = value.ToInteger(&error); } if (NS_CONTENT_ATTR_HAS_VALUE == vcontent->GetAttribute(kNameSpaceID_None, nsXULAtoms::curpos, value)) { PRInt32 error; // convert it to an integer y = value.ToInteger(&error); } nsIScrollableView* s = GetScrollableView(mOuter->mPresContext); s->RemoveScrollPositionListener(this); ScrollbarChanged(mOuter->mPresContext, x*mOnePixel, y*mOnePixel); s->AddScrollPositionListener(this); } } return NS_OK; } nsIScrollableView* nsGfxScrollFrameInner::GetScrollableView(nsIPresContext* aPresContext) { nsIScrollableView* scrollingView; nsIView* view; nsIFrame* frame = nsnull; mScrollAreaBox->GetFrame(&frame); frame->GetView(aPresContext, &view); nsresult result = view->QueryInterface(kScrollViewIID, (void**)&scrollingView); NS_ASSERTION(NS_SUCCEEDED(result), "assertion gfx scrollframe does not contain a scrollframe"); return scrollingView; } void nsGfxScrollFrameInner::GetScrolledContentSize(nsSize& aSize) { // get the ara frame is the scrollarea nsIBox* child = nsnull; mScrollAreaBox->GetChildBox(&child); nsRect rect(0,0,0,0); child->GetBounds(rect); aSize.width = rect.width; aSize.height = rect.height; nsBox::AddMargin(child, aSize); nsBox::AddBorderAndPadding(mScrollAreaBox, aSize); nsBox::AddInset(mScrollAreaBox, aSize); } PRBool nsGfxScrollFrameInner::AddHorizontalScrollbar(nsBoxLayoutState& aState, nsRect& aScrollAreaSize, PRBool aOnTop) { return AddRemoveScrollbar(aState, aScrollAreaSize, aOnTop, PR_TRUE, PR_TRUE); } PRBool nsGfxScrollFrameInner::AddVerticalScrollbar(nsBoxLayoutState& aState, nsRect& aScrollAreaSize, PRBool aOnRight) { return AddRemoveScrollbar(aState, aScrollAreaSize, aOnRight, PR_FALSE, PR_TRUE); } PRBool nsGfxScrollFrameInner::RemoveHorizontalScrollbar(nsBoxLayoutState& aState, nsRect& aScrollAreaSize, PRBool aOnTop) { return AddRemoveScrollbar(aState, aScrollAreaSize, aOnTop, PR_TRUE, PR_FALSE); } PRBool nsGfxScrollFrameInner::RemoveVerticalScrollbar(nsBoxLayoutState& aState, nsRect& aScrollAreaSize, PRBool aOnRight) { return AddRemoveScrollbar(aState, aScrollAreaSize, aOnRight, PR_FALSE, PR_FALSE); } PRBool nsGfxScrollFrameInner::AddRemoveScrollbar(nsBoxLayoutState& aState, nsRect& aScrollAreaSize, PRBool aOnTop, PRBool aHorizontal, PRBool aAdd) { if (aHorizontal) { if (mNeverHasHorizontalScrollbar) return PR_FALSE; if (aAdd) SetScrollbarVisibility(mHScrollbarBox, aAdd); nsSize hSize; mHScrollbarBox->GetPrefSize(aState, hSize); nsBox::AddMargin(mHScrollbarBox, hSize); if (!aAdd) SetScrollbarVisibility(mHScrollbarBox, aAdd); PRBool fit = AddRemoveScrollbar(mHasHorizontalScrollbar, aScrollAreaSize.y, aScrollAreaSize.height, hSize.height, aOnTop, aAdd); if (!fit) SetScrollbarVisibility(mHScrollbarBox, !aAdd); return fit; } else { if (mNeverHasVerticalScrollbar) return PR_FALSE; if (aAdd) SetScrollbarVisibility(mVScrollbarBox, aAdd); nsSize vSize; mVScrollbarBox->GetPrefSize(aState, vSize); if (!aAdd) SetScrollbarVisibility(mVScrollbarBox, aAdd); nsBox::AddMargin(mVScrollbarBox, vSize); PRBool fit = AddRemoveScrollbar(mHasVerticalScrollbar, aScrollAreaSize.x, aScrollAreaSize.width, vSize.width, aOnTop, aAdd); if (!fit) SetScrollbarVisibility(mVScrollbarBox, !aAdd); return fit; } } PRBool nsGfxScrollFrameInner::AddRemoveScrollbar(PRBool& aHasScrollbar, nscoord& aXY, nscoord& aSize, nscoord aSbSize, PRBool aRightOrBottom, PRBool aAdd) { nscoord size = aSize; if (size != NS_INTRINSICSIZE) { if (aAdd) { size -= aSbSize; if (!aRightOrBottom && size >= 0) aXY += aSbSize; } else { size += aSbSize; if (!aRightOrBottom) aXY -= aSbSize; } } // not enough room? Yes? Return true. if (size >= aSbSize) { aHasScrollbar = aAdd; aSize = size; return PR_TRUE; } return PR_FALSE; } nsresult nsGfxScrollFrameInner::LayoutBox(nsBoxLayoutState& aState, nsIBox* aBox, const nsRect& aRect) { return mOuter->LayoutChildAt(aState, aBox, aRect); } NS_IMETHODIMP nsGfxScrollFrame::Layout(nsBoxLayoutState& aState) { // mark ourselves as dirty so no child under us // can post an incremental layout. mState |= NS_FRAME_HAS_DIRTY_CHILDREN; PropagateDebug(aState); PRUint32 flags = 0; aState.GetLayoutFlags(flags); nsresult rv = mInner->Layout(aState); aState.SetLayoutFlags(flags); nsBox::Layout(aState); return rv; } /** * Reflow the scroll area if it needs it and return its size. Also determine if the reflow will * cause any of the scrollbars to need to be reflowed. */ nsresult nsGfxScrollFrameInner::Layout(nsBoxLayoutState& aState) { //TODO make bidi code set these from preferences // if true places the vertical scrollbar on the right false puts it on the left. PRBool scrollBarRight = PR_TRUE; // if true places the horizontal scrollbar on the bottom false puts it on the top. PRBool scrollBarBottom = PR_TRUE; nsIFrame* frame = nsnull; mOuter->GetFrame(&frame); const nsStyleDisplay* styleDisplay = nsnull; frame->GetStyleData(eStyleStruct_Display, (const nsStyleStruct*&)styleDisplay); // get the content rect nsRect clientRect(0,0,0,0); mOuter->GetClientRect(clientRect); // get the preferred size of the scrollbars nsSize hSize(0,0); nsSize vSize(0,0); nsSize hMinSize(0,0); nsSize vMinSize(0,0); /* mHScrollbarBox->GetPrefSize(aState, hSize); mVScrollbarBox->GetPrefSize(aState, vSize); mHScrollbarBox->GetMinSize(aState, hMinSize); mVScrollbarBox->GetMinSize(aState, vMinSize); nsBox::AddMargin(mHScrollbarBox, hSize); nsBox::AddMargin(mVScrollbarBox, vSize); nsBox::AddMargin(mHScrollbarBox, hMinSize); nsBox::AddMargin(mVScrollbarBox, vMinSize); */ // the scroll area size starts off as big as our content area nsRect scrollAreaRect(clientRect); // Look at our style do we always have vertical or horizontal scrollbars? if (styleDisplay->mOverflow == NS_STYLE_OVERFLOW_SCROLL || styleDisplay->mOverflow == NS_STYLE_OVERFLOW_SCROLLBARS_HORIZONTAL) mHasHorizontalScrollbar = PR_TRUE; if (styleDisplay->mOverflow == NS_STYLE_OVERFLOW_SCROLL || styleDisplay->mOverflow == NS_STYLE_OVERFLOW_SCROLLBARS_VERTICAL) mHasVerticalScrollbar = PR_TRUE; if (mHasHorizontalScrollbar) AddHorizontalScrollbar(aState, scrollAreaRect, scrollBarBottom); if (mHasVerticalScrollbar) AddVerticalScrollbar(aState, scrollAreaRect, scrollBarRight); // layout our the scroll area LayoutBox(aState, mScrollAreaBox, scrollAreaRect); // now look at the content area and see if we need scrollbars or not PRBool needsLayout = PR_FALSE; nsSize scrolledContentSize(0,0); // if we have 'auto' scrollbars look at the vertical case if (styleDisplay->mOverflow != NS_STYLE_OVERFLOW_SCROLL && styleDisplay->mOverflow != NS_STYLE_OVERFLOW_SCROLLBARS_VERTICAL) { // get the area frame is the scrollarea GetScrolledContentSize(scrolledContentSize); // There are two cases to consider if (scrolledContentSize.height <= scrollAreaRect.height || styleDisplay->mOverflow == NS_STYLE_OVERFLOW_SCROLLBARS_HORIZONTAL || styleDisplay->mOverflow == NS_STYLE_OVERFLOW_SCROLLBARS_NONE) { if (mHasVerticalScrollbar) { // We left room for the vertical scrollbar, but it's not needed; // remove it. if (RemoveVerticalScrollbar(aState, scrollAreaRect, scrollBarRight)) needsLayout = PR_TRUE; } } else { if (!mHasVerticalScrollbar) { // We didn't leave room for the vertical scrollbar, but it turns // out we needed it if (AddVerticalScrollbar(aState, scrollAreaRect, scrollBarRight)) needsLayout = PR_TRUE; } } // ok layout at the right size if (needsLayout) { nsBoxLayoutState resizeState(aState); resizeState.SetLayoutReason(nsBoxLayoutState::Resize); LayoutBox(resizeState, mScrollAreaBox, scrollAreaRect); needsLayout = PR_FALSE; } } // if scrollbars are auto look at the horizontal case if ((NS_STYLE_OVERFLOW_SCROLL != styleDisplay->mOverflow) && (NS_STYLE_OVERFLOW_SCROLLBARS_HORIZONTAL != styleDisplay->mOverflow)) { // get the area frame is the scrollarea GetScrolledContentSize(scrolledContentSize); // if the child is wider that the scroll area // and we don't have a scrollbar add one. if (scrolledContentSize.width > scrollAreaRect.width && styleDisplay->mOverflow != NS_STYLE_OVERFLOW_SCROLLBARS_VERTICAL && styleDisplay->mOverflow != NS_STYLE_OVERFLOW_SCROLLBARS_NONE) { if (!mHasHorizontalScrollbar) { // no scrollbar? if (AddHorizontalScrollbar(aState, scrollAreaRect, scrollBarBottom)) needsLayout = PR_TRUE; // if we added a horizonal scrollbar and we did not have a vertical // there is a chance that by adding the horizonal scrollbar we will // suddenly need a vertical scrollbar. Is a special case but its // important. //if (!mHasVerticalScrollbar && scrolledContentSize.height > scrollAreaRect.height - sbSize.height) // printf("****Gfx Scrollbar Special case hit!!*****\n"); } } else { // if the area is smaller or equal to and we have a scrollbar then // remove it. if (mHasHorizontalScrollbar) { if (RemoveHorizontalScrollbar(aState, scrollAreaRect, scrollBarBottom)) needsLayout = PR_TRUE; } } } // we only need to set the rect. The inner child stays the same size. if (needsLayout) { nsBoxLayoutState resizeState(aState); resizeState.SetLayoutReason(nsBoxLayoutState::Resize); LayoutBox(resizeState, mScrollAreaBox, scrollAreaRect); needsLayout = PR_FALSE; } GetScrolledContentSize(scrolledContentSize); nsIPresContext* presContext = aState.GetPresContext(); float p2t; presContext->GetScaledPixelsToTwips(&p2t); mOnePixel = NSIntPixelsToTwips(1, p2t); const nsStyleFont* font; mOuter->GetStyleData(eStyleStruct_Font, (const nsStyleStruct*&) font); const nsFont& f = font->mFont; nsCOMPtr fm; presContext->GetMetricsFor(f, getter_AddRefs(fm)); nscoord fontHeight = 1; fm->GetHeight(fontHeight); nscoord maxX = scrolledContentSize.width - scrollAreaRect.width; nscoord maxY = scrolledContentSize.height - scrollAreaRect.height; nsIScrollableView* scrollable = GetScrollableView(presContext); scrollable->SetLineHeight(fontHeight); mHScrollbarBox->GetPrefSize(aState, hSize); mVScrollbarBox->GetPrefSize(aState, vSize); // layout vertical scrollbar nsRect vRect(clientRect); vRect.width = vSize.width; vRect.y = clientRect.y; if (mHasHorizontalScrollbar) { vRect.height -= hSize.height; if (!scrollBarBottom) vRect.y += hSize.height; } vRect.x = clientRect.x; if (scrollBarRight) vRect.x += clientRect.width - vSize.width; if (mHasVerticalScrollbar) { SetAttribute(mVScrollbarBox, nsXULAtoms::maxpos, maxY); SetAttribute(mVScrollbarBox, nsXULAtoms::pageincrement, nscoord(scrollAreaRect.height - fontHeight)); SetAttribute(mVScrollbarBox, nsXULAtoms::increment, fontHeight, PR_FALSE); } LayoutBox(aState, mVScrollbarBox, vRect); mVScrollbarBox->GetPrefSize(aState, vSize); mVScrollbarBox->GetMinSize(aState, vMinSize); if (mHasVerticalScrollbar && (vMinSize.width > vRect.width || vMinSize.height > vRect.height)) { if (RemoveVerticalScrollbar(aState, scrollAreaRect, scrollBarRight)) needsLayout = PR_TRUE; mVScrollbarBox->GetPrefSize(aState, vSize); } // layout horizontal scrollbar nsRect hRect(clientRect); hRect.height = hSize.height; hRect.x = clientRect.x; if (mHasVerticalScrollbar) { hRect.width -= vSize.width; if (!scrollBarRight) hRect.x += vSize.width; } hRect.y = clientRect.y; if (scrollBarBottom) hRect.y += clientRect.height - hSize.height; if (mHasHorizontalScrollbar) { SetAttribute(mHScrollbarBox, nsXULAtoms::maxpos, maxX); SetAttribute(mHScrollbarBox, nsXULAtoms::pageincrement, nscoord(float(scrollAreaRect.width)*0.8)); SetAttribute(mHScrollbarBox, nsXULAtoms::increment, 10*mOnePixel, PR_FALSE); } LayoutBox(aState, mHScrollbarBox, hRect); mHScrollbarBox->GetMinSize(aState, hMinSize); if (mHasHorizontalScrollbar && (hMinSize.width > hRect.width || hMinSize.height > hRect.height)) { if (RemoveHorizontalScrollbar(aState, scrollAreaRect, scrollBarBottom)) needsLayout = PR_TRUE; } // we only need to set the rect. The inner child stays the same size. if (needsLayout) { nsBoxLayoutState resizeState(aState); resizeState.SetLayoutReason(nsBoxLayoutState::Resize); LayoutBox(resizeState, mScrollAreaBox, scrollAreaRect); needsLayout = PR_FALSE; } return NS_OK; } void nsGfxScrollFrameInner::ScrollbarChanged(nsIPresContext* aPresContext, nscoord aX, nscoord aY) { nsIScrollableView* scrollable = GetScrollableView(aPresContext); scrollable->ScrollTo(aX,aY, NS_SCROLL_PROPERTY_ALWAYS_BLIT); // printf("scrolling to: %d, %d\n", aX, aY); } nsGfxScrollFrameInner::~nsGfxScrollFrameInner() { if (mDocument) { mDocument->RemoveObserver(this); mDocument = nsnull; } } /** * Returns whether it actually needed to change the attribute */ PRBool nsGfxScrollFrameInner::SetAttribute(nsIBox* aBox, nsIAtom* aAtom, nscoord aSize, PRBool aReflow) { // convert to pixels aSize /= mOnePixel; // only set the attribute if it changed. PRInt32 current = GetIntegerAttribute(aBox, aAtom, -1); if (current != aSize) { nsIFrame* frame = nsnull; aBox->GetFrame(&frame); nsCOMPtr content; frame->GetContent(getter_AddRefs(content)); char ch[100]; sprintf(ch,"%d", aSize); nsAutoString newValue; newValue.AssignWithConversion(ch); content->SetAttribute(kNameSpaceID_None, aAtom, newValue, aReflow); return PR_TRUE; } return PR_FALSE; } void nsGfxScrollFrameInner::SetScrollbarVisibility(nsIBox* aScrollbar, PRBool aVisible) { nsIFrame* frame = nsnull; aScrollbar->GetFrame(&frame); nsCOMPtr content; frame->GetContent(getter_AddRefs(content)); PRBool old = PR_TRUE; nsAutoString value; if (NS_CONTENT_ATTR_HAS_VALUE == content->GetAttribute(kNameSpaceID_None, nsXULAtoms::collapsed, value)) old = PR_FALSE; if (aVisible == old) return; if (!aVisible) content->SetAttribute(kNameSpaceID_None, nsXULAtoms::collapsed, NS_ConvertToString("true"), PR_TRUE); else content->UnsetAttribute(kNameSpaceID_None, nsXULAtoms::collapsed, PR_TRUE); } PRInt32 nsGfxScrollFrameInner::GetIntegerAttribute(nsIBox* aBox, nsIAtom* atom, PRInt32 defaultValue) { nsIFrame* frame = nsnull; aBox->GetFrame(&frame); nsCOMPtr content; frame->GetContent(getter_AddRefs(content)); nsAutoString value; if (NS_CONTENT_ATTR_HAS_VALUE == content->GetAttribute(kNameSpaceID_None, atom, value)) { PRInt32 error; // convert it to an integer defaultValue = value.ToInteger(&error); } return defaultValue; } nsresult nsGfxScrollFrame::GetContentOf(nsIContent** aContent) { GetContent(aContent); return NS_OK; }