Bug 1106321 - Serialize DEVMODE down to the content process when printing on Windows. r=jimm
authorMike Conley <mconley@mozilla.com>
Thu, 06 Aug 2015 17:36:54 -0400
changeset 257580 73dab35a83cd9161141da823877cc76ce03f958a
parent 257579 0cf9bfd5af47ed565d31694c9dae1fb879a7423f
child 257581 027e311a524ae9795a7f7b06ad1e49621a89aaa3
push id29223
push userkwierso@gmail.com
push dateThu, 13 Aug 2015 22:18:08 +0000
treeherdermozilla-central@4b35236fc76e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimm
bugs1106321
milestone43.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1106321 - Serialize DEVMODE down to the content process when printing on Windows. r=jimm
embedding/components/printingui/ipc/PPrintingTypes.ipdlh
embedding/components/webbrowserpersist/WebBrowserPersistSerializeChild.cpp
ipc/glue/IPCMessageUtils.h
widget/windows/nsPrintOptionsWin.cpp
xpcom/base/nsWindowsHelpers.h
--- a/embedding/components/printingui/ipc/PPrintingTypes.ipdlh
+++ b/embedding/components/printingui/ipc/PPrintingTypes.ipdlh
@@ -71,16 +71,17 @@ struct PrintData {
 
   /* Windows-specific things */
   nsString driverName;
   nsString deviceName;
   bool isFramesetDocument;
   bool isFramesetFrameSelected;
   bool isIFrameSelected;
   bool isRangeSelection;
+  uint8_t[] devModeData;
 
   /**
    * GTK-specific things. Some of these might look like dupes of the
    * information we're already passing, but the generalized settings that
    * we hold in nsIPrintSettings don't map perfectly to GTK's GtkPrintSettings,
    * so there are some nuances. GtkPrintSettings, for example, stores both an
    * internal name for paper size, as well as the display name.
    */
--- a/embedding/components/webbrowserpersist/WebBrowserPersistSerializeChild.cpp
+++ b/embedding/components/webbrowserpersist/WebBrowserPersistSerializeChild.cpp
@@ -4,16 +4,17 @@
  * 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 "WebBrowserPersistSerializeChild.h"
 
 #include <algorithm>
 
 #include "nsThreadUtils.h"
+#include "ipc/IPCMessageUtils.h"
 
 namespace mozilla {
 
 NS_IMPL_ISUPPORTS(WebBrowserPersistSerializeChild,
                   nsIWebBrowserPersistWriteCompletion,
                   nsIWebBrowserPersistURIMap,
                   nsIOutputStream)
 
@@ -88,27 +89,24 @@ WebBrowserPersistSerializeChild::Write(c
     // thread (which also means it's difficult to test the
     // thread-safety code this class doesn't yet have).
     //
     // This is *not* an NS_ERROR_NOT_IMPLEMENTED, because at this
     // point we've probably already misused the non-thread-safe
     // 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.
     const char* buf = aBuf;
     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);
         SendWriteData(Move(arrayBuf));
         *aWritten += toWrite;
         buf += toWrite;
         count -= toWrite;
     }
--- a/ipc/glue/IPCMessageUtils.h
+++ b/ipc/glue/IPCMessageUtils.h
@@ -117,16 +117,21 @@ struct OwningSerializedStructuredCloneBu
   operator=(const OwningSerializedStructuredCloneBuffer& aOther) = delete;
 };
 
 } // namespace mozilla
 
 namespace IPC {
 
 /**
+ * Maximum size, in bytes, of a single IPC message.
+ */
+static const uint32_t MAX_MESSAGE_SIZE = 65536;
+
+/**
  * Generic enum serializer.
  *
  * Consider using the specializations below, such as ContiguousEnumSerializer.
  *
  * This is a generic serializer for any enum type used in IPDL.
  * Programmers can define ParamTraits<E> for enum type E by deriving
  * EnumSerializer<E, MyEnumValidator> where MyEnumValidator is a struct
  * that has to define a static IsLegalValue function returning whether
--- a/widget/windows/nsPrintOptionsWin.cpp
+++ b/widget/windows/nsPrintOptionsWin.cpp
@@ -5,16 +5,18 @@
 #include "nsCOMPtr.h"
 #include "nsPrintOptionsWin.h"
 #include "nsPrintSettingsWin.h"
 #include "nsPrintDialogUtil.h"
 
 #include "nsGfxCIID.h"
 #include "nsIServiceManager.h"
 #include "nsIWebBrowserPrint.h"
+#include "nsWindowsHelpers.h"
+#include "ipc/IPCMessageUtils.h"
 
 const char kPrinterEnumeratorContractID[] = "@mozilla.org/gfx/printerenumerator;1";
 
 using namespace mozilla::embedding;
 
 /** ---------------------------------------------------
  *  See documentation in nsPrintOptionsWin.h
  *	@update 6/21/00 dwc
@@ -60,44 +62,88 @@ nsPrintOptionsWin::SerializeToPrintData(
   psWin->GetDriverName(&driverName);
 
   data->deviceName().Assign(deviceName);
   data->driverName().Assign(driverName);
 
   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;
 }
 
 NS_IMETHODIMP
 nsPrintOptionsWin::DeserializeToPrintSettings(const PrintData& data,
                                               nsIPrintSettings* settings)
 {
   nsresult rv = nsPrintOptions::DeserializeToPrintSettings(data, settings);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIPrintSettingsWin> psWin = do_QueryInterface(settings);
   if (!settings) {
     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());
+
+    nsXPIDLString printerName;
+    settings->GetPrinterName(getter_Copies(printerName));
+
+    DEVMODEW* devModeRaw = (DEVMODEW*)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY,
+                                                  data.devModeData().Length());
+    if (!devModeRaw) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
 
-  // 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);
+    nsAutoDevMode devMode(devModeRaw);
+    devModeRaw = nullptr;
 
-  ::GlobalUnlock(gDevMode);
-  ::GlobalFree(gDevMode);
+    // 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;
 }
 
 nsresult nsPrintOptionsWin::_CreatePrintSettings(nsIPrintSettings **_retval)
 {
   *_retval = nullptr;
   nsPrintSettingsWin* printSettings = new nsPrintSettingsWin(); // does not initially ref count
--- a/xpcom/base/nsWindowsHelpers.h
+++ b/xpcom/base/nsWindowsHelpers.h
@@ -115,20 +115,40 @@ public:
   static void Release(RawRef aFD)
   {
     if (aFD != Void()) {
       FreeLibrary(aFD);
     }
   }
 };
 
+
+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 {
 
 HMODULE inline
 LoadLibrarySystem32(LPCWSTR aModule)
 {
   WCHAR systemPath[MAX_PATH + 1] = { L'\0' };