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::jsapi::JSAutoRealm;
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 servo_url::ServoUrl;
33use servo_url::origin::MutableOrigin;
34use style::attr::{AttrValue, LengthOrPercentageOrAuto, parse_unsigned_integer};
35use style::stylesheets::CssRuleType;
36use style::values::specified::source_size_list::SourceSizeList;
37use style_traits::ParsingMode;
38use url::Url;
39
40use crate::css::parser_context_for_anonymous_content;
41use crate::document_loader::{LoadBlocker, LoadType};
42use crate::dom::activation::Activatable;
43use crate::dom::attr::Attr;
44use crate::dom::bindings::cell::{DomRefCell, RefMut};
45use crate::dom::bindings::codegen::Bindings::DOMRectBinding::DOMRect_Binding::DOMRectMethods;
46use crate::dom::bindings::codegen::Bindings::ElementBinding::Element_Binding::ElementMethods;
47use crate::dom::bindings::codegen::Bindings::HTMLImageElementBinding::HTMLImageElementMethods;
48use crate::dom::bindings::codegen::Bindings::MouseEventBinding::MouseEventMethods;
49use crate::dom::bindings::codegen::Bindings::NodeBinding::Node_Binding::NodeMethods;
50use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
51use crate::dom::bindings::error::{Error, Fallible};
52use crate::dom::bindings::inheritance::Castable;
53use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
54use crate::dom::bindings::reflector::DomGlobal;
55use crate::dom::bindings::root::{DomRoot, LayoutDom, MutNullableDom};
56use crate::dom::bindings::str::{DOMString, USVString};
57use crate::dom::csp::{GlobalCspReporting, Violation};
58use crate::dom::document::Document;
59use crate::dom::element::{
60 AttributeMutation, CustomElementCreationMode, Element, ElementCreator, LayoutElementHelpers,
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::medialist::MediaList;
74use crate::dom::mouseevent::MouseEvent;
75use crate::dom::node::{BindContext, Node, NodeDamage, NodeTraits, ShadowIncluding, UnbindContext};
76use crate::dom::performance::performanceresourcetiming::InitiatorType;
77use crate::dom::promise::Promise;
78use crate::dom::virtualmethods::VirtualMethods;
79use crate::dom::window::Window;
80use crate::fetch::{RequestWithGlobalScope, create_a_potential_cors_request};
81use crate::microtask::{Microtask, MicrotaskRunnable};
82use crate::network_listener::{self, FetchResponseListener, ResourceTimingListener};
83use crate::realms::enter_realm;
84use crate::script_runtime::CanGc;
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 fn process_request_eof(&mut self, _: RequestId) {}
250
251 fn process_response(
252 &mut self,
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 if let Some(ref content_type) = metadata.content_type {
270 let mime: Mime = content_type.clone().into_inner().into();
271 if mime.type_() == mime::MULTIPART && mime.subtype().as_str() == "x-mixed-replace" {
272 self.aborted = true;
273 }
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(&mut self, request_id: RequestId, payload: Vec<u8>) {
299 if self.status.is_ok() {
300 self.image_cache.notify_pending_response(
301 self.id,
302 FetchResponseMsg::ProcessResponseChunk(request_id, payload.into()),
303 );
304 }
305 }
306
307 fn process_response_eof(
308 self,
309 request_id: RequestId,
310 response: Result<(), NetworkError>,
311 timing: ResourceFetchTiming,
312 ) {
313 self.image_cache.notify_pending_response(
314 self.id,
315 FetchResponseMsg::ProcessResponseEOF(request_id, response.clone(), timing.clone()),
316 );
317 network_listener::submit_timing(&self, &response, &timing, CanGc::note());
318 }
319
320 fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<Violation>) {
321 let global = &self.resource_timing_global();
322 let elem = self.element.root();
323 let source_position = elem
324 .upcast::<Element>()
325 .compute_source_position(elem.line_number as u32);
326 global.report_csp_violations(violations, None, Some(source_position));
327 }
328}
329
330impl ResourceTimingListener for ImageContext {
331 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
332 (
333 InitiatorType::LocalName("img".to_string()),
334 self.url.clone(),
335 )
336 }
337
338 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
339 self.doc.root().global()
340 }
341}
342
343#[expect(non_snake_case)]
344impl HTMLImageElement {
345 fn fetch_image(&self, img_url: &ServoUrl, can_gc: CanGc) {
347 let window = self.owner_window();
348
349 let cache_result = window.image_cache().get_cached_image_status(
350 img_url.clone(),
351 window.origin().immutable().clone(),
352 cors_setting_for_element(self.upcast()),
353 );
354
355 match cache_result {
356 ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable {
357 image,
358 url,
359 }) => self.process_image_response(ImageResponse::Loaded(image, url), can_gc),
360 ImageCacheResult::Available(ImageOrMetadataAvailable::MetadataAvailable(
361 metadata,
362 id,
363 )) => {
364 self.process_image_response(ImageResponse::MetadataLoaded(metadata), can_gc);
365 self.register_image_cache_callback(id, ChangeType::Element);
366 },
367 ImageCacheResult::Pending(id) => {
368 self.register_image_cache_callback(id, ChangeType::Element);
369 },
370 ImageCacheResult::ReadyForRequest(id) => {
371 self.fetch_request(img_url, id);
372 self.register_image_cache_callback(id, ChangeType::Element);
373 },
374 ImageCacheResult::FailedToLoadOrDecode => {
375 self.process_image_response(ImageResponse::FailedToLoadOrDecode, can_gc)
376 },
377 };
378 }
379
380 fn register_image_cache_callback(&self, id: PendingImageId, change_type: ChangeType) {
381 let trusted_node = Trusted::new(self);
382 let generation = self.generation_id();
383 let window = self.owner_window();
384 let callback = window.register_image_cache_listener(id, move |response| {
385 let trusted_node = trusted_node.clone();
386 let window = trusted_node.root().owner_window();
387 let callback_type = change_type.clone();
388
389 window
390 .as_global_scope()
391 .task_manager()
392 .networking_task_source()
393 .queue(task!(process_image_response: move || {
394 let element = trusted_node.root();
395
396 if generation != element.generation_id() {
398 return;
399 }
400
401 match callback_type {
402 ChangeType::Element => {
403 element.process_image_response(response.response, CanGc::note());
404 }
405 ChangeType::Environment { selected_source, selected_pixel_density } => {
406 element.process_image_response_for_environment_change(
407 response.response, selected_source, generation, selected_pixel_density, CanGc::note()
408 );
409 }
410 }
411 }));
412 });
413
414 window.image_cache().add_listener(ImageLoadListener::new(
415 callback,
416 window.pipeline_id(),
417 id,
418 ));
419 }
420
421 fn fetch_request(&self, img_url: &ServoUrl, id: PendingImageId) {
422 let document = self.owner_document();
423 let window = self.owner_window();
424
425 let context = ImageContext {
426 image_cache: window.image_cache(),
427 status: Ok(()),
428 id,
429 aborted: false,
430 doc: Trusted::new(&document),
431 element: Trusted::new(self),
432 url: img_url.clone(),
433 };
434
435 let global = document.global();
438 let mut request = create_a_potential_cors_request(
439 Some(window.webview_id()),
440 img_url.clone(),
441 Destination::Image,
442 cors_setting_for_element(self.upcast()),
443 None,
444 global.get_referrer(),
445 )
446 .with_global_scope(&global)
447 .referrer_policy(referrer_policy_for_element(self.upcast()));
448
449 if self.uses_srcset_or_picture() {
450 request = request.initiator(Initiator::ImageSet);
451 }
452
453 document.fetch_background(request, context);
456 }
457
458 fn handle_loaded_image(&self, image: Image, url: ServoUrl, can_gc: CanGc) {
460 self.current_request.borrow_mut().metadata = Some(image.metadata());
461 self.current_request.borrow_mut().final_url = Some(url);
462 self.current_request.borrow_mut().image = Some(image);
463 self.current_request.borrow_mut().state = State::CompletelyAvailable;
464 LoadBlocker::terminate(&self.current_request.borrow().blocker, can_gc);
465 self.upcast::<Node>().dirty(NodeDamage::Other);
467 self.resolve_image_decode_promises();
468 }
469
470 fn process_image_response(&self, image: ImageResponse, can_gc: CanGc) {
472 let (trigger_image_load, trigger_image_error) = match (image, self.image_request.get()) {
478 (ImageResponse::Loaded(image, url), ImageRequestPhase::Current) => {
479 self.handle_loaded_image(image, url, can_gc);
480 (true, false)
481 },
482 (ImageResponse::Loaded(image, url), ImageRequestPhase::Pending) => {
483 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, can_gc);
484 self.image_request.set(ImageRequestPhase::Current);
485 self.handle_loaded_image(image, url, can_gc);
486 (true, false)
487 },
488 (ImageResponse::MetadataLoaded(meta), ImageRequestPhase::Current) => {
489 self.current_request.borrow_mut().state = State::PartiallyAvailable;
494 self.current_request.borrow_mut().metadata = Some(meta);
495 (false, false)
496 },
497 (ImageResponse::MetadataLoaded(_), ImageRequestPhase::Pending) => {
498 self.pending_request.borrow_mut().state = State::PartiallyAvailable;
502 (false, false)
503 },
504 (ImageResponse::FailedToLoadOrDecode, ImageRequestPhase::Current) => {
505 self.abort_request(State::Broken, ImageRequestPhase::Current, can_gc);
511
512 self.load_broken_image_icon();
513
514 (false, true)
518 },
519 (ImageResponse::FailedToLoadOrDecode, ImageRequestPhase::Pending) => {
520 self.abort_request(State::Broken, ImageRequestPhase::Current, can_gc);
526 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, can_gc);
527
528 mem::swap(
530 &mut *self.current_request.borrow_mut(),
531 &mut *self.pending_request.borrow_mut(),
532 );
533 self.image_request.set(ImageRequestPhase::Current);
534
535 self.current_request.borrow_mut().state = State::Broken;
537
538 self.load_broken_image_icon();
539
540 (false, true)
542 },
543 };
544
545 if trigger_image_load {
547 self.upcast::<EventTarget>()
549 .fire_event(atom!("load"), can_gc);
550 self.upcast::<EventTarget>()
551 .fire_event(atom!("loadend"), can_gc);
552 }
553
554 if trigger_image_error {
556 self.upcast::<EventTarget>()
557 .fire_event(atom!("error"), can_gc);
558 self.upcast::<EventTarget>()
559 .fire_event(atom!("loadend"), can_gc);
560 }
561 }
562
563 fn process_image_response_for_environment_change(
566 &self,
567 image: ImageResponse,
568 selected_source: USVString,
569 generation: u32,
570 selected_pixel_density: f64,
571 can_gc: CanGc,
572 ) {
573 match image {
574 ImageResponse::Loaded(image, url) => {
575 self.pending_request.borrow_mut().metadata = Some(image.metadata());
576 self.pending_request.borrow_mut().final_url = Some(url);
577 self.pending_request.borrow_mut().image = Some(image);
578 self.finish_reacting_to_environment_change(
579 selected_source,
580 generation,
581 selected_pixel_density,
582 );
583 },
584 ImageResponse::FailedToLoadOrDecode => {
585 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, can_gc);
593 },
594 ImageResponse::MetadataLoaded(meta) => {
595 self.pending_request.borrow_mut().metadata = Some(meta);
596 },
597 };
598 }
599
600 fn abort_request(&self, state: State, request: ImageRequestPhase, can_gc: CanGc) {
602 let mut request = match request {
603 ImageRequestPhase::Current => self.current_request.borrow_mut(),
604 ImageRequestPhase::Pending => self.pending_request.borrow_mut(),
605 };
606 LoadBlocker::terminate(&request.blocker, can_gc);
607 request.state = state;
608 request.image = None;
609 request.metadata = None;
610 request.current_pixel_density = None;
611
612 if matches!(state, State::Broken) {
613 self.reject_image_decode_promises();
614 } else if matches!(state, State::CompletelyAvailable) {
615 self.resolve_image_decode_promises();
616 }
617 }
618
619 fn create_source_set(&self) -> SourceSet {
621 let element = self.upcast::<Element>();
622
623 let mut source_set = SourceSet::new();
625
626 if let Some(srcset) = element.get_attribute(&ns!(), &local_name!("srcset")) {
629 source_set.image_sources = parse_a_srcset_attribute(&srcset.value());
630 }
631
632 if let Some(sizes) = element.get_attribute(&ns!(), &local_name!("sizes")) {
634 source_set.source_size = parse_a_sizes_attribute(&sizes.value());
635 }
636
637 let src = element.get_string_attribute(&local_name!("src"));
641 let no_density_source_of_1 = source_set
642 .image_sources
643 .iter()
644 .all(|source| source.descriptor.density != Some(1.));
645 let no_width_descriptor = source_set
646 .image_sources
647 .iter()
648 .all(|source| source.descriptor.width.is_none());
649 if !src.is_empty() && no_density_source_of_1 && no_width_descriptor {
650 source_set.image_sources.push(ImageSource {
651 url: src.to_string(),
652 descriptor: Descriptor {
653 width: None,
654 density: None,
655 },
656 })
657 }
658
659 self.normalise_source_densities(&mut source_set);
661
662 source_set
664 }
665
666 fn update_source_set(&self) {
668 *self.source_set.borrow_mut() = SourceSet::new();
670
671 let elem = self.upcast::<Element>();
676 let parent = elem.upcast::<Node>().GetParentElement();
677 let elements = match parent.as_ref() {
678 Some(p) => {
679 if p.is::<HTMLPictureElement>() {
680 p.upcast::<Node>()
681 .children()
682 .filter_map(DomRoot::downcast::<Element>)
683 .map(|n| DomRoot::from_ref(&*n))
684 .collect()
685 } else {
686 vec![DomRoot::from_ref(elem)]
687 }
688 },
689 None => vec![DomRoot::from_ref(elem)],
690 };
691
692 for element in &elements {
694 if *element == DomRoot::from_ref(elem) {
696 *self.source_set.borrow_mut() = self.create_source_set();
699
700 return;
702 }
703 if !element.is::<HTMLSourceElement>() {
705 continue;
706 }
707
708 let mut source_set = SourceSet::new();
709
710 match element.get_attribute(&ns!(), &local_name!("srcset")) {
714 Some(srcset) => {
715 source_set.image_sources = parse_a_srcset_attribute(&srcset.value());
716 },
717 _ => continue,
718 }
719
720 if source_set.image_sources.is_empty() {
722 continue;
723 }
724
725 if let Some(media) = element.get_attribute(&ns!(), &local_name!("media")) {
728 if !MediaList::matches_environment(&element.owner_document(), &media.value()) {
729 continue;
730 }
731 }
732
733 if let Some(sizes) = element.get_attribute(&ns!(), &local_name!("sizes")) {
736 source_set.source_size = parse_a_sizes_attribute(&sizes.value());
737 }
738
739 if let Some(type_) = element.get_attribute(&ns!(), &local_name!("type")) {
742 if !is_supported_image_mime_type(&type_.value()) {
743 continue;
744 }
745 }
746
747 if element
750 .get_attribute(&ns!(), &local_name!("width"))
751 .is_some() ||
752 element
753 .get_attribute(&ns!(), &local_name!("height"))
754 .is_some()
755 {
756 self.dimension_attribute_source.set(Some(element));
757 } else {
758 self.dimension_attribute_source.set(Some(elem));
759 }
760
761 self.normalise_source_densities(&mut source_set);
763
764 *self.source_set.borrow_mut() = source_set;
766
767 return;
769 }
770 }
771
772 fn evaluate_source_size_list(&self, source_size_list: &SourceSizeList) -> Au {
773 let document = self.owner_document();
774 let quirks_mode = document.quirks_mode();
775 source_size_list.evaluate(document.window().layout().device(), quirks_mode)
776 }
777
778 fn normalise_source_densities(&self, source_set: &mut SourceSet) {
780 let source_size = self.evaluate_source_size_list(&source_set.source_size);
782
783 for image_source in &mut source_set.image_sources {
785 if image_source.descriptor.density.is_some() {
788 continue;
789 }
790
791 if image_source.descriptor.width.is_some() {
795 let width = image_source.descriptor.width.unwrap();
796 image_source.descriptor.density = Some(width as f64 / source_size.to_f64_px());
797 } else {
798 image_source.descriptor.density = Some(1_f64);
800 }
801 }
802 }
803
804 fn select_image_source(&self) -> Option<(USVString, f64)> {
806 self.update_source_set();
808
809 if self.source_set.borrow().image_sources.is_empty() {
812 return None;
813 }
814
815 self.select_image_source_from_source_set()
817 }
818
819 fn select_image_source_from_source_set(&self) -> Option<(USVString, f64)> {
821 let source_set = self.source_set.borrow();
826 let len = source_set.image_sources.len();
827
828 let mut repeat_indices = FxHashSet::default();
830 for outer_index in 0..len {
831 if repeat_indices.contains(&outer_index) {
832 continue;
833 }
834 let imgsource = &source_set.image_sources[outer_index];
835 let pixel_density = imgsource.descriptor.density.unwrap();
836 for inner_index in (outer_index + 1)..len {
837 let imgsource2 = &source_set.image_sources[inner_index];
838 if pixel_density == imgsource2.descriptor.density.unwrap() {
839 repeat_indices.insert(inner_index);
840 }
841 }
842 }
843
844 let mut max = (0f64, 0);
845 let img_sources = &mut vec![];
846 for (index, image_source) in source_set.image_sources.iter().enumerate() {
847 if repeat_indices.contains(&index) {
848 continue;
849 }
850 let den = image_source.descriptor.density.unwrap();
851 if max.0 < den {
852 max = (den, img_sources.len());
853 }
854 img_sources.push(image_source);
855 }
856
857 let mut best_candidate = max;
860 let device_pixel_ratio = self
861 .owner_document()
862 .window()
863 .viewport_details()
864 .hidpi_scale_factor
865 .get() as f64;
866 for (index, image_source) in img_sources.iter().enumerate() {
867 let current_den = image_source.descriptor.density.unwrap();
868 if current_den < best_candidate.0 && current_den >= device_pixel_ratio {
869 best_candidate = (current_den, index);
870 }
871 }
872 let selected_source = img_sources.remove(best_candidate.1).clone();
873
874 Some((
876 USVString(selected_source.url),
877 selected_source.descriptor.density.unwrap(),
878 ))
879 }
880
881 fn init_image_request(
882 &self,
883 request: &mut RefMut<'_, ImageRequest>,
884 url: &ServoUrl,
885 src: &USVString,
886 can_gc: CanGc,
887 ) {
888 request.parsed_url = Some(url.clone());
889 request.source_url = Some(src.clone());
890 request.image = None;
891 request.metadata = None;
892 let document = self.owner_document();
893 LoadBlocker::terminate(&request.blocker, can_gc);
894 *request.blocker.borrow_mut() =
895 Some(LoadBlocker::new(&document, LoadType::Image(url.clone())));
896 }
897
898 fn prepare_image_request(
900 &self,
901 selected_source: &USVString,
902 selected_pixel_density: f64,
903 image_url: &ServoUrl,
904 can_gc: CanGc,
905 ) {
906 match self.image_request.get() {
907 ImageRequestPhase::Pending => {
908 if self
911 .pending_request
912 .borrow()
913 .parsed_url
914 .as_ref()
915 .is_some_and(|parsed_url| *parsed_url == *image_url)
916 {
917 return;
918 }
919 },
920 ImageRequestPhase::Current => {
921 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, can_gc);
923
924 let mut current_request = self.current_request.borrow_mut();
927 let mut pending_request = self.pending_request.borrow_mut();
928
929 match (current_request.parsed_url.as_ref(), current_request.state) {
930 (Some(parsed_url), State::PartiallyAvailable) => {
931 if *parsed_url == *image_url {
937 return;
939 }
940
941 self.image_request.set(ImageRequestPhase::Pending);
945 self.init_image_request(
946 &mut pending_request,
947 image_url,
948 selected_source,
949 can_gc,
950 );
951 pending_request.current_pixel_density = Some(selected_pixel_density);
952 },
953 (_, State::Broken) | (_, State::Unavailable) => {
954 self.init_image_request(
958 &mut current_request,
959 image_url,
960 selected_source,
961 can_gc,
962 );
963 current_request.current_pixel_density = Some(selected_pixel_density);
964 self.reject_image_decode_promises();
965 },
966 (_, _) => {
967 self.image_request.set(ImageRequestPhase::Pending);
971 self.init_image_request(
972 &mut pending_request,
973 image_url,
974 selected_source,
975 can_gc,
976 );
977 pending_request.current_pixel_density = Some(selected_pixel_density);
978 },
979 }
980 },
981 }
982
983 self.fetch_image(image_url, can_gc);
984 }
985
986 fn update_the_image_data_sync_steps(&self, can_gc: CanGc) {
988 let Some((selected_source, selected_pixel_density)) = self.select_image_source() else {
991 self.abort_request(State::Broken, ImageRequestPhase::Current, can_gc);
996 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, can_gc);
997 self.image_request.set(ImageRequestPhase::Current);
998
999 let this = Trusted::new(self);
1002
1003 self.owner_global().task_manager().dom_manipulation_task_source().queue(
1004 task!(image_null_source_error: move || {
1005 let this = this.root();
1006
1007 {
1009 let mut current_request =
1010 this.current_request.borrow_mut();
1011 current_request.source_url = None;
1012 current_request.parsed_url = None;
1013 }
1014
1015 let has_src_attribute = this.upcast::<Element>().has_attribute(&local_name!("src"));
1021
1022 if has_src_attribute || this.uses_srcset_or_picture() {
1023 this.upcast::<EventTarget>().fire_event(atom!("error"), CanGc::note());
1024 }
1025 }));
1026
1027 return;
1029 };
1030
1031 let Ok(image_url) = self.owner_document().base_url().join(&selected_source) else {
1034 self.abort_request(State::Broken, ImageRequestPhase::Current, can_gc);
1039 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, can_gc);
1040
1041 self.image_request.set(ImageRequestPhase::Current);
1043
1044 let this = Trusted::new(self);
1047
1048 self.owner_global()
1049 .task_manager()
1050 .dom_manipulation_task_source()
1051 .queue(task!(image_selected_source_error: move || {
1052 let this = this.root();
1053
1054 {
1056 let mut current_request =
1057 this.current_request.borrow_mut();
1058 current_request.source_url = Some(selected_source);
1059 current_request.parsed_url = None;
1060 }
1061
1062 this.upcast::<EventTarget>().fire_event(atom!("error"), CanGc::note());
1066 }));
1067
1068 return;
1070 };
1071
1072 self.prepare_image_request(&selected_source, selected_pixel_density, &image_url, can_gc);
1073 }
1074
1075 pub(crate) fn update_the_image_data(&self, can_gc: CanGc) {
1077 self.generation.set(self.generation.get() + 1);
1079
1080 if !self.owner_document().is_active() {
1082 }
1088
1089 self.current_request.borrow_mut().state = State::Unavailable;
1098
1099 let mut selected_source = None;
1103 let mut selected_pixel_density = None;
1104
1105 let src = self
1109 .upcast::<Element>()
1110 .get_string_attribute(&local_name!("src"));
1111
1112 if !self.uses_srcset_or_picture() && !src.is_empty() {
1113 selected_source = Some(USVString(src.to_string()));
1114 selected_pixel_density = Some(1_f64);
1115 };
1116
1117 self.last_selected_source
1119 .borrow_mut()
1120 .clone_from(&selected_source);
1121
1122 if let Some(selected_source) = selected_source {
1124 if let Ok(image_url) = self.owner_document().base_url().join(&selected_source) {
1128 let window = self.owner_window();
1132 let response = window.image_cache().get_image(
1133 image_url.clone(),
1134 window.origin().immutable().clone(),
1135 cors_setting_for_element(self.upcast()),
1136 );
1137
1138 if let Some(image) = response {
1140 self.abort_request(
1145 State::CompletelyAvailable,
1146 ImageRequestPhase::Current,
1147 can_gc,
1148 );
1149 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, can_gc);
1150
1151 self.image_request.set(ImageRequestPhase::Current);
1153
1154 let mut current_request = self.current_request.borrow_mut();
1157 current_request.metadata = Some(image.metadata());
1158 current_request.image = Some(image);
1159 current_request.final_url = Some(image_url.clone());
1160
1161 self.upcast::<Node>().dirty(NodeDamage::Other);
1164
1165 current_request.current_pixel_density = selected_pixel_density;
1168
1169 let this = Trusted::new(self);
1172
1173 self.owner_global()
1174 .task_manager()
1175 .dom_manipulation_task_source()
1176 .queue(task!(image_load_event: move || {
1177 let this = this.root();
1178
1179 {
1184 let mut current_request =
1185 this.current_request.borrow_mut();
1186 current_request.source_url = Some(selected_source);
1187 current_request.parsed_url = Some(image_url);
1188 }
1189
1190 this.upcast::<EventTarget>().fire_event(atom!("load"), CanGc::note());
1194 }));
1195
1196 return;
1198 }
1199 }
1200 }
1201
1202 let task = ImageElementMicrotask::UpdateImageData {
1205 elem: DomRoot::from_ref(self),
1206 generation: self.generation.get(),
1207 };
1208
1209 ScriptThread::await_stable_state(Microtask::ImageElement(task));
1210 }
1211
1212 pub(crate) fn react_to_environment_changes(&self) {
1214 let task = ImageElementMicrotask::EnvironmentChanges {
1216 elem: DomRoot::from_ref(self),
1217 generation: self.generation.get(),
1218 };
1219
1220 ScriptThread::await_stable_state(Microtask::ImageElement(task));
1221 }
1222
1223 fn react_to_environment_changes_sync_steps(&self, generation: u32, can_gc: CanGc) {
1225 let document = self.owner_document();
1226 let has_pending_request = matches!(self.image_request.get(), ImageRequestPhase::Pending);
1227
1228 if !document.is_active() || !self.uses_srcset_or_picture() || has_pending_request {
1232 return;
1233 }
1234
1235 let Some((selected_source, selected_pixel_density)) = self.select_image_source() else {
1238 return;
1240 };
1241
1242 let mut same_selected_source = self
1245 .last_selected_source
1246 .borrow()
1247 .as_ref()
1248 .is_some_and(|source| *source == selected_source);
1249
1250 same_selected_source = same_selected_source ||
1254 self.current_request
1255 .borrow()
1256 .source_url
1257 .as_ref()
1258 .is_some_and(|source| *source == selected_source);
1259
1260 let same_selected_pixel_density = self
1261 .current_request
1262 .borrow()
1263 .current_pixel_density
1264 .is_some_and(|pixel_density| pixel_density == selected_pixel_density);
1265
1266 if same_selected_source && same_selected_pixel_density {
1267 return;
1268 }
1269
1270 let Ok(image_url) = document.base_url().join(&selected_source) else {
1274 return;
1275 };
1276
1277 self.image_request.set(ImageRequestPhase::Pending);
1279 self.init_image_request(
1280 &mut self.pending_request.borrow_mut(),
1281 &image_url,
1282 &selected_source,
1283 can_gc,
1284 );
1285
1286 let window = self.owner_window();
1289 let cache_result = window.image_cache().get_cached_image_status(
1290 image_url.clone(),
1291 window.origin().immutable().clone(),
1292 cors_setting_for_element(self.upcast()),
1293 );
1294
1295 let change_type = ChangeType::Environment {
1296 selected_source: selected_source.clone(),
1297 selected_pixel_density,
1298 };
1299
1300 match cache_result {
1301 ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable { .. }) => {
1302 self.finish_reacting_to_environment_change(
1303 selected_source,
1304 generation,
1305 selected_pixel_density,
1306 );
1307 },
1308 ImageCacheResult::Available(ImageOrMetadataAvailable::MetadataAvailable(m, id)) => {
1309 self.process_image_response_for_environment_change(
1310 ImageResponse::MetadataLoaded(m),
1311 selected_source,
1312 generation,
1313 selected_pixel_density,
1314 can_gc,
1315 );
1316 self.register_image_cache_callback(id, change_type);
1317 },
1318 ImageCacheResult::FailedToLoadOrDecode => {
1319 self.process_image_response_for_environment_change(
1320 ImageResponse::FailedToLoadOrDecode,
1321 selected_source,
1322 generation,
1323 selected_pixel_density,
1324 can_gc,
1325 );
1326 },
1327 ImageCacheResult::ReadyForRequest(id) => {
1328 self.fetch_request(&image_url, id);
1329 self.register_image_cache_callback(id, change_type);
1330 },
1331 ImageCacheResult::Pending(id) => {
1332 self.register_image_cache_callback(id, change_type);
1333 },
1334 }
1335 }
1336
1337 fn react_to_decode_image_sync_steps(&self, promise: Rc<Promise>, can_gc: CanGc) {
1339 if !self.owner_document().is_fully_active() ||
1343 matches!(self.current_request.borrow().state, State::Broken)
1344 {
1345 promise.reject_error(Error::Encoding(None), can_gc);
1346 } else if matches!(
1347 self.current_request.borrow().state,
1348 State::CompletelyAvailable
1349 ) {
1350 promise.resolve_native(&(), can_gc);
1352 } else if matches!(self.current_request.borrow().state, State::Unavailable) &&
1353 self.current_request.borrow().source_url.is_none()
1354 {
1355 promise.reject_error(Error::Encoding(None), can_gc);
1360 } else {
1361 self.image_decode_promises.borrow_mut().push(promise);
1362 }
1363 }
1364
1365 fn resolve_image_decode_promises(&self) {
1367 if self.image_decode_promises.borrow().is_empty() {
1368 return;
1369 }
1370
1371 let trusted_image_decode_promises: Vec<TrustedPromise> = self
1374 .image_decode_promises
1375 .borrow()
1376 .iter()
1377 .map(|promise| TrustedPromise::new(promise.clone()))
1378 .collect();
1379
1380 self.image_decode_promises.borrow_mut().clear();
1381
1382 self.owner_global()
1383 .task_manager()
1384 .dom_manipulation_task_source()
1385 .queue(task!(fulfill_image_decode_promises: move || {
1386 for trusted_promise in trusted_image_decode_promises {
1387 trusted_promise.root().resolve_native(&(), CanGc::note());
1388 }
1389 }));
1390 }
1391
1392 fn reject_image_decode_promises(&self) {
1394 if self.image_decode_promises.borrow().is_empty() {
1395 return;
1396 }
1397
1398 let trusted_image_decode_promises: Vec<TrustedPromise> = self
1401 .image_decode_promises
1402 .borrow()
1403 .iter()
1404 .map(|promise| TrustedPromise::new(promise.clone()))
1405 .collect();
1406
1407 self.image_decode_promises.borrow_mut().clear();
1408
1409 self.owner_global()
1410 .task_manager()
1411 .dom_manipulation_task_source()
1412 .queue(task!(reject_image_decode_promises: move || {
1413 for trusted_promise in trusted_image_decode_promises {
1414 trusted_promise.root().reject_error(Error::Encoding(None), CanGc::note());
1415 }
1416 }));
1417 }
1418
1419 fn finish_reacting_to_environment_change(
1421 &self,
1422 selected_source: USVString,
1423 generation: u32,
1424 selected_pixel_density: f64,
1425 ) {
1426 let this = Trusted::new(self);
1429
1430 self.owner_global().task_manager().dom_manipulation_task_source().queue(
1431 task!(image_load_event: move || {
1432 let this = this.root();
1433
1434 if this.generation.get() != generation {
1437 this.abort_request(State::Unavailable, ImageRequestPhase::Pending, CanGc::note());
1438 this.image_request.set(ImageRequestPhase::Current);
1439 return;
1440 }
1441
1442 *this.last_selected_source.borrow_mut() = Some(selected_source);
1445
1446 {
1447 let mut pending_request = this.pending_request.borrow_mut();
1448
1449 pending_request.state = State::CompletelyAvailable;
1451
1452 pending_request.current_pixel_density = Some(selected_pixel_density);
1453
1454 mem::swap(&mut *this.current_request.borrow_mut(), &mut *pending_request);
1460 }
1461
1462 this.abort_request(State::Unavailable, ImageRequestPhase::Pending, CanGc::note());
1463 this.image_request.set(ImageRequestPhase::Current);
1464
1465 this.upcast::<Node>().dirty(NodeDamage::Other);
1467
1468 this.upcast::<EventTarget>().fire_event(atom!("load"), CanGc::note());
1470 })
1471 );
1472 }
1473
1474 fn uses_srcset_or_picture(&self) -> bool {
1476 let element = self.upcast::<Element>();
1477
1478 let has_srcset_attribute = element.has_attribute(&local_name!("srcset"));
1479 let has_parent_picture = element
1480 .upcast::<Node>()
1481 .GetParentElement()
1482 .is_some_and(|parent| parent.is::<HTMLPictureElement>());
1483 has_srcset_attribute || has_parent_picture
1484 }
1485
1486 fn new_inherited(
1487 local_name: LocalName,
1488 prefix: Option<Prefix>,
1489 document: &Document,
1490 creator: ElementCreator,
1491 ) -> HTMLImageElement {
1492 HTMLImageElement {
1493 htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
1494 image_request: Cell::new(ImageRequestPhase::Current),
1495 current_request: DomRefCell::new(ImageRequest {
1496 state: State::Unavailable,
1497 parsed_url: None,
1498 source_url: None,
1499 image: None,
1500 metadata: None,
1501 blocker: DomRefCell::new(None),
1502 final_url: None,
1503 current_pixel_density: None,
1504 }),
1505 pending_request: DomRefCell::new(ImageRequest {
1506 state: State::Unavailable,
1507 parsed_url: None,
1508 source_url: None,
1509 image: None,
1510 metadata: None,
1511 blocker: DomRefCell::new(None),
1512 final_url: None,
1513 current_pixel_density: None,
1514 }),
1515 form_owner: Default::default(),
1516 generation: Default::default(),
1517 source_set: DomRefCell::new(SourceSet::new()),
1518 dimension_attribute_source: Default::default(),
1519 last_selected_source: DomRefCell::new(None),
1520 image_decode_promises: DomRefCell::new(vec![]),
1521 line_number: creator.return_line_number(),
1522 }
1523 }
1524
1525 pub(crate) fn new(
1526 local_name: LocalName,
1527 prefix: Option<Prefix>,
1528 document: &Document,
1529 proto: Option<HandleObject>,
1530 creator: ElementCreator,
1531 can_gc: CanGc,
1532 ) -> DomRoot<HTMLImageElement> {
1533 let image_element = Node::reflect_node_with_proto(
1534 Box::new(HTMLImageElement::new_inherited(
1535 local_name, prefix, document, creator,
1536 )),
1537 document,
1538 proto,
1539 can_gc,
1540 );
1541 image_element
1542 .dimension_attribute_source
1543 .set(Some(image_element.upcast()));
1544 image_element
1545 }
1546
1547 pub(crate) fn areas(&self) -> Option<Vec<DomRoot<HTMLAreaElement>>> {
1548 let elem = self.upcast::<Element>();
1549 let usemap_attr = elem.get_attribute(&ns!(), &local_name!("usemap"))?;
1550
1551 let value = usemap_attr.value();
1552
1553 if value.is_empty() || !value.is_char_boundary(1) {
1554 return None;
1555 }
1556
1557 let (first, last) = value.split_at(1);
1558
1559 if first != "#" || last.is_empty() {
1560 return None;
1561 }
1562
1563 let useMapElements = self
1564 .owner_document()
1565 .upcast::<Node>()
1566 .traverse_preorder(ShadowIncluding::No)
1567 .filter_map(DomRoot::downcast::<HTMLMapElement>)
1568 .find(|n| {
1569 n.upcast::<Element>()
1570 .get_name()
1571 .is_some_and(|n| *n == *last)
1572 });
1573
1574 useMapElements.map(|mapElem| mapElem.get_area_elements())
1575 }
1576
1577 pub(crate) fn same_origin(&self, origin: &MutableOrigin) -> bool {
1578 if let Some(ref image) = self.current_request.borrow().image {
1579 return image.cors_status() == CorsStatus::Safe;
1580 }
1581
1582 self.current_request
1583 .borrow()
1584 .final_url
1585 .as_ref()
1586 .is_some_and(|url| url.scheme() == "data" || url.origin().same_origin(origin))
1587 }
1588
1589 fn generation_id(&self) -> u32 {
1590 self.generation.get()
1591 }
1592
1593 fn load_broken_image_icon(&self) {
1594 let window = self.owner_window();
1595 let Some(broken_image_icon) = window.image_cache().get_broken_image_icon() else {
1596 return;
1597 };
1598
1599 self.current_request.borrow_mut().metadata = Some(broken_image_icon.metadata);
1600 self.current_request.borrow_mut().image = Some(Image::Raster(broken_image_icon));
1601 self.upcast::<Node>().dirty(NodeDamage::Other);
1602 }
1603
1604 pub(crate) fn full_image_url_for_user_interface(&self) -> Option<ServoUrl> {
1607 self.owner_document()
1608 .base_url()
1609 .join(&self.CurrentSrc())
1610 .ok()
1611 }
1612}
1613
1614#[derive(JSTraceable, MallocSizeOf)]
1615pub(crate) enum ImageElementMicrotask {
1616 UpdateImageData {
1617 elem: DomRoot<HTMLImageElement>,
1618 generation: u32,
1619 },
1620 EnvironmentChanges {
1621 elem: DomRoot<HTMLImageElement>,
1622 generation: u32,
1623 },
1624 Decode {
1625 elem: DomRoot<HTMLImageElement>,
1626 #[conditional_malloc_size_of]
1627 promise: Rc<Promise>,
1628 },
1629}
1630
1631impl MicrotaskRunnable for ImageElementMicrotask {
1632 fn handler(&self, can_gc: CanGc) {
1633 match *self {
1634 ImageElementMicrotask::UpdateImageData {
1635 ref elem,
1636 ref generation,
1637 } => {
1638 if elem.generation.get() == *generation {
1642 elem.update_the_image_data_sync_steps(can_gc);
1643 }
1644 },
1645 ImageElementMicrotask::EnvironmentChanges {
1646 ref elem,
1647 ref generation,
1648 } => {
1649 elem.react_to_environment_changes_sync_steps(*generation, can_gc);
1650 },
1651 ImageElementMicrotask::Decode {
1652 ref elem,
1653 ref promise,
1654 } => {
1655 elem.react_to_decode_image_sync_steps(promise.clone(), can_gc);
1656 },
1657 }
1658 }
1659
1660 fn enter_realm(&self) -> JSAutoRealm {
1661 match self {
1662 &ImageElementMicrotask::UpdateImageData { ref elem, .. } |
1663 &ImageElementMicrotask::EnvironmentChanges { ref elem, .. } |
1664 &ImageElementMicrotask::Decode { ref elem, .. } => enter_realm(&**elem),
1665 }
1666 }
1667}
1668
1669pub(crate) trait LayoutHTMLImageElementHelpers {
1670 fn image_url(self) -> Option<ServoUrl>;
1671 fn image_density(self) -> Option<f64>;
1672 fn image_data(self) -> (Option<Image>, Option<ImageMetadata>);
1673 fn get_width(self) -> LengthOrPercentageOrAuto;
1674 fn get_height(self) -> LengthOrPercentageOrAuto;
1675 fn showing_broken_image_icon(self) -> bool;
1676}
1677
1678impl<'dom> LayoutDom<'dom, HTMLImageElement> {
1679 #[expect(unsafe_code)]
1680 fn current_request(self) -> &'dom ImageRequest {
1681 unsafe { self.unsafe_get().current_request.borrow_for_layout() }
1682 }
1683
1684 #[expect(unsafe_code)]
1685 fn dimension_attribute_source(self) -> LayoutDom<'dom, Element> {
1686 unsafe {
1687 self.unsafe_get()
1688 .dimension_attribute_source
1689 .get_inner_as_layout()
1690 .expect("dimension attribute source should be always non-null")
1691 }
1692 }
1693}
1694
1695impl LayoutHTMLImageElementHelpers for LayoutDom<'_, HTMLImageElement> {
1696 fn image_url(self) -> Option<ServoUrl> {
1697 self.current_request().parsed_url.clone()
1698 }
1699
1700 fn image_data(self) -> (Option<Image>, Option<ImageMetadata>) {
1701 let current_request = self.current_request();
1702 (current_request.image.clone(), current_request.metadata)
1703 }
1704
1705 fn image_density(self) -> Option<f64> {
1706 self.current_request().current_pixel_density
1707 }
1708
1709 fn showing_broken_image_icon(self) -> bool {
1710 matches!(self.current_request().state, State::Broken)
1711 }
1712
1713 fn get_width(self) -> LengthOrPercentageOrAuto {
1714 self.dimension_attribute_source()
1715 .get_attr_for_layout(&ns!(), &local_name!("width"))
1716 .map(AttrValue::as_dimension)
1717 .cloned()
1718 .unwrap_or(LengthOrPercentageOrAuto::Auto)
1719 }
1720
1721 fn get_height(self) -> LengthOrPercentageOrAuto {
1722 self.dimension_attribute_source()
1723 .get_attr_for_layout(&ns!(), &local_name!("height"))
1724 .map(AttrValue::as_dimension)
1725 .cloned()
1726 .unwrap_or(LengthOrPercentageOrAuto::Auto)
1727 }
1728}
1729
1730fn parse_a_sizes_attribute(value: &str) -> SourceSizeList {
1732 let mut input = ParserInput::new(value);
1733 let mut parser = Parser::new(&mut input);
1734 let url_data = Url::parse("about:blank").unwrap().into();
1735 let context =
1738 parser_context_for_anonymous_content(CssRuleType::Style, ParsingMode::empty(), &url_data);
1739 SourceSizeList::parse(&context, &mut parser)
1740}
1741
1742impl HTMLImageElementMethods<crate::DomTypeHolder> for HTMLImageElement {
1743 fn Image(
1745 window: &Window,
1746 proto: Option<HandleObject>,
1747 can_gc: CanGc,
1748 width: Option<u32>,
1749 height: Option<u32>,
1750 ) -> Fallible<DomRoot<HTMLImageElement>> {
1751 let document = window.Document();
1753
1754 let element = Element::create(
1757 QualName::new(None, ns!(html), local_name!("img")),
1758 None,
1759 &document,
1760 ElementCreator::ScriptCreated,
1761 CustomElementCreationMode::Synchronous,
1762 proto,
1763 can_gc,
1764 );
1765
1766 let image = DomRoot::downcast::<HTMLImageElement>(element).unwrap();
1767
1768 if let Some(w) = width {
1770 image.SetWidth(w);
1771 }
1772
1773 if let Some(h) = height {
1776 image.SetHeight(h);
1777 }
1778
1779 Ok(image)
1781 }
1782
1783 make_getter!(Alt, "alt");
1785 make_setter!(SetAlt, "alt");
1787
1788 make_url_getter!(Src, "src");
1790
1791 make_url_setter!(SetSrc, "src");
1793
1794 make_url_getter!(Srcset, "srcset");
1796 make_url_setter!(SetSrcset, "srcset");
1798
1799 make_getter!(Sizes, "sizes");
1801
1802 make_setter!(SetSizes, "sizes");
1804
1805 fn GetCrossOrigin(&self) -> Option<DOMString> {
1807 reflect_cross_origin_attribute(self.upcast::<Element>())
1808 }
1809
1810 fn SetCrossOrigin(&self, value: Option<DOMString>, can_gc: CanGc) {
1812 set_cross_origin_attribute(self.upcast::<Element>(), value, can_gc);
1813 }
1814
1815 make_getter!(UseMap, "usemap");
1817 make_setter!(SetUseMap, "usemap");
1819
1820 make_bool_getter!(IsMap, "ismap");
1822 make_bool_setter!(SetIsMap, "ismap");
1824
1825 fn Width(&self) -> u32 {
1827 let node = self.upcast::<Node>();
1828 node.content_box()
1829 .map(|rect| rect.size.width.to_px() as u32)
1830 .unwrap_or_else(|| self.NaturalWidth())
1831 }
1832
1833 make_dimension_uint_setter!(SetWidth, "width");
1835
1836 fn Height(&self) -> u32 {
1838 let node = self.upcast::<Node>();
1839 node.content_box()
1840 .map(|rect| rect.size.height.to_px() as u32)
1841 .unwrap_or_else(|| self.NaturalHeight())
1842 }
1843
1844 make_dimension_uint_setter!(SetHeight, "height");
1846
1847 fn NaturalWidth(&self) -> u32 {
1849 let request = self.current_request.borrow();
1850 if matches!(request.state, State::Broken) {
1851 return 0;
1852 }
1853
1854 let pixel_density = request.current_pixel_density.unwrap_or(1f64);
1855 match request.metadata {
1856 Some(ref metadata) => (metadata.width as f64 / pixel_density) as u32,
1857 None => 0,
1858 }
1859 }
1860
1861 fn NaturalHeight(&self) -> u32 {
1863 let request = self.current_request.borrow();
1864 if matches!(request.state, State::Broken) {
1865 return 0;
1866 }
1867
1868 let pixel_density = request.current_pixel_density.unwrap_or(1f64);
1869 match request.metadata {
1870 Some(ref metadata) => (metadata.height as f64 / pixel_density) as u32,
1871 None => 0,
1872 }
1873 }
1874
1875 fn Complete(&self) -> bool {
1877 let element = self.upcast::<Element>();
1878
1879 let has_srcset_attribute = element.has_attribute(&local_name!("srcset"));
1882 if !element.has_attribute(&local_name!("src")) && !has_srcset_attribute {
1883 return true;
1884 }
1885
1886 let src = element.get_string_attribute(&local_name!("src"));
1888 if !has_srcset_attribute && src.is_empty() {
1889 return true;
1890 }
1891
1892 if matches!(self.image_request.get(), ImageRequestPhase::Current) &&
1896 matches!(
1897 self.current_request.borrow().state,
1898 State::CompletelyAvailable | State::Broken
1899 )
1900 {
1901 return true;
1902 }
1903
1904 false
1906 }
1907
1908 fn CurrentSrc(&self) -> USVString {
1910 let current_request = self.current_request.borrow();
1911 let url = ¤t_request.parsed_url;
1912 match *url {
1913 Some(ref url) => USVString(url.clone().into_string()),
1914 None => {
1915 let unparsed_url = ¤t_request.source_url;
1916 match *unparsed_url {
1917 Some(ref url) => url.clone(),
1918 None => USVString("".to_owned()),
1919 }
1920 },
1921 }
1922 }
1923
1924 fn ReferrerPolicy(&self) -> DOMString {
1926 reflect_referrer_policy_attribute(self.upcast::<Element>())
1927 }
1928
1929 make_setter!(SetReferrerPolicy, "referrerpolicy");
1931
1932 fn Decode(&self, can_gc: CanGc) -> Rc<Promise> {
1934 let promise = Promise::new(&self.global(), can_gc);
1936
1937 let task = ImageElementMicrotask::Decode {
1939 elem: DomRoot::from_ref(self),
1940 promise: promise.clone(),
1941 };
1942
1943 ScriptThread::await_stable_state(Microtask::ImageElement(task));
1944
1945 promise
1947 }
1948
1949 make_getter!(Name, "name");
1951
1952 make_atomic_setter!(SetName, "name");
1954
1955 make_getter!(Align, "align");
1957
1958 make_setter!(SetAlign, "align");
1960
1961 make_uint_getter!(Hspace, "hspace");
1963
1964 make_uint_setter!(SetHspace, "hspace");
1966
1967 make_uint_getter!(Vspace, "vspace");
1969
1970 make_uint_setter!(SetVspace, "vspace");
1972
1973 make_getter!(LongDesc, "longdesc");
1975
1976 make_setter!(SetLongDesc, "longdesc");
1978
1979 make_getter!(Border, "border");
1981
1982 make_setter!(SetBorder, "border");
1984}
1985
1986impl VirtualMethods for HTMLImageElement {
1987 fn super_type(&self) -> Option<&dyn VirtualMethods> {
1988 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
1989 }
1990
1991 fn adopting_steps(&self, old_doc: &Document, can_gc: CanGc) {
1992 self.super_type().unwrap().adopting_steps(old_doc, can_gc);
1993 self.update_the_image_data(can_gc);
1994 }
1995
1996 fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
1997 self.super_type()
1998 .unwrap()
1999 .attribute_mutated(attr, mutation, can_gc);
2000 match attr.local_name() {
2001 &local_name!("src") |
2002 &local_name!("srcset") |
2003 &local_name!("width") |
2004 &local_name!("sizes") => {
2005 self.update_the_image_data(can_gc);
2009 },
2010 &local_name!("crossorigin") => {
2011 let cross_origin_state_changed = match mutation {
2014 AttributeMutation::Removed | AttributeMutation::Set(None, _) => true,
2015 AttributeMutation::Set(Some(old_value), _) => {
2016 let new_cors_setting =
2017 CorsSettings::from_enumerated_attribute(&attr.value());
2018 let old_cors_setting = CorsSettings::from_enumerated_attribute(old_value);
2019
2020 new_cors_setting != old_cors_setting
2021 },
2022 };
2023
2024 if cross_origin_state_changed {
2025 self.update_the_image_data(can_gc);
2026 }
2027 },
2028 &local_name!("referrerpolicy") => {
2029 let referrer_policy_state_changed = match mutation {
2032 AttributeMutation::Removed | AttributeMutation::Set(None, _) => {
2033 ReferrerPolicy::from(&**attr.value()) != ReferrerPolicy::EmptyString
2034 },
2035 AttributeMutation::Set(Some(old_value), _) => {
2036 ReferrerPolicy::from(&**attr.value()) != ReferrerPolicy::from(&**old_value)
2037 },
2038 };
2039
2040 if referrer_policy_state_changed {
2041 self.update_the_image_data(can_gc);
2042 }
2043 },
2044 _ => {},
2045 }
2046 }
2047
2048 fn attribute_affects_presentational_hints(&self, attr: &Attr) -> bool {
2049 match attr.local_name() {
2050 &local_name!("width") | &local_name!("height") => true,
2051 _ => self
2052 .super_type()
2053 .unwrap()
2054 .attribute_affects_presentational_hints(attr),
2055 }
2056 }
2057
2058 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
2059 match name {
2060 &local_name!("width") | &local_name!("height") => {
2061 AttrValue::from_dimension(value.into())
2062 },
2063 &local_name!("hspace") | &local_name!("vspace") => AttrValue::from_u32(value.into(), 0),
2064 _ => self
2065 .super_type()
2066 .unwrap()
2067 .parse_plain_attribute(name, value),
2068 }
2069 }
2070
2071 fn handle_event(&self, event: &Event, can_gc: CanGc) {
2072 if event.type_() != atom!("click") {
2073 return;
2074 }
2075
2076 let area_elements = self.areas();
2077 let elements = match area_elements {
2078 Some(x) => x,
2079 None => return,
2080 };
2081
2082 let mouse_event = match event.downcast::<MouseEvent>() {
2084 Some(x) => x,
2085 None => return,
2086 };
2087
2088 let point = Point2D::new(
2089 mouse_event.ClientX().to_f32().unwrap(),
2090 mouse_event.ClientY().to_f32().unwrap(),
2091 );
2092 let bcr = self.upcast::<Element>().GetBoundingClientRect(can_gc);
2093 let bcr_p = Point2D::new(bcr.X() as f32, bcr.Y() as f32);
2094
2095 for element in elements {
2097 let shape = element.get_shape_from_coords();
2098 let shp = match shape {
2099 Some(x) => x.absolute_coords(bcr_p),
2100 None => return,
2101 };
2102 if shp.hit_test(&point) {
2103 element.activation_behavior(event, self.upcast(), can_gc);
2104 return;
2105 }
2106 }
2107 }
2108
2109 fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) {
2111 if let Some(s) = self.super_type() {
2112 s.bind_to_tree(context, can_gc);
2113 }
2114 let document = self.owner_document();
2115 if context.tree_connected {
2116 document.register_responsive_image(self);
2117 }
2118
2119 let parent = self.upcast::<Node>().GetParentNode().unwrap();
2120
2121 if parent.is::<HTMLPictureElement>() && std::ptr::eq(&*parent, context.parent) {
2124 self.update_the_image_data(can_gc);
2125 }
2126 }
2127
2128 fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) {
2130 self.super_type().unwrap().unbind_from_tree(context, can_gc);
2131 let document = self.owner_document();
2132 document.unregister_responsive_image(self);
2133
2134 if context.parent.is::<HTMLPictureElement>() && !self.upcast::<Node>().has_parent() {
2137 self.update_the_image_data(can_gc);
2138 }
2139 }
2140}
2141
2142impl FormControl for HTMLImageElement {
2143 fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
2144 self.form_owner.get()
2145 }
2146
2147 fn set_form_owner(&self, form: Option<&HTMLFormElement>) {
2148 self.form_owner.set(form);
2149 }
2150
2151 fn to_element(&self) -> &Element {
2152 self.upcast::<Element>()
2153 }
2154
2155 fn is_listed(&self) -> bool {
2156 false
2157 }
2158}
2159
2160pub(crate) fn collect_sequence_characters(
2163 s: &str,
2164 mut predicate: impl FnMut(&char) -> bool,
2165) -> (&str, &str) {
2166 let i = s.find(|ch| !predicate(&ch)).unwrap_or(s.len());
2167 (&s[0..i], &s[i..])
2168}
2169
2170fn is_valid_non_negative_integer_string(s: &str) -> bool {
2173 s.chars().all(|c| c.is_ascii_digit())
2174}
2175
2176fn is_valid_floating_point_number_string(s: &str) -> bool {
2179 static RE: LazyLock<Regex> =
2180 LazyLock::new(|| Regex::new(r"^-?(?:\d+\.\d+|\d+|\.\d+)(?:(e|E)(\+|\-)?\d+)?$").unwrap());
2181
2182 RE.is_match(s)
2183}
2184
2185pub fn parse_a_srcset_attribute(input: &str) -> Vec<ImageSource> {
2188 let mut current_index = 0;
2191
2192 let mut candidates = vec![];
2194 while current_index < input.len() {
2195 let remaining_string = &input[current_index..];
2196
2197 let (collected_characters, string_after_whitespace) =
2204 collect_sequence_characters(remaining_string, |character| {
2205 *character == ',' || character.is_ascii_whitespace()
2206 });
2207
2208 current_index += collected_characters.len();
2211
2212 if string_after_whitespace.is_empty() {
2214 return candidates;
2215 }
2216
2217 let (url, _) =
2220 collect_sequence_characters(string_after_whitespace, |c| !char::is_ascii_whitespace(c));
2221
2222 current_index += url.len();
2225
2226 let mut descriptors = Vec::new();
2228
2229 if url.ends_with(',') {
2233 let image_source = ImageSource {
2234 url: url.trim_end_matches(',').into(),
2235 descriptor: Descriptor {
2236 width: None,
2237 density: None,
2238 },
2239 };
2240 candidates.push(image_source);
2241 continue;
2242 }
2243
2244 let descriptors_string = &input[current_index..];
2247 let (spaces, descriptors_string) =
2248 collect_sequence_characters(descriptors_string, |character| {
2249 character.is_ascii_whitespace()
2250 });
2251 current_index += spaces.len();
2252
2253 let mut current_descriptor = String::new();
2255
2256 let mut state = ParseState::InDescriptor;
2258
2259 let mut characters = descriptors_string.chars();
2263 let mut character = characters.next();
2264 if let Some(character) = character {
2265 current_index += character.len_utf8();
2266 }
2267
2268 loop {
2269 match (state, character) {
2270 (ParseState::InDescriptor, Some(character)) if character.is_ascii_whitespace() => {
2271 if !current_descriptor.is_empty() {
2275 descriptors.push(current_descriptor);
2276 current_descriptor = String::new();
2277 state = ParseState::AfterDescriptor;
2278 }
2279 },
2280 (ParseState::InDescriptor, Some(',')) => {
2281 if !current_descriptor.is_empty() {
2285 descriptors.push(current_descriptor);
2286 }
2287 break;
2288 },
2289 (ParseState::InDescriptor, Some('(')) => {
2290 current_descriptor.push('(');
2292 state = ParseState::InParens;
2293 },
2294 (ParseState::InDescriptor, Some(character)) => {
2295 current_descriptor.push(character);
2297 },
2298 (ParseState::InDescriptor, None) => {
2299 if !current_descriptor.is_empty() {
2302 descriptors.push(current_descriptor);
2303 }
2304 break;
2305 },
2306 (ParseState::InParens, Some(')')) => {
2307 current_descriptor.push(')');
2309 state = ParseState::InDescriptor;
2310 },
2311 (ParseState::InParens, Some(character)) => {
2312 current_descriptor.push(character);
2314 },
2315 (ParseState::InParens, None) => {
2316 descriptors.push(current_descriptor);
2319 break;
2320 },
2321 (ParseState::AfterDescriptor, Some(character))
2322 if character.is_ascii_whitespace() =>
2323 {
2324 },
2326 (ParseState::AfterDescriptor, Some(_)) => {
2327 state = ParseState::InDescriptor;
2330 continue;
2331 },
2332 (ParseState::AfterDescriptor, None) => {
2333 break;
2335 },
2336 }
2337
2338 character = characters.next();
2339 if let Some(character) = character {
2340 current_index += character.len_utf8();
2341 }
2342 }
2343
2344 let mut error = false;
2346 let mut width: Option<u32> = None;
2348 let mut density: Option<f64> = None;
2350 let mut future_compat_h: Option<u32> = None;
2352
2353 for descriptor in descriptors.into_iter() {
2356 let Some(last_character) = descriptor.chars().last() else {
2357 break;
2358 };
2359
2360 let first_part_of_string = &descriptor[0..descriptor.len() - last_character.len_utf8()];
2361 match last_character {
2362 'w' if is_valid_non_negative_integer_string(first_part_of_string) &&
2369 density.is_none() &&
2370 width.is_none() =>
2371 {
2372 match parse_unsigned_integer(first_part_of_string.chars()) {
2373 Ok(number) if number > 0 => {
2374 width = Some(number);
2375 continue;
2376 },
2377 _ => error = true,
2378 }
2379 },
2380
2381 'x' if is_valid_floating_point_number_string(first_part_of_string) &&
2395 width.is_none() &&
2396 density.is_none() &&
2397 future_compat_h.is_none() =>
2398 {
2399 match first_part_of_string.parse::<f64>() {
2400 Ok(number) if number.is_finite() && number >= 0. => {
2401 density = Some(number);
2402 continue;
2403 },
2404 _ => error = true,
2405 }
2406 },
2407
2408 'h' if is_valid_non_negative_integer_string(first_part_of_string) &&
2417 future_compat_h.is_none() &&
2418 density.is_none() =>
2419 {
2420 match parse_unsigned_integer(first_part_of_string.chars()) {
2421 Ok(number) if number > 0 => {
2422 future_compat_h = Some(number);
2423 continue;
2424 },
2425 _ => error = true,
2426 }
2427 },
2428
2429 _ => error = true,
2432 }
2433
2434 if error {
2435 break;
2436 }
2437 }
2438
2439 if future_compat_h.is_some() && width.is_none() {
2441 error = true;
2442 }
2443
2444 if !error {
2448 let image_source = ImageSource {
2449 url: url.into(),
2450 descriptor: Descriptor { width, density },
2451 };
2452 candidates.push(image_source);
2453 }
2454
2455 }
2457 candidates
2458}
2459
2460#[derive(Clone)]
2461enum ChangeType {
2462 Environment {
2463 selected_source: USVString,
2464 selected_pixel_density: f64,
2465 },
2466 Element,
2467}
2468
2469fn is_supported_image_mime_type(input: &str) -> bool {
2471 let mime_type = input.trim();
2473
2474 let mime_type_essence = match mime_type.find(';') {
2476 Some(semi) => &mime_type[..semi],
2477 _ => mime_type,
2478 };
2479
2480 if mime_type_essence.is_empty() {
2485 return true;
2486 }
2487
2488 SUPPORTED_IMAGE_MIME_TYPES.contains(&mime_type_essence)
2489}