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::{BindContext, MoveContext, Node, NodeDamage, NodeTraits, UnbindContext};
78use crate::dom::performance::performanceresourcetiming::InitiatorType;
79use crate::dom::promise::Promise;
80use crate::dom::virtualmethods::VirtualMethods;
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_runtime::CanGc;
87use crate::script_thread::ScriptThread;
88
89const SUPPORTED_IMAGE_MIME_TYPES: &[&str] = &[
93 "image/bmp",
94 "image/gif",
95 "image/jpeg",
96 "image/jpg",
97 "image/pjpeg",
98 "image/png",
99 "image/apng",
100 "image/x-png",
101 "image/svg+xml",
102 "image/vnd.microsoft.icon",
103 "image/x-icon",
104 "image/webp",
105];
106
107#[derive(Clone, Copy, Debug)]
108enum ParseState {
109 InDescriptor,
110 InParens,
111 AfterDescriptor,
112}
113
114#[derive(MallocSizeOf)]
116pub(crate) struct SourceSet {
117 image_sources: Vec<ImageSource>,
118 source_size: SourceSizeList,
119}
120
121impl SourceSet {
122 fn new() -> SourceSet {
123 SourceSet {
124 image_sources: Vec::new(),
125 source_size: SourceSizeList::empty(),
126 }
127 }
128}
129
130#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
131pub struct ImageSource {
132 pub url: String,
133 pub descriptor: Descriptor,
134}
135
136#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
137pub struct Descriptor {
138 pub width: Option<u32>,
139 pub density: Option<f64>,
140}
141
142#[derive(Clone, Copy, JSTraceable, MallocSizeOf)]
144enum State {
145 Unavailable,
146 PartiallyAvailable,
147 CompletelyAvailable,
148 Broken,
149}
150
151#[derive(Clone, Copy, JSTraceable, MallocSizeOf)]
152enum ImageRequestPhase {
153 Pending,
154 Current,
155}
156
157#[derive(JSTraceable, MallocSizeOf)]
159#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
160struct ImageRequest {
161 state: State,
162 #[no_trace]
163 parsed_url: Option<ServoUrl>,
164 source_url: Option<USVString>,
165 blocker: DomRefCell<Option<LoadBlocker>>,
166 #[no_trace]
167 image: Option<Image>,
168 #[no_trace]
169 metadata: Option<ImageMetadata>,
170 #[no_trace]
171 final_url: Option<ServoUrl>,
172 current_pixel_density: Option<f64>,
173}
174
175#[dom_struct]
176pub(crate) struct HTMLImageElement {
177 htmlelement: HTMLElement,
178 image_request: Cell<ImageRequestPhase>,
179 current_request: DomRefCell<ImageRequest>,
180 pending_request: DomRefCell<ImageRequest>,
181 form_owner: MutNullableDom<HTMLFormElement>,
182 generation: Cell<u32>,
183 source_set: DomRefCell<SourceSet>,
184 dimension_attribute_source: MutNullableDom<Element>,
187 last_selected_source: DomRefCell<Option<USVString>>,
189 #[conditional_malloc_size_of]
190 image_decode_promises: DomRefCell<Vec<Rc<Promise>>>,
191 line_number: u64,
193}
194
195impl HTMLImageElement {
196 pub(crate) fn is_usable(&self) -> Fallible<bool> {
198 if let Some(image) = &self.current_request.borrow().image {
200 let intrinsic_size = image.metadata();
201 if intrinsic_size.width == 0 || intrinsic_size.height == 0 {
202 return Ok(false);
203 }
204 }
205
206 match self.current_request.borrow().state {
207 State::Broken => Err(Error::InvalidState(None)),
209 State::CompletelyAvailable => Ok(true),
210 State::PartiallyAvailable | State::Unavailable => Ok(false),
212 }
213 }
214
215 pub(crate) fn image_data(&self) -> Option<Image> {
216 self.current_request.borrow().image.clone()
217 }
218
219 pub(crate) fn get_raster_image_data(&self) -> Option<Snapshot> {
221 let Some(raster_image) = self.image_data()?.as_raster_image() else {
222 warn!("Vector image is not supported as raster image source");
223 return None;
224 };
225 Some(raster_image.as_snapshot())
226 }
227}
228
229struct ImageContext {
231 image_cache: Arc<dyn ImageCache>,
233 status: Result<(), NetworkError>,
235 id: PendingImageId,
237 aborted: bool,
239 doc: Trusted<Document>,
241 url: ServoUrl,
242 element: Trusted<HTMLImageElement>,
243}
244
245impl FetchResponseListener for ImageContext {
246 fn should_invoke(&self) -> bool {
247 !self.aborted
248 }
249
250 fn process_request_body(&mut self, _: RequestId) {}
251
252 fn process_response(
253 &mut self,
254 _: &mut js::context::JSContext,
255 request_id: RequestId,
256 metadata: Result<FetchMetadata, NetworkError>,
257 ) {
258 debug!("got {:?} for {:?}", metadata.as_ref().map(|_| ()), self.url);
259 self.image_cache.notify_pending_response(
260 self.id,
261 FetchResponseMsg::ProcessResponse(request_id, metadata.clone()),
262 );
263
264 let metadata = metadata.ok().map(|meta| match meta {
265 FetchMetadata::Unfiltered(m) => m,
266 FetchMetadata::Filtered { unsafe_, .. } => unsafe_,
267 });
268
269 if let Some(metadata) = metadata.as_ref() &&
271 let Some(ref content_type) = metadata.content_type
272 {
273 let mime: Mime = content_type.clone().into_inner().into();
274 if mime.type_() == mime::MULTIPART && mime.subtype().as_str() == "x-mixed-replace" {
275 self.aborted = true;
276 }
277 }
278
279 let status = metadata
280 .as_ref()
281 .map(|m| m.status.clone())
282 .unwrap_or_else(HttpStatus::new_error);
283
284 self.status = {
285 if status.is_error() {
286 Err(NetworkError::ResourceLoadError(
287 "No http status code received".to_owned(),
288 ))
289 } else if status.is_success() {
290 Ok(())
291 } else {
292 Err(NetworkError::ResourceLoadError(format!(
293 "HTTP error code {}",
294 status.code()
295 )))
296 }
297 };
298 }
299
300 fn process_response_chunk(
301 &mut self,
302 _: &mut js::context::JSContext,
303 request_id: RequestId,
304 payload: Vec<u8>,
305 ) {
306 if self.status.is_ok() {
307 self.image_cache.notify_pending_response(
308 self.id,
309 FetchResponseMsg::ProcessResponseChunk(request_id, payload.into()),
310 );
311 }
312 }
313
314 fn process_response_eof(
315 self,
316 cx: &mut js::context::JSContext,
317 request_id: RequestId,
318 response: Result<(), NetworkError>,
319 timing: ResourceFetchTiming,
320 ) {
321 self.image_cache.notify_pending_response(
322 self.id,
323 FetchResponseMsg::ProcessResponseEOF(request_id, response.clone(), timing.clone()),
324 );
325 network_listener::submit_timing(cx, &self, &response, &timing);
326 }
327
328 fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<Violation>) {
329 let global = &self.resource_timing_global();
330 let elem = self.element.root();
331 let source_position = elem
332 .upcast::<Element>()
333 .compute_source_position(elem.line_number as u32);
334 global.report_csp_violations(violations, None, Some(source_position));
335 }
336}
337
338impl ResourceTimingListener for ImageContext {
339 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
340 (
341 InitiatorType::LocalName("img".to_string()),
342 self.url.clone(),
343 )
344 }
345
346 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
347 self.doc.root().global()
348 }
349}
350
351#[expect(non_snake_case)]
352impl HTMLImageElement {
353 fn fetch_image(&self, img_url: &ServoUrl, cx: &mut js::context::JSContext) {
355 let window = self.owner_window();
356
357 let cache_result = window.image_cache().get_cached_image_status(
358 img_url.clone(),
359 window.origin().immutable().clone(),
360 cors_setting_for_element(self.upcast()),
361 );
362
363 match cache_result {
364 ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable {
365 image,
366 url,
367 }) => self.process_image_response(ImageResponse::Loaded(image, url), cx),
368 ImageCacheResult::Available(ImageOrMetadataAvailable::MetadataAvailable(
369 metadata,
370 id,
371 )) => {
372 self.process_image_response(ImageResponse::MetadataLoaded(metadata), cx);
373 self.register_image_cache_callback(id, ChangeType::Element);
374 },
375 ImageCacheResult::Pending(id) => {
376 self.register_image_cache_callback(id, ChangeType::Element);
377 },
378 ImageCacheResult::ReadyForRequest(id) => {
379 self.fetch_request(img_url, id);
380 self.register_image_cache_callback(id, ChangeType::Element);
381 },
382 ImageCacheResult::FailedToLoadOrDecode => {
383 self.process_image_response(ImageResponse::FailedToLoadOrDecode, cx)
384 },
385 };
386 }
387
388 fn register_image_cache_callback(&self, id: PendingImageId, change_type: ChangeType) {
389 let trusted_node = Trusted::new(self);
390 let generation = self.generation_id();
391 let window = self.owner_window();
392 let callback = window.register_image_cache_listener(id, move |response, _| {
393 let trusted_node = trusted_node.clone();
394 let window = trusted_node.root().owner_window();
395 let callback_type = change_type.clone();
396
397 window
398 .as_global_scope()
399 .task_manager()
400 .networking_task_source()
401 .queue(task!(process_image_response: move |cx| {
402 let element = trusted_node.root();
403
404 if generation != element.generation_id() {
406 return;
407 }
408
409 match callback_type {
410 ChangeType::Element => {
411 element.process_image_response(response.response, cx);
412 }
413 ChangeType::Environment { selected_source, selected_pixel_density } => {
414 element.process_image_response_for_environment_change(
415 response.response, selected_source, generation, selected_pixel_density, cx
416 );
417 }
418 }
419 }));
420 });
421
422 window.image_cache().add_listener(ImageLoadListener::new(
423 callback,
424 window.pipeline_id(),
425 id,
426 ));
427 }
428
429 fn fetch_request(&self, img_url: &ServoUrl, id: PendingImageId) {
430 let document = self.owner_document();
431 let window = self.owner_window();
432
433 let context = ImageContext {
434 image_cache: window.image_cache(),
435 status: Ok(()),
436 id,
437 aborted: false,
438 doc: Trusted::new(&document),
439 element: Trusted::new(self),
440 url: img_url.clone(),
441 };
442
443 let global = document.global();
446 let mut request = create_a_potential_cors_request(
447 Some(window.webview_id()),
448 img_url.clone(),
449 Destination::Image,
450 cors_setting_for_element(self.upcast()),
451 None,
452 global.get_referrer(),
453 )
454 .with_global_scope(&global)
455 .referrer_policy(referrer_policy_for_element(self.upcast()));
456
457 if self.uses_srcset_or_picture() {
458 request = request.initiator(Initiator::ImageSet);
459 }
460
461 document.fetch_background(request, context);
464 }
465
466 fn handle_loaded_image(&self, image: Image, url: ServoUrl, cx: &mut js::context::JSContext) {
468 self.current_request.borrow_mut().metadata = Some(image.metadata());
469 self.current_request.borrow_mut().final_url = Some(url);
470 self.current_request.borrow_mut().image = Some(image);
471 self.current_request.borrow_mut().state = State::CompletelyAvailable;
472 LoadBlocker::terminate(&self.current_request.borrow().blocker, cx);
473 self.upcast::<Node>().dirty(NodeDamage::Other);
475 self.resolve_image_decode_promises();
476 }
477
478 fn process_image_response(&self, image: ImageResponse, cx: &mut js::context::JSContext) {
480 let (trigger_image_load, trigger_image_error) = match (image, self.image_request.get()) {
486 (ImageResponse::Loaded(image, url), ImageRequestPhase::Current) => {
487 self.handle_loaded_image(image, url, cx);
488 (true, false)
489 },
490 (ImageResponse::Loaded(image, url), ImageRequestPhase::Pending) => {
491 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
492 self.image_request.set(ImageRequestPhase::Current);
493 self.handle_loaded_image(image, url, cx);
494 (true, false)
495 },
496 (ImageResponse::MetadataLoaded(meta), ImageRequestPhase::Current) => {
497 self.current_request.borrow_mut().state = State::PartiallyAvailable;
502 self.current_request.borrow_mut().metadata = Some(meta);
503 (false, false)
504 },
505 (ImageResponse::MetadataLoaded(_), ImageRequestPhase::Pending) => {
506 self.pending_request.borrow_mut().state = State::PartiallyAvailable;
510 (false, false)
511 },
512 (ImageResponse::FailedToLoadOrDecode, ImageRequestPhase::Current) => {
513 self.abort_request(State::Broken, ImageRequestPhase::Current, cx);
519
520 self.load_broken_image_icon();
521
522 (false, true)
526 },
527 (ImageResponse::FailedToLoadOrDecode, ImageRequestPhase::Pending) => {
528 self.abort_request(State::Broken, ImageRequestPhase::Current, cx);
534 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
535
536 mem::swap(
538 &mut *self.current_request.borrow_mut(),
539 &mut *self.pending_request.borrow_mut(),
540 );
541 self.image_request.set(ImageRequestPhase::Current);
542
543 self.current_request.borrow_mut().state = State::Broken;
545
546 self.load_broken_image_icon();
547
548 (false, true)
550 },
551 };
552
553 if trigger_image_load {
555 self.upcast::<EventTarget>().fire_event(cx, atom!("load"));
557 self.upcast::<EventTarget>()
558 .fire_event(cx, atom!("loadend"));
559 }
560
561 if trigger_image_error {
563 self.upcast::<EventTarget>().fire_event(cx, atom!("error"));
564 self.upcast::<EventTarget>()
565 .fire_event(cx, atom!("loadend"));
566 }
567 }
568
569 fn process_image_response_for_environment_change(
572 &self,
573 image: ImageResponse,
574 selected_source: USVString,
575 generation: u32,
576 selected_pixel_density: f64,
577 cx: &mut js::context::JSContext,
578 ) {
579 match image {
580 ImageResponse::Loaded(image, url) => {
581 self.pending_request.borrow_mut().metadata = Some(image.metadata());
582 self.pending_request.borrow_mut().final_url = Some(url);
583 self.pending_request.borrow_mut().image = Some(image);
584 self.finish_reacting_to_environment_change(
585 selected_source,
586 generation,
587 selected_pixel_density,
588 );
589 },
590 ImageResponse::FailedToLoadOrDecode => {
591 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
599 },
600 ImageResponse::MetadataLoaded(meta) => {
601 self.pending_request.borrow_mut().metadata = Some(meta);
602 },
603 };
604 }
605
606 fn abort_request(
608 &self,
609 state: State,
610 request: ImageRequestPhase,
611 cx: &mut js::context::JSContext,
612 ) {
613 let mut request = match request {
614 ImageRequestPhase::Current => self.current_request.borrow_mut(),
615 ImageRequestPhase::Pending => self.pending_request.borrow_mut(),
616 };
617 LoadBlocker::terminate(&request.blocker, cx);
618 request.state = state;
619 request.image = None;
620 request.metadata = None;
621 request.current_pixel_density = None;
622
623 if matches!(state, State::Broken) {
624 self.reject_image_decode_promises();
625 } else if matches!(state, State::CompletelyAvailable) {
626 self.resolve_image_decode_promises();
627 }
628 }
629
630 fn create_source_set(&self) -> SourceSet {
632 let element = self.upcast::<Element>();
633
634 let mut source_set = SourceSet::new();
636
637 if let Some(srcset) = element.get_attribute_string_value(&local_name!("srcset")) {
640 source_set.image_sources = parse_a_srcset_attribute(&srcset);
641 }
642
643 if let Some(sizes) = element.get_attribute_string_value(&local_name!("sizes")) {
645 source_set.source_size = parse_a_sizes_attribute(&sizes);
646 }
647
648 let src = element.get_string_attribute(&local_name!("src"));
652 let no_density_source_of_1 = source_set
653 .image_sources
654 .iter()
655 .all(|source| source.descriptor.density != Some(1.));
656 let no_width_descriptor = source_set
657 .image_sources
658 .iter()
659 .all(|source| source.descriptor.width.is_none());
660 if !src.is_empty() && no_density_source_of_1 && no_width_descriptor {
661 source_set.image_sources.push(ImageSource {
662 url: String::from(src),
663 descriptor: Descriptor {
664 width: None,
665 density: None,
666 },
667 })
668 }
669
670 self.normalise_source_densities(&mut source_set);
672
673 source_set
675 }
676
677 fn update_source_set(&self) {
679 *self.source_set.borrow_mut() = SourceSet::new();
681
682 let elem = self.upcast::<Element>();
687 let parent = elem.upcast::<Node>().GetParentElement();
688 let elements = match parent.as_ref() {
689 Some(p) => {
690 if p.is::<HTMLPictureElement>() {
691 p.upcast::<Node>()
692 .children()
693 .filter_map(DomRoot::downcast::<Element>)
694 .map(|n| DomRoot::from_ref(&*n))
695 .collect()
696 } else {
697 vec![DomRoot::from_ref(elem)]
698 }
699 },
700 None => vec![DomRoot::from_ref(elem)],
701 };
702
703 for element in &elements {
705 if *element == DomRoot::from_ref(elem) {
707 *self.source_set.borrow_mut() = self.create_source_set();
710
711 return;
713 }
714 if !element.is::<HTMLSourceElement>() {
716 continue;
717 }
718
719 let mut source_set = SourceSet::new();
720
721 match element.get_attribute_string_value(&local_name!("srcset")) {
725 Some(srcset) => {
726 source_set.image_sources = parse_a_srcset_attribute(&srcset);
727 },
728 _ => continue,
729 }
730
731 if source_set.image_sources.is_empty() {
733 continue;
734 }
735
736 if let Some(media) = element.get_attribute_string_value(&local_name!("media")) &&
739 !MediaList::matches_environment(&element.owner_document(), &media)
740 {
741 continue;
742 }
743
744 if let Some(sizes) = element.get_attribute_string_value(&local_name!("sizes")) {
747 source_set.source_size = parse_a_sizes_attribute(&sizes);
748 }
749
750 if let Some(type_) = element.get_attribute_string_value(&local_name!("type")) &&
753 !is_supported_image_mime_type(&type_)
754 {
755 continue;
756 }
757
758 if element.has_attribute(&local_name!("width")) ||
761 element.has_attribute(&local_name!("height"))
762 {
763 self.dimension_attribute_source.set(Some(element));
764 } else {
765 self.dimension_attribute_source.set(Some(elem));
766 }
767
768 self.normalise_source_densities(&mut source_set);
770
771 *self.source_set.borrow_mut() = source_set;
773
774 return;
776 }
777 }
778
779 fn evaluate_source_size_list(&self, source_size_list: &SourceSizeList) -> Au {
780 let document = self.owner_document();
781 let quirks_mode = document.quirks_mode();
782 source_size_list.evaluate(document.window().layout().device(), quirks_mode)
783 }
784
785 fn normalise_source_densities(&self, source_set: &mut SourceSet) {
787 let source_size = self.evaluate_source_size_list(&source_set.source_size);
789
790 for image_source in &mut source_set.image_sources {
792 if image_source.descriptor.density.is_some() {
795 continue;
796 }
797
798 if let Some(width) = image_source.descriptor.width {
802 image_source.descriptor.density = Some(width as f64 / source_size.to_f64_px());
803 } else {
804 image_source.descriptor.density = Some(1_f64);
806 }
807 }
808 }
809
810 fn select_image_source(&self) -> Option<(USVString, f64)> {
812 self.update_source_set();
814
815 if self.source_set.borrow().image_sources.is_empty() {
818 return None;
819 }
820
821 self.select_image_source_from_source_set()
823 }
824
825 fn select_image_source_from_source_set(&self) -> Option<(USVString, f64)> {
827 let source_set = self.source_set.borrow();
832 let len = source_set.image_sources.len();
833
834 let mut repeat_indices = FxHashSet::default();
836 for outer_index in 0..len {
837 if repeat_indices.contains(&outer_index) {
838 continue;
839 }
840 let imgsource = &source_set.image_sources[outer_index];
841 let pixel_density = imgsource.descriptor.density.unwrap();
842 for inner_index in (outer_index + 1)..len {
843 let imgsource2 = &source_set.image_sources[inner_index];
844 if pixel_density == imgsource2.descriptor.density.unwrap() {
845 repeat_indices.insert(inner_index);
846 }
847 }
848 }
849
850 let mut max = (0f64, 0);
851 let img_sources = &mut vec![];
852 for (index, image_source) in source_set.image_sources.iter().enumerate() {
853 if repeat_indices.contains(&index) {
854 continue;
855 }
856 let den = image_source.descriptor.density.unwrap();
857 if max.0 < den {
858 max = (den, img_sources.len());
859 }
860 img_sources.push(image_source);
861 }
862
863 let mut best_candidate = max;
866 let device_pixel_ratio = self
867 .owner_document()
868 .window()
869 .viewport_details()
870 .hidpi_scale_factor
871 .get() as f64;
872 for (index, image_source) in img_sources.iter().enumerate() {
873 let current_den = image_source.descriptor.density.unwrap();
874 if current_den < best_candidate.0 && current_den >= device_pixel_ratio {
875 best_candidate = (current_den, index);
876 }
877 }
878 let selected_source = img_sources.remove(best_candidate.1).clone();
879
880 Some((
882 USVString(selected_source.url),
883 selected_source.descriptor.density.unwrap(),
884 ))
885 }
886
887 fn init_image_request(
888 &self,
889 request: &mut RefMut<'_, ImageRequest>,
890 url: &ServoUrl,
891 src: &USVString,
892 cx: &mut js::context::JSContext,
893 ) {
894 request.parsed_url = Some(url.clone());
895 request.source_url = Some(src.clone());
896 request.image = None;
897 request.metadata = None;
898 let document = self.owner_document();
899 LoadBlocker::terminate(&request.blocker, cx);
900 *request.blocker.borrow_mut() =
901 Some(LoadBlocker::new(&document, LoadType::Image(url.clone())));
902 }
903
904 fn prepare_image_request(
906 &self,
907 selected_source: &USVString,
908 selected_pixel_density: f64,
909 image_url: &ServoUrl,
910 cx: &mut js::context::JSContext,
911 ) {
912 match self.image_request.get() {
913 ImageRequestPhase::Pending => {
914 if self
917 .pending_request
918 .borrow()
919 .parsed_url
920 .as_ref()
921 .is_some_and(|parsed_url| *parsed_url == *image_url)
922 {
923 return;
924 }
925 },
926 ImageRequestPhase::Current => {
927 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
929
930 let mut current_request = self.current_request.borrow_mut();
933 let mut pending_request = self.pending_request.borrow_mut();
934
935 match (current_request.parsed_url.as_ref(), current_request.state) {
936 (Some(parsed_url), State::PartiallyAvailable) => {
937 if *parsed_url == *image_url {
943 return;
945 }
946
947 self.image_request.set(ImageRequestPhase::Pending);
951 self.init_image_request(
952 &mut pending_request,
953 image_url,
954 selected_source,
955 cx,
956 );
957 pending_request.current_pixel_density = Some(selected_pixel_density);
958 },
959 (_, State::Broken) | (_, State::Unavailable) => {
960 self.init_image_request(
964 &mut current_request,
965 image_url,
966 selected_source,
967 cx,
968 );
969 current_request.current_pixel_density = Some(selected_pixel_density);
970 self.reject_image_decode_promises();
971 },
972 (_, _) => {
973 self.image_request.set(ImageRequestPhase::Pending);
977 self.init_image_request(
978 &mut pending_request,
979 image_url,
980 selected_source,
981 cx,
982 );
983 pending_request.current_pixel_density = Some(selected_pixel_density);
984 },
985 }
986 },
987 }
988
989 self.fetch_image(image_url, cx);
990 }
991
992 fn update_the_image_data_sync_steps(&self, cx: &mut js::context::JSContext) {
994 let Some((selected_source, selected_pixel_density)) = self.select_image_source() else {
997 self.abort_request(State::Broken, ImageRequestPhase::Current, cx);
1002 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
1003 self.image_request.set(ImageRequestPhase::Current);
1004
1005 let this = Trusted::new(self);
1008
1009 self.owner_global().task_manager().dom_manipulation_task_source().queue(
1010 task!(image_null_source_error: move |cx| {
1011 let this = this.root();
1012
1013 {
1015 let mut current_request =
1016 this.current_request.borrow_mut();
1017 current_request.source_url = None;
1018 current_request.parsed_url = None;
1019 }
1020
1021 let has_src_attribute = this.upcast::<Element>().has_attribute(&local_name!("src"));
1027
1028 if has_src_attribute || this.uses_srcset_or_picture() {
1029 this.upcast::<EventTarget>().fire_event(cx, atom!("error"));
1030 }
1031 }));
1032
1033 return;
1035 };
1036
1037 let Ok(image_url) = self.owner_document().base_url().join(&selected_source) else {
1040 self.abort_request(State::Broken, ImageRequestPhase::Current, cx);
1045 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
1046
1047 self.image_request.set(ImageRequestPhase::Current);
1049
1050 let this = Trusted::new(self);
1053
1054 self.owner_global()
1055 .task_manager()
1056 .dom_manipulation_task_source()
1057 .queue(task!(image_selected_source_error: move |cx| {
1058 let this = this.root();
1059
1060 {
1062 let mut current_request =
1063 this.current_request.borrow_mut();
1064 current_request.source_url = Some(selected_source);
1065 current_request.parsed_url = None;
1066 }
1067
1068 this.upcast::<EventTarget>().fire_event(cx, atom!("error"));
1072 }));
1073
1074 return;
1076 };
1077
1078 self.prepare_image_request(&selected_source, selected_pixel_density, &image_url, cx);
1079 }
1080
1081 pub(crate) fn update_the_image_data(&self, cx: &mut js::context::JSContext) {
1083 self.generation.set(self.generation.get() + 1);
1085
1086 if !self.owner_document().is_active() {
1088 }
1094
1095 self.current_request.borrow_mut().state = State::Unavailable;
1104
1105 let mut selected_source = None;
1109 let mut selected_pixel_density = None;
1110
1111 let src = self
1115 .upcast::<Element>()
1116 .get_string_attribute(&local_name!("src"));
1117
1118 if !self.uses_srcset_or_picture() && !src.is_empty() {
1119 selected_source = Some(USVString(String::from(src)));
1120 selected_pixel_density = Some(1_f64);
1121 };
1122
1123 self.last_selected_source
1125 .borrow_mut()
1126 .clone_from(&selected_source);
1127
1128 if let Some(selected_source) = selected_source {
1130 if let Ok(image_url) = self.owner_document().base_url().join(&selected_source) {
1134 let window = self.owner_window();
1138 let response = window.image_cache().get_image(
1139 image_url.clone(),
1140 window.origin().immutable().clone(),
1141 cors_setting_for_element(self.upcast()),
1142 );
1143
1144 if let Some(image) = response {
1146 self.abort_request(State::CompletelyAvailable, ImageRequestPhase::Current, cx);
1151 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
1152
1153 self.image_request.set(ImageRequestPhase::Current);
1155
1156 let mut current_request = self.current_request.borrow_mut();
1159 current_request.metadata = Some(image.metadata());
1160 current_request.image = Some(image);
1161 current_request.final_url = Some(image_url.clone());
1162
1163 self.upcast::<Node>().dirty(NodeDamage::Other);
1166
1167 current_request.current_pixel_density = selected_pixel_density;
1170
1171 let this = Trusted::new(self);
1174
1175 self.owner_global()
1176 .task_manager()
1177 .dom_manipulation_task_source()
1178 .queue(task!(image_load_event: move |cx| {
1179 let this = this.root();
1180
1181 {
1186 let mut current_request =
1187 this.current_request.borrow_mut();
1188 current_request.source_url = Some(selected_source);
1189 current_request.parsed_url = Some(image_url);
1190 }
1191
1192 this.upcast::<EventTarget>().fire_event(cx, atom!("load"));
1196 }));
1197
1198 return;
1200 }
1201 }
1202 }
1203
1204 let task = ImageElementMicrotask::UpdateImageData {
1207 elem: DomRoot::from_ref(self),
1208 generation: self.generation.get(),
1209 };
1210
1211 ScriptThread::await_stable_state(Microtask::ImageElement(task));
1212 }
1213
1214 pub(crate) fn react_to_environment_changes(&self) {
1216 let task = ImageElementMicrotask::EnvironmentChanges {
1218 elem: DomRoot::from_ref(self),
1219 generation: self.generation.get(),
1220 };
1221
1222 ScriptThread::await_stable_state(Microtask::ImageElement(task));
1223 }
1224
1225 fn react_to_environment_changes_sync_steps(
1227 &self,
1228 generation: u32,
1229 cx: &mut js::context::JSContext,
1230 ) {
1231 let document = self.owner_document();
1232 let has_pending_request = matches!(self.image_request.get(), ImageRequestPhase::Pending);
1233
1234 if !document.is_active() || !self.uses_srcset_or_picture() || has_pending_request {
1238 return;
1239 }
1240
1241 let Some((selected_source, selected_pixel_density)) = self.select_image_source() else {
1244 return;
1246 };
1247
1248 let mut same_selected_source = self
1251 .last_selected_source
1252 .borrow()
1253 .as_ref()
1254 .is_some_and(|source| *source == selected_source);
1255
1256 same_selected_source = same_selected_source ||
1260 self.current_request
1261 .borrow()
1262 .source_url
1263 .as_ref()
1264 .is_some_and(|source| *source == selected_source);
1265
1266 let same_selected_pixel_density = self
1267 .current_request
1268 .borrow()
1269 .current_pixel_density
1270 .is_some_and(|pixel_density| pixel_density == selected_pixel_density);
1271
1272 if same_selected_source && same_selected_pixel_density {
1273 return;
1274 }
1275
1276 let Ok(image_url) = document.base_url().join(&selected_source) else {
1280 return;
1281 };
1282
1283 self.image_request.set(ImageRequestPhase::Pending);
1285 self.init_image_request(
1286 &mut self.pending_request.borrow_mut(),
1287 &image_url,
1288 &selected_source,
1289 cx,
1290 );
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, promise: Rc<Promise>, can_gc: CanGc) {
1345 if !self.owner_document().is_fully_active() ||
1349 matches!(self.current_request.borrow().state, State::Broken)
1350 {
1351 promise.reject_error(Error::Encoding(None), can_gc);
1352 } else if matches!(
1353 self.current_request.borrow().state,
1354 State::CompletelyAvailable
1355 ) {
1356 promise.resolve_native(&(), can_gc);
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(Error::Encoding(None), can_gc);
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_with_cx(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_with_cx(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: DomRoot<HTMLImageElement>,
1623 generation: u32,
1624 },
1625 EnvironmentChanges {
1626 elem: DomRoot<HTMLImageElement>,
1627 generation: u32,
1628 },
1629 Decode {
1630 elem: DomRoot<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 match *self {
1639 ImageElementMicrotask::UpdateImageData {
1640 ref elem,
1641 ref generation,
1642 } => {
1643 if elem.generation.get() == *generation {
1647 elem.update_the_image_data_sync_steps(cx);
1648 }
1649 },
1650 ImageElementMicrotask::EnvironmentChanges {
1651 ref elem,
1652 ref generation,
1653 } => {
1654 elem.react_to_environment_changes_sync_steps(*generation, cx);
1655 },
1656 ImageElementMicrotask::Decode {
1657 ref elem,
1658 ref promise,
1659 } => {
1660 elem.react_to_decode_image_sync_steps(promise.clone(), CanGc::from_cx(cx));
1661 },
1662 }
1663 }
1664
1665 fn enter_realm<'cx>(&self, cx: &'cx mut js::context::JSContext) -> AutoRealm<'cx> {
1666 match self {
1667 &ImageElementMicrotask::UpdateImageData { ref elem, .. } |
1668 &ImageElementMicrotask::EnvironmentChanges { ref elem, .. } |
1669 &ImageElementMicrotask::Decode { ref elem, .. } => enter_auto_realm(cx, &**elem),
1670 }
1671 }
1672}
1673
1674impl<'dom> LayoutDom<'dom, HTMLImageElement> {
1675 #[expect(unsafe_code)]
1676 fn current_request(self) -> &'dom ImageRequest {
1677 unsafe { self.unsafe_get().current_request.borrow_for_layout() }
1678 }
1679
1680 #[expect(unsafe_code)]
1681 fn dimension_attribute_source(self) -> LayoutDom<'dom, Element> {
1682 unsafe {
1683 self.unsafe_get()
1684 .dimension_attribute_source
1685 .get_inner_as_layout()
1686 .expect("dimension attribute source should be always non-null")
1687 }
1688 }
1689
1690 pub(crate) fn image_url(self) -> Option<ServoUrl> {
1691 self.current_request().parsed_url.clone()
1692 }
1693
1694 pub(crate) fn image_data(self) -> (Option<Image>, Option<ImageMetadata>) {
1695 let current_request = self.current_request();
1696 (current_request.image.clone(), current_request.metadata)
1697 }
1698
1699 pub(crate) fn image_density(self) -> Option<f64> {
1700 self.current_request().current_pixel_density
1701 }
1702
1703 pub(crate) fn showing_broken_image_icon(self) -> bool {
1704 matches!(self.current_request().state, State::Broken)
1705 }
1706
1707 pub(crate) fn get_width(self) -> LengthOrPercentageOrAuto {
1708 self.dimension_attribute_source()
1709 .get_attr_for_layout(&ns!(), &local_name!("width"))
1710 .map(AttrValue::as_dimension)
1711 .cloned()
1712 .unwrap_or(LengthOrPercentageOrAuto::Auto)
1713 }
1714
1715 pub(crate) fn get_height(self) -> LengthOrPercentageOrAuto {
1716 self.dimension_attribute_source()
1717 .get_attr_for_layout(&ns!(), &local_name!("height"))
1718 .map(AttrValue::as_dimension)
1719 .cloned()
1720 .unwrap_or(LengthOrPercentageOrAuto::Auto)
1721 }
1722}
1723
1724fn parse_a_sizes_attribute(value: &str) -> SourceSizeList {
1726 let mut input = ParserInput::new(value);
1727 let mut parser = Parser::new(&mut input);
1728 let url_data = Url::parse("about:blank").unwrap().into();
1729 let context =
1732 parser_context_for_anonymous_content(CssRuleType::Style, ParsingMode::empty(), &url_data);
1733 SourceSizeList::parse(&context, &mut parser)
1734}
1735
1736impl HTMLImageElementMethods<crate::DomTypeHolder> for HTMLImageElement {
1737 fn Image(
1739 cx: &mut JSContext,
1740 window: &Window,
1741 proto: Option<HandleObject>,
1742 width: Option<u32>,
1743 height: Option<u32>,
1744 ) -> Fallible<DomRoot<HTMLImageElement>> {
1745 let document = window.Document();
1747
1748 let element = Element::create(
1751 cx,
1752 QualName::new(None, ns!(html), local_name!("img")),
1753 None,
1754 &document,
1755 ElementCreator::ScriptCreated,
1756 CustomElementCreationMode::Synchronous,
1757 proto,
1758 );
1759
1760 let image = DomRoot::downcast::<HTMLImageElement>(element).unwrap();
1761
1762 if let Some(w) = width {
1764 image.SetWidth(cx, w);
1765 }
1766
1767 if let Some(h) = height {
1770 image.SetHeight(cx, h);
1771 }
1772
1773 Ok(image)
1775 }
1776
1777 make_getter!(Alt, "alt");
1779 make_setter!(SetAlt, "alt");
1781
1782 make_url_getter!(Src, "src");
1784
1785 make_url_setter!(SetSrc, "src");
1787
1788 make_url_getter!(Srcset, "srcset");
1790 make_url_setter!(SetSrcset, "srcset");
1792
1793 make_getter!(Sizes, "sizes");
1795
1796 make_setter!(SetSizes, "sizes");
1798
1799 fn GetCrossOrigin(&self) -> Option<DOMString> {
1801 reflect_cross_origin_attribute(self.upcast::<Element>())
1802 }
1803
1804 fn SetCrossOrigin(&self, cx: &mut JSContext, value: Option<DOMString>) {
1806 set_cross_origin_attribute(cx, self.upcast::<Element>(), value);
1807 }
1808
1809 make_getter!(UseMap, "usemap");
1811 make_setter!(SetUseMap, "usemap");
1813
1814 make_bool_getter!(IsMap, "ismap");
1816 make_bool_setter!(SetIsMap, "ismap");
1818
1819 fn Width(&self) -> u32 {
1821 let node = self.upcast::<Node>();
1822 node.content_box()
1823 .map(|rect| rect.size.width.to_px() as u32)
1824 .unwrap_or_else(|| self.NaturalWidth())
1825 }
1826
1827 make_dimension_uint_setter!(SetWidth, "width");
1829
1830 fn Height(&self) -> u32 {
1832 let node = self.upcast::<Node>();
1833 node.content_box()
1834 .map(|rect| rect.size.height.to_px() as u32)
1835 .unwrap_or_else(|| self.NaturalHeight())
1836 }
1837
1838 make_dimension_uint_setter!(SetHeight, "height");
1840
1841 fn NaturalWidth(&self) -> u32 {
1843 let request = self.current_request.borrow();
1844 if matches!(request.state, State::Broken) {
1845 return 0;
1846 }
1847
1848 let pixel_density = request.current_pixel_density.unwrap_or(1f64);
1849 match request.metadata {
1850 Some(ref metadata) => (metadata.width as f64 / pixel_density) as u32,
1851 None => 0,
1852 }
1853 }
1854
1855 fn NaturalHeight(&self) -> u32 {
1857 let request = self.current_request.borrow();
1858 if matches!(request.state, State::Broken) {
1859 return 0;
1860 }
1861
1862 let pixel_density = request.current_pixel_density.unwrap_or(1f64);
1863 match request.metadata {
1864 Some(ref metadata) => (metadata.height as f64 / pixel_density) as u32,
1865 None => 0,
1866 }
1867 }
1868
1869 fn Complete(&self) -> bool {
1871 let element = self.upcast::<Element>();
1872
1873 let has_srcset_attribute = element.has_attribute(&local_name!("srcset"));
1876 if !element.has_attribute(&local_name!("src")) && !has_srcset_attribute {
1877 return true;
1878 }
1879
1880 let src = element.get_string_attribute(&local_name!("src"));
1882 if !has_srcset_attribute && src.is_empty() {
1883 return true;
1884 }
1885
1886 if matches!(self.image_request.get(), ImageRequestPhase::Current) &&
1890 matches!(
1891 self.current_request.borrow().state,
1892 State::CompletelyAvailable | State::Broken
1893 )
1894 {
1895 return true;
1896 }
1897
1898 false
1900 }
1901
1902 fn CurrentSrc(&self) -> USVString {
1904 let current_request = self.current_request.borrow();
1905 let url = ¤t_request.parsed_url;
1906 match *url {
1907 Some(ref url) => USVString(url.clone().into_string()),
1908 None => {
1909 let unparsed_url = ¤t_request.source_url;
1910 match *unparsed_url {
1911 Some(ref url) => url.clone(),
1912 None => USVString("".to_owned()),
1913 }
1914 },
1915 }
1916 }
1917
1918 fn ReferrerPolicy(&self) -> DOMString {
1920 reflect_referrer_policy_attribute(self.upcast::<Element>())
1921 }
1922
1923 make_setter!(SetReferrerPolicy, "referrerpolicy");
1925
1926 fn Decode(&self, cx: &mut JSContext) -> Rc<Promise> {
1928 let promise = Promise::new2(cx, &self.global());
1930
1931 let task = ImageElementMicrotask::Decode {
1933 elem: DomRoot::from_ref(self),
1934 promise: promise.clone(),
1935 };
1936
1937 ScriptThread::await_stable_state(Microtask::ImageElement(task));
1938
1939 promise
1941 }
1942
1943 make_getter!(Name, "name");
1945
1946 make_atomic_setter!(SetName, "name");
1948
1949 make_getter!(Align, "align");
1951
1952 make_setter!(SetAlign, "align");
1954
1955 make_uint_getter!(Hspace, "hspace");
1957
1958 make_uint_setter!(SetHspace, "hspace");
1960
1961 make_uint_getter!(Vspace, "vspace");
1963
1964 make_uint_setter!(SetVspace, "vspace");
1966
1967 make_url_getter!(LongDesc, "longdesc");
1969
1970 make_url_setter!(SetLongDesc, "longdesc");
1972
1973 make_getter!(Border, "border");
1975
1976 make_setter!(SetBorder, "border");
1978}
1979
1980impl VirtualMethods for HTMLImageElement {
1981 fn super_type(&self) -> Option<&dyn VirtualMethods> {
1982 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
1983 }
1984
1985 fn adopting_steps(&self, cx: &mut JSContext, old_doc: &Document) {
1986 self.super_type().unwrap().adopting_steps(cx, old_doc);
1987 self.update_the_image_data(cx);
1988 }
1989
1990 fn attribute_mutated(
1991 &self,
1992 cx: &mut js::context::JSContext,
1993 attr: AttrRef<'_>,
1994 mutation: AttributeMutation,
1995 ) {
1996 self.super_type()
1997 .unwrap()
1998 .attribute_mutated(cx, attr, mutation);
1999 match attr.local_name() {
2000 &local_name!("src") |
2001 &local_name!("srcset") |
2002 &local_name!("width") |
2003 &local_name!("sizes") => {
2004 self.update_the_image_data(cx);
2008 },
2009 &local_name!("crossorigin") => {
2010 let cross_origin_state_changed = match mutation {
2013 AttributeMutation::Removed | AttributeMutation::Set(None, _) => true,
2014 AttributeMutation::Set(Some(old_value), _) => {
2015 let new_cors_setting =
2016 CorsSettings::from_enumerated_attribute(&attr.value());
2017 let old_cors_setting = CorsSettings::from_enumerated_attribute(old_value);
2018
2019 new_cors_setting != old_cors_setting
2020 },
2021 };
2022
2023 if cross_origin_state_changed {
2024 self.update_the_image_data(cx);
2025 }
2026 },
2027 &local_name!("referrerpolicy") => {
2028 let referrer_policy_state_changed = match mutation {
2031 AttributeMutation::Removed | AttributeMutation::Set(None, _) => {
2032 ReferrerPolicy::from(&**attr.value()) != ReferrerPolicy::EmptyString
2033 },
2034 AttributeMutation::Set(Some(old_value), _) => {
2035 ReferrerPolicy::from(&**attr.value()) != ReferrerPolicy::from(&**old_value)
2036 },
2037 };
2038
2039 if referrer_policy_state_changed {
2040 self.update_the_image_data(cx);
2041 }
2042 },
2043 _ => {},
2044 }
2045 }
2046
2047 fn attribute_affects_presentational_hints(&self, attr: AttrRef<'_>) -> bool {
2048 match attr.local_name() {
2049 &local_name!("width") | &local_name!("height") => true,
2050 _ => self
2051 .super_type()
2052 .unwrap()
2053 .attribute_affects_presentational_hints(attr),
2054 }
2055 }
2056
2057 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
2058 match name {
2059 &local_name!("width") | &local_name!("height") => {
2060 AttrValue::from_dimension(value.into())
2061 },
2062 &local_name!("hspace") | &local_name!("vspace") => AttrValue::from_u32(value.into(), 0),
2063 _ => self
2064 .super_type()
2065 .unwrap()
2066 .parse_plain_attribute(name, value),
2067 }
2068 }
2069
2070 fn handle_event(&self, cx: &mut js::context::JSContext, event: &Event) {
2071 if event.type_() != atom!("click") {
2072 return;
2073 }
2074
2075 let area_elements = self.areas();
2076 let elements = match area_elements {
2077 Some(x) => x,
2078 None => return,
2079 };
2080
2081 let mouse_event = match event.downcast::<MouseEvent>() {
2083 Some(x) => x,
2084 None => return,
2085 };
2086
2087 let point = Point2D::new(
2088 mouse_event.ClientX().to_f32().unwrap(),
2089 mouse_event.ClientY().to_f32().unwrap(),
2090 );
2091 let bcr = self.upcast::<Element>().GetBoundingClientRect(cx);
2092 let bcr_p = Point2D::new(bcr.X() as f32, bcr.Y() as f32);
2093
2094 for element in elements {
2096 let shape = element.get_shape_from_coords();
2097 let shp = match shape {
2098 Some(x) => x.absolute_coords(bcr_p),
2099 None => return,
2100 };
2101 if shp.hit_test(&point) {
2102 element.activation_behavior(cx, event, self.upcast());
2103 return;
2104 }
2105 }
2106 }
2107
2108 fn bind_to_tree(&self, cx: &mut JSContext, context: &BindContext) {
2110 if let Some(s) = self.super_type() {
2111 s.bind_to_tree(cx, context);
2112 }
2113 let document = self.owner_document();
2114 if context.tree_connected {
2115 document.register_responsive_image(self);
2116 }
2117
2118 let parent = self.upcast::<Node>().GetParentNode().unwrap();
2119
2120 if parent.is::<HTMLPictureElement>() && std::ptr::eq(&*parent, context.parent) {
2123 self.update_the_image_data(cx);
2124 }
2125 }
2126
2127 fn unbind_from_tree(&self, cx: &mut js::context::JSContext, context: &UnbindContext) {
2129 self.super_type().unwrap().unbind_from_tree(cx, context);
2130 let document = self.owner_document();
2131 document.unregister_responsive_image(self);
2132
2133 if context.parent.is::<HTMLPictureElement>() && !self.upcast::<Node>().has_parent() {
2136 self.update_the_image_data(cx);
2137 }
2138 }
2139
2140 fn moving_steps(&self, cx: &mut JSContext, context: &MoveContext) {
2142 if let Some(super_type) = self.super_type() {
2143 super_type.moving_steps(cx, context);
2144 }
2145
2146 if let Some(old_parent) = context.old_parent &&
2148 old_parent.is::<HTMLPictureElement>()
2149 {
2150 self.update_the_image_data(cx);
2151 }
2152 }
2153}
2154
2155impl FormControl for HTMLImageElement {
2156 fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
2157 self.form_owner.get()
2158 }
2159
2160 fn set_form_owner(&self, form: Option<&HTMLFormElement>) {
2161 self.form_owner.set(form);
2162 }
2163
2164 fn to_element(&self) -> &Element {
2165 self.upcast::<Element>()
2166 }
2167
2168 fn is_listed(&self) -> bool {
2169 false
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}