1use std::cell::Cell;
6use std::default::Default;
7
8use dom_struct::dom_struct;
9use html5ever::{LocalName, Prefix, local_name};
10use js::context::{JSContext, NoGC};
11use js::rust::HandleObject;
12use net_traits::blob_url_store::UrlWithBlobClaim;
13use num_traits::ToPrimitive;
14use script_bindings::cell::DomRefCell;
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::htmlimageelement::HTMLImageElement;
34use crate::dom::html::links::htmlhyperlinkelementutils::{
35 HyperlinkElement, HyperlinkElementTraits,
36};
37use crate::dom::html::links::relations::{LinkRelations, follow_hyperlink};
38use crate::dom::mouseevent::MouseEvent;
39use crate::dom::node::Node;
40use crate::dom::node::virtualmethods::VirtualMethods;
41
42#[dom_struct]
43pub(crate) struct HTMLAnchorElement {
44 htmlelement: HTMLElement,
45 rel_list: MutNullableDom<DOMTokenList>,
46 #[no_trace]
47 relations: Cell<LinkRelations>,
48 #[no_trace]
49 url: DomRefCell<Option<UrlWithBlobClaim>>,
50}
51
52impl HTMLAnchorElement {
53 fn new_inherited(
54 local_name: LocalName,
55 prefix: Option<Prefix>,
56 document: &Document,
57 ) -> HTMLAnchorElement {
58 HTMLAnchorElement {
59 htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
60 rel_list: Default::default(),
61 relations: Cell::new(LinkRelations::empty()),
62 url: DomRefCell::new(None),
63 }
64 }
65
66 pub(crate) fn new(
67 cx: &mut js::context::JSContext,
68 local_name: LocalName,
69 prefix: Option<Prefix>,
70 document: &Document,
71 proto: Option<HandleObject>,
72 ) -> DomRoot<HTMLAnchorElement> {
73 Node::reflect_node_with_proto(
74 cx,
75 Box::new(HTMLAnchorElement::new_inherited(
76 local_name, prefix, document,
77 )),
78 document,
79 proto,
80 )
81 }
82}
83
84impl HyperlinkElement for HTMLAnchorElement {
85 fn get_url(&self) -> &DomRefCell<Option<UrlWithBlobClaim>> {
86 &self.url
87 }
88}
89
90impl VirtualMethods for HTMLAnchorElement {
91 fn super_type(&self) -> Option<&dyn VirtualMethods> {
92 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
93 }
94
95 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
96 match name {
97 &local_name!("rel") => AttrValue::from_serialized_tokenlist(value.into()),
98 _ => self
99 .super_type()
100 .unwrap()
101 .parse_plain_attribute(name, value),
102 }
103 }
104
105 fn attribute_mutated(
106 &self,
107 cx: &mut js::context::JSContext,
108 attr: AttrRef<'_>,
109 mutation: AttributeMutation,
110 ) {
111 self.super_type()
112 .unwrap()
113 .attribute_mutated(cx, attr, mutation);
114
115 self.attribute_mutated_for_hyperlinks(cx.no_gc(), attr, mutation);
116
117 match *attr.local_name() {
122 local_name!("href") => self.upcast::<Element>().set_state(
123 cx.no_gc(),
124 ElementState::UNVISITED,
125 !mutation.is_removal(),
126 ),
127 local_name!("rel") | local_name!("rev") => {
128 self.relations
129 .set(LinkRelations::for_element(self.upcast()));
130 },
131 _ => {},
132 }
133 }
134}
135
136impl HTMLAnchorElementMethods<crate::DomTypeHolder> for HTMLAnchorElement {
137 fn Text(&self) -> DOMString {
139 self.upcast::<Node>().GetTextContent().unwrap()
140 }
141
142 fn SetText(&self, cx: &mut JSContext, value: DOMString) {
144 self.upcast::<Node>()
145 .set_text_content_for_element(cx, Some(value))
146 }
147
148 make_getter!(Rel, "rel");
150
151 fn SetRel(&self, cx: &mut JSContext, rel: DOMString) {
153 self.upcast::<Element>()
154 .set_tokenlist_attribute(cx, &local_name!("rel"), rel);
155 }
156
157 fn RelList(&self, cx: &mut JSContext) -> DomRoot<DOMTokenList> {
159 self.rel_list.or_init(|| {
160 DOMTokenList::new(
161 cx,
162 self.upcast(),
163 &local_name!("rel"),
164 Some(vec![
165 Atom::from("noopener"),
166 Atom::from("noreferrer"),
167 Atom::from("opener"),
168 ]),
169 )
170 })
171 }
172
173 make_getter!(Hreflang, "hreflang");
175
176 make_setter!(SetHreflang, "hreflang");
178
179 make_getter!(Type, "type");
181
182 make_setter!(SetType, "type");
184
185 make_getter!(Coords, "coords");
187
188 make_setter!(SetCoords, "coords");
190
191 make_getter!(Charset, "charset");
193
194 make_setter!(SetCharset, "charset");
196
197 make_getter!(Name, "name");
199
200 make_atomic_setter!(SetName, "name");
202
203 make_getter!(Rev, "rev");
205
206 make_setter!(SetRev, "rev");
208
209 make_getter!(Shape, "shape");
211
212 make_setter!(SetShape, "shape");
214
215 make_getter!(Target, "target");
217
218 make_setter!(SetTarget, "target");
220
221 fn Href(&self, no_gc: &NoGC) -> USVString {
223 self.get_href(no_gc)
224 }
225
226 make_url_setter!(SetHref, "href");
228
229 fn Origin(&self, no_gc: &NoGC) -> USVString {
231 self.get_origin(no_gc)
232 }
233
234 fn Protocol(&self, no_gc: &NoGC) -> USVString {
236 self.get_protocol(no_gc)
237 }
238
239 fn SetProtocol(&self, cx: &mut JSContext, value: USVString) {
241 self.set_protocol(cx, value);
242 }
243
244 fn Password(&self, no_gc: &NoGC) -> USVString {
246 self.get_password(no_gc)
247 }
248
249 fn SetPassword(&self, cx: &mut JSContext, value: USVString) {
251 self.set_password(cx, value);
252 }
253
254 fn Hash(&self, no_gc: &NoGC) -> USVString {
256 self.get_hash(no_gc)
257 }
258
259 fn SetHash(&self, cx: &mut JSContext, value: USVString) {
261 self.set_hash(cx, value);
262 }
263
264 fn Host(&self, no_gc: &NoGC) -> USVString {
266 self.get_host(no_gc)
267 }
268
269 fn SetHost(&self, cx: &mut JSContext, value: USVString) {
271 self.set_host(cx, value);
272 }
273
274 fn Hostname(&self, no_gc: &NoGC) -> USVString {
276 self.get_hostname(no_gc)
277 }
278
279 fn SetHostname(&self, cx: &mut JSContext, value: USVString) {
281 self.set_hostname(cx, value);
282 }
283
284 fn Port(&self, no_gc: &NoGC) -> USVString {
286 self.get_port(no_gc)
287 }
288
289 fn SetPort(&self, cx: &mut JSContext, value: USVString) {
291 self.set_port(cx, value);
292 }
293
294 fn Pathname(&self, no_gc: &NoGC) -> USVString {
296 self.get_pathname(no_gc)
297 }
298
299 fn SetPathname(&self, cx: &mut JSContext, value: USVString) {
301 self.set_pathname(cx, value);
302 }
303
304 fn Search(&self, no_gc: &NoGC) -> USVString {
306 self.get_search(no_gc)
307 }
308
309 fn SetSearch(&self, cx: &mut JSContext, value: USVString) {
311 self.set_search(cx, value);
312 }
313
314 fn Username(&self, no_gc: &NoGC) -> USVString {
316 self.get_username(no_gc)
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!(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(cx).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}