[ios] Create a standalone external view embedder on iOS (#21798)

diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter
index 2553764..db2650a 100755
--- a/ci/licenses_golden/licenses_flutter
+++ b/ci/licenses_golden/licenses_flutter
@@ -1003,6 +1003,8 @@
 FILE: ../../../flutter/shell/platform/darwin/ios/ios_external_texture_gl.mm
 FILE: ../../../flutter/shell/platform/darwin/ios/ios_external_texture_metal.h
 FILE: ../../../flutter/shell/platform/darwin/ios/ios_external_texture_metal.mm
+FILE: ../../../flutter/shell/platform/darwin/ios/ios_external_view_embedder.h
+FILE: ../../../flutter/shell/platform/darwin/ios/ios_external_view_embedder.mm
 FILE: ../../../flutter/shell/platform/darwin/ios/ios_render_target_gl.h
 FILE: ../../../flutter/shell/platform/darwin/ios/ios_render_target_gl.mm
 FILE: ../../../flutter/shell/platform/darwin/ios/ios_surface.h
diff --git a/shell/platform/darwin/ios/BUILD.gn b/shell/platform/darwin/ios/BUILD.gn
index fb0a845..793c97b 100644
--- a/shell/platform/darwin/ios/BUILD.gn
+++ b/shell/platform/darwin/ios/BUILD.gn
@@ -93,6 +93,8 @@
     "ios_context_software.mm",
     "ios_external_texture_gl.h",
     "ios_external_texture_gl.mm",
+    "ios_external_view_embedder.h",
+    "ios_external_view_embedder.mm",
     "ios_render_target_gl.h",
     "ios_render_target_gl.mm",
     "ios_surface.h",
diff --git a/shell/platform/darwin/ios/ios_external_view_embedder.h b/shell/platform/darwin/ios/ios_external_view_embedder.h
new file mode 100644
index 0000000..e717c2e
--- /dev/null
+++ b/shell/platform/darwin/ios/ios_external_view_embedder.h
@@ -0,0 +1,71 @@
+// 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_DARWIN_IOS_IOS_EXTERNAL_VIEW_EMBEDDER_H_
+#define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_IOS_EXTERNAL_VIEW_EMBEDDER_H_
+
+#include "flutter/flow/embedded_views.h"
+#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h"
+
+namespace flutter {
+
+class IOSExternalViewEmbedder : public ExternalViewEmbedder {
+ public:
+  IOSExternalViewEmbedder(
+      FlutterPlatformViewsController* platform_views_controller,
+      std::shared_ptr<IOSContext> context);
+
+  // |ExternalViewEmbedder|
+  virtual ~IOSExternalViewEmbedder() override;
+
+ private:
+  FlutterPlatformViewsController* platform_views_controller_;
+  std::shared_ptr<IOSContext> ios_context_;
+
+  // |ExternalViewEmbedder|
+  SkCanvas* GetRootCanvas() override;
+
+  // |ExternalViewEmbedder|
+  void CancelFrame() override;
+
+  // |ExternalViewEmbedder|
+  void BeginFrame(
+      SkISize frame_size,
+      GrDirectContext* context,
+      double device_pixel_ratio,
+      fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) override;
+
+  // |ExternalViewEmbedder|
+  void PrerollCompositeEmbeddedView(
+      int view_id,
+      std::unique_ptr<flutter::EmbeddedViewParams> params) override;
+
+  // |ExternalViewEmbedder|
+  PostPrerollResult PostPrerollAction(
+      fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) override;
+
+  // |ExternalViewEmbedder|
+  std::vector<SkCanvas*> GetCurrentCanvases() override;
+
+  // |ExternalViewEmbedder|
+  SkCanvas* CompositeEmbeddedView(int view_id) override;
+
+  // |ExternalViewEmbedder|
+  void SubmitFrame(GrDirectContext* context,
+                   std::unique_ptr<SurfaceFrame> frame) override;
+
+  // |ExternalViewEmbedder|
+  void EndFrame(
+      bool should_resubmit_frame,
+      fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) override;
+
+  // |ExternalViewEmbedder|
+  bool SupportsDynamicThreadMerging() override;
+
+  FML_DISALLOW_COPY_AND_ASSIGN(IOSExternalViewEmbedder);
+};
+
+}  // namespace flutter
+
+#endif  // FLUTTER_SHELL_PLATFORM_DARWIN_IOS_IOS_EXTERNAL_VIEW_EMBEDDER_H_
diff --git a/shell/platform/darwin/ios/ios_external_view_embedder.mm b/shell/platform/darwin/ios/ios_external_view_embedder.mm
new file mode 100644
index 0000000..8939b49
--- /dev/null
+++ b/shell/platform/darwin/ios/ios_external_view_embedder.mm
@@ -0,0 +1,96 @@
+// 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.
+
+#import "flutter/shell/platform/darwin/ios/ios_external_view_embedder.h"
+
+namespace flutter {
+
+IOSExternalViewEmbedder::IOSExternalViewEmbedder(
+    FlutterPlatformViewsController* platform_views_controller,
+    std::shared_ptr<IOSContext> context)
+    : platform_views_controller_(platform_views_controller), ios_context_(context) {
+  FML_CHECK(ios_context_);
+}
+
+IOSExternalViewEmbedder::~IOSExternalViewEmbedder() = default;
+
+// |ExternalViewEmbedder|
+SkCanvas* IOSExternalViewEmbedder::GetRootCanvas() {
+  // On iOS, the root surface is created from the on-screen render target. Only the surfaces for the
+  // various overlays are controlled by this class.
+  return nullptr;
+}
+
+// |ExternalViewEmbedder|
+void IOSExternalViewEmbedder::CancelFrame() {
+  TRACE_EVENT0("flutter", "IOSExternalViewEmbedder::CancelFrame");
+  FML_CHECK(platform_views_controller_);
+  platform_views_controller_->CancelFrame();
+}
+
+// |ExternalViewEmbedder|
+void IOSExternalViewEmbedder::BeginFrame(
+    SkISize frame_size,
+    GrDirectContext* context,
+    double device_pixel_ratio,
+    fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {
+  TRACE_EVENT0("flutter", "IOSExternalViewEmbedder::BeginFrame");
+  FML_CHECK(platform_views_controller_);
+  platform_views_controller_->SetFrameSize(frame_size);
+}
+
+// |ExternalViewEmbedder|
+void IOSExternalViewEmbedder::PrerollCompositeEmbeddedView(
+    int view_id,
+    std::unique_ptr<EmbeddedViewParams> params) {
+  TRACE_EVENT0("flutter", "IOSExternalViewEmbedder::PrerollCompositeEmbeddedView");
+  FML_CHECK(platform_views_controller_);
+  platform_views_controller_->PrerollCompositeEmbeddedView(view_id, std::move(params));
+}
+
+// |ExternalViewEmbedder|
+PostPrerollResult IOSExternalViewEmbedder::PostPrerollAction(
+    fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {
+  TRACE_EVENT0("flutter", "IOSExternalViewEmbedder::PostPrerollAction");
+  FML_CHECK(platform_views_controller_);
+  PostPrerollResult result = platform_views_controller_->PostPrerollAction(raster_thread_merger);
+  return result;
+}
+
+// |ExternalViewEmbedder|
+std::vector<SkCanvas*> IOSExternalViewEmbedder::GetCurrentCanvases() {
+  FML_CHECK(platform_views_controller_);
+  return platform_views_controller_->GetCurrentCanvases();
+}
+
+// |ExternalViewEmbedder|
+SkCanvas* IOSExternalViewEmbedder::CompositeEmbeddedView(int view_id) {
+  TRACE_EVENT0("flutter", "IOSExternalViewEmbedder::CompositeEmbeddedView");
+  FML_CHECK(platform_views_controller_);
+  return platform_views_controller_->CompositeEmbeddedView(view_id);
+}
+
+// |ExternalViewEmbedder|
+void IOSExternalViewEmbedder::SubmitFrame(GrDirectContext* context,
+                                          std::unique_ptr<SurfaceFrame> frame) {
+  TRACE_EVENT0("flutter", "IOSExternalViewEmbedder::SubmitFrame");
+  FML_CHECK(platform_views_controller_);
+  platform_views_controller_->SubmitFrame(std::move(context), ios_context_, std::move(frame));
+  TRACE_EVENT0("flutter", "IOSExternalViewEmbedder::DidSubmitFrame");
+}
+
+// |ExternalViewEmbedder|
+void IOSExternalViewEmbedder::EndFrame(bool should_resubmit_frame,
+                                       fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {
+  TRACE_EVENT0("flutter", "IOSExternalViewEmbedder::EndFrame");
+  FML_CHECK(platform_views_controller_);
+  return platform_views_controller_->EndFrame(should_resubmit_frame, raster_thread_merger);
+}
+
+// |ExternalViewEmbedder|
+bool IOSExternalViewEmbedder::SupportsDynamicThreadMerging() {
+  return true;
+}
+
+}  // namespace flutter
diff --git a/shell/platform/darwin/ios/ios_surface.h b/shell/platform/darwin/ios/ios_surface.h
index a01f9f1..8935b75 100644
--- a/shell/platform/darwin/ios/ios_surface.h
+++ b/shell/platform/darwin/ios/ios_surface.h
@@ -6,6 +6,7 @@
 #define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_IOS_SURFACE_H_
 
 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h"
+#import "flutter/shell/platform/darwin/ios/ios_external_view_embedder.h"
 
 #include <memory>
 
@@ -22,18 +23,19 @@
 // mechanism which is still in a release preview.
 bool IsIosEmbeddedViewsPreviewEnabled();
 
-class IOSSurface : public ExternalViewEmbedder {
+class IOSSurface {
  public:
   static std::unique_ptr<IOSSurface> Create(
       std::shared_ptr<IOSContext> context,
       fml::scoped_nsobject<CALayer> layer,
       FlutterPlatformViewsController* platform_views_controller);
 
-  // |ExternalViewEmbedder|
-  virtual ~IOSSurface();
-
   std::shared_ptr<IOSContext> GetContext() const;
 
+  std::shared_ptr<IOSExternalViewEmbedder> GetSurfaceExternalViewEmbedder() const;
+
+  virtual ~IOSSurface();
+
   virtual bool IsValid() const = 0;
 
   virtual void UpdateStorageSizeIfNecessary() = 0;
@@ -51,45 +53,8 @@
 
  private:
   std::shared_ptr<IOSContext> ios_context_;
-  FlutterPlatformViewsController* platform_views_controller_;
+  std::shared_ptr<IOSExternalViewEmbedder> external_view_embedder_;
 
-  // |ExternalViewEmbedder|
-  SkCanvas* GetRootCanvas() override;
-
-  // |ExternalViewEmbedder|
-  void CancelFrame() override;
-
-  // |ExternalViewEmbedder|
-  void BeginFrame(SkISize frame_size,
-                  GrDirectContext* context,
-                  double device_pixel_ratio,
-                  fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) override;
-
-  // |ExternalViewEmbedder|
-  void PrerollCompositeEmbeddedView(int view_id,
-                                    std::unique_ptr<flutter::EmbeddedViewParams> params) override;
-
-  // |ExternalViewEmbedder|
-  PostPrerollResult PostPrerollAction(
-      fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) override;
-
-  // |ExternalViewEmbedder|
-  std::vector<SkCanvas*> GetCurrentCanvases() override;
-
-  // |ExternalViewEmbedder|
-  SkCanvas* CompositeEmbeddedView(int view_id) override;
-
-  // |ExternalViewEmbedder|
-  void SubmitFrame(GrDirectContext* context, std::unique_ptr<SurfaceFrame> frame) override;
-
-  // |ExternalViewEmbedder|
-  void EndFrame(bool should_resubmit_frame,
-                fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) override;
-
-  // |ExternalViewEmbedder|
-  bool SupportsDynamicThreadMerging() override;
-
- public:
   FML_DISALLOW_COPY_AND_ASSIGN(IOSSurface);
 };
 
diff --git a/shell/platform/darwin/ios/ios_surface.mm b/shell/platform/darwin/ios/ios_surface.mm
index 331cbeb..49b418a 100644
--- a/shell/platform/darwin/ios/ios_surface.mm
+++ b/shell/platform/darwin/ios/ios_surface.mm
@@ -53,8 +53,10 @@
 
 IOSSurface::IOSSurface(std::shared_ptr<IOSContext> ios_context,
                        FlutterPlatformViewsController* platform_views_controller)
-    : ios_context_(std::move(ios_context)), platform_views_controller_(platform_views_controller) {
+    : ios_context_(std::move(ios_context)) {
   FML_DCHECK(ios_context_);
+  external_view_embedder_ =
+      std::make_shared<IOSExternalViewEmbedder>(platform_views_controller, ios_context_);
 }
 
 IOSSurface::~IOSSurface() = default;
@@ -63,80 +65,8 @@
   return ios_context_;
 }
 
-// |ExternalViewEmbedder|
-SkCanvas* IOSSurface::GetRootCanvas() {
-  // On iOS, the root surface is created from the on-screen render target. Only the surfaces for the
-  // various overlays are controlled by this class.
-  return nullptr;
-}
-
-// |ExternalViewEmbedder|
-void IOSSurface::CancelFrame() {
-  TRACE_EVENT0("flutter", "IOSSurface::CancelFrame");
-  FML_CHECK(platform_views_controller_ != nullptr);
-  platform_views_controller_->CancelFrame();
-}
-
-// |ExternalViewEmbedder|
-void IOSSurface::BeginFrame(SkISize frame_size,
-                            GrDirectContext* context,
-                            double device_pixel_ratio,
-                            fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {
-  TRACE_EVENT0("flutter", "IOSSurface::BeginFrame");
-  FML_CHECK(platform_views_controller_ != nullptr);
-  platform_views_controller_->SetFrameSize(frame_size);
-}
-
-// |ExternalViewEmbedder|
-void IOSSurface::PrerollCompositeEmbeddedView(int view_id,
-                                              std::unique_ptr<EmbeddedViewParams> params) {
-  TRACE_EVENT0("flutter", "IOSSurface::PrerollCompositeEmbeddedView");
-
-  FML_CHECK(platform_views_controller_ != nullptr);
-  platform_views_controller_->PrerollCompositeEmbeddedView(view_id, std::move(params));
-}
-
-// |ExternalViewEmbedder|
-PostPrerollResult IOSSurface::PostPrerollAction(
-    fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {
-  TRACE_EVENT0("flutter", "IOSSurface::PostPrerollAction");
-  FML_CHECK(platform_views_controller_ != nullptr);
-  PostPrerollResult result = platform_views_controller_->PostPrerollAction(raster_thread_merger);
-  return result;
-}
-
-// |ExternalViewEmbedder|
-std::vector<SkCanvas*> IOSSurface::GetCurrentCanvases() {
-  FML_CHECK(platform_views_controller_ != nullptr);
-  return platform_views_controller_->GetCurrentCanvases();
-}
-
-// |ExternalViewEmbedder|
-SkCanvas* IOSSurface::CompositeEmbeddedView(int view_id) {
-  TRACE_EVENT0("flutter", "IOSSurface::CompositeEmbeddedView");
-  FML_CHECK(platform_views_controller_ != nullptr);
-  return platform_views_controller_->CompositeEmbeddedView(view_id);
-}
-
-// |ExternalViewEmbedder|
-void IOSSurface::SubmitFrame(GrDirectContext* context, std::unique_ptr<SurfaceFrame> frame) {
-  TRACE_EVENT0("flutter", "IOSSurface::SubmitFrame");
-  FML_CHECK(platform_views_controller_ != nullptr);
-  platform_views_controller_->SubmitFrame(std::move(context), ios_context_, std::move(frame));
-  TRACE_EVENT0("flutter", "IOSSurface::DidSubmitFrame");
-}
-
-// |ExternalViewEmbedder|
-void IOSSurface::EndFrame(bool should_resubmit_frame,
-                          fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {
-  TRACE_EVENT0("flutter", "IOSSurface::EndFrame");
-  FML_CHECK(platform_views_controller_ != nullptr);
-  return platform_views_controller_->EndFrame(should_resubmit_frame, raster_thread_merger);
-}
-
-// |ExternalViewEmbedder|
-bool IOSSurface::SupportsDynamicThreadMerging() {
-  return true;
+std::shared_ptr<IOSExternalViewEmbedder> IOSSurface::GetSurfaceExternalViewEmbedder() const {
+  return external_view_embedder_;
 }
 
 }  // namespace flutter
diff --git a/shell/platform/darwin/ios/ios_surface_gl.mm b/shell/platform/darwin/ios/ios_surface_gl.mm
index 3565365..e114d5e 100644
--- a/shell/platform/darwin/ios/ios_surface_gl.mm
+++ b/shell/platform/darwin/ios/ios_surface_gl.mm
@@ -84,7 +84,7 @@
 
 // |GPUSurfaceGLDelegate|
 ExternalViewEmbedder* IOSSurfaceGL::GetExternalViewEmbedder() {
-  return this;
+  return GetSurfaceExternalViewEmbedder().get();
 }
 
 }  // namespace flutter
diff --git a/shell/platform/darwin/ios/ios_surface_metal.mm b/shell/platform/darwin/ios/ios_surface_metal.mm
index e7e4d12..ee240bd 100644
--- a/shell/platform/darwin/ios/ios_surface_metal.mm
+++ b/shell/platform/darwin/ios/ios_surface_metal.mm
@@ -55,7 +55,7 @@
 
 // |GPUSurfaceDelegate|
 ExternalViewEmbedder* IOSSurfaceMetal::GetExternalViewEmbedder() {
-  return this;
+  return GetSurfaceExternalViewEmbedder().get();
 }
 
 }  // namespace flutter
diff --git a/shell/platform/darwin/ios/ios_surface_software.mm b/shell/platform/darwin/ios/ios_surface_software.mm
index 1363bfe..7b5c2ee 100644
--- a/shell/platform/darwin/ios/ios_surface_software.mm
+++ b/shell/platform/darwin/ios/ios_surface_software.mm
@@ -124,7 +124,7 @@
 
 // |GPUSurfaceSoftwareDelegate|
 ExternalViewEmbedder* IOSSurfaceSoftware::GetExternalViewEmbedder() {
-  return this;
+  return GetSurfaceExternalViewEmbedder().get();
 }
 
 }  // namespace flutter