diff options
Diffstat (limited to 'Userland')
-rw-r--r-- | Userland/Libraries/LibWeb/HTML/HTMLImageElement.cpp | 44 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/SVG/SVGDecodedImageData.cpp | 100 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/SVG/SVGDecodedImageData.h | 14 |
3 files changed, 138 insertions, 20 deletions
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLImageElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLImageElement.cpp index 56537cd7ed..02b1b5b8da 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLImageElement.cpp +++ b/Userland/Libraries/LibWeb/HTML/HTMLImageElement.cpp @@ -29,6 +29,7 @@ #include <LibWeb/Loader/ResourceLoader.h> #include <LibWeb/Painting/PaintableBox.h> #include <LibWeb/Platform/ImageCodecPlugin.h> +#include <LibWeb/SVG/SVGDecodedImageData.h> namespace Web::HTML { @@ -545,21 +546,38 @@ void HTMLImageElement::handle_successful_fetch(AK::URL const& url_string, ImageR m_load_event_delayer.clear(); }; - auto result = Web::Platform::ImageCodecPlugin::the().decode_image(data.bytes()); - if (!result.has_value()) { - dispatch_event(DOM::Event::create(realm(), HTML::EventNames::error).release_value_but_fixme_should_propagate_errors()); - return; - } + // FIXME: Look at the MIME type instead! + bool is_svg_image = url_string.basename().ends_with(".svg"sv); - Vector<AnimatedBitmapDecodedImageData::Frame> frames; - for (auto& frame : result.value().frames) { - frames.append(AnimatedBitmapDecodedImageData::Frame { - .bitmap = frame.bitmap, - .duration = static_cast<int>(frame.duration), - }); + RefPtr<DecodedImageData> image_data; + + if (is_svg_image) { + VERIFY(document().page()); + auto result = SVG::SVGDecodedImageData::create(*document().page(), url_string, data); + if (result.is_error()) { + dbgln("Failed to decode SVG image: {}", result.error()); + dispatch_event(DOM::Event::create(realm(), HTML::EventNames::error).release_value_but_fixme_should_propagate_errors()); + return; + } + + image_data = result.release_value(); + } else { + auto result = Web::Platform::ImageCodecPlugin::the().decode_image(data.bytes()); + if (!result.has_value()) { + dispatch_event(DOM::Event::create(realm(), HTML::EventNames::error).release_value_but_fixme_should_propagate_errors()); + return; + } + + Vector<AnimatedBitmapDecodedImageData::Frame> frames; + for (auto& frame : result.value().frames) { + frames.append(AnimatedBitmapDecodedImageData::Frame { + .bitmap = frame.bitmap, + .duration = static_cast<int>(frame.duration), + }); + } + image_data = AnimatedBitmapDecodedImageData::create(move(frames), result.value().loop_count, result.value().is_animated).release_value_but_fixme_should_propagate_errors(); } - auto image_data = AnimatedBitmapDecodedImageData::create(move(frames), result.value().loop_count, result.value().is_animated).release_value_but_fixme_should_propagate_errors(); image_request.set_image_data(image_data); ListOfAvailableImages::Key key; @@ -580,7 +598,7 @@ void HTMLImageElement::handle_successful_fetch(AK::URL const& url_string, ImageR image_request.set_state(ImageRequest::State::CompletelyAvailable); // 3. Add the image to the list of available images using the key key, with the ignore higher-layer caching flag set. - document().list_of_available_images().add(key, image_data, true).release_value_but_fixme_should_propagate_errors(); + document().list_of_available_images().add(key, *image_data, true).release_value_but_fixme_should_propagate_errors(); // 4. Fire an event named load at the img element. dispatch_event(DOM::Event::create(realm(), HTML::EventNames::load).release_value_but_fixme_should_propagate_errors()); diff --git a/Userland/Libraries/LibWeb/SVG/SVGDecodedImageData.cpp b/Userland/Libraries/LibWeb/SVG/SVGDecodedImageData.cpp index bda9e99561..f2e0074e78 100644 --- a/Userland/Libraries/LibWeb/SVG/SVGDecodedImageData.cpp +++ b/Userland/Libraries/LibWeb/SVG/SVGDecodedImageData.cpp @@ -5,24 +5,114 @@ */ #include <LibGfx/Bitmap.h> +#include <LibWeb/DOM/Document.h> +#include <LibWeb/Dump.h> +#include <LibWeb/Fetch/Infrastructure/HTTP/Responses.h> +#include <LibWeb/HTML/BrowsingContext.h> +#include <LibWeb/HTML/NavigationParams.h> +#include <LibWeb/HTML/Parser/HTMLParser.h> +#include <LibWeb/Layout/Viewport.h> +#include <LibWeb/Painting/PaintContext.h> #include <LibWeb/SVG/SVGDecodedImageData.h> +#include <LibWeb/SVG/SVGSVGElement.h> namespace Web::SVG { -ErrorOr<NonnullRefPtr<SVGDecodedImageData>> SVGDecodedImageData::create(ByteBuffer) +class SVGDecodedImageData::SVGPageClient final : public PageClient { +public: + explicit SVGPageClient(Page& host_page) + : m_host_page(host_page) + { + } + + virtual ~SVGPageClient() override = default; + + Page& m_host_page; + Page* m_svg_page { nullptr }; + + virtual Page& page() override { return *m_svg_page; } + virtual Page const& page() const override { return *m_svg_page; } + virtual bool is_connection_open() const override { return false; } + virtual Gfx::Palette palette() const override { return m_host_page.client().palette(); } + virtual DevicePixelRect screen_rect() const override { return {}; } + virtual float device_pixels_per_css_pixel() const override { return m_host_page.client().device_pixels_per_css_pixel(); } + virtual CSS::PreferredColorScheme preferred_color_scheme() const override { return m_host_page.client().preferred_color_scheme(); } + virtual void request_file(FileRequest) override { } + virtual void paint(DevicePixelRect const&, Gfx::Bitmap&) override { } +}; + +ErrorOr<NonnullRefPtr<SVGDecodedImageData>> SVGDecodedImageData::create(Page& host_page, AK::URL const& url, ByteBuffer data) { - return adopt_nonnull_ref_or_enomem(new (nothrow) SVGDecodedImageData()); + auto page_client = make<SVGPageClient>(host_page); + auto page = make<Page>(*page_client); + page_client->m_svg_page = page.ptr(); + auto browsing_context = HTML::BrowsingContext::create_a_new_top_level_browsing_context(*page); + auto response = Fetch::Infrastructure::Response::create(browsing_context->vm()); + response->url_list().append(url); + HTML::NavigationParams navigation_params { + .id = {}, + .request = nullptr, + .response = response, + .origin = HTML::Origin {}, + .policy_container = HTML::PolicyContainer {}, + .final_sandboxing_flag_set = HTML::SandboxingFlagSet {}, + .cross_origin_opener_policy = HTML::CrossOriginOpenerPolicy {}, + .coop_enforcement_result = HTML::CrossOriginOpenerPolicyEnforcementResult {}, + .reserved_environment = {}, + .browsing_context = browsing_context, + .navigable = nullptr, + }; + auto document = DOM::Document::create_and_initialize(DOM::Document::Type::HTML, "text/html", move(navigation_params)).release_value_but_fixme_should_propagate_errors(); + browsing_context->set_active_document(document); + + auto parser = HTML::HTMLParser::create_with_uncertain_encoding(document, data); + parser->run(document->url()); + + // Perform some DOM surgery to make the SVG root element be the first child of the Document. + // FIXME: This is a huge hack until we figure out how to actually parse separate SVG files. + auto* svg_root = document->body()->first_child_of_type<SVG::SVGSVGElement>(); + svg_root->remove(); + document->remove_all_children(); + + MUST(document->append_child(*svg_root)); + + return adopt_nonnull_ref_or_enomem(new (nothrow) SVGDecodedImageData(move(page), move(page_client), move(document), move(svg_root))); } -SVGDecodedImageData::SVGDecodedImageData() +SVGDecodedImageData::SVGDecodedImageData(NonnullOwnPtr<Page> page, NonnullOwnPtr<SVGPageClient> page_client, JS::Handle<DOM::Document> document, JS::Handle<SVG::SVGSVGElement> root_element) + : m_page(move(page)) + , m_page_client(move(page_client)) + , m_document(move(document)) + , m_root_element(move(root_element)) { } SVGDecodedImageData::~SVGDecodedImageData() = default; -RefPtr<Gfx::Bitmap const> SVGDecodedImageData::bitmap(size_t, Gfx::IntSize) const +void SVGDecodedImageData::render(Gfx::IntSize size) const { - return nullptr; + m_document->browsing_context()->set_viewport_rect({ 0, 0, size.width(), size.height() }); + m_document->update_layout(); + + dump_tree(*m_document->layout_node()); + + Gfx::Painter painter(*m_bitmap); + PaintContext context(painter, m_page_client->palette(), m_page_client->device_pixels_per_css_pixel()); + + m_document->layout_node()->paint_all_phases(context); +} + +RefPtr<Gfx::Bitmap const> SVGDecodedImageData::bitmap(size_t, Gfx::IntSize size) const +{ + if (size.is_empty()) + return nullptr; + + if (m_bitmap && m_bitmap->size() == size) + return m_bitmap; + + m_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, size).release_value_but_fixme_should_propagate_errors(); + render(size); + return m_bitmap; } Optional<CSSPixels> SVGDecodedImageData::intrinsic_width() const diff --git a/Userland/Libraries/LibWeb/SVG/SVGDecodedImageData.h b/Userland/Libraries/LibWeb/SVG/SVGDecodedImageData.h index 1adc482684..355e954614 100644 --- a/Userland/Libraries/LibWeb/SVG/SVGDecodedImageData.h +++ b/Userland/Libraries/LibWeb/SVG/SVGDecodedImageData.h @@ -12,7 +12,7 @@ namespace Web::SVG { class SVGDecodedImageData final : public HTML::DecodedImageData { public: - static ErrorOr<NonnullRefPtr<SVGDecodedImageData>> create(ByteBuffer encoded_svg); + static ErrorOr<NonnullRefPtr<SVGDecodedImageData>> create(Page&, AK::URL const&, ByteBuffer encoded_svg); virtual ~SVGDecodedImageData() override; virtual RefPtr<Gfx::Bitmap const> bitmap(size_t frame_index, Gfx::IntSize) const override; @@ -28,7 +28,17 @@ public: virtual bool is_animated() const override { return false; } private: - SVGDecodedImageData(); + class SVGPageClient; + SVGDecodedImageData(NonnullOwnPtr<Page>, NonnullOwnPtr<SVGPageClient>, JS::Handle<DOM::Document>, JS::Handle<SVG::SVGSVGElement>); + + void render(Gfx::IntSize) const; + mutable RefPtr<Gfx::Bitmap> m_bitmap; + + NonnullOwnPtr<Page> m_page; + NonnullOwnPtr<SVGPageClient> m_page_client; + + JS::Handle<DOM::Document> m_document; + JS::Handle<SVG::SVGSVGElement> m_root_element; }; } |