mirror of
https://github.com/servo/servo
synced 2026-04-25 17:15:48 +02:00
This method is the same as `DOMString::from` with a `String` argument and `From` and `Into` are preferred when writing modern Rust. Testing: This should not change behavior and is thus covered by existing tests. Signed-off-by: Martin Robinson <mrobinson@igalia.com>
207 lines
5.5 KiB
Rust
207 lines
5.5 KiB
Rust
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||
|
||
//! The `ByteString` struct.
|
||
use std::borrow::ToOwned;
|
||
use std::default::Default;
|
||
use std::hash::{Hash, Hasher};
|
||
use std::ops::Deref;
|
||
use std::str::FromStr;
|
||
use std::{fmt, ops, slice, str};
|
||
|
||
use js::gc::{HandleObject, HandleValue};
|
||
use js::rust::wrappers::ToJSON;
|
||
|
||
pub use crate::domstring::DOMString;
|
||
use crate::error::Error;
|
||
use crate::script_runtime::JSContext;
|
||
|
||
/// Encapsulates the IDL `ByteString` type.
|
||
#[derive(Clone, Debug, Default, Eq, JSTraceable, MallocSizeOf, PartialEq)]
|
||
pub struct ByteString(Vec<u8>);
|
||
|
||
impl ByteString {
|
||
/// Creates a new `ByteString`.
|
||
pub fn new(value: Vec<u8>) -> ByteString {
|
||
ByteString(value)
|
||
}
|
||
|
||
/// Returns `self` as a string, if it encodes valid UTF-8, and `None`
|
||
/// otherwise.
|
||
pub fn as_str(&self) -> Option<&str> {
|
||
str::from_utf8(&self.0).ok()
|
||
}
|
||
|
||
/// Returns the length.
|
||
pub fn len(&self) -> usize {
|
||
self.0.len()
|
||
}
|
||
|
||
/// Checks if the ByteString is empty.
|
||
pub fn is_empty(&self) -> bool {
|
||
self.0.is_empty()
|
||
}
|
||
|
||
/// Returns `self` with A–Z replaced by a–z.
|
||
pub fn to_lower(&self) -> ByteString {
|
||
ByteString::new(self.0.to_ascii_lowercase())
|
||
}
|
||
}
|
||
|
||
impl From<ByteString> for Vec<u8> {
|
||
fn from(byte_string: ByteString) -> Vec<u8> {
|
||
byte_string.0
|
||
}
|
||
}
|
||
|
||
impl Hash for ByteString {
|
||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||
self.0.hash(state);
|
||
}
|
||
}
|
||
|
||
impl FromStr for ByteString {
|
||
type Err = ();
|
||
fn from_str(s: &str) -> Result<ByteString, ()> {
|
||
Ok(ByteString::new(s.to_owned().into_bytes()))
|
||
}
|
||
}
|
||
|
||
impl ops::Deref for ByteString {
|
||
type Target = [u8];
|
||
fn deref(&self) -> &[u8] {
|
||
&self.0
|
||
}
|
||
}
|
||
|
||
/// A string that is constructed from a UCS-2 buffer by replacing invalid code
|
||
/// points with the replacement character.
|
||
#[derive(Clone, Debug, Default, Eq, Hash, MallocSizeOf, Ord, PartialEq, PartialOrd)]
|
||
pub struct USVString(pub String);
|
||
|
||
impl Deref for USVString {
|
||
type Target = str;
|
||
|
||
#[inline]
|
||
fn deref(&self) -> &str {
|
||
&self.0
|
||
}
|
||
}
|
||
|
||
impl AsRef<str> for USVString {
|
||
fn as_ref(&self) -> &str {
|
||
&self.0
|
||
}
|
||
}
|
||
|
||
impl fmt::Display for USVString {
|
||
#[inline]
|
||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||
fmt::Display::fmt(&self.0, f)
|
||
}
|
||
}
|
||
|
||
impl PartialEq<str> for USVString {
|
||
fn eq(&self, other: &str) -> bool {
|
||
self.0 == other
|
||
}
|
||
}
|
||
|
||
impl<'a> PartialEq<&'a str> for USVString {
|
||
fn eq(&self, other: &&'a str) -> bool {
|
||
self.0 == *other
|
||
}
|
||
}
|
||
|
||
impl From<String> for USVString {
|
||
fn from(contents: String) -> USVString {
|
||
USVString(contents)
|
||
}
|
||
}
|
||
|
||
impl From<USVString> for String {
|
||
fn from(value: USVString) -> Self {
|
||
value.0
|
||
}
|
||
}
|
||
|
||
impl From<USVString> for DOMString {
|
||
fn from(value: USVString) -> Self {
|
||
value.0.into()
|
||
}
|
||
}
|
||
|
||
/// Returns whether `s` is a `token`, as defined by
|
||
/// [RFC 2616](http://tools.ietf.org/html/rfc2616#page-17).
|
||
pub fn is_token(s: &[u8]) -> bool {
|
||
if s.is_empty() {
|
||
return false; // A token must be at least a single character
|
||
}
|
||
s.iter().all(|&x| {
|
||
// http://tools.ietf.org/html/rfc2616#section-2.2
|
||
match x {
|
||
0..=31 | 127 => false, // CTLs
|
||
40 | 41 | 60 | 62 | 64 | 44 | 59 | 58 | 92 | 34 | 47 | 91 | 93 | 63 | 61 | 123 |
|
||
125 | 32 => false, // separators
|
||
x if x > 127 => false, // non-CHARs
|
||
_ => true,
|
||
}
|
||
})
|
||
}
|
||
|
||
/// 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: JSContext, data: HandleValue) -> Result<DOMString, Error> {
|
||
#[repr(C)]
|
||
struct ToJSONCallbackData {
|
||
string: Option<String>,
|
||
}
|
||
|
||
let mut out_str = ToJSONCallbackData { string: None };
|
||
|
||
#[expect(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 = unsafe { slice::from_raw_parts(string, len as usize) };
|
||
unsafe { &mut *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(c"unable to serialize JSON".to_owned()))
|
||
}
|