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

Changeset 233583 in webkit


Ignore:
Timestamp:
Jul 6, 2018, 11:31:12 AM (8 years ago)
Author:
graouts@webkit.org
Message:

[Web Animations] Using a Web Animation leaks the Document
​https://bugs.webkit.org/show_bug.cgi?id=187088
<rdar://problem/41392046>

Reviewed by Darin Adler.

Source/WebCore:

Test: webanimations/leak-document-with-web-animation.html

We need to ensure that any remaining animation is cleared when the DocumentTimeline is detached from its Document.
We rename WebAnimation::prepareAnimationForRemoval() to WebAnimation::remove() since it really actively disassociates
the animation from its timeline.

An earlier version of this patch (r233349) was rolled out due to crashes caught in the ASan configuration. The following
changes were made to make it safe:

  • We protect the DocumentAnimationScheduler instance in displayRefreshFired() against code that might run in a

requestAnimationFrame() callback that would trigger the object to be deleted.

  • We protect the WebAnimation instance in remove() against setEffectInternal() or setTimelineInternal() potentially

causing the object to be deleted. Similar protections were addede to setEffect() and setTimeline().

  • We changed ~DocumentTimeline() to a default implementation to ensure it calls ~DisplayRefreshMonitorClient()

to avoid callbacks after the object has been marked for deletion.

  • animation/AnimationTimeline.cpp:

(WebCore::AnimationTimeline::removeAnimationsForElement): We no longer need the call to removeAnimation()
since the new WebAnimation::remove() method will also set the timeline to null which will eventually call
removeAnimation() on the disassociated timeline.

  • animation/DeclarativeAnimation.cpp:

(WebCore::DeclarativeAnimation::remove):
(WebCore::DeclarativeAnimation::prepareAnimationForRemoval): Deleted.

  • animation/DeclarativeAnimation.h:
  • animation/DocumentAnimationScheduler.cpp:

(WebCore::DocumentAnimationScheduler::displayRefreshFired):

  • animation/DocumentTimeline.cpp:

(WebCore::DocumentTimeline::detachFromDocument): Call remove() on all known animations.
(WebCore::DocumentTimeline::~DocumentTimeline): Deleted.

  • animation/WebAnimation.cpp:

(WebCore::WebAnimation::remove): Set the timeline to null to fully disassociate this animation from its timeline.
(WebCore::WebAnimation::setEffect):
(WebCore::WebAnimation::setEffectInternal):
(WebCore::WebAnimation::setTimeline): Factor the internal timeline-association code out of this JS API method so
that we can call this code without any JS-facing implications.
(WebCore::WebAnimation::setTimelineInternal):
(WebCore::WebAnimation::prepareAnimationForRemoval): Deleted.

  • animation/WebAnimation.h:

LayoutTests:

  • webanimations/leak-document-with-web-animation-expected.txt: Added.
  • webanimations/leak-document-with-web-animation.html: Added.
  • webanimations/resources/web-animation-leak-iframe.html: Added.
Location:
trunk
Files:
4 added
9 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r233581 r233583  
     12018-07-06  Antoine Quint  <graouts@apple.com>
     2
     3        [Web Animations] Using a Web Animation leaks the Document
     4        https://bugs.webkit.org/show_bug.cgi?id=187088
     5        <rdar://problem/41392046>
     6
     7        Reviewed by Darin Adler.
     8
     9        * webanimations/leak-document-with-web-animation-expected.txt: Added.
     10        * webanimations/leak-document-with-web-animation.html: Added.
     11        * webanimations/resources/web-animation-leak-iframe.html: Added.
     12
    1132018-07-06  Saam Barati  <sbarati@apple.com>
    214
  • trunk/Source/WebCore/ChangeLog

    r233578 r233583  
     12018-07-06  Antoine Quint  <graouts@apple.com>
     2
     3        [Web Animations] Using a Web Animation leaks the Document
     4        https://bugs.webkit.org/show_bug.cgi?id=187088
     5        <rdar://problem/41392046>
     6
     7        Reviewed by Darin Adler.
     8
     9        Test: webanimations/leak-document-with-web-animation.html
     10
     11        We need to ensure that any remaining animation is cleared when the DocumentTimeline is detached from its Document.
     12        We rename WebAnimation::prepareAnimationForRemoval() to WebAnimation::remove() since it really actively disassociates
     13        the animation from its timeline.
     14
     15        An earlier version of this patch (r233349) was rolled out due to crashes caught in the ASan configuration. The following
     16        changes were made to make it safe:
     17
     18        - We protect the DocumentAnimationScheduler instance in displayRefreshFired() against code that might run in a
     19        requestAnimationFrame() callback that would trigger the object to be deleted.
     20
     21        - We protect the WebAnimation instance in remove() against setEffectInternal() or setTimelineInternal() potentially
     22        causing the object to be deleted. Similar protections were addede to setEffect() and setTimeline().
     23
     24        - We changed ~DocumentTimeline() to a default implementation to ensure it calls ~DisplayRefreshMonitorClient()
     25        to avoid callbacks after the object has been marked for deletion.
     26
     27
     28        * animation/AnimationTimeline.cpp:
     29        (WebCore::AnimationTimeline::removeAnimationsForElement): We no longer need the call to removeAnimation()
     30        since the new WebAnimation::remove() method will also set the timeline to null which will eventually call
     31        removeAnimation() on the disassociated timeline.
     32        * animation/DeclarativeAnimation.cpp:
     33        (WebCore::DeclarativeAnimation::remove):
     34        (WebCore::DeclarativeAnimation::prepareAnimationForRemoval): Deleted.
     35        * animation/DeclarativeAnimation.h:
     36        * animation/DocumentAnimationScheduler.cpp:
     37        (WebCore::DocumentAnimationScheduler::displayRefreshFired):
     38        * animation/DocumentTimeline.cpp:
     39        (WebCore::DocumentTimeline::detachFromDocument): Call remove() on all known animations.
     40        (WebCore::DocumentTimeline::~DocumentTimeline): Deleted.
     41        * animation/WebAnimation.cpp:
     42        (WebCore::WebAnimation::remove): Set the timeline to null to fully disassociate this animation from its timeline.
     43        (WebCore::WebAnimation::setEffect):
     44        (WebCore::WebAnimation::setEffectInternal):
     45        (WebCore::WebAnimation::setTimeline): Factor the internal timeline-association code out of this JS API method so
     46        that we can call this code without any JS-facing implications.
     47        (WebCore::WebAnimation::setTimelineInternal):
     48        (WebCore::WebAnimation::prepareAnimationForRemoval): Deleted.
     49        * animation/WebAnimation.h:
     50
    1512018-07-06  Daniel Bates  <dabates@apple.com>
    252
  • trunk/Source/WebCore/animation/AnimationTimeline.cpp

    r233361 r233583  
    177177void AnimationTimeline::removeAnimationsForElement(Element& element)
    178178{
    179     for (auto& animation : animationsForElement(element)) {
    180         animation->prepareAnimationForRemoval();
    181         removeAnimation(animation.releaseNonNull());
    182     }
     179    for (auto& animation : animationsForElement(element))
     180        animation->remove();
    183181}
    184182
  • trunk/Source/WebCore/animation/DeclarativeAnimation.cpp

    r233361 r233583  
    5050}
    5151
    52 void DeclarativeAnimation::prepareAnimationForRemoval()
    53 {
    54     WebAnimation::prepareAnimationForRemoval();
     52void DeclarativeAnimation::remove()
     53{
    5554    m_eventQueue.close();
     55    WebAnimation::remove();
    5656}
    5757
  • trunk/Source/WebCore/animation/DeclarativeAnimation.h

    r233361 r233583  
    4646    void setBackingAnimation(const Animation&);
    4747    void invalidateDOMEvents(Seconds elapsedTime = 0_s);
    48     void prepareAnimationForRemoval() final;
    4948
    5049    void setTimeline(RefPtr<AnimationTimeline>&&) final;
    … …  
    6059    AnimationEffectReadOnly::Phase phaseWithoutEffect() const;
    6160    void enqueueDOMEvent(const AtomicString&, Seconds);
     61    void remove() final;
    6262
    6363    Element& m_target;
  • trunk/Source/WebCore/animation/DocumentAnimationScheduler.cpp

    r233394 r233583  
    7676        return;
    7777
     78    // This object could be deleted after scripts in the the requestAnimationFrame callbacks are executed.
     79    auto protectedThis = makeRef(*this);
     80
    7881    m_isFiring = true;
    7982    m_lastTimestamp = Seconds(m_document->domWindow()->nowTimestamp());
  • trunk/Source/WebCore/animation/DocumentTimeline.cpp

    r233500 r233583  
    5656}
    5757
    58 DocumentTimeline::~DocumentTimeline()
    59 {
    60 }
     58DocumentTimeline::~DocumentTimeline() = default;
    6159
    6260void DocumentTimeline::detachFromDocument()
    … …  
    6563    m_eventDispatchTaskQueue.close();
    6664    m_animationScheduleTimer.stop();
     65
     66    auto& animationsToRemove = animations();
     67    while (!animationsToRemove.isEmpty())
     68        animationsToRemove.first()->remove();
     69
    6770    m_document = nullptr;
    6871}
  • trunk/Source/WebCore/animation/WebAnimation.cpp

    r233519 r233583  
    7272}
    7373
    74 void WebAnimation::prepareAnimationForRemoval()
    75 {
     74void WebAnimation::remove()
     75{
     76    // This object could be deleted after either clearing the effect or timeline relationship.
     77    auto protectedThis = makeRef(*this);
    7678    setEffectInternal(nullptr);
     79    setTimelineInternal(nullptr);
    7780}
    7881
    … …  
    129132    // while the effect was set via the API, the element still has a transition or animation set up and we must
    130133    // not break the timeline-to-animation relationship.
     134
     135    // This object could be deleted after clearing the effect relationship.
     136    auto protectedThis = makeRef(*this);
    131137    setEffectInternal(WTFMove(newEffect), isDeclarativeAnimation());
    132138
    … …  
    140146void WebAnimation::setEffectInternal(RefPtr<AnimationEffectReadOnly>&& newEffect, bool doNotRemoveFromTimeline)
    141147{
    142     auto oldEffect = m_effect;
    143 
    144     m_effect = WTFMove(newEffect);
     148    if (m_effect == newEffect)
     149        return;
     150
     151    auto oldEffect = std::exchange(m_effect, WTFMove(newEffect));
    145152
    146153    Element* previousTarget = nullptr;
    … …  
    178185    if (m_startTime)
    179186        setHoldTime(std::nullopt);
    180 
    181     if (m_timeline)
    182         m_timeline->removeAnimation(*this);
    183 
    184     if (timeline)
    185         timeline->addAnimation(*this);
    186187
    187188    if (is<KeyframeEffectReadOnly>(m_effect)) {
    … …  
    199200    }
    200201
    201     m_timeline = WTFMove(timeline);
     202    // This object could be deleted after clearing the timeline relationship.
     203    auto protectedThis = makeRef(*this);
     204    setTimelineInternal(WTFMove(timeline));
    202205
    203206    setSuspended(is<DocumentTimeline>(m_timeline) && downcast<DocumentTimeline>(*m_timeline).animationsAreSuspended());
    … …  
    208211    // and the synchronously notify flag set to false.
    209212    updateFinishedState(DidSeek::No, SynchronouslyNotify::No);
     213}
     214
     215void WebAnimation::setTimelineInternal(RefPtr<AnimationTimeline>&& timeline)
     216{
     217    if (m_timeline == timeline)
     218        return;
     219
     220    if (m_timeline)
     221        m_timeline->removeAnimation(*this);
     222
     223    m_timeline = WTFMove(timeline);
     224
     225    if (m_timeline)
     226        m_timeline->addAnimation(*this);
    210227}
    211228
  • trunk/Source/WebCore/animation/WebAnimation.h

    r233361 r233583  
    6464    AnimationEffectReadOnly* effect() const { return m_effect.get(); }
    6565    void setEffect(RefPtr<AnimationEffectReadOnly>&&);
    66     void setEffectInternal(RefPtr<AnimationEffectReadOnly>&&, bool = false);
    6766    AnimationTimeline* timeline() const { return m_timeline.get(); }
    6867    virtual void setTimeline(RefPtr<AnimationTimeline>&&);
    … …  
    118117    void setSuspended(bool);
    119118    bool isSuspended() const { return m_isSuspended; }
    120     virtual void prepareAnimationForRemoval();
     119    virtual void remove();
    121120
    122121    String description();
    … …  
    157156    void runPendingPlayTask();
    158157    void resetPendingTasks();
    159    
     158    void setEffectInternal(RefPtr<AnimationEffectReadOnly>&&, bool = false);
     159    void setTimelineInternal(RefPtr<AnimationTimeline>&&);
     160
    160161    String m_id;
    161162    RefPtr<AnimationEffectReadOnly> m_effect;
Note: See TracChangeset for help on using the changeset viewer.