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 servo_url::ServoUrl;
14use style::attr::AttrValue;
15use stylo_atoms::Atom;
16use stylo_dom::ElementState;
17
18use crate::dom::activation::Activatable;
19use crate::dom::attr::Attr;
20use crate::dom::bindings::cell::DomRefCell;
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::{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::{BindContext, Node, NodeTraits};
37use crate::dom::virtualmethods::VirtualMethods;
38use crate::links::{LinkRelations, follow_hyperlink};
39use crate::script_runtime::CanGc;
40
41#[dom_struct]
42pub(crate) struct HTMLAnchorElement {
43 htmlelement: HTMLElement,
44 rel_list: MutNullableDom<DOMTokenList>,
45 #[no_trace]
46 relations: Cell<LinkRelations>,
47 #[no_trace]
48 url: DomRefCell<Option<ServoUrl>>,
49}
50
51impl HTMLAnchorElement {
52 fn new_inherited(
53 local_name: LocalName,
54 prefix: Option<Prefix>,
55 document: &Document,
56 ) -> HTMLAnchorElement {
57 HTMLAnchorElement {
58 htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
59 rel_list: Default::default(),
60 relations: Cell::new(LinkRelations::empty()),
61 url: DomRefCell::new(None),
62 }
63 }
64
65 pub(crate) fn new(
66 cx: &mut js::context::JSContext,
67 local_name: LocalName,
68 prefix: Option<Prefix>,
69 document: &Document,
70 proto: Option<HandleObject>,
71 ) -> DomRoot<HTMLAnchorElement> {
72 Node::reflect_node_with_proto(
73 cx,
74 Box::new(HTMLAnchorElement::new_inherited(
75 local_name, prefix, document,
76 )),
77 document,
78 proto,
79 )
80 }
81
82 pub(crate) fn full_href_url_for_user_interface(&self) -> Option<ServoUrl> {
85 self.upcast::<Element>()
86 .get_attribute(&local_name!("href"))?;
87 self.owner_document().base_url().join(&self.Href()).ok()
88 }
89}
90
91impl HyperlinkElement for HTMLAnchorElement {
92 fn get_url(&self) -> &DomRefCell<Option<ServoUrl>> {
93 &self.url
94 }
95}
96
97impl VirtualMethods for HTMLAnchorElement {
98 fn super_type(&self) -> Option<&dyn VirtualMethods> {
99 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
100 }
101
102 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
103 match name {
104 &local_name!("rel") => AttrValue::from_serialized_tokenlist(value.into()),
105 _ => self
106 .super_type()
107 .unwrap()
108 .parse_plain_attribute(name, value),
109 }
110 }
111
112 fn attribute_mutated(
113 &self,
114 cx: &mut js::context::JSContext,
115 attr: &Attr,
116 mutation: AttributeMutation,
117 ) {
118 self.super_type()
119 .unwrap()
120 .attribute_mutated(cx, attr, mutation);
121
122 match *attr.local_name() {
123 local_name!("href") => self
124 .upcast::<Element>()
125 .set_state(ElementState::UNVISITED, !mutation.is_removal()),
126 local_name!("rel") | local_name!("rev") => {
127 self.relations
128 .set(LinkRelations::for_element(self.upcast()));
129 },
130 _ => {},
131 }
132 }
133
134 fn bind_to_tree(&self, cx: &mut JSContext, context: &BindContext) {
135 if let Some(s) = self.super_type() {
136 s.bind_to_tree(cx, context);
137 }
138
139 self.relations
140 .set(LinkRelations::for_element(self.upcast()));
141 }
142}
143
144impl HTMLAnchorElementMethods<crate::DomTypeHolder> for HTMLAnchorElement {
145 fn Text(&self) -> DOMString {
147 self.upcast::<Node>().GetTextContent().unwrap()
148 }
149
150 fn SetText(&self, cx: &mut JSContext, value: DOMString) {
152 self.upcast::<Node>()
153 .set_text_content_for_element(cx, Some(value))
154 }
155
156 make_getter!(Rel, "rel");
158
159 fn SetRel(&self, rel: DOMString, can_gc: CanGc) {
161 self.upcast::<Element>()
162 .set_tokenlist_attribute(&local_name!("rel"), rel, can_gc);
163 }
164
165 fn RelList(&self, can_gc: CanGc) -> DomRoot<DOMTokenList> {
167 self.rel_list.or_init(|| {
168 DOMTokenList::new(
169 self.upcast(),
170 &local_name!("rel"),
171 Some(vec![
172 Atom::from("noopener"),
173 Atom::from("noreferrer"),
174 Atom::from("opener"),
175 ]),
176 can_gc,
177 )
178 })
179 }
180
181 make_getter!(Hreflang, "hreflang");
183
184 make_setter!(SetHreflang, "hreflang");
186
187 make_getter!(Type, "type");
189
190 make_setter!(SetType, "type");
192
193 make_getter!(Coords, "coords");
195
196 make_setter!(SetCoords, "coords");
198
199 make_getter!(Charset, "charset");
201
202 make_setter!(SetCharset, "charset");
204
205 make_getter!(Name, "name");
207
208 make_atomic_setter!(SetName, "name");
210
211 make_getter!(Rev, "rev");
213
214 make_setter!(SetRev, "rev");
216
217 make_getter!(Shape, "shape");
219
220 make_setter!(SetShape, "shape");
222
223 make_getter!(Target, "target");
225
226 make_setter!(SetTarget, "target");
228
229 fn Href(&self) -> USVString {
231 self.get_href()
232 }
233
234 fn SetHref(&self, value: USVString, can_gc: CanGc) {
236 self.set_href(value, can_gc);
237 }
238
239 fn Origin(&self) -> USVString {
241 self.get_origin()
242 }
243
244 fn Protocol(&self) -> USVString {
246 self.get_protocol()
247 }
248
249 fn SetProtocol(&self, value: USVString, can_gc: CanGc) {
251 self.set_protocol(value, can_gc);
252 }
253
254 fn Password(&self) -> USVString {
256 self.get_password()
257 }
258
259 fn SetPassword(&self, value: USVString, can_gc: CanGc) {
261 self.set_password(value, can_gc);
262 }
263
264 fn Hash(&self) -> USVString {
266 self.get_hash()
267 }
268
269 fn SetHash(&self, value: USVString, can_gc: CanGc) {
271 self.set_hash(value, can_gc);
272 }
273
274 fn Host(&self) -> USVString {
276 self.get_host()
277 }
278
279 fn SetHost(&self, value: USVString, can_gc: CanGc) {
281 self.set_host(value, can_gc);
282 }
283
284 fn Hostname(&self) -> USVString {
286 self.get_hostname()
287 }
288
289 fn SetHostname(&self, value: USVString, can_gc: CanGc) {
291 self.set_hostname(value, can_gc);
292 }
293
294 fn Port(&self) -> USVString {
296 self.get_port()
297 }
298
299 fn SetPort(&self, value: USVString, can_gc: CanGc) {
301 self.set_port(value, can_gc);
302 }
303
304 fn Pathname(&self) -> USVString {
306 self.get_pathname()
307 }
308
309 fn SetPathname(&self, value: USVString, can_gc: CanGc) {
311 self.set_pathname(value, can_gc);
312 }
313
314 fn Search(&self) -> USVString {
316 self.get_search()
317 }
318
319 fn SetSearch(&self, value: USVString, can_gc: CanGc) {
321 self.set_search(value, can_gc);
322 }
323
324 fn Username(&self) -> USVString {
326 self.get_username()
327 }
328
329 fn SetUsername(&self, value: USVString, can_gc: CanGc) {
331 self.set_username(value, can_gc);
332 }
333
334 fn ReferrerPolicy(&self) -> DOMString {
336 reflect_referrer_policy_attribute(self.upcast::<Element>())
337 }
338
339 make_setter!(SetReferrerPolicy, "referrerpolicy");
341}
342
343impl Activatable for HTMLAnchorElement {
344 fn as_element(&self) -> &Element {
345 self.upcast::<Element>()
346 }
347
348 fn is_instance_activatable(&self) -> bool {
349 self.as_element().has_attribute(&local_name!("href"))
355 }
356
357 fn activation_behavior(&self, event: &Event, target: &EventTarget, _: CanGc) {
359 let element = self.as_element();
360 let mouse_event = event.downcast::<MouseEvent>().unwrap();
361 let mut ismap_suffix = None;
362
363 if let Some(element) = target.downcast::<Element>() {
366 if target.is::<HTMLImageElement>() && element.has_attribute(&local_name!("ismap")) {
367 let target_node = element.upcast::<Node>();
368 let rect = target_node.border_box().unwrap_or_default();
369 ismap_suffix = Some(format!(
370 "?{},{}",
371 mouse_event.ClientX().to_f32().unwrap() - rect.origin.x.to_f32_px(),
372 mouse_event.ClientY().to_f32().unwrap() - rect.origin.y.to_f32_px()
373 ))
374 }
375 }
376
377 follow_hyperlink(element, self.relations.get(), ismap_suffix);
380 }
381}