fune/layout/generic/nsSplittableFrame.cpp
Phil Ringnalda 811c3783d4 Back out 5 changesets (bug 1028460, bug 1031444) for somehow breaking asmjscache/test/test_cachingBasic.html on Android 2.2
Backed out changeset 0b5918ec6521 (bug 1031444)
Backed out changeset 663ff18cd4a1 (bug 1028460)
Backed out changeset ae01b3919c8c (bug 1028460)
Backed out changeset 316c8dfeca9b (bug 1028460)
Backed out changeset e237b2c61ea2 (bug 1028460)
2014-06-28 22:01:28 -07:00

295 lines
8.5 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
* base class for rendering objects that can be split across lines,
* columns, or pages
*/
#include "nsSplittableFrame.h"
#include "nsContainerFrame.h"
#include "nsIFrameInlines.h"
NS_IMPL_FRAMEARENA_HELPERS(nsSplittableFrame)
void
nsSplittableFrame::Init(nsIContent* aContent,
nsContainerFrame* aParent,
nsIFrame* aPrevInFlow)
{
nsFrame::Init(aContent, aParent, aPrevInFlow);
if (aPrevInFlow) {
// Hook the frame into the flow
SetPrevInFlow(aPrevInFlow);
aPrevInFlow->SetNextInFlow(this);
}
}
void
nsSplittableFrame::DestroyFrom(nsIFrame* aDestructRoot)
{
// Disconnect from the flow list
if (mPrevContinuation || mNextContinuation) {
RemoveFromFlow(this);
}
// Let the base class destroy the frame
nsFrame::DestroyFrom(aDestructRoot);
}
nsSplittableType
nsSplittableFrame::GetSplittableType() const
{
return NS_FRAME_SPLITTABLE;
}
nsIFrame* nsSplittableFrame::GetPrevContinuation() const
{
return mPrevContinuation;
}
void
nsSplittableFrame::SetPrevContinuation(nsIFrame* aFrame)
{
NS_ASSERTION (!aFrame || GetType() == aFrame->GetType(), "setting a prev continuation with incorrect type!");
NS_ASSERTION (!IsInPrevContinuationChain(aFrame, this), "creating a loop in continuation chain!");
mPrevContinuation = aFrame;
RemoveStateBits(NS_FRAME_IS_FLUID_CONTINUATION);
}
nsIFrame* nsSplittableFrame::GetNextContinuation() const
{
return mNextContinuation;
}
void
nsSplittableFrame::SetNextContinuation(nsIFrame* aFrame)
{
NS_ASSERTION (!aFrame || GetType() == aFrame->GetType(), "setting a next continuation with incorrect type!");
NS_ASSERTION (!IsInNextContinuationChain(aFrame, this), "creating a loop in continuation chain!");
mNextContinuation = aFrame;
if (aFrame)
aFrame->RemoveStateBits(NS_FRAME_IS_FLUID_CONTINUATION);
}
nsIFrame*
nsSplittableFrame::FirstContinuation() const
{
nsSplittableFrame* firstContinuation = const_cast<nsSplittableFrame*>(this);
while (firstContinuation->mPrevContinuation) {
firstContinuation = static_cast<nsSplittableFrame*>(firstContinuation->mPrevContinuation);
}
MOZ_ASSERT(firstContinuation, "post-condition failed");
return firstContinuation;
}
nsIFrame*
nsSplittableFrame::LastContinuation() const
{
nsSplittableFrame* lastContinuation = const_cast<nsSplittableFrame*>(this);
while (lastContinuation->mNextContinuation) {
lastContinuation = static_cast<nsSplittableFrame*>(lastContinuation->mNextContinuation);
}
MOZ_ASSERT(lastContinuation, "post-condition failed");
return lastContinuation;
}
#ifdef DEBUG
bool nsSplittableFrame::IsInPrevContinuationChain(nsIFrame* aFrame1, nsIFrame* aFrame2)
{
int32_t iterations = 0;
while (aFrame1 && iterations < 10) {
// Bail out after 10 iterations so we don't bog down debug builds too much
if (aFrame1 == aFrame2)
return true;
aFrame1 = aFrame1->GetPrevContinuation();
++iterations;
}
return false;
}
bool nsSplittableFrame::IsInNextContinuationChain(nsIFrame* aFrame1, nsIFrame* aFrame2)
{
int32_t iterations = 0;
while (aFrame1 && iterations < 10) {
// Bail out after 10 iterations so we don't bog down debug builds too much
if (aFrame1 == aFrame2)
return true;
aFrame1 = aFrame1->GetNextContinuation();
++iterations;
}
return false;
}
#endif
nsIFrame* nsSplittableFrame::GetPrevInFlow() const
{
return (GetStateBits() & NS_FRAME_IS_FLUID_CONTINUATION) ? mPrevContinuation : nullptr;
}
void
nsSplittableFrame::SetPrevInFlow(nsIFrame* aFrame)
{
NS_ASSERTION (!aFrame || GetType() == aFrame->GetType(), "setting a prev in flow with incorrect type!");
NS_ASSERTION (!IsInPrevContinuationChain(aFrame, this), "creating a loop in continuation chain!");
mPrevContinuation = aFrame;
AddStateBits(NS_FRAME_IS_FLUID_CONTINUATION);
}
nsIFrame* nsSplittableFrame::GetNextInFlow() const
{
return mNextContinuation && (mNextContinuation->GetStateBits() & NS_FRAME_IS_FLUID_CONTINUATION) ?
mNextContinuation : nullptr;
}
void
nsSplittableFrame::SetNextInFlow(nsIFrame* aFrame)
{
NS_ASSERTION (!aFrame || GetType() == aFrame->GetType(), "setting a next in flow with incorrect type!");
NS_ASSERTION (!IsInNextContinuationChain(aFrame, this), "creating a loop in continuation chain!");
mNextContinuation = aFrame;
if (aFrame)
aFrame->AddStateBits(NS_FRAME_IS_FLUID_CONTINUATION);
}
nsIFrame*
nsSplittableFrame::FirstInFlow() const
{
nsSplittableFrame* firstInFlow = const_cast<nsSplittableFrame*>(this);
while (nsIFrame* prev = firstInFlow->GetPrevInFlow()) {
firstInFlow = static_cast<nsSplittableFrame*>(prev);
}
MOZ_ASSERT(firstInFlow, "post-condition failed");
return firstInFlow;
}
nsIFrame*
nsSplittableFrame::LastInFlow() const
{
nsSplittableFrame* lastInFlow = const_cast<nsSplittableFrame*>(this);
while (nsIFrame* next = lastInFlow->GetNextInFlow()) {
lastInFlow = static_cast<nsSplittableFrame*>(next);
}
MOZ_ASSERT(lastInFlow, "post-condition failed");
return lastInFlow;
}
// Remove this frame from the flow. Connects prev in flow and next in flow
void
nsSplittableFrame::RemoveFromFlow(nsIFrame* aFrame)
{
nsIFrame* prevContinuation = aFrame->GetPrevContinuation();
nsIFrame* nextContinuation = aFrame->GetNextContinuation();
// The new continuation is fluid only if the continuation on both sides
// of the removed frame was fluid
if (aFrame->GetPrevInFlow() && aFrame->GetNextInFlow()) {
if (prevContinuation) {
prevContinuation->SetNextInFlow(nextContinuation);
}
if (nextContinuation) {
nextContinuation->SetPrevInFlow(prevContinuation);
}
} else {
if (prevContinuation) {
prevContinuation->SetNextContinuation(nextContinuation);
}
if (nextContinuation) {
nextContinuation->SetPrevContinuation(prevContinuation);
}
}
aFrame->SetPrevInFlow(nullptr);
aFrame->SetNextInFlow(nullptr);
}
nscoord
nsSplittableFrame::GetConsumedBSize() const
{
nscoord height = 0;
for (nsIFrame* prev = GetPrevInFlow(); prev; prev = prev->GetPrevInFlow()) {
height += prev->GetContentRectRelativeToSelf().height;
}
return height;
}
nscoord
nsSplittableFrame::GetEffectiveComputedBSize(const nsHTMLReflowState& aReflowState,
nscoord aConsumedBSize) const
{
nscoord bSize = aReflowState.ComputedBSize();
if (bSize == NS_INTRINSICSIZE) {
return NS_INTRINSICSIZE;
}
if (aConsumedBSize == NS_INTRINSICSIZE) {
aConsumedBSize = GetConsumedBSize();
}
bSize -= aConsumedBSize;
// We may have stretched the frame beyond its computed height. Oh well.
return std::max(0, bSize);
}
int
nsSplittableFrame::GetLogicalSkipSides(const nsHTMLReflowState* aReflowState) const
{
if (IS_TRUE_OVERFLOW_CONTAINER(this)) {
return LOGICAL_SIDES_B_BOTH;
}
if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==
NS_STYLE_BOX_DECORATION_BREAK_CLONE)) {
return 0;
}
int skip = 0;
if (GetPrevInFlow()) {
skip |= LOGICAL_SIDE_B_START;
}
if (aReflowState) {
// We're in the midst of reflow right now, so it's possible that we haven't
// created a nif yet. If our content height is going to exceed our available
// height, though, then we're going to need a next-in-flow, it just hasn't
// been created yet.
if (NS_UNCONSTRAINEDSIZE != aReflowState->AvailableBSize()) {
nscoord effectiveCH = this->GetEffectiveComputedBSize(*aReflowState);
if (effectiveCH != NS_INTRINSICSIZE &&
effectiveCH > aReflowState->AvailableBSize()) {
// Our content height is going to exceed our available height, so we're
// going to need a next-in-flow.
skip |= LOGICAL_SIDE_B_END;
}
}
} else {
nsIFrame* nif = GetNextInFlow();
if (nif && !IS_TRUE_OVERFLOW_CONTAINER(nif)) {
skip |= LOGICAL_SIDE_B_END;
}
}
return skip;
}
#ifdef DEBUG
void
nsSplittableFrame::DumpBaseRegressionData(nsPresContext* aPresContext, FILE* out, int32_t aIndent)
{
nsFrame::DumpBaseRegressionData(aPresContext, out, aIndent);
if (nullptr != mNextContinuation) {
IndentBy(out, aIndent);
fprintf(out, "<next-continuation va=\"%p\"/>\n", (void*)mNextContinuation);
}
if (nullptr != mPrevContinuation) {
IndentBy(out, aIndent);
fprintf(out, "<prev-continuation va=\"%p\"/>\n", (void*)mPrevContinuation);
}
}
#endif