/* -*- 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 "nsDeviceContextSpecProxy.h" #include "gfxASurface.h" #include "gfxPlatform.h" #include "mozilla/gfx/DrawEventRecorder.h" #include "mozilla/gfx/PrintTargetThebes.h" #include "mozilla/layout/RemotePrintJobChild.h" #include "mozilla/RefPtr.h" #include "mozilla/Unused.h" #include "nsComponentManagerUtils.h" #include "nsAppDirectoryServiceDefs.h" #include "nsDirectoryServiceUtils.h" #include "nsIPrintSettings.h" #include "private/pprio.h" using mozilla::Unused; using namespace mozilla; using namespace mozilla::gfx; NS_IMPL_ISUPPORTS(nsDeviceContextSpecProxy, nsIDeviceContextSpec) nsDeviceContextSpecProxy::nsDeviceContextSpecProxy( RemotePrintJobChild* aRemotePrintJob) : mRemotePrintJob(aRemotePrintJob) {} nsDeviceContextSpecProxy::~nsDeviceContextSpecProxy() = default; NS_IMETHODIMP nsDeviceContextSpecProxy::Init(nsIPrintSettings* aPrintSettings, bool aIsPrintPreview) { mPrintSettings = aPrintSettings; if (aIsPrintPreview) { return NS_OK; } if (!mRemotePrintJob) { NS_WARNING("We can't print via the parent without a RemotePrintJobChild."); return NS_ERROR_FAILURE; } return NS_OK; } already_AddRefed nsDeviceContextSpecProxy::MakePrintTarget() { double width, height; mPrintSettings->GetEffectiveSheetSize(&width, &height); if (width <= 0 || height <= 0) { return nullptr; } // convert twips to points width /= TWIPS_PER_POINT_FLOAT; height /= TWIPS_PER_POINT_FLOAT; RefPtr surface = gfxPlatform::GetPlatform()->CreateOffscreenSurface( mozilla::gfx::IntSize::Ceil(width, height), mozilla::gfx::SurfaceFormat::A8R8G8B8_UINT32); if (!surface) { return nullptr; } // The type of PrintTarget that we return here shouldn't really matter since // our implementation of GetDrawEventRecorder returns an object, which means // the DrawTarget returned by the PrintTarget will be a // DrawTargetWrapAndRecord. The recording will be serialized and sent over to // the parent process where PrintTranslator::TranslateRecording will call // MakePrintTarget (indirectly via PrintTranslator::CreateDrawTarget) on // whatever type of nsIDeviceContextSpecProxy is created for the platform that // we are running on. It is that DrawTarget that the recording will be // replayed on to print. // XXX(jwatt): The above isn't quite true. We do want to use a // PrintTargetRecording here, but we can't until bug 1280324 is figured out // and fixed otherwise we will cause bug 1280181 to happen again. RefPtr target = PrintTargetThebes::CreateOrNull(surface); return target.forget(); } NS_IMETHODIMP nsDeviceContextSpecProxy::GetDrawEventRecorder( mozilla::gfx::DrawEventRecorder** aDrawEventRecorder) { MOZ_ASSERT(aDrawEventRecorder); RefPtr result = mRecorder; result.forget(aDrawEventRecorder); return NS_OK; } NS_IMETHODIMP nsDeviceContextSpecProxy::BeginDocument(const nsAString& aTitle, const nsAString& aPrintToFileName, int32_t aStartPage, int32_t aEndPage) { if (!mRemotePrintJob || mRemotePrintJob->IsDestroyed()) { mRemotePrintJob = nullptr; return NS_ERROR_NOT_AVAILABLE; } mRecorder = new mozilla::layout::DrawEventRecorderPRFileDesc(); nsresult rv = mRemotePrintJob->InitializePrint(nsString(aTitle), aStartPage, aEndPage); if (NS_FAILED(rv)) { // The parent process will send a 'delete' message to tell this process to // delete our RemotePrintJobChild. As soon as we return to the event loop // and evaluate that message we will crash if we try to access // mRemotePrintJob. We must not try to use it again. mRemotePrintJob = nullptr; } return rv; } RefPtr nsDeviceContextSpecProxy::EndDocument() { if (!mRemotePrintJob || mRemotePrintJob->IsDestroyed()) { mRemotePrintJob = nullptr; return mozilla::gfx::PrintEndDocumentPromise::CreateAndReject( NS_ERROR_NOT_AVAILABLE, __func__); } Unused << mRemotePrintJob->SendFinalizePrint(); if (mRecorder) { MOZ_ASSERT(!mRecorder->IsOpen()); mRecorder->DetachResources(); mRecorder = nullptr; } return mozilla::gfx::PrintEndDocumentPromise::CreateAndResolve(true, __func__); } NS_IMETHODIMP nsDeviceContextSpecProxy::BeginPage(const IntSize& aSizeInPoints) { if (!mRemotePrintJob || mRemotePrintJob->IsDestroyed()) { mRemotePrintJob = nullptr; return NS_ERROR_NOT_AVAILABLE; } mRecorder->OpenFD(mRemotePrintJob->GetNextPageFD()); mCurrentPageSizeInPoints = aSizeInPoints; return NS_OK; } NS_IMETHODIMP nsDeviceContextSpecProxy::EndPage() { if (!mRemotePrintJob || mRemotePrintJob->IsDestroyed()) { mRemotePrintJob = nullptr; return NS_ERROR_NOT_AVAILABLE; } // Send the page recording to the parent. mRecorder->Close(); mRemotePrintJob->ProcessPage(mCurrentPageSizeInPoints, std::move(mRecorder->TakeDependentSurfaces())); return NS_OK; }