From 92e64e607ca4b00f92ba502f8d83149881da2df6 Mon Sep 17 00:00:00 2001 From: Ms2ger Date: Tue, 28 Jul 2015 17:43:00 +0200 Subject: [PATCH 1/4] Implement VirtualMethods for HTMLBaseElement. --- components/script/dom/htmlbaseelement.rs | 7 +++++++ components/script/dom/virtualmethods.rs | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/components/script/dom/htmlbaseelement.rs b/components/script/dom/htmlbaseelement.rs index f1224d37097..6f4a585b113 100644 --- a/components/script/dom/htmlbaseelement.rs +++ b/components/script/dom/htmlbaseelement.rs @@ -4,12 +4,14 @@ use dom::bindings::codegen::Bindings::HTMLBaseElementBinding; use dom::bindings::codegen::InheritTypes::HTMLBaseElementDerived; +use dom::bindings::codegen::InheritTypes::HTMLElementCast; use dom::bindings::js::Root; use dom::document::Document; use dom::eventtarget::{EventTarget, EventTargetTypeId}; use dom::element::ElementTypeId; use dom::htmlelement::{HTMLElement, HTMLElementTypeId}; use dom::node::{Node, NodeTypeId}; +use dom::virtualmethods::VirtualMethods; use util::str::DOMString; #[dom_struct] @@ -41,3 +43,8 @@ impl HTMLBaseElement { } } +impl<'a> VirtualMethods for &'a HTMLBaseElement { + fn super_type<'b>(&'b self) -> Option<&'b VirtualMethods> { + Some(HTMLElementCast::from_borrowed_ref(self) as &VirtualMethods) + } +} 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) From 7f2b21c06e521af6f921e81bd04adf49270749ee Mon Sep 17 00:00:00 2001 From: Ms2ger Date: Tue, 28 Jul 2015 12:02:51 +0200 Subject: [PATCH 2/4] Implement base_url and fallback_base_url methods. --- components/script/dom/document.rs | 38 +++++++++++++++++++++++- components/script/dom/htmlbaseelement.rs | 20 +++++++++++-- 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index d60545e159b..2f0787ad6e4 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; @@ -231,7 +233,14 @@ 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>; fn quirks_mode(self) -> QuirksMode; fn set_quirks_mode(self, mode: QuirksMode); fn set_encoding_name(self, name: DOMString); @@ -336,11 +345,38 @@ 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> { + NodeCast::from_ref(self) + .traverse_preorder() + .filter_map(HTMLBaseElementCast::to_root) + .filter(|element| ElementCast::from_ref(&**element).has_attribute(&atom!("href"))) + .next() + } + fn quirks_mode(self) -> QuirksMode { self.quirks_mode.get() } diff --git a/components/script/dom/htmlbaseelement.rs b/components/script/dom/htmlbaseelement.rs index 6f4a585b113..42e7ffe6b5d 100644 --- a/components/script/dom/htmlbaseelement.rs +++ b/components/script/dom/htmlbaseelement.rs @@ -2,18 +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::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 @@ -41,6 +45,16 @@ 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) + } } impl<'a> VirtualMethods for &'a HTMLBaseElement { From bd31b51a87e04a1a44670dbce658eb0dca0a48fd Mon Sep 17 00:00:00 2001 From: Ms2ger Date: Thu, 6 Aug 2015 18:03:35 +0200 Subject: [PATCH 3/4] Use the base URL to parse style attributes. --- components/script/dom/element.rs | 2 +- .../css-style-attr-1_dev/html4/style-attr-urls-002.htm.ini | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) delete mode 100644 tests/wpt/metadata-css/css-style-attr-1_dev/html4/style-attr-urls-002.htm.ini 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/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 From 6951119f5e7c833d26df47ce5a79b93748d2152d Mon Sep 17 00:00:00 2001 From: Ms2ger Date: Tue, 28 Jul 2015 19:33:20 +0200 Subject: [PATCH 4/4] Cache the first base element with an href attribute on the Document. --- components/script/dom/document.rs | 15 ++++++-- components/script/dom/htmlbaseelement.rs | 44 +++++++++++++++++++++++- 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 2f0787ad6e4..f53940dabe4 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -155,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 { @@ -241,6 +243,8 @@ pub trait DocumentHelpers<'a> { 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); @@ -370,11 +374,17 @@ impl<'a> DocumentHelpers<'a> for &'a Document { /// Returns the first `base` element in the DOM that has an `href` attribute. fn base_element(self) -> Option> { - NodeCast::from_ref(self) + 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() + .next(); + self.base_element.set(base.map(|element| JS::from_ref(&*element))); } fn quirks_mode(self) -> QuirksMode { @@ -1131,6 +1141,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/htmlbaseelement.rs b/components/script/dom/htmlbaseelement.rs index 42e7ffe6b5d..98799a44a89 100644 --- a/components/script/dom/htmlbaseelement.rs +++ b/components/script/dom/htmlbaseelement.rs @@ -2,7 +2,7 @@ * 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::AttrHelpers; +use dom::attr::{Attr, AttrHelpers}; use dom::bindings::codegen::Bindings::HTMLBaseElementBinding; use dom::bindings::codegen::InheritTypes::ElementCast; use dom::bindings::codegen::InheritTypes::HTMLBaseElementDerived; @@ -55,10 +55,52 @@ impl HTMLBaseElement { 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); + } }