Changeset 249594 in webkit


Ignore:
Timestamp:
Sep 6, 2019 3:15:51 PM (5 years ago)
Author:
commit-queue@webkit.org
Message:

REGRESSION (r249367): m_decodingPromises grows indefinitely until ImageLoader destruction
https://bugs.webkit.org/show_bug.cgi?id=201402

Patch by Said Abou-Hallawa <sabouhallawa@apple.com> on 2019-09-06
Reviewed by Youenn Fablet and Daniel Bates.

Source/WebCore:

Add the static functions resolvePromises() and rejectPromises(). These
functions take an lvalue reference to a Vector of promises. Inside them,
the lvalue reference argument are exchanged with an empty Vector of
promises then the promises are processed. This clears m_decodingPromises
and fixes the leak.

Add an internal API which returns the count of the pending promises of
an HTMLImageElement. This internal API will be used in the attached test.

Test: fast/images/decode-resolve-reject-no-leak.html

  • html/HTMLImageElement.h:

(WebCore::HTMLImageElement::pendingDecodePromisesCountForTesting const):

  • loader/ImageLoader.cpp:

(WebCore::resolvePromises):
ImageLoader::decode() calls BitmapImage::decode() and moves m_decodingPromises
in capture. When decoding finishes, this function is called to resolve the
promises. But ImageLoader might get deleted before the image decoding
finishes. So this function has to be static.

(WebCore::rejectPromises):
(WebCore::ImageLoader::resolveDecodePromises):
(WebCore::ImageLoader::rejectDecodePromises):
(WebCore::ImageLoader::notifyFinished):
(WebCore::ImageLoader::decode):
(WebCore::resolveDecodePromises): Deleted.
(WebCore::rejectDecodePromises): Deleted.

  • loader/ImageLoader.h:

(WebCore::ImageLoader::pendingDecodePromisesCountForTesting const):

  • testing/Internals.cpp:

(WebCore::Internals::imagePendingDecodePromisesCountForTesting):

  • testing/Internals.h:
  • testing/Internals.idl:

LayoutTests:

  • fast/images/decode-resolve-reject-no-leak-expected.txt: Added.
  • fast/images/decode-resolve-reject-no-leak.html: Added.
Location:
trunk
Files:
2 added
8 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r249593 r249594  
     12019-09-06  Said Abou-Hallawa  <sabouhallawa@apple.com>
     2
     3        REGRESSION (r249367): m_decodingPromises grows indefinitely until ImageLoader destruction
     4        https://bugs.webkit.org/show_bug.cgi?id=201402
     5
     6        Reviewed by Youenn Fablet and Daniel Bates.
     7
     8        * fast/images/decode-resolve-reject-no-leak-expected.txt: Added.
     9        * fast/images/decode-resolve-reject-no-leak.html: Added.
     10
    1112019-09-06  Said Abou-Hallawa  <sabouhallawa@apple.com>
    212
  • trunk/Source/WebCore/ChangeLog

    r249593 r249594  
     12019-09-06  Said Abou-Hallawa  <sabouhallawa@apple.com>
     2
     3        REGRESSION (r249367): m_decodingPromises grows indefinitely until ImageLoader destruction
     4        https://bugs.webkit.org/show_bug.cgi?id=201402
     5
     6        Reviewed by Youenn Fablet and Daniel Bates.
     7
     8        Add the static functions resolvePromises() and rejectPromises(). These
     9        functions take an lvalue reference to a Vector of promises. Inside them,
     10        the lvalue reference argument are exchanged with an empty Vector of
     11        promises then the promises are processed. This clears m_decodingPromises
     12        and fixes the leak.
     13
     14        Add an internal API which returns the count of the pending promises of
     15        an HTMLImageElement. This internal API will be used in the attached test.
     16
     17        Test: fast/images/decode-resolve-reject-no-leak.html
     18
     19        * html/HTMLImageElement.h:
     20        (WebCore::HTMLImageElement::pendingDecodePromisesCountForTesting const):
     21        * loader/ImageLoader.cpp:
     22        (WebCore::resolvePromises):
     23        ImageLoader::decode() calls BitmapImage::decode() and moves m_decodingPromises
     24        in capture. When decoding finishes, this function is called to resolve the
     25        promises. But ImageLoader might get deleted before the image decoding
     26        finishes. So this function has to be static.
     27
     28        (WebCore::rejectPromises):
     29        (WebCore::ImageLoader::resolveDecodePromises):
     30        (WebCore::ImageLoader::rejectDecodePromises):
     31        (WebCore::ImageLoader::notifyFinished):
     32        (WebCore::ImageLoader::decode):
     33        (WebCore::resolveDecodePromises): Deleted.
     34        (WebCore::rejectDecodePromises): Deleted.
     35        * loader/ImageLoader.h:
     36        (WebCore::ImageLoader::pendingDecodePromisesCountForTesting const):
     37        * testing/Internals.cpp:
     38        (WebCore::Internals::imagePendingDecodePromisesCountForTesting):
     39        * testing/Internals.h:
     40        * testing/Internals.idl:
     41
    1422019-09-06  Said Abou-Hallawa  <sabouhallawa@apple.com>
    243
  • trunk/Source/WebCore/html/HTMLImageElement.h

    r249480 r249594  
    105105
    106106    bool hasPendingActivity() const { return m_imageLoader.hasPendingActivity(); }
     107    size_t pendingDecodePromisesCountForTesting() const { return m_imageLoader.pendingDecodePromisesCountForTesting(); }
    107108
    108109    bool canContainRangeEndPoint() const override { return false; }
  • trunk/Source/WebCore/loader/ImageLoader.cpp

    r249367 r249594  
    276276}
    277277
    278 static inline void resolveDecodePromises(Vector<RefPtr<DeferredPromise>>&& promises)
     278static inline void resolvePromises(Vector<RefPtr<DeferredPromise>>& promises)
    279279{
    280280    ASSERT(!promises.isEmpty());
    281     for (auto& promise : promises)
     281    auto promisesToBeResolved = std::exchange(promises, { });
     282    for (auto& promise : promisesToBeResolved)
    282283        promise->resolve();
    283284}
    284285
    285 static inline void rejectDecodePromises(Vector<RefPtr<DeferredPromise>>&& promises, const char* message)
     286static inline void rejectPromises(Vector<RefPtr<DeferredPromise>>& promises, const char* message)
    286287{
    287288    ASSERT(!promises.isEmpty());
    288     for (auto& promise : promises)
     289    auto promisesToBeRejected = std::exchange(promises, { });
     290    for (auto& promise : promisesToBeRejected)
    289291        promise->reject(Exception { EncodingError, message });
     292}
     293
     294inline void ImageLoader::resolveDecodePromises()
     295{
     296    resolvePromises(m_decodingPromises);
     297}
     298
     299inline void ImageLoader::rejectDecodePromises(const char* message)
     300{
     301    rejectPromises(m_decodingPromises, message);
    290302}
    291303
     
    314326
    315327        if (hasPendingDecodePromises())
    316             rejectDecodePromises(WTFMove(m_decodingPromises), "Access control error.");
     328            rejectDecodePromises("Access control error.");
    317329       
    318330        ASSERT(!m_hasPendingLoadEvent);
     
    326338    if (m_image->wasCanceled()) {
    327339        if (hasPendingDecodePromises())
    328             rejectDecodePromises(WTFMove(m_decodingPromises), "Loading was canceled.");
     340            rejectDecodePromises("Loading was canceled.");
    329341        m_hasPendingLoadEvent = false;
    330342        // Only consider updating the protection ref-count of the Element immediately before returning
     
    403415
    404416    if (!element().document().domWindow()) {
    405         rejectDecodePromises(WTFMove(m_decodingPromises), "Inactive document.");
     417        rejectDecodePromises("Inactive document.");
    406418        return;
    407419    }
     
    409421    AtomString attr = element().imageSourceURL();
    410422    if (stripLeadingAndTrailingHTMLSpaces(attr).isEmpty()) {
    411         rejectDecodePromises(WTFMove(m_decodingPromises), "Missing source URL.");
     423        rejectDecodePromises("Missing source URL.");
    412424        return;
    413425    }
     
    422434   
    423435    if (!element().document().domWindow()) {
    424         rejectDecodePromises(WTFMove(m_decodingPromises), "Inactive document.");
     436        rejectDecodePromises("Inactive document.");
    425437        return;
    426438    }
    427439
    428440    if (!m_image || !m_image->image() || m_image->errorOccurred()) {
    429         rejectDecodePromises(WTFMove(m_decodingPromises), "Loading error.");
     441        rejectDecodePromises("Loading error.");
    430442        return;
    431443    }
     
    433445    Image* image = m_image->image();
    434446    if (!is<BitmapImage>(image)) {
    435         resolveDecodePromises(WTFMove(m_decodingPromises));
     447        resolveDecodePromises();
    436448        return;
    437449    }
     
    439451    auto& bitmapImage = downcast<BitmapImage>(*image);
    440452    bitmapImage.decode([promises = WTFMove(m_decodingPromises)]() mutable {
    441         resolveDecodePromises(WTFMove(promises));
     453        resolvePromises(promises);
    442454    });
    443455}
  • trunk/Source/WebCore/loader/ImageLoader.h

    r249367 r249594  
    6262    void clearImage(); // Cancels pending beforeload and load events, and doesn't dispatch new ones.
    6363   
     64    size_t pendingDecodePromisesCountForTesting() const { return m_decodingPromises.size(); }
    6465    void decode(Ref<DeferredPromise>&&);
    6566
     
    9697
    9798    bool hasPendingDecodePromises() const { return !m_decodingPromises.isEmpty(); }
     99    void resolveDecodePromises();
     100    void rejectDecodePromises(const char* message);
    98101    void decode();
    99102   
  • trunk/Source/WebCore/testing/Internals.cpp

    r249531 r249594  
    871871}
    872872
     873unsigned Internals::imagePendingDecodePromisesCountForTesting(HTMLImageElement& element)
     874{
     875    return element.pendingDecodePromisesCountForTesting();
     876}
     877
    873878void Internals::setClearDecoderAfterAsyncFrameRequestForTesting(HTMLImageElement& element, bool enabled)
    874879{
  • trunk/Source/WebCore/testing/Internals.h

    r248373 r249594  
    155155    void resetImageAnimation(HTMLImageElement&);
    156156    bool isImageAnimating(HTMLImageElement&);
     157    unsigned imagePendingDecodePromisesCountForTesting(HTMLImageElement&);
    157158    void setClearDecoderAfterAsyncFrameRequestForTesting(HTMLImageElement&, bool enabled);
    158159    unsigned imageDecodeCount(HTMLImageElement&);
  • trunk/Source/WebCore/testing/Internals.idl

    r248373 r249594  
    370370    void resetImageAnimation(HTMLImageElement element);
    371371    boolean isImageAnimating(HTMLImageElement element);
     372    unsigned long imagePendingDecodePromisesCountForTesting(HTMLImageElement element);
    372373    void setClearDecoderAfterAsyncFrameRequestForTesting(HTMLImageElement element, boolean enabled);
    373374    unsigned long imageDecodeCount(HTMLImageElement element);
Note: See TracChangeset for help on using the changeset viewer.