diff --git a/shell/common/engine.cc b/shell/common/engine.cc index ac628eb929465..37f71f4496d87 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -61,6 +61,7 @@ Engine::Engine( task_runners_(task_runners), weak_factory_(this) { pointer_data_dispatcher_ = dispatcher_maker(*this); + initial_route_ = settings_.route; } Engine::Engine(Delegate& delegate, diff --git a/shell/common/engine_unittests.cc b/shell/common/engine_unittests.cc index 7a822c5dca1b7..8e0108eaccc03 100644 --- a/shell/common/engine_unittests.cc +++ b/shell/common/engine_unittests.cc @@ -252,6 +252,27 @@ TEST_F(EngineTest, Create) { }); } +TEST_F(EngineTest, CreateWithRoute) { + PostUITaskSync([this] { + auto settings = settings_; + settings.route = "/testo"; + auto engine = std::make_unique( + /*delegate=*/delegate_, + /*dispatcher_maker=*/dispatcher_maker_, + /*image_decoder_task_runner=*/image_decoder_task_runner_, + /*task_runners=*/task_runners_, + /*settings=*/settings, + /*animator=*/std::move(animator_), + /*io_manager=*/io_manager_, + /*font_collection=*/std::make_shared(), + /*runtime_controller=*/std::move(runtime_controller_), + /*gpu_disabled_switch=*/std::make_shared()); + + EXPECT_TRUE(engine); + EXPECT_EQ(engine->InitialRoute(), "/testo"); + }); +} + TEST_F(EngineTest, DispatchPlatformMessageUnknown) { PostUITaskSync([this] { MockRuntimeDelegate client; diff --git a/shell/common/shell.cc b/shell/common/shell.cc index 79c532640a969..a23bc5498efe6 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -1076,10 +1076,10 @@ void Shell::OnPlatformViewDispatchPlatformMessage( // The static leak checker gets confused by the use of fml::MakeCopyable. // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) - fml::TaskRunner::RunNowOrPostTask( - task_runners_.GetUITaskRunner(), - fml::MakeCopyable([engine = engine_->GetWeakPtr(), - message = std::move(message)]() mutable { + // This logic must always explicitly post a task so that we are guaranteed + // to wake up the UI message loop to flush tasks. + task_runners_.GetUITaskRunner()->PostTask(fml::MakeCopyable( + [engine = engine_->GetWeakPtr(), message = std::move(message)]() mutable { if (engine) { engine->DispatchPlatformMessage(std::move(message)); } diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index b1ca5e66ae6d1..3b65f469820a8 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -602,13 +602,6 @@ - (void)setUpChannels { binaryMessenger:self.binaryMessenger codec:[FlutterJSONMethodCodec sharedInstance]]); - if ([_initialRoute length] > 0) { - // Flutter isn't ready to receive this method call yet but the channel buffer will cache this. - [_navigationChannel invokeMethod:@"setInitialRoute" arguments:_initialRoute]; - [_initialRoute release]; - _initialRoute = nil; - } - _restorationChannel.reset([[FlutterMethodChannel alloc] initWithName:@"flutter/restoration" binaryMessenger:self.binaryMessenger @@ -904,6 +897,13 @@ - (BOOL)createShell:(NSString*)entrypoint _isGpuDisabled = [UIApplication sharedApplication].applicationState == UIApplicationStateBackground; #endif + // Override the setting route, as the dart project or function may have specified + // different values. During construction, the Engine constuctor will read the + // value of settings.route to determine the initial route value. + if (self.initialRoute) { + settings.route = [self.initialRoute UTF8String]; + self.initialRoute = nil; + } // Create the shell. This is a blocking operation. std::unique_ptr shell = flutter::Shell::Create( diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngineTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngineTest.mm index 5779d4b496126..3b4702269bf85 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngineTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngineTest.mm @@ -201,17 +201,17 @@ - (void)testRunningInitialRouteSendsNavigationMessage { // Run with an initial route. [engine runWithEntrypoint:FlutterDefaultDartEntrypoint initialRoute:@"test"]; - // Now check that an encoded method call has been made on the binary messenger to set the - // initial route to "test". + // Initial route is set directly in the shell/engine and should not send a platform + // channel message as it will arrive too late. FlutterMethodCall* setInitialRouteMethodCall = [FlutterMethodCall methodCallWithMethodName:@"setInitialRoute" arguments:@"test"]; NSData* encodedSetInitialRouteMethod = [[FlutterJSONMethodCodec sharedInstance] encodeMethodCall:setInitialRouteMethodCall]; - OCMVerify([mockBinaryMessenger sendOnChannel:@"flutter/navigation" + OCMReject([mockBinaryMessenger sendOnChannel:@"flutter/navigation" message:encodedSetInitialRouteMethod]); } -- (void)testInitialRouteSettingsSendsNavigationMessage { +- (void)testInitialRouteSettingsDoesNotSendNavigationMessage { id mockBinaryMessenger = OCMClassMock([FlutterBinaryMessengerRelay class]); auto settings = FLTDefaultSettingsForBundle(); @@ -221,13 +221,13 @@ - (void)testInitialRouteSettingsSendsNavigationMessage { [engine setBinaryMessenger:mockBinaryMessenger]; [engine run]; - // Now check that an encoded method call has been made on the binary messenger to set the - // initial route to "test". + // Initial route is set directly in the shell/engine and should not send a platform + // channel message as it will arrive too late. FlutterMethodCall* setInitialRouteMethodCall = [FlutterMethodCall methodCallWithMethodName:@"setInitialRoute" arguments:@"test"]; NSData* encodedSetInitialRouteMethod = [[FlutterJSONMethodCodec sharedInstance] encodeMethodCall:setInitialRouteMethodCall]; - OCMVerify([mockBinaryMessenger sendOnChannel:@"flutter/navigation" + OCMReject([mockBinaryMessenger sendOnChannel:@"flutter/navigation" message:encodedSetInitialRouteMethod]); }