Skip to main content

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