aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom/document.rs
diff options
context:
space:
mode:
Diffstat (limited to 'components/script/dom/document.rs')
-rw-r--r--components/script/dom/document.rs108
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()
}
}