applets/web: Initial implementation of the web browser applet
This commit is contained in:
		| @@ -2,8 +2,11 @@ | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #include <optional> | ||||
|  | ||||
| #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 <typename T> | ||||
| void ParseRawValue(T& value, const std::vector<u8>& data) { | ||||
|     static_assert(std::is_trivially_copyable_v<T>, | ||||
|                   "It's undefined behavior to use memcpy with non-trivially copyable objects"); | ||||
|     std::memcpy(&value, data.data(), data.size()); | ||||
| } | ||||
|  | ||||
| template <typename T> | ||||
| T ParseRawValue(const std::vector<u8>& data) { | ||||
|     T value; | ||||
|     ParseRawValue(value, data); | ||||
|     return value; | ||||
| } | ||||
|  | ||||
| std::string ParseStringValue(const std::vector<u8>& data) { | ||||
|     return Common::StringFromFixedZeroTerminatedBuffer(reinterpret_cast<const char*>(data.data()), | ||||
|                                                        data.size()); | ||||
| } | ||||
|  | ||||
| WebArgInputTLVMap ReadWebArgs(const std::vector<u8>& 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<u8> 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<std::vector<u8>> 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<u8> out_data(sizeof(WebCommonReturnValue)); | ||||
|     std::memcpy(out_data.data(), &web_common_return_value, out_data.size()); | ||||
|     broker.PushNormalDataFromApplet(std::make_shared<IStorage>(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 | ||||
|   | ||||
| @@ -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; | ||||
| }; | ||||
|  | ||||
|   | ||||
							
								
								
									
										178
									
								
								src/core/hle/service/am/applets/web_types.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										178
									
								
								src/core/hle/service/am/applets/web_types.h
									
									
									
									
									
										Normal file
									
								
							| @@ -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 <array> | ||||
| #include <unordered_map> | ||||
| #include <vector> | ||||
|  | ||||
| #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<char, 0x1000> last_url{}; | ||||
|     u64 last_url_size{}; | ||||
| }; | ||||
| static_assert(sizeof(WebCommonReturnValue) == 0x1010, "WebCommonReturnValue has incorrect size."); | ||||
|  | ||||
| using WebArgInputTLVMap = std::unordered_map<WebArgInputTLVType, std::vector<u8>>; | ||||
|  | ||||
| } // namespace Service::AM::Applets | ||||
		Reference in New Issue
	
	Block a user