Bug 1106321 - Serialize DEVMODE down to the content process when printing on Windows. r=jimm

This commit is contained in:
Mike Conley 2015-08-06 17:36:54 -04:00
parent 187200ae65
commit 6ca832b8e4
5 changed files with 85 additions and 15 deletions

View File

@ -76,6 +76,7 @@ struct PrintData {
bool isFramesetFrameSelected;
bool isIFrameSelected;
bool isRangeSelection;
uint8_t[] devModeData;
/**
* GTK-specific things. Some of these might look like dupes of the

View File

@ -9,6 +9,7 @@
#include <algorithm>
#include "nsThreadUtils.h"
#include "ipc/IPCMessageUtils.h"
namespace mozilla {
@ -93,9 +94,6 @@ WebBrowserPersistSerializeChild::Write(const char* aBuf, uint32_t aCount,
// refcounting.
MOZ_RELEASE_ASSERT(NS_IsMainThread(), "Fix this class to be thread-safe.");
// Limit the size of an individual IPC message.
static const uint32_t kMaxWrite = 65536;
// Work around bug 1181433 by sending multiple messages if
// necessary to write the entire aCount bytes, even though
// nsIOutputStream.idl says we're allowed to do a short write.
@ -103,7 +101,7 @@ WebBrowserPersistSerializeChild::Write(const char* aBuf, uint32_t aCount,
uint32_t count = aCount;
*aWritten = 0;
while (count > 0) {
uint32_t toWrite = std::min(kMaxWrite, count);
uint32_t toWrite = std::min(IPC::MAX_MESSAGE_SIZE, count);
nsTArray<uint8_t> arrayBuf;
// It would be nice if this extra copy could be avoided.
arrayBuf.AppendElements(buf, toWrite);

View File

@ -121,6 +121,11 @@ struct OwningSerializedStructuredCloneBuffer : public SerializedStructuredCloneB
namespace IPC {
/**
* Maximum size, in bytes, of a single IPC message.
*/
static const uint32_t MAX_MESSAGE_SIZE = 65536;
/**
* Generic enum serializer.
*

View File

@ -10,6 +10,8 @@
#include "nsGfxCIID.h"
#include "nsIServiceManager.h"
#include "nsIWebBrowserPrint.h"
#include "nsWindowsHelpers.h"
#include "ipc/IPCMessageUtils.h"
const char kPrinterEnumeratorContractID[] = "@mozilla.org/gfx/printerenumerator;1";
@ -65,6 +67,38 @@ nsPrintOptionsWin::SerializeToPrintData(nsIPrintSettings* aSettings,
free(deviceName);
free(driverName);
// When creating the print dialog on Windows, the parent creates a DEVMODE
// which is used to convey print settings to the Windows printing backend.
// We don't, therefore, want or care about DEVMODEs sent up from the child.
if (XRE_IsParentProcess()) {
// A DEVMODE can actually be of arbitrary size. If it turns out that it'll
// make our IPC message larger than the limit, then we'll error out.
LPDEVMODEW devModeRaw;
psWin->GetDevMode(&devModeRaw); // This actually allocates a copy of the
// the nsIPrintSettingsWin DEVMODE, so
// we're now responsible for deallocating
// it. We'll use an nsAutoDevMode helper
// to do this.
if (devModeRaw) {
nsAutoDevMode devMode(devModeRaw);
devModeRaw = nullptr;
size_t devModeTotalSize = devMode->dmSize + devMode->dmDriverExtra;
size_t msgTotalSize = sizeof(PrintData) + devModeTotalSize;
if (msgTotalSize > IPC::MAX_MESSAGE_SIZE) {
return NS_ERROR_FAILURE;
}
// Instead of reaching in and manually reading each member, we'll just
// copy the bits over.
const char* devModeData = reinterpret_cast<const char*>(devMode.get());
nsTArray<uint8_t> arrayBuf;
arrayBuf.AppendElements(devModeData, devModeTotalSize);
data->devModeData().SwapElements(arrayBuf);
}
}
return NS_OK;
}
@ -80,19 +114,31 @@ nsPrintOptionsWin::DeserializeToPrintSettings(const PrintData& data,
return NS_ERROR_FAILURE;
}
psWin->SetDeviceName(data.deviceName().get());
psWin->SetDriverName(data.driverName().get());
if (XRE_IsContentProcess()) {
psWin->SetDeviceName(data.deviceName().get());
psWin->SetDriverName(data.driverName().get());
// We also need to prepare a DevMode and stuff it into our newly
// created nsIPrintSettings...
nsXPIDLString printerName;
settings->GetPrinterName(getter_Copies(printerName));
HGLOBAL gDevMode = CreateGlobalDevModeAndInit(printerName, settings);
LPDEVMODEW devMode = (LPDEVMODEW)::GlobalLock(gDevMode);
psWin->SetDevMode(devMode);
nsXPIDLString printerName;
settings->GetPrinterName(getter_Copies(printerName));
::GlobalUnlock(gDevMode);
::GlobalFree(gDevMode);
DEVMODEW* devModeRaw = (DEVMODEW*)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY,
data.devModeData().Length());
if (!devModeRaw) {
return NS_ERROR_OUT_OF_MEMORY;
}
nsAutoDevMode devMode(devModeRaw);
devModeRaw = nullptr;
// Seems a bit silly to copy the buffer out, just so that SetDevMode can
// copy it again. However, if I attempt to just pass
// data.devModeData.Elements() casted to an DEVMODEW* to SetDevMode, I get
// a "Conversion loses qualifiers" build-time error because
// data.devModeData.Elements() is of type const char *.
memcpy(devMode.get(), data.devModeData().Elements(), data.devModeData().Length());
psWin->SetDevMode(devMode); // Copies
}
return NS_OK;
}

View File

@ -120,10 +120,30 @@ public:
}
};
template<>
class nsAutoRefTraits<DEVMODEW*>
{
public:
typedef DEVMODEW* RawRef;
static RawRef Void()
{
return nullptr;
}
static void Release(RawRef aDevMode)
{
if (aDevMode != Void()) {
::HeapFree(::GetProcessHeap(), 0, aDevMode);
}
}
};
typedef nsAutoRef<HKEY> nsAutoRegKey;
typedef nsAutoRef<SC_HANDLE> nsAutoServiceHandle;
typedef nsAutoRef<HANDLE> nsAutoHandle;
typedef nsAutoRef<HMODULE> nsModuleHandle;
typedef nsAutoRef<DEVMODEW*> nsAutoDevMode;
namespace {