Permalink
Cannot retrieve contributors at this time
Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.
Sign up
Fetching contributors…
| /* This Source Code Form is subject to the terms of the Mozilla Public | |
| * License, v. 2.0. If a copy of the MPL was not distributed with this | |
| * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ | |
| use crate::dom::activation::Activatable; | |
| use crate::dom::attr::Attr; | |
| use crate::dom::bindings::cell::DomRefCell; | |
| use crate::dom::bindings::codegen::Bindings::AttrBinding::AttrMethods; | |
| use crate::dom::bindings::codegen::Bindings::HTMLAnchorElementBinding; | |
| use crate::dom::bindings::codegen::Bindings::HTMLAnchorElementBinding::HTMLAnchorElementMethods; | |
| use crate::dom::bindings::codegen::Bindings::MouseEventBinding::MouseEventMethods; | |
| use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; | |
| use crate::dom::bindings::inheritance::Castable; | |
| use crate::dom::bindings::root::{DomRoot, MutNullableDom}; | |
| use crate::dom::bindings::str::{DOMString, USVString}; | |
| use crate::dom::document::determine_policy_for_token; | |
| use crate::dom::document::Document; | |
| use crate::dom::domtokenlist::DOMTokenList; | |
| use crate::dom::element::Element; | |
| use crate::dom::event::Event; | |
| use crate::dom::eventtarget::EventTarget; | |
| use crate::dom::htmlelement::HTMLElement; | |
| use crate::dom::htmlimageelement::HTMLImageElement; | |
| use crate::dom::mouseevent::MouseEvent; | |
| use crate::dom::node::{document_from_node, Node}; | |
| use crate::dom::urlhelper::UrlHelper; | |
| use crate::dom::virtualmethods::VirtualMethods; | |
| use dom_struct::dom_struct; | |
| use html5ever::{LocalName, Prefix}; | |
| use num_traits::ToPrimitive; | |
| use servo_url::ServoUrl; | |
| use std::default::Default; | |
| use style::attr::AttrValue; | |
| #[dom_struct] | |
| pub struct HTMLAnchorElement { | |
| htmlelement: HTMLElement, | |
| rel_list: MutNullableDom<DOMTokenList>, | |
| url: DomRefCell<Option<ServoUrl>>, | |
| } | |
| impl HTMLAnchorElement { | |
| fn new_inherited( | |
| local_name: LocalName, | |
| prefix: Option<Prefix>, | |
| document: &Document, | |
| ) -> HTMLAnchorElement { | |
| HTMLAnchorElement { | |
| htmlelement: HTMLElement::new_inherited(local_name, prefix, document), | |
| rel_list: Default::default(), | |
| url: DomRefCell::new(None), | |
| } | |
| } | |
| #[allow(unrooted_must_root)] | |
| pub fn new( | |
| local_name: LocalName, | |
| prefix: Option<Prefix>, | |
| document: &Document, | |
| ) -> DomRoot<HTMLAnchorElement> { | |
| Node::reflect_node( | |
| Box::new(HTMLAnchorElement::new_inherited( | |
| local_name, prefix, document, | |
| )), | |
| document, | |
| HTMLAnchorElementBinding::Wrap, | |
| ) | |
| } | |
| // https://html.spec.whatwg.org/multipage/#concept-hyperlink-url-set | |
| fn set_url(&self) { | |
| let attribute = self | |
| .upcast::<Element>() | |
| .get_attribute(&ns!(), &local_name!("href")); | |
| *self.url.borrow_mut() = attribute.and_then(|attribute| { | |
| let document = document_from_node(self); | |
| document.base_url().join(&attribute.value()).ok() | |
| }); | |
| } | |
| // https://html.spec.whatwg.org/multipage/#reinitialise-url | |
| fn reinitialize_url(&self) { | |
| // Step 1. | |
| match *self.url.borrow() { | |
| Some(ref url) if url.scheme() == "blob" && url.cannot_be_a_base() => return, | |
| _ => (), | |
| } | |
| // Step 2. | |
| self.set_url(); | |
| } | |
| // https://html.spec.whatwg.org/multipage/#update-href | |
| fn update_href(&self, url: DOMString) { | |
| self.upcast::<Element>() | |
| .set_string_attribute(&local_name!("href"), url); | |
| } | |
| } | |
| impl VirtualMethods for HTMLAnchorElement { | |
| fn super_type(&self) -> Option<&dyn VirtualMethods> { | |
| Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods) | |
| } | |
| fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue { | |
| match name { | |
| &local_name!("rel") => AttrValue::from_serialized_tokenlist(value.into()), | |
| _ => self | |
| .super_type() | |
| .unwrap() | |
| .parse_plain_attribute(name, value), | |
| } | |
| } | |
| } | |
| impl HTMLAnchorElementMethods for HTMLAnchorElement { | |
| // https://html.spec.whatwg.org/multipage/#dom-a-text | |
| fn Text(&self) -> DOMString { | |
| self.upcast::<Node>().GetTextContent().unwrap() | |
| } | |
| // https://html.spec.whatwg.org/multipage/#dom-a-text | |
| fn SetText(&self, value: DOMString) { | |
| self.upcast::<Node>().SetTextContent(Some(value)) | |
| } | |
| // https://html.spec.whatwg.org/multipage/#dom-a-rel | |
| make_getter!(Rel, "rel"); | |
| // https://html.spec.whatwg.org/multipage/#dom-a-rel | |
| fn SetRel(&self, rel: DOMString) { | |
| self.upcast::<Element>() | |
| .set_tokenlist_attribute(&local_name!("rel"), rel); | |
| } | |
| // https://html.spec.whatwg.org/multipage/#dom-a-rellist | |
| fn RelList(&self) -> DomRoot<DOMTokenList> { | |
| self.rel_list | |
| .or_init(|| DOMTokenList::new(self.upcast(), &local_name!("rel"))) | |
| } | |
| // https://html.spec.whatwg.org/multipage/#dom-a-coords | |
| make_getter!(Coords, "coords"); | |
| // https://html.spec.whatwg.org/multipage/#dom-a-coords | |
| make_setter!(SetCoords, "coords"); | |
| // https://html.spec.whatwg.org/multipage/#dom-a-name | |
| make_getter!(Name, "name"); | |
| // https://html.spec.whatwg.org/multipage/#dom-a-name | |
| make_setter!(SetName, "name"); | |
| // https://html.spec.whatwg.org/multipage/#dom-a-rev | |
| make_getter!(Rev, "rev"); | |
| // https://html.spec.whatwg.org/multipage/#dom-a-rev | |
| make_setter!(SetRev, "rev"); | |
| // https://html.spec.whatwg.org/multipage/#dom-a-shape | |
| make_getter!(Shape, "shape"); | |
| // https://html.spec.whatwg.org/multipage/#dom-a-shape | |
| make_setter!(SetShape, "shape"); | |
| // https://html.spec.whatwg.org/multipage/#attr-hyperlink-target | |
| make_getter!(Target, "target"); | |
| // https://html.spec.whatwg.org/multipage/#attr-hyperlink-target | |
| make_setter!(SetTarget, "target"); | |
| // https://html.spec.whatwg.org/multipage/#dom-hyperlink-hash | |
| fn Hash(&self) -> USVString { | |
| // Step 1. | |
| self.reinitialize_url(); | |
| match *self.url.borrow() { | |
| // Step 3. | |
| None => USVString(String::new()), | |
| Some(ref url) => { | |
| // Steps 3-4. | |
| UrlHelper::Hash(url) | |
| }, | |
| } | |
| } | |
| // https://html.spec.whatwg.org/multipage/#dom-hyperlink-hash | |
| fn SetHash(&self, value: USVString) { | |
| // Step 1. | |
| self.reinitialize_url(); | |
| // Step 2. | |
| let url = match self.url.borrow_mut().as_mut() { | |
| // Step 3. | |
| Some(ref url) if url.scheme() == "javascript" => return, | |
| None => return, | |
| // Steps 4-5. | |
| Some(url) => { | |
| UrlHelper::SetHash(url, value); | |
| DOMString::from(url.as_str()) | |
| }, | |
| }; | |
| // Step 6. | |
| self.update_href(url); | |
| } | |
| // https://html.spec.whatwg.org/multipage/#dom-hyperlink-host | |
| fn Host(&self) -> USVString { | |
| // Step 1. | |
| self.reinitialize_url(); | |
| match *self.url.borrow() { | |
| // Step 3. | |
| None => USVString(String::new()), | |
| Some(ref url) => { | |
| if url.host().is_none() { | |
| USVString(String::new()) | |
| } else { | |
| // Steps 4-5. | |
| UrlHelper::Host(url) | |
| } | |
| }, | |
| } | |
| } | |
| // https://html.spec.whatwg.org/multipage/#dom-hyperlink-host | |
| fn SetHost(&self, value: USVString) { | |
| // Step 1. | |
| self.reinitialize_url(); | |
| // Step 2. | |
| let url = match self.url.borrow_mut().as_mut() { | |
| // Step 3. | |
| Some(ref url) if url.cannot_be_a_base() => return, | |
| None => return, | |
| // Step 4. | |
| Some(url) => { | |
| UrlHelper::SetHost(url, value); | |
| DOMString::from(url.as_str()) | |
| }, | |
| }; | |
| // Step 5. | |
| self.update_href(url); | |
| } | |
| // https://html.spec.whatwg.org/multipage/#dom-hyperlink-hostname | |
| fn Hostname(&self) -> USVString { | |
| // Step 1. | |
| self.reinitialize_url(); | |
| match *self.url.borrow() { | |
| // Step 3. | |
| None => USVString(String::new()), | |
| Some(ref url) => { | |
| // Step 4. | |
| UrlHelper::Hostname(url) | |
| }, | |
| } | |
| } | |
| // https://html.spec.whatwg.org/multipage/#dom-hyperlink-hostname | |
| fn SetHostname(&self, value: USVString) { | |
| // Step 1. | |
| self.reinitialize_url(); | |
| // Step 2. | |
| let url = match self.url.borrow_mut().as_mut() { | |
| // Step 3. | |
| Some(ref url) if url.cannot_be_a_base() => return, | |
| None => return, | |
| // Step 4. | |
| Some(url) => { | |
| UrlHelper::SetHostname(url, value); | |
| DOMString::from(url.as_str()) | |
| }, | |
| }; | |
| // Step 5. | |
| self.update_href(url); | |
| } | |
| // https://html.spec.whatwg.org/multipage/#dom-hyperlink-href | |
| fn Href(&self) -> USVString { | |
| // Step 1. | |
| self.reinitialize_url(); | |
| USVString(match *self.url.borrow() { | |
| None => { | |
| match self | |
| .upcast::<Element>() | |
| .get_attribute(&ns!(), &local_name!("href")) | |
| { | |
| // Step 3. | |
| None => String::new(), | |
| // Step 4. | |
| Some(attribute) => (**attribute.value()).to_owned(), | |
| } | |
| }, | |
| // Step 5. | |
| Some(ref url) => url.as_str().to_owned(), | |
| }) | |
| } | |
| // https://html.spec.whatwg.org/multipage/#dom-hyperlink-href | |
| fn SetHref(&self, value: USVString) { | |
| self.upcast::<Element>() | |
| .set_string_attribute(&local_name!("href"), DOMString::from_string(value.0)); | |
| self.set_url(); | |
| } | |
| // https://html.spec.whatwg.org/multipage/#dom-hyperlink-origin | |
| fn Origin(&self) -> USVString { | |
| // Step 1. | |
| self.reinitialize_url(); | |
| USVString(match *self.url.borrow() { | |
| None => { | |
| // Step 2. | |
| "".to_owned() | |
| }, | |
| Some(ref url) => { | |
| // Step 3. | |
| url.origin().ascii_serialization() | |
| }, | |
| }) | |
| } | |
| // https://html.spec.whatwg.org/multipage/#dom-hyperlink-password | |
| fn Password(&self) -> USVString { | |
| // Step 1. | |
| self.reinitialize_url(); | |
| match *self.url.borrow() { | |
| // Step 3. | |
| None => USVString(String::new()), | |
| // Steps 3-4. | |
| Some(ref url) => UrlHelper::Password(url), | |
| } | |
| } | |
| // https://html.spec.whatwg.org/multipage/#dom-hyperlink-password | |
| fn SetPassword(&self, value: USVString) { | |
| // Step 1. | |
| self.reinitialize_url(); | |
| // Step 2. | |
| let url = match self.url.borrow_mut().as_mut() { | |
| // Step 3. | |
| Some(ref url) if url.host().is_none() || url.cannot_be_a_base() => return, | |
| None => return, | |
| // Step 4. | |
| Some(url) => { | |
| UrlHelper::SetPassword(url, value); | |
| DOMString::from(url.as_str()) | |
| }, | |
| }; | |
| // Step 5. | |
| self.update_href(url); | |
| } | |
| // https://html.spec.whatwg.org/multipage/#dom-hyperlink-pathname | |
| fn Pathname(&self) -> USVString { | |
| // Step 1. | |
| self.reinitialize_url(); | |
| match *self.url.borrow() { | |
| // Step 3. | |
| None => USVString(String::new()), | |
| // Steps 4-5. | |
| Some(ref url) => UrlHelper::Pathname(url), | |
| } | |
| } | |
| // https://html.spec.whatwg.org/multipage/#dom-hyperlink-pathname | |
| fn SetPathname(&self, value: USVString) { | |
| // Step 1. | |
| self.reinitialize_url(); | |
| // Step 2. | |
| let url = match self.url.borrow_mut().as_mut() { | |
| // Step 3. | |
| Some(ref url) if url.cannot_be_a_base() => return, | |
| None => return, | |
| // Step 5. | |
| Some(url) => { | |
| UrlHelper::SetPathname(url, value); | |
| DOMString::from(url.as_str()) | |
| }, | |
| }; | |
| // Step 6. | |
| self.update_href(url); | |
| } | |
| // https://html.spec.whatwg.org/multipage/#dom-hyperlink-port | |
| fn Port(&self) -> USVString { | |
| // Step 1. | |
| self.reinitialize_url(); | |
| match *self.url.borrow() { | |
| // Step 3. | |
| None => USVString(String::new()), | |
| // Step 4. | |
| Some(ref url) => UrlHelper::Port(url), | |
| } | |
| } | |
| // https://html.spec.whatwg.org/multipage/#dom-hyperlink-port | |
| fn SetPort(&self, value: USVString) { | |
| // Step 1. | |
| self.reinitialize_url(); | |
| // Step 3. | |
| let url = match self.url.borrow_mut().as_mut() { | |
| Some(ref url) | |
| if url.host().is_none() || url.cannot_be_a_base() || url.scheme() == "file" => | |
| { | |
| return; | |
| } | |
| None => return, | |
| // Step 4. | |
| Some(url) => { | |
| UrlHelper::SetPort(url, value); | |
| DOMString::from(url.as_str()) | |
| }, | |
| }; | |
| // Step 5. | |
| self.update_href(url); | |
| } | |
| // https://html.spec.whatwg.org/multipage/#dom-hyperlink-protocol | |
| fn Protocol(&self) -> USVString { | |
| // Step 1. | |
| self.reinitialize_url(); | |
| match *self.url.borrow() { | |
| // Step 2. | |
| None => USVString(":".to_owned()), | |
| // Step 3. | |
| Some(ref url) => UrlHelper::Protocol(url), | |
| } | |
| } | |
| // https://html.spec.whatwg.org/multipage/#dom-hyperlink-protocol | |
| fn SetProtocol(&self, value: USVString) { | |
| // Step 1. | |
| self.reinitialize_url(); | |
| let url = match self.url.borrow_mut().as_mut() { | |
| // Step 2. | |
| None => return, | |
| // Step 3. | |
| Some(url) => { | |
| UrlHelper::SetProtocol(url, value); | |
| DOMString::from(url.as_str()) | |
| }, | |
| }; | |
| // Step 4. | |
| self.update_href(url); | |
| } | |
| // https://html.spec.whatwg.org/multipage/#dom-hyperlink-search | |
| fn Search(&self) -> USVString { | |
| // Step 1. | |
| self.reinitialize_url(); | |
| match *self.url.borrow() { | |
| // Step 2. | |
| None => USVString(String::new()), | |
| // Step 3. | |
| Some(ref url) => UrlHelper::Search(url), | |
| } | |
| } | |
| // https://html.spec.whatwg.org/multipage/#dom-hyperlink-search | |
| fn SetSearch(&self, value: USVString) { | |
| // Step 1. | |
| self.reinitialize_url(); | |
| // Step 2. | |
| let url = match self.url.borrow_mut().as_mut() { | |
| // Step 3. | |
| None => return, | |
| // Steps 4-5. | |
| // TODO add this element's node document character encoding as | |
| // encoding override (as described in the spec) | |
| Some(url) => { | |
| UrlHelper::SetSearch(url, value); | |
| DOMString::from(url.as_str()) | |
| }, | |
| }; | |
| // Step 6. | |
| self.update_href(url); | |
| } | |
| // https://html.spec.whatwg.org/multipage/#dom-hyperlink-username | |
| fn Username(&self) -> USVString { | |
| // Step 1. | |
| self.reinitialize_url(); | |
| match *self.url.borrow() { | |
| // Step 2. | |
| None => USVString(String::new()), | |
| // Step 3. | |
| Some(ref url) => UrlHelper::Username(url), | |
| } | |
| } | |
| // https://html.spec.whatwg.org/multipage/#dom-hyperlink-username | |
| fn SetUsername(&self, value: USVString) { | |
| // Step 1. | |
| self.reinitialize_url(); | |
| // Step 2. | |
| let url = match self.url.borrow_mut().as_mut() { | |
| // Step 3. | |
| Some(ref url) if url.host().is_none() || url.cannot_be_a_base() => return, | |
| None => return, | |
| // Step 4. | |
| Some(url) => { | |
| UrlHelper::SetUsername(url, value); | |
| DOMString::from(url.as_str()) | |
| }, | |
| }; | |
| // Step 5. | |
| self.update_href(url); | |
| } | |
| // https://html.spec.whatwg.org/multipage/#dom-hyperlink-href | |
| fn Stringifier(&self) -> DOMString { | |
| DOMString::from(self.Href().0) | |
| } | |
| } | |
| impl Activatable for HTMLAnchorElement { | |
| fn as_element(&self) -> &Element { | |
| self.upcast::<Element>() | |
| } | |
| fn is_instance_activatable(&self) -> bool { | |
| // https://html.spec.whatwg.org/multipage/#hyperlink | |
| // "a [...] element[s] with an href attribute [...] must [..] create a | |
| // hyperlink" | |
| // https://html.spec.whatwg.org/multipage/#the-a-element | |
| // "The activation behaviour of a elements *that create hyperlinks*" | |
| self.as_element().has_attribute(&local_name!("href")) | |
| } | |
| //TODO:https://html.spec.whatwg.org/multipage/#the-a-element | |
| fn pre_click_activation(&self) {} | |
| //TODO:https://html.spec.whatwg.org/multipage/#the-a-element | |
| // https://html.spec.whatwg.org/multipage/#run-canceled-activation-steps | |
| fn canceled_activation(&self) {} | |
| //https://html.spec.whatwg.org/multipage/#the-a-element:activation-behaviour | |
| fn activation_behavior(&self, event: &Event, target: &EventTarget) { | |
| let element = self.as_element(); | |
| let mouse_event = event.downcast::<MouseEvent>().unwrap(); | |
| let mut ismap_suffix = None; | |
| // Step 1: If the target of the click event is an img element with an ismap attribute | |
| // specified, then server-side image map processing must be performed. | |
| if let Some(element) = target.downcast::<Element>() { | |
| if target.is::<HTMLImageElement>() && element.has_attribute(&local_name!("ismap")) { | |
| let target_node = element.upcast::<Node>(); | |
| let rect = target_node.bounding_content_box_or_zero(); | |
| ismap_suffix = Some(format!( | |
| "?{},{}", | |
| mouse_event.ClientX().to_f32().unwrap() - rect.origin.x.to_f32_px(), | |
| mouse_event.ClientY().to_f32().unwrap() - rect.origin.y.to_f32_px() | |
| )) | |
| } | |
| } | |
| // Step 2. | |
| //TODO: Download the link is `download` attribute is set. | |
| follow_hyperlink(element, ismap_suffix); | |
| } | |
| //TODO:https://html.spec.whatwg.org/multipage/#the-a-element | |
| fn implicit_submission( | |
| &self, | |
| _ctrl_key: bool, | |
| _shift_key: bool, | |
| _alt_key: bool, | |
| _meta_key: bool, | |
| ) { | |
| } | |
| } | |
| /// <https://html.spec.whatwg.org/multipage/#following-hyperlinks-2> | |
| pub fn follow_hyperlink(subject: &Element, hyperlink_suffix: Option<String>) { | |
| // Step 1. | |
| if subject.cannot_navigate() { | |
| return; | |
| } | |
| // Step 2, done in Step 7. | |
| let document = document_from_node(subject); | |
| let window = document.window(); | |
| // Step 3: source browsing context. | |
| let source = document.browsing_context().unwrap(); | |
| // Step 4-5: target attribute. | |
| let target_attribute_value = subject.get_attribute(&ns!(), &local_name!("target")); | |
| // Step 6. | |
| let noopener = if let Some(link_types) = subject.get_attribute(&ns!(), &local_name!("rel")) { | |
| let values = link_types.Value(); | |
| let contains_noopener = values.contains("noopener"); | |
| let contains_noreferrer = values.contains("noreferrer"); | |
| contains_noreferrer || contains_noopener | |
| } else { | |
| false | |
| }; | |
| // Step 7. | |
| let (maybe_chosen, replace) = match target_attribute_value { | |
| Some(name) => source.choose_browsing_context(name.Value(), noopener), | |
| None => (Some(window.window_proxy()), false), | |
| }; | |
| // Step 8. | |
| let chosen = match maybe_chosen { | |
| Some(proxy) => proxy, | |
| None => return, | |
| }; | |
| if let Some(target_document) = chosen.document() { | |
| let target_window = target_document.window(); | |
| // Step 9, dis-owning target's opener, if necessary | |
| // will have been done as part of Step 7 above | |
| // in choose_browsing_context/create_auxiliary_browsing_context. | |
| // Step 10, 11, 12, 13. TODO: if parsing the URL failed, navigate to error page. | |
| let attribute = subject.get_attribute(&ns!(), &local_name!("href")).unwrap(); | |
| let mut href = attribute.Value(); | |
| // Step 11: append a hyperlink suffix. | |
| // https://www.w3.org/Bugs/Public/show_bug.cgi?id=28925 | |
| if let Some(suffix) = hyperlink_suffix { | |
| href.push_str(&suffix); | |
| } | |
| let url = match document.url().join(&href) { | |
| Ok(url) => url, | |
| Err(_) => return, | |
| }; | |
| // Step 12. | |
| let referrer_policy = subject | |
| .get_attribute_by_name(DOMString::from_string(String::from("referrerpolicy"))) | |
| .and_then(|attribute: DomRoot<Attr>| (determine_policy_for_token(&attribute.Value()))); | |
| // Step 13, 14. | |
| debug!("following hyperlink to {}", url); | |
| target_window.load_url(url, replace, false, referrer_policy); | |
| }; | |
| } |