summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Morph2020-11-08 07:17:12 -0500
committerGravatar Morph2020-12-18 10:33:27 -0500
commitccb439efb088c990b41a6ceb5b1b330c8c27a1aa (patch)
tree81361beb06ba045f4ea875763cacf89f8170d390 /src
parentMerge pull request #5205 from Morph1984/oss-extended-plus-minus (diff)
downloadyuzu-ccb439efb088c990b41a6ceb5b1b330c8c27a1aa.tar.gz
yuzu-ccb439efb088c990b41a6ceb5b1b330c8c27a1aa.tar.xz
yuzu-ccb439efb088c990b41a6ceb5b1b330c8c27a1aa.zip
applets: Remove the previous web browser applet implementation
Diffstat (limited to 'src')
-rw-r--r--src/core/frontend/applets/general_frontend.cpp68
-rw-r--r--src/core/frontend/applets/general_frontend.h51
-rw-r--r--src/core/frontend/applets/web_browser.cpp10
-rw-r--r--src/core/frontend/applets/web_browser.h7
-rw-r--r--src/core/hle/service/am/applets/applets.cpp35
-rw-r--r--src/core/hle/service/am/applets/applets.h20
-rw-r--r--src/core/hle/service/am/applets/web_browser.cpp532
-rw-r--r--src/core/hle/service/am/applets/web_browser.h59
-rw-r--r--src/yuzu/applets/web_browser.cpp108
-rw-r--r--src/yuzu/applets/web_browser.h31
-rw-r--r--src/yuzu/main.cpp154
-rw-r--r--src/yuzu/main.h4
12 files changed, 40 insertions, 1039 deletions
diff --git a/src/core/frontend/applets/general_frontend.cpp b/src/core/frontend/applets/general_frontend.cpp
index c30b36de7..7483ffb76 100644
--- a/src/core/frontend/applets/general_frontend.cpp
+++ b/src/core/frontend/applets/general_frontend.cpp
@@ -53,72 +53,4 @@ void DefaultPhotoViewerApplet::ShowAllPhotos(std::function<void()> finished) con
53 finished(); 53 finished();
54} 54}
55 55
56ECommerceApplet::~ECommerceApplet() = default;
57
58DefaultECommerceApplet::~DefaultECommerceApplet() = default;
59
60void DefaultECommerceApplet::ShowApplicationInformation(
61 std::function<void()> finished, u64 title_id, std::optional<u128> user_id,
62 std::optional<bool> full_display, std::optional<std::string> extra_parameter) {
63 const auto value = user_id.value_or(u128{});
64 LOG_INFO(Service_AM,
65 "Application requested frontend show application information for EShop, "
66 "title_id={:016X}, user_id={:016X}{:016X}, full_display={}, extra_parameter={}",
67 title_id, value[1], value[0],
68 full_display.has_value() ? fmt::format("{}", *full_display) : "null",
69 extra_parameter.value_or("null"));
70 finished();
71}
72
73void DefaultECommerceApplet::ShowAddOnContentList(std::function<void()> finished, u64 title_id,
74 std::optional<u128> user_id,
75 std::optional<bool> full_display) {
76 const auto value = user_id.value_or(u128{});
77 LOG_INFO(Service_AM,
78 "Application requested frontend show add on content list for EShop, "
79 "title_id={:016X}, user_id={:016X}{:016X}, full_display={}",
80 title_id, value[1], value[0],
81 full_display.has_value() ? fmt::format("{}", *full_display) : "null");
82 finished();
83}
84
85void DefaultECommerceApplet::ShowSubscriptionList(std::function<void()> finished, u64 title_id,
86 std::optional<u128> user_id) {
87 const auto value = user_id.value_or(u128{});
88 LOG_INFO(Service_AM,
89 "Application requested frontend show subscription list for EShop, title_id={:016X}, "
90 "user_id={:016X}{:016X}",
91 title_id, value[1], value[0]);
92 finished();
93}
94
95void DefaultECommerceApplet::ShowConsumableItemList(std::function<void()> finished, u64 title_id,
96 std::optional<u128> user_id) {
97 const auto value = user_id.value_or(u128{});
98 LOG_INFO(
99 Service_AM,
100 "Application requested frontend show consumable item list for EShop, title_id={:016X}, "
101 "user_id={:016X}{:016X}",
102 title_id, value[1], value[0]);
103 finished();
104}
105
106void DefaultECommerceApplet::ShowShopHome(std::function<void()> finished, u128 user_id,
107 bool full_display) {
108 LOG_INFO(Service_AM,
109 "Application requested frontend show home menu for EShop, user_id={:016X}{:016X}, "
110 "full_display={}",
111 user_id[1], user_id[0], full_display);
112 finished();
113}
114
115void DefaultECommerceApplet::ShowSettings(std::function<void()> finished, u128 user_id,
116 bool full_display) {
117 LOG_INFO(Service_AM,
118 "Application requested frontend show settings menu for EShop, user_id={:016X}{:016X}, "
119 "full_display={}",
120 user_id[1], user_id[0], full_display);
121 finished();
122}
123
124} // namespace Core::Frontend 56} // namespace Core::Frontend
diff --git a/src/core/frontend/applets/general_frontend.h b/src/core/frontend/applets/general_frontend.h
index 4b63f828e..b713b14ee 100644
--- a/src/core/frontend/applets/general_frontend.h
+++ b/src/core/frontend/applets/general_frontend.h
@@ -58,55 +58,4 @@ public:
58 void ShowAllPhotos(std::function<void()> finished) const override; 58 void ShowAllPhotos(std::function<void()> finished) const override;
59}; 59};
60 60
61class ECommerceApplet {
62public:
63 virtual ~ECommerceApplet();
64
65 // Shows a page with application icons, description, name, and price.
66 virtual void ShowApplicationInformation(std::function<void()> finished, u64 title_id,
67 std::optional<u128> user_id = {},
68 std::optional<bool> full_display = {},
69 std::optional<std::string> extra_parameter = {}) = 0;
70
71 // Shows a page with all of the add on content available for a game, with name, description, and
72 // price.
73 virtual void ShowAddOnContentList(std::function<void()> finished, u64 title_id,
74 std::optional<u128> user_id = {},
75 std::optional<bool> full_display = {}) = 0;
76
77 // Shows a page with all of the subscriptions (recurring payments) for a game, with name,
78 // description, price, and renewal period.
79 virtual void ShowSubscriptionList(std::function<void()> finished, u64 title_id,
80 std::optional<u128> user_id = {}) = 0;
81
82 // Shows a page with a list of any additional game related purchasable items (DLC,
83 // subscriptions, etc) for a particular game, with name, description, type, and price.
84 virtual void ShowConsumableItemList(std::function<void()> finished, u64 title_id,
85 std::optional<u128> user_id = {}) = 0;
86
87 // Shows the home page of the shop.
88 virtual void ShowShopHome(std::function<void()> finished, u128 user_id, bool full_display) = 0;
89
90 // Shows the user settings page of the shop.
91 virtual void ShowSettings(std::function<void()> finished, u128 user_id, bool full_display) = 0;
92};
93
94class DefaultECommerceApplet : public ECommerceApplet {
95public:
96 ~DefaultECommerceApplet() override;
97
98 void ShowApplicationInformation(std::function<void()> finished, u64 title_id,
99 std::optional<u128> user_id, std::optional<bool> full_display,
100 std::optional<std::string> extra_parameter) override;
101 void ShowAddOnContentList(std::function<void()> finished, u64 title_id,
102 std::optional<u128> user_id,
103 std::optional<bool> full_display) override;
104 void ShowSubscriptionList(std::function<void()> finished, u64 title_id,
105 std::optional<u128> user_id) override;
106 void ShowConsumableItemList(std::function<void()> finished, u64 title_id,
107 std::optional<u128> user_id) override;
108 void ShowShopHome(std::function<void()> finished, u128 user_id, bool full_display) override;
109 void ShowSettings(std::function<void()> finished, u128 user_id, bool full_display) override;
110};
111
112} // namespace Core::Frontend 61} // namespace Core::Frontend
diff --git a/src/core/frontend/applets/web_browser.cpp b/src/core/frontend/applets/web_browser.cpp
index 528295ffc..58861809e 100644
--- a/src/core/frontend/applets/web_browser.cpp
+++ b/src/core/frontend/applets/web_browser.cpp
@@ -11,14 +11,4 @@ WebBrowserApplet::~WebBrowserApplet() = default;
11 11
12DefaultWebBrowserApplet::~DefaultWebBrowserApplet() = default; 12DefaultWebBrowserApplet::~DefaultWebBrowserApplet() = default;
13 13
14void DefaultWebBrowserApplet::OpenPageLocal(std::string_view filename,
15 std::function<void()> unpack_romfs_callback,
16 std::function<void()> finished_callback) {
17 LOG_INFO(Service_AM,
18 "(STUBBED) called - No suitable web browser implementation found to open website page "
19 "at '{}'!",
20 filename);
21 finished_callback();
22}
23
24} // namespace Core::Frontend 14} // namespace Core::Frontend
diff --git a/src/core/frontend/applets/web_browser.h b/src/core/frontend/applets/web_browser.h
index 110e33bc4..6e5f4d93d 100644
--- a/src/core/frontend/applets/web_browser.h
+++ b/src/core/frontend/applets/web_browser.h
@@ -5,24 +5,17 @@
5#pragma once 5#pragma once
6 6
7#include <functional> 7#include <functional>
8#include <string_view>
9 8
10namespace Core::Frontend { 9namespace Core::Frontend {
11 10
12class WebBrowserApplet { 11class WebBrowserApplet {
13public: 12public:
14 virtual ~WebBrowserApplet(); 13 virtual ~WebBrowserApplet();
15
16 virtual void OpenPageLocal(std::string_view url, std::function<void()> unpack_romfs_callback,
17 std::function<void()> finished_callback) = 0;
18}; 14};
19 15
20class DefaultWebBrowserApplet final : public WebBrowserApplet { 16class DefaultWebBrowserApplet final : public WebBrowserApplet {
21public: 17public:
22 ~DefaultWebBrowserApplet() override; 18 ~DefaultWebBrowserApplet() override;
23
24 void OpenPageLocal(std::string_view url, std::function<void()> unpack_romfs_callback,
25 std::function<void()> finished_callback) override;
26}; 19};
27 20
28} // namespace Core::Frontend 21} // namespace Core::Frontend
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp
index 2b626bb40..08676c3fc 100644
--- a/src/core/hle/service/am/applets/applets.cpp
+++ b/src/core/hle/service/am/applets/applets.cpp
@@ -142,14 +142,14 @@ void Applet::Initialize() {
142 142
143AppletFrontendSet::AppletFrontendSet() = default; 143AppletFrontendSet::AppletFrontendSet() = default;
144 144
145AppletFrontendSet::AppletFrontendSet(ControllerApplet controller, ECommerceApplet e_commerce, 145AppletFrontendSet::AppletFrontendSet(ControllerApplet controller_applet, ErrorApplet error_applet,
146 ErrorApplet error, ParentalControlsApplet parental_controls, 146 ParentalControlsApplet parental_controls_applet,
147 PhotoViewer photo_viewer, ProfileSelect profile_select, 147 PhotoViewer photo_viewer_, ProfileSelect profile_select_,
148 SoftwareKeyboard software_keyboard, WebBrowser web_browser) 148 SoftwareKeyboard software_keyboard_, WebBrowser web_browser_)
149 : controller{std::move(controller)}, e_commerce{std::move(e_commerce)}, error{std::move(error)}, 149 : controller{std::move(controller_applet)}, error{std::move(error_applet)},
150 parental_controls{std::move(parental_controls)}, photo_viewer{std::move(photo_viewer)}, 150 parental_controls{std::move(parental_controls_applet)},
151 profile_select{std::move(profile_select)}, software_keyboard{std::move(software_keyboard)}, 151 photo_viewer{std::move(photo_viewer_)}, profile_select{std::move(profile_select_)},
152 web_browser{std::move(web_browser)} {} 152 software_keyboard{std::move(software_keyboard_)}, web_browser{std::move(web_browser_)} {}
153 153
154AppletFrontendSet::~AppletFrontendSet() = default; 154AppletFrontendSet::~AppletFrontendSet() = default;
155 155
@@ -170,10 +170,6 @@ void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) {
170 frontend.controller = std::move(set.controller); 170 frontend.controller = std::move(set.controller);
171 } 171 }
172 172
173 if (set.e_commerce != nullptr) {
174 frontend.e_commerce = std::move(set.e_commerce);
175 }
176
177 if (set.error != nullptr) { 173 if (set.error != nullptr) {
178 frontend.error = std::move(set.error); 174 frontend.error = std::move(set.error);
179 } 175 }
@@ -210,10 +206,6 @@ void AppletManager::SetDefaultAppletsIfMissing() {
210 std::make_unique<Core::Frontend::DefaultControllerApplet>(system.ServiceManager()); 206 std::make_unique<Core::Frontend::DefaultControllerApplet>(system.ServiceManager());
211 } 207 }
212 208
213 if (frontend.e_commerce == nullptr) {
214 frontend.e_commerce = std::make_unique<Core::Frontend::DefaultECommerceApplet>();
215 }
216
217 if (frontend.error == nullptr) { 209 if (frontend.error == nullptr) {
218 frontend.error = std::make_unique<Core::Frontend::DefaultErrorApplet>(); 210 frontend.error = std::make_unique<Core::Frontend::DefaultErrorApplet>();
219 } 211 }
@@ -257,13 +249,14 @@ std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id) const {
257 return std::make_shared<ProfileSelect>(system, *frontend.profile_select); 249 return std::make_shared<ProfileSelect>(system, *frontend.profile_select);
258 case AppletId::SoftwareKeyboard: 250 case AppletId::SoftwareKeyboard:
259 return std::make_shared<SoftwareKeyboard>(system, *frontend.software_keyboard); 251 return std::make_shared<SoftwareKeyboard>(system, *frontend.software_keyboard);
252 case AppletId::Web:
253 case AppletId::Shop:
254 case AppletId::OfflineWeb:
255 case AppletId::LoginShare:
256 case AppletId::WebAuth:
257 return std::make_shared<WebBrowser>(system, *frontend.web_browser);
260 case AppletId::PhotoViewer: 258 case AppletId::PhotoViewer:
261 return std::make_shared<PhotoViewer>(system, *frontend.photo_viewer); 259 return std::make_shared<PhotoViewer>(system, *frontend.photo_viewer);
262 case AppletId::LibAppletShop:
263 return std::make_shared<WebBrowser>(system, *frontend.web_browser,
264 frontend.e_commerce.get());
265 case AppletId::LibAppletOff:
266 return std::make_shared<WebBrowser>(system, *frontend.web_browser);
267 default: 260 default:
268 UNIMPLEMENTED_MSG( 261 UNIMPLEMENTED_MSG(
269 "No backend implementation exists for applet_id={:02X}! Falling back to stub applet.", 262 "No backend implementation exists for applet_id={:02X}! Falling back to stub applet.",
diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h
index a1f4cf897..4fd792c05 100644
--- a/src/core/hle/service/am/applets/applets.h
+++ b/src/core/hle/service/am/applets/applets.h
@@ -50,13 +50,13 @@ enum class AppletId : u32 {
50 ProfileSelect = 0x10, 50 ProfileSelect = 0x10,
51 SoftwareKeyboard = 0x11, 51 SoftwareKeyboard = 0x11,
52 MiiEdit = 0x12, 52 MiiEdit = 0x12,
53 LibAppletWeb = 0x13, 53 Web = 0x13,
54 LibAppletShop = 0x14, 54 Shop = 0x14,
55 PhotoViewer = 0x15, 55 PhotoViewer = 0x15,
56 Settings = 0x16, 56 Settings = 0x16,
57 LibAppletOff = 0x17, 57 OfflineWeb = 0x17,
58 LibAppletWhitelisted = 0x18, 58 LoginShare = 0x18,
59 LibAppletAuth = 0x19, 59 WebAuth = 0x19,
60 MyPage = 0x1A, 60 MyPage = 0x1A,
61}; 61};
62 62
@@ -157,7 +157,6 @@ protected:
157 157
158struct AppletFrontendSet { 158struct AppletFrontendSet {
159 using ControllerApplet = std::unique_ptr<Core::Frontend::ControllerApplet>; 159 using ControllerApplet = std::unique_ptr<Core::Frontend::ControllerApplet>;
160 using ECommerceApplet = std::unique_ptr<Core::Frontend::ECommerceApplet>;
161 using ErrorApplet = std::unique_ptr<Core::Frontend::ErrorApplet>; 160 using ErrorApplet = std::unique_ptr<Core::Frontend::ErrorApplet>;
162 using ParentalControlsApplet = std::unique_ptr<Core::Frontend::ParentalControlsApplet>; 161 using ParentalControlsApplet = std::unique_ptr<Core::Frontend::ParentalControlsApplet>;
163 using PhotoViewer = std::unique_ptr<Core::Frontend::PhotoViewerApplet>; 162 using PhotoViewer = std::unique_ptr<Core::Frontend::PhotoViewerApplet>;
@@ -166,10 +165,10 @@ struct AppletFrontendSet {
166 using WebBrowser = std::unique_ptr<Core::Frontend::WebBrowserApplet>; 165 using WebBrowser = std::unique_ptr<Core::Frontend::WebBrowserApplet>;
167 166
168 AppletFrontendSet(); 167 AppletFrontendSet();
169 AppletFrontendSet(ControllerApplet controller, ECommerceApplet e_commerce, ErrorApplet error, 168 AppletFrontendSet(ControllerApplet controller_applet, ErrorApplet error_applet,
170 ParentalControlsApplet parental_controls, PhotoViewer photo_viewer, 169 ParentalControlsApplet parental_controls_applet, PhotoViewer photo_viewer_,
171 ProfileSelect profile_select, SoftwareKeyboard software_keyboard, 170 ProfileSelect profile_select_, SoftwareKeyboard software_keyboard_,
172 WebBrowser web_browser); 171 WebBrowser web_browser_);
173 ~AppletFrontendSet(); 172 ~AppletFrontendSet();
174 173
175 AppletFrontendSet(const AppletFrontendSet&) = delete; 174 AppletFrontendSet(const AppletFrontendSet&) = delete;
@@ -179,7 +178,6 @@ struct AppletFrontendSet {
179 AppletFrontendSet& operator=(AppletFrontendSet&&) noexcept; 178 AppletFrontendSet& operator=(AppletFrontendSet&&) noexcept;
180 179
181 ControllerApplet controller; 180 ControllerApplet controller;
182 ECommerceApplet e_commerce;
183 ErrorApplet error; 181 ErrorApplet error;
184 ParentalControlsApplet parental_controls; 182 ParentalControlsApplet parental_controls;
185 PhotoViewer photo_viewer; 183 PhotoViewer photo_viewer;
diff --git a/src/core/hle/service/am/applets/web_browser.cpp b/src/core/hle/service/am/applets/web_browser.cpp
index c3b6b706a..74ef037fe 100644
--- a/src/core/hle/service/am/applets/web_browser.cpp
+++ b/src/core/hle/service/am/applets/web_browser.cpp
@@ -1,238 +1,24 @@
1// Copyright 2018 yuzu emulator team 1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <array>
6#include <cstring>
7#include <vector>
8
9#include "common/assert.h" 5#include "common/assert.h"
10#include "common/common_funcs.h"
11#include "common/common_paths.h"
12#include "common/file_util.h"
13#include "common/hex_util.h"
14#include "common/logging/log.h" 6#include "common/logging/log.h"
15#include "common/string_util.h"
16#include "core/core.h" 7#include "core/core.h"
17#include "core/file_sys/content_archive.h"
18#include "core/file_sys/mode.h"
19#include "core/file_sys/nca_metadata.h"
20#include "core/file_sys/registered_cache.h"
21#include "core/file_sys/romfs.h"
22#include "core/file_sys/system_archive/system_archive.h"
23#include "core/file_sys/vfs_types.h"
24#include "core/frontend/applets/general_frontend.h"
25#include "core/frontend/applets/web_browser.h" 8#include "core/frontend/applets/web_browser.h"
26#include "core/hle/kernel/process.h" 9#include "core/hle/result.h"
10#include "core/hle/service/am/am.h"
27#include "core/hle/service/am/applets/web_browser.h" 11#include "core/hle/service/am/applets/web_browser.h"
28#include "core/hle/service/filesystem/filesystem.h"
29#include "core/loader/loader.h"
30 12
31namespace Service::AM::Applets { 13namespace Service::AM::Applets {
32 14
33enum class WebArgTLVType : u16 { 15WebBrowser::WebBrowser(Core::System& system_, const Core::Frontend::WebBrowserApplet& frontend_)
34 InitialURL = 0x1, 16 : Applet{system_.Kernel()}, frontend(frontend_), system{system_} {}
35 ShopArgumentsURL = 0x2, ///< TODO(DarkLordZach): This is not the official name.
36 CallbackURL = 0x3,
37 CallbackableURL = 0x4,
38 ApplicationID = 0x5,
39 DocumentPath = 0x6,
40 DocumentKind = 0x7,
41 SystemDataID = 0x8,
42 ShareStartPage = 0x9,
43 Whitelist = 0xA,
44 News = 0xB,
45 UserID = 0xE,
46 AlbumEntry0 = 0xF,
47 ScreenShotEnabled = 0x10,
48 EcClientCertEnabled = 0x11,
49 Unk12 = 0x12,
50 PlayReportEnabled = 0x13,
51 Unk14 = 0x14,
52 Unk15 = 0x15,
53 BootDisplayKind = 0x17,
54 BackgroundKind = 0x18,
55 FooterEnabled = 0x19,
56 PointerEnabled = 0x1A,
57 LeftStickMode = 0x1B,
58 KeyRepeatFrame1 = 0x1C,
59 KeyRepeatFrame2 = 0x1D,
60 BootAsMediaPlayerInv = 0x1E,
61 DisplayUrlKind = 0x1F,
62 BootAsMediaPlayer = 0x21,
63 ShopJumpEnabled = 0x22,
64 MediaAutoPlayEnabled = 0x23,
65 LobbyParameter = 0x24,
66 ApplicationAlbumEntry = 0x26,
67 JsExtensionEnabled = 0x27,
68 AdditionalCommentText = 0x28,
69 TouchEnabledOnContents = 0x29,
70 UserAgentAdditionalString = 0x2A,
71 AdditionalMediaData0 = 0x2B,
72 MediaPlayerAutoCloseEnabled = 0x2C,
73 PageCacheEnabled = 0x2D,
74 WebAudioEnabled = 0x2E,
75 Unk2F = 0x2F,
76 YouTubeVideoWhitelist = 0x31,
77 FooterFixedKind = 0x32,
78 PageFadeEnabled = 0x33,
79 MediaCreatorApplicationRatingAge = 0x34,
80 BootLoadingIconEnabled = 0x35,
81 PageScrollIndicationEnabled = 0x36,
82 MediaPlayerSpeedControlEnabled = 0x37,
83 AlbumEntry1 = 0x38,
84 AlbumEntry2 = 0x39,
85 AlbumEntry3 = 0x3A,
86 AdditionalMediaData1 = 0x3B,
87 AdditionalMediaData2 = 0x3C,
88 AdditionalMediaData3 = 0x3D,
89 BootFooterButton = 0x3E,
90 OverrideWebAudioVolume = 0x3F,
91 OverrideMediaAudioVolume = 0x40,
92 BootMode = 0x41,
93 WebSessionEnabled = 0x42,
94};
95
96enum class ShimKind : u32 {
97 Shop = 1,
98 Login = 2,
99 Offline = 3,
100 Share = 4,
101 Web = 5,
102 Wifi = 6,
103 Lobby = 7,
104};
105
106enum class ShopWebTarget {
107 ApplicationInfo,
108 AddOnContentList,
109 SubscriptionList,
110 ConsumableItemList,
111 Home,
112 Settings,
113};
114
115namespace {
116
117constexpr std::size_t SHIM_KIND_COUNT = 0x8;
118
119struct WebArgHeader {
120 u16 count;
121 INSERT_PADDING_BYTES(2);
122 ShimKind kind;
123};
124static_assert(sizeof(WebArgHeader) == 0x8, "WebArgHeader has incorrect size.");
125
126struct WebArgTLV {
127 WebArgTLVType type;
128 u16 size;
129 u32 offset;
130};
131static_assert(sizeof(WebArgTLV) == 0x8, "WebArgTLV has incorrect size.");
132
133struct WebCommonReturnValue {
134 u32 result_code;
135 INSERT_PADDING_BYTES(0x4);
136 std::array<char, 0x1000> last_url;
137 u64 last_url_size;
138};
139static_assert(sizeof(WebCommonReturnValue) == 0x1010, "WebCommonReturnValue has incorrect size.");
140
141struct WebWifiPageArg {
142 INSERT_PADDING_BYTES(4);
143 std::array<char, 0x100> connection_test_url;
144 std::array<char, 0x400> initial_url;
145 std::array<u8, 0x10> nifm_network_uuid;
146 u32 nifm_requirement;
147};
148static_assert(sizeof(WebWifiPageArg) == 0x518, "WebWifiPageArg has incorrect size.");
149
150struct WebWifiReturnValue {
151 INSERT_PADDING_BYTES(4);
152 u32 result;
153};
154static_assert(sizeof(WebWifiReturnValue) == 0x8, "WebWifiReturnValue has incorrect size.");
155
156enum class OfflineWebSource : u32 {
157 OfflineHtmlPage = 0x1,
158 ApplicationLegalInformation = 0x2,
159 SystemDataPage = 0x3,
160};
161
162std::map<WebArgTLVType, std::vector<u8>> GetWebArguments(const std::vector<u8>& arg) {
163 if (arg.size() < sizeof(WebArgHeader))
164 return {};
165
166 WebArgHeader header{};
167 std::memcpy(&header, arg.data(), sizeof(WebArgHeader));
168
169 std::map<WebArgTLVType, std::vector<u8>> out;
170 u64 offset = sizeof(WebArgHeader);
171 for (std::size_t i = 0; i < header.count; ++i) {
172 if (arg.size() < (offset + sizeof(WebArgTLV)))
173 return out;
174
175 WebArgTLV tlv{};
176 std::memcpy(&tlv, arg.data() + offset, sizeof(WebArgTLV));
177 offset += sizeof(WebArgTLV);
178
179 offset += tlv.offset;
180 if (arg.size() < (offset + tlv.size))
181 return out;
182
183 std::vector<u8> data(tlv.size);
184 std::memcpy(data.data(), arg.data() + offset, tlv.size);
185 offset += tlv.size;
186
187 out.insert_or_assign(tlv.type, data);
188 }
189
190 return out;
191}
192
193FileSys::VirtualFile GetApplicationRomFS(const Core::System& system, u64 title_id,
194 FileSys::ContentRecordType type) {
195 const auto& installed{system.GetContentProvider()};
196 const auto res = installed.GetEntry(title_id, type);
197
198 if (res != nullptr) {
199 return res->GetRomFS();
200 }
201
202 if (type == FileSys::ContentRecordType::Data) {
203 return FileSys::SystemArchive::SynthesizeSystemArchive(title_id);
204 }
205
206 return nullptr;
207}
208
209} // Anonymous namespace
210
211WebBrowser::WebBrowser(Core::System& system_, Core::Frontend::WebBrowserApplet& frontend_,
212 Core::Frontend::ECommerceApplet* frontend_e_commerce_)
213 : Applet{system_.Kernel()}, frontend(frontend_),
214 frontend_e_commerce(frontend_e_commerce_), system{system_} {}
215 17
216WebBrowser::~WebBrowser() = default; 18WebBrowser::~WebBrowser() = default;
217 19
218void WebBrowser::Initialize() { 20void WebBrowser::Initialize() {
219 Applet::Initialize(); 21 Applet::Initialize();
220
221 complete = false;
222 temporary_dir.clear();
223 filename.clear();
224 status = RESULT_SUCCESS;
225
226 const auto web_arg_storage = broker.PopNormalDataToApplet();
227 ASSERT(web_arg_storage != nullptr);
228 const auto& web_arg = web_arg_storage->GetData();
229
230 ASSERT(web_arg.size() >= 0x8);
231 std::memcpy(&kind, web_arg.data() + 0x4, sizeof(ShimKind));
232
233 args = GetWebArguments(web_arg);
234
235 InitializeInternal();
236} 22}
237 23
238bool WebBrowser::TransactionComplete() const { 24bool WebBrowser::TransactionComplete() const {
@@ -247,312 +33,6 @@ void WebBrowser::ExecuteInteractive() {
247 UNIMPLEMENTED_MSG("Unexpected interactive data recieved!"); 33 UNIMPLEMENTED_MSG("Unexpected interactive data recieved!");
248} 34}
249 35
250void WebBrowser::Execute() { 36void WebBrowser::Execute() {}
251 if (complete) {
252 return;
253 }
254
255 if (status != RESULT_SUCCESS) {
256 complete = true;
257
258 // This is a workaround in order not to softlock yuzu when an error happens during the
259 // webapplet init. In order to avoid an svcBreak, the status is set to RESULT_SUCCESS
260 Finalize();
261 status = RESULT_SUCCESS;
262
263 return;
264 }
265
266 ExecuteInternal();
267}
268
269void WebBrowser::UnpackRomFS() {
270 if (unpacked)
271 return;
272
273 ASSERT(offline_romfs != nullptr);
274 const auto dir =
275 FileSys::ExtractRomFS(offline_romfs, FileSys::RomFSExtractionType::SingleDiscard);
276 const auto& vfs{system.GetFilesystem()};
277 const auto temp_dir = vfs->CreateDirectory(temporary_dir, FileSys::Mode::ReadWrite);
278 FileSys::VfsRawCopyD(dir, temp_dir);
279
280 unpacked = true;
281}
282
283void WebBrowser::Finalize() {
284 complete = true;
285
286 WebCommonReturnValue out{};
287 out.result_code = 0;
288 out.last_url_size = 0;
289
290 std::vector<u8> data(sizeof(WebCommonReturnValue));
291 std::memcpy(data.data(), &out, sizeof(WebCommonReturnValue));
292
293 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(data)));
294 broker.SignalStateChanged();
295
296 if (!temporary_dir.empty() && Common::FS::IsDirectory(temporary_dir)) {
297 Common::FS::DeleteDirRecursively(temporary_dir);
298 }
299}
300
301void WebBrowser::InitializeInternal() {
302 using WebAppletInitializer = void (WebBrowser::*)();
303
304 constexpr std::array<WebAppletInitializer, SHIM_KIND_COUNT> functions{
305 nullptr, &WebBrowser::InitializeShop,
306 nullptr, &WebBrowser::InitializeOffline,
307 nullptr, nullptr,
308 nullptr, nullptr,
309 };
310
311 const auto index = static_cast<u32>(kind);
312
313 if (index > functions.size() || functions[index] == nullptr) {
314 LOG_ERROR(Service_AM, "Invalid shim_kind={:08X}", index);
315 return;
316 }
317
318 const auto function = functions[index];
319 (this->*function)();
320}
321
322void WebBrowser::ExecuteInternal() {
323 using WebAppletExecutor = void (WebBrowser::*)();
324
325 constexpr std::array<WebAppletExecutor, SHIM_KIND_COUNT> functions{
326 nullptr, &WebBrowser::ExecuteShop,
327 nullptr, &WebBrowser::ExecuteOffline,
328 nullptr, nullptr,
329 nullptr, nullptr,
330 };
331
332 const auto index = static_cast<u32>(kind);
333
334 if (index > functions.size() || functions[index] == nullptr) {
335 LOG_ERROR(Service_AM, "Invalid shim_kind={:08X}", index);
336 return;
337 }
338
339 const auto function = functions[index];
340 (this->*function)();
341}
342
343void WebBrowser::InitializeShop() {
344 if (frontend_e_commerce == nullptr) {
345 LOG_ERROR(Service_AM, "Missing ECommerce Applet frontend!");
346 status = RESULT_UNKNOWN;
347 return;
348 }
349
350 const auto user_id_data = args.find(WebArgTLVType::UserID);
351
352 user_id = std::nullopt;
353 if (user_id_data != args.end()) {
354 user_id = u128{};
355 std::memcpy(user_id->data(), user_id_data->second.data(), sizeof(u128));
356 }
357
358 const auto url = args.find(WebArgTLVType::ShopArgumentsURL);
359
360 if (url == args.end()) {
361 LOG_ERROR(Service_AM, "Missing EShop Arguments URL for initialization!");
362 status = RESULT_UNKNOWN;
363 return;
364 }
365
366 std::vector<std::string> split_query;
367 Common::SplitString(Common::StringFromFixedZeroTerminatedBuffer(
368 reinterpret_cast<const char*>(url->second.data()), url->second.size()),
369 '?', split_query);
370
371 // 2 -> Main URL '?' Query Parameters
372 // Less is missing info, More is malformed
373 if (split_query.size() != 2) {
374 LOG_ERROR(Service_AM, "EShop Arguments has more than one question mark, malformed");
375 status = RESULT_UNKNOWN;
376 return;
377 }
378
379 std::vector<std::string> queries;
380 Common::SplitString(split_query[1], '&', queries);
381
382 const auto split_single_query =
383 [](const std::string& in) -> std::pair<std::string, std::string> {
384 const auto index = in.find('=');
385 if (index == std::string::npos || index == in.size() - 1) {
386 return {in, ""};
387 }
388
389 return {in.substr(0, index), in.substr(index + 1)};
390 };
391
392 std::transform(queries.begin(), queries.end(),
393 std::inserter(shop_query, std::next(shop_query.begin())), split_single_query);
394
395 const auto scene = shop_query.find("scene");
396
397 if (scene == shop_query.end()) {
398 LOG_ERROR(Service_AM, "No scene parameter was passed via shop query!");
399 status = RESULT_UNKNOWN;
400 return;
401 }
402
403 const std::map<std::string, ShopWebTarget, std::less<>> target_map{
404 {"product_detail", ShopWebTarget::ApplicationInfo},
405 {"aocs", ShopWebTarget::AddOnContentList},
406 {"subscriptions", ShopWebTarget::SubscriptionList},
407 {"consumption", ShopWebTarget::ConsumableItemList},
408 {"settings", ShopWebTarget::Settings},
409 {"top", ShopWebTarget::Home},
410 };
411
412 const auto target = target_map.find(scene->second);
413 if (target == target_map.end()) {
414 LOG_ERROR(Service_AM, "Scene for shop query is invalid! (scene={})", scene->second);
415 status = RESULT_UNKNOWN;
416 return;
417 }
418
419 shop_web_target = target->second;
420
421 const auto title_id_data = shop_query.find("dst_app_id");
422 if (title_id_data != shop_query.end()) {
423 title_id = std::stoull(title_id_data->second, nullptr, 0x10);
424 }
425
426 const auto mode_data = shop_query.find("mode");
427 if (mode_data != shop_query.end()) {
428 shop_full_display = mode_data->second == "full";
429 }
430}
431
432void WebBrowser::InitializeOffline() {
433 if (args.find(WebArgTLVType::DocumentPath) == args.end() ||
434 args.find(WebArgTLVType::DocumentKind) == args.end() ||
435 args.find(WebArgTLVType::ApplicationID) == args.end()) {
436 status = RESULT_UNKNOWN;
437 LOG_ERROR(Service_AM, "Missing necessary parameters for initialization!");
438 }
439
440 const auto url_data = args[WebArgTLVType::DocumentPath];
441 filename = Common::StringFromFixedZeroTerminatedBuffer(
442 reinterpret_cast<const char*>(url_data.data()), url_data.size());
443
444 OfflineWebSource source;
445 ASSERT(args[WebArgTLVType::DocumentKind].size() >= 4);
446 std::memcpy(&source, args[WebArgTLVType::DocumentKind].data(), sizeof(OfflineWebSource));
447
448 constexpr std::array<const char*, 3> WEB_SOURCE_NAMES{
449 "manual",
450 "legal",
451 "system",
452 };
453
454 temporary_dir =
455 Common::FS::SanitizePath(Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) +
456 "web_applet_" + WEB_SOURCE_NAMES[static_cast<u32>(source) - 1],
457 Common::FS::DirectorySeparator::PlatformDefault);
458 Common::FS::DeleteDirRecursively(temporary_dir);
459
460 u64 title_id = 0; // 0 corresponds to current process
461 ASSERT(args[WebArgTLVType::ApplicationID].size() >= 0x8);
462 std::memcpy(&title_id, args[WebArgTLVType::ApplicationID].data(), sizeof(u64));
463 FileSys::ContentRecordType type = FileSys::ContentRecordType::Data;
464
465 switch (source) {
466 case OfflineWebSource::OfflineHtmlPage:
467 // While there is an AppID TLV field, in official SW this is always ignored.
468 title_id = 0;
469 type = FileSys::ContentRecordType::HtmlDocument;
470 break;
471 case OfflineWebSource::ApplicationLegalInformation:
472 type = FileSys::ContentRecordType::LegalInformation;
473 break;
474 case OfflineWebSource::SystemDataPage:
475 type = FileSys::ContentRecordType::Data;
476 break;
477 }
478
479 if (title_id == 0) {
480 title_id = system.CurrentProcess()->GetTitleID();
481 }
482
483 offline_romfs = GetApplicationRomFS(system, title_id, type);
484 if (offline_romfs == nullptr) {
485 status = RESULT_UNKNOWN;
486 LOG_ERROR(Service_AM, "Failed to find offline data for request!");
487 }
488
489 std::string path_additional_directory;
490 if (source == OfflineWebSource::OfflineHtmlPage) {
491 path_additional_directory = std::string(DIR_SEP).append("html-document");
492 }
493
494 filename =
495 Common::FS::SanitizePath(temporary_dir + path_additional_directory + DIR_SEP + filename,
496 Common::FS::DirectorySeparator::PlatformDefault);
497}
498
499void WebBrowser::ExecuteShop() {
500 const auto callback = [this]() { Finalize(); };
501
502 const auto check_optional_parameter = [this](const auto& p) {
503 if (!p.has_value()) {
504 LOG_ERROR(Service_AM, "Missing one or more necessary parameters for execution!");
505 status = RESULT_UNKNOWN;
506 return false;
507 }
508
509 return true;
510 };
511
512 switch (shop_web_target) {
513 case ShopWebTarget::ApplicationInfo:
514 if (!check_optional_parameter(title_id))
515 return;
516 frontend_e_commerce->ShowApplicationInformation(callback, *title_id, user_id,
517 shop_full_display, shop_extra_parameter);
518 break;
519 case ShopWebTarget::AddOnContentList:
520 if (!check_optional_parameter(title_id))
521 return;
522 frontend_e_commerce->ShowAddOnContentList(callback, *title_id, user_id, shop_full_display);
523 break;
524 case ShopWebTarget::ConsumableItemList:
525 if (!check_optional_parameter(title_id))
526 return;
527 frontend_e_commerce->ShowConsumableItemList(callback, *title_id, user_id);
528 break;
529 case ShopWebTarget::Home:
530 if (!check_optional_parameter(user_id))
531 return;
532 if (!check_optional_parameter(shop_full_display))
533 return;
534 frontend_e_commerce->ShowShopHome(callback, *user_id, *shop_full_display);
535 break;
536 case ShopWebTarget::Settings:
537 if (!check_optional_parameter(user_id))
538 return;
539 if (!check_optional_parameter(shop_full_display))
540 return;
541 frontend_e_commerce->ShowSettings(callback, *user_id, *shop_full_display);
542 break;
543 case ShopWebTarget::SubscriptionList:
544 if (!check_optional_parameter(title_id))
545 return;
546 frontend_e_commerce->ShowSubscriptionList(callback, *title_id, user_id);
547 break;
548 default:
549 UNREACHABLE();
550 }
551}
552
553void WebBrowser::ExecuteOffline() {
554 frontend.OpenPageLocal(
555 filename, [this] { UnpackRomFS(); }, [this] { Finalize(); });
556}
557 37
558} // namespace Service::AM::Applets 38} // 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 8d4027411..0584142e5 100644
--- a/src/core/hle/service/am/applets/web_browser.h
+++ b/src/core/hle/service/am/applets/web_browser.h
@@ -1,12 +1,12 @@
1// Copyright 2018 yuzu emulator team 1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
6 6
7#include <map> 7#include "common/common_funcs.h"
8#include "core/file_sys/vfs_types.h" 8#include "common/common_types.h"
9#include "core/hle/service/am/am.h" 9#include "core/hle/result.h"
10#include "core/hle/service/am/applets/applets.h" 10#include "core/hle/service/am/applets/applets.h"
11 11
12namespace Core { 12namespace Core {
@@ -15,14 +15,9 @@ class System;
15 15
16namespace Service::AM::Applets { 16namespace Service::AM::Applets {
17 17
18enum class ShimKind : u32;
19enum class ShopWebTarget;
20enum class WebArgTLVType : u16;
21
22class WebBrowser final : public Applet { 18class WebBrowser final : public Applet {
23public: 19public:
24 WebBrowser(Core::System& system_, Core::Frontend::WebBrowserApplet& frontend_, 20 WebBrowser(Core::System& system_, const Core::Frontend::WebBrowserApplet& frontend_);
25 Core::Frontend::ECommerceApplet* frontend_e_commerce_ = nullptr);
26 21
27 ~WebBrowser() override; 22 ~WebBrowser() override;
28 23
@@ -33,49 +28,11 @@ public:
33 void ExecuteInteractive() override; 28 void ExecuteInteractive() override;
34 void Execute() override; 29 void Execute() override;
35 30
36 // Callback to be fired when the frontend needs the manual RomFS unpacked to temporary
37 // directory. This is a blocking call and may take a while as some manuals can be up to 100MB in
38 // size. Attempting to access files at filename before invocation is likely to not work.
39 void UnpackRomFS();
40
41 // Callback to be fired when the frontend is finished browsing. This will delete the temporary
42 // manual RomFS extracted files, so ensure this is only called at actual finalization.
43 void Finalize();
44
45private: 31private:
46 void InitializeInternal(); 32 const Core::Frontend::WebBrowserApplet& frontend;
47 void ExecuteInternal();
48
49 // Specific initializers for the types of web applets
50 void InitializeShop();
51 void InitializeOffline();
52
53 // Specific executors for the types of web applets
54 void ExecuteShop();
55 void ExecuteOffline();
56
57 Core::Frontend::WebBrowserApplet& frontend;
58
59 // Extra frontends for specialized functions
60 Core::Frontend::ECommerceApplet* frontend_e_commerce;
61
62 bool complete = false;
63 bool unpacked = false;
64 ResultCode status = RESULT_SUCCESS;
65
66 ShimKind kind;
67 std::map<WebArgTLVType, std::vector<u8>> args;
68
69 FileSys::VirtualFile offline_romfs;
70 std::string temporary_dir;
71 std::string filename;
72 33
73 ShopWebTarget shop_web_target; 34 bool complete{false};
74 std::map<std::string, std::string, std::less<>> shop_query; 35 ResultCode status{RESULT_SUCCESS};
75 std::optional<u64> title_id = 0;
76 std::optional<u128> user_id;
77 std::optional<bool> shop_full_display;
78 std::string shop_extra_parameter;
79 36
80 Core::System& system; 37 Core::System& system;
81}; 38};
diff --git a/src/yuzu/applets/web_browser.cpp b/src/yuzu/applets/web_browser.cpp
index 9fd8d6326..92b53fed0 100644
--- a/src/yuzu/applets/web_browser.cpp
+++ b/src/yuzu/applets/web_browser.cpp
@@ -1,115 +1,11 @@
1// Copyright 2018 yuzu Emulator Project 1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <mutex>
6
7#include <QKeyEvent>
8
9#include "core/hle/lock.h" 5#include "core/hle/lock.h"
10#include "yuzu/applets/web_browser.h" 6#include "yuzu/applets/web_browser.h"
11#include "yuzu/main.h" 7#include "yuzu/main.h"
12 8
13#ifdef YUZU_USE_QT_WEB_ENGINE 9QtWebBrowser::QtWebBrowser(GMainWindow& main_window) {}
14
15constexpr char NX_SHIM_INJECT_SCRIPT[] = R"(
16 window.nx = {};
17 window.nx.playReport = {};
18 window.nx.playReport.setCounterSetIdentifier = function () {
19 console.log("nx.playReport.setCounterSetIdentifier called - unimplemented");
20 };
21
22 window.nx.playReport.incrementCounter = function () {
23 console.log("nx.playReport.incrementCounter called - unimplemented");
24 };
25
26 window.nx.footer = {};
27 window.nx.footer.unsetAssign = function () {
28 console.log("nx.footer.unsetAssign called - unimplemented");
29 };
30
31 var yuzu_key_callbacks = [];
32 window.nx.footer.setAssign = function(key, discard1, func, discard2) {
33 switch (key) {
34 case 'A':
35 yuzu_key_callbacks[0] = func;
36 break;
37 case 'B':
38 yuzu_key_callbacks[1] = func;
39 break;
40 case 'X':
41 yuzu_key_callbacks[2] = func;
42 break;
43 case 'Y':
44 yuzu_key_callbacks[3] = func;
45 break;
46 case 'L':
47 yuzu_key_callbacks[6] = func;
48 break;
49 case 'R':
50 yuzu_key_callbacks[7] = func;
51 break;
52 }
53 };
54
55 var applet_done = false;
56 window.nx.endApplet = function() {
57 applet_done = true;
58 };
59
60 window.onkeypress = function(e) { if (e.keyCode === 13) { applet_done = true; } };
61)";
62
63QString GetNXShimInjectionScript() {
64 return QString::fromStdString(NX_SHIM_INJECT_SCRIPT);
65}
66
67NXInputWebEngineView::NXInputWebEngineView(QWidget* parent) : QWebEngineView(parent) {}
68
69void NXInputWebEngineView::keyPressEvent(QKeyEvent* event) {
70 parent()->event(event);
71}
72
73void NXInputWebEngineView::keyReleaseEvent(QKeyEvent* event) {
74 parent()->event(event);
75}
76
77#endif
78
79QtWebBrowser::QtWebBrowser(GMainWindow& main_window) {
80 connect(this, &QtWebBrowser::MainWindowOpenPage, &main_window, &GMainWindow::WebBrowserOpenPage,
81 Qt::QueuedConnection);
82 connect(&main_window, &GMainWindow::WebBrowserUnpackRomFS, this,
83 &QtWebBrowser::MainWindowUnpackRomFS, Qt::QueuedConnection);
84 connect(&main_window, &GMainWindow::WebBrowserFinishedBrowsing, this,
85 &QtWebBrowser::MainWindowFinishedBrowsing, Qt::QueuedConnection);
86}
87 10
88QtWebBrowser::~QtWebBrowser() = default; 11QtWebBrowser::~QtWebBrowser() = default;
89
90void QtWebBrowser::OpenPageLocal(std::string_view url, std::function<void()> unpack_romfs_callback_,
91 std::function<void()> finished_callback_) {
92 unpack_romfs_callback = std::move(unpack_romfs_callback_);
93 finished_callback = std::move(finished_callback_);
94
95 const auto index = url.find('?');
96 if (index == std::string::npos) {
97 emit MainWindowOpenPage(url, "");
98 } else {
99 const auto front = url.substr(0, index);
100 const auto back = url.substr(index);
101 emit MainWindowOpenPage(front, back);
102 }
103}
104
105void QtWebBrowser::MainWindowUnpackRomFS() {
106 // Acquire the HLE mutex
107 std::lock_guard lock{HLE::g_hle_lock};
108 unpack_romfs_callback();
109}
110
111void QtWebBrowser::MainWindowFinishedBrowsing() {
112 // Acquire the HLE mutex
113 std::lock_guard lock{HLE::g_hle_lock};
114 finished_callback();
115}
diff --git a/src/yuzu/applets/web_browser.h b/src/yuzu/applets/web_browser.h
index f801846cf..af053ace7 100644
--- a/src/yuzu/applets/web_browser.h
+++ b/src/yuzu/applets/web_browser.h
@@ -1,10 +1,9 @@
1// Copyright 2018 yuzu Emulator Project 1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
6 6
7#include <functional>
8#include <QObject> 7#include <QObject>
9 8
10#ifdef YUZU_USE_QT_WEB_ENGINE 9#ifdef YUZU_USE_QT_WEB_ENGINE
@@ -15,38 +14,10 @@
15 14
16class GMainWindow; 15class GMainWindow;
17 16
18#ifdef YUZU_USE_QT_WEB_ENGINE
19
20QString GetNXShimInjectionScript();
21
22class NXInputWebEngineView : public QWebEngineView {
23public:
24 explicit NXInputWebEngineView(QWidget* parent = nullptr);
25
26protected:
27 void keyPressEvent(QKeyEvent* event) override;
28 void keyReleaseEvent(QKeyEvent* event) override;
29};
30
31#endif
32
33class QtWebBrowser final : public QObject, public Core::Frontend::WebBrowserApplet { 17class QtWebBrowser final : public QObject, public Core::Frontend::WebBrowserApplet {
34 Q_OBJECT 18 Q_OBJECT
35 19
36public: 20public:
37 explicit QtWebBrowser(GMainWindow& main_window); 21 explicit QtWebBrowser(GMainWindow& main_window);
38 ~QtWebBrowser() override; 22 ~QtWebBrowser() override;
39
40 void OpenPageLocal(std::string_view url, std::function<void()> unpack_romfs_callback_,
41 std::function<void()> finished_callback_) override;
42
43signals:
44 void MainWindowOpenPage(std::string_view filename, std::string_view additional_args) const;
45
46private:
47 void MainWindowUnpackRomFS();
48 void MainWindowFinishedBrowsing();
49
50 std::function<void()> unpack_romfs_callback;
51 std::function<void()> finished_callback;
52}; 23};
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 3461fa675..7d4bba854 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -125,14 +125,6 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
125#include "yuzu/discord_impl.h" 125#include "yuzu/discord_impl.h"
126#endif 126#endif
127 127
128#ifdef YUZU_USE_QT_WEB_ENGINE
129#include <QWebEngineProfile>
130#include <QWebEngineScript>
131#include <QWebEngineScriptCollection>
132#include <QWebEngineSettings>
133#include <QWebEngineView>
134#endif
135
136#ifdef QT_STATICPLUGIN 128#ifdef QT_STATICPLUGIN
137Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin); 129Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin);
138#endif 130#endif
@@ -349,151 +341,6 @@ void GMainWindow::SoftwareKeyboardInvokeCheckDialog(std::u16string error_message
349 emit SoftwareKeyboardFinishedCheckDialog(); 341 emit SoftwareKeyboardFinishedCheckDialog();
350} 342}
351 343
352#ifdef YUZU_USE_QT_WEB_ENGINE
353
354void GMainWindow::WebBrowserOpenPage(std::string_view filename, std::string_view additional_args) {
355 NXInputWebEngineView web_browser_view(this);
356
357 // Scope to contain the QProgressDialog for initialization
358 {
359 QProgressDialog progress(this);
360 progress.setMinimumDuration(200);
361 progress.setLabelText(tr("Loading Web Applet..."));
362 progress.setRange(0, 4);
363 progress.setValue(0);
364 progress.show();
365
366 auto future = QtConcurrent::run([this] { emit WebBrowserUnpackRomFS(); });
367
368 while (!future.isFinished())
369 QApplication::processEvents();
370
371 progress.setValue(1);
372
373 // Load the special shim script to handle input and exit.
374 QWebEngineScript nx_shim;
375 nx_shim.setSourceCode(GetNXShimInjectionScript());
376 nx_shim.setWorldId(QWebEngineScript::MainWorld);
377 nx_shim.setName(QStringLiteral("nx_inject.js"));
378 nx_shim.setInjectionPoint(QWebEngineScript::DocumentCreation);
379 nx_shim.setRunsOnSubFrames(true);
380 web_browser_view.page()->profile()->scripts()->insert(nx_shim);
381
382 web_browser_view.load(
383 QUrl(QUrl::fromLocalFile(QString::fromStdString(std::string(filename))).toString() +
384 QString::fromStdString(std::string(additional_args))));
385
386 progress.setValue(2);
387
388 render_window->hide();
389 web_browser_view.setFocus();
390
391 const auto& layout = render_window->GetFramebufferLayout();
392 web_browser_view.resize(layout.screen.GetWidth(), layout.screen.GetHeight());
393 web_browser_view.move(layout.screen.left, layout.screen.top + menuBar()->height());
394 web_browser_view.setZoomFactor(static_cast<qreal>(layout.screen.GetWidth()) /
395 Layout::ScreenUndocked::Width);
396 web_browser_view.settings()->setAttribute(
397 QWebEngineSettings::LocalContentCanAccessRemoteUrls, true);
398
399 web_browser_view.show();
400
401 progress.setValue(3);
402
403 QApplication::processEvents();
404
405 progress.setValue(4);
406 }
407
408 bool finished = false;
409 QAction* exit_action = new QAction(tr("Exit Web Applet"), this);
410 connect(exit_action, &QAction::triggered, this, [&finished] { finished = true; });
411 ui.menubar->addAction(exit_action);
412
413 auto& npad =
414 Core::System::GetInstance()
415 .ServiceManager()
416 .GetService<Service::HID::Hid>("hid")
417 ->GetAppletResource()
418 ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad);
419
420 const auto fire_js_keypress = [&web_browser_view](u32 key_code) {
421 web_browser_view.page()->runJavaScript(
422 QStringLiteral("document.dispatchEvent(new KeyboardEvent('keydown', {'key': %1}));")
423 .arg(key_code));
424 };
425
426 QMessageBox::information(
427 this, tr("Exit"),
428 tr("To exit the web application, use the game provided controls to select exit, select the "
429 "'Exit Web Applet' option in the menu bar, or press the 'Enter' key."));
430
431 bool running_exit_check = false;
432 while (!finished) {
433 QApplication::processEvents();
434
435 if (!running_exit_check) {
436 web_browser_view.page()->runJavaScript(QStringLiteral("applet_done;"),
437 [&](const QVariant& res) {
438 running_exit_check = false;
439 if (res.toBool())
440 finished = true;
441 });
442 running_exit_check = true;
443 }
444
445 const auto input = npad.GetAndResetPressState();
446 for (std::size_t i = 0; i < Settings::NativeButton::NumButtons; ++i) {
447 if ((input & (1 << i)) != 0) {
448 LOG_DEBUG(Frontend, "firing input for button id={:02X}", i);
449 web_browser_view.page()->runJavaScript(
450 QStringLiteral("yuzu_key_callbacks[%1]();").arg(i));
451 }
452 }
453
454 if (input & 0x00888000) // RStick Down | LStick Down | DPad Down
455 fire_js_keypress(40); // Down Arrow Key
456 else if (input & 0x00444000) // RStick Right | LStick Right | DPad Right
457 fire_js_keypress(39); // Right Arrow Key
458 else if (input & 0x00222000) // RStick Up | LStick Up | DPad Up
459 fire_js_keypress(38); // Up Arrow Key
460 else if (input & 0x00111000) // RStick Left | LStick Left | DPad Left
461 fire_js_keypress(37); // Left Arrow Key
462 else if (input & 0x00000001) // A Button
463 fire_js_keypress(13); // Enter Key
464 }
465
466 web_browser_view.hide();
467 render_window->show();
468 render_window->setFocus();
469 ui.menubar->removeAction(exit_action);
470
471 // Needed to update render window focus/show and remove menubar action
472 QApplication::processEvents();
473 emit WebBrowserFinishedBrowsing();
474}
475
476#else
477
478void GMainWindow::WebBrowserOpenPage(std::string_view filename, std::string_view additional_args) {
479#ifndef __linux__
480 QMessageBox::warning(
481 this, tr("Web Applet"),
482 tr("This version of yuzu was built without QtWebEngine support, meaning that yuzu cannot "
483 "properly display the game manual or web page requested."),
484 QMessageBox::Ok, QMessageBox::Ok);
485#endif
486
487 LOG_INFO(Frontend,
488 "(STUBBED) called - Missing QtWebEngine dependency needed to open website page at "
489 "'{}' with arguments '{}'!",
490 filename, additional_args);
491
492 emit WebBrowserFinishedBrowsing();
493}
494
495#endif
496
497void GMainWindow::InitializeWidgets() { 344void GMainWindow::InitializeWidgets() {
498#ifdef YUZU_ENABLE_COMPATIBILITY_REPORTING 345#ifdef YUZU_ENABLE_COMPATIBILITY_REPORTING
499 ui.action_Report_Compatibility->setVisible(true); 346 ui.action_Report_Compatibility->setVisible(true);
@@ -993,7 +840,6 @@ bool GMainWindow::LoadROM(const QString& filename, std::size_t program_index) {
993 840
994 system.SetAppletFrontendSet({ 841 system.SetAppletFrontendSet({
995 std::make_unique<QtControllerSelector>(*this), // Controller Selector 842 std::make_unique<QtControllerSelector>(*this), // Controller Selector
996 nullptr, // E-Commerce
997 std::make_unique<QtErrorDisplay>(*this), // Error Display 843 std::make_unique<QtErrorDisplay>(*this), // Error Display
998 nullptr, // Parental Controls 844 nullptr, // Parental Controls
999 nullptr, // Photo Viewer 845 nullptr, // Photo Viewer
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 6242341d1..f311f2b5b 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -126,9 +126,6 @@ signals:
126 void SoftwareKeyboardFinishedText(std::optional<std::u16string> text); 126 void SoftwareKeyboardFinishedText(std::optional<std::u16string> text);
127 void SoftwareKeyboardFinishedCheckDialog(); 127 void SoftwareKeyboardFinishedCheckDialog();
128 128
129 void WebBrowserUnpackRomFS();
130 void WebBrowserFinishedBrowsing();
131
132public slots: 129public slots:
133 void OnLoadComplete(); 130 void OnLoadComplete();
134 void OnExecuteProgram(std::size_t program_index); 131 void OnExecuteProgram(std::size_t program_index);
@@ -138,7 +135,6 @@ public slots:
138 void ProfileSelectorSelectProfile(); 135 void ProfileSelectorSelectProfile();
139 void SoftwareKeyboardGetText(const Core::Frontend::SoftwareKeyboardParameters& parameters); 136 void SoftwareKeyboardGetText(const Core::Frontend::SoftwareKeyboardParameters& parameters);
140 void SoftwareKeyboardInvokeCheckDialog(std::u16string error_message); 137 void SoftwareKeyboardInvokeCheckDialog(std::u16string error_message);
141 void WebBrowserOpenPage(std::string_view filename, std::string_view arguments);
142 void OnAppFocusStateChanged(Qt::ApplicationState state); 138 void OnAppFocusStateChanged(Qt::ApplicationState state);
143 139
144private: 140private: