1#![cfg_attr(crown, allow(crown::jscontext_first_arg))]
6
7use std::cell::Cell;
8use std::rc::Rc;
9use std::sync::Arc;
10
11use bytes::Bytes;
12use dom_struct::dom_struct;
13use euclid::default::Point2D;
14use html5ever::{LocalName, Prefix, QualName, local_name, ns};
15use js::context::{JSContext, NoGC};
16use js::rust::HandleObject;
17use mime::{self, Mime};
18use net_traits::image_cache::{
19 Image, ImageCache, ImageCacheResult, ImageLoadListener, ImageOrMetadataAvailable,
20 ImageResponse, PendingImageId,
21};
22use net_traits::request::{CorsSettings, Destination, Initiator, RequestId};
23use net_traits::{
24 FetchMetadata, FetchResponseMsg, NetworkError, ReferrerPolicy, ResourceFetchTiming,
25};
26use num_traits::ToPrimitive;
27use pixels::{CorsStatus, ImageMetadata, Snapshot};
28use script_bindings::cell::DomRefCell;
29use servo_url::ServoUrl;
30use servo_url::origin::MutableOrigin;
31use style::attr::{AttrValue, LengthOrPercentageOrAuto};
32
33use crate::dom::activation::Activatable;
34use crate::dom::bindings::codegen::Bindings::DOMRectBinding::DOMRect_Binding::DOMRectMethods;
35use crate::dom::bindings::codegen::Bindings::ElementBinding::Element_Binding::ElementMethods;
36use crate::dom::bindings::codegen::Bindings::HTMLImageElementBinding::HTMLImageElementMethods;
37use crate::dom::bindings::codegen::Bindings::MouseEventBinding::MouseEventMethods;
38use crate::dom::bindings::codegen::Bindings::NodeBinding::Node_Binding::NodeMethods;
39use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
40use crate::dom::bindings::error::{Error, Fallible};
41use crate::dom::bindings::inheritance::Castable;
42use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
43use crate::dom::bindings::reflector::DomGlobal;
44use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom, ToLayoutOptional};
45use crate::dom::bindings::str::{DOMString, USVString};
46use crate::dom::csp::{GlobalCspReporting, Violation};
47use crate::dom::document::Document;
48use crate::dom::element::attributes::storage::AttrRef;
49use crate::dom::element::{
50 AttributeMutation, CustomElementCreationMode, Element, ElementCreator,
51 cors_setting_for_element, referrer_policy_for_element, reflect_cross_origin_attribute,
52 reflect_referrer_policy_attribute, set_cross_origin_attribute,
53};
54use crate::dom::event::Event;
55use crate::dom::eventtarget::EventTarget;
56use crate::dom::globalscope::GlobalScope;
57use crate::dom::html::htmlareaelement::HTMLAreaElement;
58use crate::dom::html::htmlelement::HTMLElement;
59use crate::dom::html::htmlformelement::{FormControl, HTMLFormElement};
60use crate::dom::html::htmlmapelement::HTMLMapElement;
61use crate::dom::html::htmlpictureelement::HTMLPictureElement;
62use crate::dom::iterators::ShadowIncluding;
63use crate::dom::mouseevent::MouseEvent;
64use crate::dom::node::virtualmethods::VirtualMethods;
65use crate::dom::node::{BindContext, MoveContext, Node, NodeDamage, NodeTraits, UnbindContext};
66use crate::dom::performance::performanceresourcetiming::InitiatorType;
67use crate::dom::promise::Promise;
68use crate::dom::srcset::SourceSet;
69use crate::dom::window::Window;
70use crate::event_loop::document_loader::{LoadBlocker, LoadType};
71use crate::event_loop::script_thread::ScriptThread;
72use crate::fetch::fetch::{RequestWithGlobalScope, create_a_potential_cors_request};
73use crate::fetch::network_listener::{self, FetchResponseListener, ResourceTimingListener};
74use crate::realms::enter_auto_realm;
75use crate::runtime::job_queue::MicrotaskRunnable;
76
77#[derive(Clone, Copy, Default, JSTraceable, MallocSizeOf)]
79enum State {
80 #[default]
81 Unavailable,
82 PartiallyAvailable,
83 CompletelyAvailable,
84 Broken,
85}
86
87#[derive(Clone, Copy, JSTraceable, MallocSizeOf)]
88enum ImageRequestPhase {
89 Pending,
90 Current,
91}
92
93#[derive(Default, JSTraceable, MallocSizeOf)]
95#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
96struct ImageRequest {
97 state: State,
98 #[no_trace]
99 parsed_url: Option<ServoUrl>,
100 source_url: Option<USVString>,
101 blocker: DomRefCell<Option<LoadBlocker>>,
102 #[no_trace]
103 image: Option<Image>,
104 #[no_trace]
105 metadata: Option<ImageMetadata>,
106 #[no_trace]
107 final_url: Option<ServoUrl>,
108 current_pixel_density: Option<f64>,
109}
110
111#[dom_struct]
112pub(crate) struct HTMLImageElement {
113 htmlelement: HTMLElement,
114 current_request: DomRefCell<ImageRequest>,
115 pending_request: DomRefCell<Option<ImageRequest>>,
116 form_owner: MutNullableDom<HTMLFormElement>,
117 source_set: DomRefCell<SourceSet>,
118 dimension_attribute_source: MutNullableDom<Element>,
121 last_selected_source: DomRefCell<Option<USVString>>,
123 #[conditional_malloc_size_of]
124 image_decode_promises: DomRefCell<Vec<Rc<Promise>>>,
125 line_number: u64,
127 image_request: Cell<ImageRequestPhase>,
128 generation: Cell<u32>,
129}
130
131impl HTMLImageElement {
132 pub(crate) fn is_usable(&self) -> Fallible<bool> {
134 if let Some(image) = &self.current_request.borrow().image {
136 let intrinsic_size = image.metadata();
137 if intrinsic_size.width == 0 || intrinsic_size.height == 0 {
138 return Ok(false);
139 }
140 }
141
142 match self.current_request.borrow().state {
143 State::Broken => Err(Error::InvalidState(Some(
145 "Image element's state is broken".into(),
146 ))),
147 State::CompletelyAvailable => Ok(true),
148 State::PartiallyAvailable | State::Unavailable => Ok(false),
150 }
151 }
152
153 pub(crate) fn image_data(&self) -> Option<Image> {
154 self.current_request.borrow().image.clone()
155 }
156
157 pub(crate) fn get_raster_image_data(&self) -> Option<Snapshot> {
159 let Some(raster_image) = self.image_data()?.as_raster_image() else {
160 warn!("Vector image is not supported as raster image source");
161 return None;
162 };
163 Some(raster_image.as_snapshot())
164 }
165}
166
167struct ImageContext {
169 image_cache: Arc<dyn ImageCache>,
171 status: Result<(), NetworkError>,
173 id: PendingImageId,
175 aborted: bool,
177 doc: Trusted<Document>,
179 url: ServoUrl,
180 element: Trusted<HTMLImageElement>,
181}
182
183impl FetchResponseListener for ImageContext {
184 fn should_invoke(&self) -> bool {
185 !self.aborted
186 }
187
188 fn process_request_body(&mut self, _: RequestId) {}
189
190 fn process_response(
191 &mut self,
192 _: &mut js::context::JSContext,
193 request_id: RequestId,
194 metadata: Result<FetchMetadata, NetworkError>,
195 ) {
196 debug!("got {:?} for {:?}", metadata.as_ref().map(|_| ()), self.url);
197 self.image_cache.notify_pending_response(
198 self.id,
199 FetchResponseMsg::ProcessResponse(request_id, metadata.clone()),
200 );
201
202 let metadata = metadata.ok().map(|meta| match meta {
203 FetchMetadata::Unfiltered(m) => m,
204 FetchMetadata::Filtered { unsafe_, .. } => unsafe_,
205 });
206
207 if let Some(metadata) = metadata.as_ref() &&
209 let Some(ref content_type) = metadata.content_type
210 {
211 let mime: Mime = content_type.clone().into_inner().into();
212 if mime.type_() == mime::MULTIPART && mime.subtype().as_str() == "x-mixed-replace" {
213 self.aborted = true;
214 }
215 }
216
217 self.status = match metadata.as_ref().map(|m| m.status.clone()) {
220 None => Err(NetworkError::ResourceLoadError(
221 "No http status code received".to_owned(),
222 )),
223 Some(_) => Ok(()),
224 };
225 }
226
227 fn process_response_chunk(
228 &mut self,
229 _: &mut js::context::JSContext,
230 request_id: RequestId,
231 payload: Bytes,
232 ) {
233 if self.status.is_ok() {
234 self.image_cache.notify_pending_response(
235 self.id,
236 FetchResponseMsg::ProcessResponseChunk(request_id, payload),
237 );
238 }
239 }
240
241 fn process_response_eof(
242 self,
243 cx: &mut js::context::JSContext,
244 request_id: RequestId,
245 response: Result<(), NetworkError>,
246 timing: ResourceFetchTiming,
247 ) {
248 self.image_cache.notify_pending_response(
249 self.id,
250 FetchResponseMsg::ProcessResponseEOF(request_id, response.clone(), timing.clone()),
251 );
252 network_listener::submit_timing(cx, &self, &response, &timing);
253 }
254
255 fn process_csp_violations(
256 &mut self,
257 cx: &mut js::context::JSContext,
258 _request_id: RequestId,
259 violations: Vec<Violation>,
260 ) {
261 let global = &self.resource_timing_global();
262 let elem = self.element.root();
263 let source_position = elem
264 .upcast::<Element>()
265 .compute_source_position(elem.line_number as u32);
266 global.report_csp_violations(cx, violations, None, Some(source_position));
267 }
268
269 fn process_content_length(&mut self, request_id: RequestId, size: usize) {
270 self.image_cache.notify_pending_response(
271 self.id,
272 FetchResponseMsg::ProcessContentLength(request_id, size),
273 );
274 }
275}
276
277impl ResourceTimingListener for ImageContext {
278 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
279 (
280 InitiatorType::LocalName("img".to_string()),
281 self.url.clone(),
282 )
283 }
284
285 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
286 self.doc.root().global()
287 }
288}
289
290#[expect(non_snake_case)]
291impl HTMLImageElement {
292 fn fetch_image(&self, img_url: &ServoUrl, cx: &mut js::context::JSContext) {
294 let window = self.owner_window();
295
296 let cache_result = window.image_cache().get_cached_image_status(
297 img_url.clone(),
298 window.origin().immutable().clone(),
299 cors_setting_for_element(self.upcast()),
300 );
301
302 match cache_result {
303 ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable {
304 image,
305 url,
306 }) => self.process_image_response(ImageResponse::Loaded(image, url), cx),
307 ImageCacheResult::Available(ImageOrMetadataAvailable::MetadataAvailable(
308 metadata,
309 id,
310 )) => {
311 self.process_image_response(ImageResponse::MetadataLoaded(metadata), cx);
312 self.register_image_cache_callback(id, ChangeType::Element);
313 },
314 ImageCacheResult::Pending(id) => {
315 self.register_image_cache_callback(id, ChangeType::Element);
316 },
317 ImageCacheResult::ReadyForRequest(id) => {
318 self.fetch_request(img_url, id);
319 self.register_image_cache_callback(id, ChangeType::Element);
320 },
321 ImageCacheResult::FailedToLoadOrDecode => {
322 self.process_image_response(ImageResponse::FailedToLoadOrDecode, cx)
323 },
324 };
325 }
326
327 fn register_image_cache_callback(&self, id: PendingImageId, change_type: ChangeType) {
328 let trusted_node = Trusted::new(self);
329 let generation = self.generation_id();
330 let window = self.owner_window();
331 let callback = window.register_image_cache_listener(id, move |response, _| {
332 let trusted_node = trusted_node.clone();
333 let window = trusted_node.root().owner_window();
334 let callback_type = change_type.clone();
335
336 window
337 .as_global_scope()
338 .task_manager()
339 .networking_task_source()
340 .queue(task!(process_image_response: move |cx| {
341 let element = trusted_node.root();
342
343 if generation != element.generation_id() {
345 return;
346 }
347
348 match callback_type {
349 ChangeType::Element => {
350 element.process_image_response(response.response, cx);
351 }
352 ChangeType::Environment { selected_source, selected_pixel_density } => {
353 element.process_image_response_for_environment_change(
354 response.response, selected_source, generation, selected_pixel_density, cx
355 );
356 }
357 }
358 }));
359 });
360
361 window.image_cache().add_listener(ImageLoadListener::new(
362 callback,
363 window.pipeline_id(),
364 id,
365 ));
366 }
367
368 fn fetch_request(&self, img_url: &ServoUrl, id: PendingImageId) {
369 let document = self.owner_document();
370 let window = self.owner_window();
371
372 let context = ImageContext {
373 image_cache: window.image_cache(),
374 status: Ok(()),
375 id,
376 aborted: false,
377 doc: Trusted::new(&document),
378 element: Trusted::new(self),
379 url: img_url.clone(),
380 };
381
382 let global = document.global();
385 let mut request = create_a_potential_cors_request(
386 Some(window.webview_id()),
387 img_url.clone(),
388 Destination::Image,
389 cors_setting_for_element(self.upcast()),
390 None,
391 global.get_referrer(),
392 )
393 .with_global_scope(&global)
394 .referrer_policy(referrer_policy_for_element(self.upcast()));
395
396 if self.uses_srcset_or_picture() {
397 request = request.initiator(Initiator::ImageSet);
398 }
399
400 document.fetch_background(request, context);
403 }
404
405 fn handle_loaded_image(&self, image: Image, url: ServoUrl, cx: &mut js::context::JSContext) {
407 {
408 let mut current_request = self.current_request.borrow_mut();
409 current_request.metadata = Some(image.metadata());
410 current_request.final_url = Some(url);
411 current_request.image = Some(image);
412 current_request.state = State::CompletelyAvailable;
413 }
414
415 self.pending_request.borrow_mut().take();
416
417 LoadBlocker::terminate(&self.current_request.borrow().blocker, cx);
418 self.upcast::<Node>().dirty(cx.no_gc(), NodeDamage::Other);
420 self.resolve_image_decode_promises();
421 }
422
423 fn process_image_response(&self, image: ImageResponse, cx: &mut js::context::JSContext) {
425 let (trigger_image_load, trigger_image_error) = match (image, self.image_request.get()) {
431 (ImageResponse::Loaded(image, url), ImageRequestPhase::Current) => {
432 self.handle_loaded_image(image, url, cx);
433 (true, false)
434 },
435 (ImageResponse::Loaded(image, url), ImageRequestPhase::Pending) => {
436 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
437 self.image_request.set(ImageRequestPhase::Current);
438 self.handle_loaded_image(image, url, cx);
439 (true, false)
440 },
441 (ImageResponse::MetadataLoaded(meta), ImageRequestPhase::Current) => {
442 self.current_request.borrow_mut().state = State::PartiallyAvailable;
447 self.current_request.borrow_mut().metadata = Some(meta);
448 (false, false)
449 },
450 (ImageResponse::MetadataLoaded(_), ImageRequestPhase::Pending) => {
451 self.pending_request
455 .borrow_mut()
456 .get_or_insert_default()
457 .state = State::PartiallyAvailable;
458 (false, false)
459 },
460 (ImageResponse::FailedToLoadOrDecode, ImageRequestPhase::Current) => {
461 self.abort_request(State::Broken, ImageRequestPhase::Current, cx);
467
468 self.load_broken_image_icon(cx.no_gc());
469
470 (false, true)
474 },
475 (ImageResponse::FailedToLoadOrDecode, ImageRequestPhase::Pending) => {
476 self.abort_request(State::Broken, ImageRequestPhase::Current, cx);
482 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
483
484 if self.pending_request.borrow().is_some() {
487 *self.current_request.borrow_mut() =
488 self.pending_request.borrow_mut().take().unwrap();
489 }
490 self.image_request.set(ImageRequestPhase::Current);
491
492 self.current_request.borrow_mut().state = State::Broken;
494
495 self.load_broken_image_icon(cx.no_gc());
496
497 (false, true)
499 },
500 };
501
502 if trigger_image_load {
504 self.upcast::<EventTarget>().fire_event(cx, atom!("load"));
506 self.upcast::<EventTarget>()
507 .fire_event(cx, atom!("loadend"));
508 }
509
510 if trigger_image_error {
512 self.upcast::<EventTarget>().fire_event(cx, atom!("error"));
513 self.upcast::<EventTarget>()
514 .fire_event(cx, atom!("loadend"));
515 }
516 }
517
518 fn process_image_response_for_environment_change(
521 &self,
522 image: ImageResponse,
523 selected_source: USVString,
524 generation: u32,
525 selected_pixel_density: f64,
526 cx: &mut js::context::JSContext,
527 ) {
528 match image {
529 ImageResponse::Loaded(image, url) => {
530 if let Some(pending_request) = self.pending_request.borrow_mut().as_mut() {
531 pending_request.metadata = Some(image.metadata());
532 pending_request.final_url = Some(url);
533 pending_request.image = Some(image);
534 }
535 self.finish_reacting_to_environment_change(
536 selected_source,
537 generation,
538 selected_pixel_density,
539 );
540 },
541 ImageResponse::FailedToLoadOrDecode => {
542 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
550 },
551 ImageResponse::MetadataLoaded(meta) => {
552 self.pending_request
553 .borrow_mut()
554 .get_or_insert_default()
555 .metadata = Some(meta);
556 },
557 };
558 }
559
560 fn abort_request(
562 &self,
563 state: State,
564 request: ImageRequestPhase,
565 cx: &mut js::context::JSContext,
566 ) {
567 match request {
568 ImageRequestPhase::Current => {
569 LoadBlocker::terminate(&self.current_request.borrow().blocker, cx);
570
571 let mut request = self.current_request.safe_borrow_mut(cx);
572 request.state = state;
573 request.image = None;
574 request.metadata = None;
575 request.current_pixel_density = None;
576 },
577 ImageRequestPhase::Pending => {
578 if let Some(pending_request) = &*self.pending_request.borrow() {
579 LoadBlocker::terminate(&pending_request.blocker, cx);
580 }
581 self.pending_request.borrow_mut().take();
582 },
583 };
584
585 if matches!(state, State::Broken) {
586 self.reject_image_decode_promises();
587 } else if matches!(state, State::CompletelyAvailable) {
588 self.resolve_image_decode_promises();
589 }
590 }
591
592 fn init_image_request(
593 &self,
594 request: &DomRefCell<ImageRequest>,
595 url: &ServoUrl,
596 src: &USVString,
597 cx: &mut js::context::JSContext,
598 ) {
599 {
600 let mut request = request.borrow_mut();
601 request.parsed_url = Some(url.clone());
602 request.source_url = Some(src.clone());
603 request.image = None;
604 request.metadata = None;
605 }
606 let document = self.owner_document();
607 LoadBlocker::terminate(&request.borrow().blocker, cx);
608 *request.borrow_mut().blocker.borrow_mut() =
609 Some(LoadBlocker::new(&document, LoadType::Image(url.clone())));
610 }
611
612 fn init_pending_image_request(
613 &self,
614 request: &DomRefCell<Option<ImageRequest>>,
615 url: &ServoUrl,
616 src: &USVString,
617 cx: &mut js::context::JSContext,
618 ) {
619 {
620 let mut request = request.safe_borrow_mut(cx);
621 let request = request.get_or_insert_default();
622 request.parsed_url = Some(url.clone());
623 request.source_url = Some(src.clone());
624 request.image = None;
625 request.metadata = None;
626 }
627 let document = self.owner_document();
628 LoadBlocker::terminate(
629 &request
630 .borrow()
631 .as_ref()
632 .expect("Just created a request")
633 .blocker,
634 cx,
635 );
636 *request
637 .safe_borrow_mut(cx)
638 .as_mut()
639 .expect("Just created a request")
640 .blocker
641 .borrow_mut() = Some(LoadBlocker::new(&document, LoadType::Image(url.clone())));
642 }
643
644 fn prepare_image_request(
646 &self,
647 selected_source: &USVString,
648 selected_pixel_density: f64,
649 image_url: &ServoUrl,
650 cx: &mut js::context::JSContext,
651 ) {
652 match self.image_request.get() {
653 ImageRequestPhase::Pending => {
654 if self
657 .pending_request
658 .borrow()
659 .as_ref()
660 .and_then(|pending_request| pending_request.parsed_url.as_ref())
661 .is_some_and(|parsed_url| *parsed_url == *image_url)
662 {
663 return;
664 }
665 },
666 ImageRequestPhase::Current => {
667 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
669
670 let (current_request_url, current_request_state) = {
672 let current_request = self.current_request.borrow();
673 (current_request.parsed_url.clone(), current_request.state)
674 };
675
676 match (current_request_url, current_request_state) {
677 (Some(parsed_url), State::PartiallyAvailable) => {
678 if parsed_url == *image_url {
684 return;
686 }
687
688 self.image_request.set(ImageRequestPhase::Pending);
692 self.init_pending_image_request(
693 &self.pending_request,
694 image_url,
695 selected_source,
696 cx,
697 );
698 self.pending_request
699 .borrow_mut()
700 .as_mut()
701 .expect("Just created a pending request")
702 .current_pixel_density = Some(selected_pixel_density);
703 },
704 (_, State::Broken) | (_, State::Unavailable) => {
705 self.init_image_request(
709 &self.current_request,
710 image_url,
711 selected_source,
712 cx,
713 );
714 self.current_request.borrow_mut().current_pixel_density =
715 Some(selected_pixel_density);
716 self.reject_image_decode_promises();
717 },
718 (_, _) => {
719 self.image_request.set(ImageRequestPhase::Pending);
723 self.init_pending_image_request(
724 &self.pending_request,
725 image_url,
726 selected_source,
727 cx,
728 );
729 self.pending_request
730 .borrow_mut()
731 .as_mut()
732 .expect("Just created a pending image request")
733 .current_pixel_density = Some(selected_pixel_density);
734 },
735 }
736 },
737 }
738
739 self.fetch_image(image_url, cx);
740 }
741
742 fn update_the_image_data_sync_steps(&self, cx: &mut js::context::JSContext) {
744 let Some((selected_source, selected_pixel_density)) = self
747 .source_set
748 .borrow_mut()
749 .select_image_source(self.upcast::<Element>())
750 else {
751 self.abort_request(State::Broken, ImageRequestPhase::Current, cx);
756 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
757 self.image_request.set(ImageRequestPhase::Current);
758
759 let this = Trusted::new(self);
762
763 self.owner_global().task_manager().dom_manipulation_task_source().queue(
764 task!(image_null_source_error: move |cx| {
765 let this = this.root();
766
767 {
769 let mut current_request =
770 this.current_request.borrow_mut();
771 current_request.source_url = None;
772 current_request.parsed_url = None;
773 }
774
775 let has_src_attribute = this.upcast::<Element>().has_attribute(&local_name!("src"));
781
782 if has_src_attribute || this.uses_srcset_or_picture() {
783 this.upcast::<EventTarget>().fire_event(cx, atom!("error"));
784 }
785 }));
786
787 return;
789 };
790
791 let Ok(image_url) = self.owner_document().base_url().join(&selected_source) else {
794 self.abort_request(State::Broken, ImageRequestPhase::Current, cx);
799 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
800
801 self.image_request.set(ImageRequestPhase::Current);
803
804 let this = Trusted::new(self);
807
808 self.owner_global()
809 .task_manager()
810 .dom_manipulation_task_source()
811 .queue(task!(image_selected_source_error: move |cx| {
812 let this = this.root();
813
814 {
816 let mut current_request =
817 this.current_request.borrow_mut();
818 current_request.source_url = Some(selected_source);
819 current_request.parsed_url = None;
820 }
821
822 this.upcast::<EventTarget>().fire_event(cx, atom!("error"));
826 }));
827
828 return;
830 };
831
832 self.prepare_image_request(&selected_source, selected_pixel_density, &image_url, cx);
833 }
834
835 pub(crate) fn update_the_image_data(&self, cx: &mut js::context::JSContext) {
837 self.generation.set(self.generation.get() + 1);
839
840 if !self.owner_document().is_fully_active() {
842 }
848
849 self.current_request.borrow_mut().state = State::Unavailable;
858
859 let mut selected_source = None;
863 let mut selected_pixel_density = None;
864
865 let src = self
869 .upcast::<Element>()
870 .get_string_attribute(&local_name!("src"));
871
872 if !self.uses_srcset_or_picture() && !src.is_empty() {
873 selected_source = Some(USVString(String::from(src)));
874 selected_pixel_density = Some(1_f64);
875 };
876
877 self.last_selected_source
879 .borrow_mut()
880 .clone_from(&selected_source);
881
882 if let Some(selected_source) = selected_source {
884 if let Ok(image_url) = self.owner_document().base_url().join(&selected_source) {
888 let window = self.owner_window();
892 let response = window.image_cache().get_image(
893 image_url.clone(),
894 window.origin().immutable().clone(),
895 cors_setting_for_element(self.upcast()),
896 );
897
898 if let Some(image) = response {
900 self.abort_request(State::CompletelyAvailable, ImageRequestPhase::Current, cx);
905 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
906
907 self.image_request.set(ImageRequestPhase::Current);
909
910 let mut current_request = self.current_request.borrow_mut();
913 current_request.metadata = Some(image.metadata());
914 current_request.image = Some(image);
915 current_request.final_url = Some(image_url.clone());
916
917 self.upcast::<Node>().dirty(cx.no_gc(), NodeDamage::Other);
920
921 current_request.current_pixel_density = selected_pixel_density;
924
925 let this = Trusted::new(self);
928
929 self.owner_global()
930 .task_manager()
931 .dom_manipulation_task_source()
932 .queue(task!(image_load_event: move |cx| {
933 let this = this.root();
934
935 {
940 let mut current_request =
941 this.current_request.borrow_mut();
942 current_request.source_url = Some(selected_source);
943 current_request.parsed_url = Some(image_url);
944 }
945
946 this.upcast::<EventTarget>().fire_event(cx, atom!("load"));
950 }));
951
952 return;
954 }
955 }
956 }
957
958 let task = ImageElementMicrotask::UpdateImageData {
961 elem: Dom::from_ref(self),
962 generation: self.generation.get(),
963 };
964
965 ScriptThread::await_stable_state(cx, Box::new(task));
966 }
967
968 pub(crate) fn react_to_environment_changes(&self, cx: &JSContext) {
970 let task = ImageElementMicrotask::EnvironmentChanges {
972 elem: Dom::from_ref(self),
973 generation: self.generation.get(),
974 };
975
976 ScriptThread::await_stable_state(cx, Box::new(task));
977 }
978
979 fn react_to_environment_changes_sync_steps(
981 &self,
982 generation: u32,
983 cx: &mut js::context::JSContext,
984 ) {
985 let document = self.owner_document();
986 let has_pending_request = matches!(self.image_request.get(), ImageRequestPhase::Pending);
987
988 if !document.is_fully_active() || !self.uses_srcset_or_picture() || has_pending_request {
992 return;
993 }
994
995 let Some((selected_source, selected_pixel_density)) = self
998 .source_set
999 .borrow_mut()
1000 .select_image_source(self.upcast::<Element>())
1001 else {
1002 return;
1004 };
1005
1006 let mut same_selected_source = self
1009 .last_selected_source
1010 .borrow()
1011 .as_ref()
1012 .is_some_and(|source| *source == selected_source);
1013
1014 same_selected_source = same_selected_source ||
1018 self.current_request
1019 .borrow()
1020 .source_url
1021 .as_ref()
1022 .is_some_and(|source| *source == selected_source);
1023
1024 let same_selected_pixel_density = self
1025 .current_request
1026 .borrow()
1027 .current_pixel_density
1028 .is_some_and(|pixel_density| pixel_density == selected_pixel_density);
1029
1030 if same_selected_source && same_selected_pixel_density {
1031 return;
1032 }
1033
1034 let Ok(image_url) = document.base_url().join(&selected_source) else {
1038 return;
1039 };
1040
1041 self.image_request.set(ImageRequestPhase::Pending);
1043 self.init_pending_image_request(&self.pending_request, &image_url, &selected_source, cx);
1044
1045 let window = self.owner_window();
1048 let cache_result = window.image_cache().get_cached_image_status(
1049 image_url.clone(),
1050 window.origin().immutable().clone(),
1051 cors_setting_for_element(self.upcast()),
1052 );
1053
1054 let change_type = ChangeType::Environment {
1055 selected_source: selected_source.clone(),
1056 selected_pixel_density,
1057 };
1058
1059 match cache_result {
1060 ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable { .. }) => {
1061 self.finish_reacting_to_environment_change(
1062 selected_source,
1063 generation,
1064 selected_pixel_density,
1065 );
1066 },
1067 ImageCacheResult::Available(ImageOrMetadataAvailable::MetadataAvailable(m, id)) => {
1068 self.process_image_response_for_environment_change(
1069 ImageResponse::MetadataLoaded(m),
1070 selected_source,
1071 generation,
1072 selected_pixel_density,
1073 cx,
1074 );
1075 self.register_image_cache_callback(id, change_type);
1076 },
1077 ImageCacheResult::FailedToLoadOrDecode => {
1078 self.process_image_response_for_environment_change(
1079 ImageResponse::FailedToLoadOrDecode,
1080 selected_source,
1081 generation,
1082 selected_pixel_density,
1083 cx,
1084 );
1085 },
1086 ImageCacheResult::ReadyForRequest(id) => {
1087 self.fetch_request(&image_url, id);
1088 self.register_image_cache_callback(id, change_type);
1089 },
1090 ImageCacheResult::Pending(id) => {
1091 self.register_image_cache_callback(id, change_type);
1092 },
1093 }
1094 }
1095
1096 fn react_to_decode_image_sync_steps(&self, cx: &mut JSContext, promise: Rc<Promise>) {
1098 if !self.owner_document().is_fully_active() ||
1102 matches!(self.current_request.borrow().state, State::Broken)
1103 {
1104 promise.reject_error(cx, Error::Encoding(Some("Image element's owner document is not fully active or image element's request is broken".into())));
1105 } else if matches!(
1106 self.current_request.borrow().state,
1107 State::CompletelyAvailable
1108 ) {
1109 promise.resolve_native(cx, &());
1111 } else if matches!(self.current_request.borrow().state, State::Unavailable) &&
1112 self.current_request.borrow().source_url.is_none()
1113 {
1114 promise.reject_error(
1119 cx,
1120 Error::Encoding(Some(
1121 "Image element does not provide `src` or `srcset` attributes".into(),
1122 )),
1123 );
1124 } else {
1125 self.image_decode_promises.borrow_mut().push(promise);
1126 }
1127 }
1128
1129 fn resolve_image_decode_promises(&self) {
1131 if self.image_decode_promises.borrow().is_empty() {
1132 return;
1133 }
1134
1135 let trusted_image_decode_promises: Vec<TrustedPromise> = self
1138 .image_decode_promises
1139 .borrow()
1140 .iter()
1141 .map(|promise| TrustedPromise::new(promise.clone()))
1142 .collect();
1143
1144 self.image_decode_promises.borrow_mut().clear();
1145
1146 self.owner_global()
1147 .task_manager()
1148 .dom_manipulation_task_source()
1149 .queue(task!(fulfill_image_decode_promises: move |cx| {
1150 for trusted_promise in trusted_image_decode_promises {
1151 trusted_promise.root(cx).resolve_native(cx, &());
1152 }
1153 }));
1154 }
1155
1156 fn reject_image_decode_promises(&self) {
1158 if self.image_decode_promises.borrow().is_empty() {
1159 return;
1160 }
1161
1162 let trusted_image_decode_promises: Vec<TrustedPromise> = self
1165 .image_decode_promises
1166 .borrow()
1167 .iter()
1168 .map(|promise| TrustedPromise::new(promise.clone()))
1169 .collect();
1170
1171 self.image_decode_promises.borrow_mut().clear();
1172
1173 self.owner_global()
1174 .task_manager()
1175 .dom_manipulation_task_source()
1176 .queue(task!(reject_image_decode_promises: move |cx| {
1177 for trusted_promise in trusted_image_decode_promises {
1178 trusted_promise.root(cx).reject_error(cx, Error::Encoding(Some("Image could not be decoded".into())));
1179 }
1180 }));
1181 }
1182
1183 fn finish_reacting_to_environment_change(
1185 &self,
1186 selected_source: USVString,
1187 generation: u32,
1188 selected_pixel_density: f64,
1189 ) {
1190 let this = Trusted::new(self);
1193
1194 self.owner_global()
1195 .task_manager()
1196 .dom_manipulation_task_source()
1197 .queue(task!(image_load_event: move |cx| {
1198 let this = this.root();
1199
1200 if this.generation.get() != generation {
1203 this.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
1204 this.image_request.set(ImageRequestPhase::Current);
1205 return;
1206 }
1207
1208 *this.last_selected_source.borrow_mut() = Some(selected_source);
1211
1212
1213 if let Some(pending_request) = &mut *this.pending_request.borrow_mut() {
1214 pending_request.state = State::CompletelyAvailable;
1216
1217 pending_request.current_pixel_density = Some(selected_pixel_density);
1218
1219 } else {
1224 log::error!("Pending request was null");
1225 return
1226 }
1227 *this.current_request.borrow_mut() = this.pending_request.borrow_mut().take().expect("Should have a pending request");
1228
1229
1230 this.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
1231 this.image_request.set(ImageRequestPhase::Current);
1232
1233 this.upcast::<Node>().dirty(cx.no_gc(), NodeDamage::Other);
1235
1236 this.upcast::<EventTarget>().fire_event(cx, atom!("load"));
1238 }));
1239 }
1240
1241 fn uses_srcset_or_picture(&self) -> bool {
1243 let element = self.upcast::<Element>();
1244
1245 let has_srcset_attribute = element.has_attribute(&local_name!("srcset"));
1246 let has_parent_picture = element
1247 .upcast::<Node>()
1248 .GetParentElement()
1249 .is_some_and(|parent| parent.is::<HTMLPictureElement>());
1250 has_srcset_attribute || has_parent_picture
1251 }
1252
1253 fn new_inherited(
1254 local_name: LocalName,
1255 prefix: Option<Prefix>,
1256 document: &Document,
1257 creator: ElementCreator,
1258 ) -> HTMLImageElement {
1259 HTMLImageElement {
1260 htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
1261 image_request: Cell::new(ImageRequestPhase::Current),
1262 current_request: DomRefCell::new(ImageRequest {
1263 state: State::Unavailable,
1264 parsed_url: None,
1265 source_url: None,
1266 image: None,
1267 metadata: None,
1268 blocker: DomRefCell::new(None),
1269 final_url: None,
1270 current_pixel_density: None,
1271 }),
1272 pending_request: DomRefCell::new(None),
1273 form_owner: Default::default(),
1274 generation: Default::default(),
1275 source_set: DomRefCell::new(SourceSet::new()),
1276 dimension_attribute_source: Default::default(),
1277 last_selected_source: DomRefCell::new(None),
1278 image_decode_promises: DomRefCell::new(vec![]),
1279 line_number: creator.return_line_number(),
1280 }
1281 }
1282
1283 pub(crate) fn new(
1284 cx: &mut js::context::JSContext,
1285 local_name: LocalName,
1286 prefix: Option<Prefix>,
1287 document: &Document,
1288 proto: Option<HandleObject>,
1289 creator: ElementCreator,
1290 ) -> DomRoot<HTMLImageElement> {
1291 let image_element = Node::reflect_node_with_proto(
1292 cx,
1293 Box::new(HTMLImageElement::new_inherited(
1294 local_name, prefix, document, creator,
1295 )),
1296 document,
1297 proto,
1298 );
1299 image_element
1300 .dimension_attribute_source
1301 .set(Some(image_element.upcast()));
1302 image_element
1303 }
1304
1305 pub(crate) fn areas(&self) -> Option<Vec<DomRoot<HTMLAreaElement>>> {
1306 let elem = self.upcast::<Element>();
1307 let value = elem.get_attribute_string_value(&local_name!("usemap"))?;
1308
1309 if value.is_empty() || !value.is_char_boundary(1) {
1310 return None;
1311 }
1312
1313 let (first, last) = value.split_at(1);
1314
1315 if first != "#" || last.is_empty() {
1316 return None;
1317 }
1318
1319 let useMapElements = self
1320 .owner_document()
1321 .upcast::<Node>()
1322 .traverse_preorder(ShadowIncluding::No)
1323 .filter_map(DomRoot::downcast::<HTMLMapElement>)
1324 .find(|n| {
1325 n.upcast::<Element>()
1326 .get_name()
1327 .is_some_and(|n| *n == *last)
1328 });
1329
1330 useMapElements.map(|mapElem| mapElem.get_area_elements())
1331 }
1332
1333 pub(crate) fn same_origin(&self, origin: &MutableOrigin) -> bool {
1334 if let Some(ref image) = self.current_request.borrow().image {
1335 return image.cors_status() == CorsStatus::Safe;
1336 }
1337
1338 self.current_request
1339 .borrow()
1340 .final_url
1341 .as_ref()
1342 .is_some_and(|url| url.scheme() == "data" || url.origin().same_origin(origin))
1343 }
1344
1345 fn generation_id(&self) -> u32 {
1346 self.generation.get()
1347 }
1348
1349 fn load_broken_image_icon(&self, no_gc: &NoGC) {
1350 let window = self.owner_window();
1351 let Some(broken_image_icon) = window.image_cache().get_broken_image_icon() else {
1352 return;
1353 };
1354
1355 self.current_request.borrow_mut().metadata = Some(broken_image_icon.metadata);
1356 self.current_request.borrow_mut().image = Some(Image::Raster(broken_image_icon));
1357 self.upcast::<Node>().dirty(no_gc, NodeDamage::Other);
1358 }
1359
1360 pub(crate) fn full_image_url_for_user_interface(&self) -> Option<ServoUrl> {
1363 self.owner_document()
1364 .base_url()
1365 .join(&self.CurrentSrc())
1366 .ok()
1367 }
1368
1369 pub(crate) fn set_dimension_attribute_source(&self, value: Option<&Element>) {
1370 self.dimension_attribute_source.set(value)
1371 }
1372}
1373
1374#[derive(JSTraceable, MallocSizeOf)]
1375pub(crate) enum ImageElementMicrotask {
1376 UpdateImageData {
1377 elem: Dom<HTMLImageElement>,
1378 generation: u32,
1379 },
1380 EnvironmentChanges {
1381 elem: Dom<HTMLImageElement>,
1382 generation: u32,
1383 },
1384 Decode {
1385 elem: Dom<HTMLImageElement>,
1386 #[conditional_malloc_size_of]
1387 promise: Rc<Promise>,
1388 },
1389}
1390
1391impl MicrotaskRunnable for ImageElementMicrotask {
1392 fn handler(&self, cx: &mut js::context::JSContext) {
1393 let mut realm = match self {
1394 &ImageElementMicrotask::UpdateImageData { ref elem, .. } |
1395 &ImageElementMicrotask::EnvironmentChanges { ref elem, .. } |
1396 &ImageElementMicrotask::Decode { ref elem, .. } => enter_auto_realm(cx, &**elem),
1397 };
1398 let cx = &mut realm;
1399 match *self {
1400 ImageElementMicrotask::UpdateImageData {
1401 ref elem,
1402 ref generation,
1403 } => {
1404 if elem.generation.get() == *generation {
1408 elem.update_the_image_data_sync_steps(cx);
1409 }
1410 },
1411 ImageElementMicrotask::EnvironmentChanges {
1412 ref elem,
1413 ref generation,
1414 } => {
1415 elem.react_to_environment_changes_sync_steps(*generation, cx);
1416 },
1417 ImageElementMicrotask::Decode {
1418 ref elem,
1419 ref promise,
1420 } => {
1421 elem.react_to_decode_image_sync_steps(cx, promise.clone());
1422 },
1423 }
1424 }
1425}
1426
1427impl<'dom> LayoutDom<'dom, HTMLImageElement> {
1428 #[expect(unsafe_code)]
1429 fn current_request(self) -> &'dom ImageRequest {
1430 unsafe { self.unsafe_get().current_request.borrow_for_layout() }
1431 }
1432
1433 #[expect(unsafe_code)]
1434 fn dimension_attribute_source(self) -> LayoutDom<'dom, Element> {
1435 unsafe {
1436 self.unsafe_get()
1437 .dimension_attribute_source
1438 .to_layout()
1439 .expect("dimension attribute source should be always non-null")
1440 }
1441 }
1442
1443 pub(crate) fn image_url(self) -> Option<ServoUrl> {
1444 self.current_request().parsed_url.clone()
1445 }
1446
1447 pub(crate) fn image_data(self) -> (Option<Image>, Option<ImageMetadata>) {
1448 let current_request = self.current_request();
1449 (current_request.image.clone(), current_request.metadata)
1450 }
1451
1452 pub(crate) fn image_density(self) -> Option<f64> {
1453 self.current_request().current_pixel_density
1454 }
1455
1456 pub(crate) fn showing_broken_image_icon(self) -> bool {
1457 matches!(self.current_request().state, State::Broken)
1458 }
1459
1460 pub(crate) fn get_width(self) -> LengthOrPercentageOrAuto {
1461 self.dimension_attribute_source()
1462 .get_attr_for_layout(&ns!(), &local_name!("width"))
1463 .map(AttrValue::as_dimension)
1464 .cloned()
1465 .unwrap_or(LengthOrPercentageOrAuto::Auto)
1466 }
1467
1468 pub(crate) fn get_height(self) -> LengthOrPercentageOrAuto {
1469 self.dimension_attribute_source()
1470 .get_attr_for_layout(&ns!(), &local_name!("height"))
1471 .map(AttrValue::as_dimension)
1472 .cloned()
1473 .unwrap_or(LengthOrPercentageOrAuto::Auto)
1474 }
1475}
1476
1477impl HTMLImageElementMethods<crate::DomTypeHolder> for HTMLImageElement {
1478 fn Image(
1480 cx: &mut JSContext,
1481 window: &Window,
1482 proto: Option<HandleObject>,
1483 width: Option<u32>,
1484 height: Option<u32>,
1485 ) -> Fallible<DomRoot<HTMLImageElement>> {
1486 let document = window.Document();
1488
1489 let element = Element::create(
1492 cx,
1493 QualName::new(None, ns!(html), local_name!("img")),
1494 None,
1495 &document,
1496 ElementCreator::ScriptCreated,
1497 CustomElementCreationMode::Synchronous,
1498 proto,
1499 );
1500
1501 let image = DomRoot::downcast::<HTMLImageElement>(element).unwrap();
1502
1503 if let Some(w) = width {
1505 image.SetWidth(cx, w);
1506 }
1507
1508 if let Some(h) = height {
1511 image.SetHeight(cx, h);
1512 }
1513
1514 Ok(image)
1516 }
1517
1518 make_getter!(Alt, "alt");
1520 make_setter!(SetAlt, "alt");
1522
1523 make_url_getter!(Src, "src");
1525
1526 make_url_setter!(SetSrc, "src");
1528
1529 make_url_getter!(Srcset, "srcset");
1531 make_url_setter!(SetSrcset, "srcset");
1533
1534 make_getter!(Sizes, "sizes");
1536
1537 make_setter!(SetSizes, "sizes");
1539
1540 fn GetCrossOrigin(&self) -> Option<DOMString> {
1542 reflect_cross_origin_attribute(self.upcast::<Element>())
1543 }
1544
1545 fn SetCrossOrigin(&self, cx: &mut JSContext, value: Option<DOMString>) {
1547 set_cross_origin_attribute(cx, self.upcast::<Element>(), value);
1548 }
1549
1550 make_getter!(UseMap, "usemap");
1552 make_setter!(SetUseMap, "usemap");
1554
1555 make_bool_getter!(IsMap, "ismap");
1557 make_bool_setter!(SetIsMap, "ismap");
1559
1560 fn Width(&self) -> u32 {
1562 let node = self.upcast::<Node>();
1563 node.content_box()
1564 .map(|rect| rect.size.width.to_px() as u32)
1565 .unwrap_or_else(|| self.NaturalWidth())
1566 }
1567
1568 make_dimension_uint_setter!(SetWidth, "width");
1570
1571 fn Height(&self) -> u32 {
1573 let node = self.upcast::<Node>();
1574 node.content_box()
1575 .map(|rect| rect.size.height.to_px() as u32)
1576 .unwrap_or_else(|| self.NaturalHeight())
1577 }
1578
1579 make_dimension_uint_setter!(SetHeight, "height");
1581
1582 fn NaturalWidth(&self) -> u32 {
1584 let request = self.current_request.borrow();
1585 if matches!(request.state, State::Broken) {
1586 return 0;
1587 }
1588
1589 let pixel_density = request.current_pixel_density.unwrap_or(1f64);
1590 match request.metadata {
1591 Some(ref metadata) => (metadata.width as f64 / pixel_density) as u32,
1592 None => 0,
1593 }
1594 }
1595
1596 fn NaturalHeight(&self) -> u32 {
1598 let request = self.current_request.borrow();
1599 if matches!(request.state, State::Broken) {
1600 return 0;
1601 }
1602
1603 let pixel_density = request.current_pixel_density.unwrap_or(1f64);
1604 match request.metadata {
1605 Some(ref metadata) => (metadata.height as f64 / pixel_density) as u32,
1606 None => 0,
1607 }
1608 }
1609
1610 fn Complete(&self) -> bool {
1612 let element = self.upcast::<Element>();
1613
1614 let has_srcset_attribute = element.has_attribute(&local_name!("srcset"));
1617 if !element.has_attribute(&local_name!("src")) && !has_srcset_attribute {
1618 return true;
1619 }
1620
1621 let src = element.get_string_attribute(&local_name!("src"));
1623 if !has_srcset_attribute && src.is_empty() {
1624 return true;
1625 }
1626
1627 if matches!(self.image_request.get(), ImageRequestPhase::Current) &&
1631 matches!(
1632 self.current_request.borrow().state,
1633 State::CompletelyAvailable | State::Broken
1634 )
1635 {
1636 return true;
1637 }
1638
1639 false
1641 }
1642
1643 fn CurrentSrc(&self) -> USVString {
1645 let current_request = self.current_request.borrow();
1646 let url = ¤t_request.parsed_url;
1647 match *url {
1648 Some(ref url) => USVString(url.clone().into_string()),
1649 None => {
1650 let unparsed_url = ¤t_request.source_url;
1651 match *unparsed_url {
1652 Some(ref url) => url.clone(),
1653 None => USVString(String::new()),
1654 }
1655 },
1656 }
1657 }
1658
1659 fn ReferrerPolicy(&self) -> DOMString {
1661 reflect_referrer_policy_attribute(self.upcast::<Element>())
1662 }
1663
1664 make_setter!(SetReferrerPolicy, "referrerpolicy");
1666
1667 fn Decode(&self, cx: &mut JSContext) -> Rc<Promise> {
1669 let promise = Promise::new(cx, &self.global());
1671
1672 let task = ImageElementMicrotask::Decode {
1674 elem: Dom::from_ref(self),
1675 promise: promise.clone(),
1676 };
1677
1678 ScriptThread::await_stable_state(cx, Box::new(task));
1679
1680 promise
1682 }
1683
1684 make_getter!(Name, "name");
1686
1687 make_atomic_setter!(SetName, "name");
1689
1690 make_getter!(Align, "align");
1692
1693 make_setter!(SetAlign, "align");
1695
1696 make_uint_getter!(Hspace, "hspace");
1698
1699 make_uint_setter!(SetHspace, "hspace");
1701
1702 make_uint_getter!(Vspace, "vspace");
1704
1705 make_uint_setter!(SetVspace, "vspace");
1707
1708 make_url_getter!(LongDesc, "longdesc");
1710
1711 make_url_setter!(SetLongDesc, "longdesc");
1713
1714 make_getter!(Border, "border");
1716
1717 make_setter!(SetBorder, "border");
1719}
1720
1721impl VirtualMethods for HTMLImageElement {
1722 fn super_type(&self) -> Option<&dyn VirtualMethods> {
1723 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
1724 }
1725
1726 fn adopting_steps(&self, cx: &mut JSContext, old_doc: &Document) {
1727 self.super_type().unwrap().adopting_steps(cx, old_doc);
1728 self.update_the_image_data(cx);
1729 }
1730
1731 fn attribute_mutated(
1732 &self,
1733 cx: &mut js::context::JSContext,
1734 attr: AttrRef<'_>,
1735 mutation: AttributeMutation,
1736 ) {
1737 self.super_type()
1738 .unwrap()
1739 .attribute_mutated(cx, attr, mutation);
1740 match attr.local_name() {
1741 &local_name!("src") |
1742 &local_name!("srcset") |
1743 &local_name!("width") |
1744 &local_name!("sizes") => {
1745 self.update_the_image_data(cx);
1749 },
1750 &local_name!("crossorigin") => {
1751 let cross_origin_state_changed = match mutation {
1754 AttributeMutation::Removed | AttributeMutation::Set(None, _) => true,
1755 AttributeMutation::Set(Some(old_value), _) => {
1756 let new_cors_setting =
1757 CorsSettings::from_enumerated_attribute(&attr.value());
1758 let old_cors_setting = CorsSettings::from_enumerated_attribute(old_value);
1759
1760 new_cors_setting != old_cors_setting
1761 },
1762 };
1763
1764 if cross_origin_state_changed {
1765 self.update_the_image_data(cx);
1766 }
1767 },
1768 &local_name!("referrerpolicy") => {
1769 let referrer_policy_state_changed = match mutation {
1772 AttributeMutation::Removed | AttributeMutation::Set(None, _) => {
1773 ReferrerPolicy::from(&**attr.value()) != ReferrerPolicy::EmptyString
1774 },
1775 AttributeMutation::Set(Some(old_value), _) => {
1776 ReferrerPolicy::from(&**attr.value()) != ReferrerPolicy::from(&**old_value)
1777 },
1778 };
1779
1780 if referrer_policy_state_changed {
1781 self.update_the_image_data(cx);
1782 }
1783 },
1784 _ => {},
1785 }
1786 }
1787
1788 fn attribute_affects_presentational_hints(&self, attr: AttrRef<'_>) -> bool {
1789 match attr.local_name() {
1790 &local_name!("width") | &local_name!("height") => true,
1791 _ => self
1792 .super_type()
1793 .unwrap()
1794 .attribute_affects_presentational_hints(attr),
1795 }
1796 }
1797
1798 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
1799 match name {
1800 &local_name!("width") | &local_name!("height") => {
1801 AttrValue::from_dimension(value.into())
1802 },
1803 &local_name!("hspace") | &local_name!("vspace") => AttrValue::from_u32(value.into(), 0),
1804 _ => self
1805 .super_type()
1806 .unwrap()
1807 .parse_plain_attribute(name, value),
1808 }
1809 }
1810
1811 fn handle_event(&self, cx: &mut js::context::JSContext, event: &Event) {
1812 if event.type_() != atom!("click") {
1813 return;
1814 }
1815
1816 let Some(area_elements) = self.areas() else {
1817 return;
1818 };
1819
1820 let Some(mouse_event) = event.downcast::<MouseEvent>() else {
1822 return;
1823 };
1824
1825 let click_location = Point2D::new(
1826 mouse_event.ClientX().to_f32().unwrap(),
1827 mouse_event.ClientY().to_f32().unwrap(),
1828 );
1829 let bounding_rectangle = self.upcast::<Element>().GetBoundingClientRect(cx);
1830 let image_extents =
1831 Point2D::new(bounding_rectangle.X() as f32, bounding_rectangle.Y() as f32);
1832
1833 for area_element in area_elements {
1835 if !area_element.is_instance_activatable() {
1836 continue;
1837 }
1838 let activatable_area = match area_element.get_shape_from_coords() {
1839 Some(shape) => shape.absolute_coords(image_extents),
1840 None => return,
1841 };
1842 if activatable_area.hit_test(&click_location) {
1843 area_element.activation_behavior(cx, event, self.upcast());
1844 return;
1845 }
1846 }
1847 }
1848
1849 fn bind_to_tree(&self, cx: &mut JSContext, context: &BindContext) {
1851 if let Some(s) = self.super_type() {
1852 s.bind_to_tree(cx, context);
1853 }
1854 let document = self.owner_document();
1855 if context.tree_connected {
1856 document.register_responsive_image(self);
1857 }
1858
1859 let parent = self.upcast::<Node>().GetParentNode().unwrap();
1860
1861 if parent.is::<HTMLPictureElement>() && *parent == *context.parent {
1864 self.update_the_image_data(cx);
1865 }
1866 }
1867
1868 fn unbind_from_tree(&self, cx: &mut js::context::JSContext, context: &UnbindContext) {
1870 self.super_type().unwrap().unbind_from_tree(cx, context);
1871 let document = self.owner_document();
1872 document.unregister_responsive_image(self);
1873
1874 if context.parent.is::<HTMLPictureElement>() && !self.upcast::<Node>().has_parent() {
1877 self.update_the_image_data(cx);
1878 }
1879 }
1880
1881 fn moving_steps(&self, cx: &mut JSContext, context: &MoveContext) {
1883 if let Some(super_type) = self.super_type() {
1884 super_type.moving_steps(cx, context);
1885 }
1886
1887 if let Some(old_parent) = context.old_parent &&
1889 old_parent.is::<HTMLPictureElement>()
1890 {
1891 self.update_the_image_data(cx);
1892 }
1893 }
1894}
1895
1896impl FormControl for HTMLImageElement {
1897 fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
1898 self.form_owner.get()
1899 }
1900
1901 fn set_form_owner(&self, _cx: &mut JSContext, form: Option<&HTMLFormElement>) {
1902 self.form_owner.set(form);
1903 }
1904
1905 fn to_html_element(&self) -> &HTMLElement {
1906 self.upcast::<HTMLElement>()
1907 }
1908}
1909
1910#[derive(Clone)]
1911enum ChangeType {
1912 Environment {
1913 selected_source: USVString,
1914 selected_pixel_density: f64,
1915 },
1916 Element,
1917}