mirror of
https://github.com/servo/servo.git
synced 2025-08-03 20:50:07 +01:00
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 -->
This commit is contained in:
commit
f6940f686c
8 changed files with 466 additions and 2 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -2364,6 +2364,7 @@ dependencies = [
|
||||||
name = "script_tests"
|
name = "script_tests"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"euclid 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"msg 0.0.1",
|
"msg 0.0.1",
|
||||||
"plugins 0.0.1",
|
"plugins 0.0.1",
|
||||||
"script 0.0.1",
|
"script 0.0.1",
|
||||||
|
|
|
@ -576,7 +576,7 @@ fn is_current_browsing_context(target: DOMString) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// https://html.spec.whatwg.org/multipage/#following-hyperlinks-2
|
/// 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 1: replace.
|
||||||
// Step 2: source browsing context.
|
// Step 2: source browsing context.
|
||||||
// Step 3: target browsing context.
|
// Step 3: target browsing context.
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* 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;
|
||||||
use dom::bindings::codegen::Bindings::HTMLAreaElementBinding::HTMLAreaElementMethods;
|
use dom::bindings::codegen::Bindings::HTMLAreaElementBinding::HTMLAreaElementMethods;
|
||||||
use dom::bindings::inheritance::Castable;
|
use dom::bindings::inheritance::Castable;
|
||||||
|
@ -9,13 +11,208 @@ use dom::bindings::js::{MutNullableJS, Root};
|
||||||
use dom::bindings::str::DOMString;
|
use dom::bindings::str::DOMString;
|
||||||
use dom::document::Document;
|
use dom::document::Document;
|
||||||
use dom::domtokenlist::DOMTokenList;
|
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::htmlelement::HTMLElement;
|
||||||
use dom::node::Node;
|
use dom::node::{Node, document_from_node};
|
||||||
use dom::virtualmethods::VirtualMethods;
|
use dom::virtualmethods::VirtualMethods;
|
||||||
|
use euclid::point::Point2D;
|
||||||
use html5ever_atoms::LocalName;
|
use html5ever_atoms::LocalName;
|
||||||
|
use net_traits::ReferrerPolicy;
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
|
use std::f32;
|
||||||
use style::attr::AttrValue;
|
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]
|
#[dom_struct]
|
||||||
pub struct HTMLAreaElement {
|
pub struct HTMLAreaElement {
|
||||||
htmlelement: HTMLElement,
|
htmlelement: HTMLElement,
|
||||||
|
@ -38,6 +235,26 @@ impl HTMLAreaElement {
|
||||||
document,
|
document,
|
||||||
HTMLAreaElementBinding::Wrap)
|
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 {
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -3,10 +3,14 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use app_units::{Au, AU_PER_PX};
|
use app_units::{Au, AU_PER_PX};
|
||||||
|
use dom::activation::Activatable;
|
||||||
use dom::attr::Attr;
|
use dom::attr::Attr;
|
||||||
use dom::bindings::cell::DOMRefCell;
|
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;
|
||||||
use dom::bindings::codegen::Bindings::HTMLImageElementBinding::HTMLImageElementMethods;
|
use dom::bindings::codegen::Bindings::HTMLImageElementBinding::HTMLImageElementMethods;
|
||||||
|
use dom::bindings::codegen::Bindings::MouseEventBinding::MouseEventMethods;
|
||||||
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
|
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
|
||||||
use dom::bindings::error::Fallible;
|
use dom::bindings::error::Fallible;
|
||||||
use dom::bindings::inheritance::Castable;
|
use dom::bindings::inheritance::Castable;
|
||||||
|
@ -15,17 +19,23 @@ use dom::bindings::refcounted::Trusted;
|
||||||
use dom::bindings::str::DOMString;
|
use dom::bindings::str::DOMString;
|
||||||
use dom::document::Document;
|
use dom::document::Document;
|
||||||
use dom::element::{AttributeMutation, Element, RawLayoutElementHelpers};
|
use dom::element::{AttributeMutation, Element, RawLayoutElementHelpers};
|
||||||
|
use dom::event::Event;
|
||||||
use dom::eventtarget::EventTarget;
|
use dom::eventtarget::EventTarget;
|
||||||
|
use dom::htmlareaelement::HTMLAreaElement;
|
||||||
use dom::htmlelement::HTMLElement;
|
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::node::{Node, NodeDamage, document_from_node, window_from_node};
|
||||||
use dom::values::UNSIGNED_LONG_MAX;
|
use dom::values::UNSIGNED_LONG_MAX;
|
||||||
use dom::virtualmethods::VirtualMethods;
|
use dom::virtualmethods::VirtualMethods;
|
||||||
use dom::window::Window;
|
use dom::window::Window;
|
||||||
|
use euclid::point::Point2D;
|
||||||
use html5ever_atoms::LocalName;
|
use html5ever_atoms::LocalName;
|
||||||
use ipc_channel::ipc;
|
use ipc_channel::ipc;
|
||||||
use ipc_channel::router::ROUTER;
|
use ipc_channel::router::ROUTER;
|
||||||
use net_traits::image::base::{Image, ImageMetadata};
|
use net_traits::image::base::{Image, ImageMetadata};
|
||||||
use net_traits::image_cache_thread::{ImageResponder, ImageResponse};
|
use net_traits::image_cache_thread::{ImageResponder, ImageResponse};
|
||||||
|
use num_traits::ToPrimitive;
|
||||||
use script_thread::Runnable;
|
use script_thread::Runnable;
|
||||||
use servo_url::ServoUrl;
|
use servo_url::ServoUrl;
|
||||||
use std::i32;
|
use std::i32;
|
||||||
|
@ -234,6 +244,38 @@ impl HTMLImageElement {
|
||||||
|
|
||||||
Ok(image)
|
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 {
|
pub trait LayoutHTMLImageElementHelpers {
|
||||||
|
@ -429,6 +471,43 @@ impl VirtualMethods for HTMLImageElement {
|
||||||
_ => self.super_type().unwrap().parse_plain_attribute(name, value),
|
_ => 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) {
|
fn image_dimension_setter(element: &Element, attr: LocalName, value: u32) {
|
||||||
|
|
|
@ -10,6 +10,10 @@ pub use dom::bindings::cell::DOMRefCell;
|
||||||
pub use dom::bindings::js::JS;
|
pub use dom::bindings::js::JS;
|
||||||
pub use dom::node::Node;
|
pub use dom::node::Node;
|
||||||
|
|
||||||
|
pub mod area {
|
||||||
|
pub use dom::htmlareaelement::{Area, Shape};
|
||||||
|
}
|
||||||
|
|
||||||
pub mod size_of {
|
pub mod size_of {
|
||||||
use dom::characterdata::CharacterData;
|
use dom::characterdata::CharacterData;
|
||||||
use dom::element::Element;
|
use dom::element::Element;
|
||||||
|
|
|
@ -10,6 +10,7 @@ path = "lib.rs"
|
||||||
doctest = false
|
doctest = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
euclid = "0.10.2"
|
||||||
msg = {path = "../../../components/msg"}
|
msg = {path = "../../../components/msg"}
|
||||||
plugins = {path = "../../../components/plugins"}
|
plugins = {path = "../../../components/plugins"}
|
||||||
script = {path = "../../../components/script"}
|
script = {path = "../../../components/script"}
|
||||||
|
|
122
tests/unit/script/htmlareaelement.rs
Normal file
122
tests/unit/script/htmlareaelement.rs
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
use euclid::point::Point2D;
|
||||||
|
use script::test::area::{Area, Shape};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn garbage_input() {
|
||||||
|
assert!(Area::parse(";.,()8.2", Shape::Circle).is_none())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn no_case_matching_input() {
|
||||||
|
assert!(Area::parse("8.2, 10.2", Shape::Circle).is_none())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn delimiter_input() {
|
||||||
|
assert!(Area::parse(";, ;,", Shape::Circle).is_none())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Area::Circle tests
|
||||||
|
#[test]
|
||||||
|
fn valid_circle_inputs() {
|
||||||
|
assert_eq!(Area::parse("10.2, 3.4, 5.2", Shape::Circle),
|
||||||
|
Some(Area::Circle { left: 10.2, top: 3.4, radius: 5.2 }));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn valid_negative_circle_inputs() {
|
||||||
|
assert_eq!(Area::parse("-10.2, -3.4, 5.2", Shape::Circle),
|
||||||
|
Some(Area::Circle { left: -10.2, top: -3.4, radius: 5.2 }));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn invalid_negative_circle_radius() {
|
||||||
|
assert!(Area::parse("-10.2, -3.4, -5.2", Shape::Circle).is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Area::Rectangle tests
|
||||||
|
#[test]
|
||||||
|
fn rectangle_valid_input() {
|
||||||
|
assert_eq!(Area::parse("5.2, 1.1, 10.2, 3.4", Shape::Rectangle),
|
||||||
|
Some(Area::Rectangle { top_left: (5.2, 1.1),
|
||||||
|
bottom_right: (10.2, 3.4) }));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rectangle_valid_negative_input() {
|
||||||
|
assert_eq!(Area::parse("-10.2, -3.4, -5.2, -1.1", Shape::Rectangle),
|
||||||
|
Some(Area::Rectangle { top_left: (-10.2, -3.4),
|
||||||
|
bottom_right: (-5.2, -1.1) }));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rectangle_invalid_input() {
|
||||||
|
assert_eq!(Area::parse("5.2, 4.3, 10.2, 1.1.2", Shape::Rectangle),
|
||||||
|
Some(Area::Rectangle { top_left: (5.2, 0.0),
|
||||||
|
bottom_right: (10.2, 4.3) }));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rectangle_unordered_input() {
|
||||||
|
assert_eq!(Area::parse("5.2, 1.1, 10.2, 4.3", Shape::Rectangle),
|
||||||
|
Some(Area::Rectangle { top_left: (5.2, 1.1),
|
||||||
|
bottom_right: (10.2, 4.3) }));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Area::Polygon tests
|
||||||
|
#[test]
|
||||||
|
fn polygon_six_points_valid_input() {
|
||||||
|
assert_eq!(Area::parse("1.1, 1.1, 6.1, 1.1, 3.1, 3.1", Shape::Polygon),
|
||||||
|
Some(Area::Polygon { points: vec![1.1, 1.1, 6.1, 1.1, 3.1, 3.1] }));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn polygon_six_points_valid_negative_input() {
|
||||||
|
assert_eq!(Area::parse("1.1, -1.1, 6.1, -1.1, 3.1, -3.1", Shape::Polygon),
|
||||||
|
Some(Area::Polygon { points: vec![1.1, -1.1, 6.1, -1.1, 3.1, -3.1] }));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn polygon_six_points_invalid_input() {
|
||||||
|
assert_eq!(Area::parse(";1.1, 1.1,'; 6.1,(*^() 1.1, 3.1, 3.1, 100.1 %$,;", Shape::Polygon),
|
||||||
|
Some(Area::Polygon { points: vec![1.1, 1.1, 6.1, 1.1, 3.1, 3.1] }));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn polygon_eight_points_invalid_input() {
|
||||||
|
assert_eq!(Area::parse("1.1, -1.1, 6.1, -1.1, 1.1, -3.1, 6.1, -3.1.2, 12.1", Shape::Polygon),
|
||||||
|
Some(Area::Polygon { points: vec![1.1, -1.1, 6.1, -1.1, 1.1, -3.1, 6.1, 0.0] }));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hit_test_circle() {
|
||||||
|
let circ1 = Area::Circle { left: 20.0, top: 10.0, radius: 5.0 };
|
||||||
|
assert!(!circ1.hit_test(Point2D::new(10.0, 20.0)));
|
||||||
|
let circ2 = Area::Circle { left: 10.0, top: 10.0, radius: 5.0 };
|
||||||
|
assert!(circ2.hit_test(Point2D::new(10.0, 12.0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hit_test_rectangle() {
|
||||||
|
let rect1 = Area::Rectangle { top_left: (1.0, 7.0), bottom_right: (15.0, 10.0) };
|
||||||
|
assert!(!rect1.hit_test(Point2D::new(10.0, 5.0)));
|
||||||
|
let rect2 = Area::Rectangle { top_left: (8.0, 10.0), bottom_right: (20.0, 12.0) };
|
||||||
|
assert!(rect2.hit_test(Point2D::new(10.0, 12.0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hit_test_polygon() {
|
||||||
|
let poly1 = Area::Polygon { points: vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0] };
|
||||||
|
assert!(!poly1.hit_test(Point2D::new(10.0, 5.0)));
|
||||||
|
let poly2 = Area::Polygon { points: vec![7.0, 7.5, 8.2, 9.0, 11.0, 12.0] };
|
||||||
|
assert!(!poly2.hit_test(Point2D::new(10.0, 5.0)));
|
||||||
|
}
|
|
@ -5,6 +5,7 @@
|
||||||
#![feature(plugin)]
|
#![feature(plugin)]
|
||||||
#![plugin(plugins)]
|
#![plugin(plugins)]
|
||||||
|
|
||||||
|
extern crate euclid;
|
||||||
extern crate msg;
|
extern crate msg;
|
||||||
extern crate script;
|
extern crate script;
|
||||||
extern crate servo_url;
|
extern crate servo_url;
|
||||||
|
@ -13,3 +14,5 @@ extern crate servo_url;
|
||||||
#[cfg(all(test, target_pointer_width = "64"))] mod size_of;
|
#[cfg(all(test, target_pointer_width = "64"))] mod size_of;
|
||||||
#[cfg(test)] mod textinput;
|
#[cfg(test)] mod textinput;
|
||||||
#[cfg(test)] mod headers;
|
#[cfg(test)] mod headers;
|
||||||
|
#[cfg(test)] mod htmlareaelement;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue