1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.htmlunit.javascript.host;
16
17 import java.nio.charset.Charset;
18 import java.util.Arrays;
19 import java.util.Locale;
20
21 import org.htmlunit.corejs.javascript.typedarrays.NativeArrayBuffer;
22 import org.htmlunit.corejs.javascript.typedarrays.NativeArrayBufferView;
23 import org.htmlunit.cyberneko.xerces.util.StandardEncodingTranslator;
24 import org.htmlunit.javascript.HtmlUnitScriptable;
25 import org.htmlunit.javascript.JavaScriptEngine;
26 import org.htmlunit.javascript.configuration.JsxClass;
27 import org.htmlunit.javascript.configuration.JsxConstructor;
28 import org.htmlunit.javascript.configuration.JsxFunction;
29 import org.htmlunit.javascript.configuration.JsxGetter;
30 import org.htmlunit.util.XUserDefinedCharset;
31
32
33
34
35
36
37
38
39
40 @JsxClass
41 public class TextDecoder extends HtmlUnitScriptable {
42 private String whatwgEncoding_ = "utf-8";
43
44
45
46
47
48
49 @JsxConstructor
50 public void jsConstructor(final Object encodingLabel) {
51 if (JavaScriptEngine.isUndefined(encodingLabel)) {
52 return;
53 }
54
55 String enc = JavaScriptEngine.toString(encodingLabel);
56 enc = enc.trim().toLowerCase(Locale.ROOT);
57 final String whatwgEncoding = StandardEncodingTranslator.ENCODING_FROM_LABEL.get(enc);
58
59 if (whatwgEncoding == null
60 || StandardEncodingTranslator.REPLACEMENT.equalsIgnoreCase(whatwgEncoding)) {
61 throw JavaScriptEngine.rangeError("Failed to construct 'TextDecoder': The encoding label provided ('"
62 + encodingLabel + "') is invalid.");
63 }
64
65 whatwgEncoding_ = whatwgEncoding;
66 }
67
68
69
70
71
72
73 @JsxGetter
74 public String getEncoding() {
75 return whatwgEncoding_;
76 }
77
78
79
80
81
82
83
84 @JsxFunction
85 public String decode(final Object buffer) {
86 if (JavaScriptEngine.isUndefined(buffer)) {
87 return "";
88 }
89
90 if (buffer instanceof NativeArrayBuffer arrayBuffer) {
91 return new String(arrayBuffer.getBuffer(), getEncoding(whatwgEncoding_));
92 }
93
94 if (buffer instanceof NativeArrayBufferView arrayBufferView) {
95 final NativeArrayBuffer arrayBuffer = arrayBufferView.getBuffer();
96 if (arrayBuffer != null) {
97 final int byteLength = arrayBufferView.getByteLength();
98 final int byteOffset = arrayBufferView.getByteOffset();
99 final byte[] backedBytes = arrayBuffer.getBuffer();
100 final byte[] bytes = Arrays.copyOfRange(backedBytes, byteOffset, byteOffset + byteLength);
101 return new String(bytes, getEncoding(whatwgEncoding_));
102 }
103 }
104
105 throw JavaScriptEngine.typeError("Argument 1 of TextDecoder.decode could not be"
106 + " converted to any of: ArrayBufferView, ArrayBuffer.");
107 }
108
109 private Charset getEncoding(final String encodingLabel) {
110 if (XUserDefinedCharset.NAME.equalsIgnoreCase(encodingLabel)) {
111 return XUserDefinedCharset.INSTANCE;
112 }
113
114 final String ianaEncoding = StandardEncodingTranslator
115 .ENCODING_TO_IANA_ENCODING.getOrDefault(encodingLabel, encodingLabel);
116
117 final String javaEncoding = StandardEncodingTranslator
118 .IANA_TO_JAVA_ENCODINGS.getOrDefault(ianaEncoding, ianaEncoding);
119
120 return Charset.forName(javaEncoding);
121 }
122 }