diff --git a/src/platform_api/platform_services.cpp b/src/platform_api/platform_services.cpp index dbd074c..a6dc3ae 100644 --- a/src/platform_api/platform_services.cpp +++ b/src/platform_api/platform_services.cpp @@ -1,7 +1,66 @@ #include "platform_api/platform_services.h" +#include "platform_api/platform_policy.h" + namespace pp::platform { namespace { + +class DefaultWebPlatformServices final : public WebPlatformServices { +public: + void publish_exported_image(std::string_view path) override + { + (void)path; + } + + void flush_persistent_storage() override + { + } + + [[nodiscard]] int default_canvas_resolution() override + { + return platform_default_canvas_resolution(PlatformFamily::webgl); + } + + void save_prepared_file( + std::string_view path, + std::string_view suggested_name, + PreparedFileCallback callback) override + { + (void)suggested_name; + callback(std::string(path), false); + } +}; + +WebPlatformServices* g_injected_web_platform_services = nullptr; + static_assert(sizeof(PlatformServices*) == sizeof(void*)); } + +WebPlatformServices& default_web_platform_services() +{ + static DefaultWebPlatformServices services; + return services; +} + +WebPlatformServices* injected_web_platform_services() noexcept +{ + return g_injected_web_platform_services; +} + +void set_injected_web_platform_services(WebPlatformServices* services) noexcept +{ + g_injected_web_platform_services = services; +} + +WebPlatformServices& resolve_web_platform_services(WebPlatformServices& fallback) noexcept +{ + if (auto* services = injected_web_platform_services()) + return *services; + return fallback; +} + +WebPlatformServices& active_web_platform_services() +{ + return resolve_web_platform_services(default_web_platform_services()); +} } diff --git a/src/platform_api/platform_services.h b/src/platform_api/platform_services.h index 4bf4e63..cb32245 100644 --- a/src/platform_api/platform_services.h +++ b/src/platform_api/platform_services.h @@ -90,4 +90,23 @@ public: PreparedFileCallback callback) = 0; }; +class WebPlatformServices { +public: + virtual ~WebPlatformServices() = default; + + virtual void publish_exported_image(std::string_view path) = 0; + virtual void flush_persistent_storage() = 0; + [[nodiscard]] virtual int default_canvas_resolution() = 0; + virtual void save_prepared_file( + std::string_view path, + std::string_view suggested_name, + PreparedFileCallback callback) = 0; +}; + +[[nodiscard]] WebPlatformServices& default_web_platform_services(); +[[nodiscard]] WebPlatformServices* injected_web_platform_services() noexcept; +void set_injected_web_platform_services(WebPlatformServices* services) noexcept; +[[nodiscard]] WebPlatformServices& resolve_web_platform_services(WebPlatformServices& fallback) noexcept; +[[nodiscard]] WebPlatformServices& active_web_platform_services(); + } diff --git a/src/platform_legacy/legacy_platform_services.cpp b/src/platform_legacy/legacy_platform_services.cpp index fe6b08b..fb36957 100644 --- a/src/platform_legacy/legacy_platform_services.cpp +++ b/src/platform_legacy/legacy_platform_services.cpp @@ -60,6 +60,48 @@ void invoke_picked_path_if_selected( callback(path); } +#ifdef __WEB__ +class RetainedWebPlatformServices final : public pp::platform::WebPlatformServices { +public: + void publish_exported_image(std::string_view path) override + { + (void)path; + } + + void flush_persistent_storage() override + { + webgl_sync(); + } + + [[nodiscard]] int default_canvas_resolution() override + { + return pp::platform::platform_default_canvas_resolution(pp::platform::PlatformFamily::webgl); + } + + void save_prepared_file( + std::string_view path, + std::string_view suggested_name, + pp::platform::PreparedFileCallback callback) override + { + const std::string value(path); + const std::string name(suggested_name); + webgl_pick_file_save(value, name, [callback = std::move(callback), value](bool success) { + callback(value, success); + }); + } +}; +#endif + +[[nodiscard]] pp::platform::WebPlatformServices& active_legacy_web_platform_services() +{ +#ifdef __WEB__ + static RetainedWebPlatformServices services; + return pp::platform::resolve_web_platform_services(services); +#else + return pp::platform::active_web_platform_services(); +#endif +} + // DEBT-0017: fallback for platforms that do not inject PlatformServices yet. class LegacyPlatformServices final : public pp::platform::PlatformServices { public: @@ -300,7 +342,13 @@ public: void publish_exported_image(std::string_view path) override { - if (!pp::platform::platform_publishes_exported_images(pp::platform::current_platform_family())) + const auto family = pp::platform::current_platform_family(); + if (family == pp::platform::PlatformFamily::webgl) + { + active_legacy_web_platform_services().publish_exported_image(path); + return; + } + if (!pp::platform::platform_publishes_exported_images(family)) { (void)path; return; @@ -314,11 +362,14 @@ public: void flush_persistent_storage() override { - if (!pp::platform::platform_flushes_persistent_storage(pp::platform::current_platform_family())) + const auto family = pp::platform::current_platform_family(); + if (family == pp::platform::PlatformFamily::webgl) + { + active_legacy_web_platform_services().flush_persistent_storage(); + return; + } + if (!pp::platform::platform_flushes_persistent_storage(family)) return; -#ifdef __WEB__ - webgl_sync(); -#endif } [[nodiscard]] std::vector document_browse_roots( @@ -501,8 +552,10 @@ public: [[nodiscard]] int default_canvas_resolution() override { - return pp::platform::platform_default_canvas_resolution( - pp::platform::current_platform_family()); + const auto family = pp::platform::current_platform_family(); + if (family == pp::platform::PlatformFamily::webgl) + return active_legacy_web_platform_services().default_canvas_resolution(); + return pp::platform::platform_default_canvas_resolution(family); } [[nodiscard]] bool draws_canvas_tip_for_pointer( @@ -591,6 +644,16 @@ public: std::string_view suggested_name, pp::platform::PreparedFileCallback callback) override { + const auto family = pp::platform::current_platform_family(); + if (family == pp::platform::PlatformFamily::webgl) + { + active_legacy_web_platform_services().save_prepared_file( + path, + suggested_name, + std::move(callback)); + return; + } + const std::string value(path); const std::string name(suggested_name); #ifdef __IOS__ @@ -599,10 +662,6 @@ public: [App::I->ios_view pick_file_save:value]; }); callback(value, true); -#elif __WEB__ - webgl_pick_file_save(value, name, [callback = std::move(callback), value](bool success) { - callback(value, success); - }); #else (void)name; callback(value, false); diff --git a/tests/platform_api/platform_services_tests.cpp b/tests/platform_api/platform_services_tests.cpp index 15bb2be..247cff1 100644 --- a/tests/platform_api/platform_services_tests.cpp +++ b/tests/platform_api/platform_services_tests.cpp @@ -14,6 +14,67 @@ namespace { +class FakeWebPlatformServices final : public pp::platform::WebPlatformServices { +public: + void publish_exported_image(std::string_view path) override + { + ++exported_image_publishes; + exported_image_path.assign(path); + } + + void flush_persistent_storage() override + { + ++persistent_storage_flushes; + } + + [[nodiscard]] int default_canvas_resolution() override + { + ++default_canvas_resolution_requests; + return canvas_resolution; + } + + void save_prepared_file( + std::string_view path, + std::string_view suggested_name, + pp::platform::PreparedFileCallback callback) override + { + ++save_prepared_file_requests; + prepared_file_path.assign(path); + prepared_file_name.assign(suggested_name); + callback(prepared_file_path, prepared_file_saved); + } + + int exported_image_publishes = 0; + int persistent_storage_flushes = 0; + int default_canvas_resolution_requests = 0; + int save_prepared_file_requests = 0; + int canvas_resolution = 2048; + bool prepared_file_saved = true; + std::string exported_image_path; + std::string prepared_file_path; + std::string prepared_file_name; +}; + +class ScopedInjectedWebPlatformServices final { +public: + explicit ScopedInjectedWebPlatformServices(pp::platform::WebPlatformServices* services) + : previous_(pp::platform::injected_web_platform_services()) + { + pp::platform::set_injected_web_platform_services(services); + } + + ~ScopedInjectedWebPlatformServices() + { + pp::platform::set_injected_web_platform_services(previous_); + } + + ScopedInjectedWebPlatformServices(const ScopedInjectedWebPlatformServices&) = delete; + ScopedInjectedWebPlatformServices& operator=(const ScopedInjectedWebPlatformServices&) = delete; + +private: + pp::platform::WebPlatformServices* previous_ = nullptr; +}; + class FakePlatformServices final : public pp::platform::PlatformServices { public: explicit FakePlatformServices(std::string clipboard_value) @@ -715,6 +776,68 @@ void platform_services_dispatch_prepared_file_save(pp::tests::Harness& harness) PP_EXPECT(harness, saved); } +void web_platform_services_preserve_default_web_policy(pp::tests::Harness& harness) +{ + ScopedInjectedWebPlatformServices scoped(nullptr); + auto& services = pp::platform::active_web_platform_services(); + std::string saved_path; + bool saved = true; + + services.publish_exported_image("/PanoPainter/export.png"); + services.flush_persistent_storage(); + services.save_prepared_file( + "/PanoPainter/export.png", + "export.png", + [&](std::string path, bool success) { + saved_path = std::move(path); + saved = success; + }); + + PP_EXPECT(harness, services.default_canvas_resolution() == 512); + PP_EXPECT(harness, saved_path == "/PanoPainter/export.png"); + PP_EXPECT(harness, !saved); +} + +void web_platform_services_resolve_injected_services(pp::tests::Harness& harness) +{ + FakeWebPlatformServices fallback; + FakeWebPlatformServices injected; + ScopedInjectedWebPlatformServices cleared(nullptr); + + PP_EXPECT(harness, &pp::platform::resolve_web_platform_services(fallback) == &fallback); + + { + ScopedInjectedWebPlatformServices scoped(&injected); + auto& services = pp::platform::active_web_platform_services(); + std::string saved_path; + bool saved = false; + + services.publish_exported_image("/PanoPainter/export.png"); + services.flush_persistent_storage(); + services.save_prepared_file( + "/PanoPainter/export.png", + "export.png", + [&](std::string path, bool success) { + saved_path = std::move(path); + saved = success; + }); + + PP_EXPECT(harness, &pp::platform::resolve_web_platform_services(fallback) == &injected); + PP_EXPECT(harness, services.default_canvas_resolution() == 2048); + PP_EXPECT(harness, injected.exported_image_publishes == 1); + PP_EXPECT(harness, injected.exported_image_path == "/PanoPainter/export.png"); + PP_EXPECT(harness, injected.persistent_storage_flushes == 1); + PP_EXPECT(harness, injected.default_canvas_resolution_requests == 1); + PP_EXPECT(harness, injected.save_prepared_file_requests == 1); + PP_EXPECT(harness, injected.prepared_file_path == "/PanoPainter/export.png"); + PP_EXPECT(harness, injected.prepared_file_name == "export.png"); + PP_EXPECT(harness, saved_path == "/PanoPainter/export.png"); + PP_EXPECT(harness, saved); + } + + PP_EXPECT(harness, pp::platform::injected_web_platform_services() == nullptr); +} + void platform_services_dispatch_writable_file_target(pp::tests::Harness& harness) { FakePlatformServices fake("unused"); @@ -1086,6 +1209,8 @@ int main() "platform services dispatch working directory picker policy", platform_services_dispatch_working_directory_picker_policy); harness.run("platform services dispatch prepared file save", platform_services_dispatch_prepared_file_save); + harness.run("web platform services preserve default web policy", web_platform_services_preserve_default_web_policy); + harness.run("web platform services resolve injected services", web_platform_services_resolve_injected_services); harness.run("platform services dispatch writable file target", platform_services_dispatch_writable_file_target); harness.run( "platform services dispatch document export collection policy",