use std::cell::Cell;
use std::default::Default;
use dom_struct::dom_struct;
use html5ever::{LocalName, Prefix, local_name};
use js::rust::HandleObject;
use num_traits::ToPrimitive;
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::HTMLAnchorElementBinding::HTMLAnchorElementMethods;
use crate::dom::bindings::codegen::Bindings::MouseEventBinding::MouseEventMethods;
use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
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::htmlelement::HTMLElement;
use crate::dom::htmlhyperlinkelementutils::{HyperlinkElement, HyperlinkElementTraits};
use crate::dom::htmlimageelement::HTMLImageElement;
use crate::dom::mouseevent::MouseEvent;
use crate::dom::node::{BindContext, Node};
use crate::dom::virtualmethods::VirtualMethods;
use crate::links::{LinkRelations, follow_hyperlink};
use crate::script_runtime::CanGc;
#[dom_struct]
pub(crate) struct HTMLAnchorElement {
htmlelement: HTMLElement,
rel_list: MutNullableDom<DOMTokenList>,
#[no_trace]
relations: Cell<LinkRelations>,
#[no_trace]
url: DomRefCell<Option<ServoUrl>>,
}
impl HTMLAnchorElement {
fn new_inherited(
local_name: LocalName,
prefix: Option<Prefix>,
document: &Document,
) -> HTMLAnchorElement {
HTMLAnchorElement {
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<HTMLAnchorElement> {
Node::reflect_node_with_proto(
Box::new(HTMLAnchorElement::new_inherited(
local_name, prefix, document,
)),
document,
proto,
can_gc,
)
}
}
impl HyperlinkElement for HTMLAnchorElement {
fn get_url(&self) -> &DomRefCell<Option<ServoUrl>> {
&self.url
}
}
impl VirtualMethods for HTMLAnchorElement {
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) {
self.super_type().unwrap().attribute_mutated(attr, mutation);
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) {
if let Some(s) = self.super_type() {
s.bind_to_tree(context);
}
self.relations
.set(LinkRelations::for_element(self.upcast()));
}
}
impl HTMLAnchorElementMethods<crate::DomTypeHolder> for HTMLAnchorElement {
fn Text(&self) -> DOMString {
self.upcast::<Node>().GetTextContent().unwrap()
}
fn SetText(&self, value: DOMString, can_gc: CanGc) {
self.upcast::<Node>().SetTextContent(Some(value), can_gc)
}
make_getter!(Rel, "rel");
fn SetRel(&self, rel: DOMString, can_gc: CanGc) {
self.upcast::<Element>()
.set_tokenlist_attribute(&local_name!("rel"), rel, can_gc);
}
fn RelList(&self) -> 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"),
]),
CanGc::note(),
)
})
}
make_getter!(Coords, "coords");
make_setter!(SetCoords, "coords");
make_getter!(Name, "name");
make_atomic_setter!(SetName, "name");
make_getter!(Rev, "rev");
make_setter!(SetRev, "rev");
make_getter!(Shape, "shape");
make_setter!(SetShape, "shape");
make_getter!(Target, "target");
make_setter!(SetTarget, "target");
fn Href(&self) -> USVString {
self.get_href()
}
fn SetHref(&self, value: USVString, can_gc: CanGc) {
self.set_href(value, can_gc);
}
fn Origin(&self) -> USVString {
self.get_origin()
}
fn Protocol(&self) -> USVString {
self.get_protocol()
}
fn SetProtocol(&self, value: USVString, can_gc: CanGc) {
self.set_protocol(value, can_gc);
}
fn Password(&self) -> USVString {
self.get_password()
}
fn SetPassword(&self, value: USVString, can_gc: CanGc) {
self.set_password(value, can_gc);
}
fn Hash(&self) -> USVString {
self.get_hash()
}
fn SetHash(&self, value: USVString, can_gc: CanGc) {
self.set_hash(value, can_gc);
}
fn Host(&self) -> USVString {
self.get_host()
}
fn SetHost(&self, value: USVString, can_gc: CanGc) {
self.set_host(value, can_gc);
}
fn Hostname(&self) -> USVString {
self.get_hostname()
}
fn SetHostname(&self, value: USVString, can_gc: CanGc) {
self.set_hostname(value, can_gc);
}
fn Port(&self) -> USVString {
self.get_port()
}
fn SetPort(&self, value: USVString, can_gc: CanGc) {
self.set_port(value, can_gc);
}
fn Pathname(&self) -> USVString {
self.get_pathname()
}
fn SetPathname(&self, value: USVString, can_gc: CanGc) {
self.set_pathname(value, can_gc);
}
fn Search(&self) -> USVString {
self.get_search()
}
fn SetSearch(&self, value: USVString, can_gc: CanGc) {
self.set_search(value, can_gc);
}
fn Username(&self) -> USVString {
self.get_username()
}
fn SetUsername(&self, value: USVString, can_gc: CanGc) {
self.set_username(value, can_gc);
}
fn ReferrerPolicy(&self) -> DOMString {
reflect_referrer_policy_attribute(self.upcast::<Element>())
}
make_setter!(SetReferrerPolicy, "referrerpolicy");
}
impl Activatable for HTMLAnchorElement {
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) {
let element = self.as_element();
let mouse_event = event.downcast::<MouseEvent>().unwrap();
let mut ismap_suffix = None;
if let Some(element) = target.downcast::<Element>() {
if target.is::<HTMLImageElement>() && element.has_attribute(&local_name!("ismap")) {
let target_node = element.upcast::<Node>();
let rect = target_node.bounding_content_box_or_zero(can_gc);
ismap_suffix = Some(format!(
"?{},{}",
mouse_event.ClientX().to_f32().unwrap() - rect.origin.x.to_f32_px(),
mouse_event.ClientY().to_f32().unwrap() - rect.origin.y.to_f32_px()
))
}
}
follow_hyperlink(element, self.relations.get(), ismap_suffix);
}
}