diff options
Diffstat (limited to 'components/script/dom/document.rs')
-rw-r--r-- | components/script/dom/document.rs | 108 |
1 files changed, 79 insertions, 29 deletions
diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index b39adbc53fc..dc3225bba04 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -48,7 +48,10 @@ use dom::htmlheadelement::HTMLHeadElement; use dom::htmlhtmlelement::HTMLHtmlElement; use dom::htmliframeelement::{self, HTMLIFrameElement}; use dom::htmlimageelement::HTMLImageElement; +use dom::htmllinkelement::HTMLLinkElement; +use dom::htmlmetaelement::HTMLMetaElement; use dom::htmlscriptelement::HTMLScriptElement; +use dom::htmlstyleelement::HTMLStyleElement; use dom::htmltitleelement::HTMLTitleElement; use dom::keyboardevent::KeyboardEvent; use dom::location::Location; @@ -96,8 +99,10 @@ use std::default::Default; use std::iter::FromIterator; use std::ptr; use std::rc::Rc; +use std::sync::Arc; use std::sync::mpsc::channel; use string_cache::{Atom, QualName}; +use style::stylesheets::Stylesheet; use time; use url::Url; use util::str::{DOMString, split_html_space_chars, str_join}; @@ -135,6 +140,10 @@ pub struct Document { scripts: MutNullableHeap<JS<HTMLCollection>>, anchors: MutNullableHeap<JS<HTMLCollection>>, applets: MutNullableHeap<JS<HTMLCollection>>, + /// List of stylesheets associated with nodes in this document. |None| if the list needs to be refreshed. + stylesheets: DOMRefCell<Option<Vec<Arc<Stylesheet>>>>, + /// Whether the list of stylesheets has changed since the last reflow was triggered. + stylesheets_changed_since_reflow: Cell<bool>, ready_state: Cell<DocumentReadyState>, /// Whether the DOMContentLoaded event has already been dispatched. domcontentloaded_dispatched: Cell<bool>, @@ -175,8 +184,8 @@ pub struct Document { /// This field is set to the document itself for inert documents. /// https://html.spec.whatwg.org/multipage/#appropriate-template-contents-owner-document appropriate_template_contents_owner_document: MutNullableHeap<JS<Document>>, - /// The collection of ElementStates that have been changed since the last restyle. - element_state_changes: DOMRefCell<HashMap<JS<Element>, ElementState>>, + /// For each element that has had a state change since the last restyle, track the original state. + modified_elements: DOMRefCell<HashMap<JS<Element>, ElementState>>, /// http://w3c.github.io/touch-events/#dfn-active-touch-point active_touch_points: DOMRefCell<Vec<JS<Touch>>>, } @@ -308,7 +317,7 @@ impl Document { pub fn needs_reflow(&self) -> bool { self.GetDocumentElement().is_some() && - (self.upcast::<Node>().get_has_dirty_descendants() || !self.element_state_changes.borrow().is_empty()) + (self.upcast::<Node>().get_has_dirty_descendants() || !self.modified_elements.borrow().is_empty()) } /// Returns the first `base` element in the DOM that has an `href` attribute. @@ -921,15 +930,14 @@ impl Document { ReflowReason::KeyEvent); } - pub fn node_from_nodes_and_strings(&self, nodes: Vec<NodeOrString>) + // https://dom.spec.whatwg.org/#converting-nodes-into-a-node + pub fn node_from_nodes_and_strings(&self, mut nodes: Vec<NodeOrString>) -> Fallible<Root<Node>> { if nodes.len() == 1 { - match nodes.into_iter().next().unwrap() { - NodeOrString::eNode(node) => Ok(node), - NodeOrString::eString(string) => { - Ok(Root::upcast(self.CreateTextNode(string))) - }, - } + Ok(match nodes.pop().unwrap() { + NodeOrString::eNode(node) => node, + NodeOrString::eString(string) => Root::upcast(self.CreateTextNode(string)), + }) } else { let fragment = Root::upcast::<Node>(self.CreateDocumentFragment()); for node in nodes { @@ -983,6 +991,21 @@ impl Document { count_cell.set(count_cell.get() - 1); } + pub fn invalidate_stylesheets(&self) { + self.stylesheets_changed_since_reflow.set(true); + *self.stylesheets.borrow_mut() = None; + // Mark the document element dirty so a reflow will be performed. + self.get_html_element().map(|root| { + root.upcast::<Node>().dirty(NodeDamage::NodeStyleDamaged); + }); + } + + pub fn get_and_reset_stylesheets_changed_since_reflow(&self) -> bool { + let changed = self.stylesheets_changed_since_reflow.get(); + self.stylesheets_changed_since_reflow.set(false); + changed + } + pub fn set_pending_parsing_blocking_script(&self, script: Option<&HTMLScriptElement>) { assert!(self.get_pending_parsing_blocking_script().is_none() || script.is_none()); self.pending_parsing_blocking_script.set(script); @@ -1100,6 +1123,13 @@ impl Document { if parser.is_suspended() { parser.resume(); } + } else if self.reflow_timeout.get().is_none() { + // If we don't have a parser, and the reflow timer has been reset, explicitly + // trigger a reflow. + if let LoadType::Stylesheet(_) = load { + self.window().reflow(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery, + ReflowReason::StylesheetLoaded); + } } let loader = self.loader.borrow(); @@ -1239,7 +1269,7 @@ pub enum DocumentSource { #[allow(unsafe_code)] pub trait LayoutDocumentHelpers { unsafe fn is_html_document_for_layout(&self) -> bool; - unsafe fn drain_element_state_changes(&self) -> Vec<(LayoutJS<Element>, ElementState)>; + unsafe fn drain_modified_elements(&self) -> Vec<(LayoutJS<Element>, ElementState)>; } #[allow(unsafe_code)] @@ -1251,9 +1281,9 @@ impl LayoutDocumentHelpers for LayoutJS<Document> { #[inline] #[allow(unrooted_must_root)] - unsafe fn drain_element_state_changes(&self) -> Vec<(LayoutJS<Element>, ElementState)> { - let mut changes = (*self.unsafe_get()).element_state_changes.borrow_mut_for_layout(); - let drain = changes.drain(); + unsafe fn drain_modified_elements(&self) -> Vec<(LayoutJS<Element>, ElementState)> { + let mut elements = (*self.unsafe_get()).modified_elements.borrow_mut_for_layout(); + let drain = elements.drain(); let layout_drain = drain.map(|(k, v)| (k.to_layout(), v)); Vec::from_iter(layout_drain) } @@ -1304,6 +1334,8 @@ impl Document { scripts: Default::default(), anchors: Default::default(), applets: Default::default(), + stylesheets: DOMRefCell::new(None), + stylesheets_changed_since_reflow: Cell::new(false), ready_state: Cell::new(ready_state), domcontentloaded_dispatched: Cell::new(domcontentloaded_dispatched), possibly_focused: Default::default(), @@ -1322,7 +1354,7 @@ impl Document { reflow_timeout: Cell::new(None), base_element: Default::default(), appropriate_template_contents_owner_document: Default::default(), - element_state_changes: DOMRefCell::new(HashMap::new()), + modified_elements: DOMRefCell::new(HashMap::new()), active_touch_points: DOMRefCell::new(Vec::new()), } } @@ -1369,6 +1401,31 @@ impl Document { self.GetDocumentElement().and_then(Root::downcast) } + /// Returns the list of stylesheets associated with nodes in the document. + pub fn stylesheets(&self) -> Ref<Vec<Arc<Stylesheet>>> { + { + let mut stylesheets = self.stylesheets.borrow_mut(); + if stylesheets.is_none() { + let new_stylesheets: Vec<Arc<Stylesheet>> = self.upcast::<Node>() + .traverse_preorder() + .filter_map(|node| { + if let Some(node) = node.downcast::<HTMLStyleElement>() { + node.get_stylesheet() + } else if let Some(node) = node.downcast::<HTMLLinkElement>() { + node.get_stylesheet() + } else if let Some(node) = node.downcast::<HTMLMetaElement>() { + node.get_stylesheet() + } else { + None + } + }) + .collect(); + *stylesheets = Some(new_stylesheets); + }; + } + Ref::map(self.stylesheets.borrow(), |t| t.as_ref().unwrap()) + } + /// https://html.spec.whatwg.org/multipage/#appropriate-template-contents-owner-document pub fn appropriate_template_contents_owner_document(&self) -> Root<Document> { self.appropriate_template_contents_owner_document.or_init(|| { @@ -1389,18 +1446,9 @@ impl Document { self.idmap.borrow().get(&id).map(|ref elements| Root::from_ref(&*(*elements)[0])) } - pub fn record_element_state_change(&self, el: &Element, which: ElementState) { - let mut map = self.element_state_changes.borrow_mut(); - let empty; - { - let states = map.entry(JS::from_ref(el)) - .or_insert(ElementState::empty()); - states.toggle(which); - empty = states.is_empty(); - } - if empty { - map.remove(&JS::from_ref(el)); - } + pub fn element_state_will_change(&self, el: &Element) { + let mut map = self.modified_elements.borrow_mut(); + map.entry(JS::from_ref(el)).or_insert(el.get_state()); } } @@ -1749,8 +1797,10 @@ impl DocumentMethods for Document { let name = QualName::new(ns!(SVG), atom!("title")); let elem = Element::create(name, None, self, ElementCreator::ScriptCreated); - root.upcast::<Node>() - .AppendChild(elem.upcast()) + let parent = root.upcast::<Node>(); + let child = elem.upcast::<Node>(); + parent + .InsertBefore(child, parent.GetFirstChild().r()) .unwrap() } } |