1use std::cell::Cell;
6use std::default::Default;
7use std::rc::Rc;
8use std::sync::{Arc, LazyLock};
9use std::{char, mem};
10
11use app_units::Au;
12use cssparser::{Parser, ParserInput};
13use dom_struct::dom_struct;
14use euclid::default::Point2D;
15use html5ever::{LocalName, Prefix, QualName, local_name, ns};
16use js::context::JSContext;
17use js::rust::HandleObject;
18use mime::{self, Mime};
19use net_traits::http_status::HttpStatus;
20use net_traits::image_cache::{
21 Image, ImageCache, ImageCacheResult, ImageLoadListener, ImageOrMetadataAvailable,
22 ImageResponse, PendingImageId,
23};
24use net_traits::request::{CorsSettings, Destination, Initiator, RequestId};
25use net_traits::{
26 FetchMetadata, FetchResponseMsg, NetworkError, ReferrerPolicy, ResourceFetchTiming,
27};
28use num_traits::ToPrimitive;
29use pixels::{CorsStatus, ImageMetadata, Snapshot};
30use regex::Regex;
31use rustc_hash::FxHashSet;
32use script_bindings::cell::DomRefCell;
33use servo_url::ServoUrl;
34use servo_url::origin::MutableOrigin;
35use style::attr::{AttrValue, LengthOrPercentageOrAuto, parse_unsigned_integer};
36use style::stylesheets::CssRuleType;
37use style::values::specified::source_size_list::SourceSizeList;
38use style_traits::ParsingMode;
39use url::Url;
40
41use crate::css::parser_context_for_anonymous_content;
42use crate::document_loader::{LoadBlocker, LoadType};
43use crate::dom::activation::Activatable;
44use crate::dom::bindings::codegen::Bindings::DOMRectBinding::DOMRect_Binding::DOMRectMethods;
45use crate::dom::bindings::codegen::Bindings::ElementBinding::Element_Binding::ElementMethods;
46use crate::dom::bindings::codegen::Bindings::HTMLImageElementBinding::HTMLImageElementMethods;
47use crate::dom::bindings::codegen::Bindings::MouseEventBinding::MouseEventMethods;
48use crate::dom::bindings::codegen::Bindings::NodeBinding::Node_Binding::NodeMethods;
49use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
50use crate::dom::bindings::error::{Error, Fallible};
51use crate::dom::bindings::inheritance::Castable;
52use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
53use crate::dom::bindings::reflector::DomGlobal;
54use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom, ToLayoutOptional};
55use crate::dom::bindings::str::{DOMString, USVString};
56use crate::dom::csp::{GlobalCspReporting, Violation};
57use crate::dom::document::Document;
58use crate::dom::element::attributes::storage::AttrRef;
59use crate::dom::element::{
60 AttributeMutation, CustomElementCreationMode, Element, ElementCreator,
61 cors_setting_for_element, referrer_policy_for_element, reflect_cross_origin_attribute,
62 reflect_referrer_policy_attribute, set_cross_origin_attribute,
63};
64use crate::dom::event::Event;
65use crate::dom::eventtarget::EventTarget;
66use crate::dom::globalscope::GlobalScope;
67use crate::dom::html::htmlareaelement::HTMLAreaElement;
68use crate::dom::html::htmlelement::HTMLElement;
69use crate::dom::html::htmlformelement::{FormControl, HTMLFormElement};
70use crate::dom::html::htmlmapelement::HTMLMapElement;
71use crate::dom::html::htmlpictureelement::HTMLPictureElement;
72use crate::dom::html::htmlsourceelement::HTMLSourceElement;
73use crate::dom::iterators::ShadowIncluding;
74use crate::dom::medialist::MediaList;
75use crate::dom::mouseevent::MouseEvent;
76use crate::dom::node::virtualmethods::VirtualMethods;
77use crate::dom::node::{BindContext, MoveContext, Node, NodeDamage, NodeTraits, UnbindContext};
78use crate::dom::performance::performanceresourcetiming::InitiatorType;
79use crate::dom::promise::Promise;
80use crate::dom::window::Window;
81use crate::fetch::{RequestWithGlobalScope, create_a_potential_cors_request};
82use crate::microtask::MicrotaskRunnable;
83use crate::network_listener::{self, FetchResponseListener, ResourceTimingListener};
84use crate::realms::enter_auto_realm;
85use crate::script_thread::ScriptThread;
86
87const SUPPORTED_IMAGE_MIME_TYPES: &[&str] = &[
91 "image/bmp",
92 "image/gif",
93 "image/jpeg",
94 "image/jpg",
95 "image/pjpeg",
96 "image/png",
97 "image/apng",
98 "image/x-png",
99 "image/svg+xml",
100 "image/vnd.microsoft.icon",
101 "image/x-icon",
102 "image/webp",
103];
104
105#[derive(Clone, Copy, Debug)]
106enum ParseState {
107 InDescriptor,
108 InParens,
109 AfterDescriptor,
110}
111
112#[derive(MallocSizeOf)]
114pub(crate) struct SourceSet {
115 image_sources: Vec<ImageSource>,
116 source_size: SourceSizeList,
117}
118
119impl SourceSet {
120 fn new() -> SourceSet {
121 SourceSet {
122 image_sources: Vec::new(),
123 source_size: SourceSizeList::empty(),
124 }
125 }
126}
127
128#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
129pub struct ImageSource {
130 pub url: String,
131 pub descriptor: Descriptor,
132}
133
134#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
135pub struct Descriptor {
136 pub width: Option<u32>,
137 pub density: Option<f64>,
138}
139
140#[derive(Clone, Copy, JSTraceable, MallocSizeOf)]
142enum State {
143 Unavailable,
144 PartiallyAvailable,
145 CompletelyAvailable,
146 Broken,
147}
148
149#[derive(Clone, Copy, JSTraceable, MallocSizeOf)]
150enum ImageRequestPhase {
151 Pending,
152 Current,
153}
154
155#[derive(JSTraceable, MallocSizeOf)]
157#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
158struct ImageRequest {
159 state: State,
160 #[no_trace]
161 parsed_url: Option<ServoUrl>,
162 source_url: Option<USVString>,
163 blocker: DomRefCell<Option<LoadBlocker>>,
164 #[no_trace]
165 image: Option<Image>,
166 #[no_trace]
167 metadata: Option<ImageMetadata>,
168 #[no_trace]
169 final_url: Option<ServoUrl>,
170 current_pixel_density: Option<f64>,
171}
172
173#[dom_struct]
174pub(crate) struct HTMLImageElement {
175 htmlelement: HTMLElement,
176 image_request: Cell<ImageRequestPhase>,
177 current_request: DomRefCell<ImageRequest>,
178 pending_request: DomRefCell<ImageRequest>,
179 form_owner: MutNullableDom<HTMLFormElement>,
180 generation: Cell<u32>,
181 source_set: DomRefCell<SourceSet>,
182 dimension_attribute_source: MutNullableDom<Element>,
185 last_selected_source: DomRefCell<Option<USVString>>,
187 #[conditional_malloc_size_of]
188 image_decode_promises: DomRefCell<Vec<Rc<Promise>>>,
189 line_number: u64,
191}
192
193impl HTMLImageElement {
194 pub(crate) fn is_usable(&self) -> Fallible<bool> {
196 if let Some(image) = &self.current_request.borrow().image {
198 let intrinsic_size = image.metadata();
199 if intrinsic_size.width == 0 || intrinsic_size.height == 0 {
200 return Ok(false);
201 }
202 }
203
204 match self.current_request.borrow().state {
205 State::Broken => Err(Error::InvalidState(None)),
207 State::CompletelyAvailable => Ok(true),
208 State::PartiallyAvailable | State::Unavailable => Ok(false),
210 }
211 }
212
213 pub(crate) fn image_data(&self) -> Option<Image> {
214 self.current_request.borrow().image.clone()
215 }
216
217 pub(crate) fn get_raster_image_data(&self) -> Option<Snapshot> {
219 let Some(raster_image) = self.image_data()?.as_raster_image() else {
220 warn!("Vector image is not supported as raster image source");
221 return None;
222 };
223 Some(raster_image.as_snapshot())
224 }
225}
226
227struct ImageContext {
229 image_cache: Arc<dyn ImageCache>,
231 status: Result<(), NetworkError>,
233 id: PendingImageId,
235 aborted: bool,
237 doc: Trusted<Document>,
239 url: ServoUrl,
240 element: Trusted<HTMLImageElement>,
241}
242
243impl FetchResponseListener for ImageContext {
244 fn should_invoke(&self) -> bool {
245 !self.aborted
246 }
247
248 fn process_request_body(&mut self, _: RequestId) {}
249
250 fn process_response(
251 &mut self,
252 _: &mut js::context::JSContext,
253 request_id: RequestId,
254 metadata: Result<FetchMetadata, NetworkError>,
255 ) {
256 debug!("got {:?} for {:?}", metadata.as_ref().map(|_| ()), self.url);
257 self.image_cache.notify_pending_response(
258 self.id,
259 FetchResponseMsg::ProcessResponse(request_id, metadata.clone()),
260 );
261
262 let metadata = metadata.ok().map(|meta| match meta {
263 FetchMetadata::Unfiltered(m) => m,
264 FetchMetadata::Filtered { unsafe_, .. } => unsafe_,
265 });
266
267 if let Some(metadata) = metadata.as_ref() &&
269 let Some(ref content_type) = metadata.content_type
270 {
271 let mime: Mime = content_type.clone().into_inner().into();
272 if mime.type_() == mime::MULTIPART && mime.subtype().as_str() == "x-mixed-replace" {
273 self.aborted = true;
274 }
275 }
276
277 let status = metadata
278 .as_ref()
279 .map(|m| m.status.clone())
280 .unwrap_or_else(HttpStatus::new_error);
281
282 self.status = {
283 if status.is_error() {
284 Err(NetworkError::ResourceLoadError(
285 "No http status code received".to_owned(),
286 ))
287 } else if status.is_success() {
288 Ok(())
289 } else {
290 Err(NetworkError::ResourceLoadError(format!(
291 "HTTP error code {}",
292 status.code()
293 )))
294 }
295 };
296 }
297
298 fn process_response_chunk(
299 &mut self,
300 _: &mut js::context::JSContext,
301 request_id: RequestId,
302 payload: Vec<u8>,
303 ) {
304 if self.status.is_ok() {
305 self.image_cache.notify_pending_response(
306 self.id,
307 FetchResponseMsg::ProcessResponseChunk(request_id, payload.into()),
308 );
309 }
310 }
311
312 fn process_response_eof(
313 self,
314 cx: &mut js::context::JSContext,
315 request_id: RequestId,
316 response: Result<(), NetworkError>,
317 timing: ResourceFetchTiming,
318 ) {
319 self.image_cache.notify_pending_response(
320 self.id,
321 FetchResponseMsg::ProcessResponseEOF(request_id, response.clone(), timing.clone()),
322 );
323 network_listener::submit_timing(cx, &self, &response, &timing);
324 }
325
326 fn process_csp_violations(
327 &mut self,
328 cx: &mut js::context::JSContext,
329 _request_id: RequestId,
330 violations: Vec<Violation>,
331 ) {
332 let global = &self.resource_timing_global();
333 let elem = self.element.root();
334 let source_position = elem
335 .upcast::<Element>()
336 .compute_source_position(elem.line_number as u32);
337 global.report_csp_violations(cx, violations, None, Some(source_position));
338 }
339
340 fn process_content_length(&mut self, request_id: RequestId, size: usize) {
341 self.image_cache.notify_pending_response(
342 self.id,
343 FetchResponseMsg::ProcessContentLength(request_id, size),
344 );
345 }
346}
347
348impl ResourceTimingListener for ImageContext {
349 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
350 (
351 InitiatorType::LocalName("img".to_string()),
352 self.url.clone(),
353 )
354 }
355
356 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
357 self.doc.root().global()
358 }
359}
360
361#[expect(non_snake_case)]
362impl HTMLImageElement {
363 fn fetch_image(&self, img_url: &ServoUrl, cx: &mut js::context::JSContext) {
365 let window = self.owner_window();
366
367 let cache_result = window.image_cache().get_cached_image_status(
368 img_url.clone(),
369 window.origin().immutable().clone(),
370 cors_setting_for_element(self.upcast()),
371 );
372
373 match cache_result {
374 ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable {
375 image,
376 url,
377 }) => self.process_image_response(ImageResponse::Loaded(image, url), cx),
378 ImageCacheResult::Available(ImageOrMetadataAvailable::MetadataAvailable(
379 metadata,
380 id,
381 )) => {
382 self.process_image_response(ImageResponse::MetadataLoaded(metadata), cx);
383 self.register_image_cache_callback(id, ChangeType::Element);
384 },
385 ImageCacheResult::Pending(id) => {
386 self.register_image_cache_callback(id, ChangeType::Element);
387 },
388 ImageCacheResult::ReadyForRequest(id) => {
389 self.fetch_request(img_url, id);
390 self.register_image_cache_callback(id, ChangeType::Element);
391 },
392 ImageCacheResult::FailedToLoadOrDecode => {
393 self.process_image_response(ImageResponse::FailedToLoadOrDecode, cx)
394 },
395 };
396 }
397
398 fn register_image_cache_callback(&self, id: PendingImageId, change_type: ChangeType) {
399 let trusted_node = Trusted::new(self);
400 let generation = self.generation_id();
401 let window = self.owner_window();
402 let callback = window.register_image_cache_listener(id, move |response, _| {
403 let trusted_node = trusted_node.clone();
404 let window = trusted_node.root().owner_window();
405 let callback_type = change_type.clone();
406
407 window
408 .as_global_scope()
409 .task_manager()
410 .networking_task_source()
411 .queue(task!(process_image_response: move |cx| {
412 let element = trusted_node.root();
413
414 if generation != element.generation_id() {
416 return;
417 }
418
419 match callback_type {
420 ChangeType::Element => {
421 element.process_image_response(response.response, cx);
422 }
423 ChangeType::Environment { selected_source, selected_pixel_density } => {
424 element.process_image_response_for_environment_change(
425 response.response, selected_source, generation, selected_pixel_density, cx
426 );
427 }
428 }
429 }));
430 });
431
432 window.image_cache().add_listener(ImageLoadListener::new(
433 callback,
434 window.pipeline_id(),
435 id,
436 ));
437 }
438
439 fn fetch_request(&self, img_url: &ServoUrl, id: PendingImageId) {
440 let document = self.owner_document();
441 let window = self.owner_window();
442
443 let context = ImageContext {
444 image_cache: window.image_cache(),
445 status: Ok(()),
446 id,
447 aborted: false,
448 doc: Trusted::new(&document),
449 element: Trusted::new(self),
450 url: img_url.clone(),
451 };
452
453 let global = document.global();
456 let mut request = create_a_potential_cors_request(
457 Some(window.webview_id()),
458 img_url.clone(),
459 Destination::Image,
460 cors_setting_for_element(self.upcast()),
461 None,
462 global.get_referrer(),
463 )
464 .with_global_scope(&global)
465 .referrer_policy(referrer_policy_for_element(self.upcast()));
466
467 if self.uses_srcset_or_picture() {
468 request = request.initiator(Initiator::ImageSet);
469 }
470
471 document.fetch_background(request, context);
474 }
475
476 fn handle_loaded_image(&self, image: Image, url: ServoUrl, cx: &mut js::context::JSContext) {
478 self.current_request.borrow_mut().metadata = Some(image.metadata());
479 self.current_request.borrow_mut().final_url = Some(url);
480 self.current_request.borrow_mut().image = Some(image);
481 self.current_request.borrow_mut().state = State::CompletelyAvailable;
482 LoadBlocker::terminate(&self.current_request.borrow().blocker, cx);
483 self.upcast::<Node>().dirty(NodeDamage::Other);
485 self.resolve_image_decode_promises();
486 }
487
488 fn process_image_response(&self, image: ImageResponse, cx: &mut js::context::JSContext) {
490 let (trigger_image_load, trigger_image_error) = match (image, self.image_request.get()) {
496 (ImageResponse::Loaded(image, url), ImageRequestPhase::Current) => {
497 self.handle_loaded_image(image, url, cx);
498 (true, false)
499 },
500 (ImageResponse::Loaded(image, url), ImageRequestPhase::Pending) => {
501 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
502 self.image_request.set(ImageRequestPhase::Current);
503 self.handle_loaded_image(image, url, cx);
504 (true, false)
505 },
506 (ImageResponse::MetadataLoaded(meta), ImageRequestPhase::Current) => {
507 self.current_request.borrow_mut().state = State::PartiallyAvailable;
512 self.current_request.borrow_mut().metadata = Some(meta);
513 (false, false)
514 },
515 (ImageResponse::MetadataLoaded(_), ImageRequestPhase::Pending) => {
516 self.pending_request.borrow_mut().state = State::PartiallyAvailable;
520 (false, false)
521 },
522 (ImageResponse::FailedToLoadOrDecode, ImageRequestPhase::Current) => {
523 self.abort_request(State::Broken, ImageRequestPhase::Current, cx);
529
530 self.load_broken_image_icon();
531
532 (false, true)
536 },
537 (ImageResponse::FailedToLoadOrDecode, ImageRequestPhase::Pending) => {
538 self.abort_request(State::Broken, ImageRequestPhase::Current, cx);
544 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
545
546 mem::swap(
548 &mut *self.current_request.borrow_mut(),
549 &mut *self.pending_request.borrow_mut(),
550 );
551 self.image_request.set(ImageRequestPhase::Current);
552
553 self.current_request.borrow_mut().state = State::Broken;
555
556 self.load_broken_image_icon();
557
558 (false, true)
560 },
561 };
562
563 if trigger_image_load {
565 self.upcast::<EventTarget>().fire_event(cx, atom!("load"));
567 self.upcast::<EventTarget>()
568 .fire_event(cx, atom!("loadend"));
569 }
570
571 if trigger_image_error {
573 self.upcast::<EventTarget>().fire_event(cx, atom!("error"));
574 self.upcast::<EventTarget>()
575 .fire_event(cx, atom!("loadend"));
576 }
577 }
578
579 fn process_image_response_for_environment_change(
582 &self,
583 image: ImageResponse,
584 selected_source: USVString,
585 generation: u32,
586 selected_pixel_density: f64,
587 cx: &mut js::context::JSContext,
588 ) {
589 match image {
590 ImageResponse::Loaded(image, url) => {
591 self.pending_request.borrow_mut().metadata = Some(image.metadata());
592 self.pending_request.borrow_mut().final_url = Some(url);
593 self.pending_request.borrow_mut().image = Some(image);
594 self.finish_reacting_to_environment_change(
595 selected_source,
596 generation,
597 selected_pixel_density,
598 );
599 },
600 ImageResponse::FailedToLoadOrDecode => {
601 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
609 },
610 ImageResponse::MetadataLoaded(meta) => {
611 self.pending_request.borrow_mut().metadata = Some(meta);
612 },
613 };
614 }
615
616 fn abort_request(
618 &self,
619 state: State,
620 request: ImageRequestPhase,
621 cx: &mut js::context::JSContext,
622 ) {
623 let mut request = match request {
624 ImageRequestPhase::Current => self.current_request.borrow_mut(),
625 ImageRequestPhase::Pending => self.pending_request.borrow_mut(),
626 };
627 LoadBlocker::terminate(&request.blocker, cx);
628 request.state = state;
629 request.image = None;
630 request.metadata = None;
631 request.current_pixel_density = None;
632
633 if matches!(state, State::Broken) {
634 self.reject_image_decode_promises();
635 } else if matches!(state, State::CompletelyAvailable) {
636 self.resolve_image_decode_promises();
637 }
638 }
639
640 fn create_source_set(&self) -> SourceSet {
642 let element = self.upcast::<Element>();
643
644 let mut source_set = SourceSet::new();
646
647 if let Some(srcset) = element.get_attribute_string_value(&local_name!("srcset")) {
650 source_set.image_sources = parse_a_srcset_attribute(&srcset);
651 }
652
653 if let Some(sizes) = element.get_attribute_string_value(&local_name!("sizes")) {
655 source_set.source_size = parse_a_sizes_attribute(&sizes);
656 }
657
658 let src = element.get_string_attribute(&local_name!("src"));
662 let no_density_source_of_1 = source_set
663 .image_sources
664 .iter()
665 .all(|source| source.descriptor.density != Some(1.));
666 let no_width_descriptor = source_set
667 .image_sources
668 .iter()
669 .all(|source| source.descriptor.width.is_none());
670 if !src.is_empty() && no_density_source_of_1 && no_width_descriptor {
671 source_set.image_sources.push(ImageSource {
672 url: String::from(src),
673 descriptor: Descriptor {
674 width: None,
675 density: None,
676 },
677 })
678 }
679
680 self.normalise_source_densities(&mut source_set);
682
683 source_set
685 }
686
687 fn update_source_set(&self) {
689 *self.source_set.borrow_mut() = SourceSet::new();
691
692 let elem = self.upcast::<Element>();
697 let parent = elem.upcast::<Node>().GetParentElement();
698 let elements = match parent.as_ref() {
699 Some(p) => {
700 if p.is::<HTMLPictureElement>() {
701 p.upcast::<Node>()
702 .children()
703 .filter_map(DomRoot::downcast::<Element>)
704 .map(|n| DomRoot::from_ref(&*n))
705 .collect()
706 } else {
707 vec![DomRoot::from_ref(elem)]
708 }
709 },
710 None => vec![DomRoot::from_ref(elem)],
711 };
712
713 for element in &elements {
715 if *element == DomRoot::from_ref(elem) {
717 *self.source_set.borrow_mut() = self.create_source_set();
720
721 return;
723 }
724 if !element.is::<HTMLSourceElement>() {
726 continue;
727 }
728
729 let mut source_set = SourceSet::new();
730
731 match element.get_attribute_string_value(&local_name!("srcset")) {
735 Some(srcset) => {
736 source_set.image_sources = parse_a_srcset_attribute(&srcset);
737 },
738 _ => continue,
739 }
740
741 if source_set.image_sources.is_empty() {
743 continue;
744 }
745
746 if let Some(media) = element.get_attribute_string_value(&local_name!("media")) &&
749 !MediaList::matches_environment(&element.owner_document(), &media)
750 {
751 continue;
752 }
753
754 if let Some(sizes) = element.get_attribute_string_value(&local_name!("sizes")) {
757 source_set.source_size = parse_a_sizes_attribute(&sizes);
758 }
759
760 if let Some(type_) = element.get_attribute_string_value(&local_name!("type")) &&
763 !is_supported_image_mime_type(&type_)
764 {
765 continue;
766 }
767
768 if element.has_attribute(&local_name!("width")) ||
771 element.has_attribute(&local_name!("height"))
772 {
773 self.dimension_attribute_source.set(Some(element));
774 } else {
775 self.dimension_attribute_source.set(Some(elem));
776 }
777
778 self.normalise_source_densities(&mut source_set);
780
781 *self.source_set.borrow_mut() = source_set;
783
784 return;
786 }
787 }
788
789 fn evaluate_source_size_list(&self, source_size_list: &SourceSizeList) -> Au {
790 let document = self.owner_document();
791 let quirks_mode = document.quirks_mode();
792 source_size_list.evaluate(document.window().layout().device(), quirks_mode)
793 }
794
795 fn normalise_source_densities(&self, source_set: &mut SourceSet) {
797 let source_size = self.evaluate_source_size_list(&source_set.source_size);
799
800 for image_source in &mut source_set.image_sources {
802 if image_source.descriptor.density.is_some() {
805 continue;
806 }
807
808 if let Some(width) = image_source.descriptor.width {
812 image_source.descriptor.density = Some(width as f64 / source_size.to_f64_px());
813 } else {
814 image_source.descriptor.density = Some(1_f64);
816 }
817 }
818 }
819
820 fn select_image_source(&self) -> Option<(USVString, f64)> {
822 self.update_source_set();
824
825 if self.source_set.borrow().image_sources.is_empty() {
828 return None;
829 }
830
831 self.select_image_source_from_source_set()
833 }
834
835 fn select_image_source_from_source_set(&self) -> Option<(USVString, f64)> {
837 let source_set = self.source_set.borrow();
842 let len = source_set.image_sources.len();
843
844 let mut repeat_indices = FxHashSet::default();
846 for outer_index in 0..len {
847 if repeat_indices.contains(&outer_index) {
848 continue;
849 }
850 let imgsource = &source_set.image_sources[outer_index];
851 let pixel_density = imgsource.descriptor.density.unwrap();
852 for inner_index in (outer_index + 1)..len {
853 let imgsource2 = &source_set.image_sources[inner_index];
854 if pixel_density == imgsource2.descriptor.density.unwrap() {
855 repeat_indices.insert(inner_index);
856 }
857 }
858 }
859
860 let mut max = (0f64, 0);
861 let img_sources = &mut vec![];
862 for (index, image_source) in source_set.image_sources.iter().enumerate() {
863 if repeat_indices.contains(&index) {
864 continue;
865 }
866 let den = image_source.descriptor.density.unwrap();
867 if max.0 < den {
868 max = (den, img_sources.len());
869 }
870 img_sources.push(image_source);
871 }
872
873 let mut best_candidate = max;
876 let device_pixel_ratio = self
877 .owner_document()
878 .window()
879 .viewport_details()
880 .hidpi_scale_factor
881 .get() as f64;
882 for (index, image_source) in img_sources.iter().enumerate() {
883 let current_den = image_source.descriptor.density.unwrap();
884 if current_den < best_candidate.0 && current_den >= device_pixel_ratio {
885 best_candidate = (current_den, index);
886 }
887 }
888 let selected_source = img_sources.remove(best_candidate.1).clone();
889
890 Some((
892 USVString(selected_source.url),
893 selected_source.descriptor.density.unwrap(),
894 ))
895 }
896
897 fn init_image_request(
898 &self,
899 request: &DomRefCell<ImageRequest>,
900 url: &ServoUrl,
901 src: &USVString,
902 cx: &mut js::context::JSContext,
903 ) {
904 {
905 let mut request = request.borrow_mut();
906 request.parsed_url = Some(url.clone());
907 request.source_url = Some(src.clone());
908 request.image = None;
909 request.metadata = None;
910 }
911 let document = self.owner_document();
912 LoadBlocker::terminate(&request.borrow().blocker, cx);
913 *request.borrow_mut().blocker.borrow_mut() =
914 Some(LoadBlocker::new(&document, LoadType::Image(url.clone())));
915 }
916
917 fn prepare_image_request(
919 &self,
920 selected_source: &USVString,
921 selected_pixel_density: f64,
922 image_url: &ServoUrl,
923 cx: &mut js::context::JSContext,
924 ) {
925 match self.image_request.get() {
926 ImageRequestPhase::Pending => {
927 if self
930 .pending_request
931 .borrow()
932 .parsed_url
933 .as_ref()
934 .is_some_and(|parsed_url| *parsed_url == *image_url)
935 {
936 return;
937 }
938 },
939 ImageRequestPhase::Current => {
940 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
942
943 let (current_request_url, current_request_state) = {
945 let current_request = self.current_request.borrow();
946 (current_request.parsed_url.clone(), current_request.state)
947 };
948
949 match (current_request_url, current_request_state) {
950 (Some(parsed_url), State::PartiallyAvailable) => {
951 if parsed_url == *image_url {
957 return;
959 }
960
961 self.image_request.set(ImageRequestPhase::Pending);
965 self.init_image_request(
966 &self.pending_request,
967 image_url,
968 selected_source,
969 cx,
970 );
971 self.pending_request.borrow_mut().current_pixel_density =
972 Some(selected_pixel_density);
973 },
974 (_, State::Broken) | (_, State::Unavailable) => {
975 self.init_image_request(
979 &self.current_request,
980 image_url,
981 selected_source,
982 cx,
983 );
984 self.current_request.borrow_mut().current_pixel_density =
985 Some(selected_pixel_density);
986 self.reject_image_decode_promises();
987 },
988 (_, _) => {
989 self.image_request.set(ImageRequestPhase::Pending);
993 self.init_image_request(
994 &self.pending_request,
995 image_url,
996 selected_source,
997 cx,
998 );
999 self.pending_request.borrow_mut().current_pixel_density =
1000 Some(selected_pixel_density);
1001 },
1002 }
1003 },
1004 }
1005
1006 self.fetch_image(image_url, cx);
1007 }
1008
1009 fn update_the_image_data_sync_steps(&self, cx: &mut js::context::JSContext) {
1011 let Some((selected_source, selected_pixel_density)) = self.select_image_source() else {
1014 self.abort_request(State::Broken, ImageRequestPhase::Current, cx);
1019 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
1020 self.image_request.set(ImageRequestPhase::Current);
1021
1022 let this = Trusted::new(self);
1025
1026 self.owner_global().task_manager().dom_manipulation_task_source().queue(
1027 task!(image_null_source_error: move |cx| {
1028 let this = this.root();
1029
1030 {
1032 let mut current_request =
1033 this.current_request.borrow_mut();
1034 current_request.source_url = None;
1035 current_request.parsed_url = None;
1036 }
1037
1038 let has_src_attribute = this.upcast::<Element>().has_attribute(&local_name!("src"));
1044
1045 if has_src_attribute || this.uses_srcset_or_picture() {
1046 this.upcast::<EventTarget>().fire_event(cx, atom!("error"));
1047 }
1048 }));
1049
1050 return;
1052 };
1053
1054 let Ok(image_url) = self.owner_document().base_url().join(&selected_source) else {
1057 self.abort_request(State::Broken, ImageRequestPhase::Current, cx);
1062 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
1063
1064 self.image_request.set(ImageRequestPhase::Current);
1066
1067 let this = Trusted::new(self);
1070
1071 self.owner_global()
1072 .task_manager()
1073 .dom_manipulation_task_source()
1074 .queue(task!(image_selected_source_error: move |cx| {
1075 let this = this.root();
1076
1077 {
1079 let mut current_request =
1080 this.current_request.borrow_mut();
1081 current_request.source_url = Some(selected_source);
1082 current_request.parsed_url = None;
1083 }
1084
1085 this.upcast::<EventTarget>().fire_event(cx, atom!("error"));
1089 }));
1090
1091 return;
1093 };
1094
1095 self.prepare_image_request(&selected_source, selected_pixel_density, &image_url, cx);
1096 }
1097
1098 pub(crate) fn update_the_image_data(&self, cx: &mut js::context::JSContext) {
1100 self.generation.set(self.generation.get() + 1);
1102
1103 if !self.owner_document().is_active() {
1105 }
1111
1112 self.current_request.borrow_mut().state = State::Unavailable;
1121
1122 let mut selected_source = None;
1126 let mut selected_pixel_density = None;
1127
1128 let src = self
1132 .upcast::<Element>()
1133 .get_string_attribute(&local_name!("src"));
1134
1135 if !self.uses_srcset_or_picture() && !src.is_empty() {
1136 selected_source = Some(USVString(String::from(src)));
1137 selected_pixel_density = Some(1_f64);
1138 };
1139
1140 self.last_selected_source
1142 .borrow_mut()
1143 .clone_from(&selected_source);
1144
1145 if let Some(selected_source) = selected_source {
1147 if let Ok(image_url) = self.owner_document().base_url().join(&selected_source) {
1151 let window = self.owner_window();
1155 let response = window.image_cache().get_image(
1156 image_url.clone(),
1157 window.origin().immutable().clone(),
1158 cors_setting_for_element(self.upcast()),
1159 );
1160
1161 if let Some(image) = response {
1163 self.abort_request(State::CompletelyAvailable, ImageRequestPhase::Current, cx);
1168 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
1169
1170 self.image_request.set(ImageRequestPhase::Current);
1172
1173 let mut current_request = self.current_request.borrow_mut();
1176 current_request.metadata = Some(image.metadata());
1177 current_request.image = Some(image);
1178 current_request.final_url = Some(image_url.clone());
1179
1180 self.upcast::<Node>().dirty(NodeDamage::Other);
1183
1184 current_request.current_pixel_density = selected_pixel_density;
1187
1188 let this = Trusted::new(self);
1191
1192 self.owner_global()
1193 .task_manager()
1194 .dom_manipulation_task_source()
1195 .queue(task!(image_load_event: move |cx| {
1196 let this = this.root();
1197
1198 {
1203 let mut current_request =
1204 this.current_request.borrow_mut();
1205 current_request.source_url = Some(selected_source);
1206 current_request.parsed_url = Some(image_url);
1207 }
1208
1209 this.upcast::<EventTarget>().fire_event(cx, atom!("load"));
1213 }));
1214
1215 return;
1217 }
1218 }
1219 }
1220
1221 let task = ImageElementMicrotask::UpdateImageData {
1224 elem: Dom::from_ref(self),
1225 generation: self.generation.get(),
1226 };
1227
1228 ScriptThread::await_stable_state(cx, Box::new(task));
1229 }
1230
1231 pub(crate) fn react_to_environment_changes(&self, cx: &JSContext) {
1233 let task = ImageElementMicrotask::EnvironmentChanges {
1235 elem: Dom::from_ref(self),
1236 generation: self.generation.get(),
1237 };
1238
1239 ScriptThread::await_stable_state(cx, Box::new(task));
1240 }
1241
1242 fn react_to_environment_changes_sync_steps(
1244 &self,
1245 generation: u32,
1246 cx: &mut js::context::JSContext,
1247 ) {
1248 let document = self.owner_document();
1249 let has_pending_request = matches!(self.image_request.get(), ImageRequestPhase::Pending);
1250
1251 if !document.is_active() || !self.uses_srcset_or_picture() || has_pending_request {
1255 return;
1256 }
1257
1258 let Some((selected_source, selected_pixel_density)) = self.select_image_source() else {
1261 return;
1263 };
1264
1265 let mut same_selected_source = self
1268 .last_selected_source
1269 .borrow()
1270 .as_ref()
1271 .is_some_and(|source| *source == selected_source);
1272
1273 same_selected_source = same_selected_source ||
1277 self.current_request
1278 .borrow()
1279 .source_url
1280 .as_ref()
1281 .is_some_and(|source| *source == selected_source);
1282
1283 let same_selected_pixel_density = self
1284 .current_request
1285 .borrow()
1286 .current_pixel_density
1287 .is_some_and(|pixel_density| pixel_density == selected_pixel_density);
1288
1289 if same_selected_source && same_selected_pixel_density {
1290 return;
1291 }
1292
1293 let Ok(image_url) = document.base_url().join(&selected_source) else {
1297 return;
1298 };
1299
1300 self.image_request.set(ImageRequestPhase::Pending);
1302 self.init_image_request(&self.pending_request, &image_url, &selected_source, cx);
1303
1304 let window = self.owner_window();
1307 let cache_result = window.image_cache().get_cached_image_status(
1308 image_url.clone(),
1309 window.origin().immutable().clone(),
1310 cors_setting_for_element(self.upcast()),
1311 );
1312
1313 let change_type = ChangeType::Environment {
1314 selected_source: selected_source.clone(),
1315 selected_pixel_density,
1316 };
1317
1318 match cache_result {
1319 ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable { .. }) => {
1320 self.finish_reacting_to_environment_change(
1321 selected_source,
1322 generation,
1323 selected_pixel_density,
1324 );
1325 },
1326 ImageCacheResult::Available(ImageOrMetadataAvailable::MetadataAvailable(m, id)) => {
1327 self.process_image_response_for_environment_change(
1328 ImageResponse::MetadataLoaded(m),
1329 selected_source,
1330 generation,
1331 selected_pixel_density,
1332 cx,
1333 );
1334 self.register_image_cache_callback(id, change_type);
1335 },
1336 ImageCacheResult::FailedToLoadOrDecode => {
1337 self.process_image_response_for_environment_change(
1338 ImageResponse::FailedToLoadOrDecode,
1339 selected_source,
1340 generation,
1341 selected_pixel_density,
1342 cx,
1343 );
1344 },
1345 ImageCacheResult::ReadyForRequest(id) => {
1346 self.fetch_request(&image_url, id);
1347 self.register_image_cache_callback(id, change_type);
1348 },
1349 ImageCacheResult::Pending(id) => {
1350 self.register_image_cache_callback(id, change_type);
1351 },
1352 }
1353 }
1354
1355 fn react_to_decode_image_sync_steps(&self, cx: &mut JSContext, promise: Rc<Promise>) {
1357 if !self.owner_document().is_fully_active() ||
1361 matches!(self.current_request.borrow().state, State::Broken)
1362 {
1363 promise.reject_error(cx, Error::Encoding(None));
1364 } else if matches!(
1365 self.current_request.borrow().state,
1366 State::CompletelyAvailable
1367 ) {
1368 promise.resolve_native(cx, &());
1370 } else if matches!(self.current_request.borrow().state, State::Unavailable) &&
1371 self.current_request.borrow().source_url.is_none()
1372 {
1373 promise.reject_error(cx, Error::Encoding(None));
1378 } else {
1379 self.image_decode_promises.borrow_mut().push(promise);
1380 }
1381 }
1382
1383 fn resolve_image_decode_promises(&self) {
1385 if self.image_decode_promises.borrow().is_empty() {
1386 return;
1387 }
1388
1389 let trusted_image_decode_promises: Vec<TrustedPromise> = self
1392 .image_decode_promises
1393 .borrow()
1394 .iter()
1395 .map(|promise| TrustedPromise::new(promise.clone()))
1396 .collect();
1397
1398 self.image_decode_promises.borrow_mut().clear();
1399
1400 self.owner_global()
1401 .task_manager()
1402 .dom_manipulation_task_source()
1403 .queue(task!(fulfill_image_decode_promises: move |cx| {
1404 for trusted_promise in trusted_image_decode_promises {
1405 trusted_promise.root().resolve_native(cx, &());
1406 }
1407 }));
1408 }
1409
1410 fn reject_image_decode_promises(&self) {
1412 if self.image_decode_promises.borrow().is_empty() {
1413 return;
1414 }
1415
1416 let trusted_image_decode_promises: Vec<TrustedPromise> = self
1419 .image_decode_promises
1420 .borrow()
1421 .iter()
1422 .map(|promise| TrustedPromise::new(promise.clone()))
1423 .collect();
1424
1425 self.image_decode_promises.borrow_mut().clear();
1426
1427 self.owner_global()
1428 .task_manager()
1429 .dom_manipulation_task_source()
1430 .queue(task!(reject_image_decode_promises: move |cx| {
1431 for trusted_promise in trusted_image_decode_promises {
1432 trusted_promise.root().reject_error(cx, Error::Encoding(None));
1433 }
1434 }));
1435 }
1436
1437 fn finish_reacting_to_environment_change(
1439 &self,
1440 selected_source: USVString,
1441 generation: u32,
1442 selected_pixel_density: f64,
1443 ) {
1444 let this = Trusted::new(self);
1447
1448 self.owner_global()
1449 .task_manager()
1450 .dom_manipulation_task_source()
1451 .queue(task!(image_load_event: move |cx| {
1452 let this = this.root();
1453
1454 if this.generation.get() != generation {
1457 this.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
1458 this.image_request.set(ImageRequestPhase::Current);
1459 return;
1460 }
1461
1462 *this.last_selected_source.borrow_mut() = Some(selected_source);
1465
1466 {
1467 let mut pending_request = this.pending_request.borrow_mut();
1468
1469 pending_request.state = State::CompletelyAvailable;
1471
1472 pending_request.current_pixel_density = Some(selected_pixel_density);
1473
1474 mem::swap(&mut *this.current_request.borrow_mut(), &mut *pending_request);
1480 }
1481
1482 this.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
1483 this.image_request.set(ImageRequestPhase::Current);
1484
1485 this.upcast::<Node>().dirty(NodeDamage::Other);
1487
1488 this.upcast::<EventTarget>().fire_event(cx, atom!("load"));
1490 }));
1491 }
1492
1493 fn uses_srcset_or_picture(&self) -> bool {
1495 let element = self.upcast::<Element>();
1496
1497 let has_srcset_attribute = element.has_attribute(&local_name!("srcset"));
1498 let has_parent_picture = element
1499 .upcast::<Node>()
1500 .GetParentElement()
1501 .is_some_and(|parent| parent.is::<HTMLPictureElement>());
1502 has_srcset_attribute || has_parent_picture
1503 }
1504
1505 fn new_inherited(
1506 local_name: LocalName,
1507 prefix: Option<Prefix>,
1508 document: &Document,
1509 creator: ElementCreator,
1510 ) -> HTMLImageElement {
1511 HTMLImageElement {
1512 htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
1513 image_request: Cell::new(ImageRequestPhase::Current),
1514 current_request: DomRefCell::new(ImageRequest {
1515 state: State::Unavailable,
1516 parsed_url: None,
1517 source_url: None,
1518 image: None,
1519 metadata: None,
1520 blocker: DomRefCell::new(None),
1521 final_url: None,
1522 current_pixel_density: None,
1523 }),
1524 pending_request: DomRefCell::new(ImageRequest {
1525 state: State::Unavailable,
1526 parsed_url: None,
1527 source_url: None,
1528 image: None,
1529 metadata: None,
1530 blocker: DomRefCell::new(None),
1531 final_url: None,
1532 current_pixel_density: None,
1533 }),
1534 form_owner: Default::default(),
1535 generation: Default::default(),
1536 source_set: DomRefCell::new(SourceSet::new()),
1537 dimension_attribute_source: Default::default(),
1538 last_selected_source: DomRefCell::new(None),
1539 image_decode_promises: DomRefCell::new(vec![]),
1540 line_number: creator.return_line_number(),
1541 }
1542 }
1543
1544 pub(crate) fn new(
1545 cx: &mut js::context::JSContext,
1546 local_name: LocalName,
1547 prefix: Option<Prefix>,
1548 document: &Document,
1549 proto: Option<HandleObject>,
1550 creator: ElementCreator,
1551 ) -> DomRoot<HTMLImageElement> {
1552 let image_element = Node::reflect_node_with_proto(
1553 cx,
1554 Box::new(HTMLImageElement::new_inherited(
1555 local_name, prefix, document, creator,
1556 )),
1557 document,
1558 proto,
1559 );
1560 image_element
1561 .dimension_attribute_source
1562 .set(Some(image_element.upcast()));
1563 image_element
1564 }
1565
1566 pub(crate) fn areas(&self) -> Option<Vec<DomRoot<HTMLAreaElement>>> {
1567 let elem = self.upcast::<Element>();
1568 let value = elem.get_attribute_string_value(&local_name!("usemap"))?;
1569
1570 if value.is_empty() || !value.is_char_boundary(1) {
1571 return None;
1572 }
1573
1574 let (first, last) = value.split_at(1);
1575
1576 if first != "#" || last.is_empty() {
1577 return None;
1578 }
1579
1580 let useMapElements = self
1581 .owner_document()
1582 .upcast::<Node>()
1583 .traverse_preorder(ShadowIncluding::No)
1584 .filter_map(DomRoot::downcast::<HTMLMapElement>)
1585 .find(|n| {
1586 n.upcast::<Element>()
1587 .get_name()
1588 .is_some_and(|n| *n == *last)
1589 });
1590
1591 useMapElements.map(|mapElem| mapElem.get_area_elements())
1592 }
1593
1594 pub(crate) fn same_origin(&self, origin: &MutableOrigin) -> bool {
1595 if let Some(ref image) = self.current_request.borrow().image {
1596 return image.cors_status() == CorsStatus::Safe;
1597 }
1598
1599 self.current_request
1600 .borrow()
1601 .final_url
1602 .as_ref()
1603 .is_some_and(|url| url.scheme() == "data" || url.origin().same_origin(origin))
1604 }
1605
1606 fn generation_id(&self) -> u32 {
1607 self.generation.get()
1608 }
1609
1610 fn load_broken_image_icon(&self) {
1611 let window = self.owner_window();
1612 let Some(broken_image_icon) = window.image_cache().get_broken_image_icon() else {
1613 return;
1614 };
1615
1616 self.current_request.borrow_mut().metadata = Some(broken_image_icon.metadata);
1617 self.current_request.borrow_mut().image = Some(Image::Raster(broken_image_icon));
1618 self.upcast::<Node>().dirty(NodeDamage::Other);
1619 }
1620
1621 pub(crate) fn full_image_url_for_user_interface(&self) -> Option<ServoUrl> {
1624 self.owner_document()
1625 .base_url()
1626 .join(&self.CurrentSrc())
1627 .ok()
1628 }
1629}
1630
1631#[derive(JSTraceable, MallocSizeOf)]
1632pub(crate) enum ImageElementMicrotask {
1633 UpdateImageData {
1634 elem: Dom<HTMLImageElement>,
1635 generation: u32,
1636 },
1637 EnvironmentChanges {
1638 elem: Dom<HTMLImageElement>,
1639 generation: u32,
1640 },
1641 Decode {
1642 elem: Dom<HTMLImageElement>,
1643 #[conditional_malloc_size_of]
1644 promise: Rc<Promise>,
1645 },
1646}
1647
1648impl MicrotaskRunnable for ImageElementMicrotask {
1649 fn handler(&self, cx: &mut js::context::JSContext) {
1650 let mut realm = match self {
1651 &ImageElementMicrotask::UpdateImageData { ref elem, .. } |
1652 &ImageElementMicrotask::EnvironmentChanges { ref elem, .. } |
1653 &ImageElementMicrotask::Decode { ref elem, .. } => enter_auto_realm(cx, &**elem),
1654 };
1655 let cx = &mut realm;
1656 match *self {
1657 ImageElementMicrotask::UpdateImageData {
1658 ref elem,
1659 ref generation,
1660 } => {
1661 if elem.generation.get() == *generation {
1665 elem.update_the_image_data_sync_steps(cx);
1666 }
1667 },
1668 ImageElementMicrotask::EnvironmentChanges {
1669 ref elem,
1670 ref generation,
1671 } => {
1672 elem.react_to_environment_changes_sync_steps(*generation, cx);
1673 },
1674 ImageElementMicrotask::Decode {
1675 ref elem,
1676 ref promise,
1677 } => {
1678 elem.react_to_decode_image_sync_steps(cx, promise.clone());
1679 },
1680 }
1681 }
1682}
1683
1684impl<'dom> LayoutDom<'dom, HTMLImageElement> {
1685 #[expect(unsafe_code)]
1686 fn current_request(self) -> &'dom ImageRequest {
1687 unsafe { self.unsafe_get().current_request.borrow_for_layout() }
1688 }
1689
1690 #[expect(unsafe_code)]
1691 fn dimension_attribute_source(self) -> LayoutDom<'dom, Element> {
1692 unsafe {
1693 self.unsafe_get()
1694 .dimension_attribute_source
1695 .to_layout()
1696 .expect("dimension attribute source should be always non-null")
1697 }
1698 }
1699
1700 pub(crate) fn image_url(self) -> Option<ServoUrl> {
1701 self.current_request().parsed_url.clone()
1702 }
1703
1704 pub(crate) fn image_data(self) -> (Option<Image>, Option<ImageMetadata>) {
1705 let current_request = self.current_request();
1706 (current_request.image.clone(), current_request.metadata)
1707 }
1708
1709 pub(crate) fn image_density(self) -> Option<f64> {
1710 self.current_request().current_pixel_density
1711 }
1712
1713 pub(crate) fn showing_broken_image_icon(self) -> bool {
1714 matches!(self.current_request().state, State::Broken)
1715 }
1716
1717 pub(crate) fn get_width(self) -> LengthOrPercentageOrAuto {
1718 self.dimension_attribute_source()
1719 .get_attr_for_layout(&ns!(), &local_name!("width"))
1720 .map(AttrValue::as_dimension)
1721 .cloned()
1722 .unwrap_or(LengthOrPercentageOrAuto::Auto)
1723 }
1724
1725 pub(crate) fn get_height(self) -> LengthOrPercentageOrAuto {
1726 self.dimension_attribute_source()
1727 .get_attr_for_layout(&ns!(), &local_name!("height"))
1728 .map(AttrValue::as_dimension)
1729 .cloned()
1730 .unwrap_or(LengthOrPercentageOrAuto::Auto)
1731 }
1732}
1733
1734fn parse_a_sizes_attribute(value: &str) -> SourceSizeList {
1736 let mut input = ParserInput::new(value);
1737 let mut parser = Parser::new(&mut input);
1738 let url_data = Url::parse("about:blank").unwrap().into();
1739 let context =
1742 parser_context_for_anonymous_content(CssRuleType::Style, ParsingMode::empty(), &url_data);
1743 SourceSizeList::parse(&context, &mut parser)
1744}
1745
1746impl HTMLImageElementMethods<crate::DomTypeHolder> for HTMLImageElement {
1747 fn Image(
1749 cx: &mut JSContext,
1750 window: &Window,
1751 proto: Option<HandleObject>,
1752 width: Option<u32>,
1753 height: Option<u32>,
1754 ) -> Fallible<DomRoot<HTMLImageElement>> {
1755 let document = window.Document();
1757
1758 let element = Element::create(
1761 cx,
1762 QualName::new(None, ns!(html), local_name!("img")),
1763 None,
1764 &document,
1765 ElementCreator::ScriptCreated,
1766 CustomElementCreationMode::Synchronous,
1767 proto,
1768 );
1769
1770 let image = DomRoot::downcast::<HTMLImageElement>(element).unwrap();
1771
1772 if let Some(w) = width {
1774 image.SetWidth(cx, w);
1775 }
1776
1777 if let Some(h) = height {
1780 image.SetHeight(cx, h);
1781 }
1782
1783 Ok(image)
1785 }
1786
1787 make_getter!(Alt, "alt");
1789 make_setter!(SetAlt, "alt");
1791
1792 make_url_getter!(Src, "src");
1794
1795 make_url_setter!(SetSrc, "src");
1797
1798 make_url_getter!(Srcset, "srcset");
1800 make_url_setter!(SetSrcset, "srcset");
1802
1803 make_getter!(Sizes, "sizes");
1805
1806 make_setter!(SetSizes, "sizes");
1808
1809 fn GetCrossOrigin(&self) -> Option<DOMString> {
1811 reflect_cross_origin_attribute(self.upcast::<Element>())
1812 }
1813
1814 fn SetCrossOrigin(&self, cx: &mut JSContext, value: Option<DOMString>) {
1816 set_cross_origin_attribute(cx, self.upcast::<Element>(), value);
1817 }
1818
1819 make_getter!(UseMap, "usemap");
1821 make_setter!(SetUseMap, "usemap");
1823
1824 make_bool_getter!(IsMap, "ismap");
1826 make_bool_setter!(SetIsMap, "ismap");
1828
1829 fn Width(&self) -> u32 {
1831 let node = self.upcast::<Node>();
1832 node.content_box()
1833 .map(|rect| rect.size.width.to_px() as u32)
1834 .unwrap_or_else(|| self.NaturalWidth())
1835 }
1836
1837 make_dimension_uint_setter!(SetWidth, "width");
1839
1840 fn Height(&self) -> u32 {
1842 let node = self.upcast::<Node>();
1843 node.content_box()
1844 .map(|rect| rect.size.height.to_px() as u32)
1845 .unwrap_or_else(|| self.NaturalHeight())
1846 }
1847
1848 make_dimension_uint_setter!(SetHeight, "height");
1850
1851 fn NaturalWidth(&self) -> u32 {
1853 let request = self.current_request.borrow();
1854 if matches!(request.state, State::Broken) {
1855 return 0;
1856 }
1857
1858 let pixel_density = request.current_pixel_density.unwrap_or(1f64);
1859 match request.metadata {
1860 Some(ref metadata) => (metadata.width as f64 / pixel_density) as u32,
1861 None => 0,
1862 }
1863 }
1864
1865 fn NaturalHeight(&self) -> u32 {
1867 let request = self.current_request.borrow();
1868 if matches!(request.state, State::Broken) {
1869 return 0;
1870 }
1871
1872 let pixel_density = request.current_pixel_density.unwrap_or(1f64);
1873 match request.metadata {
1874 Some(ref metadata) => (metadata.height as f64 / pixel_density) as u32,
1875 None => 0,
1876 }
1877 }
1878
1879 fn Complete(&self) -> bool {
1881 let element = self.upcast::<Element>();
1882
1883 let has_srcset_attribute = element.has_attribute(&local_name!("srcset"));
1886 if !element.has_attribute(&local_name!("src")) && !has_srcset_attribute {
1887 return true;
1888 }
1889
1890 let src = element.get_string_attribute(&local_name!("src"));
1892 if !has_srcset_attribute && src.is_empty() {
1893 return true;
1894 }
1895
1896 if matches!(self.image_request.get(), ImageRequestPhase::Current) &&
1900 matches!(
1901 self.current_request.borrow().state,
1902 State::CompletelyAvailable | State::Broken
1903 )
1904 {
1905 return true;
1906 }
1907
1908 false
1910 }
1911
1912 fn CurrentSrc(&self) -> USVString {
1914 let current_request = self.current_request.borrow();
1915 let url = ¤t_request.parsed_url;
1916 match *url {
1917 Some(ref url) => USVString(url.clone().into_string()),
1918 None => {
1919 let unparsed_url = ¤t_request.source_url;
1920 match *unparsed_url {
1921 Some(ref url) => url.clone(),
1922 None => USVString("".to_owned()),
1923 }
1924 },
1925 }
1926 }
1927
1928 fn ReferrerPolicy(&self) -> DOMString {
1930 reflect_referrer_policy_attribute(self.upcast::<Element>())
1931 }
1932
1933 make_setter!(SetReferrerPolicy, "referrerpolicy");
1935
1936 fn Decode(&self, cx: &mut JSContext) -> Rc<Promise> {
1938 let promise = Promise::new(cx, &self.global());
1940
1941 let task = ImageElementMicrotask::Decode {
1943 elem: Dom::from_ref(self),
1944 promise: promise.clone(),
1945 };
1946
1947 ScriptThread::await_stable_state(cx, Box::new(task));
1948
1949 promise
1951 }
1952
1953 make_getter!(Name, "name");
1955
1956 make_atomic_setter!(SetName, "name");
1958
1959 make_getter!(Align, "align");
1961
1962 make_setter!(SetAlign, "align");
1964
1965 make_uint_getter!(Hspace, "hspace");
1967
1968 make_uint_setter!(SetHspace, "hspace");
1970
1971 make_uint_getter!(Vspace, "vspace");
1973
1974 make_uint_setter!(SetVspace, "vspace");
1976
1977 make_url_getter!(LongDesc, "longdesc");
1979
1980 make_url_setter!(SetLongDesc, "longdesc");
1982
1983 make_getter!(Border, "border");
1985
1986 make_setter!(SetBorder, "border");
1988}
1989
1990impl VirtualMethods for HTMLImageElement {
1991 fn super_type(&self) -> Option<&dyn VirtualMethods> {
1992 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
1993 }
1994
1995 fn adopting_steps(&self, cx: &mut JSContext, old_doc: &Document) {
1996 self.super_type().unwrap().adopting_steps(cx, old_doc);
1997 self.update_the_image_data(cx);
1998 }
1999
2000 fn attribute_mutated(
2001 &self,
2002 cx: &mut js::context::JSContext,
2003 attr: AttrRef<'_>,
2004 mutation: AttributeMutation,
2005 ) {
2006 self.super_type()
2007 .unwrap()
2008 .attribute_mutated(cx, attr, mutation);
2009 match attr.local_name() {
2010 &local_name!("src") |
2011 &local_name!("srcset") |
2012 &local_name!("width") |
2013 &local_name!("sizes") => {
2014 self.update_the_image_data(cx);
2018 },
2019 &local_name!("crossorigin") => {
2020 let cross_origin_state_changed = match mutation {
2023 AttributeMutation::Removed | AttributeMutation::Set(None, _) => true,
2024 AttributeMutation::Set(Some(old_value), _) => {
2025 let new_cors_setting =
2026 CorsSettings::from_enumerated_attribute(&attr.value());
2027 let old_cors_setting = CorsSettings::from_enumerated_attribute(old_value);
2028
2029 new_cors_setting != old_cors_setting
2030 },
2031 };
2032
2033 if cross_origin_state_changed {
2034 self.update_the_image_data(cx);
2035 }
2036 },
2037 &local_name!("referrerpolicy") => {
2038 let referrer_policy_state_changed = match mutation {
2041 AttributeMutation::Removed | AttributeMutation::Set(None, _) => {
2042 ReferrerPolicy::from(&**attr.value()) != ReferrerPolicy::EmptyString
2043 },
2044 AttributeMutation::Set(Some(old_value), _) => {
2045 ReferrerPolicy::from(&**attr.value()) != ReferrerPolicy::from(&**old_value)
2046 },
2047 };
2048
2049 if referrer_policy_state_changed {
2050 self.update_the_image_data(cx);
2051 }
2052 },
2053 _ => {},
2054 }
2055 }
2056
2057 fn attribute_affects_presentational_hints(&self, attr: AttrRef<'_>) -> bool {
2058 match attr.local_name() {
2059 &local_name!("width") | &local_name!("height") => true,
2060 _ => self
2061 .super_type()
2062 .unwrap()
2063 .attribute_affects_presentational_hints(attr),
2064 }
2065 }
2066
2067 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
2068 match name {
2069 &local_name!("width") | &local_name!("height") => {
2070 AttrValue::from_dimension(value.into())
2071 },
2072 &local_name!("hspace") | &local_name!("vspace") => AttrValue::from_u32(value.into(), 0),
2073 _ => self
2074 .super_type()
2075 .unwrap()
2076 .parse_plain_attribute(name, value),
2077 }
2078 }
2079
2080 fn handle_event(&self, cx: &mut js::context::JSContext, event: &Event) {
2081 if event.type_() != atom!("click") {
2082 return;
2083 }
2084
2085 let Some(area_elements) = self.areas() else {
2086 return;
2087 };
2088
2089 let Some(mouse_event) = event.downcast::<MouseEvent>() else {
2091 return;
2092 };
2093
2094 let click_location = Point2D::new(
2095 mouse_event.ClientX().to_f32().unwrap(),
2096 mouse_event.ClientY().to_f32().unwrap(),
2097 );
2098 let bounding_rectangle = self.upcast::<Element>().GetBoundingClientRect(cx);
2099 let image_extents =
2100 Point2D::new(bounding_rectangle.X() as f32, bounding_rectangle.Y() as f32);
2101
2102 for area_element in area_elements {
2104 if !area_element.is_instance_activatable() {
2105 continue;
2106 }
2107 let activatable_area = match area_element.get_shape_from_coords() {
2108 Some(shape) => shape.absolute_coords(image_extents),
2109 None => return,
2110 };
2111 if activatable_area.hit_test(&click_location) {
2112 area_element.activation_behavior(cx, event, self.upcast());
2113 return;
2114 }
2115 }
2116 }
2117
2118 fn bind_to_tree(&self, cx: &mut JSContext, context: &BindContext) {
2120 if let Some(s) = self.super_type() {
2121 s.bind_to_tree(cx, context);
2122 }
2123 let document = self.owner_document();
2124 if context.tree_connected {
2125 document.register_responsive_image(self);
2126 }
2127
2128 let parent = self.upcast::<Node>().GetParentNode().unwrap();
2129
2130 if parent.is::<HTMLPictureElement>() && *parent == *context.parent {
2133 self.update_the_image_data(cx);
2134 }
2135 }
2136
2137 fn unbind_from_tree(&self, cx: &mut js::context::JSContext, context: &UnbindContext) {
2139 self.super_type().unwrap().unbind_from_tree(cx, context);
2140 let document = self.owner_document();
2141 document.unregister_responsive_image(self);
2142
2143 if context.parent.is::<HTMLPictureElement>() && !self.upcast::<Node>().has_parent() {
2146 self.update_the_image_data(cx);
2147 }
2148 }
2149
2150 fn moving_steps(&self, cx: &mut JSContext, context: &MoveContext) {
2152 if let Some(super_type) = self.super_type() {
2153 super_type.moving_steps(cx, context);
2154 }
2155
2156 if let Some(old_parent) = context.old_parent &&
2158 old_parent.is::<HTMLPictureElement>()
2159 {
2160 self.update_the_image_data(cx);
2161 }
2162 }
2163}
2164
2165impl FormControl for HTMLImageElement {
2166 fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
2167 self.form_owner.get()
2168 }
2169
2170 fn set_form_owner(&self, _cx: &mut JSContext, form: Option<&HTMLFormElement>) {
2171 self.form_owner.set(form);
2172 }
2173
2174 fn to_html_element(&self) -> &HTMLElement {
2175 self.upcast::<HTMLElement>()
2176 }
2177}
2178
2179pub(crate) fn collect_sequence_characters(
2182 s: &str,
2183 mut predicate: impl FnMut(&char) -> bool,
2184) -> (&str, &str) {
2185 let i = s.find(|ch| !predicate(&ch)).unwrap_or(s.len());
2186 (&s[0..i], &s[i..])
2187}
2188
2189fn is_valid_non_negative_integer_string(s: &str) -> bool {
2192 s.chars().all(|c| c.is_ascii_digit())
2193}
2194
2195fn is_valid_floating_point_number_string(s: &str) -> bool {
2198 static RE: LazyLock<Regex> =
2199 LazyLock::new(|| Regex::new(r"^-?(?:\d+\.\d+|\d+|\.\d+)(?:(e|E)(\+|\-)?\d+)?$").unwrap());
2200
2201 RE.is_match(s)
2202}
2203
2204pub fn parse_a_srcset_attribute(input: &str) -> Vec<ImageSource> {
2207 let mut current_index = 0;
2210
2211 let mut candidates = vec![];
2213 while current_index < input.len() {
2214 let remaining_string = &input[current_index..];
2215
2216 let (collected_characters, string_after_whitespace) =
2223 collect_sequence_characters(remaining_string, |character| {
2224 *character == ',' || character.is_ascii_whitespace()
2225 });
2226
2227 current_index += collected_characters.len();
2230
2231 if string_after_whitespace.is_empty() {
2233 return candidates;
2234 }
2235
2236 let (url, _) =
2239 collect_sequence_characters(string_after_whitespace, |c| !char::is_ascii_whitespace(c));
2240
2241 current_index += url.len();
2244
2245 let mut descriptors = Vec::new();
2247
2248 if url.ends_with(',') {
2252 let image_source = ImageSource {
2253 url: url.trim_end_matches(',').into(),
2254 descriptor: Descriptor {
2255 width: None,
2256 density: None,
2257 },
2258 };
2259 candidates.push(image_source);
2260 continue;
2261 }
2262
2263 let descriptors_string = &input[current_index..];
2266 let (spaces, descriptors_string) =
2267 collect_sequence_characters(descriptors_string, |character| {
2268 character.is_ascii_whitespace()
2269 });
2270 current_index += spaces.len();
2271
2272 let mut current_descriptor = String::new();
2274
2275 let mut state = ParseState::InDescriptor;
2277
2278 let mut characters = descriptors_string.chars();
2282 let mut character = characters.next();
2283 if let Some(character) = character {
2284 current_index += character.len_utf8();
2285 }
2286
2287 loop {
2288 match (state, character) {
2289 (ParseState::InDescriptor, Some(character)) if character.is_ascii_whitespace() => {
2290 if !current_descriptor.is_empty() {
2294 descriptors.push(current_descriptor);
2295 current_descriptor = String::new();
2296 state = ParseState::AfterDescriptor;
2297 }
2298 },
2299 (ParseState::InDescriptor, Some(',')) => {
2300 if !current_descriptor.is_empty() {
2304 descriptors.push(current_descriptor);
2305 }
2306 break;
2307 },
2308 (ParseState::InDescriptor, Some('(')) => {
2309 current_descriptor.push('(');
2311 state = ParseState::InParens;
2312 },
2313 (ParseState::InDescriptor, Some(character)) => {
2314 current_descriptor.push(character);
2316 },
2317 (ParseState::InDescriptor, None) => {
2318 if !current_descriptor.is_empty() {
2321 descriptors.push(current_descriptor);
2322 }
2323 break;
2324 },
2325 (ParseState::InParens, Some(')')) => {
2326 current_descriptor.push(')');
2328 state = ParseState::InDescriptor;
2329 },
2330 (ParseState::InParens, Some(character)) => {
2331 current_descriptor.push(character);
2333 },
2334 (ParseState::InParens, None) => {
2335 descriptors.push(current_descriptor);
2338 break;
2339 },
2340 (ParseState::AfterDescriptor, Some(character))
2341 if character.is_ascii_whitespace() =>
2342 {
2343 },
2345 (ParseState::AfterDescriptor, Some(_)) => {
2346 state = ParseState::InDescriptor;
2349 continue;
2350 },
2351 (ParseState::AfterDescriptor, None) => {
2352 break;
2354 },
2355 }
2356
2357 character = characters.next();
2358 if let Some(character) = character {
2359 current_index += character.len_utf8();
2360 }
2361 }
2362
2363 let mut error = false;
2365 let mut width: Option<u32> = None;
2367 let mut density: Option<f64> = None;
2369 let mut future_compat_h: Option<u32> = None;
2371
2372 for descriptor in descriptors.into_iter() {
2375 let Some(last_character) = descriptor.chars().last() else {
2376 break;
2377 };
2378
2379 let first_part_of_string = &descriptor[0..descriptor.len() - last_character.len_utf8()];
2380 match last_character {
2381 'w' if is_valid_non_negative_integer_string(first_part_of_string) &&
2388 density.is_none() &&
2389 width.is_none() =>
2390 {
2391 match parse_unsigned_integer(first_part_of_string.chars()) {
2392 Ok(number) if number > 0 => {
2393 width = Some(number);
2394 continue;
2395 },
2396 _ => error = true,
2397 }
2398 },
2399
2400 'x' if is_valid_floating_point_number_string(first_part_of_string) &&
2414 width.is_none() &&
2415 density.is_none() &&
2416 future_compat_h.is_none() =>
2417 {
2418 match first_part_of_string.parse::<f64>() {
2419 Ok(number) if number.is_finite() && number >= 0. => {
2420 density = Some(number);
2421 continue;
2422 },
2423 _ => error = true,
2424 }
2425 },
2426
2427 'h' if is_valid_non_negative_integer_string(first_part_of_string) &&
2436 future_compat_h.is_none() &&
2437 density.is_none() =>
2438 {
2439 match parse_unsigned_integer(first_part_of_string.chars()) {
2440 Ok(number) if number > 0 => {
2441 future_compat_h = Some(number);
2442 continue;
2443 },
2444 _ => error = true,
2445 }
2446 },
2447
2448 _ => error = true,
2451 }
2452
2453 if error {
2454 break;
2455 }
2456 }
2457
2458 if future_compat_h.is_some() && width.is_none() {
2460 error = true;
2461 }
2462
2463 if !error {
2467 let image_source = ImageSource {
2468 url: url.into(),
2469 descriptor: Descriptor { width, density },
2470 };
2471 candidates.push(image_source);
2472 }
2473
2474 }
2476 candidates
2477}
2478
2479#[derive(Clone)]
2480enum ChangeType {
2481 Environment {
2482 selected_source: USVString,
2483 selected_pixel_density: f64,
2484 },
2485 Element,
2486}
2487
2488fn is_supported_image_mime_type(input: &str) -> bool {
2490 let mime_type = input.trim();
2492
2493 let mime_type_essence = match mime_type.find(';') {
2495 Some(semi) => &mime_type[..semi],
2496 _ => mime_type,
2497 };
2498
2499 if mime_type_essence.is_empty() {
2504 return true;
2505 }
2506
2507 SUPPORTED_IMAGE_MIME_TYPES.contains(&mime_type_essence)
2508}