michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsSimplePageSequenceFrame.h" michael@0: michael@0: #include "nsCOMPtr.h" michael@0: #include "nsPresContext.h" michael@0: #include "gfxContext.h" michael@0: #include "nsRenderingContext.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "nsIPresShell.h" michael@0: #include "nsIPrintSettings.h" michael@0: #include "nsPageFrame.h" michael@0: #include "nsSubDocumentFrame.h" michael@0: #include "nsRegion.h" michael@0: #include "nsCSSFrameConstructor.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsDisplayList.h" michael@0: #include "nsHTMLCanvasFrame.h" michael@0: #include "mozilla/dom/HTMLCanvasElement.h" michael@0: #include "nsICanvasRenderingContextInternal.h" michael@0: #include "nsIDateTimeFormat.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: #include michael@0: michael@0: // DateTime Includes michael@0: #include "nsDateTimeFormatCID.h" michael@0: michael@0: #define OFFSET_NOT_SET -1 michael@0: michael@0: // Print Options michael@0: #include "nsIPrintOptions.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: michael@0: static const char sPrintOptionsContractID[] = "@mozilla.org/gfx/printsettings-service;1"; michael@0: michael@0: // michael@0: michael@0: #include "prlog.h" michael@0: #ifdef PR_LOGGING michael@0: PRLogModuleInfo * michael@0: GetLayoutPrintingLog() michael@0: { michael@0: static PRLogModuleInfo *sLog; michael@0: if (!sLog) michael@0: sLog = PR_NewLogModule("printing-layout"); michael@0: return sLog; michael@0: } michael@0: #define PR_PL(_p1) PR_LOG(GetLayoutPrintingLog(), PR_LOG_DEBUG, _p1) michael@0: #else michael@0: #define PR_PL(_p1) michael@0: #endif michael@0: michael@0: nsIFrame* michael@0: NS_NewSimplePageSequenceFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) michael@0: { michael@0: return new (aPresShell) nsSimplePageSequenceFrame(aContext); michael@0: } michael@0: michael@0: NS_IMPL_FRAMEARENA_HELPERS(nsSimplePageSequenceFrame) michael@0: michael@0: nsSimplePageSequenceFrame::nsSimplePageSequenceFrame(nsStyleContext* aContext) : michael@0: nsContainerFrame(aContext), michael@0: mTotalPages(-1), michael@0: mSelectionHeight(-1), michael@0: mYSelOffset(0), michael@0: mCalledBeginPage(false), michael@0: mCurrentCanvasListSetup(false) michael@0: { michael@0: nscoord halfInch = PresContext()->CSSTwipsToAppUnits(NS_INCHES_TO_TWIPS(0.5)); michael@0: mMargin.SizeTo(halfInch, halfInch, halfInch, halfInch); michael@0: michael@0: // XXX Unsafe to assume successful allocation michael@0: mPageData = new nsSharedPageData(); michael@0: mPageData->mHeadFootFont = michael@0: *PresContext()->GetDefaultFont(kGenericFont_serif, michael@0: aContext->StyleFont()->mLanguage); michael@0: mPageData->mHeadFootFont.size = nsPresContext::CSSPointsToAppUnits(10); michael@0: michael@0: nsresult rv; michael@0: mPageData->mPrintOptions = do_GetService(sPrintOptionsContractID, &rv); michael@0: michael@0: // Doing this here so we only have to go get these formats once michael@0: SetPageNumberFormat("pagenumber", "%1$d", true); michael@0: SetPageNumberFormat("pageofpages", "%1$d of %2$d", false); michael@0: } michael@0: michael@0: nsSimplePageSequenceFrame::~nsSimplePageSequenceFrame() michael@0: { michael@0: delete mPageData; michael@0: ResetPrintCanvasList(); michael@0: } michael@0: michael@0: NS_QUERYFRAME_HEAD(nsSimplePageSequenceFrame) michael@0: NS_QUERYFRAME_ENTRY(nsIPageSequenceFrame) michael@0: NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame) michael@0: michael@0: //---------------------------------------------------------------------- michael@0: michael@0: void michael@0: nsSimplePageSequenceFrame::SetDesiredSize(nsHTMLReflowMetrics& aDesiredSize, michael@0: const nsHTMLReflowState& aReflowState, michael@0: nscoord aWidth, michael@0: nscoord aHeight) michael@0: { michael@0: // Aim to fill the whole size of the document, not only so we michael@0: // can act as a background in print preview but also handle overflow michael@0: // in child page frames correctly. michael@0: // Use availableWidth so we don't cause a needless horizontal scrollbar. michael@0: aDesiredSize.Width() = std::max(aReflowState.AvailableWidth(), michael@0: nscoord(aWidth * PresContext()->GetPrintPreviewScale())); michael@0: aDesiredSize.Height() = std::max(aReflowState.ComputedHeight(), michael@0: nscoord(aHeight * PresContext()->GetPrintPreviewScale())); michael@0: } michael@0: michael@0: nsresult michael@0: nsSimplePageSequenceFrame::Reflow(nsPresContext* aPresContext, michael@0: nsHTMLReflowMetrics& aDesiredSize, michael@0: const nsHTMLReflowState& aReflowState, michael@0: nsReflowStatus& aStatus) michael@0: { michael@0: NS_PRECONDITION(aPresContext->IsRootPaginatedDocument(), michael@0: "A Page Sequence is only for real pages"); michael@0: DO_GLOBAL_REFLOW_COUNT("nsSimplePageSequenceFrame"); michael@0: DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); michael@0: NS_FRAME_TRACE_REFLOW_IN("nsSimplePageSequenceFrame::Reflow"); michael@0: michael@0: aStatus = NS_FRAME_COMPLETE; // we're always complete michael@0: michael@0: // Don't do incremental reflow until we've taught tables how to do michael@0: // it right in paginated mode. michael@0: if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) { michael@0: // Return our desired size michael@0: SetDesiredSize(aDesiredSize, aReflowState, mSize.width, mSize.height); michael@0: aDesiredSize.SetOverflowAreasToDesiredBounds(); michael@0: FinishAndStoreOverflow(&aDesiredSize); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // See if we can get a Print Settings from the Context michael@0: if (!mPageData->mPrintSettings && michael@0: aPresContext->Medium() == nsGkAtoms::print) { michael@0: mPageData->mPrintSettings = aPresContext->GetPrintSettings(); michael@0: } michael@0: michael@0: // now get out margins & edges michael@0: if (mPageData->mPrintSettings) { michael@0: nsIntMargin unwriteableTwips; michael@0: mPageData->mPrintSettings->GetUnwriteableMarginInTwips(unwriteableTwips); michael@0: NS_ASSERTION(unwriteableTwips.left >= 0 && unwriteableTwips.top >= 0 && michael@0: unwriteableTwips.right >= 0 && unwriteableTwips.bottom >= 0, michael@0: "Unwriteable twips should be non-negative"); michael@0: michael@0: nsIntMargin marginTwips; michael@0: mPageData->mPrintSettings->GetMarginInTwips(marginTwips); michael@0: mMargin = aPresContext->CSSTwipsToAppUnits(marginTwips + unwriteableTwips); michael@0: michael@0: int16_t printType; michael@0: mPageData->mPrintSettings->GetPrintRange(&printType); michael@0: mPrintRangeType = printType; michael@0: michael@0: nsIntMargin edgeTwips; michael@0: mPageData->mPrintSettings->GetEdgeInTwips(edgeTwips); michael@0: michael@0: // sanity check the values. three inches are sometimes needed michael@0: int32_t inchInTwips = NS_INCHES_TO_INT_TWIPS(3.0); michael@0: edgeTwips.top = clamped(edgeTwips.top, 0, inchInTwips); michael@0: edgeTwips.bottom = clamped(edgeTwips.bottom, 0, inchInTwips); michael@0: edgeTwips.left = clamped(edgeTwips.left, 0, inchInTwips); michael@0: edgeTwips.right = clamped(edgeTwips.right, 0, inchInTwips); michael@0: michael@0: mPageData->mEdgePaperMargin = michael@0: aPresContext->CSSTwipsToAppUnits(edgeTwips + unwriteableTwips); michael@0: } michael@0: michael@0: // *** Special Override *** michael@0: // If this is a sub-sdoc (meaning it doesn't take the whole page) michael@0: // and if this Document is in the upper left hand corner michael@0: // we need to suppress the top margin or it will reflow too small michael@0: michael@0: nsSize pageSize = aPresContext->GetPageSize(); michael@0: michael@0: mPageData->mReflowSize = pageSize; michael@0: // If we're printing a selection, we need to reflow with michael@0: // unconstrained height, to make sure we'll get to the selection michael@0: // even if it's beyond the first page of content. michael@0: if (nsIPrintSettings::kRangeSelection == mPrintRangeType) { michael@0: mPageData->mReflowSize.height = NS_UNCONSTRAINEDSIZE; michael@0: } michael@0: mPageData->mReflowMargin = mMargin; michael@0: michael@0: // We use the CSS "margin" property on the -moz-page pseudoelement michael@0: // to determine the space between each page in print preview. michael@0: // Keep a running y-offset for each page. michael@0: nscoord y = 0; michael@0: nscoord maxXMost = 0; michael@0: michael@0: // Tile the pages vertically michael@0: nsHTMLReflowMetrics kidSize(aReflowState); michael@0: for (nsIFrame* kidFrame = mFrames.FirstChild(); nullptr != kidFrame; ) { michael@0: // Set the shared data into the page frame before reflow michael@0: nsPageFrame * pf = static_cast(kidFrame); michael@0: pf->SetSharedPageData(mPageData); michael@0: michael@0: // Reflow the page michael@0: nsHTMLReflowState kidReflowState(aPresContext, aReflowState, kidFrame, michael@0: pageSize); michael@0: nsReflowStatus status; michael@0: michael@0: kidReflowState.SetComputedWidth(kidReflowState.AvailableWidth()); michael@0: //kidReflowState.SetComputedHeight(kidReflowState.AvailableHeight()); michael@0: PR_PL(("AV W: %d H: %d\n", kidReflowState.AvailableWidth(), kidReflowState.AvailableHeight())); michael@0: michael@0: nsMargin pageCSSMargin = kidReflowState.ComputedPhysicalMargin(); michael@0: y += pageCSSMargin.top; michael@0: const nscoord x = pageCSSMargin.left; michael@0: michael@0: // Place and size the page. If the page is narrower than our michael@0: // max width then center it horizontally michael@0: ReflowChild(kidFrame, aPresContext, kidSize, kidReflowState, x, y, 0, status); michael@0: michael@0: FinishReflowChild(kidFrame, aPresContext, kidSize, nullptr, x, y, 0); michael@0: y += kidSize.Height(); michael@0: y += pageCSSMargin.bottom; michael@0: michael@0: maxXMost = std::max(maxXMost, x + kidSize.Width() + pageCSSMargin.right); michael@0: michael@0: // Is the page complete? michael@0: nsIFrame* kidNextInFlow = kidFrame->GetNextInFlow(); michael@0: michael@0: if (NS_FRAME_IS_FULLY_COMPLETE(status)) { michael@0: NS_ASSERTION(!kidNextInFlow, "bad child flow list"); michael@0: } else if (!kidNextInFlow) { michael@0: // The page isn't complete and it doesn't have a next-in-flow, so michael@0: // create a continuing page. michael@0: nsIFrame* continuingPage = aPresContext->PresShell()->FrameConstructor()-> michael@0: CreateContinuingFrame(aPresContext, kidFrame, this); michael@0: michael@0: // Add it to our child list michael@0: mFrames.InsertFrame(nullptr, kidFrame, continuingPage); michael@0: } michael@0: michael@0: // Get the next page michael@0: kidFrame = kidFrame->GetNextSibling(); michael@0: } michael@0: michael@0: // Get Total Page Count michael@0: nsIFrame* page; michael@0: int32_t pageTot = 0; michael@0: for (page = mFrames.FirstChild(); page; page = page->GetNextSibling()) { michael@0: pageTot++; michael@0: } michael@0: michael@0: // Set Page Number Info michael@0: int32_t pageNum = 1; michael@0: for (page = mFrames.FirstChild(); page; page = page->GetNextSibling()) { michael@0: nsPageFrame * pf = static_cast(page); michael@0: if (pf != nullptr) { michael@0: pf->SetPageNumInfo(pageNum, pageTot); michael@0: } michael@0: pageNum++; michael@0: } michael@0: michael@0: // Create current Date/Time String michael@0: if (!mDateFormatter) michael@0: mDateFormatter = do_CreateInstance(NS_DATETIMEFORMAT_CONTRACTID); michael@0: michael@0: NS_ENSURE_TRUE(mDateFormatter, NS_ERROR_FAILURE); michael@0: michael@0: nsAutoString formattedDateString; michael@0: time_t ltime; michael@0: time( <ime ); michael@0: if (NS_SUCCEEDED(mDateFormatter->FormatTime(nullptr /* nsILocale* locale */, michael@0: kDateFormatShort, michael@0: kTimeFormatNoSeconds, michael@0: ltime, michael@0: formattedDateString))) { michael@0: SetDateTimeStr(formattedDateString); michael@0: } michael@0: michael@0: // Return our desired size michael@0: // Adjust the reflow size by PrintPreviewScale so the scrollbars end up the michael@0: // correct size michael@0: SetDesiredSize(aDesiredSize, aReflowState, maxXMost, y); michael@0: michael@0: aDesiredSize.SetOverflowAreasToDesiredBounds(); michael@0: FinishAndStoreOverflow(&aDesiredSize); michael@0: michael@0: // cache the size so we can set the desired size michael@0: // for the other reflows that happen michael@0: mSize.width = maxXMost; michael@0: mSize.height = y; michael@0: michael@0: NS_FRAME_TRACE_REFLOW_OUT("nsSimplePageSequeceFrame::Reflow", aStatus); michael@0: NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); michael@0: return NS_OK; michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: michael@0: #ifdef DEBUG_FRAME_DUMP michael@0: nsresult michael@0: nsSimplePageSequenceFrame::GetFrameName(nsAString& aResult) const michael@0: { michael@0: return MakeFrameName(NS_LITERAL_STRING("SimplePageSequence"), aResult); michael@0: } michael@0: #endif michael@0: michael@0: //==================================================================== michael@0: //== Asynch Printing michael@0: //==================================================================== michael@0: NS_IMETHODIMP michael@0: nsSimplePageSequenceFrame::GetCurrentPageNum(int32_t* aPageNum) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aPageNum); michael@0: michael@0: *aPageNum = mPageNum; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSimplePageSequenceFrame::GetNumPages(int32_t* aNumPages) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aNumPages); michael@0: michael@0: *aNumPages = mTotalPages; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSimplePageSequenceFrame::IsDoingPrintRange(bool* aDoing) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aDoing); michael@0: michael@0: *aDoing = mDoingPageRange; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSimplePageSequenceFrame::GetPrintRange(int32_t* aFromPage, int32_t* aToPage) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aFromPage); michael@0: NS_ENSURE_ARG_POINTER(aToPage); michael@0: michael@0: *aFromPage = mFromPageNum; michael@0: *aToPage = mToPageNum; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Helper Function michael@0: void michael@0: nsSimplePageSequenceFrame::SetPageNumberFormat(const char* aPropName, const char* aDefPropVal, bool aPageNumOnly) michael@0: { michael@0: // Doing this here so we only have to go get these formats once michael@0: nsXPIDLString pageNumberFormat; michael@0: // Now go get the Localized Page Formating String michael@0: nsresult rv = michael@0: nsContentUtils::GetLocalizedString(nsContentUtils::ePRINTING_PROPERTIES, michael@0: aPropName, pageNumberFormat); michael@0: if (NS_FAILED(rv)) { // back stop formatting michael@0: pageNumberFormat.AssignASCII(aDefPropVal); michael@0: } michael@0: michael@0: SetPageNumberFormat(pageNumberFormat, aPageNumOnly); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSimplePageSequenceFrame::StartPrint(nsPresContext* aPresContext, michael@0: nsIPrintSettings* aPrintSettings, michael@0: const nsAString& aDocTitle, michael@0: const nsAString& aDocURL) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aPresContext); michael@0: NS_ENSURE_ARG_POINTER(aPrintSettings); michael@0: michael@0: if (!mPageData->mPrintSettings) { michael@0: mPageData->mPrintSettings = aPrintSettings; michael@0: } michael@0: michael@0: if (!aDocTitle.IsEmpty()) { michael@0: mPageData->mDocTitle = aDocTitle; michael@0: } michael@0: if (!aDocURL.IsEmpty()) { michael@0: mPageData->mDocURL = aDocURL; michael@0: } michael@0: michael@0: aPrintSettings->GetStartPageRange(&mFromPageNum); michael@0: aPrintSettings->GetEndPageRange(&mToPageNum); michael@0: aPrintSettings->GetPageRanges(mPageRanges); michael@0: michael@0: mDoingPageRange = nsIPrintSettings::kRangeSpecifiedPageRange == mPrintRangeType || michael@0: nsIPrintSettings::kRangeSelection == mPrintRangeType; michael@0: michael@0: // If printing a range of pages make sure at least the starting page michael@0: // number is valid michael@0: int32_t totalPages = mFrames.GetLength(); michael@0: michael@0: if (mDoingPageRange) { michael@0: if (mFromPageNum > totalPages) { michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: } michael@0: michael@0: // Begin printing of the document michael@0: nsresult rv = NS_OK; michael@0: michael@0: // Determine if we are rendering only the selection michael@0: aPresContext->SetIsRenderingOnlySelection(nsIPrintSettings::kRangeSelection == mPrintRangeType); michael@0: michael@0: michael@0: if (mDoingPageRange) { michael@0: // XXX because of the hack for making the selection all print on one page michael@0: // we must make sure that the page is sized correctly before printing. michael@0: nscoord height = aPresContext->GetPageSize().height; michael@0: michael@0: int32_t pageNum = 1; michael@0: nscoord y = 0;//mMargin.top; michael@0: michael@0: for (nsIFrame* page = mFrames.FirstChild(); page; michael@0: page = page->GetNextSibling()) { michael@0: if (pageNum >= mFromPageNum && pageNum <= mToPageNum) { michael@0: nsRect rect = page->GetRect(); michael@0: rect.y = y; michael@0: rect.height = height; michael@0: page->SetRect(rect); michael@0: y += rect.height + mMargin.top + mMargin.bottom; michael@0: } michael@0: pageNum++; michael@0: } michael@0: michael@0: // adjust total number of pages michael@0: if (nsIPrintSettings::kRangeSelection != mPrintRangeType) { michael@0: totalPages = pageNum - 1; michael@0: } michael@0: } michael@0: michael@0: mPageNum = 1; michael@0: michael@0: if (mTotalPages == -1) { michael@0: mTotalPages = totalPages; michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: void michael@0: GetPrintCanvasElementsInFrame(nsIFrame* aFrame, nsTArray >* aArr) michael@0: { michael@0: if (!aFrame) { michael@0: return; michael@0: } michael@0: for (nsIFrame::ChildListIterator childLists(aFrame); michael@0: !childLists.IsDone(); childLists.Next()) { michael@0: michael@0: nsFrameList children = childLists.CurrentList(); michael@0: for (nsFrameList::Enumerator e(children); !e.AtEnd(); e.Next()) { michael@0: nsIFrame* child = e.get(); michael@0: michael@0: // Check if child is a nsHTMLCanvasFrame. michael@0: nsHTMLCanvasFrame* canvasFrame = do_QueryFrame(child); michael@0: michael@0: // If there is a canvasFrame, try to get actual canvas element. michael@0: if (canvasFrame) { michael@0: HTMLCanvasElement* canvas = michael@0: HTMLCanvasElement::FromContentOrNull(canvasFrame->GetContent()); michael@0: if (canvas && canvas->GetMozPrintCallback()) { michael@0: aArr->AppendElement(canvas); michael@0: continue; michael@0: } michael@0: } michael@0: michael@0: if (!child->GetFirstPrincipalChild()) { michael@0: nsSubDocumentFrame* subdocumentFrame = do_QueryFrame(child); michael@0: if (subdocumentFrame) { michael@0: // Descend into the subdocument michael@0: nsIFrame* root = subdocumentFrame->GetSubdocumentRootFrame(); michael@0: child = root; michael@0: } michael@0: } michael@0: // The current child is not a nsHTMLCanvasFrame OR it is but there is michael@0: // no HTMLCanvasElement on it. Check if children of `child` might michael@0: // contain a HTMLCanvasElement. michael@0: GetPrintCanvasElementsInFrame(child, aArr); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsSimplePageSequenceFrame::DetermineWhetherToPrintPage() michael@0: { michael@0: // See whether we should print this page michael@0: mPrintThisPage = true; michael@0: bool printEvenPages, printOddPages; michael@0: mPageData->mPrintSettings->GetPrintOptions(nsIPrintSettings::kPrintEvenPages, &printEvenPages); michael@0: mPageData->mPrintSettings->GetPrintOptions(nsIPrintSettings::kPrintOddPages, &printOddPages); michael@0: michael@0: // If printing a range of pages check whether the page number is in the michael@0: // range of pages to print michael@0: if (mDoingPageRange) { michael@0: if (mPageNum < mFromPageNum) { michael@0: mPrintThisPage = false; michael@0: } else if (mPageNum > mToPageNum) { michael@0: mPageNum++; michael@0: mPrintThisPage = false; michael@0: return; michael@0: } else { michael@0: int32_t length = mPageRanges.Length(); michael@0: michael@0: // Page ranges are pairs (start, end) michael@0: if (length && (length % 2 == 0)) { michael@0: mPrintThisPage = false; michael@0: michael@0: int32_t i; michael@0: for (i = 0; i < length; i += 2) { michael@0: if (mPageRanges[i] <= mPageNum && mPageNum <= mPageRanges[i+1]) { michael@0: mPrintThisPage = true; michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Check for printing of odd and even pages michael@0: if (mPageNum & 0x1) { michael@0: if (!printOddPages) { michael@0: mPrintThisPage = false; // don't print odd numbered page michael@0: } michael@0: } else { michael@0: if (!printEvenPages) { michael@0: mPrintThisPage = false; // don't print even numbered page michael@0: } michael@0: } michael@0: michael@0: if (nsIPrintSettings::kRangeSelection == mPrintRangeType) { michael@0: mPrintThisPage = true; michael@0: } michael@0: } michael@0: michael@0: nsIFrame* michael@0: nsSimplePageSequenceFrame::GetCurrentPageFrame() michael@0: { michael@0: int32_t i = 1; michael@0: for (nsFrameList::Enumerator childFrames(mFrames); !childFrames.AtEnd(); michael@0: childFrames.Next()) { michael@0: if (i == mPageNum) { michael@0: return childFrames.get(); michael@0: } michael@0: ++i; michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSimplePageSequenceFrame::PrePrintNextPage(nsITimerCallback* aCallback, bool* aDone) michael@0: { michael@0: nsIFrame* currentPage = GetCurrentPageFrame(); michael@0: if (!currentPage) { michael@0: *aDone = true; michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: DetermineWhetherToPrintPage(); michael@0: // Nothing to do if the current page doesn't get printed OR rendering to michael@0: // preview. For preview, the `CallPrintCallback` is called from within the michael@0: // HTMLCanvasElement::HandlePrintCallback. michael@0: if (!mPrintThisPage || !PresContext()->IsRootPaginatedDocument()) { michael@0: *aDone = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // If the canvasList is null, then generate it and start the render michael@0: // process for all the canvas. michael@0: if (!mCurrentCanvasListSetup) { michael@0: mCurrentCanvasListSetup = true; michael@0: GetPrintCanvasElementsInFrame(currentPage, &mCurrentCanvasList); michael@0: michael@0: if (mCurrentCanvasList.Length() != 0) { michael@0: nsresult rv = NS_OK; michael@0: michael@0: // Begin printing of the document michael@0: nsDeviceContext *dc = PresContext()->DeviceContext(); michael@0: PR_PL(("\n")); michael@0: PR_PL(("***************** BeginPage *****************\n")); michael@0: rv = dc->BeginPage(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: mCalledBeginPage = true; michael@0: michael@0: nsRefPtr renderingContext = michael@0: dc->CreateRenderingContext(); michael@0: michael@0: nsRefPtr renderingSurface = michael@0: renderingContext->ThebesContext()->CurrentSurface(); michael@0: NS_ENSURE_TRUE(renderingSurface, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: for (int32_t i = mCurrentCanvasList.Length() - 1; i >= 0 ; i--) { michael@0: HTMLCanvasElement* canvas = mCurrentCanvasList[i]; michael@0: nsIntSize size = canvas->GetSize(); michael@0: michael@0: nsRefPtr printSurface = renderingSurface-> michael@0: CreateSimilarSurface( michael@0: gfxContentType::COLOR_ALPHA, michael@0: size michael@0: ); michael@0: michael@0: if (!printSurface) { michael@0: continue; michael@0: } michael@0: michael@0: nsICanvasRenderingContextInternal* ctx = canvas->GetContextAtIndex(0); michael@0: michael@0: if (!ctx) { michael@0: continue; michael@0: } michael@0: michael@0: // Initialize the context with the new printSurface. michael@0: ctx->InitializeWithSurface(nullptr, printSurface, size.width, size.height); michael@0: michael@0: // Start the rendering process. michael@0: nsWeakFrame weakFrame = this; michael@0: canvas->DispatchPrintCallback(aCallback); michael@0: NS_ENSURE_STATE(weakFrame.IsAlive()); michael@0: } michael@0: } michael@0: } michael@0: uint32_t doneCounter = 0; michael@0: for (int32_t i = mCurrentCanvasList.Length() - 1; i >= 0 ; i--) { michael@0: HTMLCanvasElement* canvas = mCurrentCanvasList[i]; michael@0: michael@0: if (canvas->IsPrintCallbackDone()) { michael@0: doneCounter++; michael@0: } michael@0: } michael@0: // If all canvas have finished rendering, return true, otherwise false. michael@0: *aDone = doneCounter == mCurrentCanvasList.Length(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSimplePageSequenceFrame::ResetPrintCanvasList() michael@0: { michael@0: for (int32_t i = mCurrentCanvasList.Length() - 1; i >= 0 ; i--) { michael@0: HTMLCanvasElement* canvas = mCurrentCanvasList[i]; michael@0: canvas->ResetPrintCallback(); michael@0: } michael@0: michael@0: mCurrentCanvasList.Clear(); michael@0: mCurrentCanvasListSetup = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSimplePageSequenceFrame::PrintNextPage() michael@0: { michael@0: // Print each specified page michael@0: // pageNum keeps track of the current page and what pages are printing michael@0: // michael@0: // printedPageNum keeps track of the current page number to be printed michael@0: // Note: When print al the pages or a page range the printed page shows the michael@0: // actual page number, when printing selection it prints the page number starting michael@0: // with the first page of the selection. For example if the user has a michael@0: // selection that starts on page 2 and ends on page 3, the page numbers when michael@0: // print are 1 and then two (which is different than printing a page range, where michael@0: // the page numbers would have been 2 and then 3) michael@0: michael@0: nsIFrame* currentPage = GetCurrentPageFrame(); michael@0: if (!currentPage) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: nsresult rv = NS_OK; michael@0: michael@0: DetermineWhetherToPrintPage(); michael@0: michael@0: if (mPrintThisPage) { michael@0: // Begin printing of the document michael@0: nsDeviceContext* dc = PresContext()->DeviceContext(); michael@0: michael@0: // XXX This is temporary fix for printing more than one page of a selection michael@0: // This does a poor man's "dump" pagination (see Bug 89353) michael@0: // It has laid out as one long page and now we are just moving or view up/down michael@0: // one page at a time and printing the contents of what is exposed by the rect. michael@0: // currently this does not work for IFrames michael@0: // I will soon improve this to work with IFrames michael@0: bool continuePrinting = true; michael@0: nscoord width, height; michael@0: width = PresContext()->GetPageSize().width; michael@0: height = PresContext()->GetPageSize().height; michael@0: height -= mMargin.top + mMargin.bottom; michael@0: width -= mMargin.left + mMargin.right; michael@0: nscoord selectionY = height; michael@0: nsIFrame* conFrame = currentPage->GetFirstPrincipalChild(); michael@0: if (mSelectionHeight >= 0) { michael@0: conFrame->SetPosition(conFrame->GetPosition() + nsPoint(0, -mYSelOffset)); michael@0: nsContainerFrame::PositionChildViews(conFrame); michael@0: } michael@0: michael@0: // cast the frame to be a page frame michael@0: nsPageFrame * pf = static_cast(currentPage); michael@0: pf->SetPageNumInfo(mPageNum, mTotalPages); michael@0: pf->SetSharedPageData(mPageData); michael@0: michael@0: int32_t printedPageNum = 1; michael@0: while (continuePrinting) { michael@0: if (PresContext()->IsRootPaginatedDocument()) { michael@0: if (!mCalledBeginPage) { michael@0: PR_PL(("\n")); michael@0: PR_PL(("***************** BeginPage *****************\n")); michael@0: rv = dc->BeginPage(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } else { michael@0: mCalledBeginPage = false; michael@0: } michael@0: } michael@0: michael@0: PR_PL(("SeqFr::PrintNextPage -> %p PageNo: %d", pf, mPageNum)); michael@0: michael@0: nsRefPtr renderingContext = michael@0: dc->CreateRenderingContext(); michael@0: michael@0: nsRect drawingRect(nsPoint(0, 0), currentPage->GetSize()); michael@0: nsRegion drawingRegion(drawingRect); michael@0: nsLayoutUtils::PaintFrame(renderingContext, currentPage, michael@0: drawingRegion, NS_RGBA(0,0,0,0), michael@0: nsLayoutUtils::PAINT_SYNC_DECODE_IMAGES); michael@0: michael@0: if (mSelectionHeight >= 0 && selectionY < mSelectionHeight) { michael@0: selectionY += height; michael@0: printedPageNum++; michael@0: pf->SetPageNumInfo(printedPageNum, mTotalPages); michael@0: conFrame->SetPosition(conFrame->GetPosition() + nsPoint(0, -height)); michael@0: nsContainerFrame::PositionChildViews(conFrame); michael@0: michael@0: PR_PL(("***************** End Page (PrintNextPage) *****************\n")); michael@0: rv = dc->EndPage(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } else { michael@0: continuePrinting = false; michael@0: } michael@0: } michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSimplePageSequenceFrame::DoPageEnd() michael@0: { michael@0: nsresult rv = NS_OK; michael@0: if (PresContext()->IsRootPaginatedDocument() && mPrintThisPage) { michael@0: PR_PL(("***************** End Page (DoPageEnd) *****************\n")); michael@0: rv = PresContext()->DeviceContext()->EndPage(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: ResetPrintCanvasList(); michael@0: michael@0: mPageNum++; michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: static gfx3DMatrix michael@0: ComputePageSequenceTransform(nsIFrame* aFrame, float aAppUnitsPerPixel) michael@0: { michael@0: float scale = aFrame->PresContext()->GetPrintPreviewScale(); michael@0: return gfx3DMatrix::ScalingMatrix(scale, scale, 1); michael@0: } michael@0: michael@0: void michael@0: nsSimplePageSequenceFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, michael@0: const nsRect& aDirtyRect, michael@0: const nsDisplayListSet& aLists) michael@0: { michael@0: DisplayBorderBackgroundOutline(aBuilder, aLists); michael@0: michael@0: nsDisplayList content; michael@0: michael@0: { michael@0: // Clear clip state while we construct the children of the michael@0: // nsDisplayTransform, since they'll be in a different coordinate system. michael@0: DisplayListClipState::AutoSaveRestore clipState(aBuilder); michael@0: clipState.Clear(); michael@0: michael@0: nsIFrame* child = GetFirstPrincipalChild(); michael@0: while (child) { michael@0: child->BuildDisplayListForStackingContext(aBuilder, michael@0: child->GetVisualOverflowRectRelativeToSelf(), &content); michael@0: aBuilder->ResetMarkedFramesForDisplayList(); michael@0: child = child->GetNextSibling(); michael@0: } michael@0: } michael@0: michael@0: content.AppendNewToTop(new (aBuilder) michael@0: nsDisplayTransform(aBuilder, this, &content, ::ComputePageSequenceTransform)); michael@0: michael@0: aLists.Content()->AppendToTop(&content); michael@0: } michael@0: michael@0: nsIAtom* michael@0: nsSimplePageSequenceFrame::GetType() const michael@0: { michael@0: return nsGkAtoms::sequenceFrame; michael@0: } michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: void michael@0: nsSimplePageSequenceFrame::SetPageNumberFormat(const nsAString& aFormatStr, bool aForPageNumOnly) michael@0: { michael@0: NS_ASSERTION(mPageData != nullptr, "mPageData string cannot be null!"); michael@0: michael@0: if (aForPageNumOnly) { michael@0: mPageData->mPageNumFormat = aFormatStr; michael@0: } else { michael@0: mPageData->mPageNumAndTotalsFormat = aFormatStr; michael@0: } michael@0: } michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: void michael@0: nsSimplePageSequenceFrame::SetDateTimeStr(const nsAString& aDateTimeStr) michael@0: { michael@0: NS_ASSERTION(mPageData != nullptr, "mPageData string cannot be null!"); michael@0: michael@0: mPageData->mDateTimeStr = aDateTimeStr; michael@0: } michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: // For Shrink To Fit michael@0: // michael@0: // Return the percentage that the page needs to shrink to michael@0: // michael@0: NS_IMETHODIMP michael@0: nsSimplePageSequenceFrame::GetSTFPercent(float& aSTFPercent) michael@0: { michael@0: NS_ENSURE_TRUE(mPageData, NS_ERROR_UNEXPECTED); michael@0: aSTFPercent = mPageData->mShrinkToFitRatio; michael@0: return NS_OK; michael@0: }