diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index c0affb40aead..b9b02183762a 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -103,6 +103,7 @@ use style::values::specified::{self, CSSColor, CSSRGBA, LengthOrPercentage}; pub struct Element { node: Node, local_name: Atom, + tag_name: TagName, namespace: Namespace, prefix: Option, attrs: DOMRefCell>>, @@ -165,6 +166,7 @@ impl Element { Element { node: Node::new_inherited(document), local_name: local_name, + tag_name: TagName::new(), namespace: namespace, prefix: prefix, attrs: DOMRefCell::new(vec![]), @@ -1367,17 +1369,20 @@ impl ElementMethods for Element { // https://dom.spec.whatwg.org/#dom-element-tagname fn TagName(&self) -> DOMString { - let qualified_name = match self.prefix { - Some(ref prefix) => { - Cow::Owned(format!("{}:{}", &**prefix, &*self.local_name)) - }, - None => Cow::Borrowed(&*self.local_name) - }; - DOMString::from(if self.html_element_in_html_document() { - qualified_name.to_ascii_uppercase() - } else { - qualified_name.into_owned() - }) + let name = self.tag_name.or_init(|| { + let qualified_name = match self.prefix { + Some(ref prefix) => { + Cow::Owned(format!("{}:{}", &**prefix, &*self.local_name)) + }, + None => Cow::Borrowed(&*self.local_name) + }; + if self.html_element_in_html_document() { + Atom::from(qualified_name.to_ascii_uppercase()) + } else { + Atom::from(qualified_name) + } + }); + DOMString::from(&*name) } // https://dom.spec.whatwg.org/#dom-element-id @@ -2219,6 +2224,14 @@ impl VirtualMethods for Element { } } } + + fn adopting_steps(&self, old_doc: &Document) { + self.super_type().unwrap().adopting_steps(old_doc); + + if document_from_node(self).is_html_document() != old_doc.is_html_document() { + self.tag_name.clear(); + } + } } impl<'a> ::selectors::MatchAttrGeneric for Root { @@ -2691,3 +2704,38 @@ impl AtomicElementFlags { self.0.fetch_or(flags.bits() as usize, Ordering::Relaxed); } } + +/// A holder for an element's "tag name", which will be lazily +/// resolved and cached. Should be reset when the document +/// owner changes. +#[derive(JSTraceable, HeapSizeOf)] +struct TagName { + ptr: DOMRefCell>, +} + +impl TagName { + fn new() -> TagName { + TagName { ptr: DOMRefCell::new(None) } + } + + /// Retrieve a copy of the current inner value. If it is `None`, it is + /// initialized with the result of `cb` first. + fn or_init(&self, cb: F) -> Atom + where F: FnOnce() -> Atom + { + match &mut *self.ptr.borrow_mut() { + &mut Some(ref name) => name.clone(), + ptr => { + let name = cb(); + *ptr = Some(name.clone()); + name + } + } + } + + /// Clear the cached tag name, so that it will be re-calculated the + /// next time that `or_init()` is called. + fn clear(&self) { + *self.ptr.borrow_mut() = None; + } +} diff --git a/tests/unit/script/size_of.rs b/tests/unit/script/size_of.rs index 21b8e4a4ba23..b30304d5dac0 100644 --- a/tests/unit/script/size_of.rs +++ b/tests/unit/script/size_of.rs @@ -40,10 +40,10 @@ macro_rules! sizeof_checker ( // Update the sizes here sizeof_checker!(size_event_target, EventTarget, 40); sizeof_checker!(size_node, Node, 160); -sizeof_checker!(size_element, Element, 312); -sizeof_checker!(size_htmlelement, HTMLElement, 328); -sizeof_checker!(size_div, HTMLDivElement, 328); -sizeof_checker!(size_span, HTMLSpanElement, 328); +sizeof_checker!(size_element, Element, 336); +sizeof_checker!(size_htmlelement, HTMLElement, 352); +sizeof_checker!(size_div, HTMLDivElement, 352); +sizeof_checker!(size_span, HTMLSpanElement, 352); sizeof_checker!(size_text, Text, 192); sizeof_checker!(size_characterdata, CharacterData, 192); sizeof_checker!(size_servothreadsafelayoutnode, ServoThreadSafeLayoutNode, 16);