mirror of
https://github.com/servo/servo.git
synced 2025-09-30 08:39:16 +01:00
script: Move HTML DOM interfaces to script/dom/html/
(#39046)
See #38901. Testing: Refactor Fixes: Partially #38901 Signed-off-by: Ashwin Naren <arihant2math@gmail.com>
This commit is contained in:
parent
ec1b9b2480
commit
c92cd9e624
142 changed files with 546 additions and 533 deletions
529
components/script/dom/html/htmlareaelement.rs
Normal file
529
components/script/dom/html/htmlareaelement.rs
Normal file
|
@ -0,0 +1,529 @@
|
|||
/* 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 https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::default::Default;
|
||||
use std::{f32, str};
|
||||
|
||||
use cssparser::match_ignore_ascii_case;
|
||||
use dom_struct::dom_struct;
|
||||
use euclid::default::Point2D;
|
||||
use html5ever::{LocalName, Prefix, local_name};
|
||||
use js::rust::HandleObject;
|
||||
use servo_url::ServoUrl;
|
||||
use style::attr::AttrValue;
|
||||
use stylo_atoms::Atom;
|
||||
|
||||
use crate::dom::activation::Activatable;
|
||||
use crate::dom::attr::Attr;
|
||||
use crate::dom::bindings::cell::DomRefCell;
|
||||
use crate::dom::bindings::codegen::Bindings::HTMLAreaElementBinding::HTMLAreaElementMethods;
|
||||
use crate::dom::bindings::inheritance::Castable;
|
||||
use crate::dom::bindings::root::{DomRoot, MutNullableDom};
|
||||
use crate::dom::bindings::str::{DOMString, USVString};
|
||||
use crate::dom::document::Document;
|
||||
use crate::dom::domtokenlist::DOMTokenList;
|
||||
use crate::dom::element::{AttributeMutation, Element, reflect_referrer_policy_attribute};
|
||||
use crate::dom::event::Event;
|
||||
use crate::dom::eventtarget::EventTarget;
|
||||
use crate::dom::html::htmlelement::HTMLElement;
|
||||
use crate::dom::html::htmlhyperlinkelementutils::{HyperlinkElement, HyperlinkElementTraits};
|
||||
use crate::dom::node::{BindContext, Node};
|
||||
use crate::dom::virtualmethods::VirtualMethods;
|
||||
use crate::links::{LinkRelations, follow_hyperlink};
|
||||
use crate::script_runtime::CanGc;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Area {
|
||||
Circle {
|
||||
left: f32,
|
||||
top: f32,
|
||||
radius: f32,
|
||||
},
|
||||
Rectangle {
|
||||
top_left: (f32, f32),
|
||||
bottom_right: (f32, f32),
|
||||
},
|
||||
Polygon {
|
||||
/// Stored as a flat array of coordinates
|
||||
/// e.g. [x1, y1, x2, y2, x3, y3] for a triangle
|
||||
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();
|
||||
|
||||
// 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,
|
||||
_ => array.push(val),
|
||||
}
|
||||
|
||||
index += 1;
|
||||
}
|
||||
|
||||
// The input does not consist any valid characters
|
||||
if array.is_empty() {
|
||||
break;
|
||||
}
|
||||
|
||||
// Convert String to float
|
||||
match str::from_utf8(&array)
|
||||
.ok()
|
||||
.and_then(|s| s.parse::<f32>().ok())
|
||||
{
|
||||
Some(v) => number_list.push(v),
|
||||
None => number_list.push(0.0),
|
||||
};
|
||||
|
||||
array.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
|
||||
},
|
||||
|
||||
Area::Polygon { ref points } => {
|
||||
// Ray-casting algorithm to determine if point is inside polygon
|
||||
// https://en.wikipedia.org/wiki/Point_in_polygon#Ray_casting_algorithm
|
||||
let mut inside = false;
|
||||
|
||||
debug_assert!(points.len() % 2 == 0);
|
||||
let vertices = points.len() / 2;
|
||||
|
||||
for i in 0..vertices {
|
||||
let next_i = if i + 1 == vertices { 0 } else { i + 1 };
|
||||
|
||||
let xi = points[2 * i];
|
||||
let yi = points[2 * i + 1];
|
||||
let xj = points[2 * next_i];
|
||||
let yj = points[2 * next_i + 1];
|
||||
|
||||
if (yi > p.y) != (yj > p.y) && p.x < (xj - xi) * (p.y - yi) / (yj - yi) + xi {
|
||||
inside = !inside;
|
||||
}
|
||||
}
|
||||
inside
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) 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,
|
||||
},
|
||||
Area::Polygon { ref points } => {
|
||||
// let new_points = Vec::new();
|
||||
let iter = points
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, point)| match index % 2 {
|
||||
0 => point + p.x,
|
||||
_ => point + p.y,
|
||||
});
|
||||
Area::Polygon {
|
||||
points: iter.collect::<Vec<_>>(),
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[dom_struct]
|
||||
pub(crate) struct HTMLAreaElement {
|
||||
htmlelement: HTMLElement,
|
||||
rel_list: MutNullableDom<DOMTokenList>,
|
||||
#[no_trace]
|
||||
relations: Cell<LinkRelations>,
|
||||
#[no_trace]
|
||||
url: DomRefCell<Option<ServoUrl>>,
|
||||
}
|
||||
|
||||
impl HTMLAreaElement {
|
||||
fn new_inherited(
|
||||
local_name: LocalName,
|
||||
prefix: Option<Prefix>,
|
||||
document: &Document,
|
||||
) -> HTMLAreaElement {
|
||||
HTMLAreaElement {
|
||||
htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
|
||||
rel_list: Default::default(),
|
||||
relations: Cell::new(LinkRelations::empty()),
|
||||
url: DomRefCell::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
|
||||
pub(crate) fn new(
|
||||
local_name: LocalName,
|
||||
prefix: Option<Prefix>,
|
||||
document: &Document,
|
||||
proto: Option<HandleObject>,
|
||||
can_gc: CanGc,
|
||||
) -> DomRoot<HTMLAreaElement> {
|
||||
Node::reflect_node_with_proto(
|
||||
Box::new(HTMLAreaElement::new_inherited(local_name, prefix, document)),
|
||||
document,
|
||||
proto,
|
||||
can_gc,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) 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_ignore_ascii_case! { &shape,
|
||||
"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 HyperlinkElement for HTMLAreaElement {
|
||||
fn get_url(&self) -> &DomRefCell<Option<ServoUrl>> {
|
||||
&self.url
|
||||
}
|
||||
}
|
||||
|
||||
impl VirtualMethods for HTMLAreaElement {
|
||||
fn super_type(&self) -> Option<&dyn VirtualMethods> {
|
||||
Some(self.upcast::<HTMLElement>() as &dyn 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),
|
||||
}
|
||||
}
|
||||
|
||||
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
|
||||
self.super_type()
|
||||
.unwrap()
|
||||
.attribute_mutated(attr, mutation, can_gc);
|
||||
|
||||
match *attr.local_name() {
|
||||
local_name!("rel") | local_name!("rev") => {
|
||||
self.relations
|
||||
.set(LinkRelations::for_element(self.upcast()));
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) {
|
||||
if let Some(s) = self.super_type() {
|
||||
s.bind_to_tree(context, can_gc);
|
||||
}
|
||||
|
||||
self.relations
|
||||
.set(LinkRelations::for_element(self.upcast()));
|
||||
}
|
||||
}
|
||||
|
||||
impl HTMLAreaElementMethods<crate::DomTypeHolder> for HTMLAreaElement {
|
||||
// https://html.spec.whatwg.org/multipage/#attr-hyperlink-target
|
||||
make_getter!(Target, "target");
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#attr-hyperlink-target
|
||||
make_setter!(SetTarget, "target");
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#dom-a-rel
|
||||
make_getter!(Rel, "rel");
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#dom-a-rel
|
||||
fn SetRel(&self, rel: DOMString, can_gc: CanGc) {
|
||||
self.upcast::<Element>()
|
||||
.set_tokenlist_attribute(&local_name!("rel"), rel, can_gc);
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-area-rellist>
|
||||
fn RelList(&self, can_gc: CanGc) -> DomRoot<DOMTokenList> {
|
||||
self.rel_list.or_init(|| {
|
||||
DOMTokenList::new(
|
||||
self.upcast(),
|
||||
&local_name!("rel"),
|
||||
Some(vec![
|
||||
Atom::from("noopener"),
|
||||
Atom::from("noreferrer"),
|
||||
Atom::from("opener"),
|
||||
]),
|
||||
can_gc,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#attr-iframe-referrerpolicy>
|
||||
fn ReferrerPolicy(&self) -> DOMString {
|
||||
reflect_referrer_policy_attribute(self.upcast::<Element>())
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#attr-iframe-referrerpolicy
|
||||
make_setter!(SetReferrerPolicy, "referrerpolicy");
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-href>
|
||||
fn Href(&self) -> USVString {
|
||||
self.get_href()
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-href>
|
||||
fn SetHref(&self, value: USVString, can_gc: CanGc) {
|
||||
self.set_href(value, can_gc);
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-origin>
|
||||
fn Origin(&self) -> USVString {
|
||||
self.get_origin()
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-protocol>
|
||||
fn Protocol(&self) -> USVString {
|
||||
self.get_protocol()
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-protocol>
|
||||
fn SetProtocol(&self, value: USVString, can_gc: CanGc) {
|
||||
self.set_protocol(value, can_gc);
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-password>
|
||||
fn Password(&self) -> USVString {
|
||||
self.get_password()
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-password>
|
||||
fn SetPassword(&self, value: USVString, can_gc: CanGc) {
|
||||
self.set_password(value, can_gc);
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-hash>
|
||||
fn Hash(&self) -> USVString {
|
||||
self.get_hash()
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-hash>
|
||||
fn SetHash(&self, value: USVString, can_gc: CanGc) {
|
||||
self.set_hash(value, can_gc);
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-host>
|
||||
fn Host(&self) -> USVString {
|
||||
self.get_host()
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-host>
|
||||
fn SetHost(&self, value: USVString, can_gc: CanGc) {
|
||||
self.set_host(value, can_gc);
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-hostname>
|
||||
fn Hostname(&self) -> USVString {
|
||||
self.get_hostname()
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-hostname>
|
||||
fn SetHostname(&self, value: USVString, can_gc: CanGc) {
|
||||
self.set_hostname(value, can_gc);
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-port>
|
||||
fn Port(&self) -> USVString {
|
||||
self.get_port()
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-port>
|
||||
fn SetPort(&self, value: USVString, can_gc: CanGc) {
|
||||
self.set_port(value, can_gc);
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-pathname>
|
||||
fn Pathname(&self) -> USVString {
|
||||
self.get_pathname()
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-pathname>
|
||||
fn SetPathname(&self, value: USVString, can_gc: CanGc) {
|
||||
self.set_pathname(value, can_gc);
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-search>
|
||||
fn Search(&self) -> USVString {
|
||||
self.get_search()
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-search>
|
||||
fn SetSearch(&self, value: USVString, can_gc: CanGc) {
|
||||
self.set_search(value, can_gc);
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-username>
|
||||
fn Username(&self) -> USVString {
|
||||
self.get_username()
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-username>
|
||||
fn SetUsername(&self, value: USVString, can_gc: CanGc) {
|
||||
self.set_username(value, can_gc);
|
||||
}
|
||||
}
|
||||
|
||||
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 activation_behavior(&self, _event: &Event, _target: &EventTarget, _can_gc: CanGc) {
|
||||
follow_hyperlink(self.as_element(), self.relations.get(), None);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue