Make FlRenderable interface (#55763)

Added to make testing of FlRenderer easier.
diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter
index 078e1ee..ef13edb 100644
--- a/ci/licenses_golden/licenses_flutter
+++ b/ci/licenses_golden/licenses_flutter
@@ -44834,6 +44834,8 @@
 ORIGIN: ../../../flutter/shell/platform/linux/fl_plugin_registrar_private.h + ../../../flutter/LICENSE
 ORIGIN: ../../../flutter/shell/platform/linux/fl_plugin_registrar_test.cc + ../../../flutter/LICENSE
 ORIGIN: ../../../flutter/shell/platform/linux/fl_plugin_registry.cc + ../../../flutter/LICENSE
+ORIGIN: ../../../flutter/shell/platform/linux/fl_renderable.cc + ../../../flutter/LICENSE
+ORIGIN: ../../../flutter/shell/platform/linux/fl_renderable.h + ../../../flutter/LICENSE
 ORIGIN: ../../../flutter/shell/platform/linux/fl_renderer.cc + ../../../flutter/LICENSE
 ORIGIN: ../../../flutter/shell/platform/linux/fl_renderer.h + ../../../flutter/LICENSE
 ORIGIN: ../../../flutter/shell/platform/linux/fl_renderer_gdk.cc + ../../../flutter/LICENSE
@@ -47739,6 +47741,8 @@
 FILE: ../../../flutter/shell/platform/linux/fl_plugin_registrar_private.h
 FILE: ../../../flutter/shell/platform/linux/fl_plugin_registrar_test.cc
 FILE: ../../../flutter/shell/platform/linux/fl_plugin_registry.cc
+FILE: ../../../flutter/shell/platform/linux/fl_renderable.cc
+FILE: ../../../flutter/shell/platform/linux/fl_renderable.h
 FILE: ../../../flutter/shell/platform/linux/fl_renderer.cc
 FILE: ../../../flutter/shell/platform/linux/fl_renderer.h
 FILE: ../../../flutter/shell/platform/linux/fl_renderer_gdk.cc
diff --git a/shell/platform/linux/BUILD.gn b/shell/platform/linux/BUILD.gn
index 36045a9..9cbcc9f 100644
--- a/shell/platform/linux/BUILD.gn
+++ b/shell/platform/linux/BUILD.gn
@@ -129,6 +129,7 @@
     "fl_platform_handler.cc",
     "fl_plugin_registrar.cc",
     "fl_plugin_registry.cc",
+    "fl_renderable.cc",
     "fl_renderer.cc",
     "fl_renderer_gdk.cc",
     "fl_renderer_headless.cc",
diff --git a/shell/platform/linux/fl_renderable.cc b/shell/platform/linux/fl_renderable.cc
new file mode 100644
index 0000000..b8a44a5
--- /dev/null
+++ b/shell/platform/linux/fl_renderable.cc
@@ -0,0 +1,15 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "flutter/shell/platform/linux/fl_renderable.h"
+
+G_DEFINE_INTERFACE(FlRenderable, fl_renderable, G_TYPE_OBJECT)
+
+static void fl_renderable_default_init(FlRenderableInterface* iface) {}
+
+void fl_renderable_redraw(FlRenderable* self) {
+  g_return_if_fail(FL_IS_RENDERABLE(self));
+
+  FL_RENDERABLE_GET_IFACE(self)->redraw(self);
+}
diff --git a/shell/platform/linux/fl_renderable.h b/shell/platform/linux/fl_renderable.h
new file mode 100644
index 0000000..8a17caa
--- /dev/null
+++ b/shell/platform/linux/fl_renderable.h
@@ -0,0 +1,42 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FLUTTER_SHELL_PLATFORM_LINUX_FL_RENDERABLE_H_
+#define FLUTTER_SHELL_PLATFORM_LINUX_FL_RENDERABLE_H_
+
+#include <gdk/gdk.h>
+
+#include "flutter/shell/platform/embedder/embedder.h"
+
+G_BEGIN_DECLS
+
+G_DECLARE_INTERFACE(FlRenderable, fl_renderable, FL, RENDERABLE, GObject);
+
+/**
+ * FlRenderable:
+ *
+ * An interface for a class that can render views from #FlRenderer.
+ *
+ * This interface is typically implemented by #FlView and is provided to make
+ * #FlRenderer easier to test.
+ */
+
+struct _FlRenderableInterface {
+  GTypeInterface g_iface;
+
+  void (*redraw)(FlRenderable* renderable);
+};
+
+/**
+ * fl_renderable_redraw:
+ * @renderable: an #FlRenderable
+ *
+ * Indicate the renderable needs to redraw. When ready, the renderable should
+ * call fl_renderer_draw().
+ */
+void fl_renderable_redraw(FlRenderable* renderable);
+
+G_END_DECLS
+
+#endif  // FLUTTER_SHELL_PLATFORM_LINUX_FL_RENDERABLE_H_
diff --git a/shell/platform/linux/fl_renderer.cc b/shell/platform/linux/fl_renderer.cc
index 52aa9de..64a891b 100644
--- a/shell/platform/linux/fl_renderer.cc
+++ b/shell/platform/linux/fl_renderer.cc
@@ -10,7 +10,6 @@
 #include "flutter/shell/platform/embedder/embedder.h"
 #include "flutter/shell/platform/linux/fl_engine_private.h"
 #include "flutter/shell/platform/linux/fl_framebuffer.h"
-#include "flutter/shell/platform/linux/fl_view_private.h"
 
 // Vertex shader to draw Flutter window contents.
 static const char* vertex_shader_src =
@@ -77,6 +76,12 @@
 
 G_DEFINE_TYPE_WITH_PRIVATE(FlRenderer, fl_renderer, G_TYPE_OBJECT)
 
+static void free_weak_ref(gpointer value) {
+  GWeakRef* ref = static_cast<GWeakRef*>(value);
+  g_weak_ref_clear(ref);
+  free(ref);
+}
+
 // Check if running on an NVIDIA driver.
 static gboolean is_nvidia() {
   const gchar* vendor = reinterpret_cast<const gchar*>(glGetString(GL_VENDOR));
@@ -295,8 +300,8 @@
 static void fl_renderer_init(FlRenderer* self) {
   FlRendererPrivate* priv = reinterpret_cast<FlRendererPrivate*>(
       fl_renderer_get_instance_private(self));
-  priv->views =
-      g_hash_table_new_full(g_direct_hash, g_direct_equal, nullptr, nullptr);
+  priv->views = g_hash_table_new_full(g_direct_hash, g_direct_equal, nullptr,
+                                      free_weak_ref);
   priv->framebuffers_by_view_id =
       g_hash_table_new_full(g_direct_hash, g_direct_equal, nullptr,
                             (GDestroyNotify)g_ptr_array_unref);
@@ -311,15 +316,17 @@
   g_weak_ref_init(&priv->engine, engine);
 }
 
-void fl_renderer_add_view(FlRenderer* self,
-                          FlutterViewId view_id,
-                          FlView* view) {
+void fl_renderer_add_renderable(FlRenderer* self,
+                                FlutterViewId view_id,
+                                FlRenderable* renderable) {
   FlRendererPrivate* priv = reinterpret_cast<FlRendererPrivate*>(
       fl_renderer_get_instance_private(self));
 
   g_return_if_fail(FL_IS_RENDERER(self));
 
-  g_hash_table_insert(priv->views, GINT_TO_POINTER(view_id), view);
+  GWeakRef* ref = g_new(GWeakRef, 1);
+  g_weak_ref_init(ref, G_OBJECT(renderable));
+  g_hash_table_insert(priv->views, GINT_TO_POINTER(view_id), ref);
 }
 
 void fl_renderer_remove_view(FlRenderer* self, FlutterViewId view_id) {
@@ -472,10 +479,12 @@
     }
   }
 
-  FlView* view =
-      FL_VIEW(g_hash_table_lookup(priv->views, GINT_TO_POINTER(view_id)));
-  if (view != nullptr) {
-    fl_view_redraw(view);
+  GWeakRef* ref = static_cast<GWeakRef*>(
+      g_hash_table_lookup(priv->views, GINT_TO_POINTER(view_id)));
+  g_autoptr(FlRenderable) renderable =
+      ref != nullptr ? FL_RENDERABLE(g_weak_ref_get(ref)) : nullptr;
+  if (renderable != nullptr) {
+    fl_renderable_redraw(renderable);
   }
 
   return TRUE;
diff --git a/shell/platform/linux/fl_renderer.h b/shell/platform/linux/fl_renderer.h
index 35d3669..b107442 100644
--- a/shell/platform/linux/fl_renderer.h
+++ b/shell/platform/linux/fl_renderer.h
@@ -7,11 +7,11 @@
 
 #include <gtk/gtk.h>
 
-#include "flutter/shell/platform/linux/public/flutter_linux/fl_dart_project.h"
-#include "flutter/shell/platform/linux/public/flutter_linux/fl_view.h"
-
 #include "flutter/shell/platform/embedder/embedder.h"
 
+#include "flutter/shell/platform/linux/fl_renderable.h"
+#include "flutter/shell/platform/linux/public/flutter_linux/fl_engine.h"
+
 G_BEGIN_DECLS
 
 /**
@@ -79,16 +79,16 @@
 void fl_renderer_set_engine(FlRenderer* renderer, FlEngine* engine);
 
 /**
- * fl_renderer_add_view:
+ * fl_renderer_add_renderable:
  * @renderer: an #FlRenderer.
  * @view_id: the ID of the view.
- * @view: the view Flutter is renderering to.
+ * @renderable: object that is to be rendered on.
  *
  * Add a view to render on.
  */
-void fl_renderer_add_view(FlRenderer* renderer,
-                          FlutterViewId view_id,
-                          FlView* view);
+void fl_renderer_add_renderable(FlRenderer* renderer,
+                                FlutterViewId view_id,
+                                FlRenderable* renderable);
 
 /**
  * fl_renderer_remove_view:
diff --git a/shell/platform/linux/fl_renderer_test.cc b/shell/platform/linux/fl_renderer_test.cc
index b9336a1..7c4d83c 100644
--- a/shell/platform/linux/fl_renderer_test.cc
+++ b/shell/platform/linux/fl_renderer_test.cc
@@ -6,7 +6,6 @@
 
 #include "flutter/fml/logging.h"
 #include "flutter/shell/platform/linux/fl_framebuffer.h"
-#include "flutter/shell/platform/linux/testing/fl_test_gtk_logs.h"
 #include "flutter/shell/platform/linux/testing/mock_epoxy.h"
 #include "flutter/shell/platform/linux/testing/mock_renderer.h"
 
@@ -48,14 +47,12 @@
   constexpr int kWidth = 100;
   constexpr int kHeight = 100;
 
-  flutter::testing::fl_ensure_gtk_init();
-  g_autoptr(FlDartProject) project = fl_dart_project_new();
-  g_autoptr(FlView) view = fl_view_new(project);
+  g_autoptr(FlRenderable) renderable = FL_RENDERABLE(fl_mock_renderable_new());
   g_autoptr(FlMockRenderer) renderer = fl_mock_renderer_new();
   g_autoptr(FlFramebuffer) framebuffer =
       fl_framebuffer_new(GL_RGB, kWidth, kHeight);
 
-  fl_renderer_add_view(FL_RENDERER(renderer), 0, view);
+  fl_renderer_add_renderable(FL_RENDERER(renderer), 0, renderable);
   fl_renderer_wait_for_frame(FL_RENDERER(renderer), kWidth, kHeight);
 
   FlutterBackingStore backing_store;
@@ -84,8 +81,6 @@
   glGetIntegerv(GL_TEXTURE_BINDING_2D,
                 reinterpret_cast<GLint*>(&texture_2d_binding));
   EXPECT_EQ(texture_2d_binding, kFakeTextureName);
-
-  g_object_ref_sink(view);
 }
 
 static constexpr double kExpectedRefreshRate = 120.0;
@@ -94,8 +89,6 @@
 }
 
 TEST(FlRendererTest, RefreshRate) {
-  flutter::testing::fl_ensure_gtk_init();
-  g_autoptr(FlDartProject) project = fl_dart_project_new();
   g_autoptr(FlMockRenderer) renderer =
       fl_mock_renderer_new(&renderer_get_refresh_rate);
 
diff --git a/shell/platform/linux/fl_view.cc b/shell/platform/linux/fl_view.cc
index e84b144..5a68647 100644
--- a/shell/platform/linux/fl_view.cc
+++ b/shell/platform/linux/fl_view.cc
@@ -91,6 +91,8 @@
 
 static guint fl_view_signals[kSignalLastSignal];
 
+static void fl_renderable_iface_init(FlRenderableInterface* iface);
+
 static void fl_view_plugin_registry_iface_init(
     FlPluginRegistryInterface* iface);
 
@@ -107,14 +109,16 @@
     FlView,
     fl_view,
     GTK_TYPE_BOX,
-    G_IMPLEMENT_INTERFACE(fl_plugin_registry_get_type(),
-                          fl_view_plugin_registry_iface_init)
-        G_IMPLEMENT_INTERFACE(fl_keyboard_view_delegate_get_type(),
-                              fl_view_keyboard_delegate_iface_init)
-            G_IMPLEMENT_INTERFACE(fl_scrolling_view_delegate_get_type(),
-                                  fl_view_scrolling_delegate_iface_init)
-                G_IMPLEMENT_INTERFACE(fl_text_input_view_delegate_get_type(),
-                                      fl_view_text_input_delegate_iface_init))
+    G_IMPLEMENT_INTERFACE(fl_renderable_get_type(), fl_renderable_iface_init)
+        G_IMPLEMENT_INTERFACE(fl_plugin_registry_get_type(),
+                              fl_view_plugin_registry_iface_init)
+            G_IMPLEMENT_INTERFACE(fl_keyboard_view_delegate_get_type(),
+                                  fl_view_keyboard_delegate_iface_init)
+                G_IMPLEMENT_INTERFACE(fl_scrolling_view_delegate_get_type(),
+                                      fl_view_scrolling_delegate_iface_init)
+                    G_IMPLEMENT_INTERFACE(
+                        fl_text_input_view_delegate_get_type(),
+                        fl_view_text_input_delegate_iface_init))
 
 // Emit the first frame signal in the main thread.
 static gboolean first_frame_idle_cb(gpointer user_data) {
@@ -317,6 +321,20 @@
   init_scrolling(self);
 }
 
+// Implements FlRenderable::redraw
+static void fl_view_redraw(FlRenderable* renderable) {
+  FlView* self = FL_VIEW(renderable);
+
+  gtk_widget_queue_draw(GTK_WIDGET(self->gl_area));
+
+  if (!self->have_first_frame) {
+    self->have_first_frame = TRUE;
+    // This is not the main thread, so the signal needs to be done via an idle
+    // callback.
+    g_idle_add(first_frame_idle_cb, self);
+  }
+}
+
 // Implements FlPluginRegistry::get_registrar_for_plugin.
 static FlPluginRegistrar* fl_view_get_registrar_for_plugin(
     FlPluginRegistry* registry,
@@ -328,6 +346,10 @@
                                  fl_engine_get_texture_registrar(self->engine));
 }
 
+static void fl_renderable_iface_init(FlRenderableInterface* iface) {
+  iface->redraw = fl_view_redraw;
+}
+
 static void fl_view_plugin_registry_iface_init(
     FlPluginRegistryInterface* iface) {
   iface->get_registrar_for_plugin = fl_view_get_registrar_for_plugin;
@@ -621,7 +643,8 @@
 
   init_keyboard(self);
 
-  fl_renderer_add_view(FL_RENDERER(self->renderer), self->view_id, self);
+  fl_renderer_add_renderable(FL_RENDERER(self->renderer), self->view_id,
+                             FL_RENDERABLE(self));
 
   if (!fl_engine_start(self->engine, &error)) {
     g_warning("Failed to start Flutter engine: %s", error->message);
@@ -866,7 +889,8 @@
 
   self->view_id = fl_engine_add_view(self->engine, 1, 1, 1.0, self->cancellable,
                                      view_added_cb, self);
-  fl_renderer_add_view(FL_RENDERER(self->renderer), self->view_id, self);
+  fl_renderer_add_renderable(FL_RENDERER(self->renderer), self->view_id,
+                             FL_RENDERABLE(self));
 
   return self;
 }
@@ -889,19 +913,6 @@
   self->background_color = gdk_rgba_copy(color);
 }
 
-void fl_view_redraw(FlView* self) {
-  g_return_if_fail(FL_IS_VIEW(self));
-
-  gtk_widget_queue_draw(GTK_WIDGET(self->gl_area));
-
-  if (!self->have_first_frame) {
-    self->have_first_frame = TRUE;
-    // This is not the main thread, so the signal needs to be done via an idle
-    // callback.
-    g_idle_add(first_frame_idle_cb, self);
-  }
-}
-
 GHashTable* fl_view_get_keyboard_state(FlView* self) {
   g_return_val_if_fail(FL_IS_VIEW(self), nullptr);
   return fl_keyboard_handler_get_pressed_state(self->keyboard_handler);
diff --git a/shell/platform/linux/fl_view_private.h b/shell/platform/linux/fl_view_private.h
index de3fc9c..e23b178 100644
--- a/shell/platform/linux/fl_view_private.h
+++ b/shell/platform/linux/fl_view_private.h
@@ -8,14 +8,6 @@
 #include "flutter/shell/platform/linux/public/flutter_linux/fl_view.h"
 
 /**
- * fl_view_redraw:
- * @view: an #FlView.
- *
- * Indicate the view needs to redraw.
- */
-void fl_view_redraw(FlView* view);
-
-/**
  * fl_view_get_keyboard_state:
  * @view: an #FlView.
  *
diff --git a/shell/platform/linux/fl_view_test.cc b/shell/platform/linux/fl_view_test.cc
index 8abc8a1..acf1bca 100644
--- a/shell/platform/linux/fl_view_test.cc
+++ b/shell/platform/linux/fl_view_test.cc
@@ -50,7 +50,7 @@
 
   EXPECT_FALSE(first_frame_emitted);
 
-  fl_view_redraw(view);
+  fl_renderable_redraw(FL_RENDERABLE(view));
 
   // Signal is emitted in idle, clear the main loop.
   while (g_main_context_iteration(g_main_context_default(), FALSE)) {
diff --git a/shell/platform/linux/testing/mock_renderer.cc b/shell/platform/linux/testing/mock_renderer.cc
index c70980b..1106554 100644
--- a/shell/platform/linux/testing/mock_renderer.cc
+++ b/shell/platform/linux/testing/mock_renderer.cc
@@ -9,8 +9,20 @@
   FlMockRendererGetRefreshRate get_refresh_rate;
 };
 
+struct _FlMockRenderable {
+  GObject parent_instance;
+};
+
 G_DEFINE_TYPE(FlMockRenderer, fl_mock_renderer, fl_renderer_get_type())
 
+static void mock_renderable_iface_init(FlRenderableInterface* iface);
+
+G_DEFINE_TYPE_WITH_CODE(FlMockRenderable,
+                        fl_mock_renderable,
+                        g_object_get_type(),
+                        G_IMPLEMENT_INTERFACE(fl_renderable_get_type(),
+                                              mock_renderable_iface_init))
+
 // Implements FlRenderer::make_current.
 static void fl_mock_renderer_make_current(FlRenderer* renderer) {}
 
@@ -40,11 +52,27 @@
 
 static void fl_mock_renderer_init(FlMockRenderer* self) {}
 
+static void mock_renderable_redraw(FlRenderable* renderable) {}
+
+static void mock_renderable_iface_init(FlRenderableInterface* iface) {
+  iface->redraw = mock_renderable_redraw;
+}
+
+static void fl_mock_renderable_class_init(FlMockRenderableClass* klass) {}
+
+static void fl_mock_renderable_init(FlMockRenderable* self) {}
+
 // Creates a stub renderer
 FlMockRenderer* fl_mock_renderer_new(
     FlMockRendererGetRefreshRate get_refresh_rate) {
-  FlMockRenderer* fl_mock_renderer = FL_MOCK_RENDERER(
-      g_object_new_valist(fl_mock_renderer_get_type(), nullptr, nullptr));
-  fl_mock_renderer->get_refresh_rate = get_refresh_rate;
-  return fl_mock_renderer;
+  FlMockRenderer* self =
+      FL_MOCK_RENDERER(g_object_new(fl_mock_renderer_get_type(), nullptr));
+  self->get_refresh_rate = get_refresh_rate;
+  return self;
+}
+
+// Creates a sub renderable.
+FlMockRenderable* fl_mock_renderable_new() {
+  return FL_MOCK_RENDERABLE(
+      g_object_new(fl_mock_renderable_get_type(), nullptr));
 }
diff --git a/shell/platform/linux/testing/mock_renderer.h b/shell/platform/linux/testing/mock_renderer.h
index 2dbbe08..0838708 100644
--- a/shell/platform/linux/testing/mock_renderer.h
+++ b/shell/platform/linux/testing/mock_renderer.h
@@ -5,6 +5,7 @@
 #ifndef FLUTTER_SHELL_PLATFORM_LINUX_TESTING_MOCK_RENDERER_H_
 #define FLUTTER_SHELL_PLATFORM_LINUX_TESTING_MOCK_RENDERER_H_
 
+#include "flutter/shell/platform/linux/fl_renderable.h"
 #include "flutter/shell/platform/linux/fl_renderer.h"
 
 G_BEGIN_DECLS
@@ -15,11 +16,19 @@
                      MOCK_RENDERER,
                      FlRenderer)
 
+G_DECLARE_FINAL_TYPE(FlMockRenderable,
+                     fl_mock_renderable,
+                     FL,
+                     MOCK_RENDERABLE,
+                     GObject)
+
 typedef gdouble (*FlMockRendererGetRefreshRate)(FlRenderer* renderer);
 
 FlMockRenderer* fl_mock_renderer_new(
     FlMockRendererGetRefreshRate get_refresh_rate = nullptr);
 
+FlMockRenderable* fl_mock_renderable_new();
+
 G_END_DECLS
 
 #endif  // FLUTTER_SHELL_PLATFORM_LINUX_TESTING_MOCK_RENDERER_H_