1use std::cell::Cell;
6use std::default::Default;
7use std::rc::Rc;
8use std::sync::{Arc, LazyLock};
9use std::{char, mem};
10
11use app_units::Au;
12use cssparser::{Parser, ParserInput};
13use dom_struct::dom_struct;
14use euclid::default::Point2D;
15use html5ever::{LocalName, Prefix, QualName, local_name, ns};
16use js::context::JSContext;
17use js::rust::HandleObject;
18use mime::{self, Mime};
19use net_traits::image_cache::{
20 Image, ImageCache, ImageCacheResult, ImageLoadListener, ImageOrMetadataAvailable,
21 ImageResponse, PendingImageId,
22};
23use net_traits::request::{CorsSettings, Destination, Initiator, RequestId};
24use net_traits::{
25 FetchMetadata, FetchResponseMsg, NetworkError, ReferrerPolicy, ResourceFetchTiming,
26};
27use num_traits::ToPrimitive;
28use pixels::{CorsStatus, ImageMetadata, Snapshot};
29use regex::Regex;
30use rustc_hash::FxHashSet;
31use script_bindings::cell::DomRefCell;
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::bindings::codegen::Bindings::DOMRectBinding::DOMRect_Binding::DOMRectMethods;
44use crate::dom::bindings::codegen::Bindings::ElementBinding::Element_Binding::ElementMethods;
45use crate::dom::bindings::codegen::Bindings::HTMLImageElementBinding::HTMLImageElementMethods;
46use crate::dom::bindings::codegen::Bindings::MouseEventBinding::MouseEventMethods;
47use crate::dom::bindings::codegen::Bindings::NodeBinding::Node_Binding::NodeMethods;
48use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
49use crate::dom::bindings::error::{Error, Fallible};
50use crate::dom::bindings::inheritance::Castable;
51use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
52use crate::dom::bindings::reflector::DomGlobal;
53use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom, ToLayoutOptional};
54use crate::dom::bindings::str::{DOMString, USVString};
55use crate::dom::csp::{GlobalCspReporting, Violation};
56use crate::dom::document::Document;
57use crate::dom::element::attributes::storage::AttrRef;
58use crate::dom::element::{
59 AttributeMutation, CustomElementCreationMode, Element, ElementCreator,
60 cors_setting_for_element, referrer_policy_for_element, reflect_cross_origin_attribute,
61 reflect_referrer_policy_attribute, set_cross_origin_attribute,
62};
63use crate::dom::event::Event;
64use crate::dom::eventtarget::EventTarget;
65use crate::dom::globalscope::GlobalScope;
66use crate::dom::html::htmlareaelement::HTMLAreaElement;
67use crate::dom::html::htmlelement::HTMLElement;
68use crate::dom::html::htmlformelement::{FormControl, HTMLFormElement};
69use crate::dom::html::htmlmapelement::HTMLMapElement;
70use crate::dom::html::htmlpictureelement::HTMLPictureElement;
71use crate::dom::html::htmlsourceelement::HTMLSourceElement;
72use crate::dom::iterators::ShadowIncluding;
73use crate::dom::medialist::MediaList;
74use crate::dom::mouseevent::MouseEvent;
75use crate::dom::node::virtualmethods::VirtualMethods;
76use crate::dom::node::{BindContext, MoveContext, Node, NodeDamage, NodeTraits, UnbindContext};
77use crate::dom::performance::performanceresourcetiming::InitiatorType;
78use crate::dom::promise::Promise;
79use crate::dom::window::Window;
80use crate::fetch::{RequestWithGlobalScope, create_a_potential_cors_request};
81use crate::microtask::MicrotaskRunnable;
82use crate::network_listener::{self, FetchResponseListener, ResourceTimingListener};
83use crate::realms::enter_auto_realm;
84use crate::script_thread::ScriptThread;
85
86const SUPPORTED_IMAGE_MIME_TYPES: &[&str] = &[
90 "image/bmp",
91 "image/gif",
92 "image/jpeg",
93 "image/jpg",
94 "image/pjpeg",
95 "image/png",
96 "image/apng",
97 "image/x-png",
98 "image/svg+xml",
99 "image/vnd.microsoft.icon",
100 "image/x-icon",
101 "image/webp",
102];
103
104#[derive(Clone, Copy, Debug)]
105enum ParseState {
106 InDescriptor,
107 InParens,
108 AfterDescriptor,
109}
110
111#[derive(MallocSizeOf)]
113pub(crate) struct SourceSet {
114 image_sources: Vec<ImageSource>,
115 source_size: SourceSizeList,
116}
117
118impl SourceSet {
119 fn new() -> SourceSet {
120 SourceSet {
121 image_sources: Vec::new(),
122 source_size: SourceSizeList::empty(),
123 }
124 }
125}
126
127#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
128pub struct ImageSource {
129 pub url: String,
130 pub descriptor: Descriptor,
131}
132
133#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
134pub struct Descriptor {
135 pub width: Option<u32>,
136 pub density: Option<f64>,
137}
138
139#[derive(Clone, Copy, JSTraceable, MallocSizeOf)]
141enum State {
142 Unavailable,
143 PartiallyAvailable,
144 CompletelyAvailable,
145 Broken,
146}
147
148#[derive(Clone, Copy, JSTraceable, MallocSizeOf)]
149enum ImageRequestPhase {
150 Pending,
151 Current,
152}
153
154#[derive(JSTraceable, MallocSizeOf)]
156#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
157struct ImageRequest {
158 state: State,
159 #[no_trace]
160 parsed_url: Option<ServoUrl>,
161 source_url: Option<USVString>,
162 blocker: DomRefCell<Option<LoadBlocker>>,
163 #[no_trace]
164 image: Option<Image>,
165 #[no_trace]
166 metadata: Option<ImageMetadata>,
167 #[no_trace]
168 final_url: Option<ServoUrl>,
169 current_pixel_density: Option<f64>,
170}
171
172#[dom_struct]
173pub(crate) struct HTMLImageElement {
174 htmlelement: HTMLElement,
175 image_request: Cell<ImageRequestPhase>,
176 current_request: DomRefCell<ImageRequest>,
177 pending_request: DomRefCell<ImageRequest>,
178 form_owner: MutNullableDom<HTMLFormElement>,
179 generation: Cell<u32>,
180 source_set: DomRefCell<SourceSet>,
181 dimension_attribute_source: MutNullableDom<Element>,
184 last_selected_source: DomRefCell<Option<USVString>>,
186 #[conditional_malloc_size_of]
187 image_decode_promises: DomRefCell<Vec<Rc<Promise>>>,
188 line_number: u64,
190}
191
192impl HTMLImageElement {
193 pub(crate) fn is_usable(&self) -> Fallible<bool> {
195 if let Some(image) = &self.current_request.borrow().image {
197 let intrinsic_size = image.metadata();
198 if intrinsic_size.width == 0 || intrinsic_size.height == 0 {
199 return Ok(false);
200 }
201 }
202
203 match self.current_request.borrow().state {
204 State::Broken => Err(Error::InvalidState(None)),
206 State::CompletelyAvailable => Ok(true),
207 State::PartiallyAvailable | State::Unavailable => Ok(false),
209 }
210 }
211
212 pub(crate) fn image_data(&self) -> Option<Image> {
213 self.current_request.borrow().image.clone()
214 }
215
216 pub(crate) fn get_raster_image_data(&self) -> Option<Snapshot> {
218 let Some(raster_image) = self.image_data()?.as_raster_image() else {
219 warn!("Vector image is not supported as raster image source");
220 return None;
221 };
222 Some(raster_image.as_snapshot())
223 }
224}
225
226struct ImageContext {
228 image_cache: Arc<dyn ImageCache>,
230 status: Result<(), NetworkError>,
232 id: PendingImageId,
234 aborted: bool,
236 doc: Trusted<Document>,
238 url: ServoUrl,
239 element: Trusted<HTMLImageElement>,
240}
241
242impl FetchResponseListener for ImageContext {
243 fn should_invoke(&self) -> bool {
244 !self.aborted
245 }
246
247 fn process_request_body(&mut self, _: RequestId) {}
248
249 fn process_response(
250 &mut self,
251 _: &mut js::context::JSContext,
252 request_id: RequestId,
253 metadata: Result<FetchMetadata, NetworkError>,
254 ) {
255 debug!("got {:?} for {:?}", metadata.as_ref().map(|_| ()), self.url);
256 self.image_cache.notify_pending_response(
257 self.id,
258 FetchResponseMsg::ProcessResponse(request_id, metadata.clone()),
259 );
260
261 let metadata = metadata.ok().map(|meta| match meta {
262 FetchMetadata::Unfiltered(m) => m,
263 FetchMetadata::Filtered { unsafe_, .. } => unsafe_,
264 });
265
266 if let Some(metadata) = metadata.as_ref() &&
268 let Some(ref content_type) = metadata.content_type
269 {
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 self.status = match metadata.as_ref().map(|m| m.status.clone()) {
279 None => Err(NetworkError::ResourceLoadError(
280 "No http status code received".to_owned(),
281 )),
282 Some(_) => Ok(()),
283 };
284 }
285
286 fn process_response_chunk(
287 &mut self,
288 _: &mut js::context::JSContext,
289 request_id: RequestId,
290 payload: Vec<u8>,
291 ) {
292 if self.status.is_ok() {
293 self.image_cache.notify_pending_response(
294 self.id,
295 FetchResponseMsg::ProcessResponseChunk(request_id, payload.into()),
296 );
297 }
298 }
299
300 fn process_response_eof(
301 self,
302 cx: &mut js::context::JSContext,
303 request_id: RequestId,
304 response: Result<(), NetworkError>,
305 timing: ResourceFetchTiming,
306 ) {
307 self.image_cache.notify_pending_response(
308 self.id,
309 FetchResponseMsg::ProcessResponseEOF(request_id, response.clone(), timing.clone()),
310 );
311 network_listener::submit_timing(cx, &self, &response, &timing);
312 }
313
314 fn process_csp_violations(
315 &mut self,
316 cx: &mut js::context::JSContext,
317 _request_id: RequestId,
318 violations: Vec<Violation>,
319 ) {
320 let global = &self.resource_timing_global();
321 let elem = self.element.root();
322 let source_position = elem
323 .upcast::<Element>()
324 .compute_source_position(elem.line_number as u32);
325 global.report_csp_violations(cx, violations, None, Some(source_position));
326 }
327
328 fn process_content_length(&mut self, request_id: RequestId, size: usize) {
329 self.image_cache.notify_pending_response(
330 self.id,
331 FetchResponseMsg::ProcessContentLength(request_id, size),
332 );
333 }
334}
335
336impl ResourceTimingListener for ImageContext {
337 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
338 (
339 InitiatorType::LocalName("img".to_string()),
340 self.url.clone(),
341 )
342 }
343
344 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
345 self.doc.root().global()
346 }
347}
348
349#[expect(non_snake_case)]
350impl HTMLImageElement {
351 fn fetch_image(&self, img_url: &ServoUrl, cx: &mut js::context::JSContext) {
353 let window = self.owner_window();
354
355 let cache_result = window.image_cache().get_cached_image_status(
356 img_url.clone(),
357 window.origin().immutable().clone(),
358 cors_setting_for_element(self.upcast()),
359 );
360
361 match cache_result {
362 ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable {
363 image,
364 url,
365 }) => self.process_image_response(ImageResponse::Loaded(image, url), cx),
366 ImageCacheResult::Available(ImageOrMetadataAvailable::MetadataAvailable(
367 metadata,
368 id,
369 )) => {
370 self.process_image_response(ImageResponse::MetadataLoaded(metadata), cx);
371 self.register_image_cache_callback(id, ChangeType::Element);
372 },
373 ImageCacheResult::Pending(id) => {
374 self.register_image_cache_callback(id, ChangeType::Element);
375 },
376 ImageCacheResult::ReadyForRequest(id) => {
377 self.fetch_request(img_url, id);
378 self.register_image_cache_callback(id, ChangeType::Element);
379 },
380 ImageCacheResult::FailedToLoadOrDecode => {
381 self.process_image_response(ImageResponse::FailedToLoadOrDecode, cx)
382 },
383 };
384 }
385
386 fn register_image_cache_callback(&self, id: PendingImageId, change_type: ChangeType) {
387 let trusted_node = Trusted::new(self);
388 let generation = self.generation_id();
389 let window = self.owner_window();
390 let callback = window.register_image_cache_listener(id, move |response, _| {
391 let trusted_node = trusted_node.clone();
392 let window = trusted_node.root().owner_window();
393 let callback_type = change_type.clone();
394
395 window
396 .as_global_scope()
397 .task_manager()
398 .networking_task_source()
399 .queue(task!(process_image_response: move |cx| {
400 let element = trusted_node.root();
401
402 if generation != element.generation_id() {
404 return;
405 }
406
407 match callback_type {
408 ChangeType::Element => {
409 element.process_image_response(response.response, cx);
410 }
411 ChangeType::Environment { selected_source, selected_pixel_density } => {
412 element.process_image_response_for_environment_change(
413 response.response, selected_source, generation, selected_pixel_density, cx
414 );
415 }
416 }
417 }));
418 });
419
420 window.image_cache().add_listener(ImageLoadListener::new(
421 callback,
422 window.pipeline_id(),
423 id,
424 ));
425 }
426
427 fn fetch_request(&self, img_url: &ServoUrl, id: PendingImageId) {
428 let document = self.owner_document();
429 let window = self.owner_window();
430
431 let context = ImageContext {
432 image_cache: window.image_cache(),
433 status: Ok(()),
434 id,
435 aborted: false,
436 doc: Trusted::new(&document),
437 element: Trusted::new(self),
438 url: img_url.clone(),
439 };
440
441 let global = document.global();
444 let mut request = create_a_potential_cors_request(
445 Some(window.webview_id()),
446 img_url.clone(),
447 Destination::Image,
448 cors_setting_for_element(self.upcast()),
449 None,
450 global.get_referrer(),
451 )
452 .with_global_scope(&global)
453 .referrer_policy(referrer_policy_for_element(self.upcast()));
454
455 if self.uses_srcset_or_picture() {
456 request = request.initiator(Initiator::ImageSet);
457 }
458
459 document.fetch_background(request, context);
462 }
463
464 fn handle_loaded_image(&self, image: Image, url: ServoUrl, cx: &mut js::context::JSContext) {
466 self.current_request.borrow_mut().metadata = Some(image.metadata());
467 self.current_request.borrow_mut().final_url = Some(url);
468 self.current_request.borrow_mut().image = Some(image);
469 self.current_request.borrow_mut().state = State::CompletelyAvailable;
470 LoadBlocker::terminate(&self.current_request.borrow().blocker, cx);
471 self.upcast::<Node>().dirty(NodeDamage::Other);
473 self.resolve_image_decode_promises();
474 }
475
476 fn process_image_response(&self, image: ImageResponse, cx: &mut js::context::JSContext) {
478 let (trigger_image_load, trigger_image_error) = match (image, self.image_request.get()) {
484 (ImageResponse::Loaded(image, url), ImageRequestPhase::Current) => {
485 self.handle_loaded_image(image, url, cx);
486 (true, false)
487 },
488 (ImageResponse::Loaded(image, url), ImageRequestPhase::Pending) => {
489 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
490 self.image_request.set(ImageRequestPhase::Current);
491 self.handle_loaded_image(image, url, cx);
492 (true, false)
493 },
494 (ImageResponse::MetadataLoaded(meta), ImageRequestPhase::Current) => {
495 self.current_request.borrow_mut().state = State::PartiallyAvailable;
500 self.current_request.borrow_mut().metadata = Some(meta);
501 (false, false)
502 },
503 (ImageResponse::MetadataLoaded(_), ImageRequestPhase::Pending) => {
504 self.pending_request.borrow_mut().state = State::PartiallyAvailable;
508 (false, false)
509 },
510 (ImageResponse::FailedToLoadOrDecode, ImageRequestPhase::Current) => {
511 self.abort_request(State::Broken, ImageRequestPhase::Current, cx);
517
518 self.load_broken_image_icon();
519
520 (false, true)
524 },
525 (ImageResponse::FailedToLoadOrDecode, ImageRequestPhase::Pending) => {
526 self.abort_request(State::Broken, ImageRequestPhase::Current, cx);
532 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
533
534 mem::swap(
536 &mut *self.current_request.borrow_mut(),
537 &mut *self.pending_request.borrow_mut(),
538 );
539 self.image_request.set(ImageRequestPhase::Current);
540
541 self.current_request.borrow_mut().state = State::Broken;
543
544 self.load_broken_image_icon();
545
546 (false, true)
548 },
549 };
550
551 if trigger_image_load {
553 self.upcast::<EventTarget>().fire_event(cx, atom!("load"));
555 self.upcast::<EventTarget>()
556 .fire_event(cx, atom!("loadend"));
557 }
558
559 if trigger_image_error {
561 self.upcast::<EventTarget>().fire_event(cx, atom!("error"));
562 self.upcast::<EventTarget>()
563 .fire_event(cx, atom!("loadend"));
564 }
565 }
566
567 fn process_image_response_for_environment_change(
570 &self,
571 image: ImageResponse,
572 selected_source: USVString,
573 generation: u32,
574 selected_pixel_density: f64,
575 cx: &mut js::context::JSContext,
576 ) {
577 match image {
578 ImageResponse::Loaded(image, url) => {
579 self.pending_request.borrow_mut().metadata = Some(image.metadata());
580 self.pending_request.borrow_mut().final_url = Some(url);
581 self.pending_request.borrow_mut().image = Some(image);
582 self.finish_reacting_to_environment_change(
583 selected_source,
584 generation,
585 selected_pixel_density,
586 );
587 },
588 ImageResponse::FailedToLoadOrDecode => {
589 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
597 },
598 ImageResponse::MetadataLoaded(meta) => {
599 self.pending_request.borrow_mut().metadata = Some(meta);
600 },
601 };
602 }
603
604 fn abort_request(
606 &self,
607 state: State,
608 request: ImageRequestPhase,
609 cx: &mut js::context::JSContext,
610 ) {
611 let mut request = match request {
612 ImageRequestPhase::Current => self.current_request.borrow_mut(),
613 ImageRequestPhase::Pending => self.pending_request.borrow_mut(),
614 };
615 LoadBlocker::terminate(&request.blocker, cx);
616 request.state = state;
617 request.image = None;
618 request.metadata = None;
619 request.current_pixel_density = None;
620
621 if matches!(state, State::Broken) {
622 self.reject_image_decode_promises();
623 } else if matches!(state, State::CompletelyAvailable) {
624 self.resolve_image_decode_promises();
625 }
626 }
627
628 fn create_source_set(&self) -> SourceSet {
630 let element = self.upcast::<Element>();
631
632 let mut source_set = SourceSet::new();
634
635 if let Some(srcset) = element.get_attribute_string_value(&local_name!("srcset")) {
638 source_set.image_sources = parse_a_srcset_attribute(&srcset);
639 }
640
641 if let Some(sizes) = element.get_attribute_string_value(&local_name!("sizes")) {
643 source_set.source_size = parse_a_sizes_attribute(&sizes);
644 }
645
646 let src = element.get_string_attribute(&local_name!("src"));
650 let no_density_source_of_1 = source_set
651 .image_sources
652 .iter()
653 .all(|source| source.descriptor.density != Some(1.));
654 let no_width_descriptor = source_set
655 .image_sources
656 .iter()
657 .all(|source| source.descriptor.width.is_none());
658 if !src.is_empty() && no_density_source_of_1 && no_width_descriptor {
659 source_set.image_sources.push(ImageSource {
660 url: String::from(src),
661 descriptor: Descriptor {
662 width: None,
663 density: None,
664 },
665 })
666 }
667
668 self.normalise_source_densities(&mut source_set);
670
671 source_set
673 }
674
675 fn update_source_set(&self) {
677 *self.source_set.borrow_mut() = SourceSet::new();
679
680 let elem = self.upcast::<Element>();
685 let parent = elem.upcast::<Node>().GetParentElement();
686 let elements = match parent.as_ref() {
687 Some(p) => {
688 if p.is::<HTMLPictureElement>() {
689 p.upcast::<Node>()
690 .children()
691 .filter_map(DomRoot::downcast::<Element>)
692 .map(|n| DomRoot::from_ref(&*n))
693 .collect()
694 } else {
695 vec![DomRoot::from_ref(elem)]
696 }
697 },
698 None => vec![DomRoot::from_ref(elem)],
699 };
700
701 for element in &elements {
703 if *element == DomRoot::from_ref(elem) {
705 *self.source_set.borrow_mut() = self.create_source_set();
708
709 return;
711 }
712 if !element.is::<HTMLSourceElement>() {
714 continue;
715 }
716
717 let mut source_set = SourceSet::new();
718
719 match element.get_attribute_string_value(&local_name!("srcset")) {
723 Some(srcset) => {
724 source_set.image_sources = parse_a_srcset_attribute(&srcset);
725 },
726 _ => continue,
727 }
728
729 if source_set.image_sources.is_empty() {
731 continue;
732 }
733
734 if let Some(media) = element.get_attribute_string_value(&local_name!("media")) &&
737 !MediaList::matches_environment(&element.owner_document(), &media)
738 {
739 continue;
740 }
741
742 if let Some(sizes) = element.get_attribute_string_value(&local_name!("sizes")) {
745 source_set.source_size = parse_a_sizes_attribute(&sizes);
746 }
747
748 if let Some(type_) = element.get_attribute_string_value(&local_name!("type")) &&
751 !is_supported_image_mime_type(&type_)
752 {
753 continue;
754 }
755
756 if element.has_attribute(&local_name!("width")) ||
759 element.has_attribute(&local_name!("height"))
760 {
761 self.dimension_attribute_source.set(Some(element));
762 } else {
763 self.dimension_attribute_source.set(Some(elem));
764 }
765
766 self.normalise_source_densities(&mut source_set);
768
769 *self.source_set.borrow_mut() = source_set;
771
772 return;
774 }
775 }
776
777 fn evaluate_source_size_list(&self, source_size_list: &SourceSizeList) -> Au {
778 let document = self.owner_document();
779 let quirks_mode = document.quirks_mode();
780 source_size_list.evaluate(document.window().layout().device(), quirks_mode)
781 }
782
783 fn normalise_source_densities(&self, source_set: &mut SourceSet) {
785 let source_size = self.evaluate_source_size_list(&source_set.source_size);
787
788 for image_source in &mut source_set.image_sources {
790 if image_source.descriptor.density.is_some() {
793 continue;
794 }
795
796 if let Some(width) = image_source.descriptor.width {
800 image_source.descriptor.density = Some(width as f64 / source_size.to_f64_px());
801 } else {
802 image_source.descriptor.density = Some(1_f64);
804 }
805 }
806 }
807
808 fn select_image_source(&self) -> Option<(USVString, f64)> {
810 self.update_source_set();
812
813 if self.source_set.borrow().image_sources.is_empty() {
816 return None;
817 }
818
819 self.select_image_source_from_source_set()
821 }
822
823 fn select_image_source_from_source_set(&self) -> Option<(USVString, f64)> {
825 let source_set = self.source_set.borrow();
830 let len = source_set.image_sources.len();
831
832 let mut repeat_indices = FxHashSet::default();
834 for outer_index in 0..len {
835 if repeat_indices.contains(&outer_index) {
836 continue;
837 }
838 let imgsource = &source_set.image_sources[outer_index];
839 let pixel_density = imgsource.descriptor.density.unwrap();
840 for inner_index in (outer_index + 1)..len {
841 let imgsource2 = &source_set.image_sources[inner_index];
842 if pixel_density == imgsource2.descriptor.density.unwrap() {
843 repeat_indices.insert(inner_index);
844 }
845 }
846 }
847
848 let mut max = (0f64, 0);
849 let img_sources = &mut vec![];
850 for (index, image_source) in source_set.image_sources.iter().enumerate() {
851 if repeat_indices.contains(&index) {
852 continue;
853 }
854 let den = image_source.descriptor.density.unwrap();
855 if max.0 < den {
856 max = (den, img_sources.len());
857 }
858 img_sources.push(image_source);
859 }
860
861 let mut best_candidate = max;
864 let device_pixel_ratio = self
865 .owner_document()
866 .window()
867 .viewport_details()
868 .hidpi_scale_factor
869 .get() as f64;
870 for (index, image_source) in img_sources.iter().enumerate() {
871 let current_den = image_source.descriptor.density.unwrap();
872 if current_den < best_candidate.0 && current_den >= device_pixel_ratio {
873 best_candidate = (current_den, index);
874 }
875 }
876 let selected_source = img_sources.remove(best_candidate.1).clone();
877
878 Some((
880 USVString(selected_source.url),
881 selected_source.descriptor.density.unwrap(),
882 ))
883 }
884
885 fn init_image_request(
886 &self,
887 request: &DomRefCell<ImageRequest>,
888 url: &ServoUrl,
889 src: &USVString,
890 cx: &mut js::context::JSContext,
891 ) {
892 {
893 let mut request = request.borrow_mut();
894 request.parsed_url = Some(url.clone());
895 request.source_url = Some(src.clone());
896 request.image = None;
897 request.metadata = None;
898 }
899 let document = self.owner_document();
900 LoadBlocker::terminate(&request.borrow().blocker, cx);
901 *request.borrow_mut().blocker.borrow_mut() =
902 Some(LoadBlocker::new(&document, LoadType::Image(url.clone())));
903 }
904
905 fn prepare_image_request(
907 &self,
908 selected_source: &USVString,
909 selected_pixel_density: f64,
910 image_url: &ServoUrl,
911 cx: &mut js::context::JSContext,
912 ) {
913 match self.image_request.get() {
914 ImageRequestPhase::Pending => {
915 if self
918 .pending_request
919 .borrow()
920 .parsed_url
921 .as_ref()
922 .is_some_and(|parsed_url| *parsed_url == *image_url)
923 {
924 return;
925 }
926 },
927 ImageRequestPhase::Current => {
928 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
930
931 let (current_request_url, current_request_state) = {
933 let current_request = self.current_request.borrow();
934 (current_request.parsed_url.clone(), current_request.state)
935 };
936
937 match (current_request_url, current_request_state) {
938 (Some(parsed_url), State::PartiallyAvailable) => {
939 if parsed_url == *image_url {
945 return;
947 }
948
949 self.image_request.set(ImageRequestPhase::Pending);
953 self.init_image_request(
954 &self.pending_request,
955 image_url,
956 selected_source,
957 cx,
958 );
959 self.pending_request.borrow_mut().current_pixel_density =
960 Some(selected_pixel_density);
961 },
962 (_, State::Broken) | (_, State::Unavailable) => {
963 self.init_image_request(
967 &self.current_request,
968 image_url,
969 selected_source,
970 cx,
971 );
972 self.current_request.borrow_mut().current_pixel_density =
973 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 &self.pending_request,
983 image_url,
984 selected_source,
985 cx,
986 );
987 self.pending_request.borrow_mut().current_pixel_density =
988 Some(selected_pixel_density);
989 },
990 }
991 },
992 }
993
994 self.fetch_image(image_url, cx);
995 }
996
997 fn update_the_image_data_sync_steps(&self, cx: &mut js::context::JSContext) {
999 let Some((selected_source, selected_pixel_density)) = self.select_image_source() else {
1002 self.abort_request(State::Broken, ImageRequestPhase::Current, cx);
1007 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
1008 self.image_request.set(ImageRequestPhase::Current);
1009
1010 let this = Trusted::new(self);
1013
1014 self.owner_global().task_manager().dom_manipulation_task_source().queue(
1015 task!(image_null_source_error: move |cx| {
1016 let this = this.root();
1017
1018 {
1020 let mut current_request =
1021 this.current_request.borrow_mut();
1022 current_request.source_url = None;
1023 current_request.parsed_url = None;
1024 }
1025
1026 let has_src_attribute = this.upcast::<Element>().has_attribute(&local_name!("src"));
1032
1033 if has_src_attribute || this.uses_srcset_or_picture() {
1034 this.upcast::<EventTarget>().fire_event(cx, atom!("error"));
1035 }
1036 }));
1037
1038 return;
1040 };
1041
1042 let Ok(image_url) = self.owner_document().base_url().join(&selected_source) else {
1045 self.abort_request(State::Broken, ImageRequestPhase::Current, cx);
1050 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
1051
1052 self.image_request.set(ImageRequestPhase::Current);
1054
1055 let this = Trusted::new(self);
1058
1059 self.owner_global()
1060 .task_manager()
1061 .dom_manipulation_task_source()
1062 .queue(task!(image_selected_source_error: move |cx| {
1063 let this = this.root();
1064
1065 {
1067 let mut current_request =
1068 this.current_request.borrow_mut();
1069 current_request.source_url = Some(selected_source);
1070 current_request.parsed_url = None;
1071 }
1072
1073 this.upcast::<EventTarget>().fire_event(cx, atom!("error"));
1077 }));
1078
1079 return;
1081 };
1082
1083 self.prepare_image_request(&selected_source, selected_pixel_density, &image_url, cx);
1084 }
1085
1086 pub(crate) fn update_the_image_data(&self, cx: &mut js::context::JSContext) {
1088 self.generation.set(self.generation.get() + 1);
1090
1091 if !self.owner_document().is_active() {
1093 }
1099
1100 self.current_request.borrow_mut().state = State::Unavailable;
1109
1110 let mut selected_source = None;
1114 let mut selected_pixel_density = None;
1115
1116 let src = self
1120 .upcast::<Element>()
1121 .get_string_attribute(&local_name!("src"));
1122
1123 if !self.uses_srcset_or_picture() && !src.is_empty() {
1124 selected_source = Some(USVString(String::from(src)));
1125 selected_pixel_density = Some(1_f64);
1126 };
1127
1128 self.last_selected_source
1130 .borrow_mut()
1131 .clone_from(&selected_source);
1132
1133 if let Some(selected_source) = selected_source {
1135 if let Ok(image_url) = self.owner_document().base_url().join(&selected_source) {
1139 let window = self.owner_window();
1143 let response = window.image_cache().get_image(
1144 image_url.clone(),
1145 window.origin().immutable().clone(),
1146 cors_setting_for_element(self.upcast()),
1147 );
1148
1149 if let Some(image) = response {
1151 self.abort_request(State::CompletelyAvailable, ImageRequestPhase::Current, cx);
1156 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
1157
1158 self.image_request.set(ImageRequestPhase::Current);
1160
1161 let mut current_request = self.current_request.borrow_mut();
1164 current_request.metadata = Some(image.metadata());
1165 current_request.image = Some(image);
1166 current_request.final_url = Some(image_url.clone());
1167
1168 self.upcast::<Node>().dirty(NodeDamage::Other);
1171
1172 current_request.current_pixel_density = selected_pixel_density;
1175
1176 let this = Trusted::new(self);
1179
1180 self.owner_global()
1181 .task_manager()
1182 .dom_manipulation_task_source()
1183 .queue(task!(image_load_event: move |cx| {
1184 let this = this.root();
1185
1186 {
1191 let mut current_request =
1192 this.current_request.borrow_mut();
1193 current_request.source_url = Some(selected_source);
1194 current_request.parsed_url = Some(image_url);
1195 }
1196
1197 this.upcast::<EventTarget>().fire_event(cx, atom!("load"));
1201 }));
1202
1203 return;
1205 }
1206 }
1207 }
1208
1209 let task = ImageElementMicrotask::UpdateImageData {
1212 elem: Dom::from_ref(self),
1213 generation: self.generation.get(),
1214 };
1215
1216 ScriptThread::await_stable_state(cx, Box::new(task));
1217 }
1218
1219 pub(crate) fn react_to_environment_changes(&self, cx: &JSContext) {
1221 let task = ImageElementMicrotask::EnvironmentChanges {
1223 elem: Dom::from_ref(self),
1224 generation: self.generation.get(),
1225 };
1226
1227 ScriptThread::await_stable_state(cx, Box::new(task));
1228 }
1229
1230 fn react_to_environment_changes_sync_steps(
1232 &self,
1233 generation: u32,
1234 cx: &mut js::context::JSContext,
1235 ) {
1236 let document = self.owner_document();
1237 let has_pending_request = matches!(self.image_request.get(), ImageRequestPhase::Pending);
1238
1239 if !document.is_active() || !self.uses_srcset_or_picture() || has_pending_request {
1243 return;
1244 }
1245
1246 let Some((selected_source, selected_pixel_density)) = self.select_image_source() else {
1249 return;
1251 };
1252
1253 let mut same_selected_source = self
1256 .last_selected_source
1257 .borrow()
1258 .as_ref()
1259 .is_some_and(|source| *source == selected_source);
1260
1261 same_selected_source = same_selected_source ||
1265 self.current_request
1266 .borrow()
1267 .source_url
1268 .as_ref()
1269 .is_some_and(|source| *source == selected_source);
1270
1271 let same_selected_pixel_density = self
1272 .current_request
1273 .borrow()
1274 .current_pixel_density
1275 .is_some_and(|pixel_density| pixel_density == selected_pixel_density);
1276
1277 if same_selected_source && same_selected_pixel_density {
1278 return;
1279 }
1280
1281 let Ok(image_url) = document.base_url().join(&selected_source) else {
1285 return;
1286 };
1287
1288 self.image_request.set(ImageRequestPhase::Pending);
1290 self.init_image_request(&self.pending_request, &image_url, &selected_source, cx);
1291
1292 let window = self.owner_window();
1295 let cache_result = window.image_cache().get_cached_image_status(
1296 image_url.clone(),
1297 window.origin().immutable().clone(),
1298 cors_setting_for_element(self.upcast()),
1299 );
1300
1301 let change_type = ChangeType::Environment {
1302 selected_source: selected_source.clone(),
1303 selected_pixel_density,
1304 };
1305
1306 match cache_result {
1307 ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable { .. }) => {
1308 self.finish_reacting_to_environment_change(
1309 selected_source,
1310 generation,
1311 selected_pixel_density,
1312 );
1313 },
1314 ImageCacheResult::Available(ImageOrMetadataAvailable::MetadataAvailable(m, id)) => {
1315 self.process_image_response_for_environment_change(
1316 ImageResponse::MetadataLoaded(m),
1317 selected_source,
1318 generation,
1319 selected_pixel_density,
1320 cx,
1321 );
1322 self.register_image_cache_callback(id, change_type);
1323 },
1324 ImageCacheResult::FailedToLoadOrDecode => {
1325 self.process_image_response_for_environment_change(
1326 ImageResponse::FailedToLoadOrDecode,
1327 selected_source,
1328 generation,
1329 selected_pixel_density,
1330 cx,
1331 );
1332 },
1333 ImageCacheResult::ReadyForRequest(id) => {
1334 self.fetch_request(&image_url, id);
1335 self.register_image_cache_callback(id, change_type);
1336 },
1337 ImageCacheResult::Pending(id) => {
1338 self.register_image_cache_callback(id, change_type);
1339 },
1340 }
1341 }
1342
1343 fn react_to_decode_image_sync_steps(&self, cx: &mut JSContext, promise: Rc<Promise>) {
1345 if !self.owner_document().is_fully_active() ||
1349 matches!(self.current_request.borrow().state, State::Broken)
1350 {
1351 promise.reject_error(cx, Error::Encoding(None));
1352 } else if matches!(
1353 self.current_request.borrow().state,
1354 State::CompletelyAvailable
1355 ) {
1356 promise.resolve_native(cx, &());
1358 } else if matches!(self.current_request.borrow().state, State::Unavailable) &&
1359 self.current_request.borrow().source_url.is_none()
1360 {
1361 promise.reject_error(cx, Error::Encoding(None));
1366 } else {
1367 self.image_decode_promises.borrow_mut().push(promise);
1368 }
1369 }
1370
1371 fn resolve_image_decode_promises(&self) {
1373 if self.image_decode_promises.borrow().is_empty() {
1374 return;
1375 }
1376
1377 let trusted_image_decode_promises: Vec<TrustedPromise> = self
1380 .image_decode_promises
1381 .borrow()
1382 .iter()
1383 .map(|promise| TrustedPromise::new(promise.clone()))
1384 .collect();
1385
1386 self.image_decode_promises.borrow_mut().clear();
1387
1388 self.owner_global()
1389 .task_manager()
1390 .dom_manipulation_task_source()
1391 .queue(task!(fulfill_image_decode_promises: move |cx| {
1392 for trusted_promise in trusted_image_decode_promises {
1393 trusted_promise.root().resolve_native(cx, &());
1394 }
1395 }));
1396 }
1397
1398 fn reject_image_decode_promises(&self) {
1400 if self.image_decode_promises.borrow().is_empty() {
1401 return;
1402 }
1403
1404 let trusted_image_decode_promises: Vec<TrustedPromise> = self
1407 .image_decode_promises
1408 .borrow()
1409 .iter()
1410 .map(|promise| TrustedPromise::new(promise.clone()))
1411 .collect();
1412
1413 self.image_decode_promises.borrow_mut().clear();
1414
1415 self.owner_global()
1416 .task_manager()
1417 .dom_manipulation_task_source()
1418 .queue(task!(reject_image_decode_promises: move |cx| {
1419 for trusted_promise in trusted_image_decode_promises {
1420 trusted_promise.root().reject_error(cx, Error::Encoding(None));
1421 }
1422 }));
1423 }
1424
1425 fn finish_reacting_to_environment_change(
1427 &self,
1428 selected_source: USVString,
1429 generation: u32,
1430 selected_pixel_density: f64,
1431 ) {
1432 let this = Trusted::new(self);
1435
1436 self.owner_global()
1437 .task_manager()
1438 .dom_manipulation_task_source()
1439 .queue(task!(image_load_event: move |cx| {
1440 let this = this.root();
1441
1442 if this.generation.get() != generation {
1445 this.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
1446 this.image_request.set(ImageRequestPhase::Current);
1447 return;
1448 }
1449
1450 *this.last_selected_source.borrow_mut() = Some(selected_source);
1453
1454 {
1455 let mut pending_request = this.pending_request.borrow_mut();
1456
1457 pending_request.state = State::CompletelyAvailable;
1459
1460 pending_request.current_pixel_density = Some(selected_pixel_density);
1461
1462 mem::swap(&mut *this.current_request.borrow_mut(), &mut *pending_request);
1468 }
1469
1470 this.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
1471 this.image_request.set(ImageRequestPhase::Current);
1472
1473 this.upcast::<Node>().dirty(NodeDamage::Other);
1475
1476 this.upcast::<EventTarget>().fire_event(cx, atom!("load"));
1478 }));
1479 }
1480
1481 fn uses_srcset_or_picture(&self) -> bool {
1483 let element = self.upcast::<Element>();
1484
1485 let has_srcset_attribute = element.has_attribute(&local_name!("srcset"));
1486 let has_parent_picture = element
1487 .upcast::<Node>()
1488 .GetParentElement()
1489 .is_some_and(|parent| parent.is::<HTMLPictureElement>());
1490 has_srcset_attribute || has_parent_picture
1491 }
1492
1493 fn new_inherited(
1494 local_name: LocalName,
1495 prefix: Option<Prefix>,
1496 document: &Document,
1497 creator: ElementCreator,
1498 ) -> HTMLImageElement {
1499 HTMLImageElement {
1500 htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
1501 image_request: Cell::new(ImageRequestPhase::Current),
1502 current_request: DomRefCell::new(ImageRequest {
1503 state: State::Unavailable,
1504 parsed_url: None,
1505 source_url: None,
1506 image: None,
1507 metadata: None,
1508 blocker: DomRefCell::new(None),
1509 final_url: None,
1510 current_pixel_density: None,
1511 }),
1512 pending_request: DomRefCell::new(ImageRequest {
1513 state: State::Unavailable,
1514 parsed_url: None,
1515 source_url: None,
1516 image: None,
1517 metadata: None,
1518 blocker: DomRefCell::new(None),
1519 final_url: None,
1520 current_pixel_density: None,
1521 }),
1522 form_owner: Default::default(),
1523 generation: Default::default(),
1524 source_set: DomRefCell::new(SourceSet::new()),
1525 dimension_attribute_source: Default::default(),
1526 last_selected_source: DomRefCell::new(None),
1527 image_decode_promises: DomRefCell::new(vec![]),
1528 line_number: creator.return_line_number(),
1529 }
1530 }
1531
1532 pub(crate) fn new(
1533 cx: &mut js::context::JSContext,
1534 local_name: LocalName,
1535 prefix: Option<Prefix>,
1536 document: &Document,
1537 proto: Option<HandleObject>,
1538 creator: ElementCreator,
1539 ) -> DomRoot<HTMLImageElement> {
1540 let image_element = Node::reflect_node_with_proto(
1541 cx,
1542 Box::new(HTMLImageElement::new_inherited(
1543 local_name, prefix, document, creator,
1544 )),
1545 document,
1546 proto,
1547 );
1548 image_element
1549 .dimension_attribute_source
1550 .set(Some(image_element.upcast()));
1551 image_element
1552 }
1553
1554 pub(crate) fn areas(&self) -> Option<Vec<DomRoot<HTMLAreaElement>>> {
1555 let elem = self.upcast::<Element>();
1556 let value = elem.get_attribute_string_value(&local_name!("usemap"))?;
1557
1558 if value.is_empty() || !value.is_char_boundary(1) {
1559 return None;
1560 }
1561
1562 let (first, last) = value.split_at(1);
1563
1564 if first != "#" || last.is_empty() {
1565 return None;
1566 }
1567
1568 let useMapElements = self
1569 .owner_document()
1570 .upcast::<Node>()
1571 .traverse_preorder(ShadowIncluding::No)
1572 .filter_map(DomRoot::downcast::<HTMLMapElement>)
1573 .find(|n| {
1574 n.upcast::<Element>()
1575 .get_name()
1576 .is_some_and(|n| *n == *last)
1577 });
1578
1579 useMapElements.map(|mapElem| mapElem.get_area_elements())
1580 }
1581
1582 pub(crate) fn same_origin(&self, origin: &MutableOrigin) -> bool {
1583 if let Some(ref image) = self.current_request.borrow().image {
1584 return image.cors_status() == CorsStatus::Safe;
1585 }
1586
1587 self.current_request
1588 .borrow()
1589 .final_url
1590 .as_ref()
1591 .is_some_and(|url| url.scheme() == "data" || url.origin().same_origin(origin))
1592 }
1593
1594 fn generation_id(&self) -> u32 {
1595 self.generation.get()
1596 }
1597
1598 fn load_broken_image_icon(&self) {
1599 let window = self.owner_window();
1600 let Some(broken_image_icon) = window.image_cache().get_broken_image_icon() else {
1601 return;
1602 };
1603
1604 self.current_request.borrow_mut().metadata = Some(broken_image_icon.metadata);
1605 self.current_request.borrow_mut().image = Some(Image::Raster(broken_image_icon));
1606 self.upcast::<Node>().dirty(NodeDamage::Other);
1607 }
1608
1609 pub(crate) fn full_image_url_for_user_interface(&self) -> Option<ServoUrl> {
1612 self.owner_document()
1613 .base_url()
1614 .join(&self.CurrentSrc())
1615 .ok()
1616 }
1617}
1618
1619#[derive(JSTraceable, MallocSizeOf)]
1620pub(crate) enum ImageElementMicrotask {
1621 UpdateImageData {
1622 elem: Dom<HTMLImageElement>,
1623 generation: u32,
1624 },
1625 EnvironmentChanges {
1626 elem: Dom<HTMLImageElement>,
1627 generation: u32,
1628 },
1629 Decode {
1630 elem: Dom<HTMLImageElement>,
1631 #[conditional_malloc_size_of]
1632 promise: Rc<Promise>,
1633 },
1634}
1635
1636impl MicrotaskRunnable for ImageElementMicrotask {
1637 fn handler(&self, cx: &mut js::context::JSContext) {
1638 let mut realm = match self {
1639 &ImageElementMicrotask::UpdateImageData { ref elem, .. } |
1640 &ImageElementMicrotask::EnvironmentChanges { ref elem, .. } |
1641 &ImageElementMicrotask::Decode { ref elem, .. } => enter_auto_realm(cx, &**elem),
1642 };
1643 let cx = &mut realm;
1644 match *self {
1645 ImageElementMicrotask::UpdateImageData {
1646 ref elem,
1647 ref generation,
1648 } => {
1649 if elem.generation.get() == *generation {
1653 elem.update_the_image_data_sync_steps(cx);
1654 }
1655 },
1656 ImageElementMicrotask::EnvironmentChanges {
1657 ref elem,
1658 ref generation,
1659 } => {
1660 elem.react_to_environment_changes_sync_steps(*generation, cx);
1661 },
1662 ImageElementMicrotask::Decode {
1663 ref elem,
1664 ref promise,
1665 } => {
1666 elem.react_to_decode_image_sync_steps(cx, promise.clone());
1667 },
1668 }
1669 }
1670}
1671
1672impl<'dom> LayoutDom<'dom, HTMLImageElement> {
1673 #[expect(unsafe_code)]
1674 fn current_request(self) -> &'dom ImageRequest {
1675 unsafe { self.unsafe_get().current_request.borrow_for_layout() }
1676 }
1677
1678 #[expect(unsafe_code)]
1679 fn dimension_attribute_source(self) -> LayoutDom<'dom, Element> {
1680 unsafe {
1681 self.unsafe_get()
1682 .dimension_attribute_source
1683 .to_layout()
1684 .expect("dimension attribute source should be always non-null")
1685 }
1686 }
1687
1688 pub(crate) fn image_url(self) -> Option<ServoUrl> {
1689 self.current_request().parsed_url.clone()
1690 }
1691
1692 pub(crate) fn image_data(self) -> (Option<Image>, Option<ImageMetadata>) {
1693 let current_request = self.current_request();
1694 (current_request.image.clone(), current_request.metadata)
1695 }
1696
1697 pub(crate) fn image_density(self) -> Option<f64> {
1698 self.current_request().current_pixel_density
1699 }
1700
1701 pub(crate) fn showing_broken_image_icon(self) -> bool {
1702 matches!(self.current_request().state, State::Broken)
1703 }
1704
1705 pub(crate) fn get_width(self) -> LengthOrPercentageOrAuto {
1706 self.dimension_attribute_source()
1707 .get_attr_for_layout(&ns!(), &local_name!("width"))
1708 .map(AttrValue::as_dimension)
1709 .cloned()
1710 .unwrap_or(LengthOrPercentageOrAuto::Auto)
1711 }
1712
1713 pub(crate) fn get_height(self) -> LengthOrPercentageOrAuto {
1714 self.dimension_attribute_source()
1715 .get_attr_for_layout(&ns!(), &local_name!("height"))
1716 .map(AttrValue::as_dimension)
1717 .cloned()
1718 .unwrap_or(LengthOrPercentageOrAuto::Auto)
1719 }
1720}
1721
1722fn parse_a_sizes_attribute(value: &str) -> SourceSizeList {
1724 let mut input = ParserInput::new(value);
1725 let mut parser = Parser::new(&mut input);
1726 let url_data = Url::parse("about:blank").unwrap().into();
1727 let context =
1730 parser_context_for_anonymous_content(CssRuleType::Style, ParsingMode::empty(), &url_data);
1731 SourceSizeList::parse(&context, &mut parser)
1732}
1733
1734impl HTMLImageElementMethods<crate::DomTypeHolder> for HTMLImageElement {
1735 fn Image(
1737 cx: &mut JSContext,
1738 window: &Window,
1739 proto: Option<HandleObject>,
1740 width: Option<u32>,
1741 height: Option<u32>,
1742 ) -> Fallible<DomRoot<HTMLImageElement>> {
1743 let document = window.Document();
1745
1746 let element = Element::create(
1749 cx,
1750 QualName::new(None, ns!(html), local_name!("img")),
1751 None,
1752 &document,
1753 ElementCreator::ScriptCreated,
1754 CustomElementCreationMode::Synchronous,
1755 proto,
1756 );
1757
1758 let image = DomRoot::downcast::<HTMLImageElement>(element).unwrap();
1759
1760 if let Some(w) = width {
1762 image.SetWidth(cx, w);
1763 }
1764
1765 if let Some(h) = height {
1768 image.SetHeight(cx, h);
1769 }
1770
1771 Ok(image)
1773 }
1774
1775 make_getter!(Alt, "alt");
1777 make_setter!(SetAlt, "alt");
1779
1780 make_url_getter!(Src, "src");
1782
1783 make_url_setter!(SetSrc, "src");
1785
1786 make_url_getter!(Srcset, "srcset");
1788 make_url_setter!(SetSrcset, "srcset");
1790
1791 make_getter!(Sizes, "sizes");
1793
1794 make_setter!(SetSizes, "sizes");
1796
1797 fn GetCrossOrigin(&self) -> Option<DOMString> {
1799 reflect_cross_origin_attribute(self.upcast::<Element>())
1800 }
1801
1802 fn SetCrossOrigin(&self, cx: &mut JSContext, value: Option<DOMString>) {
1804 set_cross_origin_attribute(cx, self.upcast::<Element>(), value);
1805 }
1806
1807 make_getter!(UseMap, "usemap");
1809 make_setter!(SetUseMap, "usemap");
1811
1812 make_bool_getter!(IsMap, "ismap");
1814 make_bool_setter!(SetIsMap, "ismap");
1816
1817 fn Width(&self) -> u32 {
1819 let node = self.upcast::<Node>();
1820 node.content_box()
1821 .map(|rect| rect.size.width.to_px() as u32)
1822 .unwrap_or_else(|| self.NaturalWidth())
1823 }
1824
1825 make_dimension_uint_setter!(SetWidth, "width");
1827
1828 fn Height(&self) -> u32 {
1830 let node = self.upcast::<Node>();
1831 node.content_box()
1832 .map(|rect| rect.size.height.to_px() as u32)
1833 .unwrap_or_else(|| self.NaturalHeight())
1834 }
1835
1836 make_dimension_uint_setter!(SetHeight, "height");
1838
1839 fn NaturalWidth(&self) -> u32 {
1841 let request = self.current_request.borrow();
1842 if matches!(request.state, State::Broken) {
1843 return 0;
1844 }
1845
1846 let pixel_density = request.current_pixel_density.unwrap_or(1f64);
1847 match request.metadata {
1848 Some(ref metadata) => (metadata.width as f64 / pixel_density) as u32,
1849 None => 0,
1850 }
1851 }
1852
1853 fn NaturalHeight(&self) -> u32 {
1855 let request = self.current_request.borrow();
1856 if matches!(request.state, State::Broken) {
1857 return 0;
1858 }
1859
1860 let pixel_density = request.current_pixel_density.unwrap_or(1f64);
1861 match request.metadata {
1862 Some(ref metadata) => (metadata.height as f64 / pixel_density) as u32,
1863 None => 0,
1864 }
1865 }
1866
1867 fn Complete(&self) -> bool {
1869 let element = self.upcast::<Element>();
1870
1871 let has_srcset_attribute = element.has_attribute(&local_name!("srcset"));
1874 if !element.has_attribute(&local_name!("src")) && !has_srcset_attribute {
1875 return true;
1876 }
1877
1878 let src = element.get_string_attribute(&local_name!("src"));
1880 if !has_srcset_attribute && src.is_empty() {
1881 return true;
1882 }
1883
1884 if matches!(self.image_request.get(), ImageRequestPhase::Current) &&
1888 matches!(
1889 self.current_request.borrow().state,
1890 State::CompletelyAvailable | State::Broken
1891 )
1892 {
1893 return true;
1894 }
1895
1896 false
1898 }
1899
1900 fn CurrentSrc(&self) -> USVString {
1902 let current_request = self.current_request.borrow();
1903 let url = ¤t_request.parsed_url;
1904 match *url {
1905 Some(ref url) => USVString(url.clone().into_string()),
1906 None => {
1907 let unparsed_url = ¤t_request.source_url;
1908 match *unparsed_url {
1909 Some(ref url) => url.clone(),
1910 None => USVString("".to_owned()),
1911 }
1912 },
1913 }
1914 }
1915
1916 fn ReferrerPolicy(&self) -> DOMString {
1918 reflect_referrer_policy_attribute(self.upcast::<Element>())
1919 }
1920
1921 make_setter!(SetReferrerPolicy, "referrerpolicy");
1923
1924 fn Decode(&self, cx: &mut JSContext) -> Rc<Promise> {
1926 let promise = Promise::new(cx, &self.global());
1928
1929 let task = ImageElementMicrotask::Decode {
1931 elem: Dom::from_ref(self),
1932 promise: promise.clone(),
1933 };
1934
1935 ScriptThread::await_stable_state(cx, Box::new(task));
1936
1937 promise
1939 }
1940
1941 make_getter!(Name, "name");
1943
1944 make_atomic_setter!(SetName, "name");
1946
1947 make_getter!(Align, "align");
1949
1950 make_setter!(SetAlign, "align");
1952
1953 make_uint_getter!(Hspace, "hspace");
1955
1956 make_uint_setter!(SetHspace, "hspace");
1958
1959 make_uint_getter!(Vspace, "vspace");
1961
1962 make_uint_setter!(SetVspace, "vspace");
1964
1965 make_url_getter!(LongDesc, "longdesc");
1967
1968 make_url_setter!(SetLongDesc, "longdesc");
1970
1971 make_getter!(Border, "border");
1973
1974 make_setter!(SetBorder, "border");
1976}
1977
1978impl VirtualMethods for HTMLImageElement {
1979 fn super_type(&self) -> Option<&dyn VirtualMethods> {
1980 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
1981 }
1982
1983 fn adopting_steps(&self, cx: &mut JSContext, old_doc: &Document) {
1984 self.super_type().unwrap().adopting_steps(cx, old_doc);
1985 self.update_the_image_data(cx);
1986 }
1987
1988 fn attribute_mutated(
1989 &self,
1990 cx: &mut js::context::JSContext,
1991 attr: AttrRef<'_>,
1992 mutation: AttributeMutation,
1993 ) {
1994 self.super_type()
1995 .unwrap()
1996 .attribute_mutated(cx, attr, mutation);
1997 match attr.local_name() {
1998 &local_name!("src") |
1999 &local_name!("srcset") |
2000 &local_name!("width") |
2001 &local_name!("sizes") => {
2002 self.update_the_image_data(cx);
2006 },
2007 &local_name!("crossorigin") => {
2008 let cross_origin_state_changed = match mutation {
2011 AttributeMutation::Removed | AttributeMutation::Set(None, _) => true,
2012 AttributeMutation::Set(Some(old_value), _) => {
2013 let new_cors_setting =
2014 CorsSettings::from_enumerated_attribute(&attr.value());
2015 let old_cors_setting = CorsSettings::from_enumerated_attribute(old_value);
2016
2017 new_cors_setting != old_cors_setting
2018 },
2019 };
2020
2021 if cross_origin_state_changed {
2022 self.update_the_image_data(cx);
2023 }
2024 },
2025 &local_name!("referrerpolicy") => {
2026 let referrer_policy_state_changed = match mutation {
2029 AttributeMutation::Removed | AttributeMutation::Set(None, _) => {
2030 ReferrerPolicy::from(&**attr.value()) != ReferrerPolicy::EmptyString
2031 },
2032 AttributeMutation::Set(Some(old_value), _) => {
2033 ReferrerPolicy::from(&**attr.value()) != ReferrerPolicy::from(&**old_value)
2034 },
2035 };
2036
2037 if referrer_policy_state_changed {
2038 self.update_the_image_data(cx);
2039 }
2040 },
2041 _ => {},
2042 }
2043 }
2044
2045 fn attribute_affects_presentational_hints(&self, attr: AttrRef<'_>) -> bool {
2046 match attr.local_name() {
2047 &local_name!("width") | &local_name!("height") => true,
2048 _ => self
2049 .super_type()
2050 .unwrap()
2051 .attribute_affects_presentational_hints(attr),
2052 }
2053 }
2054
2055 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
2056 match name {
2057 &local_name!("width") | &local_name!("height") => {
2058 AttrValue::from_dimension(value.into())
2059 },
2060 &local_name!("hspace") | &local_name!("vspace") => AttrValue::from_u32(value.into(), 0),
2061 _ => self
2062 .super_type()
2063 .unwrap()
2064 .parse_plain_attribute(name, value),
2065 }
2066 }
2067
2068 fn handle_event(&self, cx: &mut js::context::JSContext, event: &Event) {
2069 if event.type_() != atom!("click") {
2070 return;
2071 }
2072
2073 let Some(area_elements) = self.areas() else {
2074 return;
2075 };
2076
2077 let Some(mouse_event) = event.downcast::<MouseEvent>() else {
2079 return;
2080 };
2081
2082 let click_location = Point2D::new(
2083 mouse_event.ClientX().to_f32().unwrap(),
2084 mouse_event.ClientY().to_f32().unwrap(),
2085 );
2086 let bounding_rectangle = self.upcast::<Element>().GetBoundingClientRect(cx);
2087 let image_extents =
2088 Point2D::new(bounding_rectangle.X() as f32, bounding_rectangle.Y() as f32);
2089
2090 for area_element in area_elements {
2092 if !area_element.is_instance_activatable() {
2093 continue;
2094 }
2095 let activatable_area = match area_element.get_shape_from_coords() {
2096 Some(shape) => shape.absolute_coords(image_extents),
2097 None => return,
2098 };
2099 if activatable_area.hit_test(&click_location) {
2100 area_element.activation_behavior(cx, event, self.upcast());
2101 return;
2102 }
2103 }
2104 }
2105
2106 fn bind_to_tree(&self, cx: &mut JSContext, context: &BindContext) {
2108 if let Some(s) = self.super_type() {
2109 s.bind_to_tree(cx, context);
2110 }
2111 let document = self.owner_document();
2112 if context.tree_connected {
2113 document.register_responsive_image(self);
2114 }
2115
2116 let parent = self.upcast::<Node>().GetParentNode().unwrap();
2117
2118 if parent.is::<HTMLPictureElement>() && *parent == *context.parent {
2121 self.update_the_image_data(cx);
2122 }
2123 }
2124
2125 fn unbind_from_tree(&self, cx: &mut js::context::JSContext, context: &UnbindContext) {
2127 self.super_type().unwrap().unbind_from_tree(cx, context);
2128 let document = self.owner_document();
2129 document.unregister_responsive_image(self);
2130
2131 if context.parent.is::<HTMLPictureElement>() && !self.upcast::<Node>().has_parent() {
2134 self.update_the_image_data(cx);
2135 }
2136 }
2137
2138 fn moving_steps(&self, cx: &mut JSContext, context: &MoveContext) {
2140 if let Some(super_type) = self.super_type() {
2141 super_type.moving_steps(cx, context);
2142 }
2143
2144 if let Some(old_parent) = context.old_parent &&
2146 old_parent.is::<HTMLPictureElement>()
2147 {
2148 self.update_the_image_data(cx);
2149 }
2150 }
2151}
2152
2153impl FormControl for HTMLImageElement {
2154 fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
2155 self.form_owner.get()
2156 }
2157
2158 fn set_form_owner(&self, _cx: &mut JSContext, form: Option<&HTMLFormElement>) {
2159 self.form_owner.set(form);
2160 }
2161
2162 fn to_html_element(&self) -> &HTMLElement {
2163 self.upcast::<HTMLElement>()
2164 }
2165}
2166
2167pub(crate) fn collect_sequence_characters(
2170 s: &str,
2171 mut predicate: impl FnMut(&char) -> bool,
2172) -> (&str, &str) {
2173 let i = s.find(|ch| !predicate(&ch)).unwrap_or(s.len());
2174 (&s[0..i], &s[i..])
2175}
2176
2177fn is_valid_non_negative_integer_string(s: &str) -> bool {
2180 s.chars().all(|c| c.is_ascii_digit())
2181}
2182
2183fn is_valid_floating_point_number_string(s: &str) -> bool {
2186 static RE: LazyLock<Regex> =
2187 LazyLock::new(|| Regex::new(r"^-?(?:\d+\.\d+|\d+|\.\d+)(?:(e|E)(\+|\-)?\d+)?$").unwrap());
2188
2189 RE.is_match(s)
2190}
2191
2192pub fn parse_a_srcset_attribute(input: &str) -> Vec<ImageSource> {
2195 let mut current_index = 0;
2198
2199 let mut candidates = vec![];
2201 while current_index < input.len() {
2202 let remaining_string = &input[current_index..];
2203
2204 let (collected_characters, string_after_whitespace) =
2211 collect_sequence_characters(remaining_string, |character| {
2212 *character == ',' || character.is_ascii_whitespace()
2213 });
2214
2215 current_index += collected_characters.len();
2218
2219 if string_after_whitespace.is_empty() {
2221 return candidates;
2222 }
2223
2224 let (url, _) =
2227 collect_sequence_characters(string_after_whitespace, |c| !char::is_ascii_whitespace(c));
2228
2229 current_index += url.len();
2232
2233 let mut descriptors = Vec::new();
2235
2236 if url.ends_with(',') {
2240 let image_source = ImageSource {
2241 url: url.trim_end_matches(',').into(),
2242 descriptor: Descriptor {
2243 width: None,
2244 density: None,
2245 },
2246 };
2247 candidates.push(image_source);
2248 continue;
2249 }
2250
2251 let descriptors_string = &input[current_index..];
2254 let (spaces, descriptors_string) =
2255 collect_sequence_characters(descriptors_string, |character| {
2256 character.is_ascii_whitespace()
2257 });
2258 current_index += spaces.len();
2259
2260 let mut current_descriptor = String::new();
2262
2263 let mut state = ParseState::InDescriptor;
2265
2266 let mut characters = descriptors_string.chars();
2270 let mut character = characters.next();
2271 if let Some(character) = character {
2272 current_index += character.len_utf8();
2273 }
2274
2275 loop {
2276 match (state, character) {
2277 (ParseState::InDescriptor, Some(character)) if character.is_ascii_whitespace() => {
2278 if !current_descriptor.is_empty() {
2282 descriptors.push(current_descriptor);
2283 current_descriptor = String::new();
2284 state = ParseState::AfterDescriptor;
2285 }
2286 },
2287 (ParseState::InDescriptor, Some(',')) => {
2288 if !current_descriptor.is_empty() {
2292 descriptors.push(current_descriptor);
2293 }
2294 break;
2295 },
2296 (ParseState::InDescriptor, Some('(')) => {
2297 current_descriptor.push('(');
2299 state = ParseState::InParens;
2300 },
2301 (ParseState::InDescriptor, Some(character)) => {
2302 current_descriptor.push(character);
2304 },
2305 (ParseState::InDescriptor, None) => {
2306 if !current_descriptor.is_empty() {
2309 descriptors.push(current_descriptor);
2310 }
2311 break;
2312 },
2313 (ParseState::InParens, Some(')')) => {
2314 current_descriptor.push(')');
2316 state = ParseState::InDescriptor;
2317 },
2318 (ParseState::InParens, Some(character)) => {
2319 current_descriptor.push(character);
2321 },
2322 (ParseState::InParens, None) => {
2323 descriptors.push(current_descriptor);
2326 break;
2327 },
2328 (ParseState::AfterDescriptor, Some(character))
2329 if character.is_ascii_whitespace() =>
2330 {
2331 },
2333 (ParseState::AfterDescriptor, Some(_)) => {
2334 state = ParseState::InDescriptor;
2337 continue;
2338 },
2339 (ParseState::AfterDescriptor, None) => {
2340 break;
2342 },
2343 }
2344
2345 character = characters.next();
2346 if let Some(character) = character {
2347 current_index += character.len_utf8();
2348 }
2349 }
2350
2351 let mut error = false;
2353 let mut width: Option<u32> = None;
2355 let mut density: Option<f64> = None;
2357 let mut future_compat_h: Option<u32> = None;
2359
2360 for descriptor in descriptors.into_iter() {
2363 let Some(last_character) = descriptor.chars().last() else {
2364 break;
2365 };
2366
2367 let first_part_of_string = &descriptor[0..descriptor.len() - last_character.len_utf8()];
2368 match last_character {
2369 'w' if is_valid_non_negative_integer_string(first_part_of_string) &&
2376 density.is_none() &&
2377 width.is_none() =>
2378 {
2379 match parse_unsigned_integer(first_part_of_string.chars()) {
2380 Ok(number) if number > 0 => {
2381 width = Some(number);
2382 continue;
2383 },
2384 _ => error = true,
2385 }
2386 },
2387
2388 'x' if is_valid_floating_point_number_string(first_part_of_string) &&
2402 width.is_none() &&
2403 density.is_none() &&
2404 future_compat_h.is_none() =>
2405 {
2406 match first_part_of_string.parse::<f64>() {
2407 Ok(number) if number.is_finite() && number >= 0. => {
2408 density = Some(number);
2409 continue;
2410 },
2411 _ => error = true,
2412 }
2413 },
2414
2415 'h' if is_valid_non_negative_integer_string(first_part_of_string) &&
2424 future_compat_h.is_none() &&
2425 density.is_none() =>
2426 {
2427 match parse_unsigned_integer(first_part_of_string.chars()) {
2428 Ok(number) if number > 0 => {
2429 future_compat_h = Some(number);
2430 continue;
2431 },
2432 _ => error = true,
2433 }
2434 },
2435
2436 _ => error = true,
2439 }
2440
2441 if error {
2442 break;
2443 }
2444 }
2445
2446 if future_compat_h.is_some() && width.is_none() {
2448 error = true;
2449 }
2450
2451 if !error {
2455 let image_source = ImageSource {
2456 url: url.into(),
2457 descriptor: Descriptor { width, density },
2458 };
2459 candidates.push(image_source);
2460 }
2461
2462 }
2464 candidates
2465}
2466
2467#[derive(Clone)]
2468enum ChangeType {
2469 Environment {
2470 selected_source: USVString,
2471 selected_pixel_density: f64,
2472 },
2473 Element,
2474}
2475
2476fn is_supported_image_mime_type(input: &str) -> bool {
2478 let mime_type = input.trim();
2480
2481 let mime_type_essence = match mime_type.find(';') {
2483 Some(semi) => &mime_type[..semi],
2484 _ => mime_type,
2485 };
2486
2487 if mime_type_essence.is_empty() {
2492 return true;
2493 }
2494
2495 SUPPORTED_IMAGE_MIME_TYPES.contains(&mime_type_essence)
2496}