From b731705fec2e2c9e5b5f0cef29acb7d7b7921887 Mon Sep 17 00:00:00 2001 From: Karol Kosek Date: Thu, 3 Aug 2023 01:29:31 +0200 Subject: [PATCH] LibWeb: Implement HTMLCanvasElement.toBlob() --- .../Libraries/LibWeb/HTML/EventLoop/Task.h | 3 ++ .../LibWeb/HTML/HTMLCanvasElement.cpp | 45 +++++++++++++++++++ .../Libraries/LibWeb/HTML/HTMLCanvasElement.h | 1 + .../LibWeb/HTML/HTMLCanvasElement.idl | 4 +- 4 files changed, 52 insertions(+), 1 deletion(-) diff --git a/Userland/Libraries/LibWeb/HTML/EventLoop/Task.h b/Userland/Libraries/LibWeb/HTML/EventLoop/Task.h index ba3e0f13f2..3ac67b256c 100644 --- a/Userland/Libraries/LibWeb/HTML/EventLoop/Task.h +++ b/Userland/Libraries/LibWeb/HTML/EventLoop/Task.h @@ -44,6 +44,9 @@ public: // Some elements, such as the HTMLMediaElement, must have a unique task source per instance. // Keep this field last, to serve as the base value of all unique task sources. UniqueTaskSourceStart, + + // https://html.spec.whatwg.org/multipage/canvas.html#canvas-blob-serialisation-task-source + CanvasBlobSerializationTask, }; static NonnullOwnPtr create(Source source, DOM::Document const* document, JS::SafeFunction steps) diff --git a/Userland/Libraries/LibWeb/HTML/HTMLCanvasElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLCanvasElement.cpp index dab91433f2..d5277ccff8 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLCanvasElement.cpp +++ b/Userland/Libraries/LibWeb/HTML/HTMLCanvasElement.cpp @@ -8,11 +8,15 @@ #include #include #include +#include #include #include #include #include +#include #include +#include +#include namespace Web::HTML { @@ -219,6 +223,47 @@ DeprecatedString HTMLCanvasElement::to_data_url(DeprecatedString const& type, Op return AK::URL::create_with_data(file.value().mime_type, base64_encoded_or_error.release_value(), true).to_deprecated_string(); } +// https://html.spec.whatwg.org/multipage/canvas.html#dom-canvas-toblob +WebIDL::ExceptionOr HTMLCanvasElement::to_blob(JS::NonnullGCPtr callback, DeprecatedString const& type, Optional quality) +{ + // FIXME: 1. If this canvas element's bitmap's origin-clean flag is set to false, then throw a "SecurityError" DOMException. + + // 2. Let result be null. + RefPtr bitmap_result; + + // 3. If this canvas element's bitmap has pixels (i.e., neither its horizontal dimension nor its vertical dimension is zero), + // then set result to a copy of this canvas element's bitmap. + if (m_bitmap) + bitmap_result = TRY_OR_THROW_OOM(vm(), m_bitmap->clone()); + + // 4. Run these steps in parallel: + Platform::EventLoopPlugin::the().deferred_invoke([this, callback, bitmap_result, type, quality] { + // 1. If result is non-null, then set result to a serialization of result as a file with type and quality if given. + Optional file_result; + if (bitmap_result) { + if (auto result = serialize_bitmap(*bitmap_result, type, move(quality)); !result.is_error()) + file_result = result.release_value(); + } + + // 2. Queue an element task on the canvas blob serialization task source given the canvas element to run these steps: + queue_an_element_task(Task::Source::CanvasBlobSerializationTask, [this, callback, file_result = move(file_result)] { + auto maybe_error = Bindings::throw_dom_exception_if_needed(vm(), [&]() -> WebIDL::ExceptionOr { + // 1. If result is non-null, then set result to a new Blob object, created in the relevant realm of this canvas element, representing result. [FILEAPI] + JS::GCPtr blob_result; + if (file_result.has_value()) + blob_result = TRY(FileAPI::Blob::create(realm(), file_result->buffer, TRY_OR_THROW_OOM(vm(), String::from_utf8(file_result->mime_type)))); + + // 2. Invoke callback with « result ». + TRY(WebIDL::invoke_callback(*callback, {}, move(blob_result))); + return {}; + }); + if (maybe_error.is_throw_completion()) + report_exception(maybe_error.throw_completion(), realm()); + }); + }); + return {}; +} + void HTMLCanvasElement::present() { m_context.visit( diff --git a/Userland/Libraries/LibWeb/HTML/HTMLCanvasElement.h b/Userland/Libraries/LibWeb/HTML/HTMLCanvasElement.h index 881ddd777d..63906ebc89 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLCanvasElement.h +++ b/Userland/Libraries/LibWeb/HTML/HTMLCanvasElement.h @@ -34,6 +34,7 @@ public: WebIDL::ExceptionOr set_height(unsigned); DeprecatedString to_data_url(DeprecatedString const& type, Optional quality) const; + WebIDL::ExceptionOr to_blob(JS::NonnullGCPtr callback, DeprecatedString const& type, Optional quality); void present(); diff --git a/Userland/Libraries/LibWeb/HTML/HTMLCanvasElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLCanvasElement.idl index 90df720626..6ea01e08fd 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLCanvasElement.idl +++ b/Userland/Libraries/LibWeb/HTML/HTMLCanvasElement.idl @@ -15,5 +15,7 @@ interface HTMLCanvasElement : HTMLElement { [CEReactions] attribute unsigned long height; USVString toDataURL(optional DOMString type = "image/png", optional double quality); - + undefined toBlob(BlobCallback _callback, optional DOMString type = "image/png", optional double quality); }; + +callback BlobCallback = undefined (Blob? blob);