/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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/. */ #include "nsPageContentFrame.h" #include "mozilla/PresShell.h" #include "mozilla/StaticPrefs_layout.h" #include "nsContentUtils.h" #include "nsCSSFrameConstructor.h" #include "nsPresContext.h" #include "nsGkAtoms.h" #include "nsPageSequenceFrame.h" using namespace mozilla; nsPageContentFrame* NS_NewPageContentFrame(PresShell* aPresShell, ComputedStyle* aStyle) { return new (aPresShell) nsPageContentFrame(aStyle, aPresShell->GetPresContext()); } NS_IMPL_FRAMEARENA_HELPERS(nsPageContentFrame) void nsPageContentFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aReflowOutput, const ReflowInput& aReflowInput, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsPageContentFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowInput, aReflowOutput, aStatus); MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); MOZ_ASSERT(mPD, "Need a pointer to nsSharedPageData before reflow starts"); if (GetPrevInFlow() && HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) { nsresult rv = aPresContext->PresShell()->FrameConstructor()->ReplicateFixedFrames( this); if (NS_FAILED(rv)) { return; } } // Set our size up front, since some parts of reflow depend on it // being already set. Note that the computed height may be // unconstrained; that's ok. Consumers should watch out for that. nsSize maxSize(aReflowInput.ComputedWidth(), aReflowInput.ComputedHeight()); SetSize(maxSize); WritingMode wm = aReflowInput.GetWritingMode(); aReflowOutput.ISize(wm) = aReflowInput.ComputedISize(); if (aReflowInput.ComputedBSize() != NS_UNCONSTRAINEDSIZE) { aReflowOutput.BSize(wm) = aReflowInput.ComputedBSize(); } aReflowOutput.SetOverflowAreasToDesiredBounds(); // A PageContentFrame must always have one child: the canvas frame. // Resize our frame allowing it only to be as big as we are // XXX Pay attention to the page's border and padding... if (mFrames.NotEmpty()) { nsIFrame* frame = mFrames.FirstChild(); WritingMode wm = frame->GetWritingMode(); LogicalSize logicalSize(wm, maxSize); ReflowInput kidReflowInput(aPresContext, aReflowInput, frame, logicalSize); kidReflowInput.SetComputedBSize(logicalSize.BSize(wm)); ReflowOutput kidReflowOutput(kidReflowInput); ReflowChild(frame, aPresContext, kidReflowOutput, kidReflowInput, 0, 0, ReflowChildFlags::Default, aStatus); // The document element's background should cover the entire canvas, so // take into account the combined area and any space taken up by // absolutely positioned elements nsMargin padding(0, 0, 0, 0); // XXXbz this screws up percentage padding (sets padding to zero // in the percentage padding case) kidReflowInput.mStylePadding->GetPadding(padding); // This is for shrink-to-fit, and therefore we want to use the // scrollable overflow, since the purpose of shrink to fit is to // make the content that ought to be reachable (represented by the // scrollable overflow) fit in the page. if (frame->HasOverflowAreas()) { // The background covers the content area and padding area, so check // for children sticking outside the child frame's padding edge nscoord xmost = kidReflowOutput.ScrollableOverflow().XMost(); if (xmost > kidReflowOutput.Width()) { nscoord widthToFit = xmost + padding.right + kidReflowInput.mStyleBorder->GetComputedBorderWidth(eSideRight); float ratio = float(maxSize.width) / widthToFit; NS_ASSERTION(ratio >= 0.0 && ratio < 1.0, "invalid shrink-to-fit ratio"); mPD->mShrinkToFitRatio = std::min(mPD->mShrinkToFitRatio, ratio); } // In the case of pdf.js documents, we also want to consider the height, // so that we don't clip the page in either axis if the aspect ratio of // the PDF doesn't match the destination. if (nsContentUtils::IsPDFJS(PresContext()->Document()->GetPrincipal())) { nscoord ymost = kidReflowOutput.ScrollableOverflow().YMost(); if (ymost > kidReflowOutput.Height()) { nscoord heightToFit = ymost + padding.bottom + kidReflowInput.mStyleBorder->GetComputedBorderWidth(eSideBottom); float ratio = float(maxSize.height) / heightToFit; MOZ_ASSERT(ratio >= 0.0 && ratio < 1.0); mPD->mShrinkToFitRatio = std::min(mPD->mShrinkToFitRatio, ratio); } // pdf.js pages should never overflow given the scaling above. // nsPrintJob::SetupToPrintContent ignores some ratios close to 1.0 // though and doesn't reflow us again in that case, so we need to clear // the overflow area here in case that happens. (bug 1689789) frame->ClearOverflowRects(); kidReflowOutput.mOverflowAreas = aReflowOutput.mOverflowAreas; } } // Place and size the child FinishReflowChild(frame, aPresContext, kidReflowOutput, &kidReflowInput, 0, 0, ReflowChildFlags::Default); NS_ASSERTION(aPresContext->IsDynamic() || !aStatus.IsFullyComplete() || !frame->GetNextInFlow(), "bad child flow list"); aReflowOutput.mOverflowAreas.UnionWith(kidReflowOutput.mOverflowAreas); } FinishAndStoreOverflow(&aReflowOutput); // Reflow our fixed frames nsReflowStatus fixedStatus; ReflowAbsoluteFrames(aPresContext, aReflowOutput, aReflowInput, fixedStatus); NS_ASSERTION(fixedStatus.IsComplete(), "fixed frames can be truncated, but not incomplete"); if (StaticPrefs::layout_display_list_improve_fragmentation() && mFrames.NotEmpty()) { auto* previous = static_cast(GetPrevContinuation()); const nscoord previousPageOverflow = previous ? previous->mRemainingOverflow : 0; const nsSize containerSize(aReflowInput.AvailableWidth(), aReflowInput.AvailableHeight()); const nscoord pageBSize = GetLogicalRect(containerSize).BSize(wm); const nscoord overflowBSize = LogicalRect(wm, ScrollableOverflowRect(), GetSize()).BEnd(wm); const nscoord currentPageOverflow = overflowBSize - pageBSize; nscoord remainingOverflow = std::max(currentPageOverflow, previousPageOverflow - pageBSize); if (aStatus.IsFullyComplete() && remainingOverflow > 0) { // If we have ScrollableOverflow off the end of our page, then we report // ourselves as overflow-incomplete in order to produce an additional // content-less page, which we expect to draw our overflow on our behalf. aStatus.SetOverflowIncomplete(); } mRemainingOverflow = std::max(remainingOverflow, 0); } NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aReflowOutput); } void nsPageContentFrame::AppendDirectlyOwnedAnonBoxes( nsTArray& aResult) { MOZ_ASSERT(mFrames.FirstChild(), "pageContentFrame must have a canvasFrame child"); aResult.AppendElement(mFrames.FirstChild()); } #ifdef DEBUG_FRAME_DUMP nsresult nsPageContentFrame::GetFrameName(nsAString& aResult) const { return MakeFrameName(u"PageContent"_ns, aResult); } #endif