1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 use app_units::{Au, AU_PER_PX};
6 use cssparser::{Parser, ParserInput};
7 use document_loader::{LoadType, LoadBlocker};
8 use dom::activation::Activatable;
9 use dom::attr::Attr;
10 use dom::bindings::cell::DomRefCell;
11 use dom::bindings::codegen::Bindings::DOMRectBinding::DOMRectBinding::DOMRectMethods;
12 use dom::bindings::codegen::Bindings::ElementBinding::ElementBinding::ElementMethods;
13 use dom::bindings::codegen::Bindings::HTMLImageElementBinding;
14 use dom::bindings::codegen::Bindings::HTMLImageElementBinding::HTMLImageElementMethods;
15 use dom::bindings::codegen::Bindings::MouseEventBinding::MouseEventMethods;
16 use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
17 use dom::bindings::error::Fallible;
18 use dom::bindings::inheritance::Castable;
19 use dom::bindings::refcounted::Trusted;
20 use dom::bindings::reflector::DomObject;
21 use dom::bindings::root::{DomRoot, LayoutDom, MutNullableDom};
22 use dom::bindings::str::DOMString;
23 use dom::document::Document;
24 use dom::element::{AttributeMutation, Element, RawLayoutElementHelpers};
25 use dom::element::{reflect_cross_origin_attribute, set_cross_origin_attribute};
26 use dom::event::{Event, EventBubbles, EventCancelable};
27 use dom::eventtarget::EventTarget;
28 use dom::htmlareaelement::HTMLAreaElement;
29 use dom::htmlelement::HTMLElement;
30 use dom::htmlformelement::{FormControl, HTMLFormElement};
31 use dom::htmlmapelement::HTMLMapElement;
32 use dom::mouseevent::MouseEvent;
33 use dom::node::{Node, NodeDamage, document_from_node, window_from_node};
34 use dom::progressevent::ProgressEvent;
35 use dom::values::UNSIGNED_LONG_MAX;
36 use dom::virtualmethods::VirtualMethods;
37 use dom::window::Window;
38 use dom_struct::dom_struct;
39 use euclid::Point2D;
40 use html5ever::{LocalName, Prefix};
41 use ipc_channel::ipc;
42 use ipc_channel::router::ROUTER;
43 use microtask::{Microtask, MicrotaskRunnable};
44 use net_traits::{FetchResponseListener, FetchMetadata, NetworkError, FetchResponseMsg};
45 use net_traits::image::base::{Image, ImageMetadata};
46 use net_traits::image_cache::{CanRequestImages, ImageCache, ImageOrMetadataAvailable};
47 use net_traits::image_cache::{ImageResponder, ImageResponse, ImageState, PendingImageId};
48 use net_traits::image_cache::UsePlaceholder;
49 use net_traits::request::RequestInit;
50 use network_listener::{NetworkListener, PreInvoke};
51 use num_traits::ToPrimitive;
52 use script_thread::ScriptThread;
53 use servo_url::ServoUrl;
54 use servo_url::origin::ImmutableOrigin;
55 use std::cell::{Cell, RefMut};
56 use std::char;
57 use std::default::Default;
58 use std::i32;
59 use std::sync::{Arc, Mutex};
60 use style::attr::{AttrValue, LengthOrPercentageOrAuto, parse_double, parse_unsigned_integer};
61 use style::context::QuirksMode;
62 use style::media_queries::MediaQuery;
63 use style::parser::ParserContext;
64 use style::str::is_ascii_digit;
65 use style::values::specified::{Length, ViewportPercentageLength};
66 use style::values::specified::length::NoCalcLength;
67 use style_traits::ParsingMode;
68 use task_source::TaskSource;
69 
70 enum ParseState {
71     InDescriptor,
72     InParens,
73     AfterDescriptor,
74 }
75 
76 #[derive(Debug, PartialEq)]
77 pub struct ImageSource {
78     pub url: String,
79     pub descriptor: Descriptor,
80 }
81 
82 #[derive(Debug, PartialEq)]
83 pub struct Descriptor {
84     pub wid: Option<u32>,
85     pub den: Option<f64>,
86 }
87 
88 #[derive(Clone, Copy, JSTraceable, MallocSizeOf)]
89 #[allow(dead_code)]
90 enum State {
91     Unavailable,
92     PartiallyAvailable,
93     CompletelyAvailable,
94     Broken,
95 }
96 
97 #[derive(Debug, PartialEq)]
98 pub struct Size {
99     pub query: Option<MediaQuery>,
100     pub length: Length,
101 }
102 
103 #[derive(Clone, Copy, JSTraceable, MallocSizeOf)]
104 enum ImageRequestPhase {
105     Pending,
106     Current
107 }
108 #[derive(JSTraceable, MallocSizeOf)]
109 #[must_root]
110 struct ImageRequest {
111     state: State,
112     parsed_url: Option<ServoUrl>,
113     source_url: Option<DOMString>,
114     blocker: Option<LoadBlocker>,
115     #[ignore_malloc_size_of = "Arc"]
116     image: Option<Arc<Image>>,
117     metadata: Option<ImageMetadata>,
118     final_url: Option<ServoUrl>,
119 }
120 #[dom_struct]
121 pub struct HTMLImageElement {
122     htmlelement: HTMLElement,
123     image_request: Cell<ImageRequestPhase>,
124     current_request: DomRefCell<ImageRequest>,
125     pending_request: DomRefCell<ImageRequest>,
126     form_owner: MutNullableDom<HTMLFormElement>,
127     generation: Cell<u32>,
128 }
129 
130 impl HTMLImageElement {
get_url(&self) -> Option<ServoUrl>131     pub fn get_url(&self) -> Option<ServoUrl> {
132         self.current_request.borrow().parsed_url.clone()
133     }
134 }
135 
136 /// The context required for asynchronously loading an external image.
137 struct ImageContext {
138     /// Reference to the script thread image cache.
139     image_cache: Arc<ImageCache>,
140     /// Indicates whether the request failed, and why
141     status: Result<(), NetworkError>,
142     /// The cache ID for this request.
143     id: PendingImageId,
144 }
145 
146 impl FetchResponseListener for ImageContext {
process_request_body(&mut self)147     fn process_request_body(&mut self) {}
process_request_eof(&mut self)148     fn process_request_eof(&mut self) {}
149 
process_response(&mut self, metadata: Result<FetchMetadata, NetworkError>)150     fn process_response(&mut self, metadata: Result<FetchMetadata, NetworkError>) {
151         self.image_cache.notify_pending_response(
152             self.id,
153             FetchResponseMsg::ProcessResponse(metadata.clone()));
154 
155         let metadata = metadata.ok().map(|meta| {
156             match meta {
157                 FetchMetadata::Unfiltered(m) => m,
158                 FetchMetadata::Filtered { unsafe_, .. } => unsafe_
159             }
160         });
161 
162         let status_code = metadata.as_ref().and_then(|m| {
163             m.status.as_ref().map(|&(code, _)| code)
164         }).unwrap_or(0);
165 
166         self.status = match status_code {
167             0 => Err(NetworkError::Internal("No http status code received".to_owned())),
168             200...299 => Ok(()), // HTTP ok status codes
169             _ => Err(NetworkError::Internal(format!("HTTP error code {}", status_code)))
170         };
171     }
172 
process_response_chunk(&mut self, payload: Vec<u8>)173     fn process_response_chunk(&mut self, payload: Vec<u8>) {
174         if self.status.is_ok() {
175             self.image_cache.notify_pending_response(
176                 self.id,
177                 FetchResponseMsg::ProcessResponseChunk(payload));
178         }
179     }
180 
process_response_eof(&mut self, response: Result<(), NetworkError>)181     fn process_response_eof(&mut self, response: Result<(), NetworkError>) {
182         self.image_cache.notify_pending_response(
183             self.id,
184             FetchResponseMsg::ProcessResponseEOF(response));
185     }
186 }
187 
188 impl PreInvoke for ImageContext {}
189 
190 impl HTMLImageElement {
191     /// Update the current image with a valid URL.
fetch_image(&self, img_url: &ServoUrl)192     fn fetch_image(&self, img_url: &ServoUrl) {
193         fn add_cache_listener_for_element(image_cache: Arc<ImageCache>,
194                                           id: PendingImageId,
195                                           elem: &HTMLImageElement) {
196             let trusted_node = Trusted::new(elem);
197             let (responder_sender, responder_receiver) = ipc::channel().unwrap();
198 
199             let window = window_from_node(elem);
200             let task_source = window.networking_task_source();
201             let task_canceller = window.task_canceller();
202             let generation = elem.generation.get();
203             ROUTER.add_route(responder_receiver.to_opaque(), Box::new(move |message| {
204                 debug!("Got image {:?}", message);
205                 // Return the image via a message to the script thread, which marks
206                 // the element as dirty and triggers a reflow.
207                 let element = trusted_node.clone();
208                 let image = message.to().unwrap();
209                 // FIXME(nox): Why are errors silenced here?
210                 let _ = task_source.queue_with_canceller(
211                     task!(process_image_response: move || {
212                         let element = element.root();
213                         // Ignore any image response for a previous request that has been discarded.
214                         if generation == element.generation.get() {
215                             element.process_image_response(image);
216                         }
217                     }),
218                     &task_canceller,
219                 );
220             }));
221 
222             image_cache.add_listener(id, ImageResponder::new(responder_sender, id));
223         }
224 
225         let window = window_from_node(self);
226         let image_cache = window.image_cache();
227         let response =
228             image_cache.find_image_or_metadata(img_url.clone().into(),
229                                                UsePlaceholder::Yes,
230                                                CanRequestImages::Yes);
231         match response {
232             Ok(ImageOrMetadataAvailable::ImageAvailable(image, url)) => {
233                 self.process_image_response(ImageResponse::Loaded(image, url));
234             }
235 
236             Ok(ImageOrMetadataAvailable::MetadataAvailable(m)) => {
237                 self.process_image_response(ImageResponse::MetadataLoaded(m));
238             }
239 
240             Err(ImageState::Pending(id)) => {
241                 add_cache_listener_for_element(image_cache.clone(), id, self);
242             }
243 
244             Err(ImageState::LoadError) => {
245                 self.process_image_response(ImageResponse::None);
246             }
247 
248             Err(ImageState::NotRequested(id)) => {
249                 add_cache_listener_for_element(image_cache, id, self);
250                 self.fetch_request(img_url, id);
251             }
252         }
253     }
254 
fetch_request(&self, img_url: &ServoUrl, id: PendingImageId)255     fn fetch_request(&self, img_url: &ServoUrl, id: PendingImageId) {
256         let document = document_from_node(self);
257         let window = window_from_node(self);
258 
259         let context = Arc::new(Mutex::new(ImageContext {
260             image_cache: window.image_cache(),
261             status: Ok(()),
262             id: id,
263         }));
264 
265         let (action_sender, action_receiver) = ipc::channel().unwrap();
266         let listener = NetworkListener {
267             context: context,
268             task_source: window.networking_task_source(),
269             canceller: Some(window.task_canceller()),
270         };
271         ROUTER.add_route(action_receiver.to_opaque(), Box::new(move |message| {
272             listener.notify_fetch(message.to().unwrap());
273         }));
274 
275         let request = RequestInit {
276             url: img_url.clone(),
277             origin: document.origin().immutable().clone(),
278             pipeline_id: Some(document.global().pipeline_id()),
279             .. RequestInit::default()
280         };
281 
282         // This is a background load because the load blocker already fulfills the
283         // purpose of delaying the document's load event.
284         document.loader().fetch_async_background(request, action_sender);
285     }
286 
287     /// Step 14 of https://html.spec.whatwg.org/multipage/#update-the-image-data
process_image_response(&self, image: ImageResponse)288     fn process_image_response(&self, image: ImageResponse) {
289         // TODO: Handle multipart/x-mixed-replace
290         let (trigger_image_load, trigger_image_error) = match (image, self.image_request.get()) {
291             (ImageResponse::Loaded(image, url), ImageRequestPhase::Current) |
292             (ImageResponse::PlaceholderLoaded(image, url), ImageRequestPhase::Current) => {
293                 self.current_request.borrow_mut().metadata = Some(ImageMetadata {
294                     height: image.height,
295                     width: image.width
296                 });
297                 self.current_request.borrow_mut().final_url = Some(url);
298                 self.current_request.borrow_mut().image = Some(image);
299                 self.current_request.borrow_mut().state = State::CompletelyAvailable;
300                 LoadBlocker::terminate(&mut self.current_request.borrow_mut().blocker);
301                 // Mark the node dirty
302                 self.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
303                 (true, false)
304             },
305             (ImageResponse::Loaded(image, url), ImageRequestPhase::Pending) |
306             (ImageResponse::PlaceholderLoaded(image, url), ImageRequestPhase::Pending) => {
307                 self.abort_request(State::Unavailable, ImageRequestPhase::Pending);
308                 self.image_request.set(ImageRequestPhase::Current);
309                 self.current_request.borrow_mut().metadata = Some(ImageMetadata {
310                     height: image.height,
311                     width: image.width
312                 });
313                 self.current_request.borrow_mut().final_url = Some(url);
314                 self.current_request.borrow_mut().image = Some(image);
315                 self.current_request.borrow_mut().state = State::CompletelyAvailable;
316                 LoadBlocker::terminate(&mut self.current_request.borrow_mut().blocker);
317                 self.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
318                 (true, false)
319             },
320             (ImageResponse::MetadataLoaded(meta), ImageRequestPhase::Current) => {
321                 self.current_request.borrow_mut().state = State::PartiallyAvailable;
322                 self.current_request.borrow_mut().metadata = Some(meta);
323                 (false, false)
324             },
325             (ImageResponse::MetadataLoaded(_), ImageRequestPhase::Pending) => {
326                 self.pending_request.borrow_mut().state = State::PartiallyAvailable;
327                 (false, false)
328             },
329             (ImageResponse::None, ImageRequestPhase::Current) => {
330                 self.abort_request(State::Broken, ImageRequestPhase::Current);
331                 (false, true)
332             },
333             (ImageResponse::None, ImageRequestPhase::Pending) => {
334                 self.abort_request(State::Broken, ImageRequestPhase::Current);
335                 self.abort_request(State::Broken, ImageRequestPhase::Pending);
336                 self.image_request.set(ImageRequestPhase::Current);
337                 (false, true)
338             },
339         };
340 
341         // Fire image.onload and loadend
342         if trigger_image_load {
343             // TODO: https://html.spec.whatwg.org/multipage/#fire-a-progress-event-or-event
344             self.upcast::<EventTarget>().fire_event(atom!("load"));
345             self.upcast::<EventTarget>().fire_event(atom!("loadend"));
346         }
347 
348         // Fire image.onerror
349         if trigger_image_error {
350             self.upcast::<EventTarget>().fire_event(atom!("error"));
351             self.upcast::<EventTarget>().fire_event(atom!("loadend"));
352         }
353 
354         // Trigger reflow
355         let window = window_from_node(self);
356         window.add_pending_reflow();
357     }
358 
359     /// <https://html.spec.whatwg.org/multipage/#abort-the-image-request>
abort_request(&self, state: State, request: ImageRequestPhase)360     fn abort_request(&self, state: State, request: ImageRequestPhase) {
361         let mut request = match request {
362             ImageRequestPhase::Current => self.current_request.borrow_mut(),
363             ImageRequestPhase::Pending => self.pending_request.borrow_mut(),
364         };
365         LoadBlocker::terminate(&mut request.blocker);
366         request.state = state;
367         request.image = None;
368         request.metadata = None;
369     }
370 
371     /// <https://html.spec.whatwg.org/multipage/#update-the-source-set>
update_source_set(&self) -> Vec<DOMString>372     fn update_source_set(&self) -> Vec<DOMString> {
373         let elem = self.upcast::<Element>();
374         // TODO: follow the algorithm
375         let src = elem.get_string_attribute(&local_name!("src"));
376         if src.is_empty() {
377             return vec![]
378         }
379         vec![src]
380     }
381 
382     /// <https://html.spec.whatwg.org/multipage/#select-an-image-source>
select_image_source(&self) -> Option<DOMString>383     fn select_image_source(&self) -> Option<DOMString> {
384         // TODO: select an image source from source set
385         self.update_source_set().first().cloned()
386     }
387 
init_image_request(&self, request: &mut RefMut<ImageRequest>, url: &ServoUrl, src: &DOMString)388     fn init_image_request(&self,
389                           request: &mut RefMut<ImageRequest>,
390                           url: &ServoUrl,
391                           src: &DOMString) {
392         request.parsed_url = Some(url.clone());
393         request.source_url = Some(src.clone());
394         request.image = None;
395         request.metadata = None;
396         let document = document_from_node(self);
397         LoadBlocker::terminate(&mut request.blocker);
398         request.blocker = Some(LoadBlocker::new(&*document, LoadType::Image(url.clone())));
399     }
400 
401     /// Step 12 of html.spec.whatwg.org/multipage/#update-the-image-data
prepare_image_request(&self, url: &ServoUrl, src: &DOMString)402     fn prepare_image_request(&self, url: &ServoUrl, src: &DOMString) {
403         match self.image_request.get() {
404             ImageRequestPhase::Pending => {
405                 if let Some(pending_url) = self.pending_request.borrow().parsed_url.clone() {
406                     // Step 12.1
407                     if pending_url == *url {
408                         return
409                     }
410                 }
411             },
412             ImageRequestPhase::Current => {
413                 let mut current_request = self.current_request.borrow_mut();
414                 let mut pending_request = self.pending_request.borrow_mut();
415                 // step 12.4, create a new "image_request"
416                 match (current_request.parsed_url.clone(), current_request.state) {
417                     (Some(parsed_url), State::PartiallyAvailable) => {
418                         // Step 12.2
419                         if parsed_url == *url {
420                             // 12.3 abort pending request
421                             pending_request.image = None;
422                             pending_request.parsed_url = None;
423                             LoadBlocker::terminate(&mut pending_request.blocker);
424                             // TODO: queue a task to restart animation, if restart-animation is set
425                             return
426                         }
427                         self.image_request.set(ImageRequestPhase::Pending);
428                         self.init_image_request(&mut pending_request, &url, &src);
429                     },
430                     (_, State::Broken) | (_, State::Unavailable) => {
431                         // Step 12.5
432                         self.init_image_request(&mut current_request, &url, &src);
433                     },
434                     (_, _) => {
435                         // step 12.6
436                         self.image_request.set(ImageRequestPhase::Pending);
437                         self.init_image_request(&mut pending_request, &url, &src);
438                     },
439                 }
440             }
441         }
442         self.fetch_image(&url);
443     }
444 
445     /// Step 8-12 of html.spec.whatwg.org/multipage/#update-the-image-data
update_the_image_data_sync_steps(&self)446     fn update_the_image_data_sync_steps(&self) {
447         let document = document_from_node(self);
448         let window = document.window();
449         let task_source = window.dom_manipulation_task_source();
450         let this = Trusted::new(self);
451         let src = match self.select_image_source() {
452             Some(src) => {
453                 // Step 8.
454                 // TODO: Handle pixel density.
455                 src
456             },
457             None => {
458                 // Step 9.
459                 // FIXME(nox): Why are errors silenced here?
460                 let _ = task_source.queue(
461                     task!(image_null_source_error: move || {
462                         let this = this.root();
463                         {
464                             let mut current_request =
465                                 this.current_request.borrow_mut();
466                             current_request.source_url = None;
467                             current_request.parsed_url = None;
468                         }
469                         if this.upcast::<Element>().has_attribute(&local_name!("src")) {
470                             this.upcast::<EventTarget>().fire_event(atom!("error"));
471                         }
472                         // FIXME(nox): According to the spec, setting the current
473                         // request to the broken state is done prior to queuing a
474                         // task, why is this here?
475                         this.abort_request(State::Broken, ImageRequestPhase::Current);
476                         this.abort_request(State::Broken, ImageRequestPhase::Pending);
477                     }),
478                     window.upcast(),
479                 );
480                 return;
481             },
482         };
483         // Step 10.
484         let target = Trusted::new(self.upcast::<EventTarget>());
485         // FIXME(nox): Why are errors silenced here?
486         let _ = task_source.queue(
487             task!(fire_progress_event: move || {
488                 let target = target.root();
489 
490                 let event = ProgressEvent::new(
491                     &target.global(),
492                     atom!("loadstart"),
493                     EventBubbles::DoesNotBubble,
494                     EventCancelable::NotCancelable,
495                     false,
496                     0,
497                     0,
498                 );
499                 event.upcast::<Event>().fire(&target);
500             }),
501             window.upcast(),
502         );
503         // Step 11
504         let base_url = document.base_url();
505         let parsed_url = base_url.join(&src);
506         match parsed_url {
507             Ok(url) => {
508                     // Step 12
509                 self.prepare_image_request(&url, &src);
510             },
511             Err(_) => {
512                 // Step 11.1-11.5.
513                 let src = String::from(src);
514                 // FIXME(nox): Why are errors silenced here?
515                 let _ = task_source.queue(
516                     task!(image_selected_source_error: move || {
517                         let this = this.root();
518                         {
519                             let mut current_request =
520                                 this.current_request.borrow_mut();
521                             current_request.source_url = Some(src.into());
522                         }
523                         this.upcast::<EventTarget>().fire_event(atom!("error"));
524                         this.upcast::<EventTarget>().fire_event(atom!("loadend"));
525 
526                         // FIXME(nox): According to the spec, setting the current
527                         // request to the broken state is done prior to queuing a
528                         // task, why is this here?
529                         this.abort_request(State::Broken, ImageRequestPhase::Current);
530                         this.abort_request(State::Broken, ImageRequestPhase::Pending);
531                     }),
532                     window.upcast(),
533                 );
534             }
535         }
536     }
537 
538     /// <https://html.spec.whatwg.org/multipage/#update-the-image-data>
update_the_image_data(&self)539     fn update_the_image_data(&self) {
540         let document = document_from_node(self);
541         let window = document.window();
542         let elem = self.upcast::<Element>();
543         let src = elem.get_string_attribute(&local_name!("src"));
544         let base_url = document.base_url();
545 
546         // https://html.spec.whatwg.org/multipage/#reacting-to-dom-mutations
547         // Always first set the current request to unavailable,
548         // ensuring img.complete is false.
549         {
550             let mut current_request = self.current_request.borrow_mut();
551             current_request.state = State::Unavailable;
552         }
553 
554         if !document.is_active() {
555             // Step 1 (if the document is inactive)
556             // TODO: use GlobalScope::enqueue_microtask,
557             // to queue micro task to come back to this algorithm
558         }
559         // Step 2 abort if user-agent does not supports images
560         // NOTE: Servo only supports images, skipping this step
561 
562         // step 3, 4
563         // TODO: take srcset and parent images into account
564         if !src.is_empty() {
565             // TODO: take pixel density into account
566             if let Ok(img_url) = base_url.join(&src) {
567                 // step 5, check the list of available images
568                 let image_cache = window.image_cache();
569                 let response = image_cache.find_image_or_metadata(img_url.clone().into(),
570                                                                   UsePlaceholder::No,
571                                                                   CanRequestImages::No);
572                 if let Ok(ImageOrMetadataAvailable::ImageAvailable(image, url)) = response {
573                     // Step 5.3
574                     let metadata = ImageMetadata { height: image.height, width: image.width };
575                     // Step 5.3.2 abort requests
576                     self.abort_request(State::CompletelyAvailable, ImageRequestPhase::Current);
577                     self.abort_request(State::CompletelyAvailable, ImageRequestPhase::Pending);
578                     let mut current_request = self.current_request.borrow_mut();
579                     current_request.final_url = Some(url);
580                     current_request.image = Some(image.clone());
581                     current_request.metadata = Some(metadata);
582                     let this = Trusted::new(self);
583                     let src = String::from(src);
584                     let _ = window.dom_manipulation_task_source().queue(
585                         task!(image_load_event: move || {
586                             let this = this.root();
587                             {
588                                 let mut current_request =
589                                     this.current_request.borrow_mut();
590                                 current_request.parsed_url = Some(img_url);
591                                 current_request.source_url = Some(src.into());
592                             }
593                             // TODO: restart animation, if set.
594                             this.upcast::<EventTarget>().fire_event(atom!("load"));
595                         }),
596                         window.upcast(),
597                     );
598                     return;
599                 }
600             }
601         }
602         // step 6, await a stable state.
603         self.generation.set(self.generation.get() + 1);
604         let task = ImageElementMicrotask::StableStateUpdateImageDataTask {
605             elem: DomRoot::from_ref(self),
606             generation: self.generation.get(),
607         };
608         ScriptThread::await_stable_state(Microtask::ImageElement(task));
609     }
610 
new_inherited(local_name: LocalName, prefix: Option<Prefix>, document: &Document) -> HTMLImageElement611     fn new_inherited(local_name: LocalName, prefix: Option<Prefix>, document: &Document) -> HTMLImageElement {
612         HTMLImageElement {
613             htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
614             image_request: Cell::new(ImageRequestPhase::Current),
615             current_request: DomRefCell::new(ImageRequest {
616                 state: State::Unavailable,
617                 parsed_url: None,
618                 source_url: None,
619                 image: None,
620                 metadata: None,
621                 blocker: None,
622                 final_url: None,
623             }),
624             pending_request: DomRefCell::new(ImageRequest {
625                 state: State::Unavailable,
626                 parsed_url: None,
627                 source_url: None,
628                 image: None,
629                 metadata: None,
630                 blocker: None,
631                 final_url: None,
632             }),
633             form_owner: Default::default(),
634             generation: Default::default(),
635         }
636     }
637 
638     #[allow(unrooted_must_root)]
new(local_name: LocalName, prefix: Option<Prefix>, document: &Document) -> DomRoot<HTMLImageElement>639     pub fn new(local_name: LocalName,
640                prefix: Option<Prefix>,
641                document: &Document) -> DomRoot<HTMLImageElement> {
642         Node::reflect_node(Box::new(HTMLImageElement::new_inherited(local_name, prefix, document)),
643                            document,
644                            HTMLImageElementBinding::Wrap)
645     }
646 
Image(window: &Window, width: Option<u32>, height: Option<u32>) -> Fallible<DomRoot<HTMLImageElement>>647     pub fn Image(window: &Window,
648                  width: Option<u32>,
649                  height: Option<u32>) -> Fallible<DomRoot<HTMLImageElement>> {
650         let document = window.Document();
651         let image = HTMLImageElement::new(local_name!("img"), None, &document);
652         if let Some(w) = width {
653             image.SetWidth(w);
654         }
655         if let Some(h) = height {
656             image.SetHeight(h);
657         }
658 
659         Ok(image)
660     }
areas(&self) -> Option<Vec<DomRoot<HTMLAreaElement>>>661     pub fn areas(&self) -> Option<Vec<DomRoot<HTMLAreaElement>>> {
662         let elem = self.upcast::<Element>();
663         let usemap_attr = elem.get_attribute(&ns!(), &local_name!("usemap"))?;
664 
665         let value = usemap_attr.value();
666 
667         if value.len() == 0 || !value.is_char_boundary(1) {
668             return None
669         }
670 
671         let (first, last) = value.split_at(1);
672 
673         if first != "#" || last.len() == 0 {
674             return None
675         }
676 
677         let useMapElements = document_from_node(self).upcast::<Node>()
678                                 .traverse_preorder()
679                                 .filter_map(DomRoot::downcast::<HTMLMapElement>)
680                                 .find(|n| n.upcast::<Element>().get_string_attribute(&LocalName::from("name")) == last);
681 
682         useMapElements.map(|mapElem| mapElem.get_area_elements())
683     }
684 
get_origin(&self) -> Option<ImmutableOrigin>685     pub fn get_origin(&self) -> Option<ImmutableOrigin> {
686         match self.current_request.borrow_mut().final_url {
687             Some(ref url) => Some(url.origin()),
688             None => None
689         }
690     }
691 
692 }
693 
694 #[derive(JSTraceable, MallocSizeOf)]
695 pub enum ImageElementMicrotask {
696     StableStateUpdateImageDataTask {
697         elem: DomRoot<HTMLImageElement>,
698         generation: u32,
699     }
700 }
701 
702 impl MicrotaskRunnable for ImageElementMicrotask {
handler(&self)703     fn handler(&self) {
704         match self {
705             &ImageElementMicrotask::StableStateUpdateImageDataTask { ref elem, ref generation } => {
706                 // Step 7 of https://html.spec.whatwg.org/multipage/#update-the-image-data,
707                 // stop here if other instances of this algorithm have been scheduled
708                 if elem.generation.get() == *generation {
709                     elem.update_the_image_data_sync_steps();
710                 }
711             },
712         }
713     }
714 }
715 
716 pub trait LayoutHTMLImageElementHelpers {
717     #[allow(unsafe_code)]
image(&self) -> Option<Arc<Image>>718     unsafe fn image(&self) -> Option<Arc<Image>>;
719 
720     #[allow(unsafe_code)]
image_url(&self) -> Option<ServoUrl>721     unsafe fn image_url(&self) -> Option<ServoUrl>;
722 
get_width(&self) -> LengthOrPercentageOrAuto723     fn get_width(&self) -> LengthOrPercentageOrAuto;
get_height(&self) -> LengthOrPercentageOrAuto724     fn get_height(&self) -> LengthOrPercentageOrAuto;
725 }
726 
727 impl LayoutHTMLImageElementHelpers for LayoutDom<HTMLImageElement> {
728     #[allow(unsafe_code)]
image(&self) -> Option<Arc<Image>>729     unsafe fn image(&self) -> Option<Arc<Image>> {
730         (*self.unsafe_get()).current_request.borrow_for_layout().image.clone()
731     }
732 
733     #[allow(unsafe_code)]
image_url(&self) -> Option<ServoUrl>734     unsafe fn image_url(&self) -> Option<ServoUrl> {
735         (*self.unsafe_get()).current_request.borrow_for_layout().parsed_url.clone()
736     }
737 
738     #[allow(unsafe_code)]
get_width(&self) -> LengthOrPercentageOrAuto739     fn get_width(&self) -> LengthOrPercentageOrAuto {
740         unsafe {
741             (*self.upcast::<Element>().unsafe_get())
742                 .get_attr_for_layout(&ns!(), &local_name!("width"))
743                 .map(AttrValue::as_dimension)
744                 .cloned()
745                 .unwrap_or(LengthOrPercentageOrAuto::Auto)
746         }
747     }
748 
749     #[allow(unsafe_code)]
get_height(&self) -> LengthOrPercentageOrAuto750     fn get_height(&self) -> LengthOrPercentageOrAuto {
751         unsafe {
752             (*self.upcast::<Element>().unsafe_get())
753                 .get_attr_for_layout(&ns!(), &local_name!("height"))
754                 .map(AttrValue::as_dimension)
755                 .cloned()
756                 .unwrap_or(LengthOrPercentageOrAuto::Auto)
757         }
758     }
759 }
760 
761 //https://html.spec.whatwg.org/multipage/#parse-a-sizes-attribute
parse_a_sizes_attribute(input: DOMString, width: Option<u32>) -> Vec<Size>762 pub fn parse_a_sizes_attribute(input: DOMString, width: Option<u32>) -> Vec<Size> {
763     let mut sizes = Vec::<Size>::new();
764     for unparsed_size in input.split(',') {
765         let whitespace = unparsed_size.chars().rev().take_while(|c| char::is_whitespace(*c)).count();
766         let trimmed: String = unparsed_size.chars().take(unparsed_size.chars().count() - whitespace).collect();
767 
768         if trimmed.is_empty() {
769             continue;
770         }
771         let mut input = ParserInput::new(&trimmed);
772         let url = ServoUrl::parse("about:blank").unwrap();
773         let context = ParserContext::new_for_cssom(&url,
774                                                    None,
775                                                    ParsingMode::empty(),
776                                                    QuirksMode::NoQuirks);
777         let mut parser = Parser::new(&mut input);
778         let length = parser.try(|i| Length::parse_non_negative(&context, i));
779         match length {
780             Ok(len) => sizes.push(Size {
781                 length: len,
782                 query: None
783             }),
784             Err(_) => {
785                 let mut media_query_parser = parser;
786                 let media_query = media_query_parser.try(|i| MediaQuery::parse(&context, i));
787                 if let Ok(query) = media_query {
788                     let length = Length::parse_non_negative(&context, &mut media_query_parser);
789                         if let Ok(length) = length {
790                             sizes.push(Size {
791                                 length: length,
792                                 query: Some(query)
793                         })
794                     }
795                 }
796             },
797         }
798     }
799     if sizes.is_empty() {
800         let size = match width {
801             Some(w) => Size {
802                 length: Length::from_px(w as f32),
803                 query: None
804             },
805             None => Size {
806                 length: Length::NoCalc(NoCalcLength::ViewportPercentage(ViewportPercentageLength::Vw(100.))),
807                 query: None
808             },
809         };
810         sizes.push(size);
811     }
812     sizes
813 }
814 
815 impl HTMLImageElementMethods for HTMLImageElement {
816     // https://html.spec.whatwg.org/multipage/#dom-img-alt
817     make_getter!(Alt, "alt");
818     // https://html.spec.whatwg.org/multipage/#dom-img-alt
819     make_setter!(SetAlt, "alt");
820 
821     // https://html.spec.whatwg.org/multipage/#dom-img-src
822     make_url_getter!(Src, "src");
823 
824     // https://html.spec.whatwg.org/multipage/#dom-img-src
825     make_setter!(SetSrc, "src");
826 
827     // https://html.spec.whatwg.org/multipage/#dom-img-crossOrigin
GetCrossOrigin(&self) -> Option<DOMString>828     fn GetCrossOrigin(&self) -> Option<DOMString> {
829         reflect_cross_origin_attribute(self.upcast::<Element>())
830     }
831 
832     // https://html.spec.whatwg.org/multipage/#dom-img-crossOrigin
SetCrossOrigin(&self, value: Option<DOMString>)833     fn SetCrossOrigin(&self, value: Option<DOMString>) {
834         set_cross_origin_attribute(self.upcast::<Element>(), value);
835     }
836 
837     // https://html.spec.whatwg.org/multipage/#dom-img-usemap
838     make_getter!(UseMap, "usemap");
839     // https://html.spec.whatwg.org/multipage/#dom-img-usemap
840     make_setter!(SetUseMap, "usemap");
841 
842     // https://html.spec.whatwg.org/multipage/#dom-img-ismap
843     make_bool_getter!(IsMap, "ismap");
844     // https://html.spec.whatwg.org/multipage/#dom-img-ismap
845     make_bool_setter!(SetIsMap, "ismap");
846 
847     // https://html.spec.whatwg.org/multipage/#dom-img-width
Width(&self) -> u32848     fn Width(&self) -> u32 {
849         let node = self.upcast::<Node>();
850         match node.bounding_content_box() {
851             Some(rect) => rect.size.width.to_px() as u32,
852             None => self.NaturalWidth(),
853         }
854     }
855 
856     // https://html.spec.whatwg.org/multipage/#dom-img-width
SetWidth(&self, value: u32)857     fn SetWidth(&self, value: u32) {
858         image_dimension_setter(self.upcast(), local_name!("width"), value);
859     }
860 
861     // https://html.spec.whatwg.org/multipage/#dom-img-height
Height(&self) -> u32862     fn Height(&self) -> u32 {
863         let node = self.upcast::<Node>();
864         match node.bounding_content_box() {
865             Some(rect) => rect.size.height.to_px() as u32,
866             None => self.NaturalHeight(),
867         }
868     }
869 
870     // https://html.spec.whatwg.org/multipage/#dom-img-height
SetHeight(&self, value: u32)871     fn SetHeight(&self, value: u32) {
872         image_dimension_setter(self.upcast(), local_name!("height"), value);
873     }
874 
875     // https://html.spec.whatwg.org/multipage/#dom-img-naturalwidth
NaturalWidth(&self) -> u32876     fn NaturalWidth(&self) -> u32 {
877         let ref metadata = self.current_request.borrow().metadata;
878 
879         match *metadata {
880             Some(ref metadata) => metadata.width,
881             None => 0,
882         }
883     }
884 
885     // https://html.spec.whatwg.org/multipage/#dom-img-naturalheight
NaturalHeight(&self) -> u32886     fn NaturalHeight(&self) -> u32 {
887         let ref metadata = self.current_request.borrow().metadata;
888 
889         match *metadata {
890             Some(ref metadata) => metadata.height,
891             None => 0,
892         }
893     }
894 
895     // https://html.spec.whatwg.org/multipage/#dom-img-complete
Complete(&self) -> bool896     fn Complete(&self) -> bool {
897         let elem = self.upcast::<Element>();
898         // TODO: take srcset into account
899         if !elem.has_attribute(&local_name!("src")) {
900             return true
901         }
902         let src = elem.get_string_attribute(&local_name!("src"));
903         if src.is_empty() {
904             return true
905         }
906         let request = self.current_request.borrow();
907         let request_state = request.state;
908         match request_state {
909             State::CompletelyAvailable | State::Broken => return true,
910             State::PartiallyAvailable | State::Unavailable => return false,
911         }
912     }
913 
914     // https://html.spec.whatwg.org/multipage/#dom-img-currentsrc
CurrentSrc(&self) -> DOMString915     fn CurrentSrc(&self) -> DOMString {
916         let ref url = self.current_request.borrow().source_url;
917         match *url {
918             Some(ref url) => url.clone(),
919             None => DOMString::from(""),
920         }
921     }
922 
923     // https://html.spec.whatwg.org/multipage/#dom-img-name
924     make_getter!(Name, "name");
925 
926     // https://html.spec.whatwg.org/multipage/#dom-img-name
927     make_atomic_setter!(SetName, "name");
928 
929     // https://html.spec.whatwg.org/multipage/#dom-img-align
930     make_getter!(Align, "align");
931 
932     // https://html.spec.whatwg.org/multipage/#dom-img-align
933     make_setter!(SetAlign, "align");
934 
935     // https://html.spec.whatwg.org/multipage/#dom-img-hspace
936     make_uint_getter!(Hspace, "hspace");
937 
938     // https://html.spec.whatwg.org/multipage/#dom-img-hspace
939     make_uint_setter!(SetHspace, "hspace");
940 
941     // https://html.spec.whatwg.org/multipage/#dom-img-vspace
942     make_uint_getter!(Vspace, "vspace");
943 
944     // https://html.spec.whatwg.org/multipage/#dom-img-vspace
945     make_uint_setter!(SetVspace, "vspace");
946 
947     // https://html.spec.whatwg.org/multipage/#dom-img-longdesc
948     make_getter!(LongDesc, "longdesc");
949 
950     // https://html.spec.whatwg.org/multipage/#dom-img-longdesc
951     make_setter!(SetLongDesc, "longdesc");
952 
953     // https://html.spec.whatwg.org/multipage/#dom-img-border
954     make_getter!(Border, "border");
955 
956     // https://html.spec.whatwg.org/multipage/#dom-img-border
957     make_setter!(SetBorder, "border");
958 }
959 
960 impl VirtualMethods for HTMLImageElement {
super_type(&self) -> Option<&VirtualMethods>961     fn super_type(&self) -> Option<&VirtualMethods> {
962         Some(self.upcast::<HTMLElement>() as &VirtualMethods)
963     }
964 
adopting_steps(&self, old_doc: &Document)965     fn adopting_steps(&self, old_doc: &Document) {
966         self.super_type().unwrap().adopting_steps(old_doc);
967         self.update_the_image_data();
968     }
969 
attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation)970     fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
971         self.super_type().unwrap().attribute_mutated(attr, mutation);
972         match attr.local_name() {
973             &local_name!("src") => self.update_the_image_data(),
974             _ => {},
975         }
976     }
977 
parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue978     fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
979         match name {
980             &local_name!("name") => AttrValue::from_atomic(value.into()),
981             &local_name!("width") | &local_name!("height") => AttrValue::from_dimension(value.into()),
982             &local_name!("hspace") | &local_name!("vspace") => AttrValue::from_u32(value.into(), 0),
983             _ => self.super_type().unwrap().parse_plain_attribute(name, value),
984         }
985     }
986 
handle_event(&self, event: &Event)987     fn handle_event(&self, event: &Event) {
988         if event.type_() != atom!("click") {
989             return
990         }
991 
992        let area_elements = self.areas();
993        let elements = match area_elements {
994            Some(x) => x,
995            None => return,
996        };
997 
998        // Fetch click coordinates
999        let mouse_event = match event.downcast::<MouseEvent>() {
1000            Some(x) => x,
1001            None => return,
1002        };
1003 
1004        let point = Point2D::new(mouse_event.ClientX().to_f32().unwrap(),
1005                                 mouse_event.ClientY().to_f32().unwrap());
1006        let bcr = self.upcast::<Element>().GetBoundingClientRect();
1007        let bcr_p = Point2D::new(bcr.X() as f32, bcr.Y() as f32);
1008 
1009        // Walk HTMLAreaElements
1010        for element in elements {
1011            let shape = element.get_shape_from_coords();
1012            let shp = match shape {
1013                Some(x) => x.absolute_coords(bcr_p),
1014                None => return,
1015            };
1016            if shp.hit_test(&point) {
1017                element.activation_behavior(event, self.upcast());
1018                return
1019            }
1020        }
1021     }
1022 }
1023 
1024 impl FormControl for HTMLImageElement {
form_owner(&self) -> Option<DomRoot<HTMLFormElement>>1025     fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
1026         self.form_owner.get()
1027     }
1028 
set_form_owner(&self, form: Option<&HTMLFormElement>)1029     fn set_form_owner(&self, form: Option<&HTMLFormElement>) {
1030         self.form_owner.set(form);
1031     }
1032 
to_element<'a>(&'a self) -> &'a Element1033     fn to_element<'a>(&'a self) -> &'a Element {
1034         self.upcast::<Element>()
1035     }
1036 
is_listed(&self) -> bool1037     fn is_listed(&self) -> bool {
1038         false
1039     }
1040 }
1041 
image_dimension_setter(element: &Element, attr: LocalName, value: u32)1042 fn image_dimension_setter(element: &Element, attr: LocalName, value: u32) {
1043     // This setter is a bit weird: the IDL type is unsigned long, but it's parsed as
1044     // a dimension for rendering.
1045     let value = if value > UNSIGNED_LONG_MAX {
1046         0
1047     } else {
1048         value
1049     };
1050 
1051     // FIXME: There are probably quite a few more cases of this. This is the
1052     // only overflow that was hitting on automation, but we should consider what
1053     // to do in the general case case.
1054     //
1055     // See <https://github.com/servo/app_units/issues/22>
1056     let pixel_value = if value > (i32::MAX / AU_PER_PX) as u32 {
1057         0
1058     } else {
1059         value
1060     };
1061 
1062     let dim = LengthOrPercentageOrAuto::Length(Au::from_px(pixel_value as i32));
1063     let value = AttrValue::Dimension(value.to_string(), dim);
1064     element.set_attribute(&attr, value);
1065 }
1066 
1067 /// Collect sequence of code points
collect_sequence_characters<F>(s: &str, predicate: F) -> (&str, &str) where F: Fn(&char) -> bool1068 pub fn collect_sequence_characters<F>(s: &str, predicate: F) -> (&str, &str)
1069     where F: Fn(&char) -> bool
1070 {
1071     for (i, ch) in s.chars().enumerate() {
1072         if !predicate(&ch) {
1073             return (&s[0..i], &s[i..])
1074         }
1075     }
1076 
1077     return (s, "");
1078 }
1079 
1080 /// Parse an `srcset` attribute - https://html.spec.whatwg.org/multipage/#parsing-a-srcset-attribute.
parse_a_srcset_attribute(input: &str) -> Vec<ImageSource>1081 pub fn parse_a_srcset_attribute(input: &str) -> Vec<ImageSource> {
1082     let mut url_len = 0;
1083     let mut candidates: Vec<ImageSource> = vec![];
1084     while url_len < input.len() {
1085         let position = &input[url_len..];
1086         let (spaces, position) = collect_sequence_characters(position, |c| *c == ',' || char::is_whitespace(*c));
1087         // add the length of the url that we parse to advance the start index
1088         let space_len = spaces.char_indices().count();
1089         url_len += space_len;
1090         if position.is_empty() {
1091             return candidates;
1092         }
1093         let (url, spaces) = collect_sequence_characters(position, |c| !char::is_whitespace(*c));
1094         // add the counts of urls that we parse to advance the start index
1095         url_len += url.chars().count();
1096         let comma_count = url.chars().rev().take_while(|c| *c == ',').count();
1097         let url: String = url.chars().take(url.chars().count() - comma_count).collect();
1098         // add 1 to start index, for the comma
1099         url_len += comma_count + 1;
1100         let (space, position) = collect_sequence_characters(spaces, |c| char::is_whitespace(*c));
1101         let space_len = space.len();
1102         url_len += space_len;
1103         let mut descriptors = Vec::new();
1104         let mut current_descriptor = String::new();
1105         let mut state = ParseState::InDescriptor;
1106         let mut char_stream = position.chars().enumerate();
1107         let mut buffered: Option<(usize, char)> = None;
1108         loop {
1109             let next_char = buffered.take().or_else(|| char_stream.next());
1110             if next_char.is_some() {
1111                 url_len += 1;
1112             }
1113             match state {
1114                 ParseState::InDescriptor => {
1115                     match next_char {
1116                         Some((_, ' ')) => {
1117                             if !current_descriptor.is_empty() {
1118                                 descriptors.push(current_descriptor.clone());
1119                                 current_descriptor = String::new();
1120                                 state = ParseState::AfterDescriptor;
1121                             }
1122                             continue;
1123                         }
1124                         Some((_, ',')) => {
1125                             if !current_descriptor.is_empty() {
1126                                 descriptors.push(current_descriptor.clone());
1127                             }
1128                             break;
1129                         }
1130                         Some((_, c @ '(')) => {
1131                             current_descriptor.push(c);
1132                             state = ParseState::InParens;
1133                             continue;
1134                         }
1135                         Some((_, c)) => {
1136                             current_descriptor.push(c);
1137                         }
1138                         None => {
1139                             if !current_descriptor.is_empty() {
1140                                 descriptors.push(current_descriptor.clone());
1141                             }
1142                             break;
1143                         }
1144                     }
1145                 }
1146                 ParseState::InParens => {
1147                     match next_char {
1148                         Some((_, c @ ')')) => {
1149                             current_descriptor.push(c);
1150                             state = ParseState::InDescriptor;
1151                             continue;
1152                         }
1153                         Some((_, c)) => {
1154                             current_descriptor.push(c);
1155                             continue;
1156                         }
1157                         None => {
1158                             if !current_descriptor.is_empty() {
1159                                 descriptors.push(current_descriptor.clone());
1160                             }
1161                             break;
1162                         }
1163                     }
1164                 }
1165                 ParseState::AfterDescriptor => {
1166                     match next_char {
1167                         Some((_, ' ')) => {
1168                             state = ParseState::AfterDescriptor;
1169                             continue;
1170                         }
1171                         Some((idx, c)) => {
1172                             state = ParseState::InDescriptor;
1173                             buffered = Some((idx, c));
1174                             continue;
1175                         }
1176                         None => {
1177                             if !current_descriptor.is_empty() {
1178                                 descriptors.push(current_descriptor.clone());
1179                             }
1180                             break;
1181                         }
1182                     }
1183                 }
1184             }
1185         }
1186 
1187         let mut error = false;
1188         let mut width: Option<u32> = None;
1189         let mut density: Option<f64> = None;
1190         let mut future_compat_h: Option<u32> = None;
1191         for descriptor in descriptors {
1192             let (digits, remaining) = collect_sequence_characters(&descriptor, |c| is_ascii_digit(c) || *c == '.');
1193             let valid_non_negative_integer = parse_unsigned_integer(digits.chars());
1194             let has_w = remaining == "w";
1195             let valid_floating_point = parse_double(digits);
1196             let has_x = remaining == "x";
1197             let has_h = remaining == "h";
1198             if valid_non_negative_integer.is_ok() && has_w {
1199                 let result = valid_non_negative_integer;
1200                 error = result.is_err();
1201                 if width.is_some() || density.is_some() {
1202                     error = true;
1203                 }
1204                 if let Ok(w) = result {
1205                     width = Some(w);
1206                 }
1207             } else if valid_floating_point.is_ok() && has_x {
1208                 let result = valid_floating_point;
1209                 error = result.is_err();
1210                 if width.is_some() || density.is_some() || future_compat_h.is_some() {
1211                     error = true;
1212                 }
1213                 if let Ok(x) = result {
1214                     density = Some(x);
1215                 }
1216             } else if valid_non_negative_integer.is_ok() && has_h {
1217                 let result = valid_non_negative_integer;
1218                 error = result.is_err();
1219                 if density.is_some() || future_compat_h.is_some() {
1220                     error = true;
1221                 }
1222                 if let Ok(h) = result {
1223                     future_compat_h = Some(h);
1224                 }
1225             } else {
1226                 error = true;
1227             }
1228         }
1229         if future_compat_h.is_some() && width.is_none() {
1230             error = true;
1231         }
1232         if !error {
1233             let descriptor = Descriptor { wid: width, den: density };
1234             let image_source = ImageSource { url: url, descriptor: descriptor };
1235             candidates.push(image_source);
1236         }
1237     }
1238     candidates
1239 }
1240