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