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