From a5750f437d58e63ed76235d171732ca25dd34be2 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Sun, 8 Nov 2020 23:27:33 -0500 Subject: [PATCH] applets/web: Initial implementation of the web browser applet --- .../hle/service/am/applets/web_browser.cpp | 227 +++++++++++++++++- src/core/hle/service/am/applets/web_browser.h | 25 ++ src/core/hle/service/am/applets/web_types.h | 178 ++++++++++++++ 3 files changed, 428 insertions(+), 2 deletions(-) create mode 100644 src/core/hle/service/am/applets/web_types.h diff --git a/src/core/hle/service/am/applets/web_browser.cpp b/src/core/hle/service/am/applets/web_browser.cpp index 74ef037fe..7f398254e 100644 --- a/src/core/hle/service/am/applets/web_browser.cpp +++ b/src/core/hle/service/am/applets/web_browser.cpp @@ -2,8 +2,11 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include + #include "common/assert.h" #include "common/logging/log.h" +#include "common/string_util.h" #include "core/core.h" #include "core/frontend/applets/web_browser.h" #include "core/hle/result.h" @@ -12,6 +15,76 @@ namespace Service::AM::Applets { +namespace { + +template +void ParseRawValue(T& value, const std::vector& data) { + static_assert(std::is_trivially_copyable_v, + "It's undefined behavior to use memcpy with non-trivially copyable objects"); + std::memcpy(&value, data.data(), data.size()); +} + +template +T ParseRawValue(const std::vector& data) { + T value; + ParseRawValue(value, data); + return value; +} + +std::string ParseStringValue(const std::vector& data) { + return Common::StringFromFixedZeroTerminatedBuffer(reinterpret_cast(data.data()), + data.size()); +} + +WebArgInputTLVMap ReadWebArgs(const std::vector& web_arg, WebArgHeader& web_arg_header) { + std::memcpy(&web_arg_header, web_arg.data(), sizeof(WebArgHeader)); + + if (web_arg.size() == sizeof(WebArgHeader)) { + return {}; + } + + WebArgInputTLVMap input_tlv_map; + + u64 current_offset = sizeof(WebArgHeader); + + for (std::size_t i = 0; i < web_arg_header.total_tlv_entries; ++i) { + if (web_arg.size() < current_offset + sizeof(WebArgInputTLV)) { + return input_tlv_map; + } + + WebArgInputTLV input_tlv; + std::memcpy(&input_tlv, web_arg.data() + current_offset, sizeof(WebArgInputTLV)); + + current_offset += sizeof(WebArgInputTLV); + + if (web_arg.size() < current_offset + input_tlv.arg_data_size) { + return input_tlv_map; + } + + std::vector data(input_tlv.arg_data_size); + std::memcpy(data.data(), web_arg.data() + current_offset, input_tlv.arg_data_size); + + current_offset += input_tlv.arg_data_size; + + input_tlv_map.insert_or_assign(input_tlv.input_tlv_type, std::move(data)); + } + + return input_tlv_map; +} + +std::optional> GetInputTLVData(const WebArgInputTLVMap& input_tlv_map, + WebArgInputTLVType input_tlv_type) { + const auto map_it = input_tlv_map.find(input_tlv_type); + + if (map_it == input_tlv_map.end()) { + return std::nullopt; + } + + return map_it->second; +} + +} // namespace + WebBrowser::WebBrowser(Core::System& system_, const Core::Frontend::WebBrowserApplet& frontend_) : Applet{system_.Kernel()}, frontend(frontend_), system{system_} {} @@ -19,6 +92,55 @@ WebBrowser::~WebBrowser() = default; void WebBrowser::Initialize() { Applet::Initialize(); + + LOG_INFO(Service_AM, "Initializing Web Browser Applet."); + + LOG_DEBUG(Service_AM, + "Initializing Applet with common_args: arg_version={}, lib_version={}, " + "play_startup_sound={}, size={}, system_tick={}, theme_color={}", + common_args.arguments_version, common_args.library_version, + common_args.play_startup_sound, common_args.size, common_args.system_tick, + common_args.theme_color); + + web_applet_version = WebAppletVersion{common_args.library_version}; + + const auto web_arg_storage = broker.PopNormalDataToApplet(); + ASSERT(web_arg_storage != nullptr); + + const auto& web_arg = web_arg_storage->GetData(); + ASSERT_OR_EXECUTE(web_arg.size() >= sizeof(WebArgHeader), { return; }); + + web_arg_input_tlv_map = ReadWebArgs(web_arg, web_arg_header); + + LOG_DEBUG(Service_AM, "WebArgHeader: total_tlv_entries={}, shim_kind={}", + web_arg_header.total_tlv_entries, web_arg_header.shim_kind); + + switch (web_arg_header.shim_kind) { + case ShimKind::Shop: + InitializeShop(); + break; + case ShimKind::Login: + InitializeLogin(); + break; + case ShimKind::Offline: + InitializeOffline(); + break; + case ShimKind::Share: + InitializeShare(); + break; + case ShimKind::Web: + InitializeWeb(); + break; + case ShimKind::Wifi: + InitializeWifi(); + break; + case ShimKind::Lobby: + InitializeLobby(); + break; + default: + UNREACHABLE_MSG("Invalid ShimKind={}", web_arg_header.shim_kind); + break; + } } bool WebBrowser::TransactionComplete() const { @@ -30,9 +152,110 @@ ResultCode WebBrowser::GetStatus() const { } void WebBrowser::ExecuteInteractive() { - UNIMPLEMENTED_MSG("Unexpected interactive data recieved!"); + UNIMPLEMENTED_MSG("WebSession is not implemented"); } -void WebBrowser::Execute() {} +void WebBrowser::Execute() { + switch (web_arg_header.shim_kind) { + case ShimKind::Shop: + ExecuteShop(); + break; + case ShimKind::Login: + ExecuteLogin(); + break; + case ShimKind::Offline: + ExecuteOffline(); + break; + case ShimKind::Share: + ExecuteShare(); + break; + case ShimKind::Web: + ExecuteWeb(); + break; + case ShimKind::Wifi: + ExecuteWifi(); + break; + case ShimKind::Lobby: + ExecuteLobby(); + break; + default: + UNREACHABLE_MSG("Invalid ShimKind={}", web_arg_header.shim_kind); + WebBrowserExit(WebExitReason::EndButtonPressed); + break; + } +} + +void WebBrowser::WebBrowserExit(WebExitReason exit_reason, std::string last_url) { + if ((web_arg_header.shim_kind == ShimKind::Share && + web_applet_version >= WebAppletVersion::Version196608) || + (web_arg_header.shim_kind == ShimKind::Web && + web_applet_version >= WebAppletVersion::Version524288)) { + // TODO: Push Output TLVs instead of a WebCommonReturnValue + } + + WebCommonReturnValue web_common_return_value; + + web_common_return_value.exit_reason = exit_reason; + std::memcpy(&web_common_return_value.last_url, last_url.data(), last_url.size()); + web_common_return_value.last_url_size = last_url.size(); + + LOG_DEBUG(Service_AM, "WebCommonReturnValue: exit_reason={}, last_url={}, last_url_size={}", + exit_reason, last_url, last_url.size()); + + complete = true; + std::vector out_data(sizeof(WebCommonReturnValue)); + std::memcpy(out_data.data(), &web_common_return_value, out_data.size()); + broker.PushNormalDataFromApplet(std::make_shared(system, std::move(out_data))); + broker.SignalStateChanged(); +} + +void WebBrowser::InitializeShop() {} + +void WebBrowser::InitializeLogin() {} + +void WebBrowser::InitializeOffline() {} + +void WebBrowser::InitializeShare() {} + +void WebBrowser::InitializeWeb() {} + +void WebBrowser::InitializeWifi() {} + +void WebBrowser::InitializeLobby() {} + +void WebBrowser::ExecuteShop() { + LOG_WARNING(Service_AM, "(STUBBED) called, Shop Applet is not implemented"); + WebBrowserExit(WebExitReason::EndButtonPressed); +} + +void WebBrowser::ExecuteLogin() { + LOG_WARNING(Service_AM, "(STUBBED) called, Login Applet is not implemented"); + WebBrowserExit(WebExitReason::EndButtonPressed); +} + +void WebBrowser::ExecuteOffline() { + LOG_WARNING(Service_AM, "(STUBBED) called, Offline Applet is not implemented"); + WebBrowserExit(WebExitReason::EndButtonPressed); +} + +void WebBrowser::ExecuteShare() { + LOG_WARNING(Service_AM, "(STUBBED) called, Share Applet is not implemented"); + WebBrowserExit(WebExitReason::EndButtonPressed); +} + +void WebBrowser::ExecuteWeb() { + LOG_WARNING(Service_AM, "(STUBBED) called, Web Applet is not implemented"); + WebBrowserExit(WebExitReason::EndButtonPressed); +} + +void WebBrowser::ExecuteWifi() { + LOG_WARNING(Service_AM, "(STUBBED) called, Wifi Applet is not implemented"); + WebBrowserExit(WebExitReason::EndButtonPressed); +} + +void WebBrowser::ExecuteLobby() { + LOG_WARNING(Service_AM, "(STUBBED) called, Lobby Applet is not implemented"); + WebBrowserExit(WebExitReason::EndButtonPressed); +} } // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/web_browser.h b/src/core/hle/service/am/applets/web_browser.h index 0584142e5..a1ed5fd1d 100644 --- a/src/core/hle/service/am/applets/web_browser.h +++ b/src/core/hle/service/am/applets/web_browser.h @@ -8,6 +8,7 @@ #include "common/common_types.h" #include "core/hle/result.h" #include "core/hle/service/am/applets/applets.h" +#include "core/hle/service/am/applets/web_types.h" namespace Core { class System; @@ -28,12 +29,36 @@ public: void ExecuteInteractive() override; void Execute() override; + void WebBrowserExit(WebExitReason exit_reason, std::string last_url = ""); + private: + // Initializers for the various types of browser applets + void InitializeShop(); + void InitializeLogin(); + void InitializeOffline(); + void InitializeShare(); + void InitializeWeb(); + void InitializeWifi(); + void InitializeLobby(); + + // Executors for the various types of browser applets + void ExecuteShop(); + void ExecuteLogin(); + void ExecuteOffline(); + void ExecuteShare(); + void ExecuteWeb(); + void ExecuteWifi(); + void ExecuteLobby(); + const Core::Frontend::WebBrowserApplet& frontend; bool complete{false}; ResultCode status{RESULT_SUCCESS}; + WebAppletVersion web_applet_version; + WebArgHeader web_arg_header; + WebArgInputTLVMap web_arg_input_tlv_map; + Core::System& system; }; diff --git a/src/core/hle/service/am/applets/web_types.h b/src/core/hle/service/am/applets/web_types.h new file mode 100644 index 000000000..419c2bf79 --- /dev/null +++ b/src/core/hle/service/am/applets/web_types.h @@ -0,0 +1,178 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include + +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "common/swap.h" + +namespace Service::AM::Applets { + +enum class WebAppletVersion : u32_le { + Version0 = 0x0, // Only used by WifiWebAuthApplet + Version131072 = 0x20000, // 1.0.0 - 2.3.0 + Version196608 = 0x30000, // 3.0.0 - 4.1.0 + Version327680 = 0x50000, // 5.0.0 - 5.1.0 + Version393216 = 0x60000, // 6.0.0 - 7.0.1 + Version524288 = 0x80000, // 8.0.0+ +}; + +enum class ShimKind : u32 { + Shop = 1, + Login = 2, + Offline = 3, + Share = 4, + Web = 5, + Wifi = 6, + Lobby = 7, +}; + +enum class WebExitReason : u32 { + EndButtonPressed = 0, + BackButtonPressed = 1, + ExitRequested = 2, + CallbackURL = 3, + WindowClosed = 4, + ErrorDialog = 7, +}; + +enum class WebArgInputTLVType : u16 { + InitialURL = 0x1, + CallbackURL = 0x3, + CallbackableURL = 0x4, + ApplicationID = 0x5, + DocumentPath = 0x6, + DocumentKind = 0x7, + SystemDataID = 0x8, + ShareStartPage = 0x9, + Whitelist = 0xA, + News = 0xB, + UserID = 0xE, + AlbumEntry0 = 0xF, + ScreenShotEnabled = 0x10, + EcClientCertEnabled = 0x11, + PlayReportEnabled = 0x13, + BootDisplayKind = 0x17, + BackgroundKind = 0x18, + FooterEnabled = 0x19, + PointerEnabled = 0x1A, + LeftStickMode = 0x1B, + KeyRepeatFrame1 = 0x1C, + KeyRepeatFrame2 = 0x1D, + BootAsMediaPlayerInverted = 0x1E, + DisplayURLKind = 0x1F, + BootAsMediaPlayer = 0x21, + ShopJumpEnabled = 0x22, + MediaAutoPlayEnabled = 0x23, + LobbyParameter = 0x24, + ApplicationAlbumEntry = 0x26, + JsExtensionEnabled = 0x27, + AdditionalCommentText = 0x28, + TouchEnabledOnContents = 0x29, + UserAgentAdditionalString = 0x2A, + AdditionalMediaData0 = 0x2B, + MediaPlayerAutoCloseEnabled = 0x2C, + PageCacheEnabled = 0x2D, + WebAudioEnabled = 0x2E, + YouTubeVideoWhitelist = 0x31, + FooterFixedKind = 0x32, + PageFadeEnabled = 0x33, + MediaCreatorApplicationRatingAge = 0x34, + BootLoadingIconEnabled = 0x35, + PageScrollIndicatorEnabled = 0x36, + MediaPlayerSpeedControlEnabled = 0x37, + AlbumEntry1 = 0x38, + AlbumEntry2 = 0x39, + AlbumEntry3 = 0x3A, + AdditionalMediaData1 = 0x3B, + AdditionalMediaData2 = 0x3C, + AdditionalMediaData3 = 0x3D, + BootFooterButton = 0x3E, + OverrideWebAudioVolume = 0x3F, + OverrideMediaAudioVolume = 0x40, + BootMode = 0x41, + WebSessionEnabled = 0x42, + MediaPlayerOfflineEnabled = 0x43, +}; + +enum class WebArgOutputTLVType : u16 { + ShareExitReason = 0x1, + LastURL = 0x2, + LastURLSize = 0x3, + SharePostResult = 0x4, + PostServiceName = 0x5, + PostServiceNameSize = 0x6, + PostID = 0x7, + PostIDSize = 0x8, + MediaPlayerAutoClosedByCompletion = 0x9, +}; + +enum class DocumentKind : u32 { + OfflineHtmlPage = 1, + ApplicationLegalInformation = 2, + SystemDataPage = 3, +}; + +enum class ShareStartPage : u32 { + Default, + Settings, +}; + +enum class BootDisplayKind : u32 { + Default, + White, + Black, +}; + +enum class BackgroundKind : u32 { + Default, +}; + +enum class LeftStickMode : u32 { + Pointer, + Cursor, +}; + +enum class WebSessionBootMode : u32 { + AllForeground, + AllForegroundInitiallyHidden, +}; + +struct WebArgHeader { + u16 total_tlv_entries{}; + INSERT_PADDING_BYTES(2); + ShimKind shim_kind{}; +}; +static_assert(sizeof(WebArgHeader) == 0x8, "WebArgHeader has incorrect size."); + +struct WebArgInputTLV { + WebArgInputTLVType input_tlv_type{}; + u16 arg_data_size{}; + INSERT_PADDING_WORDS(1); +}; +static_assert(sizeof(WebArgInputTLV) == 0x8, "WebArgInputTLV has incorrect size."); + +struct WebArgOutputTLV { + WebArgOutputTLVType output_tlv_type{}; + u16 arg_data_size{}; + INSERT_PADDING_WORDS(1); +}; +static_assert(sizeof(WebArgOutputTLV) == 0x8, "WebArgOutputTLV has incorrect size."); + +struct WebCommonReturnValue { + WebExitReason exit_reason{}; + INSERT_PADDING_WORDS(1); + std::array last_url{}; + u64 last_url_size{}; +}; +static_assert(sizeof(WebCommonReturnValue) == 0x1010, "WebCommonReturnValue has incorrect size."); + +using WebArgInputTLVMap = std::unordered_map>; + +} // namespace Service::AM::Applets