From d58442b5705b862ef7341990df8afb423d9f297e Mon Sep 17 00:00:00 2001 From: KernelPanicNT <76663133+KernelPanicNT@users.noreply.github.com> Date: Wed, 14 Oct 2026 14:08:05 +0000 Subject: [PATCH] feat(libSceWebBrowserDialog): open predetermined content like the browser dialog sceWebBrowserDialogOpenForPredeterminedContent takes the (param, predeterminedContentParam) signature and the 424-byte parameter of SharpProspero. It answers as sceWebBrowserDialogOpen: NOT_INITIALIZED before initialization, BUSY while running, ARG_NULL for a null dialog parameter, and otherwise finishes at once with the user-cancelled result, since there is no browser to show. A null predetermined content parameter or a size other than 424 throws, as its error code is unknown. Co-Authored-By: Claude Opus 5.5 --- .../prx/libSceWebBrowserDialog/CMakeLists.txt | 3 +- .../prx/libSceWebBrowserDialog/Export.cpp | 18 +++++++- core/libs/tests/GuestWebBrowserDialog.cpp | 42 +++++++++++++++++++ docs/dev/debt/libSceWebBrowserDialog.md | 5 +++ 4 files changed, 65 insertions(+), 3 deletions(-) create mode 100644 docs/dev/debt/libSceWebBrowserDialog.md diff --git a/core/libs/prx/libSceWebBrowserDialog/CMakeLists.txt b/core/libs/prx/libSceWebBrowserDialog/CMakeLists.txt index 2c498e5df14..16fa734c9e4 100644 --- a/core/libs/prx/libSceWebBrowserDialog/CMakeLists.txt +++ b/core/libs/prx/libSceWebBrowserDialog/CMakeLists.txt @@ -5,7 +5,8 @@ configure_windows_unwind(libSceWebBrowserDialog) if(BUILD_TESTING) add_test_executable(guest_web_browser_dialog_tests ${LIBS_INCLUDE_DIR}/tests/GuestWebBrowserDialog.cpp) target_include_directories(guest_web_browser_dialog_tests PRIVATE ${LIBS_INCLUDE_DIR}) - target_link_libraries(guest_web_browser_dialog_tests PRIVATE libSceWebBrowserDialog) + target_link_libraries(guest_web_browser_dialog_tests PRIVATE libSceWebBrowserDialog libc) + configure_windows_unwind(guest_web_browser_dialog_tests) add_test(NAME guest_web_browser_dialog COMMAND guest_web_browser_dialog_tests) if(WIN32) set_tests_properties(guest_web_browser_dialog PROPERTIES ENVIRONMENT_MODIFICATION diff --git a/core/libs/prx/libSceWebBrowserDialog/Export.cpp b/core/libs/prx/libSceWebBrowserDialog/Export.cpp index 45939e99e83..e892ed6f72a 100644 --- a/core/libs/prx/libSceWebBrowserDialog/Export.cpp +++ b/core/libs/prx/libSceWebBrowserDialog/Export.cpp @@ -16,6 +16,13 @@ constexpr int COMMON_DIALOG_ERROR_NOT_INITIALIZED = static_cast(0x80B80003u constexpr int COMMON_DIALOG_ERROR_NOT_FINISHED = static_cast(0x80B80005u); constexpr int COMMON_DIALOG_ERROR_BUSY = static_cast(0x80B80007u); constexpr int COMMON_DIALOG_ERROR_ARG_NULL = static_cast(0x80B8000Du); + +struct PredeterminedContentParam { + std::uint64_t size; + const char* domain[20]; + std::uint8_t reserved[256]; +}; +static_assert(sizeof(PredeterminedContentParam) == 424); } extern "C" { @@ -71,8 +78,15 @@ int APS5_VABI sceWebBrowserDialogSetCookie(void) { return 0; } -int APS5_VABI sceWebBrowserDialogOpenForPredeterminedContent() { - NotImplemented_nid_no_patch(__func__); +int APS5_VABI sceWebBrowserDialogOpenForPredeterminedContent(const void* param, const void* contentParam) { + const int status = g_status.load(); + if (status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; + if (status == COMMON_DIALOG_STATUS_RUNNING) return COMMON_DIALOG_ERROR_BUSY; + if (param == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL; + if (contentParam == nullptr) throw std::invalid_argument("sceWebBrowserDialogOpenForPredeterminedContent: null predetermined content parameter"); + if (static_cast(contentParam)->size != sizeof(PredeterminedContentParam)) + throw std::invalid_argument("sceWebBrowserDialogOpenForPredeterminedContent: unexpected predetermined content parameter size"); + g_status = COMMON_DIALOG_STATUS_FINISHED; return 0; } diff --git a/core/libs/tests/GuestWebBrowserDialog.cpp b/core/libs/tests/GuestWebBrowserDialog.cpp index 9c5ef4b12b6..04b390c5173 100644 --- a/core/libs/tests/GuestWebBrowserDialog.cpp +++ b/core/libs/tests/GuestWebBrowserDialog.cpp @@ -1,6 +1,7 @@ #include "prx/libc/include/general/VabiMacros.hpp" #include #include +#include extern "C" { int APS5_VABI sceWebBrowserDialogInitialize(void); @@ -10,6 +11,7 @@ int APS5_VABI sceWebBrowserDialogClose(void); int APS5_VABI sceWebBrowserDialogGetStatus(void); int APS5_VABI sceWebBrowserDialogUpdateStatus(void); int APS5_VABI sceWebBrowserDialogGetResult(void* result); +int APS5_VABI sceWebBrowserDialogOpenForPredeterminedContent(const void* param, const void* contentParam); } namespace { @@ -24,6 +26,45 @@ constexpr int COMMON_DIALOG_ERROR_ARG_NULL = static_cast(0x80B8000Du); void Require(bool value) { if (!value) std::abort(); } +struct PredeterminedContentParam { + std::uint64_t size; + const char* domain[20]; + std::uint8_t reserved[256]; +}; +static_assert(sizeof(PredeterminedContentParam) == 424); + +template +bool ThrowsInvalidArgument(TAction action) { + try { + action(); + } catch (const std::invalid_argument&) { + return true; + } + return false; +} + +void CheckPredeterminedContent(std::uint8_t* param) { + PredeterminedContentParam content{}; + content.size = sizeof(content); + content.domain[0] = "https://example.com"; + std::int32_t result = 0x5a5a5a5a; + + Require(sceWebBrowserDialogOpenForPredeterminedContent(param, &content) == COMMON_DIALOG_ERROR_NOT_INITIALIZED); + Require(sceWebBrowserDialogInitialize() == 0); + Require(sceWebBrowserDialogOpenForPredeterminedContent(nullptr, &content) == COMMON_DIALOG_ERROR_ARG_NULL); + Require(ThrowsInvalidArgument([&] { sceWebBrowserDialogOpenForPredeterminedContent(param, nullptr); })); + content.size = sizeof(content) - 8; + Require(ThrowsInvalidArgument([&] { sceWebBrowserDialogOpenForPredeterminedContent(param, &content); })); + Require(sceWebBrowserDialogGetStatus() == COMMON_DIALOG_STATUS_INITIALIZED); + content.size = sizeof(content); + Require(sceWebBrowserDialogOpenForPredeterminedContent(param, &content) == 0); + Require(sceWebBrowserDialogGetStatus() == COMMON_DIALOG_STATUS_FINISHED); + Require(sceWebBrowserDialogGetResult(&result) == 0 && result == COMMON_DIALOG_RESULT_USER_CANCELED); + Require(sceWebBrowserDialogOpenForPredeterminedContent(param, &content) == 0); + Require(sceWebBrowserDialogTerminate() == 0); + Require(sceWebBrowserDialogGetStatus() == COMMON_DIALOG_STATUS_NONE); +} + } int main() { @@ -59,5 +100,6 @@ int main() { Require(sceWebBrowserDialogTerminate() == 0); Require(sceWebBrowserDialogGetStatus() == COMMON_DIALOG_STATUS_NONE); + CheckPredeterminedContent(param); return 0; } diff --git a/docs/dev/debt/libSceWebBrowserDialog.md b/docs/dev/debt/libSceWebBrowserDialog.md new file mode 100644 index 00000000000..a3b62860071 --- /dev/null +++ b/docs/dev/debt/libSceWebBrowserDialog.md @@ -0,0 +1,5 @@ +# libSceWebBrowserDialog technical debt + +### Unknown function info + +- [sceWebBrowserDialogOpenForPredeterminedContent](../../../core/libs/prx/libSceWebBrowserDialog/Export.cpp) (libSceWebBrowserDialog) - the `(param, predeterminedContentParam)` signature and the 424-byte predetermined content parameter (a 64-bit size, 20 allowed domain pointers and 256 reserved bytes) follow SharpProspero's [parameter layout](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Dialog/WebBrowserDialog.cs#L138-L155) and [import](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Dialog/WebBrowserDialog.cs#L246-L254); the PS5 behaviour is unverified. It answers as `sceWebBrowserDialogOpen`: `NOT_INITIALIZED` before `sceWebBrowserDialogInitialize`, `BUSY` while running and `ARG_NULL` for a null dialog parameter, and otherwise finishes at once with the user-cancelled result, since there is no browser to show. A null predetermined content parameter or a size other than 424 throws, as its error code is unknown, and the allowed domains are not read