mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-31 22:25:30 +00:00
2131 lines
72 KiB
C++
2131 lines
72 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 "nsInlineFrame.h"
|
|
#include "nsBlockFrame.h"
|
|
#include "nsBlockReflowContext.h"
|
|
#include "nsHTMLIIDs.h"
|
|
#include "nsHTMLAtoms.h"
|
|
#include "nsHTMLParts.h"
|
|
#include "nsCOMPtr.h"
|
|
#include "nsIStyleContext.h"
|
|
#include "nsIPresShell.h"
|
|
#include "nsIPresContext.h"
|
|
#include "nsIRenderingContext.h"
|
|
#include "nsIFontMetrics.h"
|
|
#include "nsAbsoluteContainingBlock.h"
|
|
#include "nsLayoutAtoms.h"
|
|
|
|
// XXX TODO:
|
|
// append/insert/remove floater testing
|
|
|
|
// Theory of operation:
|
|
// XXX write this
|
|
|
|
#ifdef DEBUG
|
|
#undef NOISY_ANON_BLOCK
|
|
#undef NOISY_REFLOW_REASON
|
|
#else
|
|
#undef NOISY_ANON_BLOCK
|
|
#undef NOISY_REFLOW_REASON
|
|
#endif
|
|
|
|
nsIID nsInlineFrame::kInlineFrameCID = NS_INLINE_FRAME_CID;
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
nsresult
|
|
NS_NewPositionedInlineFrame(nsIFrame** aNewFrame)
|
|
{
|
|
NS_PRECONDITION(aNewFrame, "null OUT ptr");
|
|
if (nsnull == aNewFrame) {
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
nsPositionedInlineFrame* it = new nsPositionedInlineFrame;
|
|
if (nsnull == it) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
*aNewFrame = it;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsPositionedInlineFrame::Destroy(nsIPresContext& aPresContext)
|
|
{
|
|
mAbsoluteContainer.DestroyFrames(this, aPresContext);
|
|
return nsInlineFrame::Destroy(aPresContext);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsPositionedInlineFrame::SetInitialChildList(nsIPresContext& aPresContext,
|
|
nsIAtom* aListName,
|
|
nsIFrame* aChildList)
|
|
{
|
|
nsresult rv;
|
|
|
|
if (nsLayoutAtoms::absoluteList == aListName) {
|
|
rv = mAbsoluteContainer.SetInitialChildList(this, aPresContext, aListName, aChildList);
|
|
} else {
|
|
rv = nsInlineFrame::SetInitialChildList(aPresContext, aListName, aChildList);
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsPositionedInlineFrame::AppendFrames(nsIPresContext& aPresContext,
|
|
nsIPresShell& aPresShell,
|
|
nsIAtom* aListName,
|
|
nsIFrame* aFrameList)
|
|
{
|
|
if (nsLayoutAtoms::absoluteList == aListName) {
|
|
// XXX Temporary code until area frame is updated...
|
|
return nsFrame::AppendFrames(aPresContext, aPresShell, aListName, aFrameList);
|
|
}
|
|
|
|
return nsInlineFrame::AppendFrames(aPresContext, aPresShell, aListName,
|
|
aFrameList);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsPositionedInlineFrame::InsertFrames(nsIPresContext& aPresContext,
|
|
nsIPresShell& aPresShell,
|
|
nsIAtom* aListName,
|
|
nsIFrame* aPrevFrame,
|
|
nsIFrame* aFrameList)
|
|
{
|
|
if (nsLayoutAtoms::absoluteList == aListName) {
|
|
// XXX Temporary code until area frame is updated...
|
|
return nsFrame::InsertFrames(aPresContext, aPresShell, aListName,
|
|
aPrevFrame, aFrameList);
|
|
}
|
|
|
|
return nsInlineFrame::InsertFrames(aPresContext, aPresShell, aListName, aPrevFrame,
|
|
aFrameList);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsPositionedInlineFrame::RemoveFrame(nsIPresContext& aPresContext,
|
|
nsIPresShell& aPresShell,
|
|
nsIAtom* aListName,
|
|
nsIFrame* aOldFrame)
|
|
{
|
|
if (nsLayoutAtoms::absoluteList == aListName) {
|
|
// XXX Temporary code until area frame is updated...
|
|
return nsFrame::RemoveFrame(aPresContext, aPresShell, aListName, aOldFrame);
|
|
}
|
|
|
|
return nsInlineFrame::RemoveFrame(aPresContext, aPresShell, aListName, aOldFrame);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsPositionedInlineFrame::GetAdditionalChildListName(PRInt32 aIndex,
|
|
nsIAtom** aListName) const
|
|
{
|
|
NS_PRECONDITION(nsnull != aListName, "null OUT parameter pointer");
|
|
*aListName = nsnull;
|
|
if (0 == aIndex) {
|
|
*aListName = nsLayoutAtoms::absoluteList;
|
|
NS_ADDREF(*aListName);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsPositionedInlineFrame::FirstChild(nsIAtom* aListName, nsIFrame** aFirstChild) const
|
|
{
|
|
NS_PRECONDITION(nsnull != aFirstChild, "null OUT parameter pointer");
|
|
if (aListName == nsLayoutAtoms::absoluteList) {
|
|
return mAbsoluteContainer.FirstChild(this, aListName, aFirstChild);
|
|
}
|
|
|
|
return nsInlineFrame::FirstChild(aListName, aFirstChild);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsPositionedInlineFrame::GetFrameType(nsIAtom** aType) const
|
|
{
|
|
NS_PRECONDITION(nsnull != aType, "null OUT parameter pointer");
|
|
*aType = nsLayoutAtoms::positionedInlineFrame;
|
|
NS_ADDREF(*aType);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsPositionedInlineFrame::Reflow(nsIPresContext& aPresContext,
|
|
nsHTMLReflowMetrics& aDesiredSize,
|
|
const nsHTMLReflowState& aReflowState,
|
|
nsReflowStatus& aStatus)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
|
|
// See if it's an incremental reflow command
|
|
if (eReflowReason_Incremental == aReflowState.reason) {
|
|
// Give the absolute positioning code a chance to handle it
|
|
PRBool handled;
|
|
|
|
mAbsoluteContainer.IncrementalReflow(this, aPresContext, aReflowState, handled);
|
|
|
|
// If the incremental reflow command was handled by the absolute positioning
|
|
// code, then we're all done
|
|
if (handled) {
|
|
// Just return our current size as our desired size
|
|
// XXX I don't know how to compute that without a reflow, so for the
|
|
// time being pretend a resize reflow occured
|
|
nsHTMLReflowState reflowState(aReflowState);
|
|
reflowState.reason = eReflowReason_Resize;
|
|
reflowState.reflowCommand = nsnull;
|
|
return nsInlineFrame::Reflow(aPresContext, aDesiredSize, reflowState, aStatus);
|
|
}
|
|
}
|
|
|
|
// Let the inline frame do its reflow first
|
|
rv = nsInlineFrame::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus);
|
|
|
|
// Let the absolutely positioned container reflow any absolutely positioned
|
|
// child frames that need to be reflowed
|
|
if (NS_SUCCEEDED(rv)) {
|
|
rv = mAbsoluteContainer.Reflow(this, aPresContext, aReflowState);
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
// SectionData implementation
|
|
|
|
nsInlineFrame::SectionData::SectionData(nsIFrame* aFrameList)
|
|
{
|
|
firstBlock = nsnull;
|
|
prevFirstBlock = nsnull;
|
|
lastBlock = nsnull;
|
|
lastFrame = nsnull;
|
|
|
|
// Find the first and last block (if any!). When we exit the loop
|
|
// lastFrame will be the last frame in aList.
|
|
nsIFrame* frame = aFrameList;
|
|
firstFrame = aFrameList;
|
|
while (nsnull != frame) {
|
|
if (nsLineLayout::TreatFrameAsBlock(frame)) {
|
|
if (nsnull == firstBlock) {
|
|
prevFirstBlock = lastFrame;
|
|
firstBlock = frame;
|
|
lastBlock = frame;
|
|
}
|
|
else {
|
|
lastBlock = frame;
|
|
}
|
|
}
|
|
lastFrame = frame;
|
|
frame->GetNextSibling(&frame);
|
|
}
|
|
}
|
|
|
|
PRBool
|
|
nsInlineFrame::SectionData::SplitFrameList(nsFrameList& aSection1,
|
|
nsFrameList& aSection2,
|
|
nsFrameList& aSection3)
|
|
{
|
|
if (nsnull == firstBlock) {
|
|
// There are no blocks
|
|
return PR_FALSE;
|
|
}
|
|
|
|
// We have at least one block
|
|
if (nsnull != prevFirstBlock) {
|
|
// The first block is not the first frame in aList. Setup section1.
|
|
prevFirstBlock->SetNextSibling(nsnull);
|
|
aSection1.SetFrames(firstFrame);
|
|
}
|
|
aSection2.SetFrames(firstBlock);
|
|
|
|
if (lastFrame != lastBlock) {
|
|
// There are inline frames that follow the last block. Setup section3.
|
|
nsIFrame* nextSib;
|
|
lastBlock->GetNextSibling(&nextSib);
|
|
lastBlock->SetNextSibling(nsnull);
|
|
aSection3.SetFrames(nextSib);
|
|
}
|
|
|
|
return PR_TRUE;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// Basic nsInlineFrame methods
|
|
|
|
nsresult
|
|
NS_NewInlineFrame(nsIFrame** aNewFrame)
|
|
{
|
|
NS_PRECONDITION(aNewFrame, "null OUT ptr");
|
|
if (nsnull == aNewFrame) {
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
nsInlineFrame* it = new nsInlineFrame;
|
|
if (nsnull == it) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
*aNewFrame = it;
|
|
return NS_OK;
|
|
}
|
|
|
|
nsInlineFrame::nsInlineFrame()
|
|
{
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsInlineFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr)
|
|
{
|
|
if (nsnull == aInstancePtr) {
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
if (aIID.Equals(kInlineFrameCID)) {
|
|
nsInlineFrame* tmp = this;
|
|
*aInstancePtr = (void*) tmp;
|
|
return NS_OK;
|
|
}
|
|
return nsInlineFrameSuper::QueryInterface(aIID, aInstancePtr);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsInlineFrame::GetFrameName(nsString& aResult) const
|
|
{
|
|
return MakeFrameName("Inline", aResult);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsInlineFrame::GetFrameType(nsIAtom** aType) const
|
|
{
|
|
NS_PRECONDITION(nsnull != aType, "null OUT parameter pointer");
|
|
*aType = nsLayoutAtoms::inlineFrame;
|
|
NS_ADDREF(*aType);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsInlineFrame::Destroy(nsIPresContext& aPresContext)
|
|
{
|
|
mFrames.DestroyFrames(aPresContext);
|
|
return nsInlineFrameSuper::Destroy(aPresContext);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// nsInlineFrame child management
|
|
|
|
// Find the first inline frame, looking backwards starting at "this",
|
|
// that contains an anonymous block. Return nsnull if an anonymous
|
|
// block is not found.
|
|
nsAnonymousBlockFrame*
|
|
nsInlineFrame::FindPrevAnonymousBlock(nsInlineFrame** aBlockParent)
|
|
{
|
|
nsInlineFrame* prevInFlow = this;
|
|
while (nsnull != prevInFlow) {
|
|
// Scan the prev-in-flows frame list, looking for an anonymous
|
|
// block frame.
|
|
nsIFrame* frame = prevInFlow->mFrames.FirstChild();
|
|
while (nsnull != frame) {
|
|
if (nsLineLayout::TreatFrameAsBlock(frame)) {
|
|
*aBlockParent = prevInFlow;
|
|
return (nsAnonymousBlockFrame*) frame;
|
|
}
|
|
frame->GetNextSibling(&frame);
|
|
}
|
|
prevInFlow = (nsInlineFrame*) prevInFlow->mPrevInFlow;
|
|
}
|
|
return nsnull;
|
|
}
|
|
|
|
// Find the first inline frame, looking forwards starting at "this",
|
|
// that contains an anonymous block. Return nsnull if an anonymous
|
|
// block is not found.
|
|
nsAnonymousBlockFrame*
|
|
nsInlineFrame::FindAnonymousBlock(nsInlineFrame** aBlockParent)
|
|
{
|
|
nsInlineFrame* nextInFlow = this;
|
|
while (nsnull != nextInFlow) {
|
|
// Scan the prev-in-flows frame list, looking for an anonymous
|
|
// block frame.
|
|
nsIFrame* frame = nextInFlow->mFrames.FirstChild();
|
|
while (nsnull != frame) {
|
|
if (nsLineLayout::TreatFrameAsBlock(frame)) {
|
|
*aBlockParent = nextInFlow;
|
|
return (nsAnonymousBlockFrame*) frame;
|
|
}
|
|
frame->GetNextSibling(&frame);
|
|
}
|
|
nextInFlow = (nsInlineFrame*) nextInFlow->mNextInFlow;
|
|
}
|
|
return nsnull;
|
|
}
|
|
|
|
nsresult
|
|
nsInlineFrame::CreateAnonymousBlock(nsIPresContext& aPresContext,
|
|
nsIFrame* aInitialFrames,
|
|
nsIFrame** aResult)
|
|
{
|
|
nsIFrame* bf;
|
|
nsresult rv = NS_NewAnonymousBlockFrame(&bf);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
nsCOMPtr<nsIStyleContext> newSC;
|
|
aPresContext.ResolvePseudoStyleContextFor(mContent,
|
|
nsHTMLAtoms::mozAnonymousBlock,
|
|
mStyleContext,
|
|
PR_FALSE,
|
|
getter_AddRefs(newSC));
|
|
rv = bf->Init(aPresContext, mContent, this, newSC, nsnull);
|
|
if (NS_FAILED(rv)) {
|
|
bf->Destroy(aPresContext);
|
|
delete bf;
|
|
}
|
|
else {
|
|
// Set parent for the frames now that the anonymous block has
|
|
// been created.
|
|
nsIFrame* frame = aInitialFrames;
|
|
while (nsnull != frame) {
|
|
frame->SetParent(bf);
|
|
frame->GetNextSibling(&frame);
|
|
}
|
|
rv = bf->SetInitialChildList(aPresContext, nsnull, aInitialFrames);
|
|
}
|
|
*aResult = bf;
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsInlineFrame::SetInitialChildList(nsIPresContext& aPresContext,
|
|
nsIAtom* aListName,
|
|
nsIFrame* aFrameList)
|
|
{
|
|
if (nsnull != aListName) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
if (nsnull == aFrameList) {
|
|
return NS_OK;
|
|
}
|
|
nsCOMPtr<nsIPresShell> shell;
|
|
nsresult rv = aPresContext.GetShell(getter_AddRefs(shell));
|
|
if (NS_SUCCEEDED(rv) && shell) {
|
|
rv = AppendFrames(aPresContext, *shell, aFrameList, PR_FALSE);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsInlineFrame::AppendFrames(nsIPresContext& aPresContext,
|
|
nsIPresShell& aPresShell,
|
|
nsIAtom* aListName,
|
|
nsIFrame* aFrameList)
|
|
{
|
|
if (nsnull != aListName) {
|
|
return NS_ERROR_INVALID_ARG;
|
|
}
|
|
if (nsnull == aFrameList) {
|
|
return NS_OK;
|
|
}
|
|
return AppendFrames(aPresContext, aPresShell, aFrameList, PR_TRUE);
|
|
}
|
|
|
|
nsresult
|
|
nsInlineFrame::AppendFrames(nsIPresContext& aPresContext,
|
|
nsIPresShell& aPresShell,
|
|
nsIFrame* aFrameList,
|
|
PRBool aGenerateReflowCommands)
|
|
{
|
|
#ifdef NOISY_REFLOW_REASON
|
|
ListTag(stdout);
|
|
printf(": append ");
|
|
nsFrame::ListTag(stdout, aFrameList);
|
|
nsIFrame* lastKid = mFrames.LastChild();
|
|
if (lastKid) {
|
|
printf(" after ");
|
|
nsFrame::ListTag(stdout, lastKid);
|
|
}
|
|
printf("\n");
|
|
#endif
|
|
|
|
nsresult rv = NS_OK;
|
|
SectionData sd(aFrameList);
|
|
if (sd.HasABlock()) {
|
|
nsFrameList section1, section2, section3;
|
|
sd.SplitFrameList(section1, section2, section3);
|
|
|
|
// There is at least one block in the new frames. See if there is
|
|
// an anonymous block frame in a prev-in-flow of this frame.
|
|
nsInlineFrame* prevInline;
|
|
nsAnonymousBlockFrame* anonymousBlock;
|
|
anonymousBlock = FindPrevAnonymousBlock(&prevInline);
|
|
if (nsnull != anonymousBlock) {
|
|
// One of the invariants of nsInlineFrame is that there will be
|
|
// at most one anonymous block frame that reflows the
|
|
// nsInlineFrame's block children (not counting any
|
|
// continuations of the anonymous block frame).
|
|
//
|
|
// We maintain that invariant by noticing when a new block frame
|
|
// enters the list of children and ensuring that all of the
|
|
// frames from the first block to the last block are contained
|
|
// by the anonymous block (or one of its continuations). This
|
|
// can cause some inline frames between here and the inline that
|
|
// contains anonymousBlock to have their children stuffed into
|
|
// the anonymous block.
|
|
|
|
// Build a list of the frames between the anonymous block and
|
|
// this frame, stealing children from our continuation frames as
|
|
// necessary.
|
|
nsIFrame* inlineSiblings;
|
|
anonymousBlock->GetNextSibling(&inlineSiblings);
|
|
nsFrameList newBlockFrames;
|
|
if (nsnull != inlineSiblings) {
|
|
newBlockFrames.AppendFrames(anonymousBlock, inlineSiblings);
|
|
}
|
|
nsInlineFrame* tmp = (nsInlineFrame*) prevInline->mNextInFlow;
|
|
while ((nsnull != tmp) && (this != tmp)) {
|
|
newBlockFrames.AppendFrames(anonymousBlock, tmp->mFrames);
|
|
tmp = (nsInlineFrame*) tmp->mNextInFlow;
|
|
}
|
|
|
|
// Now tack on all of this frame's child frames (unless this
|
|
// frame is the frame that contains the anonymous block)
|
|
if (this != prevInline) {
|
|
newBlockFrames.AppendFrames(anonymousBlock, mFrames);
|
|
}
|
|
|
|
// And then append section1 and section2 frames.
|
|
if (section1.NotEmpty()) {
|
|
newBlockFrames.AppendFrames(anonymousBlock, section1);
|
|
}
|
|
newBlockFrames.AppendFrames(anonymousBlock, section2);
|
|
|
|
// Finally, if there are any frames in section3 then they are
|
|
// appended after the anonymous block. These frames will be
|
|
// reflowed when they are pushed from the prevInline frame to a
|
|
// next-in-flow after the anonymousBlock frame is reflowed.
|
|
anonymousBlock->SetNextSibling(section3.FirstChild());
|
|
|
|
// Now we can append the frames to the anonymous block and it
|
|
// can generate a reflow command.
|
|
rv = anonymousBlock->AppendFrames2(&aPresContext, &aPresShell, nsnull,
|
|
newBlockFrames.FirstChild());
|
|
#ifdef NOISY_ANON_BLOCK
|
|
printf("AppendFrames: case 1\n");
|
|
#endif
|
|
}
|
|
else {
|
|
// There is no prior block frames that are the children of this
|
|
// inline. Therefore, section 1 is appended to our frame list
|
|
// and we wrap up the frames in section 2 with a new anonymous
|
|
// block and the frames in section 3 get appended to the frame
|
|
// list after the anonymous frame (so that they can be pushed to
|
|
// a next-in-flow after this finishes reflowing its anonymous
|
|
// block).
|
|
nsIFrame* anonBlock;
|
|
rv = CreateAnonymousBlock(aPresContext, section2.FirstChild(),
|
|
&anonBlock);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
if (section1.NotEmpty()) {
|
|
mFrames.AppendFrames(nsnull, section1);
|
|
}
|
|
mFrames.AppendFrame(nsnull, anonBlock);
|
|
if (section3.NotEmpty()) {
|
|
mFrames.AppendFrames(nsnull, section3);
|
|
}
|
|
#ifdef NOISY_ANON_BLOCK
|
|
printf("AppendFrames: case 2\n");
|
|
#endif
|
|
|
|
if (aGenerateReflowCommands) {
|
|
// generate a reflow command for "this"
|
|
nsIReflowCommand* reflowCmd = nsnull;
|
|
rv = NS_NewHTMLReflowCommand(&reflowCmd, this,
|
|
nsIReflowCommand::ReflowDirty);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
aPresShell.AppendReflowCommand(reflowCmd);
|
|
NS_RELEASE(reflowCmd);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// The new frames contain no block frames
|
|
mFrames.AppendFrames(this, aFrameList);
|
|
#ifdef NOISY_ANON_BLOCK
|
|
printf("AppendFrames: case 3\n");
|
|
#endif
|
|
|
|
if (aGenerateReflowCommands) {
|
|
// generate a reflow command for "this"
|
|
nsIReflowCommand* reflowCmd = nsnull;
|
|
rv = NS_NewHTMLReflowCommand(&reflowCmd, this,
|
|
nsIReflowCommand::ReflowDirty);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
aPresShell.AppendReflowCommand(reflowCmd);
|
|
NS_RELEASE(reflowCmd);
|
|
}
|
|
}
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsInlineFrame::InsertFrames(nsIPresContext& aPresContext,
|
|
nsIPresShell& aPresShell,
|
|
nsIAtom* aListName,
|
|
nsIFrame* aPrevFrame,
|
|
nsIFrame* aFrameList)
|
|
{
|
|
if (nsnull != aListName) {
|
|
return NS_ERROR_INVALID_ARG;
|
|
}
|
|
if (nsnull == aFrameList) {
|
|
return NS_OK;
|
|
}
|
|
|
|
#ifdef NOISY_REFLOW_REASON
|
|
ListTag(stdout);
|
|
printf(": insert ");
|
|
nsFrame::ListTag(stdout, aFrameList);
|
|
if (aPrevFrame) {
|
|
printf(" after ");
|
|
nsFrame::ListTag(stdout, aPrevFrame);
|
|
}
|
|
printf("\n");
|
|
#endif
|
|
|
|
nsresult rv = NS_OK;
|
|
SectionData sd(aFrameList);
|
|
if (sd.HasABlock()) {
|
|
// Break insertion up into 3 pieces
|
|
nsFrameList section1, section2, section3;
|
|
sd.SplitFrameList(section1, section2, section3);
|
|
|
|
nsIFrame* prevFrame = aPrevFrame;
|
|
|
|
// First insert the inlines in section1 after prevFrame
|
|
if (section1.NotEmpty()) {
|
|
nsIFrame* newPrevFrame = section1.LastChild();
|
|
rv = InsertInlineFrames(aPresContext, aPresShell, prevFrame,
|
|
section1.FirstChild());
|
|
prevFrame = newPrevFrame;
|
|
}
|
|
|
|
// Next insert the frames in section2 after prevFrame
|
|
if (NS_SUCCEEDED(rv)) {
|
|
nsIFrame* newPrevFrame = section2.LastChild();
|
|
rv = InsertBlockFrames(aPresContext, aPresShell, prevFrame,
|
|
section2.FirstChild());
|
|
prevFrame = newPrevFrame;
|
|
}
|
|
|
|
// Finally, insert the frames in section3 after prevFrame
|
|
if (NS_SUCCEEDED(rv) && section3.NotEmpty()) {
|
|
rv = InsertInlineFrames(aPresContext, aPresShell, prevFrame,
|
|
section3.FirstChild());
|
|
}
|
|
}
|
|
else {
|
|
// Use simpler path when the insertion is only inline frames
|
|
rv = InsertInlineFrames(aPresContext, aPresShell, aPrevFrame, aFrameList);
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
nsInlineFrame::InsertBlockFrames(nsIPresContext& aPresContext,
|
|
nsIPresShell& aPresShell,
|
|
nsIFrame* aPrevFrame,
|
|
nsIFrame* aFrameList)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
PRBool generateReflowCommand = PR_FALSE;
|
|
nsIFrame* target = nsnull;
|
|
|
|
if (nsnull == aPrevFrame) {
|
|
// The block is being inserted at the head of all the child
|
|
// frames.
|
|
nsInlineFrame* flow;
|
|
nsAnonymousBlockFrame* anonymousBlock = FindAnonymousBlock(&flow);
|
|
if (nsnull == anonymousBlock) {
|
|
// There are no anonymous blocks so create one and place the
|
|
// frames into it.
|
|
nsIFrame* anonBlock;
|
|
rv = CreateAnonymousBlock(aPresContext, aFrameList, &anonBlock);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
mFrames.InsertFrames(this, nsnull, anonBlock);
|
|
target = this;
|
|
generateReflowCommand = PR_TRUE;
|
|
#ifdef NOISY_ANON_BLOCK
|
|
printf("InsertBlockFrames: case 1\n");
|
|
#endif
|
|
}
|
|
else {
|
|
// Take all of the frames before the anonymous block, plus the
|
|
// frames in aFrameList and insert them into the anonymous
|
|
// block.
|
|
nsFrameList frames;
|
|
frames.AppendFrames(anonymousBlock, aFrameList);
|
|
nsInlineFrame* start = this;
|
|
while (start != flow) {
|
|
frames.AppendFrames(anonymousBlock, start->mFrames);
|
|
start->GetNextInFlow((nsIFrame**) &start);
|
|
}
|
|
anonymousBlock->InsertFrames2(&aPresContext, &aPresShell, nsnull,
|
|
nsnull, frames.FirstChild());
|
|
#ifdef NOISY_ANON_BLOCK
|
|
printf("InsertBlockFrames: case 2\n");
|
|
#endif
|
|
}
|
|
}
|
|
else {
|
|
// First see if the insertion is inside the anonymous block
|
|
nsIFrame* prevFrameParent;
|
|
aPrevFrame->GetParent(&prevFrameParent);
|
|
if (nsLineLayout::TreatFrameAsBlock(prevFrameParent)) {
|
|
// The previous frame's parent is an anonymous block. This means
|
|
// that the new block frames can be safely inserted there.
|
|
nsIFrame* frame = aFrameList;
|
|
while (nsnull != frame) {
|
|
frame->SetParent(prevFrameParent);
|
|
frame->GetNextSibling(&frame);
|
|
}
|
|
nsAnonymousBlockFrame* anonymousBlock;
|
|
anonymousBlock = (nsAnonymousBlockFrame*) prevFrameParent;
|
|
anonymousBlock->InsertFrames2(&aPresContext, &aPresShell, nsnull,
|
|
aPrevFrame, aFrameList);
|
|
#ifdef NOISY_ANON_BLOCK
|
|
printf("InsertBlockFrames: case 3\n");
|
|
#endif
|
|
}
|
|
else {
|
|
// The previous frame's parent is an inline frame. First see if
|
|
// there is an anonymous block before the insertion point.
|
|
nsInlineFrame* flow = (nsInlineFrame*) prevFrameParent;
|
|
nsAnonymousBlockFrame* anonymousBlock;
|
|
anonymousBlock = flow->FindPrevAnonymousBlock(&flow);
|
|
if (nsnull != anonymousBlock) {
|
|
// We found an anonymous block before the insertion
|
|
// point. Take all of the inline frames between the anonymous
|
|
// block and prevFrameParent and strip them away from the
|
|
// inline frames that contain them.
|
|
nsFrameList frames;
|
|
nsInlineFrame* start;
|
|
flow->GetNextInFlow((nsIFrame**) &start); // start after anon block
|
|
while (start != prevFrameParent) {
|
|
frames.AppendFrames(anonymousBlock, start->mFrames);
|
|
start->GetNextInFlow((nsIFrame**) &start);
|
|
}
|
|
|
|
// Now append the frames just before and including aPrevFrame
|
|
// to "frames".
|
|
flow = (nsInlineFrame*) prevFrameParent;
|
|
nsIFrame* remainingFrames;
|
|
flow->mFrames.Split(aPrevFrame, &remainingFrames);
|
|
frames.AppendFrames(anonymousBlock, flow->mFrames);
|
|
flow->mFrames.SetFrames(remainingFrames);
|
|
generateReflowCommand = PR_TRUE;
|
|
target = flow;
|
|
|
|
// Finally, append the block frames to "frames" and then
|
|
// append the list of frames to the anonymous block.
|
|
frames.AppendFrames(anonymousBlock, aFrameList);
|
|
anonymousBlock->AppendFrames2(&aPresContext, &aPresShell, nsnull,
|
|
frames.FirstChild());
|
|
#ifdef NOISY_ANON_BLOCK
|
|
printf("InsertBlockFrames: case 4\n");
|
|
#endif
|
|
}
|
|
else {
|
|
// There is no anymous block before the insertion point. See
|
|
// if there is one after the insertion point.
|
|
flow = (nsInlineFrame*) prevFrameParent;
|
|
anonymousBlock = flow->FindAnonymousBlock(&flow);
|
|
if (nsnull != anonymousBlock) {
|
|
// We found an anonymous block after the insertion
|
|
// point. Seed the list of frames to put into the anonymous
|
|
// block with the block frames being inserted.
|
|
nsFrameList frames;
|
|
frames.AppendFrames(anonymousBlock, aFrameList);
|
|
nsInlineFrame* start = (nsInlineFrame*) prevFrameParent;
|
|
|
|
// Take the frames after aPrevFrame and place them at the
|
|
// end of the frames list.
|
|
nsIFrame* remainingFrames;
|
|
start->mFrames.Split(aPrevFrame, &remainingFrames);
|
|
if (remainingFrames) {
|
|
frames.AppendFrames(anonymousBlock, remainingFrames);
|
|
}
|
|
generateReflowCommand = PR_TRUE;
|
|
target = start;
|
|
|
|
// Gather up all of the inline frames from all of the flow
|
|
// between the insertion point and the anonymous block.
|
|
start->GetNextInFlow((nsIFrame**) &start);
|
|
while (start != flow) {
|
|
frames.AppendFrames(anonymousBlock, start->mFrames);
|
|
start->GetNextInFlow((nsIFrame**) &start);
|
|
}
|
|
|
|
// Now update the anonymous block
|
|
anonymousBlock->InsertFrames2(&aPresContext, &aPresShell, nsnull,
|
|
nsnull, frames.FirstChild());
|
|
#ifdef NOISY_ANON_BLOCK
|
|
printf("InsertBlockFrames: case 5\n");
|
|
#endif
|
|
}
|
|
else {
|
|
// There are no anonymous blocks so create one and place the
|
|
// frames into it.
|
|
nsIFrame* anonBlock;
|
|
rv = CreateAnonymousBlock(aPresContext, aFrameList, &anonBlock);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
// Insert the frame into the correct parent (it will not be
|
|
// this frame when aPrevFrame's parent != this)
|
|
flow = (nsInlineFrame*) prevFrameParent;
|
|
flow->mFrames.InsertFrames(flow, aPrevFrame, anonBlock);
|
|
generateReflowCommand = PR_TRUE;
|
|
target = flow;
|
|
#ifdef NOISY_ANON_BLOCK
|
|
printf("InsertBlockFrames: case 6\n");
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (generateReflowCommand) {
|
|
// generate a reflow command for "this"
|
|
nsIReflowCommand* reflowCmd = nsnull;
|
|
rv = NS_NewHTMLReflowCommand(&reflowCmd, target,
|
|
nsIReflowCommand::ReflowDirty);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
aPresShell.AppendReflowCommand(reflowCmd);
|
|
NS_RELEASE(reflowCmd);
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
nsInlineFrame::InsertInlineFrames(nsIPresContext& aPresContext,
|
|
nsIPresShell& aPresShell,
|
|
nsIFrame* aPrevFrame,
|
|
nsIFrame* aFrameList)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
PRBool generateReflowCommand = PR_FALSE;
|
|
nsIFrame* target = nsnull;
|
|
|
|
if (nsnull == aPrevFrame) {
|
|
// Insert the frames at the front of our list. Since the frames
|
|
// are all inline frames we just place them there, even if our
|
|
// current first frame is the anonymous block frame. The reflow
|
|
// logic will properly push the anonymous block frame to a
|
|
// next-in-flow.
|
|
mFrames.InsertFrames(this, nsnull, aFrameList);
|
|
generateReflowCommand = PR_TRUE;
|
|
target = this;
|
|
#ifdef NOISY_ANON_BLOCK
|
|
printf("InsertInlineFrames: case 1\n");
|
|
#endif
|
|
}
|
|
else {
|
|
nsIFrame* prevFrameParent;
|
|
aPrevFrame->GetParent(&prevFrameParent);
|
|
if (nsLineLayout::TreatFrameAsBlock(prevFrameParent)) {
|
|
nsAnonymousBlockFrame* anonymousBlock;
|
|
anonymousBlock = (nsAnonymousBlockFrame*) prevFrameParent;
|
|
|
|
// The previous frame's parent is an anonymous block (otherwise
|
|
// its parent would be this frame). We must not place the inline
|
|
// frames into the anonymous block if they should be in
|
|
// section3.
|
|
nsIFrame* nextSibling;
|
|
aPrevFrame->GetNextSibling(&nextSibling);
|
|
nsIFrame* anonymousBlockNextInFlow;
|
|
prevFrameParent->GetNextInFlow(&anonymousBlockNextInFlow);
|
|
if ((nsnull != nextSibling) || (nsnull != anonymousBlockNextInFlow)) {
|
|
// Easy case: there are more frames following aPrevFrame which
|
|
// means that this insertion lies in the anonymous block.
|
|
nsIFrame* frame = aFrameList;
|
|
while (nsnull != frame) {
|
|
frame->SetParent(anonymousBlock);
|
|
frame->GetNextSibling(&frame);
|
|
}
|
|
anonymousBlock->InsertFrames2(&aPresContext, &aPresShell, nsnull,
|
|
aPrevFrame, aFrameList);
|
|
#ifdef NOISY_ANON_BLOCK
|
|
printf("InsertInlineFrames: case 2\n");
|
|
#endif
|
|
}
|
|
else {
|
|
// aPrevFrame is the last frame that should be in the
|
|
// anonymous block.
|
|
nsInlineFrame* anonymousBlockParent;
|
|
anonymousBlock->GetParent((nsIFrame**)&anonymousBlockParent);
|
|
|
|
// Place the inline frames after the anonymous block
|
|
nsIFrame* frame = aFrameList;
|
|
while (nsnull != frame) {
|
|
frame->SetParent(anonymousBlockParent);
|
|
frame->GetNextSibling(&frame);
|
|
}
|
|
anonymousBlockParent->mFrames.InsertFrames(nsnull, anonymousBlock,
|
|
aFrameList);
|
|
generateReflowCommand = PR_TRUE;
|
|
target = anonymousBlockParent;
|
|
#ifdef NOISY_ANON_BLOCK
|
|
printf("InsertInlineFrames: case 3\n");
|
|
#endif
|
|
}
|
|
}
|
|
else {
|
|
// The previous frame's parent is an inline frame. Therefore
|
|
// this is either a section1 or section3 insertion. Insert the
|
|
// frames in the proper flow block (which will be aPrevFrame's
|
|
// parent which is currently stored in anonymousBlock)
|
|
nsInlineFrame* flow = (nsInlineFrame*) prevFrameParent;
|
|
flow->mFrames.InsertFrames(flow, aPrevFrame, aFrameList);
|
|
generateReflowCommand = PR_TRUE;
|
|
target = flow;
|
|
#ifdef NOISY_ANON_BLOCK
|
|
printf("InsertInlineFrames: case 4\n");
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if (generateReflowCommand) {
|
|
// generate a reflow command for "this"
|
|
nsIReflowCommand* reflowCmd = nsnull;
|
|
rv = NS_NewHTMLReflowCommand(&reflowCmd, target,
|
|
nsIReflowCommand::ReflowDirty);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
aPresShell.AppendReflowCommand(reflowCmd);
|
|
NS_RELEASE(reflowCmd);
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsInlineFrame::RemoveFrame(nsIPresContext& aPresContext,
|
|
nsIPresShell& aPresShell,
|
|
nsIAtom* aListName,
|
|
nsIFrame* aOldFrame)
|
|
{
|
|
if (nsnull != aListName) {
|
|
return NS_ERROR_INVALID_ARG;
|
|
}
|
|
|
|
#ifdef NOISY_REFLOW_REASON
|
|
ListTag(stdout);
|
|
printf(": remove ");
|
|
nsFrame::ListTag(stdout, aOldFrame);
|
|
printf("\n");
|
|
#endif
|
|
|
|
nsresult rv = NS_OK;
|
|
PRBool generateReflowCommand = PR_FALSE;
|
|
nsIFrame* target = nsnull;
|
|
|
|
nsIFrame* oldFrameParent;
|
|
if (ParentIsInlineFrame(aOldFrame, &oldFrameParent)) {
|
|
// Loop and destroy the frame and all of its
|
|
// continuations. Because the frame's parent is an inline frame we
|
|
// know that any continuations will also be in an inline frame
|
|
// parent.
|
|
nsInlineFrame* parent = (nsInlineFrame*) oldFrameParent;
|
|
while (nsnull != aOldFrame) {
|
|
// If the frame being removed has zero size then don't bother
|
|
// generating a reflow command.
|
|
nsRect bbox;
|
|
aOldFrame->GetRect(bbox);
|
|
if ((0 == bbox.width) && (0 == bbox.height)) {
|
|
// Don't bother generating a reflow command
|
|
}
|
|
else {
|
|
generateReflowCommand = PR_TRUE;
|
|
target = this;
|
|
}
|
|
|
|
// When the parent is an inline frame we have a simple task -
|
|
// just remove the frame from its parents list and generate a
|
|
// reflow command.
|
|
nsIFrame* oldFrameNextInFlow;
|
|
aOldFrame->GetNextInFlow(&oldFrameNextInFlow);
|
|
nsSplittableType st;
|
|
aOldFrame->IsSplittable(st);
|
|
if (NS_FRAME_NOT_SPLITTABLE != st) {
|
|
aOldFrame->RemoveFromFlow();
|
|
}
|
|
parent->mFrames.DestroyFrame(aPresContext, aOldFrame);
|
|
aOldFrame = oldFrameNextInFlow;
|
|
if (nsnull != aOldFrame) {
|
|
aOldFrame->GetParent((nsIFrame**) &parent);
|
|
}
|
|
}
|
|
#ifdef NOISY_ANON_BLOCK
|
|
printf("RemoveFrame: case 1\n");
|
|
#endif
|
|
}
|
|
else {
|
|
#ifdef DEBUG
|
|
nsIFrame* oldFrameNextInFlow;
|
|
aOldFrame->GetNextInFlow(&oldFrameNextInFlow);
|
|
NS_ASSERTION(nsnull == oldFrameNextInFlow, "XXX: can't remove continued frames that are in anonymous blocks -- not yet implemented");
|
|
#endif
|
|
|
|
nsIFrame* nextInFlow;
|
|
nsIFrame* prevInFlow;
|
|
|
|
// The parent is not an inline frame which means it is an
|
|
// anonymous block frame.
|
|
nsAnonymousBlockFrame* anonymousBlock =
|
|
(nsAnonymousBlockFrame*) oldFrameParent;
|
|
|
|
// It is possible that we are about to remove the last child of
|
|
// the anonymous block. In this case we remove the anonymous block.
|
|
nsIFrame* kids;
|
|
anonymousBlock->FirstChild(nsnull, &kids);
|
|
nsFrameList blockKids(kids);
|
|
if (1 == blockKids.GetLength()) {
|
|
// Remove the anonymous block
|
|
mFrames.DestroyFrame(aPresContext, anonymousBlock);
|
|
generateReflowCommand = PR_TRUE;
|
|
target = this;
|
|
#ifdef NOISY_ANON_BLOCK
|
|
printf("RemoveFrame: case 2\n");
|
|
#endif
|
|
}
|
|
else {
|
|
// If the frame being removed is a block frame then we may need to
|
|
// do something fancy.
|
|
if (nsLineLayout::TreatFrameAsBlock(aOldFrame)) {
|
|
// It is possible that we are removing the first block in the
|
|
// anonymous block or the last block. See if its so.
|
|
anonymousBlock->GetPrevInFlow(&prevInFlow);
|
|
nsIFrame* prevSib;
|
|
if ((nsnull != prevInFlow) ||
|
|
(nsnull != (prevSib = blockKids.GetPrevSiblingFor(aOldFrame)))) {
|
|
// There is a block in the anonymous block prior to the
|
|
// block that we are removing. See if we are removing the
|
|
// last block in the anonymous block.
|
|
anonymousBlock->GetNextInFlow(&nextInFlow);
|
|
nsIFrame* nextSib;
|
|
aOldFrame->GetNextSibling(&nextSib);
|
|
if ((nsnull != nextInFlow) || (nsnull != nextSib)) {
|
|
// There is a block in the anonymous block after the block
|
|
// that we are removing. This means that we can let the
|
|
// anonymous block remove the frame.
|
|
#ifdef NOISY_ANON_BLOCK
|
|
printf("RemoveFrame: case 3\n");
|
|
#endif
|
|
anonymousBlock->RemoveFrame2(&aPresContext, &aPresShell, aListName,
|
|
aOldFrame);
|
|
}
|
|
else {
|
|
// We are removing the last block. We must steal all of
|
|
// the inline frames that preceed the block being removed
|
|
// (up to the closest prior block) so that they can be
|
|
// moved outside the anonymous block and into an inline
|
|
// frame.
|
|
|
|
// First get the last frame out of the picture; delete any
|
|
// continuations it might have.
|
|
nsInlineFrame* anonymousBlockParent;
|
|
anonymousBlock->GetParent((nsIFrame**) &anonymousBlockParent);
|
|
nsAnonymousBlockFrame* ab = anonymousBlock;
|
|
anonymousBlock->RemoveFramesFrom(aOldFrame);
|
|
aOldFrame->Destroy(aPresContext);
|
|
while (nsnull != nextInFlow) {
|
|
nsIFrame* nextParent;
|
|
nextInFlow->GetParent(&nextParent);
|
|
if (nextParent != ab) {
|
|
ab = (nsAnonymousBlockFrame*) nextParent;
|
|
}
|
|
ab->RemoveFirstFrame();
|
|
nsIFrame* nextNextInFlow;
|
|
nextInFlow->GetNextInFlow(&nextNextInFlow);
|
|
nextInFlow->Destroy(aPresContext);
|
|
nextInFlow = nextNextInFlow;
|
|
}
|
|
|
|
// Any inline frames that are between the new last-block
|
|
// inside the anonymous block and the block we just
|
|
// removed need to be taken out of the anonymous block.
|
|
nsFrameList inlines;
|
|
while (nsnull != anonymousBlock) {
|
|
// Find the first inline before the last block
|
|
nsIFrame* abkids;
|
|
anonymousBlock->FirstChild(nsnull, &abkids);
|
|
if (nsnull != abkids) {
|
|
SectionData sd(abkids);
|
|
if (sd.HasABlock()) {
|
|
abkids = sd.lastBlock;
|
|
abkids->GetNextSibling(&abkids);
|
|
if (nsnull != abkids) {
|
|
// Take the frames that follow the last block
|
|
// (which are inline frames) and remove them from
|
|
// the anonymous block. Insert them into the
|
|
// inlines frame-list.
|
|
anonymousBlock->RemoveFramesFrom(abkids);
|
|
inlines.InsertFrames(nsnull, nsnull, abkids);
|
|
}
|
|
}
|
|
else {
|
|
// All of the frames are inline frames -- take them
|
|
// all away.
|
|
anonymousBlock->RemoveFramesFrom(abkids);
|
|
inlines.InsertFrames(nsnull, nsnull, abkids);
|
|
}
|
|
}
|
|
anonymousBlock->GetPrevInFlow((nsIFrame**) &anonymousBlock);
|
|
}
|
|
|
|
// Now we have all of the inline frames that need to be
|
|
// placed into an inline parent instead of the anonymous
|
|
// block parent.
|
|
if (inlines.NotEmpty()) {
|
|
// Place the inline frames after the anonymous block
|
|
// frame in the child list of the anonymousBlockParent.
|
|
anonymousBlockParent->mFrames.AppendFrames(anonymousBlockParent,
|
|
inlines);
|
|
}
|
|
generateReflowCommand = PR_TRUE;
|
|
target = anonymousBlockParent;
|
|
#ifdef NOISY_ANON_BLOCK
|
|
printf("RemoveFrame: case 4\n");
|
|
#endif
|
|
}
|
|
}
|
|
else {
|
|
// We are removing the first block child of the anonymous
|
|
// block. We must gather all of the inline frames that
|
|
// follow the block being removed from the anonymous block
|
|
// so that they can be moved outside the anonymous block and
|
|
// into an inline frame.
|
|
|
|
// Take away the first frame from the anonymous block (which
|
|
// is the frame we are trying to remove). Make sure we
|
|
// remove aOldFrame's continuations if it has any...
|
|
nsInlineFrame* anonymousBlockParent;
|
|
anonymousBlock->GetParent((nsIFrame**) &anonymousBlockParent);
|
|
anonymousBlock->RemoveFirstFrame();
|
|
aOldFrame->GetNextInFlow(&nextInFlow);
|
|
aOldFrame->Destroy(aPresContext);
|
|
while (nsnull != nextInFlow) {
|
|
nsIFrame* nextParent;
|
|
nextInFlow->GetParent(&nextParent);
|
|
if (nextParent != anonymousBlock) {
|
|
anonymousBlock = (nsAnonymousBlockFrame*) nextParent;
|
|
}
|
|
anonymousBlock->RemoveFirstFrame();
|
|
nsIFrame* nextNextInFlow;
|
|
nextInFlow->GetNextInFlow(&nextNextInFlow);
|
|
nextInFlow->Destroy(aPresContext);
|
|
nextInFlow = nextNextInFlow;
|
|
}
|
|
|
|
// Gather up the inline frames that follow aOldFrame
|
|
nsFrameList frames;
|
|
PRBool done = PR_FALSE;
|
|
while (!done && (nsnull != anonymousBlock)) {
|
|
nsIFrame* kid;
|
|
anonymousBlock->FirstChild(nsnull, &kid);
|
|
while (nsnull != kid) {
|
|
if (nsLineLayout::TreatFrameAsBlock(kid)) {
|
|
done = PR_TRUE;
|
|
break;
|
|
}
|
|
nsIFrame* next;
|
|
kid->GetNextSibling(&next);
|
|
anonymousBlock->RemoveFirstFrame();
|
|
frames.AppendFrame(nsnull, kid);
|
|
kid = next;
|
|
}
|
|
anonymousBlock->GetNextInFlow((nsIFrame**) &anonymousBlock);
|
|
}
|
|
|
|
if (frames.NotEmpty()) {
|
|
// If the anonymousBlockParent has a prev-in-flow then
|
|
// append the inline frames there, otherwise insert them
|
|
// before the anonymousBlock.
|
|
anonymousBlockParent->GetPrevInFlow(&prevInFlow);
|
|
if (nsnull != prevInFlow) {
|
|
anonymousBlockParent = (nsInlineFrame*) prevInFlow;
|
|
anonymousBlockParent->mFrames.AppendFrames(anonymousBlockParent,
|
|
frames);
|
|
}
|
|
else {
|
|
anonymousBlockParent->mFrames.InsertFrames(anonymousBlockParent,
|
|
nsnull,
|
|
frames);
|
|
}
|
|
}
|
|
generateReflowCommand = PR_TRUE;
|
|
target = anonymousBlockParent;
|
|
#ifdef NOISY_ANON_BLOCK
|
|
printf("RemoveFrame: case 5\n");
|
|
#endif
|
|
}
|
|
}
|
|
else {
|
|
// We can let the anonymousBlock remove the frame directly
|
|
#ifdef NOISY_ANON_BLOCK
|
|
printf("RemoveFrame: case 6\n");
|
|
#endif
|
|
anonymousBlock->RemoveFrame2(&aPresContext, &aPresShell, aListName,
|
|
aOldFrame);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (generateReflowCommand) {
|
|
// generate a reflow command for "this"
|
|
nsIReflowCommand* reflowCmd = nsnull;
|
|
rv = NS_NewHTMLReflowCommand(&reflowCmd, target,
|
|
nsIReflowCommand::ReflowDirty);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
aPresShell.AppendReflowCommand(reflowCmd);
|
|
NS_RELEASE(reflowCmd);
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Reflow methods
|
|
|
|
NS_IMETHODIMP
|
|
nsInlineFrame::Reflow(nsIPresContext& aPresContext,
|
|
nsHTMLReflowMetrics& aMetrics,
|
|
const nsHTMLReflowState& aReflowState,
|
|
nsReflowStatus& aStatus)
|
|
{
|
|
if (nsnull == aReflowState.mLineLayout) {
|
|
return NS_ERROR_INVALID_ARG;
|
|
}
|
|
DrainOverflow(&aPresContext);
|
|
|
|
// Set our own reflow state (additional state above and beyond
|
|
// aReflowState)
|
|
InlineReflowState irs;
|
|
irs.mPrevFrame = nsnull;
|
|
irs.mNextInFlow = (nsInlineFrame*) mNextInFlow;
|
|
irs.mNextRCFrame = nsnull;
|
|
if (eReflowReason_Incremental == aReflowState.reason) {
|
|
// Peel off the next frame in the path if this is an incremental
|
|
// reflow aimed at one of the children.
|
|
nsIFrame* target;
|
|
aReflowState.reflowCommand->GetTarget(target);
|
|
if (this != target) {
|
|
aReflowState.reflowCommand->GetNext(irs.mNextRCFrame);
|
|
}
|
|
}
|
|
|
|
nsresult rv;
|
|
if (mFrames.IsEmpty()) {
|
|
// Try to pull over one frame before starting so that we know
|
|
// whether we have an anonymous block or not.
|
|
(void) PullAnyFrame(&aPresContext, irs);
|
|
}
|
|
|
|
if (HaveAnonymousBlock()) {
|
|
if (!aReflowState.mLineLayout->LineIsEmpty()) {
|
|
// This inline frame cannot be placed on the current line
|
|
// because there already is an inline frame on this line (and we
|
|
// contain an anonymous block).
|
|
aStatus = NS_INLINE_LINE_BREAK_BEFORE();
|
|
rv = NS_OK;
|
|
}
|
|
else {
|
|
rv = ReflowBlockFrame(&aPresContext, aReflowState, irs,
|
|
aMetrics, aStatus);
|
|
|
|
// 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;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
rv = ReflowInlineFrames(&aPresContext, aReflowState, irs,
|
|
aMetrics, aStatus);
|
|
// Note: when we are reflowing inline frames the line layout code
|
|
// will properly compute our NS_FRAME_OUTSIDE_CHILDREN state for
|
|
// us.
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsInlineFrame::FindTextRuns(nsLineLayout& aLineLayout)
|
|
{
|
|
if (HaveAnonymousBlock()) {
|
|
aLineLayout.EndTextRun();
|
|
}
|
|
else {
|
|
nsIFrame* frame = mFrames.FirstChild();
|
|
while (nsnull != frame) {
|
|
nsIHTMLReflow* ihr;
|
|
nsresult rv = frame->QueryInterface(kIHTMLReflowIID, (void**)&ihr);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
ihr->FindTextRuns(aLineLayout);
|
|
}
|
|
frame->GetNextSibling(&frame);
|
|
}
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
nsInlineFrame::DrainOverflow(nsIPresContext* aPresContext)
|
|
{
|
|
// Check for an overflow list with our prev-in-flow
|
|
nsInlineFrame* prevInFlow = (nsInlineFrame*)mPrevInFlow;
|
|
if (nsnull != prevInFlow) {
|
|
if (prevInFlow->mOverflowFrames.NotEmpty()) {
|
|
// When pushing and pulling frames we need to check for whether any
|
|
// views need to be reparented.
|
|
// XXX Doing it this way means an extra pass over the frames. We could
|
|
// change InsertFrames() to do this, but that's a general purpose
|
|
// function and it doesn't seem like this functionality belongs there...
|
|
for (nsIFrame* f = prevInFlow->mOverflowFrames.FirstChild(); f; f->GetNextSibling(&f)) {
|
|
nsHTMLContainerFrame::ReparentFrameView(f, prevInFlow, this);
|
|
}
|
|
mFrames.InsertFrames(this, nsnull, prevInFlow->mOverflowFrames);
|
|
}
|
|
}
|
|
|
|
// It's also possible that we have an overflow list for ourselves
|
|
if (mOverflowFrames.NotEmpty()) {
|
|
NS_ASSERTION(mFrames.NotEmpty(), "overflow list w/o frames");
|
|
mFrames.AppendFrames(nsnull, mOverflowFrames);
|
|
}
|
|
}
|
|
|
|
nsresult
|
|
nsInlineFrame::ReflowInlineFrames(nsIPresContext* aPresContext,
|
|
const nsHTMLReflowState& aReflowState,
|
|
InlineReflowState& irs,
|
|
nsHTMLReflowMetrics& aMetrics,
|
|
nsReflowStatus& aStatus)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
aStatus = NS_FRAME_COMPLETE;
|
|
|
|
nsLineLayout* lineLayout = aReflowState.mLineLayout;
|
|
nscoord leftEdge = 0;
|
|
if (nsnull == mPrevInFlow) {
|
|
leftEdge = aReflowState.mComputedBorderPadding.left;
|
|
}
|
|
nscoord availableWidth = aReflowState.availableWidth;
|
|
if (NS_UNCONSTRAINEDSIZE != availableWidth) {
|
|
// Subtract off left and right border+padding from availableWidth
|
|
availableWidth -= leftEdge;
|
|
availableWidth -= aReflowState.mComputedBorderPadding.right;
|
|
}
|
|
lineLayout->BeginSpan(this, &aReflowState,
|
|
leftEdge, leftEdge + availableWidth);
|
|
|
|
// First reflow our current children
|
|
nsIFrame* frame = mFrames.FirstChild();
|
|
PRBool done = PR_FALSE;
|
|
while (nsnull != frame) {
|
|
PRBool reflowingFirstLetter = lineLayout->GetFirstLetterStyleOK();
|
|
rv = ReflowInlineFrame(aPresContext, aReflowState, irs, frame, aStatus);
|
|
if (NS_FAILED(rv)) {
|
|
done = PR_TRUE;
|
|
break;
|
|
}
|
|
if (NS_FRAME_COMPLETE != aStatus) {
|
|
if (!reflowingFirstLetter || NS_INLINE_IS_BREAK(aStatus)) {
|
|
done = PR_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
irs.mPrevFrame = frame;
|
|
frame->GetNextSibling(&frame);
|
|
}
|
|
|
|
// Attempt to pull frames from our next-in-flow until we can't
|
|
if (!done && (nsnull != mNextInFlow)) {
|
|
while (!done) {
|
|
PRBool reflowingFirstLetter = lineLayout->GetFirstLetterStyleOK();
|
|
PRBool isComplete;
|
|
frame = PullInlineFrame(aPresContext, irs, &isComplete);
|
|
if (nsnull == frame) {
|
|
if (!isComplete) {
|
|
aStatus = NS_FRAME_NOT_COMPLETE;
|
|
}
|
|
break;
|
|
}
|
|
rv = ReflowInlineFrame(aPresContext, aReflowState, irs, frame, aStatus);
|
|
if (NS_FAILED(rv)) {
|
|
done = PR_TRUE;
|
|
break;
|
|
}
|
|
if (NS_FRAME_COMPLETE != aStatus) {
|
|
if (!reflowingFirstLetter || NS_INLINE_IS_BREAK(aStatus)) {
|
|
done = PR_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
irs.mPrevFrame = frame;
|
|
}
|
|
}
|
|
#ifdef DEBUG
|
|
if (NS_FRAME_COMPLETE == aStatus) {
|
|
// We can't be complete AND have overflow frames!
|
|
NS_ASSERTION(mOverflowFrames.IsEmpty(), "whoops");
|
|
}
|
|
#endif
|
|
|
|
// If after reflowing our children they take up no area then make
|
|
// sure that we don't either.
|
|
//
|
|
// Note: CSS demands that empty inline elements still affect the
|
|
// line-height calculations. However, continuations of an inline
|
|
// that are empty we force to empty so that things like collapsed
|
|
// whitespace in an inline element don't affect the line-height.
|
|
nsSize size;
|
|
lineLayout->EndSpan(this, size, aMetrics.maxElementSize);
|
|
if ((0 == size.height) && (0 == size.width) &&
|
|
((nsnull != mPrevInFlow) || (nsnull != mNextInFlow))) {
|
|
// This is a continuation of a previous inline. Therefore make
|
|
// sure we don't affect the line-height.
|
|
aMetrics.width = 0;
|
|
aMetrics.height = 0;
|
|
aMetrics.ascent = 0;
|
|
aMetrics.descent = 0;
|
|
if (nsnull != aMetrics.maxElementSize) {
|
|
aMetrics.maxElementSize->width = 0;
|
|
aMetrics.maxElementSize->height = 0;
|
|
}
|
|
}
|
|
else {
|
|
// Compute final width
|
|
aMetrics.width = size.width;
|
|
if (nsnull == mPrevInFlow) {
|
|
aMetrics.width += aReflowState.mComputedBorderPadding.left;
|
|
}
|
|
if (NS_FRAME_IS_COMPLETE(aStatus)) {
|
|
aMetrics.width += aReflowState.mComputedBorderPadding.right;
|
|
}
|
|
|
|
const nsStyleFont* font;
|
|
GetStyleData(eStyleStruct_Font, (const nsStyleStruct*&)font);
|
|
aReflowState.rendContext->SetFont(font->mFont);
|
|
nsIFontMetrics* fm;
|
|
aReflowState.rendContext->GetFontMetrics(fm);
|
|
|
|
// Compute final height. The height of our box is the sum of our
|
|
// font size plus the top and bottom border and padding. The height
|
|
// of children do not affect our height.
|
|
fm->GetMaxAscent(aMetrics.ascent);
|
|
fm->GetMaxDescent(aMetrics.descent);
|
|
fm->GetHeight(aMetrics.height);
|
|
aMetrics.ascent += aReflowState.mComputedBorderPadding.top;
|
|
aMetrics.descent += aReflowState.mComputedBorderPadding.bottom;
|
|
aMetrics.height += aReflowState.mComputedBorderPadding.top +
|
|
aReflowState.mComputedBorderPadding.bottom;
|
|
|
|
// Note: we use the actual font height for sizing our selves
|
|
// instead of the computed font height. On systems where they
|
|
// disagree the actual font height is more appropriate. This
|
|
// little hack lets us override that behavior to allow for more
|
|
// precise layout in the face of imprecise fonts.
|
|
static PRBool useComputedHeight = PR_FALSE;
|
|
#if defined(XP_UNIX) || defined(XP_PC) || defined(XP_BEOS)
|
|
static PRBool firstTime = 1;
|
|
if (firstTime) {
|
|
if (getenv("GECKO_USE_COMPUTED_HEIGHT")) {
|
|
useComputedHeight = PR_TRUE;
|
|
}
|
|
}
|
|
#endif
|
|
if (useComputedHeight) {
|
|
// Special debug code that violates the above CSS2 spec
|
|
// clarification. Why? So that we can predictably compute the
|
|
// values for testing layout.
|
|
nscoord computedHeight = aReflowState.mComputedBorderPadding.top +
|
|
aReflowState.mComputedBorderPadding.bottom +
|
|
font->mFont.size;
|
|
if (computedHeight != aMetrics.height) {
|
|
#ifdef DEBUG
|
|
if (0 == (mState & 0x80000000)) {
|
|
nsFrame::ListTag(stdout, this);
|
|
printf(": using computedHeight %d instead of actual height %d\n",
|
|
computedHeight, aMetrics.height);
|
|
mState |= 0x80000000;
|
|
}
|
|
#endif
|
|
aMetrics.height = computedHeight;
|
|
}
|
|
}
|
|
NS_RELEASE(fm);
|
|
}
|
|
|
|
// For now our combined area is zero. The real value will be
|
|
// computed during vertical alignment of the line we are on.
|
|
aMetrics.mCombinedArea.x = 0;
|
|
aMetrics.mCombinedArea.y = 0;
|
|
aMetrics.mCombinedArea.width = aMetrics.width;
|
|
aMetrics.mCombinedArea.height = aMetrics.height;
|
|
|
|
#ifdef NOISY_FINAL_SIZE
|
|
ListTag(stdout);
|
|
printf(": metrics=%d,%d ascent=%d descent=%d\n",
|
|
aMetrics.width, aMetrics.height, aMetrics.ascent, aMetrics.descent);
|
|
#endif
|
|
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
nsInlineFrame::ReflowInlineFrame(nsIPresContext* aPresContext,
|
|
const nsHTMLReflowState& aReflowState,
|
|
InlineReflowState& irs,
|
|
nsIFrame* aFrame,
|
|
nsReflowStatus& aStatus)
|
|
{
|
|
// Make sure that we don't reflow a block frame when we run across
|
|
// one. This can easily happen if this inline has a mixture of
|
|
// frames (note that an anonymous block frame is used to wrap up the
|
|
// direct block children of this inline therefore when we do run
|
|
// across a block frame its an anonymous block).
|
|
if (nsLineLayout::TreatFrameAsBlock(aFrame)) {
|
|
NS_ASSERTION(aFrame != mFrames.FirstChild(), "bad anon-block status");
|
|
PushFrames(aPresContext, aFrame, irs.mPrevFrame);
|
|
aStatus = NS_INLINE_LINE_BREAK_AFTER(NS_FRAME_NOT_COMPLETE);
|
|
return NS_OK;
|
|
}
|
|
|
|
nsLineLayout* lineLayout = aReflowState.mLineLayout;
|
|
PRBool reflowingFirstLetter = lineLayout->GetFirstLetterStyleOK();
|
|
nsresult rv = lineLayout->ReflowFrame(aFrame, &irs.mNextRCFrame, aStatus);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
if (NS_INLINE_IS_BREAK(aStatus)) {
|
|
if (NS_INLINE_IS_BREAK_BEFORE(aStatus)) {
|
|
if (aFrame != mFrames.FirstChild()) {
|
|
// Change break-before status into break-after since we have
|
|
// already placed at least one child frame. This preserves the
|
|
// break-type so that it can be propogated upward.
|
|
aStatus = NS_FRAME_NOT_COMPLETE |
|
|
NS_INLINE_BREAK | NS_INLINE_BREAK_AFTER |
|
|
(aStatus & NS_INLINE_BREAK_TYPE_MASK);
|
|
PushFrames(aPresContext, aFrame, irs.mPrevFrame);
|
|
}
|
|
else {
|
|
// Preserve reflow status when breaking-before our first child
|
|
// and propogate it upward without modification.
|
|
}
|
|
}
|
|
else {
|
|
// Break-after
|
|
if (NS_FRAME_IS_NOT_COMPLETE(aStatus)) {
|
|
nsIFrame* newFrame;
|
|
rv = CreateNextInFlow(*aPresContext, this, aFrame, newFrame);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
}
|
|
nsIFrame* nextFrame;
|
|
aFrame->GetNextSibling(&nextFrame);
|
|
if (nsnull != nextFrame) {
|
|
aStatus |= NS_FRAME_NOT_COMPLETE;
|
|
PushFrames(aPresContext, nextFrame, aFrame);
|
|
}
|
|
else if (nsnull != mNextInFlow) {
|
|
// We must return an incomplete status if there are more child
|
|
// frames remaining in a next-in-flow that follows this frame.
|
|
nsInlineFrame* nextInFlow = (nsInlineFrame*) mNextInFlow;
|
|
while (nsnull != nextInFlow) {
|
|
if (nextInFlow->mFrames.NotEmpty()) {
|
|
aStatus |= NS_FRAME_NOT_COMPLETE;
|
|
break;
|
|
}
|
|
nextInFlow = (nsInlineFrame*) nextInFlow->mNextInFlow;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (NS_FRAME_IS_NOT_COMPLETE(aStatus)) {
|
|
nsIFrame* newFrame;
|
|
rv = CreateNextInFlow(*aPresContext, this, aFrame, newFrame);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
if (!reflowingFirstLetter) {
|
|
nsIFrame* nextFrame;
|
|
aFrame->GetNextSibling(&nextFrame);
|
|
if (nsnull != nextFrame) {
|
|
PushFrames(aPresContext, nextFrame, aFrame);
|
|
}
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
nsIFrame*
|
|
nsInlineFrame::PullInlineFrame(nsIPresContext* aPresContext,
|
|
InlineReflowState& irs,
|
|
PRBool* aIsComplete)
|
|
{
|
|
PRBool isComplete = PR_TRUE;
|
|
|
|
nsIFrame* frame = nsnull;
|
|
nsInlineFrame* nextInFlow = irs.mNextInFlow;
|
|
while (nsnull != nextInFlow) {
|
|
if (nextInFlow->HaveAnonymousBlock()) {
|
|
isComplete = PR_FALSE;
|
|
break;
|
|
}
|
|
frame = mFrames.PullFrame(this, irs.mPrevFrame, nextInFlow->mFrames);
|
|
if (nsnull != frame) {
|
|
isComplete = PR_FALSE;
|
|
nsHTMLContainerFrame::ReparentFrameView(frame, nextInFlow, this);
|
|
break;
|
|
}
|
|
nextInFlow = (nsInlineFrame*) nextInFlow->mNextInFlow;
|
|
irs.mNextInFlow = nextInFlow;
|
|
}
|
|
|
|
*aIsComplete = isComplete;
|
|
return frame;
|
|
}
|
|
|
|
nsIFrame*
|
|
nsInlineFrame::PullAnyFrame(nsIPresContext* aPresContext,
|
|
InlineReflowState& irs)
|
|
{
|
|
nsIFrame* frame = nsnull;
|
|
nsInlineFrame* nextInFlow = irs.mNextInFlow;
|
|
while (nsnull != nextInFlow) {
|
|
frame = mFrames.PullFrame(this, irs.mPrevFrame, nextInFlow->mFrames);
|
|
if (nsnull != frame) {
|
|
nsHTMLContainerFrame::ReparentFrameView(frame, nextInFlow, this);
|
|
break;
|
|
}
|
|
|
|
nextInFlow = (nsInlineFrame*) nextInFlow->mNextInFlow;
|
|
irs.mNextInFlow = nextInFlow;
|
|
}
|
|
|
|
return frame;
|
|
}
|
|
|
|
void
|
|
nsInlineFrame::PushFrames(nsIPresContext* aPresContext,
|
|
nsIFrame* aFromChild,
|
|
nsIFrame* aPrevSibling)
|
|
{
|
|
NS_PRECONDITION(nsnull != aFromChild, "null pointer");
|
|
NS_PRECONDITION(nsnull != aPrevSibling, "pushing first child");
|
|
#ifdef DEBUG
|
|
nsIFrame* prevNextSibling;
|
|
aPrevSibling->GetNextSibling(&prevNextSibling);
|
|
NS_PRECONDITION(prevNextSibling == aFromChild, "bad prev sibling");
|
|
#endif
|
|
|
|
// Disconnect aFromChild from its previous sibling
|
|
aPrevSibling->SetNextSibling(nsnull);
|
|
|
|
// Add the frames to our overflow list (let our next in flow drain
|
|
// our overflow list when it is ready)
|
|
NS_ASSERTION(mOverflowFrames.IsEmpty(), "bad overflow list");
|
|
mOverflowFrames.SetFrames(aFromChild);
|
|
}
|
|
|
|
nsresult
|
|
nsInlineFrame::ReflowBlockFrame(nsIPresContext* aPresContext,
|
|
const nsHTMLReflowState& aReflowState,
|
|
InlineReflowState& irs,
|
|
nsHTMLReflowMetrics& aMetrics,
|
|
nsReflowStatus& aStatus)
|
|
{
|
|
nsIFrame* blockFrame = mFrames.FirstChild();
|
|
|
|
// Compute available area
|
|
#if XXX_what_to_do
|
|
nscoord x = aReflowState.mComputedBorderPadding.left;
|
|
nscoord availableWidth = aReflowState.availableWidth;
|
|
if (NS_UNCONSTRAINEDSIZE != availableWidth) {
|
|
if (nsnull != mPrevInFlow) {
|
|
x = 0;
|
|
availableWidth -= aReflowState.mComputedBorderPadding.right;
|
|
}
|
|
else {
|
|
availableWidth -= aReflowState.mComputedBorderPadding.left +
|
|
aReflowState.mComputedBorderPadding.right;
|
|
}
|
|
}
|
|
nscoord y = aReflowState.mComputedBorderPadding.top;
|
|
nscoord availableHeight = aReflowState.availableHeight;
|
|
if (NS_UNCONSTRAINEDSIZE != aReflowState.availableHeight) {
|
|
availableHeight -= aReflowState.mComputedBorderPadding.top +
|
|
aReflowState.mComputedBorderPadding.right;
|
|
}
|
|
#else
|
|
nscoord x = 0;
|
|
nscoord availableWidth = aReflowState.availableWidth;
|
|
nscoord y = 0;
|
|
nscoord availableHeight = aReflowState.availableHeight;
|
|
#endif
|
|
|
|
// XXX_ib write me...
|
|
//XXX nscoord collapsedTopMargin = 0;
|
|
nscoord collapsedBottomMargin = 0;
|
|
|
|
// Reflow the block frame
|
|
nsBlockReflowContext bc(aPresContext, aReflowState,
|
|
nsnull != aMetrics.maxElementSize);
|
|
bc.SetNextRCFrame(irs.mNextRCFrame);
|
|
nsRect availSpace(x, y, availableWidth, availableHeight);
|
|
PRBool isAdjacentWithTop = PR_FALSE;
|
|
nsMargin computedOffsets;
|
|
nsresult rv = bc.ReflowBlock(blockFrame, availSpace, PR_FALSE, 0,
|
|
isAdjacentWithTop,
|
|
computedOffsets, aStatus);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
if (NS_INLINE_IS_BREAK_BEFORE(aStatus)) {
|
|
// We need to break before placing the block so propogate that
|
|
// status outward to our parent.
|
|
}
|
|
else {
|
|
// Place the block (during placement we might discover that none
|
|
// of it fits)
|
|
nsRect bounds;
|
|
PRBool anyFit = bc.PlaceBlock(isAdjacentWithTop,
|
|
computedOffsets, &collapsedBottomMargin,
|
|
bounds, aMetrics.mCombinedArea);
|
|
if (!anyFit) {
|
|
// None of the block fit
|
|
aStatus = NS_INLINE_LINE_BREAK_BEFORE();
|
|
}
|
|
else {
|
|
if (NS_FRAME_IS_NOT_COMPLETE(aStatus)) {
|
|
// When the block isn't complete create a continuation for it
|
|
nsIFrame* newFrame;
|
|
rv = CreateNextInFlow(*aPresContext, this, blockFrame, newFrame);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
}
|
|
|
|
// It's possible that the block frame is followed by one or more
|
|
// inline frames. Make sure we reflect that in our reflow status
|
|
// and push them to our next-in-flow.
|
|
nsIFrame* nextFrame;
|
|
blockFrame->GetNextSibling(&nextFrame);
|
|
if (nsnull != nextFrame) {
|
|
PushFrames(aPresContext, nextFrame, blockFrame);
|
|
aStatus |= NS_FRAME_NOT_COMPLETE;
|
|
}
|
|
else if (NS_FRAME_IS_COMPLETE(aStatus)) {
|
|
// When the block we reflowed is complete then we need to
|
|
// check and see if there are other frames (inline frames)
|
|
// following in our continuations so that we return the proper
|
|
// reflow status.
|
|
nsInlineFrame* nextInFlow = (nsInlineFrame*) mNextInFlow;
|
|
while (nsnull != nextInFlow) {
|
|
if (nextInFlow->mFrames.NotEmpty() ||
|
|
nextInFlow->mOverflowFrames.NotEmpty()) {
|
|
aStatus |= NS_FRAME_NOT_COMPLETE;
|
|
break;
|
|
}
|
|
nextInFlow = (nsInlineFrame*) nextInFlow->mNextInFlow;
|
|
}
|
|
}
|
|
|
|
// What we do here is to fudge the size. <b>This</b> frame will
|
|
// be 0,0 but will contain a single child (the anonymous block)
|
|
// that is properly sized.
|
|
aMetrics.width = bounds.width;
|
|
aMetrics.height = bounds.height;
|
|
aMetrics.ascent = bounds.height;
|
|
aMetrics.descent = 0;
|
|
aMetrics.mCarriedOutBottomMargin = bc.GetCarriedOutBottomMargin();
|
|
if (nsnull != aMetrics.maxElementSize) {
|
|
*aMetrics.maxElementSize = bc.GetMaxElementSize();
|
|
}
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
PRIntn
|
|
nsInlineFrame::GetSkipSides() const
|
|
{
|
|
PRIntn skip = 0;
|
|
if (nsnull != mPrevInFlow) {
|
|
nsInlineFrame* prev = (nsInlineFrame*) mPrevInFlow;
|
|
if (prev->mRect.height || prev->mRect.width) {
|
|
// Prev-in-flow is not empty therefore we don't render our left
|
|
// border edge.
|
|
skip |= 1 << NS_SIDE_LEFT;
|
|
}
|
|
else {
|
|
// If the prev-in-flow is empty, then go ahead and let our right
|
|
// edge border render.
|
|
}
|
|
}
|
|
if (nsnull != mNextInFlow) {
|
|
nsInlineFrame* next = (nsInlineFrame*) mNextInFlow;
|
|
if (next->mRect.height || next->mRect.width) {
|
|
// Next-in-flow is not empty therefore we don't render our right
|
|
// border edge.
|
|
skip |= 1 << NS_SIDE_RIGHT;
|
|
}
|
|
else {
|
|
// If the next-in-flow is empty, then go ahead and let our right
|
|
// edge border render.
|
|
}
|
|
}
|
|
return skip;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
// nsLineFrame implementation
|
|
|
|
static void
|
|
ReResolveChildList(nsIPresContext* aPresContext,
|
|
nsIStyleContext* aParentStyleContext,
|
|
nsFrameList& aFrameList)
|
|
{
|
|
nsIFrame* kid = aFrameList.FirstChild();
|
|
while (nsnull != kid) {
|
|
kid->ReResolveStyleContext(aPresContext, aParentStyleContext,
|
|
NS_STYLE_HINT_REFLOW,
|
|
nsnull, nsnull);
|
|
kid->GetNextSibling(&kid);
|
|
}
|
|
}
|
|
|
|
nsresult
|
|
NS_NewFirstLineFrame(nsIFrame** aNewFrame)
|
|
{
|
|
NS_PRECONDITION(nsnull != aNewFrame, "null ptr");
|
|
if (nsnull == aNewFrame) {
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
nsInlineFrame* it = new nsFirstLineFrame;
|
|
if (nsnull == it) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
*aNewFrame = it;
|
|
return NS_OK;
|
|
}
|
|
|
|
nsFirstLineFrame::nsFirstLineFrame()
|
|
{
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFirstLineFrame::GetFrameName(nsString& aResult) const
|
|
{
|
|
return MakeFrameName("Line", aResult);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFirstLineFrame::GetFrameType(nsIAtom** aType) const
|
|
{
|
|
NS_PRECONDITION(nsnull != aType, "null OUT parameter pointer");
|
|
*aType = nsLayoutAtoms::lineFrame;
|
|
NS_ADDREF(*aType);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFirstLineFrame::AppendFrames(nsIPresContext& aPresContext,
|
|
nsIPresShell& aPresShell,
|
|
nsIAtom* aListName,
|
|
nsIFrame* aFrameList)
|
|
{
|
|
return mParent->AppendFrames(aPresContext, aPresShell, aListName,
|
|
aFrameList);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFirstLineFrame::InsertFrames(nsIPresContext& aPresContext,
|
|
nsIPresShell& aPresShell,
|
|
nsIAtom* aListName,
|
|
nsIFrame* aPrevFrame,
|
|
nsIFrame* aFrameList)
|
|
{
|
|
return mParent->InsertFrames(aPresContext, aPresShell, aListName,
|
|
aPrevFrame, aFrameList);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFirstLineFrame::RemoveFrame(nsIPresContext& aPresContext,
|
|
nsIPresShell& aPresShell,
|
|
nsIAtom* aListName,
|
|
nsIFrame* aOldFrame)
|
|
{
|
|
return mParent->RemoveFrame(aPresContext, aPresShell, aListName,
|
|
aOldFrame);
|
|
}
|
|
|
|
nsresult
|
|
nsFirstLineFrame::AppendFrames2(nsIPresContext* aPresContext,
|
|
nsIFrame* aFrameList)
|
|
{
|
|
nsFrameList frames(aFrameList);
|
|
ReResolveChildList(aPresContext, mStyleContext, frames);
|
|
// XXX ReparentFrameView
|
|
mFrames.AppendFrames(this, aFrameList);
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsFirstLineFrame::InsertFrames2(nsIPresContext* aPresContext,
|
|
nsIFrame* aPrevFrame,
|
|
nsIFrame* aFrameList)
|
|
{
|
|
nsFrameList frames(aFrameList);
|
|
ReResolveChildList(aPresContext, mStyleContext, frames);
|
|
// XXX ReparentFrameView
|
|
mFrames.InsertFrames(this, aPrevFrame, aFrameList);
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsFirstLineFrame::RemoveFrame2(nsIPresContext* aPresContext,
|
|
nsIFrame* aOldFrame)
|
|
{
|
|
nsIFrame* nextInFlow;
|
|
aOldFrame->GetNextInFlow(&nextInFlow);
|
|
if (nextInFlow) {
|
|
DeleteChildsNextInFlow(*aPresContext, aOldFrame);
|
|
}
|
|
mFrames.RemoveFrame(aOldFrame);
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
nsFirstLineFrame::RemoveFramesFrom(nsIFrame* aFrame)
|
|
{
|
|
nsIFrame* prevFrame = mFrames.GetPrevSiblingFor(aFrame);
|
|
if (prevFrame) {
|
|
prevFrame->SetNextSibling(nsnull);
|
|
}
|
|
else {
|
|
mFrames.SetFrames(nsnull);
|
|
}
|
|
}
|
|
|
|
nsIFrame*
|
|
nsFirstLineFrame::PullInlineFrame(nsIPresContext* aPresContext,
|
|
InlineReflowState& irs,
|
|
PRBool* aIsComplete)
|
|
{
|
|
nsIFrame* frame =
|
|
nsInlineFrame::PullInlineFrame(aPresContext, irs, aIsComplete);
|
|
if (frame && !mPrevInFlow) {
|
|
// We are a first-line frame. Fixup the child frames
|
|
// style-context that we just pulled.
|
|
frame->ReResolveStyleContext(aPresContext, mStyleContext,
|
|
NS_STYLE_HINT_REFLOW, nsnull, nsnull);
|
|
}
|
|
return frame;
|
|
}
|
|
|
|
void
|
|
nsFirstLineFrame::DrainOverflow(nsIPresContext* aPresContext)
|
|
{
|
|
// Check for an overflow list with our prev-in-flow
|
|
nsFirstLineFrame* prevInFlow = (nsFirstLineFrame*)mPrevInFlow;
|
|
if (nsnull != prevInFlow) {
|
|
if (prevInFlow->mOverflowFrames.NotEmpty()) {
|
|
ReResolveChildList(aPresContext, mStyleContext,
|
|
prevInFlow->mOverflowFrames);
|
|
mFrames.InsertFrames(this, nsnull, prevInFlow->mOverflowFrames);
|
|
}
|
|
}
|
|
|
|
// It's also possible that we have an overflow list for ourselves
|
|
if (mOverflowFrames.NotEmpty()) {
|
|
NS_ASSERTION(mFrames.NotEmpty(), "overflow list w/o frames");
|
|
ReResolveChildList(aPresContext, mStyleContext, mOverflowFrames);
|
|
mFrames.AppendFrames(nsnull, mOverflowFrames);
|
|
}
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFirstLineFrame::Reflow(nsIPresContext& aPresContext,
|
|
nsHTMLReflowMetrics& aMetrics,
|
|
const nsHTMLReflowState& aReflowState,
|
|
nsReflowStatus& aStatus)
|
|
{
|
|
if (nsnull == aReflowState.mLineLayout) {
|
|
return NS_ERROR_INVALID_ARG;
|
|
}
|
|
DrainOverflow(&aPresContext);
|
|
|
|
// Set our own reflow state (additional state above and beyond
|
|
// aReflowState)
|
|
InlineReflowState irs;
|
|
irs.mPrevFrame = nsnull;
|
|
irs.mNextInFlow = (nsInlineFrame*) mNextInFlow;
|
|
irs.mNextRCFrame = nsnull;
|
|
if (eReflowReason_Incremental == aReflowState.reason) {
|
|
// Peel off the next frame in the path if this is an incremental
|
|
// reflow aimed at one of the children.
|
|
nsIFrame* target;
|
|
aReflowState.reflowCommand->GetTarget(target);
|
|
if (this != target) {
|
|
aReflowState.reflowCommand->GetNext(irs.mNextRCFrame);
|
|
}
|
|
}
|
|
|
|
nsresult rv;
|
|
PRBool wasEmpty = mFrames.IsEmpty();
|
|
if (wasEmpty) {
|
|
// Try to pull over one frame before starting so that we know
|
|
// whether we have an anonymous block or not.
|
|
PullAnyFrame(&aPresContext, irs);
|
|
}
|
|
|
|
if (HaveAnonymousBlock()) {
|
|
if (!aReflowState.mLineLayout->LineIsEmpty()) {
|
|
// This inline frame cannot be placed on the current line
|
|
// because there already is an inline frame on this line (and we
|
|
// contain an anonymous block).
|
|
aStatus = NS_INLINE_LINE_BREAK_BEFORE();
|
|
rv = NS_OK;
|
|
}
|
|
else {
|
|
rv = ReflowBlockFrame(&aPresContext, aReflowState, irs,
|
|
aMetrics, aStatus);
|
|
|
|
// 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;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (wasEmpty) {
|
|
// Fixup style of frame just pulled up
|
|
nsIFrame* firstFrame = mFrames.FirstChild();
|
|
if (firstFrame) {
|
|
firstFrame->ReResolveStyleContext(&aPresContext, mStyleContext,
|
|
NS_STYLE_HINT_REFLOW, nsnull,
|
|
nsnull);
|
|
}
|
|
}
|
|
if (nsnull == mPrevInFlow) {
|
|
// XXX This is pretty sick, but what we do here is to pull-up, in
|
|
// advance, all of the next-in-flows children. We re-resolve their
|
|
// style while we are at at it so that when we reflow they have
|
|
// the right style.
|
|
//
|
|
// All of this is so that text-runs reflow properly.
|
|
irs.mPrevFrame = mFrames.LastChild();
|
|
for (;;) {
|
|
PRBool complete;
|
|
nsIFrame* frame = PullInlineFrame(&aPresContext, irs, &complete);
|
|
if (!frame) {
|
|
break;
|
|
}
|
|
irs.mPrevFrame = frame;
|
|
}
|
|
irs.mPrevFrame = nsnull;
|
|
}
|
|
else {
|
|
// XXX do this in the Init method instead
|
|
// For continuations, we need to check and see if our style
|
|
// context is right. If its the same as the first-in-flow, then
|
|
// we need to fix it up (that way :first-line style doesn't leak
|
|
// into this continuation since we aren't the first line).
|
|
nsFirstLineFrame* first = (nsFirstLineFrame*) GetFirstInFlow();
|
|
if (mStyleContext == first->mStyleContext) {
|
|
// Fixup our style context and our children. First get the
|
|
// proper parent context.
|
|
nsIFrame* parentFrame;
|
|
first->GetParent(&parentFrame);
|
|
nsIStyleContext* parentContext;
|
|
parentFrame->GetStyleContext(&parentContext);
|
|
if (parentContext) {
|
|
// Create a new style context that is a child of the parent
|
|
// style context thus removing the :first-line style. This way
|
|
// we behave as if an anonymous (unstyled) span was the child
|
|
// of the parent frame.
|
|
nsIStyleContext* newSC;
|
|
aPresContext.ResolvePseudoStyleContextFor(mContent,
|
|
nsHTMLAtoms::mozLineFrame,
|
|
parentContext,
|
|
PR_FALSE, &newSC);
|
|
if (newSC) {
|
|
// Switch to the new style context.
|
|
SetStyleContext(&aPresContext, newSC);
|
|
|
|
// Re-resolve all children
|
|
ReResolveChildList(&aPresContext, mStyleContext, mFrames);
|
|
|
|
NS_RELEASE(newSC);
|
|
}
|
|
NS_RELEASE(parentContext);
|
|
}
|
|
}
|
|
}
|
|
|
|
rv = ReflowInlineFrames(&aPresContext, aReflowState, irs,
|
|
aMetrics, aStatus);
|
|
// Note: when we are reflowing inline frames the line layout code
|
|
// will properly compute our NS_FRAME_OUTSIDE_CHILDREN state for
|
|
// us.
|
|
}
|
|
|
|
return rv;
|
|
}
|