style: Replicate the list of stylesheets on the layout thread.

This is a patch that unifies a bit how Gecko and Stylo stylesheets work, in
order to be able to eventually move the stylesheets into the stylist, and be
able to incrementally update the invalidation map.
This commit is contained in:
Emilio Cobos Álvarez 2017-08-16 15:46:17 +02:00
parent b8159e659e
commit d1725b1f19
No known key found for this signature in database
GPG key ID: 056B727BB9C1027C
14 changed files with 413 additions and 198 deletions

View file

@ -117,7 +117,6 @@ use std::collections::HashMap;
use std::mem as std_mem;
use std::ops::{Deref, DerefMut};
use std::process;
use std::slice;
use std::sync::{Arc, Mutex, MutexGuard};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::mpsc::{Receiver, Sender, channel};
@ -130,13 +129,15 @@ use style::context::RegisteredSpeculativePainters;
use style::dom::{ShowSubtree, ShowSubtreeDataAndPrimaryValues, TElement, TNode};
use style::error_reporting::{NullReporter, RustLogReporter};
use style::invalidation::element::restyle_hints::RestyleHint;
use style::invalidation::media_queries::{MediaListKey, ToMediaListKey};
use style::logical_geometry::LogicalPoint;
use style::media_queries::{Device, MediaList, MediaType};
use style::properties::PropertyId;
use style::selector_parser::SnapshotMap;
use style::servo::restyle_damage::{REFLOW, REFLOW_OUT_OF_FLOW, REPAINT, REPOSITION, STORE_OVERFLOW};
use style::shared_lock::{SharedRwLock, SharedRwLockReadGuard, StylesheetGuards};
use style::stylesheets::{Origin, Stylesheet, StylesheetInDocument, UserAgentStylesheets};
use style::stylesheet_set::StylesheetSet;
use style::stylesheets::{Origin, OriginSet, Stylesheet, StylesheetContents, StylesheetInDocument, UserAgentStylesheets};
use style::stylist::Stylist;
use style::thread_state;
use style::timer::Timer;
@ -146,6 +147,35 @@ use style_traits::CSSPixel;
use style_traits::DevicePixel;
use style_traits::SpeculativePainter;
#[derive(Clone)]
struct DocumentStyleSheet(ServoArc<Stylesheet>);
impl PartialEq for DocumentStyleSheet {
fn eq(&self, other: &Self) -> bool {
ServoArc::ptr_eq(&self.0, &other.0)
}
}
impl ToMediaListKey for DocumentStyleSheet {
fn to_media_list_key(&self) -> MediaListKey {
self.0.to_media_list_key()
}
}
impl StylesheetInDocument for DocumentStyleSheet {
fn contents(&self, guard: &SharedRwLockReadGuard) -> &StylesheetContents {
self.0.contents(guard)
}
fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
self.0.media(guard)
}
fn enabled(&self) -> bool {
self.0.enabled()
}
}
/// Information needed by the layout thread.
pub struct LayoutThread {
/// The ID of the pipeline that we belong to.
@ -160,6 +190,9 @@ pub struct LayoutThread {
/// Performs CSS selector matching and style resolution.
stylist: Stylist,
/// The list of stylesheets synchronized with the document.
stylesheets: StylesheetSet<DocumentStyleSheet>,
/// Is the current reflow of an iframe, as opposed to a root window?
is_iframe: bool,
@ -447,17 +480,6 @@ fn add_font_face_rules(stylesheet: &Stylesheet,
}
}
#[derive(Clone)]
struct StylesheetIterator<'a>(slice::Iter<'a, ServoArc<Stylesheet>>);
impl<'a> Iterator for StylesheetIterator<'a> {
type Item = &'a Stylesheet;
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|s| &**s)
}
}
impl LayoutThread {
/// Creates a new `LayoutThread` structure.
fn new(id: PipelineId,
@ -548,7 +570,8 @@ impl LayoutThread {
viewport_size: Size2D::new(Au(0), Au(0)),
webrender_api: webrender_api_sender.create_api(),
webrender_document,
stylist: stylist,
stylist,
stylesheets: StylesheetSet::new(),
rw_data: Arc::new(Mutex::new(
LayoutThreadData {
constellation_chan: constellation_chan,
@ -687,13 +710,46 @@ impl LayoutThread {
}
/// Receives and dispatches messages from other threads.
fn handle_request_helper<'a, 'b>(&mut self,
request: Msg,
possibly_locked_rw_data: &mut RwData<'a, 'b>)
-> bool {
fn handle_request_helper<'a, 'b>(
&mut self,
request: Msg,
possibly_locked_rw_data: &mut RwData<'a, 'b>,
) -> bool {
match request {
Msg::AddStylesheet(style_info) => {
self.handle_add_stylesheet(style_info, possibly_locked_rw_data)
Msg::AddStylesheet(stylesheet, before_stylesheet) => {
let guard = stylesheet.shared_lock.read();
self.handle_add_stylesheet(
&stylesheet,
&guard,
possibly_locked_rw_data,
);
match before_stylesheet {
Some(insertion_point) => {
self.stylesheets.insert_stylesheet_before(
Some(self.stylist.device()),
DocumentStyleSheet(stylesheet.clone()),
DocumentStyleSheet(insertion_point),
&guard,
)
}
None => {
self.stylesheets.append_stylesheet(
Some(self.stylist.device()),
DocumentStyleSheet(stylesheet.clone()),
&guard,
)
}
}
}
Msg::RemoveStylesheet(stylesheet) => {
let guard = stylesheet.shared_lock.read();
self.stylesheets.remove_stylesheet(
Some(self.stylist.device()),
DocumentStyleSheet(stylesheet.clone()),
&guard,
);
}
Msg::SetQuirksMode(mode) => self.handle_set_quirks_mode(mode),
Msg::GetRPC(response_chan) => {
@ -860,14 +916,16 @@ impl LayoutThread {
let _ = self.parallel_traversal.take();
}
fn handle_add_stylesheet<'a, 'b>(&self,
stylesheet: ServoArc<Stylesheet>,
possibly_locked_rw_data: &mut RwData<'a, 'b>) {
fn handle_add_stylesheet<'a, 'b>(
&self,
stylesheet: &ServoArc<Stylesheet>,
guard: &SharedRwLockReadGuard,
possibly_locked_rw_data: &mut RwData<'a, 'b>,
) {
// Find all font-face rules and notify the font cache of them.
// GWTODO: Need to handle unloading web fonts.
let rw_data = possibly_locked_rw_data.lock();
let guard = stylesheet.shared_lock.read();
if stylesheet.is_effective_for_device(self.stylist.device(), &guard) {
add_font_face_rules(&*stylesheet,
&guard,
@ -1157,7 +1215,7 @@ impl LayoutThread {
self.document_shared_lock = Some(document_shared_lock.clone());
let author_guard = document_shared_lock.read();
let device = Device::new(MediaType::screen(), initial_viewport, device_pixel_ratio);
self.stylist.set_device(device, &author_guard, &data.document_stylesheets);
self.stylist.set_device(device, &author_guard, self.stylesheets.iter());
self.viewport_size =
self.stylist.viewport_constraints().map_or(current_screen_size, |constraints| {
@ -1176,7 +1234,7 @@ impl LayoutThread {
.send(ConstellationMsg::ViewportConstrained(self.id, constraints.clone()))
.unwrap();
}
if data.document_stylesheets.iter().any(|sheet| sheet.dirty_on_viewport_size_change()) {
if self.stylesheets.iter().any(|sheet| sheet.0.dirty_on_viewport_size_change()) {
let mut iter = element.as_node().traverse_preorder();
let mut next = iter.next();
@ -1185,6 +1243,8 @@ impl LayoutThread {
let el = node.as_element().unwrap();
if let Some(mut d) = element.mutate_data() {
if d.has_styles() {
// FIXME(emilio): This only needs to recascade,
// afaict.
d.restyle.hint.insert(RestyleHint::restyle_subtree());
}
}
@ -1207,14 +1267,24 @@ impl LayoutThread {
author: &author_guard,
ua_or_user: &ua_or_user_guard,
};
let mut extra_data = Default::default();
let needs_dirtying = self.stylist.update(
StylesheetIterator(data.document_stylesheets.iter()),
&guards,
Some(ua_stylesheets),
data.stylesheets_changed,
/* author_styles_disabled = */ false,
&mut extra_data);
let needs_dirtying = {
let mut extra_data = Default::default();
let (iter, mut origins_dirty) = self.stylesheets.flush(Some(element));
if data.stylesheets_changed {
origins_dirty = OriginSet::all();
}
let origins_rebuilt = self.stylist.rebuild(
iter,
&guards,
Some(ua_stylesheets),
/* author_style_disabled = */ false,
&mut extra_data,
origins_dirty,
);
!origins_rebuilt.is_empty()
};
let needs_reflow = viewport_size_changed && !needs_dirtying;
if needs_dirtying {
if let Some(mut d) = element.mutate_data() {

View file

@ -102,7 +102,8 @@ use style::media_queries::MediaList;
use style::properties::PropertyDeclarationBlock;
use style::selector_parser::{PseudoElement, Snapshot};
use style::shared_lock::{SharedRwLock as StyleSharedRwLock, Locked as StyleLocked};
use style::stylesheets::{CssRules, FontFaceRule, KeyframesRule, MediaRule};
use style::stylesheet_set::StylesheetSet;
use style::stylesheets::{CssRules, FontFaceRule, KeyframesRule, MediaRule, Stylesheet};
use style::stylesheets::{NamespaceRule, StyleRule, ImportRule, SupportsRule, ViewportRule};
use style::stylesheets::keyframes_rule::Keyframe;
use style::values::specified::Length;
@ -374,6 +375,7 @@ unsafe_no_jsmanaged_fields!(AttrIdentifier);
unsafe_no_jsmanaged_fields!(AttrValue);
unsafe_no_jsmanaged_fields!(Snapshot);
unsafe_no_jsmanaged_fields!(PendingRestyle);
unsafe_no_jsmanaged_fields!(Stylesheet);
unsafe_no_jsmanaged_fields!(HttpsState);
unsafe_no_jsmanaged_fields!(Request);
unsafe_no_jsmanaged_fields!(RequestInit);
@ -641,6 +643,18 @@ unsafe impl JSTraceable for StyleLocked<MediaList> {
}
}
unsafe impl<S> JSTraceable for StylesheetSet<S>
where
S: JSTraceable + ::style::stylesheets::StylesheetInDocument + PartialEq + 'static,
{
unsafe fn trace(&self, tracer: *mut JSTracer) {
for s in self.iter() {
s.trace(tracer)
}
}
}
/// Holds a set of JSTraceables that need to be rooted
struct RootedTraceableSet {
set: Vec<*const JSTraceable>,

View file

@ -36,6 +36,7 @@ use dom::bindings::xmlname::{namespace_from_domstring, validate_and_extract, xml
use dom::bindings::xmlname::XMLName::InvalidXMLName;
use dom::closeevent::CloseEvent;
use dom::comment::Comment;
use dom::cssstylesheet::CSSStyleSheet;
use dom::customelementregistry::CustomElementDefinition;
use dom::customevent::CustomEvent;
use dom::documentfragment::DocumentFragment;
@ -63,6 +64,7 @@ use dom::htmlheadelement::HTMLHeadElement;
use dom::htmlhtmlelement::HTMLHtmlElement;
use dom::htmliframeelement::HTMLIFrameElement;
use dom::htmlimageelement::HTMLImageElement;
use dom::htmlmetaelement::HTMLMetaElement;
use dom::htmlscriptelement::{HTMLScriptElement, ScriptResult};
use dom::htmltitleelement::HTMLTitleElement;
use dom::keyboardevent::KeyboardEvent;
@ -135,10 +137,12 @@ use std::time::{Duration, Instant};
use style::attr::AttrValue;
use style::context::{QuirksMode, ReflowGoal};
use style::invalidation::element::restyle_hints::{RestyleHint, RESTYLE_SELF, RESTYLE_STYLE_ATTRIBUTE};
use style::media_queries::{Device, MediaList, MediaType};
use style::selector_parser::{RestyleDamage, Snapshot};
use style::shared_lock::SharedRwLock as StyleSharedRwLock;
use style::shared_lock::{SharedRwLock as StyleSharedRwLock, SharedRwLockReadGuard};
use style::str::{HTML_SPACE_CHARACTERS, split_html_space_chars, str_join};
use style::stylesheets::Stylesheet;
use style::stylesheet_set::StylesheetSet;
use style::stylesheets::{Stylesheet, StylesheetContents, OriginSet};
use task_source::TaskSource;
use time;
use timers::OneshotTimerCallback;
@ -166,14 +170,6 @@ pub enum IsHTMLDocument {
NonHTMLDocument,
}
#[derive(JSTraceable, HeapSizeOf)]
#[must_root]
pub struct StylesheetInDocument {
pub node: JS<Node>,
#[ignore_heap_size_of = "Arc"]
pub stylesheet: Arc<Stylesheet>,
}
#[derive(Debug, HeapSizeOf)]
pub struct PendingRestyle {
/// If this element had a state or attribute change since the last restyle, track
@ -197,6 +193,34 @@ impl PendingRestyle {
}
}
#[derive(Clone, JSTraceable, HeapSizeOf)]
#[must_root]
struct StyleSheetInDocument {
#[ignore_heap_size_of = "Arc"]
sheet: Arc<Stylesheet>,
owner: JS<Element>,
}
impl PartialEq for StyleSheetInDocument {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.sheet, &other.sheet)
}
}
impl ::style::stylesheets::StylesheetInDocument for StyleSheetInDocument {
fn contents(&self, guard: &SharedRwLockReadGuard) -> &StylesheetContents {
self.sheet.contents(guard)
}
fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
self.sheet.media(guard)
}
fn enabled(&self) -> bool {
self.sheet.enabled()
}
}
/// https://dom.spec.whatwg.org/#document
#[dom_struct]
pub struct Document {
@ -229,9 +253,7 @@ pub struct Document {
/// Can be acquired once for accessing many objects.
style_shared_lock: StyleSharedRwLock,
/// List of stylesheets associated with nodes in this document. |None| if the list needs to be refreshed.
stylesheets: DOMRefCell<Option<Vec<StylesheetInDocument>>>,
/// Whether the list of stylesheets has changed since the last reflow was triggered.
stylesheets_changed_since_reflow: Cell<bool>,
stylesheets: DOMRefCell<StylesheetSet<StyleSheetInDocument>>,
stylesheet_list: MutNullableJS<StyleSheetList>,
ready_state: Cell<DocumentReadyState>,
/// Whether the DOMContentLoaded event has already been dispatched.
@ -495,15 +517,12 @@ impl Document {
// FIXME: This should check the dirty bit on the document,
// not the document element. Needs some layout changes to make
// that workable.
self.stylesheets_changed_since_reflow.get() ||
match self.GetDocumentElement() {
Some(root) => {
root.upcast::<Node>().has_dirty_descendants() ||
!self.pending_restyles.borrow().is_empty() ||
self.needs_paint()
}
None => false,
}
self.stylesheets.borrow().has_changed() ||
self.GetDocumentElement().map_or(false, |root| {
root.upcast::<Node>().has_dirty_descendants() ||
!self.pending_restyles.borrow().is_empty() ||
self.needs_paint()
})
}
/// Returns the first `base` element in the DOM that has an `href` attribute.
@ -1501,20 +1520,16 @@ impl Document {
}
pub fn invalidate_stylesheets(&self) {
self.stylesheets_changed_since_reflow.set(true);
*self.stylesheets.borrow_mut() = None;
self.stylesheets.borrow_mut().force_dirty(OriginSet::all());
// Mark the document element dirty so a reflow will be performed.
//
// FIXME(emilio): Use the StylesheetSet invalidation stuff.
if let Some(element) = self.GetDocumentElement() {
element.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 trigger_mozbrowser_event(&self, event: MozBrowserEvent) {
if PREFS.is_mozbrowser_enabled() {
if let Some((parent_pipeline_id, _)) = self.window.parent_info() {
@ -2203,8 +2218,7 @@ impl Document {
PER_PROCESS_AUTHOR_SHARED_LOCK.clone()
//StyleSharedRwLock::new()
},
stylesheets: DOMRefCell::new(None),
stylesheets_changed_since_reflow: Cell::new(false),
stylesheets: DOMRefCell::new(StylesheetSet::new()),
stylesheet_list: MutNullableJS::new(None),
ready_state: Cell::new(ready_state),
domcontentloaded_dispatched: Cell::new(domcontentloaded_dispatched),
@ -2315,40 +2329,113 @@ impl Document {
self.GetDocumentElement().and_then(Root::downcast)
}
// Ensure that the stylesheets vector is populated
fn ensure_stylesheets(&self) {
let mut stylesheets = self.stylesheets.borrow_mut();
if stylesheets.is_none() {
*stylesheets = Some(self.upcast::<Node>()
.traverse_preorder()
.filter_map(|node| {
node.get_stylesheet()
.map(|stylesheet| StylesheetInDocument {
node: JS::from_ref(&*node),
stylesheet: stylesheet,
})
})
.collect());
};
}
/// Return a reference to the per-document shared lock used in stylesheets.
pub fn style_shared_lock(&self) -> &StyleSharedRwLock {
&self.style_shared_lock
}
/// Returns the list of stylesheets associated with nodes in the document.
pub fn stylesheets(&self) -> Vec<Arc<Stylesheet>> {
self.ensure_stylesheets();
self.stylesheets.borrow().as_ref().unwrap().iter()
.map(|s| s.stylesheet.clone())
.collect()
/// Flushes the stylesheet list, and returns whether any stylesheet changed.
pub fn flush_stylesheets_for_reflow(&self) -> bool {
// NOTE(emilio): The invalidation machinery is used on the replicated
// list on the layout thread.
let mut stylesheets = self.stylesheets.borrow_mut();
let have_changed = stylesheets.has_changed();
stylesheets.flush_without_invalidation();
have_changed
}
pub fn with_style_sheets_in_document<F, T>(&self, mut f: F) -> T
where F: FnMut(&[StylesheetInDocument]) -> T {
self.ensure_stylesheets();
f(&self.stylesheets.borrow().as_ref().unwrap())
/// Returns a `Device` suitable for media query evaluation.
///
/// FIXME(emilio): This really needs to be somehow more in sync with layout.
/// Feels like a hack.
///
/// Also, shouldn't return an option, I'm quite sure.
pub fn device(&self) -> Option<Device> {
let window_size = match self.window().window_size() {
Some(ws) => ws,
None => return None,
};
let viewport_size = window_size.initial_viewport;
let device_pixel_ratio = window_size.device_pixel_ratio;
Some(Device::new(MediaType::screen(), viewport_size, device_pixel_ratio))
}
/// Remove a stylesheet owned by `owner` from the list of document sheets.
#[allow(unrooted_must_root)] // Owner needs to be rooted already necessarily.
pub fn remove_stylesheet(&self, owner: &Element, s: &Arc<Stylesheet>) {
self.window()
.layout_chan()
.send(Msg::RemoveStylesheet(s.clone()))
.unwrap();
let guard = s.shared_lock.read();
let device = self.device();
// FIXME(emilio): Would be nice to remove the clone, etc.
self.stylesheets.borrow_mut().remove_stylesheet(
device.as_ref(),
StyleSheetInDocument {
sheet: s.clone(),
owner: JS::from_ref(owner),
},
&guard,
);
}
/// Add a stylesheet owned by `owner` to the list of document sheets, in the
/// correct tree position.
#[allow(unrooted_must_root)] // Owner needs to be rooted already necessarily.
pub fn add_stylesheet(&self, owner: &Element, sheet: Arc<Stylesheet>) {
// FIXME(emilio): It'd be nice to unify more code between the elements
// that own stylesheets, but StylesheetOwner is more about loading
// them...
debug_assert!(owner.as_stylesheet_owner().is_some() ||
owner.is::<HTMLMetaElement>(), "Wat");
let mut stylesheets = self.stylesheets.borrow_mut();
let insertion_point = stylesheets.iter().find(|sheet_in_doc| {
owner.upcast::<Node>().is_before(sheet_in_doc.owner.upcast())
}).cloned();
self.window()
.layout_chan()
.send(Msg::AddStylesheet(
sheet.clone(),
insertion_point.as_ref().map(|s| s.sheet.clone())
))
.unwrap();
let sheet = StyleSheetInDocument {
sheet,
owner: JS::from_ref(owner),
};
let lock = self.style_shared_lock();
let guard = lock.read();
let device = self.device();
match insertion_point {
Some(ip) => {
stylesheets.insert_stylesheet_before(device.as_ref(), sheet, ip, &guard);
}
None => {
stylesheets.append_stylesheet(device.as_ref(), sheet, &guard);
}
}
}
/// Returns the number of document stylesheets.
pub fn stylesheet_count(&self) -> usize {
self.stylesheets.borrow().len()
}
pub fn stylesheet_at(&self, index: usize) -> Option<Root<CSSStyleSheet>> {
let stylesheets = self.stylesheets.borrow();
stylesheets.get(index).and_then(|s| {
s.owner.upcast::<Node>().get_cssom_stylesheet()
})
}
/// https://html.spec.whatwg.org/multipage/#appropriate-template-contents-owner-document
@ -3684,8 +3771,7 @@ impl DocumentMethods for Document {
self.scripts.set(None);
self.anchors.set(None);
self.applets.set(None);
*self.stylesheets.borrow_mut() = None;
self.stylesheets_changed_since_reflow.set(true);
*self.stylesheets.borrow_mut() = StylesheetSet::new();
self.animation_frame_ident.set(0);
self.animation_frame_list.borrow_mut().clear();
self.pending_restyles.borrow_mut().clear();

View file

@ -24,7 +24,6 @@ use dom::virtualmethods::VirtualMethods;
use dom_struct::dom_struct;
use html5ever::{LocalName, Prefix};
use net_traits::ReferrerPolicy;
use script_layout_interface::message::Msg;
use script_traits::{MozBrowserEvent, ScriptMsg};
use servo_arc::Arc;
use std::ascii::AsciiExt;
@ -39,8 +38,6 @@ use style::stylesheets::{CssRuleType, Stylesheet};
use style_traits::PARSING_MODE_DEFAULT;
use stylesheet_loader::{StylesheetLoader, StylesheetContextSource, StylesheetOwner};
unsafe_no_jsmanaged_fields!(Stylesheet);
#[derive(JSTraceable, PartialEq, Clone, Copy, HeapSizeOf)]
pub struct RequestGenerationId(u32);
@ -98,10 +95,16 @@ impl HTMLLinkElement {
self.request_generation_id.get()
}
// FIXME(emilio): These methods are duplicated with
// HTMLStyleElement::set_stylesheet.
pub fn set_stylesheet(&self, s: Arc<Stylesheet>) {
let doc = document_from_node(self);
if let Some(ref s) = *self.stylesheet.borrow() {
doc.remove_stylesheet(self.upcast(), s)
}
*self.stylesheet.borrow_mut() = Some(s.clone());
window_from_node(self).layout_chan().send(Msg::AddStylesheet(s)).unwrap();
document_from_node(self).invalidate_stylesheets();
self.cssom_stylesheet.set(None);
doc.add_stylesheet(self.upcast(), s);
}
pub fn get_stylesheet(&self) -> Option<Arc<Stylesheet>> {
@ -239,8 +242,9 @@ impl VirtualMethods for HTMLLinkElement {
s.unbind_from_tree(context);
}
let document = document_from_node(self);
document.invalidate_stylesheets();
if let Some(ref s) = *self.stylesheet.borrow() {
document_from_node(self).remove_stylesheet(self.upcast(), s);
}
}
}

View file

@ -99,10 +99,10 @@ impl HTMLMetaElement {
let content = content.value();
if !content.is_empty() {
if let Some(translated_rule) = ViewportRule::from_meta(&**content) {
let document = self.upcast::<Node>().owner_doc();
let document = document_from_node(self);
let shared_lock = document.style_shared_lock();
let rule = CssRule::Viewport(Arc::new(shared_lock.wrap(translated_rule)));
*self.stylesheet.borrow_mut() = Some(Arc::new(Stylesheet {
let sheet = Arc::new(Stylesheet {
contents: StylesheetContents {
rules: CssRules::new(vec![rule], shared_lock),
origin: Origin::Author,
@ -118,9 +118,9 @@ impl HTMLMetaElement {
media: Arc::new(shared_lock.wrap(MediaList::empty())),
shared_lock: shared_lock.clone(),
disabled: AtomicBool::new(false),
}));
let doc = document_from_node(self);
doc.invalidate_stylesheets();
});
*self.stylesheet.borrow_mut() = Some(sheet.clone());
document.add_stylesheet(self.upcast(), sheet);
}
}
}
@ -199,6 +199,10 @@ impl VirtualMethods for HTMLMetaElement {
if context.tree_in_doc {
self.process_referrer_attribute();
if let Some(ref s) = *self.stylesheet.borrow() {
document_from_node(self).remove_stylesheet(self.upcast(), s);
}
}
}
}

View file

@ -20,7 +20,6 @@ use dom::virtualmethods::VirtualMethods;
use dom_struct::dom_struct;
use html5ever::{LocalName, Prefix};
use net_traits::ReferrerPolicy;
use script_layout_interface::message::Msg;
use servo_arc::Arc;
use std::cell::Cell;
use style::media_queries::parse_media_query_list;
@ -109,10 +108,18 @@ impl HTMLStyleElement {
self.upcast::<EventTarget>().fire_event(atom!("load"));
}
win.layout_chan().send(Msg::AddStylesheet(sheet.clone())).unwrap();
*self.stylesheet.borrow_mut() = Some(sheet);
self.set_stylesheet(sheet);
}
// FIXME(emilio): This is duplicated with HTMLLinkElement::set_stylesheet.
pub fn set_stylesheet(&self, s: Arc<Stylesheet>) {
let doc = document_from_node(self);
if let Some(ref s) = *self.stylesheet.borrow() {
doc.remove_stylesheet(self.upcast(), s)
}
*self.stylesheet.borrow_mut() = Some(s.clone());
self.cssom_stylesheet.set(None);
doc.invalidate_stylesheets();
doc.add_stylesheet(self.upcast(), s);
}
pub fn get_stylesheet(&self) -> Option<Arc<Stylesheet>> {
@ -180,8 +187,12 @@ impl VirtualMethods for HTMLStyleElement {
s.unbind_from_tree(context);
}
let doc = document_from_node(self);
doc.invalidate_stylesheets();
if context.tree_in_doc {
if let Some(ref s) = *self.stylesheet.borrow() {
let doc = document_from_node(self);
doc.remove_stylesheet(self.upcast(), s)
}
}
}
}

View file

@ -22,7 +22,7 @@ use dom_struct::dom_struct;
use js::jsapi::JSTracer;
use std::cell::Cell;
use std::rc::Rc;
use style::media_queries::{Device, MediaList, MediaType};
use style::media_queries::MediaList;
use style_traits::ToCss;
pub enum MediaQueryListMatchState {
@ -74,14 +74,9 @@ impl MediaQueryList {
}
pub fn evaluate(&self) -> bool {
if let Some(window_size) = self.document.window().window_size() {
let viewport_size = window_size.initial_viewport;
let device_pixel_ratio = window_size.device_pixel_ratio;
let device = Device::new(MediaType::screen(), viewport_size, device_pixel_ratio);
self.document.device().map_or(false, |device| {
self.media_query_list.evaluate(&device, self.document.quirks_mode())
} else {
false
}
})
}
}

View file

@ -36,18 +36,14 @@ impl StyleSheetList {
impl StyleSheetListMethods for StyleSheetList {
// https://drafts.csswg.org/cssom/#dom-stylesheetlist-length
fn Length(&self) -> u32 {
self.document.with_style_sheets_in_document(|s| s.len() as u32)
self.document.stylesheet_count() as u32
}
// https://drafts.csswg.org/cssom/#dom-stylesheetlist-item
fn Item(&self, index: u32) -> Option<Root<StyleSheet>> {
// XXXManishearth this doesn't handle the origin clean flag
// and is a cors vulnerability
self.document.with_style_sheets_in_document(|sheets| {
sheets.get(index as usize)
.and_then(|sheet| sheet.node.get_cssom_stylesheet())
.map(Root::upcast)
})
// XXXManishearth this doesn't handle the origin clean flag and is a
// cors vulnerability
self.document.stylesheet_at(index as usize).map(Root::upcast)
}
// check-tidy: no specs after this line

View file

@ -1240,7 +1240,8 @@ impl Window {
}
let document = self.Document();
let stylesheets_changed = document.get_and_reset_stylesheets_changed_since_reflow();
let stylesheets_changed = document.flush_stylesheets_for_reflow();
// Send new document and relevant styles to layout.
let reflow = ScriptReflow {
@ -1249,11 +1250,10 @@ impl Window {
page_clip_rect: self.page_clip_rect.get(),
},
document: self.Document().upcast::<Node>().to_trusted_node_address(),
document_stylesheets: document.stylesheets(),
stylesheets_changed: stylesheets_changed,
window_size: window_size,
stylesheets_changed,
window_size,
query_type,
script_join_chan: join_chan,
query_type: query_type,
dom_count: self.Document().dom_count(),
};

View file

@ -27,8 +27,13 @@ use style::stylesheets::Stylesheet;
/// Asynchronous messages that script can send to layout.
pub enum Msg {
/// Adds the given stylesheet to the document.
AddStylesheet(ServoArc<Stylesheet>),
/// Adds the given stylesheet to the document. The second stylesheet is the
/// insertion point (if it exists, the sheet needs to be inserted before
/// it).
AddStylesheet(ServoArc<Stylesheet>, Option<ServoArc<Stylesheet>>),
/// Removes a stylesheet from the document.
RemoveStylesheet(ServoArc<Stylesheet>),
/// Change the quirks mode.
SetQuirksMode(QuirksMode),
@ -137,8 +142,6 @@ pub struct ScriptReflow {
pub reflow_info: Reflow,
/// The document node.
pub document: TrustedNodeAddress,
/// The document's list of stylesheets.
pub document_stylesheets: Vec<ServoArc<Stylesheet>>,
/// Whether the document's stylesheets have changed since the last script reflow.
pub stylesheets_changed: bool,
/// The current window size.

View file

@ -11,16 +11,17 @@ use Atom;
use dom::{TElement, TNode};
use fnv::FnvHashSet;
use invalidation::element::restyle_hints::RestyleHint;
use media_queries::Device;
use selector_parser::SelectorImpl;
use selectors::attr::CaseSensitivity;
use selectors::parser::{Component, Selector};
use shared_lock::SharedRwLockReadGuard;
use stylesheets::{CssRule, StylesheetInDocument};
use stylist::Stylist;
/// An invalidation scope represents a kind of subtree that may need to be
/// restyled.
#[derive(Debug, Hash, Eq, PartialEq)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
enum InvalidationScope {
/// All the descendants of an element with a given id.
ID(Atom),
@ -54,6 +55,7 @@ impl InvalidationScope {
///
/// TODO(emilio): We might be able to do the same analysis for removals and
/// media query changes too?
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct StylesheetInvalidationSet {
/// The style scopes we know we have to restyle so far.
invalid_scopes: FnvHashSet<InvalidationScope>,
@ -82,7 +84,7 @@ impl StylesheetInvalidationSet {
/// next time.
pub fn collect_invalidations_for<S>(
&mut self,
stylist: &Stylist,
device: &Device,
stylesheet: &S,
guard: &SharedRwLockReadGuard
)
@ -96,12 +98,12 @@ impl StylesheetInvalidationSet {
}
if !stylesheet.enabled() ||
!stylesheet.is_effective_for_device(stylist.device(), guard) {
!stylesheet.is_effective_for_device(device, guard) {
debug!(" > Stylesheet was not effective");
return; // Nothing to do here.
}
for rule in stylesheet.effective_rules(stylist.device(), guard) {
for rule in stylesheet.effective_rules(device, guard) {
self.collect_invalidations_for_rule(rule, guard);
if self.fully_invalid {
self.invalid_scopes.clear();
@ -121,6 +123,11 @@ impl StylesheetInvalidationSet {
if let Some(e) = document_element {
self.process_invalidations(e);
}
self.clear();
}
/// Clears the invalidation set without processing.
pub fn clear(&mut self) {
self.invalid_scopes.clear();
self.fully_invalid = false;
}

View file

@ -6,13 +6,14 @@
use dom::TElement;
use invalidation::stylesheets::StylesheetInvalidationSet;
use media_queries::Device;
use shared_lock::SharedRwLockReadGuard;
use std::slice;
use stylesheets::{OriginSet, PerOrigin, StylesheetInDocument};
use stylist::Stylist;
/// Entry for a StylesheetSet. We don't bother creating a constructor, because
/// there's no sensible defaults for the member variables.
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct StylesheetSetEntry<S>
where
S: StylesheetInDocument + PartialEq + 'static,
@ -38,6 +39,7 @@ where
}
/// The set of stylesheets effective for a given document.
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct StylesheetSet<S>
where
S: StylesheetInDocument + PartialEq + 'static,
@ -69,6 +71,16 @@ where
}
}
/// Returns the number of stylesheets in the set.
pub fn len(&self) -> usize {
self.entries.len()
}
/// Returns the number of stylesheets in the set.
pub fn get(&self, index: usize) -> Option<&S> {
self.entries.get(index).map(|s| &s.sheet)
}
/// Returns whether author styles have been disabled for the current
/// stylesheet set.
pub fn author_style_disabled(&self) -> bool {
@ -81,46 +93,57 @@ where
fn collect_invalidations_for(
&mut self,
stylist: &Stylist,
device: Option<&Device>,
sheet: &S,
guard: &SharedRwLockReadGuard,
) {
let origin = sheet.contents(guard).origin;
let data = self.invalidation_data.borrow_mut_for_origin(&origin);
data.invalidations.collect_invalidations_for(stylist, sheet, guard);
if let Some(device) = device {
data.invalidations.collect_invalidations_for(device, sheet, guard);
}
data.dirty = true;
}
/// Appends a new stylesheet to the current set.
///
/// FIXME(emilio): `device` shouldn't be optional, but a bunch of work needs
/// to happen to make the invalidations work properly in servo.
pub fn append_stylesheet(
&mut self,
stylist: &Stylist,
device: Option<&Device>,
sheet: S,
guard: &SharedRwLockReadGuard
) {
debug!("StylesheetSet::append_stylesheet");
self.remove_stylesheet_if_present(&sheet);
self.collect_invalidations_for(stylist, &sheet, guard);
self.collect_invalidations_for(device, &sheet, guard);
self.entries.push(StylesheetSetEntry { sheet });
}
/// Prepend a new stylesheet to the current set.
///
/// FIXME(emilio): `device` shouldn't be optional, but a bunch of work needs
/// to happen to make the invalidations work properly in servo.
pub fn prepend_stylesheet(
&mut self,
stylist: &Stylist,
device: Option<&Device>,
sheet: S,
guard: &SharedRwLockReadGuard
) {
debug!("StylesheetSet::prepend_stylesheet");
self.remove_stylesheet_if_present(&sheet);
self.collect_invalidations_for(stylist, &sheet, guard);
self.collect_invalidations_for(device, &sheet, guard);
self.entries.insert(0, StylesheetSetEntry { sheet });
}
/// Insert a given stylesheet before another stylesheet in the document.
///
/// FIXME(emilio): `device` shouldn't be optional, but a bunch of work needs
/// to happen to make the invalidations work properly in servo.
pub fn insert_stylesheet_before(
&mut self,
stylist: &Stylist,
device: Option<&Device>,
sheet: S,
before_sheet: S,
guard: &SharedRwLockReadGuard
@ -130,20 +153,23 @@ where
let index = self.entries.iter().position(|entry| {
entry.sheet == before_sheet
}).expect("`before_sheet` stylesheet not found");
self.collect_invalidations_for(stylist, &sheet, guard);
self.collect_invalidations_for(device, &sheet, guard);
self.entries.insert(index, StylesheetSetEntry { sheet });
}
/// Remove a given stylesheet from the set.
///
/// FIXME(emilio): `device` shouldn't be optional, but a bunch of work needs
/// to happen to make the invalidations work properly in servo.
pub fn remove_stylesheet(
&mut self,
stylist: &Stylist,
device: Option<&Device>,
sheet: S,
guard: &SharedRwLockReadGuard,
) {
debug!("StylesheetSet::remove_stylesheet");
self.remove_stylesheet_if_present(&sheet);
self.collect_invalidations_for(stylist, &sheet, guard);
self.collect_invalidations_for(device, &sheet, guard);
}
/// Notes that the author style has been disabled for this document.
@ -174,7 +200,6 @@ where
E: TElement,
{
debug!("StylesheetSet::flush");
debug_assert!(self.has_changed());
let mut origins = OriginSet::empty();
for (data, origin) in self.invalidation_data.iter_mut_origins() {
@ -188,6 +213,26 @@ where
(self.iter(), origins)
}
/// Flush stylesheets, but without running any of the invalidation passes.
///
/// FIXME(emilio): This should eventually disappear. Please keep this
/// Servo-only.
#[cfg(feature = "servo")]
pub fn flush_without_invalidation(&mut self) -> (StylesheetIterator<S>, OriginSet) {
debug!("StylesheetSet::flush_without_invalidation");
let mut origins = OriginSet::empty();
for (data, origin) in self.invalidation_data.iter_mut_origins() {
if data.dirty {
data.invalidations.clear();
data.dirty = false;
origins |= origin;
}
}
(self.iter(), origins)
}
/// Returns an iterator over the current list of stylesheets.
pub fn iter(&self) -> StylesheetIterator<S> {
StylesheetIterator(self.entries.iter())
@ -204,6 +249,7 @@ where
}
}
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
struct InvalidationData {
/// The stylesheet invalidations for this origin that we still haven't
/// processed.

View file

@ -196,16 +196,19 @@ impl Stylist {
author_style_disabled: bool,
extra_data: &mut PerOrigin<ExtraStyleData>,
mut origins_to_rebuild: OriginSet,
) -> bool
) -> OriginSet
where
I: Iterator<Item = &'a S> + Clone,
S: StylesheetInDocument + ToMediaListKey + 'static,
{
debug_assert!(!origins_to_rebuild.is_empty() || self.is_device_dirty);
if self.is_device_dirty {
origins_to_rebuild = OriginSet::all();
}
if origins_to_rebuild.is_empty() {
return origins_to_rebuild;
}
self.num_rebuilds += 1;
// Update viewport_constraints regardless of which origins'
@ -279,37 +282,7 @@ impl Stylist {
}
self.is_device_dirty = false;
true
}
/// clear the stylist and then rebuild it. Chances are, you want to use
/// either clear() or rebuild(), with the latter done lazily, instead.
pub fn update<'a, I, S>(
&mut self,
doc_stylesheets: I,
guards: &StylesheetGuards,
ua_stylesheets: Option<&UserAgentStylesheets>,
stylesheets_changed: bool,
author_style_disabled: bool,
extra_data: &mut PerOrigin<ExtraStyleData>
) -> bool
where
I: Iterator<Item = &'a S> + Clone,
S: StylesheetInDocument + ToMediaListKey + 'static,
{
// We have to do a dirtiness check before clearing, because if
// we're not actually dirty we need to no-op here.
if !(self.is_device_dirty || stylesheets_changed) {
return false;
}
self.rebuild(
doc_stylesheets,
guards,
ua_stylesheets,
author_style_disabled,
extra_data,
OriginSet::all(),
)
origins_to_rebuild
}
fn add_stylesheet<S>(
@ -876,13 +849,19 @@ impl Stylist {
/// FIXME(emilio): The semantics of the device for Servo and Gecko are
/// different enough we may want to unify them.
#[cfg(feature = "servo")]
pub fn set_device(&mut self,
mut device: Device,
guard: &SharedRwLockReadGuard,
stylesheets: &[Arc<::stylesheets::Stylesheet>]) {
pub fn set_device<'a, I, S>(
&mut self,
mut device: Device,
guard: &SharedRwLockReadGuard,
stylesheets: I,
)
where
I: Iterator<Item = &'a S> + Clone,
S: StylesheetInDocument + ToMediaListKey + 'static,
{
let cascaded_rule = ViewportRule {
declarations: viewport_rule::Cascade::from_stylesheets(
stylesheets.iter().map(|s| &**s),
stylesheets.clone(),
guard,
&device
).finish(),
@ -897,7 +876,7 @@ impl Stylist {
self.device = device;
let features_changed = self.media_features_change_changed_style(
stylesheets.iter().map(|s| &**s),
stylesheets,
guard
);
self.is_device_dirty |= !features_changed.is_empty();

View file

@ -891,7 +891,7 @@ pub extern "C" fn Servo_StyleSet_AppendStyleSheet(
let data = &mut *data;
let guard = global_style_data.shared_lock.read();
let sheet = unsafe { GeckoStyleSheet::new(sheet) };
data.stylesheets.append_stylesheet(&data.stylist, sheet, &guard);
data.stylesheets.append_stylesheet(Some(data.stylist.device()), sheet, &guard);
}
#[no_mangle]
@ -940,7 +940,7 @@ pub extern "C" fn Servo_StyleSet_PrependStyleSheet(
let data = &mut *data;
let guard = global_style_data.shared_lock.read();
let sheet = unsafe { GeckoStyleSheet::new(sheet) };
data.stylesheets.prepend_stylesheet(&data.stylist, sheet, &guard);
data.stylesheets.prepend_stylesheet(Some(data.stylist.device()), sheet, &guard);
}
#[no_mangle]
@ -955,7 +955,7 @@ pub extern "C" fn Servo_StyleSet_InsertStyleSheetBefore(
let guard = global_style_data.shared_lock.read();
let sheet = unsafe { GeckoStyleSheet::new(sheet) };
data.stylesheets.insert_stylesheet_before(
&data.stylist,
Some(data.stylist.device()),
sheet,
unsafe { GeckoStyleSheet::new(before_sheet) },
&guard,
@ -972,7 +972,7 @@ pub extern "C" fn Servo_StyleSet_RemoveStyleSheet(
let data = &mut *data;
let guard = global_style_data.shared_lock.read();
let sheet = unsafe { GeckoStyleSheet::new(sheet) };
data.stylesheets.remove_stylesheet(&data.stylist, sheet, &guard);
data.stylesheets.remove_stylesheet(Some(data.stylist.device()), sheet, &guard);
}
#[no_mangle]