forked from mirrors/gecko-dev
		
	 be2b512750
			
		
	
	
		be2b512750
		
	
	
	
	
		
			
			The rustup is needed for https://github.com/rust-lang/rust/pull/40039. Source-Repo: https://github.com/servo/servo Source-Revision: a204c4176dcccdad8ec99d74055c66794c3f64ba --HG-- rename : servo/components/domobject_derive/Cargo.toml => servo/components/dom_struct/Cargo.toml extra : subtree_source : https%3A//hg.mozilla.org/projects/converted-servo-linear extra : subtree_revision : 2cad140c3d6b99837f0bd15a6f3ccba0f353e049
		
			
				
	
	
		
			318 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			318 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| use dom::activation::Activatable;
 | |
| use dom::bindings::codegen::Bindings::DOMTokenListBinding::DOMTokenListMethods;
 | |
| use dom::bindings::codegen::Bindings::HTMLAreaElementBinding;
 | |
| use dom::bindings::codegen::Bindings::HTMLAreaElementBinding::HTMLAreaElementMethods;
 | |
| use dom::bindings::inheritance::Castable;
 | |
| use dom::bindings::js::{MutNullableJS, Root};
 | |
| use dom::bindings::str::DOMString;
 | |
| use dom::document::Document;
 | |
| use dom::domtokenlist::DOMTokenList;
 | |
| use dom::element::Element;
 | |
| use dom::event::Event;
 | |
| use dom::eventtarget::EventTarget;
 | |
| use dom::htmlanchorelement::follow_hyperlink;
 | |
| use dom::htmlelement::HTMLElement;
 | |
| use dom::node::{Node, document_from_node};
 | |
| use dom::virtualmethods::VirtualMethods;
 | |
| use dom_struct::dom_struct;
 | |
| use euclid::point::Point2D;
 | |
| use html5ever_atoms::LocalName;
 | |
| use net_traits::ReferrerPolicy;
 | |
| use std::default::Default;
 | |
| use std::f32;
 | |
| use style::attr::AttrValue;
 | |
| 
 | |
| #[derive(PartialEq)]
 | |
| #[derive(Debug)]
 | |
| pub enum Area {
 | |
|     Circle { left: f32, top: f32, radius: f32 },
 | |
|     Rectangle { top_left: (f32, f32), bottom_right: (f32, f32) },
 | |
|     Polygon { points: Vec<f32> },
 | |
| }
 | |
| 
 | |
| pub enum Shape {
 | |
|     Circle,
 | |
|     Rectangle,
 | |
|     Polygon,
 | |
| }
 | |
| 
 | |
| // https://html.spec.whatwg.org/multipage/#rules-for-parsing-a-list-of-floating-point-numbers
 | |
| // https://html.spec.whatwg.org/multipage/#image-map-processing-model
 | |
| impl Area {
 | |
|     pub fn parse(coord: &str, target: Shape) -> Option<Area> {
 | |
|         let points_count =  match target {
 | |
|             Shape::Circle => 3,
 | |
|             Shape::Rectangle => 4,
 | |
|             Shape::Polygon => 0,
 | |
|         };
 | |
| 
 | |
|         let size = coord.len();
 | |
|         let num = coord.as_bytes();
 | |
|         let mut index = 0;
 | |
| 
 | |
|         // Step 4: Walk till char is not a delimiter
 | |
|         while index < size {
 | |
|             let val = num[index];
 | |
|             match val {
 | |
|                 b',' | b';' | b' ' | b'\t' | b'\n' | 0x0C | b'\r'  => {},
 | |
|                 _ => break,
 | |
|             }
 | |
| 
 | |
|             index += 1;
 | |
|         }
 | |
| 
 | |
|         //This vector will hold all parsed coordinates
 | |
|         let mut number_list = Vec::new();
 | |
|         let mut array = Vec::new();
 | |
|         let ar_ref = &mut array;
 | |
| 
 | |
|         // Step 5: walk till end of string
 | |
|         while index < size {
 | |
|             // Step 5.1: walk till we hit a valid char i.e., 0 to 9, dot or dash, e, E
 | |
|             while index < size {
 | |
|                 let val = num[index];
 | |
|                 match val {
 | |
|                     b'0'...b'9' | b'.' | b'-' | b'E' | b'e' => break,
 | |
|                     _ => {},
 | |
|                 }
 | |
| 
 | |
|                 index += 1;
 | |
|             }
 | |
| 
 | |
|             // Step 5.2: collect valid symbols till we hit another delimiter
 | |
|             while index < size {
 | |
|                 let val = num[index];
 | |
| 
 | |
|                 match val {
 | |
|                     b',' | b';' | b' ' | b'\t' | b'\n' | 0x0C | b'\r' => break,
 | |
|                     _ => (*ar_ref).push(val),
 | |
|                 }
 | |
| 
 | |
|                 index += 1;
 | |
|             }
 | |
| 
 | |
|             // The input does not consist any valid charecters
 | |
|             if (*ar_ref).is_empty() {
 | |
|                 break;
 | |
|             }
 | |
| 
 | |
|             // Convert String to float
 | |
|             match String::from_utf8((*ar_ref).clone()).unwrap().parse::<f32>() {
 | |
|                 Ok(v) => number_list.push(v),
 | |
|                 Err(_) => number_list.push(0.0),
 | |
|             };
 | |
| 
 | |
|             (*ar_ref).clear();
 | |
| 
 | |
|             // For rectangle and circle, stop parsing once we have three
 | |
|             // and four coordinates respectively
 | |
|             if points_count > 0 && number_list.len() == points_count {
 | |
|                 break;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         let final_size = number_list.len();
 | |
| 
 | |
|         match target {
 | |
|             Shape::Circle => {
 | |
|                 if final_size == 3 {
 | |
|                     if number_list[2] <= 0.0 {
 | |
|                         None
 | |
|                     } else {
 | |
|                         Some(Area::Circle {
 | |
|                                  left: number_list[0],
 | |
|                                  top: number_list[1],
 | |
|                                  radius: number_list[2]
 | |
|                              })
 | |
|                     }
 | |
|                 } else {
 | |
|                     None
 | |
|                 }
 | |
|             },
 | |
| 
 | |
|             Shape::Rectangle => {
 | |
|                 if final_size == 4 {
 | |
|                     if number_list[0] > number_list[2] {
 | |
|                         number_list.swap(0, 2);
 | |
|                     }
 | |
| 
 | |
|                     if number_list[1] > number_list[3] {
 | |
|                         number_list.swap(1, 3);
 | |
|                     }
 | |
| 
 | |
|                     Some(Area::Rectangle {
 | |
|                              top_left: (number_list[0], number_list[1]),
 | |
|                              bottom_right: (number_list[2], number_list[3])
 | |
|                          })
 | |
|                 } else {
 | |
|                     None
 | |
|                 }
 | |
|             },
 | |
| 
 | |
|             Shape::Polygon => {
 | |
|                 if final_size >= 6 {
 | |
|                     if final_size % 2 != 0 {
 | |
|                         // Drop last element if there are odd number of coordinates
 | |
|                         number_list.remove(final_size - 1);
 | |
|                     }
 | |
|                     Some(Area::Polygon { points: number_list })
 | |
|                 } else {
 | |
|                     None
 | |
|                 }
 | |
|             },
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     pub fn hit_test(&self, p: Point2D<f32>) -> bool {
 | |
|         match *self {
 | |
|             Area::Circle { left, top, radius } => {
 | |
|                 (p.x - left) * (p.x - left) +
 | |
|                 (p.y - top) * (p.y - top) -
 | |
|                 radius * radius <= 0.0
 | |
|             },
 | |
| 
 | |
|             Area::Rectangle { top_left, bottom_right } => {
 | |
|                 p.x <= bottom_right.0 && p.x >= top_left.0 &&
 | |
|                 p.y <= bottom_right.1 && p.y >= top_left.1
 | |
|             },
 | |
| 
 | |
|             //TODO polygon hit_test
 | |
|             _ => false,
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     pub fn absolute_coords(&self, p: Point2D<f32>) -> Area {
 | |
|         match *self {
 | |
|             Area::Rectangle { top_left, bottom_right } => {
 | |
|                 Area::Rectangle {
 | |
|                     top_left: (top_left.0 + p.x, top_left.1 + p.y),
 | |
|                     bottom_right: (bottom_right.0 + p.x, bottom_right.1 + p.y)
 | |
|                 }
 | |
|             },
 | |
|             Area::Circle { left, top, radius } => {
 | |
|                 Area::Circle {
 | |
|                     left: (left + p.x),
 | |
|                     top: (top + p.y),
 | |
|                     radius: radius
 | |
|                 }
 | |
|             },
 | |
|             Area::Polygon { ref points } => {
 | |
| //                let new_points = Vec::new();
 | |
|                 let iter = points.iter().enumerate().map(|(index, point)| {
 | |
|                     match index % 2 {
 | |
|                         0 => point + p.x as f32,
 | |
|                         _ => point + p.y as f32,
 | |
|                     }
 | |
|                 });
 | |
|                 Area::Polygon { points: iter.collect::<Vec<_>>() }
 | |
|             },
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| #[dom_struct]
 | |
| pub struct HTMLAreaElement {
 | |
|     htmlelement: HTMLElement,
 | |
|     rel_list: MutNullableJS<DOMTokenList>,
 | |
| }
 | |
| 
 | |
| impl HTMLAreaElement {
 | |
|     fn new_inherited(local_name: LocalName, prefix: Option<DOMString>, document: &Document) -> HTMLAreaElement {
 | |
|         HTMLAreaElement {
 | |
|             htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
 | |
|             rel_list: Default::default(),
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     #[allow(unrooted_must_root)]
 | |
|     pub fn new(local_name: LocalName,
 | |
|                prefix: Option<DOMString>,
 | |
|                document: &Document) -> Root<HTMLAreaElement> {
 | |
|         Node::reflect_node(box HTMLAreaElement::new_inherited(local_name, prefix, document),
 | |
|                            document,
 | |
|                            HTMLAreaElementBinding::Wrap)
 | |
|     }
 | |
| 
 | |
|     pub fn get_shape_from_coords(&self) -> Option<Area> {
 | |
|        let elem = self.upcast::<Element>();
 | |
|        let shape = elem.get_string_attribute(&"shape".into());
 | |
|        let shp: Shape = match shape.to_lowercase().as_ref() {
 | |
|            "circle" => Shape::Circle,
 | |
|            "circ" => Shape::Circle,
 | |
|            "rectangle" => Shape::Rectangle,
 | |
|            "rect" => Shape::Rectangle,
 | |
|            "polygon" => Shape::Rectangle,
 | |
|            "poly" => Shape::Polygon,
 | |
|            _ => return None,
 | |
|         };
 | |
|         if elem.has_attribute(&"coords".into()) {
 | |
|             let attribute = elem.get_string_attribute(&"coords".into());
 | |
|             Area::parse(&attribute, shp)
 | |
|         } else {
 | |
|             None
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| impl VirtualMethods for HTMLAreaElement {
 | |
|     fn super_type(&self) -> Option<&VirtualMethods> {
 | |
|         Some(self.upcast::<HTMLElement>() as &VirtualMethods)
 | |
|     }
 | |
| 
 | |
|     fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
 | |
|         match name {
 | |
|             &local_name!("rel") => AttrValue::from_serialized_tokenlist(value.into()),
 | |
|             _ => self.super_type().unwrap().parse_plain_attribute(name, value),
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| impl HTMLAreaElementMethods for HTMLAreaElement {
 | |
|     // https://html.spec.whatwg.org/multipage/#dom-area-rellist
 | |
|     fn RelList(&self) -> Root<DOMTokenList> {
 | |
|         self.rel_list.or_init(|| {
 | |
|             DOMTokenList::new(self.upcast(), &local_name!("rel"))
 | |
|         })
 | |
|     }
 | |
| }
 | |
| 
 | |
| impl Activatable for HTMLAreaElement {
 | |
|     // https://html.spec.whatwg.org/multipage/#the-area-element:activation-behaviour
 | |
|     fn as_element(&self) -> &Element {
 | |
|         self.upcast::<Element>()
 | |
|     }
 | |
| 
 | |
|     fn is_instance_activatable(&self) -> bool {
 | |
|         self.as_element().has_attribute(&local_name!("href"))
 | |
|     }
 | |
| 
 | |
|     fn pre_click_activation(&self) {
 | |
|     }
 | |
| 
 | |
|     fn canceled_activation(&self) {
 | |
|     }
 | |
| 
 | |
|     fn implicit_submission(&self, _ctrl_key: bool, _shift_key: bool,
 | |
|                            _alt_key: bool, _meta_key: bool) {
 | |
|     }
 | |
| 
 | |
|     fn activation_behavior(&self, _event: &Event, _target: &EventTarget) {
 | |
|         // Step 1
 | |
|         let doc = document_from_node(self);
 | |
|         if !doc.is_fully_active() {
 | |
|             return;
 | |
|         }
 | |
|         // Step 2
 | |
|         // TODO : We should be choosing a browsing context and navigating to it.
 | |
|         // Step 3
 | |
|         let referrer_policy = match self.RelList().Contains("noreferrer".into()) {
 | |
|             true => Some(ReferrerPolicy::NoReferrer),
 | |
|             false => None,
 | |
|         };
 | |
|         follow_hyperlink(self.upcast::<Element>(), None, referrer_policy);
 | |
|     }
 | |
| }
 |