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;
15use stylo_dom::ElementState;
16
17use crate::dom::activation::Activatable;
18use crate::dom::attr::Attr;
19use crate::dom::bindings::cell::DomRefCell;
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::{AttributeMutation, Element, reflect_referrer_policy_attribute};
29use crate::dom::event::Event;
30use crate::dom::eventtarget::EventTarget;
31use crate::dom::html::htmlelement::HTMLElement;
32use crate::dom::html::htmlhyperlinkelementutils::{HyperlinkElement, HyperlinkElementTraits};
33use crate::dom::html::htmlimageelement::HTMLImageElement;
34use crate::dom::mouseevent::MouseEvent;
35use crate::dom::node::{BindContext, Node, NodeTraits};
36use crate::dom::virtualmethods::VirtualMethods;
37use crate::links::{LinkRelations, follow_hyperlink};
38use crate::script_runtime::CanGc;
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 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 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(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
112 self.super_type()
113 .unwrap()
114 .attribute_mutated(attr, mutation, can_gc);
115
116 match *attr.local_name() {
117 local_name!("href") => self
118 .upcast::<Element>()
119 .set_state(ElementState::UNVISITED, !mutation.is_removal()),
120 local_name!("rel") | local_name!("rev") => {
121 self.relations
122 .set(LinkRelations::for_element(self.upcast()));
123 },
124 _ => {},
125 }
126 }
127
128 fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) {
129 if let Some(s) = self.super_type() {
130 s.bind_to_tree(context, can_gc);
131 }
132
133 self.relations
134 .set(LinkRelations::for_element(self.upcast()));
135 }
136}
137
138impl HTMLAnchorElementMethods<crate::DomTypeHolder> for HTMLAnchorElement {
139 fn Text(&self) -> DOMString {
141 self.upcast::<Node>().GetTextContent().unwrap()
142 }
143
144 fn SetText(&self, value: DOMString, can_gc: CanGc) {
146 self.upcast::<Node>()
147 .set_text_content_for_element(Some(value), can_gc)
148 }
149
150 make_getter!(Rel, "rel");
152
153 fn SetRel(&self, rel: DOMString, can_gc: CanGc) {
155 self.upcast::<Element>()
156 .set_tokenlist_attribute(&local_name!("rel"), rel, can_gc);
157 }
158
159 fn RelList(&self, can_gc: CanGc) -> DomRoot<DOMTokenList> {
161 self.rel_list.or_init(|| {
162 DOMTokenList::new(
163 self.upcast(),
164 &local_name!("rel"),
165 Some(vec![
166 Atom::from("noopener"),
167 Atom::from("noreferrer"),
168 Atom::from("opener"),
169 ]),
170 can_gc,
171 )
172 })
173 }
174
175 make_getter!(Coords, "coords");
177
178 make_setter!(SetCoords, "coords");
180
181 make_getter!(Name, "name");
183
184 make_atomic_setter!(SetName, "name");
186
187 make_getter!(Rev, "rev");
189
190 make_setter!(SetRev, "rev");
192
193 make_getter!(Shape, "shape");
195
196 make_setter!(SetShape, "shape");
198
199 make_getter!(Target, "target");
201
202 make_setter!(SetTarget, "target");
204
205 fn Href(&self) -> USVString {
207 self.get_href()
208 }
209
210 fn SetHref(&self, value: USVString, can_gc: CanGc) {
212 self.set_href(value, can_gc);
213 }
214
215 fn Origin(&self) -> USVString {
217 self.get_origin()
218 }
219
220 fn Protocol(&self) -> USVString {
222 self.get_protocol()
223 }
224
225 fn SetProtocol(&self, value: USVString, can_gc: CanGc) {
227 self.set_protocol(value, can_gc);
228 }
229
230 fn Password(&self) -> USVString {
232 self.get_password()
233 }
234
235 fn SetPassword(&self, value: USVString, can_gc: CanGc) {
237 self.set_password(value, can_gc);
238 }
239
240 fn Hash(&self) -> USVString {
242 self.get_hash()
243 }
244
245 fn SetHash(&self, value: USVString, can_gc: CanGc) {
247 self.set_hash(value, can_gc);
248 }
249
250 fn Host(&self) -> USVString {
252 self.get_host()
253 }
254
255 fn SetHost(&self, value: USVString, can_gc: CanGc) {
257 self.set_host(value, can_gc);
258 }
259
260 fn Hostname(&self) -> USVString {
262 self.get_hostname()
263 }
264
265 fn SetHostname(&self, value: USVString, can_gc: CanGc) {
267 self.set_hostname(value, can_gc);
268 }
269
270 fn Port(&self) -> USVString {
272 self.get_port()
273 }
274
275 fn SetPort(&self, value: USVString, can_gc: CanGc) {
277 self.set_port(value, can_gc);
278 }
279
280 fn Pathname(&self) -> USVString {
282 self.get_pathname()
283 }
284
285 fn SetPathname(&self, value: USVString, can_gc: CanGc) {
287 self.set_pathname(value, can_gc);
288 }
289
290 fn Search(&self) -> USVString {
292 self.get_search()
293 }
294
295 fn SetSearch(&self, value: USVString, can_gc: CanGc) {
297 self.set_search(value, can_gc);
298 }
299
300 fn Username(&self) -> USVString {
302 self.get_username()
303 }
304
305 fn SetUsername(&self, value: USVString, can_gc: CanGc) {
307 self.set_username(value, can_gc);
308 }
309
310 fn ReferrerPolicy(&self) -> DOMString {
312 reflect_referrer_policy_attribute(self.upcast::<Element>())
313 }
314
315 make_setter!(SetReferrerPolicy, "referrerpolicy");
317}
318
319impl Activatable for HTMLAnchorElement {
320 fn as_element(&self) -> &Element {
321 self.upcast::<Element>()
322 }
323
324 fn is_instance_activatable(&self) -> bool {
325 self.as_element().has_attribute(&local_name!("href"))
331 }
332
333 fn activation_behavior(&self, event: &Event, target: &EventTarget, _: CanGc) {
335 let element = self.as_element();
336 let mouse_event = event.downcast::<MouseEvent>().unwrap();
337 let mut ismap_suffix = None;
338
339 if let Some(element) = target.downcast::<Element>() {
342 if target.is::<HTMLImageElement>() && element.has_attribute(&local_name!("ismap")) {
343 let target_node = element.upcast::<Node>();
344 let rect = target_node.border_box().unwrap_or_default();
345 ismap_suffix = Some(format!(
346 "?{},{}",
347 mouse_event.ClientX().to_f32().unwrap() - rect.origin.x.to_f32_px(),
348 mouse_event.ClientY().to_f32().unwrap() - rect.origin.y.to_f32_px()
349 ))
350 }
351 }
352
353 follow_hyperlink(element, self.relations.get(), ismap_suffix);
356 }
357}