1use std::cell::Cell;
6use std::mem;
7use std::rc::Rc;
8use std::sync::Arc;
9
10use bytes::Bytes;
11use dom_struct::dom_struct;
12use euclid::default::Point2D;
13use html5ever::{LocalName, Prefix, QualName, local_name, ns};
14use js::context::{JSContext, NoGC};
15use js::rust::HandleObject;
16use mime::{self, Mime};
17use net_traits::image_cache::{
18 Image, ImageCache, ImageCacheResult, ImageLoadListener, ImageOrMetadataAvailable,
19 ImageResponse, PendingImageId,
20};
21use net_traits::request::{CorsSettings, Destination, Initiator, RequestId};
22use net_traits::{
23 FetchMetadata, FetchResponseMsg, NetworkError, ReferrerPolicy, ResourceFetchTiming,
24};
25use num_traits::ToPrimitive;
26use pixels::{CorsStatus, ImageMetadata, Snapshot};
27use script_bindings::cell::DomRefCell;
28use servo_url::ServoUrl;
29use servo_url::origin::MutableOrigin;
30use style::attr::{AttrValue, LengthOrPercentageOrAuto};
31
32use crate::dom::activation::Activatable;
33use crate::dom::bindings::codegen::Bindings::DOMRectBinding::DOMRect_Binding::DOMRectMethods;
34use crate::dom::bindings::codegen::Bindings::ElementBinding::Element_Binding::ElementMethods;
35use crate::dom::bindings::codegen::Bindings::HTMLImageElementBinding::HTMLImageElementMethods;
36use crate::dom::bindings::codegen::Bindings::MouseEventBinding::MouseEventMethods;
37use crate::dom::bindings::codegen::Bindings::NodeBinding::Node_Binding::NodeMethods;
38use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
39use crate::dom::bindings::error::{Error, Fallible};
40use crate::dom::bindings::inheritance::Castable;
41use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
42use crate::dom::bindings::reflector::DomGlobal;
43use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom, ToLayoutOptional};
44use crate::dom::bindings::str::{DOMString, USVString};
45use crate::dom::csp::{GlobalCspReporting, Violation};
46use crate::dom::document::Document;
47use crate::dom::element::attributes::storage::AttrRef;
48use crate::dom::element::{
49 AttributeMutation, CustomElementCreationMode, Element, ElementCreator,
50 cors_setting_for_element, referrer_policy_for_element, reflect_cross_origin_attribute,
51 reflect_referrer_policy_attribute, set_cross_origin_attribute,
52};
53use crate::dom::event::Event;
54use crate::dom::eventtarget::EventTarget;
55use crate::dom::globalscope::GlobalScope;
56use crate::dom::html::htmlareaelement::HTMLAreaElement;
57use crate::dom::html::htmlelement::HTMLElement;
58use crate::dom::html::htmlformelement::{FormControl, HTMLFormElement};
59use crate::dom::html::htmlmapelement::HTMLMapElement;
60use crate::dom::html::htmlpictureelement::HTMLPictureElement;
61use crate::dom::iterators::ShadowIncluding;
62use crate::dom::mouseevent::MouseEvent;
63use crate::dom::node::virtualmethods::VirtualMethods;
64use crate::dom::node::{BindContext, MoveContext, Node, NodeDamage, NodeTraits, UnbindContext};
65use crate::dom::performance::performanceresourcetiming::InitiatorType;
66use crate::dom::promise::Promise;
67use crate::dom::srcset::SourceSet;
68use crate::dom::window::Window;
69use crate::event_loop::document_loader::{LoadBlocker, LoadType};
70use crate::event_loop::script_thread::ScriptThread;
71use crate::fetch::fetch::{RequestWithGlobalScope, create_a_potential_cors_request};
72use crate::fetch::network_listener::{self, FetchResponseListener, ResourceTimingListener};
73use crate::realms::enter_auto_realm;
74use crate::runtime::microtask::MicrotaskRunnable;
75
76#[derive(Clone, Copy, JSTraceable, MallocSizeOf)]
78enum State {
79 Unavailable,
80 PartiallyAvailable,
81 CompletelyAvailable,
82 Broken,
83}
84
85#[derive(Clone, Copy, JSTraceable, MallocSizeOf)]
86enum ImageRequestPhase {
87 Pending,
88 Current,
89}
90
91#[derive(JSTraceable, MallocSizeOf)]
93#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
94struct ImageRequest {
95 state: State,
96 #[no_trace]
97 parsed_url: Option<ServoUrl>,
98 source_url: Option<USVString>,
99 blocker: DomRefCell<Option<LoadBlocker>>,
100 #[no_trace]
101 image: Option<Image>,
102 #[no_trace]
103 metadata: Option<ImageMetadata>,
104 #[no_trace]
105 final_url: Option<ServoUrl>,
106 current_pixel_density: Option<f64>,
107}
108
109#[dom_struct]
110pub(crate) struct HTMLImageElement {
111 htmlelement: HTMLElement,
112 image_request: Cell<ImageRequestPhase>,
113 current_request: DomRefCell<ImageRequest>,
114 pending_request: DomRefCell<ImageRequest>,
115 form_owner: MutNullableDom<HTMLFormElement>,
116 generation: Cell<u32>,
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}
128
129impl HTMLImageElement {
130 pub(crate) fn is_usable(&self) -> Fallible<bool> {
132 if let Some(image) = &self.current_request.borrow().image {
134 let intrinsic_size = image.metadata();
135 if intrinsic_size.width == 0 || intrinsic_size.height == 0 {
136 return Ok(false);
137 }
138 }
139
140 match self.current_request.borrow().state {
141 State::Broken => Err(Error::InvalidState(Some(
143 "Image element's state is broken".into(),
144 ))),
145 State::CompletelyAvailable => Ok(true),
146 State::PartiallyAvailable | State::Unavailable => Ok(false),
148 }
149 }
150
151 pub(crate) fn image_data(&self) -> Option<Image> {
152 self.current_request.borrow().image.clone()
153 }
154
155 pub(crate) fn get_raster_image_data(&self) -> Option<Snapshot> {
157 let Some(raster_image) = self.image_data()?.as_raster_image() else {
158 warn!("Vector image is not supported as raster image source");
159 return None;
160 };
161 Some(raster_image.as_snapshot())
162 }
163}
164
165struct ImageContext {
167 image_cache: Arc<dyn ImageCache>,
169 status: Result<(), NetworkError>,
171 id: PendingImageId,
173 aborted: bool,
175 doc: Trusted<Document>,
177 url: ServoUrl,
178 element: Trusted<HTMLImageElement>,
179}
180
181impl FetchResponseListener for ImageContext {
182 fn should_invoke(&self) -> bool {
183 !self.aborted
184 }
185
186 fn process_request_body(&mut self, _: RequestId) {}
187
188 fn process_response(
189 &mut self,
190 _: &mut js::context::JSContext,
191 request_id: RequestId,
192 metadata: Result<FetchMetadata, NetworkError>,
193 ) {
194 debug!("got {:?} for {:?}", metadata.as_ref().map(|_| ()), self.url);
195 self.image_cache.notify_pending_response(
196 self.id,
197 FetchResponseMsg::ProcessResponse(request_id, metadata.clone()),
198 );
199
200 let metadata = metadata.ok().map(|meta| match meta {
201 FetchMetadata::Unfiltered(m) => m,
202 FetchMetadata::Filtered { unsafe_, .. } => unsafe_,
203 });
204
205 if let Some(metadata) = metadata.as_ref() &&
207 let Some(ref content_type) = metadata.content_type
208 {
209 let mime: Mime = content_type.clone().into_inner().into();
210 if mime.type_() == mime::MULTIPART && mime.subtype().as_str() == "x-mixed-replace" {
211 self.aborted = true;
212 }
213 }
214
215 self.status = match metadata.as_ref().map(|m| m.status.clone()) {
218 None => Err(NetworkError::ResourceLoadError(
219 "No http status code received".to_owned(),
220 )),
221 Some(_) => Ok(()),
222 };
223 }
224
225 fn process_response_chunk(
226 &mut self,
227 _: &mut js::context::JSContext,
228 request_id: RequestId,
229 payload: Bytes,
230 ) {
231 if self.status.is_ok() {
232 self.image_cache.notify_pending_response(
233 self.id,
234 FetchResponseMsg::ProcessResponseChunk(request_id, payload),
235 );
236 }
237 }
238
239 fn process_response_eof(
240 self,
241 cx: &mut js::context::JSContext,
242 request_id: RequestId,
243 response: Result<(), NetworkError>,
244 timing: ResourceFetchTiming,
245 ) {
246 self.image_cache.notify_pending_response(
247 self.id,
248 FetchResponseMsg::ProcessResponseEOF(request_id, response.clone(), timing.clone()),
249 );
250 network_listener::submit_timing(cx, &self, &response, &timing);
251 }
252
253 fn process_csp_violations(
254 &mut self,
255 cx: &mut js::context::JSContext,
256 _request_id: RequestId,
257 violations: Vec<Violation>,
258 ) {
259 let global = &self.resource_timing_global();
260 let elem = self.element.root();
261 let source_position = elem
262 .upcast::<Element>()
263 .compute_source_position(elem.line_number as u32);
264 global.report_csp_violations(cx, violations, None, Some(source_position));
265 }
266
267 fn process_content_length(&mut self, request_id: RequestId, size: usize) {
268 self.image_cache.notify_pending_response(
269 self.id,
270 FetchResponseMsg::ProcessContentLength(request_id, size),
271 );
272 }
273}
274
275impl ResourceTimingListener for ImageContext {
276 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
277 (
278 InitiatorType::LocalName("img".to_string()),
279 self.url.clone(),
280 )
281 }
282
283 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
284 self.doc.root().global()
285 }
286}
287
288#[expect(non_snake_case)]
289impl HTMLImageElement {
290 fn fetch_image(&self, img_url: &ServoUrl, cx: &mut js::context::JSContext) {
292 let window = self.owner_window();
293
294 let cache_result = window.image_cache().get_cached_image_status(
295 img_url.clone(),
296 window.origin().immutable().clone(),
297 cors_setting_for_element(self.upcast()),
298 );
299
300 match cache_result {
301 ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable {
302 image,
303 url,
304 }) => self.process_image_response(ImageResponse::Loaded(image, url), cx),
305 ImageCacheResult::Available(ImageOrMetadataAvailable::MetadataAvailable(
306 metadata,
307 id,
308 )) => {
309 self.process_image_response(ImageResponse::MetadataLoaded(metadata), cx);
310 self.register_image_cache_callback(id, ChangeType::Element);
311 },
312 ImageCacheResult::Pending(id) => {
313 self.register_image_cache_callback(id, ChangeType::Element);
314 },
315 ImageCacheResult::ReadyForRequest(id) => {
316 self.fetch_request(img_url, id);
317 self.register_image_cache_callback(id, ChangeType::Element);
318 },
319 ImageCacheResult::FailedToLoadOrDecode => {
320 self.process_image_response(ImageResponse::FailedToLoadOrDecode, cx)
321 },
322 };
323 }
324
325 fn register_image_cache_callback(&self, id: PendingImageId, change_type: ChangeType) {
326 let trusted_node = Trusted::new(self);
327 let generation = self.generation_id();
328 let window = self.owner_window();
329 let callback = window.register_image_cache_listener(id, move |response, _| {
330 let trusted_node = trusted_node.clone();
331 let window = trusted_node.root().owner_window();
332 let callback_type = change_type.clone();
333
334 window
335 .as_global_scope()
336 .task_manager()
337 .networking_task_source()
338 .queue(task!(process_image_response: move |cx| {
339 let element = trusted_node.root();
340
341 if generation != element.generation_id() {
343 return;
344 }
345
346 match callback_type {
347 ChangeType::Element => {
348 element.process_image_response(response.response, cx);
349 }
350 ChangeType::Environment { selected_source, selected_pixel_density } => {
351 element.process_image_response_for_environment_change(
352 response.response, selected_source, generation, selected_pixel_density, cx
353 );
354 }
355 }
356 }));
357 });
358
359 window.image_cache().add_listener(ImageLoadListener::new(
360 callback,
361 window.pipeline_id(),
362 id,
363 ));
364 }
365
366 fn fetch_request(&self, img_url: &ServoUrl, id: PendingImageId) {
367 let document = self.owner_document();
368 let window = self.owner_window();
369
370 let context = ImageContext {
371 image_cache: window.image_cache(),
372 status: Ok(()),
373 id,
374 aborted: false,
375 doc: Trusted::new(&document),
376 element: Trusted::new(self),
377 url: img_url.clone(),
378 };
379
380 let global = document.global();
383 let mut request = create_a_potential_cors_request(
384 Some(window.webview_id()),
385 img_url.clone(),
386 Destination::Image,
387 cors_setting_for_element(self.upcast()),
388 None,
389 global.get_referrer(),
390 )
391 .with_global_scope(&global)
392 .referrer_policy(referrer_policy_for_element(self.upcast()));
393
394 if self.uses_srcset_or_picture() {
395 request = request.initiator(Initiator::ImageSet);
396 }
397
398 document.fetch_background(request, context);
401 }
402
403 fn handle_loaded_image(&self, image: Image, url: ServoUrl, cx: &mut js::context::JSContext) {
405 self.current_request.borrow_mut().metadata = Some(image.metadata());
406 self.current_request.borrow_mut().final_url = Some(url);
407 self.current_request.borrow_mut().image = Some(image);
408 self.current_request.borrow_mut().state = State::CompletelyAvailable;
409 LoadBlocker::terminate(&self.current_request.borrow().blocker, cx);
410 self.upcast::<Node>().dirty(cx.no_gc(), NodeDamage::Other);
412 self.resolve_image_decode_promises();
413 }
414
415 fn process_image_response(&self, image: ImageResponse, cx: &mut js::context::JSContext) {
417 let (trigger_image_load, trigger_image_error) = match (image, self.image_request.get()) {
423 (ImageResponse::Loaded(image, url), ImageRequestPhase::Current) => {
424 self.handle_loaded_image(image, url, cx);
425 (true, false)
426 },
427 (ImageResponse::Loaded(image, url), ImageRequestPhase::Pending) => {
428 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
429 self.image_request.set(ImageRequestPhase::Current);
430 self.handle_loaded_image(image, url, cx);
431 (true, false)
432 },
433 (ImageResponse::MetadataLoaded(meta), ImageRequestPhase::Current) => {
434 self.current_request.borrow_mut().state = State::PartiallyAvailable;
439 self.current_request.borrow_mut().metadata = Some(meta);
440 (false, false)
441 },
442 (ImageResponse::MetadataLoaded(_), ImageRequestPhase::Pending) => {
443 self.pending_request.borrow_mut().state = State::PartiallyAvailable;
447 (false, false)
448 },
449 (ImageResponse::FailedToLoadOrDecode, ImageRequestPhase::Current) => {
450 self.abort_request(State::Broken, ImageRequestPhase::Current, cx);
456
457 self.load_broken_image_icon(cx.no_gc());
458
459 (false, true)
463 },
464 (ImageResponse::FailedToLoadOrDecode, ImageRequestPhase::Pending) => {
465 self.abort_request(State::Broken, ImageRequestPhase::Current, cx);
471 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
472
473 mem::swap(
475 &mut *self.current_request.borrow_mut(),
476 &mut *self.pending_request.borrow_mut(),
477 );
478 self.image_request.set(ImageRequestPhase::Current);
479
480 self.current_request.borrow_mut().state = State::Broken;
482
483 self.load_broken_image_icon(cx.no_gc());
484
485 (false, true)
487 },
488 };
489
490 if trigger_image_load {
492 self.upcast::<EventTarget>().fire_event(cx, atom!("load"));
494 self.upcast::<EventTarget>()
495 .fire_event(cx, atom!("loadend"));
496 }
497
498 if trigger_image_error {
500 self.upcast::<EventTarget>().fire_event(cx, atom!("error"));
501 self.upcast::<EventTarget>()
502 .fire_event(cx, atom!("loadend"));
503 }
504 }
505
506 fn process_image_response_for_environment_change(
509 &self,
510 image: ImageResponse,
511 selected_source: USVString,
512 generation: u32,
513 selected_pixel_density: f64,
514 cx: &mut js::context::JSContext,
515 ) {
516 match image {
517 ImageResponse::Loaded(image, url) => {
518 self.pending_request.borrow_mut().metadata = Some(image.metadata());
519 self.pending_request.borrow_mut().final_url = Some(url);
520 self.pending_request.borrow_mut().image = Some(image);
521 self.finish_reacting_to_environment_change(
522 selected_source,
523 generation,
524 selected_pixel_density,
525 );
526 },
527 ImageResponse::FailedToLoadOrDecode => {
528 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
536 },
537 ImageResponse::MetadataLoaded(meta) => {
538 self.pending_request.borrow_mut().metadata = Some(meta);
539 },
540 };
541 }
542
543 fn abort_request(
545 &self,
546 state: State,
547 request: ImageRequestPhase,
548 cx: &mut js::context::JSContext,
549 ) {
550 let request = match request {
551 ImageRequestPhase::Current => &self.current_request,
552 ImageRequestPhase::Pending => &self.pending_request,
553 };
554 LoadBlocker::terminate(&request.borrow().blocker, cx);
555 let mut request = request.safe_borrow_mut(cx);
556 request.state = state;
557 request.image = None;
558 request.metadata = None;
559 request.current_pixel_density = None;
560
561 if matches!(state, State::Broken) {
562 self.reject_image_decode_promises();
563 } else if matches!(state, State::CompletelyAvailable) {
564 self.resolve_image_decode_promises();
565 }
566 }
567
568 fn init_image_request(
569 &self,
570 request: &DomRefCell<ImageRequest>,
571 url: &ServoUrl,
572 src: &USVString,
573 cx: &mut js::context::JSContext,
574 ) {
575 {
576 let mut request = request.borrow_mut();
577 request.parsed_url = Some(url.clone());
578 request.source_url = Some(src.clone());
579 request.image = None;
580 request.metadata = None;
581 }
582 let document = self.owner_document();
583 LoadBlocker::terminate(&request.borrow().blocker, cx);
584 *request.borrow_mut().blocker.borrow_mut() =
585 Some(LoadBlocker::new(&document, LoadType::Image(url.clone())));
586 }
587
588 fn prepare_image_request(
590 &self,
591 selected_source: &USVString,
592 selected_pixel_density: f64,
593 image_url: &ServoUrl,
594 cx: &mut js::context::JSContext,
595 ) {
596 match self.image_request.get() {
597 ImageRequestPhase::Pending => {
598 if self
601 .pending_request
602 .borrow()
603 .parsed_url
604 .as_ref()
605 .is_some_and(|parsed_url| *parsed_url == *image_url)
606 {
607 return;
608 }
609 },
610 ImageRequestPhase::Current => {
611 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
613
614 let (current_request_url, current_request_state) = {
616 let current_request = self.current_request.borrow();
617 (current_request.parsed_url.clone(), current_request.state)
618 };
619
620 match (current_request_url, current_request_state) {
621 (Some(parsed_url), State::PartiallyAvailable) => {
622 if parsed_url == *image_url {
628 return;
630 }
631
632 self.image_request.set(ImageRequestPhase::Pending);
636 self.init_image_request(
637 &self.pending_request,
638 image_url,
639 selected_source,
640 cx,
641 );
642 self.pending_request.borrow_mut().current_pixel_density =
643 Some(selected_pixel_density);
644 },
645 (_, State::Broken) | (_, State::Unavailable) => {
646 self.init_image_request(
650 &self.current_request,
651 image_url,
652 selected_source,
653 cx,
654 );
655 self.current_request.borrow_mut().current_pixel_density =
656 Some(selected_pixel_density);
657 self.reject_image_decode_promises();
658 },
659 (_, _) => {
660 self.image_request.set(ImageRequestPhase::Pending);
664 self.init_image_request(
665 &self.pending_request,
666 image_url,
667 selected_source,
668 cx,
669 );
670 self.pending_request.borrow_mut().current_pixel_density =
671 Some(selected_pixel_density);
672 },
673 }
674 },
675 }
676
677 self.fetch_image(image_url, cx);
678 }
679
680 fn update_the_image_data_sync_steps(&self, cx: &mut js::context::JSContext) {
682 let Some((selected_source, selected_pixel_density)) = self
685 .source_set
686 .borrow_mut()
687 .select_image_source(self.upcast::<Element>())
688 else {
689 self.abort_request(State::Broken, ImageRequestPhase::Current, cx);
694 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
695 self.image_request.set(ImageRequestPhase::Current);
696
697 let this = Trusted::new(self);
700
701 self.owner_global().task_manager().dom_manipulation_task_source().queue(
702 task!(image_null_source_error: move |cx| {
703 let this = this.root();
704
705 {
707 let mut current_request =
708 this.current_request.borrow_mut();
709 current_request.source_url = None;
710 current_request.parsed_url = None;
711 }
712
713 let has_src_attribute = this.upcast::<Element>().has_attribute(&local_name!("src"));
719
720 if has_src_attribute || this.uses_srcset_or_picture() {
721 this.upcast::<EventTarget>().fire_event(cx, atom!("error"));
722 }
723 }));
724
725 return;
727 };
728
729 let Ok(image_url) = self.owner_document().base_url().join(&selected_source) else {
732 self.abort_request(State::Broken, ImageRequestPhase::Current, cx);
737 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
738
739 self.image_request.set(ImageRequestPhase::Current);
741
742 let this = Trusted::new(self);
745
746 self.owner_global()
747 .task_manager()
748 .dom_manipulation_task_source()
749 .queue(task!(image_selected_source_error: move |cx| {
750 let this = this.root();
751
752 {
754 let mut current_request =
755 this.current_request.borrow_mut();
756 current_request.source_url = Some(selected_source);
757 current_request.parsed_url = None;
758 }
759
760 this.upcast::<EventTarget>().fire_event(cx, atom!("error"));
764 }));
765
766 return;
768 };
769
770 self.prepare_image_request(&selected_source, selected_pixel_density, &image_url, cx);
771 }
772
773 pub(crate) fn update_the_image_data(&self, cx: &mut js::context::JSContext) {
775 self.generation.set(self.generation.get() + 1);
777
778 if !self.owner_document().is_fully_active() {
780 }
786
787 self.current_request.borrow_mut().state = State::Unavailable;
796
797 let mut selected_source = None;
801 let mut selected_pixel_density = None;
802
803 let src = self
807 .upcast::<Element>()
808 .get_string_attribute(&local_name!("src"));
809
810 if !self.uses_srcset_or_picture() && !src.is_empty() {
811 selected_source = Some(USVString(String::from(src)));
812 selected_pixel_density = Some(1_f64);
813 };
814
815 self.last_selected_source
817 .borrow_mut()
818 .clone_from(&selected_source);
819
820 if let Some(selected_source) = selected_source {
822 if let Ok(image_url) = self.owner_document().base_url().join(&selected_source) {
826 let window = self.owner_window();
830 let response = window.image_cache().get_image(
831 image_url.clone(),
832 window.origin().immutable().clone(),
833 cors_setting_for_element(self.upcast()),
834 );
835
836 if let Some(image) = response {
838 self.abort_request(State::CompletelyAvailable, ImageRequestPhase::Current, cx);
843 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
844
845 self.image_request.set(ImageRequestPhase::Current);
847
848 let mut current_request = self.current_request.borrow_mut();
851 current_request.metadata = Some(image.metadata());
852 current_request.image = Some(image);
853 current_request.final_url = Some(image_url.clone());
854
855 self.upcast::<Node>().dirty(cx.no_gc(), NodeDamage::Other);
858
859 current_request.current_pixel_density = selected_pixel_density;
862
863 let this = Trusted::new(self);
866
867 self.owner_global()
868 .task_manager()
869 .dom_manipulation_task_source()
870 .queue(task!(image_load_event: move |cx| {
871 let this = this.root();
872
873 {
878 let mut current_request =
879 this.current_request.borrow_mut();
880 current_request.source_url = Some(selected_source);
881 current_request.parsed_url = Some(image_url);
882 }
883
884 this.upcast::<EventTarget>().fire_event(cx, atom!("load"));
888 }));
889
890 return;
892 }
893 }
894 }
895
896 let task = ImageElementMicrotask::UpdateImageData {
899 elem: Dom::from_ref(self),
900 generation: self.generation.get(),
901 };
902
903 ScriptThread::await_stable_state(cx, Box::new(task));
904 }
905
906 pub(crate) fn react_to_environment_changes(&self, cx: &JSContext) {
908 let task = ImageElementMicrotask::EnvironmentChanges {
910 elem: Dom::from_ref(self),
911 generation: self.generation.get(),
912 };
913
914 ScriptThread::await_stable_state(cx, Box::new(task));
915 }
916
917 fn react_to_environment_changes_sync_steps(
919 &self,
920 generation: u32,
921 cx: &mut js::context::JSContext,
922 ) {
923 let document = self.owner_document();
924 let has_pending_request = matches!(self.image_request.get(), ImageRequestPhase::Pending);
925
926 if !document.is_fully_active() || !self.uses_srcset_or_picture() || has_pending_request {
930 return;
931 }
932
933 let Some((selected_source, selected_pixel_density)) = self
936 .source_set
937 .borrow_mut()
938 .select_image_source(self.upcast::<Element>())
939 else {
940 return;
942 };
943
944 let mut same_selected_source = self
947 .last_selected_source
948 .borrow()
949 .as_ref()
950 .is_some_and(|source| *source == selected_source);
951
952 same_selected_source = same_selected_source ||
956 self.current_request
957 .borrow()
958 .source_url
959 .as_ref()
960 .is_some_and(|source| *source == selected_source);
961
962 let same_selected_pixel_density = self
963 .current_request
964 .borrow()
965 .current_pixel_density
966 .is_some_and(|pixel_density| pixel_density == selected_pixel_density);
967
968 if same_selected_source && same_selected_pixel_density {
969 return;
970 }
971
972 let Ok(image_url) = document.base_url().join(&selected_source) else {
976 return;
977 };
978
979 self.image_request.set(ImageRequestPhase::Pending);
981 self.init_image_request(&self.pending_request, &image_url, &selected_source, cx);
982
983 let window = self.owner_window();
986 let cache_result = window.image_cache().get_cached_image_status(
987 image_url.clone(),
988 window.origin().immutable().clone(),
989 cors_setting_for_element(self.upcast()),
990 );
991
992 let change_type = ChangeType::Environment {
993 selected_source: selected_source.clone(),
994 selected_pixel_density,
995 };
996
997 match cache_result {
998 ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable { .. }) => {
999 self.finish_reacting_to_environment_change(
1000 selected_source,
1001 generation,
1002 selected_pixel_density,
1003 );
1004 },
1005 ImageCacheResult::Available(ImageOrMetadataAvailable::MetadataAvailable(m, id)) => {
1006 self.process_image_response_for_environment_change(
1007 ImageResponse::MetadataLoaded(m),
1008 selected_source,
1009 generation,
1010 selected_pixel_density,
1011 cx,
1012 );
1013 self.register_image_cache_callback(id, change_type);
1014 },
1015 ImageCacheResult::FailedToLoadOrDecode => {
1016 self.process_image_response_for_environment_change(
1017 ImageResponse::FailedToLoadOrDecode,
1018 selected_source,
1019 generation,
1020 selected_pixel_density,
1021 cx,
1022 );
1023 },
1024 ImageCacheResult::ReadyForRequest(id) => {
1025 self.fetch_request(&image_url, id);
1026 self.register_image_cache_callback(id, change_type);
1027 },
1028 ImageCacheResult::Pending(id) => {
1029 self.register_image_cache_callback(id, change_type);
1030 },
1031 }
1032 }
1033
1034 fn react_to_decode_image_sync_steps(&self, cx: &mut JSContext, promise: Rc<Promise>) {
1036 if !self.owner_document().is_fully_active() ||
1040 matches!(self.current_request.borrow().state, State::Broken)
1041 {
1042 promise.reject_error(cx, Error::Encoding(Some("Image element's owner document is not fully active or image element's request is broken".into())));
1043 } else if matches!(
1044 self.current_request.borrow().state,
1045 State::CompletelyAvailable
1046 ) {
1047 promise.resolve_native(cx, &());
1049 } else if matches!(self.current_request.borrow().state, State::Unavailable) &&
1050 self.current_request.borrow().source_url.is_none()
1051 {
1052 promise.reject_error(
1057 cx,
1058 Error::Encoding(Some(
1059 "Image element does not provide `src` or `srcset` attributes".into(),
1060 )),
1061 );
1062 } else {
1063 self.image_decode_promises.borrow_mut().push(promise);
1064 }
1065 }
1066
1067 fn resolve_image_decode_promises(&self) {
1069 if self.image_decode_promises.borrow().is_empty() {
1070 return;
1071 }
1072
1073 let trusted_image_decode_promises: Vec<TrustedPromise> = self
1076 .image_decode_promises
1077 .borrow()
1078 .iter()
1079 .map(|promise| TrustedPromise::new(promise.clone()))
1080 .collect();
1081
1082 self.image_decode_promises.borrow_mut().clear();
1083
1084 self.owner_global()
1085 .task_manager()
1086 .dom_manipulation_task_source()
1087 .queue(task!(fulfill_image_decode_promises: move |cx| {
1088 for trusted_promise in trusted_image_decode_promises {
1089 trusted_promise.root().resolve_native(cx, &());
1090 }
1091 }));
1092 }
1093
1094 fn reject_image_decode_promises(&self) {
1096 if self.image_decode_promises.borrow().is_empty() {
1097 return;
1098 }
1099
1100 let trusted_image_decode_promises: Vec<TrustedPromise> = self
1103 .image_decode_promises
1104 .borrow()
1105 .iter()
1106 .map(|promise| TrustedPromise::new(promise.clone()))
1107 .collect();
1108
1109 self.image_decode_promises.borrow_mut().clear();
1110
1111 self.owner_global()
1112 .task_manager()
1113 .dom_manipulation_task_source()
1114 .queue(task!(reject_image_decode_promises: move |cx| {
1115 for trusted_promise in trusted_image_decode_promises {
1116 trusted_promise.root().reject_error(cx, Error::Encoding(Some("Image could not be decoded".into())));
1117 }
1118 }));
1119 }
1120
1121 fn finish_reacting_to_environment_change(
1123 &self,
1124 selected_source: USVString,
1125 generation: u32,
1126 selected_pixel_density: f64,
1127 ) {
1128 let this = Trusted::new(self);
1131
1132 self.owner_global()
1133 .task_manager()
1134 .dom_manipulation_task_source()
1135 .queue(task!(image_load_event: move |cx| {
1136 let this = this.root();
1137
1138 if this.generation.get() != generation {
1141 this.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
1142 this.image_request.set(ImageRequestPhase::Current);
1143 return;
1144 }
1145
1146 *this.last_selected_source.borrow_mut() = Some(selected_source);
1149
1150 {
1151 let mut pending_request = this.pending_request.borrow_mut();
1152
1153 pending_request.state = State::CompletelyAvailable;
1155
1156 pending_request.current_pixel_density = Some(selected_pixel_density);
1157
1158 mem::swap(&mut *this.current_request.borrow_mut(), &mut *pending_request);
1164 }
1165
1166 this.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
1167 this.image_request.set(ImageRequestPhase::Current);
1168
1169 this.upcast::<Node>().dirty(cx.no_gc(), NodeDamage::Other);
1171
1172 this.upcast::<EventTarget>().fire_event(cx, atom!("load"));
1174 }));
1175 }
1176
1177 fn uses_srcset_or_picture(&self) -> bool {
1179 let element = self.upcast::<Element>();
1180
1181 let has_srcset_attribute = element.has_attribute(&local_name!("srcset"));
1182 let has_parent_picture = element
1183 .upcast::<Node>()
1184 .GetParentElement()
1185 .is_some_and(|parent| parent.is::<HTMLPictureElement>());
1186 has_srcset_attribute || has_parent_picture
1187 }
1188
1189 fn new_inherited(
1190 local_name: LocalName,
1191 prefix: Option<Prefix>,
1192 document: &Document,
1193 creator: ElementCreator,
1194 ) -> HTMLImageElement {
1195 HTMLImageElement {
1196 htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
1197 image_request: Cell::new(ImageRequestPhase::Current),
1198 current_request: DomRefCell::new(ImageRequest {
1199 state: State::Unavailable,
1200 parsed_url: None,
1201 source_url: None,
1202 image: None,
1203 metadata: None,
1204 blocker: DomRefCell::new(None),
1205 final_url: None,
1206 current_pixel_density: None,
1207 }),
1208 pending_request: DomRefCell::new(ImageRequest {
1209 state: State::Unavailable,
1210 parsed_url: None,
1211 source_url: None,
1212 image: None,
1213 metadata: None,
1214 blocker: DomRefCell::new(None),
1215 final_url: None,
1216 current_pixel_density: None,
1217 }),
1218 form_owner: Default::default(),
1219 generation: Default::default(),
1220 source_set: DomRefCell::new(SourceSet::new()),
1221 dimension_attribute_source: Default::default(),
1222 last_selected_source: DomRefCell::new(None),
1223 image_decode_promises: DomRefCell::new(vec![]),
1224 line_number: creator.return_line_number(),
1225 }
1226 }
1227
1228 pub(crate) fn new(
1229 cx: &mut js::context::JSContext,
1230 local_name: LocalName,
1231 prefix: Option<Prefix>,
1232 document: &Document,
1233 proto: Option<HandleObject>,
1234 creator: ElementCreator,
1235 ) -> DomRoot<HTMLImageElement> {
1236 let image_element = Node::reflect_node_with_proto(
1237 cx,
1238 Box::new(HTMLImageElement::new_inherited(
1239 local_name, prefix, document, creator,
1240 )),
1241 document,
1242 proto,
1243 );
1244 image_element
1245 .dimension_attribute_source
1246 .set(Some(image_element.upcast()));
1247 image_element
1248 }
1249
1250 pub(crate) fn areas(&self) -> Option<Vec<DomRoot<HTMLAreaElement>>> {
1251 let elem = self.upcast::<Element>();
1252 let value = elem.get_attribute_string_value(&local_name!("usemap"))?;
1253
1254 if value.is_empty() || !value.is_char_boundary(1) {
1255 return None;
1256 }
1257
1258 let (first, last) = value.split_at(1);
1259
1260 if first != "#" || last.is_empty() {
1261 return None;
1262 }
1263
1264 let useMapElements = self
1265 .owner_document()
1266 .upcast::<Node>()
1267 .traverse_preorder(ShadowIncluding::No)
1268 .filter_map(DomRoot::downcast::<HTMLMapElement>)
1269 .find(|n| {
1270 n.upcast::<Element>()
1271 .get_name()
1272 .is_some_and(|n| *n == *last)
1273 });
1274
1275 useMapElements.map(|mapElem| mapElem.get_area_elements())
1276 }
1277
1278 pub(crate) fn same_origin(&self, origin: &MutableOrigin) -> bool {
1279 if let Some(ref image) = self.current_request.borrow().image {
1280 return image.cors_status() == CorsStatus::Safe;
1281 }
1282
1283 self.current_request
1284 .borrow()
1285 .final_url
1286 .as_ref()
1287 .is_some_and(|url| url.scheme() == "data" || url.origin().same_origin(origin))
1288 }
1289
1290 fn generation_id(&self) -> u32 {
1291 self.generation.get()
1292 }
1293
1294 fn load_broken_image_icon(&self, no_gc: &NoGC) {
1295 let window = self.owner_window();
1296 let Some(broken_image_icon) = window.image_cache().get_broken_image_icon() else {
1297 return;
1298 };
1299
1300 self.current_request.borrow_mut().metadata = Some(broken_image_icon.metadata);
1301 self.current_request.borrow_mut().image = Some(Image::Raster(broken_image_icon));
1302 self.upcast::<Node>().dirty(no_gc, NodeDamage::Other);
1303 }
1304
1305 pub(crate) fn full_image_url_for_user_interface(&self) -> Option<ServoUrl> {
1308 self.owner_document()
1309 .base_url()
1310 .join(&self.CurrentSrc())
1311 .ok()
1312 }
1313
1314 pub(crate) fn set_dimension_attribute_source(&self, value: Option<&Element>) {
1315 self.dimension_attribute_source.set(value)
1316 }
1317}
1318
1319#[derive(JSTraceable, MallocSizeOf)]
1320pub(crate) enum ImageElementMicrotask {
1321 UpdateImageData {
1322 elem: Dom<HTMLImageElement>,
1323 generation: u32,
1324 },
1325 EnvironmentChanges {
1326 elem: Dom<HTMLImageElement>,
1327 generation: u32,
1328 },
1329 Decode {
1330 elem: Dom<HTMLImageElement>,
1331 #[conditional_malloc_size_of]
1332 promise: Rc<Promise>,
1333 },
1334}
1335
1336impl MicrotaskRunnable for ImageElementMicrotask {
1337 fn handler(&self, cx: &mut js::context::JSContext) {
1338 let mut realm = match self {
1339 &ImageElementMicrotask::UpdateImageData { ref elem, .. } |
1340 &ImageElementMicrotask::EnvironmentChanges { ref elem, .. } |
1341 &ImageElementMicrotask::Decode { ref elem, .. } => enter_auto_realm(cx, &**elem),
1342 };
1343 let cx = &mut realm;
1344 match *self {
1345 ImageElementMicrotask::UpdateImageData {
1346 ref elem,
1347 ref generation,
1348 } => {
1349 if elem.generation.get() == *generation {
1353 elem.update_the_image_data_sync_steps(cx);
1354 }
1355 },
1356 ImageElementMicrotask::EnvironmentChanges {
1357 ref elem,
1358 ref generation,
1359 } => {
1360 elem.react_to_environment_changes_sync_steps(*generation, cx);
1361 },
1362 ImageElementMicrotask::Decode {
1363 ref elem,
1364 ref promise,
1365 } => {
1366 elem.react_to_decode_image_sync_steps(cx, promise.clone());
1367 },
1368 }
1369 }
1370}
1371
1372impl<'dom> LayoutDom<'dom, HTMLImageElement> {
1373 #[expect(unsafe_code)]
1374 fn current_request(self) -> &'dom ImageRequest {
1375 unsafe { self.unsafe_get().current_request.borrow_for_layout() }
1376 }
1377
1378 #[expect(unsafe_code)]
1379 fn dimension_attribute_source(self) -> LayoutDom<'dom, Element> {
1380 unsafe {
1381 self.unsafe_get()
1382 .dimension_attribute_source
1383 .to_layout()
1384 .expect("dimension attribute source should be always non-null")
1385 }
1386 }
1387
1388 pub(crate) fn image_url(self) -> Option<ServoUrl> {
1389 self.current_request().parsed_url.clone()
1390 }
1391
1392 pub(crate) fn image_data(self) -> (Option<Image>, Option<ImageMetadata>) {
1393 let current_request = self.current_request();
1394 (current_request.image.clone(), current_request.metadata)
1395 }
1396
1397 pub(crate) fn image_density(self) -> Option<f64> {
1398 self.current_request().current_pixel_density
1399 }
1400
1401 pub(crate) fn showing_broken_image_icon(self) -> bool {
1402 matches!(self.current_request().state, State::Broken)
1403 }
1404
1405 pub(crate) fn get_width(self) -> LengthOrPercentageOrAuto {
1406 self.dimension_attribute_source()
1407 .get_attr_for_layout(&ns!(), &local_name!("width"))
1408 .map(AttrValue::as_dimension)
1409 .cloned()
1410 .unwrap_or(LengthOrPercentageOrAuto::Auto)
1411 }
1412
1413 pub(crate) fn get_height(self) -> LengthOrPercentageOrAuto {
1414 self.dimension_attribute_source()
1415 .get_attr_for_layout(&ns!(), &local_name!("height"))
1416 .map(AttrValue::as_dimension)
1417 .cloned()
1418 .unwrap_or(LengthOrPercentageOrAuto::Auto)
1419 }
1420}
1421
1422impl HTMLImageElementMethods<crate::DomTypeHolder> for HTMLImageElement {
1423 fn Image(
1425 cx: &mut JSContext,
1426 window: &Window,
1427 proto: Option<HandleObject>,
1428 width: Option<u32>,
1429 height: Option<u32>,
1430 ) -> Fallible<DomRoot<HTMLImageElement>> {
1431 let document = window.Document();
1433
1434 let element = Element::create(
1437 cx,
1438 QualName::new(None, ns!(html), local_name!("img")),
1439 None,
1440 &document,
1441 ElementCreator::ScriptCreated,
1442 CustomElementCreationMode::Synchronous,
1443 proto,
1444 );
1445
1446 let image = DomRoot::downcast::<HTMLImageElement>(element).unwrap();
1447
1448 if let Some(w) = width {
1450 image.SetWidth(cx, w);
1451 }
1452
1453 if let Some(h) = height {
1456 image.SetHeight(cx, h);
1457 }
1458
1459 Ok(image)
1461 }
1462
1463 make_getter!(Alt, "alt");
1465 make_setter!(SetAlt, "alt");
1467
1468 make_url_getter!(Src, "src");
1470
1471 make_url_setter!(SetSrc, "src");
1473
1474 make_url_getter!(Srcset, "srcset");
1476 make_url_setter!(SetSrcset, "srcset");
1478
1479 make_getter!(Sizes, "sizes");
1481
1482 make_setter!(SetSizes, "sizes");
1484
1485 fn GetCrossOrigin(&self) -> Option<DOMString> {
1487 reflect_cross_origin_attribute(self.upcast::<Element>())
1488 }
1489
1490 fn SetCrossOrigin(&self, cx: &mut JSContext, value: Option<DOMString>) {
1492 set_cross_origin_attribute(cx, self.upcast::<Element>(), value);
1493 }
1494
1495 make_getter!(UseMap, "usemap");
1497 make_setter!(SetUseMap, "usemap");
1499
1500 make_bool_getter!(IsMap, "ismap");
1502 make_bool_setter!(SetIsMap, "ismap");
1504
1505 fn Width(&self) -> u32 {
1507 let node = self.upcast::<Node>();
1508 node.content_box()
1509 .map(|rect| rect.size.width.to_px() as u32)
1510 .unwrap_or_else(|| self.NaturalWidth())
1511 }
1512
1513 make_dimension_uint_setter!(SetWidth, "width");
1515
1516 fn Height(&self) -> u32 {
1518 let node = self.upcast::<Node>();
1519 node.content_box()
1520 .map(|rect| rect.size.height.to_px() as u32)
1521 .unwrap_or_else(|| self.NaturalHeight())
1522 }
1523
1524 make_dimension_uint_setter!(SetHeight, "height");
1526
1527 fn NaturalWidth(&self) -> u32 {
1529 let request = self.current_request.borrow();
1530 if matches!(request.state, State::Broken) {
1531 return 0;
1532 }
1533
1534 let pixel_density = request.current_pixel_density.unwrap_or(1f64);
1535 match request.metadata {
1536 Some(ref metadata) => (metadata.width as f64 / pixel_density) as u32,
1537 None => 0,
1538 }
1539 }
1540
1541 fn NaturalHeight(&self) -> u32 {
1543 let request = self.current_request.borrow();
1544 if matches!(request.state, State::Broken) {
1545 return 0;
1546 }
1547
1548 let pixel_density = request.current_pixel_density.unwrap_or(1f64);
1549 match request.metadata {
1550 Some(ref metadata) => (metadata.height as f64 / pixel_density) as u32,
1551 None => 0,
1552 }
1553 }
1554
1555 fn Complete(&self) -> bool {
1557 let element = self.upcast::<Element>();
1558
1559 let has_srcset_attribute = element.has_attribute(&local_name!("srcset"));
1562 if !element.has_attribute(&local_name!("src")) && !has_srcset_attribute {
1563 return true;
1564 }
1565
1566 let src = element.get_string_attribute(&local_name!("src"));
1568 if !has_srcset_attribute && src.is_empty() {
1569 return true;
1570 }
1571
1572 if matches!(self.image_request.get(), ImageRequestPhase::Current) &&
1576 matches!(
1577 self.current_request.borrow().state,
1578 State::CompletelyAvailable | State::Broken
1579 )
1580 {
1581 return true;
1582 }
1583
1584 false
1586 }
1587
1588 fn CurrentSrc(&self) -> USVString {
1590 let current_request = self.current_request.borrow();
1591 let url = ¤t_request.parsed_url;
1592 match *url {
1593 Some(ref url) => USVString(url.clone().into_string()),
1594 None => {
1595 let unparsed_url = ¤t_request.source_url;
1596 match *unparsed_url {
1597 Some(ref url) => url.clone(),
1598 None => USVString("".to_owned()),
1599 }
1600 },
1601 }
1602 }
1603
1604 fn ReferrerPolicy(&self) -> DOMString {
1606 reflect_referrer_policy_attribute(self.upcast::<Element>())
1607 }
1608
1609 make_setter!(SetReferrerPolicy, "referrerpolicy");
1611
1612 fn Decode(&self, cx: &mut JSContext) -> Rc<Promise> {
1614 let promise = Promise::new(cx, &self.global());
1616
1617 let task = ImageElementMicrotask::Decode {
1619 elem: Dom::from_ref(self),
1620 promise: promise.clone(),
1621 };
1622
1623 ScriptThread::await_stable_state(cx, Box::new(task));
1624
1625 promise
1627 }
1628
1629 make_getter!(Name, "name");
1631
1632 make_atomic_setter!(SetName, "name");
1634
1635 make_getter!(Align, "align");
1637
1638 make_setter!(SetAlign, "align");
1640
1641 make_uint_getter!(Hspace, "hspace");
1643
1644 make_uint_setter!(SetHspace, "hspace");
1646
1647 make_uint_getter!(Vspace, "vspace");
1649
1650 make_uint_setter!(SetVspace, "vspace");
1652
1653 make_url_getter!(LongDesc, "longdesc");
1655
1656 make_url_setter!(SetLongDesc, "longdesc");
1658
1659 make_getter!(Border, "border");
1661
1662 make_setter!(SetBorder, "border");
1664}
1665
1666impl VirtualMethods for HTMLImageElement {
1667 fn super_type(&self) -> Option<&dyn VirtualMethods> {
1668 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
1669 }
1670
1671 fn adopting_steps(&self, cx: &mut JSContext, old_doc: &Document) {
1672 self.super_type().unwrap().adopting_steps(cx, old_doc);
1673 self.update_the_image_data(cx);
1674 }
1675
1676 fn attribute_mutated(
1677 &self,
1678 cx: &mut js::context::JSContext,
1679 attr: AttrRef<'_>,
1680 mutation: AttributeMutation,
1681 ) {
1682 self.super_type()
1683 .unwrap()
1684 .attribute_mutated(cx, attr, mutation);
1685 match attr.local_name() {
1686 &local_name!("src") |
1687 &local_name!("srcset") |
1688 &local_name!("width") |
1689 &local_name!("sizes") => {
1690 self.update_the_image_data(cx);
1694 },
1695 &local_name!("crossorigin") => {
1696 let cross_origin_state_changed = match mutation {
1699 AttributeMutation::Removed | AttributeMutation::Set(None, _) => true,
1700 AttributeMutation::Set(Some(old_value), _) => {
1701 let new_cors_setting =
1702 CorsSettings::from_enumerated_attribute(&attr.value());
1703 let old_cors_setting = CorsSettings::from_enumerated_attribute(old_value);
1704
1705 new_cors_setting != old_cors_setting
1706 },
1707 };
1708
1709 if cross_origin_state_changed {
1710 self.update_the_image_data(cx);
1711 }
1712 },
1713 &local_name!("referrerpolicy") => {
1714 let referrer_policy_state_changed = match mutation {
1717 AttributeMutation::Removed | AttributeMutation::Set(None, _) => {
1718 ReferrerPolicy::from(&**attr.value()) != ReferrerPolicy::EmptyString
1719 },
1720 AttributeMutation::Set(Some(old_value), _) => {
1721 ReferrerPolicy::from(&**attr.value()) != ReferrerPolicy::from(&**old_value)
1722 },
1723 };
1724
1725 if referrer_policy_state_changed {
1726 self.update_the_image_data(cx);
1727 }
1728 },
1729 _ => {},
1730 }
1731 }
1732
1733 fn attribute_affects_presentational_hints(&self, attr: AttrRef<'_>) -> bool {
1734 match attr.local_name() {
1735 &local_name!("width") | &local_name!("height") => true,
1736 _ => self
1737 .super_type()
1738 .unwrap()
1739 .attribute_affects_presentational_hints(attr),
1740 }
1741 }
1742
1743 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
1744 match name {
1745 &local_name!("width") | &local_name!("height") => {
1746 AttrValue::from_dimension(value.into())
1747 },
1748 &local_name!("hspace") | &local_name!("vspace") => AttrValue::from_u32(value.into(), 0),
1749 _ => self
1750 .super_type()
1751 .unwrap()
1752 .parse_plain_attribute(name, value),
1753 }
1754 }
1755
1756 fn handle_event(&self, cx: &mut js::context::JSContext, event: &Event) {
1757 if event.type_() != atom!("click") {
1758 return;
1759 }
1760
1761 let Some(area_elements) = self.areas() else {
1762 return;
1763 };
1764
1765 let Some(mouse_event) = event.downcast::<MouseEvent>() else {
1767 return;
1768 };
1769
1770 let click_location = Point2D::new(
1771 mouse_event.ClientX().to_f32().unwrap(),
1772 mouse_event.ClientY().to_f32().unwrap(),
1773 );
1774 let bounding_rectangle = self.upcast::<Element>().GetBoundingClientRect(cx);
1775 let image_extents =
1776 Point2D::new(bounding_rectangle.X() as f32, bounding_rectangle.Y() as f32);
1777
1778 for area_element in area_elements {
1780 if !area_element.is_instance_activatable() {
1781 continue;
1782 }
1783 let activatable_area = match area_element.get_shape_from_coords() {
1784 Some(shape) => shape.absolute_coords(image_extents),
1785 None => return,
1786 };
1787 if activatable_area.hit_test(&click_location) {
1788 area_element.activation_behavior(cx, event, self.upcast());
1789 return;
1790 }
1791 }
1792 }
1793
1794 fn bind_to_tree(&self, cx: &mut JSContext, context: &BindContext) {
1796 if let Some(s) = self.super_type() {
1797 s.bind_to_tree(cx, context);
1798 }
1799 let document = self.owner_document();
1800 if context.tree_connected {
1801 document.register_responsive_image(self);
1802 }
1803
1804 let parent = self.upcast::<Node>().GetParentNode().unwrap();
1805
1806 if parent.is::<HTMLPictureElement>() && *parent == *context.parent {
1809 self.update_the_image_data(cx);
1810 }
1811 }
1812
1813 fn unbind_from_tree(&self, cx: &mut js::context::JSContext, context: &UnbindContext) {
1815 self.super_type().unwrap().unbind_from_tree(cx, context);
1816 let document = self.owner_document();
1817 document.unregister_responsive_image(self);
1818
1819 if context.parent.is::<HTMLPictureElement>() && !self.upcast::<Node>().has_parent() {
1822 self.update_the_image_data(cx);
1823 }
1824 }
1825
1826 fn moving_steps(&self, cx: &mut JSContext, context: &MoveContext) {
1828 if let Some(super_type) = self.super_type() {
1829 super_type.moving_steps(cx, context);
1830 }
1831
1832 if let Some(old_parent) = context.old_parent &&
1834 old_parent.is::<HTMLPictureElement>()
1835 {
1836 self.update_the_image_data(cx);
1837 }
1838 }
1839}
1840
1841impl FormControl for HTMLImageElement {
1842 fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
1843 self.form_owner.get()
1844 }
1845
1846 fn set_form_owner(&self, _cx: &mut JSContext, form: Option<&HTMLFormElement>) {
1847 self.form_owner.set(form);
1848 }
1849
1850 fn to_html_element(&self) -> &HTMLElement {
1851 self.upcast::<HTMLElement>()
1852 }
1853}
1854
1855#[derive(Clone)]
1856enum ChangeType {
1857 Environment {
1858 selected_source: USVString,
1859 selected_pixel_density: f64,
1860 },
1861 Element,
1862}