Title: [259760] trunk/Source/WebKit
Revision
259760
Author
beid...@apple.com
Date
2020-04-08 15:15:16 -0700 (Wed, 08 Apr 2020)

Log Message

Fix handling non-linearized PDFs when incremental PDF loading is enabled.
<rdar://problem/60619506> and https://bugs.webkit.org/show_bug.cgi?id=210208

Reviewed by Tim Horton (and I think Geoff Garen, probably. It's confusing.)

When we try to load a non-linearized PDF with PDFKit, it makes an outlandishly large range request
to try to verify the PDF file size.

That's covered by <rdar://problem/61473378>

Meanwhile we need to detect that and fallback to non-incremental PDF loading.

* WebProcess/Plugins/PDF/PDFPlugin.h:
* WebProcess/Plugins/PDF/PDFPlugin.mm:
(WebKit::PDFPlugin::receivedInvalidRangeRequest):
(WebKit::dataProviderGetBytesAtPositionCallback):
(WebKit::PDFPlugin::threadEntry):
(WebKit::PDFPlugin::installPDFDocument):

Modified Paths

Diff

Modified: trunk/Source/WebKit/ChangeLog (259759 => 259760)


--- trunk/Source/WebKit/ChangeLog	2020-04-08 22:06:15 UTC (rev 259759)
+++ trunk/Source/WebKit/ChangeLog	2020-04-08 22:15:16 UTC (rev 259760)
@@ -1,3 +1,24 @@
+2020-04-08  Brady Eidson  <beid...@apple.com>
+
+        Fix handling non-linearized PDFs when incremental PDF loading is enabled.
+        <rdar://problem/60619506> and https://bugs.webkit.org/show_bug.cgi?id=210208
+
+        Reviewed by Tim Horton (and I think Geoff Garen, probably. It's confusing.)
+
+        When we try to load a non-linearized PDF with PDFKit, it makes an outlandishly large range request
+        to try to verify the PDF file size.
+        
+        That's covered by <rdar://problem/61473378>
+        
+        Meanwhile we need to detect that and fallback to non-incremental PDF loading.
+
+        * WebProcess/Plugins/PDF/PDFPlugin.h:
+        * WebProcess/Plugins/PDF/PDFPlugin.mm:
+        (WebKit::PDFPlugin::receivedInvalidRangeRequest):
+        (WebKit::dataProviderGetBytesAtPositionCallback):
+        (WebKit::PDFPlugin::threadEntry):
+        (WebKit::PDFPlugin::installPDFDocument):
+
 2020-04-08  Per Arne Vollan  <pvol...@apple.com>
 
         Allow use of syscall from the WebContent sandbox

Modified: trunk/Source/WebKit/WebProcess/Plugins/PDF/PDFPlugin.h (259759 => 259760)


--- trunk/Source/WebKit/WebProcess/Plugins/PDF/PDFPlugin.h	2020-04-08 22:06:15 UTC (rev 259759)
+++ trunk/Source/WebKit/WebProcess/Plugins/PDF/PDFPlugin.h	2020-04-08 22:15:16 UTC (rev 259760)
@@ -135,6 +135,8 @@
 #if HAVE(INCREMENTAL_PDF_APIS)
     void getResourceBytesAtPosition(size_t count, off_t position, CompletionHandler<void(const uint8_t*, size_t count)>&&);
     size_t getResourceBytesAtPositionMainThread(void* buffer, off_t position, size_t count);
+    void receivedNonLinearizedPDFSentinel();
+    bool incrementalPDFLoadingEnabled() const { return m_incrementalPDFLoadingEnabled; }
 #ifndef NDEBUG
     void pdfLog(const String& event);
     size_t incrementThreadsWaitingOnCallback() { return ++m_threadsWaitingOnCallback; }

Modified: trunk/Source/WebKit/WebProcess/Plugins/PDF/PDFPlugin.mm (259759 => 259760)


--- trunk/Source/WebKit/WebProcess/Plugins/PDF/PDFPlugin.mm	2020-04-08 22:06:15 UTC (rev 259759)
+++ trunk/Source/WebKit/WebProcess/Plugins/PDF/PDFPlugin.mm	2020-04-08 22:15:16 UTC (rev 259760)
@@ -90,6 +90,7 @@
 #import <pal/spi/cg/CoreGraphicsSPI.h>
 #import <pal/spi/mac/NSMenuSPI.h>
 #import <wtf/HexNumber.h>
+#import <wtf/Scope.h>
 #import <wtf/UUID.h>
 #import <wtf/WTFSemaphore.h>
 #import <wtf/WorkQueue.h>
@@ -136,6 +137,11 @@
 // will jump to the next or previous page, to match PDFKit behavior.
 static const int defaultScrollMagnitudeThresholdForPageFlip = 20;
 
+// <rdar://problem/61473378> - PDFKit asks for a "way too large" range when the PDF it is loading
+// incrementally turns out to be non-linearized.
+// We'll assume any size over 4gb is PDFKit noticing non-linearized data.
+static const uint32_t nonLinearizedPDFSentinel = std::numeric_limits<uint32_t>::max();
+
 @interface WKPDFPluginAccessibilityObject : NSObject {
     PDFLayerController *_pdfLayerController;
     __unsafe_unretained NSObject *_parent;
@@ -693,22 +699,68 @@
 }
 #endif // !LOG_DISABLED
 
+void PDFPlugin::receivedNonLinearizedPDFSentinel()
+{
+    m_incrementalPDFLoadingEnabled = false;
+
+    if (!isMainThread()) {
+#if !LOG_DISABLED
+        pdfLog("Disabling incremental PDF loading on background thread");
+#endif
+        callOnMainThread([this, protectedThis = makeRef(*this)] {
+            receivedNonLinearizedPDFSentinel();
+        });
+        return;
+    }
+
+#if !LOG_DISABLED
+    pdfLog(makeString("Cancelling all ", m_streamLoaderMap.size(), " range request loaders"));
+#endif
+
+    for (auto iterator = m_streamLoaderMap.begin(); iterator != m_streamLoaderMap.end(); iterator = m_streamLoaderMap.begin()) {
+        m_outstandingByteRangeRequests.remove(iterator->value);
+        cancelAndForgetLoader(*iterator->key);
+    }
+
+    if (!m_documentFinishedLoading || m_pdfDocument)
+        return;
+
+    m_pdfDocument = adoptNS([[pdfDocumentClass() alloc] initWithData:rawData()]);
+    installPDFDocument();
+    tryRunScriptsInPDFDocument();
+}
+
 static size_t dataProviderGetBytesAtPositionCallback(void* info, void* buffer, off_t position, size_t count)
 {
+    Ref<PDFPlugin> plugin = *((PDFPlugin*)info);
+
     if (isMainThread()) {
 #if !LOG_DISABLED
-        ((PDFPlugin*)info)->pdfLog(makeString("Handling request for ", count, " bytes at position ", position, " synchronously on the main thread"));
+        plugin->pdfLog(makeString("Handling request for ", count, " bytes at position ", position, " synchronously on the main thread"));
 #endif
-        return ((PDFPlugin*)info)->getResourceBytesAtPositionMainThread(buffer, position, count);
+        return plugin->getResourceBytesAtPositionMainThread(buffer, position, count);
     }
 
+    // It's possible we previously encountered an invalid range and therefore disabled incremental loading,
+    // but PDFDocument is still trying to read data using a different strategy.
+    // Always ignore such requests.
+    if (!plugin->incrementalPDFLoadingEnabled())
+        return 0;
+
 #if !LOG_DISABLED
-    Ref<PDFPlugin> debugPluginRef = *((PDFPlugin*)info);
+    auto debugPluginRef = plugin.copyRef();
     debugPluginRef->incrementThreadsWaitingOnCallback();
     debugPluginRef->pdfLog(makeString("PDF data provider requesting ", count, " bytes at position ", position));
 #endif
 
-    Ref<PDFPlugin> plugin = *((PDFPlugin*)info);
+    if (position > nonLinearizedPDFSentinel) {
+#if !LOG_DISABLED
+        plugin->pdfLog(makeString("Received invalid range request for ", count, " bytes at position ", position, ". PDF is probably not linearized. Falling back to non-incremental loading."));
+#endif
+        plugin->receivedNonLinearizedPDFSentinel();
+        return 0;
+    }
+
     WTF::Semaphore dataSemaphore { 0 };
     size_t bytesProvided = 0;
 
@@ -802,6 +854,12 @@
         dataProviderReleaseInfoCallback,
     };
 
+    auto scopeExit = makeScopeExit([protectedPlugin = WTFMove(protectedPlugin)] () mutable {
+        // Keep the PDFPlugin alive until the end of this function and the end
+        // of the last main thread task submitted by this function.
+        callOnMainThread([protectedPlugin = WTFMove(protectedPlugin)] { });
+    });
+
     // Balanced by a deref inside of the dataProviderReleaseInfoCallback
     ref();
 
@@ -809,13 +867,22 @@
     CGDataProviderSetProperty(dataProvider.get(), kCGDataProviderHasHighLatency, kCFBooleanTrue);
     m_backgroundThreadDocument = adoptNS([[pdfDocumentClass() alloc] initWithProvider:dataProvider.get()]);
 
+    if (!m_incrementalPDFLoadingEnabled) {
+        m_backgroundThreadDocument = nil;
+        return;
+    }
+
     WTF::Semaphore firstPageSemaphore { 0 };
     auto firstPageQueue = WorkQueue::create("PDF first page work queue");
 
     [m_backgroundThreadDocument preloadDataOfPagesInRange:NSMakeRange(0, 1) onQueue:firstPageQueue->dispatchQueue() completion:[&firstPageSemaphore, this] (NSIndexSet *) mutable {
-        callOnMainThread([this] {
-            adoptBackgroundThreadDocument();
-        });
+        if (m_incrementalPDFLoadingEnabled) {
+            callOnMainThread([this] {
+                adoptBackgroundThreadDocument();
+            });
+        } else
+            m_backgroundThreadDocument = nil;
+
         firstPageSemaphore.signal();
     }];
 
@@ -824,10 +891,6 @@
 #if !LOG_DISABLED
     pdfLog("Finished preloading first page");
 #endif
-
-    // The main thread dispatch below removes the last reference to the PDF thread.
-    // It must be the last code executed in this function.
-    callOnMainThread([protectedPlugin = WTFMove(protectedPlugin)] { });
 }
 
 void PDFPlugin::unconditionalCompleteOutstandingRangeRequests()
@@ -1469,6 +1532,7 @@
 {
     ASSERT(m_pdfDocument);
     ASSERT(isMainThread());
+    LOG(IncrementalPDF, "Installing PDF document");
 
 #if HAVE(INCREMENTAL_PDF_APIS)
     maybeClearHighLatencyDataProviderFlag();
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to