1use std::cell::Cell;
6use std::default::Default;
7
8use dom_struct::dom_struct;
9use html5ever::{LocalName, Prefix, local_name};
10use js::rust::HandleObject;
11use num_traits::ToPrimitive;
12use servo_url::ServoUrl;
13use style::attr::AttrValue;
14use stylo_atoms::Atom;
15
16use crate::dom::activation::Activatable;
17use crate::dom::attr::Attr;
18use crate::dom::bindings::cell::DomRefCell;
19use crate::dom::bindings::codegen::Bindings::HTMLAnchorElementBinding::HTMLAnchorElementMethods;
20use crate::dom::bindings::codegen::Bindings::MouseEventBinding::MouseEventMethods;
21use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
22use crate::dom::bindings::inheritance::Castable;
23use crate::dom::bindings::root::{DomRoot, MutNullableDom};
24use crate::dom::bindings::str::{DOMString, USVString};
25use crate::dom::document::Document;
26use crate::dom::domtokenlist::DOMTokenList;
27use crate::dom::element::{AttributeMutation, Element, reflect_referrer_policy_attribute};
28use crate::dom::event::Event;
29use crate::dom::eventtarget::EventTarget;
30use crate::dom::html::htmlelement::HTMLElement;
31use crate::dom::html::htmlhyperlinkelementutils::{HyperlinkElement, HyperlinkElementTraits};
32use crate::dom::html::htmlimageelement::HTMLImageElement;
33use crate::dom::mouseevent::MouseEvent;
34use crate::dom::node::{BindContext, Node};
35use crate::dom::virtualmethods::VirtualMethods;
36use crate::links::{LinkRelations, follow_hyperlink};
37use crate::script_runtime::CanGc;
38
39#[dom_struct]
40pub(crate) struct HTMLAnchorElement {
41 htmlelement: HTMLElement,
42 rel_list: MutNullableDom<DOMTokenList>,
43 #[no_trace]
44 relations: Cell<LinkRelations>,
45 #[no_trace]
46 url: DomRefCell<Option<ServoUrl>>,
47}
48
49impl HTMLAnchorElement {
50 fn new_inherited(
51 local_name: LocalName,
52 prefix: Option<Prefix>,
53 document: &Document,
54 ) -> HTMLAnchorElement {
55 HTMLAnchorElement {
56 htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
57 rel_list: Default::default(),
58 relations: Cell::new(LinkRelations::empty()),
59 url: DomRefCell::new(None),
60 }
61 }
62
63 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
64 pub(crate) fn new(
65 local_name: LocalName,
66 prefix: Option<Prefix>,
67 document: &Document,
68 proto: Option<HandleObject>,
69 can_gc: CanGc,
70 ) -> DomRoot<HTMLAnchorElement> {
71 Node::reflect_node_with_proto(
72 Box::new(HTMLAnchorElement::new_inherited(
73 local_name, prefix, document,
74 )),
75 document,
76 proto,
77 can_gc,
78 )
79 }
80}
81
82impl HyperlinkElement for HTMLAnchorElement {
83 fn get_url(&self) -> &DomRefCell<Option<ServoUrl>> {
84 &self.url
85 }
86}
87
88impl VirtualMethods for HTMLAnchorElement {
89 fn super_type(&self) -> Option<&dyn VirtualMethods> {
90 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
91 }
92
93 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
94 match name {
95 &local_name!("rel") => AttrValue::from_serialized_tokenlist(value.into()),
96 _ => self
97 .super_type()
98 .unwrap()
99 .parse_plain_attribute(name, value),
100 }
101 }
102
103 fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
104 self.super_type()
105 .unwrap()
106 .attribute_mutated(attr, mutation, can_gc);
107
108 match *attr.local_name() {
109 local_name!("rel") | local_name!("rev") => {
110 self.relations
111 .set(LinkRelations::for_element(self.upcast()));
112 },
113 _ => {},
114 }
115 }
116
117 fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) {
118 if let Some(s) = self.super_type() {
119 s.bind_to_tree(context, can_gc);
120 }
121
122 self.relations
123 .set(LinkRelations::for_element(self.upcast()));
124 }
125}
126
127impl HTMLAnchorElementMethods<crate::DomTypeHolder> for HTMLAnchorElement {
128 fn Text(&self) -> DOMString {
130 self.upcast::<Node>().GetTextContent().unwrap()
131 }
132
133 fn SetText(&self, value: DOMString, can_gc: CanGc) {
135 self.upcast::<Node>()
136 .set_text_content_for_element(Some(value), can_gc)
137 }
138
139 make_getter!(Rel, "rel");
141
142 fn SetRel(&self, rel: DOMString, can_gc: CanGc) {
144 self.upcast::<Element>()
145 .set_tokenlist_attribute(&local_name!("rel"), rel, can_gc);
146 }
147
148 fn RelList(&self, can_gc: CanGc) -> DomRoot<DOMTokenList> {
150 self.rel_list.or_init(|| {
151 DOMTokenList::new(
152 self.upcast(),
153 &local_name!("rel"),
154 Some(vec![
155 Atom::from("noopener"),
156 Atom::from("noreferrer"),
157 Atom::from("opener"),
158 ]),
159 can_gc,
160 )
161 })
162 }
163
164 make_getter!(Coords, "coords");
166
167 make_setter!(SetCoords, "coords");
169
170 make_getter!(Name, "name");
172
173 make_atomic_setter!(SetName, "name");
175
176 make_getter!(Rev, "rev");
178
179 make_setter!(SetRev, "rev");
181
182 make_getter!(Shape, "shape");
184
185 make_setter!(SetShape, "shape");
187
188 make_getter!(Target, "target");
190
191 make_setter!(SetTarget, "target");
193
194 fn Href(&self) -> USVString {
196 self.get_href()
197 }
198
199 fn SetHref(&self, value: USVString, can_gc: CanGc) {
201 self.set_href(value, can_gc);
202 }
203
204 fn Origin(&self) -> USVString {
206 self.get_origin()
207 }
208
209 fn Protocol(&self) -> USVString {
211 self.get_protocol()
212 }
213
214 fn SetProtocol(&self, value: USVString, can_gc: CanGc) {
216 self.set_protocol(value, can_gc);
217 }
218
219 fn Password(&self) -> USVString {
221 self.get_password()
222 }
223
224 fn SetPassword(&self, value: USVString, can_gc: CanGc) {
226 self.set_password(value, can_gc);
227 }
228
229 fn Hash(&self) -> USVString {
231 self.get_hash()
232 }
233
234 fn SetHash(&self, value: USVString, can_gc: CanGc) {
236 self.set_hash(value, can_gc);
237 }
238
239 fn Host(&self) -> USVString {
241 self.get_host()
242 }
243
244 fn SetHost(&self, value: USVString, can_gc: CanGc) {
246 self.set_host(value, can_gc);
247 }
248
249 fn Hostname(&self) -> USVString {
251 self.get_hostname()
252 }
253
254 fn SetHostname(&self, value: USVString, can_gc: CanGc) {
256 self.set_hostname(value, can_gc);
257 }
258
259 fn Port(&self) -> USVString {
261 self.get_port()
262 }
263
264 fn SetPort(&self, value: USVString, can_gc: CanGc) {
266 self.set_port(value, can_gc);
267 }
268
269 fn Pathname(&self) -> USVString {
271 self.get_pathname()
272 }
273
274 fn SetPathname(&self, value: USVString, can_gc: CanGc) {
276 self.set_pathname(value, can_gc);
277 }
278
279 fn Search(&self) -> USVString {
281 self.get_search()
282 }
283
284 fn SetSearch(&self, value: USVString, can_gc: CanGc) {
286 self.set_search(value, can_gc);
287 }
288
289 fn Username(&self) -> USVString {
291 self.get_username()
292 }
293
294 fn SetUsername(&self, value: USVString, can_gc: CanGc) {
296 self.set_username(value, can_gc);
297 }
298
299 fn ReferrerPolicy(&self) -> DOMString {
301 reflect_referrer_policy_attribute(self.upcast::<Element>())
302 }
303
304 make_setter!(SetReferrerPolicy, "referrerpolicy");
306}
307
308impl Activatable for HTMLAnchorElement {
309 fn as_element(&self) -> &Element {
310 self.upcast::<Element>()
311 }
312
313 fn is_instance_activatable(&self) -> bool {
314 self.as_element().has_attribute(&local_name!("href"))
320 }
321
322 fn activation_behavior(&self, event: &Event, target: &EventTarget, _: CanGc) {
324 let element = self.as_element();
325 let mouse_event = event.downcast::<MouseEvent>().unwrap();
326 let mut ismap_suffix = None;
327
328 if let Some(element) = target.downcast::<Element>() {
331 if target.is::<HTMLImageElement>() && element.has_attribute(&local_name!("ismap")) {
332 let target_node = element.upcast::<Node>();
333 let rect = target_node.border_box().unwrap_or_default();
334 ismap_suffix = Some(format!(
335 "?{},{}",
336 mouse_event.ClientX().to_f32().unwrap() - rect.origin.x.to_f32_px(),
337 mouse_event.ClientY().to_f32().unwrap() - rect.origin.y.to_f32_px()
338 ))
339 }
340 }
341
342 follow_hyperlink(element, self.relations.get(), ismap_suffix);
345 }
346}