void TEST_availSpace(void){ CU_ASSERT(availSpace(heap_kb) == (1000/2) - (sizeof(void*)*3)); CU_ASSERT(availSpace(heap_mb) == (1000000/2) - (sizeof(void*)*3)); }
bool nsBlockReflowContext::ComputeCollapsedTopMargin(const nsHTMLReflowState& aRS, nsCollapsingMargin* aMargin, nsIFrame* aClearanceFrame, bool* aMayNeedRetry, bool* aBlockIsEmpty) { // Include frame's top margin aMargin->Include(aRS.mComputedMargin.top); // The inclusion of the bottom margin when empty is done by the caller // since it doesn't need to be done by the top-level (non-recursive) // caller. #ifdef NOISY_VERTICAL_MARGINS nsFrame::ListTag(stdout, aRS.frame); printf(": %d => %d\n", aRS.mComputedMargin.top, aMargin->get()); #endif bool dirtiedLine = false; bool setBlockIsEmpty = false; // Calculate the frame's generational top-margin from its child // blocks. Note that if the frame has a non-zero top-border or // top-padding then this step is skipped because it will be a margin // root. It is also skipped if the frame is a margin root for other // reasons. nsIFrame* frame = DescendIntoBlockLevelFrame(aRS.frame); nsPresContext* prescontext = frame->PresContext(); nsBlockFrame* block = nsnull; if (0 == aRS.mComputedBorderPadding.top) { block = nsLayoutUtils::GetAsBlock(frame); if (block) { bool topMarginRoot, unused; block->IsMarginRoot(&topMarginRoot, &unused); if (topMarginRoot) { block = nsnull; } } } // iterate not just through the lines of 'block' but also its // overflow lines and the normal and overflow lines of its next in // flows. Note that this will traverse some frames more than once: // for example, if A contains B and A->nextinflow contains // B->nextinflow, we'll traverse B->nextinflow twice. But this is // OK because our traversal is idempotent. for ( ;block; block = static_cast<nsBlockFrame*>(block->GetNextInFlow())) { for (PRIntn overflowLines = false; overflowLines <= true; ++overflowLines) { nsBlockFrame::line_iterator line; nsBlockFrame::line_iterator line_end; bool anyLines = true; if (overflowLines) { nsBlockFrame::FrameLines* frames = block->GetOverflowLines(); nsLineList* lines = frames ? &frames->mLines : nsnull; if (!lines) { anyLines = false; } else { line = lines->begin(); line_end = lines->end(); } } else { line = block->begin_lines(); line_end = block->end_lines(); } for (; anyLines && line != line_end; ++line) { if (!aClearanceFrame && line->HasClearance()) { // If we don't have a clearance frame, then we're computing // the collapsed margin in the first pass, assuming that all // lines have no clearance. So clear their clearance flags. line->ClearHasClearance(); line->MarkDirty(); dirtiedLine = true; } bool isEmpty; if (line->IsInline()) { isEmpty = line->IsEmpty(); } else { nsIFrame* kid = line->mFirstChild; if (kid == aClearanceFrame) { line->SetHasClearance(); line->MarkDirty(); dirtiedLine = true; goto done; } // Here is where we recur. Now that we have determined that a // generational collapse is required we need to compute the // child blocks margin and so in so that we can look into // it. For its margins to be computed we need to have a reflow // state for it. // We may have to construct an extra reflow state here if // we drilled down through a block wrapper. At the moment // we can only drill down one level so we only have to support // one extra reflow state. const nsHTMLReflowState* outerReflowState = &aRS; if (frame != aRS.frame) { NS_ASSERTION(frame->GetParent() == aRS.frame, "Can only drill through one level of block wrapper"); nsSize availSpace(aRS.ComputedWidth(), aRS.ComputedHeight()); outerReflowState = new nsHTMLReflowState(prescontext, aRS, frame, availSpace); } { nsSize availSpace(outerReflowState->ComputedWidth(), outerReflowState->ComputedHeight()); nsHTMLReflowState innerReflowState(prescontext, *outerReflowState, kid, availSpace); // Record that we're being optimistic by assuming the kid // has no clearance if (kid->GetStyleDisplay()->mBreakType != NS_STYLE_CLEAR_NONE) { *aMayNeedRetry = true; } if (ComputeCollapsedTopMargin(innerReflowState, aMargin, aClearanceFrame, aMayNeedRetry, &isEmpty)) { line->MarkDirty(); dirtiedLine = true; } if (isEmpty) aMargin->Include(innerReflowState.mComputedMargin.bottom); } if (outerReflowState != &aRS) { delete const_cast<nsHTMLReflowState*>(outerReflowState); } } if (!isEmpty) { if (!setBlockIsEmpty && aBlockIsEmpty) { setBlockIsEmpty = true; *aBlockIsEmpty = false; } goto done; } } if (!setBlockIsEmpty && aBlockIsEmpty) { // The first time we reach here is when this is the first block // and we have processed all its normal lines. setBlockIsEmpty = true; // All lines are empty, or we wouldn't be here! *aBlockIsEmpty = aRS.frame->IsSelfEmpty(); } } } done: if (!setBlockIsEmpty && aBlockIsEmpty) { *aBlockIsEmpty = aRS.frame->IsEmpty(); } #ifdef NOISY_VERTICAL_MARGINS nsFrame::ListTag(stdout, aRS.frame); printf(": => %d\n", aMargin->get()); #endif return dirtiedLine; }
// XXXldb This behavior doesn't quite fit with CSS1 and CSS2 -- // technically we're supposed let the current line flow around the // float as well unless it won't fit next to what we already have. // But nobody else implements it that way... PRBool nsBlockReflowState::AddFloat(nsLineLayout* aLineLayout, nsIFrame* aFloat, nscoord aAvailableWidth, nsReflowStatus& aReflowStatus) { NS_PRECONDITION(!aLineLayout || mBlock->end_lines() != mCurrentLine, "null ptr"); NS_PRECONDITION(aFloat->GetStateBits() & NS_FRAME_OUT_OF_FLOW, "aFloat must be an out-of-flow frame"); // Set the geometric parent of the float aFloat->SetParent(mBlock); aReflowStatus = NS_FRAME_COMPLETE; // 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 float. nscoord ox, oy; mFloatManager->GetTranslation(ox, oy); nscoord dx = ox - mFloatManagerX; nscoord dy = oy - mFloatManagerY; mFloatManager->Translate(-dx, -dy); PRBool placed; // Now place the float immediately if possible. Otherwise stash it // away in mPendingFloats and place it later. // If one or more floats has already been pushed to the next line, // don't let this one go on the current line, since that would violate // float ordering. nsRect floatAvailableSpace = GetFloatAvailableSpace().mRect; if (!aLineLayout || (mBelowCurrentLineFloats.IsEmpty() && (aLineLayout->LineIsEmpty() || mBlock->ComputeFloatWidth(*this, floatAvailableSpace, aFloat) <= aAvailableWidth))) { // And then place it // force it to fit if we're at the top of the block and we can't // break before this PRBool forceFit = !aLineLayout || (IsAdjacentWithTop() && !aLineLayout->LineIsBreakable()); placed = FlowAndPlaceFloat(aFloat, aReflowStatus, forceFit); NS_ASSERTION(placed || !forceFit, "If we asked for force-fit, it should have been placed"); if (forceFit || (placed && !NS_FRAME_IS_TRUNCATED(aReflowStatus))) { // Pass on updated available space to the current inline reflow engine nsFlowAreaRect floatAvailSpace = GetFloatAvailableSpace(mY, forceFit); nsRect availSpace(nsPoint(floatAvailSpace.mRect.x + BorderPadding().left, mY), floatAvailSpace.mRect.Size()); if (aLineLayout) { aLineLayout->UpdateBand(availSpace, aFloat); // Record this float in the current-line list mCurrentLineFloats.Append(mFloatCacheFreeList.Alloc(aFloat)); } // If we can't break here, hide the fact that it's truncated // XXX We can probably do this more cleanly aReflowStatus &= ~NS_FRAME_TRUNCATED; } else { if (IsAdjacentWithTop()) { // Pushing the line to the next page won't give us any more space; // therefore, we break. NS_ASSERTION(aLineLayout->LineIsBreakable(), "We can't get here unless forceFit is false"); aReflowStatus = NS_INLINE_LINE_BREAK_BEFORE(); } else { // Make sure we propagate the truncated status; this signals the // block to push the line to the next page. aReflowStatus |= NS_FRAME_TRUNCATED; } } } else { // Always claim to be placed; we don't know whether we fit yet, so we // deal with this in PlaceBelowCurrentLineFloats placed = PR_TRUE; // This float will be placed after the line is done (it is a // below-current-line float). mBelowCurrentLineFloats.Append(mFloatCacheFreeList.Alloc(aFloat)); } // Restore coordinate system mFloatManager->Translate(dx, dy); return placed; }