/* -*- 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 "nsIPrintSession.h" #include "nsIPrintSettings.h" #include "nsIUUIDGenerator.h" #include "private/pprio.h" using mozilla::Unused; using namespace mozilla; using namespace mozilla::gfx; NS_IMPL_ISUPPORTS(nsDeviceContextSpecProxy, nsIDeviceContextSpec) NS_IMETHODIMP nsDeviceContextSpecProxy::Init(nsIWidget* aWidget, nsIPrintSettings* aPrintSettings, bool aIsPrintPreview) { nsresult rv; mRealDeviceContextSpec = do_CreateInstance("@mozilla.org/gfx/devicecontextspec;1", &rv); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } mRealDeviceContextSpec->Init(nullptr, aPrintSettings, aIsPrintPreview); if (NS_WARN_IF(NS_FAILED(rv))) { mRealDeviceContextSpec = nullptr; return rv; } mPrintSettings = aPrintSettings; if (aIsPrintPreview) { return NS_OK; } // nsIPrintSettings only has a weak reference to nsIPrintSession, so we hold // it to make sure it's available for the lifetime of the print. rv = mPrintSettings->GetPrintSession(getter_AddRefs(mPrintSession)); if (NS_FAILED(rv) || !mPrintSession) { NS_WARNING("We can't print via the parent without an nsIPrintSession."); return NS_ERROR_FAILURE; } rv = mPrintSession->GetRemotePrintJob(getter_AddRefs(mRemotePrintJob)); if (NS_FAILED(rv) || !mRemotePrintJob) { NS_WARNING("We can't print via the parent without a RemotePrintJobChild."); return NS_ERROR_FAILURE; } return NS_OK; } already_AddRefed nsDeviceContextSpecProxy::MakePrintTarget() { MOZ_ASSERT(mRealDeviceContextSpec); double width, height; nsresult rv = mPrintSettings->GetEffectivePageSize(&width, &height); if (NS_WARN_IF(NS_FAILED(rv)) || 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::Truncate(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; } float nsDeviceContextSpecProxy::GetDPI() { MOZ_ASSERT(mRealDeviceContextSpec); return mRealDeviceContextSpec->GetDPI(); } float nsDeviceContextSpecProxy::GetPrintingScale() { MOZ_ASSERT(mRealDeviceContextSpec); return mRealDeviceContextSpec->GetPrintingScale(); } gfxPoint nsDeviceContextSpecProxy::GetPrintingTranslate() { MOZ_ASSERT(mRealDeviceContextSpec); return mRealDeviceContextSpec->GetPrintingTranslate(); } NS_IMETHODIMP nsDeviceContextSpecProxy::BeginDocument(const nsAString& aTitle, const nsAString& aPrintToFileName, int32_t aStartPage, int32_t aEndPage) { mRecorder = new mozilla::layout::DrawEventRecorderPRFileDesc(); nsresult rv = mRemotePrintJob->InitializePrint(nsString(aTitle), nsString(aPrintToFileName), 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; } NS_IMETHODIMP nsDeviceContextSpecProxy::EndDocument() { if (mRemotePrintJob) { Unused << mRemotePrintJob->SendFinalizePrint(); } return NS_OK; } NS_IMETHODIMP nsDeviceContextSpecProxy::AbortDocument() { if (mRemotePrintJob) { Unused << mRemotePrintJob->SendAbortPrint(NS_OK); } return NS_OK; } NS_IMETHODIMP nsDeviceContextSpecProxy::BeginPage() { mRecorder->OpenFD(mRemotePrintJob->GetNextPageFD()); return NS_OK; } NS_IMETHODIMP nsDeviceContextSpecProxy::EndPage() { // Send the page recording to the parent. mRecorder->Close(); mRemotePrintJob->ProcessPage(); return NS_OK; }