⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 259760 in webkit


Ignore:
Timestamp:
Apr 8, 2020, 3:15:16 PM (6 years ago)
Author:
beidson@apple.com
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):

Location:
trunk/Source/WebKit
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebKit/ChangeLog

    r259758 r259760  
     12020-04-08  Brady Eidson  <beidson@apple.com>
     2
     3        Fix handling non-linearized PDFs when incremental PDF loading is enabled.
     4        <rdar://problem/60619506> and https://bugs.webkit.org/show_bug.cgi?id=210208
     5
     6        Reviewed by Tim Horton (and I think Geoff Garen, probably. It's confusing.)
     7
     8        When we try to load a non-linearized PDF with PDFKit, it makes an outlandishly large range request
     9        to try to verify the PDF file size.
     10       
     11        That's covered by <rdar://problem/61473378>
     12       
     13        Meanwhile we need to detect that and fallback to non-incremental PDF loading.
     14
     15        * WebProcess/Plugins/PDF/PDFPlugin.h:
     16        * WebProcess/Plugins/PDF/PDFPlugin.mm:
     17        (WebKit::PDFPlugin::receivedInvalidRangeRequest):
     18        (WebKit::dataProviderGetBytesAtPositionCallback):
     19        (WebKit::PDFPlugin::threadEntry):
     20        (WebKit::PDFPlugin::installPDFDocument):
     21
    1222020-04-08  Per Arne Vollan  <pvollan@apple.com>
    223
  • trunk/Source/WebKit/WebProcess/Plugins/PDF/PDFPlugin.h

    r259597 r259760  
    136136    void getResourceBytesAtPosition(size_t count, off_t position, CompletionHandler<void(const uint8_t*, size_t count)>&&);
    137137    size_t getResourceBytesAtPositionMainThread(void* buffer, off_t position, size_t count);
     138    void receivedNonLinearizedPDFSentinel();
     139    bool incrementalPDFLoadingEnabled() const { return m_incrementalPDFLoadingEnabled; }
    138140#ifndef NDEBUG
    139141    void pdfLog(const String& event);
  • trunk/Source/WebKit/WebProcess/Plugins/PDF/PDFPlugin.mm

    r259597 r259760  
    9191#import <pal/spi/mac/NSMenuSPI.h>
    9292#import <wtf/HexNumber.h>
     93#import <wtf/Scope.h>
    9394#import <wtf/UUID.h>
    9495#import <wtf/WTFSemaphore.h>
     
    137138static const int defaultScrollMagnitudeThresholdForPageFlip = 20;
    138139
     140// <rdar://problem/61473378> - PDFKit asks for a "way too large" range when the PDF it is loading
     141// incrementally turns out to be non-linearized.
     142// We'll assume any size over 4gb is PDFKit noticing non-linearized data.
     143static const uint32_t nonLinearizedPDFSentinel = std::numeric_limits<uint32_t>::max();
     144
    139145@interface WKPDFPluginAccessibilityObject : NSObject {
    140146    PDFLayerController *_pdfLayerController;
     
    694700#endif // !LOG_DISABLED
    695701
     702void PDFPlugin::receivedNonLinearizedPDFSentinel()
     703{
     704    m_incrementalPDFLoadingEnabled = false;
     705
     706    if (!isMainThread()) {
     707#if !LOG_DISABLED
     708        pdfLog("Disabling incremental PDF loading on background thread");
     709#endif
     710        callOnMainThread([this, protectedThis = makeRef(*this)] {
     711            receivedNonLinearizedPDFSentinel();
     712        });
     713        return;
     714    }
     715
     716#if !LOG_DISABLED
     717    pdfLog(makeString("Cancelling all ", m_streamLoaderMap.size(), " range request loaders"));
     718#endif
     719
     720    for (auto iterator = m_streamLoaderMap.begin(); iterator != m_streamLoaderMap.end(); iterator = m_streamLoaderMap.begin()) {
     721        m_outstandingByteRangeRequests.remove(iterator->value);
     722        cancelAndForgetLoader(*iterator->key);
     723    }
     724
     725    if (!m_documentFinishedLoading || m_pdfDocument)
     726        return;
     727
     728    m_pdfDocument = adoptNS([[pdfDocumentClass() alloc] initWithData:rawData()]);
     729    installPDFDocument();
     730    tryRunScriptsInPDFDocument();
     731}
     732
    696733static size_t dataProviderGetBytesAtPositionCallback(void* info, void* buffer, off_t position, size_t count)
    697734{
     735    Ref<PDFPlugin> plugin = *((PDFPlugin*)info);
     736
    698737    if (isMainThread()) {
    699738#if !LOG_DISABLED
    700         ((PDFPlugin*)info)->pdfLog(makeString("Handling request for ", count, " bytes at position ", position, " synchronously on the main thread"));
     739        plugin->pdfLog(makeString("Handling request for ", count, " bytes at position ", position, " synchronously on the main thread"));
    701740#endif
    702         return ((PDFPlugin*)info)->getResourceBytesAtPositionMainThread(buffer, position, count);
    703     }
     741        return plugin->getResourceBytesAtPositionMainThread(buffer, position, count);
     742    }
     743
     744    // It's possible we previously encountered an invalid range and therefore disabled incremental loading,
     745    // but PDFDocument is still trying to read data using a different strategy.
     746    // Always ignore such requests.
     747    if (!plugin->incrementalPDFLoadingEnabled())
     748        return 0;
    704749
    705750#if !LOG_DISABLED
    706     Ref<PDFPlugin> debugPluginRef = *((PDFPlugin*)info);
     751    auto debugPluginRef = plugin.copyRef();
    707752    debugPluginRef->incrementThreadsWaitingOnCallback();
    708753    debugPluginRef->pdfLog(makeString("PDF data provider requesting ", count, " bytes at position ", position));
    709754#endif
    710755
    711     Ref<PDFPlugin> plugin = *((PDFPlugin*)info);
     756    if (position > nonLinearizedPDFSentinel) {
     757#if !LOG_DISABLED
     758        plugin->pdfLog(makeString("Received invalid range request for ", count, " bytes at position ", position, ". PDF is probably not linearized. Falling back to non-incremental loading."));
     759#endif
     760        plugin->receivedNonLinearizedPDFSentinel();
     761        return 0;
     762    }
     763
    712764    WTF::Semaphore dataSemaphore { 0 };
    713765    size_t bytesProvided = 0;
     
    803855    };
    804856
     857    auto scopeExit = makeScopeExit([protectedPlugin = WTFMove(protectedPlugin)] () mutable {
     858        // Keep the PDFPlugin alive until the end of this function and the end
     859        // of the last main thread task submitted by this function.
     860        callOnMainThread([protectedPlugin = WTFMove(protectedPlugin)] { });
     861    });
     862
    805863    // Balanced by a deref inside of the dataProviderReleaseInfoCallback
    806864    ref();
     
    810868    m_backgroundThreadDocument = adoptNS([[pdfDocumentClass() alloc] initWithProvider:dataProvider.get()]);
    811869
     870    if (!m_incrementalPDFLoadingEnabled) {
     871        m_backgroundThreadDocument = nil;
     872        return;
     873    }
     874
    812875    WTF::Semaphore firstPageSemaphore { 0 };
    813876    auto firstPageQueue = WorkQueue::create("PDF first page work queue");
    814877
    815878    [m_backgroundThreadDocument preloadDataOfPagesInRange:NSMakeRange(0, 1) onQueue:firstPageQueue->dispatchQueue() completion:[&firstPageSemaphore, this] (NSIndexSet *) mutable {
    816         callOnMainThread([this] {
    817             adoptBackgroundThreadDocument();
    818         });
     879        if (m_incrementalPDFLoadingEnabled) {
     880            callOnMainThread([this] {
     881                adoptBackgroundThreadDocument();
     882            });
     883        } else
     884            m_backgroundThreadDocument = nil;
     885
    819886        firstPageSemaphore.signal();
    820887    }];
     
    825892    pdfLog("Finished preloading first page");
    826893#endif
    827 
    828     // The main thread dispatch below removes the last reference to the PDF thread.
    829     // It must be the last code executed in this function.
    830     callOnMainThread([protectedPlugin = WTFMove(protectedPlugin)] { });
    831894}
    832895
     
    14701533    ASSERT(m_pdfDocument);
    14711534    ASSERT(isMainThread());
     1535    LOG(IncrementalPDF, "Installing PDF document");
    14721536
    14731537#if HAVE(INCREMENTAL_PDF_APIS)
Note: See TracChangeset for help on using the changeset viewer.