Skip to main content

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::context::JSContext;
11use js::rust::HandleObject;
12use num_traits::ToPrimitive;
13use script_bindings::cell::DomRefCell;
14use servo_url::ServoUrl;
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::htmlhyperlinkelementutils::{HyperlinkElement, HyperlinkElementTraits};
34use crate::dom::html::htmlimageelement::HTMLImageElement;
35use crate::dom::mouseevent::MouseEvent;
36use crate::dom::node::{Node, NodeTraits};
37use crate::dom::virtualmethods::VirtualMethods;
38use crate::links::{LinkRelations, follow_hyperlink};
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        cx: &mut js::context::JSContext,
66        local_name: LocalName,
67        prefix: Option<Prefix>,
68        document: &Document,
69        proto: Option<HandleObject>,
70    ) -> DomRoot<HTMLAnchorElement> {
71        Node::reflect_node_with_proto(
72            cx,
73            Box::new(HTMLAnchorElement::new_inherited(
74                local_name, prefix, document,
75            )),
76            document,
77            proto,
78        )
79    }
80
81    /// Get the full URL of the `href` attribute of this `<a>` element, returning `None` if
82    /// the URL could not be joined with the `Document` URL.
83    pub(crate) fn full_href_url_for_user_interface(&self) -> Option<ServoUrl> {
84        if !self.upcast::<Element>().has_attribute(&local_name!("href")) {
85            return None;
86        }
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: AttrRef<'_>,
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
135impl HTMLAnchorElementMethods<crate::DomTypeHolder> for HTMLAnchorElement {
136    /// <https://html.spec.whatwg.org/multipage/#dom-a-text>
137    fn Text(&self) -> DOMString {
138        self.upcast::<Node>().GetTextContent().unwrap()
139    }
140
141    /// <https://html.spec.whatwg.org/multipage/#dom-a-text>
142    fn SetText(&self, cx: &mut JSContext, value: DOMString) {
143        self.upcast::<Node>()
144            .set_text_content_for_element(cx, Some(value))
145    }
146
147    // https://html.spec.whatwg.org/multipage/#dom-a-rel
148    make_getter!(Rel, "rel");
149
150    /// <https://html.spec.whatwg.org/multipage/#dom-a-rel>
151    fn SetRel(&self, cx: &mut JSContext, rel: DOMString) {
152        self.upcast::<Element>()
153            .set_tokenlist_attribute(cx, &local_name!("rel"), rel);
154    }
155
156    /// <https://html.spec.whatwg.org/multipage/#dom-a-rellist>
157    fn RelList(&self, cx: &mut JSContext) -> DomRoot<DOMTokenList> {
158        self.rel_list.or_init(|| {
159            DOMTokenList::new(
160                cx,
161                self.upcast(),
162                &local_name!("rel"),
163                Some(vec![
164                    Atom::from("noopener"),
165                    Atom::from("noreferrer"),
166                    Atom::from("opener"),
167                ]),
168            )
169        })
170    }
171
172    // https://html.spec.whatwg.org/multipage/#dom-a-hreflang
173    make_getter!(Hreflang, "hreflang");
174
175    // https://html.spec.whatwg.org/multipage/#dom-a-hreflang
176    make_setter!(cx, SetHreflang, "hreflang");
177
178    // https://html.spec.whatwg.org/multipage/#dom-a-type
179    make_getter!(Type, "type");
180
181    // https://html.spec.whatwg.org/multipage/#dom-a-type
182    make_setter!(cx, SetType, "type");
183
184    // https://html.spec.whatwg.org/multipage/#dom-a-coords
185    make_getter!(Coords, "coords");
186
187    // https://html.spec.whatwg.org/multipage/#dom-a-coords
188    make_setter!(cx, SetCoords, "coords");
189
190    // https://html.spec.whatwg.org/multipage/#dom-a-charset
191    make_getter!(Charset, "charset");
192
193    // https://html.spec.whatwg.org/multipage/#dom-a-charset
194    make_setter!(cx, SetCharset, "charset");
195
196    // https://html.spec.whatwg.org/multipage/#dom-a-name
197    make_getter!(Name, "name");
198
199    // https://html.spec.whatwg.org/multipage/#dom-a-name
200    make_atomic_setter!(cx, SetName, "name");
201
202    // https://html.spec.whatwg.org/multipage/#dom-a-rev
203    make_getter!(Rev, "rev");
204
205    // https://html.spec.whatwg.org/multipage/#dom-a-rev
206    make_setter!(cx, SetRev, "rev");
207
208    // https://html.spec.whatwg.org/multipage/#dom-a-shape
209    make_getter!(Shape, "shape");
210
211    // https://html.spec.whatwg.org/multipage/#dom-a-shape
212    make_setter!(cx, SetShape, "shape");
213
214    // https://html.spec.whatwg.org/multipage/#attr-hyperlink-target
215    make_getter!(Target, "target");
216
217    // https://html.spec.whatwg.org/multipage/#attr-hyperlink-target
218    make_setter!(cx, SetTarget, "target");
219
220    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-href>
221    fn Href(&self) -> USVString {
222        self.get_href()
223    }
224
225    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-href>
226    fn SetHref(&self, cx: &mut JSContext, value: USVString) {
227        self.set_href(cx, value);
228    }
229
230    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-origin>
231    fn Origin(&self) -> USVString {
232        self.get_origin()
233    }
234
235    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-protocol>
236    fn Protocol(&self) -> USVString {
237        self.get_protocol()
238    }
239
240    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-protocol>
241    fn SetProtocol(&self, cx: &mut JSContext, value: USVString) {
242        self.set_protocol(cx, value);
243    }
244
245    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-password>
246    fn Password(&self) -> USVString {
247        self.get_password()
248    }
249
250    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-password>
251    fn SetPassword(&self, cx: &mut JSContext, value: USVString) {
252        self.set_password(cx, value);
253    }
254
255    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-hash>
256    fn Hash(&self) -> USVString {
257        self.get_hash()
258    }
259
260    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-hash>
261    fn SetHash(&self, cx: &mut JSContext, value: USVString) {
262        self.set_hash(cx, value);
263    }
264
265    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-host>
266    fn Host(&self) -> USVString {
267        self.get_host()
268    }
269
270    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-host>
271    fn SetHost(&self, cx: &mut JSContext, value: USVString) {
272        self.set_host(cx, value);
273    }
274
275    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-hostname>
276    fn Hostname(&self) -> USVString {
277        self.get_hostname()
278    }
279
280    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-hostname>
281    fn SetHostname(&self, cx: &mut JSContext, value: USVString) {
282        self.set_hostname(cx, value);
283    }
284
285    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-port>
286    fn Port(&self) -> USVString {
287        self.get_port()
288    }
289
290    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-port>
291    fn SetPort(&self, cx: &mut JSContext, value: USVString) {
292        self.set_port(cx, value);
293    }
294
295    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-pathname>
296    fn Pathname(&self) -> USVString {
297        self.get_pathname()
298    }
299
300    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-pathname>
301    fn SetPathname(&self, cx: &mut JSContext, value: USVString) {
302        self.set_pathname(cx, value);
303    }
304
305    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-search>
306    fn Search(&self) -> USVString {
307        self.get_search()
308    }
309
310    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-search>
311    fn SetSearch(&self, cx: &mut JSContext, value: USVString) {
312        self.set_search(cx, value);
313    }
314
315    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-username>
316    fn Username(&self) -> USVString {
317        self.get_username()
318    }
319
320    /// <https://html.spec.whatwg.org/multipage/#dom-hyperlink-username>
321    fn SetUsername(&self, cx: &mut JSContext, value: USVString) {
322        self.set_username(cx, value);
323    }
324
325    /// <https://html.spec.whatwg.org/multipage/#dom-a-referrerpolicy>
326    fn ReferrerPolicy(&self) -> DOMString {
327        reflect_referrer_policy_attribute(self.upcast::<Element>())
328    }
329
330    // https://html.spec.whatwg.org/multipage/#dom-script-referrerpolicy
331    make_setter!(cx, SetReferrerPolicy, "referrerpolicy");
332}
333
334impl Activatable for HTMLAnchorElement {
335    fn as_element(&self) -> &Element {
336        self.upcast::<Element>()
337    }
338
339    fn is_instance_activatable(&self) -> bool {
340        // https://html.spec.whatwg.org/multipage/#hyperlink
341        // "a [...] element[s] with an href attribute [...] must [..] create a
342        // hyperlink"
343        // https://html.spec.whatwg.org/multipage/#the-a-element
344        // "The activation behaviour of a elements *that create hyperlinks*"
345        self.as_element().has_attribute(&local_name!("href"))
346    }
347
348    /// <https://html.spec.whatwg.org/multipage/#the-a-element:activation-behaviour>
349    fn activation_behavior(
350        &self,
351        cx: &mut js::context::JSContext,
352        event: &Event,
353        target: &EventTarget,
354    ) {
355        let element = self.as_element();
356        let mouse_event = event.downcast::<MouseEvent>().unwrap();
357        let mut ismap_suffix = None;
358
359        // Step 1: If the target of the click event is an img element with an ismap attribute
360        // specified, then server-side image map processing must be performed.
361        if let Some(element) = target.downcast::<Element>() &&
362            target.is::<HTMLImageElement>() &&
363            element.has_attribute(&local_name!("ismap"))
364        {
365            let target_node = element.upcast::<Node>();
366            let rect = target_node.border_box().unwrap_or_default();
367            ismap_suffix = Some(format!(
368                "?{},{}",
369                mouse_event.ClientX().to_f32().unwrap() - rect.origin.x.to_f32_px(),
370                mouse_event.ClientY().to_f32().unwrap() - rect.origin.y.to_f32_px()
371            ))
372        }
373
374        // Step 2.
375        // TODO: Download the link is `download` attribute is set.
376        follow_hyperlink(cx, element, self.relations.get(), ismap_suffix);
377    }
378}