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); + } }