diff options
Diffstat (limited to 'components/script_bindings/str.rs')
-rw-r--r-- | components/script_bindings/str.rs | 65 |
1 files changed, 64 insertions, 1 deletions
diff --git a/components/script_bindings/str.rs b/components/script_bindings/str.rs index 09d48512f3e..0ef6e0c528a 100644 --- a/components/script_bindings/str.rs +++ b/components/script_bindings/str.rs @@ -10,14 +10,19 @@ use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; use std::str::FromStr; use std::sync::LazyLock; -use std::{fmt, ops, str}; +use std::{fmt, ops, slice, str}; use cssparser::CowRcStr; use html5ever::{LocalName, Namespace}; +use js::rust::wrappers::ToJSON; +use js::rust::{HandleObject, HandleValue}; use num_traits::Zero; use regex::Regex; use stylo_atoms::Atom; +use crate::error::Error; +use crate::script_runtime::JSContext as SafeJSContext; + /// Encapsulates the IDL `ByteString` type. #[derive(Clone, Debug, Default, Eq, JSTraceable, MallocSizeOf, PartialEq)] pub struct ByteString(Vec<u8>); @@ -293,6 +298,64 @@ impl DOMString { } } +/// Because this converts to a DOMString it becomes UTF-8 encoded which is closer to +/// the spec definition of <https://infra.spec.whatwg.org/#serialize-a-javascript-value-to-json-bytes> +/// but we generally do not operate on anything that is truly a WTF-16 string. +/// +/// <https://infra.spec.whatwg.org/#serialize-a-javascript-value-to-a-json-string> +pub fn serialize_jsval_to_json_utf8( + cx: SafeJSContext, + data: HandleValue, +) -> Result<DOMString, Error> { + #[repr(C)] + struct ToJSONCallbackData { + string: Option<String>, + } + + let mut out_str = ToJSONCallbackData { string: None }; + + #[allow(unsafe_code)] + unsafe extern "C" fn write_callback( + string: *const u16, + len: u32, + data: *mut std::ffi::c_void, + ) -> bool { + let data = data as *mut ToJSONCallbackData; + let string_chars = slice::from_raw_parts(string, len as usize); + (*data) + .string + .get_or_insert_with(Default::default) + .push_str(&String::from_utf16_lossy(string_chars)); + true + } + + // 1. Let result be ? Call(%JSON.stringify%, undefined, « value »). + unsafe { + let stringify_result = ToJSON( + *cx, + data, + HandleObject::null(), + HandleValue::null(), + Some(write_callback), + &mut out_str as *mut ToJSONCallbackData as *mut _, + ); + // Note: ToJSON returns false when a JS error is thrown, so we need to return + // JSFailed to propagate the raised exception + if !stringify_result { + return Err(Error::JSFailed); + } + } + + // 2. If result is undefined, then throw a TypeError. + // Note: ToJSON will not call the callback if the data cannot be serialized. + // 3. Assert: result is a string. + // 4. Return result. + out_str + .string + .map(Into::into) + .ok_or_else(|| Error::Type("unable to serialize JSON".to_owned())) +} + impl Borrow<str> for DOMString { #[inline] fn borrow(&self) -> &str { |