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, Size2D};
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, UsePlaceholder,
23};
24use net_traits::request::{CorsSettings, Destination, Initiator, RequestId};
25use net_traits::{
26 FetchMetadata, FetchResponseListener, FetchResponseMsg, NetworkError, ReferrerPolicy,
27 ResourceFetchTiming, ResourceTimingType,
28};
29use num_traits::ToPrimitive;
30use pixels::{
31 CorsStatus, ImageMetadata, PixelFormat, Snapshot, SnapshotAlphaMode, SnapshotPixelFormat,
32};
33use regex::Regex;
34use rustc_hash::FxHashSet;
35use servo_url::ServoUrl;
36use servo_url::origin::MutableOrigin;
37use style::attr::{AttrValue, LengthOrPercentageOrAuto, parse_unsigned_integer};
38use style::context::QuirksMode;
39use style::parser::ParserContext;
40use style::stylesheets::{CssRuleType, Origin};
41use style::values::specified::source_size_list::SourceSizeList;
42use style_traits::ParsingMode;
43use url::Url;
44
45use crate::document_loader::{LoadBlocker, LoadType};
46use crate::dom::activation::Activatable;
47use crate::dom::attr::Attr;
48use crate::dom::bindings::cell::{DomRefCell, RefMut};
49use crate::dom::bindings::codegen::Bindings::DOMRectBinding::DOMRect_Binding::DOMRectMethods;
50use crate::dom::bindings::codegen::Bindings::ElementBinding::Element_Binding::ElementMethods;
51use crate::dom::bindings::codegen::Bindings::HTMLImageElementBinding::HTMLImageElementMethods;
52use crate::dom::bindings::codegen::Bindings::MouseEventBinding::MouseEventMethods;
53use crate::dom::bindings::codegen::Bindings::NodeBinding::Node_Binding::NodeMethods;
54use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
55use crate::dom::bindings::error::{Error, Fallible};
56use crate::dom::bindings::inheritance::Castable;
57use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
58use crate::dom::bindings::reflector::DomGlobal;
59use crate::dom::bindings::root::{DomRoot, LayoutDom, MutNullableDom};
60use crate::dom::bindings::str::{DOMString, USVString};
61use crate::dom::csp::{GlobalCspReporting, Violation};
62use crate::dom::document::Document;
63use crate::dom::element::{
64 AttributeMutation, CustomElementCreationMode, Element, ElementCreator, LayoutElementHelpers,
65 cors_setting_for_element, referrer_policy_for_element, reflect_cross_origin_attribute,
66 reflect_referrer_policy_attribute, set_cross_origin_attribute,
67};
68use crate::dom::event::Event;
69use crate::dom::eventtarget::EventTarget;
70use crate::dom::globalscope::GlobalScope;
71use crate::dom::html::htmlareaelement::HTMLAreaElement;
72use crate::dom::html::htmlelement::HTMLElement;
73use crate::dom::html::htmlformelement::{FormControl, HTMLFormElement};
74use crate::dom::html::htmlmapelement::HTMLMapElement;
75use crate::dom::html::htmlpictureelement::HTMLPictureElement;
76use crate::dom::html::htmlsourceelement::HTMLSourceElement;
77use crate::dom::medialist::MediaList;
78use crate::dom::mouseevent::MouseEvent;
79use crate::dom::node::{BindContext, Node, NodeDamage, NodeTraits, ShadowIncluding, UnbindContext};
80use crate::dom::performanceresourcetiming::InitiatorType;
81use crate::dom::promise::Promise;
82use crate::dom::virtualmethods::VirtualMethods;
83use crate::dom::window::Window;
84use crate::fetch::create_a_potential_cors_request;
85use crate::microtask::{Microtask, MicrotaskRunnable};
86use crate::network_listener::{self, PreInvoke, ResourceTimingListener};
87use crate::realms::enter_realm;
88use crate::script_runtime::CanGc;
89use crate::script_thread::ScriptThread;
90
91const SUPPORTED_IMAGE_MIME_TYPES: &[&str] = &[
95 "image/bmp",
96 "image/gif",
97 "image/jpeg",
98 "image/jpg",
99 "image/pjpeg",
100 "image/png",
101 "image/apng",
102 "image/x-png",
103 "image/svg+xml",
104 "image/vnd.microsoft.icon",
105 "image/x-icon",
106 "image/webp",
107];
108
109#[derive(Clone, Copy, Debug)]
110enum ParseState {
111 InDescriptor,
112 InParens,
113 AfterDescriptor,
114}
115
116#[derive(MallocSizeOf)]
118pub(crate) struct SourceSet {
119 image_sources: Vec<ImageSource>,
120 source_size: SourceSizeList,
121}
122
123impl SourceSet {
124 fn new() -> SourceSet {
125 SourceSet {
126 image_sources: Vec::new(),
127 source_size: SourceSizeList::empty(),
128 }
129 }
130}
131
132#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
133pub struct ImageSource {
134 pub url: String,
135 pub descriptor: Descriptor,
136}
137
138#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
139pub struct Descriptor {
140 pub width: Option<u32>,
141 pub density: Option<f64>,
142}
143
144#[derive(Clone, Copy, JSTraceable, MallocSizeOf)]
145#[allow(dead_code)]
146enum State {
147 Unavailable,
148 PartiallyAvailable,
149 CompletelyAvailable,
150 Broken,
151}
152
153#[derive(Clone, Copy, JSTraceable, MallocSizeOf)]
154enum ImageRequestPhase {
155 Pending,
156 Current,
157}
158#[derive(JSTraceable, MallocSizeOf)]
159#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
160struct ImageRequest {
161 state: State,
162 #[no_trace]
163 parsed_url: Option<ServoUrl>,
164 source_url: Option<USVString>,
165 blocker: DomRefCell<Option<LoadBlocker>>,
166 #[no_trace]
167 image: Option<Image>,
168 #[no_trace]
169 metadata: Option<ImageMetadata>,
170 #[no_trace]
171 final_url: Option<ServoUrl>,
172 current_pixel_density: Option<f64>,
173}
174#[dom_struct]
175pub(crate) struct HTMLImageElement {
176 htmlelement: HTMLElement,
177 image_request: Cell<ImageRequestPhase>,
178 current_request: DomRefCell<ImageRequest>,
179 pending_request: DomRefCell<ImageRequest>,
180 form_owner: MutNullableDom<HTMLFormElement>,
181 generation: Cell<u32>,
182 source_set: DomRefCell<SourceSet>,
183 dimension_attribute_source: MutNullableDom<Element>,
186 last_selected_source: DomRefCell<Option<USVString>>,
187 #[conditional_malloc_size_of]
188 image_decode_promises: DomRefCell<Vec<Rc<Promise>>>,
189 line_number: u64,
191}
192
193impl HTMLImageElement {
194 pub(crate) fn is_usable(&self) -> Fallible<bool> {
196 if let Some(image) = &self.current_request.borrow().image {
198 let intrinsic_size = image.metadata();
199 if intrinsic_size.width == 0 || intrinsic_size.height == 0 {
200 return Ok(false);
201 }
202 }
203
204 match self.current_request.borrow().state {
205 State::Broken => Err(Error::InvalidState(None)),
207 State::CompletelyAvailable => Ok(true),
208 State::PartiallyAvailable | State::Unavailable => Ok(false),
210 }
211 }
212
213 pub(crate) fn image_data(&self) -> Option<Image> {
214 self.current_request.borrow().image.clone()
215 }
216
217 pub(crate) fn get_raster_image_data(&self) -> Option<Snapshot> {
219 let Some(img) = self.image_data()?.as_raster_image() else {
220 warn!("Vector image is not supported as raster image source");
221 return None;
222 };
223
224 let size = Size2D::new(img.metadata.width, img.metadata.height);
225 let format = match img.format {
226 PixelFormat::BGRA8 => SnapshotPixelFormat::BGRA,
227 PixelFormat::RGBA8 => SnapshotPixelFormat::RGBA,
228 pixel_format => {
229 unimplemented!("unsupported pixel format ({:?})", pixel_format)
230 },
231 };
232
233 let alpha_mode = SnapshotAlphaMode::Transparent {
234 premultiplied: false,
235 };
236
237 let snapshot = Snapshot::from_vec(
238 size.cast(),
239 format,
240 alpha_mode,
241 img.first_frame().bytes.to_vec(),
242 );
243
244 Some(snapshot)
245 }
246}
247
248struct ImageContext {
250 image_cache: Arc<dyn ImageCache>,
252 status: Result<(), NetworkError>,
254 id: PendingImageId,
256 aborted: bool,
258 doc: Trusted<Document>,
260 resource_timing: ResourceFetchTiming,
262 url: ServoUrl,
263 element: Trusted<HTMLImageElement>,
264}
265
266impl FetchResponseListener for ImageContext {
267 fn process_request_body(&mut self, _: RequestId) {}
268 fn process_request_eof(&mut self, _: RequestId) {}
269
270 fn process_response(
271 &mut self,
272 request_id: RequestId,
273 metadata: Result<FetchMetadata, NetworkError>,
274 ) {
275 debug!("got {:?} for {:?}", metadata.as_ref().map(|_| ()), self.url);
276 self.image_cache.notify_pending_response(
277 self.id,
278 FetchResponseMsg::ProcessResponse(request_id, metadata.clone()),
279 );
280
281 let metadata = metadata.ok().map(|meta| match meta {
282 FetchMetadata::Unfiltered(m) => m,
283 FetchMetadata::Filtered { unsafe_, .. } => unsafe_,
284 });
285
286 if let Some(metadata) = metadata.as_ref() {
288 if let Some(ref content_type) = metadata.content_type {
289 let mime: Mime = content_type.clone().into_inner().into();
290 if mime.type_() == mime::MULTIPART && mime.subtype().as_str() == "x-mixed-replace" {
291 self.aborted = true;
292 }
293 }
294 }
295
296 let status = metadata
297 .as_ref()
298 .map(|m| m.status.clone())
299 .unwrap_or_else(HttpStatus::new_error);
300
301 self.status = {
302 if status.is_error() {
303 Err(NetworkError::Internal(
304 "No http status code received".to_owned(),
305 ))
306 } else if status.is_success() {
307 Ok(())
308 } else {
309 Err(NetworkError::Internal(format!(
310 "HTTP error code {}",
311 status.code()
312 )))
313 }
314 };
315 }
316
317 fn process_response_chunk(&mut self, request_id: RequestId, payload: Vec<u8>) {
318 if self.status.is_ok() {
319 self.image_cache.notify_pending_response(
320 self.id,
321 FetchResponseMsg::ProcessResponseChunk(request_id, payload),
322 );
323 }
324 }
325
326 fn process_response_eof(
327 &mut self,
328 request_id: RequestId,
329 response: Result<ResourceFetchTiming, NetworkError>,
330 ) {
331 self.image_cache.notify_pending_response(
332 self.id,
333 FetchResponseMsg::ProcessResponseEOF(request_id, response),
334 );
335 }
336
337 fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming {
338 &mut self.resource_timing
339 }
340
341 fn resource_timing(&self) -> &ResourceFetchTiming {
342 &self.resource_timing
343 }
344
345 fn submit_resource_timing(&mut self) {
346 network_listener::submit_timing(self, CanGc::note())
347 }
348
349 fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<Violation>) {
350 let global = &self.resource_timing_global();
351 let elem = self.element.root();
352 let source_position = elem
353 .upcast::<Element>()
354 .compute_source_position(elem.line_number as u32);
355 global.report_csp_violations(violations, None, Some(source_position));
356 }
357}
358
359impl ResourceTimingListener for ImageContext {
360 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
361 (
362 InitiatorType::LocalName("img".to_string()),
363 self.url.clone(),
364 )
365 }
366
367 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
368 self.doc.root().global()
369 }
370}
371
372impl PreInvoke for ImageContext {
373 fn should_invoke(&self) -> bool {
374 !self.aborted
375 }
376}
377
378#[allow(non_snake_case)]
379impl HTMLImageElement {
380 fn fetch_image(&self, img_url: &ServoUrl, can_gc: CanGc) {
382 let window = self.owner_window();
383
384 let cache_result = window.image_cache().get_cached_image_status(
385 img_url.clone(),
386 window.origin().immutable().clone(),
387 cors_setting_for_element(self.upcast()),
388 UsePlaceholder::Yes,
389 );
390
391 match cache_result {
392 ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable {
393 image,
394 url,
395 is_placeholder,
396 }) => {
397 if is_placeholder {
398 if let Some(raster_image) = image.as_raster_image() {
399 self.process_image_response(
400 ImageResponse::PlaceholderLoaded(raster_image, url),
401 can_gc,
402 )
403 }
404 } else {
405 self.process_image_response(ImageResponse::Loaded(image, url), can_gc)
406 }
407 },
408 ImageCacheResult::Available(ImageOrMetadataAvailable::MetadataAvailable(
409 metadata,
410 id,
411 )) => {
412 self.process_image_response(ImageResponse::MetadataLoaded(metadata), can_gc);
413 self.register_image_cache_callback(id, ChangeType::Element);
414 },
415 ImageCacheResult::Pending(id) => {
416 self.register_image_cache_callback(id, ChangeType::Element);
417 },
418 ImageCacheResult::ReadyForRequest(id) => {
419 self.fetch_request(img_url, id);
420 self.register_image_cache_callback(id, ChangeType::Element);
421 },
422 ImageCacheResult::LoadError => self.process_image_response(ImageResponse::None, can_gc),
423 };
424 }
425
426 fn register_image_cache_callback(&self, id: PendingImageId, change_type: ChangeType) {
427 let trusted_node = Trusted::new(self);
428 let generation = self.generation_id();
429 let window = self.owner_window();
430 let sender = window.register_image_cache_listener(id, move |response| {
431 let trusted_node = trusted_node.clone();
432 let window = trusted_node.root().owner_window();
433 let callback_type = change_type.clone();
434
435 window
436 .as_global_scope()
437 .task_manager()
438 .networking_task_source()
439 .queue(task!(process_image_response: move || {
440 let element = trusted_node.root();
441
442 if generation != element.generation_id() {
444 return;
445 }
446
447 match callback_type {
448 ChangeType::Element => {
449 element.process_image_response(response.response, CanGc::note());
450 }
451 ChangeType::Environment { selected_source, selected_pixel_density } => {
452 element.process_image_response_for_environment_change(
453 response.response, selected_source, generation, selected_pixel_density, CanGc::note()
454 );
455 }
456 }
457 }));
458 });
459
460 window
461 .image_cache()
462 .add_listener(ImageLoadListener::new(sender, window.pipeline_id(), id));
463 }
464
465 fn fetch_request(&self, img_url: &ServoUrl, id: PendingImageId) {
466 let document = self.owner_document();
467 let window = self.owner_window();
468
469 let context = ImageContext {
470 image_cache: window.image_cache(),
471 status: Ok(()),
472 id,
473 aborted: false,
474 doc: Trusted::new(&document),
475 element: Trusted::new(self),
476 resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource),
477 url: img_url.clone(),
478 };
479
480 let global = document.global();
483 let mut request = create_a_potential_cors_request(
484 Some(window.webview_id()),
485 img_url.clone(),
486 Destination::Image,
487 cors_setting_for_element(self.upcast()),
488 None,
489 global.get_referrer(),
490 document.insecure_requests_policy(),
491 document.has_trustworthy_ancestor_or_current_origin(),
492 global.policy_container(),
493 )
494 .origin(document.origin().immutable().clone())
495 .pipeline_id(Some(document.global().pipeline_id()))
496 .referrer_policy(referrer_policy_for_element(self.upcast()));
497
498 if Self::uses_srcset_or_picture(self.upcast()) {
499 request = request.initiator(Initiator::ImageSet);
500 }
501
502 document.fetch_background(request, context);
505 }
506
507 fn handle_loaded_image(&self, image: Image, url: ServoUrl, can_gc: CanGc) {
509 self.current_request.borrow_mut().metadata = Some(image.metadata());
510 self.current_request.borrow_mut().final_url = Some(url);
511 self.current_request.borrow_mut().image = Some(image);
512 self.current_request.borrow_mut().state = State::CompletelyAvailable;
513 LoadBlocker::terminate(&self.current_request.borrow().blocker, can_gc);
514 self.upcast::<Node>().dirty(NodeDamage::Other);
516 self.resolve_image_decode_promises();
517 }
518
519 fn process_image_response(&self, image: ImageResponse, can_gc: CanGc) {
521 let (trigger_image_load, trigger_image_error) = match (image, self.image_request.get()) {
523 (ImageResponse::Loaded(image, url), ImageRequestPhase::Current) => {
524 self.handle_loaded_image(image, url, can_gc);
525 (true, false)
526 },
527 (ImageResponse::PlaceholderLoaded(image, url), ImageRequestPhase::Current) => {
528 self.handle_loaded_image(Image::Raster(image), url, can_gc);
529 (false, true)
530 },
531 (ImageResponse::Loaded(image, url), ImageRequestPhase::Pending) => {
532 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, can_gc);
533 self.image_request.set(ImageRequestPhase::Current);
534 self.handle_loaded_image(image, url, can_gc);
535 (true, false)
536 },
537 (ImageResponse::PlaceholderLoaded(image, url), ImageRequestPhase::Pending) => {
538 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, can_gc);
539 self.image_request.set(ImageRequestPhase::Current);
540 self.handle_loaded_image(Image::Raster(image), url, can_gc);
541 (false, true)
542 },
543 (ImageResponse::MetadataLoaded(meta), ImageRequestPhase::Current) => {
544 self.current_request.borrow_mut().state = State::PartiallyAvailable;
545 self.current_request.borrow_mut().metadata = Some(meta);
546 (false, false)
547 },
548 (ImageResponse::MetadataLoaded(_), ImageRequestPhase::Pending) => {
549 self.pending_request.borrow_mut().state = State::PartiallyAvailable;
550 (false, false)
551 },
552 (ImageResponse::None, ImageRequestPhase::Current) => {
553 self.abort_request(State::Broken, ImageRequestPhase::Current, can_gc);
554 (false, true)
555 },
556 (ImageResponse::None, ImageRequestPhase::Pending) => {
557 self.abort_request(State::Broken, ImageRequestPhase::Current, can_gc);
558 self.abort_request(State::Broken, ImageRequestPhase::Pending, can_gc);
559 self.image_request.set(ImageRequestPhase::Current);
560 (false, true)
561 },
562 };
563
564 if trigger_image_load {
566 self.upcast::<EventTarget>()
568 .fire_event(atom!("load"), can_gc);
569 self.upcast::<EventTarget>()
570 .fire_event(atom!("loadend"), can_gc);
571 }
572
573 if trigger_image_error {
575 self.upcast::<EventTarget>()
576 .fire_event(atom!("error"), can_gc);
577 self.upcast::<EventTarget>()
578 .fire_event(atom!("loadend"), can_gc);
579 }
580 }
581
582 fn process_image_response_for_environment_change(
583 &self,
584 image: ImageResponse,
585 src: USVString,
586 generation: u32,
587 selected_pixel_density: f64,
588 can_gc: CanGc,
589 ) {
590 match image {
591 ImageResponse::Loaded(image, url) => {
592 self.pending_request.borrow_mut().metadata = Some(image.metadata());
593 self.pending_request.borrow_mut().final_url = Some(url);
594 self.pending_request.borrow_mut().image = Some(image);
595 self.finish_reacting_to_environment_change(src, generation, selected_pixel_density);
596 },
597 ImageResponse::PlaceholderLoaded(image, url) => {
598 let image = Image::Raster(image);
599 self.pending_request.borrow_mut().metadata = Some(image.metadata());
600 self.pending_request.borrow_mut().final_url = Some(url);
601 self.pending_request.borrow_mut().image = Some(image);
602 self.finish_reacting_to_environment_change(src, generation, selected_pixel_density);
603 },
604 ImageResponse::MetadataLoaded(meta) => {
605 self.pending_request.borrow_mut().metadata = Some(meta);
606 },
607 ImageResponse::None => {
608 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, can_gc);
609 },
610 };
611 }
612
613 fn abort_request(&self, state: State, request: ImageRequestPhase, can_gc: CanGc) {
615 let mut request = match request {
616 ImageRequestPhase::Current => self.current_request.borrow_mut(),
617 ImageRequestPhase::Pending => self.pending_request.borrow_mut(),
618 };
619 LoadBlocker::terminate(&request.blocker, can_gc);
620 request.state = state;
621 request.image = None;
622 request.metadata = None;
623
624 if matches!(state, State::Broken) {
625 self.reject_image_decode_promises();
626 } else if matches!(state, State::CompletelyAvailable) {
627 self.resolve_image_decode_promises();
628 }
629 }
630
631 fn create_source_set(&self) -> SourceSet {
633 let element = self.upcast::<Element>();
634
635 let mut source_set = SourceSet::new();
637
638 if let Some(srcset) = element.get_attribute(&ns!(), &local_name!("srcset")) {
641 source_set.image_sources = parse_a_srcset_attribute(&srcset.value());
642 }
643
644 if let Some(sizes) = element.get_attribute(&ns!(), &local_name!("sizes")) {
646 source_set.source_size = parse_a_sizes_attribute(&sizes.value());
647 }
648
649 let src_attribute = element.get_string_attribute(&local_name!("src"));
653 let is_src_empty = src_attribute.is_empty();
654 let no_density_source_of_1 = source_set
655 .image_sources
656 .iter()
657 .all(|source| source.descriptor.density != Some(1.));
658 let no_width_descriptor = source_set
659 .image_sources
660 .iter()
661 .all(|source| source.descriptor.width.is_none());
662 if !is_src_empty && no_density_source_of_1 && no_width_descriptor {
663 source_set.image_sources.push(ImageSource {
664 url: src_attribute.to_string(),
665 descriptor: Descriptor {
666 width: None,
667 density: None,
668 },
669 })
670 }
671
672 self.normalise_source_densities(&mut source_set);
674
675 source_set
677 }
678
679 fn update_source_set(&self) {
681 *self.source_set.borrow_mut() = SourceSet::new();
683
684 let elem = self.upcast::<Element>();
689 let parent = elem.upcast::<Node>().GetParentElement();
690 let elements = match parent.as_ref() {
691 Some(p) => {
692 if p.is::<HTMLPictureElement>() {
693 p.upcast::<Node>()
694 .children()
695 .filter_map(DomRoot::downcast::<Element>)
696 .map(|n| DomRoot::from_ref(&*n))
697 .collect()
698 } else {
699 vec![DomRoot::from_ref(elem)]
700 }
701 },
702 None => vec![DomRoot::from_ref(elem)],
703 };
704
705 for element in &elements {
707 if *element == DomRoot::from_ref(elem) {
709 *self.source_set.borrow_mut() = self.create_source_set();
712
713 return;
715 }
716
717 if !element.is::<HTMLSourceElement>() {
719 continue;
720 }
721
722 let mut source_set = SourceSet::new();
723
724 match element.get_attribute(&ns!(), &local_name!("srcset")) {
728 Some(srcset) => {
729 source_set.image_sources = parse_a_srcset_attribute(&srcset.value());
730 },
731 _ => continue,
732 }
733
734 if source_set.image_sources.is_empty() {
736 continue;
737 }
738
739 if let Some(media) = element.get_attribute(&ns!(), &local_name!("media")) {
742 if !MediaList::matches_environment(&element.owner_document(), &media.value()) {
743 continue;
744 }
745 }
746
747 if let Some(sizes) = element.get_attribute(&ns!(), &local_name!("sizes")) {
750 source_set.source_size = parse_a_sizes_attribute(&sizes.value());
751 }
752
753 if let Some(type_) = element.get_attribute(&ns!(), &local_name!("type")) {
756 if !is_supported_image_mime_type(&type_.value()) {
757 continue;
758 }
759 }
760
761 if element
764 .get_attribute(&ns!(), &local_name!("width"))
765 .is_some() ||
766 element
767 .get_attribute(&ns!(), &local_name!("height"))
768 .is_some()
769 {
770 self.dimension_attribute_source.set(Some(element));
771 } else {
772 self.dimension_attribute_source.set(Some(elem));
773 }
774
775 self.normalise_source_densities(&mut source_set);
777
778 *self.source_set.borrow_mut() = source_set;
780
781 return;
783 }
784 }
785
786 fn evaluate_source_size_list(&self, source_size_list: &SourceSizeList) -> Au {
787 let document = self.owner_document();
788 let quirks_mode = document.quirks_mode();
789 source_size_list.evaluate(document.window().layout().device(), quirks_mode)
790 }
791
792 fn normalise_source_densities(&self, source_set: &mut SourceSet) {
794 let source_size = self.evaluate_source_size_list(&source_set.source_size);
796
797 for image_source in &mut source_set.image_sources {
799 if image_source.descriptor.density.is_some() {
802 continue;
803 }
804
805 if image_source.descriptor.width.is_some() {
809 let width = image_source.descriptor.width.unwrap();
810 image_source.descriptor.density = Some(width as f64 / source_size.to_f64_px());
811 } else {
812 image_source.descriptor.density = Some(1_f64);
814 }
815 }
816 }
817
818 fn select_image_source(&self) -> Option<(USVString, f64)> {
820 self.update_source_set();
822
823 if self.source_set.borrow().image_sources.is_empty() {
826 return None;
827 }
828
829 self.select_image_source_from_source_set()
831 }
832
833 fn select_image_source_from_source_set(&self) -> Option<(USVString, f64)> {
835 let source_set = self.source_set.borrow();
840 let len = source_set.image_sources.len();
841
842 let mut repeat_indices = FxHashSet::default();
844 for outer_index in 0..len {
845 if repeat_indices.contains(&outer_index) {
846 continue;
847 }
848 let imgsource = &source_set.image_sources[outer_index];
849 let pixel_density = imgsource.descriptor.density.unwrap();
850 for inner_index in (outer_index + 1)..len {
851 let imgsource2 = &source_set.image_sources[inner_index];
852 if pixel_density == imgsource2.descriptor.density.unwrap() {
853 repeat_indices.insert(inner_index);
854 }
855 }
856 }
857
858 let mut max = (0f64, 0);
859 let img_sources = &mut vec![];
860 for (index, image_source) in source_set.image_sources.iter().enumerate() {
861 if repeat_indices.contains(&index) {
862 continue;
863 }
864 let den = image_source.descriptor.density.unwrap();
865 if max.0 < den {
866 max = (den, img_sources.len());
867 }
868 img_sources.push(image_source);
869 }
870
871 let mut best_candidate = max;
874 let device_pixel_ratio = self
875 .owner_document()
876 .window()
877 .viewport_details()
878 .hidpi_scale_factor
879 .get() as f64;
880 for (index, image_source) in img_sources.iter().enumerate() {
881 let current_den = image_source.descriptor.density.unwrap();
882 if current_den < best_candidate.0 && current_den >= device_pixel_ratio {
883 best_candidate = (current_den, index);
884 }
885 }
886 let selected_source = img_sources.remove(best_candidate.1).clone();
887
888 Some((
890 USVString(selected_source.url),
891 selected_source.descriptor.density.unwrap(),
892 ))
893 }
894
895 fn init_image_request(
896 &self,
897 request: &mut RefMut<'_, ImageRequest>,
898 url: &ServoUrl,
899 src: &USVString,
900 can_gc: CanGc,
901 ) {
902 request.parsed_url = Some(url.clone());
903 request.source_url = Some(src.clone());
904 request.image = None;
905 request.metadata = None;
906 let document = self.owner_document();
907 LoadBlocker::terminate(&request.blocker, can_gc);
908 *request.blocker.borrow_mut() =
909 Some(LoadBlocker::new(&document, LoadType::Image(url.clone())));
910 }
911
912 fn prepare_image_request(
914 &self,
915 url: &ServoUrl,
916 src: &USVString,
917 selected_pixel_density: f64,
918 can_gc: CanGc,
919 ) {
920 match self.image_request.get() {
921 ImageRequestPhase::Pending => {
922 if let Some(pending_url) = self.pending_request.borrow().parsed_url.clone() {
923 if pending_url == *url {
925 return;
926 }
927 }
928 },
929 ImageRequestPhase::Current => {
930 let mut current_request = self.current_request.borrow_mut();
931 let mut pending_request = self.pending_request.borrow_mut();
932 match (current_request.parsed_url.clone(), current_request.state) {
934 (Some(parsed_url), State::PartiallyAvailable) => {
935 if parsed_url == *url {
937 pending_request.image = None;
939 pending_request.parsed_url = None;
940 LoadBlocker::terminate(&pending_request.blocker, can_gc);
941 return;
943 }
944 pending_request.current_pixel_density = Some(selected_pixel_density);
945 self.image_request.set(ImageRequestPhase::Pending);
946 self.init_image_request(&mut pending_request, url, src, can_gc);
947 },
948 (_, State::Broken) | (_, State::Unavailable) => {
949 current_request.current_pixel_density = Some(selected_pixel_density);
951 self.init_image_request(&mut current_request, url, src, can_gc);
952 self.reject_image_decode_promises();
953 },
954 (_, _) => {
955 pending_request.current_pixel_density = Some(selected_pixel_density);
957 self.image_request.set(ImageRequestPhase::Pending);
958 self.init_image_request(&mut pending_request, url, src, can_gc);
959 },
960 }
961 },
962 }
963 self.fetch_image(url, can_gc);
964 }
965
966 fn update_the_image_data_sync_steps(&self, can_gc: CanGc) {
968 let document = self.owner_document();
969 let global = self.owner_global();
970 let task_manager = global.task_manager();
971 let task_source = task_manager.dom_manipulation_task_source();
972 let this = Trusted::new(self);
973 let (src, pixel_density) = match self.select_image_source() {
974 Some(data) => data,
976 None => {
977 self.abort_request(State::Broken, ImageRequestPhase::Current, can_gc);
978 self.abort_request(State::Broken, ImageRequestPhase::Pending, can_gc);
979 task_source.queue(task!(image_null_source_error: move || {
981 let this = this.root();
982 {
983 let mut current_request =
984 this.current_request.borrow_mut();
985 current_request.source_url = None;
986 current_request.parsed_url = None;
987 }
988 let elem = this.upcast::<Element>();
989 let src_present = elem.has_attribute(&local_name!("src"));
990
991 if src_present || Self::uses_srcset_or_picture(elem) {
992 this.upcast::<EventTarget>().fire_event(atom!("error"), CanGc::note());
993 }
994 }));
995 return;
996 },
997 };
998
999 let base_url = document.base_url();
1001 let parsed_url = base_url.join(&src.0);
1002 match parsed_url {
1003 Ok(url) => {
1004 self.prepare_image_request(&url, &src, pixel_density, can_gc);
1006 },
1007 Err(_) => {
1008 self.abort_request(State::Broken, ImageRequestPhase::Current, can_gc);
1009 self.abort_request(State::Broken, ImageRequestPhase::Pending, can_gc);
1010 let src = src.0;
1012 task_source.queue(task!(image_selected_source_error: move || {
1013 let this = this.root();
1014 {
1015 let mut current_request =
1016 this.current_request.borrow_mut();
1017 current_request.source_url = Some(USVString(src))
1018 }
1019 this.upcast::<EventTarget>().fire_event(atom!("error"), CanGc::note());
1020
1021 }));
1022 },
1023 }
1024 }
1025
1026 pub(crate) fn update_the_image_data(&self, can_gc: CanGc) {
1028 let document = self.owner_document();
1029 let window = document.window();
1030 let elem = self.upcast::<Element>();
1031 let src = elem.get_url_attribute(&local_name!("src"));
1032 let base_url = document.base_url();
1033
1034 {
1038 let mut current_request = self.current_request.borrow_mut();
1039 current_request.state = State::Unavailable;
1040 }
1041
1042 if !document.is_active() {
1043 }
1047 let mut selected_source = None;
1052 let mut pixel_density = None;
1053 let src_set = elem.get_url_attribute(&local_name!("srcset"));
1054 let is_parent_picture = elem
1055 .upcast::<Node>()
1056 .GetParentElement()
1057 .is_some_and(|p| p.is::<HTMLPictureElement>());
1058 if src_set.is_empty() && !is_parent_picture && !src.is_empty() {
1059 selected_source = Some(src.clone());
1060 pixel_density = Some(1_f64);
1061 };
1062
1063 self.last_selected_source
1065 .borrow_mut()
1066 .clone_from(&selected_source);
1067
1068 if let Some(src) = selected_source {
1070 if let Ok(img_url) = base_url.join(&src) {
1071 let image_cache = window.image_cache();
1072 let response = image_cache.get_image(
1073 img_url.clone(),
1074 window.origin().immutable().clone(),
1075 cors_setting_for_element(self.upcast()),
1076 );
1077
1078 if let Some(image) = response {
1079 self.generation.set(self.generation.get() + 1);
1082 let metadata = image.metadata();
1084 self.abort_request(
1086 State::CompletelyAvailable,
1087 ImageRequestPhase::Current,
1088 can_gc,
1089 );
1090 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, can_gc);
1091 let mut current_request = self.current_request.borrow_mut();
1092 current_request.final_url = Some(img_url.clone());
1093 current_request.image = Some(image);
1094 current_request.metadata = Some(metadata);
1095 current_request.current_pixel_density = pixel_density;
1097 let this = Trusted::new(self);
1098 let src = src.0;
1099
1100 self.owner_global()
1101 .task_manager()
1102 .dom_manipulation_task_source()
1103 .queue(task!(image_load_event: move || {
1104 let this = this.root();
1105 {
1106 let mut current_request =
1107 this.current_request.borrow_mut();
1108 current_request.parsed_url = Some(img_url);
1109 current_request.source_url = Some(USVString(src));
1110 }
1111 this.upcast::<EventTarget>().fire_event(atom!("load"), CanGc::note());
1113 }));
1114 return;
1115 }
1116 }
1117 }
1118 self.generation.set(self.generation.get() + 1);
1120 let task = ImageElementMicrotask::StableStateUpdateImageData {
1121 elem: DomRoot::from_ref(self),
1122 generation: self.generation.get(),
1123 };
1124 ScriptThread::await_stable_state(Microtask::ImageElement(task));
1125 }
1126
1127 pub(crate) fn react_to_environment_changes(&self) {
1129 let task = ImageElementMicrotask::EnvironmentChanges {
1131 elem: DomRoot::from_ref(self),
1132 generation: self.generation.get(),
1133 };
1134 ScriptThread::await_stable_state(Microtask::ImageElement(task));
1135 }
1136
1137 fn react_to_environment_changes_sync_steps(&self, generation: u32, can_gc: CanGc) {
1139 let elem = self.upcast::<Element>();
1140 let document = elem.owner_document();
1141 let has_pending_request = matches!(self.image_request.get(), ImageRequestPhase::Pending);
1142
1143 if !document.is_active() || !Self::uses_srcset_or_picture(elem) || has_pending_request {
1145 return;
1146 }
1147
1148 let (selected_source, selected_pixel_density) = match self.select_image_source() {
1150 Some(selected) => selected,
1151 None => return,
1152 };
1153
1154 let same_source = match *self.last_selected_source.borrow() {
1156 Some(ref last_src) => *last_src == selected_source,
1157 _ => false,
1158 };
1159
1160 let same_selected_pixel_density = match self.current_request.borrow().current_pixel_density
1161 {
1162 Some(den) => selected_pixel_density == den,
1163 _ => false,
1164 };
1165
1166 if same_source && same_selected_pixel_density {
1167 return;
1168 }
1169
1170 let base_url = document.base_url();
1171 let img_url = match base_url.join(&selected_source.0) {
1173 Ok(url) => url,
1174 Err(_) => return,
1175 };
1176
1177 self.image_request.set(ImageRequestPhase::Pending);
1179 self.init_image_request(
1180 &mut self.pending_request.borrow_mut(),
1181 &img_url,
1182 &selected_source,
1183 can_gc,
1184 );
1185
1186 let window = self.owner_window();
1188 let cache_result = window.image_cache().get_cached_image_status(
1189 img_url.clone(),
1190 window.origin().immutable().clone(),
1191 cors_setting_for_element(self.upcast()),
1192 UsePlaceholder::No,
1193 );
1194
1195 let change_type = ChangeType::Environment {
1196 selected_source: selected_source.clone(),
1197 selected_pixel_density,
1198 };
1199
1200 match cache_result {
1201 ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable { .. }) => {
1202 self.finish_reacting_to_environment_change(
1204 selected_source,
1205 generation,
1206 selected_pixel_density,
1207 )
1208 },
1209 ImageCacheResult::Available(ImageOrMetadataAvailable::MetadataAvailable(m, id)) => {
1210 self.process_image_response_for_environment_change(
1211 ImageResponse::MetadataLoaded(m),
1212 selected_source,
1213 generation,
1214 selected_pixel_density,
1215 can_gc,
1216 );
1217 self.register_image_cache_callback(id, change_type);
1218 },
1219 ImageCacheResult::LoadError => {
1220 self.process_image_response_for_environment_change(
1221 ImageResponse::None,
1222 selected_source,
1223 generation,
1224 selected_pixel_density,
1225 can_gc,
1226 );
1227 },
1228 ImageCacheResult::ReadyForRequest(id) => {
1229 self.fetch_request(&img_url, id);
1230 self.register_image_cache_callback(id, change_type);
1231 },
1232 ImageCacheResult::Pending(id) => {
1233 self.register_image_cache_callback(id, change_type);
1234 },
1235 }
1236 }
1237
1238 fn react_to_decode_image_sync_steps(&self, promise: Rc<Promise>, can_gc: CanGc) {
1240 if !self.owner_document().is_fully_active() ||
1244 matches!(self.current_request.borrow().state, State::Broken)
1245 {
1246 promise.reject_error(Error::Encoding, can_gc);
1247 } else if matches!(
1248 self.current_request.borrow().state,
1249 State::CompletelyAvailable
1250 ) {
1251 promise.resolve_native(&(), can_gc);
1253 } else if matches!(self.current_request.borrow().state, State::Unavailable) &&
1254 self.current_request.borrow().source_url.is_none()
1255 {
1256 promise.reject_error(Error::Encoding, can_gc);
1260 } else {
1261 self.image_decode_promises.borrow_mut().push(promise);
1262 }
1263 }
1264
1265 fn resolve_image_decode_promises(&self) {
1267 if self.image_decode_promises.borrow().is_empty() {
1268 return;
1269 }
1270
1271 let trusted_image_decode_promises: Vec<TrustedPromise> = self
1275 .image_decode_promises
1276 .borrow()
1277 .iter()
1278 .map(|promise| TrustedPromise::new(promise.clone()))
1279 .collect();
1280
1281 self.image_decode_promises.borrow_mut().clear();
1282
1283 self.owner_global()
1284 .task_manager()
1285 .dom_manipulation_task_source()
1286 .queue(task!(fulfill_image_decode_promises: move || {
1287 for trusted_promise in trusted_image_decode_promises {
1288 trusted_promise.root().resolve_native(&(), CanGc::note());
1289 }
1290 }));
1291 }
1292
1293 fn reject_image_decode_promises(&self) {
1295 if self.image_decode_promises.borrow().is_empty() {
1296 return;
1297 }
1298
1299 let trusted_image_decode_promises: Vec<TrustedPromise> = self
1302 .image_decode_promises
1303 .borrow()
1304 .iter()
1305 .map(|promise| TrustedPromise::new(promise.clone()))
1306 .collect();
1307
1308 self.image_decode_promises.borrow_mut().clear();
1309
1310 self.owner_global()
1311 .task_manager()
1312 .dom_manipulation_task_source()
1313 .queue(task!(reject_image_decode_promises: move || {
1314 for trusted_promise in trusted_image_decode_promises {
1315 trusted_promise.root().reject_error(Error::Encoding, CanGc::note());
1316 }
1317 }));
1318 }
1319
1320 fn finish_reacting_to_environment_change(
1322 &self,
1323 src: USVString,
1324 generation: u32,
1325 selected_pixel_density: f64,
1326 ) {
1327 let this = Trusted::new(self);
1328 let src = src.0;
1329 self.owner_global().task_manager().dom_manipulation_task_source().queue(
1330 task!(image_load_event: move || {
1331 let this = this.root();
1332 let relevant_mutation = this.generation.get() != generation;
1333 if relevant_mutation {
1335 this.abort_request(State::Unavailable, ImageRequestPhase::Pending, CanGc::note());
1336 return;
1337 }
1338 *this.last_selected_source.borrow_mut() = Some(USVString(src));
1340
1341 {
1342 let mut pending_request = this.pending_request.borrow_mut();
1343 pending_request.current_pixel_density = Some(selected_pixel_density);
1344
1345 pending_request.state = State::CompletelyAvailable;
1347
1348 #[allow(clippy::swap_with_temporary)]
1353 mem::swap(&mut this.current_request.borrow_mut(), &mut pending_request);
1354 }
1355 this.abort_request(State::Unavailable, ImageRequestPhase::Pending, CanGc::note());
1356
1357 this.upcast::<Node>().dirty(NodeDamage::Other);
1359
1360 this.upcast::<EventTarget>().fire_event(atom!("load"), CanGc::note());
1362 })
1363 );
1364 }
1365
1366 fn uses_srcset_or_picture(elem: &Element) -> bool {
1367 let has_src = elem.has_attribute(&local_name!("srcset"));
1368 let is_parent_picture = elem
1369 .upcast::<Node>()
1370 .GetParentElement()
1371 .is_some_and(|p| p.is::<HTMLPictureElement>());
1372 has_src || is_parent_picture
1373 }
1374
1375 fn new_inherited(
1376 local_name: LocalName,
1377 prefix: Option<Prefix>,
1378 document: &Document,
1379 creator: ElementCreator,
1380 ) -> HTMLImageElement {
1381 HTMLImageElement {
1382 htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
1383 image_request: Cell::new(ImageRequestPhase::Current),
1384 current_request: DomRefCell::new(ImageRequest {
1385 state: State::Unavailable,
1386 parsed_url: None,
1387 source_url: None,
1388 image: None,
1389 metadata: None,
1390 blocker: DomRefCell::new(None),
1391 final_url: None,
1392 current_pixel_density: None,
1393 }),
1394 pending_request: DomRefCell::new(ImageRequest {
1395 state: State::Unavailable,
1396 parsed_url: None,
1397 source_url: None,
1398 image: None,
1399 metadata: None,
1400 blocker: DomRefCell::new(None),
1401 final_url: None,
1402 current_pixel_density: None,
1403 }),
1404 form_owner: Default::default(),
1405 generation: Default::default(),
1406 source_set: DomRefCell::new(SourceSet::new()),
1407 dimension_attribute_source: Default::default(),
1408 last_selected_source: DomRefCell::new(None),
1409 image_decode_promises: DomRefCell::new(vec![]),
1410 line_number: creator.return_line_number(),
1411 }
1412 }
1413
1414 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
1415 pub(crate) fn new(
1416 local_name: LocalName,
1417 prefix: Option<Prefix>,
1418 document: &Document,
1419 proto: Option<HandleObject>,
1420 creator: ElementCreator,
1421 can_gc: CanGc,
1422 ) -> DomRoot<HTMLImageElement> {
1423 let image_element = Node::reflect_node_with_proto(
1424 Box::new(HTMLImageElement::new_inherited(
1425 local_name, prefix, document, creator,
1426 )),
1427 document,
1428 proto,
1429 can_gc,
1430 );
1431 image_element
1432 .dimension_attribute_source
1433 .set(Some(image_element.upcast()));
1434 image_element
1435 }
1436
1437 pub(crate) fn areas(&self) -> Option<Vec<DomRoot<HTMLAreaElement>>> {
1438 let elem = self.upcast::<Element>();
1439 let usemap_attr = elem.get_attribute(&ns!(), &local_name!("usemap"))?;
1440
1441 let value = usemap_attr.value();
1442
1443 if value.is_empty() || !value.is_char_boundary(1) {
1444 return None;
1445 }
1446
1447 let (first, last) = value.split_at(1);
1448
1449 if first != "#" || last.is_empty() {
1450 return None;
1451 }
1452
1453 let useMapElements = self
1454 .owner_document()
1455 .upcast::<Node>()
1456 .traverse_preorder(ShadowIncluding::No)
1457 .filter_map(DomRoot::downcast::<HTMLMapElement>)
1458 .find(|n| {
1459 n.upcast::<Element>()
1460 .get_name()
1461 .is_some_and(|n| *n == *last)
1462 });
1463
1464 useMapElements.map(|mapElem| mapElem.get_area_elements())
1465 }
1466
1467 pub(crate) fn same_origin(&self, origin: &MutableOrigin) -> bool {
1468 if let Some(ref image) = self.current_request.borrow().image {
1469 return image.cors_status() == CorsStatus::Safe;
1470 }
1471
1472 self.current_request
1473 .borrow()
1474 .final_url
1475 .as_ref()
1476 .is_some_and(|url| url.scheme() == "data" || url.origin().same_origin(origin))
1477 }
1478
1479 fn generation_id(&self) -> u32 {
1480 self.generation.get()
1481 }
1482}
1483
1484#[derive(JSTraceable, MallocSizeOf)]
1485pub(crate) enum ImageElementMicrotask {
1486 StableStateUpdateImageData {
1487 elem: DomRoot<HTMLImageElement>,
1488 generation: u32,
1489 },
1490 EnvironmentChanges {
1491 elem: DomRoot<HTMLImageElement>,
1492 generation: u32,
1493 },
1494 Decode {
1495 elem: DomRoot<HTMLImageElement>,
1496 #[conditional_malloc_size_of]
1497 promise: Rc<Promise>,
1498 },
1499}
1500
1501impl MicrotaskRunnable for ImageElementMicrotask {
1502 fn handler(&self, can_gc: CanGc) {
1503 match *self {
1504 ImageElementMicrotask::StableStateUpdateImageData {
1505 ref elem,
1506 ref generation,
1507 } => {
1508 if elem.generation.get() == *generation {
1511 elem.update_the_image_data_sync_steps(can_gc);
1512 }
1513 },
1514 ImageElementMicrotask::EnvironmentChanges {
1515 ref elem,
1516 ref generation,
1517 } => {
1518 elem.react_to_environment_changes_sync_steps(*generation, can_gc);
1519 },
1520 ImageElementMicrotask::Decode {
1521 ref elem,
1522 ref promise,
1523 } => {
1524 elem.react_to_decode_image_sync_steps(promise.clone(), can_gc);
1525 },
1526 }
1527 }
1528
1529 fn enter_realm(&self) -> JSAutoRealm {
1530 match self {
1531 &ImageElementMicrotask::StableStateUpdateImageData { ref elem, .. } |
1532 &ImageElementMicrotask::EnvironmentChanges { ref elem, .. } |
1533 &ImageElementMicrotask::Decode { ref elem, .. } => enter_realm(&**elem),
1534 }
1535 }
1536}
1537
1538pub(crate) trait LayoutHTMLImageElementHelpers {
1539 fn image_url(self) -> Option<ServoUrl>;
1540 fn image_density(self) -> Option<f64>;
1541 fn image_data(self) -> (Option<Image>, Option<ImageMetadata>);
1542 fn get_width(self) -> LengthOrPercentageOrAuto;
1543 fn get_height(self) -> LengthOrPercentageOrAuto;
1544}
1545
1546impl<'dom> LayoutDom<'dom, HTMLImageElement> {
1547 #[allow(unsafe_code)]
1548 fn current_request(self) -> &'dom ImageRequest {
1549 unsafe { self.unsafe_get().current_request.borrow_for_layout() }
1550 }
1551
1552 #[allow(unsafe_code)]
1553 fn dimension_attribute_source(self) -> LayoutDom<'dom, Element> {
1554 unsafe {
1555 self.unsafe_get()
1556 .dimension_attribute_source
1557 .get_inner_as_layout()
1558 .expect("dimension attribute source should be always non-null")
1559 }
1560 }
1561}
1562
1563impl LayoutHTMLImageElementHelpers for LayoutDom<'_, HTMLImageElement> {
1564 fn image_url(self) -> Option<ServoUrl> {
1565 self.current_request().parsed_url.clone()
1566 }
1567
1568 fn image_data(self) -> (Option<Image>, Option<ImageMetadata>) {
1569 let current_request = self.current_request();
1570 (current_request.image.clone(), current_request.metadata)
1571 }
1572
1573 fn image_density(self) -> Option<f64> {
1574 self.current_request().current_pixel_density
1575 }
1576
1577 fn get_width(self) -> LengthOrPercentageOrAuto {
1578 self.dimension_attribute_source()
1579 .get_attr_for_layout(&ns!(), &local_name!("width"))
1580 .map(AttrValue::as_dimension)
1581 .cloned()
1582 .unwrap_or(LengthOrPercentageOrAuto::Auto)
1583 }
1584
1585 fn get_height(self) -> LengthOrPercentageOrAuto {
1586 self.dimension_attribute_source()
1587 .get_attr_for_layout(&ns!(), &local_name!("height"))
1588 .map(AttrValue::as_dimension)
1589 .cloned()
1590 .unwrap_or(LengthOrPercentageOrAuto::Auto)
1591 }
1592}
1593
1594fn parse_a_sizes_attribute(value: &str) -> SourceSizeList {
1596 let mut input = ParserInput::new(value);
1597 let mut parser = Parser::new(&mut input);
1598 let url_data = Url::parse("about:blank").unwrap().into();
1599 let context = ParserContext::new(
1600 Origin::Author,
1601 &url_data,
1602 Some(CssRuleType::Style),
1603 ParsingMode::empty(),
1606 QuirksMode::NoQuirks,
1607 Default::default(),
1608 None,
1609 None,
1610 );
1611 SourceSizeList::parse(&context, &mut parser)
1612}
1613
1614#[allow(non_snake_case)]
1615impl HTMLImageElementMethods<crate::DomTypeHolder> for HTMLImageElement {
1616 fn Image(
1618 window: &Window,
1619 proto: Option<HandleObject>,
1620 can_gc: CanGc,
1621 width: Option<u32>,
1622 height: Option<u32>,
1623 ) -> Fallible<DomRoot<HTMLImageElement>> {
1624 let document = window.Document();
1626
1627 let element = Element::create(
1630 QualName::new(None, ns!(html), local_name!("img")),
1631 None,
1632 &document,
1633 ElementCreator::ScriptCreated,
1634 CustomElementCreationMode::Synchronous,
1635 proto,
1636 can_gc,
1637 );
1638
1639 let image = DomRoot::downcast::<HTMLImageElement>(element).unwrap();
1640
1641 if let Some(w) = width {
1643 image.SetWidth(w);
1644 }
1645
1646 if let Some(h) = height {
1649 image.SetHeight(h);
1650 }
1651
1652 Ok(image)
1654 }
1655
1656 make_getter!(Alt, "alt");
1658 make_setter!(SetAlt, "alt");
1660
1661 make_url_getter!(Src, "src");
1663
1664 make_url_setter!(SetSrc, "src");
1666
1667 make_url_getter!(Srcset, "srcset");
1669 make_url_setter!(SetSrcset, "srcset");
1671
1672 make_getter!(Sizes, "sizes");
1674
1675 make_setter!(SetSizes, "sizes");
1677
1678 fn GetCrossOrigin(&self) -> Option<DOMString> {
1680 reflect_cross_origin_attribute(self.upcast::<Element>())
1681 }
1682
1683 fn SetCrossOrigin(&self, value: Option<DOMString>, can_gc: CanGc) {
1685 set_cross_origin_attribute(self.upcast::<Element>(), value, can_gc);
1686 }
1687
1688 make_getter!(UseMap, "usemap");
1690 make_setter!(SetUseMap, "usemap");
1692
1693 make_bool_getter!(IsMap, "ismap");
1695 make_bool_setter!(SetIsMap, "ismap");
1697
1698 fn Width(&self) -> u32 {
1700 let node = self.upcast::<Node>();
1701 node.content_box()
1702 .map(|rect| rect.size.width.to_px() as u32)
1703 .unwrap_or_else(|| self.NaturalWidth())
1704 }
1705
1706 make_dimension_uint_setter!(SetWidth, "width");
1708
1709 fn Height(&self) -> u32 {
1711 let node = self.upcast::<Node>();
1712 node.content_box()
1713 .map(|rect| rect.size.height.to_px() as u32)
1714 .unwrap_or_else(|| self.NaturalHeight())
1715 }
1716
1717 make_dimension_uint_setter!(SetHeight, "height");
1719
1720 fn NaturalWidth(&self) -> u32 {
1722 let request = self.current_request.borrow();
1723 let pixel_density = request.current_pixel_density.unwrap_or(1f64);
1724
1725 match request.metadata {
1726 Some(ref metadata) => (metadata.width as f64 / pixel_density) as u32,
1727 None => 0,
1728 }
1729 }
1730
1731 fn NaturalHeight(&self) -> u32 {
1733 let request = self.current_request.borrow();
1734 let pixel_density = request.current_pixel_density.unwrap_or(1f64);
1735
1736 match request.metadata {
1737 Some(ref metadata) => (metadata.height as f64 / pixel_density) as u32,
1738 None => 0,
1739 }
1740 }
1741
1742 fn Complete(&self) -> bool {
1744 let elem = self.upcast::<Element>();
1745 let srcset_absent = !elem.has_attribute(&local_name!("srcset"));
1746 if !elem.has_attribute(&local_name!("src")) && srcset_absent {
1747 return true;
1748 }
1749 let src = elem.get_string_attribute(&local_name!("src"));
1750 if srcset_absent && src.is_empty() {
1751 return true;
1752 }
1753 let request = self.current_request.borrow();
1754 let request_state = request.state;
1755 match request_state {
1756 State::CompletelyAvailable | State::Broken => true,
1757 State::PartiallyAvailable | State::Unavailable => false,
1758 }
1759 }
1760
1761 fn CurrentSrc(&self) -> USVString {
1763 let current_request = self.current_request.borrow();
1764 let url = ¤t_request.parsed_url;
1765 match *url {
1766 Some(ref url) => USVString(url.clone().into_string()),
1767 None => {
1768 let unparsed_url = ¤t_request.source_url;
1769 match *unparsed_url {
1770 Some(ref url) => url.clone(),
1771 None => USVString("".to_owned()),
1772 }
1773 },
1774 }
1775 }
1776
1777 fn ReferrerPolicy(&self) -> DOMString {
1779 reflect_referrer_policy_attribute(self.upcast::<Element>())
1780 }
1781
1782 make_setter!(SetReferrerPolicy, "referrerpolicy");
1784
1785 fn Decode(&self, can_gc: CanGc) -> Rc<Promise> {
1787 let promise = Promise::new(&self.global(), can_gc);
1789
1790 let task = ImageElementMicrotask::Decode {
1792 elem: DomRoot::from_ref(self),
1793 promise: promise.clone(),
1794 };
1795 ScriptThread::await_stable_state(Microtask::ImageElement(task));
1796
1797 promise
1799 }
1800
1801 make_getter!(Name, "name");
1803
1804 make_atomic_setter!(SetName, "name");
1806
1807 make_getter!(Align, "align");
1809
1810 make_setter!(SetAlign, "align");
1812
1813 make_uint_getter!(Hspace, "hspace");
1815
1816 make_uint_setter!(SetHspace, "hspace");
1818
1819 make_uint_getter!(Vspace, "vspace");
1821
1822 make_uint_setter!(SetVspace, "vspace");
1824
1825 make_getter!(LongDesc, "longdesc");
1827
1828 make_setter!(SetLongDesc, "longdesc");
1830
1831 make_getter!(Border, "border");
1833
1834 make_setter!(SetBorder, "border");
1836}
1837
1838impl VirtualMethods for HTMLImageElement {
1839 fn super_type(&self) -> Option<&dyn VirtualMethods> {
1840 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
1841 }
1842
1843 fn adopting_steps(&self, old_doc: &Document, can_gc: CanGc) {
1844 self.super_type().unwrap().adopting_steps(old_doc, can_gc);
1845 self.update_the_image_data(can_gc);
1846 }
1847
1848 fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
1849 self.super_type()
1850 .unwrap()
1851 .attribute_mutated(attr, mutation, can_gc);
1852 match attr.local_name() {
1853 &local_name!("src") |
1854 &local_name!("srcset") |
1855 &local_name!("width") |
1856 &local_name!("sizes") => {
1857 self.update_the_image_data(can_gc);
1861 },
1862 &local_name!("crossorigin") => {
1863 let cross_origin_state_changed = match mutation {
1866 AttributeMutation::Removed | AttributeMutation::Set(None) => true,
1867 AttributeMutation::Set(Some(old_value)) => {
1868 let new_cors_setting =
1869 CorsSettings::from_enumerated_attribute(&attr.value());
1870 let old_cors_setting = CorsSettings::from_enumerated_attribute(old_value);
1871
1872 new_cors_setting != old_cors_setting
1873 },
1874 };
1875
1876 if cross_origin_state_changed {
1877 self.update_the_image_data(can_gc);
1878 }
1879 },
1880 &local_name!("referrerpolicy") => {
1881 let referrer_policy_state_changed = match mutation {
1884 AttributeMutation::Removed | AttributeMutation::Set(None) => {
1885 ReferrerPolicy::from(&**attr.value()) != ReferrerPolicy::EmptyString
1886 },
1887 AttributeMutation::Set(Some(old_value)) => {
1888 ReferrerPolicy::from(&**attr.value()) != ReferrerPolicy::from(&**old_value)
1889 },
1890 };
1891
1892 if referrer_policy_state_changed {
1893 self.update_the_image_data(can_gc);
1894 }
1895 },
1896 _ => {},
1897 }
1898 }
1899
1900 fn attribute_affects_presentational_hints(&self, attr: &Attr) -> bool {
1901 match attr.local_name() {
1902 &local_name!("width") | &local_name!("height") => true,
1903 _ => self
1904 .super_type()
1905 .unwrap()
1906 .attribute_affects_presentational_hints(attr),
1907 }
1908 }
1909
1910 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
1911 match name {
1912 &local_name!("width") | &local_name!("height") => {
1913 AttrValue::from_dimension(value.into())
1914 },
1915 &local_name!("hspace") | &local_name!("vspace") => AttrValue::from_u32(value.into(), 0),
1916 _ => self
1917 .super_type()
1918 .unwrap()
1919 .parse_plain_attribute(name, value),
1920 }
1921 }
1922
1923 fn handle_event(&self, event: &Event, can_gc: CanGc) {
1924 if event.type_() != atom!("click") {
1925 return;
1926 }
1927
1928 let area_elements = self.areas();
1929 let elements = match area_elements {
1930 Some(x) => x,
1931 None => return,
1932 };
1933
1934 let mouse_event = match event.downcast::<MouseEvent>() {
1936 Some(x) => x,
1937 None => return,
1938 };
1939
1940 let point = Point2D::new(
1941 mouse_event.ClientX().to_f32().unwrap(),
1942 mouse_event.ClientY().to_f32().unwrap(),
1943 );
1944 let bcr = self.upcast::<Element>().GetBoundingClientRect(can_gc);
1945 let bcr_p = Point2D::new(bcr.X() as f32, bcr.Y() as f32);
1946
1947 for element in elements {
1949 let shape = element.get_shape_from_coords();
1950 let shp = match shape {
1951 Some(x) => x.absolute_coords(bcr_p),
1952 None => return,
1953 };
1954 if shp.hit_test(&point) {
1955 element.activation_behavior(event, self.upcast(), can_gc);
1956 return;
1957 }
1958 }
1959 }
1960
1961 fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) {
1963 if let Some(s) = self.super_type() {
1964 s.bind_to_tree(context, can_gc);
1965 }
1966 let document = self.owner_document();
1967 if context.tree_connected {
1968 document.register_responsive_image(self);
1969 }
1970
1971 let parent = self.upcast::<Node>().GetParentNode().unwrap();
1972
1973 if parent.is::<HTMLPictureElement>() && std::ptr::eq(&*parent, context.parent) {
1976 self.update_the_image_data(can_gc);
1977 }
1978 }
1979
1980 fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) {
1982 self.super_type().unwrap().unbind_from_tree(context, can_gc);
1983 let document = self.owner_document();
1984 document.unregister_responsive_image(self);
1985
1986 if context.parent.is::<HTMLPictureElement>() && !self.upcast::<Node>().has_parent() {
1989 self.update_the_image_data(can_gc);
1990 }
1991 }
1992}
1993
1994impl FormControl for HTMLImageElement {
1995 fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
1996 self.form_owner.get()
1997 }
1998
1999 fn set_form_owner(&self, form: Option<&HTMLFormElement>) {
2000 self.form_owner.set(form);
2001 }
2002
2003 fn to_element(&self) -> &Element {
2004 self.upcast::<Element>()
2005 }
2006
2007 fn is_listed(&self) -> bool {
2008 false
2009 }
2010}
2011
2012pub(crate) fn collect_sequence_characters(
2015 s: &str,
2016 mut predicate: impl FnMut(&char) -> bool,
2017) -> (&str, &str) {
2018 let i = s.find(|ch| !predicate(&ch)).unwrap_or(s.len());
2019 (&s[0..i], &s[i..])
2020}
2021
2022fn is_valid_non_negative_integer_string(s: &str) -> bool {
2025 s.chars().all(|c| c.is_ascii_digit())
2026}
2027
2028fn is_valid_floating_point_number_string(s: &str) -> bool {
2031 static RE: LazyLock<Regex> =
2032 LazyLock::new(|| Regex::new(r"^-?(?:\d+\.\d+|\d+|\.\d+)(?:(e|E)(\+|\-)?\d+)?$").unwrap());
2033
2034 RE.is_match(s)
2035}
2036
2037pub fn parse_a_srcset_attribute(input: &str) -> Vec<ImageSource> {
2040 let mut current_index = 0;
2043
2044 let mut candidates = vec![];
2046 while current_index < input.len() {
2047 let remaining_string = &input[current_index..];
2048
2049 let (collected_characters, string_after_whitespace) =
2056 collect_sequence_characters(remaining_string, |character| {
2057 *character == ',' || character.is_ascii_whitespace()
2058 });
2059
2060 current_index += collected_characters.len();
2063
2064 if string_after_whitespace.is_empty() {
2066 return candidates;
2067 }
2068
2069 let (url, _) =
2072 collect_sequence_characters(string_after_whitespace, |c| !char::is_ascii_whitespace(c));
2073
2074 current_index += url.len();
2077
2078 let mut descriptors = Vec::new();
2080
2081 if url.ends_with(',') {
2085 let image_source = ImageSource {
2086 url: url.trim_end_matches(',').into(),
2087 descriptor: Descriptor {
2088 width: None,
2089 density: None,
2090 },
2091 };
2092 candidates.push(image_source);
2093 continue;
2094 }
2095
2096 let descriptors_string = &input[current_index..];
2099 let (spaces, descriptors_string) =
2100 collect_sequence_characters(descriptors_string, |character| {
2101 character.is_ascii_whitespace()
2102 });
2103 current_index += spaces.len();
2104
2105 let mut current_descriptor = String::new();
2107
2108 let mut state = ParseState::InDescriptor;
2110
2111 let mut characters = descriptors_string.chars();
2115 let mut character = characters.next();
2116 if let Some(character) = character {
2117 current_index += character.len_utf8();
2118 }
2119
2120 loop {
2121 match (state, character) {
2122 (ParseState::InDescriptor, Some(character)) if character.is_ascii_whitespace() => {
2123 if !current_descriptor.is_empty() {
2127 descriptors.push(current_descriptor);
2128 current_descriptor = String::new();
2129 state = ParseState::AfterDescriptor;
2130 }
2131 },
2132 (ParseState::InDescriptor, Some(',')) => {
2133 if !current_descriptor.is_empty() {
2137 descriptors.push(current_descriptor);
2138 }
2139 break;
2140 },
2141 (ParseState::InDescriptor, Some('(')) => {
2142 current_descriptor.push('(');
2144 state = ParseState::InParens;
2145 },
2146 (ParseState::InDescriptor, Some(character)) => {
2147 current_descriptor.push(character);
2149 },
2150 (ParseState::InDescriptor, None) => {
2151 if !current_descriptor.is_empty() {
2154 descriptors.push(current_descriptor);
2155 }
2156 break;
2157 },
2158 (ParseState::InParens, Some(')')) => {
2159 current_descriptor.push(')');
2161 state = ParseState::InDescriptor;
2162 },
2163 (ParseState::InParens, Some(character)) => {
2164 current_descriptor.push(character);
2166 },
2167 (ParseState::InParens, None) => {
2168 descriptors.push(current_descriptor);
2171 break;
2172 },
2173 (ParseState::AfterDescriptor, Some(character))
2174 if character.is_ascii_whitespace() =>
2175 {
2176 },
2178 (ParseState::AfterDescriptor, Some(_)) => {
2179 state = ParseState::InDescriptor;
2182 continue;
2183 },
2184 (ParseState::AfterDescriptor, None) => {
2185 break;
2187 },
2188 }
2189
2190 character = characters.next();
2191 if let Some(character) = character {
2192 current_index += character.len_utf8();
2193 }
2194 }
2195
2196 let mut error = false;
2198 let mut width: Option<u32> = None;
2200 let mut density: Option<f64> = None;
2202 let mut future_compat_h: Option<u32> = None;
2204
2205 for descriptor in descriptors.into_iter() {
2208 let Some(last_character) = descriptor.chars().last() else {
2209 break;
2210 };
2211
2212 let first_part_of_string = &descriptor[0..descriptor.len() - last_character.len_utf8()];
2213 match last_character {
2214 'w' if is_valid_non_negative_integer_string(first_part_of_string) &&
2221 density.is_none() &&
2222 width.is_none() =>
2223 {
2224 match parse_unsigned_integer(first_part_of_string.chars()) {
2225 Ok(number) if number > 0 => {
2226 width = Some(number);
2227 continue;
2228 },
2229 _ => error = true,
2230 }
2231 },
2232
2233 'x' if is_valid_floating_point_number_string(first_part_of_string) &&
2247 width.is_none() &&
2248 density.is_none() &&
2249 future_compat_h.is_none() =>
2250 {
2251 match first_part_of_string.parse::<f64>() {
2252 Ok(number) if number.is_finite() && number >= 0. => {
2253 density = Some(number);
2254 continue;
2255 },
2256 _ => error = true,
2257 }
2258 },
2259
2260 'h' if is_valid_non_negative_integer_string(first_part_of_string) &&
2269 future_compat_h.is_none() &&
2270 density.is_none() =>
2271 {
2272 match parse_unsigned_integer(first_part_of_string.chars()) {
2273 Ok(number) if number > 0 => {
2274 future_compat_h = Some(number);
2275 continue;
2276 },
2277 _ => error = true,
2278 }
2279 },
2280
2281 _ => error = true,
2284 }
2285
2286 if error {
2287 break;
2288 }
2289 }
2290
2291 if future_compat_h.is_some() && width.is_none() {
2293 error = true;
2294 }
2295
2296 if !error {
2300 let image_source = ImageSource {
2301 url: url.into(),
2302 descriptor: Descriptor { width, density },
2303 };
2304 candidates.push(image_source);
2305 }
2306
2307 }
2309 candidates
2310}
2311
2312#[derive(Clone)]
2313enum ChangeType {
2314 Environment {
2315 selected_source: USVString,
2316 selected_pixel_density: f64,
2317 },
2318 Element,
2319}
2320
2321fn is_supported_image_mime_type(input: &str) -> bool {
2323 let mime_type = input.trim();
2325
2326 let mime_type_essence = match mime_type.find(';') {
2328 Some(semi) => &mime_type[..semi],
2329 _ => mime_type,
2330 };
2331
2332 if mime_type_essence.is_empty() {
2337 return true;
2338 }
2339
2340 SUPPORTED_IMAGE_MIME_TYPES.contains(&mime_type_essence)
2341}