script/dom/
textencoder.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::ptr;
6
7use dom_struct::dom_struct;
8use js::gc::CustomAutoRooterGuard;
9use js::jsapi::JSObject;
10use js::rust::HandleObject;
11use js::typedarray;
12use js::typedarray::HeapUint8Array;
13use script_bindings::trace::RootedTraceableBox;
14
15use crate::dom::bindings::buffer_source::create_buffer_source;
16use crate::dom::bindings::codegen::Bindings::TextEncoderBinding::{
17    TextEncoderEncodeIntoResult, TextEncoderMethods,
18};
19use crate::dom::bindings::error::Fallible;
20use crate::dom::bindings::reflector::{Reflector, reflect_dom_object_with_proto};
21use crate::dom::bindings::root::DomRoot;
22use crate::dom::bindings::str::{DOMString, USVString};
23use crate::dom::globalscope::GlobalScope;
24use crate::script_runtime::{CanGc, JSContext};
25
26#[dom_struct]
27pub(crate) struct TextEncoder {
28    reflector_: Reflector,
29}
30
31impl TextEncoder {
32    fn new_inherited() -> TextEncoder {
33        TextEncoder {
34            reflector_: Reflector::new(),
35        }
36    }
37
38    fn new(
39        global: &GlobalScope,
40        proto: Option<HandleObject>,
41        can_gc: CanGc,
42    ) -> DomRoot<TextEncoder> {
43        reflect_dom_object_with_proto(
44            Box::new(TextEncoder::new_inherited()),
45            global,
46            proto,
47            can_gc,
48        )
49    }
50}
51
52impl TextEncoderMethods<crate::DomTypeHolder> for TextEncoder {
53    /// <https://encoding.spec.whatwg.org/#dom-textencoder>
54    fn Constructor(
55        global: &GlobalScope,
56        proto: Option<HandleObject>,
57        can_gc: CanGc,
58    ) -> Fallible<DomRoot<TextEncoder>> {
59        Ok(TextEncoder::new(global, proto, can_gc))
60    }
61
62    /// <https://encoding.spec.whatwg.org/#dom-textencoder-encoding>
63    fn Encoding(&self) -> DOMString {
64        DOMString::from("utf-8")
65    }
66
67    /// <https://encoding.spec.whatwg.org/#dom-textencoder-encode>
68    fn Encode(
69        &self,
70        cx: JSContext,
71        input: USVString,
72        can_gc: CanGc,
73    ) -> RootedTraceableBox<HeapUint8Array> {
74        let encoded = input.0.as_bytes();
75
76        rooted!(in(*cx) let mut js_object = ptr::null_mut::<JSObject>());
77        create_buffer_source(cx, encoded, js_object.handle_mut(), can_gc)
78            .expect("Converting input to uint8 array should never fail")
79    }
80
81    /// <https://encoding.spec.whatwg.org/#dom-textencoder-encodeinto>
82    #[expect(unsafe_code)]
83    fn EncodeInto(
84        &self,
85        source: USVString,
86        mut destination: CustomAutoRooterGuard<typedarray::Uint8Array>,
87    ) -> TextEncoderEncodeIntoResult {
88        let available = destination.len();
89
90        // Bail out if the destination has no space available.
91        if available == 0 {
92            return TextEncoderEncodeIntoResult {
93                read: Some(0),
94                written: Some(0),
95            };
96        }
97
98        let mut read = 0;
99        let mut written = 0;
100
101        let dest = unsafe { destination.as_mut_slice() };
102
103        // Step 3, 4, 5, 6
104        // Turn the source into a queue of scalar values.
105        // Iterate over the source values.
106        for result in source.0.chars() {
107            let utf8_len = result.len_utf8();
108            if available - written >= utf8_len {
109                // Step 6.4.1
110                // If destination’s byte length − written is greater than or equal to the number of bytes in result
111                read += if result > '\u{FFFF}' { 2 } else { 1 };
112
113                // Write the bytes in result into destination, with startingOffset set to written.
114                let target = &mut dest[written..written + utf8_len];
115                result.encode_utf8(target);
116
117                // Increment written by the number of bytes in result.
118                written += utf8_len;
119            } else {
120                // Step 6.4.2
121                // Bail out when destination buffer is full.
122                break;
123            }
124        }
125
126        TextEncoderEncodeIntoResult {
127            read: Some(read),
128            written: Some(written as _),
129        }
130    }
131}