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::realm::AutoRealm;
18use js::rust::HandleObject;
19use mime::{self, Mime};
20use net_traits::http_status::HttpStatus;
21use net_traits::image_cache::{
22 Image, ImageCache, ImageCacheResult, ImageLoadListener, ImageOrMetadataAvailable,
23 ImageResponse, PendingImageId,
24};
25use net_traits::request::{CorsSettings, Destination, Initiator, RequestId};
26use net_traits::{
27 FetchMetadata, FetchResponseMsg, NetworkError, ReferrerPolicy, ResourceFetchTiming,
28};
29use num_traits::ToPrimitive;
30use pixels::{CorsStatus, ImageMetadata, Snapshot};
31use regex::Regex;
32use rustc_hash::FxHashSet;
33use script_bindings::cell::{DomRefCell, RefMut};
34use servo_url::ServoUrl;
35use servo_url::origin::MutableOrigin;
36use style::attr::{AttrValue, LengthOrPercentageOrAuto, parse_unsigned_integer};
37use style::stylesheets::CssRuleType;
38use style::values::specified::source_size_list::SourceSizeList;
39use style_traits::ParsingMode;
40use url::Url;
41
42use crate::css::parser_context_for_anonymous_content;
43use crate::document_loader::{LoadBlocker, LoadType};
44use crate::dom::activation::Activatable;
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::attributes::storage::AttrRef;
60use crate::dom::element::{
61 AttributeMutation, CustomElementCreationMode, Element, ElementCreator,
62 cors_setting_for_element, referrer_policy_for_element, reflect_cross_origin_attribute,
63 reflect_referrer_policy_attribute, set_cross_origin_attribute,
64};
65use crate::dom::event::Event;
66use crate::dom::eventtarget::EventTarget;
67use crate::dom::globalscope::GlobalScope;
68use crate::dom::html::htmlareaelement::HTMLAreaElement;
69use crate::dom::html::htmlelement::HTMLElement;
70use crate::dom::html::htmlformelement::{FormControl, HTMLFormElement};
71use crate::dom::html::htmlmapelement::HTMLMapElement;
72use crate::dom::html::htmlpictureelement::HTMLPictureElement;
73use crate::dom::html::htmlsourceelement::HTMLSourceElement;
74use crate::dom::iterators::ShadowIncluding;
75use crate::dom::medialist::MediaList;
76use crate::dom::mouseevent::MouseEvent;
77use crate::dom::node::virtualmethods::VirtualMethods;
78use crate::dom::node::{BindContext, MoveContext, Node, NodeDamage, NodeTraits, UnbindContext};
79use crate::dom::performance::performanceresourcetiming::InitiatorType;
80use crate::dom::promise::Promise;
81use crate::dom::window::Window;
82use crate::fetch::{RequestWithGlobalScope, create_a_potential_cors_request};
83use crate::microtask::{Microtask, MicrotaskRunnable};
84use crate::network_listener::{self, FetchResponseListener, ResourceTimingListener};
85use crate::realms::enter_auto_realm;
86use crate::script_thread::ScriptThread;
87
88const SUPPORTED_IMAGE_MIME_TYPES: &[&str] = &[
92 "image/bmp",
93 "image/gif",
94 "image/jpeg",
95 "image/jpg",
96 "image/pjpeg",
97 "image/png",
98 "image/apng",
99 "image/x-png",
100 "image/svg+xml",
101 "image/vnd.microsoft.icon",
102 "image/x-icon",
103 "image/webp",
104];
105
106#[derive(Clone, Copy, Debug)]
107enum ParseState {
108 InDescriptor,
109 InParens,
110 AfterDescriptor,
111}
112
113#[derive(MallocSizeOf)]
115pub(crate) struct SourceSet {
116 image_sources: Vec<ImageSource>,
117 source_size: SourceSizeList,
118}
119
120impl SourceSet {
121 fn new() -> SourceSet {
122 SourceSet {
123 image_sources: Vec::new(),
124 source_size: SourceSizeList::empty(),
125 }
126 }
127}
128
129#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
130pub struct ImageSource {
131 pub url: String,
132 pub descriptor: Descriptor,
133}
134
135#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
136pub struct Descriptor {
137 pub width: Option<u32>,
138 pub density: Option<f64>,
139}
140
141#[derive(Clone, Copy, JSTraceable, MallocSizeOf)]
143enum State {
144 Unavailable,
145 PartiallyAvailable,
146 CompletelyAvailable,
147 Broken,
148}
149
150#[derive(Clone, Copy, JSTraceable, MallocSizeOf)]
151enum ImageRequestPhase {
152 Pending,
153 Current,
154}
155
156#[derive(JSTraceable, MallocSizeOf)]
158#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
159struct ImageRequest {
160 state: State,
161 #[no_trace]
162 parsed_url: Option<ServoUrl>,
163 source_url: Option<USVString>,
164 blocker: DomRefCell<Option<LoadBlocker>>,
165 #[no_trace]
166 image: Option<Image>,
167 #[no_trace]
168 metadata: Option<ImageMetadata>,
169 #[no_trace]
170 final_url: Option<ServoUrl>,
171 current_pixel_density: Option<f64>,
172}
173
174#[dom_struct]
175pub(crate) struct HTMLImageElement {
176 htmlelement: HTMLElement,
177 image_request: Cell<ImageRequestPhase>,
178 current_request: DomRefCell<ImageRequest>,
179 pending_request: DomRefCell<ImageRequest>,
180 form_owner: MutNullableDom<HTMLFormElement>,
181 generation: Cell<u32>,
182 source_set: DomRefCell<SourceSet>,
183 dimension_attribute_source: MutNullableDom<Element>,
186 last_selected_source: DomRefCell<Option<USVString>>,
188 #[conditional_malloc_size_of]
189 image_decode_promises: DomRefCell<Vec<Rc<Promise>>>,
190 line_number: u64,
192}
193
194impl HTMLImageElement {
195 pub(crate) fn is_usable(&self) -> Fallible<bool> {
197 if let Some(image) = &self.current_request.borrow().image {
199 let intrinsic_size = image.metadata();
200 if intrinsic_size.width == 0 || intrinsic_size.height == 0 {
201 return Ok(false);
202 }
203 }
204
205 match self.current_request.borrow().state {
206 State::Broken => Err(Error::InvalidState(None)),
208 State::CompletelyAvailable => Ok(true),
209 State::PartiallyAvailable | State::Unavailable => Ok(false),
211 }
212 }
213
214 pub(crate) fn image_data(&self) -> Option<Image> {
215 self.current_request.borrow().image.clone()
216 }
217
218 pub(crate) fn get_raster_image_data(&self) -> Option<Snapshot> {
220 let Some(raster_image) = self.image_data()?.as_raster_image() else {
221 warn!("Vector image is not supported as raster image source");
222 return None;
223 };
224 Some(raster_image.as_snapshot())
225 }
226}
227
228struct ImageContext {
230 image_cache: Arc<dyn ImageCache>,
232 status: Result<(), NetworkError>,
234 id: PendingImageId,
236 aborted: bool,
238 doc: Trusted<Document>,
240 url: ServoUrl,
241 element: Trusted<HTMLImageElement>,
242}
243
244impl FetchResponseListener for ImageContext {
245 fn should_invoke(&self) -> bool {
246 !self.aborted
247 }
248
249 fn process_request_body(&mut self, _: RequestId) {}
250
251 fn process_response(
252 &mut self,
253 _: &mut js::context::JSContext,
254 request_id: RequestId,
255 metadata: Result<FetchMetadata, NetworkError>,
256 ) {
257 debug!("got {:?} for {:?}", metadata.as_ref().map(|_| ()), self.url);
258 self.image_cache.notify_pending_response(
259 self.id,
260 FetchResponseMsg::ProcessResponse(request_id, metadata.clone()),
261 );
262
263 let metadata = metadata.ok().map(|meta| match meta {
264 FetchMetadata::Unfiltered(m) => m,
265 FetchMetadata::Filtered { unsafe_, .. } => unsafe_,
266 });
267
268 if let Some(metadata) = metadata.as_ref() &&
270 let Some(ref content_type) = metadata.content_type
271 {
272 let mime: Mime = content_type.clone().into_inner().into();
273 if mime.type_() == mime::MULTIPART && mime.subtype().as_str() == "x-mixed-replace" {
274 self.aborted = true;
275 }
276 }
277
278 let status = metadata
279 .as_ref()
280 .map(|m| m.status.clone())
281 .unwrap_or_else(HttpStatus::new_error);
282
283 self.status = {
284 if status.is_error() {
285 Err(NetworkError::ResourceLoadError(
286 "No http status code received".to_owned(),
287 ))
288 } else if status.is_success() {
289 Ok(())
290 } else {
291 Err(NetworkError::ResourceLoadError(format!(
292 "HTTP error code {}",
293 status.code()
294 )))
295 }
296 };
297 }
298
299 fn process_response_chunk(
300 &mut self,
301 _: &mut js::context::JSContext,
302 request_id: RequestId,
303 payload: Vec<u8>,
304 ) {
305 if self.status.is_ok() {
306 self.image_cache.notify_pending_response(
307 self.id,
308 FetchResponseMsg::ProcessResponseChunk(request_id, payload.into()),
309 );
310 }
311 }
312
313 fn process_response_eof(
314 self,
315 cx: &mut js::context::JSContext,
316 request_id: RequestId,
317 response: Result<(), NetworkError>,
318 timing: ResourceFetchTiming,
319 ) {
320 self.image_cache.notify_pending_response(
321 self.id,
322 FetchResponseMsg::ProcessResponseEOF(request_id, response.clone(), timing.clone()),
323 );
324 network_listener::submit_timing(cx, &self, &response, &timing);
325 }
326
327 fn process_csp_violations(
328 &mut self,
329 cx: &mut js::context::JSContext,
330 _request_id: RequestId,
331 violations: Vec<Violation>,
332 ) {
333 let global = &self.resource_timing_global();
334 let elem = self.element.root();
335 let source_position = elem
336 .upcast::<Element>()
337 .compute_source_position(elem.line_number as u32);
338 global.report_csp_violations(cx, violations, None, Some(source_position));
339 }
340}
341
342impl ResourceTimingListener for ImageContext {
343 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
344 (
345 InitiatorType::LocalName("img".to_string()),
346 self.url.clone(),
347 )
348 }
349
350 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
351 self.doc.root().global()
352 }
353}
354
355#[expect(non_snake_case)]
356impl HTMLImageElement {
357 fn fetch_image(&self, img_url: &ServoUrl, cx: &mut js::context::JSContext) {
359 let window = self.owner_window();
360
361 let cache_result = window.image_cache().get_cached_image_status(
362 img_url.clone(),
363 window.origin().immutable().clone(),
364 cors_setting_for_element(self.upcast()),
365 );
366
367 match cache_result {
368 ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable {
369 image,
370 url,
371 }) => self.process_image_response(ImageResponse::Loaded(image, url), cx),
372 ImageCacheResult::Available(ImageOrMetadataAvailable::MetadataAvailable(
373 metadata,
374 id,
375 )) => {
376 self.process_image_response(ImageResponse::MetadataLoaded(metadata), cx);
377 self.register_image_cache_callback(id, ChangeType::Element);
378 },
379 ImageCacheResult::Pending(id) => {
380 self.register_image_cache_callback(id, ChangeType::Element);
381 },
382 ImageCacheResult::ReadyForRequest(id) => {
383 self.fetch_request(img_url, id);
384 self.register_image_cache_callback(id, ChangeType::Element);
385 },
386 ImageCacheResult::FailedToLoadOrDecode => {
387 self.process_image_response(ImageResponse::FailedToLoadOrDecode, cx)
388 },
389 };
390 }
391
392 fn register_image_cache_callback(&self, id: PendingImageId, change_type: ChangeType) {
393 let trusted_node = Trusted::new(self);
394 let generation = self.generation_id();
395 let window = self.owner_window();
396 let callback = window.register_image_cache_listener(id, move |response, _| {
397 let trusted_node = trusted_node.clone();
398 let window = trusted_node.root().owner_window();
399 let callback_type = change_type.clone();
400
401 window
402 .as_global_scope()
403 .task_manager()
404 .networking_task_source()
405 .queue(task!(process_image_response: move |cx| {
406 let element = trusted_node.root();
407
408 if generation != element.generation_id() {
410 return;
411 }
412
413 match callback_type {
414 ChangeType::Element => {
415 element.process_image_response(response.response, cx);
416 }
417 ChangeType::Environment { selected_source, selected_pixel_density } => {
418 element.process_image_response_for_environment_change(
419 response.response, selected_source, generation, selected_pixel_density, cx
420 );
421 }
422 }
423 }));
424 });
425
426 window.image_cache().add_listener(ImageLoadListener::new(
427 callback,
428 window.pipeline_id(),
429 id,
430 ));
431 }
432
433 fn fetch_request(&self, img_url: &ServoUrl, id: PendingImageId) {
434 let document = self.owner_document();
435 let window = self.owner_window();
436
437 let context = ImageContext {
438 image_cache: window.image_cache(),
439 status: Ok(()),
440 id,
441 aborted: false,
442 doc: Trusted::new(&document),
443 element: Trusted::new(self),
444 url: img_url.clone(),
445 };
446
447 let global = document.global();
450 let mut request = create_a_potential_cors_request(
451 Some(window.webview_id()),
452 img_url.clone(),
453 Destination::Image,
454 cors_setting_for_element(self.upcast()),
455 None,
456 global.get_referrer(),
457 )
458 .with_global_scope(&global)
459 .referrer_policy(referrer_policy_for_element(self.upcast()));
460
461 if self.uses_srcset_or_picture() {
462 request = request.initiator(Initiator::ImageSet);
463 }
464
465 document.fetch_background(request, context);
468 }
469
470 fn handle_loaded_image(&self, image: Image, url: ServoUrl, cx: &mut js::context::JSContext) {
472 self.current_request.borrow_mut().metadata = Some(image.metadata());
473 self.current_request.borrow_mut().final_url = Some(url);
474 self.current_request.borrow_mut().image = Some(image);
475 self.current_request.borrow_mut().state = State::CompletelyAvailable;
476 LoadBlocker::terminate(&self.current_request.borrow().blocker, cx);
477 self.upcast::<Node>().dirty(NodeDamage::Other);
479 self.resolve_image_decode_promises();
480 }
481
482 fn process_image_response(&self, image: ImageResponse, cx: &mut js::context::JSContext) {
484 let (trigger_image_load, trigger_image_error) = match (image, self.image_request.get()) {
490 (ImageResponse::Loaded(image, url), ImageRequestPhase::Current) => {
491 self.handle_loaded_image(image, url, cx);
492 (true, false)
493 },
494 (ImageResponse::Loaded(image, url), ImageRequestPhase::Pending) => {
495 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
496 self.image_request.set(ImageRequestPhase::Current);
497 self.handle_loaded_image(image, url, cx);
498 (true, false)
499 },
500 (ImageResponse::MetadataLoaded(meta), ImageRequestPhase::Current) => {
501 self.current_request.borrow_mut().state = State::PartiallyAvailable;
506 self.current_request.borrow_mut().metadata = Some(meta);
507 (false, false)
508 },
509 (ImageResponse::MetadataLoaded(_), ImageRequestPhase::Pending) => {
510 self.pending_request.borrow_mut().state = State::PartiallyAvailable;
514 (false, false)
515 },
516 (ImageResponse::FailedToLoadOrDecode, ImageRequestPhase::Current) => {
517 self.abort_request(State::Broken, ImageRequestPhase::Current, cx);
523
524 self.load_broken_image_icon();
525
526 (false, true)
530 },
531 (ImageResponse::FailedToLoadOrDecode, ImageRequestPhase::Pending) => {
532 self.abort_request(State::Broken, ImageRequestPhase::Current, cx);
538 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
539
540 mem::swap(
542 &mut *self.current_request.borrow_mut(),
543 &mut *self.pending_request.borrow_mut(),
544 );
545 self.image_request.set(ImageRequestPhase::Current);
546
547 self.current_request.borrow_mut().state = State::Broken;
549
550 self.load_broken_image_icon();
551
552 (false, true)
554 },
555 };
556
557 if trigger_image_load {
559 self.upcast::<EventTarget>().fire_event(cx, atom!("load"));
561 self.upcast::<EventTarget>()
562 .fire_event(cx, atom!("loadend"));
563 }
564
565 if trigger_image_error {
567 self.upcast::<EventTarget>().fire_event(cx, atom!("error"));
568 self.upcast::<EventTarget>()
569 .fire_event(cx, atom!("loadend"));
570 }
571 }
572
573 fn process_image_response_for_environment_change(
576 &self,
577 image: ImageResponse,
578 selected_source: USVString,
579 generation: u32,
580 selected_pixel_density: f64,
581 cx: &mut js::context::JSContext,
582 ) {
583 match image {
584 ImageResponse::Loaded(image, url) => {
585 self.pending_request.borrow_mut().metadata = Some(image.metadata());
586 self.pending_request.borrow_mut().final_url = Some(url);
587 self.pending_request.borrow_mut().image = Some(image);
588 self.finish_reacting_to_environment_change(
589 selected_source,
590 generation,
591 selected_pixel_density,
592 );
593 },
594 ImageResponse::FailedToLoadOrDecode => {
595 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
603 },
604 ImageResponse::MetadataLoaded(meta) => {
605 self.pending_request.borrow_mut().metadata = Some(meta);
606 },
607 };
608 }
609
610 fn abort_request(
612 &self,
613 state: State,
614 request: ImageRequestPhase,
615 cx: &mut js::context::JSContext,
616 ) {
617 let mut request = match request {
618 ImageRequestPhase::Current => self.current_request.borrow_mut(),
619 ImageRequestPhase::Pending => self.pending_request.borrow_mut(),
620 };
621 LoadBlocker::terminate(&request.blocker, cx);
622 request.state = state;
623 request.image = None;
624 request.metadata = None;
625 request.current_pixel_density = None;
626
627 if matches!(state, State::Broken) {
628 self.reject_image_decode_promises();
629 } else if matches!(state, State::CompletelyAvailable) {
630 self.resolve_image_decode_promises();
631 }
632 }
633
634 fn create_source_set(&self) -> SourceSet {
636 let element = self.upcast::<Element>();
637
638 let mut source_set = SourceSet::new();
640
641 if let Some(srcset) = element.get_attribute_string_value(&local_name!("srcset")) {
644 source_set.image_sources = parse_a_srcset_attribute(&srcset);
645 }
646
647 if let Some(sizes) = element.get_attribute_string_value(&local_name!("sizes")) {
649 source_set.source_size = parse_a_sizes_attribute(&sizes);
650 }
651
652 let src = element.get_string_attribute(&local_name!("src"));
656 let no_density_source_of_1 = source_set
657 .image_sources
658 .iter()
659 .all(|source| source.descriptor.density != Some(1.));
660 let no_width_descriptor = source_set
661 .image_sources
662 .iter()
663 .all(|source| source.descriptor.width.is_none());
664 if !src.is_empty() && no_density_source_of_1 && no_width_descriptor {
665 source_set.image_sources.push(ImageSource {
666 url: String::from(src),
667 descriptor: Descriptor {
668 width: None,
669 density: None,
670 },
671 })
672 }
673
674 self.normalise_source_densities(&mut source_set);
676
677 source_set
679 }
680
681 fn update_source_set(&self) {
683 *self.source_set.borrow_mut() = SourceSet::new();
685
686 let elem = self.upcast::<Element>();
691 let parent = elem.upcast::<Node>().GetParentElement();
692 let elements = match parent.as_ref() {
693 Some(p) => {
694 if p.is::<HTMLPictureElement>() {
695 p.upcast::<Node>()
696 .children()
697 .filter_map(DomRoot::downcast::<Element>)
698 .map(|n| DomRoot::from_ref(&*n))
699 .collect()
700 } else {
701 vec![DomRoot::from_ref(elem)]
702 }
703 },
704 None => vec![DomRoot::from_ref(elem)],
705 };
706
707 for element in &elements {
709 if *element == DomRoot::from_ref(elem) {
711 *self.source_set.borrow_mut() = self.create_source_set();
714
715 return;
717 }
718 if !element.is::<HTMLSourceElement>() {
720 continue;
721 }
722
723 let mut source_set = SourceSet::new();
724
725 match element.get_attribute_string_value(&local_name!("srcset")) {
729 Some(srcset) => {
730 source_set.image_sources = parse_a_srcset_attribute(&srcset);
731 },
732 _ => continue,
733 }
734
735 if source_set.image_sources.is_empty() {
737 continue;
738 }
739
740 if let Some(media) = element.get_attribute_string_value(&local_name!("media")) &&
743 !MediaList::matches_environment(&element.owner_document(), &media)
744 {
745 continue;
746 }
747
748 if let Some(sizes) = element.get_attribute_string_value(&local_name!("sizes")) {
751 source_set.source_size = parse_a_sizes_attribute(&sizes);
752 }
753
754 if let Some(type_) = element.get_attribute_string_value(&local_name!("type")) &&
757 !is_supported_image_mime_type(&type_)
758 {
759 continue;
760 }
761
762 if element.has_attribute(&local_name!("width")) ||
765 element.has_attribute(&local_name!("height"))
766 {
767 self.dimension_attribute_source.set(Some(element));
768 } else {
769 self.dimension_attribute_source.set(Some(elem));
770 }
771
772 self.normalise_source_densities(&mut source_set);
774
775 *self.source_set.borrow_mut() = source_set;
777
778 return;
780 }
781 }
782
783 fn evaluate_source_size_list(&self, source_size_list: &SourceSizeList) -> Au {
784 let document = self.owner_document();
785 let quirks_mode = document.quirks_mode();
786 source_size_list.evaluate(document.window().layout().device(), quirks_mode)
787 }
788
789 fn normalise_source_densities(&self, source_set: &mut SourceSet) {
791 let source_size = self.evaluate_source_size_list(&source_set.source_size);
793
794 for image_source in &mut source_set.image_sources {
796 if image_source.descriptor.density.is_some() {
799 continue;
800 }
801
802 if let Some(width) = image_source.descriptor.width {
806 image_source.descriptor.density = Some(width as f64 / source_size.to_f64_px());
807 } else {
808 image_source.descriptor.density = Some(1_f64);
810 }
811 }
812 }
813
814 fn select_image_source(&self) -> Option<(USVString, f64)> {
816 self.update_source_set();
818
819 if self.source_set.borrow().image_sources.is_empty() {
822 return None;
823 }
824
825 self.select_image_source_from_source_set()
827 }
828
829 fn select_image_source_from_source_set(&self) -> Option<(USVString, f64)> {
831 let source_set = self.source_set.borrow();
836 let len = source_set.image_sources.len();
837
838 let mut repeat_indices = FxHashSet::default();
840 for outer_index in 0..len {
841 if repeat_indices.contains(&outer_index) {
842 continue;
843 }
844 let imgsource = &source_set.image_sources[outer_index];
845 let pixel_density = imgsource.descriptor.density.unwrap();
846 for inner_index in (outer_index + 1)..len {
847 let imgsource2 = &source_set.image_sources[inner_index];
848 if pixel_density == imgsource2.descriptor.density.unwrap() {
849 repeat_indices.insert(inner_index);
850 }
851 }
852 }
853
854 let mut max = (0f64, 0);
855 let img_sources = &mut vec![];
856 for (index, image_source) in source_set.image_sources.iter().enumerate() {
857 if repeat_indices.contains(&index) {
858 continue;
859 }
860 let den = image_source.descriptor.density.unwrap();
861 if max.0 < den {
862 max = (den, img_sources.len());
863 }
864 img_sources.push(image_source);
865 }
866
867 let mut best_candidate = max;
870 let device_pixel_ratio = self
871 .owner_document()
872 .window()
873 .viewport_details()
874 .hidpi_scale_factor
875 .get() as f64;
876 for (index, image_source) in img_sources.iter().enumerate() {
877 let current_den = image_source.descriptor.density.unwrap();
878 if current_den < best_candidate.0 && current_den >= device_pixel_ratio {
879 best_candidate = (current_den, index);
880 }
881 }
882 let selected_source = img_sources.remove(best_candidate.1).clone();
883
884 Some((
886 USVString(selected_source.url),
887 selected_source.descriptor.density.unwrap(),
888 ))
889 }
890
891 fn init_image_request(
892 &self,
893 request: &mut RefMut<'_, ImageRequest>,
894 url: &ServoUrl,
895 src: &USVString,
896 cx: &mut js::context::JSContext,
897 ) {
898 request.parsed_url = Some(url.clone());
899 request.source_url = Some(src.clone());
900 request.image = None;
901 request.metadata = None;
902 let document = self.owner_document();
903 LoadBlocker::terminate(&request.blocker, cx);
904 *request.blocker.borrow_mut() =
905 Some(LoadBlocker::new(&document, LoadType::Image(url.clone())));
906 }
907
908 fn prepare_image_request(
910 &self,
911 selected_source: &USVString,
912 selected_pixel_density: f64,
913 image_url: &ServoUrl,
914 cx: &mut js::context::JSContext,
915 ) {
916 match self.image_request.get() {
917 ImageRequestPhase::Pending => {
918 if self
921 .pending_request
922 .borrow()
923 .parsed_url
924 .as_ref()
925 .is_some_and(|parsed_url| *parsed_url == *image_url)
926 {
927 return;
928 }
929 },
930 ImageRequestPhase::Current => {
931 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
933
934 let mut current_request = self.current_request.borrow_mut();
937 let mut pending_request = self.pending_request.borrow_mut();
938
939 match (current_request.parsed_url.as_ref(), current_request.state) {
940 (Some(parsed_url), State::PartiallyAvailable) => {
941 if *parsed_url == *image_url {
947 return;
949 }
950
951 self.image_request.set(ImageRequestPhase::Pending);
955 self.init_image_request(
956 &mut pending_request,
957 image_url,
958 selected_source,
959 cx,
960 );
961 pending_request.current_pixel_density = Some(selected_pixel_density);
962 },
963 (_, State::Broken) | (_, State::Unavailable) => {
964 self.init_image_request(
968 &mut current_request,
969 image_url,
970 selected_source,
971 cx,
972 );
973 current_request.current_pixel_density = Some(selected_pixel_density);
974 self.reject_image_decode_promises();
975 },
976 (_, _) => {
977 self.image_request.set(ImageRequestPhase::Pending);
981 self.init_image_request(
982 &mut pending_request,
983 image_url,
984 selected_source,
985 cx,
986 );
987 pending_request.current_pixel_density = Some(selected_pixel_density);
988 },
989 }
990 },
991 }
992
993 self.fetch_image(image_url, cx);
994 }
995
996 fn update_the_image_data_sync_steps(&self, cx: &mut js::context::JSContext) {
998 let Some((selected_source, selected_pixel_density)) = self.select_image_source() else {
1001 self.abort_request(State::Broken, ImageRequestPhase::Current, cx);
1006 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
1007 self.image_request.set(ImageRequestPhase::Current);
1008
1009 let this = Trusted::new(self);
1012
1013 self.owner_global().task_manager().dom_manipulation_task_source().queue(
1014 task!(image_null_source_error: move |cx| {
1015 let this = this.root();
1016
1017 {
1019 let mut current_request =
1020 this.current_request.borrow_mut();
1021 current_request.source_url = None;
1022 current_request.parsed_url = None;
1023 }
1024
1025 let has_src_attribute = this.upcast::<Element>().has_attribute(&local_name!("src"));
1031
1032 if has_src_attribute || this.uses_srcset_or_picture() {
1033 this.upcast::<EventTarget>().fire_event(cx, atom!("error"));
1034 }
1035 }));
1036
1037 return;
1039 };
1040
1041 let Ok(image_url) = self.owner_document().base_url().join(&selected_source) else {
1044 self.abort_request(State::Broken, ImageRequestPhase::Current, cx);
1049 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
1050
1051 self.image_request.set(ImageRequestPhase::Current);
1053
1054 let this = Trusted::new(self);
1057
1058 self.owner_global()
1059 .task_manager()
1060 .dom_manipulation_task_source()
1061 .queue(task!(image_selected_source_error: move |cx| {
1062 let this = this.root();
1063
1064 {
1066 let mut current_request =
1067 this.current_request.borrow_mut();
1068 current_request.source_url = Some(selected_source);
1069 current_request.parsed_url = None;
1070 }
1071
1072 this.upcast::<EventTarget>().fire_event(cx, atom!("error"));
1076 }));
1077
1078 return;
1080 };
1081
1082 self.prepare_image_request(&selected_source, selected_pixel_density, &image_url, cx);
1083 }
1084
1085 pub(crate) fn update_the_image_data(&self, cx: &mut js::context::JSContext) {
1087 self.generation.set(self.generation.get() + 1);
1089
1090 if !self.owner_document().is_active() {
1092 }
1098
1099 self.current_request.borrow_mut().state = State::Unavailable;
1108
1109 let mut selected_source = None;
1113 let mut selected_pixel_density = None;
1114
1115 let src = self
1119 .upcast::<Element>()
1120 .get_string_attribute(&local_name!("src"));
1121
1122 if !self.uses_srcset_or_picture() && !src.is_empty() {
1123 selected_source = Some(USVString(String::from(src)));
1124 selected_pixel_density = Some(1_f64);
1125 };
1126
1127 self.last_selected_source
1129 .borrow_mut()
1130 .clone_from(&selected_source);
1131
1132 if let Some(selected_source) = selected_source {
1134 if let Ok(image_url) = self.owner_document().base_url().join(&selected_source) {
1138 let window = self.owner_window();
1142 let response = window.image_cache().get_image(
1143 image_url.clone(),
1144 window.origin().immutable().clone(),
1145 cors_setting_for_element(self.upcast()),
1146 );
1147
1148 if let Some(image) = response {
1150 self.abort_request(State::CompletelyAvailable, ImageRequestPhase::Current, cx);
1155 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
1156
1157 self.image_request.set(ImageRequestPhase::Current);
1159
1160 let mut current_request = self.current_request.borrow_mut();
1163 current_request.metadata = Some(image.metadata());
1164 current_request.image = Some(image);
1165 current_request.final_url = Some(image_url.clone());
1166
1167 self.upcast::<Node>().dirty(NodeDamage::Other);
1170
1171 current_request.current_pixel_density = selected_pixel_density;
1174
1175 let this = Trusted::new(self);
1178
1179 self.owner_global()
1180 .task_manager()
1181 .dom_manipulation_task_source()
1182 .queue(task!(image_load_event: move |cx| {
1183 let this = this.root();
1184
1185 {
1190 let mut current_request =
1191 this.current_request.borrow_mut();
1192 current_request.source_url = Some(selected_source);
1193 current_request.parsed_url = Some(image_url);
1194 }
1195
1196 this.upcast::<EventTarget>().fire_event(cx, atom!("load"));
1200 }));
1201
1202 return;
1204 }
1205 }
1206 }
1207
1208 let task = ImageElementMicrotask::UpdateImageData {
1211 elem: DomRoot::from_ref(self),
1212 generation: self.generation.get(),
1213 };
1214
1215 ScriptThread::await_stable_state(cx, Microtask::ImageElement(task));
1216 }
1217
1218 pub(crate) fn react_to_environment_changes(&self, cx: &JSContext) {
1220 let task = ImageElementMicrotask::EnvironmentChanges {
1222 elem: DomRoot::from_ref(self),
1223 generation: self.generation.get(),
1224 };
1225
1226 ScriptThread::await_stable_state(cx, Microtask::ImageElement(task));
1227 }
1228
1229 fn react_to_environment_changes_sync_steps(
1231 &self,
1232 generation: u32,
1233 cx: &mut js::context::JSContext,
1234 ) {
1235 let document = self.owner_document();
1236 let has_pending_request = matches!(self.image_request.get(), ImageRequestPhase::Pending);
1237
1238 if !document.is_active() || !self.uses_srcset_or_picture() || has_pending_request {
1242 return;
1243 }
1244
1245 let Some((selected_source, selected_pixel_density)) = self.select_image_source() else {
1248 return;
1250 };
1251
1252 let mut same_selected_source = self
1255 .last_selected_source
1256 .borrow()
1257 .as_ref()
1258 .is_some_and(|source| *source == selected_source);
1259
1260 same_selected_source = same_selected_source ||
1264 self.current_request
1265 .borrow()
1266 .source_url
1267 .as_ref()
1268 .is_some_and(|source| *source == selected_source);
1269
1270 let same_selected_pixel_density = self
1271 .current_request
1272 .borrow()
1273 .current_pixel_density
1274 .is_some_and(|pixel_density| pixel_density == selected_pixel_density);
1275
1276 if same_selected_source && same_selected_pixel_density {
1277 return;
1278 }
1279
1280 let Ok(image_url) = document.base_url().join(&selected_source) else {
1284 return;
1285 };
1286
1287 self.image_request.set(ImageRequestPhase::Pending);
1289 self.init_image_request(
1290 &mut self.pending_request.borrow_mut(),
1291 &image_url,
1292 &selected_source,
1293 cx,
1294 );
1295
1296 let window = self.owner_window();
1299 let cache_result = window.image_cache().get_cached_image_status(
1300 image_url.clone(),
1301 window.origin().immutable().clone(),
1302 cors_setting_for_element(self.upcast()),
1303 );
1304
1305 let change_type = ChangeType::Environment {
1306 selected_source: selected_source.clone(),
1307 selected_pixel_density,
1308 };
1309
1310 match cache_result {
1311 ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable { .. }) => {
1312 self.finish_reacting_to_environment_change(
1313 selected_source,
1314 generation,
1315 selected_pixel_density,
1316 );
1317 },
1318 ImageCacheResult::Available(ImageOrMetadataAvailable::MetadataAvailable(m, id)) => {
1319 self.process_image_response_for_environment_change(
1320 ImageResponse::MetadataLoaded(m),
1321 selected_source,
1322 generation,
1323 selected_pixel_density,
1324 cx,
1325 );
1326 self.register_image_cache_callback(id, change_type);
1327 },
1328 ImageCacheResult::FailedToLoadOrDecode => {
1329 self.process_image_response_for_environment_change(
1330 ImageResponse::FailedToLoadOrDecode,
1331 selected_source,
1332 generation,
1333 selected_pixel_density,
1334 cx,
1335 );
1336 },
1337 ImageCacheResult::ReadyForRequest(id) => {
1338 self.fetch_request(&image_url, id);
1339 self.register_image_cache_callback(id, change_type);
1340 },
1341 ImageCacheResult::Pending(id) => {
1342 self.register_image_cache_callback(id, change_type);
1343 },
1344 }
1345 }
1346
1347 fn react_to_decode_image_sync_steps(&self, cx: &mut JSContext, promise: Rc<Promise>) {
1349 if !self.owner_document().is_fully_active() ||
1353 matches!(self.current_request.borrow().state, State::Broken)
1354 {
1355 promise.reject_error(cx, Error::Encoding(None));
1356 } else if matches!(
1357 self.current_request.borrow().state,
1358 State::CompletelyAvailable
1359 ) {
1360 promise.resolve_native(cx, &());
1362 } else if matches!(self.current_request.borrow().state, State::Unavailable) &&
1363 self.current_request.borrow().source_url.is_none()
1364 {
1365 promise.reject_error(cx, Error::Encoding(None));
1370 } else {
1371 self.image_decode_promises.borrow_mut().push(promise);
1372 }
1373 }
1374
1375 fn resolve_image_decode_promises(&self) {
1377 if self.image_decode_promises.borrow().is_empty() {
1378 return;
1379 }
1380
1381 let trusted_image_decode_promises: Vec<TrustedPromise> = self
1384 .image_decode_promises
1385 .borrow()
1386 .iter()
1387 .map(|promise| TrustedPromise::new(promise.clone()))
1388 .collect();
1389
1390 self.image_decode_promises.borrow_mut().clear();
1391
1392 self.owner_global()
1393 .task_manager()
1394 .dom_manipulation_task_source()
1395 .queue(task!(fulfill_image_decode_promises: move |cx| {
1396 for trusted_promise in trusted_image_decode_promises {
1397 trusted_promise.root().resolve_native(cx, &());
1398 }
1399 }));
1400 }
1401
1402 fn reject_image_decode_promises(&self) {
1404 if self.image_decode_promises.borrow().is_empty() {
1405 return;
1406 }
1407
1408 let trusted_image_decode_promises: Vec<TrustedPromise> = self
1411 .image_decode_promises
1412 .borrow()
1413 .iter()
1414 .map(|promise| TrustedPromise::new(promise.clone()))
1415 .collect();
1416
1417 self.image_decode_promises.borrow_mut().clear();
1418
1419 self.owner_global()
1420 .task_manager()
1421 .dom_manipulation_task_source()
1422 .queue(task!(reject_image_decode_promises: move |cx| {
1423 for trusted_promise in trusted_image_decode_promises {
1424 trusted_promise.root().reject_error(cx, Error::Encoding(None));
1425 }
1426 }));
1427 }
1428
1429 fn finish_reacting_to_environment_change(
1431 &self,
1432 selected_source: USVString,
1433 generation: u32,
1434 selected_pixel_density: f64,
1435 ) {
1436 let this = Trusted::new(self);
1439
1440 self.owner_global()
1441 .task_manager()
1442 .dom_manipulation_task_source()
1443 .queue(task!(image_load_event: move |cx| {
1444 let this = this.root();
1445
1446 if this.generation.get() != generation {
1449 this.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
1450 this.image_request.set(ImageRequestPhase::Current);
1451 return;
1452 }
1453
1454 *this.last_selected_source.borrow_mut() = Some(selected_source);
1457
1458 {
1459 let mut pending_request = this.pending_request.borrow_mut();
1460
1461 pending_request.state = State::CompletelyAvailable;
1463
1464 pending_request.current_pixel_density = Some(selected_pixel_density);
1465
1466 mem::swap(&mut *this.current_request.borrow_mut(), &mut *pending_request);
1472 }
1473
1474 this.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
1475 this.image_request.set(ImageRequestPhase::Current);
1476
1477 this.upcast::<Node>().dirty(NodeDamage::Other);
1479
1480 this.upcast::<EventTarget>().fire_event(cx, atom!("load"));
1482 }));
1483 }
1484
1485 fn uses_srcset_or_picture(&self) -> bool {
1487 let element = self.upcast::<Element>();
1488
1489 let has_srcset_attribute = element.has_attribute(&local_name!("srcset"));
1490 let has_parent_picture = element
1491 .upcast::<Node>()
1492 .GetParentElement()
1493 .is_some_and(|parent| parent.is::<HTMLPictureElement>());
1494 has_srcset_attribute || has_parent_picture
1495 }
1496
1497 fn new_inherited(
1498 local_name: LocalName,
1499 prefix: Option<Prefix>,
1500 document: &Document,
1501 creator: ElementCreator,
1502 ) -> HTMLImageElement {
1503 HTMLImageElement {
1504 htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
1505 image_request: Cell::new(ImageRequestPhase::Current),
1506 current_request: DomRefCell::new(ImageRequest {
1507 state: State::Unavailable,
1508 parsed_url: None,
1509 source_url: None,
1510 image: None,
1511 metadata: None,
1512 blocker: DomRefCell::new(None),
1513 final_url: None,
1514 current_pixel_density: None,
1515 }),
1516 pending_request: DomRefCell::new(ImageRequest {
1517 state: State::Unavailable,
1518 parsed_url: None,
1519 source_url: None,
1520 image: None,
1521 metadata: None,
1522 blocker: DomRefCell::new(None),
1523 final_url: None,
1524 current_pixel_density: None,
1525 }),
1526 form_owner: Default::default(),
1527 generation: Default::default(),
1528 source_set: DomRefCell::new(SourceSet::new()),
1529 dimension_attribute_source: Default::default(),
1530 last_selected_source: DomRefCell::new(None),
1531 image_decode_promises: DomRefCell::new(vec![]),
1532 line_number: creator.return_line_number(),
1533 }
1534 }
1535
1536 pub(crate) fn new(
1537 cx: &mut js::context::JSContext,
1538 local_name: LocalName,
1539 prefix: Option<Prefix>,
1540 document: &Document,
1541 proto: Option<HandleObject>,
1542 creator: ElementCreator,
1543 ) -> DomRoot<HTMLImageElement> {
1544 let image_element = Node::reflect_node_with_proto(
1545 cx,
1546 Box::new(HTMLImageElement::new_inherited(
1547 local_name, prefix, document, creator,
1548 )),
1549 document,
1550 proto,
1551 );
1552 image_element
1553 .dimension_attribute_source
1554 .set(Some(image_element.upcast()));
1555 image_element
1556 }
1557
1558 pub(crate) fn areas(&self) -> Option<Vec<DomRoot<HTMLAreaElement>>> {
1559 let elem = self.upcast::<Element>();
1560 let value = elem.get_attribute_string_value(&local_name!("usemap"))?;
1561
1562 if value.is_empty() || !value.is_char_boundary(1) {
1563 return None;
1564 }
1565
1566 let (first, last) = value.split_at(1);
1567
1568 if first != "#" || last.is_empty() {
1569 return None;
1570 }
1571
1572 let useMapElements = self
1573 .owner_document()
1574 .upcast::<Node>()
1575 .traverse_preorder(ShadowIncluding::No)
1576 .filter_map(DomRoot::downcast::<HTMLMapElement>)
1577 .find(|n| {
1578 n.upcast::<Element>()
1579 .get_name()
1580 .is_some_and(|n| *n == *last)
1581 });
1582
1583 useMapElements.map(|mapElem| mapElem.get_area_elements())
1584 }
1585
1586 pub(crate) fn same_origin(&self, origin: &MutableOrigin) -> bool {
1587 if let Some(ref image) = self.current_request.borrow().image {
1588 return image.cors_status() == CorsStatus::Safe;
1589 }
1590
1591 self.current_request
1592 .borrow()
1593 .final_url
1594 .as_ref()
1595 .is_some_and(|url| url.scheme() == "data" || url.origin().same_origin(origin))
1596 }
1597
1598 fn generation_id(&self) -> u32 {
1599 self.generation.get()
1600 }
1601
1602 fn load_broken_image_icon(&self) {
1603 let window = self.owner_window();
1604 let Some(broken_image_icon) = window.image_cache().get_broken_image_icon() else {
1605 return;
1606 };
1607
1608 self.current_request.borrow_mut().metadata = Some(broken_image_icon.metadata);
1609 self.current_request.borrow_mut().image = Some(Image::Raster(broken_image_icon));
1610 self.upcast::<Node>().dirty(NodeDamage::Other);
1611 }
1612
1613 pub(crate) fn full_image_url_for_user_interface(&self) -> Option<ServoUrl> {
1616 self.owner_document()
1617 .base_url()
1618 .join(&self.CurrentSrc())
1619 .ok()
1620 }
1621}
1622
1623#[derive(JSTraceable, MallocSizeOf)]
1624pub(crate) enum ImageElementMicrotask {
1625 UpdateImageData {
1626 elem: DomRoot<HTMLImageElement>,
1627 generation: u32,
1628 },
1629 EnvironmentChanges {
1630 elem: DomRoot<HTMLImageElement>,
1631 generation: u32,
1632 },
1633 Decode {
1634 elem: DomRoot<HTMLImageElement>,
1635 #[conditional_malloc_size_of]
1636 promise: Rc<Promise>,
1637 },
1638}
1639
1640impl MicrotaskRunnable for ImageElementMicrotask {
1641 fn handler(&self, cx: &mut js::context::JSContext) {
1642 match *self {
1643 ImageElementMicrotask::UpdateImageData {
1644 ref elem,
1645 ref generation,
1646 } => {
1647 if elem.generation.get() == *generation {
1651 elem.update_the_image_data_sync_steps(cx);
1652 }
1653 },
1654 ImageElementMicrotask::EnvironmentChanges {
1655 ref elem,
1656 ref generation,
1657 } => {
1658 elem.react_to_environment_changes_sync_steps(*generation, cx);
1659 },
1660 ImageElementMicrotask::Decode {
1661 ref elem,
1662 ref promise,
1663 } => {
1664 elem.react_to_decode_image_sync_steps(cx, promise.clone());
1665 },
1666 }
1667 }
1668
1669 fn enter_realm<'cx>(&self, cx: &'cx mut js::context::JSContext) -> AutoRealm<'cx> {
1670 match self {
1671 &ImageElementMicrotask::UpdateImageData { ref elem, .. } |
1672 &ImageElementMicrotask::EnvironmentChanges { ref elem, .. } |
1673 &ImageElementMicrotask::Decode { ref elem, .. } => enter_auto_realm(cx, &**elem),
1674 }
1675 }
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 pub(crate) fn image_url(self) -> Option<ServoUrl> {
1695 self.current_request().parsed_url.clone()
1696 }
1697
1698 pub(crate) fn image_data(self) -> (Option<Image>, Option<ImageMetadata>) {
1699 let current_request = self.current_request();
1700 (current_request.image.clone(), current_request.metadata)
1701 }
1702
1703 pub(crate) fn image_density(self) -> Option<f64> {
1704 self.current_request().current_pixel_density
1705 }
1706
1707 pub(crate) fn showing_broken_image_icon(self) -> bool {
1708 matches!(self.current_request().state, State::Broken)
1709 }
1710
1711 pub(crate) fn get_width(self) -> LengthOrPercentageOrAuto {
1712 self.dimension_attribute_source()
1713 .get_attr_for_layout(&ns!(), &local_name!("width"))
1714 .map(AttrValue::as_dimension)
1715 .cloned()
1716 .unwrap_or(LengthOrPercentageOrAuto::Auto)
1717 }
1718
1719 pub(crate) fn get_height(self) -> LengthOrPercentageOrAuto {
1720 self.dimension_attribute_source()
1721 .get_attr_for_layout(&ns!(), &local_name!("height"))
1722 .map(AttrValue::as_dimension)
1723 .cloned()
1724 .unwrap_or(LengthOrPercentageOrAuto::Auto)
1725 }
1726}
1727
1728fn parse_a_sizes_attribute(value: &str) -> SourceSizeList {
1730 let mut input = ParserInput::new(value);
1731 let mut parser = Parser::new(&mut input);
1732 let url_data = Url::parse("about:blank").unwrap().into();
1733 let context =
1736 parser_context_for_anonymous_content(CssRuleType::Style, ParsingMode::empty(), &url_data);
1737 SourceSizeList::parse(&context, &mut parser)
1738}
1739
1740impl HTMLImageElementMethods<crate::DomTypeHolder> for HTMLImageElement {
1741 fn Image(
1743 cx: &mut JSContext,
1744 window: &Window,
1745 proto: Option<HandleObject>,
1746 width: Option<u32>,
1747 height: Option<u32>,
1748 ) -> Fallible<DomRoot<HTMLImageElement>> {
1749 let document = window.Document();
1751
1752 let element = Element::create(
1755 cx,
1756 QualName::new(None, ns!(html), local_name!("img")),
1757 None,
1758 &document,
1759 ElementCreator::ScriptCreated,
1760 CustomElementCreationMode::Synchronous,
1761 proto,
1762 );
1763
1764 let image = DomRoot::downcast::<HTMLImageElement>(element).unwrap();
1765
1766 if let Some(w) = width {
1768 image.SetWidth(cx, w);
1769 }
1770
1771 if let Some(h) = height {
1774 image.SetHeight(cx, h);
1775 }
1776
1777 Ok(image)
1779 }
1780
1781 make_getter!(Alt, "alt");
1783 make_setter!(SetAlt, "alt");
1785
1786 make_url_getter!(Src, "src");
1788
1789 make_url_setter!(SetSrc, "src");
1791
1792 make_url_getter!(Srcset, "srcset");
1794 make_url_setter!(SetSrcset, "srcset");
1796
1797 make_getter!(Sizes, "sizes");
1799
1800 make_setter!(SetSizes, "sizes");
1802
1803 fn GetCrossOrigin(&self) -> Option<DOMString> {
1805 reflect_cross_origin_attribute(self.upcast::<Element>())
1806 }
1807
1808 fn SetCrossOrigin(&self, cx: &mut JSContext, value: Option<DOMString>) {
1810 set_cross_origin_attribute(cx, self.upcast::<Element>(), value);
1811 }
1812
1813 make_getter!(UseMap, "usemap");
1815 make_setter!(SetUseMap, "usemap");
1817
1818 make_bool_getter!(IsMap, "ismap");
1820 make_bool_setter!(SetIsMap, "ismap");
1822
1823 fn Width(&self) -> u32 {
1825 let node = self.upcast::<Node>();
1826 node.content_box()
1827 .map(|rect| rect.size.width.to_px() as u32)
1828 .unwrap_or_else(|| self.NaturalWidth())
1829 }
1830
1831 make_dimension_uint_setter!(SetWidth, "width");
1833
1834 fn Height(&self) -> u32 {
1836 let node = self.upcast::<Node>();
1837 node.content_box()
1838 .map(|rect| rect.size.height.to_px() as u32)
1839 .unwrap_or_else(|| self.NaturalHeight())
1840 }
1841
1842 make_dimension_uint_setter!(SetHeight, "height");
1844
1845 fn NaturalWidth(&self) -> u32 {
1847 let request = self.current_request.borrow();
1848 if matches!(request.state, State::Broken) {
1849 return 0;
1850 }
1851
1852 let pixel_density = request.current_pixel_density.unwrap_or(1f64);
1853 match request.metadata {
1854 Some(ref metadata) => (metadata.width as f64 / pixel_density) as u32,
1855 None => 0,
1856 }
1857 }
1858
1859 fn NaturalHeight(&self) -> u32 {
1861 let request = self.current_request.borrow();
1862 if matches!(request.state, State::Broken) {
1863 return 0;
1864 }
1865
1866 let pixel_density = request.current_pixel_density.unwrap_or(1f64);
1867 match request.metadata {
1868 Some(ref metadata) => (metadata.height as f64 / pixel_density) as u32,
1869 None => 0,
1870 }
1871 }
1872
1873 fn Complete(&self) -> bool {
1875 let element = self.upcast::<Element>();
1876
1877 let has_srcset_attribute = element.has_attribute(&local_name!("srcset"));
1880 if !element.has_attribute(&local_name!("src")) && !has_srcset_attribute {
1881 return true;
1882 }
1883
1884 let src = element.get_string_attribute(&local_name!("src"));
1886 if !has_srcset_attribute && src.is_empty() {
1887 return true;
1888 }
1889
1890 if matches!(self.image_request.get(), ImageRequestPhase::Current) &&
1894 matches!(
1895 self.current_request.borrow().state,
1896 State::CompletelyAvailable | State::Broken
1897 )
1898 {
1899 return true;
1900 }
1901
1902 false
1904 }
1905
1906 fn CurrentSrc(&self) -> USVString {
1908 let current_request = self.current_request.borrow();
1909 let url = ¤t_request.parsed_url;
1910 match *url {
1911 Some(ref url) => USVString(url.clone().into_string()),
1912 None => {
1913 let unparsed_url = ¤t_request.source_url;
1914 match *unparsed_url {
1915 Some(ref url) => url.clone(),
1916 None => USVString("".to_owned()),
1917 }
1918 },
1919 }
1920 }
1921
1922 fn ReferrerPolicy(&self) -> DOMString {
1924 reflect_referrer_policy_attribute(self.upcast::<Element>())
1925 }
1926
1927 make_setter!(SetReferrerPolicy, "referrerpolicy");
1929
1930 fn Decode(&self, cx: &mut JSContext) -> Rc<Promise> {
1932 let promise = Promise::new(cx, &self.global());
1934
1935 let task = ImageElementMicrotask::Decode {
1937 elem: DomRoot::from_ref(self),
1938 promise: promise.clone(),
1939 };
1940
1941 ScriptThread::await_stable_state(cx, Microtask::ImageElement(task));
1942
1943 promise
1945 }
1946
1947 make_getter!(Name, "name");
1949
1950 make_atomic_setter!(SetName, "name");
1952
1953 make_getter!(Align, "align");
1955
1956 make_setter!(SetAlign, "align");
1958
1959 make_uint_getter!(Hspace, "hspace");
1961
1962 make_uint_setter!(SetHspace, "hspace");
1964
1965 make_uint_getter!(Vspace, "vspace");
1967
1968 make_uint_setter!(SetVspace, "vspace");
1970
1971 make_url_getter!(LongDesc, "longdesc");
1973
1974 make_url_setter!(SetLongDesc, "longdesc");
1976
1977 make_getter!(Border, "border");
1979
1980 make_setter!(SetBorder, "border");
1982}
1983
1984impl VirtualMethods for HTMLImageElement {
1985 fn super_type(&self) -> Option<&dyn VirtualMethods> {
1986 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
1987 }
1988
1989 fn adopting_steps(&self, cx: &mut JSContext, old_doc: &Document) {
1990 self.super_type().unwrap().adopting_steps(cx, old_doc);
1991 self.update_the_image_data(cx);
1992 }
1993
1994 fn attribute_mutated(
1995 &self,
1996 cx: &mut js::context::JSContext,
1997 attr: AttrRef<'_>,
1998 mutation: AttributeMutation,
1999 ) {
2000 self.super_type()
2001 .unwrap()
2002 .attribute_mutated(cx, attr, mutation);
2003 match attr.local_name() {
2004 &local_name!("src") |
2005 &local_name!("srcset") |
2006 &local_name!("width") |
2007 &local_name!("sizes") => {
2008 self.update_the_image_data(cx);
2012 },
2013 &local_name!("crossorigin") => {
2014 let cross_origin_state_changed = match mutation {
2017 AttributeMutation::Removed | AttributeMutation::Set(None, _) => true,
2018 AttributeMutation::Set(Some(old_value), _) => {
2019 let new_cors_setting =
2020 CorsSettings::from_enumerated_attribute(&attr.value());
2021 let old_cors_setting = CorsSettings::from_enumerated_attribute(old_value);
2022
2023 new_cors_setting != old_cors_setting
2024 },
2025 };
2026
2027 if cross_origin_state_changed {
2028 self.update_the_image_data(cx);
2029 }
2030 },
2031 &local_name!("referrerpolicy") => {
2032 let referrer_policy_state_changed = match mutation {
2035 AttributeMutation::Removed | AttributeMutation::Set(None, _) => {
2036 ReferrerPolicy::from(&**attr.value()) != ReferrerPolicy::EmptyString
2037 },
2038 AttributeMutation::Set(Some(old_value), _) => {
2039 ReferrerPolicy::from(&**attr.value()) != ReferrerPolicy::from(&**old_value)
2040 },
2041 };
2042
2043 if referrer_policy_state_changed {
2044 self.update_the_image_data(cx);
2045 }
2046 },
2047 _ => {},
2048 }
2049 }
2050
2051 fn attribute_affects_presentational_hints(&self, attr: AttrRef<'_>) -> bool {
2052 match attr.local_name() {
2053 &local_name!("width") | &local_name!("height") => true,
2054 _ => self
2055 .super_type()
2056 .unwrap()
2057 .attribute_affects_presentational_hints(attr),
2058 }
2059 }
2060
2061 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
2062 match name {
2063 &local_name!("width") | &local_name!("height") => {
2064 AttrValue::from_dimension(value.into())
2065 },
2066 &local_name!("hspace") | &local_name!("vspace") => AttrValue::from_u32(value.into(), 0),
2067 _ => self
2068 .super_type()
2069 .unwrap()
2070 .parse_plain_attribute(name, value),
2071 }
2072 }
2073
2074 fn handle_event(&self, cx: &mut js::context::JSContext, event: &Event) {
2075 if event.type_() != atom!("click") {
2076 return;
2077 }
2078
2079 let area_elements = self.areas();
2080 let elements = match area_elements {
2081 Some(x) => x,
2082 None => return,
2083 };
2084
2085 let mouse_event = match event.downcast::<MouseEvent>() {
2087 Some(x) => x,
2088 None => return,
2089 };
2090
2091 let point = Point2D::new(
2092 mouse_event.ClientX().to_f32().unwrap(),
2093 mouse_event.ClientY().to_f32().unwrap(),
2094 );
2095 let bcr = self.upcast::<Element>().GetBoundingClientRect(cx);
2096 let bcr_p = Point2D::new(bcr.X() as f32, bcr.Y() as f32);
2097
2098 for element in elements {
2100 let shape = element.get_shape_from_coords();
2101 let shp = match shape {
2102 Some(x) => x.absolute_coords(bcr_p),
2103 None => return,
2104 };
2105 if shp.hit_test(&point) {
2106 element.activation_behavior(cx, event, self.upcast());
2107 return;
2108 }
2109 }
2110 }
2111
2112 fn bind_to_tree(&self, cx: &mut JSContext, context: &BindContext) {
2114 if let Some(s) = self.super_type() {
2115 s.bind_to_tree(cx, context);
2116 }
2117 let document = self.owner_document();
2118 if context.tree_connected {
2119 document.register_responsive_image(self);
2120 }
2121
2122 let parent = self.upcast::<Node>().GetParentNode().unwrap();
2123
2124 if parent.is::<HTMLPictureElement>() && std::ptr::eq(&*parent, context.parent) {
2127 self.update_the_image_data(cx);
2128 }
2129 }
2130
2131 fn unbind_from_tree(&self, cx: &mut js::context::JSContext, context: &UnbindContext) {
2133 self.super_type().unwrap().unbind_from_tree(cx, context);
2134 let document = self.owner_document();
2135 document.unregister_responsive_image(self);
2136
2137 if context.parent.is::<HTMLPictureElement>() && !self.upcast::<Node>().has_parent() {
2140 self.update_the_image_data(cx);
2141 }
2142 }
2143
2144 fn moving_steps(&self, cx: &mut JSContext, context: &MoveContext) {
2146 if let Some(super_type) = self.super_type() {
2147 super_type.moving_steps(cx, context);
2148 }
2149
2150 if let Some(old_parent) = context.old_parent &&
2152 old_parent.is::<HTMLPictureElement>()
2153 {
2154 self.update_the_image_data(cx);
2155 }
2156 }
2157}
2158
2159impl FormControl for HTMLImageElement {
2160 fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
2161 self.form_owner.get()
2162 }
2163
2164 fn set_form_owner(&self, form: Option<&HTMLFormElement>) {
2165 self.form_owner.set(form);
2166 }
2167
2168 fn to_html_element(&self) -> &HTMLElement {
2169 self.upcast::<HTMLElement>()
2170 }
2171}
2172
2173pub(crate) fn collect_sequence_characters(
2176 s: &str,
2177 mut predicate: impl FnMut(&char) -> bool,
2178) -> (&str, &str) {
2179 let i = s.find(|ch| !predicate(&ch)).unwrap_or(s.len());
2180 (&s[0..i], &s[i..])
2181}
2182
2183fn is_valid_non_negative_integer_string(s: &str) -> bool {
2186 s.chars().all(|c| c.is_ascii_digit())
2187}
2188
2189fn is_valid_floating_point_number_string(s: &str) -> bool {
2192 static RE: LazyLock<Regex> =
2193 LazyLock::new(|| Regex::new(r"^-?(?:\d+\.\d+|\d+|\.\d+)(?:(e|E)(\+|\-)?\d+)?$").unwrap());
2194
2195 RE.is_match(s)
2196}
2197
2198pub fn parse_a_srcset_attribute(input: &str) -> Vec<ImageSource> {
2201 let mut current_index = 0;
2204
2205 let mut candidates = vec![];
2207 while current_index < input.len() {
2208 let remaining_string = &input[current_index..];
2209
2210 let (collected_characters, string_after_whitespace) =
2217 collect_sequence_characters(remaining_string, |character| {
2218 *character == ',' || character.is_ascii_whitespace()
2219 });
2220
2221 current_index += collected_characters.len();
2224
2225 if string_after_whitespace.is_empty() {
2227 return candidates;
2228 }
2229
2230 let (url, _) =
2233 collect_sequence_characters(string_after_whitespace, |c| !char::is_ascii_whitespace(c));
2234
2235 current_index += url.len();
2238
2239 let mut descriptors = Vec::new();
2241
2242 if url.ends_with(',') {
2246 let image_source = ImageSource {
2247 url: url.trim_end_matches(',').into(),
2248 descriptor: Descriptor {
2249 width: None,
2250 density: None,
2251 },
2252 };
2253 candidates.push(image_source);
2254 continue;
2255 }
2256
2257 let descriptors_string = &input[current_index..];
2260 let (spaces, descriptors_string) =
2261 collect_sequence_characters(descriptors_string, |character| {
2262 character.is_ascii_whitespace()
2263 });
2264 current_index += spaces.len();
2265
2266 let mut current_descriptor = String::new();
2268
2269 let mut state = ParseState::InDescriptor;
2271
2272 let mut characters = descriptors_string.chars();
2276 let mut character = characters.next();
2277 if let Some(character) = character {
2278 current_index += character.len_utf8();
2279 }
2280
2281 loop {
2282 match (state, character) {
2283 (ParseState::InDescriptor, Some(character)) if character.is_ascii_whitespace() => {
2284 if !current_descriptor.is_empty() {
2288 descriptors.push(current_descriptor);
2289 current_descriptor = String::new();
2290 state = ParseState::AfterDescriptor;
2291 }
2292 },
2293 (ParseState::InDescriptor, Some(',')) => {
2294 if !current_descriptor.is_empty() {
2298 descriptors.push(current_descriptor);
2299 }
2300 break;
2301 },
2302 (ParseState::InDescriptor, Some('(')) => {
2303 current_descriptor.push('(');
2305 state = ParseState::InParens;
2306 },
2307 (ParseState::InDescriptor, Some(character)) => {
2308 current_descriptor.push(character);
2310 },
2311 (ParseState::InDescriptor, None) => {
2312 if !current_descriptor.is_empty() {
2315 descriptors.push(current_descriptor);
2316 }
2317 break;
2318 },
2319 (ParseState::InParens, Some(')')) => {
2320 current_descriptor.push(')');
2322 state = ParseState::InDescriptor;
2323 },
2324 (ParseState::InParens, Some(character)) => {
2325 current_descriptor.push(character);
2327 },
2328 (ParseState::InParens, None) => {
2329 descriptors.push(current_descriptor);
2332 break;
2333 },
2334 (ParseState::AfterDescriptor, Some(character))
2335 if character.is_ascii_whitespace() =>
2336 {
2337 },
2339 (ParseState::AfterDescriptor, Some(_)) => {
2340 state = ParseState::InDescriptor;
2343 continue;
2344 },
2345 (ParseState::AfterDescriptor, None) => {
2346 break;
2348 },
2349 }
2350
2351 character = characters.next();
2352 if let Some(character) = character {
2353 current_index += character.len_utf8();
2354 }
2355 }
2356
2357 let mut error = false;
2359 let mut width: Option<u32> = None;
2361 let mut density: Option<f64> = None;
2363 let mut future_compat_h: Option<u32> = None;
2365
2366 for descriptor in descriptors.into_iter() {
2369 let Some(last_character) = descriptor.chars().last() else {
2370 break;
2371 };
2372
2373 let first_part_of_string = &descriptor[0..descriptor.len() - last_character.len_utf8()];
2374 match last_character {
2375 'w' if is_valid_non_negative_integer_string(first_part_of_string) &&
2382 density.is_none() &&
2383 width.is_none() =>
2384 {
2385 match parse_unsigned_integer(first_part_of_string.chars()) {
2386 Ok(number) if number > 0 => {
2387 width = Some(number);
2388 continue;
2389 },
2390 _ => error = true,
2391 }
2392 },
2393
2394 'x' if is_valid_floating_point_number_string(first_part_of_string) &&
2408 width.is_none() &&
2409 density.is_none() &&
2410 future_compat_h.is_none() =>
2411 {
2412 match first_part_of_string.parse::<f64>() {
2413 Ok(number) if number.is_finite() && number >= 0. => {
2414 density = Some(number);
2415 continue;
2416 },
2417 _ => error = true,
2418 }
2419 },
2420
2421 'h' if is_valid_non_negative_integer_string(first_part_of_string) &&
2430 future_compat_h.is_none() &&
2431 density.is_none() =>
2432 {
2433 match parse_unsigned_integer(first_part_of_string.chars()) {
2434 Ok(number) if number > 0 => {
2435 future_compat_h = Some(number);
2436 continue;
2437 },
2438 _ => error = true,
2439 }
2440 },
2441
2442 _ => error = true,
2445 }
2446
2447 if error {
2448 break;
2449 }
2450 }
2451
2452 if future_compat_h.is_some() && width.is_none() {
2454 error = true;
2455 }
2456
2457 if !error {
2461 let image_source = ImageSource {
2462 url: url.into(),
2463 descriptor: Descriptor { width, density },
2464 };
2465 candidates.push(image_source);
2466 }
2467
2468 }
2470 candidates
2471}
2472
2473#[derive(Clone)]
2474enum ChangeType {
2475 Environment {
2476 selected_source: USVString,
2477 selected_pixel_density: f64,
2478 },
2479 Element,
2480}
2481
2482fn is_supported_image_mime_type(input: &str) -> bool {
2484 let mime_type = input.trim();
2486
2487 let mime_type_essence = match mime_type.find(';') {
2489 Some(semi) => &mime_type[..semi],
2490 _ => mime_type,
2491 };
2492
2493 if mime_type_essence.is_empty() {
2498 return true;
2499 }
2500
2501 SUPPORTED_IMAGE_MIME_TYPES.contains(&mime_type_essence)
2502}