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_thread::ScriptThread;
87
88const SUPPORTED_IMAGE_MIME_TYPES: &[&str] = &[
92 "image/bmp",
93 "image/gif",
94 "image/jpeg",
95 "image/jpg",
96 "image/pjpeg",
97 "image/png",
98 "image/apng",
99 "image/x-png",
100 "image/svg+xml",
101 "image/vnd.microsoft.icon",
102 "image/x-icon",
103 "image/webp",
104];
105
106#[derive(Clone, Copy, Debug)]
107enum ParseState {
108 InDescriptor,
109 InParens,
110 AfterDescriptor,
111}
112
113#[derive(MallocSizeOf)]
115pub(crate) struct SourceSet {
116 image_sources: Vec<ImageSource>,
117 source_size: SourceSizeList,
118}
119
120impl SourceSet {
121 fn new() -> SourceSet {
122 SourceSet {
123 image_sources: Vec::new(),
124 source_size: SourceSizeList::empty(),
125 }
126 }
127}
128
129#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
130pub struct ImageSource {
131 pub url: String,
132 pub descriptor: Descriptor,
133}
134
135#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
136pub struct Descriptor {
137 pub width: Option<u32>,
138 pub density: Option<f64>,
139}
140
141#[derive(Clone, Copy, JSTraceable, MallocSizeOf)]
143enum State {
144 Unavailable,
145 PartiallyAvailable,
146 CompletelyAvailable,
147 Broken,
148}
149
150#[derive(Clone, Copy, JSTraceable, MallocSizeOf)]
151enum ImageRequestPhase {
152 Pending,
153 Current,
154}
155
156#[derive(JSTraceable, MallocSizeOf)]
158#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
159struct ImageRequest {
160 state: State,
161 #[no_trace]
162 parsed_url: Option<ServoUrl>,
163 source_url: Option<USVString>,
164 blocker: DomRefCell<Option<LoadBlocker>>,
165 #[no_trace]
166 image: Option<Image>,
167 #[no_trace]
168 metadata: Option<ImageMetadata>,
169 #[no_trace]
170 final_url: Option<ServoUrl>,
171 current_pixel_density: Option<f64>,
172}
173
174#[dom_struct]
175pub(crate) struct HTMLImageElement {
176 htmlelement: HTMLElement,
177 image_request: Cell<ImageRequestPhase>,
178 current_request: DomRefCell<ImageRequest>,
179 pending_request: DomRefCell<ImageRequest>,
180 form_owner: MutNullableDom<HTMLFormElement>,
181 generation: Cell<u32>,
182 source_set: DomRefCell<SourceSet>,
183 dimension_attribute_source: MutNullableDom<Element>,
186 last_selected_source: DomRefCell<Option<USVString>>,
188 #[conditional_malloc_size_of]
189 image_decode_promises: DomRefCell<Vec<Rc<Promise>>>,
190 line_number: u64,
192}
193
194impl HTMLImageElement {
195 pub(crate) fn is_usable(&self) -> Fallible<bool> {
197 if let Some(image) = &self.current_request.borrow().image {
199 let intrinsic_size = image.metadata();
200 if intrinsic_size.width == 0 || intrinsic_size.height == 0 {
201 return Ok(false);
202 }
203 }
204
205 match self.current_request.borrow().state {
206 State::Broken => Err(Error::InvalidState(None)),
208 State::CompletelyAvailable => Ok(true),
209 State::PartiallyAvailable | State::Unavailable => Ok(false),
211 }
212 }
213
214 pub(crate) fn image_data(&self) -> Option<Image> {
215 self.current_request.borrow().image.clone()
216 }
217
218 pub(crate) fn get_raster_image_data(&self) -> Option<Snapshot> {
220 let Some(raster_image) = self.image_data()?.as_raster_image() else {
221 warn!("Vector image is not supported as raster image source");
222 return None;
223 };
224 Some(raster_image.as_snapshot())
225 }
226}
227
228struct ImageContext {
230 image_cache: Arc<dyn ImageCache>,
232 status: Result<(), NetworkError>,
234 id: PendingImageId,
236 aborted: bool,
238 doc: Trusted<Document>,
240 url: ServoUrl,
241 element: Trusted<HTMLImageElement>,
242}
243
244impl FetchResponseListener for ImageContext {
245 fn should_invoke(&self) -> bool {
246 !self.aborted
247 }
248
249 fn process_request_body(&mut self, _: RequestId) {}
250
251 fn process_response(
252 &mut self,
253 _: &mut js::context::JSContext,
254 request_id: RequestId,
255 metadata: Result<FetchMetadata, NetworkError>,
256 ) {
257 debug!("got {:?} for {:?}", metadata.as_ref().map(|_| ()), self.url);
258 self.image_cache.notify_pending_response(
259 self.id,
260 FetchResponseMsg::ProcessResponse(request_id, metadata.clone()),
261 );
262
263 let metadata = metadata.ok().map(|meta| match meta {
264 FetchMetadata::Unfiltered(m) => m,
265 FetchMetadata::Filtered { unsafe_, .. } => unsafe_,
266 });
267
268 if let Some(metadata) = metadata.as_ref() &&
270 let Some(ref content_type) = metadata.content_type
271 {
272 let mime: Mime = content_type.clone().into_inner().into();
273 if mime.type_() == mime::MULTIPART && mime.subtype().as_str() == "x-mixed-replace" {
274 self.aborted = true;
275 }
276 }
277
278 let status = metadata
279 .as_ref()
280 .map(|m| m.status.clone())
281 .unwrap_or_else(HttpStatus::new_error);
282
283 self.status = {
284 if status.is_error() {
285 Err(NetworkError::ResourceLoadError(
286 "No http status code received".to_owned(),
287 ))
288 } else if status.is_success() {
289 Ok(())
290 } else {
291 Err(NetworkError::ResourceLoadError(format!(
292 "HTTP error code {}",
293 status.code()
294 )))
295 }
296 };
297 }
298
299 fn process_response_chunk(
300 &mut self,
301 _: &mut js::context::JSContext,
302 request_id: RequestId,
303 payload: Vec<u8>,
304 ) {
305 if self.status.is_ok() {
306 self.image_cache.notify_pending_response(
307 self.id,
308 FetchResponseMsg::ProcessResponseChunk(request_id, payload.into()),
309 );
310 }
311 }
312
313 fn process_response_eof(
314 self,
315 cx: &mut js::context::JSContext,
316 request_id: RequestId,
317 response: Result<(), NetworkError>,
318 timing: ResourceFetchTiming,
319 ) {
320 self.image_cache.notify_pending_response(
321 self.id,
322 FetchResponseMsg::ProcessResponseEOF(request_id, response.clone(), timing.clone()),
323 );
324 network_listener::submit_timing(cx, &self, &response, &timing);
325 }
326
327 fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<Violation>) {
328 let global = &self.resource_timing_global();
329 let elem = self.element.root();
330 let source_position = elem
331 .upcast::<Element>()
332 .compute_source_position(elem.line_number as u32);
333 global.report_csp_violations(violations, None, Some(source_position));
334 }
335}
336
337impl ResourceTimingListener for ImageContext {
338 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
339 (
340 InitiatorType::LocalName("img".to_string()),
341 self.url.clone(),
342 )
343 }
344
345 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
346 self.doc.root().global()
347 }
348}
349
350#[expect(non_snake_case)]
351impl HTMLImageElement {
352 fn fetch_image(&self, img_url: &ServoUrl, cx: &mut js::context::JSContext) {
354 let window = self.owner_window();
355
356 let cache_result = window.image_cache().get_cached_image_status(
357 img_url.clone(),
358 window.origin().immutable().clone(),
359 cors_setting_for_element(self.upcast()),
360 );
361
362 match cache_result {
363 ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable {
364 image,
365 url,
366 }) => self.process_image_response(ImageResponse::Loaded(image, url), cx),
367 ImageCacheResult::Available(ImageOrMetadataAvailable::MetadataAvailable(
368 metadata,
369 id,
370 )) => {
371 self.process_image_response(ImageResponse::MetadataLoaded(metadata), cx);
372 self.register_image_cache_callback(id, ChangeType::Element);
373 },
374 ImageCacheResult::Pending(id) => {
375 self.register_image_cache_callback(id, ChangeType::Element);
376 },
377 ImageCacheResult::ReadyForRequest(id) => {
378 self.fetch_request(img_url, id);
379 self.register_image_cache_callback(id, ChangeType::Element);
380 },
381 ImageCacheResult::FailedToLoadOrDecode => {
382 self.process_image_response(ImageResponse::FailedToLoadOrDecode, cx)
383 },
384 };
385 }
386
387 fn register_image_cache_callback(&self, id: PendingImageId, change_type: ChangeType) {
388 let trusted_node = Trusted::new(self);
389 let generation = self.generation_id();
390 let window = self.owner_window();
391 let callback = window.register_image_cache_listener(id, move |response, _| {
392 let trusted_node = trusted_node.clone();
393 let window = trusted_node.root().owner_window();
394 let callback_type = change_type.clone();
395
396 window
397 .as_global_scope()
398 .task_manager()
399 .networking_task_source()
400 .queue(task!(process_image_response: move |cx| {
401 let element = trusted_node.root();
402
403 if generation != element.generation_id() {
405 return;
406 }
407
408 match callback_type {
409 ChangeType::Element => {
410 element.process_image_response(response.response, cx);
411 }
412 ChangeType::Environment { selected_source, selected_pixel_density } => {
413 element.process_image_response_for_environment_change(
414 response.response, selected_source, generation, selected_pixel_density, cx
415 );
416 }
417 }
418 }));
419 });
420
421 window.image_cache().add_listener(ImageLoadListener::new(
422 callback,
423 window.pipeline_id(),
424 id,
425 ));
426 }
427
428 fn fetch_request(&self, img_url: &ServoUrl, id: PendingImageId) {
429 let document = self.owner_document();
430 let window = self.owner_window();
431
432 let context = ImageContext {
433 image_cache: window.image_cache(),
434 status: Ok(()),
435 id,
436 aborted: false,
437 doc: Trusted::new(&document),
438 element: Trusted::new(self),
439 url: img_url.clone(),
440 };
441
442 let global = document.global();
445 let mut request = create_a_potential_cors_request(
446 Some(window.webview_id()),
447 img_url.clone(),
448 Destination::Image,
449 cors_setting_for_element(self.upcast()),
450 None,
451 global.get_referrer(),
452 )
453 .with_global_scope(&global)
454 .referrer_policy(referrer_policy_for_element(self.upcast()));
455
456 if self.uses_srcset_or_picture() {
457 request = request.initiator(Initiator::ImageSet);
458 }
459
460 document.fetch_background(request, context);
463 }
464
465 fn handle_loaded_image(&self, image: Image, url: ServoUrl, cx: &mut js::context::JSContext) {
467 self.current_request.borrow_mut().metadata = Some(image.metadata());
468 self.current_request.borrow_mut().final_url = Some(url);
469 self.current_request.borrow_mut().image = Some(image);
470 self.current_request.borrow_mut().state = State::CompletelyAvailable;
471 LoadBlocker::terminate(&self.current_request.borrow().blocker, cx);
472 self.upcast::<Node>().dirty(NodeDamage::Other);
474 self.resolve_image_decode_promises();
475 }
476
477 fn process_image_response(&self, image: ImageResponse, cx: &mut js::context::JSContext) {
479 let (trigger_image_load, trigger_image_error) = match (image, self.image_request.get()) {
485 (ImageResponse::Loaded(image, url), ImageRequestPhase::Current) => {
486 self.handle_loaded_image(image, url, cx);
487 (true, false)
488 },
489 (ImageResponse::Loaded(image, url), ImageRequestPhase::Pending) => {
490 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
491 self.image_request.set(ImageRequestPhase::Current);
492 self.handle_loaded_image(image, url, cx);
493 (true, false)
494 },
495 (ImageResponse::MetadataLoaded(meta), ImageRequestPhase::Current) => {
496 self.current_request.borrow_mut().state = State::PartiallyAvailable;
501 self.current_request.borrow_mut().metadata = Some(meta);
502 (false, false)
503 },
504 (ImageResponse::MetadataLoaded(_), ImageRequestPhase::Pending) => {
505 self.pending_request.borrow_mut().state = State::PartiallyAvailable;
509 (false, false)
510 },
511 (ImageResponse::FailedToLoadOrDecode, ImageRequestPhase::Current) => {
512 self.abort_request(State::Broken, ImageRequestPhase::Current, cx);
518
519 self.load_broken_image_icon();
520
521 (false, true)
525 },
526 (ImageResponse::FailedToLoadOrDecode, ImageRequestPhase::Pending) => {
527 self.abort_request(State::Broken, ImageRequestPhase::Current, cx);
533 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
534
535 mem::swap(
537 &mut *self.current_request.borrow_mut(),
538 &mut *self.pending_request.borrow_mut(),
539 );
540 self.image_request.set(ImageRequestPhase::Current);
541
542 self.current_request.borrow_mut().state = State::Broken;
544
545 self.load_broken_image_icon();
546
547 (false, true)
549 },
550 };
551
552 if trigger_image_load {
554 self.upcast::<EventTarget>().fire_event(cx, atom!("load"));
556 self.upcast::<EventTarget>()
557 .fire_event(cx, atom!("loadend"));
558 }
559
560 if trigger_image_error {
562 self.upcast::<EventTarget>().fire_event(cx, atom!("error"));
563 self.upcast::<EventTarget>()
564 .fire_event(cx, atom!("loadend"));
565 }
566 }
567
568 fn process_image_response_for_environment_change(
571 &self,
572 image: ImageResponse,
573 selected_source: USVString,
574 generation: u32,
575 selected_pixel_density: f64,
576 cx: &mut js::context::JSContext,
577 ) {
578 match image {
579 ImageResponse::Loaded(image, url) => {
580 self.pending_request.borrow_mut().metadata = Some(image.metadata());
581 self.pending_request.borrow_mut().final_url = Some(url);
582 self.pending_request.borrow_mut().image = Some(image);
583 self.finish_reacting_to_environment_change(
584 selected_source,
585 generation,
586 selected_pixel_density,
587 );
588 },
589 ImageResponse::FailedToLoadOrDecode => {
590 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
598 },
599 ImageResponse::MetadataLoaded(meta) => {
600 self.pending_request.borrow_mut().metadata = Some(meta);
601 },
602 };
603 }
604
605 fn abort_request(
607 &self,
608 state: State,
609 request: ImageRequestPhase,
610 cx: &mut js::context::JSContext,
611 ) {
612 let mut request = match request {
613 ImageRequestPhase::Current => self.current_request.borrow_mut(),
614 ImageRequestPhase::Pending => self.pending_request.borrow_mut(),
615 };
616 LoadBlocker::terminate(&request.blocker, cx);
617 request.state = state;
618 request.image = None;
619 request.metadata = None;
620 request.current_pixel_density = None;
621
622 if matches!(state, State::Broken) {
623 self.reject_image_decode_promises();
624 } else if matches!(state, State::CompletelyAvailable) {
625 self.resolve_image_decode_promises();
626 }
627 }
628
629 fn create_source_set(&self) -> SourceSet {
631 let element = self.upcast::<Element>();
632
633 let mut source_set = SourceSet::new();
635
636 if let Some(srcset) = element.get_attribute_string_value(&local_name!("srcset")) {
639 source_set.image_sources = parse_a_srcset_attribute(&srcset);
640 }
641
642 if let Some(sizes) = element.get_attribute_string_value(&local_name!("sizes")) {
644 source_set.source_size = parse_a_sizes_attribute(&sizes);
645 }
646
647 let src = element.get_string_attribute(&local_name!("src"));
651 let no_density_source_of_1 = source_set
652 .image_sources
653 .iter()
654 .all(|source| source.descriptor.density != Some(1.));
655 let no_width_descriptor = source_set
656 .image_sources
657 .iter()
658 .all(|source| source.descriptor.width.is_none());
659 if !src.is_empty() && no_density_source_of_1 && no_width_descriptor {
660 source_set.image_sources.push(ImageSource {
661 url: String::from(src),
662 descriptor: Descriptor {
663 width: None,
664 density: None,
665 },
666 })
667 }
668
669 self.normalise_source_densities(&mut source_set);
671
672 source_set
674 }
675
676 fn update_source_set(&self) {
678 *self.source_set.borrow_mut() = SourceSet::new();
680
681 let elem = self.upcast::<Element>();
686 let parent = elem.upcast::<Node>().GetParentElement();
687 let elements = match parent.as_ref() {
688 Some(p) => {
689 if p.is::<HTMLPictureElement>() {
690 p.upcast::<Node>()
691 .children()
692 .filter_map(DomRoot::downcast::<Element>)
693 .map(|n| DomRoot::from_ref(&*n))
694 .collect()
695 } else {
696 vec![DomRoot::from_ref(elem)]
697 }
698 },
699 None => vec![DomRoot::from_ref(elem)],
700 };
701
702 for element in &elements {
704 if *element == DomRoot::from_ref(elem) {
706 *self.source_set.borrow_mut() = self.create_source_set();
709
710 return;
712 }
713 if !element.is::<HTMLSourceElement>() {
715 continue;
716 }
717
718 let mut source_set = SourceSet::new();
719
720 match element.get_attribute_string_value(&local_name!("srcset")) {
724 Some(srcset) => {
725 source_set.image_sources = parse_a_srcset_attribute(&srcset);
726 },
727 _ => continue,
728 }
729
730 if source_set.image_sources.is_empty() {
732 continue;
733 }
734
735 if let Some(media) = element.get_attribute_string_value(&local_name!("media")) &&
738 !MediaList::matches_environment(&element.owner_document(), &media)
739 {
740 continue;
741 }
742
743 if let Some(sizes) = element.get_attribute_string_value(&local_name!("sizes")) {
746 source_set.source_size = parse_a_sizes_attribute(&sizes);
747 }
748
749 if let Some(type_) = element.get_attribute_string_value(&local_name!("type")) &&
752 !is_supported_image_mime_type(&type_)
753 {
754 continue;
755 }
756
757 if element.has_attribute(&local_name!("width")) ||
760 element.has_attribute(&local_name!("height"))
761 {
762 self.dimension_attribute_source.set(Some(element));
763 } else {
764 self.dimension_attribute_source.set(Some(elem));
765 }
766
767 self.normalise_source_densities(&mut source_set);
769
770 *self.source_set.borrow_mut() = source_set;
772
773 return;
775 }
776 }
777
778 fn evaluate_source_size_list(&self, source_size_list: &SourceSizeList) -> Au {
779 let document = self.owner_document();
780 let quirks_mode = document.quirks_mode();
781 source_size_list.evaluate(document.window().layout().device(), quirks_mode)
782 }
783
784 fn normalise_source_densities(&self, source_set: &mut SourceSet) {
786 let source_size = self.evaluate_source_size_list(&source_set.source_size);
788
789 for image_source in &mut source_set.image_sources {
791 if image_source.descriptor.density.is_some() {
794 continue;
795 }
796
797 if let Some(width) = image_source.descriptor.width {
801 image_source.descriptor.density = Some(width as f64 / source_size.to_f64_px());
802 } else {
803 image_source.descriptor.density = Some(1_f64);
805 }
806 }
807 }
808
809 fn select_image_source(&self) -> Option<(USVString, f64)> {
811 self.update_source_set();
813
814 if self.source_set.borrow().image_sources.is_empty() {
817 return None;
818 }
819
820 self.select_image_source_from_source_set()
822 }
823
824 fn select_image_source_from_source_set(&self) -> Option<(USVString, f64)> {
826 let source_set = self.source_set.borrow();
831 let len = source_set.image_sources.len();
832
833 let mut repeat_indices = FxHashSet::default();
835 for outer_index in 0..len {
836 if repeat_indices.contains(&outer_index) {
837 continue;
838 }
839 let imgsource = &source_set.image_sources[outer_index];
840 let pixel_density = imgsource.descriptor.density.unwrap();
841 for inner_index in (outer_index + 1)..len {
842 let imgsource2 = &source_set.image_sources[inner_index];
843 if pixel_density == imgsource2.descriptor.density.unwrap() {
844 repeat_indices.insert(inner_index);
845 }
846 }
847 }
848
849 let mut max = (0f64, 0);
850 let img_sources = &mut vec![];
851 for (index, image_source) in source_set.image_sources.iter().enumerate() {
852 if repeat_indices.contains(&index) {
853 continue;
854 }
855 let den = image_source.descriptor.density.unwrap();
856 if max.0 < den {
857 max = (den, img_sources.len());
858 }
859 img_sources.push(image_source);
860 }
861
862 let mut best_candidate = max;
865 let device_pixel_ratio = self
866 .owner_document()
867 .window()
868 .viewport_details()
869 .hidpi_scale_factor
870 .get() as f64;
871 for (index, image_source) in img_sources.iter().enumerate() {
872 let current_den = image_source.descriptor.density.unwrap();
873 if current_den < best_candidate.0 && current_den >= device_pixel_ratio {
874 best_candidate = (current_den, index);
875 }
876 }
877 let selected_source = img_sources.remove(best_candidate.1).clone();
878
879 Some((
881 USVString(selected_source.url),
882 selected_source.descriptor.density.unwrap(),
883 ))
884 }
885
886 fn init_image_request(
887 &self,
888 request: &mut RefMut<'_, ImageRequest>,
889 url: &ServoUrl,
890 src: &USVString,
891 cx: &mut js::context::JSContext,
892 ) {
893 request.parsed_url = Some(url.clone());
894 request.source_url = Some(src.clone());
895 request.image = None;
896 request.metadata = None;
897 let document = self.owner_document();
898 LoadBlocker::terminate(&request.blocker, cx);
899 *request.blocker.borrow_mut() =
900 Some(LoadBlocker::new(&document, LoadType::Image(url.clone())));
901 }
902
903 fn prepare_image_request(
905 &self,
906 selected_source: &USVString,
907 selected_pixel_density: f64,
908 image_url: &ServoUrl,
909 cx: &mut js::context::JSContext,
910 ) {
911 match self.image_request.get() {
912 ImageRequestPhase::Pending => {
913 if self
916 .pending_request
917 .borrow()
918 .parsed_url
919 .as_ref()
920 .is_some_and(|parsed_url| *parsed_url == *image_url)
921 {
922 return;
923 }
924 },
925 ImageRequestPhase::Current => {
926 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
928
929 let mut current_request = self.current_request.borrow_mut();
932 let mut pending_request = self.pending_request.borrow_mut();
933
934 match (current_request.parsed_url.as_ref(), current_request.state) {
935 (Some(parsed_url), State::PartiallyAvailable) => {
936 if *parsed_url == *image_url {
942 return;
944 }
945
946 self.image_request.set(ImageRequestPhase::Pending);
950 self.init_image_request(
951 &mut pending_request,
952 image_url,
953 selected_source,
954 cx,
955 );
956 pending_request.current_pixel_density = Some(selected_pixel_density);
957 },
958 (_, State::Broken) | (_, State::Unavailable) => {
959 self.init_image_request(
963 &mut current_request,
964 image_url,
965 selected_source,
966 cx,
967 );
968 current_request.current_pixel_density = Some(selected_pixel_density);
969 self.reject_image_decode_promises();
970 },
971 (_, _) => {
972 self.image_request.set(ImageRequestPhase::Pending);
976 self.init_image_request(
977 &mut pending_request,
978 image_url,
979 selected_source,
980 cx,
981 );
982 pending_request.current_pixel_density = Some(selected_pixel_density);
983 },
984 }
985 },
986 }
987
988 self.fetch_image(image_url, cx);
989 }
990
991 fn update_the_image_data_sync_steps(&self, cx: &mut js::context::JSContext) {
993 let Some((selected_source, selected_pixel_density)) = self.select_image_source() else {
996 self.abort_request(State::Broken, ImageRequestPhase::Current, cx);
1001 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
1002 self.image_request.set(ImageRequestPhase::Current);
1003
1004 let this = Trusted::new(self);
1007
1008 self.owner_global().task_manager().dom_manipulation_task_source().queue(
1009 task!(image_null_source_error: move |cx| {
1010 let this = this.root();
1011
1012 {
1014 let mut current_request =
1015 this.current_request.borrow_mut();
1016 current_request.source_url = None;
1017 current_request.parsed_url = None;
1018 }
1019
1020 let has_src_attribute = this.upcast::<Element>().has_attribute(&local_name!("src"));
1026
1027 if has_src_attribute || this.uses_srcset_or_picture() {
1028 this.upcast::<EventTarget>().fire_event(cx, atom!("error"));
1029 }
1030 }));
1031
1032 return;
1034 };
1035
1036 let Ok(image_url) = self.owner_document().base_url().join(&selected_source) else {
1039 self.abort_request(State::Broken, ImageRequestPhase::Current, cx);
1044 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
1045
1046 self.image_request.set(ImageRequestPhase::Current);
1048
1049 let this = Trusted::new(self);
1052
1053 self.owner_global()
1054 .task_manager()
1055 .dom_manipulation_task_source()
1056 .queue(task!(image_selected_source_error: move |cx| {
1057 let this = this.root();
1058
1059 {
1061 let mut current_request =
1062 this.current_request.borrow_mut();
1063 current_request.source_url = Some(selected_source);
1064 current_request.parsed_url = None;
1065 }
1066
1067 this.upcast::<EventTarget>().fire_event(cx, atom!("error"));
1071 }));
1072
1073 return;
1075 };
1076
1077 self.prepare_image_request(&selected_source, selected_pixel_density, &image_url, cx);
1078 }
1079
1080 pub(crate) fn update_the_image_data(&self, cx: &mut js::context::JSContext) {
1082 self.generation.set(self.generation.get() + 1);
1084
1085 if !self.owner_document().is_active() {
1087 }
1093
1094 self.current_request.borrow_mut().state = State::Unavailable;
1103
1104 let mut selected_source = None;
1108 let mut selected_pixel_density = None;
1109
1110 let src = self
1114 .upcast::<Element>()
1115 .get_string_attribute(&local_name!("src"));
1116
1117 if !self.uses_srcset_or_picture() && !src.is_empty() {
1118 selected_source = Some(USVString(String::from(src)));
1119 selected_pixel_density = Some(1_f64);
1120 };
1121
1122 self.last_selected_source
1124 .borrow_mut()
1125 .clone_from(&selected_source);
1126
1127 if let Some(selected_source) = selected_source {
1129 if let Ok(image_url) = self.owner_document().base_url().join(&selected_source) {
1133 let window = self.owner_window();
1137 let response = window.image_cache().get_image(
1138 image_url.clone(),
1139 window.origin().immutable().clone(),
1140 cors_setting_for_element(self.upcast()),
1141 );
1142
1143 if let Some(image) = response {
1145 self.abort_request(State::CompletelyAvailable, ImageRequestPhase::Current, cx);
1150 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
1151
1152 self.image_request.set(ImageRequestPhase::Current);
1154
1155 let mut current_request = self.current_request.borrow_mut();
1158 current_request.metadata = Some(image.metadata());
1159 current_request.image = Some(image);
1160 current_request.final_url = Some(image_url.clone());
1161
1162 self.upcast::<Node>().dirty(NodeDamage::Other);
1165
1166 current_request.current_pixel_density = selected_pixel_density;
1169
1170 let this = Trusted::new(self);
1173
1174 self.owner_global()
1175 .task_manager()
1176 .dom_manipulation_task_source()
1177 .queue(task!(image_load_event: move |cx| {
1178 let this = this.root();
1179
1180 {
1185 let mut current_request =
1186 this.current_request.borrow_mut();
1187 current_request.source_url = Some(selected_source);
1188 current_request.parsed_url = Some(image_url);
1189 }
1190
1191 this.upcast::<EventTarget>().fire_event(cx, atom!("load"));
1195 }));
1196
1197 return;
1199 }
1200 }
1201 }
1202
1203 let task = ImageElementMicrotask::UpdateImageData {
1206 elem: DomRoot::from_ref(self),
1207 generation: self.generation.get(),
1208 };
1209
1210 ScriptThread::await_stable_state(Microtask::ImageElement(task));
1211 }
1212
1213 pub(crate) fn react_to_environment_changes(&self) {
1215 let task = ImageElementMicrotask::EnvironmentChanges {
1217 elem: DomRoot::from_ref(self),
1218 generation: self.generation.get(),
1219 };
1220
1221 ScriptThread::await_stable_state(Microtask::ImageElement(task));
1222 }
1223
1224 fn react_to_environment_changes_sync_steps(
1226 &self,
1227 generation: u32,
1228 cx: &mut js::context::JSContext,
1229 ) {
1230 let document = self.owner_document();
1231 let has_pending_request = matches!(self.image_request.get(), ImageRequestPhase::Pending);
1232
1233 if !document.is_active() || !self.uses_srcset_or_picture() || has_pending_request {
1237 return;
1238 }
1239
1240 let Some((selected_source, selected_pixel_density)) = self.select_image_source() else {
1243 return;
1245 };
1246
1247 let mut same_selected_source = self
1250 .last_selected_source
1251 .borrow()
1252 .as_ref()
1253 .is_some_and(|source| *source == selected_source);
1254
1255 same_selected_source = same_selected_source ||
1259 self.current_request
1260 .borrow()
1261 .source_url
1262 .as_ref()
1263 .is_some_and(|source| *source == selected_source);
1264
1265 let same_selected_pixel_density = self
1266 .current_request
1267 .borrow()
1268 .current_pixel_density
1269 .is_some_and(|pixel_density| pixel_density == selected_pixel_density);
1270
1271 if same_selected_source && same_selected_pixel_density {
1272 return;
1273 }
1274
1275 let Ok(image_url) = document.base_url().join(&selected_source) else {
1279 return;
1280 };
1281
1282 self.image_request.set(ImageRequestPhase::Pending);
1284 self.init_image_request(
1285 &mut self.pending_request.borrow_mut(),
1286 &image_url,
1287 &selected_source,
1288 cx,
1289 );
1290
1291 let window = self.owner_window();
1294 let cache_result = window.image_cache().get_cached_image_status(
1295 image_url.clone(),
1296 window.origin().immutable().clone(),
1297 cors_setting_for_element(self.upcast()),
1298 );
1299
1300 let change_type = ChangeType::Environment {
1301 selected_source: selected_source.clone(),
1302 selected_pixel_density,
1303 };
1304
1305 match cache_result {
1306 ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable { .. }) => {
1307 self.finish_reacting_to_environment_change(
1308 selected_source,
1309 generation,
1310 selected_pixel_density,
1311 );
1312 },
1313 ImageCacheResult::Available(ImageOrMetadataAvailable::MetadataAvailable(m, id)) => {
1314 self.process_image_response_for_environment_change(
1315 ImageResponse::MetadataLoaded(m),
1316 selected_source,
1317 generation,
1318 selected_pixel_density,
1319 cx,
1320 );
1321 self.register_image_cache_callback(id, change_type);
1322 },
1323 ImageCacheResult::FailedToLoadOrDecode => {
1324 self.process_image_response_for_environment_change(
1325 ImageResponse::FailedToLoadOrDecode,
1326 selected_source,
1327 generation,
1328 selected_pixel_density,
1329 cx,
1330 );
1331 },
1332 ImageCacheResult::ReadyForRequest(id) => {
1333 self.fetch_request(&image_url, id);
1334 self.register_image_cache_callback(id, change_type);
1335 },
1336 ImageCacheResult::Pending(id) => {
1337 self.register_image_cache_callback(id, change_type);
1338 },
1339 }
1340 }
1341
1342 fn react_to_decode_image_sync_steps(&self, cx: &mut JSContext, promise: Rc<Promise>) {
1344 if !self.owner_document().is_fully_active() ||
1348 matches!(self.current_request.borrow().state, State::Broken)
1349 {
1350 promise.reject_error_with_cx(cx, Error::Encoding(None));
1351 } else if matches!(
1352 self.current_request.borrow().state,
1353 State::CompletelyAvailable
1354 ) {
1355 promise.resolve_native_with_cx(cx, &());
1357 } else if matches!(self.current_request.borrow().state, State::Unavailable) &&
1358 self.current_request.borrow().source_url.is_none()
1359 {
1360 promise.reject_error_with_cx(cx, Error::Encoding(None));
1365 } else {
1366 self.image_decode_promises.borrow_mut().push(promise);
1367 }
1368 }
1369
1370 fn resolve_image_decode_promises(&self) {
1372 if self.image_decode_promises.borrow().is_empty() {
1373 return;
1374 }
1375
1376 let trusted_image_decode_promises: Vec<TrustedPromise> = self
1379 .image_decode_promises
1380 .borrow()
1381 .iter()
1382 .map(|promise| TrustedPromise::new(promise.clone()))
1383 .collect();
1384
1385 self.image_decode_promises.borrow_mut().clear();
1386
1387 self.owner_global()
1388 .task_manager()
1389 .dom_manipulation_task_source()
1390 .queue(task!(fulfill_image_decode_promises: move |cx| {
1391 for trusted_promise in trusted_image_decode_promises {
1392 trusted_promise.root().resolve_native_with_cx(cx, &());
1393 }
1394 }));
1395 }
1396
1397 fn reject_image_decode_promises(&self) {
1399 if self.image_decode_promises.borrow().is_empty() {
1400 return;
1401 }
1402
1403 let trusted_image_decode_promises: Vec<TrustedPromise> = self
1406 .image_decode_promises
1407 .borrow()
1408 .iter()
1409 .map(|promise| TrustedPromise::new(promise.clone()))
1410 .collect();
1411
1412 self.image_decode_promises.borrow_mut().clear();
1413
1414 self.owner_global()
1415 .task_manager()
1416 .dom_manipulation_task_source()
1417 .queue(task!(reject_image_decode_promises: move |cx| {
1418 for trusted_promise in trusted_image_decode_promises {
1419 trusted_promise.root().reject_error_with_cx(cx, Error::Encoding(None));
1420 }
1421 }));
1422 }
1423
1424 fn finish_reacting_to_environment_change(
1426 &self,
1427 selected_source: USVString,
1428 generation: u32,
1429 selected_pixel_density: f64,
1430 ) {
1431 let this = Trusted::new(self);
1434
1435 self.owner_global()
1436 .task_manager()
1437 .dom_manipulation_task_source()
1438 .queue(task!(image_load_event: move |cx| {
1439 let this = this.root();
1440
1441 if this.generation.get() != generation {
1444 this.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
1445 this.image_request.set(ImageRequestPhase::Current);
1446 return;
1447 }
1448
1449 *this.last_selected_source.borrow_mut() = Some(selected_source);
1452
1453 {
1454 let mut pending_request = this.pending_request.borrow_mut();
1455
1456 pending_request.state = State::CompletelyAvailable;
1458
1459 pending_request.current_pixel_density = Some(selected_pixel_density);
1460
1461 mem::swap(&mut *this.current_request.borrow_mut(), &mut *pending_request);
1467 }
1468
1469 this.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
1470 this.image_request.set(ImageRequestPhase::Current);
1471
1472 this.upcast::<Node>().dirty(NodeDamage::Other);
1474
1475 this.upcast::<EventTarget>().fire_event(cx, atom!("load"));
1477 }));
1478 }
1479
1480 fn uses_srcset_or_picture(&self) -> bool {
1482 let element = self.upcast::<Element>();
1483
1484 let has_srcset_attribute = element.has_attribute(&local_name!("srcset"));
1485 let has_parent_picture = element
1486 .upcast::<Node>()
1487 .GetParentElement()
1488 .is_some_and(|parent| parent.is::<HTMLPictureElement>());
1489 has_srcset_attribute || has_parent_picture
1490 }
1491
1492 fn new_inherited(
1493 local_name: LocalName,
1494 prefix: Option<Prefix>,
1495 document: &Document,
1496 creator: ElementCreator,
1497 ) -> HTMLImageElement {
1498 HTMLImageElement {
1499 htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
1500 image_request: Cell::new(ImageRequestPhase::Current),
1501 current_request: DomRefCell::new(ImageRequest {
1502 state: State::Unavailable,
1503 parsed_url: None,
1504 source_url: None,
1505 image: None,
1506 metadata: None,
1507 blocker: DomRefCell::new(None),
1508 final_url: None,
1509 current_pixel_density: None,
1510 }),
1511 pending_request: DomRefCell::new(ImageRequest {
1512 state: State::Unavailable,
1513 parsed_url: None,
1514 source_url: None,
1515 image: None,
1516 metadata: None,
1517 blocker: DomRefCell::new(None),
1518 final_url: None,
1519 current_pixel_density: None,
1520 }),
1521 form_owner: Default::default(),
1522 generation: Default::default(),
1523 source_set: DomRefCell::new(SourceSet::new()),
1524 dimension_attribute_source: Default::default(),
1525 last_selected_source: DomRefCell::new(None),
1526 image_decode_promises: DomRefCell::new(vec![]),
1527 line_number: creator.return_line_number(),
1528 }
1529 }
1530
1531 pub(crate) fn new(
1532 cx: &mut js::context::JSContext,
1533 local_name: LocalName,
1534 prefix: Option<Prefix>,
1535 document: &Document,
1536 proto: Option<HandleObject>,
1537 creator: ElementCreator,
1538 ) -> DomRoot<HTMLImageElement> {
1539 let image_element = Node::reflect_node_with_proto(
1540 cx,
1541 Box::new(HTMLImageElement::new_inherited(
1542 local_name, prefix, document, creator,
1543 )),
1544 document,
1545 proto,
1546 );
1547 image_element
1548 .dimension_attribute_source
1549 .set(Some(image_element.upcast()));
1550 image_element
1551 }
1552
1553 pub(crate) fn areas(&self) -> Option<Vec<DomRoot<HTMLAreaElement>>> {
1554 let elem = self.upcast::<Element>();
1555 let value = elem.get_attribute_string_value(&local_name!("usemap"))?;
1556
1557 if value.is_empty() || !value.is_char_boundary(1) {
1558 return None;
1559 }
1560
1561 let (first, last) = value.split_at(1);
1562
1563 if first != "#" || last.is_empty() {
1564 return None;
1565 }
1566
1567 let useMapElements = self
1568 .owner_document()
1569 .upcast::<Node>()
1570 .traverse_preorder(ShadowIncluding::No)
1571 .filter_map(DomRoot::downcast::<HTMLMapElement>)
1572 .find(|n| {
1573 n.upcast::<Element>()
1574 .get_name()
1575 .is_some_and(|n| *n == *last)
1576 });
1577
1578 useMapElements.map(|mapElem| mapElem.get_area_elements())
1579 }
1580
1581 pub(crate) fn same_origin(&self, origin: &MutableOrigin) -> bool {
1582 if let Some(ref image) = self.current_request.borrow().image {
1583 return image.cors_status() == CorsStatus::Safe;
1584 }
1585
1586 self.current_request
1587 .borrow()
1588 .final_url
1589 .as_ref()
1590 .is_some_and(|url| url.scheme() == "data" || url.origin().same_origin(origin))
1591 }
1592
1593 fn generation_id(&self) -> u32 {
1594 self.generation.get()
1595 }
1596
1597 fn load_broken_image_icon(&self) {
1598 let window = self.owner_window();
1599 let Some(broken_image_icon) = window.image_cache().get_broken_image_icon() else {
1600 return;
1601 };
1602
1603 self.current_request.borrow_mut().metadata = Some(broken_image_icon.metadata);
1604 self.current_request.borrow_mut().image = Some(Image::Raster(broken_image_icon));
1605 self.upcast::<Node>().dirty(NodeDamage::Other);
1606 }
1607
1608 pub(crate) fn full_image_url_for_user_interface(&self) -> Option<ServoUrl> {
1611 self.owner_document()
1612 .base_url()
1613 .join(&self.CurrentSrc())
1614 .ok()
1615 }
1616}
1617
1618#[derive(JSTraceable, MallocSizeOf)]
1619pub(crate) enum ImageElementMicrotask {
1620 UpdateImageData {
1621 elem: DomRoot<HTMLImageElement>,
1622 generation: u32,
1623 },
1624 EnvironmentChanges {
1625 elem: DomRoot<HTMLImageElement>,
1626 generation: u32,
1627 },
1628 Decode {
1629 elem: DomRoot<HTMLImageElement>,
1630 #[conditional_malloc_size_of]
1631 promise: Rc<Promise>,
1632 },
1633}
1634
1635impl MicrotaskRunnable for ImageElementMicrotask {
1636 fn handler(&self, cx: &mut js::context::JSContext) {
1637 match *self {
1638 ImageElementMicrotask::UpdateImageData {
1639 ref elem,
1640 ref generation,
1641 } => {
1642 if elem.generation.get() == *generation {
1646 elem.update_the_image_data_sync_steps(cx);
1647 }
1648 },
1649 ImageElementMicrotask::EnvironmentChanges {
1650 ref elem,
1651 ref generation,
1652 } => {
1653 elem.react_to_environment_changes_sync_steps(*generation, cx);
1654 },
1655 ImageElementMicrotask::Decode {
1656 ref elem,
1657 ref promise,
1658 } => {
1659 elem.react_to_decode_image_sync_steps(cx, promise.clone());
1660 },
1661 }
1662 }
1663
1664 fn enter_realm<'cx>(&self, cx: &'cx mut js::context::JSContext) -> AutoRealm<'cx> {
1665 match self {
1666 &ImageElementMicrotask::UpdateImageData { ref elem, .. } |
1667 &ImageElementMicrotask::EnvironmentChanges { ref elem, .. } |
1668 &ImageElementMicrotask::Decode { ref elem, .. } => enter_auto_realm(cx, &**elem),
1669 }
1670 }
1671}
1672
1673impl<'dom> LayoutDom<'dom, HTMLImageElement> {
1674 #[expect(unsafe_code)]
1675 fn current_request(self) -> &'dom ImageRequest {
1676 unsafe { self.unsafe_get().current_request.borrow_for_layout() }
1677 }
1678
1679 #[expect(unsafe_code)]
1680 fn dimension_attribute_source(self) -> LayoutDom<'dom, Element> {
1681 unsafe {
1682 self.unsafe_get()
1683 .dimension_attribute_source
1684 .get_inner_as_layout()
1685 .expect("dimension attribute source should be always non-null")
1686 }
1687 }
1688
1689 pub(crate) fn image_url(self) -> Option<ServoUrl> {
1690 self.current_request().parsed_url.clone()
1691 }
1692
1693 pub(crate) fn image_data(self) -> (Option<Image>, Option<ImageMetadata>) {
1694 let current_request = self.current_request();
1695 (current_request.image.clone(), current_request.metadata)
1696 }
1697
1698 pub(crate) fn image_density(self) -> Option<f64> {
1699 self.current_request().current_pixel_density
1700 }
1701
1702 pub(crate) fn showing_broken_image_icon(self) -> bool {
1703 matches!(self.current_request().state, State::Broken)
1704 }
1705
1706 pub(crate) fn get_width(self) -> LengthOrPercentageOrAuto {
1707 self.dimension_attribute_source()
1708 .get_attr_for_layout(&ns!(), &local_name!("width"))
1709 .map(AttrValue::as_dimension)
1710 .cloned()
1711 .unwrap_or(LengthOrPercentageOrAuto::Auto)
1712 }
1713
1714 pub(crate) fn get_height(self) -> LengthOrPercentageOrAuto {
1715 self.dimension_attribute_source()
1716 .get_attr_for_layout(&ns!(), &local_name!("height"))
1717 .map(AttrValue::as_dimension)
1718 .cloned()
1719 .unwrap_or(LengthOrPercentageOrAuto::Auto)
1720 }
1721}
1722
1723fn parse_a_sizes_attribute(value: &str) -> SourceSizeList {
1725 let mut input = ParserInput::new(value);
1726 let mut parser = Parser::new(&mut input);
1727 let url_data = Url::parse("about:blank").unwrap().into();
1728 let context =
1731 parser_context_for_anonymous_content(CssRuleType::Style, ParsingMode::empty(), &url_data);
1732 SourceSizeList::parse(&context, &mut parser)
1733}
1734
1735impl HTMLImageElementMethods<crate::DomTypeHolder> for HTMLImageElement {
1736 fn Image(
1738 cx: &mut JSContext,
1739 window: &Window,
1740 proto: Option<HandleObject>,
1741 width: Option<u32>,
1742 height: Option<u32>,
1743 ) -> Fallible<DomRoot<HTMLImageElement>> {
1744 let document = window.Document();
1746
1747 let element = Element::create(
1750 cx,
1751 QualName::new(None, ns!(html), local_name!("img")),
1752 None,
1753 &document,
1754 ElementCreator::ScriptCreated,
1755 CustomElementCreationMode::Synchronous,
1756 proto,
1757 );
1758
1759 let image = DomRoot::downcast::<HTMLImageElement>(element).unwrap();
1760
1761 if let Some(w) = width {
1763 image.SetWidth(cx, w);
1764 }
1765
1766 if let Some(h) = height {
1769 image.SetHeight(cx, h);
1770 }
1771
1772 Ok(image)
1774 }
1775
1776 make_getter!(Alt, "alt");
1778 make_setter!(SetAlt, "alt");
1780
1781 make_url_getter!(Src, "src");
1783
1784 make_url_setter!(SetSrc, "src");
1786
1787 make_url_getter!(Srcset, "srcset");
1789 make_url_setter!(SetSrcset, "srcset");
1791
1792 make_getter!(Sizes, "sizes");
1794
1795 make_setter!(SetSizes, "sizes");
1797
1798 fn GetCrossOrigin(&self) -> Option<DOMString> {
1800 reflect_cross_origin_attribute(self.upcast::<Element>())
1801 }
1802
1803 fn SetCrossOrigin(&self, cx: &mut JSContext, value: Option<DOMString>) {
1805 set_cross_origin_attribute(cx, self.upcast::<Element>(), value);
1806 }
1807
1808 make_getter!(UseMap, "usemap");
1810 make_setter!(SetUseMap, "usemap");
1812
1813 make_bool_getter!(IsMap, "ismap");
1815 make_bool_setter!(SetIsMap, "ismap");
1817
1818 fn Width(&self) -> u32 {
1820 let node = self.upcast::<Node>();
1821 node.content_box()
1822 .map(|rect| rect.size.width.to_px() as u32)
1823 .unwrap_or_else(|| self.NaturalWidth())
1824 }
1825
1826 make_dimension_uint_setter!(SetWidth, "width");
1828
1829 fn Height(&self) -> u32 {
1831 let node = self.upcast::<Node>();
1832 node.content_box()
1833 .map(|rect| rect.size.height.to_px() as u32)
1834 .unwrap_or_else(|| self.NaturalHeight())
1835 }
1836
1837 make_dimension_uint_setter!(SetHeight, "height");
1839
1840 fn NaturalWidth(&self) -> u32 {
1842 let request = self.current_request.borrow();
1843 if matches!(request.state, State::Broken) {
1844 return 0;
1845 }
1846
1847 let pixel_density = request.current_pixel_density.unwrap_or(1f64);
1848 match request.metadata {
1849 Some(ref metadata) => (metadata.width as f64 / pixel_density) as u32,
1850 None => 0,
1851 }
1852 }
1853
1854 fn NaturalHeight(&self) -> u32 {
1856 let request = self.current_request.borrow();
1857 if matches!(request.state, State::Broken) {
1858 return 0;
1859 }
1860
1861 let pixel_density = request.current_pixel_density.unwrap_or(1f64);
1862 match request.metadata {
1863 Some(ref metadata) => (metadata.height as f64 / pixel_density) as u32,
1864 None => 0,
1865 }
1866 }
1867
1868 fn Complete(&self) -> bool {
1870 let element = self.upcast::<Element>();
1871
1872 let has_srcset_attribute = element.has_attribute(&local_name!("srcset"));
1875 if !element.has_attribute(&local_name!("src")) && !has_srcset_attribute {
1876 return true;
1877 }
1878
1879 let src = element.get_string_attribute(&local_name!("src"));
1881 if !has_srcset_attribute && src.is_empty() {
1882 return true;
1883 }
1884
1885 if matches!(self.image_request.get(), ImageRequestPhase::Current) &&
1889 matches!(
1890 self.current_request.borrow().state,
1891 State::CompletelyAvailable | State::Broken
1892 )
1893 {
1894 return true;
1895 }
1896
1897 false
1899 }
1900
1901 fn CurrentSrc(&self) -> USVString {
1903 let current_request = self.current_request.borrow();
1904 let url = ¤t_request.parsed_url;
1905 match *url {
1906 Some(ref url) => USVString(url.clone().into_string()),
1907 None => {
1908 let unparsed_url = ¤t_request.source_url;
1909 match *unparsed_url {
1910 Some(ref url) => url.clone(),
1911 None => USVString("".to_owned()),
1912 }
1913 },
1914 }
1915 }
1916
1917 fn ReferrerPolicy(&self) -> DOMString {
1919 reflect_referrer_policy_attribute(self.upcast::<Element>())
1920 }
1921
1922 make_setter!(SetReferrerPolicy, "referrerpolicy");
1924
1925 fn Decode(&self, cx: &mut JSContext) -> Rc<Promise> {
1927 let promise = Promise::new2(cx, &self.global());
1929
1930 let task = ImageElementMicrotask::Decode {
1932 elem: DomRoot::from_ref(self),
1933 promise: promise.clone(),
1934 };
1935
1936 ScriptThread::await_stable_state(Microtask::ImageElement(task));
1937
1938 promise
1940 }
1941
1942 make_getter!(Name, "name");
1944
1945 make_atomic_setter!(SetName, "name");
1947
1948 make_getter!(Align, "align");
1950
1951 make_setter!(SetAlign, "align");
1953
1954 make_uint_getter!(Hspace, "hspace");
1956
1957 make_uint_setter!(SetHspace, "hspace");
1959
1960 make_uint_getter!(Vspace, "vspace");
1962
1963 make_uint_setter!(SetVspace, "vspace");
1965
1966 make_url_getter!(LongDesc, "longdesc");
1968
1969 make_url_setter!(SetLongDesc, "longdesc");
1971
1972 make_getter!(Border, "border");
1974
1975 make_setter!(SetBorder, "border");
1977}
1978
1979impl VirtualMethods for HTMLImageElement {
1980 fn super_type(&self) -> Option<&dyn VirtualMethods> {
1981 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
1982 }
1983
1984 fn adopting_steps(&self, cx: &mut JSContext, old_doc: &Document) {
1985 self.super_type().unwrap().adopting_steps(cx, old_doc);
1986 self.update_the_image_data(cx);
1987 }
1988
1989 fn attribute_mutated(
1990 &self,
1991 cx: &mut js::context::JSContext,
1992 attr: AttrRef<'_>,
1993 mutation: AttributeMutation,
1994 ) {
1995 self.super_type()
1996 .unwrap()
1997 .attribute_mutated(cx, attr, mutation);
1998 match attr.local_name() {
1999 &local_name!("src") |
2000 &local_name!("srcset") |
2001 &local_name!("width") |
2002 &local_name!("sizes") => {
2003 self.update_the_image_data(cx);
2007 },
2008 &local_name!("crossorigin") => {
2009 let cross_origin_state_changed = match mutation {
2012 AttributeMutation::Removed | AttributeMutation::Set(None, _) => true,
2013 AttributeMutation::Set(Some(old_value), _) => {
2014 let new_cors_setting =
2015 CorsSettings::from_enumerated_attribute(&attr.value());
2016 let old_cors_setting = CorsSettings::from_enumerated_attribute(old_value);
2017
2018 new_cors_setting != old_cors_setting
2019 },
2020 };
2021
2022 if cross_origin_state_changed {
2023 self.update_the_image_data(cx);
2024 }
2025 },
2026 &local_name!("referrerpolicy") => {
2027 let referrer_policy_state_changed = match mutation {
2030 AttributeMutation::Removed | AttributeMutation::Set(None, _) => {
2031 ReferrerPolicy::from(&**attr.value()) != ReferrerPolicy::EmptyString
2032 },
2033 AttributeMutation::Set(Some(old_value), _) => {
2034 ReferrerPolicy::from(&**attr.value()) != ReferrerPolicy::from(&**old_value)
2035 },
2036 };
2037
2038 if referrer_policy_state_changed {
2039 self.update_the_image_data(cx);
2040 }
2041 },
2042 _ => {},
2043 }
2044 }
2045
2046 fn attribute_affects_presentational_hints(&self, attr: AttrRef<'_>) -> bool {
2047 match attr.local_name() {
2048 &local_name!("width") | &local_name!("height") => true,
2049 _ => self
2050 .super_type()
2051 .unwrap()
2052 .attribute_affects_presentational_hints(attr),
2053 }
2054 }
2055
2056 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
2057 match name {
2058 &local_name!("width") | &local_name!("height") => {
2059 AttrValue::from_dimension(value.into())
2060 },
2061 &local_name!("hspace") | &local_name!("vspace") => AttrValue::from_u32(value.into(), 0),
2062 _ => self
2063 .super_type()
2064 .unwrap()
2065 .parse_plain_attribute(name, value),
2066 }
2067 }
2068
2069 fn handle_event(&self, cx: &mut js::context::JSContext, event: &Event) {
2070 if event.type_() != atom!("click") {
2071 return;
2072 }
2073
2074 let area_elements = self.areas();
2075 let elements = match area_elements {
2076 Some(x) => x,
2077 None => return,
2078 };
2079
2080 let mouse_event = match event.downcast::<MouseEvent>() {
2082 Some(x) => x,
2083 None => return,
2084 };
2085
2086 let point = Point2D::new(
2087 mouse_event.ClientX().to_f32().unwrap(),
2088 mouse_event.ClientY().to_f32().unwrap(),
2089 );
2090 let bcr = self.upcast::<Element>().GetBoundingClientRect(cx);
2091 let bcr_p = Point2D::new(bcr.X() as f32, bcr.Y() as f32);
2092
2093 for element in elements {
2095 let shape = element.get_shape_from_coords();
2096 let shp = match shape {
2097 Some(x) => x.absolute_coords(bcr_p),
2098 None => return,
2099 };
2100 if shp.hit_test(&point) {
2101 element.activation_behavior(cx, event, self.upcast());
2102 return;
2103 }
2104 }
2105 }
2106
2107 fn bind_to_tree(&self, cx: &mut JSContext, context: &BindContext) {
2109 if let Some(s) = self.super_type() {
2110 s.bind_to_tree(cx, context);
2111 }
2112 let document = self.owner_document();
2113 if context.tree_connected {
2114 document.register_responsive_image(self);
2115 }
2116
2117 let parent = self.upcast::<Node>().GetParentNode().unwrap();
2118
2119 if parent.is::<HTMLPictureElement>() && std::ptr::eq(&*parent, context.parent) {
2122 self.update_the_image_data(cx);
2123 }
2124 }
2125
2126 fn unbind_from_tree(&self, cx: &mut js::context::JSContext, context: &UnbindContext) {
2128 self.super_type().unwrap().unbind_from_tree(cx, context);
2129 let document = self.owner_document();
2130 document.unregister_responsive_image(self);
2131
2132 if context.parent.is::<HTMLPictureElement>() && !self.upcast::<Node>().has_parent() {
2135 self.update_the_image_data(cx);
2136 }
2137 }
2138
2139 fn moving_steps(&self, cx: &mut JSContext, context: &MoveContext) {
2141 if let Some(super_type) = self.super_type() {
2142 super_type.moving_steps(cx, context);
2143 }
2144
2145 if let Some(old_parent) = context.old_parent &&
2147 old_parent.is::<HTMLPictureElement>()
2148 {
2149 self.update_the_image_data(cx);
2150 }
2151 }
2152}
2153
2154impl FormControl for HTMLImageElement {
2155 fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
2156 self.form_owner.get()
2157 }
2158
2159 fn set_form_owner(&self, form: Option<&HTMLFormElement>) {
2160 self.form_owner.set(form);
2161 }
2162
2163 fn to_element(&self) -> &Element {
2164 self.upcast::<Element>()
2165 }
2166
2167 fn is_listed(&self) -> bool {
2168 false
2169 }
2170}
2171
2172pub(crate) fn collect_sequence_characters(
2175 s: &str,
2176 mut predicate: impl FnMut(&char) -> bool,
2177) -> (&str, &str) {
2178 let i = s.find(|ch| !predicate(&ch)).unwrap_or(s.len());
2179 (&s[0..i], &s[i..])
2180}
2181
2182fn is_valid_non_negative_integer_string(s: &str) -> bool {
2185 s.chars().all(|c| c.is_ascii_digit())
2186}
2187
2188fn is_valid_floating_point_number_string(s: &str) -> bool {
2191 static RE: LazyLock<Regex> =
2192 LazyLock::new(|| Regex::new(r"^-?(?:\d+\.\d+|\d+|\.\d+)(?:(e|E)(\+|\-)?\d+)?$").unwrap());
2193
2194 RE.is_match(s)
2195}
2196
2197pub fn parse_a_srcset_attribute(input: &str) -> Vec<ImageSource> {
2200 let mut current_index = 0;
2203
2204 let mut candidates = vec![];
2206 while current_index < input.len() {
2207 let remaining_string = &input[current_index..];
2208
2209 let (collected_characters, string_after_whitespace) =
2216 collect_sequence_characters(remaining_string, |character| {
2217 *character == ',' || character.is_ascii_whitespace()
2218 });
2219
2220 current_index += collected_characters.len();
2223
2224 if string_after_whitespace.is_empty() {
2226 return candidates;
2227 }
2228
2229 let (url, _) =
2232 collect_sequence_characters(string_after_whitespace, |c| !char::is_ascii_whitespace(c));
2233
2234 current_index += url.len();
2237
2238 let mut descriptors = Vec::new();
2240
2241 if url.ends_with(',') {
2245 let image_source = ImageSource {
2246 url: url.trim_end_matches(',').into(),
2247 descriptor: Descriptor {
2248 width: None,
2249 density: None,
2250 },
2251 };
2252 candidates.push(image_source);
2253 continue;
2254 }
2255
2256 let descriptors_string = &input[current_index..];
2259 let (spaces, descriptors_string) =
2260 collect_sequence_characters(descriptors_string, |character| {
2261 character.is_ascii_whitespace()
2262 });
2263 current_index += spaces.len();
2264
2265 let mut current_descriptor = String::new();
2267
2268 let mut state = ParseState::InDescriptor;
2270
2271 let mut characters = descriptors_string.chars();
2275 let mut character = characters.next();
2276 if let Some(character) = character {
2277 current_index += character.len_utf8();
2278 }
2279
2280 loop {
2281 match (state, character) {
2282 (ParseState::InDescriptor, Some(character)) if character.is_ascii_whitespace() => {
2283 if !current_descriptor.is_empty() {
2287 descriptors.push(current_descriptor);
2288 current_descriptor = String::new();
2289 state = ParseState::AfterDescriptor;
2290 }
2291 },
2292 (ParseState::InDescriptor, Some(',')) => {
2293 if !current_descriptor.is_empty() {
2297 descriptors.push(current_descriptor);
2298 }
2299 break;
2300 },
2301 (ParseState::InDescriptor, Some('(')) => {
2302 current_descriptor.push('(');
2304 state = ParseState::InParens;
2305 },
2306 (ParseState::InDescriptor, Some(character)) => {
2307 current_descriptor.push(character);
2309 },
2310 (ParseState::InDescriptor, None) => {
2311 if !current_descriptor.is_empty() {
2314 descriptors.push(current_descriptor);
2315 }
2316 break;
2317 },
2318 (ParseState::InParens, Some(')')) => {
2319 current_descriptor.push(')');
2321 state = ParseState::InDescriptor;
2322 },
2323 (ParseState::InParens, Some(character)) => {
2324 current_descriptor.push(character);
2326 },
2327 (ParseState::InParens, None) => {
2328 descriptors.push(current_descriptor);
2331 break;
2332 },
2333 (ParseState::AfterDescriptor, Some(character))
2334 if character.is_ascii_whitespace() =>
2335 {
2336 },
2338 (ParseState::AfterDescriptor, Some(_)) => {
2339 state = ParseState::InDescriptor;
2342 continue;
2343 },
2344 (ParseState::AfterDescriptor, None) => {
2345 break;
2347 },
2348 }
2349
2350 character = characters.next();
2351 if let Some(character) = character {
2352 current_index += character.len_utf8();
2353 }
2354 }
2355
2356 let mut error = false;
2358 let mut width: Option<u32> = None;
2360 let mut density: Option<f64> = None;
2362 let mut future_compat_h: Option<u32> = None;
2364
2365 for descriptor in descriptors.into_iter() {
2368 let Some(last_character) = descriptor.chars().last() else {
2369 break;
2370 };
2371
2372 let first_part_of_string = &descriptor[0..descriptor.len() - last_character.len_utf8()];
2373 match last_character {
2374 'w' if is_valid_non_negative_integer_string(first_part_of_string) &&
2381 density.is_none() &&
2382 width.is_none() =>
2383 {
2384 match parse_unsigned_integer(first_part_of_string.chars()) {
2385 Ok(number) if number > 0 => {
2386 width = Some(number);
2387 continue;
2388 },
2389 _ => error = true,
2390 }
2391 },
2392
2393 'x' if is_valid_floating_point_number_string(first_part_of_string) &&
2407 width.is_none() &&
2408 density.is_none() &&
2409 future_compat_h.is_none() =>
2410 {
2411 match first_part_of_string.parse::<f64>() {
2412 Ok(number) if number.is_finite() && number >= 0. => {
2413 density = Some(number);
2414 continue;
2415 },
2416 _ => error = true,
2417 }
2418 },
2419
2420 'h' if is_valid_non_negative_integer_string(first_part_of_string) &&
2429 future_compat_h.is_none() &&
2430 density.is_none() =>
2431 {
2432 match parse_unsigned_integer(first_part_of_string.chars()) {
2433 Ok(number) if number > 0 => {
2434 future_compat_h = Some(number);
2435 continue;
2436 },
2437 _ => error = true,
2438 }
2439 },
2440
2441 _ => error = true,
2444 }
2445
2446 if error {
2447 break;
2448 }
2449 }
2450
2451 if future_compat_h.is_some() && width.is_none() {
2453 error = true;
2454 }
2455
2456 if !error {
2460 let image_source = ImageSource {
2461 url: url.into(),
2462 descriptor: Descriptor { width, density },
2463 };
2464 candidates.push(image_source);
2465 }
2466
2467 }
2469 candidates
2470}
2471
2472#[derive(Clone)]
2473enum ChangeType {
2474 Environment {
2475 selected_source: USVString,
2476 selected_pixel_density: f64,
2477 },
2478 Element,
2479}
2480
2481fn is_supported_image_mime_type(input: &str) -> bool {
2483 let mime_type = input.trim();
2485
2486 let mime_type_essence = match mime_type.find(';') {
2488 Some(semi) => &mime_type[..semi],
2489 _ => mime_type,
2490 };
2491
2492 if mime_type_essence.is_empty() {
2497 return true;
2498 }
2499
2500 SUPPORTED_IMAGE_MIME_TYPES.contains(&mime_type_essence)
2501}