script/dom/html/
htmlanchorelement.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use 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;
15
16use crate::dom::activation::Activatable;
17use crate::dom::attr::Attr;
18use crate::dom::bindings::cell::DomRefCell;
19use crate::dom::bindings::codegen::Bindings::HTMLAnchorElementBinding::HTMLAnchorElementMethods;
20use crate::dom::bindings::codegen::Bindings::MouseEventBinding::MouseEventMethods;
21use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
22use crate::dom::bindings::inheritance::Castable;
23use crate::dom::bindings::root::{DomRoot, MutNullableDom};
24use crate::dom::bindings::str::{DOMString, USVString};
25use crate::dom::document::Document;
26use crate::dom::domtokenlist::DOMTokenList;
27use crate::dom::element::{AttributeMutation, Element, reflect_referrer_policy_attribute};
28use crate::dom::event::Event;
29use crate::dom::eventtarget::EventTarget;
30use crate::dom::html::htmlelement::HTMLElement;
31use crate::dom::html::htmlhyperlinkelementutils::{HyperlinkElement, HyperlinkElementTraits};
32use crate::dom::html::htmlimageelement::HTMLImageElement;
33use crate::dom::mouseevent::MouseEvent;
34use crate::dom::node::{BindContext, Node};
35use crate::dom::virtualmethods::VirtualMethods;
36use crate::links::{LinkRelations, follow_hyperlink};
37use crate::script_runtime::CanGc;
38
39#[dom_struct]
40pub(crate) struct HTMLAnchorElement {
41    htmlelement: HTMLElement,
42    rel_list: MutNullableDom<DOMTokenList>,
43    #[no_trace]
44    relations: Cell<LinkRelations>,
45    #[no_trace]
46    url: DomRefCell<Option<ServoUrl>>,
47}
48
49impl HTMLAnchorElement {
50    fn new_inherited(
51        local_name: LocalName,
52        prefix: Option<Prefix>,
53        document: &Document,
54    ) -> HTMLAnchorElement {
55        HTMLAnchorElement {
56            htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
57            rel_list: Default::default(),
58            relations: Cell::new(LinkRelations::empty()),
59            url: DomRefCell::new(None),
60        }
61    }
62
63    #[cfg_attr(crown, allow(crown::unrooted_must_root))]
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
82impl HyperlinkElement for HTMLAnchorElement {
83    fn get_url(&self) -> &DomRefCell<Option<ServoUrl>> {
84        &self.url
85    }
86}
87
88impl VirtualMethods for HTMLAnchorElement {
89    fn super_type(&self) -> Option<&dyn VirtualMethods> {
90        Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
91    }
92
93    fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
94        match name {
95            &local_name!("rel") => AttrValue::from_serialized_tokenlist(value.into()),
96            _ => self
97                .super_type()
98                .unwrap()
99                .parse_plain_attribute(name, value),
100        }
101    }
102
103    fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
104        self.super_type()
105            .unwrap()
106            .attribute_mutated(attr, mutation, can_gc);
107
108        match *attr.local_name() {
109            local_name!("rel") | local_name!("rev") => {
110                self.relations
111                    .set(LinkRelations::for_element(self.upcast()));
112            },
113            _ => {},
114        }
115    }
116
117    fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) {
118        if let Some(s) = self.super_type() {
119            s.bind_to_tree(context, can_gc);
120        }
121
122        self.relations
123            .set(LinkRelations::for_element(self.upcast()));
124    }
125}
126
127impl HTMLAnchorElementMethods<crate::DomTypeHolder> for HTMLAnchorElement {
128    // https://html.spec.whatwg.org/multipage/#dom-a-text
129    fn Text(&self) -> DOMString {
130        self.upcast::<Node>().GetTextContent().unwrap()
131    }
132
133    // https://html.spec.whatwg.org/multipage/#dom-a-text
134    fn SetText(&self, value: DOMString, can_gc: CanGc) {
135        self.upcast::<Node>()
136            .set_text_content_for_element(Some(value), can_gc)
137    }
138
139    // https://html.spec.whatwg.org/multipage/#dom-a-rel
140    make_getter!(Rel, "rel");
141
142    // https://html.spec.whatwg.org/multipage/#dom-a-rel
143    fn SetRel(&self, rel: DOMString, can_gc: CanGc) {
144        self.upcast::<Element>()
145            .set_tokenlist_attribute(&local_name!("rel"), rel, can_gc);
146    }
147
148    // https://html.spec.whatwg.org/multipage/#dom-a-rellist
149    fn RelList(&self, can_gc: CanGc) -> DomRoot<DOMTokenList> {
150        self.rel_list.or_init(|| {
151            DOMTokenList::new(
152                self.upcast(),
153                &local_name!("rel"),
154                Some(vec![
155                    Atom::from("noopener"),
156                    Atom::from("noreferrer"),
157                    Atom::from("opener"),
158                ]),
159                can_gc,
160            )
161        })
162    }
163
164    // https://html.spec.whatwg.org/multipage/#dom-a-coords
165    make_getter!(Coords, "coords");
166
167    // https://html.spec.whatwg.org/multipage/#dom-a-coords
168    make_setter!(SetCoords, "coords");
169
170    // https://html.spec.whatwg.org/multipage/#dom-a-name
171    make_getter!(Name, "name");
172
173    // https://html.spec.whatwg.org/multipage/#dom-a-name
174    make_atomic_setter!(SetName, "name");
175
176    // https://html.spec.whatwg.org/multipage/#dom-a-rev
177    make_getter!(Rev, "rev");
178
179    // https://html.spec.whatwg.org/multipage/#dom-a-rev
180    make_setter!(SetRev, "rev");
181
182    // https://html.spec.whatwg.org/multipage/#dom-a-shape
183    make_getter!(Shape, "shape");
184
185    // https://html.spec.whatwg.org/multipage/#dom-a-shape
186    make_setter!(SetShape, "shape");
187
188    // https://html.spec.whatwg.org/multipage/#attr-hyperlink-target
189    make_getter!(Target, "target");
190
191    // https://html.spec.whatwg.org/multipage/#attr-hyperlink-target
192    make_setter!(SetTarget, "target");
193
194    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-href>
195    fn Href(&self) -> USVString {
196        self.get_href()
197    }
198
199    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-href>
200    fn SetHref(&self, value: USVString, can_gc: CanGc) {
201        self.set_href(value, can_gc);
202    }
203
204    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-origin>
205    fn Origin(&self) -> USVString {
206        self.get_origin()
207    }
208
209    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-protocol>
210    fn Protocol(&self) -> USVString {
211        self.get_protocol()
212    }
213
214    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-protocol>
215    fn SetProtocol(&self, value: USVString, can_gc: CanGc) {
216        self.set_protocol(value, can_gc);
217    }
218
219    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-password>
220    fn Password(&self) -> USVString {
221        self.get_password()
222    }
223
224    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-password>
225    fn SetPassword(&self, value: USVString, can_gc: CanGc) {
226        self.set_password(value, can_gc);
227    }
228
229    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-hash>
230    fn Hash(&self) -> USVString {
231        self.get_hash()
232    }
233
234    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-hash>
235    fn SetHash(&self, value: USVString, can_gc: CanGc) {
236        self.set_hash(value, can_gc);
237    }
238
239    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-host>
240    fn Host(&self) -> USVString {
241        self.get_host()
242    }
243
244    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-host>
245    fn SetHost(&self, value: USVString, can_gc: CanGc) {
246        self.set_host(value, can_gc);
247    }
248
249    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-hostname>
250    fn Hostname(&self) -> USVString {
251        self.get_hostname()
252    }
253
254    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-hostname>
255    fn SetHostname(&self, value: USVString, can_gc: CanGc) {
256        self.set_hostname(value, can_gc);
257    }
258
259    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-port>
260    fn Port(&self) -> USVString {
261        self.get_port()
262    }
263
264    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-port>
265    fn SetPort(&self, value: USVString, can_gc: CanGc) {
266        self.set_port(value, can_gc);
267    }
268
269    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-pathname>
270    fn Pathname(&self) -> USVString {
271        self.get_pathname()
272    }
273
274    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-pathname>
275    fn SetPathname(&self, value: USVString, can_gc: CanGc) {
276        self.set_pathname(value, can_gc);
277    }
278
279    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-search>
280    fn Search(&self) -> USVString {
281        self.get_search()
282    }
283
284    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-search>
285    fn SetSearch(&self, value: USVString, can_gc: CanGc) {
286        self.set_search(value, can_gc);
287    }
288
289    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-username>
290    fn Username(&self) -> USVString {
291        self.get_username()
292    }
293
294    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-username>
295    fn SetUsername(&self, value: USVString, can_gc: CanGc) {
296        self.set_username(value, can_gc);
297    }
298
299    // https://html.spec.whatwg.org/multipage/#dom-a-referrerpolicy
300    fn ReferrerPolicy(&self) -> DOMString {
301        reflect_referrer_policy_attribute(self.upcast::<Element>())
302    }
303
304    // https://html.spec.whatwg.org/multipage/#dom-script-referrerpolicy
305    make_setter!(SetReferrerPolicy, "referrerpolicy");
306}
307
308impl Activatable for HTMLAnchorElement {
309    fn as_element(&self) -> &Element {
310        self.upcast::<Element>()
311    }
312
313    fn is_instance_activatable(&self) -> bool {
314        // https://html.spec.whatwg.org/multipage/#hyperlink
315        // "a [...] element[s] with an href attribute [...] must [..] create a
316        // hyperlink"
317        // https://html.spec.whatwg.org/multipage/#the-a-element
318        // "The activation behaviour of a elements *that create hyperlinks*"
319        self.as_element().has_attribute(&local_name!("href"))
320    }
321
322    // https://html.spec.whatwg.org/multipage/#the-a-element:activation-behaviour
323    fn activation_behavior(&self, event: &Event, target: &EventTarget, _: CanGc) {
324        let element = self.as_element();
325        let mouse_event = event.downcast::<MouseEvent>().unwrap();
326        let mut ismap_suffix = None;
327
328        // Step 1: If the target of the click event is an img element with an ismap attribute
329        // specified, then server-side image map processing must be performed.
330        if let Some(element) = target.downcast::<Element>() {
331            if target.is::<HTMLImageElement>() && element.has_attribute(&local_name!("ismap")) {
332                let target_node = element.upcast::<Node>();
333                let rect = target_node.border_box().unwrap_or_default();
334                ismap_suffix = Some(format!(
335                    "?{},{}",
336                    mouse_event.ClientX().to_f32().unwrap() - rect.origin.x.to_f32_px(),
337                    mouse_event.ClientY().to_f32().unwrap() - rect.origin.y.to_f32_px()
338                ))
339            }
340        }
341
342        // Step 2.
343        // TODO: Download the link is `download` attribute is set.
344        follow_hyperlink(element, self.relations.get(), ismap_suffix);
345    }
346}