diff --git a/src/main/java/org/htmlunit/javascript/host/event/ProgressEvent.java b/src/main/java/org/htmlunit/javascript/host/event/ProgressEvent.java index 9caf0079d6..2f1a6747be 100644 --- a/src/main/java/org/htmlunit/javascript/host/event/ProgressEvent.java +++ b/src/main/java/org/htmlunit/javascript/host/event/ProgressEvent.java @@ -101,6 +101,22 @@ public ProgressEvent(final EventTarget target, final String type) { super(target, type); } + /** + * Creates a new event instance. + * @param target the event target + * @param type the event type + * @param lengthComputable whether the total size is known + * @param loaded the number of bytes loaded + * @param total the total number of bytes + */ + public ProgressEvent(final EventTarget target, final String type, + final boolean lengthComputable, final long loaded, final long total) { + super(target, type); + lengthComputable_ = lengthComputable; + loaded_ = loaded; + total_ = total; + } + /** * Returns the lengthComputable property from the event. * @return the lengthComputable property from the event. diff --git a/src/main/java/org/htmlunit/javascript/host/file/FileReader.java b/src/main/java/org/htmlunit/javascript/host/file/FileReader.java index 0189aaaeb3..a941e4b84f 100644 --- a/src/main/java/org/htmlunit/javascript/host/file/FileReader.java +++ b/src/main/java/org/htmlunit/javascript/host/file/FileReader.java @@ -36,6 +36,7 @@ import org.htmlunit.javascript.configuration.JsxSetter; import org.htmlunit.javascript.host.event.Event; import org.htmlunit.javascript.host.event.EventTarget; +import org.htmlunit.javascript.host.event.ProgressEvent; import org.htmlunit.protocol.data.DataURLConnection; import org.htmlunit.util.MimeType; import org.htmlunit.util.StringUtils; @@ -103,12 +104,20 @@ public Object getResult() { public void readAsDataURL(final Object object) throws IOException { readyState_ = LOADING; + if (!(object instanceof Blob blob)) { + throw JavaScriptEngine.typeError( + "FileReader.readAsDataURL: Argument 1 does not implement interface Blob."); + } + result_ = DataURLConnection.DATA_PREFIX; - final byte[] bytes = ((Blob) object).getBytes(); + final byte[] bytes = blob.getBytes(); + fireEvent(new ProgressEvent(this, Event.TYPE_LOAD_START, true, 0, bytes.length)); + fireEvent(new ProgressEvent(this, Event.TYPE_PROGRESS, true, bytes.length, bytes.length)); + final String value = new String(Base64.getEncoder().encode(bytes), StandardCharsets.US_ASCII); - String contentType = ((Blob) object).getType(); + String contentType = blob.getType(); if (StringUtils.isEmptyOrNull(contentType)) { contentType = MimeType.APPLICATION_OCTET_STREAM; } @@ -116,8 +125,8 @@ public void readAsDataURL(final Object object) throws IOException { result_ += contentType + ";base64," + value; readyState_ = DONE; - final Event event = new Event(this, Event.TYPE_LOAD); - fireEvent(event); + fireEvent(new ProgressEvent(this, Event.TYPE_LOAD, true, bytes.length, bytes.length)); + fireEvent(new ProgressEvent(this, Event.TYPE_LOAD_END, true, bytes.length, bytes.length)); } /** @@ -128,21 +137,25 @@ public void readAsDataURL(final Object object) throws IOException { public void readAsArrayBuffer(final Object object) { readyState_ = LOADING; - if (object instanceof Blob blob) { - final byte[] bytes = blob.getBytes(); + if (!(object instanceof Blob blob)) { + throw JavaScriptEngine.typeError( + "FileReader.readAsArrayBuffer: Argument 1 does not implement interface Blob."); + } - final NativeArrayBuffer buffer = new NativeArrayBuffer(bytes.length); - System.arraycopy(bytes, 0, buffer.getBuffer(), 0, bytes.length); - buffer.setParentScope(getParentScope()); - buffer.setPrototype(ScriptableObject.getClassPrototype(getWindow(), buffer.getClassName())); + final byte[] bytes = blob.getBytes(); + fireEvent(new ProgressEvent(this, Event.TYPE_LOAD_START, true, 0, bytes.length)); + fireEvent(new ProgressEvent(this, Event.TYPE_PROGRESS, true, bytes.length, bytes.length)); - result_ = buffer; - } + final NativeArrayBuffer buffer = new NativeArrayBuffer(bytes.length); + System.arraycopy(bytes, 0, buffer.getBuffer(), 0, bytes.length); + buffer.setParentScope(getParentScope()); + buffer.setPrototype(ScriptableObject.getClassPrototype(getWindow(), buffer.getClassName())); + result_ = buffer; readyState_ = DONE; - final Event event = new Event(this, Event.TYPE_LOAD); - fireEvent(event); + fireEvent(new ProgressEvent(this, Event.TYPE_LOAD, true, bytes.length, bytes.length)); + fireEvent(new ProgressEvent(this, Event.TYPE_LOAD_END, true, bytes.length, bytes.length)); } /** @@ -157,6 +170,11 @@ public void readAsArrayBuffer(final Object object) { public void readAsText(final Object object, final Object encoding) { readyState_ = LOADING; + if (!(object instanceof Blob blob)) { + throw JavaScriptEngine.typeError( + "FileReader.readAsText: Argument 1 does not implement interface Blob."); + } + Charset charset = StandardCharsets.UTF_8; if (encoding != null && !JavaScriptEngine.isUndefined(encoding)) { final String encAsString = JavaScriptEngine.toString(encoding); @@ -173,14 +191,51 @@ public void readAsText(final Object object, final Object encoding) { } } - if (object instanceof Blob blob) { - result_ = new String(blob.getBytes(), charset); - } + final byte[] bytes = blob.getBytes(); + fireEvent(new ProgressEvent(this, Event.TYPE_LOAD_START, true, 0, bytes.length)); + fireEvent(new ProgressEvent(this, Event.TYPE_PROGRESS, true, bytes.length, bytes.length)); + result_ = new String(bytes, charset); readyState_ = DONE; - final Event event = new Event(this, Event.TYPE_LOAD); - fireEvent(event); + fireEvent(new ProgressEvent(this, Event.TYPE_LOAD, true, bytes.length, bytes.length)); + fireEvent(new ProgressEvent(this, Event.TYPE_LOAD_END, true, bytes.length, bytes.length)); + } + + /** + * Returns the {@code onloadstart} event handler for this {@link FileReader}. + * @return the {@code onloadstart} event handler for this {@link FileReader} + */ + @JsxGetter + public Function getOnloadstart() { + return getEventHandler(Event.TYPE_LOAD_START); + } + + /** + * Sets the {@code onloadstart} event handler for this {@link FileReader}. + * @param onloadstart the {@code onloadstart} event handler for this {@link FileReader} + */ + @JsxSetter + public void setOnloadstart(final Object onloadstart) { + setEventHandler(Event.TYPE_LOAD_START, onloadstart); + } + + /** + * Returns the {@code onprogress} event handler for this {@link FileReader}. + * @return the {@code onprogress} event handler for this {@link FileReader} + */ + @JsxGetter + public Function getOnprogress() { + return getEventHandler(Event.TYPE_PROGRESS); + } + + /** + * Sets the {@code onprogress} event handler for this {@link FileReader}. + * @param onprogress the {@code onprogress} event handler for this {@link FileReader} + */ + @JsxSetter + public void setOnprogress(final Object onprogress) { + setEventHandler(Event.TYPE_PROGRESS, onprogress); } /** @@ -201,6 +256,24 @@ public void setOnload(final Object onload) { setEventHandler(Event.TYPE_LOAD, onload); } + /** + * Returns the {@code onloadend} event handler for this {@link FileReader}. + * @return the {@code onloadend} event handler for this {@link FileReader} + */ + @JsxGetter + public Function getOnloadend() { + return getEventHandler(Event.TYPE_LOAD_END); + } + + /** + * Sets the {@code onloadend} event handler for this {@link FileReader}. + * @param onloadend the {@code onloadend} event handler for this {@link FileReader} + */ + @JsxSetter + public void setOnloadend(final Object onloadend) { + setEventHandler(Event.TYPE_LOAD_END, onloadend); + } + /** * Returns the {@code onerror} event handler for this {@link FileReader}. * @return the {@code onerror} event handler for this {@link FileReader} diff --git a/src/test/java/org/htmlunit/javascript/host/file/FileReaderTest.java b/src/test/java/org/htmlunit/javascript/host/file/FileReaderTest.java index 6ca93008b4..d43a6a1b8d 100644 --- a/src/test/java/org/htmlunit/javascript/host/file/FileReaderTest.java +++ b/src/test/java/org/htmlunit/javascript/host/file/FileReaderTest.java @@ -798,6 +798,97 @@ public void readAsText_blob() throws Exception { loadPageVerifyTitle2(html); } + /** + * @throws Exception if the test fails + */ + @Test + @Alerts({"loadstart", "progress", "load", "loadend"}) + public void readAsTextEventLifecycle() throws Exception { + final String html = DOCTYPE_HTML + + "\n" + + "\n" + + " \n" + + "\n" + + "\n" + + "\n" + + ""; + + loadPageVerifyTitle2(html); + } + + /** + * @throws Exception if the test fails + */ + @Test + @Alerts({"true", "8", "8"}) + public void readAsDataURLProgressEventProperties() throws Exception { + final String html = DOCTYPE_HTML + + "\n" + + "\n" + + " \n" + + "\n" + + "\n" + + "\n" + + ""; + + loadPageVerifyTitle2(html); + } + + /** + * @throws Exception if the test fails + */ + @Test + @Alerts({"loadstart", "progress", "[object ArrayBuffer]", "8", "loadend"}) + public void readAsArrayBufferPropertyHandlers() throws Exception { + final String html = DOCTYPE_HTML + + "\n" + + "\n" + + " \n" + + "\n" + + "\n" + + "\n" + + ""; + + loadPageVerifyTitle2(html); + } + /** * @throws Exception if the test fails */