Files
tubestation/layout/printing/nsPagePrintTimer.cpp
Daniel Holbert 8128e88474 Bug 1859025: Add some profiler markers as guideposts for what's going on during long print operations. r=profiler-reviewers,canaltinova
This patch adds a new layout profiling sub-category "LAYOUT_Printing" for the
markers added here.

I'm adding an "interval"-type marker ("AUTO_PROFILER_MARKER_TEXT") for the
function-calls that seem likely to occupy measurable amounts of time (due to
touching the filesystem or printer driver), vs. single-point-in-time markers
("PROFILER_MARKER_TEXT") for functions whose duration isn't particularly long
or interesting, or whose durations we're already measuring with other
closely-associated interval-markers.

Differential Revision: https://phabricator.services.mozilla.com/D191001
2023-10-18 05:34:50 +00:00

228 lines
6.7 KiB
C++

/* -*- 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 "nsPagePrintTimer.h"
#include "mozilla/dom/Document.h"
#include "mozilla/ProfilerMarkers.h"
#include "mozilla/Unused.h"
#include "nsPrintJob.h"
#include "nsPrintObject.h"
using namespace mozilla;
NS_IMPL_ISUPPORTS_INHERITED(nsPagePrintTimer, mozilla::Runnable,
nsITimerCallback)
nsPagePrintTimer::nsPagePrintTimer(nsPrintJob* aPrintJob,
nsIDocumentViewerPrint* aDocViewerPrint,
mozilla::dom::Document* aDocument,
uint32_t aDelay)
: Runnable("nsPagePrintTimer"),
mPrintJob(aPrintJob),
mDocViewerPrint(aDocViewerPrint),
mDocument(aDocument),
mDelay(aDelay),
mFiringCount(0),
mPrintObj(nullptr),
mWatchDogCount(0),
mDone(false) {
MOZ_ASSERT(aDocViewerPrint && aDocument);
mDocViewerPrint->IncrementDestroyBlockedCount();
}
nsPagePrintTimer::~nsPagePrintTimer() { Disconnect(); }
void nsPagePrintTimer::Disconnect() {
mPrintJob = nullptr;
mPrintObj = nullptr;
if (mDocViewerPrint) {
// This matches the IncrementDestroyBlockedCount call in the constructor.
mDocViewerPrint->DecrementDestroyBlockedCount();
mDocViewerPrint = nullptr;
}
}
nsresult nsPagePrintTimer::StartTimer(bool aUseDelay) {
uint32_t delay = 0;
if (aUseDelay) {
if (mFiringCount < 10) {
// Longer delay for the few first pages.
delay = mDelay + ((10 - mFiringCount) * 100);
} else {
delay = mDelay;
}
}
return NS_NewTimerWithCallback(getter_AddRefs(mTimer), this, delay,
nsITimer::TYPE_ONE_SHOT,
GetMainThreadSerialEventTarget());
}
nsresult nsPagePrintTimer::StartWatchDogTimer() {
if (mWatchDogTimer) {
mWatchDogTimer->Cancel();
}
// Instead of just doing one timer for a long period do multiple so we
// can check if the user cancelled the printing.
return NS_NewTimerWithCallback(getter_AddRefs(mWatchDogTimer), this,
WATCH_DOG_INTERVAL, nsITimer::TYPE_ONE_SHOT,
GetMainThreadSerialEventTarget());
}
void nsPagePrintTimer::StopWatchDogTimer() {
if (mWatchDogTimer) {
mWatchDogTimer->Cancel();
mWatchDogTimer = nullptr;
}
}
// nsRunnable
NS_IMETHODIMP
nsPagePrintTimer::Run() {
bool initNewTimer = true;
// Check to see if we are done
// inRange will be true if a sheet is actually printed
bool inRange;
bool donePrinting;
// donePrinting will be true if it completed successfully or
// if the printing was cancelled
donePrinting = !mPrintJob || mPrintJob->PrintSheet(mPrintObj, inRange);
if (donePrinting) {
if (mWaitingForRemotePrint ||
// If we are not waiting for the remote printing, it is the time to
// end printing task by calling DonePrintingSheets.
(!mPrintJob || mPrintJob->DonePrintingSheets(mPrintObj, NS_OK))) {
initNewTimer = false;
mDone = true;
}
}
// Note that the Stop() destroys this after the print job finishes
// (The nsPrintJob stops holding a reference when DonePrintingSheets
// returns true.)
Stop();
if (initNewTimer) {
++mFiringCount;
nsresult result = StartTimer(inRange);
if (NS_FAILED(result)) {
mDone = true; // had a failure.. we are finished..
if (mPrintJob) {
mPrintJob->SetIsPrinting(false);
}
}
}
return NS_OK;
}
// nsITimerCallback
NS_IMETHODIMP
nsPagePrintTimer::Notify(nsITimer* timer) {
// When finished there may be still pending notifications, which we can just
// ignore.
if (mDone) {
return NS_OK;
}
// There are four things that call Notify with different values for timer:
// 1) the delay between sheets (timer == mTimer)
// 2) canvasPrintState done (timer == null)
// 3) the watch dog timer (timer == mWatchDogTimer)
// 4) the waiting for remote print "timer" (timer == mWaitingForRemotePrint)
if (!timer) {
// Reset the counter since a mozPrintCallback has finished.
mWatchDogCount = 0;
} else if (timer == mTimer) {
// Reset the watchdog timer before the start of every sheet.
mWatchDogCount = 0;
mTimer = nullptr;
} else if (timer == mWaitingForRemotePrint) {
mWaitingForRemotePrint = nullptr;
// If we are still waiting for the sheet delay timer, don't let the
// notification from the remote print job trigger the next sheet.
if (mTimer) {
return NS_OK;
}
} else if (timer == mWatchDogTimer) {
mWatchDogCount++;
PROFILER_MARKER_TEXT(
"nsPagePrintTimer::Notify", LAYOUT_Printing, {},
nsPrintfCString("Watchdog Timer Count %d", mWatchDogCount));
if (mWatchDogCount > WATCH_DOG_MAX_COUNT) {
Fail();
return NS_OK;
}
}
bool donePrePrint = true;
// Don't start to pre-print if we're waiting on the parent still.
if (mPrintJob && !mWaitingForRemotePrint) {
donePrePrint = mPrintJob->PrePrintSheet();
}
if (donePrePrint && !mWaitingForRemotePrint) {
StopWatchDogTimer();
// Pass nullptr here since name already was set in constructor.
mDocument->Dispatch(do_AddRef(this));
} else {
// Start the watch dog if we're waiting for preprint to ensure that if any
// mozPrintCallbacks take to long we error out.
StartWatchDogTimer();
}
return NS_OK;
}
void nsPagePrintTimer::WaitForRemotePrint() {
mWaitingForRemotePrint = NS_NewTimer();
if (!mWaitingForRemotePrint) {
NS_WARNING("Failed to wait for remote print, we might time-out.");
}
}
void nsPagePrintTimer::RemotePrintFinished() {
if (!mWaitingForRemotePrint) {
return;
}
// now clean up print or print the next webshell
if (mDone && mPrintJob) {
mDone = mPrintJob->DonePrintingSheets(mPrintObj, NS_OK);
}
mWaitingForRemotePrint->SetTarget(GetMainThreadSerialEventTarget());
mozilla::Unused << mWaitingForRemotePrint->InitWithCallback(
this, 0, nsITimer::TYPE_ONE_SHOT);
}
nsresult nsPagePrintTimer::Start(nsPrintObject* aPO) {
mPrintObj = aPO;
mDone = false;
return StartTimer(false);
}
void nsPagePrintTimer::Stop() {
if (mTimer) {
mTimer->Cancel();
mTimer = nullptr;
}
StopWatchDogTimer();
}
void nsPagePrintTimer::Fail() {
NS_WARNING("nsPagePrintTimer::Fail called");
PROFILER_MARKER_TEXT("nsPagePrintTimer", LAYOUT_Printing, {},
"nsPagePrintTimer::Fail aborting print operation"_ns);
mDone = true;
Stop();
if (mPrintJob) {
mPrintJob->CleanupOnFailure(NS_OK, false);
}
}