diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index ae878fca6646..81ed7670f5a0 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -2412,7 +2412,7 @@ bool nsLayoutUtils::GetLayerTransformForFrame(nsIFrame* aFrame, Matrix4x4Flagged* aTransform) { // FIXME/bug 796690: we can sometimes compute a transform in these // cases, it just increases complexity considerably. Punt for now. - if (aFrame->Extend3DContext() || aFrame->HasTransformGetter()) { + if (aFrame->Extend3DContext() || aFrame->GetTransformGetter()) { return false; } diff --git a/layout/generic/PrintedSheetFrame.cpp b/layout/generic/PrintedSheetFrame.cpp index 1730a12671c8..e044ab4bb13c 100644 --- a/layout/generic/PrintedSheetFrame.cpp +++ b/layout/generic/PrintedSheetFrame.cpp @@ -31,59 +31,6 @@ NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame) NS_IMPL_FRAMEARENA_HELPERS(PrintedSheetFrame) -std::tuple GetRowAndColFromIdx(uint32_t aIdxOnSheet, - uint32_t aNumCols) { - // Compute the row index by *dividing* the item's ordinal position by how - // many items fit in each row (i.e. the number of columns), and flooring. - // Compute the column index by getting the remainder of that division: - // Notably, mNumRows is irrelevant to this computation; that's because - // we're adding new items column-by-column rather than row-by-row. - return {aIdxOnSheet / aNumCols, aIdxOnSheet % aNumCols}; -} - -// Helper for BuildDisplayList: -gfx::Matrix4x4 ComputePagesPerSheetTransform(nsIFrame* aFrame, - float aAppUnitsPerPixel) { - MOZ_ASSERT(aFrame->IsPageFrame()); - auto* pageFrame = static_cast(aFrame); - - // Variables that we use in our transform (initialized with reasonable - // defaults that work for the regular one-page-per-sheet scenario): - float scale = 1.0f; - nsPoint gridOrigin; - uint32_t rowIdx = 0; - uint32_t colIdx = 0; - - nsSharedPageData* pd = pageFrame->GetSharedPageData(); - if (pd) { - const auto* ppsInfo = pd->PagesPerSheetInfo(); - if (ppsInfo->mNumPages > 1) { - scale = pd->mPagesPerSheetScale; - gridOrigin = pd->mPagesPerSheetGridOrigin; - std::tie(rowIdx, colIdx) = GetRowAndColFromIdx(pageFrame->IndexOnSheet(), - pd->mPagesPerSheetNumCols); - } - } - - // Scale down the page based on the above-computed scale: - auto transform = gfx::Matrix4x4::Scaling(scale, scale, 1); - - // Draw the page at an offset, to get it in its pages-per-sheet "cell": - nsSize pageSize = pageFrame->PresContext()->GetPageSize(); - transform.PreTranslate( - NSAppUnitsToFloatPixels(colIdx * pageSize.width, aAppUnitsPerPixel), - NSAppUnitsToFloatPixels(rowIdx * pageSize.height, aAppUnitsPerPixel), 0); - - // Also add the grid origin as an offset (so that we're not drawing into the - // sheet's unwritable area). Note that this is a PostTranslate operation - // (vs. PreTranslate above), since gridOrigin is an offset on the sheet - // itself, whereas the offset above was in the scaled coordinate space of the - // pages. - return transform.PostTranslate( - NSAppUnitsToFloatPixels(gridOrigin.x, aAppUnitsPerPixel), - NSAppUnitsToFloatPixels(gridOrigin.y, aAppUnitsPerPixel), 0); -} - void PrintedSheetFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists) { if (PresContext()->IsScreen()) { @@ -92,30 +39,9 @@ void PrintedSheetFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, DisplayBorderBackgroundOutline(aBuilder, aLists); } - // Let each of our children (pages) draw itself, with a supplemental - // transform to shrink it & place it in its pages-per-sheet cell: for (auto* frame : mFrames) { if (!frame->HasAnyStateBits(NS_PAGE_SKIPPED_BY_CUSTOM_RANGE)) { - // We'll be drawing our nsPageFrame children with a (usually-trivial) - // N-pages-per-sheet transform applied, so our passed-in visible rect - // isn't meaningful while we're drawing our children, because the - // transform could scale down content whose coordinates are off-screen - // such that it ends up on-screen. So: we temporarily update the visible - // rect to be the child nsPageFrame's whole frame-rect (represented in - // this PrintedSheetFrame's coordinate space. - nsDisplayList content; - { - nsRect visibleRect = frame->GetRect(); - nsDisplayListBuilder::AutoBuildingDisplayList buildingForChild( - aBuilder, this, visibleRect, visibleRect); - - frame->BuildDisplayListForStackingContext(aBuilder, &content); - } - content.AppendNewToTop(aBuilder, frame, &content, - content.GetBuildingRect(), - ComputePagesPerSheetTransform); - - aLists.Content()->AppendToTop(&content); + BuildDisplayListForChild(aBuilder, frame, aLists); } } } diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h index 86b7f53d50ef..3b3e9f750ce8 100644 --- a/layout/generic/nsIFrame.h +++ b/layout/generic/nsIFrame.h @@ -1975,9 +1975,18 @@ class nsIFrame : public nsQueryFrame { } /** - * Return true if this frame might be using a transform getter. + * Returns a matrix (in pixels) for the current frame. The matrix should be + * relative to the current frame's coordinate space. + * + * @param aFrame The frame to compute the transform for. + * @param aAppUnitsPerPixel The number of app units per graphics unit. */ - virtual bool HasTransformGetter() const { return false; } + using ComputeTransformFunction = Matrix4x4 (*)(const nsIFrame*, + float aAppUnitsPerPixel); + /** Returns the transform getter of this frame, if any. */ + virtual ComputeTransformFunction GetTransformGetter() const { + return nullptr; + } /** * Returns true if this frame is an SVG frame that has SVG transforms applied diff --git a/layout/generic/nsPageContentFrame.cpp b/layout/generic/nsPageContentFrame.cpp index cb4d46678f91..24b23310bd03 100644 --- a/layout/generic/nsPageContentFrame.cpp +++ b/layout/generic/nsPageContentFrame.cpp @@ -9,9 +9,11 @@ #include "mozilla/StaticPrefs_layout.h" #include "nsContentUtils.h" +#include "nsPageFrame.h" #include "nsCSSFrameConstructor.h" #include "nsPresContext.h" #include "nsGkAtoms.h" +#include "nsLayoutUtils.h" #include "nsPageSequenceFrame.h" using namespace mozilla; @@ -164,6 +166,235 @@ void nsPageContentFrame::Reflow(nsPresContext* aPresContext, NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aReflowOutput); } +using PageAndOffset = std::pair; + +// Returns the previous continuation PageContentFrames that have overflow areas, +// and their offsets to the top of the given PageContentFrame |aPage|. Since the +// iteration is done backwards, the returned pages are arranged in descending +// order of page number. +static nsTArray GetPreviousPagesWithOverflow( + nsPageContentFrame* aPage) { + nsTArray pages(8); + + auto GetPreviousPageContentFrame = [](nsPageContentFrame* aPageCF) { + nsIFrame* prevCont = aPageCF->GetPrevContinuation(); + MOZ_ASSERT(!prevCont || prevCont->IsPageContentFrame(), + "Expected nsPageContentFrame or nullptr"); + + return static_cast(prevCont); + }; + + nsPageContentFrame* pageCF = aPage; + // The collective height of all prev-continuations we've traversed so far: + nscoord offsetToCurrentPageBStart = 0; + const auto wm = pageCF->GetWritingMode(); + while ((pageCF = GetPreviousPageContentFrame(pageCF))) { + offsetToCurrentPageBStart += pageCF->BSize(wm); + + if (pageCF->HasOverflowAreas()) { + pages.EmplaceBack(pageCF, offsetToCurrentPageBStart); + } + } + + return pages; +} + +static void BuildPreviousPageOverflow(nsDisplayListBuilder* aBuilder, + nsPageFrame* aPageFrame, + nsPageContentFrame* aCurrentPageCF, + const nsDisplayListSet& aLists) { + const auto previousPagesAndOffsets = + GetPreviousPagesWithOverflow(aCurrentPageCF); + + const auto wm = aCurrentPageCF->GetWritingMode(); + for (const PageAndOffset& pair : Reversed(previousPagesAndOffsets)) { + auto* prevPageCF = pair.first; + const nscoord offsetToCurrentPageBStart = pair.second; + // Only scrollable overflow create new pages, not ink overflow. + const LogicalRect scrollableOverflow( + wm, prevPageCF->ScrollableOverflowRectRelativeToSelf(), + prevPageCF->GetSize()); + const auto remainingOverflow = + scrollableOverflow.BEnd(wm) - offsetToCurrentPageBStart; + if (remainingOverflow <= 0) { + continue; + } + + // This rect represents the piece of prevPageCF's overflow that ends up on + // the current pageContentFrame (in prevPageCF's coordinate system). + // Note that we use InkOverflow here since this is for painting. + LogicalRect overflowRect(wm, prevPageCF->InkOverflowRectRelativeToSelf(), + prevPageCF->GetSize()); + overflowRect.BStart(wm) = offsetToCurrentPageBStart; + overflowRect.BSize(wm) = std::min(remainingOverflow, prevPageCF->BSize(wm)); + + { + // Convert the overflowRect to the coordinate system of aPageFrame, and + // set it as the visible rect for display list building. + const nsRect visibleRect = + overflowRect.GetPhysicalRect(wm, aPageFrame->GetSize()) + + prevPageCF->GetOffsetTo(aPageFrame); + nsDisplayListBuilder::AutoBuildingDisplayList buildingForChild( + aBuilder, aPageFrame, visibleRect, visibleRect); + + // This part is tricky. Because display items are positioned based on the + // frame tree, building a display list for the previous page yields + // display items that are outside of the current page bounds. + // To fix that, an additional reference frame offset is added, which + // shifts the display items down (block axis) as if the current and + // previous page were one long page in the same coordinate system. + const nsSize containerSize = aPageFrame->GetSize(); + LogicalPoint pageOffset(wm, aCurrentPageCF->GetOffsetTo(prevPageCF), + containerSize); + pageOffset.B(wm) -= offsetToCurrentPageBStart; + buildingForChild.SetAdditionalOffset( + pageOffset.GetPhysicalPoint(wm, containerSize)); + + aPageFrame->BuildDisplayListForChild(aBuilder, prevPageCF, aLists); + } + } +} + +/** + * Remove all leaf display items that are not for descendants of + * aBuilder->GetReferenceFrame() from aList. + * @param aPage the page we're constructing the display list for + * @param aExtraPage the page we constructed aList for + * @param aList the list that is modified in-place + */ +static void PruneDisplayListForExtraPage(nsDisplayListBuilder* aBuilder, + nsPageFrame* aPage, + nsIFrame* aExtraPage, + nsDisplayList* aList) { + nsDisplayList newList; + + while (true) { + nsDisplayItem* i = aList->RemoveBottom(); + if (!i) break; + nsDisplayList* subList = i->GetSameCoordinateSystemChildren(); + if (subList) { + PruneDisplayListForExtraPage(aBuilder, aPage, aExtraPage, subList); + i->UpdateBounds(aBuilder); + } else { + nsIFrame* f = i->Frame(); + if (!nsLayoutUtils::IsProperAncestorFrameCrossDoc(aPage, f)) { + // We're throwing this away so call its destructor now. The memory + // is owned by aBuilder which destroys all items at once. + i->Destroy(aBuilder); + continue; + } + } + newList.AppendToTop(i); + } + aList->AppendToTop(&newList); +} + +static void BuildDisplayListForExtraPage(nsDisplayListBuilder* aBuilder, + nsPageFrame* aPage, + nsIFrame* aExtraPage, + nsDisplayList* aList) { + // The only content in aExtraPage we care about is out-of-flow content from + // aPage, whose placeholders have occurred in aExtraPage. If + // NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO is not set, then aExtraPage has + // no such content. + if (!aExtraPage->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) { + return; + } + nsDisplayList list; + aExtraPage->BuildDisplayListForStackingContext(aBuilder, &list); + PruneDisplayListForExtraPage(aBuilder, aPage, aExtraPage, &list); + aList->AppendToTop(&list); +} + +static gfx::Matrix4x4 ComputePageContentTransform(const nsIFrame* aFrame, + float aAppUnitsPerPixel) { + float scale = aFrame->PresContext()->GetPageScale(); + return gfx::Matrix4x4::Scaling(scale, scale, 1); +} + +nsIFrame::ComputeTransformFunction nsPageContentFrame::GetTransformGetter() + const { + return ComputePageContentTransform; +} + +void nsPageContentFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, + const nsDisplayListSet& aLists) { + MOZ_ASSERT(GetParent()); + MOZ_ASSERT(GetParent()->IsPageFrame()); + auto* pageFrame = static_cast(GetParent()); + auto pageNum = pageFrame->GetPageNum(); + NS_ASSERTION(pageNum <= 255, "Too many pages to handle OOFs"); + + if (aBuilder->GetBuildingExtraPagesForPageNum()) { + return mozilla::ViewportFrame::BuildDisplayList(aBuilder, aLists); + } + + nsDisplayListCollection set(aBuilder); + + const nsRect clipRect(aBuilder->ToReferenceFrame(this), GetSize()); + DisplayListClipState::AutoSaveRestore clipState(aBuilder); + + // Overwrite current clip, since we're going to wrap in a transform and the + // current clip is no longer meaningful. + clipState.Clear(); + clipState.ClipContainingBlockDescendants(clipRect); + + if (StaticPrefs::layout_display_list_improve_fragmentation() && + pageNum <= 255) { + nsDisplayListBuilder::AutoPageNumberSetter p(aBuilder, pageNum); + BuildPreviousPageOverflow(aBuilder, pageFrame, this, set); + } + + mozilla::ViewportFrame::BuildDisplayList(aBuilder, set); + + nsDisplayList content; + set.SerializeWithCorrectZOrder(&content, GetContent()); + + // We may need to paint out-of-flow frames whose placeholders are on other + // pages. Add those pages to our display list. Note that out-of-flow frames + // can't be placed after their placeholders so + // we don't have to process earlier pages. The display lists for + // these extra pages are pruned so that only display items for the + // page we currently care about (which we would have reached by + // following placeholders to their out-of-flows) end up on the list. + // + // Stacking context frames that wrap content on their normal page, + // as well as OOF content for this page will have their container + // items duplicated. We tell the builder to include our page number + // in the unique key for any extra page items so that they can be + // differentiated from the ones created on the normal page. + if (pageNum <= 255) { + const nsRect overflowRect = ScrollableOverflowRectRelativeToSelf(); + nsDisplayListBuilder::AutoPageNumberSetter p(aBuilder, pageNum); + + // The static_cast here is technically unnecessary, but it helps + // devirtualize the GetNextContinuation() function call if pcf has a + // concrete type (with an inherited `final` GetNextContinuation() impl). + auto* pageCF = this; + while ((pageCF = static_cast( + pageCF->GetNextContinuation()))) { + nsRect childVisible = overflowRect + GetOffsetTo(pageCF); + + nsDisplayListBuilder::AutoBuildingDisplayList buildingForChild( + aBuilder, pageCF, childVisible, childVisible); + BuildDisplayListForExtraPage(aBuilder, pageFrame, pageCF, &content); + } + } + + // Add the canvas background color to the bottom of the list. This + // happens after we've built the list so that AddCanvasBackgroundColorItem + // can monkey with the contents if necessary. + const nsRect backgroundRect(aBuilder->ToReferenceFrame(this), GetSize()); + PresShell()->AddCanvasBackgroundColorItem( + aBuilder, &content, this, backgroundRect, NS_RGBA(0, 0, 0, 0)); + + content.AppendNewToTop( + aBuilder, this, &content, content.GetBuildingRect(), + nsDisplayTransform::WithTransformGetter); + + aLists.Content()->AppendToTop(&content); +} + void nsPageContentFrame::AppendDirectlyOwnedAnonBoxes( nsTArray& aResult) { MOZ_ASSERT(mFrames.FirstChild(), diff --git a/layout/generic/nsPageContentFrame.h b/layout/generic/nsPageContentFrame.h index 6b0d5b6a90a8..ba7b07a76c72 100644 --- a/layout/generic/nsPageContentFrame.h +++ b/layout/generic/nsPageContentFrame.h @@ -37,7 +37,9 @@ class nsPageContentFrame final : public mozilla::ViewportFrame { void SetSharedPageData(nsSharedPageData* aPD) { mPD = aPD; } - bool HasTransformGetter() const override { return true; } + ComputeTransformFunction GetTransformGetter() const override; + void BuildDisplayList(nsDisplayListBuilder*, + const nsDisplayListSet&) override; /** * Return our canvas frame. diff --git a/layout/generic/nsPageFrame.cpp b/layout/generic/nsPageFrame.cpp index 08bc775d098f..3eb12a7ccfd1 100644 --- a/layout/generic/nsPageFrame.cpp +++ b/layout/generic/nsPageFrame.cpp @@ -400,63 +400,6 @@ void nsPageFrame::DrawHeaderFooter(gfxContext& aRenderingContext, } } -/** - * Remove all leaf display items that are not for descendants of - * aBuilder->GetReferenceFrame() from aList. - * @param aPage the page we're constructing the display list for - * @param aExtraPage the page we constructed aList for - * @param aList the list that is modified in-place - */ -static void PruneDisplayListForExtraPage(nsDisplayListBuilder* aBuilder, - nsPageFrame* aPage, - nsIFrame* aExtraPage, - nsDisplayList* aList) { - nsDisplayList newList; - - while (true) { - nsDisplayItem* i = aList->RemoveBottom(); - if (!i) break; - nsDisplayList* subList = i->GetSameCoordinateSystemChildren(); - if (subList) { - PruneDisplayListForExtraPage(aBuilder, aPage, aExtraPage, subList); - i->UpdateBounds(aBuilder); - } else { - nsIFrame* f = i->Frame(); - if (!nsLayoutUtils::IsProperAncestorFrameCrossDoc(aPage, f)) { - // We're throwing this away so call its destructor now. The memory - // is owned by aBuilder which destroys all items at once. - i->Destroy(aBuilder); - continue; - } - } - newList.AppendToTop(i); - } - aList->AppendToTop(&newList); -} - -static void BuildDisplayListForExtraPage(nsDisplayListBuilder* aBuilder, - nsPageFrame* aPage, - nsIFrame* aExtraPage, - nsDisplayList* aList) { - // The only content in aExtraPage we care about is out-of-flow content from - // aPage, whose placeholders have occurred in aExtraPage. If - // NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO is not set, then aExtraPage has - // no such content. - if (!aExtraPage->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) { - return; - } - nsDisplayList list; - aExtraPage->BuildDisplayListForStackingContext(aBuilder, &list); - PruneDisplayListForExtraPage(aBuilder, aPage, aExtraPage, &list); - aList->AppendToTop(&list); -} - -static gfx::Matrix4x4 ComputePageTransform(nsIFrame* aFrame, - float aAppUnitsPerPixel) { - float scale = aFrame->PresContext()->GetPageScale(); - return gfx::Matrix4x4::Scaling(scale, scale, 1); -} - class nsDisplayHeaderFooter final : public nsPaintedDisplayItem { public: nsDisplayHeaderFooter(nsDisplayListBuilder* aBuilder, nsPageFrame* aFrame) @@ -530,207 +473,69 @@ static void PaintMarginGuides(nsIFrame* aFrame, DrawTarget* aDrawTarget, options); } -//------------------------------------------------------------------------------ -using PageAndOffset = std::pair; - -// Returns the previous continuation PageContentFrames that have overflow areas, -// and their offsets to the top of the given PageContentFrame |aPage|. Since the -// iteration is done backwards, the returned pages are arranged in descending -// order of page number. -static nsTArray GetPreviousPagesWithOverflow( - nsPageContentFrame* aPage) { - nsTArray pages(8); - - auto GetPreviousPageContentFrame = [](nsPageContentFrame* aPageCF) { - nsIFrame* prevCont = aPageCF->GetPrevContinuation(); - MOZ_ASSERT(!prevCont || prevCont->IsPageContentFrame(), - "Expected nsPageContentFrame or nullptr"); - - return static_cast(prevCont); - }; - - nsPageContentFrame* pageCF = aPage; - // The collective height of all prev-continuations we've traversed so far: - nscoord offsetToCurrentPageBStart = 0; - const auto wm = pageCF->GetWritingMode(); - while ((pageCF = GetPreviousPageContentFrame(pageCF))) { - offsetToCurrentPageBStart += pageCF->BSize(wm); - - if (pageCF->HasOverflowAreas()) { - pages.EmplaceBack(pageCF, offsetToCurrentPageBStart); - } - } - - return pages; +static std::tuple GetRowAndColFromIdx(uint32_t aIdxOnSheet, + uint32_t aNumCols) { + // Compute the row index by *dividing* the item's ordinal position by how + // many items fit in each row (i.e. the number of columns), and flooring. + // Compute the column index by getting the remainder of that division: + // Notably, mNumRows is irrelevant to this computation; that's because + // we're adding new items column-by-column rather than row-by-row. + return {aIdxOnSheet / aNumCols, aIdxOnSheet % aNumCols}; } -static void BuildPreviousPageOverflow(nsDisplayListBuilder* aBuilder, - nsPageFrame* aPageFrame, - nsPageContentFrame* aCurrentPageCF, - const nsDisplayListSet& aLists) { - const auto previousPagesAndOffsets = - GetPreviousPagesWithOverflow(aCurrentPageCF); +// Helper for BuildDisplayList: +static gfx::Matrix4x4 ComputePagesPerSheetTransform(const nsIFrame* aFrame, + float aAppUnitsPerPixel) { + MOZ_ASSERT(aFrame->IsPageFrame()); + auto* pageFrame = static_cast(aFrame); - const auto wm = aCurrentPageCF->GetWritingMode(); - for (const PageAndOffset& pair : Reversed(previousPagesAndOffsets)) { - auto* prevPageCF = pair.first; - const nscoord offsetToCurrentPageBStart = pair.second; - // Only scrollable overflow create new pages, not ink overflow. - const LogicalRect scrollableOverflow( - wm, prevPageCF->ScrollableOverflowRectRelativeToSelf(), - prevPageCF->GetSize()); - const auto remainingOverflow = - scrollableOverflow.BEnd(wm) - offsetToCurrentPageBStart; - if (remainingOverflow <= 0) { - continue; - } + // Variables that we use in our transform (initialized with reasonable + // defaults that work for the regular one-page-per-sheet scenario): + float scale = 1.0f; + nsPoint gridOrigin; + uint32_t rowIdx = 0; + uint32_t colIdx = 0; - // This rect represents the piece of prevPageCF's overflow that ends up on - // the current pageContentFrame (in prevPageCF's coordinate system). - // Note that we use InkOverflow here since this is for painting. - LogicalRect overflowRect(wm, prevPageCF->InkOverflowRectRelativeToSelf(), - prevPageCF->GetSize()); - overflowRect.BStart(wm) = offsetToCurrentPageBStart; - overflowRect.BSize(wm) = std::min(remainingOverflow, prevPageCF->BSize(wm)); - - { - // Convert the overflowRect to the coordinate system of aPageFrame, and - // set it as the visible rect for display list building. - const nsRect visibleRect = - overflowRect.GetPhysicalRect(wm, aPageFrame->GetSize()) + - prevPageCF->GetOffsetTo(aPageFrame); - nsDisplayListBuilder::AutoBuildingDisplayList buildingForChild( - aBuilder, aPageFrame, visibleRect, visibleRect); - - // This part is tricky. Because display items are positioned based on the - // frame tree, building a display list for the previous page yields - // display items that are outside of the current page bounds. - // To fix that, an additional reference frame offset is added, which - // shifts the display items down (block axis) as if the current and - // previous page were one long page in the same coordinate system. - const nsSize containerSize = aPageFrame->GetSize(); - LogicalPoint pageOffset(wm, aCurrentPageCF->GetOffsetTo(prevPageCF), - containerSize); - pageOffset.B(wm) -= offsetToCurrentPageBStart; - buildingForChild.SetAdditionalOffset( - pageOffset.GetPhysicalPoint(wm, containerSize)); - - aPageFrame->BuildDisplayListForChild(aBuilder, prevPageCF, aLists); + if (nsSharedPageData* pd = pageFrame->GetSharedPageData()) { + const auto* ppsInfo = pd->PagesPerSheetInfo(); + if (ppsInfo->mNumPages > 1) { + scale = pd->mPagesPerSheetScale; + gridOrigin = pd->mPagesPerSheetGridOrigin; + std::tie(rowIdx, colIdx) = GetRowAndColFromIdx(pageFrame->IndexOnSheet(), + pd->mPagesPerSheetNumCols); } } + + // Scale down the page based on the above-computed scale: + auto transform = gfx::Matrix4x4::Scaling(scale, scale, 1); + + // Draw the page at an offset, to get it in its pages-per-sheet "cell": + nsSize pageSize = pageFrame->PresContext()->GetPageSize(); + transform.PreTranslate( + NSAppUnitsToFloatPixels(colIdx * pageSize.width, aAppUnitsPerPixel), + NSAppUnitsToFloatPixels(rowIdx * pageSize.height, aAppUnitsPerPixel), 0); + + // Also add the grid origin as an offset (so that we're not drawing into the + // sheet's unwritable area). Note that this is a PostTranslate operation + // (vs. PreTranslate above), since gridOrigin is an offset on the sheet + // itself, whereas the offset above was in the scaled coordinate space of the + // pages. + return transform.PostTranslate( + NSAppUnitsToFloatPixels(gridOrigin.x, aAppUnitsPerPixel), + NSAppUnitsToFloatPixels(gridOrigin.y, aAppUnitsPerPixel), 0); +} + +nsIFrame::ComputeTransformFunction nsPageFrame::GetTransformGetter() const { + return ComputePagesPerSheetTransform; } void nsPageFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists) { - nsDisplayListCollection set(aBuilder); - nsPresContext* pc = PresContext(); - - nsIFrame* child = mFrames.FirstChild(); - float scale = pc->GetPageScale(); - nsRect clipRect(nsPoint(0, 0), child->GetSize()); - // Note: this computation matches how we compute maxSize.height - // in nsPageFrame::Reflow - nscoord expectedPageContentHeight = NSToCoordCeil(GetSize().height / scale); - if (clipRect.height > expectedPageContentHeight) { - // We're doing print-selection, with one long page-content frame. - // Clip to the appropriate page-content slice for the current page. - NS_ASSERTION(mPageNum > 0, "page num should be positive"); - // Note: The pageContentFrame's y-position has been set such that a zero - // y-value matches the top edge of the current page. So, to clip to the - // current page's content (in coordinates *relative* to the page content - // frame), we just negate its y-position and add the top margin. - // - // FIXME(emilio): Rather than the default margin, this should probably use - // mPageContentMargin? - clipRect.y = NSToCoordCeil( - (-child->GetRect().y + pc->GetDefaultPageMargin().top) / scale); - clipRect.height = expectedPageContentHeight; - NS_ASSERTION(clipRect.y < child->GetSize().height, - "Should be clipping to region inside the page content bounds"); - } - clipRect += aBuilder->ToReferenceFrame(child); - nsDisplayList content; - { - DisplayListClipState::AutoSaveRestore clipState(aBuilder); - - // Overwrite current clip, since we're going to wrap in a transform - // and the current clip is no longer meaningful. - clipState.Clear(); - clipState.ClipContainingBlockDescendants(clipRect); - - MOZ_ASSERT(child->IsPageContentFrame(), "unexpected child frame type"); - auto* currentPageCF = static_cast(child); - - if (StaticPrefs::layout_display_list_improve_fragmentation() && - mPageNum <= 255) { - nsDisplayListBuilder::AutoPageNumberSetter p(aBuilder, mPageNum); - BuildPreviousPageOverflow(aBuilder, this, currentPageCF, set); - } - - // Set the visible rect to scrollable overflow rect of the child - // nsPageContentFrame in parent nsPageFrame coordinate space. - const nsRect childOverflowRect = - child->ScrollableOverflowRectRelativeToSelf(); - const nsRect visibleRect = childOverflowRect + child->GetOffsetTo(this); - - nsDisplayListBuilder::AutoBuildingDisplayList buildingForChild( - aBuilder, this, visibleRect, visibleRect); - BuildDisplayListForChild(aBuilder, child, set); - - set.SerializeWithCorrectZOrder(&content, child->GetContent()); - - // We may need to paint out-of-flow frames whose placeholders are - // on other pages. Add those pages to our display list. Note that - // out-of-flow frames can't be placed after their placeholders so - // we don't have to process earlier pages. The display lists for - // these extra pages are pruned so that only display items for the - // page we currently care about (which we would have reached by - // following placeholders to their out-of-flows) end up on the list. - // - // Stacking context frames that wrap content on their normal page, - // as well as OOF content for this page will have their container - // items duplicated. We tell the builder to include our page number - // in the unique key for any extra page items so that they can be - // differentiated from the ones created on the normal page. - NS_ASSERTION(mPageNum <= 255, "Too many pages to handle OOFs"); - if (mPageNum <= 255) { - nsDisplayListBuilder::AutoPageNumberSetter p(aBuilder, mPageNum); - // The static_cast here is technically unnecessary, but it helps - // devirtualize the GetNextContinuation() function call if pcf has a - // concrete type (with an inherited `final` GetNextContinuation() impl). - auto* pageCF = currentPageCF; - while ((pageCF = static_cast( - pageCF->GetNextContinuation()))) { - nsRect childVisible = childOverflowRect + child->GetOffsetTo(pageCF); - - nsDisplayListBuilder::AutoBuildingDisplayList buildingForChild( - aBuilder, pageCF, childVisible, childVisible); - BuildDisplayListForExtraPage(aBuilder, this, pageCF, &content); - } - } - - // Invoke AutoBuildingDisplayList to ensure that the correct visibleRect - // is used to compute the visible rect if AddCanvasBackgroundColorItem - // creates a display item. - nsDisplayListBuilder::AutoBuildingDisplayList building( - aBuilder, child, childOverflowRect, childOverflowRect); - - // Add the canvas background color to the bottom of the list. This - // happens after we've built the list so that AddCanvasBackgroundColorItem - // can monkey with the contents if necessary. - nsRect backgroundRect = - nsRect(aBuilder->ToReferenceFrame(child), child->GetSize()); - - pc->GetPresShell()->AddCanvasBackgroundColorItem( - aBuilder, &content, child, backgroundRect, NS_RGBA(0, 0, 0, 0)); - } - - content.AppendNewToTop(aBuilder, child, &content, - content.GetBuildingRect(), - ::ComputePageTransform); - + nsDisplayListSet set(&content, &content, &content, &content, &content, + &content); + nsContainerFrame::BuildDisplayList(aBuilder, set); if (pc->IsRootPaginatedDocument()) { content.AppendNewToTop(aBuilder, this); @@ -743,7 +548,18 @@ void nsPageFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, } } - aLists.Content()->AppendToTop(&content); + // We'll be drawing the page with a (usually-trivial) + // N-pages-per-sheet transform applied, so our passed-in visible rect + // isn't meaningful while we're drawing our children, because the + // transform could scale down content whose coordinates are off-screen + // such that it ends up on-screen. So: we temporarily update the visible + // rect to be the child nsPageFrame's whole frame-rect (represented in + // this PrintedSheetFrame's coordinate space. + content.AppendNewToTop( + aBuilder, this, &content, content.GetBuildingRect(), + nsDisplayTransform::WithTransformGetter); + + set.MoveTo(aLists); } //------------------------------------------------------------------------------ diff --git a/layout/generic/nsPageFrame.h b/layout/generic/nsPageFrame.h index e29652483c0f..5f3c0c6fc512 100644 --- a/layout/generic/nsPageFrame.h +++ b/layout/generic/nsPageFrame.h @@ -65,6 +65,8 @@ class nsPageFrame final : public nsContainerFrame { mIndexOnSheet = aIndexOnSheet; } + ComputeTransformFunction GetTransformGetter() const override; + protected: explicit nsPageFrame(ComputedStyle* aStyle, nsPresContext* aPresContext); virtual ~nsPageFrame(); diff --git a/layout/generic/nsPageSequenceFrame.cpp b/layout/generic/nsPageSequenceFrame.cpp index bfec63607547..09bd79dbba1a 100644 --- a/layout/generic/nsPageSequenceFrame.cpp +++ b/layout/generic/nsPageSequenceFrame.cpp @@ -694,14 +694,19 @@ nsresult nsPageSequenceFrame::DoPageEnd() { return rv; } -gfx::Matrix4x4 ComputePageSequenceTransform(nsIFrame* aFrame, - float aAppUnitsPerPixel) { +static gfx::Matrix4x4 ComputePageSequenceTransform(const nsIFrame* aFrame, + float aAppUnitsPerPixel) { MOZ_ASSERT(aFrame->IsPageSequenceFrame()); float scale = - static_cast(aFrame)->GetPrintPreviewScale(); + static_cast(aFrame)->GetPrintPreviewScale(); return gfx::Matrix4x4::Scaling(scale, scale, 1); } +nsIFrame::ComputeTransformFunction nsPageSequenceFrame::GetTransformGetter() + const { + return ComputePageSequenceTransform; +} + void nsPageSequenceFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists) { aBuilder->SetDisablePartialUpdates(true); @@ -731,9 +736,9 @@ void nsPageSequenceFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, } } - content.AppendNewToTop(aBuilder, this, &content, - content.GetBuildingRect(), - ::ComputePageSequenceTransform); + content.AppendNewToTop( + aBuilder, this, &content, content.GetBuildingRect(), + nsDisplayTransform::WithTransformGetter); aLists.Content()->AppendToTop(&content); } diff --git a/layout/generic/nsPageSequenceFrame.h b/layout/generic/nsPageSequenceFrame.h index 66c6483c32a7..dd7b4d1fefcf 100644 --- a/layout/generic/nsPageSequenceFrame.h +++ b/layout/generic/nsPageSequenceFrame.h @@ -149,7 +149,7 @@ class nsPageSequenceFrame final : public nsContainerFrame { // user's settings bool HonorPrintBackgroundSettings() const override { return false; } - bool HasTransformGetter() const override { return true; } + ComputeTransformFunction GetTransformGetter() const override; #ifdef DEBUG_FRAME_DUMP nsresult GetFrameName(nsAString& aResult) const override; diff --git a/layout/painting/nsDisplayList.cpp b/layout/painting/nsDisplayList.cpp index 3ab386d3b0af..fd4f8d4d7809 100644 --- a/layout/painting/nsDisplayList.cpp +++ b/layout/painting/nsDisplayList.cpp @@ -7312,12 +7312,12 @@ nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder, const nsRect& aChildrenBuildingRect) : nsPaintedDisplayItem(aBuilder, aFrame), mTransform(Some(Matrix4x4())), - mTransformGetter(nullptr), mAnimatedGeometryRootForChildren(mAnimatedGeometryRoot), mAnimatedGeometryRootForScrollMetadata(mAnimatedGeometryRoot), mChildrenBuildingRect(aChildrenBuildingRect), mPrerenderDecision(PrerenderDecision::No), - mIsTransformSeparator(true) { + mIsTransformSeparator(true), + mHasTransformGetter(false) { MOZ_COUNT_CTOR(nsDisplayTransform); MOZ_ASSERT(aFrame, "Must have a frame!"); Init(aBuilder, aList); @@ -7328,12 +7328,12 @@ nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder, const nsRect& aChildrenBuildingRect, PrerenderDecision aPrerenderDecision) : nsPaintedDisplayItem(aBuilder, aFrame), - mTransformGetter(nullptr), mAnimatedGeometryRootForChildren(mAnimatedGeometryRoot), mAnimatedGeometryRootForScrollMetadata(mAnimatedGeometryRoot), mChildrenBuildingRect(aChildrenBuildingRect), mPrerenderDecision(aPrerenderDecision), - mIsTransformSeparator(false) { + mIsTransformSeparator(false), + mHasTransformGetter(false) { MOZ_COUNT_CTOR(nsDisplayTransform); MOZ_ASSERT(aFrame, "Must have a frame!"); SetReferenceFrameToAncestor(aBuilder); @@ -7343,16 +7343,17 @@ nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder, nsDisplayTransform::nsDisplayTransform( nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList, const nsRect& aChildrenBuildingRect, - ComputeTransformFunction aTransformGetter) + decltype(WithTransformGetter)) : nsPaintedDisplayItem(aBuilder, aFrame), - mTransformGetter(aTransformGetter), mAnimatedGeometryRootForChildren(mAnimatedGeometryRoot), mAnimatedGeometryRootForScrollMetadata(mAnimatedGeometryRoot), mChildrenBuildingRect(aChildrenBuildingRect), mPrerenderDecision(PrerenderDecision::No), - mIsTransformSeparator(false) { + mIsTransformSeparator(false), + mHasTransformGetter(true) { MOZ_COUNT_CTOR(nsDisplayTransform); MOZ_ASSERT(aFrame, "Must have a frame!"); + MOZ_ASSERT(aFrame->GetTransformGetter()); Init(aBuilder, aList); } @@ -7880,8 +7881,8 @@ const Matrix4x4Flagged& nsDisplayTransform::GetTransform() const { float scale = mFrame->PresContext()->AppUnitsPerDevPixel(); - if (mTransformGetter) { - mTransform.emplace(mTransformGetter(mFrame, scale)); + if (mHasTransformGetter) { + mTransform.emplace((mFrame->GetTransformGetter())(mFrame, scale)); Point3D newOrigin = Point3D(NSAppUnitsToFloatPixels(mToReferenceFrame.x, scale), NSAppUnitsToFloatPixels(mToReferenceFrame.y, scale), 0.0f); @@ -7916,8 +7917,8 @@ const Matrix4x4Flagged& nsDisplayTransform::GetInverseTransform() const { Matrix4x4 nsDisplayTransform::GetTransformForRendering( LayoutDevicePoint* aOutOrigin) const { - if (!mFrame->HasPerspective() || mTransformGetter || mIsTransformSeparator) { - if (!mTransformGetter && !mIsTransformSeparator && aOutOrigin) { + if (!mFrame->HasPerspective() || mHasTransformGetter || mIsTransformSeparator) { + if (!mHasTransformGetter && !mIsTransformSeparator && aOutOrigin) { // If aOutOrigin is provided, put the offset to origin into it, because // we need to keep it separate for webrender. The combination of // *aOutOrigin and the returned matrix here should always be equivalent @@ -7934,7 +7935,7 @@ Matrix4x4 nsDisplayTransform::GetTransformForRendering( } return GetTransform().GetMatrix(); } - MOZ_ASSERT(!mTransformGetter); + MOZ_ASSERT(!mHasTransformGetter); float scale = mFrame->PresContext()->AppUnitsPerDevPixel(); // Don't include perspective transform, or the offset to origin, since @@ -7950,7 +7951,6 @@ const Matrix4x4& nsDisplayTransform::GetAccumulatedPreserved3DTransform( return GetTransform().GetMatrix(); } - // XXX: should go back to fix mTransformGetter. if (!mTransformPreserves3D) { const nsIFrame* establisher; // Establisher of the 3D rendering context. for (establisher = mFrame; diff --git a/layout/painting/nsDisplayList.h b/layout/painting/nsDisplayList.h index 0b38159b5b3d..86bb0b3d32ce 100644 --- a/layout/painting/nsDisplayList.h +++ b/layout/painting/nsDisplayList.h @@ -6655,15 +6655,9 @@ class nsDisplayTransform : public nsPaintedDisplayItem { public: enum class PrerenderDecision : uint8_t { No, Full, Partial }; - /** - * Returns a matrix (in pixels) for the current frame. The matrix should be - * relative to the current frame's coordinate space. - * - * @param aFrame The frame to compute the transform for. - * @param aAppUnitsPerPixel The number of app units per graphics unit. - */ - typedef Matrix4x4 (*ComputeTransformFunction)(nsIFrame* aFrame, - float aAppUnitsPerPixel); + enum { + WithTransformGetter, + }; /* Constructor accepts a display list, empties it, and wraps it up. It also * ferries the underlying frame to the nsDisplayItem constructor. @@ -6677,7 +6671,7 @@ class nsDisplayTransform : public nsPaintedDisplayItem { nsDisplayTransform(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList, const nsRect& aChildrenBuildingRect, - ComputeTransformFunction aTransformGetter); + decltype(WithTransformGetter)); MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayTransform) @@ -6774,7 +6768,7 @@ class nsDisplayTransform : public nsPaintedDisplayItem { // If we were created using a transform-getter, then we don't // belong to a transformed frame, and aren't a reference frame // for our children. - if (!mTransformGetter) { + if (!mHasTransformGetter) { return mFrame; } return nsPaintedDisplayItem::ReferenceFrameForChildren(); @@ -7009,7 +7003,6 @@ class nsDisplayTransform : public nsPaintedDisplayItem { mutable mozilla::Maybe mInverseTransform; // Accumulated transform of ancestors on the preserves-3d chain. mozilla::UniquePtr mTransformPreserves3D; - ComputeTransformFunction mTransformGetter; RefPtr mAnimatedGeometryRootForChildren; RefPtr mAnimatedGeometryRootForScrollMetadata; nsRect mChildrenBuildingRect; @@ -7030,6 +7023,8 @@ class nsDisplayTransform : public nsPaintedDisplayItem { bool mIsTransformSeparator : 1; // True if this nsDisplayTransform should get flattened bool mShouldFlatten : 1; + // True if we have a transform getter. + bool mHasTransformGetter : 1; }; /* A display item that applies a perspective transformation to a single