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