Source code

Revision control

Copy as Markdown

Other Tools

/* -*- 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;
bool donePrinting;
// donePrinting will be true if it completed successfully or
// if the printing was cancelled
donePrinting = !mPrintJob || mPrintJob->PrintSheet(mPrintObj);
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(/*aUseDelay*/ true);
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);
}
}