2020-01-18 09:38:21 +01:00
/*
2023-05-11 19:23:36 +02:00
* Copyright ( c ) 2018 - 2023 , Andreas Kling < kling @ serenityos . org >
2020-01-18 09:38:21 +01:00
*
2021-04-22 01:24:48 -07:00
* SPDX - License - Identifier : BSD - 2 - Clause
2020-01-18 09:38:21 +01:00
*/
2023-05-11 19:23:36 +02:00
# include <LibCore/Timer.h>
2020-02-14 23:28:42 +01:00
# include <LibGfx/Bitmap.h>
2023-01-28 22:23:16 +00:00
# include <LibWeb/ARIA/Roles.h>
2024-04-27 12:09:58 +12:00
# include <LibWeb/Bindings/HTMLImageElementPrototype.h>
2021-07-30 19:31:46 +01:00
# include <LibWeb/CSS/Parser/Parser.h>
2021-09-24 13:49:57 +02:00
# include <LibWeb/CSS/StyleComputer.h>
2020-03-07 10:32:51 +01:00
# include <LibWeb/DOM/Document.h>
2020-04-14 20:10:48 +02:00
# include <LibWeb/DOM/Event.h>
2023-05-11 19:23:36 +02:00
# include <LibWeb/Fetch/Fetching/Fetching.h>
# include <LibWeb/Fetch/Infrastructure/FetchController.h>
# include <LibWeb/Fetch/Response.h>
2023-05-20 16:11:36 +02:00
# include <LibWeb/HTML/AnimatedBitmapDecodedImageData.h>
2023-05-11 19:23:36 +02:00
# include <LibWeb/HTML/CORSSettingAttribute.h>
2020-11-21 19:15:57 +00:00
# include <LibWeb/HTML/EventNames.h>
2020-07-26 15:08:16 +02:00
# include <LibWeb/HTML/HTMLImageElement.h>
2023-05-11 19:23:36 +02:00
# include <LibWeb/HTML/HTMLLinkElement.h>
# include <LibWeb/HTML/HTMLPictureElement.h>
# include <LibWeb/HTML/HTMLSourceElement.h>
# include <LibWeb/HTML/ImageRequest.h>
# include <LibWeb/HTML/ListOfAvailableImages.h>
2022-03-26 14:29:52 +01:00
# include <LibWeb/HTML/Parser/HTMLParser.h>
2023-05-11 19:23:36 +02:00
# include <LibWeb/HTML/PotentialCORSRequest.h>
2020-11-22 15:53:01 +01:00
# include <LibWeb/Layout/ImageBox.h>
2020-06-01 20:42:50 +02:00
# include <LibWeb/Loader/ResourceLoader.h>
2022-03-10 23:13:37 +01:00
# include <LibWeb/Painting/PaintableBox.h>
2023-05-11 19:23:36 +02:00
# include <LibWeb/Platform/ImageCodecPlugin.h>
2023-05-20 17:19:11 +02:00
# include <LibWeb/SVG/SVGDecodedImageData.h>
2019-10-05 22:07:45 +02:00
2020-07-28 18:20:36 +02:00
namespace Web : : HTML {
2020-03-07 10:27:02 +01:00
2023-11-19 19:47:52 +01:00
JS_DEFINE_ALLOCATOR ( HTMLImageElement ) ;
2022-02-18 21:00:52 +01:00
HTMLImageElement : : HTMLImageElement ( DOM : : Document & document , DOM : : QualifiedName qualified_name )
2022-03-23 18:55:54 -04:00
: HTMLElement ( document , move ( qualified_name ) )
2019-10-05 22:07:45 +02:00
{
2023-05-11 19:23:36 +02:00
m_animation_timer = Core : : Timer : : try_create ( ) . release_value_but_fixme_should_propagate_errors ( ) ;
m_animation_timer - > on_timeout = [ this ] { animate ( ) ; } ;
2023-07-29 10:20:53 +02:00
2023-08-23 18:58:42 +02:00
document . register_viewport_client ( * this ) ;
2019-10-05 22:07:45 +02:00
}
2022-03-14 13:21:51 -06:00
HTMLImageElement : : ~ HTMLImageElement ( ) = default ;
2019-10-05 23:20:35 +02:00
2023-07-29 10:20:53 +02:00
void HTMLImageElement : : finalize ( )
{
Base : : finalize ( ) ;
2023-08-23 18:58:42 +02:00
document ( ) . unregister_viewport_client ( * this ) ;
2023-07-29 10:20:53 +02:00
}
2023-08-07 08:41:28 +02:00
void HTMLImageElement : : initialize ( JS : : Realm & realm )
2023-01-10 06:28:20 -05:00
{
2023-08-07 08:41:28 +02:00
Base : : initialize ( realm ) ;
2024-03-16 13:13:08 +01:00
WEB_SET_PROTOTYPE_FOR_INTERFACE ( HTMLImageElement ) ;
2023-01-28 12:33:35 -05:00
2023-12-15 15:41:28 +01:00
m_current_request = ImageRequest : : create ( realm , document ( ) . page ( ) ) ;
2023-08-18 12:56:21 +02:00
}
2023-08-23 18:58:42 +02:00
void HTMLImageElement : : adopted_from ( DOM : : Document & old_document )
{
old_document . unregister_viewport_client ( * this ) ;
document ( ) . register_viewport_client ( * this ) ;
}
2023-08-18 12:56:21 +02:00
void HTMLImageElement : : visit_edges ( Cell : : Visitor & visitor )
{
Base : : visit_edges ( visitor ) ;
visitor . visit ( m_current_request ) ;
visitor . visit ( m_pending_request ) ;
2023-11-21 18:50:09 +00:00
visit_lazy_loading_element ( visitor ) ;
2023-01-10 06:28:20 -05:00
}
2020-07-26 20:01:35 +02:00
void HTMLImageElement : : apply_presentational_hints ( CSS : : StyleProperties & style ) const
2020-07-22 01:16:27 +02:00
{
for_each_attribute ( [ & ] ( auto & name , auto & value ) {
2024-04-10 21:44:11 -04:00
if ( name = = HTML : : AttributeNames : : hspace ) {
2022-03-26 11:43:27 +01:00
if ( auto parsed_value = parse_dimension_value ( value ) ) {
style . set_property ( CSS : : PropertyID : : MarginLeft , * parsed_value ) ;
style . set_property ( CSS : : PropertyID : : MarginRight , * parsed_value ) ;
}
} else if ( name = = HTML : : AttributeNames : : vspace ) {
if ( auto parsed_value = parse_dimension_value ( value ) ) {
style . set_property ( CSS : : PropertyID : : MarginTop , * parsed_value ) ;
style . set_property ( CSS : : PropertyID : : MarginBottom , * parsed_value ) ;
}
2020-07-22 01:16:27 +02:00
}
} ) ;
}
2024-02-03 09:33:33 -05:00
void HTMLImageElement : : form_associated_element_attribute_changed ( FlyString const & name , Optional < String > const & value )
2019-10-06 23:03:51 +11:00
{
2023-05-11 19:23:36 +02:00
if ( name = = HTML : : AttributeNames : : crossorigin ) {
2023-11-19 18:10:36 +13:00
m_cors_setting = cors_setting_attribute_from_keyword ( value ) ;
2023-05-11 19:23:36 +02:00
}
if ( name . is_one_of ( HTML : : AttributeNames : : src , HTML : : AttributeNames : : srcset ) ) {
update_the_image_data ( true ) . release_value_but_fixme_should_propagate_errors ( ) ;
2023-04-01 15:15:23 +02:00
}
2022-09-07 16:46:05 +02:00
if ( name = = HTML : : AttributeNames : : alt ) {
if ( layout_node ( ) )
2024-02-18 20:10:37 -05:00
did_update_alt_text ( verify_cast < Layout : : ImageBox > ( * layout_node ( ) ) ) ;
2022-09-07 16:46:05 +02:00
}
2020-06-02 20:27:26 +02:00
}
2022-10-17 14:41:50 +02:00
JS : : GCPtr < Layout : : Node > HTMLImageElement : : create_layout_node ( NonnullRefPtr < CSS : : StyleProperties > style )
2019-10-05 23:20:35 +02:00
{
2023-05-12 07:17:01 +02:00
return heap ( ) . allocate_without_realm < Layout : : ImageBox > ( document ( ) , * this , move ( style ) , * this ) ;
2020-06-13 22:22:54 +02:00
}
2023-11-24 14:45:45 +01:00
RefPtr < Gfx : : ImmutableBitmap > HTMLImageElement : : immutable_bitmap ( ) const
2023-05-12 07:17:01 +02:00
{
return current_image_bitmap ( ) ;
}
2023-11-26 17:39:21 +01:00
RefPtr < Gfx : : Bitmap const > HTMLImageElement : : bitmap ( ) const
{
if ( auto immutable_bitmap = this - > immutable_bitmap ( ) )
return immutable_bitmap - > bitmap ( ) ;
return { } ;
}
2024-02-18 20:10:37 -05:00
bool HTMLImageElement : : is_image_available ( ) const
{
return m_current_request & & m_current_request - > is_available ( ) ;
}
2023-05-20 16:27:31 +02:00
Optional < CSSPixels > HTMLImageElement : : intrinsic_width ( ) const
{
if ( auto image_data = m_current_request - > image_data ( ) )
return image_data - > intrinsic_width ( ) ;
return { } ;
}
Optional < CSSPixels > HTMLImageElement : : intrinsic_height ( ) const
{
if ( auto image_data = m_current_request - > image_data ( ) )
return image_data - > intrinsic_height ( ) ;
return { } ;
}
2023-09-03 17:33:58 -05:00
Optional < CSSPixelFraction > HTMLImageElement : : intrinsic_aspect_ratio ( ) const
2023-05-20 16:27:31 +02:00
{
if ( auto image_data = m_current_request - > image_data ( ) )
return image_data - > intrinsic_aspect_ratio ( ) ;
return { } ;
}
2023-11-24 14:45:45 +01:00
RefPtr < Gfx : : ImmutableBitmap > HTMLImageElement : : current_image_bitmap ( Gfx : : IntSize size ) const
2019-10-05 23:41:14 +02:00
{
2023-05-11 19:23:36 +02:00
if ( auto data = m_current_request - > image_data ( ) )
2023-05-20 16:40:44 +02:00
return data - > bitmap ( m_current_frame_index , size ) ;
2023-05-11 19:23:36 +02:00
return nullptr ;
2019-10-05 23:41:14 +02:00
}
2019-12-18 20:57:18 +01:00
2023-05-12 07:17:01 +02:00
void HTMLImageElement : : set_visible_in_viewport ( bool )
{
// FIXME: Loosen grip on image data when it's not visible, e.g via volatile memory.
}
2022-02-25 18:42:37 +01:00
// https://html.spec.whatwg.org/multipage/embedded-content.html#dom-img-width
unsigned HTMLImageElement : : width ( ) const
{
const_cast < DOM : : Document & > ( document ( ) ) . update_layout ( ) ;
// Return the rendered width of the image, in CSS pixels, if the image is being rendered.
2023-04-20 16:01:16 +01:00
if ( auto * paintable_box = this - > paintable_box ( ) )
2023-06-12 21:37:35 +03:00
return paintable_box - > content_width ( ) . to_int ( ) ;
2022-02-25 18:42:37 +01:00
2022-04-12 13:28:52 -03:00
// NOTE: This step seems to not be in the spec, but all browsers do it.
2024-01-16 19:04:45 +01:00
if ( auto width_attr = get_attribute ( HTML : : AttributeNames : : width ) ; width_attr . has_value ( ) ) {
if ( auto converted = width_attr - > to_number < unsigned > ( ) ; converted . has_value ( ) )
return * converted ;
}
2022-04-12 13:28:52 -03:00
2022-02-25 18:42:37 +01:00
// ...or else the density-corrected intrinsic width and height of the image, in CSS pixels,
// if the image has intrinsic dimensions and is available but not being rendered.
2023-05-12 07:17:01 +02:00
if ( auto bitmap = current_image_bitmap ( ) )
return bitmap - > width ( ) ;
2022-02-25 18:42:37 +01:00
// ...or else 0, if the image is not available or does not have intrinsic dimensions.
return 0 ;
}
2023-05-25 20:37:57 +01:00
WebIDL : : ExceptionOr < void > HTMLImageElement : : set_width ( unsigned width )
2022-02-25 18:42:37 +01:00
{
2023-10-08 11:42:00 +13:00
return set_attribute ( HTML : : AttributeNames : : width , MUST ( String : : number ( width ) ) ) ;
2022-02-25 18:42:37 +01:00
}
// https://html.spec.whatwg.org/multipage/embedded-content.html#dom-img-height
unsigned HTMLImageElement : : height ( ) const
{
const_cast < DOM : : Document & > ( document ( ) ) . update_layout ( ) ;
// Return the rendered height of the image, in CSS pixels, if the image is being rendered.
2023-04-20 16:01:16 +01:00
if ( auto * paintable_box = this - > paintable_box ( ) )
2023-06-12 21:37:35 +03:00
return paintable_box - > content_height ( ) . to_int ( ) ;
2022-02-25 18:42:37 +01:00
2022-04-12 13:28:52 -03:00
// NOTE: This step seems to not be in the spec, but all browsers do it.
2024-01-16 19:04:45 +01:00
if ( auto height_attr = get_attribute ( HTML : : AttributeNames : : height ) ; height_attr . has_value ( ) ) {
if ( auto converted = height_attr - > to_number < unsigned > ( ) ; converted . has_value ( ) )
return * converted ;
}
2022-04-12 13:28:52 -03:00
2022-02-25 18:42:37 +01:00
// ...or else the density-corrected intrinsic height and height of the image, in CSS pixels,
// if the image has intrinsic dimensions and is available but not being rendered.
2023-05-12 07:17:01 +02:00
if ( auto bitmap = current_image_bitmap ( ) )
return bitmap - > height ( ) ;
2022-02-25 18:42:37 +01:00
// ...or else 0, if the image is not available or does not have intrinsic dimensions.
return 0 ;
}
2023-05-25 20:37:57 +01:00
WebIDL : : ExceptionOr < void > HTMLImageElement : : set_height ( unsigned height )
2022-02-25 18:42:37 +01:00
{
2023-10-08 11:42:00 +13:00
return set_attribute ( HTML : : AttributeNames : : height , MUST ( String : : number ( height ) ) ) ;
2022-02-25 18:42:37 +01:00
}
2022-04-12 13:20:43 -03:00
// https://html.spec.whatwg.org/multipage/embedded-content.html#dom-img-naturalwidth
unsigned HTMLImageElement : : natural_width ( ) const
{
// Return the density-corrected intrinsic width of the image, in CSS pixels,
// if the image has intrinsic dimensions and is available.
2023-05-12 07:17:01 +02:00
if ( auto bitmap = current_image_bitmap ( ) )
return bitmap - > width ( ) ;
2022-04-12 13:20:43 -03:00
// ...or else 0.
return 0 ;
}
// https://html.spec.whatwg.org/multipage/embedded-content.html#dom-img-naturalheight
unsigned HTMLImageElement : : natural_height ( ) const
{
// Return the density-corrected intrinsic height of the image, in CSS pixels,
// if the image has intrinsic dimensions and is available.
2023-05-12 07:17:01 +02:00
if ( auto bitmap = current_image_bitmap ( ) )
return bitmap - > height ( ) ;
2022-04-12 13:20:43 -03:00
// ...or else 0.
return 0 ;
}
2022-10-03 14:54:27 +02:00
// https://html.spec.whatwg.org/multipage/embedded-content.html#dom-img-complete
bool HTMLImageElement : : complete ( ) const
{
// The IDL attribute complete must return true if any of the following conditions is true:
// - Both the src attribute and the srcset attribute are omitted.
if ( ! has_attribute ( HTML : : AttributeNames : : src ) & & ! has_attribute ( HTML : : AttributeNames : : srcset ) )
return true ;
// - The srcset attribute is omitted and the src attribute's value is the empty string.
2023-10-10 15:00:58 +03:30
if ( ! has_attribute ( HTML : : AttributeNames : : srcset ) & & attribute ( HTML : : AttributeNames : : src ) . value ( ) . is_empty ( ) )
2022-10-03 14:54:27 +02:00
return true ;
// - The img element's current request's state is completely available and its pending request is null.
2023-07-11 07:53:09 +02:00
if ( m_current_request - > state ( ) = = ImageRequest : : State : : CompletelyAvailable & & ! m_pending_request )
return true ;
2022-10-03 14:54:27 +02:00
// - The img element's current request's state is broken and its pending request is null.
2023-07-11 07:53:09 +02:00
if ( m_current_request - > state ( ) = = ImageRequest : : State : : Broken & & ! m_pending_request )
2022-10-03 14:54:27 +02:00
return true ;
return false ;
}
2023-01-28 22:23:16 +00:00
Optional < ARIA : : Role > HTMLImageElement : : default_role ( ) const
2022-11-28 17:58:13 -06:00
{
// https://www.w3.org/TR/html-aria/#el-img
// https://www.w3.org/TR/html-aria/#el-img-no-alt
2023-10-10 15:00:58 +03:30
if ( ! alt ( ) . is_empty ( ) )
2023-01-28 22:23:16 +00:00
return ARIA : : Role : : img ;
2022-11-28 17:58:13 -06:00
// https://www.w3.org/TR/html-aria/#el-img-empty-alt
2023-01-28 22:23:16 +00:00
return ARIA : : Role : : presentation ;
2022-11-28 17:58:13 -06:00
}
2023-05-11 19:23:36 +02:00
// https://html.spec.whatwg.org/multipage/images.html#use-srcset-or-picture
bool HTMLImageElement : : uses_srcset_or_picture ( ) const
{
// An img element is said to use srcset or picture if it has a srcset attribute specified
// or if it has a parent that is a picture element.
return has_attribute ( HTML : : AttributeNames : : srcset ) | | ( parent ( ) & & is < HTMLPictureElement > ( * parent ( ) ) ) ;
}
2023-07-27 15:05:42 +02:00
// We batch handling of successfully fetched images to avoid interleaving 1 image, 1 layout, 1 image, 1 layout, etc.
// The processing timer is 1ms instead of 0ms, since layout is driven by a 0ms timer, and if we use 0ms here,
// the event loop will process them in insertion order. This is a bit of a hack, but it works.
struct BatchingDispatcher {
public :
BatchingDispatcher ( )
2024-04-16 20:34:01 +02:00
: m_timer ( Core : : Timer : : create_single_shot ( 1 , [ this ] { process ( ) ; } ) )
2023-07-27 15:05:42 +02:00
{
}
void enqueue ( JS : : SafeFunction < void ( ) > callback )
{
2023-12-28 10:24:00 +01:00
// NOTE: We don't want to flush the queue on every image load, since that would be slow.
// However, we don't want to keep growing the batch forever either.
static constexpr size_t max_loads_to_batch_before_flushing = 16 ;
2023-07-27 15:05:42 +02:00
m_queue . append ( move ( callback ) ) ;
2023-12-28 10:24:00 +01:00
if ( m_queue . size ( ) < max_loads_to_batch_before_flushing )
m_timer - > restart ( ) ;
2023-07-27 15:05:42 +02:00
}
private :
void process ( )
{
auto queue = move ( m_queue ) ;
for ( auto & callback : queue )
callback ( ) ;
}
NonnullRefPtr < Core : : Timer > m_timer ;
Vector < JS : : SafeFunction < void ( ) > > m_queue ;
} ;
static BatchingDispatcher & batching_dispatcher ( )
{
static BatchingDispatcher dispatcher ;
return dispatcher ;
}
2023-05-11 19:23:36 +02:00
// https://html.spec.whatwg.org/multipage/images.html#update-the-image-data
2023-06-11 08:08:44 +02:00
ErrorOr < void > HTMLImageElement : : update_the_image_data ( bool restart_animations , bool maybe_omit_events )
2023-05-11 19:23:36 +02:00
{
// 1. If the element's node document is not fully active, then:
if ( ! document ( ) . is_fully_active ( ) ) {
// FIXME: 1. Continue running this algorithm in parallel.
// FIXME: 2. Wait until the element's node document is fully active.
// FIXME: 3. If another instance of this algorithm for this img element was started after this instance
// (even if it aborted and is no longer running), then return.
// FIXME: 4. Queue a microtask to continue this algorithm.
}
// 2. FIXME: If the user agent cannot support images, or its support for images has been disabled,
// then abort the image request for the current request and the pending request,
// set current request's state to unavailable, set pending request to null, and return.
2023-06-11 08:08:44 +02:00
// 3. Let previous URL be the current request's current URL.
auto previous_url = m_current_request - > current_url ( ) ;
// 4. Let selected source be null and selected pixel density be undefined.
2023-05-11 19:23:36 +02:00
Optional < String > selected_source ;
Optional < float > selected_pixel_density ;
2023-06-11 08:08:44 +02:00
// 5. If the element does not use srcset or picture
2023-05-11 19:23:36 +02:00
// and it has a src attribute specified whose value is not the empty string,
// then set selected source to the value of the element's src attribute
// and set selected pixel density to 1.0.
2023-12-29 18:25:04 +01:00
auto maybe_src_attribute = attribute ( HTML : : AttributeNames : : src ) ;
if ( ! uses_srcset_or_picture ( ) & & maybe_src_attribute . has_value ( ) & & ! maybe_src_attribute . value ( ) . is_empty ( ) ) {
selected_source = maybe_src_attribute . release_value ( ) ;
2023-05-11 19:23:36 +02:00
selected_pixel_density = 1.0f ;
}
2023-06-11 08:08:44 +02:00
// 6. Set the element's last selected source to selected source.
2023-05-11 19:23:36 +02:00
m_last_selected_source = selected_source ;
2023-06-11 08:08:44 +02:00
// 7. If selected source is not null, then:
2023-05-11 19:23:36 +02:00
if ( selected_source . has_value ( ) ) {
// 1. Parse selected source, relative to the element's node document.
// If that is not successful, then abort this inner set of steps.
// Otherwise, let urlString be the resulting URL string.
2024-04-05 09:26:03 +02:00
auto url_string = document ( ) . parse_url ( selected_source . value ( ) ) ;
2023-05-11 19:23:36 +02:00
if ( ! url_string . is_valid ( ) )
2023-06-11 08:08:44 +02:00
goto after_step_7 ;
2023-05-11 19:23:36 +02:00
// 2. Let key be a tuple consisting of urlString, the img element's crossorigin attribute's mode,
// and, if that mode is not No CORS, the node document's origin.
ListOfAvailableImages : : Key key ;
key . url = url_string ;
key . mode = m_cors_setting ;
key . origin = document ( ) . origin ( ) ;
// 3. If the list of available images contains an entry for key, then:
2023-12-23 18:17:04 +01:00
if ( auto * entry = document ( ) . list_of_available_images ( ) . get ( key ) ) {
2023-05-11 19:23:36 +02:00
// 1. Set the ignore higher-layer caching flag for that entry.
entry - > ignore_higher_layer_caching = true ;
// 2. Abort the image request for the current request and the pending request.
2023-06-11 06:53:40 +02:00
abort_the_image_request ( realm ( ) , m_current_request ) ;
abort_the_image_request ( realm ( ) , m_pending_request ) ;
2023-05-11 19:23:36 +02:00
// 3. Set pending request to null.
m_pending_request = nullptr ;
// 4. Let current request be a new image request whose image data is that of the entry and whose state is completely available.
2023-12-15 15:41:28 +01:00
m_current_request = ImageRequest : : create ( realm ( ) , document ( ) . page ( ) ) ;
2023-05-11 19:23:36 +02:00
m_current_request - > set_image_data ( entry - > image_data ) ;
m_current_request - > set_state ( ImageRequest : : State : : CompletelyAvailable ) ;
// 5. Prepare current request for presentation given img.
m_current_request - > prepare_for_presentation ( * this ) ;
// 6. Set current request's current pixel density to selected pixel density.
// FIXME: Spec bug! `selected_pixel_density` can be undefined here, per the spec.
// That's why we value_or(1.0f) it.
m_current_request - > set_current_pixel_density ( selected_pixel_density . value_or ( 1.0f ) ) ;
// 7. Queue an element task on the DOM manipulation task source given the img element and following steps:
2023-06-11 08:08:44 +02:00
queue_an_element_task ( HTML : : Task : : Source : : DOMManipulation , [ this , restart_animations , maybe_omit_events , url_string , previous_url ] {
2023-05-11 19:23:36 +02:00
// 1. If restart animation is set, then restart the animation.
if ( restart_animations )
restart_the_animation ( ) ;
// 2. Set current request's current URL to urlString.
2023-08-18 14:11:55 +02:00
m_current_request - > set_current_url ( realm ( ) , url_string ) ;
2023-05-11 19:23:36 +02:00
2023-06-11 08:08:44 +02:00
// 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 )
2023-08-13 13:05:26 +02:00
dispatch_event ( DOM : : Event : : create ( realm ( ) , HTML : : EventNames : : load ) ) ;
2023-05-11 19:23:36 +02:00
} ) ;
// 8. Abort the update the image data algorithm.
return { } ;
}
}
2023-06-11 08:08:44 +02:00
after_step_7 :
// 8. Queue a microtask to perform the rest of this algorithm, allowing the task that invoked this algorithm to continue.
2024-04-13 16:32:39 +02:00
queue_a_microtask ( & document ( ) , JS : : create_heap_function ( this - > heap ( ) , [ this , restart_animations , maybe_omit_events , previous_url ] ( ) mutable {
2023-06-11 08:08:44 +02:00
// FIXME: 9. If another instance of this algorithm for this img element was started after this instance
2023-05-11 19:23:36 +02:00
// (even if it aborted and is no longer running), then return.
2023-06-11 08:08:44 +02:00
// 10. Let selected source and selected pixel density be
2023-05-11 19:23:36 +02:00
// the URL and pixel density that results from selecting an image source, respectively.
Optional < ImageSource > selected_source ;
Optional < float > pixel_density ;
if ( auto result = select_an_image_source ( ) ; result . has_value ( ) ) {
selected_source = result . value ( ) . source ;
pixel_density = result . value ( ) . pixel_density ;
}
2023-06-11 08:08:44 +02:00
// 11. If selected source is null, then:
2023-05-11 19:23:36 +02:00
if ( ! selected_source . has_value ( ) ) {
// 1. Set the current request's state to broken,
// abort the image request for the current request and the pending request,
// and set pending request to null.
m_current_request - > set_state ( ImageRequest : : State : : Broken ) ;
2023-06-11 06:53:40 +02:00
abort_the_image_request ( realm ( ) , m_current_request ) ;
abort_the_image_request ( realm ( ) , m_pending_request ) ;
2023-05-11 19:23:36 +02:00
m_pending_request = nullptr ;
// 2. Queue an element task on the DOM manipulation task source given the img element and the following steps:
2023-06-11 08:08:44 +02:00
queue_an_element_task ( HTML : : Task : : Source : : DOMManipulation , [ this , maybe_omit_events , previous_url ] {
2023-05-11 19:23:36 +02:00
// 1. Change the current request's current URL to the empty string.
2023-08-18 14:11:55 +02:00
m_current_request - > set_current_url ( realm ( ) , " " sv ) ;
2023-05-11 19:23:36 +02:00
2023-06-11 08:08:44 +02:00
// 2. If all of the following conditions are true:
// - the element has a src attribute or it uses srcset or picture; and
// - maybe omit events is not set or previousURL is not the empty string
if (
( has_attribute ( HTML : : AttributeNames : : src ) | | uses_srcset_or_picture ( ) )
& & ( ! maybe_omit_events | | m_current_request - > current_url ( ) ! = " " sv ) ) {
2023-08-13 13:05:26 +02:00
dispatch_event ( DOM : : Event : : create ( realm ( ) , HTML : : EventNames : : error ) ) ;
2023-05-11 19:23:36 +02:00
}
} ) ;
// 3. Return.
return ;
}
2023-06-11 08:08:44 +02:00
// 12. Parse selected source, relative to the element's node document, and let urlString be the resulting URL string.
2023-12-16 17:49:34 +03:30
auto url_string = document ( ) . parse_url ( selected_source . value ( ) . url . to_byte_string ( ) ) ;
2023-05-11 19:23:36 +02:00
// If that is not successful, then:
if ( ! url_string . is_valid ( ) ) {
// 1. Abort the image request for the current request and the pending request.
2023-06-11 06:53:40 +02:00
abort_the_image_request ( realm ( ) , m_current_request ) ;
abort_the_image_request ( realm ( ) , m_pending_request ) ;
2023-05-11 19:23:36 +02:00
// 2. Set the current request's state to broken.
m_current_request - > set_state ( ImageRequest : : State : : Broken ) ;
// 3. Set pending request to null.
m_pending_request = nullptr ;
// 4. Queue an element task on the DOM manipulation task source given the img element and the following steps:
2023-06-11 08:08:44 +02:00
queue_an_element_task ( HTML : : Task : : Source : : DOMManipulation , [ this , selected_source , maybe_omit_events , previous_url ] {
2023-05-11 19:23:36 +02:00
// 1. Change the current request's current URL to selected source.
2023-08-18 14:11:55 +02:00
m_current_request - > set_current_url ( realm ( ) , selected_source . value ( ) . url ) ;
2023-05-11 19:23:36 +02:00
2023-06-11 08:08:44 +02:00
// 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 )
2023-08-13 13:05:26 +02:00
dispatch_event ( DOM : : Event : : create ( realm ( ) , HTML : : EventNames : : error ) ) ;
2023-05-11 19:23:36 +02:00
} ) ;
// 5. Return.
return ;
}
2023-06-11 08:08:44 +02:00
// 13. If the pending request is not null and urlString is the same as the pending request's current URL, then return.
2023-05-11 19:23:36 +02:00
if ( m_pending_request & & url_string = = m_pending_request - > current_url ( ) )
return ;
2023-06-11 08:08:44 +02:00
// 14. If urlString is the same as the current request's current URL and current request's state is partially available,
2023-05-11 19:23:36 +02:00
// then abort the image request for the pending request,
// queue an element task on the DOM manipulation task source given the img element
// to restart the animation if restart animation is set, and return.
if ( url_string = = m_current_request - > current_url ( ) & & m_current_request - > state ( ) = = ImageRequest : : State : : PartiallyAvailable ) {
2023-06-11 06:53:40 +02:00
abort_the_image_request ( realm ( ) , m_pending_request ) ;
2023-05-11 19:23:36 +02:00
if ( restart_animations ) {
queue_an_element_task ( HTML : : Task : : Source : : DOMManipulation , [ this ] {
restart_the_animation ( ) ;
} ) ;
}
return ;
}
2023-06-11 08:08:44 +02:00
// 15. If the pending request is not null, then abort the image request for the pending request.
2023-06-11 06:53:40 +02:00
abort_the_image_request ( realm ( ) , m_pending_request ) ;
2023-05-11 19:23:36 +02:00
2023-06-11 14:46:54 +02:00
// AD-HOC: At this point we start deviating from the spec in order to allow sharing ImageRequest between
// multiple image elements (as well as CSS background-images, etc.)
2023-06-11 08:08:44 +02:00
// 16. Set image request to a new image request whose current URL is urlString.
2023-12-15 15:41:28 +01:00
auto image_request = ImageRequest : : create ( realm ( ) , document ( ) . page ( ) ) ;
2023-08-18 14:11:55 +02:00
image_request - > set_current_url ( realm ( ) , url_string ) ;
2023-05-11 19:23:36 +02:00
2023-06-11 08:08:44 +02:00
// 17. If current request's state is unavailable or broken, then set the current request to image request.
2023-05-11 19:23:36 +02:00
// Otherwise, set the pending request to image request.
if ( m_current_request - > state ( ) = = ImageRequest : : State : : Unavailable | | m_current_request - > state ( ) = = ImageRequest : : State : : Broken )
m_current_request = image_request ;
else
m_pending_request = image_request ;
2023-06-11 08:08:44 +02:00
// 23. Let delay load event be true if the img's lazy loading attribute is in the Eager state, or if scripting is disabled for the img, and false otherwise.
2023-11-21 18:50:09 +00:00
auto delay_load_event = lazy_loading_attribute ( ) = = LazyLoading : : Eager ;
2023-05-11 19:23:36 +02:00
2023-05-12 07:48:59 +02:00
// When delay load event is true, fetching the image must delay the load event of the element's node document
// until the task that is queued by the networking task source once the resource has been fetched (defined below) has been run.
if ( delay_load_event )
m_load_event_delayer . emplace ( document ( ) ) ;
2023-08-18 12:56:21 +02:00
add_callbacks_to_image_request ( * image_request , maybe_omit_events , url_string , previous_url ) ;
2023-05-11 19:23:36 +02:00
2023-06-11 14:46:54 +02:00
// AD-HOC: If the image request is already available or fetching, no need to start another fetch.
2023-06-14 12:45:56 +02:00
if ( image_request - > is_available ( ) | | image_request - > is_fetching ( ) )
2023-06-11 14:46:54 +02:00
return ;
2023-05-11 19:23:36 +02:00
2023-06-11 14:46:54 +02:00
// 18. Let request be the result of creating a potential-CORS request given urlString, "image",
// and the current state of the element's crossorigin content attribute.
auto request = create_potential_CORS_request ( vm ( ) , url_string , Fetch : : Infrastructure : : Request : : Destination : : Image , m_cors_setting ) ;
2023-05-11 19:23:36 +02:00
2023-06-11 14:46:54 +02:00
// 19. Set request's client to the element's node document's relevant settings object.
request - > set_client ( & document ( ) . relevant_settings_object ( ) ) ;
2023-05-11 19:23:36 +02:00
2023-06-11 14:46:54 +02:00
// 20. If the element uses srcset or picture, set request's initiator to "imageset".
if ( uses_srcset_or_picture ( ) )
request - > set_initiator ( Fetch : : Infrastructure : : Request : : Initiator : : ImageSet ) ;
2023-05-11 19:23:36 +02:00
2023-06-11 14:46:54 +02:00
// 21. Set request's referrer policy to the current state of the element's referrerpolicy attribute.
2024-03-05 09:35:25 -07:00
request - > set_referrer_policy ( ReferrerPolicy : : from_string ( get_attribute_value ( HTML : : AttributeNames : : referrerpolicy ) ) . value_or ( ReferrerPolicy : : ReferrerPolicy : : EmptyString ) ) ;
2023-05-11 19:23:36 +02:00
2023-06-11 14:46:54 +02:00
// FIXME: 22. Set request's priority to the current state of the element's fetchpriority attribute.
2023-05-11 19:23:36 +02:00
2023-07-07 00:00:06 +01:00
// 24. If the will lazy load element steps given the img return true, then:
2023-11-21 18:50:09 +00:00
if ( will_lazy_load_element ( ) ) {
2023-07-07 00:00:06 +01:00
// 1. Set the img's lazy load resumption steps to the rest of this algorithm starting with the step labeled fetch the image.
2023-09-25 18:47:34 +02:00
set_lazy_load_resumption_steps ( [ this , request , image_request ] ( ) {
2023-07-07 00:00:06 +01:00
image_request - > fetch_image ( realm ( ) , request ) ;
2023-09-25 18:47:34 +02:00
} ) ;
2023-07-07 00:00:06 +01:00
// 2. Start intersection-observing a lazy loading element for the img element.
document ( ) . start_intersection_observing_a_lazy_loading_element ( * this ) ;
// 3. Return.
return ;
}
2023-05-11 19:23:36 +02:00
2023-06-11 14:46:54 +02:00
image_request - > fetch_image ( realm ( ) , request ) ;
2024-04-13 16:32:39 +02:00
} ) ) ;
2023-06-11 14:46:54 +02:00
return { } ;
2023-05-11 19:23:36 +02:00
}
2024-03-18 16:22:27 +13:00
void HTMLImageElement : : add_callbacks_to_image_request ( JS : : NonnullGCPtr < ImageRequest > image_request , bool maybe_omit_events , URL : : URL const & url_string , URL : : URL const & previous_url )
2023-07-29 10:20:53 +02:00
{
image_request - > add_callbacks (
[ this , image_request , maybe_omit_events , url_string , previous_url ] ( ) {
batching_dispatcher ( ) . enqueue ( [ this , image_request , maybe_omit_events , url_string , previous_url ] {
VERIFY ( image_request - > shared_image_request ( ) ) ;
auto image_data = image_request - > shared_image_request ( ) - > image_data ( ) ;
image_request - > set_image_data ( image_data ) ;
ListOfAvailableImages : : Key key ;
key . url = url_string ;
key . mode = m_cors_setting ;
key . origin = document ( ) . origin ( ) ;
// 1. If image request is the pending request, abort the image request for the current request,
// upgrade the pending request to the current request
// and prepare image request for presentation given the img element.
if ( image_request = = m_pending_request ) {
abort_the_image_request ( realm ( ) , m_current_request ) ;
upgrade_pending_request_to_current_request ( ) ;
image_request - > prepare_for_presentation ( * this ) ;
}
// 2. Set image request to the completely available state.
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.
2023-12-23 18:17:04 +01:00
document ( ) . list_of_available_images ( ) . add ( key , * image_data , true ) ;
2023-07-29 10:20:53 +02:00
// 4. 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 )
2023-08-13 13:05:26 +02:00
dispatch_event ( DOM : : Event : : create ( realm ( ) , HTML : : EventNames : : load ) ) ;
2023-07-29 10:20:53 +02:00
set_needs_style_update ( true ) ;
document ( ) . set_needs_layout ( ) ;
if ( image_data - > is_animated ( ) & & image_data - > frame_count ( ) > 1 ) {
m_current_frame_index = 0 ;
m_animation_timer - > set_interval ( image_data - > frame_duration ( 0 ) ) ;
m_animation_timer - > start ( ) ;
}
m_load_event_delayer . clear ( ) ;
} ) ;
} ,
[ this , image_request , maybe_omit_events , url_string , previous_url ] ( ) {
// The image data is not in a supported file format;
// the user agent must set image request's state to broken,
image_request - > set_state ( ImageRequest : : State : : Broken ) ;
// abort the image request for the current request and the pending request,
abort_the_image_request ( realm ( ) , m_current_request ) ;
abort_the_image_request ( realm ( ) , m_pending_request ) ;
// upgrade the pending request to the current request if image request is the pending request,
if ( image_request = = m_pending_request )
upgrade_pending_request_to_current_request ( ) ;
// and then, if maybe omit events is not set or previousURL is not equal to urlString,
// queue an element task on the DOM manipulation task source given the img element
// to fire an event named error at the img element.
if ( ! maybe_omit_events | | previous_url ! = url_string )
2023-08-13 13:05:26 +02:00
dispatch_event ( DOM : : Event : : create ( realm ( ) , HTML : : EventNames : : error ) ) ;
2023-07-29 10:20:53 +02:00
m_load_event_delayer . clear ( ) ;
} ) ;
}
2023-08-23 18:58:42 +02:00
void HTMLImageElement : : did_set_viewport_rect ( CSSPixelRect const & viewport_rect )
2023-07-29 10:20:53 +02:00
{
if ( viewport_rect . size ( ) = = m_last_seen_viewport_size )
return ;
m_last_seen_viewport_size = viewport_rect . size ( ) ;
batching_dispatcher ( ) . enqueue ( [ this ] {
react_to_changes_in_the_environment ( ) ;
} ) ;
}
// https://html.spec.whatwg.org/multipage/images.html#img-environment-changes
void HTMLImageElement : : react_to_changes_in_the_environment ( )
{
// FIXME: 1. Await a stable state.
// The synchronous section consists of all the remaining steps of this algorithm
// until the algorithm says the synchronous section has ended.
// (Steps in synchronous sections are marked with ⌛.)
// 2. ⌛ If the img element does not use srcset or picture,
// its node document is not fully active,
// FIXME: has image data whose resource type is multipart/x-mixed-replace,
// or the pending request is not null,
// then return.
if ( ! uses_srcset_or_picture ( ) | | ! document ( ) . is_fully_active ( ) | | m_pending_request )
return ;
// 3. ⌛ Let selected source and selected pixel density be the URL and pixel density
// that results from selecting an image source, respectively.
Optional < String > selected_source ;
Optional < float > pixel_density ;
if ( auto result = select_an_image_source ( ) ; result . has_value ( ) ) {
selected_source = result . value ( ) . source . url ;
pixel_density = result . value ( ) . pixel_density ;
}
// 4. ⌛ If selected source is null, then return.
if ( ! selected_source . has_value ( ) )
return ;
// 5. ⌛ If selected source and selected pixel density are the same
// as the element's last selected source and current pixel density, then return.
if ( selected_source = = m_last_selected_source & & pixel_density = = m_current_request - > current_pixel_density ( ) )
return ;
// 6. ⌛ Parse selected source, relative to the element's node document,
// and let urlString be the resulting URL string. If that is not successful, then return.
auto url_string = document ( ) . parse_url ( selected_source . value ( ) ) ;
if ( ! url_string . is_valid ( ) )
return ;
// 7. ⌛ Let corsAttributeState be the state of the element's crossorigin content attribute.
auto cors_attribute_state = m_cors_setting ;
// 8. ⌛ Let origin be the img element's node document's origin.
auto origin = document ( ) . origin ( ) ;
// 9. ⌛ Let client be the img element's node document's relevant settings object.
auto & client = document ( ) . relevant_settings_object ( ) ;
// 10. ⌛ Let key be a tuple consisting of urlString, corsAttributeState, and, if corsAttributeState is not No CORS, origin.
ListOfAvailableImages : : Key key ;
key . url = url_string ;
key . mode = m_cors_setting ;
if ( cors_attribute_state ! = CORSSettingAttribute : : NoCORS )
key . origin = document ( ) . origin ( ) ;
// 11. ⌛ Let image request be a new image request whose current URL is urlString
2023-12-15 15:41:28 +01:00
auto image_request = ImageRequest : : create ( realm ( ) , document ( ) . page ( ) ) ;
2023-08-18 14:11:55 +02:00
image_request - > set_current_url ( realm ( ) , url_string ) ;
2023-07-29 10:20:53 +02:00
// 12. ⌛ Let the element's pending request be image request.
m_pending_request = image_request ;
// FIXME: 13. End the synchronous section, continuing the remaining steps in parallel.
2023-12-12 20:07:19 +01:00
auto step_15 = [ this ] ( String const & selected_source , JS : : NonnullGCPtr < ImageRequest > image_request , ListOfAvailableImages : : Key const & key , JS : : NonnullGCPtr < DecodedImageData > image_data ) {
2023-08-04 05:10:29 -05:00
// 15. 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 , image_request , key , image_data ] {
// 1. FIXME: If the img element has experienced relevant mutations since this algorithm started, then let pending request be null and abort these steps.
// AD-HOC: Check if we have a pending request still, otherwise we will crash when upgrading the request. This will happen if the image has experienced mutations,
// but since the pending request may be set by another task soon after it is cleared, this check is probably not sufficient.
if ( ! m_pending_request )
return ;
// 2. Let the img element's last selected source be selected source and the img element's current pixel density be selected pixel density.
m_last_selected_source = selected_source ;
// 3. Set the image request's state to completely available.
image_request - > set_state ( ImageRequest : : State : : CompletelyAvailable ) ;
// 4. Add the image to the list of available images using the key key, with the ignore higher-layer caching flag set.
2023-12-23 18:17:04 +01:00
document ( ) . list_of_available_images ( ) . add ( key , image_data , true ) ;
2023-08-04 05:10:29 -05:00
// 5. Upgrade the pending request to the current request.
upgrade_pending_request_to_current_request ( ) ;
// 6. Prepare image request for presentation given the img element.
image_request - > prepare_for_presentation ( * this ) ;
// FIXME: This is ad-hoc, updating the layout here should probably be handled by prepare_for_presentation().
set_needs_style_update ( true ) ;
document ( ) . set_needs_layout ( ) ;
// 7. Fire an event named load at the img element.
2023-08-13 13:05:26 +02:00
dispatch_event ( DOM : : Event : : create ( realm ( ) , HTML : : EventNames : : load ) ) ;
2023-08-04 05:10:29 -05:00
} ) ;
} ;
2023-07-29 10:20:53 +02:00
// 14. If the list of available images contains an entry for key, then set image request's image data to that of the entry.
// Continue to the next step.
2023-12-23 18:17:04 +01:00
if ( auto * entry = document ( ) . list_of_available_images ( ) . get ( key ) ) {
2023-07-29 10:20:53 +02:00
image_request - > set_image_data ( entry - > image_data ) ;
2023-08-18 12:56:21 +02:00
step_15 ( selected_source . value ( ) , * image_request , key , entry - > image_data ) ;
2023-07-29 10:20:53 +02:00
}
// Otherwise:
else {
// 1. Let request be the result of creating a potential-CORS request given urlString, "image", and corsAttributeState.
auto request = create_potential_CORS_request ( vm ( ) , url_string , Fetch : : Infrastructure : : Request : : Destination : : Image , m_cors_setting ) ;
// 2. Set request's client to client, initiator to "imageset", and set request's synchronous flag.
request - > set_client ( & client ) ;
request - > set_initiator ( Fetch : : Infrastructure : : Request : : Initiator : : ImageSet ) ;
// 3. Set request's referrer policy to the current state of the element's referrerpolicy attribute.
2024-03-05 09:35:25 -07:00
request - > set_referrer_policy ( ReferrerPolicy : : from_string ( get_attribute_value ( HTML : : AttributeNames : : referrerpolicy ) ) . value_or ( ReferrerPolicy : : ReferrerPolicy : : EmptyString ) ) ;
2023-07-29 10:20:53 +02:00
// FIXME: 4. Set request's priority to the current state of the element's fetchpriority attribute.
2023-08-04 05:10:29 -05:00
// Set the callbacks to handle steps 6 and 7 before starting the fetch request.
image_request - > add_callbacks (
[ step_15 , selected_source = selected_source . value ( ) , image_request , key ] ( ) mutable {
// 6. If response's unsafe response is a network error
// NOTE: This is handled in the second callback below.
// FIXME: or if the image format is unsupported (as determined by applying the image sniffing rules, again as mentioned earlier),
// or if the user agent is able to determine that image request's image is corrupted in some
// fatal way such that the image dimensions cannot be obtained,
// NOTE: This is also handled in the other callback.
// FIXME: or if the resource type is multipart/x-mixed-replace,
// then let pending request be null and abort these steps.
batching_dispatcher ( ) . enqueue ( [ step_15 , selected_source = move ( selected_source ) , image_request , key ] {
// 7. Otherwise, response's unsafe response is image request's image data. It can be either CORS-same-origin
// or CORS-cross-origin; this affects the image's interaction with other APIs (e.g., when used on a canvas).
VERIFY ( image_request - > shared_image_request ( ) ) ;
auto image_data = image_request - > shared_image_request ( ) - > image_data ( ) ;
image_request - > set_image_data ( image_data ) ;
2023-12-12 20:07:19 +01:00
step_15 ( selected_source , image_request , key , * image_data ) ;
2023-08-04 05:10:29 -05:00
} ) ;
} ,
[ this ] ( ) {
// 6. If response's unsafe response is a network error
// or if the image format is unsupported (as determined by applying the image sniffing rules, again as mentioned earlier),
// ...
// or if the user agent is able to determine that image request's image is corrupted in some
// fatal way such that the image dimensions cannot be obtained,
m_pending_request = nullptr ;
} ) ;
2023-07-29 10:20:53 +02:00
// 5. Let response be the result of fetching request.
image_request - > fetch_image ( realm ( ) , request ) ;
}
}
2023-05-11 19:23:36 +02:00
// https://html.spec.whatwg.org/multipage/images.html#upgrade-the-pending-request-to-the-current-request
void HTMLImageElement : : upgrade_pending_request_to_current_request ( )
{
// 1. Let the img element's current request be the pending request.
VERIFY ( m_pending_request ) ;
m_current_request = m_pending_request ;
// 2. Let the img element's pending request be null.
m_pending_request = nullptr ;
}
void HTMLImageElement : : handle_failed_fetch ( )
{
// AD-HOC
2023-08-13 13:05:26 +02:00
dispatch_event ( DOM : : Event : : create ( realm ( ) , HTML : : EventNames : : error ) ) ;
2023-05-11 19:23:36 +02:00
}
// https://html.spec.whatwg.org/multipage/rendering.html#restart-the-animation
void HTMLImageElement : : restart_the_animation ( )
{
m_current_frame_index = 0 ;
2024-03-16 14:47:57 +01:00
auto image_data = m_current_request - > image_data ( ) ;
if ( image_data & & image_data - > frame_count ( ) > 1 ) {
m_animation_timer - > start ( ) ;
} else {
m_animation_timer - > stop ( ) ;
}
2023-05-11 19:23:36 +02:00
}
// https://html.spec.whatwg.org/multipage/images.html#update-the-source-set
static void update_the_source_set ( DOM : : Element & element )
{
// When asked to update the source set for a given img or link element el, user agents must do the following:
VERIFY ( is < HTMLImageElement > ( element ) | | is < HTMLLinkElement > ( element ) ) ;
// 1. Set el's source set to an empty source set.
if ( is < HTMLImageElement > ( element ) )
static_cast < HTMLImageElement & > ( element ) . set_source_set ( SourceSet { } ) ;
else if ( is < HTMLLinkElement > ( element ) )
TODO ( ) ;
// 2. Let elements be « el ».
JS : : MarkedVector < DOM : : Element * > elements ( element . heap ( ) ) ;
elements . append ( & element ) ;
// 3. If el is an img element whose parent node is a picture element,
// then replace the contents of elements with el's parent node's child elements, retaining relative order.
if ( is < HTMLImageElement > ( element ) & & element . parent ( ) & & is < HTMLPictureElement > ( * element . parent ( ) ) ) {
elements . clear ( ) ;
element . parent ( ) - > for_each_child_of_type < DOM : : Element > ( [ & ] ( auto & child ) {
elements . append ( & child ) ;
} ) ;
}
// 4. For each child in elements:
for ( auto child : elements ) {
// 1. If child is el:
if ( child = = & element ) {
// 1. Let default source be the empty string.
String default_source ;
// 2. Let srcset be the empty string.
String srcset ;
// 3. Let sizes be the empty string.
String sizes ;
// 4. If el is an img element that has a srcset attribute, then set srcset to that attribute's value.
if ( is < HTMLImageElement > ( element ) ) {
2023-10-10 15:00:58 +03:30
if ( auto srcset_value = element . attribute ( HTML : : AttributeNames : : srcset ) ; srcset_value . has_value ( ) )
srcset = srcset_value . release_value ( ) ;
2023-05-11 19:23:36 +02:00
}
// 5. Otherwise, if el is a link element that has an imagesrcset attribute, then set srcset to that attribute's value.
else if ( is < HTMLLinkElement > ( element ) ) {
2023-10-10 15:00:58 +03:30
if ( auto imagesrcset_value = element . attribute ( HTML : : AttributeNames : : imagesrcset ) ; imagesrcset_value . has_value ( ) )
srcset = imagesrcset_value . release_value ( ) ;
2023-05-11 19:23:36 +02:00
}
// 6. If el is an img element that has a sizes attribute, then set sizes to that attribute's value.
if ( is < HTMLImageElement > ( element ) ) {
2023-10-10 15:00:58 +03:30
if ( auto sizes_value = element . attribute ( HTML : : AttributeNames : : sizes ) ; sizes_value . has_value ( ) )
sizes = sizes_value . release_value ( ) ;
2023-05-11 19:23:36 +02:00
}
// 7. Otherwise, if el is a link element that has an imagesizes attribute, then set sizes to that attribute's value.
else if ( is < HTMLLinkElement > ( element ) ) {
2023-10-10 15:00:58 +03:30
if ( auto imagesizes_value = element . attribute ( HTML : : AttributeNames : : imagesizes ) ; imagesizes_value . has_value ( ) )
sizes = imagesizes_value . release_value ( ) ;
2023-05-11 19:23:36 +02:00
}
// 8. If el is an img element that has a src attribute, then set default source to that attribute's value.
if ( is < HTMLImageElement > ( element ) ) {
2023-10-10 15:00:58 +03:30
if ( auto src_value = element . attribute ( HTML : : AttributeNames : : src ) ; src_value . has_value ( ) )
default_source = src_value . release_value ( ) ;
2023-05-11 19:23:36 +02:00
}
// 9. Otherwise, if el is a link element that has an href attribute, then set default source to that attribute's value.
else if ( is < HTMLLinkElement > ( element ) ) {
2023-10-10 15:00:58 +03:30
if ( auto href_value = element . attribute ( HTML : : AttributeNames : : href ) ; href_value . has_value ( ) )
default_source = href_value . release_value ( ) ;
2023-05-11 19:23:36 +02:00
}
// 10. Let el's source set be the result of creating a source set given default source, srcset, and sizes.
if ( is < HTMLImageElement > ( element ) )
2023-07-28 15:52:06 +02:00
static_cast < HTMLImageElement & > ( element ) . set_source_set ( SourceSet : : create ( element , default_source , srcset , sizes ) ) ;
2023-05-11 19:23:36 +02:00
else if ( is < HTMLLinkElement > ( element ) )
TODO ( ) ;
return ;
}
// 2. If child is not a source element, then continue.
if ( ! is < HTMLSourceElement > ( child ) )
continue ;
// 3. If child does not have a srcset attribute, continue to the next child.
if ( ! child - > has_attribute ( HTML : : AttributeNames : : srcset ) )
continue ;
// 4. Parse child's srcset attribute and let the returned source set be source set.
2024-01-16 19:04:45 +01:00
auto source_set = parse_a_srcset_attribute ( child - > get_attribute_value ( HTML : : AttributeNames : : srcset ) ) ;
2023-05-11 19:23:36 +02:00
// 5. If source set has zero image sources, continue to the next child.
if ( source_set . is_empty ( ) )
continue ;
2023-05-28 04:40:27 +00:00
// 6. If child has a media attribute, and its value does not match the environment, continue to the next child.
if ( child - > has_attribute ( HTML : : AttributeNames : : media ) ) {
auto media_query = parse_media_query ( CSS : : Parser : : ParsingContext { element . document ( ) } ,
2024-01-16 19:04:45 +01:00
child - > get_attribute_value ( HTML : : AttributeNames : : media ) ) ;
2024-03-10 08:41:18 +01:00
if ( ! media_query | | ! element . document ( ) . window ( ) | | ! media_query - > evaluate ( * element . document ( ) . window ( ) ) ) {
2023-05-28 04:40:27 +00:00
continue ;
}
}
2023-05-11 19:23:36 +02:00
// 7. Parse child's sizes attribute, and let source set's source size be the returned value.
2024-01-16 19:04:45 +01:00
source_set . m_source_size = parse_a_sizes_attribute ( element . document ( ) , child - > get_attribute_value ( HTML : : AttributeNames : : sizes ) ) ;
2023-05-11 19:23:36 +02:00
// FIXME: 8. If child has a type attribute, and its value is an unknown or unsupported MIME type, continue to the next child.
if ( child - > has_attribute ( HTML : : AttributeNames : : type ) ) {
}
// FIXME: 9. If child has width or height attributes, set el's dimension attribute source to child.
// Otherwise, set el's dimension attribute source to el.
// 10. Normalize the source densities of source set.
2023-07-28 15:52:06 +02:00
source_set . normalize_source_densities ( element ) ;
2023-05-11 19:23:36 +02:00
// 11. Let el's source set be source set.
if ( is < HTMLImageElement > ( element ) )
static_cast < HTMLImageElement & > ( element ) . set_source_set ( move ( source_set ) ) ;
else if ( is < HTMLLinkElement > ( element ) )
TODO ( ) ;
// 12. Return.
return ;
}
}
// https://html.spec.whatwg.org/multipage/images.html#select-an-image-source
Optional < ImageSourceAndPixelDensity > HTMLImageElement : : select_an_image_source ( )
{
// 1. Update the source set for el.
update_the_source_set ( * this ) ;
// 2. If el's source set is empty, return null as the URL and undefined as the pixel density.
if ( m_source_set . is_empty ( ) )
return { } ;
// 3. Return the result of selecting an image from el's source set.
return m_source_set . select_an_image_source ( ) ;
}
void HTMLImageElement : : set_source_set ( SourceSet source_set )
{
m_source_set = move ( source_set ) ;
}
void HTMLImageElement : : animate ( )
{
auto image_data = m_current_request - > image_data ( ) ;
if ( ! image_data ) {
return ;
}
m_current_frame_index = ( m_current_frame_index + 1 ) % image_data - > frame_count ( ) ;
auto current_frame_duration = image_data - > frame_duration ( m_current_frame_index ) ;
if ( current_frame_duration ! = m_animation_timer - > interval ( ) ) {
m_animation_timer - > restart ( current_frame_duration ) ;
}
if ( m_current_frame_index = = image_data - > frame_count ( ) - 1 ) {
+ + m_loops_completed ;
if ( m_loops_completed > 0 & & m_loops_completed = = image_data - > loop_count ( ) ) {
m_animation_timer - > stop ( ) ;
}
}
2024-01-14 13:46:52 +01:00
if ( paintable ( ) )
paintable ( ) - > set_needs_display ( ) ;
2023-05-11 19:23:36 +02:00
}
2020-03-07 10:27:02 +01:00
}