1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.htmlunit.javascript.host.file;
16
17 import java.io.IOException;
18 import java.nio.charset.Charset;
19 import java.nio.charset.StandardCharsets;
20 import java.nio.charset.UnsupportedCharsetException;
21 import java.util.Base64;
22 import java.util.Locale;
23
24 import org.apache.commons.io.Charsets;
25 import org.apache.commons.logging.Log;
26 import org.apache.commons.logging.LogFactory;
27 import org.htmlunit.corejs.javascript.Function;
28 import org.htmlunit.corejs.javascript.ScriptableObject;
29 import org.htmlunit.corejs.javascript.typedarrays.NativeArrayBuffer;
30 import org.htmlunit.javascript.JavaScriptEngine;
31 import org.htmlunit.javascript.configuration.JsxClass;
32 import org.htmlunit.javascript.configuration.JsxConstant;
33 import org.htmlunit.javascript.configuration.JsxConstructor;
34 import org.htmlunit.javascript.configuration.JsxFunction;
35 import org.htmlunit.javascript.configuration.JsxGetter;
36 import org.htmlunit.javascript.configuration.JsxSetter;
37 import org.htmlunit.javascript.host.event.Event;
38 import org.htmlunit.javascript.host.event.EventTarget;
39 import org.htmlunit.javascript.host.event.ProgressEvent;
40 import org.htmlunit.protocol.data.DataURLConnection;
41 import org.htmlunit.util.MimeType;
42 import org.htmlunit.util.StringUtils;
43
44
45
46
47
48
49
50
51 @JsxClass
52 public class FileReader extends EventTarget {
53
54 private static final Log LOG = LogFactory.getLog(FileReader.class);
55
56
57 @JsxConstant
58 public static final int EMPTY = 0;
59
60
61 @JsxConstant
62 public static final int LOADING = 1;
63
64
65 @JsxConstant
66 public static final int DONE = 2;
67
68 private int readyState_ = EMPTY;
69 private Object result_;
70
71
72
73
74 @Override
75 @JsxConstructor
76 public void jsConstructor() {
77 super.jsConstructor();
78 }
79
80
81
82
83
84
85 @JsxGetter
86 public int getReadyState() {
87 return readyState_;
88 }
89
90
91
92
93
94 @JsxGetter
95 public Object getResult() {
96 return result_;
97 }
98
99
100
101
102
103
104 @JsxFunction
105 public void readAsDataURL(final Object object) throws IOException {
106 readyState_ = LOADING;
107
108 if (!(object instanceof Blob blob)) {
109 throw JavaScriptEngine.typeError(
110 "FileReader.readAsDataURL: Argument 1 does not implement interface Blob.");
111 }
112
113 result_ = DataURLConnection.DATA_PREFIX;
114
115 final byte[] bytes = blob.getBytes();
116 fireEvent(new ProgressEvent(this, Event.TYPE_LOAD_START, true, 0, bytes.length));
117 fireEvent(new ProgressEvent(this, Event.TYPE_PROGRESS, true, bytes.length, bytes.length));
118
119 final String value = new String(Base64.getEncoder().encode(bytes), StandardCharsets.US_ASCII);
120
121 String contentType = blob.getType();
122 if (StringUtils.isEmptyOrNull(contentType)) {
123 contentType = MimeType.APPLICATION_OCTET_STREAM;
124 }
125
126 result_ += contentType + ";base64," + value;
127 readyState_ = DONE;
128
129 fireEvent(new ProgressEvent(this, Event.TYPE_LOAD, true, bytes.length, bytes.length));
130 fireEvent(new ProgressEvent(this, Event.TYPE_LOAD_END, true, bytes.length, bytes.length));
131 }
132
133
134
135
136
137 @JsxFunction
138 public void readAsArrayBuffer(final Object object) {
139 readyState_ = LOADING;
140
141 if (!(object instanceof Blob blob)) {
142 throw JavaScriptEngine.typeError(
143 "FileReader.readAsArrayBuffer: Argument 1 does not implement interface Blob.");
144 }
145
146 final byte[] bytes = blob.getBytes();
147 fireEvent(new ProgressEvent(this, Event.TYPE_LOAD_START, true, 0, bytes.length));
148 fireEvent(new ProgressEvent(this, Event.TYPE_PROGRESS, true, bytes.length, bytes.length));
149
150 final NativeArrayBuffer buffer = new NativeArrayBuffer(bytes.length);
151 System.arraycopy(bytes, 0, buffer.getBuffer(), 0, bytes.length);
152 buffer.setParentScope(getParentScope());
153 buffer.setPrototype(ScriptableObject.getClassPrototype(getParentScope(), buffer.getClassName()));
154
155 result_ = buffer;
156 readyState_ = DONE;
157
158 fireEvent(new ProgressEvent(this, Event.TYPE_LOAD, true, bytes.length, bytes.length));
159 fireEvent(new ProgressEvent(this, Event.TYPE_LOAD_END, true, bytes.length, bytes.length));
160 }
161
162
163
164
165
166
167
168
169
170 @JsxFunction
171 public void readAsText(final Object object, final Object encoding) {
172 readyState_ = LOADING;
173
174 if (!(object instanceof Blob blob)) {
175 throw JavaScriptEngine.typeError(
176 "FileReader.readAsText: Argument 1 does not implement interface Blob.");
177 }
178
179 Charset charset = StandardCharsets.UTF_8;
180 if (encoding != null && !JavaScriptEngine.isUndefined(encoding)) {
181 final String encAsString = JavaScriptEngine.toString(encoding);
182 if (StringUtils.isNotBlank(encAsString)) {
183 try {
184 charset = Charsets.toCharset(encAsString.trim().toLowerCase(Locale.ROOT));
185 }
186 catch (final UnsupportedCharsetException e) {
187 if (LOG.isWarnEnabled()) {
188 LOG.warn("FileReader readAsText was called with an unsupported encoding '"
189 + encoding + "'. Using UTF-8 instead.");
190 }
191 }
192 }
193 }
194
195 final byte[] bytes = blob.getBytes();
196 fireEvent(new ProgressEvent(this, Event.TYPE_LOAD_START, true, 0, bytes.length));
197 fireEvent(new ProgressEvent(this, Event.TYPE_PROGRESS, true, bytes.length, bytes.length));
198
199 result_ = new String(bytes, charset);
200 readyState_ = DONE;
201
202 fireEvent(new ProgressEvent(this, Event.TYPE_LOAD, true, bytes.length, bytes.length));
203 fireEvent(new ProgressEvent(this, Event.TYPE_LOAD_END, true, bytes.length, bytes.length));
204 }
205
206
207
208
209
210 @JsxGetter
211 public Function getOnloadstart() {
212 return getEventHandler(Event.TYPE_LOAD_START);
213 }
214
215
216
217
218
219 @JsxSetter
220 public void setOnloadstart(final Object onloadstart) {
221 setEventHandler(Event.TYPE_LOAD_START, onloadstart);
222 }
223
224
225
226
227
228 @JsxGetter
229 public Function getOnprogress() {
230 return getEventHandler(Event.TYPE_PROGRESS);
231 }
232
233
234
235
236
237 @JsxSetter
238 public void setOnprogress(final Object onprogress) {
239 setEventHandler(Event.TYPE_PROGRESS, onprogress);
240 }
241
242
243
244
245
246 @JsxGetter
247 public Function getOnload() {
248 return getEventHandler(Event.TYPE_LOAD);
249 }
250
251
252
253
254
255 @JsxSetter
256 public void setOnload(final Object onload) {
257 setEventHandler(Event.TYPE_LOAD, onload);
258 }
259
260
261
262
263
264 @JsxGetter
265 public Function getOnloadend() {
266 return getEventHandler(Event.TYPE_LOAD_END);
267 }
268
269
270
271
272
273 @JsxSetter
274 public void setOnloadend(final Object onloadend) {
275 setEventHandler(Event.TYPE_LOAD_END, onloadend);
276 }
277
278
279
280
281
282 @JsxGetter
283 public Function getOnerror() {
284 return getEventHandler(Event.TYPE_ERROR);
285 }
286
287
288
289
290
291 @JsxSetter
292 public void setOnerror(final Object onerror) {
293 setEventHandler(Event.TYPE_ERROR, onerror);
294 }
295 }