/* -*- 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" 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; } rv = NS_GetSpecialDirectory(NS_APP_CONTENT_PROCESS_TEMP_DIR, getter_AddRefs(mRecordingDir)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } mUuidGenerator = do_GetService("@mozilla.org/uuid-generator;1", &rv); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } 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(); } nsresult nsDeviceContextSpecProxy::CreateUniqueTempPath(nsACString& aFilePath) { MOZ_ASSERT(mRecordingDir); MOZ_ASSERT(mUuidGenerator); nsID uuid; nsresult rv = mUuidGenerator->GenerateUUIDInPlace(&uuid); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } char uuidChars[NSID_LENGTH]; uuid.ToProvidedString(uuidChars); mRecordingFileName.AssignASCII(uuidChars); nsCOMPtr recordingFile; rv = mRecordingDir->Clone(getter_AddRefs(recordingFile)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } rv = recordingFile->AppendNative(mRecordingFileName); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } return recordingFile->GetNativePath(aFilePath); } NS_IMETHODIMP nsDeviceContextSpecProxy::BeginDocument(const nsAString& aTitle, const nsAString& aPrintToFileName, int32_t aStartPage, int32_t aEndPage) { nsAutoCString recordingPath; nsresult rv = CreateUniqueTempPath(recordingPath); if (NS_FAILED(rv)) { return rv; } mRecorder = new mozilla::layout::DrawEventRecorderPRFileDesc(recordingPath.get()); return mRemotePrintJob->InitializePrint(nsString(aTitle), nsString(aPrintToFileName), aStartPage, aEndPage); } NS_IMETHODIMP nsDeviceContextSpecProxy::EndDocument() { Unused << mRemotePrintJob->SendFinalizePrint(); return NS_OK; } NS_IMETHODIMP nsDeviceContextSpecProxy::AbortDocument() { Unused << mRemotePrintJob->SendAbortPrint(NS_OK); return NS_OK; } NS_IMETHODIMP nsDeviceContextSpecProxy::BeginPage() { // Reopen the file, if necessary, ready for the next page. if (!mRecorder->IsOpen()) { nsAutoCString recordingPath; nsresult rv = CreateUniqueTempPath(recordingPath); if (NS_FAILED(rv)) { return rv; } mRecorder->OpenNew(recordingPath.get()); } return NS_OK; } NS_IMETHODIMP nsDeviceContextSpecProxy::EndPage() { // Send the page recording to the parent. mRecorder->Close(); mRemotePrintJob->ProcessPage(mRecordingFileName); return NS_OK; }