diff --git a/shell/common/fixtures/shell_test.dart b/shell/common/fixtures/shell_test.dart index 75fad7e109283..492ddb8960d03 100644 --- a/shell/common/fixtures/shell_test.dart +++ b/shell/common/fixtures/shell_test.dart @@ -163,3 +163,19 @@ void localtimesMatch() { // formatting since package:intl is not available. notifyLocalTime(timeStr.split(":")[0]); } + +void notifyCanAccessResource(bool success) native 'NotifyCanAccessResource'; + +void notifySetAssetBundlePath() native 'NotifySetAssetBundlePath'; + +@pragma('vm:entry-point') +void canAccessResourceFromAssetDir() async { + notifySetAssetBundlePath(); + window.sendPlatformMessage( + 'flutter/assets', + Uint8List.fromList(utf8.encode('kernel_blob.bin')).buffer.asByteData(), + (ByteData byteData) { + notifyCanAccessResource(byteData != null); + }, + ); +} diff --git a/shell/common/shell.cc b/shell/common/shell.cc index 1503519ca7f4d..2da9e9786e385 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -1404,6 +1404,7 @@ bool Shell::OnServiceProtocolRunInView( configuration.AddAssetResolver( std::make_unique(fml::OpenDirectory( asset_directory_path.c_str(), false, fml::FilePermission::kRead))); + configuration.AddAssetResolver(RestoreOriginalAssetResolver()); auto& allocator = response->GetAllocator(); response->SetObject(); @@ -1516,6 +1517,7 @@ bool Shell::OnServiceProtocolSetAssetBundlePath( auto asset_manager = std::make_shared(); + asset_manager->PushFront(RestoreOriginalAssetResolver()); asset_manager->PushFront(std::make_unique( fml::OpenDirectory(params.at("assetDirectory").data(), false, fml::FilePermission::kRead))); @@ -1622,4 +1624,16 @@ void Shell::OnDisplayUpdates(DisplayUpdateType update_type, display_manager_->HandleDisplayUpdates(update_type, displays); } +// Add the original asset directory to the resolvers so that unmodified assets +// bundled with the application specific format (APK, IPA) can be used without +// syncing to the Dart devFS. +std::unique_ptr Shell::RestoreOriginalAssetResolver() { + if (fml::UniqueFD::traits_type::IsValid(settings_.assets_dir)) { + return std::make_unique( + fml::Duplicate(settings_.assets_dir)); + } + return std::make_unique(fml::OpenDirectory( + settings_.assets_path.c_str(), false, fml::FilePermission::kRead)); +}; + } // namespace flutter diff --git a/shell/common/shell.h b/shell/common/shell.h index 74ad1c280e3ae..a345b0461e5fc 100644 --- a/shell/common/shell.h +++ b/shell/common/shell.h @@ -10,6 +10,7 @@ #include #include +#include "flutter/assets/directory_asset_bundle.h" #include "flutter/common/settings.h" #include "flutter/common/task_runners.h" #include "flutter/flow/surface.h" @@ -612,6 +613,10 @@ class Shell final : public PlatformView::Delegate, const ServiceProtocol::Handler::ServiceProtocolMap& params, rapidjson::Document* response); + // Creates an asset bundle from the original settings asset path or + // directory. + std::unique_ptr RestoreOriginalAssetResolver(); + // For accessing the Shell via the raster thread, necessary for various // rasterizer callbacks. std::unique_ptr> weak_factory_gpu_; diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index 2e708d0cbc612..fbf0647eb1191 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -2135,5 +2135,54 @@ TEST_F(ShellTest, IgnoresInvalidMetrics) { DestroyShell(std::move(shell), std::move(task_runners)); } +TEST_F(ShellTest, OnServiceProtocolSetAssetBundlePathWorks) { + Settings settings = CreateSettingsForFixture(); + std::unique_ptr shell = CreateShell(settings); + RunConfiguration configuration = + RunConfiguration::InferFromSettings(settings); + configuration.SetEntrypoint("canAccessResourceFromAssetDir"); + + // Verify isolate can load a known resource with the + // default asset directory - kernel_blob.bin + fml::AutoResetWaitableEvent latch; + + // Callback used to signal whether the resource was loaded successfully. + bool can_access_resource = false; + auto native_can_access_resource = [&can_access_resource, + &latch](Dart_NativeArguments args) { + Dart_Handle exception = nullptr; + can_access_resource = + tonic::DartConverter::FromArguments(args, 0, exception); + latch.Signal(); + }; + AddNativeCallback("NotifyCanAccessResource", + CREATE_NATIVE_ENTRY(native_can_access_resource)); + + // Callback used to delay the asset load until after the service + // protocol method has finished. + auto native_notify_set_asset_bundle_path = + [&shell](Dart_NativeArguments args) { + // Update the asset directory to a bonus path. + ServiceProtocol::Handler::ServiceProtocolMap params; + params["assetDirectory"] = "assetDirectory"; + rapidjson::Document document; + OnServiceProtocol(shell.get(), ServiceProtocolEnum::kSetAssetBundlePath, + shell->GetTaskRunners().GetUITaskRunner(), params, + &document); + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + document.Accept(writer); + }; + AddNativeCallback("NotifySetAssetBundlePath", + CREATE_NATIVE_ENTRY(native_notify_set_asset_bundle_path)); + + RunEngine(shell.get(), std::move(configuration)); + + latch.Wait(); + ASSERT_TRUE(can_access_resource); + + DestroyShell(std::move(shell)); +} + } // namespace testing } // namespace flutter