[Blink] Reset finished SVG image animations on reload [chromium/src : main]

0 views
Skip to first unread message

zhenbang Jiang (Gerrit)

unread,
Apr 16, 2026, 10:40:26 PMApr 16
to Kevin Ellis, android-bu...@system.gserviceaccount.com, Chromium LUCI CQ, Menard, Alexis, chromium...@chromium.org, Dirk Schulze, Fredrik Söderquist, Olga Gerchikov, Nate Chapin, Stephen Chenney, blink-rev...@chromium.org, blink-revie...@chromium.org, blink-...@chromium.org, fmalit...@chromium.org, gavinp...@chromium.org, kouhe...@chromium.org, loading...@chromium.org, pdr+svgw...@chromium.org
Attention needed from Kevin Ellis

zhenbang Jiang added 1 comment

Patchset-level comments
File-level comment, Patchset 5 (Latest):
zhenbang Jiang . resolved

Hi, could you take a look at this? thanks!

Open in Gerrit

Related details

Attention is currently required from:
  • Kevin Ellis
Submit Requirements:
  • requirement satisfiedCode-Coverage
  • requirement is not satisfiedCode-Owners
  • requirement is not satisfiedCode-Review
  • requirement is not satisfiedReview-Enforcement
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: comment
Gerrit-Project: chromium/src
Gerrit-Branch: main
Gerrit-Change-Id: I662712aeac3f523dc9c39dbe4711a318cc6156d0
Gerrit-Change-Number: 7760909
Gerrit-PatchSet: 5
Gerrit-Owner: zhenbang Jiang <edom...@gmail.com>
Gerrit-Reviewer: Kevin Ellis <kev...@chromium.org>
Gerrit-Reviewer: zhenbang Jiang <edom...@gmail.com>
Gerrit-CC: Dirk Schulze <dsch...@chromium.org>
Gerrit-CC: Fredrik Söderquist <f...@opera.com>
Gerrit-CC: Menard, Alexis <alexis...@intel.com>
Gerrit-CC: Nate Chapin <jap...@chromium.org>
Gerrit-CC: Olga Gerchikov <gerc...@microsoft.com>
Gerrit-CC: Stephen Chenney <sche...@chromium.org>
Gerrit-Attention: Kevin Ellis <kev...@chromium.org>
Gerrit-Comment-Date: Fri, 17 Apr 2026 02:39:51 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
satisfied_requirement
unsatisfied_requirement
open
diffy

Fredrik Söderquist (Gerrit)

unread,
Apr 17, 2026, 7:40:27 AMApr 17
to zhenbang Jiang, Kevin Ellis, android-bu...@system.gserviceaccount.com, Chromium LUCI CQ, Menard, Alexis, chromium...@chromium.org, Dirk Schulze, Olga Gerchikov, Nate Chapin, Stephen Chenney, blink-rev...@chromium.org, blink-revie...@chromium.org, blink-...@chromium.org, fmalit...@chromium.org, gavinp...@chromium.org, kouhe...@chromium.org, loading...@chromium.org, pdr+svgw...@chromium.org
Attention needed from Kevin Ellis and zhenbang Jiang

Fredrik Söderquist added 1 comment

File third_party/blink/renderer/core/loader/image_loader.cc
Line 907, Patchset 5 (Latest):void ImageLoader::ResetAnimationOrDeferUntilAttach() {
Fredrik Söderquist . unresolved

I don't think there's supposed to be any connection between resetting an animation and whether the animation is visible at all, so I suspect what you want to do here is rather to separate resetting the animation and potentially triggering invalidation (if needed).

Open in Gerrit

Related details

Attention is currently required from:
  • Kevin Ellis
  • zhenbang Jiang
Submit Requirements:
    • requirement satisfiedCode-Coverage
    • requirement is not satisfiedCode-Owners
    • requirement is not satisfiedCode-Review
    • requirement is not satisfiedNo-Unresolved-Comments
    • requirement is not satisfiedReview-Enforcement
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: comment
    Gerrit-Project: chromium/src
    Gerrit-Branch: main
    Gerrit-Change-Id: I662712aeac3f523dc9c39dbe4711a318cc6156d0
    Gerrit-Change-Number: 7760909
    Gerrit-PatchSet: 5
    Gerrit-Owner: zhenbang Jiang <edom...@gmail.com>
    Gerrit-Reviewer: Kevin Ellis <kev...@chromium.org>
    Gerrit-Reviewer: zhenbang Jiang <edom...@gmail.com>
    Gerrit-CC: Dirk Schulze <dsch...@chromium.org>
    Gerrit-CC: Fredrik Söderquist <f...@opera.com>
    Gerrit-CC: Menard, Alexis <alexis...@intel.com>
    Gerrit-CC: Nate Chapin <jap...@chromium.org>
    Gerrit-CC: Olga Gerchikov <gerc...@microsoft.com>
    Gerrit-CC: Stephen Chenney <sche...@chromium.org>
    Gerrit-Attention: Kevin Ellis <kev...@chromium.org>
    Gerrit-Attention: zhenbang Jiang <edom...@gmail.com>
    Gerrit-Comment-Date: Fri, 17 Apr 2026 11:40:20 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: No
    satisfied_requirement
    unsatisfied_requirement
    open
    diffy

    Kevin Ellis (Gerrit)

    unread,
    Apr 21, 2026, 10:18:18 AMApr 21
    to zhenbang Jiang, android-bu...@system.gserviceaccount.com, Chromium LUCI CQ, Menard, Alexis, chromium...@chromium.org, Dirk Schulze, Fredrik Söderquist, Olga Gerchikov, Nate Chapin, Stephen Chenney, blink-rev...@chromium.org, blink-revie...@chromium.org, blink-...@chromium.org, fmalit...@chromium.org, gavinp...@chromium.org, kouhe...@chromium.org, loading...@chromium.org, pdr+svgw...@chromium.org
    Attention needed from zhenbang Jiang

    Kevin Ellis added 2 comments

    File third_party/blink/renderer/core/animation/document_animations.cc
    Line 274, Patchset 5 (Latest): if (timeline->HasAnimations()) {
    Kevin Ellis . unresolved

    Not sure we need another HasAnimations method. We already have Element::HasAnimations(), Document::getAnimations(), Element::getAnimations(), and Element::GetElementAnimations().

    This method would return true if any element in the document has an animation, or even if it recently had an animation that simply hasn't been garbage collected yet. Methinks Element::GetElementAnimations() provides what you need.

    File third_party/blink/renderer/core/svg/graphics/svg_image.cc
    Line 574, Patchset 5 (Latest): document.GetDocumentAnimations().HasAnimations();
    Kevin Ellis . unresolved

    Element already has a HasAnimations method.

    root_element->HasAnimations()

    Open in Gerrit

    Related details

    Attention is currently required from:
    • zhenbang Jiang
    Gerrit-Attention: zhenbang Jiang <edom...@gmail.com>
    Gerrit-Comment-Date: Tue, 21 Apr 2026 14:18:12 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: No
    satisfied_requirement
    unsatisfied_requirement
    open
    diffy

    zhenbang Jiang (Gerrit)

    unread,
    Apr 23, 2026, 2:08:06 AMApr 23
    to Kevin Ellis, android-bu...@system.gserviceaccount.com, Chromium LUCI CQ, Menard, Alexis, chromium...@chromium.org, Dirk Schulze, Fredrik Söderquist, Olga Gerchikov, Nate Chapin, Stephen Chenney, zol...@webkit.org, blink-revi...@chromium.org, blink-rev...@chromium.org, blink-revie...@chromium.org, blink-...@chromium.org, fmalit...@chromium.org, gavinp...@chromium.org, kouhe...@chromium.org, loading...@chromium.org, pdr+svgw...@chromium.org
    Attention needed from Fredrik Söderquist and Kevin Ellis

    zhenbang Jiang added 4 comments

    Patchset-level comments
    File-level comment, Patchset 7 (Latest):
    zhenbang Jiang . resolved

    PTAL again,thanks!

    File third_party/blink/renderer/core/animation/document_animations.cc
    Line 274, Patchset 5: if (timeline->HasAnimations()) {
    Kevin Ellis . resolved

    Not sure we need another HasAnimations method. We already have Element::HasAnimations(), Document::getAnimations(), Element::getAnimations(), and Element::GetElementAnimations().

    This method would return true if any element in the document has an animation, or even if it recently had an animation that simply hasn't been garbage collected yet. Methinks Element::GetElementAnimations() provides what you need.

    zhenbang Jiang

    Thanks, I removed the extra DocumentAnimations::HasAnimations() helper.

    The current check now uses existing element-level animation state via Element::HasAnimations() instead. I did not use root_element->GetElementAnimations() alone because the CSS animations we need to detect may live on descendant elements inside the isolated SVG document, not on the outer <svg> root itself. Restricting the check to the root element would miss those cases and skip reuse/reset again.

    I also avoided Document::getAnimations()/Element::getAnimations() here because those paths update style/layout, while this check needs to stay lightweight during SVG image reuse.

    File third_party/blink/renderer/core/loader/image_loader.cc
    Line 907, Patchset 5:void ImageLoader::ResetAnimationOrDeferUntilAttach() {
    Fredrik Söderquist . resolved

    I don't think there's supposed to be any connection between resetting an animation and whether the animation is visible at all, so I suspect what you want to do here is rather to separate resetting the animation and potentially triggering invalidation (if needed).

    zhenbang Jiang

    Thanks, I split reset from invalidation.

    The reused SVG image is now reset independently of visibility or layout attachment. Paint invalidation is triggered separately only when a LayoutImageResource already exists, so reset no longer depends on whether the animation is currently visible, while attached layout objects still repaint when needed.

    File third_party/blink/renderer/core/svg/graphics/svg_image.cc
    Line 574, Patchset 5: document.GetDocumentAnimations().HasAnimations();
    Kevin Ellis . resolved

    Element already has a HasAnimations method.

    root_element->HasAnimations()

    zhenbang Jiang

    Done

    Open in Gerrit

    Related details

    Attention is currently required from:
    • Fredrik Söderquist
    • Kevin Ellis
    Submit Requirements:
      • requirement satisfiedCode-Coverage
      • requirement is not satisfiedCode-Owners
      • requirement is not satisfiedCode-Review
      • requirement is not satisfiedReview-Enforcement
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: comment
      Gerrit-Project: chromium/src
      Gerrit-Branch: main
      Gerrit-Change-Id: I662712aeac3f523dc9c39dbe4711a318cc6156d0
      Gerrit-Change-Number: 7760909
      Gerrit-PatchSet: 7
      Gerrit-Owner: zhenbang Jiang <edom...@gmail.com>
      Gerrit-Reviewer: Kevin Ellis <kev...@chromium.org>
      Gerrit-Reviewer: zhenbang Jiang <edom...@gmail.com>
      Gerrit-CC: Dirk Schulze <dsch...@chromium.org>
      Gerrit-CC: Fredrik Söderquist <f...@opera.com>
      Gerrit-CC: Menard, Alexis <alexis...@intel.com>
      Gerrit-CC: Nate Chapin <jap...@chromium.org>
      Gerrit-CC: Olga Gerchikov <gerc...@microsoft.com>
      Gerrit-CC: Stephen Chenney <sche...@chromium.org>
      Gerrit-Attention: Kevin Ellis <kev...@chromium.org>
      Gerrit-Attention: Fredrik Söderquist <f...@opera.com>
      Gerrit-Comment-Date: Thu, 23 Apr 2026 06:07:34 +0000
      Gerrit-HasComments: Yes
      Gerrit-Has-Labels: No
      Comment-In-Reply-To: Kevin Ellis <kev...@chromium.org>
      Comment-In-Reply-To: Fredrik Söderquist <f...@opera.com>
      satisfied_requirement
      unsatisfied_requirement
      open
      diffy

      Fredrik Söderquist (Gerrit)

      unread,
      Apr 23, 2026, 8:03:56 AMApr 23
      to zhenbang Jiang, Kevin Ellis, android-bu...@system.gserviceaccount.com, Chromium LUCI CQ, Menard, Alexis, chromium...@chromium.org, Dirk Schulze, Olga Gerchikov, Nate Chapin, Stephen Chenney, zol...@webkit.org, blink-revi...@chromium.org, blink-rev...@chromium.org, blink-revie...@chromium.org, blink-...@chromium.org, fmalit...@chromium.org, gavinp...@chromium.org, kouhe...@chromium.org, loading...@chromium.org, pdr+svgw...@chromium.org
      Attention needed from Kevin Ellis and zhenbang Jiang

      Fredrik Söderquist added 13 comments

      Commit Message
      Line 17, Patchset 7 (Latest):- Defer image animation reset until the LayoutImageResource is attached when
      Fredrik Söderquist . unresolved

      Break at 72.

      Line 30, Patchset 7 (Latest):Verification:
      - Open https://cdn.svgator.com/testing/reload-issue/index.html
      - Wait for the animation to finish
      - Refresh the page
      - Verify that the animation starts again from the initial frame instead of
      remaining on the final frame
      Fredrik Söderquist . unresolved

      This belongs in the bug.

      File third_party/blink/renderer/core/animation/document_animations.cc
      Line 303, Patchset 7 (Latest): !animation->effect() || !DynamicTo<CSSAnimation>(animation.Get())) {
      Fredrik Söderquist . unresolved

      Use `IsA<>`.

      File third_party/blink/renderer/core/html/lazy_load_image_observer_test.cc
      Line 483, Patchset 7 (Parent): Compositor().BeginFrame();
      Fredrik Söderquist . unresolved

      Why remove the `BeginFrame()` calls - I think this test wants them?

      File third_party/blink/renderer/core/layout/layout_image_resource.h
      Line 61, Patchset 7 (Latest): void InvalidatePaintForAnimationReset();
      Fredrik Söderquist . unresolved

      I think that `ResetAnimation()` is now dead, so you can remove it. (If there's not going to be a need for a flag, then you may need to keep it.)

      Also, I think this could just be named `InvalidatePaint()`. It should probably also do nothing if there's no `ImageResourceContent` attached. There's also the case where the `LayoutImageResource` is holding on to a different `ImageResourceContent` than the one currently active in the `ImageLoader`, in which case it ought to do nothing I think.

      File third_party/blink/renderer/core/loader/image_loader.cc
      Line 113, Patchset 7 (Latest): if (!image_content || !image_content->HasImage()) {
      return false;
      }
      auto* svg_image = DynamicTo<SVGImage>(image_content->GetImage());
      return svg_image && svg_image->ShouldResetForReuse();
      Fredrik Söderquist . unresolved

      I don't think that this belongs here. `ImageLoader` should just call `ResetAnimation` and leave any details to the `Image`.

      Line 902, Patchset 7 (Latest):void ImageLoader::ResetAnimationAndMaybeInvalidate() {
      Fredrik Söderquist . unresolved

      Just call this `ResetAnimation`.

      File third_party/blink/renderer/core/svg/graphics/svg_image.h
      Line 268, Patchset 7 (Latest): Vector<Persistent<CSSAnimation>> css_animations_pending_resume_;
      Fredrik Söderquist . unresolved

      This is not great. It's better to reverse the order here (i.e have a `Persistent<>` that holds a `GCedHeapVector<>`).

      File third_party/blink/renderer/core/svg/graphics/svg_image.cc
      Line 454, Patchset 7 (Latest): // Once this SVGImage has painted with animation content, future reload-like
      // reuse should continue to rewind it back to the initial frame.
      Fredrik Söderquist . unresolved

      We shouldn't care about users of our interface. Phrase this so that it makes sense from a local perspective.

      Line 456, Patchset 7 (Latest): was_ever_animated_ = was_ever_animated_ || IsCurrentlyAnimated();
      Fredrik Söderquist . unresolved

      So if the SVG document does not contain any animations, then on each paint we're going to traverse the entire document to look for them? Not great.

      Line 542, Patchset 7 (Latest): for (CSSAnimation* animation : animations_to_resume) {
      if (!animation || animation->ReplaceStateRemoved() ||
      !animation->effect()) {
      continue;
      }
      animation->Unpause();
      Fredrik Söderquist . unresolved

      This looks like knowledge that would be better to keep on the animation-side (probably together with the `Prepare...` call). Could probably wrap this up into a little helper class with a simple interface.

      Line 604, Patchset 7 (Latest): for (Node& node : NodeTraversal::DescendantsOf(*root_element)) {
      if (auto* element = DynamicTo<Element>(node);
      element && element->HasAnimations()) {
      Fredrik Söderquist . unresolved

      `ElementTraversal`?

      File third_party/blink/renderer/core/svg/graphics/svg_image_test.cc
      Line 353, Patchset 7 (Latest): if (timer->Value().IsActive()) {
      test::RunDelayedTasks(base::Milliseconds(1) +
      timer->Value().NextFireInterval());
      PumpFrame();
      }
      Fredrik Söderquist . unresolved

      This looks like bodge. We should know the state of the timer here.

      Open in Gerrit

      Related details

      Attention is currently required from:
      • Kevin Ellis
      • zhenbang Jiang
      Submit Requirements:
        • requirement satisfiedCode-Coverage
        • requirement is not satisfiedCode-Owners
        • requirement is not satisfiedCode-Review
        • requirement is not satisfiedNo-Unresolved-Comments
        • requirement is not satisfiedReview-Enforcement
        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
        Gerrit-MessageType: comment
        Gerrit-Project: chromium/src
        Gerrit-Branch: main
        Gerrit-Change-Id: I662712aeac3f523dc9c39dbe4711a318cc6156d0
        Gerrit-Change-Number: 7760909
        Gerrit-PatchSet: 7
        Gerrit-Owner: zhenbang Jiang <edom...@gmail.com>
        Gerrit-Reviewer: Kevin Ellis <kev...@chromium.org>
        Gerrit-Reviewer: zhenbang Jiang <edom...@gmail.com>
        Gerrit-CC: Dirk Schulze <dsch...@chromium.org>
        Gerrit-CC: Fredrik Söderquist <f...@opera.com>
        Gerrit-CC: Menard, Alexis <alexis...@intel.com>
        Gerrit-CC: Nate Chapin <jap...@chromium.org>
        Gerrit-CC: Olga Gerchikov <gerc...@microsoft.com>
        Gerrit-CC: Stephen Chenney <sche...@chromium.org>
        Gerrit-Attention: Kevin Ellis <kev...@chromium.org>
        Gerrit-Attention: zhenbang Jiang <edom...@gmail.com>
        Gerrit-Comment-Date: Thu, 23 Apr 2026 12:03:42 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: No
        satisfied_requirement
        unsatisfied_requirement
        open
        diffy

        zhenbang Jiang (Gerrit)

        unread,
        May 7, 2026, 9:01:44 AMMay 7
        to Kevin Ellis, android-bu...@system.gserviceaccount.com, Chromium LUCI CQ, Menard, Alexis, chromium...@chromium.org, Dirk Schulze, Fredrik Söderquist, Olga Gerchikov, Nate Chapin, Stephen Chenney, blink-reviews-p...@chromium.org, kinuko...@chromium.org, drott+bl...@chromium.org, fserb...@chromium.org, zol...@webkit.org, blink-revi...@chromium.org, blink-rev...@chromium.org, blink-revie...@chromium.org, blink-...@chromium.org, fmalit...@chromium.org, gavinp...@chromium.org, kouhe...@chromium.org, loading...@chromium.org, pdr+svgw...@chromium.org
        Attention needed from Fredrik Söderquist and Kevin Ellis

        zhenbang Jiang added 14 comments

        Patchset-level comments
        File-level comment, Patchset 14 (Latest):
        zhenbang Jiang . resolved

        PTAL, thanks~

        Commit Message
        Line 17, Patchset 7:- Defer image animation reset until the LayoutImageResource is attached when
        Fredrik Söderquist . resolved

        Break at 72.

        zhenbang Jiang

        Done


        - Open https://cdn.svgator.com/testing/reload-issue/index.html
        - Wait for the animation to finish
        - Refresh the page
        - Verify that the animation starts again from the initial frame instead of
        remaining on the final frame
        Fredrik Söderquist . resolved

        This belongs in the bug.

        zhenbang Jiang

        Done

        File third_party/blink/renderer/core/animation/document_animations.cc
        Line 303, Patchset 7: !animation->effect() || !DynamicTo<CSSAnimation>(animation.Get())) {
        Fredrik Söderquist . resolved

        Use `IsA<>`.

        zhenbang Jiang

        Done

        File third_party/blink/renderer/core/html/lazy_load_image_observer_test.cc
        Line 483, Patchset 7 (Parent): Compositor().BeginFrame();
        Fredrik Söderquist . resolved

        Why remove the `BeginFrame()` calls - I think this test wants them?

        zhenbang Jiang

        My understanding is that the ordinary image path no longer goes through layout_object_->SetShouldDoFullPaintInvalidation() , so after switching loading from lazy to eager there is no longer a naturally pending frame at this point. If we keep the first BeginFrame() here, it now hits the DCHECK .

        It seems that this test previously consumed a frame here only because it was relying on an old shared post-update side effect: image updates would also trigger a full paint invalidation. With this change, that reset/invalidation behavior is intentionally narrowed to SVG images that actually need to be rewound on reuse/reload, so that unrelated image resources are not also reset/invalidated and the fix stays scoped to the SVG animation issue itself.

        So my understanding is that keeping the first BeginFrame() here would effectively preserve a dependency on an old shared side effect, rather than on behavior that is essential to the core intent of this test.

        File third_party/blink/renderer/core/layout/layout_image_resource.h
        Line 61, Patchset 7: void InvalidatePaintForAnimationReset();
        Fredrik Söderquist . resolved

        I think that `ResetAnimation()` is now dead, so you can remove it. (If there's not going to be a need for a flag, then you may need to keep it.)

        Also, I think this could just be named `InvalidatePaint()`. It should probably also do nothing if there's no `ImageResourceContent` attached. There's also the case where the `LayoutImageResource` is holding on to a different `ImageResourceContent` than the one currently active in the `ImageLoader`, in which case it ought to do nothing I think.

        zhenbang Jiang

        Done

        File third_party/blink/renderer/core/loader/image_loader.cc
        Line 113, Patchset 7: if (!image_content || !image_content->HasImage()) {

        return false;
        }
        auto* svg_image = DynamicTo<SVGImage>(image_content->GetImage());
        return svg_image && svg_image->ShouldResetForReuse();
        Fredrik Söderquist . resolved

        I don't think that this belongs here. `ImageLoader` should just call `ResetAnimation` and leave any details to the `Image`.

        zhenbang Jiang

        Done

        Line 902, Patchset 7:void ImageLoader::ResetAnimationAndMaybeInvalidate() {
        Fredrik Söderquist . resolved

        Just call this `ResetAnimation`.

        zhenbang Jiang

        Done

        File third_party/blink/renderer/core/svg/graphics/svg_image.h
        Line 268, Patchset 7: Vector<Persistent<CSSAnimation>> css_animations_pending_resume_;
        Fredrik Söderquist . resolved

        This is not great. It's better to reverse the order here (i.e have a `Persistent<>` that holds a `GCedHeapVector<>`).

        zhenbang Jiang

        Done

        File third_party/blink/renderer/core/svg/graphics/svg_image.cc
        Line 454, Patchset 7: // Once this SVGImage has painted with animation content, future reload-like

        // reuse should continue to rewind it back to the initial frame.
        Fredrik Söderquist . resolved

        We shouldn't care about users of our interface. Phrase this so that it makes sense from a local perspective.

        zhenbang Jiang

        Done

        Line 456, Patchset 7: was_ever_animated_ = was_ever_animated_ || IsCurrentlyAnimated();
        Fredrik Söderquist . resolved

        So if the SVG document does not contain any animations, then on each paint we're going to traverse the entire document to look for them? Not great.

        zhenbang Jiang

        Done

        Line 542, Patchset 7: for (CSSAnimation* animation : animations_to_resume) {

        if (!animation || animation->ReplaceStateRemoved() ||
        !animation->effect()) {
        continue;
        }
        animation->Unpause();
        Fredrik Söderquist . resolved

        This looks like knowledge that would be better to keep on the animation-side (probably together with the `Prepare...` call). Could probably wrap this up into a little helper class with a simple interface.

        zhenbang Jiang

        Done

        Line 604, Patchset 7: for (Node& node : NodeTraversal::DescendantsOf(*root_element)) {

        if (auto* element = DynamicTo<Element>(node);
        element && element->HasAnimations()) {
        Fredrik Söderquist . resolved

        `ElementTraversal`?

        zhenbang Jiang

        Done

        File third_party/blink/renderer/core/svg/graphics/svg_image_test.cc
        Line 353, Patchset 7: if (timer->Value().IsActive()) {

        test::RunDelayedTasks(base::Milliseconds(1) +
        timer->Value().NextFireInterval());
        PumpFrame();
        }
        Fredrik Söderquist . resolved

        This looks like bodge. We should know the state of the timer here.

        zhenbang Jiang

        Done

        Open in Gerrit

        Related details

        Attention is currently required from:
        • Fredrik Söderquist
        • Kevin Ellis
        Submit Requirements:
          • requirement satisfiedCode-Coverage
          • requirement is not satisfiedCode-Owners
          • requirement is not satisfiedCode-Review
          • requirement is not satisfiedReview-Enforcement
          Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
          Gerrit-MessageType: comment
          Gerrit-Project: chromium/src
          Gerrit-Branch: main
          Gerrit-Change-Id: I662712aeac3f523dc9c39dbe4711a318cc6156d0
          Gerrit-Change-Number: 7760909
          Gerrit-PatchSet: 14
          Gerrit-Owner: zhenbang Jiang <edom...@gmail.com>
          Gerrit-Reviewer: Kevin Ellis <kev...@chromium.org>
          Gerrit-Reviewer: zhenbang Jiang <edom...@gmail.com>
          Gerrit-CC: Dirk Schulze <dsch...@chromium.org>
          Gerrit-CC: Fredrik Söderquist <f...@opera.com>
          Gerrit-CC: Menard, Alexis <alexis...@intel.com>
          Gerrit-CC: Nate Chapin <jap...@chromium.org>
          Gerrit-CC: Olga Gerchikov <gerc...@microsoft.com>
          Gerrit-CC: Stephen Chenney <sche...@chromium.org>
          Gerrit-Attention: Kevin Ellis <kev...@chromium.org>
          Gerrit-Attention: Fredrik Söderquist <f...@opera.com>
          Gerrit-Comment-Date: Thu, 07 May 2026 13:01:27 +0000
          Gerrit-HasComments: Yes
          Gerrit-Has-Labels: No
          Comment-In-Reply-To: Fredrik Söderquist <f...@opera.com>
          satisfied_requirement
          unsatisfied_requirement
          open
          diffy

          Kevin Ellis (Gerrit)

          unread,
          May 7, 2026, 10:52:30 AMMay 7
          to zhenbang Jiang, android-bu...@system.gserviceaccount.com, Chromium LUCI CQ, Menard, Alexis, chromium...@chromium.org, Dirk Schulze, Fredrik Söderquist, Olga Gerchikov, Nate Chapin, Stephen Chenney, blink-reviews-p...@chromium.org, kinuko...@chromium.org, drott+bl...@chromium.org, fserb...@chromium.org, zol...@webkit.org, blink-revi...@chromium.org, blink-rev...@chromium.org, blink-revie...@chromium.org, blink-...@chromium.org, fmalit...@chromium.org, gavinp...@chromium.org, kouhe...@chromium.org, loading...@chromium.org, pdr+svgw...@chromium.org
          Attention needed from Fredrik Söderquist and zhenbang Jiang

          Kevin Ellis added 3 comments

          File third_party/blink/renderer/core/animation/document_animations.cc
          Line 337, Patchset 14 (Latest): !animation->effect() || !IsA<CSSAnimation>(animation.Get())) {
          Kevin Ellis . unresolved

          Insufficient check. A CSSAnimation without an owning element is treated as a programatic animation. This happens for example if you grab a reference to a CSS animation from JavaScript and call play() on it once no longer associated with a style rule. You need to also check that OwningElement is not null.

          Line 341, Patchset 14 (Latest): animation->SetCurrentTimeInternal(AnimationTimeDelta());
          Kevin Ellis . unresolved

          This is also affecting CSS animations on elements which have no SVG content. This is also assuming the animation has a monotonic (time-based) timeline, and is problematic for animations using scroll-timelines.

          File third_party/blink/renderer/core/animation/document_animations_test.cc
          Line 148, Patchset 14 (Latest):TEST_F(DocumentAnimationsTest, PrepareAnimationsForSVGImageResetAndResume) {
          Kevin Ellis . unresolved

          No SVG in the test body. This seems to suggest a broader effect on animationed content.

          Open in Gerrit

          Related details

          Attention is currently required from:
          • Fredrik Söderquist
          • zhenbang Jiang
          Submit Requirements:
            • requirement satisfiedCode-Coverage
            • requirement is not satisfiedCode-Owners
            • requirement is not satisfiedCode-Review
            • requirement is not satisfiedNo-Unresolved-Comments
            • requirement is not satisfiedReview-Enforcement
            Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
            Gerrit-MessageType: comment
            Gerrit-Project: chromium/src
            Gerrit-Branch: main
            Gerrit-Change-Id: I662712aeac3f523dc9c39dbe4711a318cc6156d0
            Gerrit-Change-Number: 7760909
            Gerrit-PatchSet: 14
            Gerrit-Owner: zhenbang Jiang <edom...@gmail.com>
            Gerrit-Reviewer: Kevin Ellis <kev...@chromium.org>
            Gerrit-Reviewer: zhenbang Jiang <edom...@gmail.com>
            Gerrit-CC: Dirk Schulze <dsch...@chromium.org>
            Gerrit-CC: Fredrik Söderquist <f...@opera.com>
            Gerrit-CC: Menard, Alexis <alexis...@intel.com>
            Gerrit-CC: Nate Chapin <jap...@chromium.org>
            Gerrit-CC: Olga Gerchikov <gerc...@microsoft.com>
            Gerrit-CC: Stephen Chenney <sche...@chromium.org>
            Gerrit-Attention: Fredrik Söderquist <f...@opera.com>
            Gerrit-Attention: zhenbang Jiang <edom...@gmail.com>
            Gerrit-Comment-Date: Thu, 07 May 2026 14:52:22 +0000
            Gerrit-HasComments: Yes
            Gerrit-Has-Labels: No
            satisfied_requirement
            unsatisfied_requirement
            open
            diffy

            Fredrik Söderquist (Gerrit)

            unread,
            May 7, 2026, 11:25:45 AMMay 7
            to zhenbang Jiang, Kevin Ellis, android-bu...@system.gserviceaccount.com, Chromium LUCI CQ, Menard, Alexis, chromium...@chromium.org, Dirk Schulze, Olga Gerchikov, Nate Chapin, Stephen Chenney, blink-reviews-p...@chromium.org, kinuko...@chromium.org, drott+bl...@chromium.org, fserb...@chromium.org, zol...@webkit.org, blink-revi...@chromium.org, blink-rev...@chromium.org, blink-revie...@chromium.org, blink-...@chromium.org, fmalit...@chromium.org, gavinp...@chromium.org, kouhe...@chromium.org, loading...@chromium.org, pdr+svgw...@chromium.org
            Attention needed from zhenbang Jiang

            Fredrik Söderquist added 10 comments

            File third_party/blink/renderer/core/animation/document_animations.h
            Line 55, Patchset 14 (Latest):class CORE_EXPORT SVGImageAnimationReset final
            Fredrik Söderquist . unresolved

            This is a weird name. Maybe it should be something like `AnimationsToRewind`, `AnimationsToReset` or something like that?

            File third_party/blink/renderer/core/animation/document_animations.cc
            Line 329, Patchset 14 (Latest): // SVGImage rewind happens on the main thread without a JS context. Rewind CSS
            // animations before the next paint and defer resuming them until after that
            // paint so the first sampled frame stays at time=0. Explicitly paused
            Fredrik Söderquist . unresolved

            Talking about paint here seems a bit weird - this is a concept for the user of this function/class.

            File third_party/blink/renderer/core/html/lazy_load_image_observer_test.cc
            Line 483, Patchset 7 (Parent): Compositor().BeginFrame();
            Fredrik Söderquist . unresolved

            Why remove the `BeginFrame()` calls - I think this test wants them?

            zhenbang Jiang

            My understanding is that the ordinary image path no longer goes through layout_object_->SetShouldDoFullPaintInvalidation() , so after switching loading from lazy to eager there is no longer a naturally pending frame at this point. If we keep the first BeginFrame() here, it now hits the DCHECK .

            It seems that this test previously consumed a frame here only because it was relying on an old shared post-update side effect: image updates would also trigger a full paint invalidation. With this change, that reset/invalidation behavior is intentionally narrowed to SVG images that actually need to be rewound on reuse/reload, so that unrelated image resources are not also reset/invalidated and the fix stays scoped to the SVG animation issue itself.

            So my understanding is that keeping the first BeginFrame() here would effectively preserve a dependency on an old shared side effect, rather than on behavior that is essential to the core intent of this test.

            Fredrik Söderquist

            Other images will need the paint invalidation as well - `BitmapImage::ResetAnimation()` for example has side-effects and would need to repaint to signal the "rewind".

            File third_party/blink/renderer/core/loader/image_loader.cc
            Line 900, Patchset 14 (Latest): if (!image->ShouldResetAnimationForReuse()) {
            return;
            }
            Fredrik Söderquist . unresolved

            This shouldn't be here. Let the `ResetAnimation()` implementations handle this.

            File third_party/blink/renderer/core/svg/graphics/svg_image.h
            Line 272, Patchset 14 (Latest): bool was_ever_animated_ = false;
            mutable std::optional<bool> contains_animations_;
            bool contains_animations_cache_initialized_ = false;
            Fredrik Söderquist . unresolved

            This looks complicated - and related to each other. Could we do a small state machine enum instead? `has_pending_timeline_rewind_` might fit in that as well.

            Line 115, Patchset 14 (Latest): bool ShouldResetForReuse() const;
            Fredrik Söderquist . unresolved

            Shouldn't be public. (If it should exist at all.)

            File third_party/blink/renderer/core/svg/graphics/svg_image.cc
            Line 103, Patchset 14 (Latest): css_animation_reset_(MakeGarbageCollected<SVGImageAnimationReset>()),
            Fredrik Söderquist . unresolved

            Do we need to allocate this up-front? Doesn't seem so.

            Line 533, Patchset 14 (Latest): .GetDocumentAnimations()
            .PrepareAnimationsForSVGImageReset(*css_animation_reset_);
            }
            has_pending_timeline_rewind_ = false;
            }

            void SVGImage::StartAnimation() {
            SVGSVGElement* root_element = RootElement();
            if (!root_element)
            return;
            css_animation_reset_->Resume();
            Fredrik Söderquist . unresolved

            Remind me again why we can't just rewind in `FlushPendingTimelineRewind()`. Are there side-effects?

            Line 595, Patchset 14 (Latest): bool contains_animations = false;
            SVGSVGElement* root_element = RootElement();
            if (root_element) {
            const Document& document = root_element->GetDocument();
            if (HasSmilAnimations(document) || root_element->HasAnimations()) {
            contains_animations = true;
            } else {
            for (Element& element : ElementTraversal::DescendantsOf(*root_element)) {
            if (element.HasAnimations()) {
            contains_animations = true;
            break;
            }
            }
            }
            }

            if (contains_animations || contains_animations_cache_initialized_) {
            contains_animations_ = contains_animations;
            }
            Fredrik Söderquist . unresolved

            It might be better to separate the cached version from the uncached version. It may also be worth considering to handle this in `ServiceAnimations` (where we should have a good idea about what animations are going to run).

            Line 748, Patchset 14 (Latest): contains_animations_.reset();
            contains_animations_cache_initialized_ = false;
            was_ever_animated_ = false;
            has_pending_timeline_rewind_ = false;
            Fredrik Söderquist . unresolved

            Use default initializers.

            Open in Gerrit

            Related details

            Attention is currently required from:
            • zhenbang Jiang
            Gerrit-Attention: zhenbang Jiang <edom...@gmail.com>
            Gerrit-Comment-Date: Thu, 07 May 2026 15:25:32 +0000
            Gerrit-HasComments: Yes
            Gerrit-Has-Labels: No
            Comment-In-Reply-To: Fredrik Söderquist <f...@opera.com>
            Comment-In-Reply-To: zhenbang Jiang <edom...@gmail.com>
            satisfied_requirement
            unsatisfied_requirement
            open
            diffy

            Philip Rogers (Gerrit)

            unread,
            May 7, 2026, 5:59:02 PMMay 7
            to zhenbang Jiang, Kevin Ellis, android-bu...@system.gserviceaccount.com, Chromium LUCI CQ, Menard, Alexis, chromium...@chromium.org, Dirk Schulze, Fredrik Söderquist, Olga Gerchikov, Nate Chapin, Stephen Chenney, blink-reviews-p...@chromium.org, kinuko...@chromium.org, drott+bl...@chromium.org, fserb...@chromium.org, zol...@webkit.org, blink-revi...@chromium.org, blink-rev...@chromium.org, blink-revie...@chromium.org, blink-...@chromium.org, fmalit...@chromium.org, gavinp...@chromium.org, kouhe...@chromium.org, loading...@chromium.org, pdr+svgw...@chromium.org
            Attention needed from zhenbang Jiang

            Philip Rogers added 2 comments

            File third_party/blink/renderer/core/animation/document_animations.cc
            Line 337, Patchset 14 (Latest): !animation->effect() || !IsA<CSSAnimation>(animation.Get())) {
            Kevin Ellis . unresolved

            Insufficient check. A CSSAnimation without an owning element is treated as a programatic animation. This happens for example if you grab a reference to a CSS animation from JavaScript and call play() on it once no longer associated with a style rule. You need to also check that OwningElement is not null.

            Philip Rogers

            SVG images don't support javascript, so is this an issue?

            Line 341, Patchset 14 (Latest): animation->SetCurrentTimeInternal(AnimationTimeDelta());
            Kevin Ellis . unresolved

            This is also affecting CSS animations on elements which have no SVG content. This is also assuming the animation has a monotonic (time-based) timeline, and is problematic for animations using scroll-timelines.

            Philip Rogers

            Can you have scroll timeline animations in SVG? SVG images aren't interactive, so I think this isn't an issue.

            Gerrit-CC: Philip Rogers <p...@chromium.org>
            Gerrit-CC: Stephen Chenney <sche...@chromium.org>
            Gerrit-Attention: zhenbang Jiang <edom...@gmail.com>
            Gerrit-Comment-Date: Thu, 07 May 2026 21:58:53 +0000
            Gerrit-HasComments: Yes
            Gerrit-Has-Labels: No
            Comment-In-Reply-To: Kevin Ellis <kev...@chromium.org>
            satisfied_requirement
            unsatisfied_requirement
            open
            diffy

            zhenbang Jiang (Gerrit)

            unread,
            May 13, 2026, 11:59:34 PMMay 13
            to Philip Rogers, Kevin Ellis, android-bu...@system.gserviceaccount.com, Chromium LUCI CQ, Menard, Alexis, chromium...@chromium.org, Dirk Schulze, Fredrik Söderquist, Olga Gerchikov, Nate Chapin, Stephen Chenney, blink-reviews-p...@chromium.org, kinuko...@chromium.org, drott+bl...@chromium.org, fserb...@chromium.org, zol...@webkit.org, blink-revi...@chromium.org, blink-rev...@chromium.org, blink-revie...@chromium.org, blink-...@chromium.org, fmalit...@chromium.org, gavinp...@chromium.org, kouhe...@chromium.org, loading...@chromium.org, pdr+svgw...@chromium.org
            Attention needed from Fredrik Söderquist, Kevin Ellis and Philip Rogers

            zhenbang Jiang added 14 comments

            zhenbang Jiang . resolved

            PTAL, thanks~

            File third_party/blink/renderer/core/animation/document_animations.h
            Line 55, Patchset 14:class CORE_EXPORT SVGImageAnimationReset final
            Fredrik Söderquist . resolved

            This is a weird name. Maybe it should be something like `AnimationsToRewind`, `AnimationsToReset` or something like that?

            zhenbang Jiang

            Done

            File third_party/blink/renderer/core/animation/document_animations.cc
            Line 329, Patchset 14: // SVGImage rewind happens on the main thread without a JS context. Rewind CSS

            // animations before the next paint and defer resuming them until after that
            // paint so the first sampled frame stays at time=0. Explicitly paused
            Fredrik Söderquist . resolved

            Talking about paint here seems a bit weird - this is a concept for the user of this function/class.

            zhenbang Jiang

            Done

            Line 337, Patchset 14: !animation->effect() || !IsA<CSSAnimation>(animation.Get())) {
            Kevin Ellis . resolved

            Insufficient check. A CSSAnimation without an owning element is treated as a programatic animation. This happens for example if you grab a reference to a CSS animation from JavaScript and call play() on it once no longer associated with a style rule. You need to also check that OwningElement is not null.

            Philip Rogers

            SVG images don't support javascript, so is this an issue?

            zhenbang Jiang

            Even though SVG images do not support JavaScript, this helper lives on DocumentAnimations and should only operate on CSS animations that are still style-owned.

            A CSSAnimation without an OwningElement is no longer associated with a style rule and should not be rewound/resumed by this SVG image reset path. I added the OwningElement() check so those animations are skipped.

            Line 341, Patchset 14: animation->SetCurrentTimeInternal(AnimationTimeDelta());
            Kevin Ellis . resolved

            This is also affecting CSS animations on elements which have no SVG content. This is also assuming the animation has a monotonic (time-based) timeline, and is problematic for animations using scroll-timelines.

            Philip Rogers

            Can you have scroll timeline animations in SVG? SVG images aren't interactive, so I think this isn't an issue.

            zhenbang Jiang

            I added two filters to guard against unintended side effects:

            Skip non-monotonic timelines before setting current time to 0, so scroll/view timelines are not affected.
            Only handle CSS animations whose KeyframeEffect target is an SVG element, so animations on non-SVG content are not rewound by this SVG image reset path.

            Together with the OwningElement() check, this keeps the helper limited to style-owned CSS animations on SVG content using time-based timelines.

            File third_party/blink/renderer/core/animation/document_animations_test.cc
            Line 148, Patchset 14:TEST_F(DocumentAnimationsTest, PrepareAnimationsForSVGImageResetAndResume) {
            Kevin Ellis . resolved

            No SVG in the test body. This seems to suggest a broader effect on animationed content.

            zhenbang Jiang

            Done

            File third_party/blink/renderer/core/html/lazy_load_image_observer_test.cc
            Line 483, Patchset 7 (Parent): Compositor().BeginFrame();
            Fredrik Söderquist . resolved

            Why remove the `BeginFrame()` calls - I think this test wants them?

            zhenbang Jiang

            My understanding is that the ordinary image path no longer goes through layout_object_->SetShouldDoFullPaintInvalidation() , so after switching loading from lazy to eager there is no longer a naturally pending frame at this point. If we keep the first BeginFrame() here, it now hits the DCHECK .

            It seems that this test previously consumed a frame here only because it was relying on an old shared post-update side effect: image updates would also trigger a full paint invalidation. With this change, that reset/invalidation behavior is intentionally narrowed to SVG images that actually need to be rewound on reuse/reload, so that unrelated image resources are not also reset/invalidated and the fix stays scoped to the SVG animation issue itself.

            So my understanding is that keeping the first BeginFrame() here would effectively preserve a dependency on an old shared side effect, rather than on behavior that is essential to the core intent of this test.

            Fredrik Söderquist

            Other images will need the paint invalidation as well - `BitmapImage::ResetAnimation()` for example has side-effects and would need to repaint to signal the "rewind".

            zhenbang Jiang

            Thanks, this is a helpful point.

            I kept the generic protocol in ImageLoader: it calls ResetAnimation(kForReuse), and invalidates paint only if the implementation returns true. This keeps the type-specific decision inside each Image implementation.

            I did try making BitmapImage participate in this reuse reset path, but that regressed image-animation-paused-exact-frame.tentative.html. As I understand it, that test relies on an animated GIF naturally reaching the green frame before image-animation is set to paused; resetting BitmapImage from this hook rewinds the GIF and changes the frame that ends up being paused.

            So BitmapImage currently returns false for kForReuse, not because its reset side effects would not need repaint, but because this particular ImageLoader reuse hook should not reset ordinary GIF playback. SVGImage returns true when it actually needs to rewind for the SVG image reuse/reload case. Other Image implementations can still opt in by returning true if their kForReuse reset is appropriate and has repaint-visible side effects.

            File third_party/blink/renderer/core/loader/image_loader.cc
            Line 900, Patchset 14: if (!image->ShouldResetAnimationForReuse()) {
            return;
            }
            Fredrik Söderquist . resolved

            This shouldn't be here. Let the `ResetAnimation()` implementations handle this.

            zhenbang Jiang

            Done

            File third_party/blink/renderer/core/svg/graphics/svg_image.h
            Line 272, Patchset 14: bool was_ever_animated_ = false;

            mutable std::optional<bool> contains_animations_;
            bool contains_animations_cache_initialized_ = false;
            Fredrik Söderquist . resolved

            This looks complicated - and related to each other. Could we do a small state machine enum instead? `has_pending_timeline_rewind_` might fit in that as well.

            zhenbang Jiang

            Done

            Line 115, Patchset 14: bool ShouldResetForReuse() const;
            Fredrik Söderquist . resolved

            Shouldn't be public. (If it should exist at all.)

            zhenbang Jiang

            Done

            File third_party/blink/renderer/core/svg/graphics/svg_image.cc
            Line 103, Patchset 14: css_animation_reset_(MakeGarbageCollected<SVGImageAnimationReset>()),
            Fredrik Söderquist . resolved

            Do we need to allocate this up-front? Doesn't seem so.

            zhenbang Jiang

            Done

            Line 533, Patchset 14: .GetDocumentAnimations()

            .PrepareAnimationsForSVGImageReset(*css_animation_reset_);
            }
            has_pending_timeline_rewind_ = false;
            }

            void SVGImage::StartAnimation() {
            SVGSVGElement* root_element = RootElement();
            if (!root_element)
            return;
            css_animation_reset_->Resume();
            Fredrik Söderquist . resolved

            Remind me again why we can't just rewind in `FlushPendingTimelineRewind()`. Are there side-effects?

            zhenbang Jiang

            We shouldn’t only do the timeline rewind within FlushPendingTimelineRewind().If we did, the very first frame rendered after reset could already be past time=0, which would cause the animation to fail to visibly restart from the start.
            This happens because the lifecycle update in the same draw pass can advance any still-running CSS animations right before the reset frame is sampled.To address this, I’ve added logic to also rewind and temporarily pause active CSS animations at this point.These animations will be resumed once playback restarts, while any animations that were already paused will stay in their paused state.

            Line 595, Patchset 14: bool contains_animations = false;

            SVGSVGElement* root_element = RootElement();
            if (root_element) {
            const Document& document = root_element->GetDocument();
            if (HasSmilAnimations(document) || root_element->HasAnimations()) {
            contains_animations = true;
            } else {
            for (Element& element : ElementTraversal::DescendantsOf(*root_element)) {
            if (element.HasAnimations()) {
            contains_animations = true;
            break;
            }
            }
            }
            }

            if (contains_animations || contains_animations_cache_initialized_) {
            contains_animations_ = contains_animations;
            }
            Fredrik Söderquist . resolved

            It might be better to separate the cached version from the uncached version. It may also be worth considering to handle this in `ServiceAnimations` (where we should have a good idea about what animations are going to run).

            zhenbang Jiang

            Done

            Line 748, Patchset 14: contains_animations_.reset();

            contains_animations_cache_initialized_ = false;
            was_ever_animated_ = false;
            has_pending_timeline_rewind_ = false;
            Fredrik Söderquist . resolved

            Use default initializers.

            zhenbang Jiang

            Done

            Open in Gerrit

            Related details

            Attention is currently required from:
            • Fredrik Söderquist
            • Kevin Ellis
            • Philip Rogers
            Submit Requirements:
              • requirement satisfiedCode-Coverage
              • requirement is not satisfiedCode-Owners
              • requirement is not satisfiedCode-Review
              • requirement is not satisfiedReview-Enforcement
              Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
              Gerrit-MessageType: comment
              Gerrit-Project: chromium/src
              Gerrit-Branch: main
              Gerrit-Change-Id: I662712aeac3f523dc9c39dbe4711a318cc6156d0
              Gerrit-Change-Number: 7760909
              Gerrit-PatchSet: 16
              Gerrit-Owner: zhenbang Jiang <edom...@gmail.com>
              Gerrit-Reviewer: Kevin Ellis <kev...@chromium.org>
              Gerrit-Reviewer: zhenbang Jiang <edom...@gmail.com>
              Gerrit-CC: Dirk Schulze <dsch...@chromium.org>
              Gerrit-CC: Fredrik Söderquist <f...@opera.com>
              Gerrit-CC: Menard, Alexis <alexis...@intel.com>
              Gerrit-CC: Nate Chapin <jap...@chromium.org>
              Gerrit-CC: Olga Gerchikov <gerc...@microsoft.com>
              Gerrit-CC: Philip Rogers <p...@chromium.org>
              Gerrit-CC: Stephen Chenney <sche...@chromium.org>
              Gerrit-Attention: Kevin Ellis <kev...@chromium.org>
              Gerrit-Attention: Philip Rogers <p...@chromium.org>
              Gerrit-Attention: Fredrik Söderquist <f...@opera.com>
              Gerrit-Comment-Date: Thu, 14 May 2026 03:59:29 +0000
              Gerrit-HasComments: Yes
              Gerrit-Has-Labels: No
              Comment-In-Reply-To: Kevin Ellis <kev...@chromium.org>
              Comment-In-Reply-To: Philip Rogers <p...@chromium.org>
              satisfied_requirement
              unsatisfied_requirement
              open
              diffy

              Kevin Ellis (Gerrit)

              unread,
              May 14, 2026, 1:50:44 PMMay 14
              to zhenbang Jiang, Philip Rogers, android-bu...@system.gserviceaccount.com, Chromium LUCI CQ, Menard, Alexis, chromium...@chromium.org, Dirk Schulze, Fredrik Söderquist, Olga Gerchikov, Nate Chapin, Stephen Chenney, blink-reviews-p...@chromium.org, kinuko...@chromium.org, drott+bl...@chromium.org, fserb...@chromium.org, zol...@webkit.org, blink-revi...@chromium.org, blink-rev...@chromium.org, blink-revie...@chromium.org, blink-...@chromium.org, fmalit...@chromium.org, gavinp...@chromium.org, kouhe...@chromium.org, loading...@chromium.org, pdr+svgw...@chromium.org
              Attention needed from Fredrik Söderquist, Philip Rogers and zhenbang Jiang

              Kevin Ellis added 3 comments

              File third_party/blink/renderer/core/animation/document_animations.cc
              Line 337, Patchset 14: !animation->effect() || !IsA<CSSAnimation>(animation.Get())) {
              Kevin Ellis . unresolved

              Insufficient check. A CSSAnimation without an owning element is treated as a programatic animation. This happens for example if you grab a reference to a CSS animation from JavaScript and call play() on it once no longer associated with a style rule. You need to also check that OwningElement is not null.

              Philip Rogers

              SVG images don't support javascript, so is this an issue?

              zhenbang Jiang

              Even though SVG images do not support JavaScript, this helper lives on DocumentAnimations and should only operate on CSS animations that are still style-owned.

              A CSSAnimation without an OwningElement is no longer associated with a style rule and should not be rewound/resumed by this SVG image reset path. I added the OwningElement() check so those animations are skipped.

              Kevin Ellis

              A CSS animation / transition or programmatic animation can affect an SVG element if the SVG is not embedded in an IMG tag, but rather injected inline. Comments aside, this method is not differentiating how the SVG in being included. It might be as simple as adding a check that the root element is <SVG>.

              That said, document_animations_test.cc is using an inline SVG. Should the reset be applied in this case or only in SVG within an IMG case.

              If we ensure that this method is only being used for isolated SVG document's then the check for owning element and timeline time, or even the effect target are indeed irrelevant.

              Line 341, Patchset 14: animation->SetCurrentTimeInternal(AnimationTimeDelta());
              Kevin Ellis . resolved

              This is also affecting CSS animations on elements which have no SVG content. This is also assuming the animation has a monotonic (time-based) timeline, and is problematic for animations using scroll-timelines.

              Philip Rogers

              Can you have scroll timeline animations in SVG? SVG images aren't interactive, so I think this isn't an issue.

              zhenbang Jiang

              I added two filters to guard against unintended side effects:

              Skip non-monotonic timelines before setting current time to 0, so scroll/view timelines are not affected.
              Only handle CSS animations whose KeyframeEffect target is an SVG element, so animations on non-SVG content are not rewound by this SVG image reset path.

              Together with the OwningElement() check, this keeps the helper limited to style-owned CSS animations on SVG content using time-based timelines.

              Kevin Ellis

              @pdr: You could have an animation affecting an SVG that is linked to a scroll-timeline attached to the enclosing scroll container.

              Line 341, Patchset 16 (Latest): !To<CSSAnimation>(*animation).OwningElement()) {
              Kevin Ellis . unresolved
              ```
              ... !animation->IsCSSAnimation() || !animation->OwningElement() ...
              ```
              No need to cast.
              Open in Gerrit

              Related details

              Attention is currently required from:
              • Fredrik Söderquist
              • Philip Rogers
              • zhenbang Jiang
              Submit Requirements:
                • requirement satisfiedCode-Coverage
                • requirement is not satisfiedCode-Owners
                • requirement is not satisfiedCode-Review
                • requirement is not satisfiedNo-Unresolved-Comments
                • requirement is not satisfiedReview-Enforcement
                Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                Gerrit-MessageType: comment
                Gerrit-Project: chromium/src
                Gerrit-Branch: main
                Gerrit-Change-Id: I662712aeac3f523dc9c39dbe4711a318cc6156d0
                Gerrit-Change-Number: 7760909
                Gerrit-PatchSet: 16
                Gerrit-Owner: zhenbang Jiang <edom...@gmail.com>
                Gerrit-Reviewer: Kevin Ellis <kev...@chromium.org>
                Gerrit-Reviewer: zhenbang Jiang <edom...@gmail.com>
                Gerrit-CC: Dirk Schulze <dsch...@chromium.org>
                Gerrit-CC: Fredrik Söderquist <f...@opera.com>
                Gerrit-CC: Menard, Alexis <alexis...@intel.com>
                Gerrit-CC: Nate Chapin <jap...@chromium.org>
                Gerrit-CC: Olga Gerchikov <gerc...@microsoft.com>
                Gerrit-CC: Philip Rogers <p...@chromium.org>
                Gerrit-CC: Stephen Chenney <sche...@chromium.org>
                Gerrit-Attention: Philip Rogers <p...@chromium.org>
                Gerrit-Attention: Fredrik Söderquist <f...@opera.com>
                Gerrit-Attention: zhenbang Jiang <edom...@gmail.com>
                Gerrit-Comment-Date: Thu, 14 May 2026 17:50:36 +0000
                Gerrit-HasComments: Yes
                Gerrit-Has-Labels: No
                Comment-In-Reply-To: Kevin Ellis <kev...@chromium.org>
                Comment-In-Reply-To: Philip Rogers <p...@chromium.org>
                Comment-In-Reply-To: zhenbang Jiang <edom...@gmail.com>
                satisfied_requirement
                unsatisfied_requirement
                open
                diffy

                Kevin Ellis (Gerrit)

                unread,
                May 14, 2026, 2:16:50 PMMay 14
                to zhenbang Jiang, Philip Rogers, android-bu...@system.gserviceaccount.com, Chromium LUCI CQ, Menard, Alexis, chromium...@chromium.org, Dirk Schulze, Fredrik Söderquist, Olga Gerchikov, Nate Chapin, Stephen Chenney, blink-reviews-p...@chromium.org, kinuko...@chromium.org, drott+bl...@chromium.org, fserb...@chromium.org, zol...@webkit.org, blink-revi...@chromium.org, blink-rev...@chromium.org, blink-revie...@chromium.org, blink-...@chromium.org, fmalit...@chromium.org, gavinp...@chromium.org, kouhe...@chromium.org, loading...@chromium.org, pdr+svgw...@chromium.org
                Attention needed from Fredrik Söderquist, Philip Rogers and zhenbang Jiang

                Kevin Ellis added 2 comments

                File third_party/blink/renderer/core/animation/document_animations.cc
                Line 328, Patchset 16 (Latest): SVGImageAnimationsToReset& animations_to_reset) {
                Kevin Ellis . unresolved

                To alleviate my concerns over calling the method on the wrong type of document and applying too generally, can you add a check.

                ```
                DCHECK(document_.View()->GetFrame().GetChromeClient().IsIsolatedSVGChromeClient());
                ```

                If we ensure that we are not calling this method on the document associated with the HTML element then all of the safeguards for timeline time and the type of animation are no longer required. My earlier reservations were based on the fact that this could result in non-isolated animations being reset.

                Line 341, Patchset 16 (Latest): !To<CSSAnimation>(*animation).OwningElement()) {
                Kevin Ellis . resolved
                ```
                ... !animation->IsCSSAnimation() || !animation->OwningElement() ...
                ```
                No need to cast.
                Kevin Ellis

                No longer relevant with the proposed check to enforce restriction to SVG documents.

                Gerrit-Comment-Date: Thu, 14 May 2026 18:16:45 +0000
                satisfied_requirement
                unsatisfied_requirement
                open
                diffy

                zhenbang Jiang (Gerrit)

                unread,
                May 18, 2026, 7:08:31 AMMay 18
                to Philip Rogers, Kevin Ellis, android-bu...@system.gserviceaccount.com, Chromium LUCI CQ, Menard, Alexis, chromium...@chromium.org, Dirk Schulze, Fredrik Söderquist, Olga Gerchikov, Nate Chapin, Stephen Chenney, blink-reviews-p...@chromium.org, kinuko...@chromium.org, drott+bl...@chromium.org, fserb...@chromium.org, zol...@webkit.org, blink-revi...@chromium.org, blink-rev...@chromium.org, blink-revie...@chromium.org, blink-...@chromium.org, fmalit...@chromium.org, gavinp...@chromium.org, kouhe...@chromium.org, loading...@chromium.org, pdr+svgw...@chromium.org
                Attention needed from Fredrik Söderquist, Kevin Ellis and Philip Rogers

                zhenbang Jiang added 3 comments

                Patchset-level comments
                File-level comment, Patchset 17 (Latest):
                zhenbang Jiang . resolved

                Plz take a look again, thanks!

                File third_party/blink/renderer/core/animation/document_animations.cc
                Line 328, Patchset 16: SVGImageAnimationsToReset& animations_to_reset) {
                Kevin Ellis . resolved

                To alleviate my concerns over calling the method on the wrong type of document and applying too generally, can you add a check.

                ```
                DCHECK(document_.View()->GetFrame().GetChromeClient().IsIsolatedSVGChromeClient());
                ```

                If we ensure that we are not calling this method on the document associated with the HTML element then all of the safeguards for timeline time and the type of animation are no longer required. My earlier reservations were based on the fact that this could result in non-isolated animations being reset.

                zhenbang Jiang

                Done

                Line 337, Patchset 14: !animation->effect() || !IsA<CSSAnimation>(animation.Get())) {
                Kevin Ellis . resolved

                Insufficient check. A CSSAnimation without an owning element is treated as a programatic animation. This happens for example if you grab a reference to a CSS animation from JavaScript and call play() on it once no longer associated with a style rule. You need to also check that OwningElement is not null.

                Philip Rogers

                SVG images don't support javascript, so is this an issue?

                zhenbang Jiang

                Even though SVG images do not support JavaScript, this helper lives on DocumentAnimations and should only operate on CSS animations that are still style-owned.

                A CSSAnimation without an OwningElement is no longer associated with a style rule and should not be rewound/resumed by this SVG image reset path. I added the OwningElement() check so those animations are skipped.

                Kevin Ellis

                A CSS animation / transition or programmatic animation can affect an SVG element if the SVG is not embedded in an IMG tag, but rather injected inline. Comments aside, this method is not differentiating how the SVG in being included. It might be as simple as adding a check that the root element is <SVG>.

                That said, document_animations_test.cc is using an inline SVG. Should the reset be applied in this case or only in SVG within an IMG case.

                If we ensure that this method is only being used for isolated SVG document's then the check for owning element and timeline time, or even the effect target are indeed irrelevant.

                zhenbang Jiang

                Added. I narrowed the contract of this helper to isolated SVG documents and added a DCHECK for IsIsolatedSVGChromeClient() at the entry point.

                That makes the intended scope explicit and avoids applying this reset path to regular HTML documents.

                Open in Gerrit

                Related details

                Attention is currently required from:
                • Fredrik Söderquist
                • Kevin Ellis
                • Philip Rogers
                Submit Requirements:
                  • requirement satisfiedCode-Coverage
                  • requirement is not satisfiedCode-Owners
                  • requirement is not satisfiedCode-Review
                  • requirement is not satisfiedReview-Enforcement
                  Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                  Gerrit-MessageType: comment
                  Gerrit-Project: chromium/src
                  Gerrit-Branch: main
                  Gerrit-Change-Id: I662712aeac3f523dc9c39dbe4711a318cc6156d0
                  Gerrit-Change-Number: 7760909
                  Gerrit-PatchSet: 17
                  Gerrit-Owner: zhenbang Jiang <edom...@gmail.com>
                  Gerrit-Reviewer: Kevin Ellis <kev...@chromium.org>
                  Gerrit-Reviewer: zhenbang Jiang <edom...@gmail.com>
                  Gerrit-CC: Dirk Schulze <dsch...@chromium.org>
                  Gerrit-CC: Fredrik Söderquist <f...@opera.com>
                  Gerrit-CC: Menard, Alexis <alexis...@intel.com>
                  Gerrit-CC: Nate Chapin <jap...@chromium.org>
                  Gerrit-CC: Olga Gerchikov <gerc...@microsoft.com>
                  Gerrit-CC: Philip Rogers <p...@chromium.org>
                  Gerrit-CC: Stephen Chenney <sche...@chromium.org>
                  Gerrit-Attention: Kevin Ellis <kev...@chromium.org>
                  Gerrit-Attention: Philip Rogers <p...@chromium.org>
                  Gerrit-Attention: Fredrik Söderquist <f...@opera.com>
                  Gerrit-Comment-Date: Mon, 18 May 2026 11:07:58 +0000
                  Gerrit-HasComments: Yes
                  Gerrit-Has-Labels: No
                  Comment-In-Reply-To: Kevin Ellis <kev...@chromium.org>
                  satisfied_requirement
                  unsatisfied_requirement
                  open
                  diffy

                  Fredrik Söderquist (Gerrit)

                  unread,
                  May 18, 2026, 12:36:59 PMMay 18
                  to zhenbang Jiang, Philip Rogers, Kevin Ellis, android-bu...@system.gserviceaccount.com, Chromium LUCI CQ, Menard, Alexis, chromium...@chromium.org, Dirk Schulze, Olga Gerchikov, Nate Chapin, Stephen Chenney, blink-reviews-p...@chromium.org, kinuko...@chromium.org, drott+bl...@chromium.org, fserb...@chromium.org, zol...@webkit.org, blink-revi...@chromium.org, blink-rev...@chromium.org, blink-revie...@chromium.org, blink-...@chromium.org, fmalit...@chromium.org, gavinp...@chromium.org, kouhe...@chromium.org, loading...@chromium.org, pdr+svgw...@chromium.org
                  Attention needed from Kevin Ellis, Philip Rogers and zhenbang Jiang

                  Fredrik Söderquist added 19 comments

                  File third_party/blink/renderer/core/html/lazy_load_image_observer_test.cc
                  Line 483, Patchset 7 (Parent): Compositor().BeginFrame();
                  Fredrik Söderquist . unresolved

                  Why remove the `BeginFrame()` calls - I think this test wants them?

                  zhenbang Jiang

                  My understanding is that the ordinary image path no longer goes through layout_object_->SetShouldDoFullPaintInvalidation() , so after switching loading from lazy to eager there is no longer a naturally pending frame at this point. If we keep the first BeginFrame() here, it now hits the DCHECK .

                  It seems that this test previously consumed a frame here only because it was relying on an old shared post-update side effect: image updates would also trigger a full paint invalidation. With this change, that reset/invalidation behavior is intentionally narrowed to SVG images that actually need to be rewound on reuse/reload, so that unrelated image resources are not also reset/invalidated and the fix stays scoped to the SVG animation issue itself.

                  So my understanding is that keeping the first BeginFrame() here would effectively preserve a dependency on an old shared side effect, rather than on behavior that is essential to the core intent of this test.

                  Fredrik Söderquist

                  Other images will need the paint invalidation as well - `BitmapImage::ResetAnimation()` for example has side-effects and would need to repaint to signal the "rewind".

                  zhenbang Jiang

                  Thanks, this is a helpful point.

                  I kept the generic protocol in ImageLoader: it calls ResetAnimation(kForReuse), and invalidates paint only if the implementation returns true. This keeps the type-specific decision inside each Image implementation.

                  I did try making BitmapImage participate in this reuse reset path, but that regressed image-animation-paused-exact-frame.tentative.html. As I understand it, that test relies on an animated GIF naturally reaching the green frame before image-animation is set to paused; resetting BitmapImage from this hook rewinds the GIF and changes the frame that ends up being paused.

                  So BitmapImage currently returns false for kForReuse, not because its reset side effects would not need repaint, but because this particular ImageLoader reuse hook should not reset ordinary GIF playback. SVGImage returns true when it actually needs to rewind for the SVG image reuse/reload case. Other Image implementations can still opt in by returning true if their kForReuse reset is appropriate and has repaint-visible side effects.

                  Fredrik Söderquist

                  There shouldn't be a `kForReuse` argument. Please remove it. The `image-animation-paused-exact-frame.tentative.html` test looks very fragile (and I'm not sure it necessarily tests what it sets out to test) - in which way is it failing? It's not impossible that this end up exposing an issue with our "reset animation flag" handling (it should only be done under certain conditions). The answer is however not to stop resetting `BitmapImage`s.

                  File third_party/blink/renderer/core/svg/graphics/svg_image.cc
                  Line 518, Patchset 17 (Latest): case AnimationState::kAnimatedRewindPending:
                  Fredrik Söderquist . unresolved

                  This (and `kUnknownRewindPending`) could just `break` (stay in the same state).

                  Line 523, Patchset 17 (Latest): case AnimationState::kNotAnimated:
                  Fredrik Söderquist . unresolved

                  Why would this switch to the `kUnknownRewindPending` state?

                  Line 533, Patchset 14: .GetDocumentAnimations()
                  .PrepareAnimationsForSVGImageReset(*css_animation_reset_);
                  }
                  has_pending_timeline_rewind_ = false;
                  }

                  void SVGImage::StartAnimation() {
                  SVGSVGElement* root_element = RootElement();
                  if (!root_element)
                  return;
                  css_animation_reset_->Resume();
                  Fredrik Söderquist . unresolved

                  Remind me again why we can't just rewind in `FlushPendingTimelineRewind()`. Are there side-effects?

                  zhenbang Jiang

                  We shouldn’t only do the timeline rewind within FlushPendingTimelineRewind().If we did, the very first frame rendered after reset could already be past time=0, which would cause the animation to fail to visibly restart from the start.
                  This happens because the lifecycle update in the same draw pass can advance any still-running CSS animations right before the reset frame is sampled.To address this, I’ve added logic to also rewind and temporarily pause active CSS animations at this point.These animations will be resumed once playback restarts, while any animations that were already paused will stay in their paused state.

                  Fredrik Söderquist

                  Well, `FlushPendingTimelineRewind()` is called right before performing the lifecycle update, so if the animations are not reset after it, the paint that follows _will_ be with an incorrect time for these animations. The `StartAnimation()` call that then follows is intended to restart the timeline for animations. I think the problem here may rather be that the CSS animations aren't following the same timeline control (and thus will always tick when `ServiceAnimations()` is called).

                  Line 556, Patchset 17 (Latest): break;
                  Fredrik Söderquist . unresolved

                  `NOTREACHED()`

                  Line 565, Patchset 17 (Latest): css_animations_to_reset_->Resume();
                  Fredrik Söderquist . unresolved

                  This can drop the object after it's been used.

                  Line 584, Patchset 17 (Latest): if (option == ResetAnimationOption::kForReuse && !ShouldResetForReuse()) {
                  Fredrik Söderquist . unresolved

                  I don't see why this would even be needed. If there are no animations, then the below should effectively be a no-op. The term "reuse" makes no sense in this context.

                  Line 608, Patchset 17 (Latest):bool SVGImage::ShouldResetForReuse() const {
                  switch (animation_state_) {
                  case AnimationState::kAnimated:
                  case AnimationState::kAnimatedRewindPending:
                  return true;
                  case AnimationState::kNotAnimated:
                  return false;
                  case AnimationState::kUnknown:
                  case AnimationState::kUnknownRewindPending:
                  return IsCurrentlyAnimated();
                  }
                  }
                  Fredrik Söderquist . unresolved

                  This is effectively the same as `IsCurrentlyAnimated()`. (Which I guess then transitively means that `IsCurrentlyAnimated()` and `MaybeAnimated()` are identical, and both aren't needed.)

                  Line 627, Patchset 17 (Latest): contains_animations = true;
                  Fredrik Söderquist . unresolved

                  `return true`

                  Line 631, Patchset 17 (Latest): contains_animations = true;
                  Fredrik Söderquist . unresolved

                  `return true`

                  Line 637, Patchset 17 (Latest): return contains_animations;
                  Fredrik Söderquist . unresolved

                  `return false` (variable not needed)

                  Line 789, Patchset 17 (Latest): const bool contains_animations = DetectAnimatedContent();
                  if (contains_animations) {
                  Fredrik Söderquist . unresolved

                  No need to have this local variable

                  Line 797, Patchset 17 (Latest): if (animation_state_ == AnimationState::kUnknown) {
                  Fredrik Söderquist . unresolved

                  Don't we want both states to transition to `kNotAnimated`?

                  Line 816, Patchset 17 (Latest): animation_state_ = AnimationState::kUnknown;
                  Fredrik Söderquist . unresolved

                  Why set this here?

                  File third_party/blink/renderer/core/svg/graphics/svg_image_test.cc
                  Line 220, Patchset 17 (Latest): animation->SetCurrentTimeInternal(ANIMATION_TIME_DELTA_FROM_SECONDS(1));
                  ASSERT_TRUE(animation->CurrentTimeInternal().has_value());
                  EXPECT_EQ(animation->CurrentTimeInternal().value(),
                  ANIMATION_TIME_DELTA_FROM_SECONDS(1));
                  Fredrik Söderquist . unresolved

                  Could we test this by advancing the time within the whole image instead rather than mutating specific animations?

                  Line 256, Patchset 17 (Latest): animation->SetCurrentTimeInternal(ANIMATION_TIME_DELTA_FROM_SECONDS(1));
                  ASSERT_TRUE(animation->CurrentTimeInternal().has_value());
                  EXPECT_EQ(animation->CurrentTimeInternal().value(),
                  ANIMATION_TIME_DELTA_FROM_SECONDS(1));
                  Fredrik Söderquist . unresolved

                  Ditto here.

                  Line 304, Patchset 17 (Latest): animation->SetCurrentTimeInternal(ANIMATION_TIME_DELTA_FROM_SECONDS(0.5));
                  ASSERT_TRUE(animation->CurrentTimeInternal().has_value());
                  EXPECT_EQ(animation->CurrentTimeInternal().value(),
                  ANIMATION_TIME_DELTA_FROM_SECONDS(0.5));
                  Fredrik Söderquist . unresolved

                  Ditto here.

                  Line 348, Patchset 17 (Latest): animation->pause();
                  Fredrik Söderquist . unresolved

                  How will this happen within an `SVGImage`? Can we write the test in such a way instead of using this synthetic method?

                  Line 751, Patchset 17 (Latest): first_animation->SetCurrentTimeInternal(ANIMATION_TIME_DELTA_FROM_SECONDS(1));
                  Fredrik Söderquist . unresolved

                  Could we achieve this by advancing the image's timeline instead?

                  Open in Gerrit

                  Related details

                  Attention is currently required from:
                  • Kevin Ellis
                  • Philip Rogers
                  • zhenbang Jiang
                  Submit Requirements:
                    • requirement satisfiedCode-Coverage
                    • requirement is not satisfiedCode-Owners
                    • requirement is not satisfiedCode-Review
                    • requirement is not satisfiedNo-Unresolved-Comments
                    • requirement is not satisfiedReview-Enforcement
                    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                    Gerrit-MessageType: comment
                    Gerrit-Project: chromium/src
                    Gerrit-Branch: main
                    Gerrit-Change-Id: I662712aeac3f523dc9c39dbe4711a318cc6156d0
                    Gerrit-Change-Number: 7760909
                    Gerrit-PatchSet: 17
                    Gerrit-Owner: zhenbang Jiang <edom...@gmail.com>
                    Gerrit-Reviewer: Kevin Ellis <kev...@chromium.org>
                    Gerrit-Reviewer: zhenbang Jiang <edom...@gmail.com>
                    Gerrit-CC: Dirk Schulze <dsch...@chromium.org>
                    Gerrit-CC: Fredrik Söderquist <f...@opera.com>
                    Gerrit-CC: Menard, Alexis <alexis...@intel.com>
                    Gerrit-CC: Nate Chapin <jap...@chromium.org>
                    Gerrit-CC: Olga Gerchikov <gerc...@microsoft.com>
                    Gerrit-CC: Philip Rogers <p...@chromium.org>
                    Gerrit-CC: Stephen Chenney <sche...@chromium.org>
                    Gerrit-Attention: Kevin Ellis <kev...@chromium.org>
                    Gerrit-Attention: Philip Rogers <p...@chromium.org>
                    Gerrit-Attention: zhenbang Jiang <edom...@gmail.com>
                    Gerrit-Comment-Date: Mon, 18 May 2026 16:36:41 +0000
                    Gerrit-HasComments: Yes
                    Gerrit-Has-Labels: No
                    satisfied_requirement
                    unsatisfied_requirement
                    open
                    diffy

                    Kevin Ellis (Gerrit)

                    unread,
                    May 21, 2026, 4:08:06 PM (11 days ago) May 21
                    to zhenbang Jiang, Philip Rogers, android-bu...@system.gserviceaccount.com, Chromium LUCI CQ, Menard, Alexis, chromium...@chromium.org, Dirk Schulze, Fredrik Söderquist, Olga Gerchikov, Nate Chapin, Stephen Chenney, blink-reviews-p...@chromium.org, kinuko...@chromium.org, drott+bl...@chromium.org, fserb...@chromium.org, zol...@webkit.org, blink-revi...@chromium.org, blink-rev...@chromium.org, blink-revie...@chromium.org, blink-...@chromium.org, fmalit...@chromium.org, gavinp...@chromium.org, kouhe...@chromium.org, loading...@chromium.org, pdr+svgw...@chromium.org
                    Attention needed from Philip Rogers and zhenbang Jiang

                    Kevin Ellis added 1 comment

                    Patchset-level comments
                    Kevin Ellis . resolved

                    Removing myself from the attention set until the unresolved comments have been addressed. Will take a look once ready for re-review.

                    Open in Gerrit

                    Related details

                    Attention is currently required from:
                    • Philip Rogers
                    • zhenbang Jiang
                    Gerrit-Attention: Philip Rogers <p...@chromium.org>
                    Gerrit-Attention: zhenbang Jiang <edom...@gmail.com>
                    Gerrit-Comment-Date: Thu, 21 May 2026 20:08:00 +0000
                    Gerrit-HasComments: Yes
                    Gerrit-Has-Labels: No
                    satisfied_requirement
                    unsatisfied_requirement
                    open
                    diffy

                    zhenbang Jiang (Gerrit)

                    unread,
                    May 28, 2026, 7:06:04 AM (4 days ago) May 28
                    to Philip Rogers, Kevin Ellis, android-bu...@system.gserviceaccount.com, Chromium LUCI CQ, Menard, Alexis, chromium...@chromium.org, Dirk Schulze, Fredrik Söderquist, Olga Gerchikov, Nate Chapin, Stephen Chenney, blink-reviews-p...@chromium.org, kinuko...@chromium.org, drott+bl...@chromium.org, fserb...@chromium.org, zol...@webkit.org, blink-revi...@chromium.org, blink-rev...@chromium.org, blink-revie...@chromium.org, blink-...@chromium.org, fmalit...@chromium.org, gavinp...@chromium.org, kouhe...@chromium.org, loading...@chromium.org, pdr+svgw...@chromium.org
                    Attention needed from Fredrik Söderquist and Philip Rogers

                    zhenbang Jiang added 20 comments

                    zhenbang Jiang . resolved

                    Plz take a look again, thanks!

                    File third_party/blink/renderer/core/html/lazy_load_image_observer_test.cc
                    Line 483, Patchset 7 (Parent): Compositor().BeginFrame();
                    Fredrik Söderquist . resolved

                    Why remove the `BeginFrame()` calls - I think this test wants them?

                    zhenbang Jiang

                    My understanding is that the ordinary image path no longer goes through layout_object_->SetShouldDoFullPaintInvalidation() , so after switching loading from lazy to eager there is no longer a naturally pending frame at this point. If we keep the first BeginFrame() here, it now hits the DCHECK .

                    It seems that this test previously consumed a frame here only because it was relying on an old shared post-update side effect: image updates would also trigger a full paint invalidation. With this change, that reset/invalidation behavior is intentionally narrowed to SVG images that actually need to be rewound on reuse/reload, so that unrelated image resources are not also reset/invalidated and the fix stays scoped to the SVG animation issue itself.

                    So my understanding is that keeping the first BeginFrame() here would effectively preserve a dependency on an old shared side effect, rather than on behavior that is essential to the core intent of this test.

                    Fredrik Söderquist

                    Other images will need the paint invalidation as well - `BitmapImage::ResetAnimation()` for example has side-effects and would need to repaint to signal the "rewind".

                    zhenbang Jiang

                    Thanks, this is a helpful point.

                    I kept the generic protocol in ImageLoader: it calls ResetAnimation(kForReuse), and invalidates paint only if the implementation returns true. This keeps the type-specific decision inside each Image implementation.

                    I did try making BitmapImage participate in this reuse reset path, but that regressed image-animation-paused-exact-frame.tentative.html. As I understand it, that test relies on an animated GIF naturally reaching the green frame before image-animation is set to paused; resetting BitmapImage from this hook rewinds the GIF and changes the frame that ends up being paused.

                    So BitmapImage currently returns false for kForReuse, not because its reset side effects would not need repaint, but because this particular ImageLoader reuse hook should not reset ordinary GIF playback. SVGImage returns true when it actually needs to rewind for the SVG image reuse/reload case. Other Image implementations can still opt in by returning true if their kForReuse reset is appropriate and has repaint-visible side effects.

                    Fredrik Söderquist

                    There shouldn't be a `kForReuse` argument. Please remove it. The `image-animation-paused-exact-frame.tentative.html` test looks very fragile (and I'm not sure it necessarily tests what it sets out to test) - in which way is it failing? It's not impossible that this end up exposing an issue with our "reset animation flag" handling (it should only be done under certain conditions). The answer is however not to stop resetting `BitmapImage`s.

                    zhenbang Jiang

                    Done. Removed kForReuse and kept ImageLoader generic. BitmapImage still resets and requests repaint for real rewinds, but it now avoids emitting a reset sequence before any cached PaintImage exists. In that initial-load state there is no existing bitmap animation timeline to rewind or painted frame to invalidate; emitting the sequence there caused image-animation-paused-exact-frame.tentative.html to reset the GIF too early. This matches the “reset flag should only be done under certain conditions” point without stopping BitmapImage reset.

                    File third_party/blink/renderer/core/svg/graphics/svg_image.cc
                    Line 518, Patchset 17: case AnimationState::kAnimatedRewindPending:
                    Fredrik Söderquist . resolved

                    This (and `kUnknownRewindPending`) could just `break` (stay in the same state).

                    zhenbang Jiang

                    Done

                    Line 523, Patchset 17: case AnimationState::kNotAnimated:
                    Fredrik Söderquist . resolved

                    Why would this switch to the `kUnknownRewindPending` state?

                    zhenbang Jiang

                    Done. kNotAnimated no longer transitions to kUnknownRewindPending, and kUnknownRewindPending now just keeps its state. Only kUnknown can schedule an unknown pending rewind.

                    Line 533, Patchset 14: .GetDocumentAnimations()
                    .PrepareAnimationsForSVGImageReset(*css_animation_reset_);
                    }
                    has_pending_timeline_rewind_ = false;
                    }

                    void SVGImage::StartAnimation() {
                    SVGSVGElement* root_element = RootElement();
                    if (!root_element)
                    return;
                    css_animation_reset_->Resume();
                    Fredrik Söderquist . resolved

                    Remind me again why we can't just rewind in `FlushPendingTimelineRewind()`. Are there side-effects?

                    zhenbang Jiang

                    We shouldn’t only do the timeline rewind within FlushPendingTimelineRewind().If we did, the very first frame rendered after reset could already be past time=0, which would cause the animation to fail to visibly restart from the start.
                    This happens because the lifecycle update in the same draw pass can advance any still-running CSS animations right before the reset frame is sampled.To address this, I’ve added logic to also rewind and temporarily pause active CSS animations at this point.These animations will be resumed once playback restarts, while any animations that were already paused will stay in their paused state.

                    Fredrik Söderquist

                    Well, `FlushPendingTimelineRewind()` is called right before performing the lifecycle update, so if the animations are not reset after it, the paint that follows _will_ be with an incorrect time for these animations. The `StartAnimation()` call that then follows is intended to restart the timeline for animations. I think the problem here may rather be that the CSS animations aren't following the same timeline control (and thus will always tick when `ServiceAnimations()` is called).

                    zhenbang Jiang

                    Done. The rewind still happens in FlushPendingTimelineRewind(); ResetAnimation() only schedules it. The extra work there is specifically for CSS animations, since they can tick during ServiceAnimations() and don't fully follow the SVG timeline control. We now reset their current time to 0 and temporarily pause only running CSS animations until StartAnimation() resumes them. Explicitly paused animations remain paused. This helper is restricted to isolated SVG image documents.

                    Line 556, Patchset 17: break;
                    Fredrik Söderquist . resolved

                    `NOTREACHED()`

                    zhenbang Jiang

                    Done

                    Line 565, Patchset 17: css_animations_to_reset_->Resume();
                    Fredrik Söderquist . resolved

                    This can drop the object after it's been used.

                    zhenbang Jiang

                    Done. StartAnimation() now copies css_animations_to_reset_ into a local Persistent, clears the member, and then calls Resume() through the local Persistent. This keeps the helper alive during Resume() while avoiding retaining the stale reset list afterwards.

                    Line 584, Patchset 17: if (option == ResetAnimationOption::kForReuse && !ShouldResetForReuse()) {
                    Fredrik Söderquist . resolved

                    I don't see why this would even be needed. If there are no animations, then the below should effectively be a no-op. The term "reuse" makes no sense in this context.

                    zhenbang Jiang

                    one. Removed kForReuse and ShouldResetForReuse(). ResetAnimation() now uses the AnimationState state machine directly; kNotAnimated naturally produces no pending rewind and returns false, while animated/unknown states can schedule one. ImageLoader just calls the parameterless Image::ResetAnimation().

                    Line 608, Patchset 17:bool SVGImage::ShouldResetForReuse() const {

                    switch (animation_state_) {
                    case AnimationState::kAnimated:
                    case AnimationState::kAnimatedRewindPending:
                    return true;
                    case AnimationState::kNotAnimated:
                    return false;
                    case AnimationState::kUnknown:
                    case AnimationState::kUnknownRewindPending:
                    return IsCurrentlyAnimated();
                    }
                    }
                    Fredrik Söderquist . resolved

                    This is effectively the same as `IsCurrentlyAnimated()`. (Which I guess then transitively means that `IsCurrentlyAnimated()` and `MaybeAnimated()` are identical, and both aren't needed.)

                    zhenbang Jiang

                    Removed ShouldResetForReuse() and IsCurrentlyAnimated(). MaybeAnimated() now uses AnimationState directly and falls back to DetectAnimatedContent() only for unknown states.

                    Line 627, Patchset 17: contains_animations = true;
                    Fredrik Söderquist . resolved

                    `return true`

                    zhenbang Jiang

                    Done

                    Line 631, Patchset 17: contains_animations = true;
                    Fredrik Söderquist . resolved

                    `return true`

                    zhenbang Jiang

                    Done

                    Line 637, Patchset 17: return contains_animations;
                    Fredrik Söderquist . resolved

                    `return false` (variable not needed)

                    zhenbang Jiang

                    Done

                    Line 789, Patchset 17: const bool contains_animations = DetectAnimatedContent();
                    if (contains_animations) {
                    Fredrik Söderquist . resolved

                    No need to have this local variable

                    zhenbang Jiang

                    Done. Removed the local contains_animations variable and now call DetectAnimatedContent() directly. If no animations are detected, both kUnknown and kUnknownRewindPending transition to kNotAnimated.

                    Line 797, Patchset 17: if (animation_state_ == AnimationState::kUnknown) {
                    Fredrik Söderquist . resolved

                    Don't we want both states to transition to `kNotAnimated`?

                    zhenbang Jiang

                    Done. When DetectAnimatedContent() returns false, both kUnknown and kUnknownRewindPending now fall through to the same kNotAnimated assignment.

                    Line 816, Patchset 17: animation_state_ = AnimationState::kUnknown;
                    Fredrik Söderquist . resolved

                    Why set this here?

                    zhenbang Jiang

                    Removed the explicit assignment from the initialization path and rely on the member default initializer: animation_state_ now defaults to kUnknown in SVGImage.

                    File third_party/blink/renderer/core/svg/graphics/svg_image_test.cc
                    Line 220, Patchset 17: animation->SetCurrentTimeInternal(ANIMATION_TIME_DELTA_FROM_SECONDS(1));

                    ASSERT_TRUE(animation->CurrentTimeInternal().has_value());
                    EXPECT_EQ(animation->CurrentTimeInternal().value(),
                    ANIMATION_TIME_DELTA_FROM_SECONDS(1));
                    Fredrik Söderquist . resolved

                    Could we test this by advancing the time within the whole image instead rather than mutating specific animations?

                    zhenbang Jiang

                    Done

                    Line 256, Patchset 17: animation->SetCurrentTimeInternal(ANIMATION_TIME_DELTA_FROM_SECONDS(1));

                    ASSERT_TRUE(animation->CurrentTimeInternal().has_value());
                    EXPECT_EQ(animation->CurrentTimeInternal().value(),
                    ANIMATION_TIME_DELTA_FROM_SECONDS(1));
                    Fredrik Söderquist . resolved

                    Ditto here.

                    zhenbang Jiang

                    Done

                    Line 304, Patchset 17: animation->SetCurrentTimeInternal(ANIMATION_TIME_DELTA_FROM_SECONDS(0.5));

                    ASSERT_TRUE(animation->CurrentTimeInternal().has_value());
                    EXPECT_EQ(animation->CurrentTimeInternal().value(),
                    ANIMATION_TIME_DELTA_FROM_SECONDS(0.5));
                    Fredrik Söderquist . resolved

                    Ditto here.

                    zhenbang Jiang

                    Done

                    Line 348, Patchset 17: animation->pause();
                    Fredrik Söderquist . resolved

                    How will this happen within an `SVGImage`? Can we write the test in such a way instead of using this synthetic method?

                    zhenbang Jiang

                    Done. The test no longer mutates the Animation directly or calls pause() synthetically. It now uses SVG content with a CSS-paused animation (`animation: ... paused`) and verifies that ResetAnimation() preserves that paused state instead of collecting it for resume.

                    Line 751, Patchset 17: first_animation->SetCurrentTimeInternal(ANIMATION_TIME_DELTA_FROM_SECONDS(1));
                    Fredrik Söderquist . resolved

                    Could we achieve this by advancing the image's timeline instead?

                    zhenbang Jiang

                    Done

                    Open in Gerrit

                    Related details

                    Attention is currently required from:
                    • Fredrik Söderquist
                    • Philip Rogers
                    Submit Requirements:
                      • requirement satisfiedCode-Coverage
                      • requirement is not satisfiedCode-Owners
                      • requirement is not satisfiedCode-Review
                      • requirement is not satisfiedReview-Enforcement
                      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                      Gerrit-MessageType: comment
                      Gerrit-Project: chromium/src
                      Gerrit-Branch: main
                      Gerrit-Change-Id: I662712aeac3f523dc9c39dbe4711a318cc6156d0
                      Gerrit-Change-Number: 7760909
                      Gerrit-PatchSet: 21
                      Gerrit-Owner: zhenbang Jiang <edom...@gmail.com>
                      Gerrit-Reviewer: Kevin Ellis <kev...@chromium.org>
                      Gerrit-Reviewer: zhenbang Jiang <edom...@gmail.com>
                      Gerrit-CC: Dirk Schulze <dsch...@chromium.org>
                      Gerrit-CC: Fredrik Söderquist <f...@opera.com>
                      Gerrit-CC: Menard, Alexis <alexis...@intel.com>
                      Gerrit-CC: Nate Chapin <jap...@chromium.org>
                      Gerrit-CC: Olga Gerchikov <gerc...@microsoft.com>
                      Gerrit-CC: Philip Rogers <p...@chromium.org>
                      Gerrit-CC: Stephen Chenney <sche...@chromium.org>
                      Gerrit-Attention: Philip Rogers <p...@chromium.org>
                      Gerrit-Attention: Fredrik Söderquist <f...@opera.com>
                      Gerrit-Comment-Date: Thu, 28 May 2026 11:05:33 +0000
                      Gerrit-HasComments: Yes
                      Gerrit-Has-Labels: No
                      satisfied_requirement
                      unsatisfied_requirement
                      open
                      diffy

                      Fredrik Söderquist (Gerrit)

                      unread,
                      May 28, 2026, 10:30:51 AM (4 days ago) May 28
                      to zhenbang Jiang, Philip Rogers, Kevin Ellis, android-bu...@system.gserviceaccount.com, Chromium LUCI CQ, Menard, Alexis, chromium...@chromium.org, Dirk Schulze, Olga Gerchikov, Nate Chapin, Stephen Chenney, blink-reviews-p...@chromium.org, kinuko...@chromium.org, drott+bl...@chromium.org, fserb...@chromium.org, zol...@webkit.org, blink-revi...@chromium.org, blink-rev...@chromium.org, blink-revie...@chromium.org, blink-...@chromium.org, fmalit...@chromium.org, gavinp...@chromium.org, kouhe...@chromium.org, loading...@chromium.org, pdr+svgw...@chromium.org
                      Attention needed from Philip Rogers and zhenbang Jiang

                      Fredrik Söderquist added 7 comments

                      File third_party/blink/renderer/core/loader/image_loader.cc
                      Line 906, Patchset 21 (Latest): if (!image->ResetAnimation()) {
                      return;
                      }
                      Fredrik Söderquist . unresolved

                      Let's move this repaint optimization to a separate CL. This CL is big enough as it is, and we'll be better off keeping the bug fix and the optimization separate.

                      File third_party/blink/renderer/core/svg/graphics/svg_image.cc
                      Line 586, Patchset 21 (Latest): if (css_animations_to_reset_) {
                      Persistent<SVGImageAnimationsToReset> animations_to_reset =
                      css_animations_to_reset_;
                      css_animations_to_reset_ = nullptr;
                      animations_to_reset->Resume();
                      }
                      Fredrik Söderquist . unresolved
                      ```suggestion
                      if (css_animations_to_reset_) {
                      css_animations_to_reset_.Release()->Resume();
                      }
                      ```
                      Line 635, Patchset 21 (Latest): } else {
                      Fredrik Söderquist . unresolved

                      You can drop this `else` to save on indentation. I'd actually prefer to keep the structure from `MaybeAnimated()` with preferring early returns rather than adding indentation.

                      File third_party/blink/renderer/core/svg/graphics/svg_image_test.cc
                      Line 45, Patchset 21 (Latest):Animation* GetBoxAnimation(LocalFrame& local_frame) {
                      Fredrik Söderquist . unresolved

                      Maybe make this take an `id` argument as well. Then you can reuse it for the tests where not all ids are `"box"` as well.

                      Line 768, Patchset 21 (Latest): Element* first_box =
                      first_local_frame->GetDocument()->getElementById(AtomicString("box"));
                      ASSERT_TRUE(first_box);
                      HeapVector<Member<Animation>> first_animations = first_box->getAnimations();
                      ASSERT_EQ(first_animations.size(), 1u);
                      Animation* first_animation = first_animations[0].Get();
                      Fredrik Söderquist . unresolved

                      Use the helper?

                      Line 803, Patchset 21 (Latest): Element* hidden_box =
                      second_local_frame->GetDocument()->getElementById(AtomicString("box"));
                      ASSERT_TRUE(hidden_box);
                      HeapVector<Member<Animation>> hidden_animations = hidden_box->getAnimations();
                      ASSERT_EQ(hidden_animations.size(), 1u);
                      Animation* hidden_animation = hidden_animations[0].Get();
                      Fredrik Söderquist . unresolved

                      Ditto

                      Line 822, Patchset 21 (Latest): Element* visible_box =
                      second_local_frame->GetDocument()->getElementById(AtomicString("box"));
                      ASSERT_TRUE(visible_box);
                      HeapVector<Member<Animation>> visible_animations =
                      visible_box->getAnimations();
                      ASSERT_EQ(visible_animations.size(), 1u);
                      Animation* visible_animation = visible_animations[0].Get();
                      Fredrik Söderquist . unresolved

                      Ditto

                      Open in Gerrit

                      Related details

                      Attention is currently required from:
                      • Philip Rogers
                      • zhenbang Jiang
                      Submit Requirements:
                        • requirement satisfiedCode-Coverage
                        • requirement is not satisfiedCode-Owners
                        • requirement is not satisfiedCode-Review
                        • requirement is not satisfiedNo-Unresolved-Comments
                        • requirement is not satisfiedReview-Enforcement
                        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                        Gerrit-MessageType: comment
                        Gerrit-Project: chromium/src
                        Gerrit-Branch: main
                        Gerrit-Change-Id: I662712aeac3f523dc9c39dbe4711a318cc6156d0
                        Gerrit-Change-Number: 7760909
                        Gerrit-PatchSet: 21
                        Gerrit-Owner: zhenbang Jiang <edom...@gmail.com>
                        Gerrit-Reviewer: Kevin Ellis <kev...@chromium.org>
                        Gerrit-Reviewer: zhenbang Jiang <edom...@gmail.com>
                        Gerrit-CC: Dirk Schulze <dsch...@chromium.org>
                        Gerrit-CC: Fredrik Söderquist <f...@opera.com>
                        Gerrit-CC: Menard, Alexis <alexis...@intel.com>
                        Gerrit-CC: Nate Chapin <jap...@chromium.org>
                        Gerrit-CC: Olga Gerchikov <gerc...@microsoft.com>
                        Gerrit-CC: Philip Rogers <p...@chromium.org>
                        Gerrit-CC: Stephen Chenney <sche...@chromium.org>
                        Gerrit-Attention: Philip Rogers <p...@chromium.org>
                        Gerrit-Attention: zhenbang Jiang <edom...@gmail.com>
                        Gerrit-Comment-Date: Thu, 28 May 2026 14:30:33 +0000
                        Gerrit-HasComments: Yes
                        Gerrit-Has-Labels: No
                        satisfied_requirement
                        unsatisfied_requirement
                        open
                        diffy

                        zhenbang Jiang (Gerrit)

                        unread,
                        May 29, 2026, 5:22:24 AM (3 days ago) May 29
                        to Philip Rogers, Kevin Ellis, android-bu...@system.gserviceaccount.com, Chromium LUCI CQ, Menard, Alexis, chromium...@chromium.org, Dirk Schulze, Fredrik Söderquist, Olga Gerchikov, Nate Chapin, Stephen Chenney, blink-reviews-p...@chromium.org, kinuko...@chromium.org, drott+bl...@chromium.org, fserb...@chromium.org, zol...@webkit.org, blink-revi...@chromium.org, blink-rev...@chromium.org, blink-revie...@chromium.org, blink-...@chromium.org, fmalit...@chromium.org, gavinp...@chromium.org, kouhe...@chromium.org, loading...@chromium.org, pdr+svgw...@chromium.org
                        Attention needed from Fredrik Söderquist and Philip Rogers

                        zhenbang Jiang added 8 comments

                        Patchset-level comments
                        File-level comment, Patchset 22 (Latest):
                        zhenbang Jiang . resolved

                        Done, Plz take a look again.

                        File third_party/blink/renderer/core/loader/image_loader.cc
                        Line 906, Patchset 21: if (!image->ResetAnimation()) {
                        return;
                        }
                        Fredrik Söderquist . resolved

                        Let's move this repaint optimization to a separate CL. This CL is big enough as it is, and we'll be better off keeping the bug fix and the optimization separate.

                        zhenbang Jiang

                        Done.

                        I removed the repaint optimization from this CL. ImageLoader now calls Image::ResetAnimation() as a side-effect-only API and still invalidates paint when the active LayoutImageResource matches the current ImageResourceContent.

                        I also changed ResetAnimation() back to void, so this CL no longer carries a “can skip repaint” signal. BitmapImage still participates in reset, and SVGImage keeps the state-machine based reset behavior.

                        The repaint-skipping optimization can be handled in a follow-up CL.

                        File third_party/blink/renderer/core/svg/graphics/svg_image.cc
                        Line 586, Patchset 21: if (css_animations_to_reset_) {

                        Persistent<SVGImageAnimationsToReset> animations_to_reset =
                        css_animations_to_reset_;
                        css_animations_to_reset_ = nullptr;
                        animations_to_reset->Resume();
                        }
                        Fredrik Söderquist . resolved
                        ```suggestion
                        if (css_animations_to_reset_) {
                        css_animations_to_reset_.Release()->Resume();
                        }
                        ```
                        zhenbang Jiang

                        Done

                        Line 635, Patchset 21: } else {
                        Fredrik Söderquist . resolved

                        You can drop this `else` to save on indentation. I'd actually prefer to keep the structure from `MaybeAnimated()` with preferring early returns rather than adding indentation.

                        zhenbang Jiang

                        Done

                        File third_party/blink/renderer/core/svg/graphics/svg_image_test.cc
                        Line 45, Patchset 21:Animation* GetBoxAnimation(LocalFrame& local_frame) {
                        Fredrik Söderquist . resolved

                        Maybe make this take an `id` argument as well. Then you can reuse it for the tests where not all ids are `"box"` as well.

                        zhenbang Jiang

                        Done

                        Line 768, Patchset 21: Element* first_box =

                        first_local_frame->GetDocument()->getElementById(AtomicString("box"));
                        ASSERT_TRUE(first_box);
                        HeapVector<Member<Animation>> first_animations = first_box->getAnimations();
                        ASSERT_EQ(first_animations.size(), 1u);
                        Animation* first_animation = first_animations[0].Get();
                        Fredrik Söderquist . resolved

                        Use the helper?

                        zhenbang Jiang

                        Done

                        Line 803, Patchset 21: Element* hidden_box =

                        second_local_frame->GetDocument()->getElementById(AtomicString("box"));
                        ASSERT_TRUE(hidden_box);
                        HeapVector<Member<Animation>> hidden_animations = hidden_box->getAnimations();
                        ASSERT_EQ(hidden_animations.size(), 1u);
                        Animation* hidden_animation = hidden_animations[0].Get();
                        Fredrik Söderquist . resolved

                        Ditto

                        zhenbang Jiang

                        Done

                        Line 822, Patchset 21: Element* visible_box =

                        second_local_frame->GetDocument()->getElementById(AtomicString("box"));
                        ASSERT_TRUE(visible_box);
                        HeapVector<Member<Animation>> visible_animations =
                        visible_box->getAnimations();
                        ASSERT_EQ(visible_animations.size(), 1u);
                        Animation* visible_animation = visible_animations[0].Get();
                        Fredrik Söderquist . resolved

                        Ditto

                        zhenbang Jiang

                        Done

                        Open in Gerrit

                        Related details

                        Attention is currently required from:
                        • Fredrik Söderquist
                        • Philip Rogers
                        Submit Requirements:
                          • requirement satisfiedCode-Coverage
                          • requirement is not satisfiedCode-Owners
                          • requirement is not satisfiedCode-Review
                          • requirement is not satisfiedReview-Enforcement
                          Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                          Gerrit-MessageType: comment
                          Gerrit-Project: chromium/src
                          Gerrit-Branch: main
                          Gerrit-Change-Id: I662712aeac3f523dc9c39dbe4711a318cc6156d0
                          Gerrit-Change-Number: 7760909
                          Gerrit-PatchSet: 22
                          Gerrit-Owner: zhenbang Jiang <edom...@gmail.com>
                          Gerrit-Reviewer: Kevin Ellis <kev...@chromium.org>
                          Gerrit-Reviewer: zhenbang Jiang <edom...@gmail.com>
                          Gerrit-CC: Dirk Schulze <dsch...@chromium.org>
                          Gerrit-CC: Fredrik Söderquist <f...@opera.com>
                          Gerrit-CC: Menard, Alexis <alexis...@intel.com>
                          Gerrit-CC: Nate Chapin <jap...@chromium.org>
                          Gerrit-CC: Olga Gerchikov <gerc...@microsoft.com>
                          Gerrit-CC: Philip Rogers <p...@chromium.org>
                          Gerrit-CC: Stephen Chenney <sche...@chromium.org>
                          Gerrit-Attention: Philip Rogers <p...@chromium.org>
                          Gerrit-Attention: Fredrik Söderquist <f...@opera.com>
                          Gerrit-Comment-Date: Fri, 29 May 2026 09:22:03 +0000
                          Gerrit-HasComments: Yes
                          Gerrit-Has-Labels: No
                          Comment-In-Reply-To: Fredrik Söderquist <f...@opera.com>
                          satisfied_requirement
                          unsatisfied_requirement
                          open
                          diffy

                          Fredrik Söderquist (Gerrit)

                          unread,
                          May 29, 2026, 7:26:39 AM (3 days ago) May 29
                          to zhenbang Jiang, Philip Rogers, Kevin Ellis, android-bu...@system.gserviceaccount.com, Chromium LUCI CQ, Menard, Alexis, chromium...@chromium.org, Dirk Schulze, Olga Gerchikov, Nate Chapin, Stephen Chenney, blink-reviews-p...@chromium.org, kinuko...@chromium.org, drott+bl...@chromium.org, fserb...@chromium.org, zol...@webkit.org, blink-revi...@chromium.org, blink-rev...@chromium.org, blink-revie...@chromium.org, blink-...@chromium.org, fmalit...@chromium.org, gavinp...@chromium.org, kouhe...@chromium.org, loading...@chromium.org, pdr+svgw...@chromium.org
                          Attention needed from Philip Rogers and zhenbang Jiang

                          Fredrik Söderquist voted and added 1 comment

                          Votes added by Fredrik Söderquist

                          Code-Review+1

                          1 comment

                          Patchset-level comments
                          Fredrik Söderquist . resolved

                          LGTM for the SVG/layout/loader bits, but perhaps we should add a flag for this just in case?

                          Open in Gerrit

                          Related details

                          Attention is currently required from:
                          • Philip Rogers
                          • zhenbang Jiang
                          Submit Requirements:
                          • requirement satisfiedCode-Coverage
                          • requirement satisfiedCode-Owners
                          • requirement is not satisfiedCode-Review
                          • requirement is not satisfiedReview-Enforcement
                          Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                          Gerrit-MessageType: comment
                          Gerrit-Project: chromium/src
                          Gerrit-Branch: main
                          Gerrit-Change-Id: I662712aeac3f523dc9c39dbe4711a318cc6156d0
                          Gerrit-Change-Number: 7760909
                          Gerrit-PatchSet: 22
                          Gerrit-Owner: zhenbang Jiang <edom...@gmail.com>
                          Gerrit-Reviewer: Fredrik Söderquist <f...@opera.com>
                          Gerrit-Reviewer: Kevin Ellis <kev...@chromium.org>
                          Gerrit-Reviewer: zhenbang Jiang <edom...@gmail.com>
                          Gerrit-CC: Dirk Schulze <dsch...@chromium.org>
                          Gerrit-CC: Menard, Alexis <alexis...@intel.com>
                          Gerrit-CC: Nate Chapin <jap...@chromium.org>
                          Gerrit-CC: Olga Gerchikov <gerc...@microsoft.com>
                          Gerrit-CC: Philip Rogers <p...@chromium.org>
                          Gerrit-CC: Stephen Chenney <sche...@chromium.org>
                          Gerrit-Attention: Philip Rogers <p...@chromium.org>
                          Gerrit-Attention: zhenbang Jiang <edom...@gmail.com>
                          Gerrit-Comment-Date: Fri, 29 May 2026 11:26:20 +0000
                          Gerrit-HasComments: Yes
                          Gerrit-Has-Labels: Yes
                          satisfied_requirement
                          unsatisfied_requirement
                          open
                          diffy

                          zhenbang Jiang (Gerrit)

                          unread,
                          4:22 AM (11 hours ago) 4:22 AM
                          to Fredrik Söderquist, Philip Rogers, Kevin Ellis, android-bu...@system.gserviceaccount.com, Chromium LUCI CQ, Menard, Alexis, chromium...@chromium.org, Dirk Schulze, Olga Gerchikov, Nate Chapin, Stephen Chenney, blink-reviews-p...@chromium.org, kinuko...@chromium.org, drott+bl...@chromium.org, fserb...@chromium.org, zol...@webkit.org, blink-revi...@chromium.org, blink-rev...@chromium.org, blink-revie...@chromium.org, blink-...@chromium.org, fmalit...@chromium.org, gavinp...@chromium.org, kouhe...@chromium.org, loading...@chromium.org, pdr+svgw...@chromium.org
                          Attention needed from Fredrik Söderquist and Philip Rogers

                          zhenbang Jiang added 1 comment

                          Patchset-level comments
                          Fredrik Söderquist . resolved

                          LGTM for the SVG/layout/loader bits, but perhaps we should add a flag for this just in case?

                          zhenbang Jiang

                          I added a Blink runtime-enabled feature for this behavior: SvgImageAnimationReset.

                          I set it to stable so the fix remains enabled by default, and the flag can work as a kill switch if needed. I'm not fully sure whether stable is the preferred status for this case, so please let me know if you'd prefer a different status/default setup.

                          The flag is checked at the ImageLoader reset entry point, and SVGImage::ResetAnimation() also checks it to cover direct reset callers. When the flag is disabled, the new SVG image reset/invalidation path is skipped.

                          I also added a test covering the disabled-flag case to verify that the cached detached SVG image is not rewound when SvgImageAnimationReset is disabled.

                          Could you please take another look and let me know if this is the right way to add the flag?

                          Open in Gerrit

                          Related details

                          Attention is currently required from:
                          • Fredrik Söderquist
                          • Philip Rogers
                          Submit Requirements:
                          • requirement satisfiedCode-Coverage
                          • requirement is not satisfiedCode-Owners
                          • requirement is not satisfiedCode-Review
                          • requirement is not satisfiedReview-Enforcement
                          Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                          Gerrit-MessageType: comment
                          Gerrit-Project: chromium/src
                          Gerrit-Branch: main
                          Gerrit-Change-Id: I662712aeac3f523dc9c39dbe4711a318cc6156d0
                          Gerrit-Change-Number: 7760909
                          Gerrit-PatchSet: 23
                          Gerrit-Owner: zhenbang Jiang <edom...@gmail.com>
                          Gerrit-Reviewer: Fredrik Söderquist <f...@opera.com>
                          Gerrit-Reviewer: Kevin Ellis <kev...@chromium.org>
                          Gerrit-Reviewer: zhenbang Jiang <edom...@gmail.com>
                          Gerrit-CC: Dirk Schulze <dsch...@chromium.org>
                          Gerrit-CC: Menard, Alexis <alexis...@intel.com>
                          Gerrit-CC: Nate Chapin <jap...@chromium.org>
                          Gerrit-CC: Olga Gerchikov <gerc...@microsoft.com>
                          Gerrit-CC: Philip Rogers <p...@chromium.org>
                          Gerrit-CC: Stephen Chenney <sche...@chromium.org>
                          Gerrit-Attention: Philip Rogers <p...@chromium.org>
                          Gerrit-Attention: Fredrik Söderquist <f...@opera.com>
                          Gerrit-Comment-Date: Mon, 01 Jun 2026 08:22:21 +0000
                          satisfied_requirement
                          unsatisfied_requirement
                          open
                          diffy

                          Fredrik Söderquist (Gerrit)

                          unread,
                          8:44 AM (7 hours ago) 8:44 AM
                          to zhenbang Jiang, Philip Rogers, Kevin Ellis, android-bu...@system.gserviceaccount.com, Chromium LUCI CQ, Menard, Alexis, chromium...@chromium.org, Dirk Schulze, Olga Gerchikov, Nate Chapin, Stephen Chenney, jmedle...@chromium.org, blink-reviews-p...@chromium.org, kinuko...@chromium.org, drott+bl...@chromium.org, fserb...@chromium.org, zol...@webkit.org, blink-revi...@chromium.org, blink-rev...@chromium.org, blink-revie...@chromium.org, blink-...@chromium.org, fmalit...@chromium.org, gavinp...@chromium.org, kouhe...@chromium.org, loading...@chromium.org, pdr+svgw...@chromium.org
                          Attention needed from Philip Rogers and zhenbang Jiang

                          Fredrik Söderquist added 1 comment

                          File third_party/blink/renderer/core/loader/image_loader.cc
                          Line 902, Patchset 23 (Latest): if (!RuntimeEnabledFeatures::SvgImageAnimationResetEnabled()) {
                          Fredrik Söderquist . unresolved

                          Now when the flag is off, the old behavior is not maintained. You'll need to keep the old `LayoutImageResource::ResetAnimation()` and essentially all the old code and most likely check the flag in a few other places. For `SVGImage` this will be complicated I think (landing a no-op refactoring there first might make it easier).

                          So here I'd expect something like:
                          ```
                          if (RuntimeEnabledFeatures::SvgImageAnimationResetEnabled()) {
                          ...the new code...
                          } else {
                          ...the old code...
                          }
                          ```
                          Open in Gerrit

                          Related details

                          Attention is currently required from:
                          • Philip Rogers
                          • zhenbang Jiang
                          Submit Requirements:
                            • requirement satisfiedCode-Coverage
                            • requirement is not satisfiedCode-Owners
                            • requirement is not satisfiedCode-Review
                            • requirement is not satisfiedNo-Unresolved-Comments
                            • requirement is not satisfiedReview-Enforcement
                            Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                            Gerrit-MessageType: comment
                            Gerrit-Project: chromium/src
                            Gerrit-Branch: main
                            Gerrit-Change-Id: I662712aeac3f523dc9c39dbe4711a318cc6156d0
                            Gerrit-Change-Number: 7760909
                            Gerrit-PatchSet: 23
                            Gerrit-Owner: zhenbang Jiang <edom...@gmail.com>
                            Gerrit-Reviewer: Fredrik Söderquist <f...@opera.com>
                            Gerrit-Reviewer: Kevin Ellis <kev...@chromium.org>
                            Gerrit-Reviewer: zhenbang Jiang <edom...@gmail.com>
                            Gerrit-CC: Dirk Schulze <dsch...@chromium.org>
                            Gerrit-CC: Menard, Alexis <alexis...@intel.com>
                            Gerrit-CC: Nate Chapin <jap...@chromium.org>
                            Gerrit-CC: Olga Gerchikov <gerc...@microsoft.com>
                            Gerrit-CC: Philip Rogers <p...@chromium.org>
                            Gerrit-CC: Stephen Chenney <sche...@chromium.org>
                            Gerrit-Attention: Philip Rogers <p...@chromium.org>
                            Gerrit-Attention: zhenbang Jiang <edom...@gmail.com>
                            Gerrit-Comment-Date: Mon, 01 Jun 2026 12:44:11 +0000
                            Gerrit-HasComments: Yes
                            Gerrit-Has-Labels: No
                            satisfied_requirement
                            unsatisfied_requirement
                            open
                            diffy
                            Reply all
                            Reply to author
                            Forward
                            0 new messages