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();