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::jsapi::JSAutoRealm;
17use js::rust::HandleObject;
18use mime::{self, Mime};
19use net_traits::http_status::HttpStatus;
20use net_traits::image_cache::{
21 Image, ImageCache, ImageCacheResult, ImageLoadListener, ImageOrMetadataAvailable,
22 ImageResponse, PendingImageId,
23};
24use net_traits::request::{CorsSettings, Destination, Initiator, RequestId};
25use net_traits::{
26 FetchMetadata, FetchResponseMsg, NetworkError, ReferrerPolicy, ResourceFetchTiming,
27};
28use num_traits::ToPrimitive;
29use pixels::{CorsStatus, ImageMetadata, Snapshot};
30use regex::Regex;
31use rustc_hash::FxHashSet;
32use servo_url::ServoUrl;
33use servo_url::origin::MutableOrigin;
34use style::attr::{AttrValue, LengthOrPercentageOrAuto, parse_unsigned_integer};
35use style::context::QuirksMode;
36use style::parser::ParserContext;
37use style::stylesheets::{CssRuleType, Origin};
38use style::values::specified::source_size_list::SourceSizeList;
39use style_traits::ParsingMode;
40use url::Url;
41
42use crate::document_loader::{LoadBlocker, LoadType};
43use crate::dom::activation::Activatable;
44use crate::dom::attr::Attr;
45use crate::dom::bindings::cell::{DomRefCell, RefMut};
46use crate::dom::bindings::codegen::Bindings::DOMRectBinding::DOMRect_Binding::DOMRectMethods;
47use crate::dom::bindings::codegen::Bindings::ElementBinding::Element_Binding::ElementMethods;
48use crate::dom::bindings::codegen::Bindings::HTMLImageElementBinding::HTMLImageElementMethods;
49use crate::dom::bindings::codegen::Bindings::MouseEventBinding::MouseEventMethods;
50use crate::dom::bindings::codegen::Bindings::NodeBinding::Node_Binding::NodeMethods;
51use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
52use crate::dom::bindings::error::{Error, Fallible};
53use crate::dom::bindings::inheritance::Castable;
54use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
55use crate::dom::bindings::reflector::DomGlobal;
56use crate::dom::bindings::root::{DomRoot, LayoutDom, MutNullableDom};
57use crate::dom::bindings::str::{DOMString, USVString};
58use crate::dom::csp::{GlobalCspReporting, Violation};
59use crate::dom::document::Document;
60use crate::dom::element::{
61 AttributeMutation, CustomElementCreationMode, Element, ElementCreator, LayoutElementHelpers,
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::{BindContext, Node, NodeDamage, NodeTraits, ShadowIncluding, UnbindContext};
77use crate::dom::performance::performanceresourcetiming::InitiatorType;
78use crate::dom::promise::Promise;
79use crate::dom::virtualmethods::VirtualMethods;
80use crate::dom::window::Window;
81use crate::fetch::create_a_potential_cors_request;
82use crate::microtask::{Microtask, MicrotaskRunnable};
83use crate::network_listener::{self, FetchResponseListener, ResourceTimingListener};
84use crate::realms::enter_realm;
85use crate::script_runtime::CanGc;
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)]
142enum State {
143 Unavailable,
144 PartiallyAvailable,
145 CompletelyAvailable,
146 Broken,
147}
148
149#[derive(Clone, Copy, JSTraceable, MallocSizeOf)]
150enum ImageRequestPhase {
151 Pending,
152 Current,
153}
154#[derive(JSTraceable, MallocSizeOf)]
155#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
156struct ImageRequest {
157 state: State,
158 #[no_trace]
159 parsed_url: Option<ServoUrl>,
160 source_url: Option<USVString>,
161 blocker: DomRefCell<Option<LoadBlocker>>,
162 #[no_trace]
163 image: Option<Image>,
164 #[no_trace]
165 metadata: Option<ImageMetadata>,
166 #[no_trace]
167 final_url: Option<ServoUrl>,
168 current_pixel_density: Option<f64>,
169}
170#[dom_struct]
171pub(crate) struct HTMLImageElement {
172 htmlelement: HTMLElement,
173 image_request: Cell<ImageRequestPhase>,
174 current_request: DomRefCell<ImageRequest>,
175 pending_request: DomRefCell<ImageRequest>,
176 form_owner: MutNullableDom<HTMLFormElement>,
177 generation: Cell<u32>,
178 source_set: DomRefCell<SourceSet>,
179 dimension_attribute_source: MutNullableDom<Element>,
182 last_selected_source: DomRefCell<Option<USVString>>,
183 #[conditional_malloc_size_of]
184 image_decode_promises: DomRefCell<Vec<Rc<Promise>>>,
185 line_number: u64,
187}
188
189impl HTMLImageElement {
190 pub(crate) fn is_usable(&self) -> Fallible<bool> {
192 if let Some(image) = &self.current_request.borrow().image {
194 let intrinsic_size = image.metadata();
195 if intrinsic_size.width == 0 || intrinsic_size.height == 0 {
196 return Ok(false);
197 }
198 }
199
200 match self.current_request.borrow().state {
201 State::Broken => Err(Error::InvalidState(None)),
203 State::CompletelyAvailable => Ok(true),
204 State::PartiallyAvailable | State::Unavailable => Ok(false),
206 }
207 }
208
209 pub(crate) fn image_data(&self) -> Option<Image> {
210 self.current_request.borrow().image.clone()
211 }
212
213 pub(crate) fn get_raster_image_data(&self) -> Option<Snapshot> {
215 let Some(raster_image) = self.image_data()?.as_raster_image() else {
216 warn!("Vector image is not supported as raster image source");
217 return None;
218 };
219 Some(raster_image.as_snapshot())
220 }
221}
222
223struct ImageContext {
225 image_cache: Arc<dyn ImageCache>,
227 status: Result<(), NetworkError>,
229 id: PendingImageId,
231 aborted: bool,
233 doc: Trusted<Document>,
235 url: ServoUrl,
236 element: Trusted<HTMLImageElement>,
237}
238
239impl FetchResponseListener for ImageContext {
240 fn should_invoke(&self) -> bool {
241 !self.aborted
242 }
243
244 fn process_request_body(&mut self, _: RequestId) {}
245 fn process_request_eof(&mut self, _: RequestId) {}
246
247 fn process_response(
248 &mut self,
249 request_id: RequestId,
250 metadata: Result<FetchMetadata, NetworkError>,
251 ) {
252 debug!("got {:?} for {:?}", metadata.as_ref().map(|_| ()), self.url);
253 self.image_cache.notify_pending_response(
254 self.id,
255 FetchResponseMsg::ProcessResponse(request_id, metadata.clone()),
256 );
257
258 let metadata = metadata.ok().map(|meta| match meta {
259 FetchMetadata::Unfiltered(m) => m,
260 FetchMetadata::Filtered { unsafe_, .. } => unsafe_,
261 });
262
263 if let Some(metadata) = metadata.as_ref() {
265 if let Some(ref content_type) = metadata.content_type {
266 let mime: Mime = content_type.clone().into_inner().into();
267 if mime.type_() == mime::MULTIPART && mime.subtype().as_str() == "x-mixed-replace" {
268 self.aborted = true;
269 }
270 }
271 }
272
273 let status = metadata
274 .as_ref()
275 .map(|m| m.status.clone())
276 .unwrap_or_else(HttpStatus::new_error);
277
278 self.status = {
279 if status.is_error() {
280 Err(NetworkError::Internal(
281 "No http status code received".to_owned(),
282 ))
283 } else if status.is_success() {
284 Ok(())
285 } else {
286 Err(NetworkError::Internal(format!(
287 "HTTP error code {}",
288 status.code()
289 )))
290 }
291 };
292 }
293
294 fn process_response_chunk(&mut self, request_id: RequestId, payload: Vec<u8>) {
295 if self.status.is_ok() {
296 self.image_cache.notify_pending_response(
297 self.id,
298 FetchResponseMsg::ProcessResponseChunk(request_id, payload.into()),
299 );
300 }
301 }
302
303 fn process_response_eof(
304 self,
305 request_id: RequestId,
306 response: Result<ResourceFetchTiming, NetworkError>,
307 ) {
308 self.image_cache.notify_pending_response(
309 self.id,
310 FetchResponseMsg::ProcessResponseEOF(request_id, response.clone()),
311 );
312 if let Ok(response) = response {
313 network_listener::submit_timing(&self, &response, CanGc::note());
314 }
315 }
316
317 fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<Violation>) {
318 let global = &self.resource_timing_global();
319 let elem = self.element.root();
320 let source_position = elem
321 .upcast::<Element>()
322 .compute_source_position(elem.line_number as u32);
323 global.report_csp_violations(violations, None, Some(source_position));
324 }
325}
326
327impl ResourceTimingListener for ImageContext {
328 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
329 (
330 InitiatorType::LocalName("img".to_string()),
331 self.url.clone(),
332 )
333 }
334
335 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
336 self.doc.root().global()
337 }
338}
339
340#[allow(non_snake_case)]
341impl HTMLImageElement {
342 fn fetch_image(&self, img_url: &ServoUrl, can_gc: CanGc) {
344 let window = self.owner_window();
345
346 let cache_result = window.image_cache().get_cached_image_status(
347 img_url.clone(),
348 window.origin().immutable().clone(),
349 cors_setting_for_element(self.upcast()),
350 );
351
352 match cache_result {
353 ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable {
354 image,
355 url,
356 }) => self.process_image_response(ImageResponse::Loaded(image, url), can_gc),
357 ImageCacheResult::Available(ImageOrMetadataAvailable::MetadataAvailable(
358 metadata,
359 id,
360 )) => {
361 self.process_image_response(ImageResponse::MetadataLoaded(metadata), can_gc);
362 self.register_image_cache_callback(id, ChangeType::Element);
363 },
364 ImageCacheResult::Pending(id) => {
365 self.register_image_cache_callback(id, ChangeType::Element);
366 },
367 ImageCacheResult::ReadyForRequest(id) => {
368 self.fetch_request(img_url, id);
369 self.register_image_cache_callback(id, ChangeType::Element);
370 },
371 ImageCacheResult::FailedToLoadOrDecode => {
372 self.process_image_response(ImageResponse::FailedToLoadOrDecode, can_gc)
373 },
374 };
375 }
376
377 fn register_image_cache_callback(&self, id: PendingImageId, change_type: ChangeType) {
378 let trusted_node = Trusted::new(self);
379 let generation = self.generation_id();
380 let window = self.owner_window();
381 let callback = window.register_image_cache_listener(id, move |response| {
382 let trusted_node = trusted_node.clone();
383 let window = trusted_node.root().owner_window();
384 let callback_type = change_type.clone();
385
386 window
387 .as_global_scope()
388 .task_manager()
389 .networking_task_source()
390 .queue(task!(process_image_response: move || {
391 let element = trusted_node.root();
392
393 if generation != element.generation_id() {
395 return;
396 }
397
398 match callback_type {
399 ChangeType::Element => {
400 element.process_image_response(response.response, CanGc::note());
401 }
402 ChangeType::Environment { selected_source, selected_pixel_density } => {
403 element.process_image_response_for_environment_change(
404 response.response, selected_source, generation, selected_pixel_density, CanGc::note()
405 );
406 }
407 }
408 }));
409 });
410
411 window.image_cache().add_listener(ImageLoadListener::new(
412 callback,
413 window.pipeline_id(),
414 id,
415 ));
416 }
417
418 fn fetch_request(&self, img_url: &ServoUrl, id: PendingImageId) {
419 let document = self.owner_document();
420 let window = self.owner_window();
421
422 let context = ImageContext {
423 image_cache: window.image_cache(),
424 status: Ok(()),
425 id,
426 aborted: false,
427 doc: Trusted::new(&document),
428 element: Trusted::new(self),
429 url: img_url.clone(),
430 };
431
432 let global = document.global();
435 let mut request = create_a_potential_cors_request(
436 Some(window.webview_id()),
437 img_url.clone(),
438 Destination::Image,
439 cors_setting_for_element(self.upcast()),
440 None,
441 global.get_referrer(),
442 document.insecure_requests_policy(),
443 document.has_trustworthy_ancestor_or_current_origin(),
444 global.policy_container(),
445 )
446 .origin(document.origin().immutable().clone())
447 .pipeline_id(Some(document.global().pipeline_id()))
448 .referrer_policy(referrer_policy_for_element(self.upcast()));
449
450 if Self::uses_srcset_or_picture(self.upcast()) {
451 request = request.initiator(Initiator::ImageSet);
452 }
453
454 document.fetch_background(request, context);
457 }
458
459 fn handle_loaded_image(&self, image: Image, url: ServoUrl, can_gc: CanGc) {
461 self.current_request.borrow_mut().metadata = Some(image.metadata());
462 self.current_request.borrow_mut().final_url = Some(url);
463 self.current_request.borrow_mut().image = Some(image);
464 self.current_request.borrow_mut().state = State::CompletelyAvailable;
465 LoadBlocker::terminate(&self.current_request.borrow().blocker, can_gc);
466 self.upcast::<Node>().dirty(NodeDamage::Other);
468 self.resolve_image_decode_promises();
469 }
470
471 fn process_image_response(&self, image: ImageResponse, can_gc: CanGc) {
473 let (trigger_image_load, trigger_image_error) = match (image, self.image_request.get()) {
475 (ImageResponse::Loaded(image, url), ImageRequestPhase::Current) => {
476 self.handle_loaded_image(image, url, can_gc);
477 (true, false)
478 },
479 (ImageResponse::Loaded(image, url), ImageRequestPhase::Pending) => {
480 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, can_gc);
481 self.image_request.set(ImageRequestPhase::Current);
482 self.handle_loaded_image(image, url, can_gc);
483 (true, false)
484 },
485 (ImageResponse::MetadataLoaded(meta), ImageRequestPhase::Current) => {
486 self.current_request.borrow_mut().state = State::PartiallyAvailable;
487 self.current_request.borrow_mut().metadata = Some(meta);
488 (false, false)
489 },
490 (ImageResponse::MetadataLoaded(_), ImageRequestPhase::Pending) => {
491 self.pending_request.borrow_mut().state = State::PartiallyAvailable;
492 (false, false)
493 },
494 (ImageResponse::FailedToLoadOrDecode, ImageRequestPhase::Current) => {
495 self.abort_request(State::Broken, ImageRequestPhase::Current, can_gc);
496
497 self.load_broken_image_icon();
498
499 (false, true)
500 },
501 (ImageResponse::FailedToLoadOrDecode, ImageRequestPhase::Pending) => {
502 self.abort_request(State::Broken, ImageRequestPhase::Current, can_gc);
503 self.abort_request(State::Broken, ImageRequestPhase::Pending, can_gc);
504 self.image_request.set(ImageRequestPhase::Current);
505
506 self.load_broken_image_icon();
507
508 (false, true)
509 },
510 };
511
512 if trigger_image_load {
514 self.upcast::<EventTarget>()
516 .fire_event(atom!("load"), can_gc);
517 self.upcast::<EventTarget>()
518 .fire_event(atom!("loadend"), can_gc);
519 }
520
521 if trigger_image_error {
523 self.upcast::<EventTarget>()
524 .fire_event(atom!("error"), can_gc);
525 self.upcast::<EventTarget>()
526 .fire_event(atom!("loadend"), can_gc);
527 }
528 }
529
530 fn process_image_response_for_environment_change(
533 &self,
534 image: ImageResponse,
535 src: USVString,
536 generation: u32,
537 selected_pixel_density: f64,
538 can_gc: CanGc,
539 ) {
540 match image {
541 ImageResponse::Loaded(image, url) => {
542 self.pending_request.borrow_mut().metadata = Some(image.metadata());
543 self.pending_request.borrow_mut().final_url = Some(url);
544 self.pending_request.borrow_mut().image = Some(image);
545 self.finish_reacting_to_environment_change(src, generation, selected_pixel_density);
546 },
547 ImageResponse::FailedToLoadOrDecode => {
548 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, can_gc);
556 },
557 ImageResponse::MetadataLoaded(meta) => {
558 self.pending_request.borrow_mut().metadata = Some(meta);
559 },
560 };
561 }
562
563 fn abort_request(&self, state: State, request: ImageRequestPhase, can_gc: CanGc) {
565 let mut request = match request {
566 ImageRequestPhase::Current => self.current_request.borrow_mut(),
567 ImageRequestPhase::Pending => self.pending_request.borrow_mut(),
568 };
569 LoadBlocker::terminate(&request.blocker, can_gc);
570 request.state = state;
571 request.image = None;
572 request.metadata = None;
573
574 if matches!(state, State::Broken) {
575 self.reject_image_decode_promises();
576 } else if matches!(state, State::CompletelyAvailable) {
577 self.resolve_image_decode_promises();
578 }
579 }
580
581 fn create_source_set(&self) -> SourceSet {
583 let element = self.upcast::<Element>();
584
585 let mut source_set = SourceSet::new();
587
588 if let Some(srcset) = element.get_attribute(&ns!(), &local_name!("srcset")) {
591 source_set.image_sources = parse_a_srcset_attribute(&srcset.value());
592 }
593
594 if let Some(sizes) = element.get_attribute(&ns!(), &local_name!("sizes")) {
596 source_set.source_size = parse_a_sizes_attribute(&sizes.value());
597 }
598
599 let src_attribute = element.get_string_attribute(&local_name!("src"));
603 let is_src_empty = src_attribute.is_empty();
604 let no_density_source_of_1 = source_set
605 .image_sources
606 .iter()
607 .all(|source| source.descriptor.density != Some(1.));
608 let no_width_descriptor = source_set
609 .image_sources
610 .iter()
611 .all(|source| source.descriptor.width.is_none());
612 if !is_src_empty && no_density_source_of_1 && no_width_descriptor {
613 source_set.image_sources.push(ImageSource {
614 url: src_attribute.to_string(),
615 descriptor: Descriptor {
616 width: None,
617 density: None,
618 },
619 })
620 }
621
622 self.normalise_source_densities(&mut source_set);
624
625 source_set
627 }
628
629 fn update_source_set(&self) {
631 *self.source_set.borrow_mut() = SourceSet::new();
633
634 let elem = self.upcast::<Element>();
639 let parent = elem.upcast::<Node>().GetParentElement();
640 let elements = match parent.as_ref() {
641 Some(p) => {
642 if p.is::<HTMLPictureElement>() {
643 p.upcast::<Node>()
644 .children()
645 .filter_map(DomRoot::downcast::<Element>)
646 .map(|n| DomRoot::from_ref(&*n))
647 .collect()
648 } else {
649 vec![DomRoot::from_ref(elem)]
650 }
651 },
652 None => vec![DomRoot::from_ref(elem)],
653 };
654
655 for element in &elements {
657 if *element == DomRoot::from_ref(elem) {
659 *self.source_set.borrow_mut() = self.create_source_set();
662
663 return;
665 }
666
667 if !element.is::<HTMLSourceElement>() {
669 continue;
670 }
671
672 let mut source_set = SourceSet::new();
673
674 match element.get_attribute(&ns!(), &local_name!("srcset")) {
678 Some(srcset) => {
679 source_set.image_sources = parse_a_srcset_attribute(&srcset.value());
680 },
681 _ => continue,
682 }
683
684 if source_set.image_sources.is_empty() {
686 continue;
687 }
688
689 if let Some(media) = element.get_attribute(&ns!(), &local_name!("media")) {
692 if !MediaList::matches_environment(&element.owner_document(), &media.value()) {
693 continue;
694 }
695 }
696
697 if let Some(sizes) = element.get_attribute(&ns!(), &local_name!("sizes")) {
700 source_set.source_size = parse_a_sizes_attribute(&sizes.value());
701 }
702
703 if let Some(type_) = element.get_attribute(&ns!(), &local_name!("type")) {
706 if !is_supported_image_mime_type(&type_.value()) {
707 continue;
708 }
709 }
710
711 if element
714 .get_attribute(&ns!(), &local_name!("width"))
715 .is_some() ||
716 element
717 .get_attribute(&ns!(), &local_name!("height"))
718 .is_some()
719 {
720 self.dimension_attribute_source.set(Some(element));
721 } else {
722 self.dimension_attribute_source.set(Some(elem));
723 }
724
725 self.normalise_source_densities(&mut source_set);
727
728 *self.source_set.borrow_mut() = source_set;
730
731 return;
733 }
734 }
735
736 fn evaluate_source_size_list(&self, source_size_list: &SourceSizeList) -> Au {
737 let document = self.owner_document();
738 let quirks_mode = document.quirks_mode();
739 source_size_list.evaluate(document.window().layout().device(), quirks_mode)
740 }
741
742 fn normalise_source_densities(&self, source_set: &mut SourceSet) {
744 let source_size = self.evaluate_source_size_list(&source_set.source_size);
746
747 for image_source in &mut source_set.image_sources {
749 if image_source.descriptor.density.is_some() {
752 continue;
753 }
754
755 if image_source.descriptor.width.is_some() {
759 let width = image_source.descriptor.width.unwrap();
760 image_source.descriptor.density = Some(width as f64 / source_size.to_f64_px());
761 } else {
762 image_source.descriptor.density = Some(1_f64);
764 }
765 }
766 }
767
768 fn select_image_source(&self) -> Option<(USVString, f64)> {
770 self.update_source_set();
772
773 if self.source_set.borrow().image_sources.is_empty() {
776 return None;
777 }
778
779 self.select_image_source_from_source_set()
781 }
782
783 fn select_image_source_from_source_set(&self) -> Option<(USVString, f64)> {
785 let source_set = self.source_set.borrow();
790 let len = source_set.image_sources.len();
791
792 let mut repeat_indices = FxHashSet::default();
794 for outer_index in 0..len {
795 if repeat_indices.contains(&outer_index) {
796 continue;
797 }
798 let imgsource = &source_set.image_sources[outer_index];
799 let pixel_density = imgsource.descriptor.density.unwrap();
800 for inner_index in (outer_index + 1)..len {
801 let imgsource2 = &source_set.image_sources[inner_index];
802 if pixel_density == imgsource2.descriptor.density.unwrap() {
803 repeat_indices.insert(inner_index);
804 }
805 }
806 }
807
808 let mut max = (0f64, 0);
809 let img_sources = &mut vec![];
810 for (index, image_source) in source_set.image_sources.iter().enumerate() {
811 if repeat_indices.contains(&index) {
812 continue;
813 }
814 let den = image_source.descriptor.density.unwrap();
815 if max.0 < den {
816 max = (den, img_sources.len());
817 }
818 img_sources.push(image_source);
819 }
820
821 let mut best_candidate = max;
824 let device_pixel_ratio = self
825 .owner_document()
826 .window()
827 .viewport_details()
828 .hidpi_scale_factor
829 .get() as f64;
830 for (index, image_source) in img_sources.iter().enumerate() {
831 let current_den = image_source.descriptor.density.unwrap();
832 if current_den < best_candidate.0 && current_den >= device_pixel_ratio {
833 best_candidate = (current_den, index);
834 }
835 }
836 let selected_source = img_sources.remove(best_candidate.1).clone();
837
838 Some((
840 USVString(selected_source.url),
841 selected_source.descriptor.density.unwrap(),
842 ))
843 }
844
845 fn init_image_request(
846 &self,
847 request: &mut RefMut<'_, ImageRequest>,
848 url: &ServoUrl,
849 src: &USVString,
850 can_gc: CanGc,
851 ) {
852 request.parsed_url = Some(url.clone());
853 request.source_url = Some(src.clone());
854 request.image = None;
855 request.metadata = None;
856 let document = self.owner_document();
857 LoadBlocker::terminate(&request.blocker, can_gc);
858 *request.blocker.borrow_mut() =
859 Some(LoadBlocker::new(&document, LoadType::Image(url.clone())));
860 }
861
862 fn prepare_image_request(
864 &self,
865 url: &ServoUrl,
866 src: &USVString,
867 selected_pixel_density: f64,
868 can_gc: CanGc,
869 ) {
870 match self.image_request.get() {
871 ImageRequestPhase::Pending => {
872 if let Some(pending_url) = self.pending_request.borrow().parsed_url.clone() {
873 if pending_url == *url {
875 return;
876 }
877 }
878 },
879 ImageRequestPhase::Current => {
880 let mut current_request = self.current_request.borrow_mut();
881 let mut pending_request = self.pending_request.borrow_mut();
882 match (current_request.parsed_url.clone(), current_request.state) {
884 (Some(parsed_url), State::PartiallyAvailable) => {
885 if parsed_url == *url {
887 pending_request.image = None;
889 pending_request.parsed_url = None;
890 LoadBlocker::terminate(&pending_request.blocker, can_gc);
891 return;
893 }
894 pending_request.current_pixel_density = Some(selected_pixel_density);
895 self.image_request.set(ImageRequestPhase::Pending);
896 self.init_image_request(&mut pending_request, url, src, can_gc);
897 },
898 (_, State::Broken) | (_, State::Unavailable) => {
899 current_request.current_pixel_density = Some(selected_pixel_density);
901 self.init_image_request(&mut current_request, url, src, can_gc);
902 self.reject_image_decode_promises();
903 },
904 (_, _) => {
905 pending_request.current_pixel_density = Some(selected_pixel_density);
907 self.image_request.set(ImageRequestPhase::Pending);
908 self.init_image_request(&mut pending_request, url, src, can_gc);
909 },
910 }
911 },
912 }
913 self.fetch_image(url, can_gc);
914 }
915
916 fn update_the_image_data_sync_steps(&self, can_gc: CanGc) {
918 let document = self.owner_document();
919 let global = self.owner_global();
920 let task_manager = global.task_manager();
921 let task_source = task_manager.dom_manipulation_task_source();
922 let this = Trusted::new(self);
923 let (src, pixel_density) = match self.select_image_source() {
924 Some(data) => data,
926 None => {
927 self.abort_request(State::Broken, ImageRequestPhase::Current, can_gc);
928 self.abort_request(State::Broken, ImageRequestPhase::Pending, can_gc);
929 task_source.queue(task!(image_null_source_error: move || {
931 let this = this.root();
932 {
933 let mut current_request =
934 this.current_request.borrow_mut();
935 current_request.source_url = None;
936 current_request.parsed_url = None;
937 }
938 let elem = this.upcast::<Element>();
939 let src_present = elem.has_attribute(&local_name!("src"));
940
941 if src_present || Self::uses_srcset_or_picture(elem) {
942 this.upcast::<EventTarget>().fire_event(atom!("error"), CanGc::note());
943 }
944 }));
945 return;
946 },
947 };
948
949 let base_url = document.base_url();
951 let parsed_url = base_url.join(&src.0);
952 match parsed_url {
953 Ok(url) => {
954 self.prepare_image_request(&url, &src, pixel_density, can_gc);
956 },
957 Err(_) => {
958 self.abort_request(State::Broken, ImageRequestPhase::Current, can_gc);
959 self.abort_request(State::Broken, ImageRequestPhase::Pending, can_gc);
960 let src = src.0;
962 task_source.queue(task!(image_selected_source_error: move || {
963 let this = this.root();
964 {
965 let mut current_request =
966 this.current_request.borrow_mut();
967 current_request.source_url = Some(USVString(src))
968 }
969 this.upcast::<EventTarget>().fire_event(atom!("error"), CanGc::note());
970
971 }));
972 },
973 }
974 }
975
976 pub(crate) fn update_the_image_data(&self, can_gc: CanGc) {
978 let document = self.owner_document();
979 let window = document.window();
980 let elem = self.upcast::<Element>();
981 let src = elem.get_url_attribute(&local_name!("src"));
982 let base_url = document.base_url();
983
984 {
988 let mut current_request = self.current_request.borrow_mut();
989 current_request.state = State::Unavailable;
990 }
991
992 if !document.is_active() {
993 }
997 let mut selected_source = None;
1002 let mut pixel_density = None;
1003 let src_set = elem.get_url_attribute(&local_name!("srcset"));
1004 let is_parent_picture = elem
1005 .upcast::<Node>()
1006 .GetParentElement()
1007 .is_some_and(|p| p.is::<HTMLPictureElement>());
1008 if src_set.is_empty() && !is_parent_picture && !src.is_empty() {
1009 selected_source = Some(src.clone());
1010 pixel_density = Some(1_f64);
1011 };
1012
1013 self.last_selected_source
1015 .borrow_mut()
1016 .clone_from(&selected_source);
1017
1018 if let Some(src) = selected_source {
1020 if let Ok(img_url) = base_url.join(&src) {
1021 let image_cache = window.image_cache();
1022 let response = image_cache.get_image(
1023 img_url.clone(),
1024 window.origin().immutable().clone(),
1025 cors_setting_for_element(self.upcast()),
1026 );
1027
1028 if let Some(image) = response {
1029 self.generation.set(self.generation.get() + 1);
1032 let metadata = image.metadata();
1034 self.abort_request(
1036 State::CompletelyAvailable,
1037 ImageRequestPhase::Current,
1038 can_gc,
1039 );
1040 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, can_gc);
1041 let mut current_request = self.current_request.borrow_mut();
1042 current_request.final_url = Some(img_url.clone());
1043 current_request.image = Some(image);
1044 current_request.metadata = Some(metadata);
1045 current_request.current_pixel_density = pixel_density;
1047 let this = Trusted::new(self);
1048 let src = src.0;
1049
1050 self.owner_global()
1051 .task_manager()
1052 .dom_manipulation_task_source()
1053 .queue(task!(image_load_event: move || {
1054 let this = this.root();
1055 {
1056 let mut current_request =
1057 this.current_request.borrow_mut();
1058 current_request.parsed_url = Some(img_url);
1059 current_request.source_url = Some(USVString(src));
1060 }
1061 this.upcast::<EventTarget>().fire_event(atom!("load"), CanGc::note());
1063 }));
1064 return;
1065 }
1066 }
1067 }
1068 self.generation.set(self.generation.get() + 1);
1070 let task = ImageElementMicrotask::StableStateUpdateImageData {
1071 elem: DomRoot::from_ref(self),
1072 generation: self.generation.get(),
1073 };
1074 ScriptThread::await_stable_state(Microtask::ImageElement(task));
1075 }
1076
1077 pub(crate) fn react_to_environment_changes(&self) {
1079 let task = ImageElementMicrotask::EnvironmentChanges {
1081 elem: DomRoot::from_ref(self),
1082 generation: self.generation.get(),
1083 };
1084 ScriptThread::await_stable_state(Microtask::ImageElement(task));
1085 }
1086
1087 fn react_to_environment_changes_sync_steps(&self, generation: u32, can_gc: CanGc) {
1089 let elem = self.upcast::<Element>();
1090 let document = elem.owner_document();
1091 let has_pending_request = matches!(self.image_request.get(), ImageRequestPhase::Pending);
1092
1093 if !document.is_active() || !Self::uses_srcset_or_picture(elem) || has_pending_request {
1095 return;
1096 }
1097
1098 let (selected_source, selected_pixel_density) = match self.select_image_source() {
1100 Some(selected) => selected,
1101 None => return,
1102 };
1103
1104 let same_source = match *self.last_selected_source.borrow() {
1106 Some(ref last_src) => *last_src == selected_source,
1107 _ => false,
1108 };
1109
1110 let same_selected_pixel_density = match self.current_request.borrow().current_pixel_density
1111 {
1112 Some(den) => selected_pixel_density == den,
1113 _ => false,
1114 };
1115
1116 if same_source && same_selected_pixel_density {
1117 return;
1118 }
1119
1120 let base_url = document.base_url();
1121 let img_url = match base_url.join(&selected_source.0) {
1123 Ok(url) => url,
1124 Err(_) => return,
1125 };
1126
1127 self.image_request.set(ImageRequestPhase::Pending);
1129 self.init_image_request(
1130 &mut self.pending_request.borrow_mut(),
1131 &img_url,
1132 &selected_source,
1133 can_gc,
1134 );
1135
1136 let window = self.owner_window();
1138 let cache_result = window.image_cache().get_cached_image_status(
1139 img_url.clone(),
1140 window.origin().immutable().clone(),
1141 cors_setting_for_element(self.upcast()),
1142 );
1143
1144 let change_type = ChangeType::Environment {
1145 selected_source: selected_source.clone(),
1146 selected_pixel_density,
1147 };
1148
1149 match cache_result {
1150 ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable { .. }) => {
1151 self.finish_reacting_to_environment_change(
1153 selected_source,
1154 generation,
1155 selected_pixel_density,
1156 )
1157 },
1158 ImageCacheResult::Available(ImageOrMetadataAvailable::MetadataAvailable(m, id)) => {
1159 self.process_image_response_for_environment_change(
1160 ImageResponse::MetadataLoaded(m),
1161 selected_source,
1162 generation,
1163 selected_pixel_density,
1164 can_gc,
1165 );
1166 self.register_image_cache_callback(id, change_type);
1167 },
1168 ImageCacheResult::FailedToLoadOrDecode => {
1169 self.process_image_response_for_environment_change(
1170 ImageResponse::FailedToLoadOrDecode,
1171 selected_source,
1172 generation,
1173 selected_pixel_density,
1174 can_gc,
1175 );
1176 },
1177 ImageCacheResult::ReadyForRequest(id) => {
1178 self.fetch_request(&img_url, id);
1179 self.register_image_cache_callback(id, change_type);
1180 },
1181 ImageCacheResult::Pending(id) => {
1182 self.register_image_cache_callback(id, change_type);
1183 },
1184 }
1185 }
1186
1187 fn react_to_decode_image_sync_steps(&self, promise: Rc<Promise>, can_gc: CanGc) {
1189 if !self.owner_document().is_fully_active() ||
1193 matches!(self.current_request.borrow().state, State::Broken)
1194 {
1195 promise.reject_error(Error::Encoding, can_gc);
1196 } else if matches!(
1197 self.current_request.borrow().state,
1198 State::CompletelyAvailable
1199 ) {
1200 promise.resolve_native(&(), can_gc);
1202 } else if matches!(self.current_request.borrow().state, State::Unavailable) &&
1203 self.current_request.borrow().source_url.is_none()
1204 {
1205 promise.reject_error(Error::Encoding, can_gc);
1209 } else {
1210 self.image_decode_promises.borrow_mut().push(promise);
1211 }
1212 }
1213
1214 fn resolve_image_decode_promises(&self) {
1216 if self.image_decode_promises.borrow().is_empty() {
1217 return;
1218 }
1219
1220 let trusted_image_decode_promises: Vec<TrustedPromise> = self
1224 .image_decode_promises
1225 .borrow()
1226 .iter()
1227 .map(|promise| TrustedPromise::new(promise.clone()))
1228 .collect();
1229
1230 self.image_decode_promises.borrow_mut().clear();
1231
1232 self.owner_global()
1233 .task_manager()
1234 .dom_manipulation_task_source()
1235 .queue(task!(fulfill_image_decode_promises: move || {
1236 for trusted_promise in trusted_image_decode_promises {
1237 trusted_promise.root().resolve_native(&(), CanGc::note());
1238 }
1239 }));
1240 }
1241
1242 fn reject_image_decode_promises(&self) {
1244 if self.image_decode_promises.borrow().is_empty() {
1245 return;
1246 }
1247
1248 let trusted_image_decode_promises: Vec<TrustedPromise> = self
1251 .image_decode_promises
1252 .borrow()
1253 .iter()
1254 .map(|promise| TrustedPromise::new(promise.clone()))
1255 .collect();
1256
1257 self.image_decode_promises.borrow_mut().clear();
1258
1259 self.owner_global()
1260 .task_manager()
1261 .dom_manipulation_task_source()
1262 .queue(task!(reject_image_decode_promises: move || {
1263 for trusted_promise in trusted_image_decode_promises {
1264 trusted_promise.root().reject_error(Error::Encoding, CanGc::note());
1265 }
1266 }));
1267 }
1268
1269 fn finish_reacting_to_environment_change(
1271 &self,
1272 src: USVString,
1273 generation: u32,
1274 selected_pixel_density: f64,
1275 ) {
1276 let this = Trusted::new(self);
1277 let src = src.0;
1278 self.owner_global().task_manager().dom_manipulation_task_source().queue(
1279 task!(image_load_event: move || {
1280 let this = this.root();
1281 let relevant_mutation = this.generation.get() != generation;
1282 if relevant_mutation {
1284 this.abort_request(State::Unavailable, ImageRequestPhase::Pending, CanGc::note());
1285 return;
1286 }
1287 *this.last_selected_source.borrow_mut() = Some(USVString(src));
1289
1290 {
1291 let mut pending_request = this.pending_request.borrow_mut();
1292 pending_request.current_pixel_density = Some(selected_pixel_density);
1293
1294 pending_request.state = State::CompletelyAvailable;
1296
1297 #[allow(clippy::swap_with_temporary)]
1302 mem::swap(&mut this.current_request.borrow_mut(), &mut pending_request);
1303 }
1304 this.abort_request(State::Unavailable, ImageRequestPhase::Pending, CanGc::note());
1305
1306 this.upcast::<Node>().dirty(NodeDamage::Other);
1308
1309 this.upcast::<EventTarget>().fire_event(atom!("load"), CanGc::note());
1311 })
1312 );
1313 }
1314
1315 fn uses_srcset_or_picture(elem: &Element) -> bool {
1316 let has_src = elem.has_attribute(&local_name!("srcset"));
1317 let is_parent_picture = elem
1318 .upcast::<Node>()
1319 .GetParentElement()
1320 .is_some_and(|p| p.is::<HTMLPictureElement>());
1321 has_src || is_parent_picture
1322 }
1323
1324 fn new_inherited(
1325 local_name: LocalName,
1326 prefix: Option<Prefix>,
1327 document: &Document,
1328 creator: ElementCreator,
1329 ) -> HTMLImageElement {
1330 HTMLImageElement {
1331 htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
1332 image_request: Cell::new(ImageRequestPhase::Current),
1333 current_request: DomRefCell::new(ImageRequest {
1334 state: State::Unavailable,
1335 parsed_url: None,
1336 source_url: None,
1337 image: None,
1338 metadata: None,
1339 blocker: DomRefCell::new(None),
1340 final_url: None,
1341 current_pixel_density: None,
1342 }),
1343 pending_request: DomRefCell::new(ImageRequest {
1344 state: State::Unavailable,
1345 parsed_url: None,
1346 source_url: None,
1347 image: None,
1348 metadata: None,
1349 blocker: DomRefCell::new(None),
1350 final_url: None,
1351 current_pixel_density: None,
1352 }),
1353 form_owner: Default::default(),
1354 generation: Default::default(),
1355 source_set: DomRefCell::new(SourceSet::new()),
1356 dimension_attribute_source: Default::default(),
1357 last_selected_source: DomRefCell::new(None),
1358 image_decode_promises: DomRefCell::new(vec![]),
1359 line_number: creator.return_line_number(),
1360 }
1361 }
1362
1363 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
1364 pub(crate) fn new(
1365 local_name: LocalName,
1366 prefix: Option<Prefix>,
1367 document: &Document,
1368 proto: Option<HandleObject>,
1369 creator: ElementCreator,
1370 can_gc: CanGc,
1371 ) -> DomRoot<HTMLImageElement> {
1372 let image_element = Node::reflect_node_with_proto(
1373 Box::new(HTMLImageElement::new_inherited(
1374 local_name, prefix, document, creator,
1375 )),
1376 document,
1377 proto,
1378 can_gc,
1379 );
1380 image_element
1381 .dimension_attribute_source
1382 .set(Some(image_element.upcast()));
1383 image_element
1384 }
1385
1386 pub(crate) fn areas(&self) -> Option<Vec<DomRoot<HTMLAreaElement>>> {
1387 let elem = self.upcast::<Element>();
1388 let usemap_attr = elem.get_attribute(&ns!(), &local_name!("usemap"))?;
1389
1390 let value = usemap_attr.value();
1391
1392 if value.is_empty() || !value.is_char_boundary(1) {
1393 return None;
1394 }
1395
1396 let (first, last) = value.split_at(1);
1397
1398 if first != "#" || last.is_empty() {
1399 return None;
1400 }
1401
1402 let useMapElements = self
1403 .owner_document()
1404 .upcast::<Node>()
1405 .traverse_preorder(ShadowIncluding::No)
1406 .filter_map(DomRoot::downcast::<HTMLMapElement>)
1407 .find(|n| {
1408 n.upcast::<Element>()
1409 .get_name()
1410 .is_some_and(|n| *n == *last)
1411 });
1412
1413 useMapElements.map(|mapElem| mapElem.get_area_elements())
1414 }
1415
1416 pub(crate) fn same_origin(&self, origin: &MutableOrigin) -> bool {
1417 if let Some(ref image) = self.current_request.borrow().image {
1418 return image.cors_status() == CorsStatus::Safe;
1419 }
1420
1421 self.current_request
1422 .borrow()
1423 .final_url
1424 .as_ref()
1425 .is_some_and(|url| url.scheme() == "data" || url.origin().same_origin(origin))
1426 }
1427
1428 fn generation_id(&self) -> u32 {
1429 self.generation.get()
1430 }
1431
1432 fn load_broken_image_icon(&self) {
1433 let window = self.owner_window();
1434 let Some(broken_image_icon) = window.image_cache().get_broken_image_icon() else {
1435 return;
1436 };
1437
1438 self.current_request.borrow_mut().metadata = Some(broken_image_icon.metadata);
1439 self.current_request.borrow_mut().image = Some(Image::Raster(broken_image_icon));
1440 self.upcast::<Node>().dirty(NodeDamage::Other);
1441 }
1442}
1443
1444#[derive(JSTraceable, MallocSizeOf)]
1445pub(crate) enum ImageElementMicrotask {
1446 StableStateUpdateImageData {
1447 elem: DomRoot<HTMLImageElement>,
1448 generation: u32,
1449 },
1450 EnvironmentChanges {
1451 elem: DomRoot<HTMLImageElement>,
1452 generation: u32,
1453 },
1454 Decode {
1455 elem: DomRoot<HTMLImageElement>,
1456 #[conditional_malloc_size_of]
1457 promise: Rc<Promise>,
1458 },
1459}
1460
1461impl MicrotaskRunnable for ImageElementMicrotask {
1462 fn handler(&self, can_gc: CanGc) {
1463 match *self {
1464 ImageElementMicrotask::StableStateUpdateImageData {
1465 ref elem,
1466 ref generation,
1467 } => {
1468 if elem.generation.get() == *generation {
1471 elem.update_the_image_data_sync_steps(can_gc);
1472 }
1473 },
1474 ImageElementMicrotask::EnvironmentChanges {
1475 ref elem,
1476 ref generation,
1477 } => {
1478 elem.react_to_environment_changes_sync_steps(*generation, can_gc);
1479 },
1480 ImageElementMicrotask::Decode {
1481 ref elem,
1482 ref promise,
1483 } => {
1484 elem.react_to_decode_image_sync_steps(promise.clone(), can_gc);
1485 },
1486 }
1487 }
1488
1489 fn enter_realm(&self) -> JSAutoRealm {
1490 match self {
1491 &ImageElementMicrotask::StableStateUpdateImageData { ref elem, .. } |
1492 &ImageElementMicrotask::EnvironmentChanges { ref elem, .. } |
1493 &ImageElementMicrotask::Decode { ref elem, .. } => enter_realm(&**elem),
1494 }
1495 }
1496}
1497
1498pub(crate) trait LayoutHTMLImageElementHelpers {
1499 fn image_url(self) -> Option<ServoUrl>;
1500 fn image_density(self) -> Option<f64>;
1501 fn image_data(self) -> (Option<Image>, Option<ImageMetadata>);
1502 fn get_width(self) -> LengthOrPercentageOrAuto;
1503 fn get_height(self) -> LengthOrPercentageOrAuto;
1504 fn showing_broken_image_icon(self) -> bool;
1505}
1506
1507impl<'dom> LayoutDom<'dom, HTMLImageElement> {
1508 #[expect(unsafe_code)]
1509 fn current_request(self) -> &'dom ImageRequest {
1510 unsafe { self.unsafe_get().current_request.borrow_for_layout() }
1511 }
1512
1513 #[expect(unsafe_code)]
1514 fn dimension_attribute_source(self) -> LayoutDom<'dom, Element> {
1515 unsafe {
1516 self.unsafe_get()
1517 .dimension_attribute_source
1518 .get_inner_as_layout()
1519 .expect("dimension attribute source should be always non-null")
1520 }
1521 }
1522}
1523
1524impl LayoutHTMLImageElementHelpers for LayoutDom<'_, HTMLImageElement> {
1525 fn image_url(self) -> Option<ServoUrl> {
1526 self.current_request().parsed_url.clone()
1527 }
1528
1529 fn image_data(self) -> (Option<Image>, Option<ImageMetadata>) {
1530 let current_request = self.current_request();
1531 (current_request.image.clone(), current_request.metadata)
1532 }
1533
1534 fn image_density(self) -> Option<f64> {
1535 self.current_request().current_pixel_density
1536 }
1537
1538 fn showing_broken_image_icon(self) -> bool {
1539 matches!(self.current_request().state, State::Broken)
1540 }
1541
1542 fn get_width(self) -> LengthOrPercentageOrAuto {
1543 self.dimension_attribute_source()
1544 .get_attr_for_layout(&ns!(), &local_name!("width"))
1545 .map(AttrValue::as_dimension)
1546 .cloned()
1547 .unwrap_or(LengthOrPercentageOrAuto::Auto)
1548 }
1549
1550 fn get_height(self) -> LengthOrPercentageOrAuto {
1551 self.dimension_attribute_source()
1552 .get_attr_for_layout(&ns!(), &local_name!("height"))
1553 .map(AttrValue::as_dimension)
1554 .cloned()
1555 .unwrap_or(LengthOrPercentageOrAuto::Auto)
1556 }
1557}
1558
1559fn parse_a_sizes_attribute(value: &str) -> SourceSizeList {
1561 let mut input = ParserInput::new(value);
1562 let mut parser = Parser::new(&mut input);
1563 let url_data = Url::parse("about:blank").unwrap().into();
1564 let context = ParserContext::new(
1565 Origin::Author,
1566 &url_data,
1567 Some(CssRuleType::Style),
1568 ParsingMode::empty(),
1571 QuirksMode::NoQuirks,
1572 Default::default(),
1573 None,
1574 None,
1575 );
1576 SourceSizeList::parse(&context, &mut parser)
1577}
1578
1579#[allow(non_snake_case)]
1580impl HTMLImageElementMethods<crate::DomTypeHolder> for HTMLImageElement {
1581 fn Image(
1583 window: &Window,
1584 proto: Option<HandleObject>,
1585 can_gc: CanGc,
1586 width: Option<u32>,
1587 height: Option<u32>,
1588 ) -> Fallible<DomRoot<HTMLImageElement>> {
1589 let document = window.Document();
1591
1592 let element = Element::create(
1595 QualName::new(None, ns!(html), local_name!("img")),
1596 None,
1597 &document,
1598 ElementCreator::ScriptCreated,
1599 CustomElementCreationMode::Synchronous,
1600 proto,
1601 can_gc,
1602 );
1603
1604 let image = DomRoot::downcast::<HTMLImageElement>(element).unwrap();
1605
1606 if let Some(w) = width {
1608 image.SetWidth(w);
1609 }
1610
1611 if let Some(h) = height {
1614 image.SetHeight(h);
1615 }
1616
1617 Ok(image)
1619 }
1620
1621 make_getter!(Alt, "alt");
1623 make_setter!(SetAlt, "alt");
1625
1626 make_url_getter!(Src, "src");
1628
1629 make_url_setter!(SetSrc, "src");
1631
1632 make_url_getter!(Srcset, "srcset");
1634 make_url_setter!(SetSrcset, "srcset");
1636
1637 make_getter!(Sizes, "sizes");
1639
1640 make_setter!(SetSizes, "sizes");
1642
1643 fn GetCrossOrigin(&self) -> Option<DOMString> {
1645 reflect_cross_origin_attribute(self.upcast::<Element>())
1646 }
1647
1648 fn SetCrossOrigin(&self, value: Option<DOMString>, can_gc: CanGc) {
1650 set_cross_origin_attribute(self.upcast::<Element>(), value, can_gc);
1651 }
1652
1653 make_getter!(UseMap, "usemap");
1655 make_setter!(SetUseMap, "usemap");
1657
1658 make_bool_getter!(IsMap, "ismap");
1660 make_bool_setter!(SetIsMap, "ismap");
1662
1663 fn Width(&self) -> u32 {
1665 let node = self.upcast::<Node>();
1666 node.content_box()
1667 .map(|rect| rect.size.width.to_px() as u32)
1668 .unwrap_or_else(|| self.NaturalWidth())
1669 }
1670
1671 make_dimension_uint_setter!(SetWidth, "width");
1673
1674 fn Height(&self) -> u32 {
1676 let node = self.upcast::<Node>();
1677 node.content_box()
1678 .map(|rect| rect.size.height.to_px() as u32)
1679 .unwrap_or_else(|| self.NaturalHeight())
1680 }
1681
1682 make_dimension_uint_setter!(SetHeight, "height");
1684
1685 fn NaturalWidth(&self) -> u32 {
1687 let request = self.current_request.borrow();
1688 if matches!(request.state, State::Broken) {
1689 return 0;
1690 }
1691
1692 let pixel_density = request.current_pixel_density.unwrap_or(1f64);
1693 match request.metadata {
1694 Some(ref metadata) => (metadata.width as f64 / pixel_density) as u32,
1695 None => 0,
1696 }
1697 }
1698
1699 fn NaturalHeight(&self) -> u32 {
1701 let request = self.current_request.borrow();
1702 if matches!(request.state, State::Broken) {
1703 return 0;
1704 }
1705
1706 let pixel_density = request.current_pixel_density.unwrap_or(1f64);
1707 match request.metadata {
1708 Some(ref metadata) => (metadata.height as f64 / pixel_density) as u32,
1709 None => 0,
1710 }
1711 }
1712
1713 fn Complete(&self) -> bool {
1715 let elem = self.upcast::<Element>();
1716 let srcset_absent = !elem.has_attribute(&local_name!("srcset"));
1717 if !elem.has_attribute(&local_name!("src")) && srcset_absent {
1718 return true;
1719 }
1720 let src = elem.get_string_attribute(&local_name!("src"));
1721 if srcset_absent && src.is_empty() {
1722 return true;
1723 }
1724 let request = self.current_request.borrow();
1725 let request_state = request.state;
1726 match request_state {
1727 State::CompletelyAvailable | State::Broken => true,
1728 State::PartiallyAvailable | State::Unavailable => false,
1729 }
1730 }
1731
1732 fn CurrentSrc(&self) -> USVString {
1734 let current_request = self.current_request.borrow();
1735 let url = ¤t_request.parsed_url;
1736 match *url {
1737 Some(ref url) => USVString(url.clone().into_string()),
1738 None => {
1739 let unparsed_url = ¤t_request.source_url;
1740 match *unparsed_url {
1741 Some(ref url) => url.clone(),
1742 None => USVString("".to_owned()),
1743 }
1744 },
1745 }
1746 }
1747
1748 fn ReferrerPolicy(&self) -> DOMString {
1750 reflect_referrer_policy_attribute(self.upcast::<Element>())
1751 }
1752
1753 make_setter!(SetReferrerPolicy, "referrerpolicy");
1755
1756 fn Decode(&self, can_gc: CanGc) -> Rc<Promise> {
1758 let promise = Promise::new(&self.global(), can_gc);
1760
1761 let task = ImageElementMicrotask::Decode {
1763 elem: DomRoot::from_ref(self),
1764 promise: promise.clone(),
1765 };
1766 ScriptThread::await_stable_state(Microtask::ImageElement(task));
1767
1768 promise
1770 }
1771
1772 make_getter!(Name, "name");
1774
1775 make_atomic_setter!(SetName, "name");
1777
1778 make_getter!(Align, "align");
1780
1781 make_setter!(SetAlign, "align");
1783
1784 make_uint_getter!(Hspace, "hspace");
1786
1787 make_uint_setter!(SetHspace, "hspace");
1789
1790 make_uint_getter!(Vspace, "vspace");
1792
1793 make_uint_setter!(SetVspace, "vspace");
1795
1796 make_getter!(LongDesc, "longdesc");
1798
1799 make_setter!(SetLongDesc, "longdesc");
1801
1802 make_getter!(Border, "border");
1804
1805 make_setter!(SetBorder, "border");
1807}
1808
1809impl VirtualMethods for HTMLImageElement {
1810 fn super_type(&self) -> Option<&dyn VirtualMethods> {
1811 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
1812 }
1813
1814 fn adopting_steps(&self, old_doc: &Document, can_gc: CanGc) {
1815 self.super_type().unwrap().adopting_steps(old_doc, can_gc);
1816 self.update_the_image_data(can_gc);
1817 }
1818
1819 fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
1820 self.super_type()
1821 .unwrap()
1822 .attribute_mutated(attr, mutation, can_gc);
1823 match attr.local_name() {
1824 &local_name!("src") |
1825 &local_name!("srcset") |
1826 &local_name!("width") |
1827 &local_name!("sizes") => {
1828 self.update_the_image_data(can_gc);
1832 },
1833 &local_name!("crossorigin") => {
1834 let cross_origin_state_changed = match mutation {
1837 AttributeMutation::Removed | AttributeMutation::Set(None, _) => true,
1838 AttributeMutation::Set(Some(old_value), _) => {
1839 let new_cors_setting =
1840 CorsSettings::from_enumerated_attribute(&attr.value());
1841 let old_cors_setting = CorsSettings::from_enumerated_attribute(old_value);
1842
1843 new_cors_setting != old_cors_setting
1844 },
1845 };
1846
1847 if cross_origin_state_changed {
1848 self.update_the_image_data(can_gc);
1849 }
1850 },
1851 &local_name!("referrerpolicy") => {
1852 let referrer_policy_state_changed = match mutation {
1855 AttributeMutation::Removed | AttributeMutation::Set(None, _) => {
1856 ReferrerPolicy::from(&**attr.value()) != ReferrerPolicy::EmptyString
1857 },
1858 AttributeMutation::Set(Some(old_value), _) => {
1859 ReferrerPolicy::from(&**attr.value()) != ReferrerPolicy::from(&**old_value)
1860 },
1861 };
1862
1863 if referrer_policy_state_changed {
1864 self.update_the_image_data(can_gc);
1865 }
1866 },
1867 _ => {},
1868 }
1869 }
1870
1871 fn attribute_affects_presentational_hints(&self, attr: &Attr) -> bool {
1872 match attr.local_name() {
1873 &local_name!("width") | &local_name!("height") => true,
1874 _ => self
1875 .super_type()
1876 .unwrap()
1877 .attribute_affects_presentational_hints(attr),
1878 }
1879 }
1880
1881 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
1882 match name {
1883 &local_name!("width") | &local_name!("height") => {
1884 AttrValue::from_dimension(value.into())
1885 },
1886 &local_name!("hspace") | &local_name!("vspace") => AttrValue::from_u32(value.into(), 0),
1887 _ => self
1888 .super_type()
1889 .unwrap()
1890 .parse_plain_attribute(name, value),
1891 }
1892 }
1893
1894 fn handle_event(&self, event: &Event, can_gc: CanGc) {
1895 if event.type_() != atom!("click") {
1896 return;
1897 }
1898
1899 let area_elements = self.areas();
1900 let elements = match area_elements {
1901 Some(x) => x,
1902 None => return,
1903 };
1904
1905 let mouse_event = match event.downcast::<MouseEvent>() {
1907 Some(x) => x,
1908 None => return,
1909 };
1910
1911 let point = Point2D::new(
1912 mouse_event.ClientX().to_f32().unwrap(),
1913 mouse_event.ClientY().to_f32().unwrap(),
1914 );
1915 let bcr = self.upcast::<Element>().GetBoundingClientRect(can_gc);
1916 let bcr_p = Point2D::new(bcr.X() as f32, bcr.Y() as f32);
1917
1918 for element in elements {
1920 let shape = element.get_shape_from_coords();
1921 let shp = match shape {
1922 Some(x) => x.absolute_coords(bcr_p),
1923 None => return,
1924 };
1925 if shp.hit_test(&point) {
1926 element.activation_behavior(event, self.upcast(), can_gc);
1927 return;
1928 }
1929 }
1930 }
1931
1932 fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) {
1934 if let Some(s) = self.super_type() {
1935 s.bind_to_tree(context, can_gc);
1936 }
1937 let document = self.owner_document();
1938 if context.tree_connected {
1939 document.register_responsive_image(self);
1940 }
1941
1942 let parent = self.upcast::<Node>().GetParentNode().unwrap();
1943
1944 if parent.is::<HTMLPictureElement>() && std::ptr::eq(&*parent, context.parent) {
1947 self.update_the_image_data(can_gc);
1948 }
1949 }
1950
1951 fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) {
1953 self.super_type().unwrap().unbind_from_tree(context, can_gc);
1954 let document = self.owner_document();
1955 document.unregister_responsive_image(self);
1956
1957 if context.parent.is::<HTMLPictureElement>() && !self.upcast::<Node>().has_parent() {
1960 self.update_the_image_data(can_gc);
1961 }
1962 }
1963}
1964
1965impl FormControl for HTMLImageElement {
1966 fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
1967 self.form_owner.get()
1968 }
1969
1970 fn set_form_owner(&self, form: Option<&HTMLFormElement>) {
1971 self.form_owner.set(form);
1972 }
1973
1974 fn to_element(&self) -> &Element {
1975 self.upcast::<Element>()
1976 }
1977
1978 fn is_listed(&self) -> bool {
1979 false
1980 }
1981}
1982
1983pub(crate) fn collect_sequence_characters(
1986 s: &str,
1987 mut predicate: impl FnMut(&char) -> bool,
1988) -> (&str, &str) {
1989 let i = s.find(|ch| !predicate(&ch)).unwrap_or(s.len());
1990 (&s[0..i], &s[i..])
1991}
1992
1993fn is_valid_non_negative_integer_string(s: &str) -> bool {
1996 s.chars().all(|c| c.is_ascii_digit())
1997}
1998
1999fn is_valid_floating_point_number_string(s: &str) -> bool {
2002 static RE: LazyLock<Regex> =
2003 LazyLock::new(|| Regex::new(r"^-?(?:\d+\.\d+|\d+|\.\d+)(?:(e|E)(\+|\-)?\d+)?$").unwrap());
2004
2005 RE.is_match(s)
2006}
2007
2008pub fn parse_a_srcset_attribute(input: &str) -> Vec<ImageSource> {
2011 let mut current_index = 0;
2014
2015 let mut candidates = vec![];
2017 while current_index < input.len() {
2018 let remaining_string = &input[current_index..];
2019
2020 let (collected_characters, string_after_whitespace) =
2027 collect_sequence_characters(remaining_string, |character| {
2028 *character == ',' || character.is_ascii_whitespace()
2029 });
2030
2031 current_index += collected_characters.len();
2034
2035 if string_after_whitespace.is_empty() {
2037 return candidates;
2038 }
2039
2040 let (url, _) =
2043 collect_sequence_characters(string_after_whitespace, |c| !char::is_ascii_whitespace(c));
2044
2045 current_index += url.len();
2048
2049 let mut descriptors = Vec::new();
2051
2052 if url.ends_with(',') {
2056 let image_source = ImageSource {
2057 url: url.trim_end_matches(',').into(),
2058 descriptor: Descriptor {
2059 width: None,
2060 density: None,
2061 },
2062 };
2063 candidates.push(image_source);
2064 continue;
2065 }
2066
2067 let descriptors_string = &input[current_index..];
2070 let (spaces, descriptors_string) =
2071 collect_sequence_characters(descriptors_string, |character| {
2072 character.is_ascii_whitespace()
2073 });
2074 current_index += spaces.len();
2075
2076 let mut current_descriptor = String::new();
2078
2079 let mut state = ParseState::InDescriptor;
2081
2082 let mut characters = descriptors_string.chars();
2086 let mut character = characters.next();
2087 if let Some(character) = character {
2088 current_index += character.len_utf8();
2089 }
2090
2091 loop {
2092 match (state, character) {
2093 (ParseState::InDescriptor, Some(character)) if character.is_ascii_whitespace() => {
2094 if !current_descriptor.is_empty() {
2098 descriptors.push(current_descriptor);
2099 current_descriptor = String::new();
2100 state = ParseState::AfterDescriptor;
2101 }
2102 },
2103 (ParseState::InDescriptor, Some(',')) => {
2104 if !current_descriptor.is_empty() {
2108 descriptors.push(current_descriptor);
2109 }
2110 break;
2111 },
2112 (ParseState::InDescriptor, Some('(')) => {
2113 current_descriptor.push('(');
2115 state = ParseState::InParens;
2116 },
2117 (ParseState::InDescriptor, Some(character)) => {
2118 current_descriptor.push(character);
2120 },
2121 (ParseState::InDescriptor, None) => {
2122 if !current_descriptor.is_empty() {
2125 descriptors.push(current_descriptor);
2126 }
2127 break;
2128 },
2129 (ParseState::InParens, Some(')')) => {
2130 current_descriptor.push(')');
2132 state = ParseState::InDescriptor;
2133 },
2134 (ParseState::InParens, Some(character)) => {
2135 current_descriptor.push(character);
2137 },
2138 (ParseState::InParens, None) => {
2139 descriptors.push(current_descriptor);
2142 break;
2143 },
2144 (ParseState::AfterDescriptor, Some(character))
2145 if character.is_ascii_whitespace() =>
2146 {
2147 },
2149 (ParseState::AfterDescriptor, Some(_)) => {
2150 state = ParseState::InDescriptor;
2153 continue;
2154 },
2155 (ParseState::AfterDescriptor, None) => {
2156 break;
2158 },
2159 }
2160
2161 character = characters.next();
2162 if let Some(character) = character {
2163 current_index += character.len_utf8();
2164 }
2165 }
2166
2167 let mut error = false;
2169 let mut width: Option<u32> = None;
2171 let mut density: Option<f64> = None;
2173 let mut future_compat_h: Option<u32> = None;
2175
2176 for descriptor in descriptors.into_iter() {
2179 let Some(last_character) = descriptor.chars().last() else {
2180 break;
2181 };
2182
2183 let first_part_of_string = &descriptor[0..descriptor.len() - last_character.len_utf8()];
2184 match last_character {
2185 'w' if is_valid_non_negative_integer_string(first_part_of_string) &&
2192 density.is_none() &&
2193 width.is_none() =>
2194 {
2195 match parse_unsigned_integer(first_part_of_string.chars()) {
2196 Ok(number) if number > 0 => {
2197 width = Some(number);
2198 continue;
2199 },
2200 _ => error = true,
2201 }
2202 },
2203
2204 'x' if is_valid_floating_point_number_string(first_part_of_string) &&
2218 width.is_none() &&
2219 density.is_none() &&
2220 future_compat_h.is_none() =>
2221 {
2222 match first_part_of_string.parse::<f64>() {
2223 Ok(number) if number.is_finite() && number >= 0. => {
2224 density = Some(number);
2225 continue;
2226 },
2227 _ => error = true,
2228 }
2229 },
2230
2231 'h' if is_valid_non_negative_integer_string(first_part_of_string) &&
2240 future_compat_h.is_none() &&
2241 density.is_none() =>
2242 {
2243 match parse_unsigned_integer(first_part_of_string.chars()) {
2244 Ok(number) if number > 0 => {
2245 future_compat_h = Some(number);
2246 continue;
2247 },
2248 _ => error = true,
2249 }
2250 },
2251
2252 _ => error = true,
2255 }
2256
2257 if error {
2258 break;
2259 }
2260 }
2261
2262 if future_compat_h.is_some() && width.is_none() {
2264 error = true;
2265 }
2266
2267 if !error {
2271 let image_source = ImageSource {
2272 url: url.into(),
2273 descriptor: Descriptor { width, density },
2274 };
2275 candidates.push(image_source);
2276 }
2277
2278 }
2280 candidates
2281}
2282
2283#[derive(Clone)]
2284enum ChangeType {
2285 Environment {
2286 selected_source: USVString,
2287 selected_pixel_density: f64,
2288 },
2289 Element,
2290}
2291
2292fn is_supported_image_mime_type(input: &str) -> bool {
2294 let mime_type = input.trim();
2296
2297 let mime_type_essence = match mime_type.find(';') {
2299 Some(semi) => &mime_type[..semi],
2300 _ => mime_type,
2301 };
2302
2303 if mime_type_essence.is_empty() {
2308 return true;
2309 }
2310
2311 SUPPORTED_IMAGE_MIME_TYPES.contains(&mime_type_essence)
2312}