aboutsummaryrefslogtreecommitdiffstats
path: root/components/script
diff options
context:
space:
mode:
authorbors-servo <lbergstrom+bors@mozilla.com>2017-01-11 12:25:46 -0800
committerGitHub <noreply@github.com>2017-01-11 12:25:46 -0800
commitf6940f686cce249626e4fe32cc0fe5e0dcd40dd6 (patch)
tree3f63cd2d96fe9b60d1788eb6c30a0bf08ca42826 /components/script
parent5d3847dddc9bb7907abfa5d38a7927d6c656fbc1 (diff)
parentd3be70b4de755ead9e6fef6995c8085e0763b88a (diff)
downloadservo-f6940f686cce249626e4fe32cc0fe5e0dcd40dd6.tar.gz
servo-f6940f686cce249626e4fe32cc0fe5e0dcd40dd6.zip
Auto merge of #13972 - shravan-achar:master, r=Manishearth,emilio,jdm
ImageMaps: Implemented support for parsing coord attribute to a shape… <!-- Please describe your changes on the following line: --> Image Maps: (Part 1) Implemented support for parsing coord attribute to a shape object. Implemented a hit_test method to see if a point is within the area. Tests for constructors and hit_test included --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [ ] `./mach build -d` does not report any errors - [ ] `./mach test-tidy` does not report any errors - [ ] These changes fix #__ (github issue number if applicable). <!-- Either: --> - [ ] There are tests for these changes OR - [ ] These changes do not require tests because _____ <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> … object in HTMLAreaElement <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/13972) <!-- Reviewable:end -->
Diffstat (limited to 'components/script')
-rw-r--r--components/script/dom/htmlanchorelement.rs2
-rw-r--r--components/script/dom/htmlareaelement.rs256
-rw-r--r--components/script/dom/htmlimageelement.rs79
-rw-r--r--components/script/test.rs4
4 files changed, 339 insertions, 2 deletions
diff --git a/components/script/dom/htmlanchorelement.rs b/components/script/dom/htmlanchorelement.rs
index 52c355f2bab..3731b9d46de 100644
--- a/components/script/dom/htmlanchorelement.rs
+++ b/components/script/dom/htmlanchorelement.rs
@@ -576,7 +576,7 @@ fn is_current_browsing_context(target: DOMString) -> bool {
}
/// https://html.spec.whatwg.org/multipage/#following-hyperlinks-2
-fn follow_hyperlink(subject: &Element, hyperlink_suffix: Option<String>, referrer_policy: Option<ReferrerPolicy>) {
+pub fn follow_hyperlink(subject: &Element, hyperlink_suffix: Option<String>, referrer_policy: Option<ReferrerPolicy>) {
// Step 1: replace.
// Step 2: source browsing context.
// Step 3: target browsing context.
diff --git a/components/script/dom/htmlareaelement.rs b/components/script/dom/htmlareaelement.rs
index 79240885167..7b86a61a773 100644
--- a/components/script/dom/htmlareaelement.rs
+++ b/components/script/dom/htmlareaelement.rs
@@ -2,6 +2,8 @@
* 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;
@@ -9,13 +11,208 @@ 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;
+use dom::node::{Node, document_from_node};
use dom::virtualmethods::VirtualMethods;
+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,
@@ -38,6 +235,26 @@ impl HTMLAreaElement {
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 {
@@ -61,3 +278,40 @@ impl HTMLAreaElementMethods for HTMLAreaElement {
})
}
}
+
+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);
+ }
+}
diff --git a/components/script/dom/htmlimageelement.rs b/components/script/dom/htmlimageelement.rs
index a86f0258918..29ddcd0a7c4 100644
--- a/components/script/dom/htmlimageelement.rs
+++ b/components/script/dom/htmlimageelement.rs
@@ -3,10 +3,14 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use app_units::{Au, AU_PER_PX};
+use dom::activation::Activatable;
use dom::attr::Attr;
use dom::bindings::cell::DOMRefCell;
+use dom::bindings::codegen::Bindings::DOMRectBinding::DOMRectBinding::DOMRectMethods;
+use dom::bindings::codegen::Bindings::ElementBinding::ElementBinding::ElementMethods;
use dom::bindings::codegen::Bindings::HTMLImageElementBinding;
use dom::bindings::codegen::Bindings::HTMLImageElementBinding::HTMLImageElementMethods;
+use dom::bindings::codegen::Bindings::MouseEventBinding::MouseEventMethods;
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
use dom::bindings::error::Fallible;
use dom::bindings::inheritance::Castable;
@@ -15,17 +19,23 @@ use dom::bindings::refcounted::Trusted;
use dom::bindings::str::DOMString;
use dom::document::Document;
use dom::element::{AttributeMutation, Element, RawLayoutElementHelpers};
+use dom::event::Event;
use dom::eventtarget::EventTarget;
+use dom::htmlareaelement::HTMLAreaElement;
use dom::htmlelement::HTMLElement;
+use dom::htmlmapelement::HTMLMapElement;
+use dom::mouseevent::MouseEvent;
use dom::node::{Node, NodeDamage, document_from_node, window_from_node};
use dom::values::UNSIGNED_LONG_MAX;
use dom::virtualmethods::VirtualMethods;
use dom::window::Window;
+use euclid::point::Point2D;
use html5ever_atoms::LocalName;
use ipc_channel::ipc;
use ipc_channel::router::ROUTER;
use net_traits::image::base::{Image, ImageMetadata};
use net_traits::image_cache_thread::{ImageResponder, ImageResponse};
+use num_traits::ToPrimitive;
use script_thread::Runnable;
use servo_url::ServoUrl;
use std::i32;
@@ -234,6 +244,38 @@ impl HTMLImageElement {
Ok(image)
}
+ pub fn areas(&self) -> Option<Vec<Root<HTMLAreaElement>>> {
+ let elem = self.upcast::<Element>();
+ let usemap_attr;
+ if elem.has_attribute(&LocalName::from("usemap")) {
+ usemap_attr = elem.get_string_attribute(&local_name!("usemap"));
+ } else {
+ return None;
+ }
+
+ let (first, last) = usemap_attr.split_at(1);
+
+ match first {
+ "#" => {},
+ _ => return None,
+ };
+
+ match last.len() {
+ 0 => return None,
+ _ => {},
+ };
+
+ let map = self.upcast::<Node>()
+ .following_siblings()
+ .filter_map(Root::downcast::<HTMLMapElement>)
+ .find(|n| n.upcast::<Element>().get_string_attribute(&LocalName::from("name")) == last);
+
+ let elements: Vec<Root<HTMLAreaElement>> = map.unwrap().upcast::<Node>()
+ .children()
+ .filter_map(Root::downcast::<HTMLAreaElement>)
+ .collect();
+ Some(elements)
+ }
}
pub trait LayoutHTMLImageElementHelpers {
@@ -429,6 +471,43 @@ impl VirtualMethods for HTMLImageElement {
_ => self.super_type().unwrap().parse_plain_attribute(name, value),
}
}
+
+ fn handle_event(&self, event: &Event) {
+ if (event.type_() == atom!("click")) {
+ let area_elements = self.areas();
+ let elements = if let Some(x) = area_elements {
+ x
+ } else {
+ return
+ };
+
+ // Fetch click coordinates
+ let mouse_event = if let Some(x) = event.downcast::<MouseEvent>() {
+ x
+ } else {
+ return;
+ };
+
+ let point = Point2D::new(mouse_event.ClientX().to_f32().unwrap(), mouse_event.ClientY().to_f32().unwrap());
+ // Walk HTMLAreaElements
+ let mut index = 0;
+ while index < elements.len() {
+ let shape = elements[index].get_shape_from_coords();
+ let p = Point2D::new(self.upcast::<Element>().GetBoundingClientRect().X() as f32,
+ self.upcast::<Element>().GetBoundingClientRect().Y() as f32);
+ let shp = if let Some(x) = shape {
+ x.absolute_coords(p)
+ } else {
+ return
+ };
+ if shp.hit_test(point) {
+ elements[index].activation_behavior(event, self.upcast());
+ return
+ }
+ index += 1;
+ }
+ }
+ }
}
fn image_dimension_setter(element: &Element, attr: LocalName, value: u32) {
diff --git a/components/script/test.rs b/components/script/test.rs
index 31353b5ffa3..83523c47817 100644
--- a/components/script/test.rs
+++ b/components/script/test.rs
@@ -10,6 +10,10 @@ pub use dom::bindings::cell::DOMRefCell;
pub use dom::bindings::js::JS;
pub use dom::node::Node;
+pub mod area {
+ pub use dom::htmlareaelement::{Area, Shape};
+}
+
pub mod size_of {
use dom::characterdata::CharacterData;
use dom::element::Element;