Files
servo/components/script_bindings/str.rs
Martin Robinson e725fdbfdf script_bindings: Eliminate DOMString::from_string (#43089)
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>
2026-03-08 11:46:42 +00:00

207 lines
5.5 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* 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 AZ replaced by az.
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()))
}