applets/web: Initial implementation of the web browser applet
This commit is contained in:
parent
ccb439efb0
commit
a5750f437d
|
@ -2,8 +2,11 @@
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
|
#include "common/string_util.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/frontend/applets/web_browser.h"
|
#include "core/frontend/applets/web_browser.h"
|
||||||
#include "core/hle/result.h"
|
#include "core/hle/result.h"
|
||||||
|
@ -12,6 +15,76 @@
|
||||||
|
|
||||||
namespace Service::AM::Applets {
|
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_)
|
WebBrowser::WebBrowser(Core::System& system_, const Core::Frontend::WebBrowserApplet& frontend_)
|
||||||
: Applet{system_.Kernel()}, frontend(frontend_), system{system_} {}
|
: Applet{system_.Kernel()}, frontend(frontend_), system{system_} {}
|
||||||
|
|
||||||
|
@ -19,6 +92,55 @@ WebBrowser::~WebBrowser() = default;
|
||||||
|
|
||||||
void WebBrowser::Initialize() {
|
void WebBrowser::Initialize() {
|
||||||
Applet::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 {
|
bool WebBrowser::TransactionComplete() const {
|
||||||
|
@ -30,9 +152,110 @@ ResultCode WebBrowser::GetStatus() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebBrowser::ExecuteInteractive() {
|
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
|
} // namespace Service::AM::Applets
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/hle/result.h"
|
#include "core/hle/result.h"
|
||||||
#include "core/hle/service/am/applets/applets.h"
|
#include "core/hle/service/am/applets/applets.h"
|
||||||
|
#include "core/hle/service/am/applets/web_types.h"
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
class System;
|
class System;
|
||||||
|
@ -28,12 +29,36 @@ public:
|
||||||
void ExecuteInteractive() override;
|
void ExecuteInteractive() override;
|
||||||
void Execute() override;
|
void Execute() override;
|
||||||
|
|
||||||
|
void WebBrowserExit(WebExitReason exit_reason, std::string last_url = "");
|
||||||
|
|
||||||
private:
|
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;
|
const Core::Frontend::WebBrowserApplet& frontend;
|
||||||
|
|
||||||
bool complete{false};
|
bool complete{false};
|
||||||
ResultCode status{RESULT_SUCCESS};
|
ResultCode status{RESULT_SUCCESS};
|
||||||
|
|
||||||
|
WebAppletVersion web_applet_version;
|
||||||
|
WebArgHeader web_arg_header;
|
||||||
|
WebArgInputTLVMap web_arg_input_tlv_map;
|
||||||
|
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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