Add dart_entrypoint_argc/argv to the FlutterProjectArgs (#21737)

diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc
index 971ef22..06b45ac 100644
--- a/shell/platform/embedder/embedder.cc
+++ b/shell/platform/embedder/embedder.cc
@@ -1126,6 +1126,20 @@
     }
   }
 
+  if (SAFE_ACCESS(args, dart_entrypoint_argc, 0) > 0) {
+    if (SAFE_ACCESS(args, dart_entrypoint_argv, nullptr) == nullptr) {
+      return LOG_EMBEDDER_ERROR(kInvalidArguments,
+                                "Could not determine Dart entrypoint arguments "
+                                "as dart_entrypoint_argc "
+                                "was set, but dart_entrypoint_argv was null.");
+    }
+    std::vector<std::string> arguments(args->dart_entrypoint_argc);
+    for (int i = 0; i < args->dart_entrypoint_argc; ++i) {
+      arguments[i] = std::string{args->dart_entrypoint_argv[i]};
+    }
+    settings.dart_entrypoint_args = std::move(arguments);
+  }
+
   if (!run_configuration.IsValid()) {
     return LOG_EMBEDDER_ERROR(
         kInvalidArguments,
diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h
index 8ed2dad..51d832b 100644
--- a/shell/platform/embedder/embedder.h
+++ b/shell/platform/embedder/embedder.h
@@ -1368,6 +1368,19 @@
   /// matches what the platform would natively resolve to as possible.
   FlutterComputePlatformResolvedLocaleCallback
       compute_platform_resolved_locale_callback;
+
+  /// The command line argument count for arguments passed through to the Dart
+  /// entrypoint.
+  int dart_entrypoint_argc;
+
+  /// The command line arguments passed through to the Dart entrypoint. The
+  /// strings must be `NULL` terminated.
+  ///
+  /// The strings will be copied out and so any strings passed in here can
+  /// be safely collected after initializing the engine with
+  /// `FlutterProjectArgs`.
+  const char* const* dart_entrypoint_argv;
+
 } FlutterProjectArgs;
 
 //------------------------------------------------------------------------------
diff --git a/shell/platform/embedder/fixtures/main.dart b/shell/platform/embedder/fixtures/main.dart
index de4cf42..5dae4e4 100644
--- a/shell/platform/embedder/fixtures/main.dart
+++ b/shell/platform/embedder/fixtures/main.dart
@@ -733,3 +733,10 @@
   };
   window.scheduleFrame();
 }
+
+void nativeArgumentsCallback(List<String> args) native 'NativeArgumentsCallback';
+
+@pragma('vm:entry-point')
+void dart_entrypoint_args(List<String> args) {
+  nativeArgumentsCallback(args);
+}
diff --git a/shell/platform/embedder/tests/embedder_config_builder.cc b/shell/platform/embedder/tests/embedder_config_builder.cc
index 5c1f741..93d4c6a 100644
--- a/shell/platform/embedder/tests/embedder_config_builder.cc
+++ b/shell/platform/embedder/tests/embedder_config_builder.cc
@@ -217,6 +217,14 @@
   command_line_arguments_.emplace_back(std::move(arg));
 }
 
+void EmbedderConfigBuilder::AddDartEntrypointArgument(std::string arg) {
+  if (arg.size() == 0) {
+    return;
+  }
+
+  dart_entrypoint_arguments_.emplace_back(std::move(arg));
+}
+
 void EmbedderConfigBuilder::SetPlatformTaskRunner(
     const FlutterTaskRunnerDescription* runner) {
   if (runner == nullptr) {
@@ -317,6 +325,23 @@
     project_args.command_line_argc = 0;
   }
 
+  std::vector<const char*> dart_args;
+  dart_args.reserve(dart_entrypoint_arguments_.size());
+
+  for (const auto& arg : dart_entrypoint_arguments_) {
+    dart_args.push_back(arg.c_str());
+  }
+
+  if (dart_args.size() > 0) {
+    project_args.dart_entrypoint_argv = dart_args.data();
+    project_args.dart_entrypoint_argc = dart_args.size();
+  } else {
+    // Clear it out in case this is not the first engine launch from the
+    // embedder config builder.
+    project_args.dart_entrypoint_argv = nullptr;
+    project_args.dart_entrypoint_argc = 0;
+  }
+
   auto result =
       run ? FlutterEngineRun(FLUTTER_ENGINE_VERSION, &renderer_config_,
                              &project_args, &context_, &engine)
diff --git a/shell/platform/embedder/tests/embedder_config_builder.h b/shell/platform/embedder/tests/embedder_config_builder.h
index 29dc4ef..4c37d64 100644
--- a/shell/platform/embedder/tests/embedder_config_builder.h
+++ b/shell/platform/embedder/tests/embedder_config_builder.h
@@ -76,6 +76,8 @@
 
   void AddCommandLineArgument(std::string arg);
 
+  void AddDartEntrypointArgument(std::string arg);
+
   void SetPlatformTaskRunner(const FlutterTaskRunnerDescription* runner);
 
   void SetRenderTaskRunner(const FlutterTaskRunnerDescription* runner);
@@ -106,6 +108,7 @@
   FlutterCustomTaskRunners custom_task_runners_ = {};
   FlutterCompositor compositor_ = {};
   std::vector<std::string> command_line_arguments_;
+  std::vector<std::string> dart_entrypoint_arguments_;
 
   UniqueEngine SetupEngine(bool run) const;
 
diff --git a/shell/platform/embedder/tests/embedder_unittests.cc b/shell/platform/embedder/tests/embedder_unittests.cc
index 786b7da..01309bb 100644
--- a/shell/platform/embedder/tests/embedder_unittests.cc
+++ b/shell/platform/embedder/tests/embedder_unittests.cc
@@ -474,6 +474,33 @@
 }
 
 //------------------------------------------------------------------------------
+///
+TEST_F(EmbedderTest, DartEntrypointArgs) {
+  auto& context = GetEmbedderContext(ContextType::kSoftwareContext);
+  EmbedderConfigBuilder builder(context);
+  builder.SetSoftwareRendererConfig();
+  builder.AddDartEntrypointArgument("foo");
+  builder.AddDartEntrypointArgument("bar");
+  builder.SetDartEntrypoint("dart_entrypoint_args");
+  fml::AutoResetWaitableEvent callback_latch;
+  std::vector<std::string> callback_args;
+  auto nativeArgumentsCallback = [&callback_args,
+                                  &callback_latch](Dart_NativeArguments args) {
+    Dart_Handle exception = nullptr;
+    callback_args =
+        tonic::DartConverter<std::vector<std::string>>::FromArguments(
+            args, 0, exception);
+    callback_latch.Signal();
+  };
+  context.AddNativeCallback("NativeArgumentsCallback",
+                            CREATE_NATIVE_ENTRY(nativeArgumentsCallback));
+  auto engine = builder.LaunchEngine();
+  callback_latch.Wait();
+  ASSERT_EQ(callback_args[0], "foo");
+  ASSERT_EQ(callback_args[1], "bar");
+}
+
+//------------------------------------------------------------------------------
 /// These snapshots may be materialized from symbols and the size field may not
 /// be relevant. Since this information is redundant, engine launch should not
 /// be gated on a non-zero buffer size.