SVGDecodedImageData.cpp 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  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/DisplayListPlayerSkia.h>
  18. #include <LibWeb/Painting/PaintContext.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. JS_DEFINE_ALLOCATOR(SVGDecodedImageData::SVGPageClient);
  25. ErrorOr<JS::NonnullGCPtr<SVGDecodedImageData>> SVGDecodedImageData::create(JS::Realm& realm, JS::NonnullGCPtr<Page> host_page, URL::URL const& url, ByteBuffer data)
  26. {
  27. auto page_client = SVGPageClient::create(Bindings::main_thread_vm(), host_page);
  28. auto page = Page::create(Bindings::main_thread_vm(), *page_client);
  29. page_client->m_svg_page = page.ptr();
  30. page->set_top_level_traversable(MUST(Web::HTML::TraversableNavigable::create_a_new_top_level_traversable(*page, nullptr, {})));
  31. JS::NonnullGCPtr<HTML::Navigable> navigable = page->top_level_traversable();
  32. auto response = Fetch::Infrastructure::Response::create(navigable->vm());
  33. response->url_list().append(url);
  34. auto navigation_params = navigable->heap().allocate_without_realm<HTML::NavigationParams>();
  35. navigation_params->navigable = navigable;
  36. navigation_params->response = response;
  37. navigation_params->origin = HTML::Origin {};
  38. navigation_params->policy_container = HTML::PolicyContainer {};
  39. navigation_params->final_sandboxing_flag_set = HTML::SandboxingFlagSet {};
  40. navigation_params->cross_origin_opener_policy = HTML::CrossOriginOpenerPolicy {};
  41. // FIXME: Use Navigable::navigate() instead of manually replacing the navigable's document.
  42. auto document = DOM::Document::create_and_initialize(DOM::Document::Type::HTML, "text/html"_string, navigation_params).release_value_but_fixme_should_propagate_errors();
  43. navigable->set_ongoing_navigation({});
  44. navigable->active_document()->destroy();
  45. navigable->active_session_history_entry()->document_state()->set_document(document);
  46. auto parser = HTML::HTMLParser::create_with_uncertain_encoding(document, data);
  47. parser->run(document->url());
  48. // Perform some DOM surgery to make the SVG root element be the first child of the Document.
  49. // FIXME: This is a huge hack until we figure out how to actually parse separate SVG files.
  50. auto* svg_root = document->body()->first_child_of_type<SVG::SVGSVGElement>();
  51. if (!svg_root)
  52. return Error::from_string_literal("SVGDecodedImageData: Invalid SVG input");
  53. svg_root->remove();
  54. document->remove_all_children();
  55. MUST(document->append_child(*svg_root));
  56. return realm.heap().allocate<SVGDecodedImageData>(realm, page, page_client, document, *svg_root);
  57. }
  58. SVGDecodedImageData::SVGDecodedImageData(JS::NonnullGCPtr<Page> page, JS::NonnullGCPtr<SVGPageClient> page_client, JS::NonnullGCPtr<DOM::Document> document, JS::NonnullGCPtr<SVG::SVGSVGElement> root_element)
  59. : m_page(page)
  60. , m_page_client(page_client)
  61. , m_document(document)
  62. , m_root_element(root_element)
  63. {
  64. }
  65. SVGDecodedImageData::~SVGDecodedImageData() = default;
  66. void SVGDecodedImageData::visit_edges(Cell::Visitor& visitor)
  67. {
  68. Base::visit_edges(visitor);
  69. visitor.visit(m_page);
  70. visitor.visit(m_document);
  71. visitor.visit(m_page_client);
  72. visitor.visit(m_root_element);
  73. }
  74. RefPtr<Gfx::Bitmap> SVGDecodedImageData::render(Gfx::IntSize size) const
  75. {
  76. auto bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied, size).release_value_but_fixme_should_propagate_errors();
  77. VERIFY(m_document->navigable());
  78. m_document->navigable()->set_viewport_size(size.to_type<CSSPixels>());
  79. m_document->update_layout();
  80. auto display_list = Painting::DisplayList::create();
  81. Painting::DisplayListRecorder display_list_recorder(display_list);
  82. m_document->navigable()->record_display_list(display_list_recorder, {});
  83. auto painting_command_executor_type = m_page_client->display_list_player_type();
  84. switch (painting_command_executor_type) {
  85. case DisplayListPlayerType::SkiaGPUIfAvailable:
  86. case DisplayListPlayerType::SkiaCPU: {
  87. Painting::DisplayListPlayerSkia display_list_player { *bitmap };
  88. display_list_player.execute(display_list);
  89. break;
  90. }
  91. default:
  92. VERIFY_NOT_REACHED();
  93. }
  94. return bitmap;
  95. }
  96. RefPtr<Gfx::ImmutableBitmap> SVGDecodedImageData::bitmap(size_t, Gfx::IntSize size) const
  97. {
  98. if (size.is_empty())
  99. return nullptr;
  100. if (auto it = m_cached_rendered_bitmaps.find(size); it != m_cached_rendered_bitmaps.end())
  101. return it->value;
  102. // Prevent the cache from growing too big.
  103. // FIXME: Evict least used entries.
  104. if (m_cached_rendered_bitmaps.size() > 10)
  105. m_cached_rendered_bitmaps.remove(m_cached_rendered_bitmaps.begin());
  106. auto immutable_bitmap = Gfx::ImmutableBitmap::create(*render(size));
  107. m_cached_rendered_bitmaps.set(size, immutable_bitmap);
  108. return immutable_bitmap;
  109. }
  110. Optional<CSSPixels> SVGDecodedImageData::intrinsic_width() const
  111. {
  112. // https://www.w3.org/TR/SVG2/coords.html#SizingSVGInCSS
  113. m_document->update_style();
  114. auto const* root_element_style = m_root_element->computed_css_values();
  115. VERIFY(root_element_style);
  116. auto const& width_value = root_element_style->size_value(CSS::PropertyID::Width);
  117. if (width_value.is_length() && width_value.length().is_absolute())
  118. return width_value.length().absolute_length_to_px();
  119. return {};
  120. }
  121. Optional<CSSPixels> SVGDecodedImageData::intrinsic_height() const
  122. {
  123. // https://www.w3.org/TR/SVG2/coords.html#SizingSVGInCSS
  124. m_document->update_style();
  125. auto const* root_element_style = m_root_element->computed_css_values();
  126. VERIFY(root_element_style);
  127. auto const& height_value = root_element_style->size_value(CSS::PropertyID::Height);
  128. if (height_value.is_length() && height_value.length().is_absolute())
  129. return height_value.length().absolute_length_to_px();
  130. return {};
  131. }
  132. Optional<CSSPixelFraction> SVGDecodedImageData::intrinsic_aspect_ratio() const
  133. {
  134. // https://www.w3.org/TR/SVG2/coords.html#SizingSVGInCSS
  135. auto width = intrinsic_width();
  136. auto height = intrinsic_height();
  137. if (height.has_value() && *height == 0)
  138. return {};
  139. if (width.has_value() && height.has_value())
  140. return *width / *height;
  141. if (auto const& viewbox = m_root_element->view_box(); viewbox.has_value()) {
  142. auto viewbox_width = CSSPixels::nearest_value_for(viewbox->width);
  143. if (viewbox_width == 0)
  144. return {};
  145. auto viewbox_height = CSSPixels::nearest_value_for(viewbox->height);
  146. if (viewbox_height == 0)
  147. return {};
  148. return viewbox_width / viewbox_height;
  149. }
  150. return {};
  151. }
  152. void SVGDecodedImageData::SVGPageClient::visit_edges(Visitor& visitor)
  153. {
  154. Base::visit_edges(visitor);
  155. visitor.visit(m_host_page);
  156. visitor.visit(m_svg_page);
  157. }
  158. }