1use std::cell::Cell;
6use std::mem;
7use std::rc::Rc;
8use std::sync::Arc;
9
10use dom_struct::dom_struct;
11use euclid::default::Point2D;
12use html5ever::{LocalName, Prefix, QualName, local_name, ns};
13use js::context::{JSContext, NoGC};
14use js::rust::HandleObject;
15use mime::{self, Mime};
16use net_traits::image_cache::{
17 Image, ImageCache, ImageCacheResult, ImageLoadListener, ImageOrMetadataAvailable,
18 ImageResponse, PendingImageId,
19};
20use net_traits::request::{CorsSettings, Destination, Initiator, RequestId};
21use net_traits::{
22 FetchMetadata, FetchResponseMsg, NetworkError, ReferrerPolicy, ResourceFetchTiming,
23};
24use num_traits::ToPrimitive;
25use pixels::{CorsStatus, ImageMetadata, Snapshot};
26use script_bindings::cell::DomRefCell;
27use servo_url::ServoUrl;
28use servo_url::origin::MutableOrigin;
29use style::attr::{AttrValue, LengthOrPercentageOrAuto};
30
31use crate::document_loader::{LoadBlocker, LoadType};
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::fetch::{RequestWithGlobalScope, create_a_potential_cors_request};
70use crate::microtask::MicrotaskRunnable;
71use crate::network_listener::{self, FetchResponseListener, ResourceTimingListener};
72use crate::realms::enter_auto_realm;
73use crate::script_thread::ScriptThread;
74
75#[derive(Clone, Copy, JSTraceable, MallocSizeOf)]
77enum State {
78 Unavailable,
79 PartiallyAvailable,
80 CompletelyAvailable,
81 Broken,
82}
83
84#[derive(Clone, Copy, JSTraceable, MallocSizeOf)]
85enum ImageRequestPhase {
86 Pending,
87 Current,
88}
89
90#[derive(JSTraceable, MallocSizeOf)]
92#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
93struct ImageRequest {
94 state: State,
95 #[no_trace]
96 parsed_url: Option<ServoUrl>,
97 source_url: Option<USVString>,
98 blocker: DomRefCell<Option<LoadBlocker>>,
99 #[no_trace]
100 image: Option<Image>,
101 #[no_trace]
102 metadata: Option<ImageMetadata>,
103 #[no_trace]
104 final_url: Option<ServoUrl>,
105 current_pixel_density: Option<f64>,
106}
107
108#[dom_struct]
109pub(crate) struct HTMLImageElement {
110 htmlelement: HTMLElement,
111 image_request: Cell<ImageRequestPhase>,
112 current_request: DomRefCell<ImageRequest>,
113 pending_request: DomRefCell<ImageRequest>,
114 form_owner: MutNullableDom<HTMLFormElement>,
115 generation: Cell<u32>,
116 source_set: DomRefCell<SourceSet>,
117 dimension_attribute_source: MutNullableDom<Element>,
120 last_selected_source: DomRefCell<Option<USVString>>,
122 #[conditional_malloc_size_of]
123 image_decode_promises: DomRefCell<Vec<Rc<Promise>>>,
124 line_number: u64,
126}
127
128impl HTMLImageElement {
129 pub(crate) fn is_usable(&self) -> Fallible<bool> {
131 if let Some(image) = &self.current_request.borrow().image {
133 let intrinsic_size = image.metadata();
134 if intrinsic_size.width == 0 || intrinsic_size.height == 0 {
135 return Ok(false);
136 }
137 }
138
139 match self.current_request.borrow().state {
140 State::Broken => Err(Error::InvalidState(None)),
142 State::CompletelyAvailable => Ok(true),
143 State::PartiallyAvailable | State::Unavailable => Ok(false),
145 }
146 }
147
148 pub(crate) fn image_data(&self) -> Option<Image> {
149 self.current_request.borrow().image.clone()
150 }
151
152 pub(crate) fn get_raster_image_data(&self) -> Option<Snapshot> {
154 let Some(raster_image) = self.image_data()?.as_raster_image() else {
155 warn!("Vector image is not supported as raster image source");
156 return None;
157 };
158 Some(raster_image.as_snapshot())
159 }
160}
161
162struct ImageContext {
164 image_cache: Arc<dyn ImageCache>,
166 status: Result<(), NetworkError>,
168 id: PendingImageId,
170 aborted: bool,
172 doc: Trusted<Document>,
174 url: ServoUrl,
175 element: Trusted<HTMLImageElement>,
176}
177
178impl FetchResponseListener for ImageContext {
179 fn should_invoke(&self) -> bool {
180 !self.aborted
181 }
182
183 fn process_request_body(&mut self, _: RequestId) {}
184
185 fn process_response(
186 &mut self,
187 _: &mut js::context::JSContext,
188 request_id: RequestId,
189 metadata: Result<FetchMetadata, NetworkError>,
190 ) {
191 debug!("got {:?} for {:?}", metadata.as_ref().map(|_| ()), self.url);
192 self.image_cache.notify_pending_response(
193 self.id,
194 FetchResponseMsg::ProcessResponse(request_id, metadata.clone()),
195 );
196
197 let metadata = metadata.ok().map(|meta| match meta {
198 FetchMetadata::Unfiltered(m) => m,
199 FetchMetadata::Filtered { unsafe_, .. } => unsafe_,
200 });
201
202 if let Some(metadata) = metadata.as_ref() &&
204 let Some(ref content_type) = metadata.content_type
205 {
206 let mime: Mime = content_type.clone().into_inner().into();
207 if mime.type_() == mime::MULTIPART && mime.subtype().as_str() == "x-mixed-replace" {
208 self.aborted = true;
209 }
210 }
211
212 self.status = match metadata.as_ref().map(|m| m.status.clone()) {
215 None => Err(NetworkError::ResourceLoadError(
216 "No http status code received".to_owned(),
217 )),
218 Some(_) => Ok(()),
219 };
220 }
221
222 fn process_response_chunk(
223 &mut self,
224 _: &mut js::context::JSContext,
225 request_id: RequestId,
226 payload: Vec<u8>,
227 ) {
228 if self.status.is_ok() {
229 self.image_cache.notify_pending_response(
230 self.id,
231 FetchResponseMsg::ProcessResponseChunk(request_id, payload.into()),
232 );
233 }
234 }
235
236 fn process_response_eof(
237 self,
238 cx: &mut js::context::JSContext,
239 request_id: RequestId,
240 response: Result<(), NetworkError>,
241 timing: ResourceFetchTiming,
242 ) {
243 self.image_cache.notify_pending_response(
244 self.id,
245 FetchResponseMsg::ProcessResponseEOF(request_id, response.clone(), timing.clone()),
246 );
247 network_listener::submit_timing(cx, &self, &response, &timing);
248 }
249
250 fn process_csp_violations(
251 &mut self,
252 cx: &mut js::context::JSContext,
253 _request_id: RequestId,
254 violations: Vec<Violation>,
255 ) {
256 let global = &self.resource_timing_global();
257 let elem = self.element.root();
258 let source_position = elem
259 .upcast::<Element>()
260 .compute_source_position(elem.line_number as u32);
261 global.report_csp_violations(cx, violations, None, Some(source_position));
262 }
263
264 fn process_content_length(&mut self, request_id: RequestId, size: usize) {
265 self.image_cache.notify_pending_response(
266 self.id,
267 FetchResponseMsg::ProcessContentLength(request_id, size),
268 );
269 }
270}
271
272impl ResourceTimingListener for ImageContext {
273 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
274 (
275 InitiatorType::LocalName("img".to_string()),
276 self.url.clone(),
277 )
278 }
279
280 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
281 self.doc.root().global()
282 }
283}
284
285#[expect(non_snake_case)]
286impl HTMLImageElement {
287 fn fetch_image(&self, img_url: &ServoUrl, cx: &mut js::context::JSContext) {
289 let window = self.owner_window();
290
291 let cache_result = window.image_cache().get_cached_image_status(
292 img_url.clone(),
293 window.origin().immutable().clone(),
294 cors_setting_for_element(self.upcast()),
295 );
296
297 match cache_result {
298 ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable {
299 image,
300 url,
301 }) => self.process_image_response(ImageResponse::Loaded(image, url), cx),
302 ImageCacheResult::Available(ImageOrMetadataAvailable::MetadataAvailable(
303 metadata,
304 id,
305 )) => {
306 self.process_image_response(ImageResponse::MetadataLoaded(metadata), cx);
307 self.register_image_cache_callback(id, ChangeType::Element);
308 },
309 ImageCacheResult::Pending(id) => {
310 self.register_image_cache_callback(id, ChangeType::Element);
311 },
312 ImageCacheResult::ReadyForRequest(id) => {
313 self.fetch_request(img_url, id);
314 self.register_image_cache_callback(id, ChangeType::Element);
315 },
316 ImageCacheResult::FailedToLoadOrDecode => {
317 self.process_image_response(ImageResponse::FailedToLoadOrDecode, cx)
318 },
319 };
320 }
321
322 fn register_image_cache_callback(&self, id: PendingImageId, change_type: ChangeType) {
323 let trusted_node = Trusted::new(self);
324 let generation = self.generation_id();
325 let window = self.owner_window();
326 let callback = window.register_image_cache_listener(id, move |response, _| {
327 let trusted_node = trusted_node.clone();
328 let window = trusted_node.root().owner_window();
329 let callback_type = change_type.clone();
330
331 window
332 .as_global_scope()
333 .task_manager()
334 .networking_task_source()
335 .queue(task!(process_image_response: move |cx| {
336 let element = trusted_node.root();
337
338 if generation != element.generation_id() {
340 return;
341 }
342
343 match callback_type {
344 ChangeType::Element => {
345 element.process_image_response(response.response, cx);
346 }
347 ChangeType::Environment { selected_source, selected_pixel_density } => {
348 element.process_image_response_for_environment_change(
349 response.response, selected_source, generation, selected_pixel_density, cx
350 );
351 }
352 }
353 }));
354 });
355
356 window.image_cache().add_listener(ImageLoadListener::new(
357 callback,
358 window.pipeline_id(),
359 id,
360 ));
361 }
362
363 fn fetch_request(&self, img_url: &ServoUrl, id: PendingImageId) {
364 let document = self.owner_document();
365 let window = self.owner_window();
366
367 let context = ImageContext {
368 image_cache: window.image_cache(),
369 status: Ok(()),
370 id,
371 aborted: false,
372 doc: Trusted::new(&document),
373 element: Trusted::new(self),
374 url: img_url.clone(),
375 };
376
377 let global = document.global();
380 let mut request = create_a_potential_cors_request(
381 Some(window.webview_id()),
382 img_url.clone(),
383 Destination::Image,
384 cors_setting_for_element(self.upcast()),
385 None,
386 global.get_referrer(),
387 )
388 .with_global_scope(&global)
389 .referrer_policy(referrer_policy_for_element(self.upcast()));
390
391 if self.uses_srcset_or_picture() {
392 request = request.initiator(Initiator::ImageSet);
393 }
394
395 document.fetch_background(request, context);
398 }
399
400 fn handle_loaded_image(&self, image: Image, url: ServoUrl, cx: &mut js::context::JSContext) {
402 self.current_request.borrow_mut().metadata = Some(image.metadata());
403 self.current_request.borrow_mut().final_url = Some(url);
404 self.current_request.borrow_mut().image = Some(image);
405 self.current_request.borrow_mut().state = State::CompletelyAvailable;
406 LoadBlocker::terminate(&self.current_request.borrow().blocker, cx);
407 self.upcast::<Node>().dirty(cx.no_gc(), NodeDamage::Other);
409 self.resolve_image_decode_promises();
410 }
411
412 fn process_image_response(&self, image: ImageResponse, cx: &mut js::context::JSContext) {
414 let (trigger_image_load, trigger_image_error) = match (image, self.image_request.get()) {
420 (ImageResponse::Loaded(image, url), ImageRequestPhase::Current) => {
421 self.handle_loaded_image(image, url, cx);
422 (true, false)
423 },
424 (ImageResponse::Loaded(image, url), ImageRequestPhase::Pending) => {
425 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
426 self.image_request.set(ImageRequestPhase::Current);
427 self.handle_loaded_image(image, url, cx);
428 (true, false)
429 },
430 (ImageResponse::MetadataLoaded(meta), ImageRequestPhase::Current) => {
431 self.current_request.borrow_mut().state = State::PartiallyAvailable;
436 self.current_request.borrow_mut().metadata = Some(meta);
437 (false, false)
438 },
439 (ImageResponse::MetadataLoaded(_), ImageRequestPhase::Pending) => {
440 self.pending_request.borrow_mut().state = State::PartiallyAvailable;
444 (false, false)
445 },
446 (ImageResponse::FailedToLoadOrDecode, ImageRequestPhase::Current) => {
447 self.abort_request(State::Broken, ImageRequestPhase::Current, cx);
453
454 self.load_broken_image_icon(cx.no_gc());
455
456 (false, true)
460 },
461 (ImageResponse::FailedToLoadOrDecode, ImageRequestPhase::Pending) => {
462 self.abort_request(State::Broken, ImageRequestPhase::Current, cx);
468 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
469
470 mem::swap(
472 &mut *self.current_request.borrow_mut(),
473 &mut *self.pending_request.borrow_mut(),
474 );
475 self.image_request.set(ImageRequestPhase::Current);
476
477 self.current_request.borrow_mut().state = State::Broken;
479
480 self.load_broken_image_icon(cx.no_gc());
481
482 (false, true)
484 },
485 };
486
487 if trigger_image_load {
489 self.upcast::<EventTarget>().fire_event(cx, atom!("load"));
491 self.upcast::<EventTarget>()
492 .fire_event(cx, atom!("loadend"));
493 }
494
495 if trigger_image_error {
497 self.upcast::<EventTarget>().fire_event(cx, atom!("error"));
498 self.upcast::<EventTarget>()
499 .fire_event(cx, atom!("loadend"));
500 }
501 }
502
503 fn process_image_response_for_environment_change(
506 &self,
507 image: ImageResponse,
508 selected_source: USVString,
509 generation: u32,
510 selected_pixel_density: f64,
511 cx: &mut js::context::JSContext,
512 ) {
513 match image {
514 ImageResponse::Loaded(image, url) => {
515 self.pending_request.borrow_mut().metadata = Some(image.metadata());
516 self.pending_request.borrow_mut().final_url = Some(url);
517 self.pending_request.borrow_mut().image = Some(image);
518 self.finish_reacting_to_environment_change(
519 selected_source,
520 generation,
521 selected_pixel_density,
522 );
523 },
524 ImageResponse::FailedToLoadOrDecode => {
525 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
533 },
534 ImageResponse::MetadataLoaded(meta) => {
535 self.pending_request.borrow_mut().metadata = Some(meta);
536 },
537 };
538 }
539
540 fn abort_request(
542 &self,
543 state: State,
544 request: ImageRequestPhase,
545 cx: &mut js::context::JSContext,
546 ) {
547 let request = match request {
548 ImageRequestPhase::Current => &self.current_request,
549 ImageRequestPhase::Pending => &self.pending_request,
550 };
551 LoadBlocker::terminate(&request.borrow().blocker, cx);
552 let mut request = request.safe_borrow_mut(cx);
553 request.state = state;
554 request.image = None;
555 request.metadata = None;
556 request.current_pixel_density = None;
557
558 if matches!(state, State::Broken) {
559 self.reject_image_decode_promises();
560 } else if matches!(state, State::CompletelyAvailable) {
561 self.resolve_image_decode_promises();
562 }
563 }
564
565 fn init_image_request(
566 &self,
567 request: &DomRefCell<ImageRequest>,
568 url: &ServoUrl,
569 src: &USVString,
570 cx: &mut js::context::JSContext,
571 ) {
572 {
573 let mut request = request.borrow_mut();
574 request.parsed_url = Some(url.clone());
575 request.source_url = Some(src.clone());
576 request.image = None;
577 request.metadata = None;
578 }
579 let document = self.owner_document();
580 LoadBlocker::terminate(&request.borrow().blocker, cx);
581 *request.borrow_mut().blocker.borrow_mut() =
582 Some(LoadBlocker::new(&document, LoadType::Image(url.clone())));
583 }
584
585 fn prepare_image_request(
587 &self,
588 selected_source: &USVString,
589 selected_pixel_density: f64,
590 image_url: &ServoUrl,
591 cx: &mut js::context::JSContext,
592 ) {
593 match self.image_request.get() {
594 ImageRequestPhase::Pending => {
595 if self
598 .pending_request
599 .borrow()
600 .parsed_url
601 .as_ref()
602 .is_some_and(|parsed_url| *parsed_url == *image_url)
603 {
604 return;
605 }
606 },
607 ImageRequestPhase::Current => {
608 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
610
611 let (current_request_url, current_request_state) = {
613 let current_request = self.current_request.borrow();
614 (current_request.parsed_url.clone(), current_request.state)
615 };
616
617 match (current_request_url, current_request_state) {
618 (Some(parsed_url), State::PartiallyAvailable) => {
619 if parsed_url == *image_url {
625 return;
627 }
628
629 self.image_request.set(ImageRequestPhase::Pending);
633 self.init_image_request(
634 &self.pending_request,
635 image_url,
636 selected_source,
637 cx,
638 );
639 self.pending_request.borrow_mut().current_pixel_density =
640 Some(selected_pixel_density);
641 },
642 (_, State::Broken) | (_, State::Unavailable) => {
643 self.init_image_request(
647 &self.current_request,
648 image_url,
649 selected_source,
650 cx,
651 );
652 self.current_request.borrow_mut().current_pixel_density =
653 Some(selected_pixel_density);
654 self.reject_image_decode_promises();
655 },
656 (_, _) => {
657 self.image_request.set(ImageRequestPhase::Pending);
661 self.init_image_request(
662 &self.pending_request,
663 image_url,
664 selected_source,
665 cx,
666 );
667 self.pending_request.borrow_mut().current_pixel_density =
668 Some(selected_pixel_density);
669 },
670 }
671 },
672 }
673
674 self.fetch_image(image_url, cx);
675 }
676
677 fn update_the_image_data_sync_steps(&self, cx: &mut js::context::JSContext) {
679 let Some((selected_source, selected_pixel_density)) = self
682 .source_set
683 .borrow_mut()
684 .select_image_source(self.upcast::<Element>())
685 else {
686 self.abort_request(State::Broken, ImageRequestPhase::Current, cx);
691 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
692 self.image_request.set(ImageRequestPhase::Current);
693
694 let this = Trusted::new(self);
697
698 self.owner_global().task_manager().dom_manipulation_task_source().queue(
699 task!(image_null_source_error: move |cx| {
700 let this = this.root();
701
702 {
704 let mut current_request =
705 this.current_request.borrow_mut();
706 current_request.source_url = None;
707 current_request.parsed_url = None;
708 }
709
710 let has_src_attribute = this.upcast::<Element>().has_attribute(&local_name!("src"));
716
717 if has_src_attribute || this.uses_srcset_or_picture() {
718 this.upcast::<EventTarget>().fire_event(cx, atom!("error"));
719 }
720 }));
721
722 return;
724 };
725
726 let Ok(image_url) = self.owner_document().base_url().join(&selected_source) else {
729 self.abort_request(State::Broken, ImageRequestPhase::Current, cx);
734 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
735
736 self.image_request.set(ImageRequestPhase::Current);
738
739 let this = Trusted::new(self);
742
743 self.owner_global()
744 .task_manager()
745 .dom_manipulation_task_source()
746 .queue(task!(image_selected_source_error: move |cx| {
747 let this = this.root();
748
749 {
751 let mut current_request =
752 this.current_request.borrow_mut();
753 current_request.source_url = Some(selected_source);
754 current_request.parsed_url = None;
755 }
756
757 this.upcast::<EventTarget>().fire_event(cx, atom!("error"));
761 }));
762
763 return;
765 };
766
767 self.prepare_image_request(&selected_source, selected_pixel_density, &image_url, cx);
768 }
769
770 pub(crate) fn update_the_image_data(&self, cx: &mut js::context::JSContext) {
772 self.generation.set(self.generation.get() + 1);
774
775 if !self.owner_document().is_active() {
777 }
783
784 self.current_request.borrow_mut().state = State::Unavailable;
793
794 let mut selected_source = None;
798 let mut selected_pixel_density = None;
799
800 let src = self
804 .upcast::<Element>()
805 .get_string_attribute(&local_name!("src"));
806
807 if !self.uses_srcset_or_picture() && !src.is_empty() {
808 selected_source = Some(USVString(String::from(src)));
809 selected_pixel_density = Some(1_f64);
810 };
811
812 self.last_selected_source
814 .borrow_mut()
815 .clone_from(&selected_source);
816
817 if let Some(selected_source) = selected_source {
819 if let Ok(image_url) = self.owner_document().base_url().join(&selected_source) {
823 let window = self.owner_window();
827 let response = window.image_cache().get_image(
828 image_url.clone(),
829 window.origin().immutable().clone(),
830 cors_setting_for_element(self.upcast()),
831 );
832
833 if let Some(image) = response {
835 self.abort_request(State::CompletelyAvailable, ImageRequestPhase::Current, cx);
840 self.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
841
842 self.image_request.set(ImageRequestPhase::Current);
844
845 let mut current_request = self.current_request.borrow_mut();
848 current_request.metadata = Some(image.metadata());
849 current_request.image = Some(image);
850 current_request.final_url = Some(image_url.clone());
851
852 self.upcast::<Node>().dirty(cx.no_gc(), NodeDamage::Other);
855
856 current_request.current_pixel_density = selected_pixel_density;
859
860 let this = Trusted::new(self);
863
864 self.owner_global()
865 .task_manager()
866 .dom_manipulation_task_source()
867 .queue(task!(image_load_event: move |cx| {
868 let this = this.root();
869
870 {
875 let mut current_request =
876 this.current_request.borrow_mut();
877 current_request.source_url = Some(selected_source);
878 current_request.parsed_url = Some(image_url);
879 }
880
881 this.upcast::<EventTarget>().fire_event(cx, atom!("load"));
885 }));
886
887 return;
889 }
890 }
891 }
892
893 let task = ImageElementMicrotask::UpdateImageData {
896 elem: Dom::from_ref(self),
897 generation: self.generation.get(),
898 };
899
900 ScriptThread::await_stable_state(cx, Box::new(task));
901 }
902
903 pub(crate) fn react_to_environment_changes(&self, cx: &JSContext) {
905 let task = ImageElementMicrotask::EnvironmentChanges {
907 elem: Dom::from_ref(self),
908 generation: self.generation.get(),
909 };
910
911 ScriptThread::await_stable_state(cx, Box::new(task));
912 }
913
914 fn react_to_environment_changes_sync_steps(
916 &self,
917 generation: u32,
918 cx: &mut js::context::JSContext,
919 ) {
920 let document = self.owner_document();
921 let has_pending_request = matches!(self.image_request.get(), ImageRequestPhase::Pending);
922
923 if !document.is_active() || !self.uses_srcset_or_picture() || has_pending_request {
927 return;
928 }
929
930 let Some((selected_source, selected_pixel_density)) = self
933 .source_set
934 .borrow_mut()
935 .select_image_source(self.upcast::<Element>())
936 else {
937 return;
939 };
940
941 let mut same_selected_source = self
944 .last_selected_source
945 .borrow()
946 .as_ref()
947 .is_some_and(|source| *source == selected_source);
948
949 same_selected_source = same_selected_source ||
953 self.current_request
954 .borrow()
955 .source_url
956 .as_ref()
957 .is_some_and(|source| *source == selected_source);
958
959 let same_selected_pixel_density = self
960 .current_request
961 .borrow()
962 .current_pixel_density
963 .is_some_and(|pixel_density| pixel_density == selected_pixel_density);
964
965 if same_selected_source && same_selected_pixel_density {
966 return;
967 }
968
969 let Ok(image_url) = document.base_url().join(&selected_source) else {
973 return;
974 };
975
976 self.image_request.set(ImageRequestPhase::Pending);
978 self.init_image_request(&self.pending_request, &image_url, &selected_source, cx);
979
980 let window = self.owner_window();
983 let cache_result = window.image_cache().get_cached_image_status(
984 image_url.clone(),
985 window.origin().immutable().clone(),
986 cors_setting_for_element(self.upcast()),
987 );
988
989 let change_type = ChangeType::Environment {
990 selected_source: selected_source.clone(),
991 selected_pixel_density,
992 };
993
994 match cache_result {
995 ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable { .. }) => {
996 self.finish_reacting_to_environment_change(
997 selected_source,
998 generation,
999 selected_pixel_density,
1000 );
1001 },
1002 ImageCacheResult::Available(ImageOrMetadataAvailable::MetadataAvailable(m, id)) => {
1003 self.process_image_response_for_environment_change(
1004 ImageResponse::MetadataLoaded(m),
1005 selected_source,
1006 generation,
1007 selected_pixel_density,
1008 cx,
1009 );
1010 self.register_image_cache_callback(id, change_type);
1011 },
1012 ImageCacheResult::FailedToLoadOrDecode => {
1013 self.process_image_response_for_environment_change(
1014 ImageResponse::FailedToLoadOrDecode,
1015 selected_source,
1016 generation,
1017 selected_pixel_density,
1018 cx,
1019 );
1020 },
1021 ImageCacheResult::ReadyForRequest(id) => {
1022 self.fetch_request(&image_url, id);
1023 self.register_image_cache_callback(id, change_type);
1024 },
1025 ImageCacheResult::Pending(id) => {
1026 self.register_image_cache_callback(id, change_type);
1027 },
1028 }
1029 }
1030
1031 fn react_to_decode_image_sync_steps(&self, cx: &mut JSContext, promise: Rc<Promise>) {
1033 if !self.owner_document().is_fully_active() ||
1037 matches!(self.current_request.borrow().state, State::Broken)
1038 {
1039 promise.reject_error(cx, Error::Encoding(None));
1040 } else if matches!(
1041 self.current_request.borrow().state,
1042 State::CompletelyAvailable
1043 ) {
1044 promise.resolve_native(cx, &());
1046 } else if matches!(self.current_request.borrow().state, State::Unavailable) &&
1047 self.current_request.borrow().source_url.is_none()
1048 {
1049 promise.reject_error(cx, Error::Encoding(None));
1054 } else {
1055 self.image_decode_promises.borrow_mut().push(promise);
1056 }
1057 }
1058
1059 fn resolve_image_decode_promises(&self) {
1061 if self.image_decode_promises.borrow().is_empty() {
1062 return;
1063 }
1064
1065 let trusted_image_decode_promises: Vec<TrustedPromise> = self
1068 .image_decode_promises
1069 .borrow()
1070 .iter()
1071 .map(|promise| TrustedPromise::new(promise.clone()))
1072 .collect();
1073
1074 self.image_decode_promises.borrow_mut().clear();
1075
1076 self.owner_global()
1077 .task_manager()
1078 .dom_manipulation_task_source()
1079 .queue(task!(fulfill_image_decode_promises: move |cx| {
1080 for trusted_promise in trusted_image_decode_promises {
1081 trusted_promise.root().resolve_native(cx, &());
1082 }
1083 }));
1084 }
1085
1086 fn reject_image_decode_promises(&self) {
1088 if self.image_decode_promises.borrow().is_empty() {
1089 return;
1090 }
1091
1092 let trusted_image_decode_promises: Vec<TrustedPromise> = self
1095 .image_decode_promises
1096 .borrow()
1097 .iter()
1098 .map(|promise| TrustedPromise::new(promise.clone()))
1099 .collect();
1100
1101 self.image_decode_promises.borrow_mut().clear();
1102
1103 self.owner_global()
1104 .task_manager()
1105 .dom_manipulation_task_source()
1106 .queue(task!(reject_image_decode_promises: move |cx| {
1107 for trusted_promise in trusted_image_decode_promises {
1108 trusted_promise.root().reject_error(cx, Error::Encoding(None));
1109 }
1110 }));
1111 }
1112
1113 fn finish_reacting_to_environment_change(
1115 &self,
1116 selected_source: USVString,
1117 generation: u32,
1118 selected_pixel_density: f64,
1119 ) {
1120 let this = Trusted::new(self);
1123
1124 self.owner_global()
1125 .task_manager()
1126 .dom_manipulation_task_source()
1127 .queue(task!(image_load_event: move |cx| {
1128 let this = this.root();
1129
1130 if this.generation.get() != generation {
1133 this.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
1134 this.image_request.set(ImageRequestPhase::Current);
1135 return;
1136 }
1137
1138 *this.last_selected_source.borrow_mut() = Some(selected_source);
1141
1142 {
1143 let mut pending_request = this.pending_request.borrow_mut();
1144
1145 pending_request.state = State::CompletelyAvailable;
1147
1148 pending_request.current_pixel_density = Some(selected_pixel_density);
1149
1150 mem::swap(&mut *this.current_request.borrow_mut(), &mut *pending_request);
1156 }
1157
1158 this.abort_request(State::Unavailable, ImageRequestPhase::Pending, cx);
1159 this.image_request.set(ImageRequestPhase::Current);
1160
1161 this.upcast::<Node>().dirty(cx.no_gc(), NodeDamage::Other);
1163
1164 this.upcast::<EventTarget>().fire_event(cx, atom!("load"));
1166 }));
1167 }
1168
1169 fn uses_srcset_or_picture(&self) -> bool {
1171 let element = self.upcast::<Element>();
1172
1173 let has_srcset_attribute = element.has_attribute(&local_name!("srcset"));
1174 let has_parent_picture = element
1175 .upcast::<Node>()
1176 .GetParentElement()
1177 .is_some_and(|parent| parent.is::<HTMLPictureElement>());
1178 has_srcset_attribute || has_parent_picture
1179 }
1180
1181 fn new_inherited(
1182 local_name: LocalName,
1183 prefix: Option<Prefix>,
1184 document: &Document,
1185 creator: ElementCreator,
1186 ) -> HTMLImageElement {
1187 HTMLImageElement {
1188 htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
1189 image_request: Cell::new(ImageRequestPhase::Current),
1190 current_request: DomRefCell::new(ImageRequest {
1191 state: State::Unavailable,
1192 parsed_url: None,
1193 source_url: None,
1194 image: None,
1195 metadata: None,
1196 blocker: DomRefCell::new(None),
1197 final_url: None,
1198 current_pixel_density: None,
1199 }),
1200 pending_request: DomRefCell::new(ImageRequest {
1201 state: State::Unavailable,
1202 parsed_url: None,
1203 source_url: None,
1204 image: None,
1205 metadata: None,
1206 blocker: DomRefCell::new(None),
1207 final_url: None,
1208 current_pixel_density: None,
1209 }),
1210 form_owner: Default::default(),
1211 generation: Default::default(),
1212 source_set: DomRefCell::new(SourceSet::new()),
1213 dimension_attribute_source: Default::default(),
1214 last_selected_source: DomRefCell::new(None),
1215 image_decode_promises: DomRefCell::new(vec![]),
1216 line_number: creator.return_line_number(),
1217 }
1218 }
1219
1220 pub(crate) fn new(
1221 cx: &mut js::context::JSContext,
1222 local_name: LocalName,
1223 prefix: Option<Prefix>,
1224 document: &Document,
1225 proto: Option<HandleObject>,
1226 creator: ElementCreator,
1227 ) -> DomRoot<HTMLImageElement> {
1228 let image_element = Node::reflect_node_with_proto(
1229 cx,
1230 Box::new(HTMLImageElement::new_inherited(
1231 local_name, prefix, document, creator,
1232 )),
1233 document,
1234 proto,
1235 );
1236 image_element
1237 .dimension_attribute_source
1238 .set(Some(image_element.upcast()));
1239 image_element
1240 }
1241
1242 pub(crate) fn areas(&self) -> Option<Vec<DomRoot<HTMLAreaElement>>> {
1243 let elem = self.upcast::<Element>();
1244 let value = elem.get_attribute_string_value(&local_name!("usemap"))?;
1245
1246 if value.is_empty() || !value.is_char_boundary(1) {
1247 return None;
1248 }
1249
1250 let (first, last) = value.split_at(1);
1251
1252 if first != "#" || last.is_empty() {
1253 return None;
1254 }
1255
1256 let useMapElements = self
1257 .owner_document()
1258 .upcast::<Node>()
1259 .traverse_preorder(ShadowIncluding::No)
1260 .filter_map(DomRoot::downcast::<HTMLMapElement>)
1261 .find(|n| {
1262 n.upcast::<Element>()
1263 .get_name()
1264 .is_some_and(|n| *n == *last)
1265 });
1266
1267 useMapElements.map(|mapElem| mapElem.get_area_elements())
1268 }
1269
1270 pub(crate) fn same_origin(&self, origin: &MutableOrigin) -> bool {
1271 if let Some(ref image) = self.current_request.borrow().image {
1272 return image.cors_status() == CorsStatus::Safe;
1273 }
1274
1275 self.current_request
1276 .borrow()
1277 .final_url
1278 .as_ref()
1279 .is_some_and(|url| url.scheme() == "data" || url.origin().same_origin(origin))
1280 }
1281
1282 fn generation_id(&self) -> u32 {
1283 self.generation.get()
1284 }
1285
1286 fn load_broken_image_icon(&self, no_gc: &NoGC) {
1287 let window = self.owner_window();
1288 let Some(broken_image_icon) = window.image_cache().get_broken_image_icon() else {
1289 return;
1290 };
1291
1292 self.current_request.borrow_mut().metadata = Some(broken_image_icon.metadata);
1293 self.current_request.borrow_mut().image = Some(Image::Raster(broken_image_icon));
1294 self.upcast::<Node>().dirty(no_gc, NodeDamage::Other);
1295 }
1296
1297 pub(crate) fn full_image_url_for_user_interface(&self) -> Option<ServoUrl> {
1300 self.owner_document()
1301 .base_url()
1302 .join(&self.CurrentSrc())
1303 .ok()
1304 }
1305
1306 pub(crate) fn set_dimension_attribute_source(&self, value: Option<&Element>) {
1307 self.dimension_attribute_source.set(value)
1308 }
1309}
1310
1311#[derive(JSTraceable, MallocSizeOf)]
1312pub(crate) enum ImageElementMicrotask {
1313 UpdateImageData {
1314 elem: Dom<HTMLImageElement>,
1315 generation: u32,
1316 },
1317 EnvironmentChanges {
1318 elem: Dom<HTMLImageElement>,
1319 generation: u32,
1320 },
1321 Decode {
1322 elem: Dom<HTMLImageElement>,
1323 #[conditional_malloc_size_of]
1324 promise: Rc<Promise>,
1325 },
1326}
1327
1328impl MicrotaskRunnable for ImageElementMicrotask {
1329 fn handler(&self, cx: &mut js::context::JSContext) {
1330 let mut realm = match self {
1331 &ImageElementMicrotask::UpdateImageData { ref elem, .. } |
1332 &ImageElementMicrotask::EnvironmentChanges { ref elem, .. } |
1333 &ImageElementMicrotask::Decode { ref elem, .. } => enter_auto_realm(cx, &**elem),
1334 };
1335 let cx = &mut realm;
1336 match *self {
1337 ImageElementMicrotask::UpdateImageData {
1338 ref elem,
1339 ref generation,
1340 } => {
1341 if elem.generation.get() == *generation {
1345 elem.update_the_image_data_sync_steps(cx);
1346 }
1347 },
1348 ImageElementMicrotask::EnvironmentChanges {
1349 ref elem,
1350 ref generation,
1351 } => {
1352 elem.react_to_environment_changes_sync_steps(*generation, cx);
1353 },
1354 ImageElementMicrotask::Decode {
1355 ref elem,
1356 ref promise,
1357 } => {
1358 elem.react_to_decode_image_sync_steps(cx, promise.clone());
1359 },
1360 }
1361 }
1362}
1363
1364impl<'dom> LayoutDom<'dom, HTMLImageElement> {
1365 #[expect(unsafe_code)]
1366 fn current_request(self) -> &'dom ImageRequest {
1367 unsafe { self.unsafe_get().current_request.borrow_for_layout() }
1368 }
1369
1370 #[expect(unsafe_code)]
1371 fn dimension_attribute_source(self) -> LayoutDom<'dom, Element> {
1372 unsafe {
1373 self.unsafe_get()
1374 .dimension_attribute_source
1375 .to_layout()
1376 .expect("dimension attribute source should be always non-null")
1377 }
1378 }
1379
1380 pub(crate) fn image_url(self) -> Option<ServoUrl> {
1381 self.current_request().parsed_url.clone()
1382 }
1383
1384 pub(crate) fn image_data(self) -> (Option<Image>, Option<ImageMetadata>) {
1385 let current_request = self.current_request();
1386 (current_request.image.clone(), current_request.metadata)
1387 }
1388
1389 pub(crate) fn image_density(self) -> Option<f64> {
1390 self.current_request().current_pixel_density
1391 }
1392
1393 pub(crate) fn showing_broken_image_icon(self) -> bool {
1394 matches!(self.current_request().state, State::Broken)
1395 }
1396
1397 pub(crate) fn get_width(self) -> LengthOrPercentageOrAuto {
1398 self.dimension_attribute_source()
1399 .get_attr_for_layout(&ns!(), &local_name!("width"))
1400 .map(AttrValue::as_dimension)
1401 .cloned()
1402 .unwrap_or(LengthOrPercentageOrAuto::Auto)
1403 }
1404
1405 pub(crate) fn get_height(self) -> LengthOrPercentageOrAuto {
1406 self.dimension_attribute_source()
1407 .get_attr_for_layout(&ns!(), &local_name!("height"))
1408 .map(AttrValue::as_dimension)
1409 .cloned()
1410 .unwrap_or(LengthOrPercentageOrAuto::Auto)
1411 }
1412}
1413
1414impl HTMLImageElementMethods<crate::DomTypeHolder> for HTMLImageElement {
1415 fn Image(
1417 cx: &mut JSContext,
1418 window: &Window,
1419 proto: Option<HandleObject>,
1420 width: Option<u32>,
1421 height: Option<u32>,
1422 ) -> Fallible<DomRoot<HTMLImageElement>> {
1423 let document = window.Document();
1425
1426 let element = Element::create(
1429 cx,
1430 QualName::new(None, ns!(html), local_name!("img")),
1431 None,
1432 &document,
1433 ElementCreator::ScriptCreated,
1434 CustomElementCreationMode::Synchronous,
1435 proto,
1436 );
1437
1438 let image = DomRoot::downcast::<HTMLImageElement>(element).unwrap();
1439
1440 if let Some(w) = width {
1442 image.SetWidth(cx, w);
1443 }
1444
1445 if let Some(h) = height {
1448 image.SetHeight(cx, h);
1449 }
1450
1451 Ok(image)
1453 }
1454
1455 make_getter!(Alt, "alt");
1457 make_setter!(SetAlt, "alt");
1459
1460 make_url_getter!(Src, "src");
1462
1463 make_url_setter!(SetSrc, "src");
1465
1466 make_url_getter!(Srcset, "srcset");
1468 make_url_setter!(SetSrcset, "srcset");
1470
1471 make_getter!(Sizes, "sizes");
1473
1474 make_setter!(SetSizes, "sizes");
1476
1477 fn GetCrossOrigin(&self) -> Option<DOMString> {
1479 reflect_cross_origin_attribute(self.upcast::<Element>())
1480 }
1481
1482 fn SetCrossOrigin(&self, cx: &mut JSContext, value: Option<DOMString>) {
1484 set_cross_origin_attribute(cx, self.upcast::<Element>(), value);
1485 }
1486
1487 make_getter!(UseMap, "usemap");
1489 make_setter!(SetUseMap, "usemap");
1491
1492 make_bool_getter!(IsMap, "ismap");
1494 make_bool_setter!(SetIsMap, "ismap");
1496
1497 fn Width(&self) -> u32 {
1499 let node = self.upcast::<Node>();
1500 node.content_box()
1501 .map(|rect| rect.size.width.to_px() as u32)
1502 .unwrap_or_else(|| self.NaturalWidth())
1503 }
1504
1505 make_dimension_uint_setter!(SetWidth, "width");
1507
1508 fn Height(&self) -> u32 {
1510 let node = self.upcast::<Node>();
1511 node.content_box()
1512 .map(|rect| rect.size.height.to_px() as u32)
1513 .unwrap_or_else(|| self.NaturalHeight())
1514 }
1515
1516 make_dimension_uint_setter!(SetHeight, "height");
1518
1519 fn NaturalWidth(&self) -> u32 {
1521 let request = self.current_request.borrow();
1522 if matches!(request.state, State::Broken) {
1523 return 0;
1524 }
1525
1526 let pixel_density = request.current_pixel_density.unwrap_or(1f64);
1527 match request.metadata {
1528 Some(ref metadata) => (metadata.width as f64 / pixel_density) as u32,
1529 None => 0,
1530 }
1531 }
1532
1533 fn NaturalHeight(&self) -> u32 {
1535 let request = self.current_request.borrow();
1536 if matches!(request.state, State::Broken) {
1537 return 0;
1538 }
1539
1540 let pixel_density = request.current_pixel_density.unwrap_or(1f64);
1541 match request.metadata {
1542 Some(ref metadata) => (metadata.height as f64 / pixel_density) as u32,
1543 None => 0,
1544 }
1545 }
1546
1547 fn Complete(&self) -> bool {
1549 let element = self.upcast::<Element>();
1550
1551 let has_srcset_attribute = element.has_attribute(&local_name!("srcset"));
1554 if !element.has_attribute(&local_name!("src")) && !has_srcset_attribute {
1555 return true;
1556 }
1557
1558 let src = element.get_string_attribute(&local_name!("src"));
1560 if !has_srcset_attribute && src.is_empty() {
1561 return true;
1562 }
1563
1564 if matches!(self.image_request.get(), ImageRequestPhase::Current) &&
1568 matches!(
1569 self.current_request.borrow().state,
1570 State::CompletelyAvailable | State::Broken
1571 )
1572 {
1573 return true;
1574 }
1575
1576 false
1578 }
1579
1580 fn CurrentSrc(&self) -> USVString {
1582 let current_request = self.current_request.borrow();
1583 let url = ¤t_request.parsed_url;
1584 match *url {
1585 Some(ref url) => USVString(url.clone().into_string()),
1586 None => {
1587 let unparsed_url = ¤t_request.source_url;
1588 match *unparsed_url {
1589 Some(ref url) => url.clone(),
1590 None => USVString("".to_owned()),
1591 }
1592 },
1593 }
1594 }
1595
1596 fn ReferrerPolicy(&self) -> DOMString {
1598 reflect_referrer_policy_attribute(self.upcast::<Element>())
1599 }
1600
1601 make_setter!(SetReferrerPolicy, "referrerpolicy");
1603
1604 fn Decode(&self, cx: &mut JSContext) -> Rc<Promise> {
1606 let promise = Promise::new(cx, &self.global());
1608
1609 let task = ImageElementMicrotask::Decode {
1611 elem: Dom::from_ref(self),
1612 promise: promise.clone(),
1613 };
1614
1615 ScriptThread::await_stable_state(cx, Box::new(task));
1616
1617 promise
1619 }
1620
1621 make_getter!(Name, "name");
1623
1624 make_atomic_setter!(SetName, "name");
1626
1627 make_getter!(Align, "align");
1629
1630 make_setter!(SetAlign, "align");
1632
1633 make_uint_getter!(Hspace, "hspace");
1635
1636 make_uint_setter!(SetHspace, "hspace");
1638
1639 make_uint_getter!(Vspace, "vspace");
1641
1642 make_uint_setter!(SetVspace, "vspace");
1644
1645 make_url_getter!(LongDesc, "longdesc");
1647
1648 make_url_setter!(SetLongDesc, "longdesc");
1650
1651 make_getter!(Border, "border");
1653
1654 make_setter!(SetBorder, "border");
1656}
1657
1658impl VirtualMethods for HTMLImageElement {
1659 fn super_type(&self) -> Option<&dyn VirtualMethods> {
1660 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
1661 }
1662
1663 fn adopting_steps(&self, cx: &mut JSContext, old_doc: &Document) {
1664 self.super_type().unwrap().adopting_steps(cx, old_doc);
1665 self.update_the_image_data(cx);
1666 }
1667
1668 fn attribute_mutated(
1669 &self,
1670 cx: &mut js::context::JSContext,
1671 attr: AttrRef<'_>,
1672 mutation: AttributeMutation,
1673 ) {
1674 self.super_type()
1675 .unwrap()
1676 .attribute_mutated(cx, attr, mutation);
1677 match attr.local_name() {
1678 &local_name!("src") |
1679 &local_name!("srcset") |
1680 &local_name!("width") |
1681 &local_name!("sizes") => {
1682 self.update_the_image_data(cx);
1686 },
1687 &local_name!("crossorigin") => {
1688 let cross_origin_state_changed = match mutation {
1691 AttributeMutation::Removed | AttributeMutation::Set(None, _) => true,
1692 AttributeMutation::Set(Some(old_value), _) => {
1693 let new_cors_setting =
1694 CorsSettings::from_enumerated_attribute(&attr.value());
1695 let old_cors_setting = CorsSettings::from_enumerated_attribute(old_value);
1696
1697 new_cors_setting != old_cors_setting
1698 },
1699 };
1700
1701 if cross_origin_state_changed {
1702 self.update_the_image_data(cx);
1703 }
1704 },
1705 &local_name!("referrerpolicy") => {
1706 let referrer_policy_state_changed = match mutation {
1709 AttributeMutation::Removed | AttributeMutation::Set(None, _) => {
1710 ReferrerPolicy::from(&**attr.value()) != ReferrerPolicy::EmptyString
1711 },
1712 AttributeMutation::Set(Some(old_value), _) => {
1713 ReferrerPolicy::from(&**attr.value()) != ReferrerPolicy::from(&**old_value)
1714 },
1715 };
1716
1717 if referrer_policy_state_changed {
1718 self.update_the_image_data(cx);
1719 }
1720 },
1721 _ => {},
1722 }
1723 }
1724
1725 fn attribute_affects_presentational_hints(&self, attr: AttrRef<'_>) -> bool {
1726 match attr.local_name() {
1727 &local_name!("width") | &local_name!("height") => true,
1728 _ => self
1729 .super_type()
1730 .unwrap()
1731 .attribute_affects_presentational_hints(attr),
1732 }
1733 }
1734
1735 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
1736 match name {
1737 &local_name!("width") | &local_name!("height") => {
1738 AttrValue::from_dimension(value.into())
1739 },
1740 &local_name!("hspace") | &local_name!("vspace") => AttrValue::from_u32(value.into(), 0),
1741 _ => self
1742 .super_type()
1743 .unwrap()
1744 .parse_plain_attribute(name, value),
1745 }
1746 }
1747
1748 fn handle_event(&self, cx: &mut js::context::JSContext, event: &Event) {
1749 if event.type_() != atom!("click") {
1750 return;
1751 }
1752
1753 let Some(area_elements) = self.areas() else {
1754 return;
1755 };
1756
1757 let Some(mouse_event) = event.downcast::<MouseEvent>() else {
1759 return;
1760 };
1761
1762 let click_location = Point2D::new(
1763 mouse_event.ClientX().to_f32().unwrap(),
1764 mouse_event.ClientY().to_f32().unwrap(),
1765 );
1766 let bounding_rectangle = self.upcast::<Element>().GetBoundingClientRect(cx);
1767 let image_extents =
1768 Point2D::new(bounding_rectangle.X() as f32, bounding_rectangle.Y() as f32);
1769
1770 for area_element in area_elements {
1772 if !area_element.is_instance_activatable() {
1773 continue;
1774 }
1775 let activatable_area = match area_element.get_shape_from_coords() {
1776 Some(shape) => shape.absolute_coords(image_extents),
1777 None => return,
1778 };
1779 if activatable_area.hit_test(&click_location) {
1780 area_element.activation_behavior(cx, event, self.upcast());
1781 return;
1782 }
1783 }
1784 }
1785
1786 fn bind_to_tree(&self, cx: &mut JSContext, context: &BindContext) {
1788 if let Some(s) = self.super_type() {
1789 s.bind_to_tree(cx, context);
1790 }
1791 let document = self.owner_document();
1792 if context.tree_connected {
1793 document.register_responsive_image(self);
1794 }
1795
1796 let parent = self.upcast::<Node>().GetParentNode().unwrap();
1797
1798 if parent.is::<HTMLPictureElement>() && *parent == *context.parent {
1801 self.update_the_image_data(cx);
1802 }
1803 }
1804
1805 fn unbind_from_tree(&self, cx: &mut js::context::JSContext, context: &UnbindContext) {
1807 self.super_type().unwrap().unbind_from_tree(cx, context);
1808 let document = self.owner_document();
1809 document.unregister_responsive_image(self);
1810
1811 if context.parent.is::<HTMLPictureElement>() && !self.upcast::<Node>().has_parent() {
1814 self.update_the_image_data(cx);
1815 }
1816 }
1817
1818 fn moving_steps(&self, cx: &mut JSContext, context: &MoveContext) {
1820 if let Some(super_type) = self.super_type() {
1821 super_type.moving_steps(cx, context);
1822 }
1823
1824 if let Some(old_parent) = context.old_parent &&
1826 old_parent.is::<HTMLPictureElement>()
1827 {
1828 self.update_the_image_data(cx);
1829 }
1830 }
1831}
1832
1833impl FormControl for HTMLImageElement {
1834 fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
1835 self.form_owner.get()
1836 }
1837
1838 fn set_form_owner(&self, _cx: &mut JSContext, form: Option<&HTMLFormElement>) {
1839 self.form_owner.set(form);
1840 }
1841
1842 fn to_html_element(&self) -> &HTMLElement {
1843 self.upcast::<HTMLElement>()
1844 }
1845}
1846
1847#[derive(Clone)]
1848enum ChangeType {
1849 Environment {
1850 selected_source: USVString,
1851 selected_pixel_density: f64,
1852 },
1853 Element,
1854}