Преглед изворни кода

LibWeb: Convert callers of ImageCodecPlugin to the async API

The HTMLLinkElement caller is a bit hairy, so we shove an await() in
there temporarily. This is sure to cause fun times for anyone debugging
task/microtask execution order.
Andrew Kaster пре 1 година
родитељ
комит
651e78fedb

+ 3 - 1
Tests/LibWeb/Text/input/createBitmap.html

@@ -1,7 +1,7 @@
 <script src="include.js"></script>
 <canvas id="test" width="2" height="2" hidden="hidden"></canvas>
 <script>
-    test(() => {
+    asyncTest((done) => {
         // The blob is a JXL image for a 2x2 checkerboard
         // Made with the following tree on https://jxl-art.lucaversari.it/:
         // Width 2
@@ -40,6 +40,8 @@
                 println(`Invalid value in the bitmap`);
             else
                 println(`Bitmap is correctly loaded`);
+
+            done();
         })
     });
 </script>

+ 20 - 13
Userland/Libraries/LibWeb/HTML/HTMLLinkElement.cpp

@@ -463,21 +463,25 @@ void HTMLLinkElement::resource_did_load_favicon()
     document().check_favicon_after_loading_link_resource();
 }
 
-static bool decode_favicon(ReadonlyBytes favicon_data, URL::URL const& favicon_url, JS::GCPtr<Navigable> navigable)
+static NonnullRefPtr<Core::Promise<Web::Platform::DecodedImage>> decode_favicon(ReadonlyBytes favicon_data, URL::URL const& favicon_url, JS::GCPtr<Navigable> navigable)
 {
-    auto decoded_image = Platform::ImageCodecPlugin::the().decode_image(favicon_data);
-    if (!decoded_image.has_value() || decoded_image->frames.is_empty()) {
-        dbgln_if(IMAGE_DECODER_DEBUG, "Could not decode favicon {}", favicon_url);
-        return false;
-    }
+    auto on_failed_decode = [favicon_url]([[maybe_unused]] Error& error) {
+        dbgln_if(IMAGE_DECODER_DEBUG, "Failed to decode favicon {}: {}", favicon_url, error);
+    };
+
+    auto on_successful_decode = [navigable = JS::Handle(*navigable)](Web::Platform::DecodedImage& decoded_image) -> ErrorOr<void> {
+        auto favicon_bitmap = decoded_image.frames[0].bitmap;
+        dbgln_if(IMAGE_DECODER_DEBUG, "Decoded favicon, {}", favicon_bitmap->size());
 
-    auto favicon_bitmap = decoded_image->frames[0].bitmap;
-    dbgln_if(IMAGE_DECODER_DEBUG, "Decoded favicon, {}", favicon_bitmap->size());
+        if (navigable && navigable->is_traversable())
+            navigable->traversable_navigable()->page().client().page_did_change_favicon(*favicon_bitmap);
+
+        return {};
+    };
 
-    if (navigable && navigable->is_traversable())
-        navigable->traversable_navigable()->page().client().page_did_change_favicon(*favicon_bitmap);
+    auto promise = Platform::ImageCodecPlugin::the().decode_image(favicon_data, move(on_successful_decode), move(on_failed_decode));
 
-    return favicon_bitmap;
+    return promise;
 }
 
 bool HTMLLinkElement::load_favicon_and_use_if_window_is_active()
@@ -485,7 +489,10 @@ bool HTMLLinkElement::load_favicon_and_use_if_window_is_active()
     if (!has_loaded_icon())
         return false;
 
-    return decode_favicon(resource()->encoded_data(), resource()->url(), navigable());
+    // FIXME: Refactor the caller(s) to handle the async nature of image loading
+    auto promise = decode_favicon(resource()->encoded_data(), resource()->url(), navigable());
+    auto result = promise->await();
+    return !result.is_error();
 }
 
 // https://html.spec.whatwg.org/multipage/links.html#rel-icon:the-link-element-3
@@ -519,7 +526,7 @@ WebIDL::ExceptionOr<void> HTMLLinkElement::load_fallback_favicon_if_needed(JS::N
         auto global = JS::NonnullGCPtr { realm.global_object() };
 
         auto process_body = JS::create_heap_function(realm.heap(), [document, request](ByteBuffer body) {
-            decode_favicon(body, request->url(), document->navigable());
+            (void)decode_favicon(body, request->url(), document->navigable());
         });
         auto process_body_error = JS::create_heap_function(realm.heap(), [](JS::GCPtr<WebIDL::DOMException>) {
         });

+ 8 - 5
Userland/Libraries/LibWeb/HTML/HTMLVideoElement.cpp

@@ -182,11 +182,14 @@ WebIDL::ExceptionOr<void> HTMLVideoElement::determine_element_poster_frame(Optio
             m_fetch_controller = nullptr;
 
             // 6. If an image is thus obtained, the poster frame is that image. Otherwise, there is no poster frame.
-            auto image = Platform::ImageCodecPlugin::the().decode_image(image_data);
-            if (!image.has_value() || image->frames.is_empty())
-                return;
-
-            m_poster_frame = move(image.release_value().frames[0].bitmap);
+            (void)Platform::ImageCodecPlugin::the().decode_image(
+                image_data,
+                [strong_this = JS::Handle(*this)](Web::Platform::DecodedImage& image) -> ErrorOr<void> {
+                    if (!image.frames.is_empty())
+                        strong_this->m_poster_frame = move(image.frames[0].bitmap);
+                    return {};
+                },
+                [](auto&) {});
         });
 
         VERIFY(response->body());

+ 27 - 25
Userland/Libraries/LibWeb/HTML/SharedImageRequest.cpp

@@ -141,46 +141,48 @@ void SharedImageRequest::handle_successful_fetch(URL::URL const& url_string, Str
 
     bool const is_svg_image = mime_type == "image/svg+xml"sv || url_string.basename().ends_with(".svg"sv);
 
-    JS::GCPtr<DecodedImageData> image_data;
-
-    auto handle_failed_decode = [&] {
-        m_state = State::Failed;
-        for (auto& callback : m_callbacks) {
+    auto handle_failed_decode = [strong_this = JS::Handle(*this)](Error&) -> void {
+        strong_this->m_state = State::Failed;
+        for (auto& callback : strong_this->m_callbacks) {
             if (callback.on_fail)
                 callback.on_fail->function()();
         }
     };
 
+    auto handle_successful_decode = [](SharedImageRequest& self) {
+        self.m_state = State::Finished;
+        for (auto& callback : self.m_callbacks) {
+            if (callback.on_finish)
+                callback.on_finish->function()();
+        }
+        self.m_callbacks.clear();
+    };
+
     if (is_svg_image) {
         auto result = SVG::SVGDecodedImageData::create(m_document->realm(), m_page, url_string, data);
-        if (result.is_error())
-            return handle_failed_decode();
-
-        image_data = result.release_value();
-    } else {
-        auto result = Web::Platform::ImageCodecPlugin::the().decode_image(data.bytes());
-        if (!result.has_value())
-            return handle_failed_decode();
+        if (result.is_error()) {
+            handle_failed_decode(result.error());
+        } else {
+            m_image_data = result.release_value();
+            handle_successful_decode(*this);
+        }
+        return;
+    }
 
+    auto handle_successful_bitmap_decode = [strong_this = JS::Handle(*this), handle_successful_decode = move(handle_successful_decode)](Web::Platform::DecodedImage& result) -> ErrorOr<void> {
         Vector<AnimatedBitmapDecodedImageData::Frame> frames;
-        for (auto& frame : result.value().frames) {
+        for (auto& frame : result.frames) {
             frames.append(AnimatedBitmapDecodedImageData::Frame {
                 .bitmap = Gfx::ImmutableBitmap::create(*frame.bitmap),
                 .duration = static_cast<int>(frame.duration),
             });
         }
-        image_data = AnimatedBitmapDecodedImageData::create(m_document->realm(), move(frames), result.value().loop_count, result.value().is_animated).release_value_but_fixme_should_propagate_errors();
-    }
-
-    m_image_data = image_data;
-
-    m_state = State::Finished;
+        strong_this->m_image_data = AnimatedBitmapDecodedImageData::create(strong_this->m_document->realm(), move(frames), result.loop_count, result.is_animated).release_value_but_fixme_should_propagate_errors();
+        handle_successful_decode(*strong_this);
+        return {};
+    };
 
-    for (auto& callback : m_callbacks) {
-        if (callback.on_finish)
-            callback.on_finish->function()();
-    }
-    m_callbacks.clear();
+    (void)Web::Platform::ImageCodecPlugin::the().decode_image(data.bytes(), move(handle_successful_bitmap_decode), move(handle_failed_decode));
 }
 
 void SharedImageRequest::handle_failed_fetch()

+ 22 - 19
Userland/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.cpp

@@ -203,7 +203,7 @@ JS::NonnullGCPtr<JS::Promise> WindowOrWorkerGlobalScopeMixin::create_image_bitma
     image.visit(
         [&](JS::Handle<FileAPI::Blob>& blob) {
             // Run these step in parallel:
-            Platform::EventLoopPlugin::the().deferred_invoke([=, this]() {
+            Platform::EventLoopPlugin::the().deferred_invoke([=]() {
                 // 1. Let imageData be the result of reading image's data. If an error occurs during reading of the
                 // object, then reject p with an "InvalidStateError" DOMException and abort these steps.
                 // FIXME: I guess this is always fine for us as the data is already read.
@@ -213,24 +213,27 @@ JS::NonnullGCPtr<JS::Promise> WindowOrWorkerGlobalScopeMixin::create_image_bitma
                 // 2. Apply the image sniffing rules to determine the file format of imageData, with MIME type of
                 // image (as given by image's type attribute) giving the official type.
 
-                // 3. If imageData is not in a supported image file format (e.g., it's not an image at all), or if
-                // imageData is corrupted in some fatal way such that the image dimensions cannot be obtained
-                // (e.g., a vector graphic with no natural size), then reject p with an "InvalidStateError" DOMException
-                // and abort these steps.
-                auto result = Web::Platform::ImageCodecPlugin::the().decode_image(image_data);
-                if (!result.has_value()) {
-                    p->reject(WebIDL::InvalidStateError::create(this_impl().realm(), "image does not contain a supported image format"_string));
-                    return;
-                }
-
-                // 4. Set imageBitmap's bitmap data to imageData, cropped to the source rectangle with formatting.
-                // If this is an animated image, imageBitmap's bitmap data must only be taken from the default image
-                // of the animation (the one that the format defines is to be used when animation is not supported
-                // or is disabled), or, if there is no such image, the first frame of the animation.
-                image_bitmap->set_bitmap(result.value().frames.take_first().bitmap);
-
-                // 5. Resolve p with imageBitmap.
-                p->fulfill(image_bitmap);
+                auto on_failed_decode = [p = JS::Handle(*p)](Error&) {
+                    // 3. If imageData is not in a supported image file format (e.g., it's not an image at all), or if
+                    // imageData is corrupted in some fatal way such that the image dimensions cannot be obtained
+                    // (e.g., a vector graphic with no natural size), then reject p with an "InvalidStateError" DOMException
+                    // and abort these steps.
+                    p->reject(WebIDL::InvalidStateError::create(relevant_realm(*p), "image does not contain a supported image format"_string));
+                };
+
+                auto on_successful_decode = [image_bitmap = JS::Handle(*image_bitmap), p = JS::Handle(*p)](Web::Platform::DecodedImage& result) -> ErrorOr<void> {
+                    // 4. Set imageBitmap's bitmap data to imageData, cropped to the source rectangle with formatting.
+                    // If this is an animated image, imageBitmap's bitmap data must only be taken from the default image
+                    // of the animation (the one that the format defines is to be used when animation is not supported
+                    // or is disabled), or, if there is no such image, the first frame of the animation.
+                    image_bitmap->set_bitmap(result.frames.take_first().bitmap);
+
+                    // 5. Resolve p with imageBitmap.
+                    p->fulfill(image_bitmap);
+                    return {};
+                };
+
+                (void)Web::Platform::ImageCodecPlugin::the().decode_image(image_data, move(on_successful_decode), move(on_failed_decode));
             });
         },
         [&](auto&) {

+ 0 - 9
Userland/Libraries/LibWeb/Platform/ImageCodecPlugin.cpp

@@ -25,13 +25,4 @@ void ImageCodecPlugin::install(ImageCodecPlugin& plugin)
     s_the = &plugin;
 }
 
-Optional<DecodedImage> ImageCodecPlugin::decode_image(ReadonlyBytes encoded_data)
-{
-    auto promise = decode_image(encoded_data, {}, {});
-    auto result = promise->await();
-    if (result.is_error())
-        return {};
-    return result.release_value();
-}
-
 }

+ 0 - 1
Userland/Libraries/LibWeb/Platform/ImageCodecPlugin.h

@@ -33,7 +33,6 @@ public:
     virtual ~ImageCodecPlugin();
 
     virtual NonnullRefPtr<Core::Promise<DecodedImage>> decode_image(ReadonlyBytes, Function<ErrorOr<void>(DecodedImage&)> on_resolved, Function<void(Error&)> on_rejected) = 0;
-    Optional<DecodedImage> decode_image(ReadonlyBytes);
 };
 
 }