Bug 1399787 - Part 2.a. Add functions to load the content of a PDF from a FileDescriptor. draft
authorcku <cku@mozilla.com>
Wed, 01 Nov 2017 21:33:28 +0800
changeset 710596 73864ffeb519e67e7fd6a1a41b668398c8406be7
parent 710595 92f335be52b4563ec5b0afc8664430a854c30f59
child 710597 0e17c0219f626351fa3c78e53fbaf4840c01d017
push id92855
push usercku@mozilla.com
push dateMon, 11 Dec 2017 02:44:26 +0000
bugs1399787
milestone59.0a1
Bug 1399787 - Part 2.a. Add functions to load the content of a PDF from a FileDescriptor. All the functions added in Part 2 are utilities for sharing EMF/PDF contents between processes. MozReview-Commit-ID: 3qKosXH56kY
modules/pdfium/pdfium.def
widget/windows/PDFViaEMFPrintHelper.cpp
widget/windows/PDFViaEMFPrintHelper.h
widget/windows/PDFiumEngineShim.cpp
widget/windows/PDFiumEngineShim.h
widget/windows/gtest/moz.build
--- a/modules/pdfium/pdfium.def
+++ b/modules/pdfium/pdfium.def
@@ -2,13 +2,14 @@
 ; 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/.
 
 LIBRARY pdfium
 EXPORTS
     FPDF_InitLibrary
     FPDF_DestroyLibrary
     FPDF_LoadDocument
+    FPDF_LoadCustomDocument
     FPDF_CloseDocument
     FPDF_GetPageCount
     FPDF_LoadPage
     FPDF_ClosePage
     FPDF_RenderPage
--- a/widget/windows/PDFViaEMFPrintHelper.cpp
+++ b/widget/windows/PDFViaEMFPrintHelper.cpp
@@ -20,16 +20,17 @@ float ComputeScaleFactor(int aDCWidth, i
 
   return (aDCWidth >= aPageWidth && aDCHeight >= aPageHeight)
     ? 1.0 /* If page fits DC - no scaling needed. */
     : std::min(static_cast<float>(aDCWidth) / static_cast<float>(aPageWidth),         static_cast<float>(aDCHeight) / static_cast<float>(aPageHeight));
 }
 
 PDFViaEMFPrintHelper::PDFViaEMFPrintHelper()
   : mPDFDoc(nullptr)
+  , mPrfile(nullptr)
 {
 }
 
 PDFViaEMFPrintHelper::~PDFViaEMFPrintHelper()
 {
   CloseDocument();
 }
 
@@ -70,16 +71,48 @@ PDFViaEMFPrintHelper::OpenDocument(const
   NS_ENSURE_TRUE(CreatePDFiumEngineIfNeed(), NS_ERROR_FAILURE);
 
   mPDFDoc = mPDFiumEngine->LoadDocument(aFileName, nullptr);
   NS_ENSURE_TRUE(mPDFDoc, NS_ERROR_FAILURE);
 
   return NS_OK;
 }
 
+nsresult
+PDFViaEMFPrintHelper::OpenDocument(const FileDescriptor& aFD)
+{
+  MOZ_ASSERT(!mPrfile, "Forget to call CloseDocument?");
+
+  if (mPDFDoc) {
+    MOZ_ASSERT_UNREACHABLE("We can only open one PDF at a time, "
+                           "Use CloseDocument() to close the opened file "
+                           "before calling OpenDocument()");
+    return NS_ERROR_FAILURE;
+  }
+
+  NS_ENSURE_TRUE(CreatePDFiumEngineIfNeed(), NS_ERROR_FAILURE);
+
+  auto rawFD = aFD.ClonePlatformHandle();
+  PRFileDesc* prfile = PR_ImportFile(PROsfd(rawFD.release()));
+  NS_ENSURE_TRUE(prfile, NS_ERROR_FAILURE);
+
+  mPDFDoc = mPDFiumEngine->LoadDocument(prfile, nullptr);
+  if (!mPDFDoc) {
+    PR_Close(prfile);
+    return NS_ERROR_FAILURE;
+  }
+
+  // mPDFiumEngine keeps using this handle until we close mPDFDoc. Instead of
+  // closing this HANDLE here, we close it in
+  // PDFViaEMFPrintHelper::CloseDocument.
+  mPrfile = prfile;
+
+  return NS_OK;
+}
+
 bool
 PDFViaEMFPrintHelper::RenderPageToDC(HDC aDC, unsigned int aPageIndex,
                                      int aPageWidth, int aPageHeight)
 {
   MOZ_ASSERT(aDC && mPDFDoc);
   MOZ_ASSERT(static_cast<int>(aPageIndex) <
              mPDFiumEngine->GetPageCount(mPDFDoc));
 
@@ -171,16 +204,21 @@ PDFViaEMFPrintHelper::DrawPageToFile(con
 
 void
 PDFViaEMFPrintHelper::CloseDocument()
 {
   if (mPDFDoc) {
     mPDFiumEngine->CloseDocument(mPDFDoc);
     mPDFDoc = nullptr;
   }
+
+  if (mPrfile) {
+    PR_Close(mPrfile);
+    mPrfile = nullptr;
+  }
 }
 
 bool
 PDFViaEMFPrintHelper::CreatePDFiumEngineIfNeed()
 {
   if (!mPDFiumEngine) {
     mPDFiumEngine = PDFiumEngineShim::GetInstanceOrNull();
   }
--- a/widget/windows/PDFViaEMFPrintHelper.h
+++ b/widget/windows/PDFViaEMFPrintHelper.h
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef PDFVIAEMFPRINTHELPER_H_
 #define PDFVIAEMFPRINTHELPER_H_
 
 #include "nsCOMPtr.h"
 #include "PDFiumEngineShim.h"
 #include "mozilla/Vector.h"
+#include "mozilla/ipc/FileDescriptor.h"
 
 /* include windows.h for the HDC definitions that we need. */
 #include <windows.h>
 
 class nsIFile;
 class nsFileInputStream;
 
 namespace mozilla {
@@ -23,22 +24,25 @@ namespace widget {
  * This class helps draw a PDF file to a given Windows DC.
  * To do that it first converts the PDF file to EMF.
  * Windows EMF:
  * https://msdn.microsoft.com/en-us/windows/hardware/drivers/print/emf-data-type
  */
 class PDFViaEMFPrintHelper
 {
 public:
+  typedef mozilla::ipc::FileDescriptor FileDescriptor;
+
   PDFViaEMFPrintHelper();
   virtual ~PDFViaEMFPrintHelper();
 
   /** Loads the specified PDF file. */
   NS_IMETHOD OpenDocument(nsIFile *aFile);
   NS_IMETHOD OpenDocument(const char* aFileName);
+  NS_IMETHOD OpenDocument(const FileDescriptor& aFD);
 
   /** Releases document buffer. */
   void CloseDocument();
 
   int GetPageCount() const { return mPDFiumEngine->GetPageCount(mPDFDoc); }
 
   /** Convert specified PDF page to EMF and draw the EMF onto the given DC. */
   bool DrawPage(HDC aPrinterDC, unsigned int aPageIndex,
@@ -50,14 +54,15 @@ public:
 
 protected:
   virtual bool CreatePDFiumEngineIfNeed();
   bool RenderPageToDC(HDC aDC, unsigned int aPageIndex,
                       int aPageWidth, int aPageHeight);
 
   RefPtr<PDFiumEngineShim>    mPDFiumEngine;
   FPDF_DOCUMENT               mPDFDoc;
+  PRFileDesc*                 mPrfile;
 };
 
 } // namespace widget
 } // namespace mozilla
 
 #endif /* PDFVIAEMFPRINTHELPER_H_ */
--- a/widget/windows/PDFiumEngineShim.cpp
+++ b/widget/windows/PDFiumEngineShim.cpp
@@ -1,15 +1,15 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * 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 "PDFiumEngineShim.h"
-
+#include "private/pprio.h"
 
 namespace mozilla {
 namespace widget {
 
 static PDFiumEngineShim* sPDFiumEngineShim;
 
 /* static */
 already_AddRefed<PDFiumEngineShim>
@@ -82,16 +82,20 @@ PDFiumEngineShim::Init(const nsCString& 
   mFPDF_DestroyLibrary = (FPDF_DestroyLibrary_Pfn)PR_FindFunctionSymbol(
     mPRLibrary, "FPDF_DestroyLibrary");
   NS_ENSURE_TRUE(mFPDF_DestroyLibrary, false);
 
   mFPDF_LoadDocument = (FPDF_LoadDocument_Pfn)PR_FindFunctionSymbol(
     mPRLibrary, "FPDF_LoadDocument");
   NS_ENSURE_TRUE(mFPDF_LoadDocument, false);
 
+  mFPDF_LoadCustomDocument = (FPDF_LoadCustomDocument_Pfn)PR_FindFunctionSymbol(
+    mPRLibrary, "FPDF_LoadCustomDocument");
+  NS_ENSURE_TRUE(mFPDF_LoadCustomDocument, false);
+
   mFPDF_CloseDocument = (FPDF_CloseDocument_Pfn)PR_FindFunctionSymbol(
     mPRLibrary, "FPDF_CloseDocument");
   NS_ENSURE_TRUE(mFPDF_CloseDocument, false);
 
   mFPDF_GetPageCount = (FPDF_GetPageCount_Pfn)PR_FindFunctionSymbol(
     mPRLibrary, "FPDF_GetPageCount");
   NS_ENSURE_TRUE(mFPDF_GetPageCount, false);
 
@@ -115,16 +119,50 @@ PDFiumEngineShim::Init(const nsCString& 
 FPDF_DOCUMENT
 PDFiumEngineShim::LoadDocument(FPDF_STRING file_path,
                                FPDF_BYTESTRING aPassword)
 {
   MOZ_ASSERT(mInitialized);
   return mFPDF_LoadDocument(file_path, aPassword);
 }
 
+FPDF_DOCUMENT
+PDFiumEngineShim::LoadDocument(PRFileDesc* aPrfile,
+                               FPDF_BYTESTRING aPassword)
+{
+  MOZ_ASSERT(mInitialized && aPrfile);
+
+  PROffset32 fileLength = PR_Seek64(aPrfile, 0, PR_SEEK_END);
+  if (fileLength == -1) {
+    NS_WARNING("Failed to access the given FD.");
+    return nullptr;
+  }
+
+  FPDF_FILEACCESS fileAccess;
+  fileAccess.m_FileLen = static_cast<unsigned long>(fileLength);
+  fileAccess.m_Param = reinterpret_cast<void*>(aPrfile);
+  fileAccess.m_GetBlock =
+    [](void* param, unsigned long pos, unsigned char* buf, unsigned long size)
+    {
+      PRFileDesc* prfile = reinterpret_cast<PRFileDesc*>(param);
+
+      if (PR_Seek64(prfile, pos, PR_SEEK_SET) != pos) {
+        return 0;
+      }
+
+      if (PR_Read(prfile, buf, size) <= 0) {
+        return 0;
+      }
+
+      return 1;
+    };
+
+  return mFPDF_LoadCustomDocument(&fileAccess, aPassword);
+}
+
 void
 PDFiumEngineShim::CloseDocument(FPDF_DOCUMENT aDocument)
 {
   MOZ_ASSERT(mInitialized);
   mFPDF_CloseDocument(aDocument);
 }
 
 int
--- a/widget/windows/PDFiumEngineShim.h
+++ b/widget/windows/PDFiumEngineShim.h
@@ -6,26 +6,28 @@
 #ifndef PDFIUMENGINESHIM_H
 #define PDFIUMENGINESHIM_H
 
 #include "prlink.h"
 #include "fpdfview.h"
 
 /* include windows.h for the HDC definitions that we need. */
 #include <windows.h>
+#include "private/pprio.h"
 
 namespace mozilla {
 namespace widget {
 
 typedef void (STDCALL *FPDF_InitLibrary_Pfn)();
 typedef void (STDCALL *FPDF_DestroyLibrary_Pfn)();
 
 typedef FPDF_DOCUMENT (STDCALL *FPDF_LoadDocument_Pfn)(FPDF_STRING file_path,
                                                       FPDF_BYTESTRING password);
-
+typedef FPDF_DOCUMENT (STDCALL *FPDF_LoadCustomDocument_Pfn)(FPDF_FILEACCESS* pFileAccess,
+                                                             FPDF_BYTESTRING password);
 typedef void(STDCALL *FPDF_CloseDocument_Pfn)(FPDF_DOCUMENT aDocument);
 
 typedef int (STDCALL *FPDF_GetPageCount_Pfn)(FPDF_DOCUMENT aDocument);
 
 typedef FPDF_PAGE (STDCALL *FPDF_LoadPage_Pfn)(FPDF_DOCUMENT aDocument,
                                                int aPageIndex);
 typedef void (STDCALL *FPDF_ClosePage_Pfn)(FPDF_PAGE aPage);
 typedef void (STDCALL *FPDF_RenderPage_Pfn)(HDC aDC,
@@ -50,16 +52,18 @@ public:
   static already_AddRefed<PDFiumEngineShim> GetInstanceOrNull();
   // This function is used for testing purpose only, do not call it in regular
   // code.
   static already_AddRefed<PDFiumEngineShim>
   GetInstanceOrNull(const nsCString& aLibrary);
 
   FPDF_DOCUMENT LoadDocument(FPDF_STRING file_path,
                              FPDF_BYTESTRING aPassword);
+  FPDF_DOCUMENT LoadDocument(PRFileDesc* aPrfile,
+                             FPDF_BYTESTRING aPassword);
   void CloseDocument(FPDF_DOCUMENT aDocument);
   int GetPageCount(FPDF_DOCUMENT aDocument);
   int GetPageSizeByIndex(FPDF_DOCUMENT aDocument, int aPageIndex,
                          double* aWidth, double* aHeight);
 
   FPDF_PAGE LoadPage(FPDF_DOCUMENT aDocument, int aPageIndex);
   void ClosePage(FPDF_PAGE aPage);
   void RenderPage(HDC aDC, FPDF_PAGE aPage,
@@ -72,16 +76,17 @@ private:
   ~PDFiumEngineShim();
   bool Init(const nsCString& aLibrary);
 
   bool        mInitialized ;
 
   FPDF_InitLibrary_Pfn        mFPDF_InitLibrary;
   FPDF_DestroyLibrary_Pfn     mFPDF_DestroyLibrary;
   FPDF_LoadDocument_Pfn       mFPDF_LoadDocument;
+  FPDF_LoadCustomDocument_Pfn mFPDF_LoadCustomDocument;
   FPDF_CloseDocument_Pfn      mFPDF_CloseDocument;
   FPDF_GetPageCount_Pfn       mFPDF_GetPageCount;
   FPDF_LoadPage_Pfn           mFPDF_LoadPage;
   FPDF_ClosePage_Pfn          mFPDF_ClosePage;
   FPDF_RenderPage_Pfn         mFPDF_RenderPage;
 
   PRLibrary*  mPRLibrary;
 };
--- a/widget/windows/gtest/moz.build
+++ b/widget/windows/gtest/moz.build
@@ -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/.
 
 UNIFIED_SOURCES = [
     'TestEMFConversion.cpp',
 ]
 
 LOCAL_INCLUDES += [
+    '/ipc/chromium/src',
     '/modules/pdfium/pdfium/public',
     '/widget/windows',
 ]
 
 TEST_HARNESS_FILES.gtest += [
     'pdfium_ref_x64.dll',
     'pdfium_ref_x86.dll',
     'PrinterTestPage.pdf',