1use std::cell::Cell;
6use std::sync::Arc;
7
8use atomic_refcell::{AtomicRef, AtomicRefCell};
9use bytes::Bytes;
10use dom_struct::dom_struct;
11use euclid::default::Point2D;
12use html5ever::{LocalName, Prefix, QualName, local_name, ns};
13use js::context::{JSContext, NoGC};
14use js::rust::HandleObject;
15use mime::{self, Mime};
16use net_traits::image_cache::{
17 Image, ImageCache, ImageCacheResult, ImageLoadListener, ImageOrMetadataAvailable,
18 ImageResponse, PendingImageId,
19};
20use net_traits::request::{CorsSettings, Destination, Initiator, RequestId};
21use net_traits::{
22 FetchMetadata, FetchResponseMsg, Metadata, NetworkError, ReferrerPolicy, ResourceFetchTiming,
23};
24use num_traits::ToPrimitive;
25use pixels::{CorsStatus, ImageMetadata, Snapshot};
26use script_bindings::cell::DomRefCell;
27use servo_base::cross_process_instant::CrossProcessInstant;
28use servo_url::ServoUrl;
29use servo_url::origin::MutableOrigin;
30use style::Atom;
31use style::attr::{AttrValue, LengthOrPercentageOrAuto, parse_unsigned_integer};
32
33use crate::dom::activation::Activatable;
34use crate::dom::bindings::cell::AtomicSafeBorrowMut;
35use crate::dom::bindings::codegen::Bindings::DOMRectBinding::DOMRect_Binding::DOMRectMethods;
36use crate::dom::bindings::codegen::Bindings::ElementBinding::Element_Binding::ElementMethods;
37use crate::dom::bindings::codegen::Bindings::HTMLImageElementBinding::HTMLImageElementMethods;
38use crate::dom::bindings::codegen::Bindings::MouseEventBinding::MouseEventMethods;
39use crate::dom::bindings::codegen::Bindings::NodeBinding::Node_Binding::NodeMethods;
40use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::ShadowRootMethods;
41use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
42use crate::dom::bindings::error::{Error, Fallible};
43use crate::dom::bindings::inheritance::Castable;
44use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
45use crate::dom::bindings::reflector::DomGlobal;
46use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom, ToLayoutOptional};
47use crate::dom::bindings::str::{DOMString, USVString};
48use crate::dom::csp::{GlobalCspReporting, Violation};
49use crate::dom::document::Document;
50use crate::dom::element::attributes::storage::AttrRef;
51use crate::dom::element::{
52 AttributeMutation, CustomElementCreationMode, Element, ElementCreator,
53 cors_setting_for_element, referrer_policy_for_element, reflect_cross_origin_attribute,
54 reflect_referrer_policy_attribute, set_cross_origin_attribute,
55};
56use crate::dom::event::Event;
57use crate::dom::eventtarget::EventTarget;
58use crate::dom::globalscope::GlobalScope;
59use crate::dom::html::htmlareaelement::HTMLAreaElement;
60use crate::dom::html::htmlelement::HTMLElement;
61use crate::dom::html::htmlformelement::{FormControl, HTMLFormElement};
62use crate::dom::html::htmlmapelement::HTMLMapElement;
63use crate::dom::html::htmlpictureelement::HTMLPictureElement;
64use crate::dom::iterators::ShadowIncluding;
65use crate::dom::mouseevent::MouseEvent;
66use crate::dom::node::virtualmethods::VirtualMethods;
67use crate::dom::node::{BindContext, MoveContext, Node, NodeDamage, NodeTraits, UnbindContext};
68use crate::dom::performance::performanceresourcetiming::InitiatorType;
69use crate::dom::promise::Promise;
70use crate::dom::srcset::SourceSet;
71use crate::dom::window::Window;
72use crate::dom::{RootedPromise, TracedPromise};
73use crate::event_loop::document_loader::{LoadBlocker, LoadType};
74use crate::event_loop::script_thread::ScriptThread;
75use crate::fetch::fetch::{RequestWithGlobalScope, create_a_potential_cors_request};
76use crate::fetch::network_listener::{self, FetchResponseListener, ResourceTimingListener};
77use crate::realms::enter_auto_realm;
78use crate::runtime::job_queue::MicrotaskRunnable;
79
80#[derive(Clone, Copy, Default, JSTraceable, MallocSizeOf)]
82enum State {
83 #[default]
84 Unavailable,
85 PartiallyAvailable,
86 CompletelyAvailable,
87 Broken,
88}
89
90#[derive(Clone, Copy, JSTraceable, MallocSizeOf)]
91enum ImageRequestPhase {
92 Pending,
93 Current,
94}
95
96#[derive(Default, JSTraceable, MallocSizeOf)]
98#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
99struct ImageRequest {
100 state: State,
101 #[no_trace]
102 parsed_url: Option<ServoUrl>,
103 source_url: Option<USVString>,
104 blocker: DomRefCell<Option<LoadBlocker>>,
105 #[no_trace]
106 image: Option<Image>,
107 #[no_trace]
108 metadata: Option<ImageMetadata>,
109 #[no_trace]
110 final_url: Option<ServoUrl>,
111 #[no_trace]
113 load_time: Option<CrossProcessInstant>,
114 current_pixel_density: Option<f64>,
115}
116
117#[dom_struct]
118pub(crate) struct HTMLImageElement {
119 htmlelement: HTMLElement,
120 #[custom_trace]
121 current_request: AtomicRefCell<Box<ImageRequest>>,
122 pending_request: DomRefCell<Option<Box<ImageRequest>>>,
123 form_owner: MutNullableDom<HTMLFormElement>,
124 source_set: DomRefCell<SourceSet>,
125 dimension_attribute_source: MutNullableDom<Element>,
128 last_selected_source: DomRefCell<Option<USVString>>,
130 image_decode_promises: DomRefCell<Vec<TracedPromise>>,
131 line_number: u64,
133 image_request: Cell<ImageRequestPhase>,
134 generation: Cell<u32>,
135}
136
137impl HTMLImageElement {
138 pub(crate) fn is_usable(&self) -> Fallible<bool> {
140 if let Some(image) = &self.current_request.borrow().image {
142 let intrinsic_size = image.metadata();
143 if intrinsic_size.width == 0 || intrinsic_size.height == 0 {
144 return Ok(false);
145 }
146 }
147
148 match self.current_request.borrow().state {
149 State::Broken => Err(Error::InvalidState(Some(
151 "Image element's state is broken".into(),
152 ))),
153 State::CompletelyAvailable => Ok(true),
154 State::PartiallyAvailable | State::Unavailable => Ok(false),
156 }
157 }
158
159 pub(crate) fn image_data(&self) -> Option<Image> {
160 self.current_request.borrow().image.clone()
161 }
162
163 pub(crate) fn get_raster_image_data(&self) -> Option<Snapshot> {
165 let Some(raster_image) = self.image_data()?.as_raster_image() else {
166 warn!("Vector image is not supported as raster image source");
167 return None;
168 };
169 Some(raster_image.as_snapshot())
170 }
171}
172
173struct ImageContext {
175 image_cache: Arc<dyn ImageCache>,
177 status: Result<(), NetworkError>,
179 id: PendingImageId,
181 aborted: bool,
183 url: ServoUrl,
184 element: Trusted<HTMLImageElement>,
185}
186
187impl FetchResponseListener for ImageContext {
188 fn should_invoke(&self) -> bool {
189 !self.aborted
190 }
191
192 fn process_request_body(&mut self, _: &GlobalScope, _: RequestId) {}
193
194 fn process_response(
195 &mut self,
196 _: &mut JSContext,
197 _: &GlobalScope,
198 request_id: RequestId,
199 metadata: Result<FetchMetadata, NetworkError>,
200 ) {
201 debug!("got {:?} for {:?}", metadata.as_ref().map(|_| ()), self.url);
202 self.image_cache.notify_pending_response(
203 self.id,
204 FetchResponseMsg::ProcessResponse(request_id, metadata.clone()),
205 );
206
207 let metadata: Option<Metadata> = metadata.ok().map(Into::into);
208
209 if let Some(metadata) = metadata.as_ref() &&
211 let Some(ref content_type) = metadata.content_type
212 {
213 let mime: Mime = content_type.clone().into_inner().into();
214 if mime.type_() == mime::MULTIPART && mime.subtype().as_str() == "x-mixed-replace" {
215 self.aborted = true;
216 }
217 }
218
219 self.status = match metadata.as_ref().map(|m| m.status.clone()) {
222 None => Err(NetworkError::ResourceLoadError(
223 "No http status code received".to_owned(),
224 )),
225 Some(_) => Ok(()),
226 };
227 }
228
229 fn process_response_chunk(
230 &mut self,
231 _: &mut JSContext,
232 _: &GlobalScope,
233 request_id: RequestId,
234 payload: Bytes,
235 ) {
236 if self.status.is_ok() {
237 self.image_cache.notify_pending_response(
238 self.id,
239 FetchResponseMsg::ProcessResponseChunk(request_id, payload),
240 );
241 }
242 }
243
244 fn process_response_eof(
245 self,
246 cx: &mut JSContext,
247 global: &GlobalScope,
248 request_id: RequestId,
249 response: Result<(), NetworkError>,
250 timing: ResourceFetchTiming,
251 ) {
252 self.image_cache.notify_pending_response(
253 self.id,
254 FetchResponseMsg::ProcessResponseEOF(request_id, response.clone(), timing.clone()),
255 );
256 network_listener::submit_timing(cx, global, &self, &response, &timing);
257 }
258
259 fn process_csp_violations(
260 &mut self,
261 cx: &mut JSContext,
262 global: &GlobalScope,
263 _request_id: RequestId,
264 violations: Vec<Violation>,
265 ) {
266 let elem = self.element.root();
267 let source_position = elem
268 .upcast::<Element>()
269 .compute_source_position(elem.line_number as u32);
270 global.report_csp_violations(cx, violations, None, Some(source_position));
271 }
272
273 fn process_content_length(&mut self, request_id: RequestId, size: usize) {
274 self.image_cache.notify_pending_response(
275 self.id,
276 FetchResponseMsg::ProcessContentLength(request_id, size),
277 );
278 }
279}
280
281impl ResourceTimingListener for ImageContext {
282 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
283 (InitiatorType::LocalName("img".into()), self.url.clone())
284 }
285}
286
287#[expect(non_snake_case)]
288impl HTMLImageElement {
289 fn fetch_image(&self, cx: &mut JSContext, img_url: &ServoUrl) {
291 let window = self.owner_window_unrooted(cx.no_gc());
292
293 let cache_result = window.image_cache().get_cached_image_status(
294 img_url.clone(),
295 window.origin().immutable(),
296 cors_setting_for_element(self.upcast()),
297 );
298
299 match cache_result {
300 ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable {
301 image,
302 url,
303 }) => self.process_image_response(cx, ImageResponse::Loaded(image, url)),
304 ImageCacheResult::Available(ImageOrMetadataAvailable::MetadataAvailable(
305 metadata,
306 id,
307 )) => {
308 self.process_image_response(cx, ImageResponse::MetadataLoaded(metadata));
309 self.register_image_cache_callback(id, ChangeType::Element);
310 },
311 ImageCacheResult::Pending(id) => {
312 self.register_image_cache_callback(id, ChangeType::Element);
313 },
314 ImageCacheResult::ReadyForRequest(id) => {
315 self.fetch_request(img_url, id);
316 self.register_image_cache_callback(id, ChangeType::Element);
317 },
318 ImageCacheResult::FailedToLoadOrDecode => {
319 self.process_image_response(cx, ImageResponse::FailedToLoadOrDecode)
320 },
321 };
322 }
323
324 fn register_image_cache_callback(&self, id: PendingImageId, change_type: ChangeType) {
325 let trusted_node = Trusted::new(self);
326 let generation = self.generation_id();
327 let window = self.owner_window();
328 let callback = window.register_image_cache_listener(id, move |response, _| {
329 let trusted_node = trusted_node.clone();
330 let window = trusted_node.root().owner_window();
331 let callback_type = change_type.clone();
332
333 window
334 .as_global_scope()
335 .task_manager()
336 .networking_task_source()
337 .queue(task!(process_image_response: move |cx| {
338 let element = trusted_node.root();
339
340 if generation != element.generation_id() {
342 return;
343 }
344
345 match callback_type {
346 ChangeType::Element => {
347 element.process_image_response(cx, response.response);
348 }
349 ChangeType::Environment { selected_source, selected_pixel_density } => {
350 element.process_image_response_for_environment_change(
351 cx, response.response, selected_source, generation, selected_pixel_density
352 );
353 }
354 }
355 }));
356 });
357
358 window.image_cache().add_listener(ImageLoadListener::new(
359 callback,
360 window.pipeline_id(),
361 id,
362 ));
363 }
364
365 fn fetch_request(&self, img_url: &ServoUrl, id: PendingImageId) {
366 let document = self.owner_document();
367 let window = self.owner_window();
368
369 let context = ImageContext {
370 image_cache: window.image_cache(),
371 status: Ok(()),
372 id,
373 aborted: false,
374 element: Trusted::new(self),
375 url: img_url.clone(),
376 };
377
378 let global = document.global();
381 let mut request = create_a_potential_cors_request(
382 Some(window.webview_id()),
383 img_url.clone(),
384 Destination::Image,
385 cors_setting_for_element(self.upcast()),
386 None,
387 global.get_referrer(),
388 )
389 .with_global_scope(&global)
390 .referrer_policy(referrer_policy_for_element(self.upcast()));
391
392 if self.uses_srcset_or_picture() {
393 request = request.initiator(Initiator::ImageSet);
394 }
395
396 document.fetch_background(request, context);
399 }
400
401 fn handle_loaded_image(&self, cx: &mut JSContext, image: Image, url: ServoUrl) {
403 {
404 let mut current_request = self.current_request.safe_borrow_mut(cx.no_gc());
405 current_request.metadata = Some(image.metadata());
406 current_request.final_url = Some(url);
407 current_request.image = Some(image);
408 current_request.state = State::CompletelyAvailable;
409 current_request.load_time = Some(CrossProcessInstant::now());
410 }
411
412 self.pending_request.safe_borrow_mut(cx.no_gc()).take();
413
414 LoadBlocker::terminate(cx, &self.current_request.borrow().blocker);
415 self.upcast::<Node>().dirty(cx.no_gc(), NodeDamage::Other);
417 self.resolve_image_decode_promises(cx);
418 }
419
420 fn fire_event_including_host(&self, cx: &mut JSContext, event: Atom) {
421 let node = self.upcast::<Node>();
425 if node.is_in_ua_widget() &&
426 let Some(containing_shadow_root) = node.containing_shadow_root()
427 {
428 containing_shadow_root
429 .Host()
430 .upcast::<EventTarget>()
431 .fire_event(cx, event);
432 } else {
433 self.upcast::<EventTarget>().fire_event(cx, event);
434 }
435 }
436
437 fn process_image_response(&self, cx: &mut JSContext, image: ImageResponse) {
439 let (trigger_image_load, trigger_image_error) = match (image, self.image_request.get()) {
445 (ImageResponse::Loaded(image, url), ImageRequestPhase::Current) => {
446 self.handle_loaded_image(cx, image, url);
447 (true, false)
448 },
449 (ImageResponse::Loaded(image, url), ImageRequestPhase::Pending) => {
450 self.abort_request(cx, State::Unavailable, ImageRequestPhase::Pending);
451 self.image_request.set(ImageRequestPhase::Current);
452 self.handle_loaded_image(cx, image, url);
453 (true, false)
454 },
455 (ImageResponse::MetadataLoaded(meta), ImageRequestPhase::Current) => {
456 self.current_request.safe_borrow_mut(cx.no_gc()).state = State::PartiallyAvailable;
461 self.current_request.safe_borrow_mut(cx.no_gc()).metadata = Some(meta);
462 (false, false)
463 },
464 (ImageResponse::MetadataLoaded(_), ImageRequestPhase::Pending) => {
465 self.pending_request
469 .safe_borrow_mut(cx.no_gc())
470 .get_or_insert_default()
471 .state = State::PartiallyAvailable;
472 (false, false)
473 },
474 (ImageResponse::FailedToLoadOrDecode, ImageRequestPhase::Current) => {
475 self.abort_request(cx, State::Broken, ImageRequestPhase::Current);
481
482 self.load_broken_image_icon(cx.no_gc());
483
484 (false, true)
488 },
489 (ImageResponse::FailedToLoadOrDecode, ImageRequestPhase::Pending) => {
490 self.abort_request(cx, State::Broken, ImageRequestPhase::Current);
496 self.abort_request(cx, State::Unavailable, ImageRequestPhase::Pending);
497
498 if self.pending_request.borrow().is_some() {
501 *self.current_request.safe_borrow_mut(cx.no_gc()) = self
502 .pending_request
503 .safe_borrow_mut(cx.no_gc())
504 .take()
505 .unwrap();
506 }
507 self.image_request.set(ImageRequestPhase::Current);
508
509 self.current_request.safe_borrow_mut(cx.no_gc()).state = State::Broken;
511
512 self.load_broken_image_icon(cx.no_gc());
513
514 (false, true)
516 },
517 };
518
519 if trigger_image_load {
521 self.fire_event_including_host(cx, atom!("load"));
522 self.upcast::<EventTarget>()
524 .fire_event(cx, atom!("loadend"));
525 }
526
527 if trigger_image_error {
529 self.fire_event_including_host(cx, atom!("error"));
530 self.upcast::<EventTarget>()
531 .fire_event(cx, atom!("loadend"));
532 }
533 }
534
535 fn process_image_response_for_environment_change(
538 &self,
539 cx: &mut JSContext,
540 image: ImageResponse,
541 selected_source: USVString,
542 generation: u32,
543 selected_pixel_density: f64,
544 ) {
545 match image {
546 ImageResponse::Loaded(image, url) => {
547 if let Some(pending_request) =
548 self.pending_request.safe_borrow_mut(cx.no_gc()).as_mut()
549 {
550 pending_request.metadata = Some(image.metadata());
551 pending_request.final_url = Some(url);
552 pending_request.image = Some(image);
553 pending_request.load_time = Some(CrossProcessInstant::now());
554 }
555 self.finish_reacting_to_environment_change(
556 selected_source,
557 generation,
558 selected_pixel_density,
559 );
560 },
561 ImageResponse::FailedToLoadOrDecode => {
562 self.abort_request(cx, State::Unavailable, ImageRequestPhase::Pending);
570 },
571 ImageResponse::MetadataLoaded(meta) => {
572 self.pending_request
573 .safe_borrow_mut(cx.no_gc())
574 .get_or_insert_default()
575 .metadata = Some(meta);
576 },
577 };
578 }
579
580 fn abort_request(&self, cx: &mut JSContext, state: State, request: ImageRequestPhase) {
582 match request {
583 ImageRequestPhase::Current => {
584 LoadBlocker::terminate(cx, &self.current_request.borrow().blocker);
585
586 let mut request = self.current_request.safe_borrow_mut(cx);
587 request.state = state;
588 request.image = None;
589 request.metadata = None;
590 request.current_pixel_density = None;
591 },
592 ImageRequestPhase::Pending => {
593 if let Some(pending_request) = &*self.pending_request.borrow() {
594 LoadBlocker::terminate(cx, &pending_request.blocker);
595 }
596 self.pending_request.safe_borrow_mut(cx.no_gc()).take();
597 },
598 };
599
600 if matches!(state, State::Broken) {
601 self.reject_image_decode_promises(cx);
602 } else if matches!(state, State::CompletelyAvailable) {
603 self.resolve_image_decode_promises(cx);
604 }
605 }
606
607 fn init_image_request(
608 &self,
609 cx: &mut JSContext,
610 request: &AtomicRefCell<Box<ImageRequest>>,
611 url: &ServoUrl,
612 src: &USVString,
613 ) {
614 {
615 let mut request = request.safe_borrow_mut(cx.no_gc());
616 request.parsed_url = Some(url.clone());
617 request.source_url = Some(src.clone());
618 request.image = None;
619 request.metadata = None;
620 }
621 let document = self.owner_document();
622 LoadBlocker::terminate(cx, &request.borrow().blocker);
623 *request
624 .safe_borrow_mut(cx.no_gc())
625 .blocker
626 .safe_borrow_mut(cx.no_gc()) =
627 Some(LoadBlocker::new(&document, LoadType::Image(url.clone())));
628 }
629
630 fn init_pending_image_request(
631 &self,
632 cx: &mut JSContext,
633 request: &DomRefCell<Option<Box<ImageRequest>>>,
634 url: &ServoUrl,
635 src: &USVString,
636 ) {
637 {
638 let mut request = request.safe_borrow_mut(cx);
639 let request = request.get_or_insert_default();
640 request.parsed_url = Some(url.clone());
641 request.source_url = Some(src.clone());
642 request.image = None;
643 request.metadata = None;
644 }
645 let document = self.owner_document();
646 LoadBlocker::terminate(
647 cx,
648 &request
649 .borrow()
650 .as_ref()
651 .expect("Just created a request")
652 .blocker,
653 );
654 *request
655 .safe_borrow_mut(cx)
656 .as_mut()
657 .expect("Just created a request")
658 .blocker
659 .safe_borrow_mut(cx.no_gc()) =
660 Some(LoadBlocker::new(&document, LoadType::Image(url.clone())));
661 }
662
663 fn prepare_image_request(
665 &self,
666 cx: &mut JSContext,
667 selected_source: &USVString,
668 selected_pixel_density: f64,
669 image_url: &ServoUrl,
670 ) {
671 match self.image_request.get() {
672 ImageRequestPhase::Pending => {
673 if self
676 .pending_request
677 .borrow()
678 .as_ref()
679 .and_then(|pending_request| pending_request.parsed_url.as_ref())
680 .is_some_and(|parsed_url| *parsed_url == *image_url)
681 {
682 return;
683 }
684 },
685 ImageRequestPhase::Current => {
686 self.abort_request(cx, State::Unavailable, ImageRequestPhase::Pending);
688
689 let (current_request_url, current_request_state) = {
691 let current_request = self.current_request.borrow();
692 (current_request.parsed_url.clone(), current_request.state)
693 };
694
695 match (current_request_url, current_request_state) {
696 (Some(parsed_url), State::PartiallyAvailable) => {
697 if parsed_url == *image_url {
703 return;
705 }
706
707 self.image_request.set(ImageRequestPhase::Pending);
711 self.init_pending_image_request(
712 cx,
713 &self.pending_request,
714 image_url,
715 selected_source,
716 );
717 self.pending_request
718 .safe_borrow_mut(cx.no_gc())
719 .as_mut()
720 .expect("Just created a pending request")
721 .current_pixel_density = Some(selected_pixel_density);
722 },
723 (_, State::Broken) | (_, State::Unavailable) => {
724 self.init_image_request(
728 cx,
729 &self.current_request,
730 image_url,
731 selected_source,
732 );
733 self.current_request
734 .safe_borrow_mut(cx.no_gc())
735 .current_pixel_density = Some(selected_pixel_density);
736 self.reject_image_decode_promises(cx);
737 },
738 (_, _) => {
739 self.image_request.set(ImageRequestPhase::Pending);
743 self.init_pending_image_request(
744 cx,
745 &self.pending_request,
746 image_url,
747 selected_source,
748 );
749 self.pending_request
750 .safe_borrow_mut(cx.no_gc())
751 .as_mut()
752 .expect("Just created a pending image request")
753 .current_pixel_density = Some(selected_pixel_density);
754 },
755 }
756 },
757 }
758
759 self.fetch_image(cx, image_url);
760 }
761
762 fn update_the_image_data_sync_steps(&self, cx: &mut JSContext) {
764 let Some((selected_source, selected_pixel_density)) = self
767 .source_set
768 .safe_borrow_mut(cx.no_gc())
769 .select_image_source(cx.no_gc(), self.upcast::<Element>())
770 else {
771 self.abort_request(cx, State::Broken, ImageRequestPhase::Current);
776 self.abort_request(cx, State::Unavailable, ImageRequestPhase::Pending);
777 self.image_request.set(ImageRequestPhase::Current);
778
779 let this = Trusted::new(self);
782
783 self.owner_global().task_manager().dom_manipulation_task_source().queue(
784 task!(image_null_source_error: move |cx| {
785 let this = this.root();
786
787 {
789 let mut current_request =
790 this.current_request.safe_borrow_mut(cx.no_gc());
791 current_request.source_url = None;
792 current_request.parsed_url = None;
793 }
794
795 let has_src_attribute = this.upcast::<Element>().has_attribute(&local_name!("src"));
801
802 if has_src_attribute || this.uses_srcset_or_picture() {
803 this.upcast::<EventTarget>().fire_event(cx, atom!("error"));
804 }
805 }));
806
807 return;
809 };
810
811 let Ok(image_url) = self.owner_document().encoding_parse_a_url(&selected_source) else {
814 self.abort_request(cx, State::Broken, ImageRequestPhase::Current);
819 self.abort_request(cx, State::Unavailable, ImageRequestPhase::Pending);
820
821 self.image_request.set(ImageRequestPhase::Current);
823
824 let this = Trusted::new(self);
827
828 self.owner_global()
829 .task_manager()
830 .dom_manipulation_task_source()
831 .queue(task!(image_selected_source_error: move |cx| {
832 let this = this.root();
833
834 {
836 let mut current_request =
837 this.current_request.safe_borrow_mut(cx.no_gc());
838 current_request.source_url = Some(selected_source);
839 current_request.parsed_url = None;
840 }
841
842 this.upcast::<EventTarget>().fire_event(cx, atom!("error"));
846 }));
847
848 return;
850 };
851
852 self.prepare_image_request(cx, &selected_source, selected_pixel_density, &image_url);
853 }
854
855 pub(crate) fn update_the_image_data(&self, cx: &mut JSContext) {
857 self.generation.set(self.generation.get() + 1);
859
860 if !self.owner_document().is_fully_active() {
862 }
868
869 self.source_set
872 .safe_borrow_mut(cx.no_gc())
873 .update_source_set(cx.no_gc(), self.upcast::<Element>());
874
875 self.current_request.safe_borrow_mut(cx.no_gc()).state = State::Unavailable;
884
885 let mut selected_source = None;
889 let mut selected_pixel_density = None;
890
891 let src = self
895 .upcast::<Element>()
896 .get_string_attribute(&local_name!("src"));
897
898 if !self.uses_srcset_or_picture() && !src.is_empty() {
899 selected_source = Some(USVString(String::from(src)));
900 selected_pixel_density = Some(1_f64);
901 };
902
903 self.last_selected_source
905 .safe_borrow_mut(cx.no_gc())
906 .clone_from(&selected_source);
907
908 if let Some(selected_source) = selected_source {
910 if let Ok(image_url) = self.owner_document().encoding_parse_a_url(&selected_source) {
914 let window = self.owner_window();
918 let response = window.image_cache().get_image(
919 image_url.clone(),
920 window.origin().immutable().clone(),
921 cors_setting_for_element(self.upcast()),
922 );
923
924 if let Some(image) = response {
926 self.abort_request(cx, State::CompletelyAvailable, ImageRequestPhase::Current);
931 self.abort_request(cx, State::Unavailable, ImageRequestPhase::Pending);
932
933 self.image_request.set(ImageRequestPhase::Current);
935
936 let mut current_request = self.current_request.safe_borrow_mut(cx.no_gc());
939 current_request.metadata = Some(image.metadata());
940 current_request.image = Some(image);
941 current_request.final_url = Some(image_url.clone());
942
943 self.upcast::<Node>().dirty(cx.no_gc(), NodeDamage::Other);
946
947 current_request.current_pixel_density = selected_pixel_density;
950
951 let this = Trusted::new(self);
954
955 self.owner_global()
956 .task_manager()
957 .dom_manipulation_task_source()
958 .queue(task!(image_load_event: move |cx| {
959 let this = this.root();
960
961 {
966 let mut current_request =
967 this.current_request.safe_borrow_mut(cx.no_gc());
968 current_request.source_url = Some(selected_source);
969 current_request.parsed_url = Some(image_url);
970 current_request.load_time = Some(CrossProcessInstant::now());
971 }
972
973 this.fire_event_including_host(cx, atom!("load"));
977 }));
978
979 return;
981 }
982 }
983 }
984
985 let task = ImageElementMicrotask::UpdateImageData {
988 elem: Dom::from_ref(self),
989 generation: self.generation.get(),
990 };
991
992 ScriptThread::await_stable_state(cx, Box::new(task));
993 }
994
995 pub(crate) fn react_to_environment_changes(&self, cx: &JSContext) {
997 let task = ImageElementMicrotask::EnvironmentChanges {
999 elem: Dom::from_ref(self),
1000 generation: self.generation.get(),
1001 };
1002
1003 ScriptThread::await_stable_state(cx, Box::new(task));
1004 }
1005
1006 fn react_to_environment_changes_sync_steps(&self, cx: &mut JSContext, generation: u32) {
1008 let document = self.owner_document();
1009 let has_pending_request = matches!(self.image_request.get(), ImageRequestPhase::Pending);
1010
1011 if !document.is_fully_active() || !self.uses_srcset_or_picture() || has_pending_request {
1015 return;
1016 }
1017
1018 let Some((selected_source, selected_pixel_density)) = self
1021 .source_set
1022 .safe_borrow_mut(cx.no_gc())
1023 .select_image_source(cx.no_gc(), self.upcast::<Element>())
1024 else {
1025 return;
1027 };
1028
1029 let mut same_selected_source = self
1032 .last_selected_source
1033 .borrow()
1034 .as_ref()
1035 .is_some_and(|source| *source == selected_source);
1036
1037 same_selected_source = same_selected_source ||
1041 self.current_request
1042 .borrow()
1043 .source_url
1044 .as_ref()
1045 .is_some_and(|source| *source == selected_source);
1046
1047 let same_selected_pixel_density = self
1048 .current_request
1049 .borrow()
1050 .current_pixel_density
1051 .is_some_and(|pixel_density| pixel_density == selected_pixel_density);
1052
1053 if same_selected_source && same_selected_pixel_density {
1054 return;
1055 }
1056
1057 let Ok(image_url) = document.encoding_parse_a_url(&selected_source) else {
1061 return;
1062 };
1063
1064 self.image_request.set(ImageRequestPhase::Pending);
1066 self.init_pending_image_request(cx, &self.pending_request, &image_url, &selected_source);
1067
1068 let window = self.owner_window();
1071 let cache_result = window.image_cache().get_cached_image_status(
1072 image_url.clone(),
1073 window.origin().immutable(),
1074 cors_setting_for_element(self.upcast()),
1075 );
1076
1077 let change_type = ChangeType::Environment {
1078 selected_source: selected_source.clone(),
1079 selected_pixel_density,
1080 };
1081
1082 match cache_result {
1083 ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable { .. }) => {
1084 self.finish_reacting_to_environment_change(
1085 selected_source,
1086 generation,
1087 selected_pixel_density,
1088 );
1089 },
1090 ImageCacheResult::Available(ImageOrMetadataAvailable::MetadataAvailable(m, id)) => {
1091 self.process_image_response_for_environment_change(
1092 cx,
1093 ImageResponse::MetadataLoaded(m),
1094 selected_source,
1095 generation,
1096 selected_pixel_density,
1097 );
1098 self.register_image_cache_callback(id, change_type);
1099 },
1100 ImageCacheResult::FailedToLoadOrDecode => {
1101 self.process_image_response_for_environment_change(
1102 cx,
1103 ImageResponse::FailedToLoadOrDecode,
1104 selected_source,
1105 generation,
1106 selected_pixel_density,
1107 );
1108 },
1109 ImageCacheResult::ReadyForRequest(id) => {
1110 self.fetch_request(&image_url, id);
1111 self.register_image_cache_callback(id, change_type);
1112 },
1113 ImageCacheResult::Pending(id) => {
1114 self.register_image_cache_callback(id, change_type);
1115 },
1116 }
1117 }
1118
1119 fn react_to_decode_image_sync_steps(&self, cx: &mut JSContext, promise: &RootedPromise) {
1121 if !self.owner_document().is_fully_active() ||
1125 matches!(self.current_request.borrow().state, State::Broken)
1126 {
1127 promise.reject_error(cx, Error::Encoding(Some("Image element's owner document is not fully active or image element's request is broken".into())));
1128 } else if matches!(
1129 self.current_request.borrow().state,
1130 State::CompletelyAvailable
1131 ) {
1132 promise.resolve_native(cx, &());
1134 } else if matches!(self.current_request.borrow().state, State::Unavailable) &&
1135 self.current_request.borrow().source_url.is_none()
1136 {
1137 promise.reject_error(
1142 cx,
1143 Error::Encoding(Some(
1144 "Image element does not provide `src` or `srcset` attributes".into(),
1145 )),
1146 );
1147 } else {
1148 self.image_decode_promises
1149 .safe_borrow_mut(cx.no_gc())
1150 .push(promise.to_traced());
1151 }
1152 }
1153
1154 fn resolve_image_decode_promises(&self, cx: &mut JSContext) {
1156 if self.image_decode_promises.borrow().is_empty() {
1157 return;
1158 }
1159
1160 let trusted_image_decode_promises: Vec<TrustedPromise> = self
1163 .image_decode_promises
1164 .borrow()
1165 .iter()
1166 .map(|promise| TrustedPromise::from(&promise.root(cx)))
1167 .collect();
1168
1169 self.image_decode_promises
1170 .safe_borrow_mut(cx.no_gc())
1171 .clear();
1172
1173 self.owner_global()
1174 .task_manager()
1175 .dom_manipulation_task_source()
1176 .queue(task!(fulfill_image_decode_promises: move |cx| {
1177 for trusted_promise in trusted_image_decode_promises {
1178 trusted_promise.root(cx).resolve_native(cx, &());
1179 }
1180 }));
1181 }
1182
1183 fn reject_image_decode_promises(&self, cx: &mut JSContext) {
1185 if self.image_decode_promises.borrow().is_empty() {
1186 return;
1187 }
1188
1189 let trusted_image_decode_promises: Vec<TrustedPromise> = self
1192 .image_decode_promises
1193 .borrow()
1194 .iter()
1195 .map(|promise| TrustedPromise::from(&promise.root(cx)))
1196 .collect();
1197
1198 self.image_decode_promises
1199 .safe_borrow_mut(cx.no_gc())
1200 .clear();
1201
1202 self.owner_global()
1203 .task_manager()
1204 .dom_manipulation_task_source()
1205 .queue(task!(reject_image_decode_promises: move |cx| {
1206 for trusted_promise in trusted_image_decode_promises {
1207 trusted_promise.root(cx).reject_error(cx, Error::Encoding(Some("Image could not be decoded".into())));
1208 }
1209 }));
1210 }
1211
1212 fn finish_reacting_to_environment_change(
1214 &self,
1215 selected_source: USVString,
1216 generation: u32,
1217 selected_pixel_density: f64,
1218 ) {
1219 let this = Trusted::new(self);
1222
1223 self.owner_global()
1224 .task_manager()
1225 .dom_manipulation_task_source()
1226 .queue(task!(image_load_event: move |cx| {
1227 let this = this.root();
1228
1229 if this.generation.get() != generation {
1232 this.abort_request(cx, State::Unavailable, ImageRequestPhase::Pending);
1233 this.image_request.set(ImageRequestPhase::Current);
1234 return;
1235 }
1236
1237 *this.last_selected_source.safe_borrow_mut(cx.no_gc()) = Some(selected_source);
1240
1241
1242 if let Some(pending_request) = &mut *this.pending_request.safe_borrow_mut(cx.no_gc()) {
1243 pending_request.state = State::CompletelyAvailable;
1245
1246 pending_request.current_pixel_density = Some(selected_pixel_density);
1247
1248 } else {
1253 log::error!("Pending request was null");
1254 return
1255 }
1256 *this.current_request.safe_borrow_mut(cx.no_gc()) = this.pending_request.safe_borrow_mut(cx.no_gc()).take().expect("Should have a pending request");
1257
1258
1259 this.abort_request(cx, State::Unavailable, ImageRequestPhase::Pending);
1260 this.image_request.set(ImageRequestPhase::Current);
1261
1262 this.upcast::<Node>().dirty(cx.no_gc(), NodeDamage::Other);
1264
1265 this.upcast::<EventTarget>().fire_event(cx, atom!("load"));
1267 }));
1268 }
1269
1270 fn uses_srcset_or_picture(&self) -> bool {
1272 let element = self.upcast::<Element>();
1273
1274 let has_srcset_attribute = element.has_attribute(&local_name!("srcset"));
1275 let has_parent_picture = element
1276 .upcast::<Node>()
1277 .GetParentElement()
1278 .is_some_and(|parent| parent.is::<HTMLPictureElement>());
1279 has_srcset_attribute || has_parent_picture
1280 }
1281
1282 fn new_inherited(
1283 local_name: LocalName,
1284 prefix: Option<Prefix>,
1285 document: &Document,
1286 creator: ElementCreator,
1287 ) -> HTMLImageElement {
1288 HTMLImageElement {
1289 htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
1290 image_request: Cell::new(ImageRequestPhase::Current),
1291 current_request: AtomicRefCell::new(Box::new(ImageRequest {
1292 state: State::Unavailable,
1293 parsed_url: None,
1294 source_url: None,
1295 image: None,
1296 metadata: None,
1297 blocker: DomRefCell::new(None),
1298 final_url: None,
1299 load_time: None,
1300 current_pixel_density: None,
1301 })),
1302 pending_request: DomRefCell::new(None),
1303 form_owner: Default::default(),
1304 generation: Default::default(),
1305 source_set: DomRefCell::new(SourceSet::new()),
1306 dimension_attribute_source: Default::default(),
1307 last_selected_source: DomRefCell::new(None),
1308 image_decode_promises: DomRefCell::new(vec![]),
1309 line_number: creator.return_line_number(),
1310 }
1311 }
1312
1313 pub(crate) fn new(
1314 cx: &mut JSContext,
1315 local_name: LocalName,
1316 prefix: Option<Prefix>,
1317 document: &Document,
1318 proto: Option<HandleObject>,
1319 creator: ElementCreator,
1320 ) -> DomRoot<HTMLImageElement> {
1321 let image_element = Node::reflect_node_with_proto(
1322 cx,
1323 Box::new(HTMLImageElement::new_inherited(
1324 local_name, prefix, document, creator,
1325 )),
1326 document,
1327 proto,
1328 );
1329 image_element
1330 .dimension_attribute_source
1331 .set(Some(image_element.upcast()));
1332 image_element
1333 }
1334
1335 pub(crate) fn areas(&self) -> Option<Vec<DomRoot<HTMLAreaElement>>> {
1336 let elem = self.upcast::<Element>();
1337 let value = elem.get_attribute_string_value(&local_name!("usemap"))?;
1338
1339 if value.is_empty() || !value.is_char_boundary(1) {
1340 return None;
1341 }
1342
1343 let (first, last) = value.split_at(1);
1344
1345 if first != "#" || last.is_empty() {
1346 return None;
1347 }
1348
1349 let useMapElements = self
1350 .owner_document()
1351 .upcast::<Node>()
1352 .traverse_preorder(ShadowIncluding::No)
1353 .filter_map(DomRoot::downcast::<HTMLMapElement>)
1354 .find(|n| {
1355 n.upcast::<Element>()
1356 .get_name()
1357 .is_some_and(|n| *n == *last)
1358 });
1359
1360 useMapElements.map(|mapElem| mapElem.get_area_elements())
1361 }
1362
1363 pub(crate) fn same_origin(&self, origin: &MutableOrigin) -> bool {
1364 if let Some(ref image) = self.current_request.borrow().image {
1365 return image.cors_status() == CorsStatus::Safe;
1366 }
1367
1368 self.current_request
1369 .borrow()
1370 .final_url
1371 .as_ref()
1372 .is_some_and(|url| url.scheme() == "data" || url.origin().same_origin(origin))
1373 }
1374
1375 fn generation_id(&self) -> u32 {
1376 self.generation.get()
1377 }
1378
1379 fn load_broken_image_icon(&self, no_gc: &NoGC) {
1380 let window = self.owner_window();
1381 let Some(broken_image_icon) = window.image_cache().get_broken_image_icon() else {
1382 return;
1383 };
1384
1385 self.current_request.safe_borrow_mut(no_gc).metadata = Some(broken_image_icon.metadata);
1386 self.current_request.safe_borrow_mut(no_gc).image = Some(Image::Raster(broken_image_icon));
1387 self.upcast::<Node>().dirty(no_gc, NodeDamage::Other);
1388 }
1389
1390 pub(crate) fn full_image_url_for_user_interface(&self) -> Option<ServoUrl> {
1393 self.owner_document()
1394 .base_url()
1395 .join(&self.CurrentSrc())
1396 .ok()
1397 }
1398
1399 pub(crate) fn set_dimension_attribute_source(&self, no_gc: &NoGC, value: Option<&Element>) {
1400 self.dimension_attribute_source.set(value);
1401 self.upcast::<Node>().dirty(no_gc, NodeDamage::Other);
1402 }
1403
1404 fn image_is_available(&self) -> bool {
1406 matches!(
1409 self.current_request.borrow().state,
1410 State::PartiallyAvailable | State::CompletelyAvailable
1411 )
1412 }
1413
1414 fn dimensions(&self, cx: &mut JSContext) -> (u32, u32) {
1416 let natural_dimensions = if self.image_is_available() {
1419 Some(self.density_corrected_natural_width_and_height())
1420 } else {
1421 None
1423 };
1424 Self::determine_dimensions(cx, self.upcast(), natural_dimensions)
1426 }
1427
1428 pub(crate) fn determine_dimensions(
1430 cx: &mut JSContext,
1431 element: &Element,
1432 natural_dimensions: Option<(u32, u32)>,
1433 ) -> (u32, u32) {
1434 if let Some(content_box) = element.upcast::<Node>().content_box(cx) {
1437 return (
1438 content_box.size.width.to_px() as u32,
1439 content_box.size.height.to_px() as u32,
1440 );
1441 }
1442 let (mut width, mut height) = natural_dimensions.unwrap_or_default();
1446 if let Some(width_value) = element.get_attribute_string_ref(&local_name!("width")) {
1448 if let Ok(attribute_width) =
1451 parse_unsigned_integer(width_value.as_attr_ref().value().as_ref().chars())
1452 {
1453 width = attribute_width;
1454 }
1455 }
1456 if let Some(height_value) = element.get_attribute_string_ref(&local_name!("height")) {
1458 if let Ok(attribute_height) =
1461 parse_unsigned_integer(height_value.as_attr_ref().value().as_ref().chars())
1462 {
1463 height = attribute_height;
1464 }
1465 }
1466 (width, height)
1468 }
1469
1470 fn density_corrected_natural_width_and_height(&self) -> (u32, u32) {
1472 let current_request = self.current_request.borrow();
1474 let density = current_request.current_pixel_density.unwrap_or(1f64);
1475
1476 let intrinsic_width = current_request
1492 .metadata
1493 .map(|metadata| (metadata.width as f64 / density) as u32)
1494 .unwrap_or(300);
1495 let intrinsic_height = current_request
1496 .metadata
1497 .map(|metadata| (metadata.height as f64 / density) as u32)
1498 .unwrap_or(150);
1499 (intrinsic_width, intrinsic_height)
1500 }
1501}
1502
1503#[derive(JSTraceable, MallocSizeOf)]
1504pub(crate) enum ImageElementMicrotask {
1505 UpdateImageData {
1506 elem: Dom<HTMLImageElement>,
1507 generation: u32,
1508 },
1509 EnvironmentChanges {
1510 elem: Dom<HTMLImageElement>,
1511 generation: u32,
1512 },
1513 Decode {
1514 elem: Dom<HTMLImageElement>,
1515 promise: TracedPromise,
1516 },
1517}
1518
1519impl MicrotaskRunnable for ImageElementMicrotask {
1520 fn handler(&self, cx: &mut JSContext) {
1521 let mut realm = match self {
1522 &ImageElementMicrotask::UpdateImageData { ref elem, .. } |
1523 &ImageElementMicrotask::EnvironmentChanges { ref elem, .. } |
1524 &ImageElementMicrotask::Decode { ref elem, .. } => enter_auto_realm(cx, &**elem),
1525 };
1526 let cx = &mut realm;
1527 match *self {
1528 ImageElementMicrotask::UpdateImageData {
1529 ref elem,
1530 ref generation,
1531 } => {
1532 if elem.generation.get() == *generation {
1536 elem.update_the_image_data_sync_steps(cx);
1537 }
1538 },
1539 ImageElementMicrotask::EnvironmentChanges {
1540 ref elem,
1541 ref generation,
1542 } => {
1543 elem.react_to_environment_changes_sync_steps(cx, *generation);
1544 },
1545 ImageElementMicrotask::Decode {
1546 ref elem,
1547 ref promise,
1548 } => {
1549 let promise = promise.root(cx);
1550 elem.react_to_decode_image_sync_steps(cx, &promise);
1551 },
1552 }
1553 }
1554}
1555
1556impl<'dom> LayoutDom<'dom, HTMLImageElement> {
1557 fn current_request(self) -> AtomicRef<'dom, ImageRequest> {
1558 AtomicRef::map(self.unsafe_get().current_request.borrow(), |request| {
1559 &**request
1560 })
1561 }
1562
1563 #[expect(unsafe_code)]
1564 fn dimension_attribute_source(self) -> LayoutDom<'dom, Element> {
1565 unsafe {
1566 self.unsafe_get()
1567 .dimension_attribute_source
1568 .to_layout()
1569 .expect("dimension attribute source should be always non-null")
1570 }
1571 }
1572
1573 pub(crate) fn image_url(self) -> Option<ServoUrl> {
1574 self.current_request().parsed_url.clone()
1575 }
1576
1577 pub(crate) fn image_data(self) -> (Option<Image>, Option<ImageMetadata>) {
1578 let current_request = self.current_request();
1579 (current_request.image.clone(), current_request.metadata)
1580 }
1581
1582 pub(crate) fn load_time(self) -> Option<CrossProcessInstant> {
1583 self.current_request().load_time
1584 }
1585
1586 pub(crate) fn image_density(self) -> Option<f64> {
1587 self.current_request().current_pixel_density
1588 }
1589
1590 pub(crate) fn showing_broken_image_icon(self) -> bool {
1591 matches!(self.current_request().state, State::Broken)
1592 }
1593
1594 pub(crate) fn width(self) -> LengthOrPercentageOrAuto {
1595 self.dimension_attribute_source()
1596 .dimension_attr_value(local_name!("width"))
1597 }
1598
1599 pub(crate) fn height(self) -> LengthOrPercentageOrAuto {
1600 self.dimension_attribute_source()
1601 .dimension_attr_value(local_name!("height"))
1602 }
1603
1604 pub(crate) fn margin_right_left(self) -> LengthOrPercentageOrAuto {
1605 self.dimension_attribute_source()
1606 .dimension_attr_value(local_name!("hspace"))
1607 }
1608
1609 pub(crate) fn margin_top_bottom(self) -> LengthOrPercentageOrAuto {
1610 self.dimension_attribute_source()
1611 .dimension_attr_value(local_name!("vspace"))
1612 }
1613}
1614
1615impl HTMLImageElementMethods<crate::DomTypeHolder> for HTMLImageElement {
1616 fn Image(
1618 cx: &mut JSContext,
1619 window: &Window,
1620 proto: Option<HandleObject>,
1621 width: Option<u32>,
1622 height: Option<u32>,
1623 ) -> Fallible<DomRoot<HTMLImageElement>> {
1624 let document = window.Document();
1626
1627 let element = Element::create(
1630 cx,
1631 QualName::new(None, ns!(html), local_name!("img")),
1632 None,
1633 &document,
1634 ElementCreator::ScriptCreated,
1635 CustomElementCreationMode::Synchronous,
1636 proto,
1637 );
1638
1639 let image = DomRoot::downcast::<HTMLImageElement>(element).unwrap();
1640
1641 if let Some(w) = width {
1643 image.SetWidth(cx, w);
1644 }
1645
1646 if let Some(h) = height {
1649 image.SetHeight(cx, 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, cx: &mut JSContext, value: Option<DOMString>) {
1685 set_cross_origin_attribute(cx, self.upcast::<Element>(), value);
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, cx: &mut JSContext) -> u32 {
1700 self.dimensions(cx).0
1702 }
1703
1704 make_dimension_uint_setter!(SetWidth, "width");
1706
1707 fn Height(&self, cx: &mut JSContext) -> u32 {
1709 self.dimensions(cx).1
1711 }
1712
1713 make_dimension_uint_setter!(SetHeight, "height");
1715
1716 fn NaturalWidth(&self) -> u32 {
1718 if !self.image_is_available() {
1720 return 0;
1721 }
1722
1723 self.density_corrected_natural_width_and_height().0
1726 }
1727
1728 fn NaturalHeight(&self) -> u32 {
1730 if !self.image_is_available() {
1732 return 0;
1733 }
1734
1735 self.density_corrected_natural_width_and_height().1
1738 }
1739
1740 fn Complete(&self) -> bool {
1742 let element = self.upcast::<Element>();
1743
1744 let has_srcset_attribute = element.has_attribute(&local_name!("srcset"));
1747 if !element.has_attribute(&local_name!("src")) && !has_srcset_attribute {
1748 return true;
1749 }
1750
1751 let src = element.get_string_attribute(&local_name!("src"));
1753 if !has_srcset_attribute && src.is_empty() {
1754 return true;
1755 }
1756
1757 if matches!(self.image_request.get(), ImageRequestPhase::Current) &&
1761 matches!(
1762 self.current_request.borrow().state,
1763 State::CompletelyAvailable | State::Broken
1764 )
1765 {
1766 return true;
1767 }
1768
1769 false
1771 }
1772
1773 fn CurrentSrc(&self) -> USVString {
1775 let current_request = self.current_request.borrow();
1776 let url = ¤t_request.parsed_url;
1777 match *url {
1778 Some(ref url) => USVString(url.clone().into_string()),
1779 None => {
1780 let unparsed_url = ¤t_request.source_url;
1781 match *unparsed_url {
1782 Some(ref url) => url.clone(),
1783 None => USVString(String::new()),
1784 }
1785 },
1786 }
1787 }
1788
1789 fn ReferrerPolicy(&self) -> DOMString {
1791 reflect_referrer_policy_attribute(self.upcast::<Element>())
1792 }
1793
1794 make_setter!(SetReferrerPolicy, "referrerpolicy");
1796
1797 fn Decode(&self, cx: &mut JSContext) -> RootedPromise {
1799 let promise = Promise::new(cx, &self.global());
1801
1802 let task = ImageElementMicrotask::Decode {
1804 elem: Dom::from_ref(self),
1805 promise: promise.to_traced(),
1806 };
1807
1808 ScriptThread::await_stable_state(cx, Box::new(task));
1809
1810 promise
1812 }
1813
1814 make_getter!(Name, "name");
1816
1817 make_atomic_setter!(SetName, "name");
1819
1820 make_getter!(Align, "align");
1822
1823 make_setter!(SetAlign, "align");
1825
1826 make_dimension_uint_getter!(Hspace, "hspace");
1828
1829 make_dimension_uint_setter!(SetHspace, "hspace");
1831
1832 make_dimension_uint_getter!(Vspace, "vspace");
1834
1835 make_dimension_uint_setter!(SetVspace, "vspace");
1837
1838 make_url_getter!(LongDesc, "longdesc");
1840
1841 make_url_setter!(SetLongDesc, "longdesc");
1843
1844 make_getter!(Border, "border");
1846
1847 make_setter!(SetBorder, "border");
1849}
1850
1851impl VirtualMethods for HTMLImageElement {
1852 fn super_type(&self) -> Option<&dyn VirtualMethods> {
1853 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
1854 }
1855
1856 fn adopting_steps(&self, cx: &mut JSContext, old_doc: &Document) {
1857 self.super_type().unwrap().adopting_steps(cx, old_doc);
1858 self.update_the_image_data(cx);
1859 }
1860
1861 fn attribute_mutated(
1862 &self,
1863 cx: &mut JSContext,
1864 attr: AttrRef<'_>,
1865 mutation: AttributeMutation,
1866 ) {
1867 self.super_type()
1868 .unwrap()
1869 .attribute_mutated(cx, attr, mutation);
1870
1871 match attr.local_name() {
1872 &local_name!("src") | &local_name!("srcset") | &local_name!("sizes") => {
1885 self.update_the_image_data(cx);
1886 },
1887 &local_name!("crossorigin") => {
1888 let cross_origin_state_changed = match mutation {
1891 AttributeMutation::Removed | AttributeMutation::Set(None) => true,
1892 AttributeMutation::Set(Some(old_value)) => {
1893 let new_cors_setting =
1894 CorsSettings::from_enumerated_attribute(&attr.value());
1895 let old_cors_setting = CorsSettings::from_enumerated_attribute(old_value);
1896
1897 new_cors_setting != old_cors_setting
1898 },
1899 };
1900
1901 if cross_origin_state_changed {
1902 self.update_the_image_data(cx);
1903 }
1904 },
1905 &local_name!("referrerpolicy") => {
1906 let referrer_policy_state_changed = match mutation {
1909 AttributeMutation::Removed | AttributeMutation::Set(None) => {
1910 ReferrerPolicy::from(&**attr.value()) != ReferrerPolicy::EmptyString
1911 },
1912 AttributeMutation::Set(Some(old_value)) => {
1913 ReferrerPolicy::from(&**attr.value()) != ReferrerPolicy::from(&**old_value)
1914 },
1915 };
1916
1917 if referrer_policy_state_changed {
1918 self.update_the_image_data(cx);
1919 }
1920 },
1921 _ => {},
1922 }
1923 }
1924
1925 fn attribute_affects_presentational_hints(&self, attr: AttrRef<'_>) -> bool {
1926 match attr.local_name() {
1927 &local_name!("width") |
1928 &local_name!("height") |
1929 &local_name!("hspace") |
1930 &local_name!("vspace") => true,
1931 _ => self
1932 .super_type()
1933 .unwrap()
1934 .attribute_affects_presentational_hints(attr),
1935 }
1936 }
1937
1938 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
1939 match name {
1940 &local_name!("width") |
1941 &local_name!("height") |
1942 &local_name!("hspace") |
1943 &local_name!("vspace") => AttrValue::from_dimension(value.into()),
1944 _ => self
1945 .super_type()
1946 .unwrap()
1947 .parse_plain_attribute(name, value),
1948 }
1949 }
1950
1951 fn handle_event(&self, cx: &mut JSContext, event: &Event) {
1952 if event.type_() != atom!("click") {
1953 return;
1954 }
1955
1956 let Some(area_elements) = self.areas() else {
1957 return;
1958 };
1959
1960 let Some(mouse_event) = event.downcast::<MouseEvent>() else {
1962 return;
1963 };
1964
1965 let click_location = Point2D::new(
1966 mouse_event.ClientX().to_f32().unwrap(),
1967 mouse_event.ClientY().to_f32().unwrap(),
1968 );
1969 let bounding_rectangle = self.upcast::<Element>().GetBoundingClientRect(cx);
1970 let image_extents =
1971 Point2D::new(bounding_rectangle.X() as f32, bounding_rectangle.Y() as f32);
1972
1973 for area_element in area_elements {
1975 if !area_element.is_instance_activatable() {
1976 continue;
1977 }
1978 let activatable_area = match area_element.get_shape_from_coords() {
1979 Some(shape) => shape.absolute_coords(image_extents),
1980 None => return,
1981 };
1982 if activatable_area.hit_test(&click_location) {
1983 area_element.activation_behavior(cx, event, self.upcast());
1984 return;
1985 }
1986 }
1987 }
1988
1989 fn bind_to_tree(&self, cx: &mut JSContext, context: &BindContext) {
1991 if let Some(s) = self.super_type() {
1992 s.bind_to_tree(cx, context);
1993 }
1994 let document = self.owner_document();
1995 if context.tree_connected {
1996 document.register_responsive_image(self);
1997 }
1998
1999 let parent = self.upcast::<Node>().GetParentNode().unwrap();
2000
2001 if parent.is::<HTMLPictureElement>() && *parent == *context.parent {
2004 self.update_the_image_data(cx);
2005 }
2006 }
2007
2008 fn unbind_from_tree(&self, cx: &mut JSContext, context: &UnbindContext) {
2010 self.super_type().unwrap().unbind_from_tree(cx, context);
2011 let document = self.owner_document();
2012 document.unregister_responsive_image(self);
2013
2014 if context.parent.is::<HTMLPictureElement>() && !self.upcast::<Node>().has_parent() {
2017 self.update_the_image_data(cx);
2018 }
2019 }
2020
2021 fn moving_steps(&self, cx: &mut JSContext, context: &MoveContext) {
2023 if let Some(super_type) = self.super_type() {
2024 super_type.moving_steps(cx, context);
2025 }
2026
2027 if context.is_subtree_root && context.old_ancestor.is::<HTMLPictureElement>() {
2030 self.update_the_image_data(cx);
2031 }
2032 }
2033}
2034
2035impl FormControl for HTMLImageElement {
2036 fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
2037 self.form_owner.get()
2038 }
2039
2040 fn set_form_owner(&self, _cx: &mut JSContext, form: Option<&HTMLFormElement>) {
2041 self.form_owner.set(form);
2042 }
2043
2044 fn to_html_element(&self) -> &HTMLElement {
2045 self.upcast::<HTMLElement>()
2046 }
2047}
2048
2049#[derive(Clone)]
2050enum ChangeType {
2051 Environment {
2052 selected_source: USVString,
2053 selected_pixel_density: f64,
2054 },
2055 Element,
2056}