mirror of
				https://github.com/LadybirdBrowser/ladybird.git
				synced 2025-10-31 21:30:58 +00:00 
			
		
		
		
	 9b79a686eb
			
		
	
	
		9b79a686eb
		
	
	
	
	
		
			
			The main motivation behind this is to remove JS specifics of the Realm from the implementation of the Heap. As a side effect of this change, this is a bit nicer to read than the previous approach, and in my opinion, also makes it a little more clear that this method is specific to a JavaScript Realm.
		
			
				
	
	
		
			189 lines
		
	
	
	
		
			7.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			189 lines
		
	
	
	
		
			7.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  * Copyright (c) 2023, Andreas Kling <andreas@ladybird.org>
 | |
|  *
 | |
|  * SPDX-License-Identifier: BSD-2-Clause
 | |
|  */
 | |
| 
 | |
| #include <LibGfx/Bitmap.h>
 | |
| #include <LibWeb/Bindings/MainThreadVM.h>
 | |
| #include <LibWeb/DOM/Document.h>
 | |
| #include <LibWeb/Fetch/Infrastructure/HTTP/Responses.h>
 | |
| #include <LibWeb/HTML/BrowsingContext.h>
 | |
| #include <LibWeb/HTML/DocumentState.h>
 | |
| #include <LibWeb/HTML/NavigationParams.h>
 | |
| #include <LibWeb/HTML/Parser/HTMLParser.h>
 | |
| #include <LibWeb/HTML/TraversableNavigable.h>
 | |
| #include <LibWeb/Layout/Viewport.h>
 | |
| #include <LibWeb/Page/Page.h>
 | |
| #include <LibWeb/Painting/DisplayListPlayerSkia.h>
 | |
| #include <LibWeb/Painting/PaintContext.h>
 | |
| #include <LibWeb/Painting/ViewportPaintable.h>
 | |
| #include <LibWeb/SVG/SVGDecodedImageData.h>
 | |
| #include <LibWeb/SVG/SVGSVGElement.h>
 | |
| 
 | |
| namespace Web::SVG {
 | |
| 
 | |
| JS_DEFINE_ALLOCATOR(SVGDecodedImageData);
 | |
| JS_DEFINE_ALLOCATOR(SVGDecodedImageData::SVGPageClient);
 | |
| 
 | |
| ErrorOr<JS::NonnullGCPtr<SVGDecodedImageData>> SVGDecodedImageData::create(JS::Realm& realm, JS::NonnullGCPtr<Page> host_page, URL::URL const& url, ByteBuffer data)
 | |
| {
 | |
|     auto page_client = SVGPageClient::create(Bindings::main_thread_vm(), host_page);
 | |
|     auto page = Page::create(Bindings::main_thread_vm(), *page_client);
 | |
|     page_client->m_svg_page = page.ptr();
 | |
|     page->set_top_level_traversable(MUST(Web::HTML::TraversableNavigable::create_a_new_top_level_traversable(*page, nullptr, {})));
 | |
|     JS::NonnullGCPtr<HTML::Navigable> navigable = page->top_level_traversable();
 | |
|     auto response = Fetch::Infrastructure::Response::create(navigable->vm());
 | |
|     response->url_list().append(url);
 | |
|     auto navigation_params = navigable->heap().allocate_without_realm<HTML::NavigationParams>();
 | |
|     navigation_params->navigable = navigable;
 | |
|     navigation_params->response = response;
 | |
|     navigation_params->origin = URL::Origin {};
 | |
|     navigation_params->policy_container = HTML::PolicyContainer {};
 | |
|     navigation_params->final_sandboxing_flag_set = HTML::SandboxingFlagSet {};
 | |
|     navigation_params->opener_policy = HTML::OpenerPolicy {};
 | |
| 
 | |
|     // FIXME: Use Navigable::navigate() instead of manually replacing the navigable's document.
 | |
|     auto document = DOM::Document::create_and_initialize(DOM::Document::Type::HTML, "text/html"_string, navigation_params).release_value_but_fixme_should_propagate_errors();
 | |
|     navigable->set_ongoing_navigation({});
 | |
|     navigable->active_document()->destroy();
 | |
|     navigable->active_session_history_entry()->document_state()->set_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>();
 | |
|     if (!svg_root)
 | |
|         return Error::from_string_literal("SVGDecodedImageData: Invalid SVG input");
 | |
| 
 | |
|     svg_root->remove();
 | |
|     document->remove_all_children();
 | |
| 
 | |
|     MUST(document->append_child(*svg_root));
 | |
| 
 | |
|     return realm.create<SVGDecodedImageData>(page, page_client, document, *svg_root);
 | |
| }
 | |
| 
 | |
| SVGDecodedImageData::SVGDecodedImageData(JS::NonnullGCPtr<Page> page, JS::NonnullGCPtr<SVGPageClient> page_client, JS::NonnullGCPtr<DOM::Document> document, JS::NonnullGCPtr<SVG::SVGSVGElement> root_element)
 | |
|     : m_page(page)
 | |
|     , m_page_client(page_client)
 | |
|     , m_document(document)
 | |
|     , m_root_element(root_element)
 | |
| {
 | |
| }
 | |
| 
 | |
| SVGDecodedImageData::~SVGDecodedImageData() = default;
 | |
| 
 | |
| void SVGDecodedImageData::visit_edges(Cell::Visitor& visitor)
 | |
| {
 | |
|     Base::visit_edges(visitor);
 | |
|     visitor.visit(m_page);
 | |
|     visitor.visit(m_document);
 | |
|     visitor.visit(m_page_client);
 | |
|     visitor.visit(m_root_element);
 | |
| }
 | |
| 
 | |
| RefPtr<Gfx::Bitmap> SVGDecodedImageData::render(Gfx::IntSize size) const
 | |
| {
 | |
|     auto bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied, size).release_value_but_fixme_should_propagate_errors();
 | |
|     VERIFY(m_document->navigable());
 | |
|     m_document->navigable()->set_viewport_size(size.to_type<CSSPixels>());
 | |
|     m_document->update_layout();
 | |
| 
 | |
|     auto display_list = m_document->record_display_list({});
 | |
|     if (!display_list)
 | |
|         return {};
 | |
| 
 | |
|     auto painting_command_executor_type = m_page_client->display_list_player_type();
 | |
|     switch (painting_command_executor_type) {
 | |
|     case DisplayListPlayerType::SkiaGPUIfAvailable:
 | |
|     case DisplayListPlayerType::SkiaCPU: {
 | |
|         Painting::DisplayListPlayerSkia display_list_player { *bitmap };
 | |
|         display_list_player.execute(*display_list);
 | |
|         break;
 | |
|     }
 | |
|     default:
 | |
|         VERIFY_NOT_REACHED();
 | |
|     }
 | |
| 
 | |
|     return bitmap;
 | |
| }
 | |
| 
 | |
| RefPtr<Gfx::ImmutableBitmap> SVGDecodedImageData::bitmap(size_t, Gfx::IntSize size) const
 | |
| {
 | |
|     if (size.is_empty())
 | |
|         return nullptr;
 | |
| 
 | |
|     if (auto it = m_cached_rendered_bitmaps.find(size); it != m_cached_rendered_bitmaps.end())
 | |
|         return it->value;
 | |
| 
 | |
|     // Prevent the cache from growing too big.
 | |
|     // FIXME: Evict least used entries.
 | |
|     if (m_cached_rendered_bitmaps.size() > 10)
 | |
|         m_cached_rendered_bitmaps.remove(m_cached_rendered_bitmaps.begin());
 | |
| 
 | |
|     auto immutable_bitmap = Gfx::ImmutableBitmap::create(*render(size));
 | |
|     m_cached_rendered_bitmaps.set(size, immutable_bitmap);
 | |
|     return immutable_bitmap;
 | |
| }
 | |
| 
 | |
| Optional<CSSPixels> SVGDecodedImageData::intrinsic_width() const
 | |
| {
 | |
|     // https://www.w3.org/TR/SVG2/coords.html#SizingSVGInCSS
 | |
|     m_document->update_style();
 | |
|     auto const root_element_style = m_root_element->computed_css_values();
 | |
|     VERIFY(root_element_style.has_value());
 | |
|     auto const& width_value = root_element_style->size_value(CSS::PropertyID::Width);
 | |
|     if (width_value.is_length() && width_value.length().is_absolute())
 | |
|         return width_value.length().absolute_length_to_px();
 | |
|     return {};
 | |
| }
 | |
| 
 | |
| Optional<CSSPixels> SVGDecodedImageData::intrinsic_height() const
 | |
| {
 | |
|     // https://www.w3.org/TR/SVG2/coords.html#SizingSVGInCSS
 | |
|     m_document->update_style();
 | |
|     auto const root_element_style = m_root_element->computed_css_values();
 | |
|     VERIFY(root_element_style.has_value());
 | |
|     auto const& height_value = root_element_style->size_value(CSS::PropertyID::Height);
 | |
|     if (height_value.is_length() && height_value.length().is_absolute())
 | |
|         return height_value.length().absolute_length_to_px();
 | |
|     return {};
 | |
| }
 | |
| 
 | |
| Optional<CSSPixelFraction> SVGDecodedImageData::intrinsic_aspect_ratio() const
 | |
| {
 | |
|     // https://www.w3.org/TR/SVG2/coords.html#SizingSVGInCSS
 | |
|     auto width = intrinsic_width();
 | |
|     auto height = intrinsic_height();
 | |
|     if (height.has_value() && *height == 0)
 | |
|         return {};
 | |
| 
 | |
|     if (width.has_value() && height.has_value())
 | |
|         return *width / *height;
 | |
| 
 | |
|     if (auto const& viewbox = m_root_element->view_box(); viewbox.has_value()) {
 | |
|         auto viewbox_width = CSSPixels::nearest_value_for(viewbox->width);
 | |
| 
 | |
|         if (viewbox_width == 0)
 | |
|             return {};
 | |
| 
 | |
|         auto viewbox_height = CSSPixels::nearest_value_for(viewbox->height);
 | |
|         if (viewbox_height == 0)
 | |
|             return {};
 | |
| 
 | |
|         return viewbox_width / viewbox_height;
 | |
|     }
 | |
|     return {};
 | |
| }
 | |
| 
 | |
| void SVGDecodedImageData::SVGPageClient::visit_edges(Visitor& visitor)
 | |
| {
 | |
|     Base::visit_edges(visitor);
 | |
|     visitor.visit(m_host_page);
 | |
|     visitor.visit(m_svg_page);
 | |
| }
 | |
| 
 | |
| }
 |