mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 12:32:43 +00:00 
			
		
		
		
	LibWeb: Make HTML::SharedImageRequest GC allocated
This allows to partially solve the problem of cyclic dependency between HTMLImageElement and SharedImageRequest that prevents all image elements from being deallocated.
This commit is contained in:
		
							parent
							
								
									bbfedf2e5a
								
							
						
					
					
						commit
						934afcb9d5
					
				
					 9 changed files with 46 additions and 18 deletions
				
			
		|  | @ -31,7 +31,7 @@ void ImageStyleValue::load_any_resources(DOM::Document& document) | ||||||
|         return; |         return; | ||||||
|     m_document = &document; |     m_document = &document; | ||||||
| 
 | 
 | ||||||
|     m_image_request = HTML::SharedImageRequest::get_or_create(*document.page(), m_url).release_value_but_fixme_should_propagate_errors(); |     m_image_request = HTML::SharedImageRequest::get_or_create(document.realm(), *document.page(), m_url); | ||||||
|     m_image_request->add_callbacks( |     m_image_request->add_callbacks( | ||||||
|         [this, weak_this = make_weak_ptr()] { |         [this, weak_this = make_weak_ptr()] { | ||||||
|             if (!weak_this) |             if (!weak_this) | ||||||
|  |  | ||||||
|  | @ -10,6 +10,7 @@ | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <AK/URL.h> | #include <AK/URL.h> | ||||||
|  | #include <LibJS/Heap/Handle.h> | ||||||
| #include <LibWeb/CSS/Enums.h> | #include <LibWeb/CSS/Enums.h> | ||||||
| #include <LibWeb/CSS/StyleValues/AbstractImageStyleValue.h> | #include <LibWeb/CSS/StyleValues/AbstractImageStyleValue.h> | ||||||
| 
 | 
 | ||||||
|  | @ -43,7 +44,7 @@ public: | ||||||
| private: | private: | ||||||
|     ImageStyleValue(AK::URL const&); |     ImageStyleValue(AK::URL const&); | ||||||
| 
 | 
 | ||||||
|     RefPtr<HTML::SharedImageRequest> m_image_request; |     JS::Handle<HTML::SharedImageRequest> m_image_request; | ||||||
| 
 | 
 | ||||||
|     void animate(); |     void animate(); | ||||||
|     Gfx::Bitmap const* bitmap(size_t frame_index, Gfx::IntSize = {}) const; |     Gfx::Bitmap const* bitmap(size_t frame_index, Gfx::IntSize = {}) const; | ||||||
|  |  | ||||||
|  | @ -394,7 +394,7 @@ ErrorOr<void> HTMLImageElement::update_the_image_data(bool restart_animations, b | ||||||
|                     restart_the_animation(); |                     restart_the_animation(); | ||||||
| 
 | 
 | ||||||
|                 // 2. Set current request's current URL to urlString.
 |                 // 2. Set current request's current URL to urlString.
 | ||||||
|                 m_current_request->set_current_url(url_string); |                 m_current_request->set_current_url(realm(), url_string); | ||||||
| 
 | 
 | ||||||
|                 // 3. If maybe omit events is not set or previousURL is not equal to urlString, then fire an event named load at the img element.
 |                 // 3. If maybe omit events is not set or previousURL is not equal to urlString, then fire an event named load at the img element.
 | ||||||
|                 if (!maybe_omit_events || previous_url != url_string) |                 if (!maybe_omit_events || previous_url != url_string) | ||||||
|  | @ -433,7 +433,7 @@ after_step_7: | ||||||
|             // 2. Queue an element task on the DOM manipulation task source given the img element and the following steps:
 |             // 2. Queue an element task on the DOM manipulation task source given the img element and the following steps:
 | ||||||
|             queue_an_element_task(HTML::Task::Source::DOMManipulation, [this, maybe_omit_events, previous_url] { |             queue_an_element_task(HTML::Task::Source::DOMManipulation, [this, maybe_omit_events, previous_url] { | ||||||
|                 // 1. Change the current request's current URL to the empty string.
 |                 // 1. Change the current request's current URL to the empty string.
 | ||||||
|                 m_current_request->set_current_url(""sv); |                 m_current_request->set_current_url(realm(), ""sv); | ||||||
| 
 | 
 | ||||||
|                 // 2. If all of the following conditions are true:
 |                 // 2. If all of the following conditions are true:
 | ||||||
|                 //    - the element has a src attribute or it uses srcset or picture; and
 |                 //    - the element has a src attribute or it uses srcset or picture; and
 | ||||||
|  | @ -466,7 +466,7 @@ after_step_7: | ||||||
|             // 4. Queue an element task on the DOM manipulation task source given the img element and the following steps:
 |             // 4. Queue an element task on the DOM manipulation task source given the img element and the following steps:
 | ||||||
|             queue_an_element_task(HTML::Task::Source::DOMManipulation, [this, selected_source, maybe_omit_events, previous_url] { |             queue_an_element_task(HTML::Task::Source::DOMManipulation, [this, selected_source, maybe_omit_events, previous_url] { | ||||||
|                 // 1. Change the current request's current URL to selected source.
 |                 // 1. Change the current request's current URL to selected source.
 | ||||||
|                 m_current_request->set_current_url(selected_source.value().url); |                 m_current_request->set_current_url(realm(), selected_source.value().url); | ||||||
| 
 | 
 | ||||||
|                 // 2. If maybe omit events is not set or previousURL is not equal to selected source, then fire an event named error at the img element.
 |                 // 2. If maybe omit events is not set or previousURL is not equal to selected source, then fire an event named error at the img element.
 | ||||||
|                 if (!maybe_omit_events || previous_url != selected_source.value().url) |                 if (!maybe_omit_events || previous_url != selected_source.value().url) | ||||||
|  | @ -503,7 +503,7 @@ after_step_7: | ||||||
| 
 | 
 | ||||||
|         // 16. Set image request to a new image request whose current URL is urlString.
 |         // 16. Set image request to a new image request whose current URL is urlString.
 | ||||||
|         auto image_request = ImageRequest::create(realm(), *document().page()); |         auto image_request = ImageRequest::create(realm(), *document().page()); | ||||||
|         image_request->set_current_url(url_string); |         image_request->set_current_url(realm(), url_string); | ||||||
| 
 | 
 | ||||||
|         // 17. If current request's state is unavailable or broken, then set the current request to image request.
 |         // 17. If current request's state is unavailable or broken, then set the current request to image request.
 | ||||||
|         //     Otherwise, set the pending request to image request.
 |         //     Otherwise, set the pending request to image request.
 | ||||||
|  | @ -698,7 +698,7 @@ void HTMLImageElement::react_to_changes_in_the_environment() | ||||||
| 
 | 
 | ||||||
|     // 11. ⌛ Let image request be a new image request whose current URL is urlString
 |     // 11. ⌛ Let image request be a new image request whose current URL is urlString
 | ||||||
|     auto image_request = ImageRequest::create(realm(), *document().page()); |     auto image_request = ImageRequest::create(realm(), *document().page()); | ||||||
|     image_request->set_current_url(url_string); |     image_request->set_current_url(realm(), url_string); | ||||||
| 
 | 
 | ||||||
|     // 12. ⌛ Let the element's pending request be image request.
 |     // 12. ⌛ Let the element's pending request be image request.
 | ||||||
|     m_pending_request = image_request; |     m_pending_request = image_request; | ||||||
|  |  | ||||||
|  | @ -39,6 +39,12 @@ void HTMLObjectElement::initialize(JS::Realm& realm) | ||||||
|     set_prototype(&Bindings::ensure_web_prototype<Bindings::HTMLObjectElementPrototype>(realm, "HTMLObjectElement")); |     set_prototype(&Bindings::ensure_web_prototype<Bindings::HTMLObjectElementPrototype>(realm, "HTMLObjectElement")); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void HTMLObjectElement::visit_edges(Cell::Visitor& visitor) | ||||||
|  | { | ||||||
|  |     Base::visit_edges(visitor); | ||||||
|  |     visitor.visit(m_image_request); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void HTMLObjectElement::attribute_changed(DeprecatedFlyString const& name, DeprecatedString const& value) | void HTMLObjectElement::attribute_changed(DeprecatedFlyString const& name, DeprecatedString const& value) | ||||||
| { | { | ||||||
|     NavigableContainer::attribute_changed(name, value); |     NavigableContainer::attribute_changed(name, value); | ||||||
|  | @ -316,7 +322,7 @@ void HTMLObjectElement::load_image() | ||||||
|     // NOTE: This currently reloads the image instead of reusing the resource we've already downloaded.
 |     // NOTE: This currently reloads the image instead of reusing the resource we've already downloaded.
 | ||||||
|     auto data = attribute(HTML::AttributeNames::data); |     auto data = attribute(HTML::AttributeNames::data); | ||||||
|     auto url = document().parse_url(data); |     auto url = document().parse_url(data); | ||||||
|     m_image_request = HTML::SharedImageRequest::get_or_create(*document().page(), url).release_value_but_fixme_should_propagate_errors(); |     m_image_request = HTML::SharedImageRequest::get_or_create(realm(), *document().page(), url); | ||||||
|     m_image_request->add_callbacks( |     m_image_request->add_callbacks( | ||||||
|         [this] { |         [this] { | ||||||
|             run_object_representation_completed_steps(Representation::Image); |             run_object_representation_completed_steps(Representation::Image); | ||||||
|  |  | ||||||
|  | @ -45,6 +45,8 @@ public: | ||||||
|     // https://html.spec.whatwg.org/multipage/forms.html#category-listed
 |     // https://html.spec.whatwg.org/multipage/forms.html#category-listed
 | ||||||
|     virtual bool is_listed() const override { return true; } |     virtual bool is_listed() const override { return true; } | ||||||
| 
 | 
 | ||||||
|  |     virtual void visit_edges(Cell::Visitor&) override; | ||||||
|  | 
 | ||||||
| private: | private: | ||||||
|     HTMLObjectElement(DOM::Document&, DOM::QualifiedName); |     HTMLObjectElement(DOM::Document&, DOM::QualifiedName); | ||||||
| 
 | 
 | ||||||
|  | @ -80,7 +82,7 @@ private: | ||||||
| 
 | 
 | ||||||
|     RefPtr<DecodedImageData const> image_data() const; |     RefPtr<DecodedImageData const> image_data() const; | ||||||
| 
 | 
 | ||||||
|     RefPtr<SharedImageRequest> m_image_request; |     JS::GCPtr<SharedImageRequest> m_image_request; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -33,6 +33,12 @@ ImageRequest::~ImageRequest() | ||||||
| { | { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void ImageRequest::visit_edges(JS::Cell::Visitor& visitor) | ||||||
|  | { | ||||||
|  |     Base::visit_edges(visitor); | ||||||
|  |     visitor.visit(m_shared_image_request); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // https://html.spec.whatwg.org/multipage/images.html#img-available
 | // https://html.spec.whatwg.org/multipage/images.html#img-available
 | ||||||
| bool ImageRequest::is_available() const | bool ImageRequest::is_available() const | ||||||
| { | { | ||||||
|  | @ -60,11 +66,11 @@ AK::URL const& ImageRequest::current_url() const | ||||||
|     return m_current_url; |     return m_current_url; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ImageRequest::set_current_url(AK::URL url) | void ImageRequest::set_current_url(JS::Realm& realm, AK::URL url) | ||||||
| { | { | ||||||
|     m_current_url = move(url); |     m_current_url = move(url); | ||||||
|     if (m_current_url.is_valid()) |     if (m_current_url.is_valid()) | ||||||
|         m_shared_image_request = SharedImageRequest::get_or_create(m_page, m_current_url).release_value_but_fixme_should_propagate_errors(); |         m_shared_image_request = SharedImageRequest::get_or_create(realm, m_page, m_current_url); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // https://html.spec.whatwg.org/multipage/images.html#abort-the-image-request
 | // https://html.spec.whatwg.org/multipage/images.html#abort-the-image-request
 | ||||||
|  |  | ||||||
|  | @ -40,7 +40,7 @@ public: | ||||||
|     void set_state(State); |     void set_state(State); | ||||||
| 
 | 
 | ||||||
|     AK::URL const& current_url() const; |     AK::URL const& current_url() const; | ||||||
|     void set_current_url(AK::URL); |     void set_current_url(JS::Realm&, AK::URL); | ||||||
| 
 | 
 | ||||||
|     [[nodiscard]] RefPtr<DecodedImageData const> image_data() const; |     [[nodiscard]] RefPtr<DecodedImageData const> image_data() const; | ||||||
|     void set_image_data(RefPtr<DecodedImageData const>); |     void set_image_data(RefPtr<DecodedImageData const>); | ||||||
|  | @ -59,6 +59,8 @@ public: | ||||||
| 
 | 
 | ||||||
|     SharedImageRequest const* shared_image_request() const { return m_shared_image_request; } |     SharedImageRequest const* shared_image_request() const { return m_shared_image_request; } | ||||||
| 
 | 
 | ||||||
|  |     virtual void visit_edges(JS::Cell::Visitor&) override; | ||||||
|  | 
 | ||||||
| private: | private: | ||||||
|     explicit ImageRequest(Page&); |     explicit ImageRequest(Page&); | ||||||
| 
 | 
 | ||||||
|  | @ -84,7 +86,7 @@ private: | ||||||
|     // which is either a struct consisting of a width and a height or is null. It must initially be null.
 |     // which is either a struct consisting of a width and a height or is null. It must initially be null.
 | ||||||
|     Optional<Gfx::FloatSize> m_preferred_density_corrected_dimensions; |     Optional<Gfx::FloatSize> m_preferred_density_corrected_dimensions; | ||||||
| 
 | 
 | ||||||
|     RefPtr<SharedImageRequest> m_shared_image_request; |     JS::GCPtr<SharedImageRequest> m_shared_image_request; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // https://html.spec.whatwg.org/multipage/images.html#abort-the-image-request
 | // https://html.spec.whatwg.org/multipage/images.html#abort-the-image-request
 | ||||||
|  |  | ||||||
|  | @ -24,11 +24,11 @@ static HashMap<AK::URL, SharedImageRequest*>& shared_image_requests() | ||||||
|     return requests; |     return requests; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ErrorOr<NonnullRefPtr<SharedImageRequest>> SharedImageRequest::get_or_create(Page& page, AK::URL const& url) | JS::NonnullGCPtr<SharedImageRequest> SharedImageRequest::get_or_create(JS::Realm& realm, Page& page, AK::URL const& url) | ||||||
| { | { | ||||||
|     if (auto it = shared_image_requests().find(url); it != shared_image_requests().end()) |     if (auto it = shared_image_requests().find(url); it != shared_image_requests().end()) | ||||||
|         return *it->value; |         return *it->value; | ||||||
|     auto request = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) SharedImageRequest(page, url))); |     auto request = realm.heap().allocate<SharedImageRequest>(realm, page, url); | ||||||
|     shared_image_requests().set(url, request); |     shared_image_requests().set(url, request); | ||||||
|     return request; |     return request; | ||||||
| } | } | ||||||
|  | @ -44,6 +44,12 @@ SharedImageRequest::~SharedImageRequest() | ||||||
|     shared_image_requests().remove(m_url); |     shared_image_requests().remove(m_url); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void SharedImageRequest::visit_edges(JS::Cell::Visitor& visitor) | ||||||
|  | { | ||||||
|  |     Base::visit_edges(visitor); | ||||||
|  |     visitor.visit(m_fetch_controller); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| RefPtr<DecodedImageData const> SharedImageRequest::image_data() const | RefPtr<DecodedImageData const> SharedImageRequest::image_data() const | ||||||
| { | { | ||||||
|     return m_image_data; |     return m_image_data; | ||||||
|  |  | ||||||
|  | @ -16,9 +16,12 @@ | ||||||
| 
 | 
 | ||||||
| namespace Web::HTML { | namespace Web::HTML { | ||||||
| 
 | 
 | ||||||
| class SharedImageRequest : public RefCounted<SharedImageRequest> { | class SharedImageRequest : public JS::Cell { | ||||||
|  |     JS_CELL(ImageRequest, JS::Cell); | ||||||
|  | 
 | ||||||
| public: | public: | ||||||
|     static ErrorOr<NonnullRefPtr<SharedImageRequest>> get_or_create(Page&, AK::URL const&); |     [[nodiscard]] static JS::NonnullGCPtr<SharedImageRequest> get_or_create(JS::Realm&, Page&, AK::URL const&); | ||||||
|  | 
 | ||||||
|     ~SharedImageRequest(); |     ~SharedImageRequest(); | ||||||
| 
 | 
 | ||||||
|     AK::URL const& url() const { return m_url; } |     AK::URL const& url() const { return m_url; } | ||||||
|  | @ -35,6 +38,8 @@ public: | ||||||
|     bool is_fetching() const; |     bool is_fetching() const; | ||||||
|     bool needs_fetching() const; |     bool needs_fetching() const; | ||||||
| 
 | 
 | ||||||
|  |     virtual void visit_edges(JS::Cell::Visitor&) override; | ||||||
|  | 
 | ||||||
| private: | private: | ||||||
|     explicit SharedImageRequest(Page&, AK::URL); |     explicit SharedImageRequest(Page&, AK::URL); | ||||||
| 
 | 
 | ||||||
|  | @ -60,7 +65,7 @@ private: | ||||||
| 
 | 
 | ||||||
|     AK::URL m_url; |     AK::URL m_url; | ||||||
|     RefPtr<DecodedImageData const> m_image_data; |     RefPtr<DecodedImageData const> m_image_data; | ||||||
|     JS::Handle<Fetch::Infrastructure::FetchController> m_fetch_controller; |     JS::GCPtr<Fetch::Infrastructure::FetchController> m_fetch_controller; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Aliaksandr Kalenik
						Aliaksandr Kalenik