Skip to main content

skyline_web/
session.rs

1use nnsdk::web::offlinewebsession::*;
2use std::ffi::CString;
3
4use crate::PageResult;
5
6pub use nnsdk::web::{
7    offlinewebsession::OfflineWebSession, OfflineBackgroundKind as Background,
8    OfflineBootDisplayKind as BootDisplay, WebSessionBootMode as Visibility,
9};
10
11extern "C" {
12    #[link_name = "\u{1}_ZN2nn3web17OfflineWebSession11RequestExitEv"]
13    pub fn request_exit(session: &OfflineWebSession);
14}
15
16pub struct WebSession(pub(crate) OfflineWebSession);
17
18impl WebSession {
19    /// Sends a message, blocking until it succeeds
20    pub fn send(&self, message: &str) {
21        let len = message.len() + 1;
22        let message = CString::new(message).unwrap();
23
24        while unsafe { !TrySendContentMessage(&self.0, message.as_ptr() as _, len) } {}
25    }
26
27    /// Attempts to send a message, returning true if it succeeds
28    pub fn try_send(&self, message: &str) -> bool {
29        let len = message.len() + 1;
30        let message = CString::new(message).unwrap();
31
32        unsafe { TrySendContentMessage(&self.0, message.as_ptr() as _, len) }
33    }
34
35    /// Blocks until a message is recieved
36    ///
37    /// Up to 4 KiB in size, for larger or more efficient sizes use `recv_max`
38    pub fn recv(&self) -> String {
39        self.recv_max(0x10000)
40    }
41
42    /// Blocks until a message is recieved, up to `max_size` bytes
43    pub fn recv_max(&self, max_size: usize) -> String {
44        let mut buffer = vec![0u8; max_size];
45
46        loop {
47            if let Some(size) = self.inner_recv(&mut buffer) {
48                if size != 0 {
49                    buffer.truncate(size - 1);
50                    buffer.shrink_to_fit();
51                    let message = String::from_utf8(buffer).map(|string| string).unwrap();
52
53                    break message;
54                }
55            }
56        }
57    }
58
59    /// Attempts to recieve a message without blocking
60    ///
61    /// Up to 4 KiB in size, for larger or more efficient sizes use `try_recv_max`
62    pub fn try_recv(&self) -> Option<String> {
63        self.try_recv_max(0x10000)
64    }
65
66    /// Attempts to recieve a message without blocking, up to `max_size` bytes
67    pub fn try_recv_max(&self, max_size: usize) -> Option<String> {
68        let mut buffer = vec![0u8; max_size];
69
70        self.inner_recv(&mut buffer)
71            .map(|size| {
72                if size != 0 {
73                    buffer.truncate(size - 1);
74                    buffer.shrink_to_fit();
75                    String::from_utf8(buffer).map(|string| string).ok()
76                } else {
77                    None
78                }
79            })
80            .flatten()
81    }
82
83    fn inner_recv<T: AsMut<[u8]>>(&self, buffer: &mut T) -> Option<usize> {
84        let buffer = buffer.as_mut();
85        let mut out_size = 0;
86
87        unsafe {
88            if skyline::nn::web::offlinewebsession::TryReceiveContentMessage(
89                &self.0,
90                &mut out_size,
91                buffer.as_mut_ptr(),
92                buffer.len(),
93            ) != false
94            {
95                Some(out_size)
96            } else {
97                None
98            }
99        }
100    }
101
102    /// Show a previously hidden web session
103    pub fn show(&self) {
104        unsafe { Appear(&self.0) };
105    }
106
107    /// Wait until the page has been exited
108    pub fn wait_for_exit(&self) -> PageResult {
109        let return_value = PageResult::new();
110        unsafe { WaitForExit(&self.0, return_value.as_ref()) };
111        return_value
112    }
113
114    // Exit the browser forcefully
115    pub fn exit(&self) {
116        unsafe { request_exit(&self.0); }
117    }
118}
119
120
121
122#[cfg(feature = "json")]
123use serde::{de::DeserializeOwned, Serialize};
124
125#[cfg(feature = "json")]
126impl WebSession {
127    /// Send a type as a JSON value, blocking until it sends
128    pub fn send_json<T: Serialize>(&self, obj: &T) {
129        self.send(&serde_json::to_string(obj).unwrap())
130    }
131
132    /// Attempt to send a type as a JSON value, returning false if it doesn't succeed
133    pub fn try_send_json<T: Serialize>(&self, obj: &T) -> bool {
134        self.try_send(&serde_json::to_string(obj).unwrap())
135    }
136
137    /// Receive a given type as a JSON message, blocking until one is ready
138    pub fn recv_json<T: DeserializeOwned>(&self) -> serde_json::Result<T> {
139        serde_json::from_str(&self.recv())
140    }
141
142    /// Receive a given type as a JSON message, returning None if a message is not ready
143    pub fn try_recv_json<T: DeserializeOwned>(&self) -> Option<serde_json::Result<T>> {
144        self.try_recv().map(|msg| serde_json::from_str(&msg))
145    }
146
147    /// Receive a given type as a JSON message, blocking until one is ready, setting a custom max
148    /// payload size.
149    pub fn recv_json_max<T: DeserializeOwned>(&self, max_size: usize) -> serde_json::Result<T> {
150        serde_json::from_str(&self.recv_max(max_size))
151    }
152
153    /// Receive a given type as a JSON message, returning None if a message is not ready, with a
154    /// max message size of `max_size`
155    pub fn try_recv_json_max<T: DeserializeOwned>(
156        &self,
157        max_size: usize,
158    ) -> Option<serde_json::Result<T>> {
159        self.try_recv_max(max_size)
160            .map(|msg| serde_json::from_str(&msg))
161    }
162}