mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-01 14:45:29 +00:00
5319 lines
162 KiB
C++
5319 lines
162 KiB
C++
/* -*- 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.0 (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 Communicator client 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.
|
|
*/
|
|
#include "nsBlockFrame.h"
|
|
#include "nsFrameReflowState.h"
|
|
#include "nsLineLayout.h"
|
|
#include "nsInlineReflow.h"
|
|
#include "nsAbsoluteFrame.h"
|
|
#include "nsPlaceholderFrame.h"
|
|
#include "nsStyleConsts.h"
|
|
#include "nsHTMLIIDs.h"
|
|
#include "nsCSSRendering.h"
|
|
#include "nsIPresContext.h"
|
|
#include "nsIPresShell.h"
|
|
#include "nsIReflowCommand.h"
|
|
#include "nsISpaceManager.h"
|
|
#include "nsIStyleContext.h"
|
|
#include "nsIView.h"
|
|
#include "nsIFontMetrics.h"
|
|
#include "nsHTMLParts.h"
|
|
#include "nsHTMLAtoms.h"
|
|
#include "nsHTMLValue.h"
|
|
#include "nsDOMEvent.h"
|
|
#include "nsIHTMLContent.h"
|
|
#include "prprf.h"
|
|
|
|
// XXX temporary for :first-letter support
|
|
#include "nsITextContent.h"
|
|
static NS_DEFINE_IID(kITextContentIID, NS_ITEXT_CONTENT_IID);/* XXX */
|
|
|
|
//XXX begin
|
|
|
|
// 11-03-98: low hanging memory fruit: get rid of text-runs that have
|
|
// only one piece of text in them!
|
|
|
|
// 09-17-98: I don't like keeping mInnerBottomMargin
|
|
|
|
// 09-18-98: floating block elements don't size quite right because we
|
|
// wrap them in a body frame and the body frame doesn't honor the css
|
|
// width/height properties (among others!). The body code needs
|
|
// updating.
|
|
|
|
//XXX end
|
|
|
|
#ifdef NS_DEBUG
|
|
#undef NOISY_FIRST_LINE
|
|
#undef REALLY_NOISY_FIRST_LINE
|
|
#undef NOISY_FIRST_LETTER
|
|
#undef NOISY_MAX_ELEMENT_SIZE
|
|
#undef NOISY_RUNIN
|
|
#undef NOISY_FLOATER_CLEARING
|
|
#else
|
|
#undef NOISY_FIRST_LINE
|
|
#undef REALLY_NOISY_FIRST_LINE
|
|
#undef NOISY_FIRST_LETTER
|
|
#undef NOISY_MAX_ELEMENT_SIZE
|
|
#undef NOISY_RUNIN
|
|
#undef NOISY_FLOATER_CLEARING
|
|
#endif
|
|
|
|
/* 52b33130-0b99-11d2-932e-00805f8add32 */
|
|
#define NS_BLOCK_FRAME_CID \
|
|
{ 0x52b33130, 0x0b99, 0x11d2, {0x93, 0x2e, 0x00, 0x80, 0x5f, 0x8a, 0xdd, 0x32}}
|
|
|
|
static const nsIID kBlockFrameCID = NS_BLOCK_FRAME_CID;
|
|
|
|
|
|
class BulletFrame;
|
|
struct LineData;
|
|
class nsBlockFrame;
|
|
|
|
struct nsBlockReflowState : public nsFrameReflowState {
|
|
nsBlockReflowState(nsIPresContext& aPresContext,
|
|
const nsHTMLReflowState& aReflowState,
|
|
const nsHTMLReflowMetrics& aMetrics);
|
|
|
|
~nsBlockReflowState();
|
|
|
|
/**
|
|
* Update the mCurrentBand data based on the current mY position.
|
|
*/
|
|
void GetAvailableSpace();
|
|
|
|
void InitFloater(nsPlaceholderFrame* aPlaceholderFrame);
|
|
|
|
void AddFloater(nsPlaceholderFrame* aPlaceholderFrame);
|
|
|
|
void PlaceFloater(nsPlaceholderFrame* aFloater, PRBool& aIsLeftFloater);
|
|
|
|
void PlaceFloaters(nsVoidArray* aFloaters, PRBool aAllOfThem);
|
|
|
|
void ClearFloaters(PRUint8 aBreakType);
|
|
|
|
PRBool IsLeftMostChild(nsIFrame* aFrame);
|
|
|
|
nsLineLayout mLineLayout;
|
|
nsInlineReflow* mInlineReflow;
|
|
|
|
nsISpaceManager* mSpaceManager;
|
|
nscoord mSpaceManagerX, mSpaceManagerY;
|
|
nsBlockFrame* mBlock;
|
|
nsBlockFrame* mNextInFlow;
|
|
|
|
PRBool mInlineReflowPrepared;
|
|
|
|
nsBlockFrame* mRunInFrame;
|
|
nsBlockFrame* mRunInToFrame;
|
|
|
|
PRUint8 mTextAlign;
|
|
|
|
PRUintn mPrevMarginFlags;
|
|
// previous margin flag bits (above and beyond the carried-out-margin flags)
|
|
#define HAVE_CARRIED_MARGIN 0x100
|
|
#define ALREADY_APPLIED_BOTTOM_MARGIN 0x200
|
|
|
|
nscoord mBottomEdge; // maximum Y
|
|
|
|
PRBool mUnconstrainedWidth;
|
|
PRBool mUnconstrainedHeight;
|
|
nscoord mY;
|
|
nscoord mKidXMost;
|
|
|
|
nsIFrame* mPrevChild;
|
|
|
|
LineData* mFreeList;
|
|
|
|
nsVoidArray mPendingFloaters;
|
|
|
|
LineData* mCurrentLine;
|
|
LineData* mPrevLine;
|
|
|
|
// The next list ordinal for counting list bullets
|
|
PRInt32 mNextListOrdinal;
|
|
|
|
// XXX what happens if we need more than 12 trapezoids?
|
|
struct BlockBandData : public nsBandData {
|
|
// Trapezoids used during band processing
|
|
nsBandTrapezoid data[12];
|
|
|
|
// Bounding rect of available space between any left and right floaters
|
|
nsRect availSpace;
|
|
|
|
BlockBandData() {
|
|
size = 12;
|
|
trapezoids = data;
|
|
}
|
|
|
|
/**
|
|
* Computes the bounding rect of the available space, i.e. space
|
|
* between any left and right floaters Uses the current trapezoid
|
|
* data, see nsISpaceManager::GetBandData(). Also updates member
|
|
* data "availSpace".
|
|
*/
|
|
void ComputeAvailSpaceRect();
|
|
};
|
|
|
|
BlockBandData mCurrentBand;
|
|
};
|
|
|
|
// XXX This is vile. Make it go away
|
|
void
|
|
nsLineLayout::InitFloater(nsPlaceholderFrame* aFrame)
|
|
{
|
|
mBlockReflowState->InitFloater(aFrame);
|
|
}
|
|
void
|
|
nsLineLayout::AddFloater(nsPlaceholderFrame* aFrame)
|
|
{
|
|
mBlockReflowState->AddFloater(aFrame);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
#ifdef REALLY_NOISY_FIRST_LINE
|
|
static void
|
|
DumpStyleGeneaology(nsIFrame* aFrame, const char* gap)
|
|
{
|
|
fputs(gap, stdout);
|
|
aFrame->ListTag(stdout);
|
|
fputs(name, out);
|
|
printf(": ");
|
|
nsIStyleContext* sc;
|
|
aFrame->GetStyleContext(sc);
|
|
while (nsnull != sc) {
|
|
nsIStyleContext* psc;
|
|
printf("%p ", sc);
|
|
psc = sc->GetParent();
|
|
NS_RELEASE(sc);
|
|
sc = psc;
|
|
}
|
|
printf("\n");
|
|
}
|
|
#endif
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
#include "nsHTMLImage.h"
|
|
class BulletFrame : public nsFrame {
|
|
public:
|
|
BulletFrame(nsIContent* aContent, nsIFrame* aParentFrame);
|
|
virtual ~BulletFrame();
|
|
|
|
// nsIFrame
|
|
NS_IMETHOD DeleteFrame(nsIPresContext& aPresContext);
|
|
NS_IMETHOD Paint(nsIPresContext &aCX,
|
|
nsIRenderingContext& aRenderingContext,
|
|
const nsRect& aDirtyRect);
|
|
NS_IMETHOD GetFrameName(nsString& aResult) const;
|
|
NS_IMETHOD List(FILE* out, PRInt32 aIndent, nsIListFilter *aFilter) const;
|
|
|
|
// nsIHTMLReflow
|
|
NS_IMETHOD Reflow(nsIPresContext& aPresContext,
|
|
nsHTMLReflowMetrics& aMetrics,
|
|
const nsHTMLReflowState& aReflowState,
|
|
nsReflowStatus& aStatus);
|
|
|
|
void SetListItemOrdinal(nsBlockReflowState& aBlockState);
|
|
|
|
void GetDesiredSize(nsIPresContext* aPresContext,
|
|
const nsHTMLReflowState& aReflowState,
|
|
nsHTMLReflowMetrics& aMetrics);
|
|
|
|
void GetListItemText(nsIPresContext& aCX,
|
|
const nsStyleList& aMol,
|
|
nsString& aResult);
|
|
|
|
PRInt32 mOrdinal;
|
|
nsMargin mPadding;
|
|
nsHTMLImageLoader mImageLoader;
|
|
};
|
|
|
|
BulletFrame::BulletFrame(nsIContent* aContent, nsIFrame* aParentFrame)
|
|
: nsFrame(aContent, aParentFrame)
|
|
{
|
|
}
|
|
|
|
BulletFrame::~BulletFrame()
|
|
{
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
BulletFrame::DeleteFrame(nsIPresContext& aPresContext)
|
|
{
|
|
// Release image loader first so that it's refcnt can go to zero
|
|
mImageLoader.DestroyLoader();
|
|
return nsFrame::DeleteFrame(aPresContext);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
BulletFrame::GetFrameName(nsString& aResult) const
|
|
{
|
|
return MakeFrameName("Bullet", aResult);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
BulletFrame::List(FILE* out, PRInt32 aIndent, nsIListFilter *aFilter) const
|
|
{
|
|
PRInt32 i;
|
|
for (i = aIndent; --i >= 0; ) fputs(" ", out);
|
|
fprintf(out, "Bullet(%d)@%p ", ContentIndexInContainer(this), this);
|
|
nsIView* view;
|
|
GetView(view);
|
|
if (nsnull != view) {
|
|
fprintf(out, " [view=%p]", view);
|
|
}
|
|
|
|
out << mRect;
|
|
if (0 != mState) {
|
|
fprintf(out, " [state=%08x]", mState);
|
|
}
|
|
fputs("<>\n", out);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_METHOD
|
|
BulletFrame::Paint(nsIPresContext& aCX,
|
|
nsIRenderingContext& aRenderingContext,
|
|
const nsRect& aDirtyRect)
|
|
{
|
|
const nsStyleDisplay* disp =
|
|
(const nsStyleDisplay*)mStyleContext->GetStyleData(eStyleStruct_Display);
|
|
nscoord width;
|
|
|
|
if (disp->mVisible) {
|
|
const nsStyleList* myList =
|
|
(const nsStyleList*)mStyleContext->GetStyleData(eStyleStruct_List);
|
|
|
|
if (myList->mListStyleImage.Length() > 0) {
|
|
nsIImage* image = mImageLoader.GetImage();
|
|
if (nsnull == image) {
|
|
if (!mImageLoader.GetLoadImageFailed()) {
|
|
// No image yet
|
|
return NS_OK;
|
|
}
|
|
}
|
|
else {
|
|
nsRect innerArea(mPadding.left, mPadding.top,
|
|
mRect.width - (mPadding.left + mPadding.right),
|
|
mRect.height - (mPadding.top + mPadding.bottom));
|
|
aRenderingContext.DrawImage(image, innerArea);
|
|
return NS_OK;
|
|
}
|
|
}
|
|
|
|
const nsStyleFont* myFont =
|
|
(const nsStyleFont*)mStyleContext->GetStyleData(eStyleStruct_Font);
|
|
const nsStyleColor* myColor =
|
|
(const nsStyleColor*)mStyleContext->GetStyleData(eStyleStruct_Color);
|
|
nsIFontMetrics* fm;
|
|
aRenderingContext.SetColor(myColor->mColor);
|
|
|
|
nsAutoString text;
|
|
switch (myList->mListStyleType) {
|
|
case NS_STYLE_LIST_STYLE_NONE:
|
|
break;
|
|
|
|
default:
|
|
case NS_STYLE_LIST_STYLE_BASIC:
|
|
case NS_STYLE_LIST_STYLE_DISC:
|
|
aRenderingContext.FillEllipse(mPadding.left, mPadding.top,
|
|
mRect.width - (mPadding.left + mPadding.right),
|
|
mRect.height - (mPadding.top + mPadding.bottom));
|
|
break;
|
|
|
|
case NS_STYLE_LIST_STYLE_CIRCLE:
|
|
aRenderingContext.DrawEllipse(mPadding.left, mPadding.top,
|
|
mRect.width - (mPadding.left + mPadding.right),
|
|
mRect.height - (mPadding.top + mPadding.bottom));
|
|
break;
|
|
|
|
case NS_STYLE_LIST_STYLE_SQUARE:
|
|
aRenderingContext.FillRect(mPadding.left, mPadding.top,
|
|
mRect.width - (mPadding.left + mPadding.right),
|
|
mRect.height - (mPadding.top + mPadding.bottom));
|
|
break;
|
|
|
|
case NS_STYLE_LIST_STYLE_DECIMAL:
|
|
case NS_STYLE_LIST_STYLE_LOWER_ROMAN:
|
|
case NS_STYLE_LIST_STYLE_UPPER_ROMAN:
|
|
case NS_STYLE_LIST_STYLE_LOWER_ALPHA:
|
|
case NS_STYLE_LIST_STYLE_UPPER_ALPHA:
|
|
fm = aCX.GetMetricsFor(myFont->mFont);
|
|
GetListItemText(aCX, *myList, text);
|
|
aRenderingContext.SetFont(fm);
|
|
aRenderingContext.GetWidth(text, width);
|
|
aRenderingContext.DrawString(text, mPadding.left, mPadding.top, width);
|
|
NS_RELEASE(fm);
|
|
break;
|
|
}
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
BulletFrame::SetListItemOrdinal(nsBlockReflowState& aReflowState)
|
|
{
|
|
// Assume that the ordinal comes from the block reflow state
|
|
mOrdinal = aReflowState.mNextListOrdinal;
|
|
|
|
// Try to get value directly from the list-item, if it specifies a
|
|
// value attribute. Note: we do this with our parent's content
|
|
// because our parent is the list-item.
|
|
nsHTMLValue value;
|
|
nsIContent* parentContent;
|
|
mContentParent->GetContent(parentContent);
|
|
nsIHTMLContent* hc;
|
|
if (NS_OK == parentContent->QueryInterface(kIHTMLContentIID, (void**) &hc)) {
|
|
if (NS_CONTENT_ATTR_HAS_VALUE ==
|
|
hc->GetAttribute(nsHTMLAtoms::value, value)) {
|
|
if (eHTMLUnit_Integer == value.GetUnit()) {
|
|
// Use ordinal specified by the value attribute
|
|
mOrdinal = value.GetIntValue();
|
|
if (mOrdinal <= 0) {
|
|
mOrdinal = 1;
|
|
}
|
|
}
|
|
}
|
|
NS_RELEASE(hc);
|
|
}
|
|
NS_RELEASE(parentContent);
|
|
|
|
aReflowState.mNextListOrdinal = mOrdinal + 1;
|
|
}
|
|
|
|
static const char* gLowerRomanCharsA = "ixcm";
|
|
static const char* gUpperRomanCharsA = "IXCM";
|
|
static const char* gLowerRomanCharsB = "vld?";
|
|
static const char* gUpperRomanCharsB = "VLD?";
|
|
static const char* gLowerAlphaChars = "abcdefghijklmnopqrstuvwxyz";
|
|
static const char* gUpperAlphaChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
|
|
// XXX change roman/alpha to use unsigned math so that maxint and
|
|
// maxnegint will work
|
|
void
|
|
BulletFrame::GetListItemText(nsIPresContext& aCX,
|
|
const nsStyleList& aListStyle,
|
|
nsString& result)
|
|
{
|
|
PRInt32 ordinal = mOrdinal;
|
|
char cbuf[40];
|
|
switch (aListStyle.mListStyleType) {
|
|
case NS_STYLE_LIST_STYLE_DECIMAL:
|
|
PR_snprintf(cbuf, sizeof(cbuf), "%ld", ordinal);
|
|
result.Append(cbuf);
|
|
break;
|
|
|
|
case NS_STYLE_LIST_STYLE_LOWER_ROMAN:
|
|
case NS_STYLE_LIST_STYLE_UPPER_ROMAN:
|
|
{
|
|
if (ordinal <= 0) {
|
|
ordinal = 1;
|
|
}
|
|
nsAutoString addOn, decStr;
|
|
decStr.Append(ordinal, 10);
|
|
PRIntn len = decStr.Length();
|
|
const PRUnichar* dp = decStr.GetUnicode();
|
|
const PRUnichar* end = dp + len;
|
|
PRIntn romanPos = len;
|
|
PRIntn n;
|
|
|
|
const char* achars;
|
|
const char* bchars;
|
|
if (aListStyle.mListStyleType == NS_STYLE_LIST_STYLE_LOWER_ROMAN) {
|
|
achars = gLowerRomanCharsA;
|
|
bchars = gLowerRomanCharsB;
|
|
}
|
|
else {
|
|
achars = gUpperRomanCharsA;
|
|
bchars = gUpperRomanCharsB;
|
|
}
|
|
for (; dp < end; dp++) {
|
|
romanPos--;
|
|
addOn.SetLength(0);
|
|
switch(*dp) {
|
|
case '3': addOn.Append(achars[romanPos]);
|
|
case '2': addOn.Append(achars[romanPos]);
|
|
case '1': addOn.Append(achars[romanPos]);
|
|
break;
|
|
case '4':
|
|
addOn.Append(achars[romanPos]);
|
|
// FALLTHROUGH
|
|
case '5': case '6':
|
|
case '7': case '8':
|
|
addOn.Append(bchars[romanPos]);
|
|
for(n=0;n<(*dp-'5');n++) {
|
|
addOn.Append(achars[romanPos]);
|
|
}
|
|
break;
|
|
case '9':
|
|
addOn.Append(achars[romanPos]);
|
|
addOn.Append(achars[romanPos+1]);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
result.Append(addOn);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case NS_STYLE_LIST_STYLE_LOWER_ALPHA:
|
|
case NS_STYLE_LIST_STYLE_UPPER_ALPHA:
|
|
{
|
|
PRInt32 anOffset = -1;
|
|
PRInt32 aBase = 26;
|
|
PRInt32 ndex=0;
|
|
PRInt32 root=1;
|
|
PRInt32 next=aBase;
|
|
PRInt32 expn=1;
|
|
const char* chars =
|
|
(aListStyle.mListStyleType == NS_STYLE_LIST_STYLE_LOWER_ALPHA)
|
|
? gLowerAlphaChars : gUpperAlphaChars;
|
|
|
|
// must be positive here...
|
|
if (ordinal <= 0) {
|
|
ordinal = 1;
|
|
}
|
|
ordinal--; // a == 0
|
|
|
|
// scale up in baseN; exceed current value.
|
|
while (next<=ordinal) {
|
|
root=next;
|
|
next*=aBase;
|
|
expn++;
|
|
}
|
|
while (0!=(expn--)) {
|
|
ndex = ((root<=ordinal) && (0!=root)) ? (ordinal/root): 0;
|
|
ordinal %= root;
|
|
if (root>1)
|
|
result.Append(chars[ndex+anOffset]);
|
|
else
|
|
result.Append(chars[ndex]);
|
|
root /= aBase;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
result.Append(".");
|
|
}
|
|
|
|
#define MIN_BULLET_SIZE 5 // from laytext.c
|
|
|
|
static nsresult
|
|
UpdateBulletCB(nsIPresContext& aPresContext, nsIFrame* aFrame, PRIntn aStatus)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
if (NS_IMAGE_LOAD_STATUS_SIZE_AVAILABLE & aStatus) {
|
|
// Now that the size is available, trigger a reflow of the bullet
|
|
// frame.
|
|
nsIPresShell* shell;
|
|
shell = aPresContext.GetShell();
|
|
if (nsnull != shell) {
|
|
nsIReflowCommand* cmd;
|
|
rv = NS_NewHTMLReflowCommand(&cmd, aFrame,
|
|
nsIReflowCommand::ContentChanged);
|
|
if (NS_OK == rv) {
|
|
shell->EnterReflowLock();
|
|
shell->AppendReflowCommand(cmd);
|
|
NS_RELEASE(cmd);
|
|
shell->ExitReflowLock();
|
|
}
|
|
NS_RELEASE(shell);
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
void
|
|
BulletFrame::GetDesiredSize(nsIPresContext* aCX,
|
|
const nsHTMLReflowState& aReflowState,
|
|
nsHTMLReflowMetrics& aMetrics)
|
|
{
|
|
const nsStyleList* myList =
|
|
(const nsStyleList*)mStyleContext->GetStyleData(eStyleStruct_List);
|
|
nscoord ascent;
|
|
|
|
if (myList->mListStyleImage.Length() > 0) {
|
|
mImageLoader.SetURL(myList->mListStyleImage);
|
|
mImageLoader.GetDesiredSize(aCX, aReflowState, this, UpdateBulletCB,
|
|
aMetrics);
|
|
if (!mImageLoader.GetLoadImageFailed()) {
|
|
nsHTMLContainerFrame::CreateViewForFrame(*aCX, this, mStyleContext,
|
|
PR_FALSE);
|
|
aMetrics.ascent = aMetrics.height;
|
|
aMetrics.descent = 0;
|
|
return;
|
|
}
|
|
}
|
|
|
|
const nsStyleFont* myFont =
|
|
(const nsStyleFont*)mStyleContext->GetStyleData(eStyleStruct_Font);
|
|
nsIFontMetrics* fm = aCX->GetMetricsFor(myFont->mFont);
|
|
nscoord bulletSize;
|
|
float p2t;
|
|
float t2p;
|
|
|
|
nsAutoString text;
|
|
switch (myList->mListStyleType) {
|
|
case NS_STYLE_LIST_STYLE_NONE:
|
|
aMetrics.width = 0;
|
|
aMetrics.height = 0;
|
|
aMetrics.ascent = 0;
|
|
aMetrics.descent = 0;
|
|
break;
|
|
|
|
default:
|
|
case NS_STYLE_LIST_STYLE_DISC:
|
|
case NS_STYLE_LIST_STYLE_CIRCLE:
|
|
case NS_STYLE_LIST_STYLE_BASIC:
|
|
case NS_STYLE_LIST_STYLE_SQUARE:
|
|
t2p = aCX->GetTwipsToPixels();
|
|
fm->GetMaxAscent(ascent);
|
|
bulletSize = NSTwipsToIntPixels(
|
|
(nscoord)NSToIntRound(0.8f * (float(ascent) / 2.0f)), t2p);
|
|
if (bulletSize < 1) {
|
|
bulletSize = MIN_BULLET_SIZE;
|
|
}
|
|
p2t = aCX->GetPixelsToTwips();
|
|
bulletSize = NSIntPixelsToTwips(bulletSize, p2t);
|
|
mPadding.bottom = ascent / 8;
|
|
aMetrics.width = mPadding.right + bulletSize;
|
|
aMetrics.height = mPadding.bottom + bulletSize;
|
|
aMetrics.ascent = mPadding.bottom + bulletSize;
|
|
aMetrics.descent = 0;
|
|
break;
|
|
|
|
case NS_STYLE_LIST_STYLE_DECIMAL:
|
|
case NS_STYLE_LIST_STYLE_LOWER_ROMAN:
|
|
case NS_STYLE_LIST_STYLE_UPPER_ROMAN:
|
|
case NS_STYLE_LIST_STYLE_LOWER_ALPHA:
|
|
case NS_STYLE_LIST_STYLE_UPPER_ALPHA:
|
|
GetListItemText(*aCX, *myList, text);
|
|
fm->GetHeight(aMetrics.height);
|
|
aReflowState.rendContext->SetFont(fm);
|
|
aReflowState.rendContext->GetWidth(text, aMetrics.width);
|
|
aMetrics.width += mPadding.right;
|
|
fm->GetMaxAscent(aMetrics.ascent);
|
|
fm->GetMaxDescent(aMetrics.descent);
|
|
break;
|
|
}
|
|
NS_RELEASE(fm);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
BulletFrame::Reflow(nsIPresContext& aPresContext,
|
|
nsHTMLReflowMetrics& aMetrics,
|
|
const nsHTMLReflowState& aReflowState,
|
|
nsReflowStatus& aStatus)
|
|
{
|
|
// Get the base size
|
|
GetDesiredSize(&aPresContext, aReflowState, aMetrics);
|
|
|
|
// Add in the border and padding; split the top/bottom between the
|
|
// ascent and descent to make things look nice
|
|
const nsStyleSpacing* space =(const nsStyleSpacing*)
|
|
mStyleContext->GetStyleData(eStyleStruct_Spacing);
|
|
nsMargin borderPadding;
|
|
nsHTMLReflowState::ComputeBorderPaddingFor(this,
|
|
aReflowState.parentReflowState,
|
|
borderPadding);
|
|
aMetrics.width += borderPadding.left + borderPadding.right;
|
|
aMetrics.height += borderPadding.top + borderPadding.bottom;
|
|
aMetrics.ascent += borderPadding.top;
|
|
aMetrics.descent += borderPadding.bottom;
|
|
|
|
if (nsnull != aMetrics.maxElementSize) {
|
|
aMetrics.maxElementSize->width = aMetrics.width;
|
|
aMetrics.maxElementSize->height = aMetrics.height;
|
|
}
|
|
aStatus = NS_FRAME_COMPLETE;
|
|
return NS_OK;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
#define LINE_IS_DIRTY 0x1
|
|
#define LINE_IS_BLOCK 0x2
|
|
#define LINE_NEED_DID_REFLOW 0x8
|
|
#define LINE_TOP_MARGIN_IS_AUTO 0x10
|
|
#define LINE_BOTTOM_MARGIN_IS_AUTO 0x20
|
|
#define LINE_OUTSIDE_CHILDREN 0x40
|
|
|
|
struct LineData {
|
|
LineData(nsIFrame* aFrame, PRInt32 aCount, PRUint16 flags) {
|
|
mFirstChild = aFrame;
|
|
mChildCount = aCount;
|
|
mState = LINE_IS_DIRTY | LINE_NEED_DID_REFLOW | flags;
|
|
mFloaters = nsnull;
|
|
mNext = nsnull;
|
|
mBounds.SetRect(0,0,0,0);
|
|
mCombinedArea.SetRect(0,0,0,0);
|
|
mCarriedOutTopMargin = 0;
|
|
mCarriedOutBottomMargin = 0;
|
|
mBreakType = NS_STYLE_CLEAR_NONE;
|
|
}
|
|
|
|
~LineData();
|
|
|
|
void List(FILE* out, PRInt32 aIndent, nsIListFilter *aFilter = nsnull,
|
|
PRBool aOutputMe=PR_TRUE) const;
|
|
|
|
PRInt32 ChildCount() const {
|
|
return PRInt32(mChildCount);
|
|
}
|
|
|
|
nsIFrame* LastChild() const;
|
|
|
|
PRBool IsLastChild(nsIFrame* aFrame) const;
|
|
|
|
PRBool IsBlock() const {
|
|
return 0 != (LINE_IS_BLOCK & mState);
|
|
}
|
|
|
|
void SetIsBlock() {
|
|
mState |= LINE_IS_BLOCK;
|
|
}
|
|
|
|
void ClearIsBlock() {
|
|
mState &= ~LINE_IS_BLOCK;
|
|
}
|
|
|
|
void SetIsBlock(PRBool aValue) {
|
|
if (aValue) {
|
|
SetIsBlock();
|
|
}
|
|
else {
|
|
ClearIsBlock();
|
|
}
|
|
}
|
|
|
|
void MarkDirty() {
|
|
mState |= LINE_IS_DIRTY;
|
|
}
|
|
|
|
void ClearDirty() {
|
|
mState &= ~LINE_IS_DIRTY;
|
|
}
|
|
|
|
void SetNeedDidReflow() {
|
|
mState |= LINE_NEED_DID_REFLOW;
|
|
}
|
|
|
|
void ClearNeedDidReflow() {
|
|
mState &= ~LINE_NEED_DID_REFLOW;
|
|
}
|
|
|
|
PRBool NeedsDidReflow() {
|
|
return 0 != (LINE_NEED_DID_REFLOW & mState);
|
|
}
|
|
|
|
PRBool IsDirty() const {
|
|
return 0 != (LINE_IS_DIRTY & mState);
|
|
}
|
|
|
|
void SetOutsideChildren() {
|
|
mState |= LINE_OUTSIDE_CHILDREN;
|
|
}
|
|
|
|
void ClearOutsideChildren() {
|
|
mState &= ~LINE_OUTSIDE_CHILDREN;
|
|
}
|
|
|
|
PRBool OutsideChildren() const {
|
|
return 0 != (LINE_OUTSIDE_CHILDREN & mState);
|
|
}
|
|
|
|
PRUint16 GetState() const { return mState; }
|
|
|
|
char* StateToString(char* aBuf, PRInt32 aBufSize) const;
|
|
|
|
PRBool Contains(nsIFrame* aFrame) const;
|
|
|
|
void SetMarginFlags(PRUintn aFlags) {
|
|
mState &= ~(LINE_TOP_MARGIN_IS_AUTO|LINE_BOTTOM_MARGIN_IS_AUTO);
|
|
if (NS_CARRIED_TOP_MARGIN_IS_AUTO & aFlags) {
|
|
mState |= LINE_TOP_MARGIN_IS_AUTO;
|
|
}
|
|
if (NS_CARRIED_BOTTOM_MARGIN_IS_AUTO & aFlags) {
|
|
mState |= LINE_BOTTOM_MARGIN_IS_AUTO;
|
|
}
|
|
}
|
|
|
|
PRUintn GetMarginFlags() {
|
|
return ((LINE_TOP_MARGIN_IS_AUTO & mState)
|
|
? NS_CARRIED_TOP_MARGIN_IS_AUTO : 0) |
|
|
((LINE_BOTTOM_MARGIN_IS_AUTO & mState) ?
|
|
NS_CARRIED_BOTTOM_MARGIN_IS_AUTO : 0);
|
|
}
|
|
|
|
void UnplaceFloaters(nsISpaceManager* aSpaceManager) {
|
|
if (nsnull != mFloaters) {
|
|
PRInt32 i, n = mFloaters->Count();
|
|
for (i = 0; i < n; i++) {
|
|
nsPlaceholderFrame* pf = (nsPlaceholderFrame*) mFloaters->ElementAt(i);
|
|
nsIFrame* floater = pf->GetAnchoredItem();
|
|
aSpaceManager->RemoveRegion(floater);
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef NS_DEBUG
|
|
void Verify();
|
|
#endif
|
|
|
|
nsIFrame* mFirstChild;
|
|
PRUint16 mChildCount;
|
|
PRUint16 mState;
|
|
PRUint8 mBreakType;
|
|
nsRect mBounds;
|
|
nsRect mCombinedArea;
|
|
nscoord mCarriedOutTopMargin;
|
|
nscoord mCarriedOutBottomMargin;
|
|
nsVoidArray* mFloaters;
|
|
LineData* mNext;
|
|
};
|
|
|
|
LineData::~LineData()
|
|
{
|
|
if (nsnull != mFloaters) {
|
|
delete mFloaters;
|
|
}
|
|
}
|
|
|
|
static void
|
|
ListFloaters(FILE* out, PRInt32 aIndent, nsVoidArray* aFloaters)
|
|
{
|
|
PRInt32 j, i, n = aFloaters->Count();
|
|
for (i = 0; i < n; i++) {
|
|
for (j = aIndent; --j >= 0; ) fputs(" ", out);
|
|
nsPlaceholderFrame* ph = (nsPlaceholderFrame*) aFloaters->ElementAt(i);
|
|
if (nsnull != ph) {
|
|
fprintf(out, "placeholder@%p\n", ph);
|
|
nsIFrame* frame = ph->GetAnchoredItem();
|
|
if (nsnull != frame) {
|
|
frame->List(out, aIndent + 1, nsnull);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
ListTextRuns(FILE* out, PRInt32 aIndent, nsTextRun* aRuns)
|
|
{
|
|
while (nsnull != aRuns) {
|
|
aRuns->List(out, aIndent);
|
|
aRuns = aRuns->GetNext();
|
|
}
|
|
}
|
|
|
|
char*
|
|
LineData::StateToString(char* aBuf, PRInt32 aBufSize) const
|
|
{
|
|
PR_snprintf(aBuf, aBufSize, "%s,%s",
|
|
(mState & LINE_IS_DIRTY) ? "dirty" : "clean",
|
|
(mState & LINE_IS_BLOCK) ? "block" : "inline");
|
|
return aBuf;
|
|
}
|
|
|
|
void
|
|
LineData::List(FILE* out, PRInt32 aIndent, nsIListFilter *aFilter,
|
|
PRBool aOutputMe) const
|
|
{
|
|
PRInt32 i;
|
|
|
|
if (aOutputMe) {
|
|
for (i = aIndent; --i >= 0; ) fputs(" ", out);
|
|
char cbuf[100];
|
|
fprintf(out, "line %p: count=%d state=%s ",
|
|
this, ChildCount(), StateToString(cbuf, sizeof(cbuf)));
|
|
if (0 != mCarriedOutTopMargin) {
|
|
fprintf(out, "tm=%d ", mCarriedOutTopMargin);
|
|
}
|
|
if (0 != mCarriedOutBottomMargin) {
|
|
fprintf(out, "bm=%d ", mCarriedOutBottomMargin);
|
|
}
|
|
out << mBounds;
|
|
fprintf(out, " ca=");
|
|
out << mCombinedArea;
|
|
fprintf(out, " <\n");
|
|
}
|
|
|
|
nsIFrame* frame = mFirstChild;
|
|
PRInt32 n = ChildCount();
|
|
while (--n >= 0) {
|
|
frame->List(out, aIndent + 1, aFilter);
|
|
frame->GetNextSibling(frame);
|
|
}
|
|
|
|
if (aOutputMe) {
|
|
for (i = aIndent; --i >= 0; ) fputs(" ", out);
|
|
if (nsnull != mFloaters) {
|
|
fputs("> floaters <\n", out);
|
|
ListFloaters(out, aIndent + 1, mFloaters);
|
|
for (i = aIndent; --i >= 0; ) fputs(" ", out);
|
|
}
|
|
fputs(">\n", out);
|
|
}
|
|
}
|
|
|
|
nsIFrame*
|
|
LineData::LastChild() const
|
|
{
|
|
nsIFrame* frame = mFirstChild;
|
|
PRInt32 n = ChildCount() - 1;
|
|
while (--n >= 0) {
|
|
frame->GetNextSibling(frame);
|
|
}
|
|
return frame;
|
|
}
|
|
|
|
PRBool
|
|
LineData::IsLastChild(nsIFrame* aFrame) const
|
|
{
|
|
nsIFrame* lastFrame = LastChild();
|
|
return aFrame == lastFrame;
|
|
}
|
|
|
|
PRBool
|
|
LineData::Contains(nsIFrame* aFrame) const
|
|
{
|
|
PRInt32 n = ChildCount();
|
|
nsIFrame* frame = mFirstChild;
|
|
while (--n >= 0) {
|
|
if (frame == aFrame) {
|
|
return PR_TRUE;
|
|
}
|
|
frame->GetNextSibling(frame);
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|
|
static PRInt32
|
|
LengthOf(nsIFrame* aFrame)
|
|
{
|
|
PRInt32 result = 0;
|
|
while (nsnull != aFrame) {
|
|
result++;
|
|
aFrame->GetNextSibling(aFrame);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
#ifdef NS_DEBUG
|
|
void
|
|
LineData::Verify()
|
|
{
|
|
nsIFrame* lastFrame = LastChild();
|
|
if (nsnull != lastFrame) {
|
|
nsIFrame* nextInFlow;
|
|
lastFrame->GetNextInFlow(nextInFlow);
|
|
if (nsnull != mNext) {
|
|
nsIFrame* nextSibling;
|
|
lastFrame->GetNextSibling(nextSibling);
|
|
NS_ASSERTION(mNext->mFirstChild == nextSibling, "bad line list");
|
|
}
|
|
}
|
|
PRInt32 len = LengthOf(mFirstChild);
|
|
NS_ASSERTION(len >= ChildCount(), "bad mChildCount");
|
|
}
|
|
|
|
static void
|
|
VerifyLines(LineData* aLine)
|
|
{
|
|
while (nsnull != aLine) {
|
|
aLine->Verify();
|
|
aLine = aLine->mNext;
|
|
}
|
|
}
|
|
|
|
static void
|
|
VerifyChildCount(LineData* aLines, PRBool aEmptyOK = PR_FALSE)
|
|
{
|
|
if (nsnull != aLines) {
|
|
PRInt32 childCount = LengthOf(aLines->mFirstChild);
|
|
PRInt32 sum = 0;
|
|
LineData* line = aLines;
|
|
while (nsnull != line) {
|
|
if (!aEmptyOK) {
|
|
NS_ASSERTION(0 != line->ChildCount(), "empty line left in line list");
|
|
}
|
|
sum += line->ChildCount();
|
|
line = line->mNext;
|
|
}
|
|
if (sum != childCount) {
|
|
printf("Bad sibling list/line mChildCount's\n");
|
|
LineData* line = aLines;
|
|
while (nsnull != line) {
|
|
line->List(stdout, 1);
|
|
if (nsnull != line->mNext) {
|
|
nsIFrame* lastFrame = line->LastChild();
|
|
if (nsnull != lastFrame) {
|
|
nsIFrame* nextSibling;
|
|
lastFrame->GetNextSibling(nextSibling);
|
|
if (line->mNext->mFirstChild != nextSibling) {
|
|
printf(" [list broken: nextSibling=%p mNext->mFirstChild=%p]\n",
|
|
nextSibling, line->mNext->mFirstChild);
|
|
}
|
|
}
|
|
}
|
|
line = line->mNext;
|
|
}
|
|
NS_ASSERTION(sum == childCount, "bad sibling list/line mChildCount's");
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
DeleteLineList(nsIPresContext& aPresContext, LineData* aLine)
|
|
{
|
|
if (nsnull != aLine) {
|
|
// Delete our child frames before doing anything else. In particular
|
|
// we do all of this before our base class releases it's hold on the
|
|
// view.
|
|
for (nsIFrame* child = aLine->mFirstChild; child; ) {
|
|
nsIFrame* nextChild;
|
|
child->GetNextSibling(nextChild);
|
|
child->DeleteFrame(aPresContext);
|
|
child = nextChild;
|
|
}
|
|
|
|
while (nsnull != aLine) {
|
|
LineData* next = aLine->mNext;
|
|
delete aLine;
|
|
aLine = next;
|
|
}
|
|
}
|
|
}
|
|
|
|
static LineData*
|
|
LastLine(LineData* aLine)
|
|
{
|
|
if (nsnull != aLine) {
|
|
while (nsnull != aLine->mNext) {
|
|
aLine = aLine->mNext;
|
|
}
|
|
}
|
|
return aLine;
|
|
}
|
|
|
|
static LineData*
|
|
FindLineContaining(LineData* aLine, nsIFrame* aFrame)
|
|
{
|
|
while (nsnull != aLine) {
|
|
if (aLine->Contains(aFrame)) {
|
|
return aLine;
|
|
}
|
|
aLine = aLine->mNext;
|
|
}
|
|
return nsnull;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void
|
|
nsBlockReflowState::BlockBandData::ComputeAvailSpaceRect()
|
|
{
|
|
nsBandTrapezoid* trapezoid = data;
|
|
|
|
if (count > 1) {
|
|
// If there's more than one trapezoid that means there are floaters
|
|
PRInt32 i;
|
|
|
|
// Stop when we get to space occupied by a right floater, or when we've
|
|
// looked at every trapezoid and none are right floaters
|
|
for (i = 0; i < count; i++) {
|
|
nsBandTrapezoid* trapezoid = &data[i];
|
|
if (trapezoid->state != nsBandTrapezoid::Available) {
|
|
const nsStyleDisplay* display;
|
|
if (nsBandTrapezoid::OccupiedMultiple == trapezoid->state) {
|
|
PRInt32 j, numFrames = trapezoid->frames->Count();
|
|
NS_ASSERTION(numFrames > 0, "bad trapezoid frame list");
|
|
for (j = 0; j < numFrames; j++) {
|
|
nsIFrame* f = (nsIFrame*)trapezoid->frames->ElementAt(j);
|
|
f->GetStyleData(eStyleStruct_Display,
|
|
(const nsStyleStruct*&)display);
|
|
if (NS_STYLE_FLOAT_RIGHT == display->mFloats) {
|
|
goto foundRightFloater;
|
|
}
|
|
}
|
|
} else {
|
|
trapezoid->frame->GetStyleData(eStyleStruct_Display,
|
|
(const nsStyleStruct*&)display);
|
|
if (NS_STYLE_FLOAT_RIGHT == display->mFloats) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
foundRightFloater:
|
|
|
|
if (i > 0) {
|
|
trapezoid = &data[i - 1];
|
|
}
|
|
}
|
|
|
|
if (nsBandTrapezoid::Available == trapezoid->state) {
|
|
// The trapezoid is available
|
|
trapezoid->GetRect(availSpace);
|
|
} else {
|
|
const nsStyleDisplay* display;
|
|
|
|
// The trapezoid is occupied. That means there's no available space
|
|
trapezoid->GetRect(availSpace);
|
|
|
|
// XXX Better handle the case of multiple frames
|
|
if (nsBandTrapezoid::Occupied == trapezoid->state) {
|
|
trapezoid->frame->GetStyleData(eStyleStruct_Display,
|
|
(const nsStyleStruct*&)display);
|
|
if (NS_STYLE_FLOAT_LEFT == display->mFloats) {
|
|
availSpace.x = availSpace.XMost();
|
|
}
|
|
}
|
|
availSpace.width = 0;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
nsBlockReflowState::nsBlockReflowState(nsIPresContext& aPresContext,
|
|
const nsHTMLReflowState& aReflowState,
|
|
const nsHTMLReflowMetrics& aMetrics)
|
|
: nsFrameReflowState(aPresContext, aReflowState, aMetrics),
|
|
mLineLayout(aPresContext, aReflowState.spaceManager)
|
|
{
|
|
mInlineReflow = nsnull;
|
|
mLineLayout.Init(this);
|
|
|
|
mSpaceManager = aReflowState.spaceManager;
|
|
|
|
// Translate into our content area and then save the
|
|
// coordinate system origin for later.
|
|
mSpaceManager->Translate(mBorderPadding.left, mBorderPadding.top);
|
|
mSpaceManager->GetTranslation(mSpaceManagerX, mSpaceManagerY);
|
|
|
|
mPresContext = aPresContext;
|
|
mBlock = (nsBlockFrame*) frame;
|
|
mBlock->GetNextInFlow((nsIFrame*&)mNextInFlow);
|
|
mKidXMost = 0;
|
|
|
|
mRunInFrame = nsnull;
|
|
mRunInToFrame = nsnull;
|
|
|
|
mY = 0;
|
|
mUnconstrainedWidth = maxSize.width == NS_UNCONSTRAINEDSIZE;
|
|
mUnconstrainedHeight = maxSize.height == NS_UNCONSTRAINEDSIZE;
|
|
#ifdef NS_DEBUG
|
|
if (!mUnconstrainedWidth && (maxSize.width > 100000)) {
|
|
mBlock->ListTag(stdout);
|
|
printf(": bad parent: maxSize WAS %d,%d\n",
|
|
maxSize.width, maxSize.height);
|
|
maxSize.width = NS_UNCONSTRAINEDSIZE;
|
|
mUnconstrainedWidth = PR_TRUE;
|
|
}
|
|
if (!mUnconstrainedHeight && (maxSize.height > 100000)) {
|
|
mBlock->ListTag(stdout);
|
|
printf(": bad parent: maxSize WAS %d,%d\n",
|
|
maxSize.width, maxSize.height);
|
|
maxSize.height = NS_UNCONSTRAINEDSIZE;
|
|
mUnconstrainedHeight = PR_TRUE;
|
|
}
|
|
#endif
|
|
|
|
mTextAlign = mStyleText->mTextAlign;
|
|
mPrevMarginFlags = 0;
|
|
|
|
nscoord lr = mBorderPadding.left + mBorderPadding.right;
|
|
mY = mBorderPadding.top;
|
|
if (eHTMLFrameConstraint_FixedContent == widthConstraint) {
|
|
// The CSS2 spec says that the width attribute defines the width
|
|
// of the "content area" which does not include the border
|
|
// padding. So we add those back in.
|
|
mBorderArea.width = minWidth + lr;
|
|
mContentArea.width = minWidth;
|
|
}
|
|
else {
|
|
if (mUnconstrainedWidth) {
|
|
mBorderArea.width = NS_UNCONSTRAINEDSIZE;
|
|
mContentArea.width = NS_UNCONSTRAINEDSIZE;
|
|
}
|
|
else {
|
|
mBorderArea.width = maxSize.width;
|
|
mContentArea.width = maxSize.width - lr;
|
|
}
|
|
}
|
|
mBorderArea.height = maxSize.height;
|
|
mContentArea.height = maxSize.height;
|
|
mBottomEdge = maxSize.height;
|
|
if (!mUnconstrainedHeight) {
|
|
mBottomEdge -= mBorderPadding.bottom;
|
|
}
|
|
|
|
mPrevChild = nsnull;
|
|
mFreeList = nsnull;
|
|
mPrevLine = nsnull;
|
|
}
|
|
|
|
nsBlockReflowState::~nsBlockReflowState()
|
|
{
|
|
// Restore the coordinate system
|
|
mSpaceManager->Translate(-mBorderPadding.left, -mBorderPadding.top);
|
|
|
|
LineData* line = mFreeList;
|
|
while (nsnull != line) {
|
|
NS_ASSERTION((0 == line->ChildCount()) && (nsnull == line->mFirstChild),
|
|
"bad free line");
|
|
LineData* next = line->mNext;
|
|
delete line;
|
|
line = next;
|
|
}
|
|
}
|
|
|
|
// Get the available reflow space for the current y coordinate. The
|
|
// available space is relative to our coordinate system (0,0) is our
|
|
// upper left corner.
|
|
void
|
|
nsBlockReflowState::GetAvailableSpace()
|
|
{
|
|
nsISpaceManager* sm = mSpaceManager;
|
|
|
|
#ifdef NS_DEBUG
|
|
// Verify that the caller setup the coordinate system properly
|
|
nscoord wx, wy;
|
|
sm->GetTranslation(wx, wy);
|
|
NS_ASSERTION((wx == mSpaceManagerX) && (wy == mSpaceManagerY),
|
|
"bad coord system");
|
|
#endif
|
|
|
|
// Fill in band data for the specific Y coordinate. Because the
|
|
// space manager is pre-translated into our content-area (so that
|
|
// nested blocks/inlines will line up properly), we have to remove
|
|
// the Y translation to find the band coordinates relative to our
|
|
// inner (content area) upper left corner (0,0).
|
|
sm->GetBandData(mY - mBorderPadding.top, mContentArea, mCurrentBand);
|
|
|
|
// Compute the bounding rect of the available space, i.e. space
|
|
// between any left and right floaters.
|
|
mCurrentBand.ComputeAvailSpaceRect();
|
|
|
|
NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
|
|
("nsBlockReflowState::GetAvailableSpace: band={%d,%d,%d,%d} count=%d",
|
|
mCurrentBand.availSpace.x, mCurrentBand.availSpace.y,
|
|
mCurrentBand.availSpace.width, mCurrentBand.availSpace.height,
|
|
mCurrentBand.count));
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
nsIAtom* nsBlockFrame::gFloaterAtom;
|
|
nsIAtom* nsBlockFrame::gBulletAtom;
|
|
|
|
nsresult
|
|
NS_NewBlockFrame(nsIContent* aContent, nsIFrame* aParentFrame,
|
|
nsIFrame*& aNewFrame, PRUint32 aFlags)
|
|
{
|
|
nsBlockFrame* it = new nsBlockFrame(aContent, aParentFrame);
|
|
if (nsnull == it) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
aNewFrame = it;
|
|
it->SetFlags(aFlags);
|
|
return NS_OK;
|
|
}
|
|
|
|
nsBlockFrame::nsBlockFrame(nsIContent* aContent, nsIFrame* aParent)
|
|
: nsBlockFrameSuper(aContent, aParent)
|
|
{
|
|
// XXX for now these are a memory leak
|
|
if (nsnull == gFloaterAtom) {
|
|
gFloaterAtom = NS_NewAtom("Floater-list");
|
|
}
|
|
if (nsnull == gBulletAtom) {
|
|
gBulletAtom = NS_NewAtom("Bullet-list");
|
|
}
|
|
}
|
|
|
|
nsBlockFrame::~nsBlockFrame()
|
|
{
|
|
NS_IF_RELEASE(mFirstLineStyle);
|
|
NS_IF_RELEASE(mFirstLetterStyle);
|
|
nsTextRun::DeleteTextRuns(mTextRuns);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBlockFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr)
|
|
{
|
|
if (NULL == aInstancePtr) {
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
if (aIID.Equals(kBlockFrameCID)) {
|
|
*aInstancePtr = (void*) this;
|
|
return NS_OK;
|
|
}
|
|
return nsBlockFrameSuper::QueryInterface(aIID, aInstancePtr);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBlockFrame::ReResolveStyleContext(nsIPresContext* aPresContext,
|
|
nsIStyleContext* aParentContext)
|
|
{
|
|
nsIStyleContext* oldContext = mStyleContext;
|
|
nsresult rv = nsFrame::ReResolveStyleContext(aPresContext, aParentContext);
|
|
if (NS_OK != rv) {
|
|
return rv;
|
|
}
|
|
if (oldContext != mStyleContext) {
|
|
// Re-resolve the :first-line pseudo style context
|
|
if (nsnull == mPrevInFlow) {
|
|
nsIStyleContext* newFirstLineStyle =
|
|
aPresContext->ProbePseudoStyleContextFor(mContent,
|
|
nsHTMLAtoms::firstLinePseudo,
|
|
mStyleContext);
|
|
if (newFirstLineStyle != mFirstLineStyle) {
|
|
NS_IF_RELEASE(mFirstLineStyle);
|
|
mFirstLineStyle = newFirstLineStyle;
|
|
}
|
|
else {
|
|
NS_IF_RELEASE(newFirstLineStyle);
|
|
}
|
|
|
|
// Re-resolve the :first-letter pseudo style context
|
|
nsIStyleContext* newFirstLetterStyle =
|
|
aPresContext->ProbePseudoStyleContextFor(mContent,
|
|
nsHTMLAtoms::firstLetterPseudo,
|
|
(nsnull != mFirstLineStyle
|
|
? mFirstLineStyle
|
|
: mStyleContext));
|
|
if (newFirstLetterStyle != mFirstLetterStyle) {
|
|
NS_IF_RELEASE(mFirstLetterStyle);
|
|
mFirstLetterStyle = newFirstLetterStyle;
|
|
}
|
|
else {
|
|
NS_IF_RELEASE(newFirstLetterStyle);
|
|
}
|
|
}
|
|
|
|
// Update the child frames on each line
|
|
LineData* line = mLines;
|
|
while (nsnull != line) {
|
|
nsIFrame* child = line->mFirstChild;
|
|
PRInt32 n = line->mChildCount;
|
|
while ((--n >= 0) && NS_SUCCEEDED(rv)) {
|
|
if (line == mLines) {
|
|
rv = child->ReResolveStyleContext(aPresContext,
|
|
(nsnull != mFirstLineStyle
|
|
? mFirstLineStyle
|
|
: mStyleContext));
|
|
}
|
|
else {
|
|
rv = child->ReResolveStyleContext(aPresContext, mStyleContext);
|
|
}
|
|
child->GetNextSibling(child);
|
|
}
|
|
line = line->mNext;
|
|
}
|
|
|
|
// Update the child frames on each overflow line too
|
|
line = mOverflowLines;
|
|
while (nsnull != line) {
|
|
nsIFrame* child = line->mFirstChild;
|
|
PRInt32 n = line->mChildCount;
|
|
while ((--n >= 0) && NS_SUCCEEDED(rv)) {
|
|
rv = child->ReResolveStyleContext(aPresContext, mStyleContext);
|
|
child->GetNextSibling(child);
|
|
}
|
|
line = line->mNext;
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBlockFrame::SetInitialChildList(nsIPresContext& aPresContext,
|
|
nsIAtom* aListName,
|
|
nsIFrame* aChildList)
|
|
{
|
|
nsresult rv = AppendNewFrames(aPresContext, aChildList);
|
|
if (NS_OK != rv) {
|
|
return rv;
|
|
}
|
|
|
|
// Create list bullet if this is a list-item. Note that this is done
|
|
// here so that RenumberLists will work (it needs the bullets to
|
|
// store the bullet numbers).
|
|
const nsStyleDisplay* styleDisplay;
|
|
GetStyleData(eStyleStruct_Display, (const nsStyleStruct*&) styleDisplay);
|
|
if ((nsnull == mPrevInFlow) &&
|
|
(NS_STYLE_DISPLAY_LIST_ITEM == styleDisplay->mDisplay) &&
|
|
(nsnull == mBullet)) {
|
|
// Create bullet frame
|
|
mBullet = new BulletFrame(mContent, this);
|
|
if (nsnull == mBullet) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
// Resolve style for the bullet frame
|
|
nsIStyleContext* kidSC;
|
|
kidSC = aPresContext.ResolvePseudoStyleContextFor(mContent,
|
|
nsHTMLAtoms::bulletPseudo,
|
|
mStyleContext);
|
|
mBullet->SetStyleContext(&aPresContext, kidSC);
|
|
NS_RELEASE(kidSC);
|
|
|
|
// If the list bullet frame should be positioned inside then add
|
|
// it to the flow now.
|
|
const nsStyleList* styleList;
|
|
GetStyleData(eStyleStruct_List, (const nsStyleStruct*&) styleList);
|
|
if (NS_STYLE_LIST_STYLE_POSITION_INSIDE == styleList->mListStylePosition) {
|
|
InsertNewFrame(aPresContext, this, mBullet, nsnull);
|
|
}
|
|
}
|
|
|
|
// Lookup up the two pseudo style contexts
|
|
if (nsnull == mPrevInFlow) {
|
|
mFirstLineStyle = aPresContext.
|
|
ProbePseudoStyleContextFor(mContent, nsHTMLAtoms::firstLinePseudo,
|
|
mStyleContext);
|
|
mFirstLetterStyle = aPresContext.
|
|
ProbePseudoStyleContextFor(mContent, nsHTMLAtoms::firstLetterPseudo,
|
|
(nsnull != mFirstLineStyle
|
|
? mFirstLineStyle
|
|
: mStyleContext));
|
|
#ifdef NOISY_FIRST_LETTER
|
|
if (nsnull != mFirstLetterStyle) {
|
|
printf("block(%d)@%p: first-letter style found\n",
|
|
ContentIndexInContainer(this), this);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBlockFrame::DeleteFrame(nsIPresContext& aPresContext)
|
|
{
|
|
// When we have a bullet frame and it's not in our child list then
|
|
// we need to delete it ourselves (this is the common case for
|
|
// list-item's that have outside bullets).
|
|
if ((nsnull != mBullet) &&
|
|
((nsnull == mLines) || (mBullet != mLines->mFirstChild))) {
|
|
mBullet->DeleteFrame(aPresContext);
|
|
mBullet = nsnull;
|
|
}
|
|
|
|
DeleteLineList(aPresContext, mLines);
|
|
DeleteLineList(aPresContext, mOverflowLines);
|
|
DeleteFrameList(aPresContext, &mFloaters);
|
|
|
|
return nsBlockFrameSuper::DeleteFrame(aPresContext);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBlockFrame::IsSplittable(nsSplittableType& aIsSplittable) const
|
|
{
|
|
aIsSplittable = NS_FRAME_SPLITTABLE_NON_RECTANGULAR;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBlockFrame::CreateContinuingFrame(nsIPresContext& aCX,
|
|
nsIFrame* aParent,
|
|
nsIStyleContext* aStyleContext,
|
|
nsIFrame*& aContinuingFrame)
|
|
{
|
|
nsBlockFrame* cf = new nsBlockFrame(mContent, aParent);
|
|
if (nsnull == cf) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
PrepareContinuingFrame(aCX, aParent, aStyleContext, cf);
|
|
aContinuingFrame = cf;
|
|
NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
|
|
("nsBlockFrame::CreateContinuingFrame: newFrame=%p", cf));
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBlockFrame::GetFrameName(nsString& aResult) const
|
|
{
|
|
return MakeFrameName("Block", aResult);
|
|
}
|
|
|
|
NS_METHOD
|
|
nsBlockFrame::List(FILE* out, PRInt32 aIndent, nsIListFilter *aFilter) const
|
|
{
|
|
PRInt32 i;
|
|
|
|
// if a filter is present, only output this frame if the filter says
|
|
// we should
|
|
nsAutoString tagString;
|
|
if (nsnull != mContent) {
|
|
nsIAtom* tag;
|
|
mContent->GetTag(tag);
|
|
if (tag != nsnull) {
|
|
tag->ToString(tagString);
|
|
NS_RELEASE(tag);
|
|
}
|
|
}
|
|
PRBool outputMe = (nsnull == aFilter) || aFilter->OutputTag(&tagString);
|
|
if (outputMe) {
|
|
// Indent
|
|
for (i = aIndent; --i >= 0; ) fputs(" ", out);
|
|
|
|
// Output the tag
|
|
ListTag(out);
|
|
nsIView* view;
|
|
GetView(view);
|
|
if (nsnull != view) {
|
|
fprintf(out, " [view=%p]", view);
|
|
}
|
|
|
|
// Output the flow linkage
|
|
if (nsnull != mPrevInFlow) {
|
|
fprintf(out, "prev-in-flow=%p ", mPrevInFlow);
|
|
}
|
|
if (nsnull != mNextInFlow) {
|
|
fprintf(out, "next-in-flow=%p ", mNextInFlow);
|
|
}
|
|
|
|
// Output the rect and state
|
|
out << mRect;
|
|
if (0 != mState) {
|
|
fprintf(out, " [state=%08x]", mState);
|
|
}
|
|
fputs("<\n", out);
|
|
aIndent++;
|
|
}
|
|
|
|
// Output bullet first
|
|
if (nsnull != mBullet) {
|
|
if (outputMe) {
|
|
for (i = aIndent; --i >= 0; ) fputs(" ", out);
|
|
fprintf(out, "bullet <\n");
|
|
}
|
|
mBullet->List(out, aIndent+1, aFilter);
|
|
if (outputMe) {
|
|
for (i = aIndent; --i >= 0; ) fputs(" ", out);
|
|
fputs(">\n", out);
|
|
}
|
|
}
|
|
|
|
// Output the lines
|
|
if (nsnull != mLines) {
|
|
LineData* line = mLines;
|
|
while (nsnull != line) {
|
|
line->List(out, aIndent, aFilter, outputMe);
|
|
line = line->mNext;
|
|
}
|
|
}
|
|
|
|
// Output floaters next
|
|
if (nsnull != mFloaters) {
|
|
if (outputMe) {
|
|
for (i = aIndent; --i >= 0; ) fputs(" ", out);
|
|
fprintf(out, "all-floaters <\n");
|
|
}
|
|
nsIFrame* floater = mFloaters;
|
|
while (nsnull != floater) {
|
|
floater->List(out, aIndent+1, aFilter);
|
|
floater->GetNextSibling(floater);
|
|
}
|
|
if (outputMe) {
|
|
for (i = aIndent; --i >= 0; ) fputs(" ", out);
|
|
fputs(">\n", out);
|
|
}
|
|
}
|
|
|
|
// Output the text-runs
|
|
if (outputMe) {
|
|
if (nsnull != mTextRuns) {
|
|
for (i = aIndent; --i >= 0; ) fputs(" ", out);
|
|
fputs("text-runs <\n", out);
|
|
|
|
ListTextRuns(out, aIndent + 1, mTextRuns);
|
|
|
|
for (i = aIndent; --i >= 0; ) fputs(" ", out);
|
|
fputs(">\n", out);
|
|
}
|
|
|
|
aIndent--;
|
|
for (i = aIndent; --i >= 0; ) fputs(" ", out);
|
|
fputs(">\n", out);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Child frame enumeration
|
|
|
|
NS_IMETHODIMP
|
|
nsBlockFrame::FirstChild(nsIAtom* aListName, nsIFrame*& aFirstChild) const
|
|
{
|
|
if (nsnull == aListName) {
|
|
aFirstChild = (nsnull != mLines) ? mLines->mFirstChild : nsnull;
|
|
return NS_OK;
|
|
}
|
|
else if (aListName == gFloaterAtom) {
|
|
aFirstChild = mFloaters;
|
|
return NS_OK;
|
|
}
|
|
else if (aListName == gBulletAtom) {
|
|
aFirstChild = mBullet;
|
|
return NS_OK;
|
|
}
|
|
aFirstChild = nsnull;
|
|
return NS_ERROR_INVALID_ARG;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBlockFrame::GetAdditionalChildListName(PRInt32 aIndex,
|
|
nsIAtom*& aListName) const
|
|
{
|
|
if (aIndex < 0) {
|
|
return NS_ERROR_INVALID_ARG;
|
|
}
|
|
nsIAtom* atom = nsnull;
|
|
switch (aIndex) {
|
|
case NS_BLOCK_FRAME_FLOATER_LIST_INDEX:
|
|
atom = gFloaterAtom;
|
|
NS_ADDREF(atom);
|
|
break;
|
|
case NS_BLOCK_FRAME_BULLET_LIST_INDEX:
|
|
atom = gBulletAtom;
|
|
NS_ADDREF(atom);
|
|
break;
|
|
}
|
|
aListName = atom;
|
|
return NS_OK;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Reflow methods
|
|
|
|
NS_IMETHODIMP
|
|
nsBlockFrame::IsPercentageBase(PRBool& aBase) const
|
|
{
|
|
aBase = PR_TRUE;
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
nsBlockFrame::TakeRunInFrames(nsBlockFrame* aRunInFrame)
|
|
{
|
|
// Simply steal the run-in-frame's line list and make it our
|
|
// own. XXX Very similar to the logic in DrainOverflowLines...
|
|
LineData* line = aRunInFrame->mLines;
|
|
|
|
// Make all the frames on the mOverflowLines list mine
|
|
nsIFrame* lastFrame = nsnull;
|
|
nsIFrame* frame = line->mFirstChild;
|
|
while (nsnull != frame) {
|
|
nsIFrame* geometricParent;
|
|
nsIFrame* contentParent;
|
|
frame->GetGeometricParent(geometricParent);
|
|
frame->GetContentParent(contentParent);
|
|
if (contentParent == geometricParent) {
|
|
frame->SetContentParent(this);
|
|
}
|
|
frame->SetGeometricParent(this);
|
|
lastFrame = frame;
|
|
frame->GetNextSibling(frame);
|
|
}
|
|
|
|
// Join the line lists
|
|
if (nsnull == mLines) {
|
|
mLines = line;
|
|
}
|
|
else {
|
|
// Join the sibling lists together
|
|
lastFrame->SetNextSibling(mLines->mFirstChild);
|
|
|
|
// Place overflow lines at the front of our line list
|
|
LineData* lastLine = LastLine(line);
|
|
lastLine->mNext = mLines;
|
|
mLines = line;
|
|
}
|
|
|
|
aRunInFrame->mLines = nsnull;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBlockFrame::Reflow(nsIPresContext& aPresContext,
|
|
nsHTMLReflowMetrics& aMetrics,
|
|
const nsHTMLReflowState& aReflowState,
|
|
nsReflowStatus& aStatus)
|
|
{
|
|
NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
|
|
("enter nsBlockFrame::Reflow: maxSize=%d,%d reason=%d",
|
|
aReflowState.maxSize.width,
|
|
aReflowState.maxSize.height,
|
|
aReflowState.reason));
|
|
|
|
// If this is the initial reflow, generate any synthetic content
|
|
// that needs generating.
|
|
if (eReflowReason_Initial == aReflowState.reason) {
|
|
NS_ASSERTION(0 != (NS_FRAME_FIRST_REFLOW & mState), "bad mState");
|
|
}
|
|
else {
|
|
NS_ASSERTION(0 == (NS_FRAME_FIRST_REFLOW & mState), "bad mState");
|
|
}
|
|
|
|
// Replace parent provided reflow state with our own significantly
|
|
// more extensive version.
|
|
nsBlockReflowState state(aPresContext, aReflowState, aMetrics);
|
|
if (NS_BODY_NO_AUTO_MARGINS & mFlags) {
|
|
// Pretend that there is a really big bottom margin preceeding the
|
|
// first line so that it's margin is never applied.
|
|
}
|
|
if (NS_BODY_THE_BODY & mFlags) {
|
|
state.mIsMarginRoot = PR_TRUE;
|
|
}
|
|
|
|
// Prepare inline-reflow engine
|
|
nsInlineReflow inlineReflow(state.mLineLayout, state, this, PR_TRUE);
|
|
state.mInlineReflow = &inlineReflow;
|
|
state.mLineLayout.PushInline(&inlineReflow);
|
|
|
|
nsresult rv = NS_OK;
|
|
if (eReflowReason_Initial == state.reason) {
|
|
if (nsnull != aReflowState.mRunInFrame) {
|
|
#ifdef NOISY_RUNIN
|
|
ListTag(stdout);
|
|
printf(": run-in from: ");
|
|
aReflowState.mRunInFrame->ListTag(stdout);
|
|
printf("\n");
|
|
#endif
|
|
// Take frames away from the run-in frame
|
|
TakeRunInFrames(aReflowState.mRunInFrame);
|
|
}
|
|
if (!DrainOverflowLines()) {
|
|
RenumberLists(state);
|
|
rv = InitialReflow(state);
|
|
}
|
|
else {
|
|
RenumberLists(state);
|
|
rv = ResizeReflow(state);
|
|
}
|
|
mState &= ~NS_FRAME_FIRST_REFLOW;
|
|
}
|
|
else if (eReflowReason_Incremental == state.reason) {
|
|
#if XXX
|
|
// We can have an overflow here if our parent doesn't bother to
|
|
// continue us
|
|
DrainOverflowLines();
|
|
#endif
|
|
nsIFrame* target;
|
|
state.reflowCommand->GetTarget(target);
|
|
if (this == target) {
|
|
RenumberLists(state);
|
|
nsIReflowCommand::ReflowType type;
|
|
state.reflowCommand->GetType(type);
|
|
switch (type) {
|
|
case nsIReflowCommand::FrameAppended:
|
|
rv = FrameAppendedReflow(state);
|
|
break;
|
|
|
|
case nsIReflowCommand::FrameInserted:
|
|
rv = FrameInsertedReflow(state);
|
|
break;
|
|
|
|
case nsIReflowCommand::FrameRemoved:
|
|
rv = FrameRemovedReflow(state);
|
|
break;
|
|
|
|
case nsIReflowCommand::StyleChanged:
|
|
rv = StyleChangedReflow(state);
|
|
break;
|
|
|
|
default:
|
|
// Map any other incremental operations into full reflows
|
|
rv = ResizeReflow(state);
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
// Get next frame in reflow command chain
|
|
state.reflowCommand->GetNext(state.mNextRCFrame);
|
|
|
|
// Now do the reflow
|
|
rv = ChildIncrementalReflow(state);
|
|
}
|
|
}
|
|
else if ((eReflowReason_Resize == state.reason) ||
|
|
(eReflowReason_StyleChange == state.reason)) {
|
|
DrainOverflowLines();
|
|
rv = ResizeReflow(state);
|
|
}
|
|
|
|
// Compute our final size
|
|
ComputeFinalSize(state, aMetrics);
|
|
|
|
BuildFloaterList();
|
|
|
|
#ifdef NS_DEBUG
|
|
if (GetVerifyTreeEnable()) {
|
|
VerifyChildCount(mLines);
|
|
VerifyLines(mLines);
|
|
}
|
|
#endif
|
|
aStatus = rv;
|
|
NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
|
|
("exit nsBlockFrame::Reflow: size=%d,%d rv=%x",
|
|
aMetrics.width, aMetrics.height, rv));
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
nsBlockFrame::BuildFloaterList()
|
|
{
|
|
nsIFrame* head = nsnull;
|
|
nsIFrame* current = nsnull;
|
|
LineData* line = mLines;
|
|
while (nsnull != line) {
|
|
if (nsnull != line->mFloaters) {
|
|
nsVoidArray& array = *line->mFloaters;
|
|
PRInt32 i, n = array.Count();
|
|
for (i = 0; i < n; i++) {
|
|
nsPlaceholderFrame* ph = (nsPlaceholderFrame*) array[i];
|
|
nsIFrame* floater = ph->GetAnchoredItem();
|
|
if (nsnull == head) {
|
|
current = head = floater;
|
|
}
|
|
else {
|
|
current->SetNextSibling(floater);
|
|
current = floater;
|
|
}
|
|
}
|
|
}
|
|
line = line->mNext;
|
|
}
|
|
|
|
// Terminate end of floater list just in case a floater was removed
|
|
if (nsnull != current) {
|
|
current->SetNextSibling(nsnull);
|
|
}
|
|
mFloaters = head;
|
|
}
|
|
|
|
void
|
|
nsBlockFrame::RenumberLists(nsBlockReflowState& aState)
|
|
{
|
|
// Setup initial list ordinal value
|
|
PRInt32 ordinal = 1;
|
|
nsIHTMLContent* hc;
|
|
if (mContent && (NS_OK == mContent->QueryInterface(kIHTMLContentIID, (void**) &hc))) {
|
|
nsHTMLValue value;
|
|
if (NS_CONTENT_ATTR_HAS_VALUE ==
|
|
hc->GetAttribute(nsHTMLAtoms::start, value)) {
|
|
if (eHTMLUnit_Integer == value.GetUnit()) {
|
|
ordinal = value.GetIntValue();
|
|
if (ordinal <= 0) {
|
|
ordinal = 1;
|
|
}
|
|
}
|
|
}
|
|
NS_RELEASE(hc);
|
|
}
|
|
aState.mNextListOrdinal = ordinal;
|
|
|
|
// Get to first-in-flow
|
|
nsBlockFrame* block = this;
|
|
while (nsnull != block->mPrevInFlow) {
|
|
block = (nsBlockFrame*) block->mPrevInFlow;
|
|
}
|
|
|
|
// For each flow-block...
|
|
while (nsnull != block) {
|
|
// For each frame in the flow-block...
|
|
nsIFrame* frame = block->mLines ? block->mLines->mFirstChild : nsnull;
|
|
while (nsnull != frame) {
|
|
// If the frame is a list-item and the frame implements our
|
|
// block frame API then get it's bullet and set the list item
|
|
// ordinal.
|
|
const nsStyleDisplay* display;
|
|
frame->GetStyleData(eStyleStruct_Display,
|
|
(const nsStyleStruct*&) display);
|
|
if (NS_STYLE_DISPLAY_LIST_ITEM == display->mDisplay) {
|
|
// Make certain that the frame isa block-frame in case
|
|
// something foriegn has crept in.
|
|
nsBlockFrame* listItem;
|
|
if (NS_OK == frame->QueryInterface(kBlockFrameCID,
|
|
(void**) &listItem)) {
|
|
if (nsnull != listItem->mBullet) {
|
|
listItem->mBullet->SetListItemOrdinal(aState);
|
|
}
|
|
}
|
|
}
|
|
frame->GetNextSibling(frame);
|
|
}
|
|
block = (nsBlockFrame*) block->mNextInFlow;
|
|
}
|
|
}
|
|
|
|
void
|
|
nsBlockFrame::ComputeFinalSize(nsBlockReflowState& aState,
|
|
nsHTMLReflowMetrics& aMetrics)
|
|
{
|
|
// XXX handle floater problems this way...
|
|
PRBool isFixedWidth =
|
|
eHTMLFrameConstraint_FixedContent == aState.widthConstraint;
|
|
PRBool isFixedHeight =
|
|
eHTMLFrameConstraint_FixedContent == aState.heightConstraint;
|
|
#if 0
|
|
if (NS_STYLE_FLOAT_NONE != aState.mStyleDisplay->mFloats) {
|
|
isFixedWidth = PR_FALSE;
|
|
isFixedHeight = PR_FALSE;
|
|
}
|
|
#else
|
|
if (NS_BODY_SHRINK_WRAP & mFlags) {
|
|
isFixedWidth = PR_FALSE;
|
|
isFixedHeight = PR_FALSE;
|
|
}
|
|
#endif
|
|
|
|
// Compute final width
|
|
if (isFixedWidth) {
|
|
// Use style defined width
|
|
aMetrics.width = aState.mBorderPadding.left +
|
|
aState.minWidth + aState.mBorderPadding.right;
|
|
}
|
|
else {
|
|
nscoord computedWidth = aState.mKidXMost + aState.mBorderPadding.right;
|
|
PRBool compact = PR_FALSE;
|
|
if (NS_STYLE_DISPLAY_COMPACT == aState.mStyleDisplay->mDisplay) {
|
|
// If we are display: compact AND we have no lines or we have
|
|
// exactly one line and that line is not a block line AND that
|
|
// line doesn't end in a BR of any sort THEN we remain a compact
|
|
// frame.
|
|
if ((nsnull == mLines) ||
|
|
((nsnull == mLines->mNext) && !mLines->IsBlock() &&
|
|
(NS_STYLE_CLEAR_NONE == mLines->mBreakType) &&
|
|
(computedWidth <= aState.mCompactMarginWidth))) {
|
|
compact = PR_TRUE;
|
|
}
|
|
}
|
|
|
|
// There are two options here. We either shrink wrap around our
|
|
// contents or we fluff out to the maximum available width. Note:
|
|
// We always shrink wrap when given an unconstrained width.
|
|
if ((0 == (NS_BODY_SHRINK_WRAP & mFlags)) &&
|
|
!aState.mUnconstrainedWidth &&
|
|
!compact) {
|
|
// Fluff out to the max width if we aren't already that wide
|
|
if (computedWidth < aState.maxSize.width) {
|
|
computedWidth = aState.maxSize.width;
|
|
}
|
|
}
|
|
aMetrics.width = computedWidth;
|
|
}
|
|
|
|
// Compute final height
|
|
if (isFixedHeight) {
|
|
// Use style defined height
|
|
aMetrics.height = aState.mBorderPadding.top +
|
|
aState.minHeight + aState.mBorderPadding.bottom;
|
|
}
|
|
else {
|
|
// Shrink wrap our height around our contents.
|
|
if (aState.mIsMarginRoot) {
|
|
// When we are a margin root make sure that our last childs
|
|
// bottom margin is fully applied.
|
|
if ((NS_BODY_NO_AUTO_MARGINS & mFlags) &&
|
|
(NS_CARRIED_BOTTOM_MARGIN_IS_AUTO & aState.mPrevMarginFlags)) {
|
|
// Do not apply last auto margin when the last childs margin
|
|
// is auto and we are configured to ignore them.
|
|
}
|
|
else {
|
|
aState.mY += aState.mPrevBottomMargin;
|
|
}
|
|
}
|
|
aState.mY += aState.mBorderPadding.bottom;
|
|
aMetrics.height = aState.mY;
|
|
}
|
|
|
|
// Return top and bottom margin information
|
|
if (aState.mIsMarginRoot) {
|
|
aMetrics.mCarriedOutTopMargin = 0;
|
|
aMetrics.mCarriedOutBottomMargin = 0;
|
|
aMetrics.mCarriedOutMarginFlags = 0;
|
|
}
|
|
else {
|
|
aMetrics.mCarriedOutTopMargin = aState.mCollapsedTopMargin;
|
|
aMetrics.mCarriedOutBottomMargin = aState.mPrevBottomMargin;
|
|
aMetrics.mCarriedOutMarginFlags = aState.mCarriedOutMarginFlags;
|
|
if (NS_CARRIED_BOTTOM_MARGIN_IS_AUTO & aState.mPrevMarginFlags) {
|
|
aMetrics.mCarriedOutMarginFlags |= NS_CARRIED_BOTTOM_MARGIN_IS_AUTO;
|
|
}
|
|
}
|
|
|
|
// Special check for zero sized content: If our content is zero
|
|
// sized then we collapse into nothingness.
|
|
if ((eHTMLFrameConstraint_Unconstrained == aState.widthConstraint) &&
|
|
(eHTMLFrameConstraint_Unconstrained == aState.heightConstraint) &&
|
|
((0 == aState.mKidXMost - aState.mBorderPadding.left) &&
|
|
(0 == aState.mY - aState.mBorderPadding.top))) {
|
|
aMetrics.width = 0;
|
|
aMetrics.height = 0;
|
|
}
|
|
|
|
aMetrics.ascent = aMetrics.height;
|
|
aMetrics.descent = 0;
|
|
|
|
// XXX this needs ALOT OF REWORKING
|
|
if (aState.mComputeMaxElementSize) {
|
|
nscoord maxWidth, maxHeight;
|
|
|
|
if (aState.mNoWrap) {
|
|
maxWidth = 0;
|
|
maxHeight = 0;
|
|
|
|
// Find the maximum line-box width and use that as the
|
|
// max-element width
|
|
LineData* line = mLines;
|
|
while (nsnull != line) {
|
|
nscoord xm = line->mBounds.XMost();
|
|
if (xm > maxWidth) {
|
|
maxWidth = xm;
|
|
}
|
|
line = line->mNext;
|
|
}
|
|
|
|
// XXX winging it!
|
|
maxHeight = aState.mMaxElementSize.height +
|
|
aState.mBorderPadding.top + aState.mBorderPadding.bottom;
|
|
}
|
|
else {
|
|
maxWidth = aState.mMaxElementSize.width +
|
|
aState.mBorderPadding.left + aState.mBorderPadding.right;
|
|
maxHeight = aState.mMaxElementSize.height +
|
|
aState.mBorderPadding.top + aState.mBorderPadding.bottom;
|
|
|
|
// XXX This is still an approximation of the truth:
|
|
|
|
// 1. it doesn't take into account that after a floater is
|
|
// placed we always place at least *something* on the line.
|
|
|
|
// 2. if a floater doesn't start at the left margin (because
|
|
// it's placed relative to a floater on a preceeding line, say),
|
|
// then we get the wrong answer.
|
|
|
|
LineData* line = mLines;
|
|
while (nsnull != line) {
|
|
if (nsnull != line->mFloaters) {
|
|
nsRect r;
|
|
nsMargin floaterMargin;
|
|
|
|
// Sum up the widths of the floaters on this line
|
|
PRInt32 sum = 0;
|
|
PRInt32 n = line->mFloaters->Count();
|
|
for (PRInt32 i = 0; i < n; i++) {
|
|
nsPlaceholderFrame* placeholder = (nsPlaceholderFrame*)
|
|
line->mFloaters->ElementAt(i);
|
|
nsIFrame* floater = placeholder->GetAnchoredItem();
|
|
floater->GetRect(r);
|
|
const nsStyleDisplay* floaterDisplay;
|
|
const nsStyleSpacing* floaterSpacing;
|
|
floater->GetStyleData(eStyleStruct_Display,
|
|
(const nsStyleStruct*&)floaterDisplay);
|
|
floater->GetStyleData(eStyleStruct_Spacing,
|
|
(const nsStyleStruct*&)floaterSpacing);
|
|
|
|
// Compute the margin for the floater (again!)
|
|
nsHTMLReflowState::ComputeMarginFor(floater, &aState,
|
|
floaterMargin);
|
|
|
|
nscoord width = r.width + floaterMargin.left + floaterMargin.right;
|
|
NS_ASSERTION((NS_STYLE_FLOAT_LEFT == floaterDisplay->mFloats) ||
|
|
(NS_STYLE_FLOAT_RIGHT == floaterDisplay->mFloats),
|
|
"invalid float type");
|
|
sum += width;
|
|
}
|
|
|
|
// See if we need a larger max-element width
|
|
if (sum > maxWidth) {
|
|
maxWidth = sum;
|
|
}
|
|
}
|
|
line = line->mNext;
|
|
}
|
|
}
|
|
|
|
// Store away the final value
|
|
aMetrics.maxElementSize->width = maxWidth;
|
|
aMetrics.maxElementSize->height = maxHeight;
|
|
#ifdef NOISY_MAX_ELEMENT_SIZE
|
|
ListTag(stdout);
|
|
printf(": max-element-size:%d,%d desired:%d,%d\n",
|
|
maxWidth, maxHeight, aMetrics.width, aMetrics.height);
|
|
#endif
|
|
}
|
|
|
|
// Compute the combined area of our children
|
|
// XXX take into account the overflow->clip property!
|
|
nscoord x0 = 0, y0 = 0, x1 = aMetrics.width, y1 = aMetrics.height;
|
|
LineData* line = mLines;
|
|
while (nsnull != line) {
|
|
// Compute min and max x/y values for the reflowed frame's
|
|
// combined areas
|
|
nscoord x = line->mCombinedArea.x;
|
|
nscoord y = line->mCombinedArea.y;
|
|
nscoord xmost = x + line->mCombinedArea.width;
|
|
nscoord ymost = y + line->mCombinedArea.height;
|
|
if (x < x0) x0 = x;
|
|
if (xmost > x1) x1 = xmost;
|
|
if (y < y0) y0 = y;
|
|
if (ymost > y1) y1 = ymost;
|
|
|
|
// If the line has floaters, factor those in as well
|
|
nsVoidArray* floaters = line->mFloaters;
|
|
if (nsnull != floaters) {
|
|
PRInt32 i, n = floaters->Count();
|
|
for (i = 0; i < n; i++) {
|
|
nsPlaceholderFrame* ph = (nsPlaceholderFrame*) floaters->ElementAt(i);
|
|
nsIFrame* frame = ph->GetAnchoredItem();
|
|
// XXX This is wrong! The floater may have a combined area
|
|
// that exceeds its bounding box!
|
|
nsRect r;
|
|
frame->GetRect(r);
|
|
if (r.x < x0) x0 = r.x;
|
|
if (r.XMost() > x1) x1 = r.XMost();
|
|
if (r.y < y0) y0 = r.y;
|
|
if (r.YMost() > y1) y1 = r.YMost();
|
|
}
|
|
}
|
|
line = line->mNext;
|
|
}
|
|
if (nsnull != mBullet) {
|
|
nsRect r;
|
|
mBullet->GetRect(r);
|
|
if (r.x < x0) x0 = r.x;
|
|
if (r.XMost() > x1) x1 = r.XMost();
|
|
if (r.y < y0) y0 = r.y;
|
|
if (r.YMost() > y1) y1 = r.YMost();
|
|
}
|
|
aMetrics.mCombinedArea.x = x0;
|
|
aMetrics.mCombinedArea.y = y0;
|
|
aMetrics.mCombinedArea.width = x1 - x0;
|
|
aMetrics.mCombinedArea.height = y1 - y0;
|
|
|
|
// If the combined area of our children exceeds our bounding box
|
|
// then set the NS_FRAME_OUTSIDE_CHILDREN flag, otherwise clear it.
|
|
if ((aMetrics.mCombinedArea.x < 0) ||
|
|
(aMetrics.mCombinedArea.y < 0) ||
|
|
(aMetrics.mCombinedArea.XMost() > aMetrics.width) ||
|
|
(aMetrics.mCombinedArea.YMost() > aMetrics.height)) {
|
|
mState |= NS_FRAME_OUTSIDE_CHILDREN;
|
|
}
|
|
else {
|
|
mState &= ~NS_FRAME_OUTSIDE_CHILDREN;
|
|
}
|
|
}
|
|
|
|
nsresult
|
|
nsBlockFrame::AppendNewFrames(nsIPresContext& aPresContext,
|
|
nsIFrame* aNewFrame)
|
|
{
|
|
// Get our last line and then get its last child
|
|
nsIFrame* lastFrame;
|
|
LineData* lastLine = LastLine(mLines);
|
|
if (nsnull != lastLine) {
|
|
lastFrame = lastLine->LastChild();
|
|
} else {
|
|
lastFrame = nsnull;
|
|
}
|
|
|
|
// Add the new frames to the sibling list; wrap any frames that
|
|
// require wrapping
|
|
if (nsnull != lastFrame) {
|
|
lastFrame->SetNextSibling(aNewFrame);
|
|
}
|
|
nsresult rv;
|
|
|
|
// Make sure that new inlines go onto the end of the lastLine when
|
|
// the lastLine is mapping inline frames.
|
|
PRInt32 pendingInlines = 0;
|
|
if (nsnull != lastLine) {
|
|
if (!lastLine->IsBlock()) {
|
|
pendingInlines = 1;
|
|
}
|
|
}
|
|
|
|
// Now create some lines for the new frames
|
|
nsIFrame* prevFrame = lastFrame;
|
|
for (nsIFrame* frame = aNewFrame; nsnull != frame; frame->GetNextSibling(frame)) {
|
|
// See if the child is a block or non-block
|
|
const nsStyleDisplay* kidDisplay;
|
|
rv = frame->GetStyleData(eStyleStruct_Display,
|
|
(const nsStyleStruct*&) kidDisplay);
|
|
if (NS_OK != rv) {
|
|
return rv;
|
|
}
|
|
const nsStylePosition* kidPosition;
|
|
rv = frame->GetStyleData(eStyleStruct_Position,
|
|
(const nsStyleStruct*&) kidPosition);
|
|
if (NS_OK != rv) {
|
|
return rv;
|
|
}
|
|
PRBool isBlock =
|
|
nsLineLayout::TreatFrameAsBlock(kidDisplay, kidPosition);
|
|
|
|
// See if we need to move the frame outside of the flow, and insert a
|
|
// placeholder frame in its place
|
|
nsIFrame* placeholder;
|
|
if (MoveFrameOutOfFlow(aPresContext, frame, kidDisplay, kidPosition,
|
|
placeholder)) {
|
|
// Reset the previous frame's next sibling pointer
|
|
if (nsnull != prevFrame) {
|
|
prevFrame->SetNextSibling(placeholder);
|
|
}
|
|
|
|
// The placeholder frame is always inline
|
|
frame = placeholder;
|
|
isBlock = PR_FALSE;
|
|
}
|
|
else {
|
|
// Wrap the frame in a view if necessary
|
|
nsIStyleContext* kidSC;
|
|
frame->GetStyleContext(kidSC);
|
|
rv = CreateViewForFrame(aPresContext, frame, kidSC, PR_FALSE);
|
|
NS_RELEASE(kidSC);
|
|
if (NS_OK != rv) {
|
|
return rv;
|
|
}
|
|
}
|
|
|
|
// If the child is an inline then add it to the lastLine (if it's
|
|
// an inline line, otherwise make a new line). If the child is a
|
|
// block then make a new line and put the child in that line.
|
|
if (isBlock) {
|
|
// If the previous line has pending inline data to be reflowed,
|
|
// do so now.
|
|
if (0 != pendingInlines) {
|
|
// Set this to true in case we don't end up reflowing all of the
|
|
// frames on the line (because they end up being pushed).
|
|
lastLine->MarkDirty();
|
|
pendingInlines = 0;
|
|
}
|
|
|
|
// Create a line for the block
|
|
LineData* line = new LineData(frame, 1, LINE_IS_BLOCK);
|
|
if (nsnull == line) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
if (nsnull == lastLine) {
|
|
mLines = line;
|
|
}
|
|
else {
|
|
lastLine->mNext = line;
|
|
}
|
|
lastLine = line;
|
|
}
|
|
else {
|
|
// Queue up the inlines for reflow later on
|
|
if (0 == pendingInlines) {
|
|
LineData* line = new LineData(frame, 0, 0);
|
|
if (nsnull == line) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
if (nsnull == lastLine) {
|
|
mLines = line;
|
|
}
|
|
else {
|
|
lastLine->mNext = line;
|
|
}
|
|
lastLine = line;
|
|
}
|
|
lastLine->mChildCount++;
|
|
pendingInlines++;
|
|
}
|
|
|
|
// Remember the previous frame
|
|
prevFrame = frame;
|
|
}
|
|
|
|
if (0 != pendingInlines) {
|
|
// Set this to true in case we don't end up reflowing all of the
|
|
// frames on the line (because they end up being pushed).
|
|
lastLine->MarkDirty();
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsBlockFrame::InitialReflow(nsBlockReflowState& aState)
|
|
{
|
|
// Generate text-run information
|
|
nsresult rv = FindTextRuns(aState);
|
|
if (NS_OK != rv) {
|
|
return rv;
|
|
}
|
|
|
|
// Reflow everything
|
|
aState.GetAvailableSpace();
|
|
return ResizeReflow(aState);
|
|
}
|
|
|
|
void
|
|
nsBlockFrame::RecoverLineMargins(nsBlockReflowState& aState,
|
|
LineData* aLine,
|
|
nscoord& aTopMarginResult,
|
|
nscoord& aBottomMarginResult)
|
|
{
|
|
nscoord childsCarriedOutTopMargin = aLine->mCarriedOutTopMargin;
|
|
nscoord childsCarriedOutBottomMargin = aLine->mCarriedOutBottomMargin;
|
|
nscoord childsTopMargin = 0;
|
|
nscoord childsBottomMargin = 0;
|
|
if (aLine->IsBlock()) {
|
|
// Recover the block frames computed bottom margin value
|
|
nsIFrame* frame = aLine->mFirstChild;
|
|
if (nsnull != frame) {
|
|
const nsStyleSpacing* spacing;
|
|
frame->GetStyleData(eStyleStruct_Spacing,
|
|
(const nsStyleStruct*&) spacing);
|
|
if (nsnull != spacing) {
|
|
nsMargin margin;
|
|
nsInlineReflow::CalculateBlockMarginsFor(aState.mPresContext, frame,
|
|
&aState, spacing, margin);
|
|
childsTopMargin = margin.top;
|
|
childsBottomMargin = margin.bottom;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Collapse the carried-out-margins with the childs margins
|
|
aBottomMarginResult =
|
|
nsInlineReflow::MaxMargin(childsCarriedOutBottomMargin, childsBottomMargin);
|
|
aTopMarginResult =
|
|
nsInlineReflow::MaxMargin(childsCarriedOutTopMargin, childsTopMargin);
|
|
}
|
|
|
|
nsresult
|
|
nsBlockFrame::FrameAppendedReflow(nsBlockReflowState& aState)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
|
|
// Get the first of the newly appended frames
|
|
nsIFrame* firstAppendedFrame;
|
|
aState.reflowCommand->GetChildFrame(firstAppendedFrame);
|
|
|
|
// Add the new frames to the child list, and create new lines. Each
|
|
// impacted line will be marked dirty
|
|
AppendNewFrames(aState.mPresContext, firstAppendedFrame);
|
|
RenumberLists(aState);
|
|
|
|
// Generate text-run information
|
|
rv = FindTextRuns(aState);
|
|
if (NS_OK != rv) {
|
|
return rv;
|
|
}
|
|
|
|
// Recover our reflow state
|
|
LineData* firstDirtyLine = mLines;
|
|
LineData* lastCleanLine = nsnull;
|
|
PRBool haveRunIn = PR_FALSE;
|
|
while (nsnull != firstDirtyLine) {
|
|
if (firstDirtyLine->IsDirty()) {
|
|
break;
|
|
}
|
|
|
|
// Recover run-in state
|
|
if (firstDirtyLine->IsBlock()) {
|
|
nsIFrame* frame = firstDirtyLine->mFirstChild;
|
|
const nsStyleDisplay* display;
|
|
frame->GetStyleData(eStyleStruct_Display,
|
|
(const nsStyleStruct*&) display);
|
|
if ((NS_STYLE_DISPLAY_RUN_IN == display->mDisplay) ||
|
|
(NS_STYLE_DISPLAY_COMPACT == display->mDisplay)) {
|
|
haveRunIn = PR_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Recover xmost
|
|
nscoord xmost = firstDirtyLine->mBounds.XMost();
|
|
if (xmost > aState.mKidXMost) {
|
|
aState.mKidXMost = xmost;
|
|
}
|
|
|
|
// Recover the mPrevBottomMargin and the mCollapsedTopMargin values
|
|
nscoord topMargin, bottomMargin;
|
|
RecoverLineMargins(aState, firstDirtyLine, topMargin, bottomMargin);
|
|
if (0 == firstDirtyLine->mBounds.height) {
|
|
// For zero height lines, collapse the lines top and bottom
|
|
// margins together to produce the effective bottomMargin value.
|
|
bottomMargin = nsInlineReflow::MaxMargin(topMargin, bottomMargin);
|
|
bottomMargin = nsInlineReflow::MaxMargin(aState.mPrevBottomMargin,
|
|
bottomMargin);
|
|
}
|
|
else {
|
|
aState.mPrevMarginFlags = firstDirtyLine->GetMarginFlags();
|
|
}
|
|
aState.mPrevBottomMargin = bottomMargin;
|
|
|
|
// Advance Y to be below the line.
|
|
aState.mY = firstDirtyLine->mBounds.YMost();
|
|
|
|
// Advance to the next line
|
|
lastCleanLine = firstDirtyLine;
|
|
firstDirtyLine = firstDirtyLine->mNext;
|
|
}
|
|
|
|
if (haveRunIn) {
|
|
// XXX For now, reflow the world when we contain a frame that is a
|
|
// run-in/compact frame. (lazy code)
|
|
lastCleanLine = nsnull;
|
|
firstDirtyLine = mLines;
|
|
aState.mY = aState.mBorderPadding.top;
|
|
aState.mPrevMarginFlags = 0;
|
|
aState.mPrevBottomMargin = 0;
|
|
}
|
|
|
|
if (nsnull != lastCleanLine) {
|
|
// Unplace any floaters on the lastCleanLine and every line that
|
|
// follows it (which shouldn't be much given that this is
|
|
// frame-appended reflow)
|
|
LineData* line = lastCleanLine;
|
|
while (nsnull != line) {
|
|
line->UnplaceFloaters(aState.mSpaceManager);
|
|
line = line->mNext;
|
|
}
|
|
|
|
// Place any floaters the last clean line has
|
|
if (nsnull != lastCleanLine->mFloaters) {
|
|
aState.mCurrentLine = lastCleanLine;
|
|
aState.PlaceFloaters(lastCleanLine->mFloaters, PR_TRUE);
|
|
}
|
|
|
|
// Apply any clear-after semantics the line might have
|
|
//XXX nscoord y0, dy;
|
|
switch (lastCleanLine->mBreakType) {
|
|
case NS_STYLE_CLEAR_LEFT:
|
|
case NS_STYLE_CLEAR_RIGHT:
|
|
case NS_STYLE_CLEAR_LEFT_AND_RIGHT:
|
|
NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
|
|
("line %p: ClearFloaters: y=%d, break-type=%d\n",
|
|
lastCleanLine, aState.mY, lastCleanLine->mBreakType));
|
|
//XXX y0 = aState.mY;
|
|
aState.ClearFloaters(lastCleanLine->mBreakType);
|
|
#if 0
|
|
dy = aState.mY - y0;
|
|
if (dy > aState.mPrevBottomMargin) {
|
|
aState.mPrevBottomMargin = dy;
|
|
aState.mPrevMarginFlags |= ALREADY_APPLIED_BOTTOM_MARGIN;
|
|
}
|
|
#endif
|
|
NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
|
|
(" cleared floaters, now y=%d\n", aState.mY));
|
|
break;
|
|
}
|
|
}
|
|
|
|
aState.GetAvailableSpace();
|
|
NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
|
|
("nsBlockReflowState::FrameAppendedReflow: y=%d firstDirtyLine=%p",
|
|
aState.mY, firstDirtyLine));
|
|
|
|
// Reflow lines from there forward
|
|
aState.mPrevLine = lastCleanLine;
|
|
return ReflowLinesAt(aState, firstDirtyLine);
|
|
}
|
|
|
|
// XXX keep the text-run data in the first-in-flow of the block
|
|
nsresult
|
|
nsBlockFrame::FindTextRuns(nsBlockReflowState& aState)
|
|
{
|
|
// Destroy old run information first
|
|
nsTextRun::DeleteTextRuns(mTextRuns);
|
|
mTextRuns = nsnull;
|
|
aState.mLineLayout.ResetTextRuns();
|
|
|
|
// Ask each child that implements nsIInlineReflow to find its text runs
|
|
LineData* line = mLines;
|
|
while (nsnull != line) {
|
|
if (!line->IsBlock()) {
|
|
nsIFrame* frame = line->mFirstChild;
|
|
PRInt32 n = line->ChildCount();
|
|
while (--n >= 0) {
|
|
nsIHTMLReflow* hr;
|
|
if (NS_OK == frame->QueryInterface(kIHTMLReflowIID, (void**)&hr)) {
|
|
nsresult rv = hr->FindTextRuns(aState.mLineLayout);
|
|
if (NS_OK != rv) {
|
|
return rv;
|
|
}
|
|
}
|
|
else {
|
|
// A frame that doesn't implement nsIHTMLReflow isn't text
|
|
// therefore it will end an open text run.
|
|
aState.mLineLayout.EndTextRun();
|
|
}
|
|
frame->GetNextSibling(frame);
|
|
}
|
|
}
|
|
else {
|
|
// A frame that doesn't implement nsIInlineReflow isn't text
|
|
// therefore it will end an open text run.
|
|
aState.mLineLayout.EndTextRun();
|
|
}
|
|
line = line->mNext;
|
|
}
|
|
aState.mLineLayout.EndTextRun();
|
|
|
|
// Now take the text-runs away from the line layout engine.
|
|
mTextRuns = aState.mLineLayout.TakeTextRuns();
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
// XXX rewrite this
|
|
nsresult
|
|
nsBlockFrame::FrameInsertedReflow(nsBlockReflowState& aState)
|
|
{
|
|
// Get the inserted frame
|
|
nsIFrame* newFrame;
|
|
aState.reflowCommand->GetChildFrame(newFrame);
|
|
|
|
// Get the previous sibling frame
|
|
nsIFrame* prevSibling;
|
|
aState.reflowCommand->GetPrevSiblingFrame(prevSibling);
|
|
|
|
// Insert the frame. This marks the line dirty...
|
|
InsertNewFrame(aState.mPresContext, this, newFrame, prevSibling);
|
|
|
|
#if XXX
|
|
LineData* line = mLines;
|
|
while (nsnull != line->mNext) {
|
|
if (line->IsDirty()) {
|
|
break;
|
|
}
|
|
line = line->mNext;
|
|
}
|
|
NS_ASSERTION(nsnull != line, "bad inserted reflow");
|
|
//XXX return ReflowDirtyLines(aState, line);
|
|
#endif
|
|
|
|
// XXX Correct implementation: reflow the dirty lines only; all
|
|
// other lines can be moved; recover state before first dirty line.
|
|
|
|
// XXX temporary
|
|
aState.mSpaceManager->ClearRegions();
|
|
aState.GetAvailableSpace();
|
|
aState.mPrevLine = nsnull;
|
|
return ReflowLinesAt(aState, mLines);
|
|
}
|
|
|
|
// XXX rewrite this
|
|
nsresult
|
|
nsBlockFrame::FrameRemovedReflow(nsBlockReflowState& aState)
|
|
{
|
|
if (nsnull == mLines) {
|
|
return NS_OK;
|
|
}
|
|
|
|
// Get the deleted frame
|
|
nsIFrame* deletedFrame;
|
|
aState.reflowCommand->GetChildFrame(deletedFrame);
|
|
|
|
// Find the previous sibling frame
|
|
nsIFrame* prevSibling = nsnull;
|
|
for (nsIFrame* f = mLines->mFirstChild; f != deletedFrame;
|
|
f->GetNextSibling(f)) {
|
|
if (nsnull == f) {
|
|
// We didn't find the deleted frame in our child list
|
|
NS_WARNING("Can't find deleted frame");
|
|
return NS_OK;
|
|
}
|
|
|
|
prevSibling = f;
|
|
}
|
|
|
|
// Find the line that contains deletedFrame; we also find the pointer to
|
|
// the line.
|
|
nsBlockFrame* flow = this;
|
|
LineData** linep = &flow->mLines;
|
|
LineData* line = flow->mLines;
|
|
while (nsnull != line) {
|
|
if (line->Contains(deletedFrame)) {
|
|
break;
|
|
}
|
|
linep = &line->mNext;
|
|
line = line->mNext;
|
|
}
|
|
NS_ASSERTION(nsnull != line, "can't find deleted frame in lines");
|
|
|
|
// Remove frame and its continuations
|
|
while (nsnull != deletedFrame) {
|
|
while ((nsnull != line) && (nsnull != deletedFrame)) {
|
|
#ifdef NS_DEBUG
|
|
nsIFrame* parent;
|
|
deletedFrame->GetGeometricParent(parent);
|
|
NS_ASSERTION(flow == parent, "messed up delete code");
|
|
#endif
|
|
NS_FRAME_TRACE(NS_FRAME_TRACE_CHILD_REFLOW,
|
|
("nsBlockFrame::ContentDeleted: deadFrame=%p", deletedFrame));
|
|
|
|
// See if the frame is a floater (actually, the floaters
|
|
// placeholder). If it is, then destroy the floated frame too.
|
|
const nsStyleDisplay* display;
|
|
nsresult rv = deletedFrame->GetStyleData(eStyleStruct_Display,
|
|
(const nsStyleStruct*&)display);
|
|
if (NS_SUCCEEDED(rv) && (nsnull != display)) {
|
|
// XXX Sanitize "IsFloating" question *everywhere* (add a
|
|
// static method on nsFrame?)
|
|
if (NS_STYLE_FLOAT_NONE != display->mFloats) {
|
|
nsPlaceholderFrame* ph = (nsPlaceholderFrame*) deletedFrame;
|
|
nsIFrame* floater = ph->GetAnchoredItem();
|
|
if (nsnull != floater) {
|
|
floater->DeleteFrame(aState.mPresContext);
|
|
if (nsnull != line->mFloaters) {
|
|
// Wipe out the floater array for this line. It will get
|
|
// recomputed during reflow anyway.
|
|
delete line->mFloaters;
|
|
line->mFloaters = nsnull;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Remove deletedFrame from the line
|
|
if (line->mFirstChild == deletedFrame) {
|
|
nsIFrame* nextFrame;
|
|
deletedFrame->GetNextSibling(nextFrame);
|
|
line->mFirstChild = nextFrame;
|
|
}
|
|
|
|
// Take deletedFrame out of the sibling list
|
|
if (nsnull != prevSibling) {
|
|
nsIFrame* nextFrame;
|
|
deletedFrame->GetNextSibling(nextFrame);
|
|
prevSibling->SetNextSibling(nextFrame);
|
|
}
|
|
|
|
// Destroy frame; capture its next-in-flow first in case we need
|
|
// to destroy that too.
|
|
nsIFrame* nextInFlow;
|
|
deletedFrame->GetNextInFlow(nextInFlow);
|
|
if (nsnull != nextInFlow) {
|
|
deletedFrame->BreakFromNextFlow();
|
|
}
|
|
deletedFrame->DeleteFrame(aState.mPresContext);
|
|
deletedFrame = nextInFlow;
|
|
|
|
// If line is empty, remove it now
|
|
LineData* next = line->mNext;
|
|
if (0 == --line->mChildCount) {
|
|
*linep = next;
|
|
line->mNext = nsnull;
|
|
delete line;
|
|
}
|
|
else {
|
|
linep = &line->mNext;
|
|
}
|
|
line = next;
|
|
}
|
|
|
|
// Advance to next flow block if the frame has more continuations
|
|
if (nsnull != deletedFrame) {
|
|
flow = (nsBlockFrame*) flow->mNextInFlow;
|
|
NS_ASSERTION(nsnull != flow, "whoops, continuation without a parent");
|
|
line = flow->mLines;
|
|
prevSibling = nsnull;
|
|
}
|
|
}
|
|
|
|
// XXX Correct implementation: reflow the dirty lines only; all
|
|
// other lines can be moved; recover state before first dirty line.
|
|
|
|
// XXX temporary
|
|
aState.mSpaceManager->ClearRegions();
|
|
aState.GetAvailableSpace();
|
|
aState.mPrevLine = nsnull;
|
|
return ReflowLinesAt(aState, mLines);
|
|
}
|
|
|
|
// XXX Todo: some incremental reflows are passing through this block
|
|
// and into a child block; those cannot impact our text-runs. In that
|
|
// case skip the FindTextRuns work.
|
|
|
|
// XXX easy optimizations: find the line that contains the next child
|
|
// in the reflow-command path and mark it dirty and only reflow it;
|
|
// recover state before it, slide lines down after it.
|
|
|
|
nsresult
|
|
nsBlockFrame::ChildIncrementalReflow(nsBlockReflowState& aState)
|
|
{
|
|
#if 0
|
|
// Generate text-run information; this will also "fluff out" any
|
|
// inline children's frame tree.
|
|
nsresult rv = FindTextRuns(aState);
|
|
if (NS_OK != rv) {
|
|
return rv;
|
|
}
|
|
#endif
|
|
|
|
// XXX temporary
|
|
aState.mSpaceManager->ClearRegions();
|
|
aState.GetAvailableSpace();
|
|
aState.mPrevLine = nsnull;
|
|
return ReflowLinesAt(aState, mLines);
|
|
}
|
|
|
|
nsresult
|
|
nsBlockFrame::StyleChangedReflow(nsBlockReflowState& aState)
|
|
{
|
|
// XXX temporary
|
|
aState.mSpaceManager->ClearRegions();
|
|
aState.GetAvailableSpace();
|
|
aState.mPrevLine = nsnull;
|
|
return ReflowLinesAt(aState, mLines);
|
|
}
|
|
|
|
nsresult
|
|
nsBlockFrame::ResizeReflow(nsBlockReflowState& aState)
|
|
{
|
|
// Mark everything dirty
|
|
LineData* line = mLines;
|
|
while (nsnull != line) {
|
|
line->MarkDirty();
|
|
line = line->mNext;
|
|
}
|
|
|
|
// Reflow all of our lines
|
|
aState.GetAvailableSpace();
|
|
aState.mPrevLine = nsnull;
|
|
return ReflowLinesAt(aState, mLines);
|
|
}
|
|
|
|
nsresult
|
|
nsBlockFrame::ReflowLinesAt(nsBlockReflowState& aState, LineData* aLine)
|
|
{
|
|
// Inform line layout of where the text runs are
|
|
aState.mLineLayout.SetReflowTextRuns(mTextRuns);
|
|
|
|
// Reflow the lines that are already ours
|
|
while (nsnull != aLine) {
|
|
nsReflowStatus rs;
|
|
if (!ReflowLine(aState, aLine, rs)) {
|
|
if (NS_IS_REFLOW_ERROR(rs)) {
|
|
return nsresult(rs);
|
|
}
|
|
return NS_FRAME_NOT_COMPLETE;
|
|
}
|
|
aState.mLineLayout.NextLine();
|
|
aState.mPrevLine = aLine;
|
|
aLine = aLine->mNext;
|
|
}
|
|
|
|
// Pull data from a next-in-flow if we can
|
|
while (nsnull != aState.mNextInFlow) {
|
|
// Grab first line from our next-in-flow
|
|
aLine = aState.mNextInFlow->mLines;
|
|
if (nsnull == aLine) {
|
|
aState.mNextInFlow = (nsBlockFrame*) aState.mNextInFlow->mNextInFlow;
|
|
continue;
|
|
}
|
|
// XXX See if the line is not dirty; if it's not maybe we can
|
|
// avoid the pullup if it can't fit?
|
|
aState.mNextInFlow->mLines = aLine->mNext;
|
|
aLine->mNext = nsnull;
|
|
if (0 == aLine->ChildCount()) {
|
|
// The line is empty. Try the next one.
|
|
NS_ASSERTION(nsnull == aLine->mFirstChild, "bad empty line");
|
|
aLine->mNext = aState.mFreeList;
|
|
aState.mFreeList = aLine;
|
|
continue;
|
|
}
|
|
|
|
// Make the children in the line ours.
|
|
nsIFrame* frame = aLine->mFirstChild;
|
|
nsIFrame* lastFrame = nsnull;
|
|
PRInt32 n = aLine->ChildCount();
|
|
while (--n >= 0) {
|
|
nsIFrame* geometricParent;
|
|
nsIFrame* contentParent;
|
|
frame->GetGeometricParent(geometricParent);
|
|
frame->GetContentParent(contentParent);
|
|
if (contentParent == geometricParent) {
|
|
frame->SetContentParent(this);
|
|
}
|
|
frame->SetGeometricParent(this);
|
|
lastFrame = frame;
|
|
frame->GetNextSibling(frame);
|
|
}
|
|
lastFrame->SetNextSibling(nsnull);
|
|
|
|
// Add line to our line list
|
|
if (nsnull == aState.mPrevLine) {
|
|
NS_ASSERTION(nsnull == mLines, "bad aState.mPrevLine");
|
|
mLines = aLine;
|
|
}
|
|
else {
|
|
NS_ASSERTION(nsnull == aState.mPrevLine->mNext, "bad aState.mPrevLine");
|
|
aState.mPrevLine->mNext = aLine;
|
|
aState.mPrevChild->SetNextSibling(aLine->mFirstChild);
|
|
}
|
|
#ifdef NS_DEBUG
|
|
if (GetVerifyTreeEnable()) {
|
|
VerifyChildCount(mLines);
|
|
}
|
|
#endif
|
|
|
|
// Now reflow it and any lines that it makes during it's reflow.
|
|
while (nsnull != aLine) {
|
|
nsReflowStatus rs;
|
|
if (!ReflowLine(aState, aLine, rs)) {
|
|
if (NS_IS_REFLOW_ERROR(rs)) {
|
|
return nsresult(rs);
|
|
}
|
|
return NS_FRAME_NOT_COMPLETE;
|
|
}
|
|
aState.mLineLayout.NextLine();
|
|
aState.mPrevLine = aLine;
|
|
aLine = aLine->mNext;
|
|
}
|
|
}
|
|
|
|
return NS_FRAME_COMPLETE;
|
|
}
|
|
|
|
/**
|
|
* Reflow a line. The line will either contain a single block frame
|
|
* or contain 1 or more inline frames.
|
|
*/
|
|
PRBool
|
|
nsBlockFrame::ReflowLine(nsBlockReflowState& aState,
|
|
LineData* aLine,
|
|
nsReflowStatus& aReflowResult)
|
|
{
|
|
NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
|
|
("nsBlockFrame::ReflowLine: line=%p", aLine));
|
|
|
|
// Setup the line-layout for the new line
|
|
aState.mLineLayout.Reset();
|
|
aState.mCurrentLine = aLine;
|
|
aState.mInlineReflowPrepared = PR_FALSE;
|
|
|
|
// If the line already has floaters on it from last time, remove
|
|
// them from the spacemanager now.
|
|
if (nsnull != aLine->mFloaters) {
|
|
if (eReflowReason_Resize != aState.reason) {
|
|
aLine->UnplaceFloaters(aState.mSpaceManager);
|
|
}
|
|
delete aLine->mFloaters;
|
|
aLine->mFloaters = nsnull;
|
|
}
|
|
|
|
aLine->ClearDirty();
|
|
aLine->SetNeedDidReflow();
|
|
|
|
// Reflow mapped frames in the line
|
|
nsBlockFrame* nextInFlow;
|
|
PRBool keepGoing = PR_FALSE;
|
|
PRInt32 n = aLine->ChildCount();
|
|
if (0 != n) {
|
|
nsIFrame* frame = aLine->mFirstChild;
|
|
#ifdef NS_DEBUG
|
|
const nsStyleDisplay* display;
|
|
frame->GetStyleData(eStyleStruct_Display,
|
|
(const nsStyleStruct*&) display);
|
|
const nsStylePosition* position;
|
|
frame->GetStyleData(eStyleStruct_Position,
|
|
(const nsStyleStruct*&) position);
|
|
PRBool isBlock = nsLineLayout::TreatFrameAsBlock(display, position);
|
|
NS_ASSERTION(isBlock == aLine->IsBlock(), "bad line isBlock");
|
|
#endif
|
|
if (aLine->IsBlock()) {
|
|
keepGoing = ReflowBlockFrame(aState, aLine, frame, aReflowResult);
|
|
return keepGoing;
|
|
}
|
|
else {
|
|
while (--n >= 0) {
|
|
PRBool addedToLine;
|
|
keepGoing = ReflowInlineFrame(aState, aLine, frame, aReflowResult,
|
|
addedToLine);
|
|
if (!keepGoing) {
|
|
// It is possible that one or more of next lines are empty
|
|
// (because of DeleteNextInFlowsFor). If so, delete them now
|
|
// in case we are finished.
|
|
LineData* nextLine = aLine->mNext;
|
|
while ((nsnull != nextLine) && (0 == nextLine->ChildCount())) {
|
|
// Discard empty lines immediately. Empty lines can happen
|
|
// here because of DeleteNextInFlowsFor not being able to
|
|
// delete lines.
|
|
aLine->mNext = nextLine->mNext;
|
|
NS_ASSERTION(nsnull == nextLine->mFirstChild, "bad empty line");
|
|
nextLine->mNext = aState.mFreeList;
|
|
aState.mFreeList = nextLine;
|
|
nextLine = aLine->mNext;
|
|
}
|
|
goto done;
|
|
}
|
|
frame->GetNextSibling(frame);
|
|
if (addedToLine) {
|
|
NS_ASSERTION(nsnull != frame, "added to line but no new frame");
|
|
n++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Pull frames from the next line until we can't
|
|
while (nsnull != aLine->mNext) {
|
|
PRBool addedToLine;
|
|
keepGoing = PullFrame(aState, aLine, &aLine->mNext,
|
|
PR_FALSE, aReflowResult, addedToLine);
|
|
if (!keepGoing) {
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
// Pull frames from the next-in-flow(s) until we can't
|
|
nextInFlow = aState.mNextInFlow;
|
|
while (nsnull != nextInFlow) {
|
|
LineData* line = nextInFlow->mLines;
|
|
if (nsnull == line) {
|
|
nextInFlow = (nsBlockFrame*) nextInFlow->mNextInFlow;
|
|
aState.mNextInFlow = nextInFlow;
|
|
continue;
|
|
}
|
|
PRBool addedToLine;
|
|
keepGoing = PullFrame(aState, aLine, &nextInFlow->mLines,
|
|
PR_TRUE, aReflowResult, addedToLine);
|
|
if (!keepGoing) {
|
|
goto done;
|
|
}
|
|
}
|
|
keepGoing = PR_TRUE;
|
|
|
|
done:;
|
|
if (!aLine->IsBlock()) {
|
|
return PlaceLine(aState, aLine, aReflowResult);
|
|
}
|
|
return keepGoing;
|
|
}
|
|
|
|
// block's vs. inlines's:
|
|
|
|
// 1. break-before/break-after (is handled by nsInlineReflow)
|
|
// 2. pull-up: can't pull a block onto a non-empty line
|
|
// 3. blocks require vertical margin handling
|
|
// 4. left/right margins have to be figured differently for blocks
|
|
|
|
// Assume that container is aware of block/inline differences of the
|
|
// child frame and handles them through different pathways OR that the
|
|
// nsInlineReflow does and reports back the different results
|
|
|
|
// Here is how we format B0[ I0[ I1[ text0 B1 text1] ] ]
|
|
// B0 reflows I0
|
|
// I0 formats I1
|
|
// I1 reflows text0
|
|
// I1 hits B1 and notices it can't be placed
|
|
// I1 pushes B1 to overflow list
|
|
// I1 returns not-complete
|
|
// I0 creates I1'
|
|
// I0 returns not-complete
|
|
// B0 creates I0'
|
|
// B0 flushes out line
|
|
// B0 creates new line
|
|
// B0 puts I0' on new line
|
|
// B0 reflows I0'
|
|
// I0' reflows I1'
|
|
// I1' reflows B1
|
|
// I1' returns not-complete
|
|
// I0' creates I1''
|
|
// I0' returns not-complete
|
|
// B0 creates I0''
|
|
// B0 flushes out line
|
|
// B0 creates new line
|
|
// B0 puts I0'' on new line
|
|
// B0 reflows I0''
|
|
// I0'' reflows I1''
|
|
// I1'' reflows text1
|
|
// I1'' returns complete
|
|
// I0'' returns complete
|
|
// B0 flushes out line
|
|
// B0 returns complete
|
|
|
|
// Here is how we format B0[ I0[ text0 I1[ B1 text1] ] ]
|
|
|
|
// This is harder; I1 doesn't have text0 before B1 to know that we
|
|
// have to stop reflowing I1 before reflowing B1; and worse yet, we
|
|
// only have to stop if I1 begins on the same line that contains text0
|
|
// (it might not depending on the size of text0 and the size given to
|
|
// I0 to reflow into).
|
|
|
|
// To solve this we need to know when we hit B1 whether or not a break
|
|
// is required. To answer this question we need to be able to walk
|
|
// backwards up the nsReflowState's and discover where B1 will
|
|
// actually end up. Can't use the X coordinate because of
|
|
// border/padding.
|
|
|
|
// Since B0 will define this what we do is put a counter into the
|
|
// nsLineLayout structure. As the reflow recurses down the tree we
|
|
// will bump the counter whenever a non-inline frame is seen and
|
|
// placed (one that has a non-zero area too). When I1 goes to place B1
|
|
// it will see that the count is 1 (because of text0) and know to
|
|
// return a "break-before" status (instead of a not-complete; we
|
|
// return not-complete if we have placed ANY children and break-before
|
|
// when we have placed NO children).
|
|
|
|
// XXX factor this some more so that nsInlineFrame can use it too.
|
|
// The post-reflow-success code that computes the final margin
|
|
// values and carries them forward, plus the code that factors in
|
|
// the max-element size.
|
|
|
|
// XXX inline frames need this too when they wrap up blocks, right??
|
|
// Otherwise blocks in inlines won't interact with floaters properly.
|
|
void
|
|
nsBlockFrame::PrepareInlineReflow(nsBlockReflowState& aState,
|
|
nsIFrame* aFrame,
|
|
PRBool aIsBlock)
|
|
{
|
|
// Setup initial coordinate system for reflowing the frame into
|
|
nscoord x, availWidth, availHeight;
|
|
|
|
if (aIsBlock) {
|
|
// XXX Child needs to apply OUR border-padding and IT's left
|
|
// margin and right margin! How should this be done?
|
|
x = aState.mBorderPadding.left;
|
|
if (aState.mUnconstrainedWidth) {
|
|
availWidth = NS_UNCONSTRAINEDSIZE;
|
|
}
|
|
else {
|
|
availWidth = aState.mContentArea.width;
|
|
}
|
|
if (aState.mUnconstrainedHeight) {
|
|
availHeight = NS_UNCONSTRAINEDSIZE;
|
|
}
|
|
else {
|
|
/* XXX get the height right! */
|
|
availHeight = aState.mContentArea.height - aState.mY;
|
|
}
|
|
}
|
|
else {
|
|
// If the child doesn't handle run-around then we give it the band
|
|
// adjusted data. The band aligned data doesn't have our
|
|
// border/padding applied to it so add that in.
|
|
x = aState.mCurrentBand.availSpace.x + aState.mBorderPadding.left;
|
|
availWidth = aState.mCurrentBand.availSpace.width;
|
|
if (aState.mUnconstrainedHeight) {
|
|
availHeight = NS_UNCONSTRAINEDSIZE;
|
|
}
|
|
else {
|
|
/* XXX get the height right! */
|
|
availHeight = aState.mCurrentBand.availSpace.height;
|
|
}
|
|
}
|
|
aState.mInlineReflow->Init(x, aState.mY, availWidth, availHeight);
|
|
aState.mInlineReflowPrepared = PR_TRUE;
|
|
|
|
// Setup the first-letter-style-ok flag
|
|
nsLineLayout& lineLayout = aState.mLineLayout;
|
|
if (mFirstLetterStyle && (0 == lineLayout.GetLineNumber())) {
|
|
lineLayout.SetFirstLetterStyleOK(PR_TRUE);
|
|
}
|
|
else {
|
|
lineLayout.SetFirstLetterStyleOK(PR_FALSE);
|
|
}
|
|
}
|
|
|
|
// XXX switch to two versions: inline vs. block?
|
|
PRUintn
|
|
nsBlockFrame::CalculateMargins(nsBlockReflowState& aState,
|
|
LineData* aLine,
|
|
PRBool aInlineContext,
|
|
nscoord& aTopMarginResult,
|
|
nscoord& aBottomMarginResult)
|
|
{
|
|
PRUintn result = 0;
|
|
nsInlineReflow& ir = *aState.mInlineReflow;
|
|
|
|
// First get the childs top and bottom margins. For inline
|
|
// situations the child frame(s) margins were applied during line
|
|
// layout, therefore we consider them zero here.
|
|
nscoord childsTopMargin, childsBottomMargin;
|
|
PRUintn marginFlags;
|
|
if (aInlineContext) {
|
|
childsTopMargin = 0;
|
|
childsBottomMargin = 0;
|
|
marginFlags = ir.GetCarriedOutMarginFlags();
|
|
}
|
|
else {
|
|
childsTopMargin = ir.GetTopMargin();
|
|
childsBottomMargin = ir.GetBottomMargin();
|
|
marginFlags = ir.GetMarginFlags();
|
|
}
|
|
|
|
// Get the carried margin values. Note that even in an inline
|
|
// situation there may be a carried margin (e.g. an inline frame
|
|
// contains a block frame and the block frame has top/bottom
|
|
// margins). While CSS doesn't specify what this means, we do to
|
|
// improve compatability with poorly formed HTML (and commonly used
|
|
// HTML).
|
|
nscoord childsCarriedOutTopMargin = ir.GetCarriedOutTopMargin();
|
|
nscoord childsCarriedOutBottomMargin = ir.GetCarriedOutBottomMargin();
|
|
if (childsCarriedOutTopMargin || childsCarriedOutBottomMargin) {
|
|
result |= HAVE_CARRIED_MARGIN;
|
|
}
|
|
|
|
// Compute the collapsed top margin value (this is a generational
|
|
// margin collapse not a sibling margin collapse).
|
|
nscoord collapsedTopMargin =
|
|
nsInlineReflow::MaxMargin(childsCarriedOutTopMargin, childsTopMargin);
|
|
|
|
// Now perform sibling to sibling margin collapsing.
|
|
collapsedTopMargin = nsInlineReflow::MaxMargin(aState.mPrevBottomMargin,
|
|
collapsedTopMargin);
|
|
PRBool topMarginIsAuto = PR_FALSE;
|
|
if (NS_CARRIED_TOP_MARGIN_IS_AUTO & marginFlags) {
|
|
if (aInlineContext) {
|
|
topMarginIsAuto = collapsedTopMargin == childsCarriedOutTopMargin;
|
|
}
|
|
else {
|
|
topMarginIsAuto = collapsedTopMargin == childsTopMargin;
|
|
}
|
|
}
|
|
|
|
// Compute the collapsed bottom margin value (this is a generational
|
|
// margin collapse not a sibling margin collapse).
|
|
nscoord collapsedBottomMargin =
|
|
nsInlineReflow::MaxMargin(childsCarriedOutBottomMargin,
|
|
childsBottomMargin);
|
|
if (NS_CARRIED_BOTTOM_MARGIN_IS_AUTO & marginFlags) {
|
|
if (aInlineContext) {
|
|
if (collapsedBottomMargin == childsCarriedOutBottomMargin) {
|
|
result |= NS_CARRIED_BOTTOM_MARGIN_IS_AUTO;
|
|
}
|
|
}
|
|
else if (collapsedBottomMargin == childsBottomMargin) {
|
|
result |= NS_CARRIED_BOTTOM_MARGIN_IS_AUTO;
|
|
}
|
|
}
|
|
|
|
// Now that we have the collapsed margin values, address any special
|
|
// situations that limit or change how the margins are applied.
|
|
if (0 == aLine->mBounds.height) {
|
|
// When a line is empty, we collapse its top and bottom margins to
|
|
// a single bottom margin value.
|
|
// XXX This is not obviously a part of the css2 box model.
|
|
collapsedBottomMargin =
|
|
nsInlineReflow::MaxMargin(collapsedTopMargin, collapsedBottomMargin);
|
|
collapsedTopMargin = 0;
|
|
}
|
|
else {
|
|
PRBool isTopLine = (aState.mY == aState.mBorderPadding.top);
|
|
if (isTopLine) {
|
|
if (!aState.mIsMarginRoot) {
|
|
// We are not a root for margin collapsing and this is our first
|
|
// non-empty-line (with a block child).
|
|
if (topMarginIsAuto) {
|
|
// Propogate auto-margin flag outward
|
|
aState.mCarriedOutMarginFlags |= NS_CARRIED_TOP_MARGIN_IS_AUTO;
|
|
result |= NS_CARRIED_TOP_MARGIN_IS_AUTO;
|
|
}
|
|
|
|
// Keep the collapsed margin value around to pass out to our
|
|
// parent. We don't apply its top margin (our parent will) so
|
|
// zero it out.
|
|
aState.mCollapsedTopMargin = collapsedTopMargin;
|
|
collapsedTopMargin = 0;
|
|
}
|
|
else if (topMarginIsAuto && (NS_BODY_NO_AUTO_MARGINS & mFlags)) {
|
|
// Our top margin is an auto margin and we are supposed to
|
|
// ignore them. Change it to zero.
|
|
collapsedTopMargin = 0;
|
|
}
|
|
}
|
|
}
|
|
aTopMarginResult = collapsedTopMargin;
|
|
aBottomMarginResult = collapsedBottomMargin;
|
|
|
|
return result;
|
|
}
|
|
|
|
void
|
|
nsBlockFrame::SlideFrames(nsIPresContext& aPresContext,
|
|
nsISpaceManager* aSpaceManager,
|
|
LineData* aLine, nscoord aDY)
|
|
{
|
|
// Adjust the Y coordinate of the frames in the line
|
|
nsIFrame* kid = aLine->mFirstChild;
|
|
PRInt32 n = aLine->ChildCount();
|
|
while (--n >= 0) {
|
|
nsRect r;
|
|
kid->GetRect(r);
|
|
r.y += aDY;
|
|
kid->SetRect(r);
|
|
|
|
// If the child has an floaters that impact the space manager,
|
|
// slide them now
|
|
nsIHTMLReflow* ihr;
|
|
if (NS_OK == kid->QueryInterface(kIHTMLReflowIID, (void**)&ihr)) {
|
|
ihr->MoveInSpaceManager(aPresContext, aSpaceManager, 0, aDY);
|
|
}
|
|
|
|
kid->GetNextSibling(kid);
|
|
}
|
|
|
|
// Slide down our floaters too
|
|
nsVoidArray* floaters = aLine->mFloaters;
|
|
if (nsnull != floaters) {
|
|
PRInt32 i;
|
|
n = floaters->Count();
|
|
for (i = 0; i < n; i++) {
|
|
nsPlaceholderFrame* ph = (nsPlaceholderFrame*) floaters->ElementAt(i);
|
|
kid = ph->GetAnchoredItem();
|
|
nsRect r;
|
|
kid->GetRect(r);
|
|
r.y += aDY;
|
|
kid->SetRect(r);
|
|
}
|
|
}
|
|
|
|
// Slide line box too
|
|
aLine->mBounds.y += aDY;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBlockFrame::MoveInSpaceManager(nsIPresContext& aPresContext,
|
|
nsISpaceManager* aSpaceManager,
|
|
nscoord aDeltaX, nscoord aDeltaY)
|
|
{
|
|
LineData* line = mLines;
|
|
while (nsnull != line) {
|
|
PRInt32 i, n;
|
|
nsIFrame* kid;
|
|
|
|
// Move the floaters in the spacemanager
|
|
nsVoidArray* floaters = line->mFloaters;
|
|
if (nsnull != floaters) {
|
|
n = floaters->Count();
|
|
for (i = 0; i < n; i++) {
|
|
nsPlaceholderFrame* ph = (nsPlaceholderFrame*) floaters->ElementAt(i);
|
|
kid = ph->GetAnchoredItem();
|
|
aSpaceManager->OffsetRegion(kid, aDeltaX, aDeltaY);
|
|
}
|
|
}
|
|
|
|
// Tell kids about the move too
|
|
n = line->ChildCount();
|
|
kid = line->mFirstChild;
|
|
while (--n >= 0) {
|
|
nsIHTMLReflow* ihr;
|
|
if (NS_OK == kid->QueryInterface(kIHTMLReflowIID, (void**)&ihr)) {
|
|
ihr->MoveInSpaceManager(aPresContext, aSpaceManager, aDeltaX, aDeltaY);
|
|
}
|
|
kid->GetNextSibling(kid);
|
|
}
|
|
|
|
line = line->mNext;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsBlockFrame*
|
|
nsBlockFrame::FindFollowingBlockFrame(nsIFrame* aFrame)
|
|
{
|
|
nsBlockFrame* followingBlockFrame = nsnull;
|
|
nsIFrame* frame = aFrame;
|
|
for (;;) {
|
|
nsIFrame* nextFrame;
|
|
frame->GetNextSibling(nextFrame);
|
|
if (nsnull != nextFrame) {
|
|
const nsStyleDisplay* display;
|
|
nsresult rv = nextFrame->GetStyleData(eStyleStruct_Display,
|
|
(const nsStyleStruct*&) display);
|
|
if (NS_SUCCEEDED(rv) && (nsnull != display)) {
|
|
if (NS_STYLE_DISPLAY_BLOCK == display->mDisplay) {
|
|
#ifdef NOISY_RUNIN
|
|
ListTag(stdout);
|
|
printf(": frame: ");
|
|
aFrame->ListTag(stdout);
|
|
printf(" followed by: ");
|
|
nextFrame->ListTag(stdout);
|
|
printf("\n");
|
|
#endif
|
|
followingBlockFrame = (nsBlockFrame*) nextFrame;
|
|
break;
|
|
}
|
|
else if (NS_STYLE_DISPLAY_INLINE == display->mDisplay) {
|
|
// If it's a text-frame and it's just whitespace and we are
|
|
// in a normal whitespace situation THEN skip it and keep
|
|
// going...
|
|
// XXX probably should really check this ...
|
|
}
|
|
}
|
|
else
|
|
break;
|
|
frame = nextFrame;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
return followingBlockFrame;
|
|
}
|
|
|
|
PRBool
|
|
nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
|
|
LineData* aLine,
|
|
nsIFrame* aFrame,
|
|
nsReflowStatus& aReflowResult)
|
|
{
|
|
NS_PRECONDITION(0 == aState.mLineLayout.GetPlacedFrames(),
|
|
"non-empty line with a block");
|
|
|
|
nsInlineReflow& ir = *aState.mInlineReflow;
|
|
|
|
// Prepare the inline reflow engine
|
|
nsBlockFrame* runInToFrame;
|
|
nsBlockFrame* compactWithFrame;
|
|
nscoord compactMarginWidth = 0;
|
|
PRBool isCompactFrame = PR_FALSE;
|
|
PRBool asBlock = PR_TRUE;
|
|
const nsStyleDisplay* display;
|
|
nsresult rv = aFrame->GetStyleData(eStyleStruct_Display,
|
|
(const nsStyleStruct*&) display);
|
|
if ((NS_OK == rv) && (nsnull != display)) {
|
|
switch (display->mDisplay) {
|
|
case NS_STYLE_DISPLAY_TABLE:
|
|
asBlock = PR_FALSE;
|
|
break;
|
|
|
|
case NS_STYLE_DISPLAY_RUN_IN:
|
|
#ifdef NOISY_RUNIN
|
|
ListTag(stdout);
|
|
printf(": trying to see if ");
|
|
aFrame->ListTag(stdout);
|
|
printf(" is a run-in candidate\n");
|
|
#endif
|
|
runInToFrame = FindFollowingBlockFrame(aFrame);
|
|
if (nsnull != runInToFrame) {
|
|
// XXX run-in frame should be pushed to the next-in-flow too if the
|
|
// run-in-to frame is pushed.
|
|
aReflowResult = NS_FRAME_COMPLETE;
|
|
nsRect r(0, aState.mY, 0, 0);
|
|
aLine->mBounds = r;
|
|
aLine->mCombinedArea = r;
|
|
aLine->mCarriedOutTopMargin = 0;
|
|
aLine->mCarriedOutBottomMargin = 0;
|
|
aLine->SetMarginFlags(0);
|
|
aLine->ClearOutsideChildren();
|
|
aLine->mBreakType = NS_STYLE_CLEAR_NONE;
|
|
//XXX aFrame->WillReflow(aState.mPresContext);
|
|
aFrame->SetRect(r);
|
|
aState.mPrevChild = aFrame;
|
|
aState.mRunInToFrame = runInToFrame;
|
|
aState.mRunInFrame = (nsBlockFrame*) aFrame;
|
|
return PR_TRUE;
|
|
}
|
|
break;
|
|
|
|
case NS_STYLE_DISPLAY_COMPACT:
|
|
compactWithFrame = FindFollowingBlockFrame(aFrame);
|
|
if (nsnull != compactWithFrame) {
|
|
const nsStyleSpacing* spacing;
|
|
nsMargin margin;
|
|
rv = compactWithFrame->GetStyleData(eStyleStruct_Spacing,
|
|
(const nsStyleStruct*&) spacing);
|
|
if (NS_SUCCEEDED(rv) && (nsnull != spacing)) {
|
|
nsHTMLReflowState::ComputeMarginFor(compactWithFrame, &aState,
|
|
margin);
|
|
compactMarginWidth = margin.left;
|
|
}
|
|
isCompactFrame = PR_TRUE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
// clear floaters if the clear style is not none
|
|
if (NS_STYLE_CLEAR_NONE != display->mBreakType) {
|
|
aState.ClearFloaters(display->mBreakType);
|
|
}
|
|
}
|
|
PrepareInlineReflow(aState, aFrame, asBlock);
|
|
ir.SetIsFirstChild((aLine == mLines) &&
|
|
(aFrame == aLine->mFirstChild));
|
|
ir.SetCompactMarginWidth(compactMarginWidth);
|
|
if (aFrame == aState.mRunInToFrame) {
|
|
ir.SetRunInFrame(aState.mRunInFrame);
|
|
}
|
|
|
|
// Reflow the block frame
|
|
nsReflowStatus reflowStatus = ir.ReflowFrame(aFrame);
|
|
if (NS_IS_REFLOW_ERROR(reflowStatus)) {
|
|
aReflowResult = reflowStatus;
|
|
return PR_FALSE;
|
|
}
|
|
aReflowResult = reflowStatus;
|
|
|
|
// XXX We need to check the *type* of break and if it's a column/page
|
|
// break apply and cause the block to be split (assuming we are
|
|
// laying out in a column).
|
|
#if XXX
|
|
if (NS_INLINE_IS_BREAK(reflowStatus)) {
|
|
// XXX For now we ignore it
|
|
}
|
|
#endif
|
|
|
|
// Align the frame
|
|
nscoord maxAscent, maxDescent;
|
|
ir.VerticalAlignFrames(aLine->mBounds, maxAscent, maxDescent);
|
|
ir.HorizontalAlignFrames(aLine->mBounds);
|
|
ir.RelativePositionFrames(aLine->mCombinedArea);
|
|
|
|
// Calculate margins
|
|
nscoord topMargin, bottomMargin;
|
|
PRUintn marginFlags = CalculateMargins(aState, aLine, PR_FALSE,
|
|
topMargin, bottomMargin);
|
|
|
|
// Try to place the frame
|
|
nscoord newY = aLine->mBounds.YMost() + topMargin;
|
|
if ((mLines != aLine) && (newY > aState.mBottomEdge)) {
|
|
// The frame doesn't fit inside our available space. Push the
|
|
// line to the next-in-flow and return our incomplete status to
|
|
// our parent.
|
|
PushLines(aState);
|
|
aReflowResult = NS_FRAME_NOT_COMPLETE;
|
|
return PR_FALSE;
|
|
}
|
|
if (isCompactFrame) {
|
|
// For compact frames, we don't adjust the Y coordinate at all IF
|
|
// the compact frame ended up fitting in the margin space
|
|
// allocated for it.
|
|
nsRect r;
|
|
aFrame->GetRect(r);
|
|
if (r.width <= compactMarginWidth) {
|
|
// XXX margins will be wrong
|
|
// XXX ltr/rtl for horizontal placement within the margin area
|
|
// XXX vertical alignment with the compactWith frame's *first line*
|
|
newY = aState.mY;
|
|
}
|
|
}
|
|
else if ((nsnull != mBullet) && ShouldPlaceBullet(aLine)) {
|
|
const nsStyleFont* font;
|
|
rv = aFrame->GetStyleData(eStyleStruct_Font, (const nsStyleStruct*&) font);
|
|
if (NS_SUCCEEDED(rv) && (nsnull != font)) {
|
|
nsIRenderingContext& rc = *aState.rendContext;
|
|
rc.SetFont(font->mFont);
|
|
nscoord firstLineAscent = 0;
|
|
nsIFontMetrics* fm;
|
|
rv = rc.GetFontMetrics(fm);
|
|
if (NS_SUCCEEDED(rv) && (nsnull != fm)) {
|
|
fm->GetMaxAscent(firstLineAscent);
|
|
NS_RELEASE(fm);
|
|
}
|
|
PlaceBullet(aState, firstLineAscent, topMargin);
|
|
}
|
|
}
|
|
aState.mY = newY;
|
|
|
|
// Apply collapsed top-margin value
|
|
if (0 != topMargin) {
|
|
SlideFrames(aState.mPresContext, aState.mSpaceManager, aLine, topMargin);
|
|
}
|
|
|
|
// Record bottom margin value for sibling to sibling compression or
|
|
// for returning as our carried out bottom margin. Adjust running
|
|
// margin value when either we have carried margins from the line or
|
|
// we have a non-zero height line.
|
|
if ((HAVE_CARRIED_MARGIN & marginFlags) || (0 != aLine->mBounds.height)) {
|
|
aState.mPrevBottomMargin = bottomMargin;
|
|
aState.mPrevMarginFlags = marginFlags;
|
|
}
|
|
aLine->mCarriedOutTopMargin = ir.GetCarriedOutTopMargin();
|
|
aLine->mCarriedOutBottomMargin = ir.GetCarriedOutBottomMargin();
|
|
aLine->SetMarginFlags(marginFlags);
|
|
aLine->ClearOutsideChildren();
|
|
nsFrameState state;
|
|
aFrame->GetFrameState(state);
|
|
if (NS_FRAME_OUTSIDE_CHILDREN & state) {
|
|
aLine->SetOutsideChildren();
|
|
}
|
|
|
|
nscoord xmost = aLine->mBounds.XMost();
|
|
if (xmost > aState.mKidXMost) {
|
|
aState.mKidXMost = xmost;
|
|
}
|
|
|
|
// Update max-element-size
|
|
if (aState.mComputeMaxElementSize) {
|
|
const nsSize& kidMaxElementSize = ir.GetMaxElementSize();
|
|
if (kidMaxElementSize.width > aState.mMaxElementSize.width) {
|
|
aState.mMaxElementSize.width = kidMaxElementSize.width;
|
|
}
|
|
if (kidMaxElementSize.height > aState.mMaxElementSize.height) {
|
|
aState.mMaxElementSize.height = kidMaxElementSize.height;
|
|
}
|
|
}
|
|
|
|
aState.mPrevChild = aFrame;
|
|
|
|
// Refresh our available space in case a floater was placed by our
|
|
// child.
|
|
// XXX expensive!
|
|
// XXX unnecessary because the child block will contain the floater!
|
|
aState.GetAvailableSpace();
|
|
|
|
aLine->mBreakType = NS_STYLE_CLEAR_NONE;
|
|
|
|
if (NS_FRAME_IS_NOT_COMPLETE(reflowStatus)) {
|
|
// Some of the block fit. We need to have the block frame
|
|
// continued, so we make sure that it has a next-in-flow now.
|
|
nsIFrame* nextInFlow;
|
|
nsresult rv;
|
|
rv = nsHTMLContainerFrame::CreateNextInFlow(aState.mPresContext,
|
|
this,
|
|
aFrame,
|
|
nextInFlow);
|
|
if (NS_OK != rv) {
|
|
aReflowResult = rv;
|
|
return PR_FALSE;
|
|
}
|
|
if (nsnull != nextInFlow) {
|
|
// We made a next-in-flow for the block child frame. Create a
|
|
// line to map the block childs next-in-flow.
|
|
LineData* line = new LineData(nextInFlow, 1, LINE_IS_BLOCK);
|
|
if (nsnull == line) {
|
|
aReflowResult = NS_ERROR_OUT_OF_MEMORY;
|
|
return PR_FALSE;
|
|
}
|
|
line->mNext = aLine->mNext;
|
|
aLine->mNext = line;
|
|
}
|
|
|
|
// Advance mPrevLine because we are keeping aLine (since some of
|
|
// the child block frame fit). Then push any remaining lines to
|
|
// our next-in-flow
|
|
aState.mPrevLine = aLine;
|
|
if (nsnull != aLine->mNext) {
|
|
PushLines(aState);
|
|
}
|
|
aReflowResult = NS_INLINE_LINE_BREAK_AFTER(reflowStatus);
|
|
return PR_FALSE;
|
|
}
|
|
return PR_TRUE;
|
|
}
|
|
|
|
/**
|
|
* Reflow an inline frame. Returns PR_FALSE if the frame won't fit
|
|
* on the line and the line should be flushed.
|
|
*/
|
|
PRBool
|
|
nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
|
|
LineData* aLine,
|
|
nsIFrame* aFrame,
|
|
nsReflowStatus& aReflowResult,
|
|
PRBool& aAddedToLine)
|
|
{
|
|
nsresult rv;
|
|
nsIFrame* nextInFlow;
|
|
|
|
aAddedToLine = PR_FALSE;
|
|
if (!aState.mInlineReflowPrepared) {
|
|
PrepareInlineReflow(aState, aFrame, PR_FALSE);
|
|
}
|
|
|
|
// When reflowing a frame that is on the first-line, check and see
|
|
// if a special style context should be placed in the context chain.
|
|
PRBool reflowingFirstLetter = PR_FALSE;
|
|
if ((nsnull == mPrevInFlow) &&
|
|
(0 == aState.mLineLayout.GetLineNumber())) {
|
|
if (nsnull != mFirstLineStyle) {
|
|
// Update the child frames style to inherit from the first-line
|
|
// style.
|
|
// XXX add code to check first and only do it if it needs doing!
|
|
#ifdef REALLY_NOISY_FIRST_LINE
|
|
DumpStyleGeneaology(aFrame, "");
|
|
#endif
|
|
#ifdef NOISY_FIRST_LINE
|
|
aFrame->ListTag(stdout);
|
|
printf(": adding in first-line style\n");
|
|
#endif
|
|
aFrame->ReResolveStyleContext(&aState.mPresContext, mFirstLineStyle);
|
|
#ifdef REALLY_NOISY_FIRST_LINE
|
|
DumpStyleGeneaology(aFrame, " ");
|
|
#endif
|
|
}
|
|
if ((nsnull != mFirstLetterStyle) &&
|
|
aState.mLineLayout.GetFirstLetterStyleOK()) {
|
|
// XXX For now, we only allow text objects to have :first-letter
|
|
// style.
|
|
nsIContent* c;
|
|
nsresult rv = aFrame->GetContent(c);
|
|
if (NS_SUCCEEDED(rv) && (nsnull != c)) {
|
|
nsITextContent* tc;
|
|
nsresult rv = c->QueryInterface(kITextContentIID, (void**)&tc);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
// XXX add code to check first and only do it if it needs doing!
|
|
// Update the child frames style to inherit from the
|
|
// first-letter style.
|
|
aFrame->ReResolveStyleContext(&aState.mPresContext,
|
|
mFirstLetterStyle);
|
|
reflowingFirstLetter = PR_TRUE;
|
|
NS_RELEASE(tc);
|
|
}
|
|
NS_RELEASE(c);
|
|
}
|
|
}
|
|
}
|
|
|
|
PRBool isFirstChild = (aLine == mLines) &&
|
|
(aFrame == aLine->mFirstChild);
|
|
aState.mInlineReflow->SetIsFirstChild(isFirstChild);
|
|
aReflowResult = aState.mInlineReflow->ReflowFrame(aFrame);
|
|
if (NS_IS_REFLOW_ERROR(aReflowResult)) {
|
|
return PR_FALSE;
|
|
}
|
|
|
|
if (!NS_INLINE_IS_BREAK(aReflowResult)) {
|
|
aState.mPrevChild = aFrame;
|
|
if (NS_FRAME_IS_COMPLETE(aReflowResult)) {
|
|
return PR_TRUE;
|
|
}
|
|
|
|
// Create continuation frame (if necessary); add it to the end of
|
|
// the current line so that it can be pushed to the next line
|
|
// properly.
|
|
rv = nsHTMLContainerFrame::CreateNextInFlow(aState.mPresContext,
|
|
this, aFrame, nextInFlow);
|
|
if (NS_OK != rv) {
|
|
aReflowResult = rv;
|
|
return PR_FALSE;
|
|
}
|
|
if (nsnull != nextInFlow) {
|
|
// Add new child to the line
|
|
aLine->mChildCount++;
|
|
}
|
|
|
|
// XXX Special hackery to keep going if we just split because of a
|
|
// :first-letter situation.
|
|
if (reflowingFirstLetter) {
|
|
if (nsnull != nextInFlow) {
|
|
aAddedToLine = PR_TRUE;
|
|
nextInFlow->ReResolveStyleContext(&aState.mPresContext,
|
|
mFirstLineStyle
|
|
? mFirstLineStyle
|
|
: mStyleContext);
|
|
}
|
|
if (aState.mInlineReflow->GetAvailWidth() > 0) {
|
|
return PR_TRUE;
|
|
}
|
|
}
|
|
|
|
// Split line after the frame we just reflowed
|
|
aFrame->GetNextSibling(aFrame);
|
|
}
|
|
else {
|
|
if (NS_INLINE_IS_BREAK_AFTER(aReflowResult)) {
|
|
aState.mPrevChild = aFrame;
|
|
if (NS_FRAME_IS_NOT_COMPLETE(aReflowResult)) {
|
|
// Create continuation frame (if necessary); add it to the end of
|
|
// the current line so that it can be pushed to the next line
|
|
// properly.
|
|
rv = nsHTMLContainerFrame::CreateNextInFlow(aState.mPresContext,
|
|
this, aFrame,
|
|
nextInFlow);
|
|
if (NS_OK != rv) {
|
|
aReflowResult = rv;
|
|
return PR_FALSE;
|
|
}
|
|
if (nsnull != nextInFlow) {
|
|
// Add new child to the line
|
|
aLine->mChildCount++;
|
|
}
|
|
}
|
|
aFrame->GetNextSibling(aFrame);
|
|
}
|
|
}
|
|
|
|
// Split line since we aren't going to keep going
|
|
rv = SplitLine(aState, aLine, aFrame);
|
|
if (NS_IS_REFLOW_ERROR(rv)) {
|
|
aReflowResult = rv;
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|
|
void
|
|
nsBlockFrame::RestoreStyleFor(nsIPresContext& aPresContext, nsIFrame* aFrame)
|
|
{
|
|
#ifdef REALLY_NOISY_FIRST_LINE
|
|
DumpStyleGeneaology(aFrame, "");
|
|
#endif
|
|
|
|
nsIStyleContext* kidSC;
|
|
aFrame->GetStyleContext(kidSC);
|
|
if (nsnull != kidSC) {
|
|
nsIStyleContext* kidParentSC;
|
|
kidParentSC = kidSC->GetParent();
|
|
if ((kidParentSC == mFirstLineStyle) ||
|
|
(kidParentSC == mFirstLetterStyle)) {
|
|
#ifdef NOISY_FIRST_LINE
|
|
printf(" ");
|
|
aFrame->ListTag(stdout);
|
|
printf(": removing first-line style\n");
|
|
#endif
|
|
|
|
aFrame->ReResolveStyleContext(&aPresContext, mStyleContext);
|
|
|
|
#ifdef REALLY_NOISY_FIRST_LINE
|
|
DumpStyleGeneaology(aFrame, " ");
|
|
#endif
|
|
}
|
|
NS_IF_RELEASE(kidParentSC);
|
|
NS_RELEASE(kidSC);
|
|
}
|
|
}
|
|
|
|
// XXX alloc lines using free-list in aState
|
|
|
|
// XXX refactor this since the split NEVER has to deal with blocks
|
|
|
|
nsresult
|
|
nsBlockFrame::SplitLine(nsBlockReflowState& aState,
|
|
LineData* aLine,
|
|
nsIFrame* aFrame)
|
|
{
|
|
PRInt32 pushCount = aLine->ChildCount() -
|
|
aState.mInlineReflow->GetCurrentFrameNum();
|
|
NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
|
|
("nsBlockFrame::SplitLine: pushing %d frames",
|
|
pushCount));
|
|
if (0 != pushCount) {
|
|
NS_ASSERTION(nsnull != aFrame, "whoops");
|
|
LineData* to = aLine->mNext;
|
|
if (nsnull != to) {
|
|
// Only push into the next line if it's empty; otherwise we can
|
|
// end up pushing a frame which is continued into the same frame
|
|
// as it's continuation. This causes all sorts of bad side
|
|
// effects so we don't allow it.
|
|
if (0 != to->ChildCount()) {
|
|
LineData* insertedLine = new LineData(aFrame, pushCount, 0);
|
|
aLine->mNext = insertedLine;
|
|
insertedLine->mNext = to;
|
|
to = insertedLine;
|
|
} else {
|
|
to->mFirstChild = aFrame;
|
|
to->mChildCount += pushCount;
|
|
}
|
|
} else {
|
|
to = new LineData(aFrame, pushCount, 0);
|
|
aLine->mNext = to;
|
|
}
|
|
if (nsnull == to) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
aLine->mChildCount -= pushCount;
|
|
#ifdef NS_DEBUG
|
|
if (GetVerifyTreeEnable()) {
|
|
aLine->Verify();
|
|
}
|
|
#endif
|
|
NS_ASSERTION(0 != aLine->ChildCount(), "bad push");
|
|
|
|
// Let inline reflow know that some frames are no longer part of
|
|
// its state.
|
|
aState.mInlineReflow->ChangeFrameCount(aLine->ChildCount());
|
|
|
|
// Repair style contexts when frames move to the next line and
|
|
// this is the first line.
|
|
if (((nsnull != mFirstLineStyle) || (nsnull != mFirstLetterStyle)) &&
|
|
(0 == aState.mLineLayout.GetLineNumber())) {
|
|
// Take the pseudo-style out of the style context chain
|
|
nsIFrame* kid = to->mFirstChild;
|
|
PRInt32 n = pushCount;
|
|
while (--n >= 0) {
|
|
RestoreStyleFor(aState.mPresContext, kid);
|
|
kid->GetNextSibling(kid);
|
|
}
|
|
}
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
PRBool
|
|
nsBlockFrame::PullFrame(nsBlockReflowState& aState,
|
|
LineData* aLine,
|
|
LineData** aFromList,
|
|
PRBool aUpdateGeometricParent,
|
|
nsReflowStatus& aReflowResult,
|
|
PRBool& aAddedToLine)
|
|
{
|
|
LineData* fromLine = *aFromList;
|
|
NS_ASSERTION(nsnull != fromLine, "bad line to pull from");
|
|
if (0 == fromLine->ChildCount()) {
|
|
// Discard empty lines immediately. Empty lines can happen here
|
|
// because of DeleteChildsNextInFlow not being able to delete
|
|
// lines.
|
|
*aFromList = fromLine->mNext;
|
|
NS_ASSERTION(nsnull == fromLine->mFirstChild, "bad empty line");
|
|
fromLine->mNext = aState.mFreeList;
|
|
aState.mFreeList = fromLine;
|
|
return PR_TRUE;
|
|
}
|
|
|
|
// If our line is not empty and the child in aFromLine is a block
|
|
// then we cannot pull up the frame into this line.
|
|
if ((0 != aLine->ChildCount()) && fromLine->IsBlock()) {
|
|
aReflowResult = NS_INLINE_LINE_BREAK_BEFORE();
|
|
return PR_FALSE;
|
|
}
|
|
|
|
// Take frame from fromLine
|
|
nsIFrame* frame = fromLine->mFirstChild;
|
|
if (0 == aLine->mChildCount++) {
|
|
aLine->mFirstChild = frame;
|
|
aLine->SetIsBlock(fromLine->IsBlock());
|
|
#ifdef NS_DEBUG
|
|
const nsStyleDisplay* display;
|
|
frame->GetStyleData(eStyleStruct_Display,
|
|
(const nsStyleStruct*&) display);
|
|
const nsStylePosition* position;
|
|
frame->GetStyleData(eStyleStruct_Position,
|
|
(const nsStyleStruct*&) position);
|
|
PRBool isBlock = nsLineLayout::TreatFrameAsBlock(display, position);
|
|
NS_ASSERTION(isBlock == aLine->IsBlock(), "bad line isBlock");
|
|
#endif
|
|
}
|
|
if (0 != --fromLine->mChildCount) {
|
|
frame->GetNextSibling(fromLine->mFirstChild);
|
|
}
|
|
else {
|
|
// Free up the fromLine now that it's empty
|
|
*aFromList = fromLine->mNext;
|
|
fromLine->mFirstChild = nsnull;
|
|
fromLine->mNext = aState.mFreeList;
|
|
aState.mFreeList = fromLine;
|
|
}
|
|
|
|
// Change geometric parents
|
|
if (aUpdateGeometricParent) {
|
|
nsIFrame* geometricParent;
|
|
nsIFrame* contentParent;
|
|
frame->GetGeometricParent(geometricParent);
|
|
frame->GetContentParent(contentParent);
|
|
if (contentParent == geometricParent) {
|
|
frame->SetContentParent(this);
|
|
}
|
|
frame->SetGeometricParent(this);
|
|
|
|
// The frame is being pulled from a next-in-flow; therefore we
|
|
// need to add it to our sibling list.
|
|
if (nsnull != aState.mPrevChild) {
|
|
aState.mPrevChild->SetNextSibling(frame);
|
|
}
|
|
frame->SetNextSibling(nsnull);
|
|
}
|
|
|
|
// Reflow the frame
|
|
if (aLine->IsBlock()) {
|
|
aAddedToLine = PR_FALSE;
|
|
return ReflowBlockFrame(aState, aLine, frame, aReflowResult);
|
|
}
|
|
else {
|
|
return ReflowInlineFrame(aState, aLine, frame, aReflowResult,
|
|
aAddedToLine);
|
|
}
|
|
}
|
|
|
|
PRBool
|
|
nsBlockFrame::IsLastLine(nsBlockReflowState& aState,
|
|
LineData* aLine,
|
|
nsReflowStatus aReflowStatus)
|
|
{
|
|
// If the frame is not-complete then there must be another line
|
|
// following...
|
|
if (NS_FRAME_IS_COMPLETE(aReflowStatus)) {
|
|
// If any subsequent line has a frame on it then this is not the
|
|
// last line.
|
|
LineData* next = aLine->mNext;
|
|
while (nsnull != next) {
|
|
if (0 != next->ChildCount()) {
|
|
return PR_FALSE;
|
|
}
|
|
next = next->mNext;
|
|
}
|
|
|
|
// If there are next-in-flows and they have any non-empty lines
|
|
// then this is not the last line (because the pullup code will
|
|
// pullup frames from the next-in-flows after this line is
|
|
// placed). Even if the pullup code doesn't pullup, we don't want
|
|
// to signal the last line except in our last-in-flow.
|
|
nsBlockFrame* nextInFlow = (nsBlockFrame*) mNextInFlow;
|
|
while (nsnull != mNextInFlow) {
|
|
LineData* line = nextInFlow->mLines;
|
|
while (nsnull != line) {
|
|
if (0 != next->ChildCount()) {
|
|
return PR_FALSE;
|
|
}
|
|
line = line->mNext;
|
|
}
|
|
nextInFlow = (nsBlockFrame*) nextInFlow->mNextInFlow;
|
|
}
|
|
|
|
// This is the last line
|
|
return PR_TRUE;
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|
|
// XXX This is identical to the back end of the block reflow code, not
|
|
// counting the continuation of block frames part. Factor this!
|
|
|
|
PRBool
|
|
nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
|
|
LineData* aLine,
|
|
nsReflowStatus aReflowStatus)
|
|
{
|
|
PRBool isLastLine = PR_FALSE;
|
|
if (NS_STYLE_TEXT_ALIGN_JUSTIFY == aState.mStyleText->mTextAlign) {
|
|
// For justification we need to know if this line is the last
|
|
// line. If it is, then justification is disabled.
|
|
isLastLine = IsLastLine(aState, aLine, aReflowStatus);
|
|
}
|
|
|
|
// Align the children. This also determines the actual height and
|
|
// width of the line.
|
|
nsInlineReflow& ir = *aState.mInlineReflow;
|
|
nscoord maxAscent, maxDescent;
|
|
ir.VerticalAlignFrames(aLine->mBounds, maxAscent, maxDescent);
|
|
ir.HorizontalAlignFrames(aLine->mBounds, isLastLine);
|
|
ir.RelativePositionFrames(aLine->mCombinedArea);
|
|
|
|
// Calculate the bottom margin for the line.
|
|
nscoord lineBottomMargin = 0;
|
|
if (0 == aLine->mBounds.height) {
|
|
nsIFrame* brFrame = aState.mLineLayout.GetBRFrame();
|
|
if (nsnull != brFrame) {
|
|
// If a line ends in a BR, and the line is empty of height, then
|
|
// we make sure that the line ends up with some height
|
|
// anyway. Note that the height looks like vertical margin so
|
|
// that it can compress with other block margins.
|
|
nsIStyleContext* brSC;
|
|
nsIPresContext& px = aState.mPresContext;
|
|
nsresult rv = brFrame->GetStyleContext(brSC);
|
|
if ((NS_OK == rv) && (nsnull != brSC)) {
|
|
const nsStyleFont* font = (const nsStyleFont*)
|
|
brSC->GetStyleData(eStyleStruct_Font);
|
|
nsIFontMetrics* fm = px.GetMetricsFor(font->mFont);
|
|
if (nsnull != fm) {
|
|
fm->GetHeight(lineBottomMargin);
|
|
NS_RELEASE(fm);
|
|
}
|
|
NS_RELEASE(brSC);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
aState.mRunInToFrame = nsnull;
|
|
aState.mRunInFrame = nsnull;
|
|
}
|
|
|
|
// Calculate the lines top and bottom margin values. The margin will
|
|
// come from an embedded block frame, not from inline frames.
|
|
nscoord topMargin, bottomMargin;
|
|
PRUintn marginFlags = CalculateMargins(aState, aLine, PR_TRUE,
|
|
topMargin, bottomMargin);
|
|
|
|
// See if the line fit. If it doesn't we need to push it. Our first
|
|
// line will always fit.
|
|
|
|
// XXX This is a good place to check and see if we have
|
|
// below-current-line floaters, and if we do make sure that they fit
|
|
// too.
|
|
|
|
// XXX don't forget to factor in the top/bottom margin when sharing
|
|
// this with the block code
|
|
nscoord newY = aLine->mBounds.YMost() + topMargin + lineBottomMargin;
|
|
NS_FRAME_TRACE(NS_FRAME_TRACE_CHILD_REFLOW,
|
|
("nsBlockFrame::PlaceLine: newY=%d limit=%d lineHeight=%d",
|
|
newY, aState.mBottomEdge, aLine->mBounds.height));
|
|
if ((mLines != aLine) && (newY > aState.mBottomEdge)) {
|
|
// Push this line and all of it's children and anything else that
|
|
// follows to our next-in-flow
|
|
PushLines(aState);
|
|
return PR_FALSE;
|
|
}
|
|
|
|
// Compute LINE_OUTSIDE_CHILDREN state for this line. The bit is set
|
|
// if any child frame has outside children.
|
|
aLine->ClearOutsideChildren();
|
|
nsIFrame* kid = aLine->mFirstChild;
|
|
PRInt32 n = aLine->ChildCount();
|
|
while (--n >= 0) {
|
|
nsFrameState state;
|
|
kid->GetFrameState(state);
|
|
if (NS_FRAME_OUTSIDE_CHILDREN & state) {
|
|
aLine->SetOutsideChildren();
|
|
break;
|
|
}
|
|
kid->GetNextSibling(kid);
|
|
}
|
|
|
|
// Apply collapsed top-margin value
|
|
// XXX I bet the bullet placement just got broken by this code
|
|
if (0 != topMargin) {
|
|
SlideFrames(aState.mPresContext, aState.mSpaceManager, aLine, topMargin);
|
|
}
|
|
|
|
// Adjust running margin value when either we have carried margins
|
|
// from the line or we have a non-zero height line.
|
|
if ((HAVE_CARRIED_MARGIN & marginFlags) || (0 != aLine->mBounds.height)) {
|
|
aState.mPrevBottomMargin = bottomMargin;
|
|
aState.mPrevMarginFlags = marginFlags;
|
|
}
|
|
aLine->mCarriedOutTopMargin = ir.GetCarriedOutTopMargin();
|
|
aLine->mCarriedOutBottomMargin = ir.GetCarriedOutBottomMargin();
|
|
aLine->SetMarginFlags(marginFlags);
|
|
|
|
// Now that we know the line is staying put, put in the outside
|
|
// bullet if we have one.
|
|
if ((nsnull == mPrevInFlow) && (nsnull != mBullet) &&
|
|
ShouldPlaceBullet(aLine)) {
|
|
PlaceBullet(aState, maxAscent, topMargin);
|
|
}
|
|
|
|
// Update max-element-size
|
|
if (aState.mComputeMaxElementSize) {
|
|
const nsSize& kidMaxElementSize = ir.GetMaxElementSize();
|
|
if (kidMaxElementSize.width > aState.mMaxElementSize.width) {
|
|
aState.mMaxElementSize.width = kidMaxElementSize.width;
|
|
}
|
|
if (kidMaxElementSize.height > aState.mMaxElementSize.height) {
|
|
aState.mMaxElementSize.height = kidMaxElementSize.height;
|
|
}
|
|
}
|
|
|
|
nscoord xmost = aLine->mBounds.XMost();
|
|
if (xmost > aState.mKidXMost) {
|
|
aState.mKidXMost = xmost;
|
|
}
|
|
aState.mY = newY;
|
|
|
|
// Any below current line floaters to place?
|
|
if (0 != aState.mPendingFloaters.Count()) {
|
|
aState.PlaceFloaters(&aState.mPendingFloaters, PR_FALSE);
|
|
aState.mPendingFloaters.Clear();
|
|
|
|
// XXX Factor in the height of the floaters as well when considering
|
|
// whether the line fits.
|
|
// The default policy is that if there isn't room for the floaters then
|
|
// both the line and the floaters are pushed to the next-in-flow...
|
|
}
|
|
|
|
// Based on the last child we reflowed reflow status, we may need to
|
|
// clear past any floaters.
|
|
aLine->mBreakType = NS_STYLE_CLEAR_NONE;
|
|
if (NS_INLINE_IS_BREAK_AFTER(aReflowStatus)) {
|
|
PRUint8 breakType = NS_INLINE_GET_BREAK_TYPE(aReflowStatus);
|
|
aLine->mBreakType = breakType;
|
|
// Apply break to the line
|
|
switch (breakType) {
|
|
default:
|
|
break;
|
|
case NS_STYLE_CLEAR_LEFT:
|
|
case NS_STYLE_CLEAR_RIGHT:
|
|
case NS_STYLE_CLEAR_LEFT_AND_RIGHT:
|
|
NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
|
|
("nsBlockFrame::PlaceLine: clearing floaters=%d",
|
|
breakType));
|
|
#if XXX
|
|
{
|
|
nscoord y0 = aState.mY;
|
|
aState.ClearFloaters(breakType);
|
|
nscoord dy = aState.mY - y0;
|
|
if (dy > aLine->mCarriedOutBottomMargin) {
|
|
aLine->mCarriedOutBottomMargin = dy;
|
|
}
|
|
}
|
|
#else
|
|
aState.ClearFloaters(breakType);
|
|
#if 0
|
|
y0 = aState.mY;
|
|
aState.ClearFloaters(lastCleanLine->mBreakType);
|
|
dy = aState.mY - y0;
|
|
if (dy > aState.mPrevBottomMargin) {
|
|
aState.mPrevBottomMargin = dy;
|
|
aState.mPrevMarginFlags |= ALREADY_APPLIED_BOTTOM_MARGIN;
|
|
}
|
|
#endif
|
|
#endif
|
|
break;
|
|
}
|
|
// XXX page breaks, etc, need to be passed upwards too!
|
|
}
|
|
|
|
// Update available space after placing line in case below current
|
|
// line floaters were placed or in case we just used up the space in
|
|
// the current band and are ready to move into a new band.
|
|
aState.GetAvailableSpace();
|
|
|
|
return PR_TRUE;
|
|
}
|
|
|
|
PRBool
|
|
nsBlockFrame::ShouldPlaceBullet(LineData* aLine)
|
|
{
|
|
PRBool ok = PR_FALSE;
|
|
const nsStyleList* list;
|
|
GetStyleData(eStyleStruct_List, (const nsStyleStruct*&)list);
|
|
if (NS_STYLE_LIST_STYLE_POSITION_OUTSIDE == list->mListStylePosition) {
|
|
LineData* line = mLines;
|
|
while (nsnull != line) {
|
|
if (line->mBounds.height > 0) {
|
|
if (aLine == line) {
|
|
ok = PR_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if (aLine == line) {
|
|
break;
|
|
}
|
|
line = line->mNext;
|
|
}
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
void
|
|
nsBlockFrame::PlaceBullet(nsBlockReflowState& aState,
|
|
nscoord aMaxAscent,
|
|
nscoord aTopMargin)
|
|
{
|
|
// Reflow the bullet now
|
|
nsSize availSize;
|
|
availSize.width = NS_UNCONSTRAINEDSIZE;
|
|
availSize.height = NS_UNCONSTRAINEDSIZE;
|
|
nsHTMLReflowState reflowState(aState.mPresContext, mBullet, aState,
|
|
availSize, &aState.mLineLayout);
|
|
nsHTMLReflowMetrics metrics(nsnull);
|
|
nsIHTMLReflow* htmlReflow;
|
|
nsresult rv = mBullet->QueryInterface(kIHTMLReflowIID, (void**)&htmlReflow);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
nsReflowStatus status;
|
|
htmlReflow->WillReflow(aState.mPresContext);
|
|
htmlReflow->Reflow(aState.mPresContext, metrics, reflowState, status);
|
|
htmlReflow->DidReflow(aState.mPresContext, NS_FRAME_REFLOW_FINISHED);
|
|
}
|
|
|
|
// Place the bullet now; use its right margin to distance it
|
|
// from the rest of the frames in the line
|
|
nsMargin margin;
|
|
nsHTMLReflowState::ComputeMarginFor(mBullet, &aState, margin);
|
|
nscoord x = aState.mBorderPadding.left - margin.right - metrics.width;
|
|
// XXX This calculation may be wrong, especially if
|
|
// vertical-alignment occurs on the line!
|
|
nscoord y = aState.mBorderPadding.top + aMaxAscent -
|
|
metrics.ascent + aTopMargin;
|
|
mBullet->SetRect(nsRect(x, y, metrics.width, metrics.height));
|
|
}
|
|
|
|
static nsresult
|
|
FindFloatersIn(nsIFrame* aFrame, nsVoidArray*& aArray)
|
|
{
|
|
const nsStyleDisplay* display;
|
|
aFrame->GetStyleData(eStyleStruct_Display,
|
|
(const nsStyleStruct*&) display);
|
|
if (NS_STYLE_FLOAT_NONE != display->mFloats) {
|
|
if (nsnull == aArray) {
|
|
aArray = new nsVoidArray();
|
|
if (nsnull == aArray) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
}
|
|
aArray->AppendElement(aFrame);
|
|
}
|
|
|
|
if (NS_STYLE_DISPLAY_INLINE == display->mDisplay) {
|
|
nsIFrame* kid;
|
|
aFrame->FirstChild(nsnull, kid);
|
|
while (nsnull != kid) {
|
|
nsresult rv = FindFloatersIn(kid, aArray);
|
|
if (NS_OK != rv) {
|
|
return rv;
|
|
}
|
|
kid->GetNextSibling(kid);
|
|
}
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
nsBlockFrame::FindFloaters(LineData* aLine)
|
|
{
|
|
nsVoidArray* floaters = aLine->mFloaters;
|
|
if (nsnull != floaters) {
|
|
// Empty floater array before proceeding
|
|
floaters->Clear();
|
|
}
|
|
|
|
nsIFrame* frame = aLine->mFirstChild;
|
|
PRInt32 n = aLine->ChildCount();
|
|
while (--n >= 0) {
|
|
FindFloatersIn(frame, floaters);
|
|
frame->GetNextSibling(frame);
|
|
}
|
|
|
|
aLine->mFloaters = floaters;
|
|
|
|
// Get rid of floater array if we don't need it
|
|
if (nsnull != floaters) {
|
|
if (0 == floaters->Count()) {
|
|
delete floaters;
|
|
aLine->mFloaters = nsnull;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
nsBlockFrame::PushLines(nsBlockReflowState& aState)
|
|
{
|
|
NS_ASSERTION(nsnull != aState.mPrevLine, "bad push");
|
|
|
|
LineData* lastLine = aState.mPrevLine;
|
|
LineData* nextLine = lastLine->mNext;
|
|
|
|
lastLine->mNext = nsnull;
|
|
mOverflowLines = nextLine;
|
|
|
|
// Break frame sibling list
|
|
nsIFrame* lastFrame = lastLine->LastChild();
|
|
lastFrame->SetNextSibling(nsnull);
|
|
|
|
NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
|
|
("nsBlockFrame::PushLines: line=%p prevInFlow=%p nextInFlow=%p",
|
|
mOverflowLines, mPrevInFlow, mNextInFlow));
|
|
#ifdef NS_DEBUG
|
|
if (GetVerifyTreeEnable()) {
|
|
VerifyChildCount(mLines);
|
|
VerifyChildCount(mOverflowLines, PR_TRUE);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
PRBool
|
|
nsBlockFrame::DrainOverflowLines()
|
|
{
|
|
PRBool drained = PR_FALSE;
|
|
|
|
// First grab the prev-in-flows overflow lines
|
|
nsBlockFrame* prevBlock = (nsBlockFrame*) mPrevInFlow;
|
|
if (nsnull != prevBlock) {
|
|
LineData* line = prevBlock->mOverflowLines;
|
|
if (nsnull != line) {
|
|
drained = PR_TRUE;
|
|
NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
|
|
("nsBlockFrame::DrainOverflowLines: line=%p prevInFlow=%p",
|
|
line, prevBlock));
|
|
prevBlock->mOverflowLines = nsnull;
|
|
|
|
// Make all the frames on the mOverflowLines list mine
|
|
nsIFrame* lastFrame = nsnull;
|
|
nsIFrame* frame = line->mFirstChild;
|
|
while (nsnull != frame) {
|
|
nsIFrame* geometricParent;
|
|
nsIFrame* contentParent;
|
|
frame->GetGeometricParent(geometricParent);
|
|
frame->GetContentParent(contentParent);
|
|
if (contentParent == geometricParent) {
|
|
frame->SetContentParent(this);
|
|
}
|
|
frame->SetGeometricParent(this);
|
|
lastFrame = frame;
|
|
frame->GetNextSibling(frame);
|
|
}
|
|
|
|
// Join the line lists
|
|
if (nsnull == mLines) {
|
|
mLines = line;
|
|
}
|
|
else {
|
|
// Join the sibling lists together
|
|
lastFrame->SetNextSibling(mLines->mFirstChild);
|
|
|
|
// Place overflow lines at the front of our line list
|
|
LineData* lastLine = LastLine(line);
|
|
lastLine->mNext = mLines;
|
|
mLines = line;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now grab our own overflow lines
|
|
if (nsnull != mOverflowLines) {
|
|
// This can happen when we reflow and not everything fits and then
|
|
// we are told to reflow again before a next-in-flow is created
|
|
// and reflows.
|
|
NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
|
|
("nsBlockFrame::DrainOverflowLines: from me, line=%p",
|
|
mOverflowLines));
|
|
LineData* lastLine = LastLine(mLines);
|
|
if (nsnull == lastLine) {
|
|
mLines = mOverflowLines;
|
|
}
|
|
else {
|
|
lastLine->mNext = mOverflowLines;
|
|
nsIFrame* lastFrame = lastLine->LastChild();
|
|
lastFrame->SetNextSibling(mOverflowLines->mFirstChild);
|
|
|
|
// Update our last-content-index now that we have a new last child
|
|
lastLine = LastLine(mOverflowLines);
|
|
}
|
|
mOverflowLines = nsnull;
|
|
drained = PR_TRUE;
|
|
}
|
|
|
|
#ifdef NS_DEBUG
|
|
if (GetVerifyTreeEnable()) {
|
|
VerifyChildCount(mLines, PR_TRUE);
|
|
}
|
|
#endif
|
|
return drained;
|
|
}
|
|
|
|
nsresult
|
|
nsBlockFrame::InsertNewFrame(nsIPresContext& aPresContext,
|
|
nsBlockFrame* aParentFrame,
|
|
nsIFrame* aNewFrame,
|
|
nsIFrame* aPrevSibling)
|
|
{
|
|
if (nsnull == mLines) {
|
|
NS_ASSERTION(nsnull == aPrevSibling, "prev-sibling and empty line list!");
|
|
return AppendNewFrames(aPresContext, aNewFrame);
|
|
}
|
|
|
|
const nsStyleDisplay* display;
|
|
aNewFrame->GetStyleData(eStyleStruct_Display, (const nsStyleStruct*&) display);
|
|
const nsStylePosition* position;
|
|
aNewFrame->GetStyleData(eStyleStruct_Position, (const nsStyleStruct*&) position);
|
|
PRUint16 newFrameIsBlock = nsLineLayout::TreatFrameAsBlock(display, position)
|
|
? LINE_IS_BLOCK : 0;
|
|
|
|
// See if we need to move the frame outside of the flow, and insert a
|
|
// placeholder frame in its place
|
|
nsIFrame* placeholder;
|
|
if (MoveFrameOutOfFlow(aPresContext, aNewFrame, display, position, placeholder)) {
|
|
// Add the placeholder frame to the flow
|
|
aNewFrame = placeholder;
|
|
newFrameIsBlock = PR_FALSE; // placeholder frame is always inline
|
|
}
|
|
else {
|
|
// Wrap the frame in a view if necessary
|
|
nsIStyleContext* kidSC;
|
|
aNewFrame->GetStyleContext(kidSC);
|
|
nsresult rv = CreateViewForFrame(aPresContext, aNewFrame, kidSC, PR_FALSE);
|
|
NS_RELEASE(kidSC);
|
|
if (NS_OK != rv) {
|
|
return rv;
|
|
}
|
|
}
|
|
|
|
// Insert/append the frame into flows line list at the right spot
|
|
LineData* newLine;
|
|
LineData* line = aParentFrame->mLines;
|
|
if (nsnull == aPrevSibling) {
|
|
// Insert new frame into the sibling list
|
|
aNewFrame->SetNextSibling(line->mFirstChild);
|
|
|
|
if (line->IsBlock() || newFrameIsBlock) {
|
|
// Create a new line
|
|
newLine = new LineData(aNewFrame, 1, newFrameIsBlock);
|
|
if (nsnull == newLine) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
newLine->mNext = aParentFrame->mLines;
|
|
aParentFrame->mLines = newLine;
|
|
} else {
|
|
// Insert frame at the front of the line
|
|
line->mFirstChild = aNewFrame;
|
|
line->mChildCount++;
|
|
line->MarkDirty();
|
|
}
|
|
}
|
|
else {
|
|
// Find line containing the previous sibling to the new frame
|
|
line = FindLineContaining(line, aPrevSibling);
|
|
NS_ASSERTION(nsnull != line, "no line contains the previous sibling");
|
|
if (nsnull != line) {
|
|
if (line->IsBlock()) {
|
|
// Create a new line just after line
|
|
newLine = new LineData(aNewFrame, 1, newFrameIsBlock);
|
|
if (nsnull == newLine) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
newLine->mNext = line->mNext;
|
|
line->mNext = newLine;
|
|
}
|
|
else if (newFrameIsBlock) {
|
|
// Split line in two, if necessary. We can't allow a block to
|
|
// end up in an inline line.
|
|
if (line->IsLastChild(aPrevSibling)) {
|
|
// The new frame goes after prevSibling and prevSibling is
|
|
// the last frame on the line. Therefore we don't need to
|
|
// split the line, just create a new line.
|
|
newLine = new LineData(aNewFrame, 1, newFrameIsBlock);
|
|
if (nsnull == newLine) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
newLine->mNext = line->mNext;
|
|
line->mNext = newLine;
|
|
}
|
|
else {
|
|
// The new frame goes after prevSibling and prevSibling is
|
|
// somewhere in the line, but not at the end. Split the line
|
|
// just after prevSibling.
|
|
PRInt32 i, n = line->ChildCount();
|
|
nsIFrame* frame = line->mFirstChild;
|
|
for (i = 0; i < n; i++) {
|
|
if (frame == aPrevSibling) {
|
|
nsIFrame* nextSibling;
|
|
aPrevSibling->GetNextSibling(nextSibling);
|
|
|
|
// Create new line to hold the remaining frames
|
|
NS_ASSERTION(n - i - 1 > 0, "bad line count");
|
|
newLine = new LineData(nextSibling, n - i - 1, 0);
|
|
if (nsnull == newLine) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
newLine->mNext = line->mNext;
|
|
line->mNext = newLine;
|
|
line->MarkDirty();
|
|
line->mChildCount = i + 1;
|
|
break;
|
|
}
|
|
frame->GetNextSibling(frame);
|
|
}
|
|
|
|
// Now create a new line to hold the block
|
|
newLine = new LineData(aNewFrame, 1, newFrameIsBlock);
|
|
if (nsnull == newLine) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
newLine->mNext = line->mNext;
|
|
line->mNext = newLine;
|
|
}
|
|
}
|
|
else {
|
|
// Insert frame into the line.
|
|
//XXX NS_ASSERTION(line->GetLastContentIsComplete(), "bad line LCIC");
|
|
line->mChildCount++;
|
|
line->MarkDirty();
|
|
}
|
|
}
|
|
|
|
// Insert new frame into the sibling list; note: this must be done
|
|
// after the above logic because the above logic depends on the
|
|
// sibling list being in the "before insertion" state.
|
|
nsIFrame* nextSibling;
|
|
aPrevSibling->GetNextSibling(nextSibling);
|
|
aNewFrame->SetNextSibling(nextSibling);
|
|
aPrevSibling->SetNextSibling(aNewFrame);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
PRBool
|
|
nsBlockFrame::DeleteChildsNextInFlow(nsIPresContext& aPresContext,
|
|
nsIFrame* aChild)
|
|
{
|
|
NS_PRECONDITION(IsChild(aChild), "bad geometric parent");
|
|
|
|
nsIFrame* nextInFlow;
|
|
nsBlockFrame* parent;
|
|
|
|
aChild->GetNextInFlow(nextInFlow);
|
|
NS_PRECONDITION(nsnull != nextInFlow, "null next-in-flow");
|
|
nextInFlow->GetGeometricParent((nsIFrame*&)parent);
|
|
|
|
// If the next-in-flow has a next-in-flow then delete it, too (and
|
|
// delete it first).
|
|
nsIFrame* nextNextInFlow;
|
|
nextInFlow->GetNextInFlow(nextNextInFlow);
|
|
if (nsnull != nextNextInFlow) {
|
|
parent->DeleteChildsNextInFlow(aPresContext, nextInFlow);
|
|
}
|
|
|
|
#ifdef NS_DEBUG
|
|
PRInt32 childCount;
|
|
nsIFrame* firstChild;
|
|
nextInFlow->FirstChild(nsnull, firstChild);
|
|
childCount = LengthOf(firstChild);
|
|
NS_ASSERTION((0 == childCount) && (nsnull == firstChild),
|
|
"deleting !empty next-in-flow");
|
|
#endif
|
|
|
|
// Disconnect the next-in-flow from the flow list
|
|
nextInFlow->BreakFromPrevFlow();
|
|
|
|
// Remove nextInFlow from the parents line list. Also remove it from
|
|
// the sibling list.
|
|
if (RemoveChild(parent->mLines, nextInFlow)) {
|
|
NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
|
|
("nsBlockFrame::DeleteNextInFlowsFor: frame=%p (from mLines)",
|
|
nextInFlow));
|
|
goto done;
|
|
}
|
|
|
|
// If we get here then we didn't find the child on the line list. If
|
|
// it's not there then it has to be on the overflow lines list.
|
|
if (nsnull != mOverflowLines) {
|
|
if (RemoveChild(parent->mOverflowLines, nextInFlow)) {
|
|
NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
|
|
("nsBlockFrame::DeleteNextInFlowsFor: frame=%p (from overflow)",
|
|
nextInFlow));
|
|
goto done;
|
|
}
|
|
}
|
|
NS_NOTREACHED("can't find next-in-flow in overflow list");
|
|
|
|
done:;
|
|
// If the parent is us then we will finish reflowing and update the
|
|
// content offsets of our parents when we are a pseudo-frame; if the
|
|
// parent is not us then it's a next-in-flow which means it will get
|
|
// reflowed by our parent and fix its content offsets. So there.
|
|
|
|
// Delete the next-in-flow frame and adjust its parents child count
|
|
nextInFlow->DeleteFrame(aPresContext);
|
|
|
|
#ifdef NS_DEBUG
|
|
aChild->GetNextInFlow(nextInFlow);
|
|
NS_POSTCONDITION(nsnull == nextInFlow, "non null next-in-flow");
|
|
#endif
|
|
return PR_TRUE;
|
|
}
|
|
|
|
PRBool
|
|
nsBlockFrame::RemoveChild(LineData* aLines, nsIFrame* aChild)
|
|
{
|
|
LineData* line = aLines;
|
|
nsIFrame* prevChild = nsnull;
|
|
while (nsnull != line) {
|
|
nsIFrame* child = line->mFirstChild;
|
|
PRInt32 n = line->ChildCount();
|
|
while (--n >= 0) {
|
|
nsIFrame* nextChild;
|
|
child->GetNextSibling(nextChild);
|
|
if (child == aChild) {
|
|
NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
|
|
("nsBlockFrame::RemoveChild: line=%p frame=%p",
|
|
line, aChild));
|
|
// Continuations HAVE to be at the start of a line
|
|
NS_ASSERTION(child == line->mFirstChild, "bad continuation");
|
|
line->mFirstChild = nextChild;
|
|
if (0 == --line->mChildCount) {
|
|
line->mFirstChild = nsnull;
|
|
}
|
|
if (nsnull != prevChild) {
|
|
// When nextInFlow and it's continuation are in the same
|
|
// container then we remove the nextInFlow from the sibling
|
|
// list.
|
|
prevChild->SetNextSibling(nextChild);
|
|
}
|
|
return PR_TRUE;
|
|
}
|
|
prevChild = child;
|
|
child = nextChild;
|
|
}
|
|
line = line->mNext;
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// Floater support
|
|
|
|
void
|
|
nsBlockFrame::ReflowFloater(nsIPresContext& aPresContext,
|
|
nsBlockReflowState& aState,
|
|
nsIFrame* aFloaterFrame,
|
|
nsHTMLReflowState& aFloaterReflowState)
|
|
{
|
|
// If either dimension is constrained then get the border and
|
|
// padding values in advance.
|
|
nsMargin bp(0, 0, 0, 0);
|
|
if (aFloaterReflowState.HaveFixedContentWidth() ||
|
|
aFloaterReflowState.HaveFixedContentHeight()) {
|
|
nsHTMLReflowState::ComputeBorderPaddingFor(aFloaterFrame, &aState, bp);
|
|
}
|
|
|
|
// Compute the available width for the floater
|
|
nsSize& kidAvailSize = aFloaterReflowState.maxSize;
|
|
if (aFloaterReflowState.HaveFixedContentWidth()) {
|
|
// When the floater has a contrained width, give it just enough
|
|
// space for its styled width plus its borders and paddings.
|
|
kidAvailSize.width = aFloaterReflowState.minWidth + bp.left + bp.right;
|
|
}
|
|
else {
|
|
// If we are floating something and we don't know the width then
|
|
// find a maximum width for it to reflow into. Walk upwards until
|
|
// we find something with an unconstrained width.
|
|
const nsHTMLReflowState* rsp = &aState;
|
|
kidAvailSize.width = 0;
|
|
while (nsnull != rsp) {
|
|
if (eHTMLFrameConstraint_FixedContent == rsp->widthConstraint) {
|
|
kidAvailSize.width = rsp->minWidth;
|
|
break;
|
|
}
|
|
else if (NS_UNCONSTRAINEDSIZE != rsp->widthConstraint) {
|
|
kidAvailSize.width = rsp->maxSize.width;
|
|
if (kidAvailSize.width > 0) {
|
|
break;
|
|
}
|
|
}
|
|
// XXX This cast is unfortunate!
|
|
rsp = (const nsHTMLReflowState*) rsp->parentReflowState;
|
|
}
|
|
}
|
|
|
|
// Compute the available height for the floater
|
|
if (aFloaterReflowState.HaveFixedContentHeight()) {
|
|
kidAvailSize.height = aFloaterReflowState.minHeight + bp.top + bp.bottom;
|
|
}
|
|
else {
|
|
kidAvailSize.height = NS_UNCONSTRAINEDSIZE;
|
|
}
|
|
|
|
// Resize reflow the anchored item into the available space
|
|
nsIHTMLReflow* floaterReflow;
|
|
if (NS_OK == aFloaterFrame->QueryInterface(kIHTMLReflowIID,
|
|
(void**)&floaterReflow)) {
|
|
nsHTMLReflowMetrics desiredSize(nsnull);
|
|
nsReflowStatus status;
|
|
floaterReflow->WillReflow(aPresContext);
|
|
floaterReflow->Reflow(aPresContext, desiredSize, aFloaterReflowState,
|
|
status);
|
|
aFloaterFrame->SizeTo(desiredSize.width, desiredSize.height);
|
|
}
|
|
}
|
|
|
|
void
|
|
nsBlockReflowState::InitFloater(nsPlaceholderFrame* aPlaceholder)
|
|
{
|
|
nsIFrame* floater = aPlaceholder->GetAnchoredItem();
|
|
|
|
floater->SetGeometricParent(mBlock);
|
|
|
|
// XXX the choice of constructors is confusing and non-obvious
|
|
nsSize kidAvailSize(0, 0);
|
|
nsHTMLReflowState reflowState(mPresContext, floater, *this,
|
|
kidAvailSize, eReflowReason_Initial);
|
|
mBlock->ReflowFloater(mPresContext, *this, floater, reflowState);
|
|
|
|
AddFloater(aPlaceholder);
|
|
}
|
|
|
|
// This is called by the line layout's AddFloater method when a
|
|
// place-holder frame is reflowed in a line. If the floater is a
|
|
// left-most child (it's x coordinate is at the line's left margin)
|
|
// then the floater is place immediately, otherwise the floater
|
|
// placement is deferred until the line has been reflowed.
|
|
void
|
|
nsBlockReflowState::AddFloater(nsPlaceholderFrame* aPlaceholder)
|
|
{
|
|
// Update the current line's floater array
|
|
NS_ASSERTION(nsnull != mCurrentLine, "null ptr");
|
|
if (nsnull == mCurrentLine->mFloaters) {
|
|
mCurrentLine->mFloaters = new nsVoidArray();
|
|
}
|
|
mCurrentLine->mFloaters->AppendElement(aPlaceholder);
|
|
|
|
// Now place the floater immediately if possible. Otherwise stash it
|
|
// away in mPendingFloaters and place it later.
|
|
if (0 == mLineLayout.GetPlacedFrames()) {
|
|
NS_FRAME_LOG(NS_FRAME_TRACE_CHILD_REFLOW,
|
|
("nsBlockReflowState::AddFloater: IsLeftMostChild, placeHolder=%p",
|
|
aPlaceholder));
|
|
|
|
// Flush out pending bottom margin before placing floater
|
|
if (0 != mPrevBottomMargin) {
|
|
mY += mPrevBottomMargin;
|
|
mPrevMarginFlags |= ALREADY_APPLIED_BOTTOM_MARGIN;
|
|
mPrevBottomMargin = 0;
|
|
}
|
|
|
|
// Because we are in the middle of reflowing a placeholder frame
|
|
// within a line (and possibly nested in an inline frame or two
|
|
// that's a child of our block) we need to restore the space
|
|
// manager's translation to the space that the block resides in
|
|
// before placing the floater.
|
|
PRBool isLeftFloater;
|
|
nscoord ox, oy;
|
|
mSpaceManager->GetTranslation(ox, oy);
|
|
nscoord dx = ox - mSpaceManagerX;
|
|
nscoord dy = oy - mSpaceManagerY;
|
|
mSpaceManager->Translate(-dx, -dy);
|
|
PlaceFloater(aPlaceholder, isLeftFloater);
|
|
|
|
// Pass on updated available space to the current inline reflow engine
|
|
GetAvailableSpace();
|
|
mLineLayout.UpdateInlines(mCurrentBand.availSpace.x + mBorderPadding.left,
|
|
mY,
|
|
mCurrentBand.availSpace.width,
|
|
mCurrentBand.availSpace.height,
|
|
isLeftFloater);
|
|
|
|
// Restore coordinate system
|
|
mSpaceManager->Translate(dx, dy);
|
|
}
|
|
else {
|
|
// This floater will be placed after the line is done (it is a
|
|
// below current line floater).
|
|
NS_FRAME_LOG(NS_FRAME_TRACE_CHILD_REFLOW,
|
|
("nsBlockReflowState::AddFloater: pending, placeHolder=%p",
|
|
aPlaceholder));
|
|
mPendingFloaters.AppendElement(aPlaceholder);
|
|
}
|
|
}
|
|
|
|
PRBool
|
|
nsBlockReflowState::IsLeftMostChild(nsIFrame* aFrame)
|
|
{
|
|
for (;;) {
|
|
nsIFrame* parent;
|
|
aFrame->GetGeometricParent(parent);
|
|
if (parent == mBlock) {
|
|
nsIFrame* child = mCurrentLine->mFirstChild;
|
|
PRInt32 n = mCurrentLine->ChildCount();
|
|
while ((nsnull != child) && (aFrame != child) && (--n >= 0)) {
|
|
nsSize size;
|
|
|
|
// Is the child zero-sized?
|
|
child->GetSize(size);
|
|
if (size.width > 0) {
|
|
// We found a non-zero sized child frame that precedes aFrame
|
|
return PR_FALSE;
|
|
}
|
|
child->GetNextSibling(child);
|
|
}
|
|
break;
|
|
}
|
|
else {
|
|
// See if there are any non-zero sized child frames that precede
|
|
// aFrame in the child list
|
|
nsIFrame* child;
|
|
parent->FirstChild(nsnull, child);
|
|
while ((nsnull != child) && (aFrame != child)) {
|
|
nsSize size;
|
|
|
|
// Is the child zero-sized?
|
|
child->GetSize(size);
|
|
if (size.width > 0) {
|
|
// We found a non-zero sized child frame that precedes aFrame
|
|
return PR_FALSE;
|
|
}
|
|
child->GetNextSibling(child);
|
|
}
|
|
}
|
|
|
|
// aFrame is the left-most non-zero sized frame in its geometric parent.
|
|
// Walk up one level and check that its parent is left-most as well
|
|
aFrame = parent;
|
|
}
|
|
return PR_TRUE;
|
|
}
|
|
|
|
void
|
|
nsBlockReflowState::PlaceFloater(nsPlaceholderFrame* aPlaceholder,
|
|
PRBool& aIsLeftFloater)
|
|
{
|
|
nsIFrame* floater = aPlaceholder->GetAnchoredItem();
|
|
|
|
// XXX the choice of constructors is confusing and non-obvious
|
|
nsSize kidAvailSize(0, 0);
|
|
nsHTMLReflowState reflowState(mPresContext, floater, *this, kidAvailSize);
|
|
|
|
// Reflow the floater if it's targetted for a reflow
|
|
if (nsnull != reflowCommand) {
|
|
if (floater == mNextRCFrame) {
|
|
reflowState.lineLayout = nsnull;
|
|
mBlock->ReflowFloater(mPresContext, *this, floater, reflowState);
|
|
}
|
|
}
|
|
|
|
// Get the type of floater
|
|
const nsStyleDisplay* floaterDisplay;
|
|
const nsStyleSpacing* floaterSpacing;
|
|
floater->GetStyleData(eStyleStruct_Display,
|
|
(const nsStyleStruct*&)floaterDisplay);
|
|
floater->GetStyleData(eStyleStruct_Spacing,
|
|
(const nsStyleStruct*&)floaterSpacing);
|
|
|
|
// See if the floater should clear any preceeding floaters...
|
|
if (NS_STYLE_CLEAR_NONE != floaterDisplay->mBreakType) {
|
|
ClearFloaters(floaterDisplay->mBreakType);
|
|
}
|
|
else {
|
|
// Get the band of available space
|
|
GetAvailableSpace();
|
|
}
|
|
|
|
// Get the floaters bounding box and margin information
|
|
nsRect region;
|
|
floater->GetRect(region);
|
|
nsMargin floaterMargin;
|
|
ComputeMarginFor(floater, this, floaterMargin);
|
|
|
|
// Adjust the floater size by its margin. That's the area that will
|
|
// impact the space manager.
|
|
region.width += floaterMargin.left + floaterMargin.right;
|
|
region.height += floaterMargin.top + floaterMargin.bottom;
|
|
|
|
// Find a place to place the floater. The CSS2 spec doesn't want
|
|
// floaters overlapping each other or sticking out of the containing
|
|
// block (CSS2 spec section 9.5.1, see the rule list).
|
|
NS_ASSERTION((NS_STYLE_FLOAT_LEFT == floaterDisplay->mFloats) ||
|
|
(NS_STYLE_FLOAT_RIGHT == floaterDisplay->mFloats),
|
|
"invalid float type");
|
|
|
|
// While there is not enough room for the floater, clear past
|
|
// other floaters until there is room (or the band is not impacted
|
|
// by a floater).
|
|
while ((mCurrentBand.availSpace.width < region.width) &&
|
|
(mCurrentBand.availSpace.width < mContentArea.width)) {
|
|
// The CSS2 spec says that floaters should be placed as high as
|
|
// possible. We accomodate this easily by noting that if the band
|
|
// is not the full width of the content area then it must have
|
|
// been impacted by a floater. And we know that the height of the
|
|
// band will be the height of the shortest floater, therefore we
|
|
// adjust mY by that distance and keep trying until we have enough
|
|
// space for this floater.
|
|
#ifdef NOISY_FLOATER_CLEARING
|
|
mBlock->ListTag(stdout);
|
|
printf(": clearing floater during floater placement: ");
|
|
printf("availWidth=%d regionWidth=%d,%d(w/o margins) contentWidth=%d\n",
|
|
mCurrentBand.availSpace.width, region.width,
|
|
region.width - floaterMargin.left - floaterMargin.right,
|
|
mContentArea.width);
|
|
#endif
|
|
mY += mCurrentBand.availSpace.height;
|
|
GetAvailableSpace();
|
|
}
|
|
|
|
// Assign an x and y coordinate to the floater. Note that the x,y
|
|
// coordinates are computed <b>relative to the translation in the
|
|
// spacemanager</b> which means that the impacted region will be
|
|
// <b>inside</b> the border/padding area.
|
|
if (NS_STYLE_FLOAT_LEFT == floaterDisplay->mFloats) {
|
|
aIsLeftFloater = PR_TRUE;
|
|
region.x = mCurrentBand.availSpace.x;
|
|
}
|
|
else {
|
|
aIsLeftFloater = PR_FALSE;
|
|
region.x = mCurrentBand.availSpace.XMost() - region.width;
|
|
}
|
|
region.y = mY - mBorderPadding.top;
|
|
if (region.y < 0) {
|
|
// CSS2 spec, 9.5.1 rule [4]: A floating box's outer top may not
|
|
// be higher than the top of its containing block.
|
|
|
|
// XXX It's not clear if it means the higher than the outer edge
|
|
// or the border edge or the inner edge?
|
|
region.y = 0;
|
|
}
|
|
|
|
// Place the floater in the space manager
|
|
mSpaceManager->AddRectRegion(floater, region);
|
|
|
|
// Set the origin of the floater frame, in frame coordinates. These
|
|
// coordinates are <b>not</b> relative to the spacemanager
|
|
// translation, therefore we have to factor in our border/padding.
|
|
floater->MoveTo(mBorderPadding.left + floaterMargin.left + region.x,
|
|
mBorderPadding.top + floaterMargin.top + region.y);
|
|
}
|
|
|
|
/**
|
|
* Place below-current-line floaters.
|
|
*/
|
|
void
|
|
nsBlockReflowState::PlaceFloaters(nsVoidArray* aFloaters, PRBool aAllOfThem)
|
|
{
|
|
NS_PRECONDITION(aFloaters->Count() > 0, "no floaters");
|
|
|
|
PRInt32 numFloaters = aFloaters->Count();
|
|
for (PRInt32 i = 0; i < numFloaters; i++) {
|
|
nsPlaceholderFrame* placeholderFrame = (nsPlaceholderFrame*)
|
|
aFloaters->ElementAt(i);
|
|
if (!aAllOfThem && IsLeftMostChild(placeholderFrame)) {
|
|
// Left-most children are placed during the line's reflow
|
|
continue;
|
|
}
|
|
PRBool isLeftFloater;
|
|
PlaceFloater(placeholderFrame, isLeftFloater);
|
|
}
|
|
|
|
// Update available spcae now that the floaters have been placed
|
|
GetAvailableSpace();
|
|
}
|
|
|
|
void
|
|
nsBlockReflowState::ClearFloaters(PRUint8 aBreakType)
|
|
{
|
|
// Update band information based on current mY before clearing
|
|
GetAvailableSpace();
|
|
|
|
for (;;) {
|
|
PRBool haveFloater = PR_FALSE;
|
|
|
|
// Find the Y coordinate to clear to. Note that the band trapezoid
|
|
// coordinates are relative to the our spacemanager translation
|
|
// (which means the band coordinates are inside the border+padding
|
|
// area of this block frame).
|
|
NS_FRAME_LOG(NS_FRAME_TRACE_CHILD_REFLOW,
|
|
("nsBlockReflowState::ClearFloaters: mY=%d trapCount=%d",
|
|
mY, mCurrentBand.count));
|
|
nscoord clearYMost = mY - mBorderPadding.top;
|
|
nsRect tmp;
|
|
PRInt32 i;
|
|
for (i = 0; i < mCurrentBand.count; i++) {
|
|
const nsStyleDisplay* display;
|
|
nsBandTrapezoid* trapezoid = &mCurrentBand.data[i];
|
|
NS_FRAME_LOG(NS_FRAME_TRACE_CHILD_REFLOW,
|
|
("nsBlockReflowState::ClearFloaters: trap=%d state=%d",
|
|
i, trapezoid->state));
|
|
if (nsBandTrapezoid::Available != trapezoid->state) {
|
|
haveFloater = PR_TRUE;
|
|
if (nsBandTrapezoid::OccupiedMultiple == trapezoid->state) {
|
|
PRInt32 fn, numFrames = trapezoid->frames->Count();
|
|
NS_ASSERTION(numFrames > 0, "bad trapezoid frame list");
|
|
for (fn = 0; fn < numFrames; fn++) {
|
|
nsIFrame* frame = (nsIFrame*) trapezoid->frames->ElementAt(fn);
|
|
frame->GetStyleData(eStyleStruct_Display,
|
|
(const nsStyleStruct*&)display);
|
|
NS_FRAME_LOG(NS_FRAME_TRACE_CHILD_REFLOW,
|
|
("nsBlockReflowState::ClearFloaters: frame[%d]=%p floats=%d",
|
|
fn, frame, display->mFloats));
|
|
switch (display->mFloats) {
|
|
case NS_STYLE_FLOAT_LEFT:
|
|
if ((NS_STYLE_CLEAR_LEFT == aBreakType) ||
|
|
(NS_STYLE_CLEAR_LEFT_AND_RIGHT == aBreakType)) {
|
|
trapezoid->GetRect(tmp);
|
|
nscoord ym = tmp.YMost();
|
|
if (ym > clearYMost) {
|
|
clearYMost = ym;
|
|
}
|
|
}
|
|
break;
|
|
case NS_STYLE_FLOAT_RIGHT:
|
|
if ((NS_STYLE_CLEAR_RIGHT == aBreakType) ||
|
|
(NS_STYLE_CLEAR_LEFT_AND_RIGHT == aBreakType)) {
|
|
trapezoid->GetRect(tmp);
|
|
nscoord ym = tmp.YMost();
|
|
if (ym > clearYMost) {
|
|
clearYMost = ym;
|
|
NS_FRAME_LOG(NS_FRAME_TRACE_CHILD_REFLOW,
|
|
("nsBlockReflowState::ClearFloaters: right clearYMost=%d",
|
|
clearYMost));
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
trapezoid->frame->GetStyleData(eStyleStruct_Display,
|
|
(const nsStyleStruct*&)display);
|
|
NS_FRAME_LOG(NS_FRAME_TRACE_CHILD_REFLOW,
|
|
("nsBlockReflowState::ClearFloaters: frame=%p floats=%d",
|
|
trapezoid->frame, display->mFloats));
|
|
switch (display->mFloats) {
|
|
case NS_STYLE_FLOAT_LEFT:
|
|
if ((NS_STYLE_CLEAR_LEFT == aBreakType) ||
|
|
(NS_STYLE_CLEAR_LEFT_AND_RIGHT == aBreakType)) {
|
|
trapezoid->GetRect(tmp);
|
|
nscoord ym = tmp.YMost();
|
|
if (ym > clearYMost) {
|
|
clearYMost = ym;
|
|
NS_FRAME_LOG(NS_FRAME_TRACE_CHILD_REFLOW,
|
|
("nsBlockReflowState::ClearFloaters: left clearYMost=%d",
|
|
clearYMost));
|
|
}
|
|
}
|
|
break;
|
|
case NS_STYLE_FLOAT_RIGHT:
|
|
if ((NS_STYLE_CLEAR_RIGHT == aBreakType) ||
|
|
(NS_STYLE_CLEAR_LEFT_AND_RIGHT == aBreakType)) {
|
|
trapezoid->GetRect(tmp);
|
|
nscoord ym = tmp.YMost();
|
|
if (ym > clearYMost) {
|
|
clearYMost = ym;
|
|
NS_FRAME_LOG(NS_FRAME_TRACE_CHILD_REFLOW,
|
|
("nsBlockReflowState::ClearFloaters: right clearYMost=%d",
|
|
clearYMost));
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Nothing to clear
|
|
if (!haveFloater || (clearYMost == mY - mBorderPadding.top)) {
|
|
break;
|
|
}
|
|
|
|
NS_FRAME_LOG(NS_FRAME_TRACE_CHILD_REFLOW,
|
|
("nsBlockReflowState::ClearFloaters: mY=%d clearYMost=%d",
|
|
mY, clearYMost));
|
|
mY = mBorderPadding.top + clearYMost + 1;
|
|
|
|
// Get a new band
|
|
GetAvailableSpace();
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Painting, event handling
|
|
|
|
PRIntn
|
|
nsBlockFrame::GetSkipSides() const
|
|
{
|
|
PRIntn skip = 0;
|
|
if (nsnull != mPrevInFlow) {
|
|
skip |= 1 << NS_SIDE_TOP;
|
|
}
|
|
if (nsnull != mNextInFlow) {
|
|
skip |= 1 << NS_SIDE_BOTTOM;
|
|
}
|
|
return skip;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBlockFrame::Paint(nsIPresContext& aPresContext,
|
|
nsIRenderingContext& aRenderingContext,
|
|
const nsRect& aDirtyRect)
|
|
{
|
|
const nsStyleDisplay* disp = (const nsStyleDisplay*)
|
|
mStyleContext->GetStyleData(eStyleStruct_Display);
|
|
|
|
// Only paint the border and background if we're visible
|
|
if (disp->mVisible) {
|
|
PRIntn skipSides = GetSkipSides();
|
|
const nsStyleColor* color = (const nsStyleColor*)
|
|
mStyleContext->GetStyleData(eStyleStruct_Color);
|
|
const nsStyleSpacing* spacing = (const nsStyleSpacing*)
|
|
mStyleContext->GetStyleData(eStyleStruct_Spacing);
|
|
|
|
// Paint background and border
|
|
nsRect rect(0, 0, mRect.width, mRect.height);
|
|
nsCSSRendering::PaintBackground(aPresContext, aRenderingContext, this,
|
|
aDirtyRect, rect, *color, 0, 0);
|
|
nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, this,
|
|
aDirtyRect, rect, *spacing, skipSides);
|
|
}
|
|
|
|
// If overflow is hidden then set the clip rect so that children
|
|
// don't leak out of us
|
|
if (NS_STYLE_OVERFLOW_HIDDEN == disp->mOverflow) {
|
|
PRBool clipState;
|
|
aRenderingContext.PushState();
|
|
aRenderingContext.SetClipRect(nsRect(0, 0, mRect.width, mRect.height),
|
|
nsClipCombine_kIntersect, clipState);
|
|
}
|
|
|
|
// Child elements have the opportunity to override the visibility
|
|
// property and display even if the parent is hidden
|
|
PaintFloaters(aPresContext, aRenderingContext, aDirtyRect);
|
|
PaintChildren(aPresContext, aRenderingContext, aDirtyRect);
|
|
|
|
if (NS_STYLE_OVERFLOW_HIDDEN == disp->mOverflow) {
|
|
PRBool clipState;
|
|
aRenderingContext.PopState(clipState);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
nsBlockFrame::PaintFloaters(nsIPresContext& aPresContext,
|
|
nsIRenderingContext& aRenderingContext,
|
|
const nsRect& aDirtyRect)
|
|
{
|
|
for (LineData* line = mLines; nsnull != line; line = line->mNext) {
|
|
nsVoidArray* floaters = line->mFloaters;
|
|
if (nsnull == floaters) {
|
|
continue;
|
|
}
|
|
PRInt32 i, n = floaters->Count();
|
|
for (i = 0; i < n; i++) {
|
|
nsPlaceholderFrame* ph = (nsPlaceholderFrame*) floaters->ElementAt(i);
|
|
PaintChild(aPresContext, aRenderingContext, aDirtyRect,
|
|
ph->GetAnchoredItem());
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
nsBlockFrame::PaintChildren(nsIPresContext& aPresContext,
|
|
nsIRenderingContext& aRenderingContext,
|
|
const nsRect& aDirtyRect)
|
|
{
|
|
if (nsnull != mBullet) {
|
|
// Paint outside bullets manually
|
|
const nsStyleList* list = (const nsStyleList*)
|
|
mStyleContext->GetStyleData(eStyleStruct_List);
|
|
if (NS_STYLE_LIST_STYLE_POSITION_OUTSIDE == list->mListStylePosition) {
|
|
PaintChild(aPresContext, aRenderingContext, aDirtyRect, mBullet);
|
|
}
|
|
}
|
|
|
|
for (LineData* line = mLines; nsnull != line; line = line->mNext) {
|
|
// If the line has outside children or if the line intersects the
|
|
// dirty rect then paint the children in the line.
|
|
if (line->OutsideChildren() ||
|
|
!((line->mBounds.YMost() <= aDirtyRect.y) ||
|
|
(line->mBounds.y >= aDirtyRect.YMost()))) {
|
|
nsIFrame* kid = line->mFirstChild;
|
|
PRInt32 n = line->ChildCount();
|
|
while (--n >= 0) {
|
|
PaintChild(aPresContext, aRenderingContext, aDirtyRect, kid);
|
|
kid->GetNextSibling(kid);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBlockFrame::GetFrameForPoint(const nsPoint& aPoint, nsIFrame** aFrame)
|
|
{
|
|
nsresult rv = GetFrameForPointUsing(aPoint, nsnull, aFrame);
|
|
if (NS_OK == rv) {
|
|
return NS_OK;
|
|
}
|
|
if (nsnull != mBullet) {
|
|
rv = GetFrameForPointUsing(aPoint, gBulletAtom, aFrame);
|
|
if (NS_OK == rv) {
|
|
return NS_OK;
|
|
}
|
|
}
|
|
if (nsnull != mFloaters) {
|
|
rv = GetFrameForPointUsing(aPoint, gFloaterAtom, aFrame);
|
|
if (NS_OK == rv) {
|
|
return NS_OK;
|
|
}
|
|
}
|
|
*aFrame = this;
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Debugging
|
|
|
|
#ifdef NS_DEBUG
|
|
static PRBool
|
|
InLineList(LineData* aLines, nsIFrame* aFrame)
|
|
{
|
|
while (nsnull != aLines) {
|
|
nsIFrame* frame = aLines->mFirstChild;
|
|
PRInt32 n = aLines->ChildCount();
|
|
while (--n >= 0) {
|
|
if (frame == aFrame) {
|
|
return PR_TRUE;
|
|
}
|
|
frame->GetNextSibling(frame);
|
|
}
|
|
aLines = aLines->mNext;
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|
|
static PRBool
|
|
InSiblingList(LineData* aLine, nsIFrame* aFrame)
|
|
{
|
|
if (nsnull != aLine) {
|
|
nsIFrame* frame = aLine->mFirstChild;
|
|
while (nsnull != frame) {
|
|
if (frame == aFrame) {
|
|
return PR_TRUE;
|
|
}
|
|
frame->GetNextSibling(frame);
|
|
}
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|
|
PRBool
|
|
nsBlockFrame::IsChild(nsIFrame* aFrame)
|
|
{
|
|
nsIFrame* parent;
|
|
aFrame->GetGeometricParent(parent);
|
|
if (parent != (nsIFrame*)this) {
|
|
return PR_FALSE;
|
|
}
|
|
if (InLineList(mLines, aFrame) && InSiblingList(mLines, aFrame)) {
|
|
return PR_TRUE;
|
|
}
|
|
if (InLineList(mOverflowLines, aFrame) &&
|
|
InSiblingList(mOverflowLines, aFrame)) {
|
|
return PR_TRUE;
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
#endif
|
|
|
|
#define VERIFY_ASSERT(_expr, _msg) \
|
|
if (!(_expr)) { \
|
|
DumpTree(); \
|
|
} \
|
|
NS_ASSERTION(_expr, _msg)
|
|
|
|
NS_IMETHODIMP
|
|
nsBlockFrame::VerifyTree() const
|
|
{
|
|
// XXX rewrite this
|
|
return NS_OK;
|
|
}
|
|
|
|
#ifdef DO_SELECTION
|
|
nsIFrame * nsBlockFrame::FindHitFrame(nsBlockFrame * aBlockFrame,
|
|
const nscoord aX,
|
|
const nscoord aY,
|
|
const nsPoint & aPoint)
|
|
{
|
|
nsPoint mousePoint(aPoint.x-aX, aPoint.y-aY);
|
|
|
|
nsIFrame * contentFrame = nsnull;
|
|
LineData * line = aBlockFrame->mLines;
|
|
if (nsnull != line) {
|
|
// First find the line that contains the aIndex
|
|
while (nsnull != line && contentFrame == nsnull) {
|
|
nsIFrame* frame = line->mFirstChild;
|
|
PRInt32 n = line->ChildCount();
|
|
while (--n >= 0) {
|
|
nsRect bounds;
|
|
frame->GetRect(bounds);
|
|
if (bounds.Contains(mousePoint)) {
|
|
nsBlockFrame * blockFrame;
|
|
if (NS_OK == frame->QueryInterface(kBlockFrameCID, (void**)&blockFrame)) {
|
|
frame = FindHitFrame(blockFrame, bounds.x, bounds.y, aPoint);
|
|
//NS_RELEASE(blockFrame);
|
|
return frame;
|
|
} else {
|
|
return frame;
|
|
}
|
|
}
|
|
frame->GetNextSibling(frame);
|
|
}
|
|
line = line->mNext;
|
|
}
|
|
}
|
|
return aBlockFrame;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsBlockFrame::HandleEvent(nsIPresContext& aPresContext,
|
|
nsGUIEvent* aEvent,
|
|
nsEventStatus& aEventStatus)
|
|
{
|
|
if (DisplaySelection(aPresContext) == PR_FALSE) {
|
|
if (aEvent->message != NS_MOUSE_LEFT_BUTTON_DOWN) {
|
|
return NS_OK;
|
|
}
|
|
}
|
|
|
|
if (aEvent->message == NS_MOUSE_LEFT_BUTTON_DOWN) {
|
|
int x = 0;
|
|
}
|
|
|
|
//nsRect bounds;
|
|
//GetRect(bounds);
|
|
//nsIFrame * contentFrame = FindHitFrame(this, bounds.x, bounds.y, aEvent->point);
|
|
nsIFrame * contentFrame = FindHitFrame(this, 0,0, aEvent->point);
|
|
|
|
if (contentFrame == nsnull) {
|
|
return NS_OK;
|
|
}
|
|
|
|
if(nsEventStatus_eConsumeNoDefault != aEventStatus) {
|
|
if (aEvent->message == NS_MOUSE_LEFT_BUTTON_DOWN) {
|
|
} else if (aEvent->message == NS_MOUSE_MOVE && mDoingSelection ||
|
|
aEvent->message == NS_MOUSE_LEFT_BUTTON_UP) {
|
|
// no-op
|
|
} else {
|
|
return NS_OK;
|
|
}
|
|
|
|
if (aEvent->message == NS_MOUSE_LEFT_BUTTON_UP) {
|
|
if (mDoingSelection) {
|
|
contentFrame->HandleRelease(aPresContext, aEvent, aEventStatus);
|
|
}
|
|
} else if (aEvent->message == NS_MOUSE_MOVE) {
|
|
mDidDrag = PR_TRUE;
|
|
contentFrame->HandleDrag(aPresContext, aEvent, aEventStatus);
|
|
} else if (aEvent->message == NS_MOUSE_LEFT_BUTTON_DOWN) {
|
|
contentFrame->HandlePress(aPresContext, aEvent, aEventStatus);
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsIFrame * gNearByFrame = nsnull;
|
|
|
|
NS_METHOD nsBlockFrame::HandleDrag(nsIPresContext& aPresContext,
|
|
nsGUIEvent* aEvent,
|
|
nsEventStatus& aEventStatus)
|
|
{
|
|
if (DisplaySelection(aPresContext) == PR_FALSE)
|
|
{
|
|
aEventStatus = nsEventStatus_eIgnore;
|
|
return NS_OK;
|
|
}
|
|
|
|
// Keep old start and end
|
|
//nsIContent * startContent = mSelectionRange->GetStartContent(); // ref counted
|
|
//nsIContent * endContent = mSelectionRange->GetEndContent(); // ref counted
|
|
|
|
mDidDrag = PR_TRUE;
|
|
|
|
nsIFrame * contentFrame = nsnull;
|
|
LineData* line = mLines;
|
|
if (nsnull != line) {
|
|
// First find the line that contains the aIndex
|
|
while (nsnull != line && contentFrame == nsnull) {
|
|
nsIFrame* frame = line->mFirstChild;
|
|
PRInt32 n = line->ChildCount();
|
|
while (--n >= 0) {
|
|
nsRect bounds;
|
|
frame->GetRect(bounds);
|
|
if (aEvent->point.y >= bounds.y && aEvent->point.y < bounds.y+bounds.height) {
|
|
contentFrame = frame;
|
|
if (frame != gNearByFrame) {
|
|
if (gNearByFrame != nsnull) {
|
|
int x = 0;
|
|
}
|
|
aEvent->point.x = bounds.x+bounds.width-50;
|
|
gNearByFrame = frame;
|
|
return contentFrame->HandleDrag(aPresContext, aEvent, aEventStatus);
|
|
} else {
|
|
return NS_OK;
|
|
}
|
|
//break;
|
|
}
|
|
frame->GetNextSibling(frame);
|
|
}
|
|
line = line->mNext;
|
|
}
|
|
}
|
|
return NS_OK;
|
|
}
|
|
#endif
|