1use std::cell::Cell;
6use std::sync::Arc;
7
8use bytes::Bytes;
9use dom_struct::dom_struct;
10use euclid::default::Point2D;
11use html5ever::{LocalName, Prefix, QualName, local_name, ns};
12use js::context::{JSContext, NoGC};
13use js::rust::HandleObject;
14use mime::{self, Mime};
15use net_traits::image_cache::{
16 Image, ImageCache, ImageCacheResult, ImageLoadListener, ImageOrMetadataAvailable,
17 ImageResponse, PendingImageId,
18};
19use net_traits::request::{CorsSettings, Destination, Initiator, RequestId};
20use net_traits::{
21 FetchMetadata, FetchResponseMsg, Metadata, NetworkError, ReferrerPolicy, ResourceFetchTiming,
22};
23use num_traits::ToPrimitive;
24use pixels::{CorsStatus, ImageMetadata, Snapshot};
25use script_bindings::cell::DomRefCell;
26use servo_base::cross_process_instant::CrossProcessInstant;
27use servo_url::ServoUrl;
28use servo_url::origin::MutableOrigin;
29use style::attr::{AttrValue, LengthOrPercentageOrAuto};
30
31use crate::dom::activation::Activatable;
32use crate::dom::bindings::codegen::Bindings::DOMRectBinding::DOMRect_Binding::DOMRectMethods;
33use crate::dom::bindings::codegen::Bindings::ElementBinding::Element_Binding::ElementMethods;
34use crate::dom::bindings::codegen::Bindings::HTMLImageElementBinding::HTMLImageElementMethods;
35use crate::dom::bindings::codegen::Bindings::MouseEventBinding::MouseEventMethods;
36use crate::dom::bindings::codegen::Bindings::NodeBinding::Node_Binding::NodeMethods;
37use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
38use crate::dom::bindings::error::{Error, Fallible};
39use crate::dom::bindings::inheritance::Castable;
40use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
41use crate::dom::bindings::reflector::DomGlobal;
42use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom, ToLayoutOptional};
43use crate::dom::bindings::str::{DOMString, USVString};
44use crate::dom::csp::{GlobalCspReporting, Violation};
45use crate::dom::document::Document;
46use crate::dom::element::attributes::storage::AttrRef;
47use crate::dom::element::{
48 AttributeMutation, CustomElementCreationMode, Element, ElementCreator,
49 cors_setting_for_element, referrer_policy_for_element, reflect_cross_origin_attribute,
50 reflect_referrer_policy_attribute, set_cross_origin_attribute,
51};
52use crate::dom::event::Event;
53use crate::dom::eventtarget::EventTarget;
54use crate::dom::globalscope::GlobalScope;
55use crate::dom::html::htmlareaelement::HTMLAreaElement;
56use crate::dom::html::htmlelement::HTMLElement;
57use crate::dom::html::htmlformelement::{FormControl, HTMLFormElement};
58use crate::dom::html::htmlmapelement::HTMLMapElement;
59use crate::dom::html::htmlpictureelement::HTMLPictureElement;
60use crate::dom::iterators::ShadowIncluding;
61use crate::dom::mouseevent::MouseEvent;
62use crate::dom::node::virtualmethods::VirtualMethods;
63use crate::dom::node::{BindContext, MoveContext, Node, NodeDamage, NodeTraits, UnbindContext};
64use crate::dom::performance::performanceresourcetiming::InitiatorType;
65use crate::dom::promise::Promise;
66use crate::dom::srcset::SourceSet;
67use crate::dom::window::Window;
68use crate::dom::{RootedPromise, TracedPromise};
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::job_queue::MicrotaskRunnable;
75
76#[derive(Clone, Copy, Default, JSTraceable, MallocSizeOf)]
78enum State {
79 #[default]
80 Unavailable,
81 PartiallyAvailable,
82 CompletelyAvailable,
83 Broken,
84}
85
86#[derive(Clone, Copy, JSTraceable, MallocSizeOf)]
87enum ImageRequestPhase {
88 Pending,
89 Current,
90}
91
92#[derive(Default, JSTraceable, MallocSizeOf)]
94#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
95struct ImageRequest {
96 state: State,
97 #[no_trace]
98 parsed_url: Option<ServoUrl>,
99 source_url: Option<USVString>,
100 blocker: DomRefCell<Option<LoadBlocker>>,
101 #[no_trace]
102 image: Option<Image>,
103 #[no_trace]
104 metadata: Option<ImageMetadata>,
105 #[no_trace]
106 final_url: Option<ServoUrl>,
107 #[no_trace]
109 load_time: Option<CrossProcessInstant>,
110 current_pixel_density: Option<f64>,
111}
112
113#[dom_struct]
114pub(crate) struct HTMLImageElement {
115 htmlelement: HTMLElement,
116 current_request: DomRefCell<Box<ImageRequest>>,
117 pending_request: DomRefCell<Option<Box<ImageRequest>>>,
118 form_owner: MutNullableDom<HTMLFormElement>,
119 source_set: DomRefCell<SourceSet>,
120 dimension_attribute_source: MutNullableDom<Element>,
123 last_selected_source: DomRefCell<Option<USVString>>,
125 image_decode_promises: DomRefCell<Vec<TracedPromise>>,
126 line_number: u64,
128 image_request: Cell<ImageRequestPhase>,
129 generation: Cell<u32>,
130}
131
132impl HTMLImageElement {
133 pub(crate) fn is_usable(&self) -> Fallible<bool> {
135 if let Some(image) = &self.current_request.borrow().image {
137 let intrinsic_size = image.metadata();
138 if intrinsic_size.width == 0 || intrinsic_size.height == 0 {
139 return Ok(false);
140 }
141 }
142
143 match self.current_request.borrow().state {
144 State::Broken => Err(Error::InvalidState(Some(
146 "Image element's state is broken".into(),
147 ))),
148 State::CompletelyAvailable => Ok(true),
149 State::PartiallyAvailable | State::Unavailable => Ok(false),
151 }
152 }
153
154 pub(crate) fn image_data(&self) -> Option<Image> {
155 self.current_request.borrow().image.clone()
156 }
157
158 pub(crate) fn load_time(&self) -> Option<CrossProcessInstant> {
160 self.current_request.borrow().load_time
161 }
162
163 pub(crate) fn get_raster_image_data(&self) -> Option<Snapshot> {
165 let Some(raster_image) = self.image_data()?.as_raster_image() else {
166 warn!("Vector image is not supported as raster image source");
167 return None;
168 };
169 Some(raster_image.as_snapshot())
170 }
171}
172
173struct ImageContext {
175 image_cache: Arc<dyn ImageCache>,
177 status: Result<(), NetworkError>,
179 id: PendingImageId,
181 aborted: bool,
183 doc: Trusted<Document>,
185 url: ServoUrl,
186 element: Trusted<HTMLImageElement>,
187}
188
189impl FetchResponseListener for ImageContext {
190 fn should_invoke(&self) -> bool {
191 !self.aborted
192 }
193
194 fn process_request_body(&mut self, _: RequestId) {}
195
196 fn process_response(
197 &mut self,
198 _: &mut JSContext,
199 request_id: RequestId,
200 metadata: Result<FetchMetadata, NetworkError>,
201 ) {
202 debug!("got {:?} for {:?}", metadata.as_ref().map(|_| ()), self.url);
203 self.image_cache.notify_pending_response(
204 self.id,
205 FetchResponseMsg::ProcessResponse(request_id, metadata.clone()),
206 );
207
208 let metadata: Option<Metadata> = metadata.ok().map(Into::into);
209
210 if let Some(metadata) = metadata.as_ref() &&
212 let Some(ref content_type) = metadata.content_type
213 {
214 let mime: Mime = content_type.clone().into_inner().into();
215 if mime.type_() == mime::MULTIPART && mime.subtype().as_str() == "x-mixed-replace" {
216 self.aborted = true;
217 }
218 }
219
220 self.status = match metadata.as_ref().map(|m| m.status.clone()) {
223 None => Err(NetworkError::ResourceLoadError(
224 "No http status code received".to_owned(),
225 )),
226 Some(_) => Ok(()),
227 };
228 }
229
230 fn process_response_chunk(&mut self, _: &mut JSContext, request_id: RequestId, payload: Bytes) {
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 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 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, cx: &mut JSContext, img_url: &ServoUrl) {
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(cx, ImageResponse::Loaded(image, url)),
305 ImageCacheResult::Available(ImageOrMetadataAvailable::MetadataAvailable(
306 metadata,
307 id,
308 )) => {
309 self.process_image_response(cx, ImageResponse::MetadataLoaded(metadata));
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(cx, ImageResponse::FailedToLoadOrDecode)
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(cx, response.response);
349 }
350 ChangeType::Environment { selected_source, selected_pixel_density } => {
351 element.process_image_response_for_environment_change(
352 cx, response.response, selected_source, generation, selected_pixel_density
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, cx: &mut JSContext, image: Image, url: ServoUrl) {
405 {
406 let mut current_request = self.current_request.borrow_mut();
407 current_request.metadata = Some(image.metadata());
408 current_request.final_url = Some(url);
409 current_request.image = Some(image);
410 current_request.state = State::CompletelyAvailable;
411 current_request.load_time = Some(CrossProcessInstant::now());
412 }
413
414 self.pending_request.borrow_mut().take();
415
416 LoadBlocker::terminate(&self.current_request.borrow().blocker, cx);
417 self.upcast::<Node>().dirty(cx.no_gc(), NodeDamage::Other);
419 self.resolve_image_decode_promises(cx);
420 }
421
422 fn process_image_response(&self, cx: &mut JSContext, image: ImageResponse) {
424 let (trigger_image_load, trigger_image_error) = match (image, self.image_request.get()) {
430 (ImageResponse::Loaded(image, url), ImageRequestPhase::Current) => {
431 self.handle_loaded_image(cx, image, url);
432 (true, false)
433 },
434 (ImageResponse::Loaded(image, url), ImageRequestPhase::Pending) => {
435 self.abort_request(cx, State::Unavailable, ImageRequestPhase::Pending);
436 self.image_request.set(ImageRequestPhase::Current);
437 self.handle_loaded_image(cx, image, url);
438 (true, false)
439 },
440 (ImageResponse::MetadataLoaded(meta), ImageRequestPhase::Current) => {
441 self.current_request.borrow_mut().state = State::PartiallyAvailable;
446 self.current_request.borrow_mut().metadata = Some(meta);
447 (false, false)
448 },
449 (ImageResponse::MetadataLoaded(_), ImageRequestPhase::Pending) => {
450 self.pending_request
454 .borrow_mut()
455 .get_or_insert_default()
456 .state = State::PartiallyAvailable;
457 (false, false)
458 },
459 (ImageResponse::FailedToLoadOrDecode, ImageRequestPhase::Current) => {
460 self.abort_request(cx, State::Broken, ImageRequestPhase::Current);
466
467 self.load_broken_image_icon(cx.no_gc());
468
469 (false, true)
473 },
474 (ImageResponse::FailedToLoadOrDecode, ImageRequestPhase::Pending) => {
475 self.abort_request(cx, State::Broken, ImageRequestPhase::Current);
481 self.abort_request(cx, State::Unavailable, ImageRequestPhase::Pending);
482
483 if self.pending_request.borrow().is_some() {
486 *self.current_request.borrow_mut() =
487 self.pending_request.borrow_mut().take().unwrap();
488 }
489 self.image_request.set(ImageRequestPhase::Current);
490
491 self.current_request.borrow_mut().state = State::Broken;
493
494 self.load_broken_image_icon(cx.no_gc());
495
496 (false, true)
498 },
499 };
500
501 if trigger_image_load {
503 self.upcast::<EventTarget>().fire_event(cx, atom!("load"));
505 self.upcast::<EventTarget>()
506 .fire_event(cx, atom!("loadend"));
507 }
508
509 if trigger_image_error {
511 self.upcast::<EventTarget>().fire_event(cx, atom!("error"));
512 self.upcast::<EventTarget>()
513 .fire_event(cx, atom!("loadend"));
514 }
515 }
516
517 fn process_image_response_for_environment_change(
520 &self,
521 cx: &mut JSContext,
522 image: ImageResponse,
523 selected_source: USVString,
524 generation: u32,
525 selected_pixel_density: f64,
526 ) {
527 match image {
528 ImageResponse::Loaded(image, url) => {
529 if let Some(pending_request) = self.pending_request.borrow_mut().as_mut() {
530 pending_request.metadata = Some(image.metadata());
531 pending_request.final_url = Some(url);
532 pending_request.image = Some(image);
533 }
534 self.finish_reacting_to_environment_change(
535 selected_source,
536 generation,
537 selected_pixel_density,
538 );
539 },
540 ImageResponse::FailedToLoadOrDecode => {
541 self.abort_request(cx, State::Unavailable, ImageRequestPhase::Pending);
549 },
550 ImageResponse::MetadataLoaded(meta) => {
551 self.pending_request
552 .borrow_mut()
553 .get_or_insert_default()
554 .metadata = Some(meta);
555 },
556 };
557 }
558
559 fn abort_request(&self, cx: &mut JSContext, state: State, request: ImageRequestPhase) {
561 match request {
562 ImageRequestPhase::Current => {
563 LoadBlocker::terminate(&self.current_request.borrow().blocker, cx);
564
565 let mut request = self.current_request.safe_borrow_mut(cx);
566 request.state = state;
567 request.image = None;
568 request.metadata = None;
569 request.current_pixel_density = None;
570 },
571 ImageRequestPhase::Pending => {
572 if let Some(pending_request) = &*self.pending_request.borrow() {
573 LoadBlocker::terminate(&pending_request.blocker, cx);
574 }
575 self.pending_request.borrow_mut().take();
576 },
577 };
578
579 if matches!(state, State::Broken) {
580 self.reject_image_decode_promises(cx);
581 } else if matches!(state, State::CompletelyAvailable) {
582 self.resolve_image_decode_promises(cx);
583 }
584 }
585
586 fn init_image_request(
587 &self,
588 cx: &mut JSContext,
589 request: &DomRefCell<Box<ImageRequest>>,
590 url: &ServoUrl,
591 src: &USVString,
592 ) {
593 {
594 let mut request = request.borrow_mut();
595 request.parsed_url = Some(url.clone());
596 request.source_url = Some(src.clone());
597 request.image = None;
598 request.metadata = None;
599 }
600 let document = self.owner_document();
601 LoadBlocker::terminate(&request.borrow().blocker, cx);
602 *request.borrow_mut().blocker.borrow_mut() =
603 Some(LoadBlocker::new(&document, LoadType::Image(url.clone())));
604 }
605
606 fn init_pending_image_request(
607 &self,
608 cx: &mut JSContext,
609 request: &DomRefCell<Option<Box<ImageRequest>>>,
610 url: &ServoUrl,
611 src: &USVString,
612 ) {
613 {
614 let mut request = request.safe_borrow_mut(cx);
615 let request = request.get_or_insert_default();
616 request.parsed_url = Some(url.clone());
617 request.source_url = Some(src.clone());
618 request.image = None;
619 request.metadata = None;
620 }
621 let document = self.owner_document();
622 LoadBlocker::terminate(
623 &request
624 .borrow()
625 .as_ref()
626 .expect("Just created a request")
627 .blocker,
628 cx,
629 );
630 *request
631 .safe_borrow_mut(cx)
632 .as_mut()
633 .expect("Just created a request")
634 .blocker
635 .borrow_mut() = Some(LoadBlocker::new(&document, LoadType::Image(url.clone())));
636 }
637
638 fn prepare_image_request(
640 &self,
641 cx: &mut JSContext,
642 selected_source: &USVString,
643 selected_pixel_density: f64,
644 image_url: &ServoUrl,
645 ) {
646 match self.image_request.get() {
647 ImageRequestPhase::Pending => {
648 if self
651 .pending_request
652 .borrow()
653 .as_ref()
654 .and_then(|pending_request| pending_request.parsed_url.as_ref())
655 .is_some_and(|parsed_url| *parsed_url == *image_url)
656 {
657 return;
658 }
659 },
660 ImageRequestPhase::Current => {
661 self.abort_request(cx, State::Unavailable, ImageRequestPhase::Pending);
663
664 let (current_request_url, current_request_state) = {
666 let current_request = self.current_request.borrow();
667 (current_request.parsed_url.clone(), current_request.state)
668 };
669
670 match (current_request_url, current_request_state) {
671 (Some(parsed_url), State::PartiallyAvailable) => {
672 if parsed_url == *image_url {
678 return;
680 }
681
682 self.image_request.set(ImageRequestPhase::Pending);
686 self.init_pending_image_request(
687 cx,
688 &self.pending_request,
689 image_url,
690 selected_source,
691 );
692 self.pending_request
693 .borrow_mut()
694 .as_mut()
695 .expect("Just created a pending request")
696 .current_pixel_density = Some(selected_pixel_density);
697 },
698 (_, State::Broken) | (_, State::Unavailable) => {
699 self.init_image_request(
703 cx,
704 &self.current_request,
705 image_url,
706 selected_source,
707 );
708 self.current_request.borrow_mut().current_pixel_density =
709 Some(selected_pixel_density);
710 self.reject_image_decode_promises(cx);
711 },
712 (_, _) => {
713 self.image_request.set(ImageRequestPhase::Pending);
717 self.init_pending_image_request(
718 cx,
719 &self.pending_request,
720 image_url,
721 selected_source,
722 );
723 self.pending_request
724 .borrow_mut()
725 .as_mut()
726 .expect("Just created a pending image request")
727 .current_pixel_density = Some(selected_pixel_density);
728 },
729 }
730 },
731 }
732
733 self.fetch_image(cx, image_url);
734 }
735
736 fn update_the_image_data_sync_steps(&self, cx: &mut JSContext) {
738 let Some((selected_source, selected_pixel_density)) = self
741 .source_set
742 .borrow_mut()
743 .select_image_source(cx.no_gc(), self.upcast::<Element>())
744 else {
745 self.abort_request(cx, State::Broken, ImageRequestPhase::Current);
750 self.abort_request(cx, State::Unavailable, ImageRequestPhase::Pending);
751 self.image_request.set(ImageRequestPhase::Current);
752
753 let this = Trusted::new(self);
756
757 self.owner_global().task_manager().dom_manipulation_task_source().queue(
758 task!(image_null_source_error: move |cx| {
759 let this = this.root();
760
761 {
763 let mut current_request =
764 this.current_request.borrow_mut();
765 current_request.source_url = None;
766 current_request.parsed_url = None;
767 }
768
769 let has_src_attribute = this.upcast::<Element>().has_attribute(&local_name!("src"));
775
776 if has_src_attribute || this.uses_srcset_or_picture() {
777 this.upcast::<EventTarget>().fire_event(cx, atom!("error"));
778 }
779 }));
780
781 return;
783 };
784
785 let Ok(image_url) = self.owner_document().base_url().join(&selected_source) else {
788 self.abort_request(cx, State::Broken, ImageRequestPhase::Current);
793 self.abort_request(cx, State::Unavailable, ImageRequestPhase::Pending);
794
795 self.image_request.set(ImageRequestPhase::Current);
797
798 let this = Trusted::new(self);
801
802 self.owner_global()
803 .task_manager()
804 .dom_manipulation_task_source()
805 .queue(task!(image_selected_source_error: move |cx| {
806 let this = this.root();
807
808 {
810 let mut current_request =
811 this.current_request.borrow_mut();
812 current_request.source_url = Some(selected_source);
813 current_request.parsed_url = None;
814 }
815
816 this.upcast::<EventTarget>().fire_event(cx, atom!("error"));
820 }));
821
822 return;
824 };
825
826 self.prepare_image_request(cx, &selected_source, selected_pixel_density, &image_url);
827 }
828
829 pub(crate) fn update_the_image_data(&self, cx: &mut JSContext) {
831 self.generation.set(self.generation.get() + 1);
833
834 if !self.owner_document().is_fully_active() {
836 }
842
843 self.current_request.borrow_mut().state = State::Unavailable;
852
853 let mut selected_source = None;
857 let mut selected_pixel_density = None;
858
859 let src = self
863 .upcast::<Element>()
864 .get_string_attribute(&local_name!("src"));
865
866 if !self.uses_srcset_or_picture() && !src.is_empty() {
867 selected_source = Some(USVString(String::from(src)));
868 selected_pixel_density = Some(1_f64);
869 };
870
871 self.last_selected_source
873 .borrow_mut()
874 .clone_from(&selected_source);
875
876 if let Some(selected_source) = selected_source {
878 if let Ok(image_url) = self.owner_document().base_url().join(&selected_source) {
882 let window = self.owner_window();
886 let response = window.image_cache().get_image(
887 image_url.clone(),
888 window.origin().immutable().clone(),
889 cors_setting_for_element(self.upcast()),
890 );
891
892 if let Some(image) = response {
894 self.abort_request(cx, State::CompletelyAvailable, ImageRequestPhase::Current);
899 self.abort_request(cx, State::Unavailable, ImageRequestPhase::Pending);
900
901 self.image_request.set(ImageRequestPhase::Current);
903
904 let mut current_request = self.current_request.borrow_mut();
907 current_request.metadata = Some(image.metadata());
908 current_request.image = Some(image);
909 current_request.final_url = Some(image_url.clone());
910
911 self.upcast::<Node>().dirty(cx.no_gc(), NodeDamage::Other);
914
915 current_request.current_pixel_density = selected_pixel_density;
918
919 let this = Trusted::new(self);
922
923 self.owner_global()
924 .task_manager()
925 .dom_manipulation_task_source()
926 .queue(task!(image_load_event: move |cx| {
927 let this = this.root();
928
929 {
934 let mut current_request =
935 this.current_request.borrow_mut();
936 current_request.source_url = Some(selected_source);
937 current_request.parsed_url = Some(image_url);
938 }
939
940 this.upcast::<EventTarget>().fire_event(cx, atom!("load"));
944 }));
945
946 return;
948 }
949 }
950 }
951
952 let task = ImageElementMicrotask::UpdateImageData {
955 elem: Dom::from_ref(self),
956 generation: self.generation.get(),
957 };
958
959 ScriptThread::await_stable_state(cx, Box::new(task));
960 }
961
962 pub(crate) fn react_to_environment_changes(&self, cx: &JSContext) {
964 let task = ImageElementMicrotask::EnvironmentChanges {
966 elem: Dom::from_ref(self),
967 generation: self.generation.get(),
968 };
969
970 ScriptThread::await_stable_state(cx, Box::new(task));
971 }
972
973 fn react_to_environment_changes_sync_steps(&self, cx: &mut JSContext, generation: u32) {
975 let document = self.owner_document();
976 let has_pending_request = matches!(self.image_request.get(), ImageRequestPhase::Pending);
977
978 if !document.is_fully_active() || !self.uses_srcset_or_picture() || has_pending_request {
982 return;
983 }
984
985 let Some((selected_source, selected_pixel_density)) = self
988 .source_set
989 .borrow_mut()
990 .select_image_source(cx.no_gc(), self.upcast::<Element>())
991 else {
992 return;
994 };
995
996 let mut same_selected_source = self
999 .last_selected_source
1000 .borrow()
1001 .as_ref()
1002 .is_some_and(|source| *source == selected_source);
1003
1004 same_selected_source = same_selected_source ||
1008 self.current_request
1009 .borrow()
1010 .source_url
1011 .as_ref()
1012 .is_some_and(|source| *source == selected_source);
1013
1014 let same_selected_pixel_density = self
1015 .current_request
1016 .borrow()
1017 .current_pixel_density
1018 .is_some_and(|pixel_density| pixel_density == selected_pixel_density);
1019
1020 if same_selected_source && same_selected_pixel_density {
1021 return;
1022 }
1023
1024 let Ok(image_url) = document.base_url().join(&selected_source) else {
1028 return;
1029 };
1030
1031 self.image_request.set(ImageRequestPhase::Pending);
1033 self.init_pending_image_request(cx, &self.pending_request, &image_url, &selected_source);
1034
1035 let window = self.owner_window();
1038 let cache_result = window.image_cache().get_cached_image_status(
1039 image_url.clone(),
1040 window.origin().immutable().clone(),
1041 cors_setting_for_element(self.upcast()),
1042 );
1043
1044 let change_type = ChangeType::Environment {
1045 selected_source: selected_source.clone(),
1046 selected_pixel_density,
1047 };
1048
1049 match cache_result {
1050 ImageCacheResult::Available(ImageOrMetadataAvailable::ImageAvailable { .. }) => {
1051 self.finish_reacting_to_environment_change(
1052 selected_source,
1053 generation,
1054 selected_pixel_density,
1055 );
1056 },
1057 ImageCacheResult::Available(ImageOrMetadataAvailable::MetadataAvailable(m, id)) => {
1058 self.process_image_response_for_environment_change(
1059 cx,
1060 ImageResponse::MetadataLoaded(m),
1061 selected_source,
1062 generation,
1063 selected_pixel_density,
1064 );
1065 self.register_image_cache_callback(id, change_type);
1066 },
1067 ImageCacheResult::FailedToLoadOrDecode => {
1068 self.process_image_response_for_environment_change(
1069 cx,
1070 ImageResponse::FailedToLoadOrDecode,
1071 selected_source,
1072 generation,
1073 selected_pixel_density,
1074 );
1075 },
1076 ImageCacheResult::ReadyForRequest(id) => {
1077 self.fetch_request(&image_url, id);
1078 self.register_image_cache_callback(id, change_type);
1079 },
1080 ImageCacheResult::Pending(id) => {
1081 self.register_image_cache_callback(id, change_type);
1082 },
1083 }
1084 }
1085
1086 fn react_to_decode_image_sync_steps(&self, cx: &mut JSContext, promise: &RootedPromise) {
1088 if !self.owner_document().is_fully_active() ||
1092 matches!(self.current_request.borrow().state, State::Broken)
1093 {
1094 promise.reject_error(cx, Error::Encoding(Some("Image element's owner document is not fully active or image element's request is broken".into())));
1095 } else if matches!(
1096 self.current_request.borrow().state,
1097 State::CompletelyAvailable
1098 ) {
1099 promise.resolve_native(cx, &());
1101 } else if matches!(self.current_request.borrow().state, State::Unavailable) &&
1102 self.current_request.borrow().source_url.is_none()
1103 {
1104 promise.reject_error(
1109 cx,
1110 Error::Encoding(Some(
1111 "Image element does not provide `src` or `srcset` attributes".into(),
1112 )),
1113 );
1114 } else {
1115 self.image_decode_promises
1116 .borrow_mut()
1117 .push(promise.to_traced());
1118 }
1119 }
1120
1121 fn resolve_image_decode_promises(&self, cx: &mut JSContext) {
1123 if self.image_decode_promises.borrow().is_empty() {
1124 return;
1125 }
1126
1127 let trusted_image_decode_promises: Vec<TrustedPromise> = self
1130 .image_decode_promises
1131 .borrow()
1132 .iter()
1133 .map(|promise| TrustedPromise::from(&promise.root(cx)))
1134 .collect();
1135
1136 self.image_decode_promises.borrow_mut().clear();
1137
1138 self.owner_global()
1139 .task_manager()
1140 .dom_manipulation_task_source()
1141 .queue(task!(fulfill_image_decode_promises: move |cx| {
1142 for trusted_promise in trusted_image_decode_promises {
1143 trusted_promise.root(cx).resolve_native(cx, &());
1144 }
1145 }));
1146 }
1147
1148 fn reject_image_decode_promises(&self, cx: &mut JSContext) {
1150 if self.image_decode_promises.borrow().is_empty() {
1151 return;
1152 }
1153
1154 let trusted_image_decode_promises: Vec<TrustedPromise> = self
1157 .image_decode_promises
1158 .borrow()
1159 .iter()
1160 .map(|promise| TrustedPromise::from(&promise.root(cx)))
1161 .collect();
1162
1163 self.image_decode_promises.borrow_mut().clear();
1164
1165 self.owner_global()
1166 .task_manager()
1167 .dom_manipulation_task_source()
1168 .queue(task!(reject_image_decode_promises: move |cx| {
1169 for trusted_promise in trusted_image_decode_promises {
1170 trusted_promise.root(cx).reject_error(cx, Error::Encoding(Some("Image could not be decoded".into())));
1171 }
1172 }));
1173 }
1174
1175 fn finish_reacting_to_environment_change(
1177 &self,
1178 selected_source: USVString,
1179 generation: u32,
1180 selected_pixel_density: f64,
1181 ) {
1182 let this = Trusted::new(self);
1185
1186 self.owner_global()
1187 .task_manager()
1188 .dom_manipulation_task_source()
1189 .queue(task!(image_load_event: move |cx| {
1190 let this = this.root();
1191
1192 if this.generation.get() != generation {
1195 this.abort_request(cx, State::Unavailable, ImageRequestPhase::Pending);
1196 this.image_request.set(ImageRequestPhase::Current);
1197 return;
1198 }
1199
1200 *this.last_selected_source.borrow_mut() = Some(selected_source);
1203
1204
1205 if let Some(pending_request) = &mut *this.pending_request.borrow_mut() {
1206 pending_request.state = State::CompletelyAvailable;
1208
1209 pending_request.current_pixel_density = Some(selected_pixel_density);
1210
1211 } else {
1216 log::error!("Pending request was null");
1217 return
1218 }
1219 *this.current_request.borrow_mut() = this.pending_request.borrow_mut().take().expect("Should have a pending request");
1220
1221
1222 this.abort_request(cx, State::Unavailable, ImageRequestPhase::Pending);
1223 this.image_request.set(ImageRequestPhase::Current);
1224
1225 this.upcast::<Node>().dirty(cx.no_gc(), NodeDamage::Other);
1227
1228 this.upcast::<EventTarget>().fire_event(cx, atom!("load"));
1230 }));
1231 }
1232
1233 fn uses_srcset_or_picture(&self) -> bool {
1235 let element = self.upcast::<Element>();
1236
1237 let has_srcset_attribute = element.has_attribute(&local_name!("srcset"));
1238 let has_parent_picture = element
1239 .upcast::<Node>()
1240 .GetParentElement()
1241 .is_some_and(|parent| parent.is::<HTMLPictureElement>());
1242 has_srcset_attribute || has_parent_picture
1243 }
1244
1245 fn new_inherited(
1246 local_name: LocalName,
1247 prefix: Option<Prefix>,
1248 document: &Document,
1249 creator: ElementCreator,
1250 ) -> HTMLImageElement {
1251 HTMLImageElement {
1252 htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
1253 image_request: Cell::new(ImageRequestPhase::Current),
1254 current_request: DomRefCell::new(Box::new(ImageRequest {
1255 state: State::Unavailable,
1256 parsed_url: None,
1257 source_url: None,
1258 image: None,
1259 metadata: None,
1260 blocker: DomRefCell::new(None),
1261 final_url: None,
1262 load_time: None,
1263 current_pixel_density: None,
1264 })),
1265 pending_request: DomRefCell::new(None),
1266 form_owner: Default::default(),
1267 generation: Default::default(),
1268 source_set: DomRefCell::new(SourceSet::new()),
1269 dimension_attribute_source: Default::default(),
1270 last_selected_source: DomRefCell::new(None),
1271 image_decode_promises: DomRefCell::new(vec![]),
1272 line_number: creator.return_line_number(),
1273 }
1274 }
1275
1276 pub(crate) fn new(
1277 cx: &mut JSContext,
1278 local_name: LocalName,
1279 prefix: Option<Prefix>,
1280 document: &Document,
1281 proto: Option<HandleObject>,
1282 creator: ElementCreator,
1283 ) -> DomRoot<HTMLImageElement> {
1284 let image_element = Node::reflect_node_with_proto(
1285 cx,
1286 Box::new(HTMLImageElement::new_inherited(
1287 local_name, prefix, document, creator,
1288 )),
1289 document,
1290 proto,
1291 );
1292 image_element
1293 .dimension_attribute_source
1294 .set(Some(image_element.upcast()));
1295 image_element
1296 }
1297
1298 pub(crate) fn areas(&self) -> Option<Vec<DomRoot<HTMLAreaElement>>> {
1299 let elem = self.upcast::<Element>();
1300 let value = elem.get_attribute_string_value(&local_name!("usemap"))?;
1301
1302 if value.is_empty() || !value.is_char_boundary(1) {
1303 return None;
1304 }
1305
1306 let (first, last) = value.split_at(1);
1307
1308 if first != "#" || last.is_empty() {
1309 return None;
1310 }
1311
1312 let useMapElements = self
1313 .owner_document()
1314 .upcast::<Node>()
1315 .traverse_preorder(ShadowIncluding::No)
1316 .filter_map(DomRoot::downcast::<HTMLMapElement>)
1317 .find(|n| {
1318 n.upcast::<Element>()
1319 .get_name()
1320 .is_some_and(|n| *n == *last)
1321 });
1322
1323 useMapElements.map(|mapElem| mapElem.get_area_elements())
1324 }
1325
1326 pub(crate) fn same_origin(&self, origin: &MutableOrigin) -> bool {
1327 if let Some(ref image) = self.current_request.borrow().image {
1328 return image.cors_status() == CorsStatus::Safe;
1329 }
1330
1331 self.current_request
1332 .borrow()
1333 .final_url
1334 .as_ref()
1335 .is_some_and(|url| url.scheme() == "data" || url.origin().same_origin(origin))
1336 }
1337
1338 fn generation_id(&self) -> u32 {
1339 self.generation.get()
1340 }
1341
1342 fn load_broken_image_icon(&self, no_gc: &NoGC) {
1343 let window = self.owner_window();
1344 let Some(broken_image_icon) = window.image_cache().get_broken_image_icon() else {
1345 return;
1346 };
1347
1348 self.current_request.borrow_mut().metadata = Some(broken_image_icon.metadata);
1349 self.current_request.borrow_mut().image = Some(Image::Raster(broken_image_icon));
1350 self.upcast::<Node>().dirty(no_gc, NodeDamage::Other);
1351 }
1352
1353 pub(crate) fn full_image_url_for_user_interface(&self) -> Option<ServoUrl> {
1356 self.owner_document()
1357 .base_url()
1358 .join(&self.CurrentSrc())
1359 .ok()
1360 }
1361
1362 pub(crate) fn set_dimension_attribute_source(&self, value: Option<&Element>) {
1363 self.dimension_attribute_source.set(value)
1364 }
1365}
1366
1367#[derive(JSTraceable, MallocSizeOf)]
1368pub(crate) enum ImageElementMicrotask {
1369 UpdateImageData {
1370 elem: Dom<HTMLImageElement>,
1371 generation: u32,
1372 },
1373 EnvironmentChanges {
1374 elem: Dom<HTMLImageElement>,
1375 generation: u32,
1376 },
1377 Decode {
1378 elem: Dom<HTMLImageElement>,
1379 promise: TracedPromise,
1380 },
1381}
1382
1383impl MicrotaskRunnable for ImageElementMicrotask {
1384 fn handler(&self, cx: &mut JSContext) {
1385 let mut realm = match self {
1386 &ImageElementMicrotask::UpdateImageData { ref elem, .. } |
1387 &ImageElementMicrotask::EnvironmentChanges { ref elem, .. } |
1388 &ImageElementMicrotask::Decode { ref elem, .. } => enter_auto_realm(cx, &**elem),
1389 };
1390 let cx = &mut realm;
1391 match *self {
1392 ImageElementMicrotask::UpdateImageData {
1393 ref elem,
1394 ref generation,
1395 } => {
1396 if elem.generation.get() == *generation {
1400 elem.update_the_image_data_sync_steps(cx);
1401 }
1402 },
1403 ImageElementMicrotask::EnvironmentChanges {
1404 ref elem,
1405 ref generation,
1406 } => {
1407 elem.react_to_environment_changes_sync_steps(cx, *generation);
1408 },
1409 ImageElementMicrotask::Decode {
1410 ref elem,
1411 ref promise,
1412 } => {
1413 let promise = promise.root(cx);
1414 elem.react_to_decode_image_sync_steps(cx, &promise);
1415 },
1416 }
1417 }
1418}
1419
1420impl<'dom> LayoutDom<'dom, HTMLImageElement> {
1421 #[expect(unsafe_code)]
1422 fn current_request(self) -> &'dom ImageRequest {
1423 unsafe { self.unsafe_get().current_request.borrow_for_layout() }
1424 }
1425
1426 #[expect(unsafe_code)]
1427 fn dimension_attribute_source(self) -> LayoutDom<'dom, Element> {
1428 unsafe {
1429 self.unsafe_get()
1430 .dimension_attribute_source
1431 .to_layout()
1432 .expect("dimension attribute source should be always non-null")
1433 }
1434 }
1435
1436 pub(crate) fn image_url(self) -> Option<ServoUrl> {
1437 self.current_request().parsed_url.clone()
1438 }
1439
1440 pub(crate) fn image_data(self) -> (Option<Image>, Option<ImageMetadata>) {
1441 let current_request = self.current_request();
1442 (current_request.image.clone(), current_request.metadata)
1443 }
1444
1445 pub(crate) fn image_density(self) -> Option<f64> {
1446 self.current_request().current_pixel_density
1447 }
1448
1449 pub(crate) fn showing_broken_image_icon(self) -> bool {
1450 matches!(self.current_request().state, State::Broken)
1451 }
1452
1453 pub(crate) fn width(self) -> LengthOrPercentageOrAuto {
1454 self.dimension_attribute_source()
1455 .dimension_attr_value(local_name!("width"))
1456 }
1457
1458 pub(crate) fn height(self) -> LengthOrPercentageOrAuto {
1459 self.dimension_attribute_source()
1460 .dimension_attr_value(local_name!("height"))
1461 }
1462
1463 pub(crate) fn margin_right_left(self) -> LengthOrPercentageOrAuto {
1464 self.dimension_attribute_source()
1465 .dimension_attr_value(local_name!("hspace"))
1466 }
1467
1468 pub(crate) fn margin_top_bottom(self) -> LengthOrPercentageOrAuto {
1469 self.dimension_attribute_source()
1470 .dimension_attr_value(local_name!("vspace"))
1471 }
1472}
1473
1474impl HTMLImageElementMethods<crate::DomTypeHolder> for HTMLImageElement {
1475 fn Image(
1477 cx: &mut JSContext,
1478 window: &Window,
1479 proto: Option<HandleObject>,
1480 width: Option<u32>,
1481 height: Option<u32>,
1482 ) -> Fallible<DomRoot<HTMLImageElement>> {
1483 let document = window.Document();
1485
1486 let element = Element::create(
1489 cx,
1490 QualName::new(None, ns!(html), local_name!("img")),
1491 None,
1492 &document,
1493 ElementCreator::ScriptCreated,
1494 CustomElementCreationMode::Synchronous,
1495 proto,
1496 );
1497
1498 let image = DomRoot::downcast::<HTMLImageElement>(element).unwrap();
1499
1500 if let Some(w) = width {
1502 image.SetWidth(cx, w);
1503 }
1504
1505 if let Some(h) = height {
1508 image.SetHeight(cx, h);
1509 }
1510
1511 Ok(image)
1513 }
1514
1515 make_getter!(Alt, "alt");
1517 make_setter!(SetAlt, "alt");
1519
1520 make_url_getter!(Src, "src");
1522
1523 make_url_setter!(SetSrc, "src");
1525
1526 make_url_getter!(Srcset, "srcset");
1528 make_url_setter!(SetSrcset, "srcset");
1530
1531 make_getter!(Sizes, "sizes");
1533
1534 make_setter!(SetSizes, "sizes");
1536
1537 fn GetCrossOrigin(&self) -> Option<DOMString> {
1539 reflect_cross_origin_attribute(self.upcast::<Element>())
1540 }
1541
1542 fn SetCrossOrigin(&self, cx: &mut JSContext, value: Option<DOMString>) {
1544 set_cross_origin_attribute(cx, self.upcast::<Element>(), value);
1545 }
1546
1547 make_getter!(UseMap, "usemap");
1549 make_setter!(SetUseMap, "usemap");
1551
1552 make_bool_getter!(IsMap, "ismap");
1554 make_bool_setter!(SetIsMap, "ismap");
1556
1557 fn Width(&self) -> u32 {
1559 let node = self.upcast::<Node>();
1560 node.content_box()
1561 .map(|rect| rect.size.width.to_px() as u32)
1562 .unwrap_or_else(|| self.NaturalWidth())
1563 }
1564
1565 make_dimension_uint_setter!(SetWidth, "width");
1567
1568 fn Height(&self) -> u32 {
1570 let node = self.upcast::<Node>();
1571 node.content_box()
1572 .map(|rect| rect.size.height.to_px() as u32)
1573 .unwrap_or_else(|| self.NaturalHeight())
1574 }
1575
1576 make_dimension_uint_setter!(SetHeight, "height");
1578
1579 fn NaturalWidth(&self) -> u32 {
1581 let request = self.current_request.borrow();
1582 if matches!(request.state, State::Broken) {
1583 return 0;
1584 }
1585
1586 let pixel_density = request.current_pixel_density.unwrap_or(1f64);
1587 match request.metadata {
1588 Some(ref metadata) => (metadata.width as f64 / pixel_density) as u32,
1589 None => 0,
1590 }
1591 }
1592
1593 fn NaturalHeight(&self) -> u32 {
1595 let request = self.current_request.borrow();
1596 if matches!(request.state, State::Broken) {
1597 return 0;
1598 }
1599
1600 let pixel_density = request.current_pixel_density.unwrap_or(1f64);
1601 match request.metadata {
1602 Some(ref metadata) => (metadata.height as f64 / pixel_density) as u32,
1603 None => 0,
1604 }
1605 }
1606
1607 fn Complete(&self) -> bool {
1609 let element = self.upcast::<Element>();
1610
1611 let has_srcset_attribute = element.has_attribute(&local_name!("srcset"));
1614 if !element.has_attribute(&local_name!("src")) && !has_srcset_attribute {
1615 return true;
1616 }
1617
1618 let src = element.get_string_attribute(&local_name!("src"));
1620 if !has_srcset_attribute && src.is_empty() {
1621 return true;
1622 }
1623
1624 if matches!(self.image_request.get(), ImageRequestPhase::Current) &&
1628 matches!(
1629 self.current_request.borrow().state,
1630 State::CompletelyAvailable | State::Broken
1631 )
1632 {
1633 return true;
1634 }
1635
1636 false
1638 }
1639
1640 fn CurrentSrc(&self) -> USVString {
1642 let current_request = self.current_request.borrow();
1643 let url = ¤t_request.parsed_url;
1644 match *url {
1645 Some(ref url) => USVString(url.clone().into_string()),
1646 None => {
1647 let unparsed_url = ¤t_request.source_url;
1648 match *unparsed_url {
1649 Some(ref url) => url.clone(),
1650 None => USVString(String::new()),
1651 }
1652 },
1653 }
1654 }
1655
1656 fn ReferrerPolicy(&self) -> DOMString {
1658 reflect_referrer_policy_attribute(self.upcast::<Element>())
1659 }
1660
1661 make_setter!(SetReferrerPolicy, "referrerpolicy");
1663
1664 fn Decode(&self, cx: &mut JSContext) -> RootedPromise {
1666 let promise = Promise::new(cx, &self.global());
1668
1669 let task = ImageElementMicrotask::Decode {
1671 elem: Dom::from_ref(self),
1672 promise: promise.to_traced(),
1673 };
1674
1675 ScriptThread::await_stable_state(cx, Box::new(task));
1676
1677 promise
1679 }
1680
1681 make_getter!(Name, "name");
1683
1684 make_atomic_setter!(SetName, "name");
1686
1687 make_getter!(Align, "align");
1689
1690 make_setter!(SetAlign, "align");
1692
1693 make_dimension_uint_getter!(Hspace, "hspace");
1695
1696 make_dimension_uint_setter!(SetHspace, "hspace");
1698
1699 make_dimension_uint_getter!(Vspace, "vspace");
1701
1702 make_dimension_uint_setter!(SetVspace, "vspace");
1704
1705 make_url_getter!(LongDesc, "longdesc");
1707
1708 make_url_setter!(SetLongDesc, "longdesc");
1710
1711 make_getter!(Border, "border");
1713
1714 make_setter!(SetBorder, "border");
1716}
1717
1718impl VirtualMethods for HTMLImageElement {
1719 fn super_type(&self) -> Option<&dyn VirtualMethods> {
1720 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
1721 }
1722
1723 fn adopting_steps(&self, cx: &mut JSContext, old_doc: &Document) {
1724 self.super_type().unwrap().adopting_steps(cx, old_doc);
1725 self.update_the_image_data(cx);
1726 }
1727
1728 fn attribute_mutated(
1729 &self,
1730 cx: &mut JSContext,
1731 attr: AttrRef<'_>,
1732 mutation: AttributeMutation,
1733 ) {
1734 self.super_type()
1735 .unwrap()
1736 .attribute_mutated(cx, attr, mutation);
1737
1738 match attr.local_name() {
1739 &local_name!("src") | &local_name!("srcset") | &local_name!("sizes") => {
1752 self.update_the_image_data(cx);
1753 },
1754 &local_name!("crossorigin") => {
1755 let cross_origin_state_changed = match mutation {
1758 AttributeMutation::Removed | AttributeMutation::Set(None) => true,
1759 AttributeMutation::Set(Some(old_value)) => {
1760 let new_cors_setting =
1761 CorsSettings::from_enumerated_attribute(&attr.value());
1762 let old_cors_setting = CorsSettings::from_enumerated_attribute(old_value);
1763
1764 new_cors_setting != old_cors_setting
1765 },
1766 };
1767
1768 if cross_origin_state_changed {
1769 self.update_the_image_data(cx);
1770 }
1771 },
1772 &local_name!("referrerpolicy") => {
1773 let referrer_policy_state_changed = match mutation {
1776 AttributeMutation::Removed | AttributeMutation::Set(None) => {
1777 ReferrerPolicy::from(&**attr.value()) != ReferrerPolicy::EmptyString
1778 },
1779 AttributeMutation::Set(Some(old_value)) => {
1780 ReferrerPolicy::from(&**attr.value()) != ReferrerPolicy::from(&**old_value)
1781 },
1782 };
1783
1784 if referrer_policy_state_changed {
1785 self.update_the_image_data(cx);
1786 }
1787 },
1788 _ => {},
1789 }
1790 }
1791
1792 fn attribute_affects_presentational_hints(&self, attr: AttrRef<'_>) -> bool {
1793 match attr.local_name() {
1794 &local_name!("width") |
1795 &local_name!("height") |
1796 &local_name!("hspace") |
1797 &local_name!("vspace") => true,
1798 _ => self
1799 .super_type()
1800 .unwrap()
1801 .attribute_affects_presentational_hints(attr),
1802 }
1803 }
1804
1805 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
1806 match name {
1807 &local_name!("width") |
1808 &local_name!("height") |
1809 &local_name!("hspace") |
1810 &local_name!("vspace") => AttrValue::from_dimension(value.into()),
1811 _ => self
1812 .super_type()
1813 .unwrap()
1814 .parse_plain_attribute(name, value),
1815 }
1816 }
1817
1818 fn handle_event(&self, cx: &mut JSContext, event: &Event) {
1819 if event.type_() != atom!("click") {
1820 return;
1821 }
1822
1823 let Some(area_elements) = self.areas() else {
1824 return;
1825 };
1826
1827 let Some(mouse_event) = event.downcast::<MouseEvent>() else {
1829 return;
1830 };
1831
1832 let click_location = Point2D::new(
1833 mouse_event.ClientX().to_f32().unwrap(),
1834 mouse_event.ClientY().to_f32().unwrap(),
1835 );
1836 let bounding_rectangle = self.upcast::<Element>().GetBoundingClientRect(cx);
1837 let image_extents =
1838 Point2D::new(bounding_rectangle.X() as f32, bounding_rectangle.Y() as f32);
1839
1840 for area_element in area_elements {
1842 if !area_element.is_instance_activatable() {
1843 continue;
1844 }
1845 let activatable_area = match area_element.get_shape_from_coords() {
1846 Some(shape) => shape.absolute_coords(image_extents),
1847 None => return,
1848 };
1849 if activatable_area.hit_test(&click_location) {
1850 area_element.activation_behavior(cx, event, self.upcast());
1851 return;
1852 }
1853 }
1854 }
1855
1856 fn bind_to_tree(&self, cx: &mut JSContext, context: &BindContext) {
1858 if let Some(s) = self.super_type() {
1859 s.bind_to_tree(cx, context);
1860 }
1861 let document = self.owner_document();
1862 if context.tree_connected {
1863 document.register_responsive_image(self);
1864 }
1865
1866 let parent = self.upcast::<Node>().GetParentNode().unwrap();
1867
1868 if parent.is::<HTMLPictureElement>() && *parent == *context.parent {
1871 self.update_the_image_data(cx);
1872 }
1873 }
1874
1875 fn unbind_from_tree(&self, cx: &mut JSContext, context: &UnbindContext) {
1877 self.super_type().unwrap().unbind_from_tree(cx, context);
1878 let document = self.owner_document();
1879 document.unregister_responsive_image(self);
1880
1881 if context.parent.is::<HTMLPictureElement>() && !self.upcast::<Node>().has_parent() {
1884 self.update_the_image_data(cx);
1885 }
1886 }
1887
1888 fn moving_steps(&self, cx: &mut JSContext, context: &MoveContext) {
1890 if let Some(super_type) = self.super_type() {
1891 super_type.moving_steps(cx, context);
1892 }
1893
1894 if let Some(old_parent) = context.old_parent &&
1896 old_parent.is::<HTMLPictureElement>()
1897 {
1898 self.update_the_image_data(cx);
1899 }
1900 }
1901}
1902
1903impl FormControl for HTMLImageElement {
1904 fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
1905 self.form_owner.get()
1906 }
1907
1908 fn set_form_owner(&self, _cx: &mut JSContext, form: Option<&HTMLFormElement>) {
1909 self.form_owner.set(form);
1910 }
1911
1912 fn to_html_element(&self) -> &HTMLElement {
1913 self.upcast::<HTMLElement>()
1914 }
1915}
1916
1917#[derive(Clone)]
1918enum ChangeType {
1919 Environment {
1920 selected_source: USVString,
1921 selected_pixel_density: f64,
1922 },
1923 Element,
1924}