diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 562719de40c..616ca867a2e 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -23,6 +23,7 @@ use dom::bindings::codegen::InheritTypes::{HTMLAreaElementDerived, HTMLEmbedElem use dom::bindings::codegen::InheritTypes::{HTMLFormElementDerived, HTMLImageElementDerived}; use dom::bindings::codegen::InheritTypes::{HTMLScriptElementDerived, HTMLTitleElementDerived}; use dom::bindings::codegen::InheritTypes::ElementDerived; +use dom::bindings::codegen::InheritTypes::HTMLBaseElementCast; use dom::bindings::codegen::UnionTypes::NodeOrString; use dom::bindings::error::{ErrorResult, Fallible}; use dom::bindings::error::Error::{NotSupported, InvalidCharacter, Security}; @@ -45,6 +46,7 @@ use dom::element::{ElementTypeId, ActivationElementHelpers, FocusElementHelpers} use dom::event::{Event, EventBubbles, EventCancelable, EventHelpers}; use dom::eventtarget::{EventTarget, EventTargetTypeId, EventTargetHelpers}; use dom::htmlanchorelement::HTMLAnchorElement; +use dom::htmlbaseelement::HTMLBaseElement; use dom::htmlcollection::{HTMLCollection, CollectionFilter}; use dom::htmlelement::{HTMLElement, HTMLElementTypeId}; use dom::htmlheadelement::HTMLHeadElement; @@ -153,6 +155,8 @@ pub struct Document { current_parser: MutNullableHeap>, /// When we should kick off a reflow. This happens during parsing. reflow_timeout: Cell>, + /// The cached first `base` element with an `href` attribute. + base_element: MutNullableHeap>, } impl PartialEq for Document { @@ -231,7 +235,16 @@ pub trait DocumentHelpers<'a> { fn encoding_name(self) -> Ref<'a, DOMString>; fn is_html_document(self) -> bool; fn is_fully_active(self) -> bool; + /// https://dom.spec.whatwg.org/#concept-document-url fn url(self) -> Url; + /// https://html.spec.whatwg.org/multipage/#fallback-base-url + fn fallback_base_url(self) -> Url; + /// https://html.spec.whatwg.org/multipage/#document-base-url + fn base_url(self) -> Url; + /// Returns the first `base` element in the DOM that has an `href` attribute. + fn base_element(self) -> Option>; + /// Refresh the cached first base element in the DOM. + fn refresh_base_element(self); fn quirks_mode(self) -> QuirksMode; fn set_quirks_mode(self, mode: QuirksMode); fn set_encoding_name(self, name: DOMString); @@ -335,11 +348,44 @@ impl<'a> DocumentHelpers<'a> for &'a Document { true } - // https://dom.spec.whatwg.org/#dom-document-url + // https://dom.spec.whatwg.org/#concept-document-url fn url(self) -> Url { self.url.clone() } + // https://html.spec.whatwg.org/multipage/#fallback-base-url + fn fallback_base_url(self) -> Url { + // Step 1: iframe srcdoc (#4767). + // Step 2: about:blank with a creator browsing context. + // Step 3. + self.url() + } + + // https://html.spec.whatwg.org/multipage/#document-base-url + fn base_url(self) -> Url { + match self.base_element() { + // Step 1. + None => self.fallback_base_url(), + // Step 2. + Some(base) => base.frozen_base_url(), + } + } + + /// Returns the first `base` element in the DOM that has an `href` attribute. + fn base_element(self) -> Option> { + self.base_element.get().map(Root::from_rooted) + } + + /// Refresh the cached first base element in the DOM. + fn refresh_base_element(self) { + let base = NodeCast::from_ref(self) + .traverse_preorder() + .filter_map(HTMLBaseElementCast::to_root) + .filter(|element| ElementCast::from_ref(&**element).has_attribute(&atom!("href"))) + .next(); + self.base_element.set(base.map(|element| JS::from_ref(&*element))); + } + fn quirks_mode(self) -> QuirksMode { self.quirks_mode.get() } @@ -1089,6 +1135,7 @@ impl Document { loader: DOMRefCell::new(doc_loader), current_parser: Default::default(), reflow_timeout: Cell::new(None), + base_element: Default::default(), } } diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index 34309eee29a..4db26945f2e 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -1558,7 +1558,7 @@ impl<'a> VirtualMethods for &'a Element { &atom!("style") => { // Modifying the `style` attribute might change style. let doc = document_from_node(*self); - let base_url = doc.r().url(); + let base_url = doc.r().base_url(); let value = attr.value(); let style = Some(parse_style_attribute(&value, &base_url)); *self.style_attribute.borrow_mut() = style; diff --git a/components/script/dom/htmlbaseelement.rs b/components/script/dom/htmlbaseelement.rs index f1224d37097..98799a44a89 100644 --- a/components/script/dom/htmlbaseelement.rs +++ b/components/script/dom/htmlbaseelement.rs @@ -2,16 +2,22 @@ * 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::attr::{Attr, AttrHelpers}; use dom::bindings::codegen::Bindings::HTMLBaseElementBinding; +use dom::bindings::codegen::InheritTypes::ElementCast; use dom::bindings::codegen::InheritTypes::HTMLBaseElementDerived; +use dom::bindings::codegen::InheritTypes::HTMLElementCast; use dom::bindings::js::Root; -use dom::document::Document; +use dom::document::{Document, DocumentHelpers}; use dom::eventtarget::{EventTarget, EventTargetTypeId}; -use dom::element::ElementTypeId; +use dom::element::{ElementTypeId, AttributeHandlers}; use dom::htmlelement::{HTMLElement, HTMLElementTypeId}; -use dom::node::{Node, NodeTypeId}; +use dom::node::{Node, NodeTypeId, document_from_node}; +use dom::virtualmethods::VirtualMethods; use util::str::DOMString; +use url::{Url, UrlParser}; + #[dom_struct] pub struct HTMLBaseElement { htmlelement: HTMLElement @@ -39,5 +45,62 @@ impl HTMLBaseElement { let element = HTMLBaseElement::new_inherited(localName, prefix, document); Node::reflect_node(box element, document, HTMLBaseElementBinding::Wrap) } + + /// https://html.spec.whatwg.org/multipage/#frozen-base-url + pub fn frozen_base_url(&self) -> Url { + let href = ElementCast::from_ref(self).get_attribute(&ns!(""), &atom!("href")) + .expect("The frozen base url is only defined for base elements \ + that have a base url."); + let base = document_from_node(self).fallback_base_url(); + let parsed = UrlParser::new().base_url(&base).parse(&href.value()); + parsed.unwrap_or(base) + } + + /// Update the cached base element in response to adding or removing an + /// attribute. + pub fn add_remove_attr(&self, attr: &Attr) { + if *attr.local_name() == atom!("href") { + let document = document_from_node(self); + document.refresh_base_element(); + } + } + + /// Update the cached base element in response to binding or unbinding from + /// a tree. + pub fn bind_unbind(&self, tree_in_doc: bool) { + if !tree_in_doc { + return; + } + + if ElementCast::from_ref(self).has_attribute(&atom!("href")) { + let document = document_from_node(self); + document.refresh_base_element(); + } + } } +impl<'a> VirtualMethods for &'a HTMLBaseElement { + fn super_type<'b>(&'b self) -> Option<&'b VirtualMethods> { + Some(HTMLElementCast::from_borrowed_ref(self) as &VirtualMethods) + } + + fn after_set_attr(&self, attr: &Attr) { + self.super_type().unwrap().after_set_attr(attr); + self.add_remove_attr(attr); + } + + fn before_remove_attr(&self, attr: &Attr) { + self.super_type().unwrap().before_remove_attr(attr); + self.add_remove_attr(attr); + } + + fn bind_to_tree(&self, tree_in_doc: bool) { + self.super_type().unwrap().bind_to_tree(tree_in_doc); + self.bind_unbind(tree_in_doc); + } + + fn unbind_from_tree(&self, tree_in_doc: bool) { + self.super_type().unwrap().unbind_from_tree(tree_in_doc); + self.bind_unbind(tree_in_doc); + } +} diff --git a/components/script/dom/virtualmethods.rs b/components/script/dom/virtualmethods.rs index 88cc024acd5..b67cbb69d4f 100644 --- a/components/script/dom/virtualmethods.rs +++ b/components/script/dom/virtualmethods.rs @@ -7,6 +7,7 @@ use dom::bindings::codegen::InheritTypes::ElementCast; use dom::bindings::codegen::InheritTypes::HTMLAnchorElementCast; use dom::bindings::codegen::InheritTypes::HTMLAreaElementCast; use dom::bindings::codegen::InheritTypes::HTMLAppletElementCast; +use dom::bindings::codegen::InheritTypes::HTMLBaseElementCast; use dom::bindings::codegen::InheritTypes::HTMLBodyElementCast; use dom::bindings::codegen::InheritTypes::HTMLButtonElementCast; use dom::bindings::codegen::InheritTypes::HTMLCanvasElementCast; @@ -138,6 +139,10 @@ pub fn vtable_for<'a>(node: &'a &'a Node) -> &'a (VirtualMethods + 'a) { let element = HTMLAreaElementCast::to_borrowed_ref(node).unwrap(); element as &'a (VirtualMethods + 'a) } + NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLBaseElement)) => { + let element = HTMLBaseElementCast::to_borrowed_ref(node).unwrap(); + element as &'a (VirtualMethods + 'a) + } NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLBodyElement)) => { let element = HTMLBodyElementCast::to_borrowed_ref(node).unwrap(); element as &'a (VirtualMethods + 'a) diff --git a/tests/wpt/metadata-css/css-style-attr-1_dev/html4/style-attr-urls-002.htm.ini b/tests/wpt/metadata-css/css-style-attr-1_dev/html4/style-attr-urls-002.htm.ini deleted file mode 100644 index 91e74743f3f..00000000000 --- a/tests/wpt/metadata-css/css-style-attr-1_dev/html4/style-attr-urls-002.htm.ini +++ /dev/null @@ -1,3 +0,0 @@ -[style-attr-urls-002.htm] - type: reftest - expected: FAIL