Skip to content

[webview_flutter] Each WebView permanently leaks an Ecore_Evas canvas #1076

Description

@seungsoo47

Summary

webview_flutter_tizen leaks one Ecore_Evas (wayland_egl) canvas per
WebView instance. The canvas is created in InitWebView() and never freed —
Dispose() deletes the EWK view that sits on the canvas, but not the canvas
itself. Creating and destroying N WebViews over an app's lifetime leaks N
canvases, each holding a Wayland surface and EGL resources.

The obvious fix (ecore_evas_free() during teardown) does not work — it
takes the whole process down. Details below. This needs an
embedder/engine-side change, so filing separately from
#1069.

Affected code

packages/webview_flutter/tizen/src/webview.cc, WebView::InitWebView():

Ecore_Evas* evas = ecore_evas_new("wayland_egl", 0, 0, 1, 1, 0);

webview_instance_ = ewk_view_add(ecore_evas_get(evas));
if (!webview_instance_) {
  return false;
}
ecore_evas_focus_set(evas, true);

evas is a local. There is no ecore_evas_free() anywhere in the plugin.
WebView::Dispose() only calls evas_object_del(webview_instance_).

This is long-standing, not a regression — the same pattern is on master.

Why the obvious fix fails

Storing the canvas in a member and freeing it in the deferred teardown, right
after evas_object_del():

if (context->instance) {
  evas_object_del(context->instance);
}
if (context->evas) {
  ecore_evas_free(context->evas);   // added
}

kills the app on the first WebView disposal:

[I] 00:04 +1: runJavaScriptReturningResult
[E] tizen_renderer_egl.cc: PrintEGLError(323) > EGL error: EGL_NOT_INITIALIZED
[E] tizen_renderer_egl.cc: PrintEGLError(323) > EGL error: EGL_NOT_INITIALIZED
DriverError: Failed to fulfill RequestData due to remote error
Original error: ext.flutter.driver: (112) Service has disappeared

The error comes from tizen_renderer_egl.cc, i.e. the Flutter embedder's
renderer
, not from the plugin.

Cause: the wayland_egl Ecore_Evas does not own a private EGLDisplay — it
resolves the same process-wide display the Flutter embedder's renderer uses.
ecore_evas_free() ends up calling eglTerminate() on it, and eglTerminate()
invalidates every context and surface bound to that display, not just the
caller's. So:

WebView disposed
  -> ecore_evas_free(canvas)
    -> eglTerminate(shared EGLDisplay)
      -> Flutter renderer's EGL context/surfaces invalidated
        -> EGL_NOT_INITIALIZED on the next frame
          -> process dies

Ordering is not the problem — the view is deleted before the canvas. The
problem is that the resource is shared and eglTerminate() is not scoped to
one user.

Reproduction

  1. packages/webview_flutter/example
  2. Add ecore_evas_free() to the disposal path as above.
  3. flutter-tizen drive \
      --driver=test_driver/integration_test.dart \
      --target=integration_test/webview_flutter_test.dart \
      -d <tv-emulator>
    
  4. Suite dies at the second test (first WebView disposal) with the log above.
    Without the ecore_evas_free() it passes 20/20.

Environment: Tizen 10.0 TV emulator (x86_64), flutter-tizen @ current
master, webview_flutter_tizen 0.10.1.

Impact

  • One Wayland surface + EGL resources leaked per WebView instance, for the
    lifetime of the process.
  • Invisible in single-WebView apps and in the integration suite (each test
    makes one view and the process exits at the end).
  • Matters for TV apps that create and destroy WebViews repeatedly, or show
    several at once. Fix issue of multiple webviews in the 0.3.3 changelog
    suggests this area has caused trouble before.

Possible directions

Guesses, in rough order of preference — none validated:

  1. Do not create a per-view Ecore_Evas at all. Share a single
    plugin-owned canvas across all WebViews, or reuse the canvas the embedder
    already has. ewk_view_add() only needs an Evas*; nothing obviously
    requires it to be per view. This removes the leak instead of trying to free
    it.
  2. Reference-count the shared EGLDisplay so eglTerminate() only runs when
    the last user releases it. Needs embedder cooperation.
  3. Give the WebView canvas its own EGLDisplay, so freeing it cannot affect
    the renderer. May not be possible on the shared Wayland connection.

Option 1 looks cheapest and is entirely plugin-side if it holds; it just needs
verifying that a shared canvas does not break focus handling
(ecore_evas_focus_set), IME, or offscreen rendering, which are currently set
per canvas.

Current state

Left leaked deliberately, with the constraint recorded at the call site so the
next person does not repeat the experiment:

// Not freed on disposal: ecore_evas_free() would eglTerminate() the EGL
// display shared with the Flutter renderer and kill the process.
Ecore_Evas* evas = ecore_evas_new("wayland_egl", 0, 0, 1, 1, 0);

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions