Files
tubestation/servo/components/script/dom/htmlanchorelement.rs
Jaydeep 1bd60c6a3e servo: Merge #9887 - Implement HTMLHyperlinkElementUtils for HTMLAnchorElement (from schuster:HTMLHyperlinkElementUtils); r=jdm
Fixes #7857

Origin is omitted since it's still not available in rust-url, but since the previous PR also left it out, I'm assuming that's okay.

Please let me know if there are any style issues. There might be more concise ways to do the pattern matching that I don't know about, I guessed at the indentation style in one or two places.

Source-Repo: https://github.com/servo/servo
Source-Revision: 9a8d62286c6556fca7471f2db37f896174bc4d9e
2016-03-25 11:06:35 +05:00

542 lines
17 KiB
Rust

/* 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 http://mozilla.org/MPL/2.0/. */
use dom::activation::Activatable;
use dom::attr::AttrValue;
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
use dom::bindings::codegen::Bindings::HTMLAnchorElementBinding;
use dom::bindings::codegen::Bindings::HTMLAnchorElementBinding::HTMLAnchorElementMethods;
use dom::bindings::codegen::Bindings::MouseEventBinding::MouseEventMethods;
use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
use dom::bindings::inheritance::Castable;
use dom::bindings::js::{JS, MutNullableHeap, Root};
use dom::bindings::str::USVString;
use dom::document::Document;
use dom::domtokenlist::DOMTokenList;
use dom::element::Element;
use dom::event::Event;
use dom::eventtarget::EventTarget;
use dom::htmlelement::HTMLElement;
use dom::htmlimageelement::HTMLImageElement;
use dom::mouseevent::MouseEvent;
use dom::node::{Node, document_from_node, window_from_node};
use dom::urlhelper::UrlHelper;
use dom::virtualmethods::VirtualMethods;
use num::ToPrimitive;
use std::default::Default;
use string_cache::Atom;
use url::{Url, UrlParser};
use util::str::DOMString;
#[dom_struct]
pub struct HTMLAnchorElement {
htmlelement: HTMLElement,
rel_list: MutNullableHeap<JS<DOMTokenList>>,
url: DOMRefCell<Option<Url>>,
}
impl HTMLAnchorElement {
fn new_inherited(localName: Atom,
prefix: Option<DOMString>,
document: &Document) -> HTMLAnchorElement {
HTMLAnchorElement {
htmlelement:
HTMLElement::new_inherited(localName, prefix, document),
rel_list: Default::default(),
url: DOMRefCell::new(None),
}
}
#[allow(unrooted_must_root)]
pub fn new(localName: Atom,
prefix: Option<DOMString>,
document: &Document) -> Root<HTMLAnchorElement> {
let element = HTMLAnchorElement::new_inherited(localName, prefix, document);
Node::reflect_node(box element, 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!(), &atom!("href"));
*self.url.borrow_mut() = attribute.and_then(|attribute| {
let document = document_from_node(self);
let mut parser = UrlParser::new();
parser.base_url(document.url());
parser.parse(&attribute.value()).ok()
});
}
// https://html.spec.whatwg.org/multipage/#reinitialise-url
fn reinitialize_url(&self) {
// Step 1.
match *self.url.borrow() {
None => return,
Some(ref url) if url.scheme == "blob" &&
url.non_relative_scheme_data().is_some() => return,
_ => (),
}
// Step 2.
self.set_url();
}
// https://html.spec.whatwg.org/multipage/#update-href
fn update_href(&self) {
self.upcast::<Element>().set_string_attribute(&atom!("href"),
self.url.borrow().as_ref().unwrap().serialize().into());
}
}
impl VirtualMethods for HTMLAnchorElement {
fn super_type(&self) -> Option<&VirtualMethods> {
Some(self.upcast::<HTMLElement>() as &VirtualMethods)
}
fn parse_plain_attribute(&self, name: &Atom, value: DOMString) -> AttrValue {
match name {
&atom!("rel") => AttrValue::from_serialized_tokenlist(value),
_ => 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-rellist
fn RelList(&self) -> Root<DOMTokenList> {
self.rel_list.or_init(|| {
DOMTokenList::new(self.upcast(), &atom!("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/#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 3.
if let Some(url) = self.url.borrow_mut().as_mut() {
if url.scheme == "javascript" { return; }
// Steps 4-5.
UrlHelper::SetHash(url, value);
// Step 6.
self.update_href();
}
}
// 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 3.
if let Some(url) = self.url.borrow_mut().as_mut() {
if url.non_relative_scheme_data().is_some() {
return;
}
// Step 4.
UrlHelper::SetHost(url, value);
// Step 5.
self.update_href();
}
}
// 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 3.
if let Some(url) = self.url.borrow_mut().as_mut() {
if url.non_relative_scheme_data().is_some() {
return;
}
// Step 4.
UrlHelper::SetHostname(url, value);
// Step 5.
self.update_href();
}
}
// 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!(), &atom!("href")) {
// Step 3.
None => String::new(),
// Step 4.
Some(attribute) => (**attribute.value()).to_owned(),
}
},
// Step 5.
Some(ref url) => url.serialize(),
})
}
// https://html.spec.whatwg.org/multipage/#dom-hyperlink-href
fn SetHref(&self, value: USVString) {
self.upcast::<Element>().set_string_attribute(&atom!("href"),
DOMString::from_string(value.0));
self.set_url();
}
// 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 3.
if let Some(url) = self.url.borrow_mut().as_mut() {
if url.host().is_none() || url.non_relative_scheme_data().is_some() {
return;
}
// Step 4.
UrlHelper::SetPassword(url, value);
// Step 5.
self.update_href();
}
}
// 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 3.
if let Some(url) = self.url.borrow_mut().as_mut() {
if url.non_relative_scheme_data().is_some() { return; }
// Step 5.
UrlHelper::SetPathname(url, value);
// Step 6.
self.update_href();
}
}
// 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.
if let Some(url) = self.url.borrow_mut().as_mut() {
if url.host().is_none() ||
url.non_relative_scheme_data().is_some() ||
url.scheme == "file" {
return;
}
// Step 4.
UrlHelper::SetPort(url, value);
// Step 5.
self.update_href();
}
}
// 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();
// Step 2.
if let Some(url) = self.url.borrow_mut().as_mut() {
// Step 3.
UrlHelper::SetProtocol(url, value);
// Step 4.
self.update_href();
}
}
// 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 3.
if let Some(url) = self.url.borrow_mut().as_mut() {
// Steps 4-5.
// TODO add this element's node document character encoding as
// encoding override (as described in the spec)
UrlHelper::SetSearch(url, value);
// Step 6.
self.update_href();
}
}
// 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 3.
if let Some(url) = self.url.borrow_mut().as_mut() {
if url.host().is_none() || url.non_relative_scheme_data().is_some() {
return;
}
// Step 4.
UrlHelper::SetUsername(url, value);
// Step 5.
self.update_href();
}
}
// 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.upcast::<Element>().has_attribute(&atom!("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) {
//Step 1. If the node document is not fully active, abort.
let doc = document_from_node(self);
if !doc.is_fully_active() {
return;
}
//TODO: Step 2. Check if browsing context is specified and act accordingly.
//Step 3. Handle <img ismap/>.
let element = self.upcast::<Element>();
let mouse_event = event.downcast::<MouseEvent>().unwrap();
let mut ismap_suffix = None;
if let Some(element) = target.downcast::<Element>() {
if target.is::<HTMLImageElement>() && element.has_attribute(&atom!("ismap")) {
let target_node = element.upcast::<Node>();
let rect = window_from_node(target_node).content_box_query(
target_node.to_trusted_node_address());
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 4.
//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, _ctrlKey: bool, _shiftKey: bool, _altKey: bool, _metaKey: bool) {
}
}
/// https://html.spec.whatwg.org/multipage/#following-hyperlinks-2
fn follow_hyperlink(subject: &Element, hyperlink_suffix: Option<String>) {
// Step 1: replace.
// Step 2: source browsing context.
// Step 3: target browsing context.
// Step 4.
let attribute = subject.get_attribute(&ns!(), &atom!("href")).unwrap();
let mut href = attribute.Value();
// Step 6.
// https://www.w3.org/Bugs/Public/show_bug.cgi?id=28925
if let Some(suffix) = hyperlink_suffix {
href.push_str(&suffix);
}
// Step 4-5.
let document = document_from_node(subject);
let url = match document.url().join(&href) {
Ok(url) => url,
Err(_) => return,
};
// Step 7.
debug!("following hyperlink to {}", url.serialize());
let window = document.window();
window.load_url(url);
}