Skip to main content

script/dom/html/textual/
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::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        // https://html.spec.whatwg.org/multipage/#introduction-2
118        // > Similarly, for a and area elements with an href attribute and a rel attribute,
119        // > links must be created for the keywords of the rel attribute
120        // > as defined for those keywords in the link types section.
121        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    /// <https://html.spec.whatwg.org/multipage/#dom-a-text>
138    fn Text(&self) -> DOMString {
139        self.upcast::<Node>().GetTextContent().unwrap()
140    }
141
142    /// <https://html.spec.whatwg.org/multipage/#dom-a-text>
143    fn SetText(&self, cx: &mut JSContext, value: DOMString) {
144        self.upcast::<Node>()
145            .set_text_content_for_element(cx, Some(value))
146    }
147
148    // https://html.spec.whatwg.org/multipage/#dom-a-rel
149    make_getter!(Rel, "rel");
150
151    /// <https://html.spec.whatwg.org/multipage/#dom-a-rel>
152    fn SetRel(&self, cx: &mut JSContext, rel: DOMString) {
153        self.upcast::<Element>()
154            .set_tokenlist_attribute(cx, &local_name!("rel"), rel);
155    }
156
157    /// <https://html.spec.whatwg.org/multipage/#dom-a-rellist>
158    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    // https://html.spec.whatwg.org/multipage/#dom-a-hreflang
174    make_getter!(Hreflang, "hreflang");
175
176    // https://html.spec.whatwg.org/multipage/#dom-a-hreflang
177    make_setter!(SetHreflang, "hreflang");
178
179    // https://html.spec.whatwg.org/multipage/#dom-a-type
180    make_getter!(Type, "type");
181
182    // https://html.spec.whatwg.org/multipage/#dom-a-type
183    make_setter!(SetType, "type");
184
185    // https://html.spec.whatwg.org/multipage/#dom-a-coords
186    make_getter!(Coords, "coords");
187
188    // https://html.spec.whatwg.org/multipage/#dom-a-coords
189    make_setter!(SetCoords, "coords");
190
191    // https://html.spec.whatwg.org/multipage/#dom-a-charset
192    make_getter!(Charset, "charset");
193
194    // https://html.spec.whatwg.org/multipage/#dom-a-charset
195    make_setter!(SetCharset, "charset");
196
197    // https://html.spec.whatwg.org/multipage/#dom-a-name
198    make_getter!(Name, "name");
199
200    // https://html.spec.whatwg.org/multipage/#dom-a-name
201    make_atomic_setter!(SetName, "name");
202
203    // https://html.spec.whatwg.org/multipage/#dom-a-rev
204    make_getter!(Rev, "rev");
205
206    // https://html.spec.whatwg.org/multipage/#dom-a-rev
207    make_setter!(SetRev, "rev");
208
209    // https://html.spec.whatwg.org/multipage/#dom-a-shape
210    make_getter!(Shape, "shape");
211
212    // https://html.spec.whatwg.org/multipage/#dom-a-shape
213    make_setter!(SetShape, "shape");
214
215    // https://html.spec.whatwg.org/multipage/#attr-hyperlink-target
216    make_getter!(Target, "target");
217
218    // https://html.spec.whatwg.org/multipage/#attr-hyperlink-target
219    make_setter!(SetTarget, "target");
220
221    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-href>
222    fn Href(&self, no_gc: &NoGC) -> USVString {
223        self.get_href(no_gc)
224    }
225
226    // https://html.spec.whatwg.org/multipage/#dom-hyperlink-href
227    make_url_setter!(SetHref, "href");
228
229    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-origin>
230    fn Origin(&self, no_gc: &NoGC) -> USVString {
231        self.get_origin(no_gc)
232    }
233
234    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-protocol>
235    fn Protocol(&self, no_gc: &NoGC) -> USVString {
236        self.get_protocol(no_gc)
237    }
238
239    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-protocol>
240    fn SetProtocol(&self, cx: &mut JSContext, value: USVString) {
241        self.set_protocol(cx, value);
242    }
243
244    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-password>
245    fn Password(&self, no_gc: &NoGC) -> USVString {
246        self.get_password(no_gc)
247    }
248
249    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-password>
250    fn SetPassword(&self, cx: &mut JSContext, value: USVString) {
251        self.set_password(cx, value);
252    }
253
254    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-hash>
255    fn Hash(&self, no_gc: &NoGC) -> USVString {
256        self.get_hash(no_gc)
257    }
258
259    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-hash>
260    fn SetHash(&self, cx: &mut JSContext, value: USVString) {
261        self.set_hash(cx, value);
262    }
263
264    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-host>
265    fn Host(&self, no_gc: &NoGC) -> USVString {
266        self.get_host(no_gc)
267    }
268
269    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-host>
270    fn SetHost(&self, cx: &mut JSContext, value: USVString) {
271        self.set_host(cx, value);
272    }
273
274    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-hostname>
275    fn Hostname(&self, no_gc: &NoGC) -> USVString {
276        self.get_hostname(no_gc)
277    }
278
279    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-hostname>
280    fn SetHostname(&self, cx: &mut JSContext, value: USVString) {
281        self.set_hostname(cx, value);
282    }
283
284    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-port>
285    fn Port(&self, no_gc: &NoGC) -> USVString {
286        self.get_port(no_gc)
287    }
288
289    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-port>
290    fn SetPort(&self, cx: &mut JSContext, value: USVString) {
291        self.set_port(cx, value);
292    }
293
294    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-pathname>
295    fn Pathname(&self, no_gc: &NoGC) -> USVString {
296        self.get_pathname(no_gc)
297    }
298
299    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-pathname>
300    fn SetPathname(&self, cx: &mut JSContext, value: USVString) {
301        self.set_pathname(cx, value);
302    }
303
304    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-search>
305    fn Search(&self, no_gc: &NoGC) -> USVString {
306        self.get_search(no_gc)
307    }
308
309    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-search>
310    fn SetSearch(&self, cx: &mut JSContext, value: USVString) {
311        self.set_search(cx, value);
312    }
313
314    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-username>
315    fn Username(&self, no_gc: &NoGC) -> USVString {
316        self.get_username(no_gc)
317    }
318
319    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-username>
320    fn SetUsername(&self, cx: &mut JSContext, value: USVString) {
321        self.set_username(cx, value);
322    }
323
324    /// <https://html.spec.whatwg.org/multipage/#dom-a-referrerpolicy>
325    fn ReferrerPolicy(&self) -> DOMString {
326        reflect_referrer_policy_attribute(self.upcast::<Element>())
327    }
328
329    // https://html.spec.whatwg.org/multipage/#dom-script-referrerpolicy
330    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        // https://html.spec.whatwg.org/multipage/#hyperlink
340        // "a [...] element[s] with an href attribute [...] must [..] create a
341        // hyperlink"
342        // https://html.spec.whatwg.org/multipage/#the-a-element
343        // "The activation behaviour of a elements *that create hyperlinks*"
344        self.as_element().has_attribute(&local_name!("href"))
345    }
346
347    /// <https://html.spec.whatwg.org/multipage/#the-a-element:activation-behaviour>
348    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        // Step 1: If the target of the click event is an img element with an ismap attribute
359        // specified, then server-side image map processing must be performed.
360        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        // Step 2.
374        // TODO: Download the link is `download` attribute is set.
375        follow_hyperlink(cx, element, self.relations.get(), ismap_suffix);
376    }
377}