SVGDecodedImageData.cpp 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. /*
  2. * Copyright (c) 2023, Andreas Kling <kling@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <LibGfx/Bitmap.h>
  7. #include <LibWeb/Bindings/MainThreadVM.h>
  8. #include <LibWeb/DOM/Document.h>
  9. #include <LibWeb/Fetch/Infrastructure/HTTP/Responses.h>
  10. #include <LibWeb/HTML/BrowsingContext.h>
  11. #include <LibWeb/HTML/DocumentState.h>
  12. #include <LibWeb/HTML/NavigationParams.h>
  13. #include <LibWeb/HTML/Parser/HTMLParser.h>
  14. #include <LibWeb/HTML/TraversableNavigable.h>
  15. #include <LibWeb/Layout/Viewport.h>
  16. #include <LibWeb/Page/Page.h>
  17. #include <LibWeb/Painting/PaintContext.h>
  18. #include <LibWeb/Painting/PaintingCommandExecutorCPU.h>
  19. #include <LibWeb/Painting/ViewportPaintable.h>
  20. #include <LibWeb/SVG/SVGDecodedImageData.h>
  21. #include <LibWeb/SVG/SVGSVGElement.h>
  22. namespace Web::SVG {
  23. JS_DEFINE_ALLOCATOR(SVGDecodedImageData);
  24. class SVGDecodedImageData::SVGPageClient final : public PageClient {
  25. JS_CELL(SVGDecodedImageData::SVGPageClient, PageClient);
  26. public:
  27. static JS::NonnullGCPtr<SVGPageClient> create(JS::VM& vm, Page& page)
  28. {
  29. return vm.heap().allocate_without_realm<SVGPageClient>(page);
  30. }
  31. virtual ~SVGPageClient() override = default;
  32. Page& m_host_page;
  33. Page* m_svg_page { nullptr };
  34. virtual Page& page() override { return *m_svg_page; }
  35. virtual Page const& page() const override { return *m_svg_page; }
  36. virtual bool is_connection_open() const override { return false; }
  37. virtual Gfx::Palette palette() const override { return m_host_page.client().palette(); }
  38. virtual DevicePixelRect screen_rect() const override { return {}; }
  39. virtual double device_pixels_per_css_pixel() const override { return 1.0; }
  40. virtual CSS::PreferredColorScheme preferred_color_scheme() const override { return m_host_page.client().preferred_color_scheme(); }
  41. virtual void request_file(FileRequest) override { }
  42. virtual void paint(DevicePixelRect const&, Gfx::Bitmap&, Web::PaintOptions = {}) override { }
  43. private:
  44. explicit SVGPageClient(Page& host_page)
  45. : m_host_page(host_page)
  46. {
  47. }
  48. };
  49. ErrorOr<JS::NonnullGCPtr<SVGDecodedImageData>> SVGDecodedImageData::create(JS::Realm& realm, JS::NonnullGCPtr<Page> host_page, AK::URL const& url, ByteBuffer data)
  50. {
  51. auto page_client = SVGPageClient::create(Bindings::main_thread_vm(), host_page);
  52. auto page = Page::create(Bindings::main_thread_vm(), *page_client);
  53. page_client->m_svg_page = page.ptr();
  54. page->set_top_level_traversable(MUST(Web::HTML::TraversableNavigable::create_a_new_top_level_traversable(*page, nullptr, {})));
  55. JS::NonnullGCPtr<HTML::Navigable> navigable = page->top_level_traversable();
  56. auto response = Fetch::Infrastructure::Response::create(navigable->vm());
  57. response->url_list().append(url);
  58. HTML::NavigationParams navigation_params {
  59. .id = {},
  60. .navigable = navigable,
  61. .request = nullptr,
  62. .response = response,
  63. .fetch_controller = nullptr,
  64. .commit_early_hints = nullptr,
  65. .coop_enforcement_result = HTML::CrossOriginOpenerPolicyEnforcementResult {},
  66. .reserved_environment = {},
  67. .origin = HTML::Origin {},
  68. .policy_container = HTML::PolicyContainer {},
  69. .final_sandboxing_flag_set = HTML::SandboxingFlagSet {},
  70. .cross_origin_opener_policy = HTML::CrossOriginOpenerPolicy {},
  71. .about_base_url = {},
  72. };
  73. // FIXME: Use Navigable::navigate() instead of manually replacing the navigable's document.
  74. auto document = DOM::Document::create_and_initialize(DOM::Document::Type::HTML, "text/html"_string, navigation_params).release_value_but_fixme_should_propagate_errors();
  75. navigable->set_ongoing_navigation({});
  76. navigable->active_document()->destroy();
  77. navigable->active_session_history_entry()->document_state->set_document(document);
  78. auto parser = HTML::HTMLParser::create_with_uncertain_encoding(document, data);
  79. parser->run(document->url());
  80. // Perform some DOM surgery to make the SVG root element be the first child of the Document.
  81. // FIXME: This is a huge hack until we figure out how to actually parse separate SVG files.
  82. auto* svg_root = document->body()->first_child_of_type<SVG::SVGSVGElement>();
  83. if (!svg_root)
  84. return Error::from_string_literal("SVGDecodedImageData: Invalid SVG input");
  85. svg_root->remove();
  86. document->remove_all_children();
  87. MUST(document->append_child(*svg_root));
  88. return realm.heap().allocate<SVGDecodedImageData>(realm, page, page_client, document, *svg_root);
  89. }
  90. SVGDecodedImageData::SVGDecodedImageData(JS::NonnullGCPtr<Page> page, JS::NonnullGCPtr<SVGPageClient> page_client, JS::NonnullGCPtr<DOM::Document> document, JS::NonnullGCPtr<SVG::SVGSVGElement> root_element)
  91. : m_page(page)
  92. , m_page_client(page_client)
  93. , m_document(document)
  94. , m_root_element(root_element)
  95. {
  96. }
  97. SVGDecodedImageData::~SVGDecodedImageData() = default;
  98. void SVGDecodedImageData::visit_edges(Cell::Visitor& visitor)
  99. {
  100. Base::visit_edges(visitor);
  101. visitor.visit(m_page);
  102. visitor.visit(m_document);
  103. visitor.visit(m_page_client);
  104. visitor.visit(m_root_element);
  105. }
  106. RefPtr<Gfx::Bitmap> SVGDecodedImageData::render(Gfx::IntSize size) const
  107. {
  108. auto bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, size).release_value_but_fixme_should_propagate_errors();
  109. VERIFY(m_document->navigable());
  110. m_document->navigable()->set_viewport_rect({ 0, 0, size.width(), size.height() });
  111. m_document->update_layout();
  112. Painting::CommandList painting_commands;
  113. Painting::RecordingPainter recording_painter(painting_commands);
  114. PaintContext context(recording_painter, m_page_client->palette(), m_page_client->device_pixels_per_css_pixel());
  115. m_document->paintable()->paint_all_phases(context);
  116. Painting::PaintingCommandExecutorCPU executor { *bitmap };
  117. painting_commands.execute(executor);
  118. return bitmap;
  119. }
  120. RefPtr<Gfx::ImmutableBitmap> SVGDecodedImageData::bitmap(size_t, Gfx::IntSize size) const
  121. {
  122. if (size.is_empty())
  123. return nullptr;
  124. if (m_immutable_bitmap && m_immutable_bitmap->size() == size)
  125. return m_immutable_bitmap;
  126. m_immutable_bitmap = Gfx::ImmutableBitmap::create(*render(size));
  127. return m_immutable_bitmap;
  128. }
  129. Optional<CSSPixels> SVGDecodedImageData::intrinsic_width() const
  130. {
  131. // https://www.w3.org/TR/SVG2/coords.html#SizingSVGInCSS
  132. m_document->update_style();
  133. auto const* root_element_style = m_root_element->computed_css_values();
  134. VERIFY(root_element_style);
  135. auto const& width_value = root_element_style->size_value(CSS::PropertyID::Width);
  136. if (width_value.is_length() && width_value.length().is_absolute())
  137. return width_value.length().absolute_length_to_px();
  138. return {};
  139. }
  140. Optional<CSSPixels> SVGDecodedImageData::intrinsic_height() const
  141. {
  142. // https://www.w3.org/TR/SVG2/coords.html#SizingSVGInCSS
  143. m_document->update_style();
  144. auto const* root_element_style = m_root_element->computed_css_values();
  145. VERIFY(root_element_style);
  146. auto const& height_value = root_element_style->size_value(CSS::PropertyID::Height);
  147. if (height_value.is_length() && height_value.length().is_absolute())
  148. return height_value.length().absolute_length_to_px();
  149. return {};
  150. }
  151. Optional<CSSPixelFraction> SVGDecodedImageData::intrinsic_aspect_ratio() const
  152. {
  153. // https://www.w3.org/TR/SVG2/coords.html#SizingSVGInCSS
  154. auto width = intrinsic_width();
  155. auto height = intrinsic_height();
  156. if (height.has_value() && *height == 0)
  157. return {};
  158. if (width.has_value() && height.has_value())
  159. return *width / *height;
  160. if (auto const& viewbox = m_root_element->view_box(); viewbox.has_value())
  161. return CSSPixels::nearest_value_for(viewbox->width) / CSSPixels::nearest_value_for(viewbox->height);
  162. return {};
  163. }
  164. }