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(None), 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(None), 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(None), 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 pub(crate) fn full_image_url_for_user_interface(&self) -> Option<ServoUrl> {
1446 self.owner_document()
1447 .base_url()
1448 .join(&self.CurrentSrc())
1449 .ok()
1450 }
1451}
1452
1453#[derive(JSTraceable, MallocSizeOf)]
1454pub(crate) enum ImageElementMicrotask {
1455 StableStateUpdateImageData {
1456 elem: DomRoot<HTMLImageElement>,
1457 generation: u32,
1458 },
1459 EnvironmentChanges {
1460 elem: DomRoot<HTMLImageElement>,
1461 generation: u32,
1462 },
1463 Decode {
1464 elem: DomRoot<HTMLImageElement>,
1465 #[conditional_malloc_size_of]
1466 promise: Rc<Promise>,
1467 },
1468}
1469
1470impl MicrotaskRunnable for ImageElementMicrotask {
1471 fn handler(&self, can_gc: CanGc) {
1472 match *self {
1473 ImageElementMicrotask::StableStateUpdateImageData {
1474 ref elem,
1475 ref generation,
1476 } => {
1477 if elem.generation.get() == *generation {
1480 elem.update_the_image_data_sync_steps(can_gc);
1481 }
1482 },
1483 ImageElementMicrotask::EnvironmentChanges {
1484 ref elem,
1485 ref generation,
1486 } => {
1487 elem.react_to_environment_changes_sync_steps(*generation, can_gc);
1488 },
1489 ImageElementMicrotask::Decode {
1490 ref elem,
1491 ref promise,
1492 } => {
1493 elem.react_to_decode_image_sync_steps(promise.clone(), can_gc);
1494 },
1495 }
1496 }
1497
1498 fn enter_realm(&self) -> JSAutoRealm {
1499 match self {
1500 &ImageElementMicrotask::StableStateUpdateImageData { ref elem, .. } |
1501 &ImageElementMicrotask::EnvironmentChanges { ref elem, .. } |
1502 &ImageElementMicrotask::Decode { ref elem, .. } => enter_realm(&**elem),
1503 }
1504 }
1505}
1506
1507pub(crate) trait LayoutHTMLImageElementHelpers {
1508 fn image_url(self) -> Option<ServoUrl>;
1509 fn image_density(self) -> Option<f64>;
1510 fn image_data(self) -> (Option<Image>, Option<ImageMetadata>);
1511 fn get_width(self) -> LengthOrPercentageOrAuto;
1512 fn get_height(self) -> LengthOrPercentageOrAuto;
1513 fn showing_broken_image_icon(self) -> bool;
1514}
1515
1516impl<'dom> LayoutDom<'dom, HTMLImageElement> {
1517 #[expect(unsafe_code)]
1518 fn current_request(self) -> &'dom ImageRequest {
1519 unsafe { self.unsafe_get().current_request.borrow_for_layout() }
1520 }
1521
1522 #[expect(unsafe_code)]
1523 fn dimension_attribute_source(self) -> LayoutDom<'dom, Element> {
1524 unsafe {
1525 self.unsafe_get()
1526 .dimension_attribute_source
1527 .get_inner_as_layout()
1528 .expect("dimension attribute source should be always non-null")
1529 }
1530 }
1531}
1532
1533impl LayoutHTMLImageElementHelpers for LayoutDom<'_, HTMLImageElement> {
1534 fn image_url(self) -> Option<ServoUrl> {
1535 self.current_request().parsed_url.clone()
1536 }
1537
1538 fn image_data(self) -> (Option<Image>, Option<ImageMetadata>) {
1539 let current_request = self.current_request();
1540 (current_request.image.clone(), current_request.metadata)
1541 }
1542
1543 fn image_density(self) -> Option<f64> {
1544 self.current_request().current_pixel_density
1545 }
1546
1547 fn showing_broken_image_icon(self) -> bool {
1548 matches!(self.current_request().state, State::Broken)
1549 }
1550
1551 fn get_width(self) -> LengthOrPercentageOrAuto {
1552 self.dimension_attribute_source()
1553 .get_attr_for_layout(&ns!(), &local_name!("width"))
1554 .map(AttrValue::as_dimension)
1555 .cloned()
1556 .unwrap_or(LengthOrPercentageOrAuto::Auto)
1557 }
1558
1559 fn get_height(self) -> LengthOrPercentageOrAuto {
1560 self.dimension_attribute_source()
1561 .get_attr_for_layout(&ns!(), &local_name!("height"))
1562 .map(AttrValue::as_dimension)
1563 .cloned()
1564 .unwrap_or(LengthOrPercentageOrAuto::Auto)
1565 }
1566}
1567
1568fn parse_a_sizes_attribute(value: &str) -> SourceSizeList {
1570 let mut input = ParserInput::new(value);
1571 let mut parser = Parser::new(&mut input);
1572 let url_data = Url::parse("about:blank").unwrap().into();
1573 let context = ParserContext::new(
1574 Origin::Author,
1575 &url_data,
1576 Some(CssRuleType::Style),
1577 ParsingMode::empty(),
1580 QuirksMode::NoQuirks,
1581 Default::default(),
1582 None,
1583 None,
1584 );
1585 SourceSizeList::parse(&context, &mut parser)
1586}
1587
1588#[allow(non_snake_case)]
1589impl HTMLImageElementMethods<crate::DomTypeHolder> for HTMLImageElement {
1590 fn Image(
1592 window: &Window,
1593 proto: Option<HandleObject>,
1594 can_gc: CanGc,
1595 width: Option<u32>,
1596 height: Option<u32>,
1597 ) -> Fallible<DomRoot<HTMLImageElement>> {
1598 let document = window.Document();
1600
1601 let element = Element::create(
1604 QualName::new(None, ns!(html), local_name!("img")),
1605 None,
1606 &document,
1607 ElementCreator::ScriptCreated,
1608 CustomElementCreationMode::Synchronous,
1609 proto,
1610 can_gc,
1611 );
1612
1613 let image = DomRoot::downcast::<HTMLImageElement>(element).unwrap();
1614
1615 if let Some(w) = width {
1617 image.SetWidth(w);
1618 }
1619
1620 if let Some(h) = height {
1623 image.SetHeight(h);
1624 }
1625
1626 Ok(image)
1628 }
1629
1630 make_getter!(Alt, "alt");
1632 make_setter!(SetAlt, "alt");
1634
1635 make_url_getter!(Src, "src");
1637
1638 make_url_setter!(SetSrc, "src");
1640
1641 make_url_getter!(Srcset, "srcset");
1643 make_url_setter!(SetSrcset, "srcset");
1645
1646 make_getter!(Sizes, "sizes");
1648
1649 make_setter!(SetSizes, "sizes");
1651
1652 fn GetCrossOrigin(&self) -> Option<DOMString> {
1654 reflect_cross_origin_attribute(self.upcast::<Element>())
1655 }
1656
1657 fn SetCrossOrigin(&self, value: Option<DOMString>, can_gc: CanGc) {
1659 set_cross_origin_attribute(self.upcast::<Element>(), value, can_gc);
1660 }
1661
1662 make_getter!(UseMap, "usemap");
1664 make_setter!(SetUseMap, "usemap");
1666
1667 make_bool_getter!(IsMap, "ismap");
1669 make_bool_setter!(SetIsMap, "ismap");
1671
1672 fn Width(&self) -> u32 {
1674 let node = self.upcast::<Node>();
1675 node.content_box()
1676 .map(|rect| rect.size.width.to_px() as u32)
1677 .unwrap_or_else(|| self.NaturalWidth())
1678 }
1679
1680 make_dimension_uint_setter!(SetWidth, "width");
1682
1683 fn Height(&self) -> u32 {
1685 let node = self.upcast::<Node>();
1686 node.content_box()
1687 .map(|rect| rect.size.height.to_px() as u32)
1688 .unwrap_or_else(|| self.NaturalHeight())
1689 }
1690
1691 make_dimension_uint_setter!(SetHeight, "height");
1693
1694 fn NaturalWidth(&self) -> u32 {
1696 let request = self.current_request.borrow();
1697 if matches!(request.state, State::Broken) {
1698 return 0;
1699 }
1700
1701 let pixel_density = request.current_pixel_density.unwrap_or(1f64);
1702 match request.metadata {
1703 Some(ref metadata) => (metadata.width as f64 / pixel_density) as u32,
1704 None => 0,
1705 }
1706 }
1707
1708 fn NaturalHeight(&self) -> u32 {
1710 let request = self.current_request.borrow();
1711 if matches!(request.state, State::Broken) {
1712 return 0;
1713 }
1714
1715 let pixel_density = request.current_pixel_density.unwrap_or(1f64);
1716 match request.metadata {
1717 Some(ref metadata) => (metadata.height as f64 / pixel_density) as u32,
1718 None => 0,
1719 }
1720 }
1721
1722 fn Complete(&self) -> bool {
1724 let elem = self.upcast::<Element>();
1725 let srcset_absent = !elem.has_attribute(&local_name!("srcset"));
1726 if !elem.has_attribute(&local_name!("src")) && srcset_absent {
1727 return true;
1728 }
1729 let src = elem.get_string_attribute(&local_name!("src"));
1730 if srcset_absent && src.is_empty() {
1731 return true;
1732 }
1733 let request = self.current_request.borrow();
1734 let request_state = request.state;
1735 match request_state {
1736 State::CompletelyAvailable | State::Broken => true,
1737 State::PartiallyAvailable | State::Unavailable => false,
1738 }
1739 }
1740
1741 fn CurrentSrc(&self) -> USVString {
1743 let current_request = self.current_request.borrow();
1744 let url = ¤t_request.parsed_url;
1745 match *url {
1746 Some(ref url) => USVString(url.clone().into_string()),
1747 None => {
1748 let unparsed_url = ¤t_request.source_url;
1749 match *unparsed_url {
1750 Some(ref url) => url.clone(),
1751 None => USVString("".to_owned()),
1752 }
1753 },
1754 }
1755 }
1756
1757 fn ReferrerPolicy(&self) -> DOMString {
1759 reflect_referrer_policy_attribute(self.upcast::<Element>())
1760 }
1761
1762 make_setter!(SetReferrerPolicy, "referrerpolicy");
1764
1765 fn Decode(&self, can_gc: CanGc) -> Rc<Promise> {
1767 let promise = Promise::new(&self.global(), can_gc);
1769
1770 let task = ImageElementMicrotask::Decode {
1772 elem: DomRoot::from_ref(self),
1773 promise: promise.clone(),
1774 };
1775 ScriptThread::await_stable_state(Microtask::ImageElement(task));
1776
1777 promise
1779 }
1780
1781 make_getter!(Name, "name");
1783
1784 make_atomic_setter!(SetName, "name");
1786
1787 make_getter!(Align, "align");
1789
1790 make_setter!(SetAlign, "align");
1792
1793 make_uint_getter!(Hspace, "hspace");
1795
1796 make_uint_setter!(SetHspace, "hspace");
1798
1799 make_uint_getter!(Vspace, "vspace");
1801
1802 make_uint_setter!(SetVspace, "vspace");
1804
1805 make_getter!(LongDesc, "longdesc");
1807
1808 make_setter!(SetLongDesc, "longdesc");
1810
1811 make_getter!(Border, "border");
1813
1814 make_setter!(SetBorder, "border");
1816}
1817
1818impl VirtualMethods for HTMLImageElement {
1819 fn super_type(&self) -> Option<&dyn VirtualMethods> {
1820 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
1821 }
1822
1823 fn adopting_steps(&self, old_doc: &Document, can_gc: CanGc) {
1824 self.super_type().unwrap().adopting_steps(old_doc, can_gc);
1825 self.update_the_image_data(can_gc);
1826 }
1827
1828 fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
1829 self.super_type()
1830 .unwrap()
1831 .attribute_mutated(attr, mutation, can_gc);
1832 match attr.local_name() {
1833 &local_name!("src") |
1834 &local_name!("srcset") |
1835 &local_name!("width") |
1836 &local_name!("sizes") => {
1837 self.update_the_image_data(can_gc);
1841 },
1842 &local_name!("crossorigin") => {
1843 let cross_origin_state_changed = match mutation {
1846 AttributeMutation::Removed | AttributeMutation::Set(None, _) => true,
1847 AttributeMutation::Set(Some(old_value), _) => {
1848 let new_cors_setting =
1849 CorsSettings::from_enumerated_attribute(&attr.value());
1850 let old_cors_setting = CorsSettings::from_enumerated_attribute(old_value);
1851
1852 new_cors_setting != old_cors_setting
1853 },
1854 };
1855
1856 if cross_origin_state_changed {
1857 self.update_the_image_data(can_gc);
1858 }
1859 },
1860 &local_name!("referrerpolicy") => {
1861 let referrer_policy_state_changed = match mutation {
1864 AttributeMutation::Removed | AttributeMutation::Set(None, _) => {
1865 ReferrerPolicy::from(&**attr.value()) != ReferrerPolicy::EmptyString
1866 },
1867 AttributeMutation::Set(Some(old_value), _) => {
1868 ReferrerPolicy::from(&**attr.value()) != ReferrerPolicy::from(&**old_value)
1869 },
1870 };
1871
1872 if referrer_policy_state_changed {
1873 self.update_the_image_data(can_gc);
1874 }
1875 },
1876 _ => {},
1877 }
1878 }
1879
1880 fn attribute_affects_presentational_hints(&self, attr: &Attr) -> bool {
1881 match attr.local_name() {
1882 &local_name!("width") | &local_name!("height") => true,
1883 _ => self
1884 .super_type()
1885 .unwrap()
1886 .attribute_affects_presentational_hints(attr),
1887 }
1888 }
1889
1890 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
1891 match name {
1892 &local_name!("width") | &local_name!("height") => {
1893 AttrValue::from_dimension(value.into())
1894 },
1895 &local_name!("hspace") | &local_name!("vspace") => AttrValue::from_u32(value.into(), 0),
1896 _ => self
1897 .super_type()
1898 .unwrap()
1899 .parse_plain_attribute(name, value),
1900 }
1901 }
1902
1903 fn handle_event(&self, event: &Event, can_gc: CanGc) {
1904 if event.type_() != atom!("click") {
1905 return;
1906 }
1907
1908 let area_elements = self.areas();
1909 let elements = match area_elements {
1910 Some(x) => x,
1911 None => return,
1912 };
1913
1914 let mouse_event = match event.downcast::<MouseEvent>() {
1916 Some(x) => x,
1917 None => return,
1918 };
1919
1920 let point = Point2D::new(
1921 mouse_event.ClientX().to_f32().unwrap(),
1922 mouse_event.ClientY().to_f32().unwrap(),
1923 );
1924 let bcr = self.upcast::<Element>().GetBoundingClientRect(can_gc);
1925 let bcr_p = Point2D::new(bcr.X() as f32, bcr.Y() as f32);
1926
1927 for element in elements {
1929 let shape = element.get_shape_from_coords();
1930 let shp = match shape {
1931 Some(x) => x.absolute_coords(bcr_p),
1932 None => return,
1933 };
1934 if shp.hit_test(&point) {
1935 element.activation_behavior(event, self.upcast(), can_gc);
1936 return;
1937 }
1938 }
1939 }
1940
1941 fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) {
1943 if let Some(s) = self.super_type() {
1944 s.bind_to_tree(context, can_gc);
1945 }
1946 let document = self.owner_document();
1947 if context.tree_connected {
1948 document.register_responsive_image(self);
1949 }
1950
1951 let parent = self.upcast::<Node>().GetParentNode().unwrap();
1952
1953 if parent.is::<HTMLPictureElement>() && std::ptr::eq(&*parent, context.parent) {
1956 self.update_the_image_data(can_gc);
1957 }
1958 }
1959
1960 fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) {
1962 self.super_type().unwrap().unbind_from_tree(context, can_gc);
1963 let document = self.owner_document();
1964 document.unregister_responsive_image(self);
1965
1966 if context.parent.is::<HTMLPictureElement>() && !self.upcast::<Node>().has_parent() {
1969 self.update_the_image_data(can_gc);
1970 }
1971 }
1972}
1973
1974impl FormControl for HTMLImageElement {
1975 fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
1976 self.form_owner.get()
1977 }
1978
1979 fn set_form_owner(&self, form: Option<&HTMLFormElement>) {
1980 self.form_owner.set(form);
1981 }
1982
1983 fn to_element(&self) -> &Element {
1984 self.upcast::<Element>()
1985 }
1986
1987 fn is_listed(&self) -> bool {
1988 false
1989 }
1990}
1991
1992pub(crate) fn collect_sequence_characters(
1995 s: &str,
1996 mut predicate: impl FnMut(&char) -> bool,
1997) -> (&str, &str) {
1998 let i = s.find(|ch| !predicate(&ch)).unwrap_or(s.len());
1999 (&s[0..i], &s[i..])
2000}
2001
2002fn is_valid_non_negative_integer_string(s: &str) -> bool {
2005 s.chars().all(|c| c.is_ascii_digit())
2006}
2007
2008fn is_valid_floating_point_number_string(s: &str) -> bool {
2011 static RE: LazyLock<Regex> =
2012 LazyLock::new(|| Regex::new(r"^-?(?:\d+\.\d+|\d+|\.\d+)(?:(e|E)(\+|\-)?\d+)?$").unwrap());
2013
2014 RE.is_match(s)
2015}
2016
2017pub fn parse_a_srcset_attribute(input: &str) -> Vec<ImageSource> {
2020 let mut current_index = 0;
2023
2024 let mut candidates = vec![];
2026 while current_index < input.len() {
2027 let remaining_string = &input[current_index..];
2028
2029 let (collected_characters, string_after_whitespace) =
2036 collect_sequence_characters(remaining_string, |character| {
2037 *character == ',' || character.is_ascii_whitespace()
2038 });
2039
2040 current_index += collected_characters.len();
2043
2044 if string_after_whitespace.is_empty() {
2046 return candidates;
2047 }
2048
2049 let (url, _) =
2052 collect_sequence_characters(string_after_whitespace, |c| !char::is_ascii_whitespace(c));
2053
2054 current_index += url.len();
2057
2058 let mut descriptors = Vec::new();
2060
2061 if url.ends_with(',') {
2065 let image_source = ImageSource {
2066 url: url.trim_end_matches(',').into(),
2067 descriptor: Descriptor {
2068 width: None,
2069 density: None,
2070 },
2071 };
2072 candidates.push(image_source);
2073 continue;
2074 }
2075
2076 let descriptors_string = &input[current_index..];
2079 let (spaces, descriptors_string) =
2080 collect_sequence_characters(descriptors_string, |character| {
2081 character.is_ascii_whitespace()
2082 });
2083 current_index += spaces.len();
2084
2085 let mut current_descriptor = String::new();
2087
2088 let mut state = ParseState::InDescriptor;
2090
2091 let mut characters = descriptors_string.chars();
2095 let mut character = characters.next();
2096 if let Some(character) = character {
2097 current_index += character.len_utf8();
2098 }
2099
2100 loop {
2101 match (state, character) {
2102 (ParseState::InDescriptor, Some(character)) if character.is_ascii_whitespace() => {
2103 if !current_descriptor.is_empty() {
2107 descriptors.push(current_descriptor);
2108 current_descriptor = String::new();
2109 state = ParseState::AfterDescriptor;
2110 }
2111 },
2112 (ParseState::InDescriptor, Some(',')) => {
2113 if !current_descriptor.is_empty() {
2117 descriptors.push(current_descriptor);
2118 }
2119 break;
2120 },
2121 (ParseState::InDescriptor, Some('(')) => {
2122 current_descriptor.push('(');
2124 state = ParseState::InParens;
2125 },
2126 (ParseState::InDescriptor, Some(character)) => {
2127 current_descriptor.push(character);
2129 },
2130 (ParseState::InDescriptor, None) => {
2131 if !current_descriptor.is_empty() {
2134 descriptors.push(current_descriptor);
2135 }
2136 break;
2137 },
2138 (ParseState::InParens, Some(')')) => {
2139 current_descriptor.push(')');
2141 state = ParseState::InDescriptor;
2142 },
2143 (ParseState::InParens, Some(character)) => {
2144 current_descriptor.push(character);
2146 },
2147 (ParseState::InParens, None) => {
2148 descriptors.push(current_descriptor);
2151 break;
2152 },
2153 (ParseState::AfterDescriptor, Some(character))
2154 if character.is_ascii_whitespace() =>
2155 {
2156 },
2158 (ParseState::AfterDescriptor, Some(_)) => {
2159 state = ParseState::InDescriptor;
2162 continue;
2163 },
2164 (ParseState::AfterDescriptor, None) => {
2165 break;
2167 },
2168 }
2169
2170 character = characters.next();
2171 if let Some(character) = character {
2172 current_index += character.len_utf8();
2173 }
2174 }
2175
2176 let mut error = false;
2178 let mut width: Option<u32> = None;
2180 let mut density: Option<f64> = None;
2182 let mut future_compat_h: Option<u32> = None;
2184
2185 for descriptor in descriptors.into_iter() {
2188 let Some(last_character) = descriptor.chars().last() else {
2189 break;
2190 };
2191
2192 let first_part_of_string = &descriptor[0..descriptor.len() - last_character.len_utf8()];
2193 match last_character {
2194 'w' if is_valid_non_negative_integer_string(first_part_of_string) &&
2201 density.is_none() &&
2202 width.is_none() =>
2203 {
2204 match parse_unsigned_integer(first_part_of_string.chars()) {
2205 Ok(number) if number > 0 => {
2206 width = Some(number);
2207 continue;
2208 },
2209 _ => error = true,
2210 }
2211 },
2212
2213 'x' if is_valid_floating_point_number_string(first_part_of_string) &&
2227 width.is_none() &&
2228 density.is_none() &&
2229 future_compat_h.is_none() =>
2230 {
2231 match first_part_of_string.parse::<f64>() {
2232 Ok(number) if number.is_finite() && number >= 0. => {
2233 density = Some(number);
2234 continue;
2235 },
2236 _ => error = true,
2237 }
2238 },
2239
2240 'h' if is_valid_non_negative_integer_string(first_part_of_string) &&
2249 future_compat_h.is_none() &&
2250 density.is_none() =>
2251 {
2252 match parse_unsigned_integer(first_part_of_string.chars()) {
2253 Ok(number) if number > 0 => {
2254 future_compat_h = Some(number);
2255 continue;
2256 },
2257 _ => error = true,
2258 }
2259 },
2260
2261 _ => error = true,
2264 }
2265
2266 if error {
2267 break;
2268 }
2269 }
2270
2271 if future_compat_h.is_some() && width.is_none() {
2273 error = true;
2274 }
2275
2276 if !error {
2280 let image_source = ImageSource {
2281 url: url.into(),
2282 descriptor: Descriptor { width, density },
2283 };
2284 candidates.push(image_source);
2285 }
2286
2287 }
2289 candidates
2290}
2291
2292#[derive(Clone)]
2293enum ChangeType {
2294 Environment {
2295 selected_source: USVString,
2296 selected_pixel_density: f64,
2297 },
2298 Element,
2299}
2300
2301fn is_supported_image_mime_type(input: &str) -> bool {
2303 let mime_type = input.trim();
2305
2306 let mime_type_essence = match mime_type.find(';') {
2308 Some(semi) => &mime_type[..semi],
2309 _ => mime_type,
2310 };
2311
2312 if mime_type_essence.is_empty() {
2317 return true;
2318 }
2319
2320 SUPPORTED_IMAGE_MIME_TYPES.contains(&mime_type_essence)
2321}