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 servo_url::ServoUrl;
16use style::attr::AttrValue;
17use stylo_atoms::Atom;
18use stylo_dom::ElementState;
19
20use crate::dom::activation::Activatable;
21use crate::dom::bindings::codegen::Bindings::HTMLAnchorElementBinding::HTMLAnchorElementMethods;
22use crate::dom::bindings::codegen::Bindings::MouseEventBinding::MouseEventMethods;
23use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
24use crate::dom::bindings::inheritance::Castable;
25use crate::dom::bindings::root::{DomRoot, MutNullableDom};
26use crate::dom::bindings::str::{DOMString, USVString};
27use crate::dom::document::Document;
28use crate::dom::domtokenlist::DOMTokenList;
29use crate::dom::element::attributes::storage::AttrRef;
30use crate::dom::element::{AttributeMutation, Element, reflect_referrer_policy_attribute};
31use crate::dom::event::Event;
32use crate::dom::eventtarget::EventTarget;
33use crate::dom::html::htmlelement::HTMLElement;
34use crate::dom::html::htmlimageelement::HTMLImageElement;
35use crate::dom::html::links::htmlhyperlinkelementutils::{
36 HyperlinkElement, HyperlinkElementTraits,
37};
38use crate::dom::html::links::relations::{LinkRelations, follow_hyperlink};
39use crate::dom::mouseevent::MouseEvent;
40use crate::dom::node::virtualmethods::VirtualMethods;
41use crate::dom::node::{Node, NodeTraits};
42
43#[dom_struct]
44pub(crate) struct HTMLAnchorElement {
45 htmlelement: HTMLElement,
46 rel_list: MutNullableDom<DOMTokenList>,
47 #[no_trace]
48 relations: Cell<LinkRelations>,
49 #[no_trace]
50 url: DomRefCell<Option<UrlWithBlobClaim>>,
51}
52
53impl HTMLAnchorElement {
54 fn new_inherited(
55 local_name: LocalName,
56 prefix: Option<Prefix>,
57 document: &Document,
58 ) -> HTMLAnchorElement {
59 HTMLAnchorElement {
60 htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
61 rel_list: Default::default(),
62 relations: Cell::new(LinkRelations::empty()),
63 url: DomRefCell::new(None),
64 }
65 }
66
67 pub(crate) fn new(
68 cx: &mut js::context::JSContext,
69 local_name: LocalName,
70 prefix: Option<Prefix>,
71 document: &Document,
72 proto: Option<HandleObject>,
73 ) -> DomRoot<HTMLAnchorElement> {
74 Node::reflect_node_with_proto(
75 cx,
76 Box::new(HTMLAnchorElement::new_inherited(
77 local_name, prefix, document,
78 )),
79 document,
80 proto,
81 )
82 }
83
84 pub(crate) fn full_href_url_for_user_interface(&self, no_gc: &NoGC) -> Option<ServoUrl> {
87 if !self.upcast::<Element>().has_attribute(&local_name!("href")) {
88 return None;
89 }
90 self.owner_document()
91 .base_url()
92 .join(&self.Href(no_gc))
93 .ok()
94 }
95}
96
97impl HyperlinkElement for HTMLAnchorElement {
98 fn get_url(&self) -> &DomRefCell<Option<UrlWithBlobClaim>> {
99 &self.url
100 }
101}
102
103impl VirtualMethods for HTMLAnchorElement {
104 fn super_type(&self) -> Option<&dyn VirtualMethods> {
105 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
106 }
107
108 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
109 match name {
110 &local_name!("rel") => AttrValue::from_serialized_tokenlist(value.into()),
111 _ => self
112 .super_type()
113 .unwrap()
114 .parse_plain_attribute(name, value),
115 }
116 }
117
118 fn attribute_mutated(
119 &self,
120 cx: &mut js::context::JSContext,
121 attr: AttrRef<'_>,
122 mutation: AttributeMutation,
123 ) {
124 self.super_type()
125 .unwrap()
126 .attribute_mutated(cx, attr, mutation);
127
128 self.attribute_mutated_for_hyperlinks(cx.no_gc(), attr, mutation);
129
130 match *attr.local_name() {
135 local_name!("href") => self
136 .upcast::<Element>()
137 .set_state(ElementState::UNVISITED, !mutation.is_removal()),
138 local_name!("rel") | local_name!("rev") => {
139 self.relations
140 .set(LinkRelations::for_element(self.upcast()));
141 },
142 _ => {},
143 }
144 }
145}
146
147impl HTMLAnchorElementMethods<crate::DomTypeHolder> for HTMLAnchorElement {
148 fn Text(&self) -> DOMString {
150 self.upcast::<Node>().GetTextContent().unwrap()
151 }
152
153 fn SetText(&self, cx: &mut JSContext, value: DOMString) {
155 self.upcast::<Node>()
156 .set_text_content_for_element(cx, Some(value))
157 }
158
159 make_getter!(Rel, "rel");
161
162 fn SetRel(&self, cx: &mut JSContext, rel: DOMString) {
164 self.upcast::<Element>()
165 .set_tokenlist_attribute(cx, &local_name!("rel"), rel);
166 }
167
168 fn RelList(&self, cx: &mut JSContext) -> DomRoot<DOMTokenList> {
170 self.rel_list.or_init(|| {
171 DOMTokenList::new(
172 cx,
173 self.upcast(),
174 &local_name!("rel"),
175 Some(vec![
176 Atom::from("noopener"),
177 Atom::from("noreferrer"),
178 Atom::from("opener"),
179 ]),
180 )
181 })
182 }
183
184 make_getter!(Hreflang, "hreflang");
186
187 make_setter!(SetHreflang, "hreflang");
189
190 make_getter!(Type, "type");
192
193 make_setter!(SetType, "type");
195
196 make_getter!(Coords, "coords");
198
199 make_setter!(SetCoords, "coords");
201
202 make_getter!(Charset, "charset");
204
205 make_setter!(SetCharset, "charset");
207
208 make_getter!(Name, "name");
210
211 make_atomic_setter!(SetName, "name");
213
214 make_getter!(Rev, "rev");
216
217 make_setter!(SetRev, "rev");
219
220 make_getter!(Shape, "shape");
222
223 make_setter!(SetShape, "shape");
225
226 make_getter!(Target, "target");
228
229 make_setter!(SetTarget, "target");
231
232 fn Href(&self, no_gc: &NoGC) -> USVString {
234 self.get_href(no_gc)
235 }
236
237 make_url_setter!(SetHref, "href");
239
240 fn Origin(&self, no_gc: &NoGC) -> USVString {
242 self.get_origin(no_gc)
243 }
244
245 fn Protocol(&self, no_gc: &NoGC) -> USVString {
247 self.get_protocol(no_gc)
248 }
249
250 fn SetProtocol(&self, cx: &mut JSContext, value: USVString) {
252 self.set_protocol(cx, value);
253 }
254
255 fn Password(&self, no_gc: &NoGC) -> USVString {
257 self.get_password(no_gc)
258 }
259
260 fn SetPassword(&self, cx: &mut JSContext, value: USVString) {
262 self.set_password(cx, value);
263 }
264
265 fn Hash(&self, no_gc: &NoGC) -> USVString {
267 self.get_hash(no_gc)
268 }
269
270 fn SetHash(&self, cx: &mut JSContext, value: USVString) {
272 self.set_hash(cx, value);
273 }
274
275 fn Host(&self, no_gc: &NoGC) -> USVString {
277 self.get_host(no_gc)
278 }
279
280 fn SetHost(&self, cx: &mut JSContext, value: USVString) {
282 self.set_host(cx, value);
283 }
284
285 fn Hostname(&self, no_gc: &NoGC) -> USVString {
287 self.get_hostname(no_gc)
288 }
289
290 fn SetHostname(&self, cx: &mut JSContext, value: USVString) {
292 self.set_hostname(cx, value);
293 }
294
295 fn Port(&self, no_gc: &NoGC) -> USVString {
297 self.get_port(no_gc)
298 }
299
300 fn SetPort(&self, cx: &mut JSContext, value: USVString) {
302 self.set_port(cx, value);
303 }
304
305 fn Pathname(&self, no_gc: &NoGC) -> USVString {
307 self.get_pathname(no_gc)
308 }
309
310 fn SetPathname(&self, cx: &mut JSContext, value: USVString) {
312 self.set_pathname(cx, value);
313 }
314
315 fn Search(&self, no_gc: &NoGC) -> USVString {
317 self.get_search(no_gc)
318 }
319
320 fn SetSearch(&self, cx: &mut JSContext, value: USVString) {
322 self.set_search(cx, value);
323 }
324
325 fn Username(&self, no_gc: &NoGC) -> USVString {
327 self.get_username(no_gc)
328 }
329
330 fn SetUsername(&self, cx: &mut JSContext, value: USVString) {
332 self.set_username(cx, value);
333 }
334
335 fn ReferrerPolicy(&self) -> DOMString {
337 reflect_referrer_policy_attribute(self.upcast::<Element>())
338 }
339
340 make_setter!(SetReferrerPolicy, "referrerpolicy");
342}
343
344impl Activatable for HTMLAnchorElement {
345 fn as_element(&self) -> &Element {
346 self.upcast::<Element>()
347 }
348
349 fn is_instance_activatable(&self) -> bool {
350 self.as_element().has_attribute(&local_name!("href"))
356 }
357
358 fn activation_behavior(
360 &self,
361 cx: &mut js::context::JSContext,
362 event: &Event,
363 target: &EventTarget,
364 ) {
365 let element = self.as_element();
366 let mouse_event = event.downcast::<MouseEvent>().unwrap();
367 let mut ismap_suffix = None;
368
369 if let Some(element) = target.downcast::<Element>() &&
372 target.is::<HTMLImageElement>() &&
373 element.has_attribute(&local_name!("ismap"))
374 {
375 let target_node = element.upcast::<Node>();
376 let rect = target_node.border_box().unwrap_or_default();
377 ismap_suffix = Some(format!(
378 "?{},{}",
379 mouse_event.ClientX().to_f32().unwrap() - rect.origin.x.to_f32_px(),
380 mouse_event.ClientY().to_f32().unwrap() - rect.origin.y.to_f32_px()
381 ))
382 }
383
384 follow_hyperlink(cx, element, self.relations.get(), ismap_suffix);
387 }
388}