Skip to content

[html] HTML renderer fails on test with addRetained with clip #155717

Closed
@harryterkelsen

Description

@harryterkelsen

Steps to reproduce

flutter/engine#55402 introduces a test for the case when addRetained is used on a layer with pictures which are clipped in one frame and visible in a later frame.

    test('picture clipped but scrolls back in', () async {
      // Frame 1: Clip out the right circle
      final ui.SceneBuilder sceneBuilder = ui.SceneBuilder();
      sceneBuilder.pushClipRect(const ui.Rect.fromLTRB(0, 0, 125, 300));
      // Save this offsetLayer to add back in so we are using the same
      // picture layers on the next scene.
      final ui.OffsetEngineLayer offsetLayer = sceneBuilder.pushOffset(0, 0);
      sceneBuilder.addPicture(ui.Offset.zero, drawPicture((ui.Canvas canvas) {
        canvas.drawCircle(const ui.Offset(50, 150), 50,
            ui.Paint()..color = const ui.Color(0xFFFF0000));
      }));
      sceneBuilder.addPicture(ui.Offset.zero, drawPicture((ui.Canvas canvas) {
        canvas.drawCircle(const ui.Offset(200, 150), 50,
            ui.Paint()..color = const ui.Color(0xFFFF0000));
      }));
      sceneBuilder.pop();
      sceneBuilder.pop();
      await renderScene(sceneBuilder.build());

      // Frame 2: Clip out the left circle
      final ui.SceneBuilder sceneBuilder2 = ui.SceneBuilder();
      sceneBuilder2.pushClipRect(const ui.Rect.fromLTRB(150, 0, 300, 300));
      sceneBuilder2.addRetained(offsetLayer);
      sceneBuilder2.pop();
      sceneBuilder2.pop();
      await renderScene(sceneBuilder2.build());

      // Frame 3: Clip out the right circle again
      final ui.SceneBuilder sceneBuilder3 = ui.SceneBuilder();
      sceneBuilder3.pushClipRect(const ui.Rect.fromLTRB(0, 0, 125, 300));
      sceneBuilder3.addRetained(offsetLayer);
      sceneBuilder3.pop();
      sceneBuilder3.pop();
      await renderScene(sceneBuilder3.build());

      await matchGoldenFile(
          'scene_builder_picture_clipped_out_then_clipped_in.png',
          region: region);
    });

Expected results

The test should pass and the screenshot should show the red circle on the left (the red circle on the right is clipped out).

Actual results

The HTML renderer crashes on this test with this stack trace:

00:40 +117 ~23 -1: scene_builder_test.dart: SceneBuilder picture clipped but scrolls back in [E]                                                                                                       
  Bad state: No element
  org-dartlang-sdk:///lib/_internal/js_runtime/lib/js_helper.dart 1209:19      Object.wrapException
  org-dartlang-sdk:///lib/_internal/js_runtime/lib/js_array.dart 448:5         JavaScriptObject.first
  ../../../../flutter/lib/web_ui/lib/src/engine/html/scene_builder.dart 42:26  SurfaceSceneBuilder._persistedScene
  ../../../../flutter/lib/web_ui/lib/src/engine/html/scene_builder.dart 485:7  SurfaceSceneBuilder.build.<fn>
  ../../../../flutter/lib/web_ui/lib/src/engine/profiler.dart 44:12            Object.timeAction
  ../../../../flutter/lib/web_ui/lib/src/engine/html/scene_builder.dart 480:5  SurfaceSceneBuilder.build
  ../../../../flutter/lib/web_ui/test/ui/scene_builder_test.dart 363:25        <fn>
  org-dartlang-sdk:///lib/_internal/js_runtime/lib/async_patch.dart 311:19     _wrapJsFunctionForAsync.closure.$protected
  org-dartlang-sdk:///lib/_internal/js_runtime/lib/async_patch.dart 336:23     _wrapJsFunctionForAsync.<fn>
  org-dartlang-sdk:///lib/_internal/js_runtime/lib/async_patch.dart 287:19     _awaitOnObject.<fn>

Code sample

  test('picture clipped but scrolls back in', () async {
      // Frame 1: Clip out the right circle
      final ui.SceneBuilder sceneBuilder = ui.SceneBuilder();
      sceneBuilder.pushClipRect(const ui.Rect.fromLTRB(0, 0, 125, 300));
      // Save this offsetLayer to add back in so we are using the same
      // picture layers on the next scene.
      final ui.OffsetEngineLayer offsetLayer = sceneBuilder.pushOffset(0, 0);
      sceneBuilder.addPicture(ui.Offset.zero, drawPicture((ui.Canvas canvas) {
        canvas.drawCircle(const ui.Offset(50, 150), 50,
            ui.Paint()..color = const ui.Color(0xFFFF0000));
      }));
      sceneBuilder.addPicture(ui.Offset.zero, drawPicture((ui.Canvas canvas) {
        canvas.drawCircle(const ui.Offset(200, 150), 50,
            ui.Paint()..color = const ui.Color(0xFFFF0000));
      }));
      sceneBuilder.pop();
      sceneBuilder.pop();
      await renderScene(sceneBuilder.build());

      // Frame 2: Clip out the left circle
      final ui.SceneBuilder sceneBuilder2 = ui.SceneBuilder();
      sceneBuilder2.pushClipRect(const ui.Rect.fromLTRB(150, 0, 300, 300));
      sceneBuilder2.addRetained(offsetLayer);
      sceneBuilder2.pop();
      sceneBuilder2.pop();
      await renderScene(sceneBuilder2.build());

      // Frame 3: Clip out the right circle again
      final ui.SceneBuilder sceneBuilder3 = ui.SceneBuilder();
      sceneBuilder3.pushClipRect(const ui.Rect.fromLTRB(0, 0, 125, 300));
      sceneBuilder3.addRetained(offsetLayer);
      sceneBuilder3.pop();
      sceneBuilder3.pop();
      await renderScene(sceneBuilder3.build());

      await matchGoldenFile(
          'scene_builder_picture_clipped_out_then_clipped_in.png',
          region: region);
    });

Screenshots or Video

No response

Logs

00:40 +117 ~23 -1: scene_builder_test.dart: SceneBuilder picture clipped but scrolls back in [E]                                                                                                       
  Bad state: No element
  org-dartlang-sdk:///lib/_internal/js_runtime/lib/js_helper.dart 1209:19      Object.wrapException
  org-dartlang-sdk:///lib/_internal/js_runtime/lib/js_array.dart 448:5         JavaScriptObject.first
  ../../../../flutter/lib/web_ui/lib/src/engine/html/scene_builder.dart 42:26  SurfaceSceneBuilder._persistedScene
  ../../../../flutter/lib/web_ui/lib/src/engine/html/scene_builder.dart 485:7  SurfaceSceneBuilder.build.<fn>
  ../../../../flutter/lib/web_ui/lib/src/engine/profiler.dart 44:12            Object.timeAction
  ../../../../flutter/lib/web_ui/lib/src/engine/html/scene_builder.dart 480:5  SurfaceSceneBuilder.build
  ../../../../flutter/lib/web_ui/test/ui/scene_builder_test.dart 363:25        <fn>
  org-dartlang-sdk:///lib/_internal/js_runtime/lib/async_patch.dart 311:19     _wrapJsFunctionForAsync.closure.$protected
  org-dartlang-sdk:///lib/_internal/js_runtime/lib/async_patch.dart 336:23     _wrapJsFunctionForAsync.<fn>
  org-dartlang-sdk:///lib/_internal/js_runtime/lib/async_patch.dart 287:19     _awaitOnObject.<fn>

Flutter Doctor output

❯ flutter doctor -v
[✓] Flutter (Channel main, 3.26.0-1.0.pre.193, on Debian GNU/Linux rodete 6.9.10-1rodete5-amd64, locale en_US.UTF-8)
    • Flutter version 3.26.0-1.0.pre.193 on channel main at /usr/local/google/home/het/Projects/flutter
    • Upstream repository [email protected]:flutter/flutter.git
    • Framework revision f83b08495a (28 hours ago), 2024-09-24 14:23:26 -0400
    • Engine revision dc44f95b70
    • Dart version 3.6.0 (build 3.6.0-273.0.dev)
    • DevTools version 2.40.0-dev.1

[!] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
    • Android SDK at /usr/local/google/home/het/Android/Sdk
    ✗ cmdline-tools component is missing
      Run `path/to/sdkmanager --install "cmdline-tools;latest"`
      See https://developer.android.com/studio/command-line for more details.
    ✗ Android license status unknown.
      Run `flutter doctor --android-licenses` to accept the SDK licenses.
      See https://flutter.dev/to/linux-android-setup for more details.

[✓] Chrome - develop for the web
    • Chrome at google-chrome

[✗] Linux toolchain - develop for Linux desktop
    • Debian clang version 16.0.6 (26)
    • cmake version 3.29.6
    • ninja version 1.11.1
    • pkg-config version 1.8.1
    ✗ GTK 3.0 development libraries are required for Linux development.
      They are likely available from your distribution (e.g.: apt install libgtk-3-dev)

[!] Android Studio (not installed)
    • Android Studio not found; download from https://developer.android.com/studio/index.html
      (or visit https://flutter.dev/to/linux-android-setup for detailed instructions).

[✓] Connected device (2 available)
    • Linux (desktop) • linux  • linux-x64      • Debian GNU/Linux rodete 6.9.10-1rodete5-amd64
    • Chrome (web)    • chrome • web-javascript • Google Chrome 129.0.6668.70

[✓] Network resources
    • All expected network resources are available.

! Doctor found issues in 3 categories.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Issues that are less important to the Flutter projecte: web_htmlHTML rendering backend for Webplatform-webWeb applications specificallyteam-webOwned by Web platform teamtriaged-webTriaged by Web platform team

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions