Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion runtime/runtime_controller.cc
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ RuntimeController::~RuntimeController() {
}
}

bool RuntimeController::IsRootIsolateRunning() {
bool RuntimeController::IsRootIsolateRunning() const {
std::shared_ptr<DartIsolate> root_isolate = root_isolate_.lock();
if (root_isolate) {
return root_isolate->GetPhase() == DartIsolate::Phase::Running;
Expand Down
2 changes: 1 addition & 1 deletion runtime/runtime_controller.h
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,7 @@ class RuntimeController : public PlatformConfigurationClient,
///
/// @return True if root isolate running, False otherwise.
///
virtual bool IsRootIsolateRunning();
virtual bool IsRootIsolateRunning() const;

//----------------------------------------------------------------------------
/// @brief Dispatch the specified platform message to running root
Expand Down
2 changes: 1 addition & 1 deletion shell/common/engine_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ class MockRuntimeController : public RuntimeController {
MockRuntimeController(RuntimeDelegate& client,
const TaskRunners& p_task_runners)
: RuntimeController(client, p_task_runners) {}
MOCK_METHOD(bool, IsRootIsolateRunning, (), (override));
MOCK_METHOD(bool, IsRootIsolateRunning, (), (override, const));
MOCK_METHOD(bool,
DispatchPlatformMessage,
(std::unique_ptr<PlatformMessage>),
Expand Down
42 changes: 32 additions & 10 deletions shell/common/shell.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1074,16 +1074,38 @@ void Shell::OnPlatformViewDispatchPlatformMessage(
}
#endif // FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG

// 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 {
if (engine) {
engine->DispatchPlatformMessage(std::move(message));
}
}));
// If the root isolate is not yet running this may be the navigation
// channel initial route and must be dispatched immediately so that
// it can be set before isolate creation.
static constexpr char kNavigationChannel[] = "flutter/navigation";
if (!engine_->GetRuntimeController()->IsRootIsolateRunning() &&
message->channel() == kNavigationChannel) {
// 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 {
if (engine) {
engine->DispatchPlatformMessage(std::move(message));
}
}));
} else {
// In all other cases, the message must be dispatched via a new task so
// that the completion of the platform channel response future is guaranteed
// to wake up the Dart event loop, even in cases where the platform and UI
// threads are the same.

// The static leak checker gets confused by the use of fml::MakeCopyable.
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
task_runners_.GetUITaskRunner()->PostTask(
fml::MakeCopyable([engine = engine_->GetWeakPtr(),
message = std::move(message)]() mutable {
if (engine) {
engine->DispatchPlatformMessage(std::move(message));
}
}));
}
}

// |PlatformView::Delegate|
Expand Down
52 changes: 52 additions & 0 deletions shell/common/shell_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,33 @@ using ::testing::_;
using ::testing::Return;

namespace {

std::unique_ptr<PlatformMessage> MakePlatformMessage(
const std::string& channel,
const std::map<std::string, std::string>& values,
const fml::RefPtr<PlatformMessageResponse>& response) {
rapidjson::Document document;
auto& allocator = document.GetAllocator();
document.SetObject();

for (const auto& pair : values) {
rapidjson::Value key(pair.first.c_str(), strlen(pair.first.c_str()),
allocator);
rapidjson::Value value(pair.second.c_str(), strlen(pair.second.c_str()),
allocator);
document.AddMember(key, value, allocator);
}

rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
document.Accept(writer);
const uint8_t* data = reinterpret_cast<const uint8_t*>(buffer.GetString());

std::unique_ptr<PlatformMessage> message = std::make_unique<PlatformMessage>(
channel, fml::MallocMapping::Copy(data, buffer.GetSize()), response);
return message;
}

class MockPlatformViewDelegate : public PlatformView::Delegate {
MOCK_METHOD(void,
OnPlatformViewCreated,
Expand Down Expand Up @@ -4260,6 +4287,31 @@ TEST_F(ShellTest, PrintsErrorWhenPlatformMessageSentFromWrongThread) {
#endif
}

TEST_F(ShellTest, NavigationMessageDispachedImmediately) {
Settings settings = CreateSettingsForFixture();
ThreadHost thread_host("io.flutter.test." + GetCurrentTestName() + ".",
ThreadHost::Type::kPlatform);
auto task_runner = thread_host.platform_thread->GetTaskRunner();
TaskRunners task_runners("test", task_runner, task_runner, task_runner,
task_runner);
auto shell = CreateShell(settings, task_runners);

auto latch = std::make_shared<fml::CountDownLatch>(1u);
task_runner->PostTask([&]() {
auto message = MakePlatformMessage(
"flutter/navigation",
{{"method", "setInitialRoute"}, {"args", "/testo"}}, nullptr);
SendPlatformMessage(shell.get(), std::move(message));
EXPECT_EQ(shell->GetEngine()->InitialRoute(), "/testo");

latch->CountDown();
});
latch->Wait();

DestroyShell(std::move(shell), task_runners);
ASSERT_FALSE(DartVMRef::IsInstanceRunning());
}

TEST_F(ShellTest, DiesIfSoftwareRenderingAndImpellerAreEnabledDeathTest) {
#if defined(OS_FUCHSIA)
GTEST_SKIP() << "Fuchsia";
Expand Down