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::medialist::MediaList;
75use crate::dom::mouseevent::MouseEvent;
76use crate::dom::node::{
77 BindContext, MoveContext, Node, NodeDamage, NodeTraits, ShadowIncluding, UnbindContext,
78};
79use crate::dom::performance::performanceresourcetiming::InitiatorType;
80use crate::dom::promise::Promise;
81use crate::dom::virtualmethods::VirtualMethods;
82use crate::dom::window::Window;
83use crate::fetch::{RequestWithGlobalScope, create_a_potential_cors_request};
84use crate::microtask::{Microtask, MicrotaskRunnable};
85use crate::network_listener::{self, FetchResponseListener, ResourceTimingListener};
86use crate::realms::enter_auto_realm;
87use crate::script_runtime::CanGc;
88use crate::script_thread::ScriptThread;
89
90const SUPPORTED_IMAGE_MIME_TYPES: &[&str] = &[
94 "image/bmp",
95 "image/gif",
96 "image/jpeg",
97 "image/jpg",
98 "image/pjpeg",
99 "image/png",
100 "image/apng",
101 "image/x-png",
102 "image/svg+xml",
103 "image/vnd.microsoft.icon",
104 "image/x-icon",
105 "image/webp",
106];
107
108#[derive(Clone, Copy, Debug)]
109enum ParseState {
110 InDescriptor,
111 InParens,
112 AfterDescriptor,
113}
114
115#[derive(MallocSizeOf)]
117pub(crate) struct SourceSet {
118 image_sources: Vec<ImageSource>,
119 source_size: SourceSizeList,
120}
121
122impl SourceSet {
123 fn new() -> SourceSet {
124 SourceSet {
125 image_sources: Vec::new(),
126 source_size: SourceSizeList::empty(),
127 }
128 }
129}
130
131#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
132pub struct ImageSource {
133 pub url: String,
134 pub descriptor: Descriptor,
135}
136
137#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
138pub struct Descriptor {
139 pub width: Option<u32>,
140 pub density: Option<f64>,
141}
142
143#[derive(Clone, Copy, JSTraceable, MallocSizeOf)]
145enum State {
146 Unavailable,
147 PartiallyAvailable,
148 CompletelyAvailable,
149 Broken,
150}
151
152#[derive(Clone, Copy, JSTraceable, MallocSizeOf)]
153enum ImageRequestPhase {
154 Pending,
155 Current,
156}
157
158#[derive(JSTraceable, MallocSizeOf)]
160#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
161struct ImageRequest {
162 state: State,
163 #[no_trace]
164 parsed_url: Option<ServoUrl>,
165 source_url: Option<USVString>,
166 blocker: DomRefCell<Option<LoadBlocker>>,
167 #[no_trace]
168 image: Option<Image>,
169 #[no_trace]
170 metadata: Option<ImageMetadata>,
171 #[no_trace]
172 final_url: Option<ServoUrl>,
173 current_pixel_density: Option<f64>,
174}
175
176#[dom_struct]
177pub(crate) struct HTMLImageElement {
178 htmlelement: HTMLElement,
179 image_request: Cell<ImageRequestPhase>,
180 current_request: DomRefCell<ImageRequest>,
181 pending_request: DomRefCell<ImageRequest>,
182 form_owner: MutNullableDom<HTMLFormElement>,
183 generation: Cell<u32>,
184 source_set: DomRefCell<SourceSet>,
185 dimension_attribute_source: MutNullableDom<Element>,
188 last_selected_source: DomRefCell<Option<USVString>>,
190 #[conditional_malloc_size_of]
191 image_decode_promises: DomRefCell<Vec<Rc<Promise>>>,
192 line_number: u64,
194}
195
196impl HTMLImageElement {
197 pub(crate) fn is_usable(&self) -> Fallible<bool> {
199 if let Some(image) = &self.current_request.borrow().image {
201 let intrinsic_size = image.metadata();
202 if intrinsic_size.width == 0 || intrinsic_size.height == 0 {
203 return Ok(false);
204 }
205 }
206
207 match self.current_request.borrow().state {
208 State::Broken => Err(Error::InvalidState(None)),
210 State::CompletelyAvailable => Ok(true),
211 State::PartiallyAvailable | State::Unavailable => Ok(false),
213 }
214 }
215
216 pub(crate) fn image_data(&self) -> Option<Image> {
217 self.current_request.borrow().image.clone()
218 }
219
220 pub(crate) fn get_raster_image_data(&self) -> Option<Snapshot> {
222 let Some(raster_image) = self.image_data()?.as_raster_image() else {
223 warn!("Vector image is not supported as raster image source");
224 return None;
225 };
226 Some(raster_image.as_snapshot())
227 }
228}
229
230struct ImageContext {
232 image_cache: Arc<dyn ImageCache>,
234 status: Result<(), NetworkError>,
236 id: PendingImageId,
238 aborted: bool,
240 doc: Trusted<Document>,
242 url: ServoUrl,
243 element: Trusted<HTMLImageElement>,
244}
245
246impl FetchResponseListener for ImageContext {
247 fn should_invoke(&self) -> bool {
248 !self.aborted
249 }
250
251 fn process_request_body(&mut self, _: RequestId) {}
252
253 fn process_response(
254 &mut self,
255 _: &mut js::context::JSContext,
256 request_id: RequestId,
257 metadata: Result<FetchMetadata, NetworkError>,
258 ) {
259 debug!("got {:?} for {:?}", metadata.as_ref().map(|_| ()), self.url);
260 self.image_cache.notify_pending_response(
261 self.id,
262 FetchResponseMsg::ProcessResponse(request_id, metadata.clone()),
263 );
264
265 let metadata = metadata.ok().map(|meta| match meta {
266 FetchMetadata::Unfiltered(m) => m,
267 FetchMetadata::Filtered { unsafe_, .. } => unsafe_,
268 });
269
270 if let Some(metadata) = metadata.as_ref() &&
272 let Some(ref content_type) = metadata.content_type
273 {
274 let mime: Mime = content_type.clone().into_inner().into();
275 if mime.type_() == mime::MULTIPART && mime.subtype().as_str() == "x-mixed-replace" {
276 self.aborted = true;
277 }
278 }
279
280 let status = metadata
281 .as_ref()
282 .map(|m| m.status.clone())
283 .unwrap_or_else(HttpStatus::new_error);
284
285 self.status = {
286 if status.is_error() {
287 Err(NetworkError::ResourceLoadError(
288 "No http status code received".to_owned(),
289 ))
290 } else if status.is_success() {
291 Ok(())
292 } else {
293 Err(NetworkError::ResourceLoadError(format!(
294 "HTTP error code {}",
295 status.code()
296 )))
297 }
298 };
299 }
300
301 fn process_response_chunk(
302 &mut self,
303 _: &mut js::context::JSContext,
304 request_id: RequestId,
305 payload: Vec<u8>,
306 ) {
307 if self.status.is_ok() {
308 self.image_cache.notify_pending_response(
309 self.id,
310 FetchResponseMsg::ProcessResponseChunk(request_id, payload.into()),
311 );
312 }
313 }
314
315 fn process_response_eof(
316 self,
317 cx: &mut js::context::JSContext,
318 request_id: RequestId,
319 response: Result<(), NetworkError>,
320 timing: ResourceFetchTiming,
321 ) {
322 self.image_cache.notify_pending_response(
323 self.id,
324 FetchResponseMsg::ProcessResponseEOF(request_id, response.clone(), timing.clone()),
325 );
326 network_listener::submit_timing(cx, &self, &response, &timing);
327 }
328
329 fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<Violation>) {
330 let global = &self.resource_timing_global();
331 let elem = self.element.root();
332 let source_position = elem
333 .upcast::<Element>()
334 .compute_source_position(elem.line_number as u32);
335 global.report_csp_violations(violations, None, Some(source_position));
336 }
337}
338
339impl ResourceTimingListener for ImageContext {
340 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
341 (
342 InitiatorType::LocalName("img".to_string()),
343 self.url.clone(),
344 )
345 }
346
347 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
348 self.doc.root().global()
349 }
350}
351
352#[expect(non_snake_case)]
353impl HTMLImageElement {
354 fn fetch_image(&self, img_url: &ServoUrl, cx: &mut js::context::JSContext) {
356 let window = self.owner_window();
357
358 let cache_result = window.image_cache().get_cached_image_status(
359 img_url.clone(),
360 window.origin().immutable().clone(),
361 cors_setting_for_element(self.upcast()),
362 );
363
364 match cache_result {
365 ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable {
366 image,
367 url,
368 }) => self.process_image_response(ImageResponse::Loaded(image, url), cx),
369 ImageCacheResult::Available(ImageOrMetadataAvailable::MetadataAvailable(
370 metadata,
371 id,
372 )) => {
373 self.process_image_response(ImageResponse::MetadataLoaded(metadata), cx);
374 self.register_image_cache_callback(id, ChangeType::Element);
375 },
376 ImageCacheResult::Pending(id) => {
377 self.register_image_cache_callback(id, ChangeType::Element);
378 },
379 ImageCacheResult::ReadyForRequest(id) => {
380 self.fetch_request(img_url, id);
381 self.register_image_cache_callback(id, ChangeType::Element);
382 },
383 ImageCacheResult::FailedToLoadOrDecode => {
384 self.process_image_response(ImageResponse::FailedToLoadOrDecode, cx)
385 },
386 };
387 }
388
389 fn register_image_cache_callback(&self, id: PendingImageId, change_type: ChangeType) {
390 let trusted_node = Trusted::new(self);
391 let generation = self.generation_id();
392 let window = self.owner_window();
393 let callback = window.register_image_cache_listener(id, move |response, _| {
394 let trusted_node = trusted_node.clone();
395 let window = trusted_node.root().owner_window();
396 let callback_type = change_type.clone();
397
398 window
399 .as_global_scope()
400 .task_manager()
401 .networking_task_source()
402 .queue(task!(process_image_response: move |cx| {
403 let element = trusted_node.root();
404
405 if generation != element.generation_id() {
407 return;
408 }
409
410 match callback_type {
411 ChangeType::Element => {
412 element.process_image_response(response.response, cx);
413 }
414 ChangeType::Environment { selected_source, selected_pixel_density } => {
415 element.process_image_response_for_environment_change(
416 response.response, selected_source, generation, selected_pixel_density, cx
417 );
418 }
419 }
420 }));
421 });
422
423 window.image_cache().add_listener(ImageLoadListener::new(
424 callback,
425 window.pipeline_id(),
426 id,
427 ));
428 }
429
430 fn fetch_request(&self, img_url: &ServoUrl, id: PendingImageId) {
431 let document = self.owner_document();
432 let window = self.owner_window();
433
434 let context = ImageContext {
435 image_cache: window.image_cache(),
436 status: Ok(()),
437 id,
438 aborted: false,
439 doc: Trusted::new(&document),
440 element: Trusted::new(self),
441 url: img_url.clone(),
442 };
443
444 let global = document.global();
447 let mut request = create_a_potential_cors_request(
448 Some(window.webview_id()),
449 img_url.clone(),
450 Destination::Image,
451 cors_setting_for_element(self.upcast()),
452 None,
453 global.get_referrer(),
454 )
455 .with_global_scope(&global)
456 .referrer_policy(referrer_policy_for_element(self.upcast()));
457
458 if self.uses_srcset_or_picture() {
459 request = request.initiator(Initiator::ImageSet);
460 }
461
462 document.fetch_background(request, context);
465 }
466
467 fn handle_loaded_image(&self, image: Image, url: ServoUrl, cx: &mut js::context::JSContext) {
469 self.current_request.borrow_mut().metadata = Some(image.metadata());
470 self.current_request.borrow_mut().final_url = Some(url);
471 self.current_request.borrow_mut().image = Some(image);
472 self.current_request.borrow_mut().state = State::CompletelyAvailable;
473 LoadBlocker::terminate(&self.current_request.borrow().blocker, cx);
474 self.upcast::<Node>().dirty(NodeDamage::Other);
476 self.resolve_image_decode_promises();
477 }
478
479 fn process_image_response(&self, image: ImageResponse, cx: &mut js::context::JSContext) {
481 let (trigger_image_load, trigger_image_error) = match (image, self.image_request.get()) {
487 (ImageResponse::Loaded(image, url), ImageRequestPhase::Current) => {
488 self.handle_loaded_image(image, url, cx);
489 (true, false)
490 },
491 (ImageResponse::Loaded(image, url), ImageRequestPhase::Pending) => {
492 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
493 self.image_request.set(ImageRequestPhase::Current);
494 self.handle_loaded_image(image, url, cx);
495 (true, false)
496 },
497 (ImageResponse::MetadataLoaded(meta), ImageRequestPhase::Current) => {
498 self.current_request.borrow_mut().state = State::PartiallyAvailable;
503 self.current_request.borrow_mut().metadata = Some(meta);
504 (false, false)
505 },
506 (ImageResponse::MetadataLoaded(_), ImageRequestPhase::Pending) => {
507 self.pending_request.borrow_mut().state = State::PartiallyAvailable;
511 (false, false)
512 },
513 (ImageResponse::FailedToLoadOrDecode, ImageRequestPhase::Current) => {
514 self.abort_request(State::Broken, ImageRequestPhase::Current, cx);
520
521 self.load_broken_image_icon();
522
523 (false, true)
527 },
528 (ImageResponse::FailedToLoadOrDecode, ImageRequestPhase::Pending) => {
529 self.abort_request(State::Broken, ImageRequestPhase::Current, cx);
535 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
536
537 mem::swap(
539 &mut *self.current_request.borrow_mut(),
540 &mut *self.pending_request.borrow_mut(),
541 );
542 self.image_request.set(ImageRequestPhase::Current);
543
544 self.current_request.borrow_mut().state = State::Broken;
546
547 self.load_broken_image_icon();
548
549 (false, true)
551 },
552 };
553
554 if trigger_image_load {
556 self.upcast::<EventTarget>().fire_event(cx, atom!("load"));
558 self.upcast::<EventTarget>()
559 .fire_event(cx, atom!("loadend"));
560 }
561
562 if trigger_image_error {
564 self.upcast::<EventTarget>().fire_event(cx, atom!("error"));
565 self.upcast::<EventTarget>()
566 .fire_event(cx, atom!("loadend"));
567 }
568 }
569
570 fn process_image_response_for_environment_change(
573 &self,
574 image: ImageResponse,
575 selected_source: USVString,
576 generation: u32,
577 selected_pixel_density: f64,
578 cx: &mut js::context::JSContext,
579 ) {
580 match image {
581 ImageResponse::Loaded(image, url) => {
582 self.pending_request.borrow_mut().metadata = Some(image.metadata());
583 self.pending_request.borrow_mut().final_url = Some(url);
584 self.pending_request.borrow_mut().image = Some(image);
585 self.finish_reacting_to_environment_change(
586 selected_source,
587 generation,
588 selected_pixel_density,
589 );
590 },
591 ImageResponse::FailedToLoadOrDecode => {
592 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
600 },
601 ImageResponse::MetadataLoaded(meta) => {
602 self.pending_request.borrow_mut().metadata = Some(meta);
603 },
604 };
605 }
606
607 fn abort_request(
609 &self,
610 state: State,
611 request: ImageRequestPhase,
612 cx: &mut js::context::JSContext,
613 ) {
614 let mut request = match request {
615 ImageRequestPhase::Current => self.current_request.borrow_mut(),
616 ImageRequestPhase::Pending => self.pending_request.borrow_mut(),
617 };
618 LoadBlocker::terminate(&request.blocker, cx);
619 request.state = state;
620 request.image = None;
621 request.metadata = None;
622 request.current_pixel_density = None;
623
624 if matches!(state, State::Broken) {
625 self.reject_image_decode_promises();
626 } else if matches!(state, State::CompletelyAvailable) {
627 self.resolve_image_decode_promises();
628 }
629 }
630
631 fn create_source_set(&self) -> SourceSet {
633 let element = self.upcast::<Element>();
634
635 let mut source_set = SourceSet::new();
637
638 if let Some(srcset) = element.get_attribute(&local_name!("srcset")) {
641 source_set.image_sources = parse_a_srcset_attribute(&srcset.value());
642 }
643
644 if let Some(sizes) = element.get_attribute(&local_name!("sizes")) {
646 source_set.source_size = parse_a_sizes_attribute(&sizes.value());
647 }
648
649 let src = element.get_string_attribute(&local_name!("src"));
653 let no_density_source_of_1 = source_set
654 .image_sources
655 .iter()
656 .all(|source| source.descriptor.density != Some(1.));
657 let no_width_descriptor = source_set
658 .image_sources
659 .iter()
660 .all(|source| source.descriptor.width.is_none());
661 if !src.is_empty() && no_density_source_of_1 && no_width_descriptor {
662 source_set.image_sources.push(ImageSource {
663 url: src.to_string(),
664 descriptor: Descriptor {
665 width: None,
666 density: None,
667 },
668 })
669 }
670
671 self.normalise_source_densities(&mut source_set);
673
674 source_set
676 }
677
678 fn update_source_set(&self) {
680 *self.source_set.borrow_mut() = SourceSet::new();
682
683 let elem = self.upcast::<Element>();
688 let parent = elem.upcast::<Node>().GetParentElement();
689 let elements = match parent.as_ref() {
690 Some(p) => {
691 if p.is::<HTMLPictureElement>() {
692 p.upcast::<Node>()
693 .children()
694 .filter_map(DomRoot::downcast::<Element>)
695 .map(|n| DomRoot::from_ref(&*n))
696 .collect()
697 } else {
698 vec![DomRoot::from_ref(elem)]
699 }
700 },
701 None => vec![DomRoot::from_ref(elem)],
702 };
703
704 for element in &elements {
706 if *element == DomRoot::from_ref(elem) {
708 *self.source_set.borrow_mut() = self.create_source_set();
711
712 return;
714 }
715 if !element.is::<HTMLSourceElement>() {
717 continue;
718 }
719
720 let mut source_set = SourceSet::new();
721
722 match element.get_attribute(&local_name!("srcset")) {
726 Some(srcset) => {
727 source_set.image_sources = parse_a_srcset_attribute(&srcset.value());
728 },
729 _ => continue,
730 }
731
732 if source_set.image_sources.is_empty() {
734 continue;
735 }
736
737 if let Some(media) = element.get_attribute(&local_name!("media")) &&
740 !MediaList::matches_environment(&element.owner_document(), &media.value())
741 {
742 continue;
743 }
744
745 if let Some(sizes) = element.get_attribute(&local_name!("sizes")) {
748 source_set.source_size = parse_a_sizes_attribute(&sizes.value());
749 }
750
751 if let Some(type_) = element.get_attribute(&local_name!("type")) &&
754 !is_supported_image_mime_type(&type_.value())
755 {
756 continue;
757 }
758
759 if element.get_attribute(&local_name!("width")).is_some() ||
762 element.get_attribute(&local_name!("height")).is_some()
763 {
764 self.dimension_attribute_source.set(Some(element));
765 } else {
766 self.dimension_attribute_source.set(Some(elem));
767 }
768
769 self.normalise_source_densities(&mut source_set);
771
772 *self.source_set.borrow_mut() = source_set;
774
775 return;
777 }
778 }
779
780 fn evaluate_source_size_list(&self, source_size_list: &SourceSizeList) -> Au {
781 let document = self.owner_document();
782 let quirks_mode = document.quirks_mode();
783 source_size_list.evaluate(document.window().layout().device(), quirks_mode)
784 }
785
786 fn normalise_source_densities(&self, source_set: &mut SourceSet) {
788 let source_size = self.evaluate_source_size_list(&source_set.source_size);
790
791 for image_source in &mut source_set.image_sources {
793 if image_source.descriptor.density.is_some() {
796 continue;
797 }
798
799 if let Some(width) = image_source.descriptor.width {
803 image_source.descriptor.density = Some(width as f64 / source_size.to_f64_px());
804 } else {
805 image_source.descriptor.density = Some(1_f64);
807 }
808 }
809 }
810
811 fn select_image_source(&self) -> Option<(USVString, f64)> {
813 self.update_source_set();
815
816 if self.source_set.borrow().image_sources.is_empty() {
819 return None;
820 }
821
822 self.select_image_source_from_source_set()
824 }
825
826 fn select_image_source_from_source_set(&self) -> Option<(USVString, f64)> {
828 let source_set = self.source_set.borrow();
833 let len = source_set.image_sources.len();
834
835 let mut repeat_indices = FxHashSet::default();
837 for outer_index in 0..len {
838 if repeat_indices.contains(&outer_index) {
839 continue;
840 }
841 let imgsource = &source_set.image_sources[outer_index];
842 let pixel_density = imgsource.descriptor.density.unwrap();
843 for inner_index in (outer_index + 1)..len {
844 let imgsource2 = &source_set.image_sources[inner_index];
845 if pixel_density == imgsource2.descriptor.density.unwrap() {
846 repeat_indices.insert(inner_index);
847 }
848 }
849 }
850
851 let mut max = (0f64, 0);
852 let img_sources = &mut vec![];
853 for (index, image_source) in source_set.image_sources.iter().enumerate() {
854 if repeat_indices.contains(&index) {
855 continue;
856 }
857 let den = image_source.descriptor.density.unwrap();
858 if max.0 < den {
859 max = (den, img_sources.len());
860 }
861 img_sources.push(image_source);
862 }
863
864 let mut best_candidate = max;
867 let device_pixel_ratio = self
868 .owner_document()
869 .window()
870 .viewport_details()
871 .hidpi_scale_factor
872 .get() as f64;
873 for (index, image_source) in img_sources.iter().enumerate() {
874 let current_den = image_source.descriptor.density.unwrap();
875 if current_den < best_candidate.0 && current_den >= device_pixel_ratio {
876 best_candidate = (current_den, index);
877 }
878 }
879 let selected_source = img_sources.remove(best_candidate.1).clone();
880
881 Some((
883 USVString(selected_source.url),
884 selected_source.descriptor.density.unwrap(),
885 ))
886 }
887
888 fn init_image_request(
889 &self,
890 request: &mut RefMut<'_, ImageRequest>,
891 url: &ServoUrl,
892 src: &USVString,
893 cx: &mut js::context::JSContext,
894 ) {
895 request.parsed_url = Some(url.clone());
896 request.source_url = Some(src.clone());
897 request.image = None;
898 request.metadata = None;
899 let document = self.owner_document();
900 LoadBlocker::terminate(&request.blocker, cx);
901 *request.blocker.borrow_mut() =
902 Some(LoadBlocker::new(&document, LoadType::Image(url.clone())));
903 }
904
905 fn prepare_image_request(
907 &self,
908 selected_source: &USVString,
909 selected_pixel_density: f64,
910 image_url: &ServoUrl,
911 cx: &mut js::context::JSContext,
912 ) {
913 match self.image_request.get() {
914 ImageRequestPhase::Pending => {
915 if self
918 .pending_request
919 .borrow()
920 .parsed_url
921 .as_ref()
922 .is_some_and(|parsed_url| *parsed_url == *image_url)
923 {
924 return;
925 }
926 },
927 ImageRequestPhase::Current => {
928 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
930
931 let mut current_request = self.current_request.borrow_mut();
934 let mut pending_request = self.pending_request.borrow_mut();
935
936 match (current_request.parsed_url.as_ref(), current_request.state) {
937 (Some(parsed_url), State::PartiallyAvailable) => {
938 if *parsed_url == *image_url {
944 return;
946 }
947
948 self.image_request.set(ImageRequestPhase::Pending);
952 self.init_image_request(
953 &mut pending_request,
954 image_url,
955 selected_source,
956 cx,
957 );
958 pending_request.current_pixel_density = Some(selected_pixel_density);
959 },
960 (_, State::Broken) | (_, State::Unavailable) => {
961 self.init_image_request(
965 &mut current_request,
966 image_url,
967 selected_source,
968 cx,
969 );
970 current_request.current_pixel_density = Some(selected_pixel_density);
971 self.reject_image_decode_promises();
972 },
973 (_, _) => {
974 self.image_request.set(ImageRequestPhase::Pending);
978 self.init_image_request(
979 &mut pending_request,
980 image_url,
981 selected_source,
982 cx,
983 );
984 pending_request.current_pixel_density = Some(selected_pixel_density);
985 },
986 }
987 },
988 }
989
990 self.fetch_image(image_url, cx);
991 }
992
993 fn update_the_image_data_sync_steps(&self, cx: &mut js::context::JSContext) {
995 let Some((selected_source, selected_pixel_density)) = self.select_image_source() else {
998 self.abort_request(State::Broken, ImageRequestPhase::Current, cx);
1003 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
1004 self.image_request.set(ImageRequestPhase::Current);
1005
1006 let this = Trusted::new(self);
1009
1010 self.owner_global().task_manager().dom_manipulation_task_source().queue(
1011 task!(image_null_source_error: move |cx| {
1012 let this = this.root();
1013
1014 {
1016 let mut current_request =
1017 this.current_request.borrow_mut();
1018 current_request.source_url = None;
1019 current_request.parsed_url = None;
1020 }
1021
1022 let has_src_attribute = this.upcast::<Element>().has_attribute(&local_name!("src"));
1028
1029 if has_src_attribute || this.uses_srcset_or_picture() {
1030 this.upcast::<EventTarget>().fire_event(cx, atom!("error"));
1031 }
1032 }));
1033
1034 return;
1036 };
1037
1038 let Ok(image_url) = self.owner_document().base_url().join(&selected_source) else {
1041 self.abort_request(State::Broken, ImageRequestPhase::Current, cx);
1046 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
1047
1048 self.image_request.set(ImageRequestPhase::Current);
1050
1051 let this = Trusted::new(self);
1054
1055 self.owner_global()
1056 .task_manager()
1057 .dom_manipulation_task_source()
1058 .queue(task!(image_selected_source_error: move |cx| {
1059 let this = this.root();
1060
1061 {
1063 let mut current_request =
1064 this.current_request.borrow_mut();
1065 current_request.source_url = Some(selected_source);
1066 current_request.parsed_url = None;
1067 }
1068
1069 this.upcast::<EventTarget>().fire_event(cx, atom!("error"));
1073 }));
1074
1075 return;
1077 };
1078
1079 self.prepare_image_request(&selected_source, selected_pixel_density, &image_url, cx);
1080 }
1081
1082 pub(crate) fn update_the_image_data(&self, cx: &mut js::context::JSContext) {
1084 self.generation.set(self.generation.get() + 1);
1086
1087 if !self.owner_document().is_active() {
1089 }
1095
1096 self.current_request.borrow_mut().state = State::Unavailable;
1105
1106 let mut selected_source = None;
1110 let mut selected_pixel_density = None;
1111
1112 let src = self
1116 .upcast::<Element>()
1117 .get_string_attribute(&local_name!("src"));
1118
1119 if !self.uses_srcset_or_picture() && !src.is_empty() {
1120 selected_source = Some(USVString(src.to_string()));
1121 selected_pixel_density = Some(1_f64);
1122 };
1123
1124 self.last_selected_source
1126 .borrow_mut()
1127 .clone_from(&selected_source);
1128
1129 if let Some(selected_source) = selected_source {
1131 if let Ok(image_url) = self.owner_document().base_url().join(&selected_source) {
1135 let window = self.owner_window();
1139 let response = window.image_cache().get_image(
1140 image_url.clone(),
1141 window.origin().immutable().clone(),
1142 cors_setting_for_element(self.upcast()),
1143 );
1144
1145 if let Some(image) = response {
1147 self.abort_request(State::CompletelyAvailable, ImageRequestPhase::Current, cx);
1152 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
1153
1154 self.image_request.set(ImageRequestPhase::Current);
1156
1157 let mut current_request = self.current_request.borrow_mut();
1160 current_request.metadata = Some(image.metadata());
1161 current_request.image = Some(image);
1162 current_request.final_url = Some(image_url.clone());
1163
1164 self.upcast::<Node>().dirty(NodeDamage::Other);
1167
1168 current_request.current_pixel_density = selected_pixel_density;
1171
1172 let this = Trusted::new(self);
1175
1176 self.owner_global()
1177 .task_manager()
1178 .dom_manipulation_task_source()
1179 .queue(task!(image_load_event: move |cx| {
1180 let this = this.root();
1181
1182 {
1187 let mut current_request =
1188 this.current_request.borrow_mut();
1189 current_request.source_url = Some(selected_source);
1190 current_request.parsed_url = Some(image_url);
1191 }
1192
1193 this.upcast::<EventTarget>().fire_event(cx, atom!("load"));
1197 }));
1198
1199 return;
1201 }
1202 }
1203 }
1204
1205 let task = ImageElementMicrotask::UpdateImageData {
1208 elem: DomRoot::from_ref(self),
1209 generation: self.generation.get(),
1210 };
1211
1212 ScriptThread::await_stable_state(Microtask::ImageElement(task));
1213 }
1214
1215 pub(crate) fn react_to_environment_changes(&self) {
1217 let task = ImageElementMicrotask::EnvironmentChanges {
1219 elem: DomRoot::from_ref(self),
1220 generation: self.generation.get(),
1221 };
1222
1223 ScriptThread::await_stable_state(Microtask::ImageElement(task));
1224 }
1225
1226 fn react_to_environment_changes_sync_steps(
1228 &self,
1229 generation: u32,
1230 cx: &mut js::context::JSContext,
1231 ) {
1232 let document = self.owner_document();
1233 let has_pending_request = matches!(self.image_request.get(), ImageRequestPhase::Pending);
1234
1235 if !document.is_active() || !self.uses_srcset_or_picture() || has_pending_request {
1239 return;
1240 }
1241
1242 let Some((selected_source, selected_pixel_density)) = self.select_image_source() else {
1245 return;
1247 };
1248
1249 let mut same_selected_source = self
1252 .last_selected_source
1253 .borrow()
1254 .as_ref()
1255 .is_some_and(|source| *source == selected_source);
1256
1257 same_selected_source = same_selected_source ||
1261 self.current_request
1262 .borrow()
1263 .source_url
1264 .as_ref()
1265 .is_some_and(|source| *source == selected_source);
1266
1267 let same_selected_pixel_density = self
1268 .current_request
1269 .borrow()
1270 .current_pixel_density
1271 .is_some_and(|pixel_density| pixel_density == selected_pixel_density);
1272
1273 if same_selected_source && same_selected_pixel_density {
1274 return;
1275 }
1276
1277 let Ok(image_url) = document.base_url().join(&selected_source) else {
1281 return;
1282 };
1283
1284 self.image_request.set(ImageRequestPhase::Pending);
1286 self.init_image_request(
1287 &mut self.pending_request.borrow_mut(),
1288 &image_url,
1289 &selected_source,
1290 cx,
1291 );
1292
1293 let window = self.owner_window();
1296 let cache_result = window.image_cache().get_cached_image_status(
1297 image_url.clone(),
1298 window.origin().immutable().clone(),
1299 cors_setting_for_element(self.upcast()),
1300 );
1301
1302 let change_type = ChangeType::Environment {
1303 selected_source: selected_source.clone(),
1304 selected_pixel_density,
1305 };
1306
1307 match cache_result {
1308 ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable { .. }) => {
1309 self.finish_reacting_to_environment_change(
1310 selected_source,
1311 generation,
1312 selected_pixel_density,
1313 );
1314 },
1315 ImageCacheResult::Available(ImageOrMetadataAvailable::MetadataAvailable(m, id)) => {
1316 self.process_image_response_for_environment_change(
1317 ImageResponse::MetadataLoaded(m),
1318 selected_source,
1319 generation,
1320 selected_pixel_density,
1321 cx,
1322 );
1323 self.register_image_cache_callback(id, change_type);
1324 },
1325 ImageCacheResult::FailedToLoadOrDecode => {
1326 self.process_image_response_for_environment_change(
1327 ImageResponse::FailedToLoadOrDecode,
1328 selected_source,
1329 generation,
1330 selected_pixel_density,
1331 cx,
1332 );
1333 },
1334 ImageCacheResult::ReadyForRequest(id) => {
1335 self.fetch_request(&image_url, id);
1336 self.register_image_cache_callback(id, change_type);
1337 },
1338 ImageCacheResult::Pending(id) => {
1339 self.register_image_cache_callback(id, change_type);
1340 },
1341 }
1342 }
1343
1344 fn react_to_decode_image_sync_steps(&self, promise: Rc<Promise>, can_gc: CanGc) {
1346 if !self.owner_document().is_fully_active() ||
1350 matches!(self.current_request.borrow().state, State::Broken)
1351 {
1352 promise.reject_error(Error::Encoding(None), can_gc);
1353 } else if matches!(
1354 self.current_request.borrow().state,
1355 State::CompletelyAvailable
1356 ) {
1357 promise.resolve_native(&(), can_gc);
1359 } else if matches!(self.current_request.borrow().state, State::Unavailable) &&
1360 self.current_request.borrow().source_url.is_none()
1361 {
1362 promise.reject_error(Error::Encoding(None), can_gc);
1367 } else {
1368 self.image_decode_promises.borrow_mut().push(promise);
1369 }
1370 }
1371
1372 fn resolve_image_decode_promises(&self) {
1374 if self.image_decode_promises.borrow().is_empty() {
1375 return;
1376 }
1377
1378 let trusted_image_decode_promises: Vec<TrustedPromise> = self
1381 .image_decode_promises
1382 .borrow()
1383 .iter()
1384 .map(|promise| TrustedPromise::new(promise.clone()))
1385 .collect();
1386
1387 self.image_decode_promises.borrow_mut().clear();
1388
1389 self.owner_global()
1390 .task_manager()
1391 .dom_manipulation_task_source()
1392 .queue(task!(fulfill_image_decode_promises: move |cx| {
1393 for trusted_promise in trusted_image_decode_promises {
1394 trusted_promise.root().resolve_native(&(), CanGc::from_cx(cx));
1395 }
1396 }));
1397 }
1398
1399 fn reject_image_decode_promises(&self) {
1401 if self.image_decode_promises.borrow().is_empty() {
1402 return;
1403 }
1404
1405 let trusted_image_decode_promises: Vec<TrustedPromise> = self
1408 .image_decode_promises
1409 .borrow()
1410 .iter()
1411 .map(|promise| TrustedPromise::new(promise.clone()))
1412 .collect();
1413
1414 self.image_decode_promises.borrow_mut().clear();
1415
1416 self.owner_global()
1417 .task_manager()
1418 .dom_manipulation_task_source()
1419 .queue(task!(reject_image_decode_promises: move |cx| {
1420 for trusted_promise in trusted_image_decode_promises {
1421 trusted_promise.root().reject_error(Error::Encoding(None), CanGc::from_cx(cx));
1422 }
1423 }));
1424 }
1425
1426 fn finish_reacting_to_environment_change(
1428 &self,
1429 selected_source: USVString,
1430 generation: u32,
1431 selected_pixel_density: f64,
1432 ) {
1433 let this = Trusted::new(self);
1436
1437 self.owner_global()
1438 .task_manager()
1439 .dom_manipulation_task_source()
1440 .queue(task!(image_load_event: move |cx| {
1441 let this = this.root();
1442
1443 if this.generation.get() != generation {
1446 this.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
1447 this.image_request.set(ImageRequestPhase::Current);
1448 return;
1449 }
1450
1451 *this.last_selected_source.borrow_mut() = Some(selected_source);
1454
1455 {
1456 let mut pending_request = this.pending_request.borrow_mut();
1457
1458 pending_request.state = State::CompletelyAvailable;
1460
1461 pending_request.current_pixel_density = Some(selected_pixel_density);
1462
1463 mem::swap(&mut *this.current_request.borrow_mut(), &mut *pending_request);
1469 }
1470
1471 this.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
1472 this.image_request.set(ImageRequestPhase::Current);
1473
1474 this.upcast::<Node>().dirty(NodeDamage::Other);
1476
1477 this.upcast::<EventTarget>().fire_event(cx, atom!("load"));
1479 }));
1480 }
1481
1482 fn uses_srcset_or_picture(&self) -> bool {
1484 let element = self.upcast::<Element>();
1485
1486 let has_srcset_attribute = element.has_attribute(&local_name!("srcset"));
1487 let has_parent_picture = element
1488 .upcast::<Node>()
1489 .GetParentElement()
1490 .is_some_and(|parent| parent.is::<HTMLPictureElement>());
1491 has_srcset_attribute || has_parent_picture
1492 }
1493
1494 fn new_inherited(
1495 local_name: LocalName,
1496 prefix: Option<Prefix>,
1497 document: &Document,
1498 creator: ElementCreator,
1499 ) -> HTMLImageElement {
1500 HTMLImageElement {
1501 htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
1502 image_request: Cell::new(ImageRequestPhase::Current),
1503 current_request: DomRefCell::new(ImageRequest {
1504 state: State::Unavailable,
1505 parsed_url: None,
1506 source_url: None,
1507 image: None,
1508 metadata: None,
1509 blocker: DomRefCell::new(None),
1510 final_url: None,
1511 current_pixel_density: None,
1512 }),
1513 pending_request: DomRefCell::new(ImageRequest {
1514 state: State::Unavailable,
1515 parsed_url: None,
1516 source_url: None,
1517 image: None,
1518 metadata: None,
1519 blocker: DomRefCell::new(None),
1520 final_url: None,
1521 current_pixel_density: None,
1522 }),
1523 form_owner: Default::default(),
1524 generation: Default::default(),
1525 source_set: DomRefCell::new(SourceSet::new()),
1526 dimension_attribute_source: Default::default(),
1527 last_selected_source: DomRefCell::new(None),
1528 image_decode_promises: DomRefCell::new(vec![]),
1529 line_number: creator.return_line_number(),
1530 }
1531 }
1532
1533 pub(crate) fn new(
1534 cx: &mut js::context::JSContext,
1535 local_name: LocalName,
1536 prefix: Option<Prefix>,
1537 document: &Document,
1538 proto: Option<HandleObject>,
1539 creator: ElementCreator,
1540 ) -> DomRoot<HTMLImageElement> {
1541 let image_element = Node::reflect_node_with_proto(
1542 cx,
1543 Box::new(HTMLImageElement::new_inherited(
1544 local_name, prefix, document, creator,
1545 )),
1546 document,
1547 proto,
1548 );
1549 image_element
1550 .dimension_attribute_source
1551 .set(Some(image_element.upcast()));
1552 image_element
1553 }
1554
1555 pub(crate) fn areas(&self) -> Option<Vec<DomRoot<HTMLAreaElement>>> {
1556 let elem = self.upcast::<Element>();
1557 let usemap_attr = elem.get_attribute(&local_name!("usemap"))?;
1558
1559 let value = usemap_attr.value();
1560
1561 if value.is_empty() || !value.is_char_boundary(1) {
1562 return None;
1563 }
1564
1565 let (first, last) = value.split_at(1);
1566
1567 if first != "#" || last.is_empty() {
1568 return None;
1569 }
1570
1571 let useMapElements = self
1572 .owner_document()
1573 .upcast::<Node>()
1574 .traverse_preorder(ShadowIncluding::No)
1575 .filter_map(DomRoot::downcast::<HTMLMapElement>)
1576 .find(|n| {
1577 n.upcast::<Element>()
1578 .get_name()
1579 .is_some_and(|n| *n == *last)
1580 });
1581
1582 useMapElements.map(|mapElem| mapElem.get_area_elements())
1583 }
1584
1585 pub(crate) fn same_origin(&self, origin: &MutableOrigin) -> bool {
1586 if let Some(ref image) = self.current_request.borrow().image {
1587 return image.cors_status() == CorsStatus::Safe;
1588 }
1589
1590 self.current_request
1591 .borrow()
1592 .final_url
1593 .as_ref()
1594 .is_some_and(|url| url.scheme() == "data" || url.origin().same_origin(origin))
1595 }
1596
1597 fn generation_id(&self) -> u32 {
1598 self.generation.get()
1599 }
1600
1601 fn load_broken_image_icon(&self) {
1602 let window = self.owner_window();
1603 let Some(broken_image_icon) = window.image_cache().get_broken_image_icon() else {
1604 return;
1605 };
1606
1607 self.current_request.borrow_mut().metadata = Some(broken_image_icon.metadata);
1608 self.current_request.borrow_mut().image = Some(Image::Raster(broken_image_icon));
1609 self.upcast::<Node>().dirty(NodeDamage::Other);
1610 }
1611
1612 pub(crate) fn full_image_url_for_user_interface(&self) -> Option<ServoUrl> {
1615 self.owner_document()
1616 .base_url()
1617 .join(&self.CurrentSrc())
1618 .ok()
1619 }
1620}
1621
1622#[derive(JSTraceable, MallocSizeOf)]
1623pub(crate) enum ImageElementMicrotask {
1624 UpdateImageData {
1625 elem: DomRoot<HTMLImageElement>,
1626 generation: u32,
1627 },
1628 EnvironmentChanges {
1629 elem: DomRoot<HTMLImageElement>,
1630 generation: u32,
1631 },
1632 Decode {
1633 elem: DomRoot<HTMLImageElement>,
1634 #[conditional_malloc_size_of]
1635 promise: Rc<Promise>,
1636 },
1637}
1638
1639impl MicrotaskRunnable for ImageElementMicrotask {
1640 fn handler(&self, cx: &mut js::context::JSContext) {
1641 match *self {
1642 ImageElementMicrotask::UpdateImageData {
1643 ref elem,
1644 ref generation,
1645 } => {
1646 if elem.generation.get() == *generation {
1650 elem.update_the_image_data_sync_steps(cx);
1651 }
1652 },
1653 ImageElementMicrotask::EnvironmentChanges {
1654 ref elem,
1655 ref generation,
1656 } => {
1657 elem.react_to_environment_changes_sync_steps(*generation, cx);
1658 },
1659 ImageElementMicrotask::Decode {
1660 ref elem,
1661 ref promise,
1662 } => {
1663 elem.react_to_decode_image_sync_steps(promise.clone(), CanGc::from_cx(cx));
1664 },
1665 }
1666 }
1667
1668 fn enter_realm<'cx>(&self, cx: &'cx mut js::context::JSContext) -> AutoRealm<'cx> {
1669 match self {
1670 &ImageElementMicrotask::UpdateImageData { ref elem, .. } |
1671 &ImageElementMicrotask::EnvironmentChanges { ref elem, .. } |
1672 &ImageElementMicrotask::Decode { ref elem, .. } => enter_auto_realm(cx, &**elem),
1673 }
1674 }
1675}
1676
1677impl<'dom> LayoutDom<'dom, HTMLImageElement> {
1678 #[expect(unsafe_code)]
1679 fn current_request(self) -> &'dom ImageRequest {
1680 unsafe { self.unsafe_get().current_request.borrow_for_layout() }
1681 }
1682
1683 #[expect(unsafe_code)]
1684 fn dimension_attribute_source(self) -> LayoutDom<'dom, Element> {
1685 unsafe {
1686 self.unsafe_get()
1687 .dimension_attribute_source
1688 .get_inner_as_layout()
1689 .expect("dimension attribute source should be always non-null")
1690 }
1691 }
1692
1693 pub(crate) fn image_url(self) -> Option<ServoUrl> {
1694 self.current_request().parsed_url.clone()
1695 }
1696
1697 pub(crate) fn image_data(self) -> (Option<Image>, Option<ImageMetadata>) {
1698 let current_request = self.current_request();
1699 (current_request.image.clone(), current_request.metadata)
1700 }
1701
1702 pub(crate) fn image_density(self) -> Option<f64> {
1703 self.current_request().current_pixel_density
1704 }
1705
1706 pub(crate) fn showing_broken_image_icon(self) -> bool {
1707 matches!(self.current_request().state, State::Broken)
1708 }
1709
1710 pub(crate) fn get_width(self) -> LengthOrPercentageOrAuto {
1711 self.dimension_attribute_source()
1712 .get_attr_for_layout(&ns!(), &local_name!("width"))
1713 .map(AttrValue::as_dimension)
1714 .cloned()
1715 .unwrap_or(LengthOrPercentageOrAuto::Auto)
1716 }
1717
1718 pub(crate) fn get_height(self) -> LengthOrPercentageOrAuto {
1719 self.dimension_attribute_source()
1720 .get_attr_for_layout(&ns!(), &local_name!("height"))
1721 .map(AttrValue::as_dimension)
1722 .cloned()
1723 .unwrap_or(LengthOrPercentageOrAuto::Auto)
1724 }
1725}
1726
1727fn parse_a_sizes_attribute(value: &str) -> SourceSizeList {
1729 let mut input = ParserInput::new(value);
1730 let mut parser = Parser::new(&mut input);
1731 let url_data = Url::parse("about:blank").unwrap().into();
1732 let context =
1735 parser_context_for_anonymous_content(CssRuleType::Style, ParsingMode::empty(), &url_data);
1736 SourceSizeList::parse(&context, &mut parser)
1737}
1738
1739impl HTMLImageElementMethods<crate::DomTypeHolder> for HTMLImageElement {
1740 fn Image(
1742 cx: &mut JSContext,
1743 window: &Window,
1744 proto: Option<HandleObject>,
1745 width: Option<u32>,
1746 height: Option<u32>,
1747 ) -> Fallible<DomRoot<HTMLImageElement>> {
1748 let document = window.Document();
1750
1751 let element = Element::create(
1754 cx,
1755 QualName::new(None, ns!(html), local_name!("img")),
1756 None,
1757 &document,
1758 ElementCreator::ScriptCreated,
1759 CustomElementCreationMode::Synchronous,
1760 proto,
1761 );
1762
1763 let image = DomRoot::downcast::<HTMLImageElement>(element).unwrap();
1764
1765 if let Some(w) = width {
1767 image.SetWidth(cx, w);
1768 }
1769
1770 if let Some(h) = height {
1773 image.SetHeight(cx, h);
1774 }
1775
1776 Ok(image)
1778 }
1779
1780 make_getter!(Alt, "alt");
1782 make_setter!(SetAlt, "alt");
1784
1785 make_url_getter!(Src, "src");
1787
1788 make_url_setter!(SetSrc, "src");
1790
1791 make_url_getter!(Srcset, "srcset");
1793 make_url_setter!(SetSrcset, "srcset");
1795
1796 make_getter!(Sizes, "sizes");
1798
1799 make_setter!(SetSizes, "sizes");
1801
1802 fn GetCrossOrigin(&self) -> Option<DOMString> {
1804 reflect_cross_origin_attribute(self.upcast::<Element>())
1805 }
1806
1807 fn SetCrossOrigin(&self, cx: &mut JSContext, value: Option<DOMString>) {
1809 set_cross_origin_attribute(cx, self.upcast::<Element>(), value);
1810 }
1811
1812 make_getter!(UseMap, "usemap");
1814 make_setter!(SetUseMap, "usemap");
1816
1817 make_bool_getter!(IsMap, "ismap");
1819 make_bool_setter!(SetIsMap, "ismap");
1821
1822 fn Width(&self) -> u32 {
1824 let node = self.upcast::<Node>();
1825 node.content_box()
1826 .map(|rect| rect.size.width.to_px() as u32)
1827 .unwrap_or_else(|| self.NaturalWidth())
1828 }
1829
1830 make_dimension_uint_setter!(SetWidth, "width");
1832
1833 fn Height(&self) -> u32 {
1835 let node = self.upcast::<Node>();
1836 node.content_box()
1837 .map(|rect| rect.size.height.to_px() as u32)
1838 .unwrap_or_else(|| self.NaturalHeight())
1839 }
1840
1841 make_dimension_uint_setter!(SetHeight, "height");
1843
1844 fn NaturalWidth(&self) -> u32 {
1846 let request = self.current_request.borrow();
1847 if matches!(request.state, State::Broken) {
1848 return 0;
1849 }
1850
1851 let pixel_density = request.current_pixel_density.unwrap_or(1f64);
1852 match request.metadata {
1853 Some(ref metadata) => (metadata.width as f64 / pixel_density) as u32,
1854 None => 0,
1855 }
1856 }
1857
1858 fn NaturalHeight(&self) -> u32 {
1860 let request = self.current_request.borrow();
1861 if matches!(request.state, State::Broken) {
1862 return 0;
1863 }
1864
1865 let pixel_density = request.current_pixel_density.unwrap_or(1f64);
1866 match request.metadata {
1867 Some(ref metadata) => (metadata.height as f64 / pixel_density) as u32,
1868 None => 0,
1869 }
1870 }
1871
1872 fn Complete(&self) -> bool {
1874 let element = self.upcast::<Element>();
1875
1876 let has_srcset_attribute = element.has_attribute(&local_name!("srcset"));
1879 if !element.has_attribute(&local_name!("src")) && !has_srcset_attribute {
1880 return true;
1881 }
1882
1883 let src = element.get_string_attribute(&local_name!("src"));
1885 if !has_srcset_attribute && src.is_empty() {
1886 return true;
1887 }
1888
1889 if matches!(self.image_request.get(), ImageRequestPhase::Current) &&
1893 matches!(
1894 self.current_request.borrow().state,
1895 State::CompletelyAvailable | State::Broken
1896 )
1897 {
1898 return true;
1899 }
1900
1901 false
1903 }
1904
1905 fn CurrentSrc(&self) -> USVString {
1907 let current_request = self.current_request.borrow();
1908 let url = ¤t_request.parsed_url;
1909 match *url {
1910 Some(ref url) => USVString(url.clone().into_string()),
1911 None => {
1912 let unparsed_url = ¤t_request.source_url;
1913 match *unparsed_url {
1914 Some(ref url) => url.clone(),
1915 None => USVString("".to_owned()),
1916 }
1917 },
1918 }
1919 }
1920
1921 fn ReferrerPolicy(&self) -> DOMString {
1923 reflect_referrer_policy_attribute(self.upcast::<Element>())
1924 }
1925
1926 make_setter!(SetReferrerPolicy, "referrerpolicy");
1928
1929 fn Decode(&self, cx: &mut JSContext) -> Rc<Promise> {
1931 let promise = Promise::new2(cx, &self.global());
1933
1934 let task = ImageElementMicrotask::Decode {
1936 elem: DomRoot::from_ref(self),
1937 promise: promise.clone(),
1938 };
1939
1940 ScriptThread::await_stable_state(Microtask::ImageElement(task));
1941
1942 promise
1944 }
1945
1946 make_getter!(Name, "name");
1948
1949 make_atomic_setter!(SetName, "name");
1951
1952 make_getter!(Align, "align");
1954
1955 make_setter!(SetAlign, "align");
1957
1958 make_uint_getter!(Hspace, "hspace");
1960
1961 make_uint_setter!(SetHspace, "hspace");
1963
1964 make_uint_getter!(Vspace, "vspace");
1966
1967 make_uint_setter!(SetVspace, "vspace");
1969
1970 make_url_getter!(LongDesc, "longdesc");
1972
1973 make_url_setter!(SetLongDesc, "longdesc");
1975
1976 make_getter!(Border, "border");
1978
1979 make_setter!(SetBorder, "border");
1981}
1982
1983impl VirtualMethods for HTMLImageElement {
1984 fn super_type(&self) -> Option<&dyn VirtualMethods> {
1985 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
1986 }
1987
1988 fn adopting_steps(&self, cx: &mut JSContext, old_doc: &Document) {
1989 self.super_type().unwrap().adopting_steps(cx, old_doc);
1990 self.update_the_image_data(cx);
1991 }
1992
1993 fn attribute_mutated(
1994 &self,
1995 cx: &mut js::context::JSContext,
1996 attr: AttrRef<'_>,
1997 mutation: AttributeMutation,
1998 ) {
1999 self.super_type()
2000 .unwrap()
2001 .attribute_mutated(cx, attr, mutation);
2002 match attr.local_name() {
2003 &local_name!("src") |
2004 &local_name!("srcset") |
2005 &local_name!("width") |
2006 &local_name!("sizes") => {
2007 self.update_the_image_data(cx);
2011 },
2012 &local_name!("crossorigin") => {
2013 let cross_origin_state_changed = match mutation {
2016 AttributeMutation::Removed | AttributeMutation::Set(None, _) => true,
2017 AttributeMutation::Set(Some(old_value), _) => {
2018 let new_cors_setting =
2019 CorsSettings::from_enumerated_attribute(&attr.value());
2020 let old_cors_setting = CorsSettings::from_enumerated_attribute(old_value);
2021
2022 new_cors_setting != old_cors_setting
2023 },
2024 };
2025
2026 if cross_origin_state_changed {
2027 self.update_the_image_data(cx);
2028 }
2029 },
2030 &local_name!("referrerpolicy") => {
2031 let referrer_policy_state_changed = match mutation {
2034 AttributeMutation::Removed | AttributeMutation::Set(None, _) => {
2035 ReferrerPolicy::from(&**attr.value()) != ReferrerPolicy::EmptyString
2036 },
2037 AttributeMutation::Set(Some(old_value), _) => {
2038 ReferrerPolicy::from(&**attr.value()) != ReferrerPolicy::from(&**old_value)
2039 },
2040 };
2041
2042 if referrer_policy_state_changed {
2043 self.update_the_image_data(cx);
2044 }
2045 },
2046 _ => {},
2047 }
2048 }
2049
2050 fn attribute_affects_presentational_hints(&self, attr: AttrRef<'_>) -> bool {
2051 match attr.local_name() {
2052 &local_name!("width") | &local_name!("height") => true,
2053 _ => self
2054 .super_type()
2055 .unwrap()
2056 .attribute_affects_presentational_hints(attr),
2057 }
2058 }
2059
2060 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
2061 match name {
2062 &local_name!("width") | &local_name!("height") => {
2063 AttrValue::from_dimension(value.into())
2064 },
2065 &local_name!("hspace") | &local_name!("vspace") => AttrValue::from_u32(value.into(), 0),
2066 _ => self
2067 .super_type()
2068 .unwrap()
2069 .parse_plain_attribute(name, value),
2070 }
2071 }
2072
2073 fn handle_event(&self, cx: &mut js::context::JSContext, event: &Event) {
2074 if event.type_() != atom!("click") {
2075 return;
2076 }
2077
2078 let area_elements = self.areas();
2079 let elements = match area_elements {
2080 Some(x) => x,
2081 None => return,
2082 };
2083
2084 let mouse_event = match event.downcast::<MouseEvent>() {
2086 Some(x) => x,
2087 None => return,
2088 };
2089
2090 let point = Point2D::new(
2091 mouse_event.ClientX().to_f32().unwrap(),
2092 mouse_event.ClientY().to_f32().unwrap(),
2093 );
2094 let bcr = self.upcast::<Element>().GetBoundingClientRect(cx);
2095 let bcr_p = Point2D::new(bcr.X() as f32, bcr.Y() as f32);
2096
2097 for element in elements {
2099 let shape = element.get_shape_from_coords();
2100 let shp = match shape {
2101 Some(x) => x.absolute_coords(bcr_p),
2102 None => return,
2103 };
2104 if shp.hit_test(&point) {
2105 element.activation_behavior(cx, event, self.upcast());
2106 return;
2107 }
2108 }
2109 }
2110
2111 fn bind_to_tree(&self, cx: &mut JSContext, context: &BindContext) {
2113 if let Some(s) = self.super_type() {
2114 s.bind_to_tree(cx, context);
2115 }
2116 let document = self.owner_document();
2117 if context.tree_connected {
2118 document.register_responsive_image(self);
2119 }
2120
2121 let parent = self.upcast::<Node>().GetParentNode().unwrap();
2122
2123 if parent.is::<HTMLPictureElement>() && std::ptr::eq(&*parent, context.parent) {
2126 self.update_the_image_data(cx);
2127 }
2128 }
2129
2130 fn unbind_from_tree(&self, cx: &mut js::context::JSContext, context: &UnbindContext) {
2132 self.super_type().unwrap().unbind_from_tree(cx, context);
2133 let document = self.owner_document();
2134 document.unregister_responsive_image(self);
2135
2136 if context.parent.is::<HTMLPictureElement>() && !self.upcast::<Node>().has_parent() {
2139 self.update_the_image_data(cx);
2140 }
2141 }
2142
2143 fn moving_steps(&self, cx: &mut JSContext, context: &MoveContext) {
2145 if let Some(super_type) = self.super_type() {
2146 super_type.moving_steps(cx, context);
2147 }
2148
2149 if let Some(old_parent) = context.old_parent &&
2151 old_parent.is::<HTMLPictureElement>()
2152 {
2153 self.update_the_image_data(cx);
2154 }
2155 }
2156}
2157
2158impl FormControl for HTMLImageElement {
2159 fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
2160 self.form_owner.get()
2161 }
2162
2163 fn set_form_owner(&self, form: Option<&HTMLFormElement>) {
2164 self.form_owner.set(form);
2165 }
2166
2167 fn to_element(&self) -> &Element {
2168 self.upcast::<Element>()
2169 }
2170
2171 fn is_listed(&self) -> bool {
2172 false
2173 }
2174}
2175
2176pub(crate) fn collect_sequence_characters(
2179 s: &str,
2180 mut predicate: impl FnMut(&char) -> bool,
2181) -> (&str, &str) {
2182 let i = s.find(|ch| !predicate(&ch)).unwrap_or(s.len());
2183 (&s[0..i], &s[i..])
2184}
2185
2186fn is_valid_non_negative_integer_string(s: &str) -> bool {
2189 s.chars().all(|c| c.is_ascii_digit())
2190}
2191
2192fn is_valid_floating_point_number_string(s: &str) -> bool {
2195 static RE: LazyLock<Regex> =
2196 LazyLock::new(|| Regex::new(r"^-?(?:\d+\.\d+|\d+|\.\d+)(?:(e|E)(\+|\-)?\d+)?$").unwrap());
2197
2198 RE.is_match(s)
2199}
2200
2201pub fn parse_a_srcset_attribute(input: &str) -> Vec<ImageSource> {
2204 let mut current_index = 0;
2207
2208 let mut candidates = vec![];
2210 while current_index < input.len() {
2211 let remaining_string = &input[current_index..];
2212
2213 let (collected_characters, string_after_whitespace) =
2220 collect_sequence_characters(remaining_string, |character| {
2221 *character == ',' || character.is_ascii_whitespace()
2222 });
2223
2224 current_index += collected_characters.len();
2227
2228 if string_after_whitespace.is_empty() {
2230 return candidates;
2231 }
2232
2233 let (url, _) =
2236 collect_sequence_characters(string_after_whitespace, |c| !char::is_ascii_whitespace(c));
2237
2238 current_index += url.len();
2241
2242 let mut descriptors = Vec::new();
2244
2245 if url.ends_with(',') {
2249 let image_source = ImageSource {
2250 url: url.trim_end_matches(',').into(),
2251 descriptor: Descriptor {
2252 width: None,
2253 density: None,
2254 },
2255 };
2256 candidates.push(image_source);
2257 continue;
2258 }
2259
2260 let descriptors_string = &input[current_index..];
2263 let (spaces, descriptors_string) =
2264 collect_sequence_characters(descriptors_string, |character| {
2265 character.is_ascii_whitespace()
2266 });
2267 current_index += spaces.len();
2268
2269 let mut current_descriptor = String::new();
2271
2272 let mut state = ParseState::InDescriptor;
2274
2275 let mut characters = descriptors_string.chars();
2279 let mut character = characters.next();
2280 if let Some(character) = character {
2281 current_index += character.len_utf8();
2282 }
2283
2284 loop {
2285 match (state, character) {
2286 (ParseState::InDescriptor, Some(character)) if character.is_ascii_whitespace() => {
2287 if !current_descriptor.is_empty() {
2291 descriptors.push(current_descriptor);
2292 current_descriptor = String::new();
2293 state = ParseState::AfterDescriptor;
2294 }
2295 },
2296 (ParseState::InDescriptor, Some(',')) => {
2297 if !current_descriptor.is_empty() {
2301 descriptors.push(current_descriptor);
2302 }
2303 break;
2304 },
2305 (ParseState::InDescriptor, Some('(')) => {
2306 current_descriptor.push('(');
2308 state = ParseState::InParens;
2309 },
2310 (ParseState::InDescriptor, Some(character)) => {
2311 current_descriptor.push(character);
2313 },
2314 (ParseState::InDescriptor, None) => {
2315 if !current_descriptor.is_empty() {
2318 descriptors.push(current_descriptor);
2319 }
2320 break;
2321 },
2322 (ParseState::InParens, Some(')')) => {
2323 current_descriptor.push(')');
2325 state = ParseState::InDescriptor;
2326 },
2327 (ParseState::InParens, Some(character)) => {
2328 current_descriptor.push(character);
2330 },
2331 (ParseState::InParens, None) => {
2332 descriptors.push(current_descriptor);
2335 break;
2336 },
2337 (ParseState::AfterDescriptor, Some(character))
2338 if character.is_ascii_whitespace() =>
2339 {
2340 },
2342 (ParseState::AfterDescriptor, Some(_)) => {
2343 state = ParseState::InDescriptor;
2346 continue;
2347 },
2348 (ParseState::AfterDescriptor, None) => {
2349 break;
2351 },
2352 }
2353
2354 character = characters.next();
2355 if let Some(character) = character {
2356 current_index += character.len_utf8();
2357 }
2358 }
2359
2360 let mut error = false;
2362 let mut width: Option<u32> = None;
2364 let mut density: Option<f64> = None;
2366 let mut future_compat_h: Option<u32> = None;
2368
2369 for descriptor in descriptors.into_iter() {
2372 let Some(last_character) = descriptor.chars().last() else {
2373 break;
2374 };
2375
2376 let first_part_of_string = &descriptor[0..descriptor.len() - last_character.len_utf8()];
2377 match last_character {
2378 'w' if is_valid_non_negative_integer_string(first_part_of_string) &&
2385 density.is_none() &&
2386 width.is_none() =>
2387 {
2388 match parse_unsigned_integer(first_part_of_string.chars()) {
2389 Ok(number) if number > 0 => {
2390 width = Some(number);
2391 continue;
2392 },
2393 _ => error = true,
2394 }
2395 },
2396
2397 'x' if is_valid_floating_point_number_string(first_part_of_string) &&
2411 width.is_none() &&
2412 density.is_none() &&
2413 future_compat_h.is_none() =>
2414 {
2415 match first_part_of_string.parse::<f64>() {
2416 Ok(number) if number.is_finite() && number >= 0. => {
2417 density = Some(number);
2418 continue;
2419 },
2420 _ => error = true,
2421 }
2422 },
2423
2424 'h' if is_valid_non_negative_integer_string(first_part_of_string) &&
2433 future_compat_h.is_none() &&
2434 density.is_none() =>
2435 {
2436 match parse_unsigned_integer(first_part_of_string.chars()) {
2437 Ok(number) if number > 0 => {
2438 future_compat_h = Some(number);
2439 continue;
2440 },
2441 _ => error = true,
2442 }
2443 },
2444
2445 _ => error = true,
2448 }
2449
2450 if error {
2451 break;
2452 }
2453 }
2454
2455 if future_compat_h.is_some() && width.is_none() {
2457 error = true;
2458 }
2459
2460 if !error {
2464 let image_source = ImageSource {
2465 url: url.into(),
2466 descriptor: Descriptor { width, density },
2467 };
2468 candidates.push(image_source);
2469 }
2470
2471 }
2473 candidates
2474}
2475
2476#[derive(Clone)]
2477enum ChangeType {
2478 Environment {
2479 selected_source: USVString,
2480 selected_pixel_density: f64,
2481 },
2482 Element,
2483}
2484
2485fn is_supported_image_mime_type(input: &str) -> bool {
2487 let mime_type = input.trim();
2489
2490 let mime_type_essence = match mime_type.find(';') {
2492 Some(semi) => &mime_type[..semi],
2493 _ => mime_type,
2494 };
2495
2496 if mime_type_essence.is_empty() {
2501 return true;
2502 }
2503
2504 SUPPORTED_IMAGE_MIME_TYPES.contains(&mime_type_essence)
2505}