Files
tubestation/servo/components/layout/context.rs
Simon Sapin 1f820224e4 servo: Merge #12515 - Make the style crate more concrete (from servo:concrete-style); r=bholley
Background:

The changes to Servo code to support Stylo began in the `selectors` crate with making pseudo-elements generic, defined be the user, so that different users (such as Servo and Gecko/Stylo) could have a different set of pseudo-elements supported and parsed. Adding a trait makes sense there since `selectors` is in its own repository and has others users (or at least [one](https://github.com/SimonSapin/kuchiki)).

Then we kind of kept going with the same pattern and added a bunch of traits in the `style` crate to make everything generic, allowing Servo and Gecko/Stylo to do things differently. But we’ve also added a `gecko` Cargo feature to do conditional compilation, at first to enable or disable some CSS properties and values in the Mako templates. Since we’re doing conditional compilation anyway, it’s often easier and simpler to do it more (with `#[cfg(feature = "gecko")]` and `#[cfg(feature = "servo")]`) that to keep adding traits and making everything generic. When a type is generic, any method that we want to call on it needs to be part of some trait.

----

The first several commits move some code around, mostly from `geckolib` to `style` (with `#[cfg(feature = "gecko")]`) but otherwise don’t change much.

The following commits remove some traits and many type parameters through the `style` crate, replacing them with pairs of conditionally-compiled API-compatible items (types, methods, …).

Simplifying code is nice to make it more maintainable, but this is motivated by another change described in https://github.com/servo/servo/pull/12391#issuecomment-232183942. (Porting Servo for that change proved difficult because some code in the `style` crate was becoming generic over `String` vs `Atom`, and this PR will help make that concrete. That change, in turn, is motivated by removing geckolib’s `[replace]` override for string-cache, in order to enable using a single Cargo "workspace" in this repository.)

r? @bholley

---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: -->
- [x] `./mach build -d` does not report any errors
- [x] `./mach test-tidy` does not report any errors
- [x] These changes fix #__ (github issue number if applicable).

<!-- Either: -->
- [ ] There are tests for these changes OR
- [x] These changes do not require new tests because refactoring

<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->

Source-Repo: https://github.com/servo/servo
Source-Revision: 2d01d41a506bcbc7f26a2284b9f42390d6ef96ab
2016-07-20 02:58:34 -05:00

234 lines
9.3 KiB
Rust

/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
//! Data needed by the layout thread.
// for thread_local
#![allow(unsafe_code)]
use app_units::Au;
use euclid::Rect;
use fnv::FnvHasher;
use gfx::display_list::WebRenderImageInfo;
use gfx::font_cache_thread::FontCacheThread;
use gfx::font_context::FontContext;
use gfx_traits::LayerId;
use heapsize::HeapSizeOf;
use ipc_channel::ipc::{self, IpcSharedMemory};
use net_traits::image::base::Image;
use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheThread, ImageResponse, ImageState};
use net_traits::image_cache_thread::{ImageOrMetadataAvailable, UsePlaceholder};
use std::cell::{RefCell, RefMut};
use std::collections::HashMap;
use std::hash::BuildHasherDefault;
use std::rc::Rc;
use std::sync::{Arc, Mutex, RwLock};
use style::context::{LocalStyleContext, StyleContext, SharedStyleContext};
use url::Url;
use util::opts;
struct LocalLayoutContext {
style_context: LocalStyleContext,
font_context: RefCell<FontContext>,
}
impl HeapSizeOf for LocalLayoutContext {
// FIXME(njn): measure other fields eventually.
fn heap_size_of_children(&self) -> usize {
self.font_context.heap_size_of_children()
}
}
thread_local!(static LOCAL_CONTEXT_KEY: RefCell<Option<Rc<LocalLayoutContext>>> = RefCell::new(None));
pub fn heap_size_of_local_context() -> usize {
LOCAL_CONTEXT_KEY.with(|r| {
r.borrow().clone().map_or(0, |context| context.heap_size_of_children())
})
}
// Keep this implementation in sync with the one in ports/geckolib/traversal.rs.
fn create_or_get_local_context(shared_layout_context: &SharedLayoutContext)
-> Rc<LocalLayoutContext> {
LOCAL_CONTEXT_KEY.with(|r| {
let mut r = r.borrow_mut();
if let Some(context) = r.clone() {
if shared_layout_context.style_context.screen_size_changed {
context.style_context.applicable_declarations_cache.borrow_mut().evict_all();
}
context
} else {
let font_cache_thread = shared_layout_context.font_cache_thread.lock().unwrap().clone();
let local_style_data = shared_layout_context.style_context.local_context_creation_data.lock().unwrap();
let context = Rc::new(LocalLayoutContext {
style_context: LocalStyleContext::new(&local_style_data),
font_context: RefCell::new(FontContext::new(font_cache_thread)),
});
*r = Some(context.clone());
context
}
})
}
/// Layout information shared among all workers. This must be thread-safe.
pub struct SharedLayoutContext {
/// Bits shared by the layout and style system.
pub style_context: SharedStyleContext,
/// The shared image cache thread.
pub image_cache_thread: ImageCacheThread,
/// A channel for the image cache to send responses to.
pub image_cache_sender: Mutex<ImageCacheChan>,
/// Interface to the font cache thread.
pub font_cache_thread: Mutex<FontCacheThread>,
/// The visible rects for each layer, as reported to us by the compositor.
pub visible_rects: Arc<HashMap<LayerId, Rect<Au>, BuildHasherDefault<FnvHasher>>>,
/// A cache of WebRender image info.
pub webrender_image_cache: Arc<RwLock<HashMap<(Url, UsePlaceholder),
WebRenderImageInfo,
BuildHasherDefault<FnvHasher>>>>,
}
pub struct LayoutContext<'a> {
pub shared: &'a SharedLayoutContext,
cached_local_layout_context: Rc<LocalLayoutContext>,
}
impl<'a> StyleContext<'a> for LayoutContext<'a> {
fn shared_context(&self) -> &'a SharedStyleContext {
&self.shared.style_context
}
fn local_context(&self) -> &LocalStyleContext {
&self.cached_local_layout_context.style_context
}
}
impl<'a> LayoutContext<'a> {
pub fn new(shared_layout_context: &'a SharedLayoutContext) -> LayoutContext<'a> {
let local_context = create_or_get_local_context(shared_layout_context);
LayoutContext {
shared: shared_layout_context,
cached_local_layout_context: local_context,
}
}
#[inline(always)]
pub fn style_context(&self) -> &SharedStyleContext {
&self.shared.style_context
}
#[inline(always)]
pub fn font_context(&self) -> RefMut<FontContext> {
self.cached_local_layout_context.font_context.borrow_mut()
}
fn get_or_request_image_synchronously(&self, url: Url, use_placeholder: UsePlaceholder)
-> Option<Arc<Image>> {
debug_assert!(opts::get().output_file.is_some() || opts::get().exit_after_load);
// See if the image is already available
let result = self.shared.image_cache_thread.find_image(url.clone(), use_placeholder);
match result {
Ok(image) => return Some(image),
Err(ImageState::LoadError) => {
// Image failed to load, so just return nothing
return None
}
Err(_) => {}
}
// If we are emitting an output file, then we need to block on
// image load or we risk emitting an output file missing the image.
let (sync_tx, sync_rx) = ipc::channel().unwrap();
self.shared.image_cache_thread.request_image(url, ImageCacheChan(sync_tx), None);
loop {
match sync_rx.recv() {
Err(_) => return None,
Ok(response) => {
match response.image_response {
ImageResponse::Loaded(image) | ImageResponse::PlaceholderLoaded(image) => {
return Some(image)
}
ImageResponse::None | ImageResponse::MetadataLoaded(_) => {}
}
}
}
}
}
pub fn get_or_request_image_or_meta(&self, url: Url, use_placeholder: UsePlaceholder)
-> Option<ImageOrMetadataAvailable> {
// If we are emitting an output file, load the image synchronously.
if opts::get().output_file.is_some() || opts::get().exit_after_load {
return self.get_or_request_image_synchronously(url, use_placeholder)
.map(|img| ImageOrMetadataAvailable::ImageAvailable(img));
}
// See if the image is already available
let result = self.shared.image_cache_thread.find_image_or_metadata(url.clone(),
use_placeholder);
match result {
Ok(image_or_metadata) => Some(image_or_metadata),
// Image failed to load, so just return nothing
Err(ImageState::LoadError) => None,
// Not yet requested, async mode - request image or metadata from the cache
Err(ImageState::NotRequested) => {
let sender = self.shared.image_cache_sender.lock().unwrap().clone();
self.shared.image_cache_thread.request_image_and_metadata(url, sender, None);
None
}
// Image has been requested, is still pending. Return no image for this paint loop.
// When the image loads it will trigger a reflow and/or repaint.
Err(ImageState::Pending) => None,
}
}
pub fn get_webrender_image_for_url(&self,
url: &Url,
use_placeholder: UsePlaceholder,
fetch_image_data_as_well: bool)
-> Option<(WebRenderImageInfo, Option<IpcSharedMemory>)> {
if !fetch_image_data_as_well {
let webrender_image_cache = self.shared.webrender_image_cache.read().unwrap();
if let Some(existing_webrender_image) =
webrender_image_cache.get(&((*url).clone(), use_placeholder)) {
return Some(((*existing_webrender_image).clone(), None))
}
}
match self.get_or_request_image_or_meta((*url).clone(), use_placeholder) {
Some(ImageOrMetadataAvailable::ImageAvailable(image)) => {
let image_info = WebRenderImageInfo::from_image(&*image);
if image_info.key.is_none() {
let bytes = if !fetch_image_data_as_well {
None
} else {
Some(image.bytes.clone())
};
Some((image_info, bytes))
} else if !fetch_image_data_as_well {
let mut webrender_image_cache = self.shared
.webrender_image_cache
.write()
.unwrap();
webrender_image_cache.insert(((*url).clone(), use_placeholder),
image_info);
Some((image_info, None))
} else {
Some((image_info, Some(image.bytes.clone())))
}
}
None | Some(ImageOrMetadataAvailable::MetadataAvailable(_)) => None,
}
}
}