Move most of geckolib into style::gecko

This commit is contained in:
Manish Goregaokar 2016-09-22 16:14:19 +05:30
parent bc9cbc87ba
commit b2e592b121
27 changed files with 167 additions and 157 deletions

2
ports/cef/Cargo.lock generated
View file

@ -2155,6 +2155,7 @@ version = "0.0.1"
dependencies = [
"app_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"encoding 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2169,6 +2170,7 @@ dependencies = [
"matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"num-integer 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
"ordered-float 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"plugins 0.0.1",
"quickersort 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",

View file

@ -351,12 +351,12 @@ dependencies = [
"matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"num-integer 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
"ordered-float 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"quickersort 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"selectors 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 0.8.8 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
"style_traits 0.0.1",
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",

View file

@ -1,51 +0,0 @@
/* 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/. */
use std::cell::RefCell;
use std::rc::Rc;
use style::context::{LocalStyleContext, StyleContext, SharedStyleContext};
thread_local!(static LOCAL_CONTEXT_KEY: RefCell<Option<Rc<LocalStyleContext>>> = RefCell::new(None));
// Keep this implementation in sync with the one in components/layout/context.rs.
fn create_or_get_local_context(shared: &SharedStyleContext) -> Rc<LocalStyleContext> {
LOCAL_CONTEXT_KEY.with(|r| {
let mut r = r.borrow_mut();
if let Some(context) = r.clone() {
if shared.screen_size_changed {
context.applicable_declarations_cache.borrow_mut().evict_all();
}
context
} else {
let context = Rc::new(LocalStyleContext::new(&shared.local_context_creation_data.lock().unwrap()));
*r = Some(context.clone());
context
}
})
}
pub struct StandaloneStyleContext<'a> {
pub shared: &'a SharedStyleContext,
cached_local_context: Rc<LocalStyleContext>,
}
impl<'a> StandaloneStyleContext<'a> {
pub fn new(shared: &'a SharedStyleContext) -> Self {
let local_context = create_or_get_local_context(shared);
StandaloneStyleContext {
shared: shared,
cached_local_context: local_context,
}
}
}
impl<'a> StyleContext<'a> for StandaloneStyleContext<'a> {
fn shared_context(&self) -> &'a SharedStyleContext {
&self.shared
}
fn local_context(&self) -> &LocalStyleContext {
&self.cached_local_context
}
}

View file

@ -1,105 +0,0 @@
/* 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/. */
use euclid::size::TypedSize2D;
use style::gecko_bindings::bindings::RawServoStyleSet;
use style::gecko_bindings::sugar::ownership::{HasBoxFFI, HasFFI, HasSimpleFFI};
use num_cpus;
use std::cmp;
use std::collections::HashMap;
use std::env;
use std::sync::{Arc, RwLock};
use std::sync::mpsc::{Receiver, Sender, channel};
use style::animation::Animation;
use style::context::SharedStyleContext;
use style::dom::OpaqueNode;
use style::media_queries::{Device, MediaType};
use style::parallel::WorkQueueData;
use style::selector_matching::Stylist;
use style::stylesheets::Stylesheet;
use style::thread_state;
use style::workqueue::WorkQueue;
use style_traits::ViewportPx;
pub struct PerDocumentStyleData {
/// Rule processor.
pub stylist: Arc<Stylist>,
/// List of stylesheets, mirrored from Gecko.
pub stylesheets: Vec<Arc<Stylesheet>>,
/// Whether the stylesheets list above has changed since the last restyle.
pub stylesheets_changed: bool,
// FIXME(bholley): Hook these up to something.
pub new_animations_sender: Sender<Animation>,
pub new_animations_receiver: Receiver<Animation>,
pub running_animations: Arc<RwLock<HashMap<OpaqueNode, Vec<Animation>>>>,
pub expired_animations: Arc<RwLock<HashMap<OpaqueNode, Vec<Animation>>>>,
// FIXME(bholley): This shouldn't be per-document.
pub work_queue: Option<WorkQueue<SharedStyleContext, WorkQueueData>>,
pub num_threads: usize,
}
lazy_static! {
pub static ref NUM_THREADS: usize = {
match env::var("STYLO_THREADS").map(|s| s.parse::<usize>().expect("invalid STYLO_THREADS")) {
Ok(num) => num,
_ => cmp::max(num_cpus::get() * 3 / 4, 1),
}
};
}
impl PerDocumentStyleData {
pub fn new() -> PerDocumentStyleData {
// FIXME(bholley): Real window size.
let window_size: TypedSize2D<f32, ViewportPx> = TypedSize2D::new(800.0, 600.0);
let device = Device::new(MediaType::Screen, window_size);
let (new_anims_sender, new_anims_receiver) = channel();
PerDocumentStyleData {
stylist: Arc::new(Stylist::new(device)),
stylesheets: vec![],
stylesheets_changed: true,
new_animations_sender: new_anims_sender,
new_animations_receiver: new_anims_receiver,
running_animations: Arc::new(RwLock::new(HashMap::new())),
expired_animations: Arc::new(RwLock::new(HashMap::new())),
work_queue: if *NUM_THREADS <= 1 {
None
} else {
WorkQueue::new("StyleWorker", thread_state::LAYOUT, *NUM_THREADS).ok()
},
num_threads: *NUM_THREADS,
}
}
pub fn flush_stylesheets(&mut self) {
// The stylist wants to be flushed if either the stylesheets change or the
// device dimensions change. When we add support for media queries, we'll
// need to detect the latter case and trigger a flush as well.
if self.stylesheets_changed {
let _ = Arc::get_mut(&mut self.stylist).unwrap()
.update(&self.stylesheets, None, true);
self.stylesheets_changed = false;
}
}
}
unsafe impl HasFFI for PerDocumentStyleData {
type FFIType = RawServoStyleSet;
}
unsafe impl HasSimpleFFI for PerDocumentStyleData {}
unsafe impl HasBoxFFI for PerDocumentStyleData {}
impl Drop for PerDocumentStyleData {
fn drop(&mut self) {
if let Some(ref mut queue) = self.work_queue {
queue.shutdown();
}
}
}

View file

@ -5,25 +5,8 @@
#![allow(unsafe_code)]
use app_units::Au;
use data::{NUM_THREADS, PerDocumentStyleData};
use env_logger;
use euclid::Size2D;
use style::gecko_bindings::bindings::{RawGeckoElementBorrowed, RawGeckoNodeBorrowed};
use style::gecko_bindings::bindings::{RawServoStyleSetBorrowed, RawServoStyleSetOwned};
use style::gecko_bindings::bindings::{RawServoStyleSetBorrowedMut, RawGeckoDocumentBorrowed};
use style::gecko_bindings::bindings::{RawServoStyleSheetBorrowed, ServoComputedValuesBorrowed};
use style::gecko_bindings::bindings::{RawServoStyleSheetStrong, ServoComputedValuesStrong};
use style::gecko_bindings::bindings::{ServoComputedValuesBorrowedOrNull, ServoDeclarationBlock};
use style::gecko_bindings::bindings::{ServoDeclarationBlockBorrowed, ServoDeclarationBlockStrong};
use style::gecko_bindings::bindings::{ThreadSafePrincipalHolder, ThreadSafeURIHolder, nsHTMLCSSStyleSheet};
use style::gecko_bindings::ptr::{GeckoArcPrincipal, GeckoArcURI};
use style::gecko_bindings::structs::{SheetParsingMode, nsIAtom};
use style::gecko_bindings::structs::ServoElementSnapshot;
use style::gecko_bindings::structs::nsRestyleHint;
use style::gecko_bindings::sugar::ownership::{FFIArcHelpers, HasArcFFI, HasBoxFFI};
use style::gecko_bindings::sugar::ownership::{HasFFI, HasSimpleFFI, Strong};
use style::gecko_string_cache::Atom;
use snapshot::GeckoElementSnapshot;
use std::mem::transmute;
use std::ptr;
use std::slice;
@ -34,17 +17,36 @@ use style::arc_ptr_eq;
use style::context::{LocalStyleContextCreationInfo, ReflowGoal, SharedStyleContext};
use style::dom::{NodeInfo, TDocument, TElement, TNode};
use style::error_reporting::StdoutErrorReporter;
use style::gecko::data::{NUM_THREADS, PerDocumentStyleData};
use style::gecko::selector_impl::{GeckoSelectorImpl, PseudoElement};
use style::gecko::snapshot::GeckoElementSnapshot;
use style::gecko::traversal::RecalcStyleOnly;
use style::gecko::wrapper::{DUMMY_BASE_URL, GeckoDeclarationBlock, GeckoDocument};
use style::gecko::wrapper::{GeckoElement, GeckoNode};
use style::gecko_bindings::bindings::{RawGeckoElementBorrowed, RawGeckoNodeBorrowed};
use style::gecko_bindings::bindings::{RawServoStyleSetBorrowed, RawServoStyleSetOwned};
use style::gecko_bindings::bindings::{RawServoStyleSetBorrowedMut, RawGeckoDocumentBorrowed};
use style::gecko_bindings::bindings::{RawServoStyleSheetBorrowed, ServoComputedValuesBorrowed};
use style::gecko_bindings::bindings::{RawServoStyleSheetStrong, ServoComputedValuesStrong};
use style::gecko_bindings::bindings::{ServoDeclarationBlockBorrowed, ServoDeclarationBlockStrong};
use style::gecko_bindings::bindings::{ThreadSafePrincipalHolder, ThreadSafeURIHolder};
use style::gecko_bindings::bindings::{nsHTMLCSSStyleSheet, ServoComputedValuesBorrowedOrNull};
use style::gecko_bindings::ptr::{GeckoArcPrincipal, GeckoArcURI};
use style::gecko_bindings::structs::{SheetParsingMode, nsIAtom};
use style::gecko_bindings::structs::ServoElementSnapshot;
use style::gecko_bindings::structs::nsRestyleHint;
use style::gecko_bindings::sugar::ownership::{FFIArcHelpers, HasArcFFI, HasBoxFFI};
use style::gecko_bindings::sugar::ownership::{HasSimpleFFI, Strong};
use style::parallel;
use style::parser::ParserContextExtraData;
use style::properties::{ComputedValues, PropertyDeclarationBlock, parse_one_declaration};
use style::properties::{ComputedValues, parse_one_declaration};
use style::selector_impl::PseudoElementCascadeType;
use style::sequential;
use style::string_cache::Atom;
use style::stylesheets::{Origin, Stylesheet};
use style::gecko::selector_impl::{GeckoSelectorImpl, PseudoElement};
use style::timer::Timer;
use traversal::RecalcStyleOnly;
use url::Url;
use wrapper::{DUMMY_BASE_URL, GeckoDocument, GeckoElement, GeckoNode};
/*
* For Gecko->Servo function calls, we need to redeclare the same signature that was declared in
@ -337,23 +339,6 @@ pub extern "C" fn Servo_StyleSet_Drop(data: RawServoStyleSetOwned) -> () {
let _ = data.into_box::<PerDocumentStyleData>();
}
pub struct GeckoDeclarationBlock {
pub declarations: Option<Arc<PropertyDeclarationBlock>>,
// XXX The following two fields are made atomic to work around the
// ownership system so that they can be changed inside a shared
// instance. It wouldn't provide safety as Rust usually promises,
// but it is fine as far as we only access them in a single thread.
// If we need to access them in different threads, we would need
// to redesign how it works with MiscContainer in Gecko side.
pub cache: AtomicPtr<nsHTMLCSSStyleSheet>,
pub immutable: AtomicBool,
}
unsafe impl HasFFI for GeckoDeclarationBlock {
type FFIType = ServoDeclarationBlock;
}
unsafe impl HasArcFFI for GeckoDeclarationBlock {}
#[no_mangle]
pub extern "C" fn Servo_ParseStyleAttribute(bytes: *const u8, length: u32,
cache: *mut nsHTMLCSSStyleSheet)

View file

@ -7,22 +7,12 @@
extern crate app_units;
extern crate env_logger;
extern crate euclid;
#[macro_use] extern crate lazy_static;
extern crate libc;
#[macro_use] extern crate log;
extern crate num_cpus;
extern crate selectors;
extern crate style_traits;
extern crate url;
mod context;
mod data;
mod snapshot;
mod snapshot_helpers;
#[allow(non_snake_case)]
pub mod glue;
mod traversal;
mod wrapper;
// FIXME(bholley): This should probably go away once we harmonize the allocators.
#[no_mangle]

View file

@ -1,150 +0,0 @@
/* 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/. */
use style::gecko_bindings::bindings;
use style::gecko_bindings::structs::ServoElementSnapshot;
use style::gecko_bindings::structs::ServoElementSnapshotFlags as Flags;
use style::string_cache::Atom;
use selectors::parser::AttrSelector;
use snapshot_helpers;
use style::element_state::ElementState;
use style::restyle_hints::ElementSnapshot;
use style::selector_impl::TheSelectorImpl;
use wrapper::AttrSelectorHelpers;
// NB: This is sound, in some sense, because during computation of restyle hints
// the snapshot is kept alive by the modified elements table.
#[derive(Debug)]
pub struct GeckoElementSnapshot(*mut ServoElementSnapshot);
impl GeckoElementSnapshot {
#[inline]
pub unsafe fn from_raw(raw: *mut ServoElementSnapshot) -> Self {
GeckoElementSnapshot(raw)
}
#[inline]
fn is_html_element_in_html_document(&self) -> bool {
unsafe { (*self.0).mIsHTMLElementInHTMLDocument }
}
#[inline]
fn has_any(&self, flags: Flags) -> bool {
unsafe { ((*self.0).mContains as u8 & flags as u8) != 0 }
}
}
impl ::selectors::MatchAttr for GeckoElementSnapshot {
type Impl = TheSelectorImpl;
fn match_attr_has(&self, attr: &AttrSelector<TheSelectorImpl>) -> bool {
unsafe {
bindings::Gecko_SnapshotHasAttr(self.0,
attr.ns_or_null(),
attr.select_name(self.is_html_element_in_html_document()))
}
}
fn match_attr_equals(&self, attr: &AttrSelector<TheSelectorImpl>, value: &Atom) -> bool {
unsafe {
bindings::Gecko_SnapshotAttrEquals(self.0,
attr.ns_or_null(),
attr.select_name(self.is_html_element_in_html_document()),
value.as_ptr(),
/* ignoreCase = */ false)
}
}
fn match_attr_equals_ignore_ascii_case(&self, attr: &AttrSelector<TheSelectorImpl>, value: &Atom) -> bool {
unsafe {
bindings::Gecko_SnapshotAttrEquals(self.0,
attr.ns_or_null(),
attr.select_name(self.is_html_element_in_html_document()),
value.as_ptr(),
/* ignoreCase = */ true)
}
}
fn match_attr_includes(&self, attr: &AttrSelector<TheSelectorImpl>, value: &Atom) -> bool {
unsafe {
bindings::Gecko_SnapshotAttrIncludes(self.0,
attr.ns_or_null(),
attr.select_name(self.is_html_element_in_html_document()),
value.as_ptr())
}
}
fn match_attr_dash(&self, attr: &AttrSelector<TheSelectorImpl>, value: &Atom) -> bool {
unsafe {
bindings::Gecko_SnapshotAttrDashEquals(self.0,
attr.ns_or_null(),
attr.select_name(self.is_html_element_in_html_document()),
value.as_ptr())
}
}
fn match_attr_prefix(&self, attr: &AttrSelector<TheSelectorImpl>, value: &Atom) -> bool {
unsafe {
bindings::Gecko_SnapshotAttrHasPrefix(self.0,
attr.ns_or_null(),
attr.select_name(self.is_html_element_in_html_document()),
value.as_ptr())
}
}
fn match_attr_substring(&self, attr: &AttrSelector<TheSelectorImpl>, value: &Atom) -> bool {
unsafe {
bindings::Gecko_SnapshotAttrHasSubstring(self.0,
attr.ns_or_null(),
attr.select_name(self.is_html_element_in_html_document()),
value.as_ptr())
}
}
fn match_attr_suffix(&self, attr: &AttrSelector<TheSelectorImpl>, value: &Atom) -> bool {
unsafe {
bindings::Gecko_SnapshotAttrHasSuffix(self.0,
attr.ns_or_null(),
attr.select_name(self.is_html_element_in_html_document()),
value.as_ptr())
}
}
}
impl ElementSnapshot for GeckoElementSnapshot {
fn state(&self) -> Option<ElementState> {
if self.has_any(Flags::State) {
Some(ElementState::from_bits_truncate(unsafe { (*self.0).mState as u16 }))
} else {
None
}
}
#[inline]
fn has_attrs(&self) -> bool {
self.has_any(Flags::Attributes)
}
fn id_attr(&self) -> Option<Atom> {
let ptr = unsafe {
bindings::Gecko_SnapshotAtomAttrValue(self.0,
atom!("id").as_ptr())
};
if ptr.is_null() {
None
} else {
Some(Atom::from(ptr))
}
}
// TODO: share logic with Element::{has_class, each_class}?
fn has_class(&self, name: &Atom) -> bool {
snapshot_helpers::has_class(self.0,
name,
bindings::Gecko_SnapshotClassOrClassList)
}
fn each_class<F>(&self, callback: F)
where F: FnMut(&Atom)
{
snapshot_helpers::each_class(self.0,
callback,
bindings::Gecko_SnapshotClassOrClassList)
}
}

View file

@ -1,53 +0,0 @@
/* 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/. */
//! Element an snapshot common logic.
use style::gecko_bindings::structs::nsIAtom;
use style::string_cache::Atom;
use std::{ptr, slice};
pub type ClassOrClassList<T> = unsafe extern fn (T, *mut *mut nsIAtom, *mut *mut *mut nsIAtom) -> u32;
pub fn has_class<T>(item: T,
name: &Atom,
getter: ClassOrClassList<T>) -> bool
{
unsafe {
let mut class: *mut nsIAtom = ptr::null_mut();
let mut list: *mut *mut nsIAtom = ptr::null_mut();
let length = getter(item, &mut class, &mut list);
match length {
0 => false,
1 => name.as_ptr() == class,
n => {
let classes = slice::from_raw_parts(list, n as usize);
classes.iter().any(|ptr| name.as_ptr() == *ptr)
}
}
}
}
pub fn each_class<F, T>(item: T,
mut callback: F,
getter: ClassOrClassList<T>)
where F: FnMut(&Atom)
{
unsafe {
let mut class: *mut nsIAtom = ptr::null_mut();
let mut list: *mut *mut nsIAtom = ptr::null_mut();
let length = getter(item, &mut class, &mut list);
match length {
0 => {}
1 => Atom::with(class, &mut callback),
n => {
let classes = slice::from_raw_parts(list, n as usize);
for c in classes {
Atom::with(*c, &mut callback)
}
}
}
}
}

View file

@ -1,49 +0,0 @@
/* 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/. */
use context::StandaloneStyleContext;
use std::mem;
use style::context::{LocalStyleContext, SharedStyleContext, StyleContext};
use style::dom::OpaqueNode;
use style::traversal::{DomTraversalContext, recalc_style_at};
use style::traversal::RestyleResult;
use wrapper::GeckoNode;
pub struct RecalcStyleOnly<'lc> {
context: StandaloneStyleContext<'lc>,
root: OpaqueNode,
}
impl<'lc, 'ln> DomTraversalContext<GeckoNode<'ln>> for RecalcStyleOnly<'lc> {
type SharedContext = SharedStyleContext;
#[allow(unsafe_code)]
fn new<'a>(shared: &'a Self::SharedContext, root: OpaqueNode) -> Self {
// See the comment in RecalcStyleAndConstructFlows::new for an explanation of why this is
// necessary.
let shared_lc: &'lc Self::SharedContext = unsafe { mem::transmute(shared) };
RecalcStyleOnly {
context: StandaloneStyleContext::new(shared_lc),
root: root,
}
}
fn process_preorder(&self, node: GeckoNode<'ln>) -> RestyleResult {
// FIXME(pcwalton): Stop allocating here. Ideally this should just be done by the HTML
// parser.
node.initialize_data();
recalc_style_at(&self.context, self.root, node)
}
fn process_postorder(&self, _: GeckoNode<'ln>) {
unreachable!();
}
/// We don't use the post-order traversal for anything.
fn needs_postorder_traversal(&self) -> bool { false }
fn local_context(&self) -> &LocalStyleContext {
self.context.local_context()
}
}

View file

@ -1,709 +0,0 @@
/* 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/. */
#![allow(unsafe_code)]
use style::gecko_bindings::bindings;
use style::gecko_bindings::bindings::{Gecko_CalcStyleDifference, Gecko_StoreStyleDifference};
use style::gecko_bindings::bindings::{Gecko_DropStyleChildrenIterator, Gecko_MaybeCreateStyleChildrenIterator};
use style::gecko_bindings::bindings::{Gecko_ElementState, Gecko_GetDocumentElement};
use style::gecko_bindings::bindings::{Gecko_GetFirstChild, Gecko_GetFirstChildElement};
use style::gecko_bindings::bindings::{Gecko_GetLastChild, Gecko_GetLastChildElement};
use style::gecko_bindings::bindings::{Gecko_GetNextSibling, Gecko_GetNextSiblingElement, Gecko_GetNextStyleChild};
use style::gecko_bindings::bindings::{Gecko_GetNodeFlags, Gecko_SetNodeFlags, Gecko_UnsetNodeFlags};
use style::gecko_bindings::bindings::{Gecko_GetParentElement, Gecko_GetParentNode};
use style::gecko_bindings::bindings::{Gecko_GetPrevSibling, Gecko_GetPrevSiblingElement};
use style::gecko_bindings::bindings::{Gecko_GetServoDeclarationBlock, Gecko_IsHTMLElementInHTMLDocument};
use style::gecko_bindings::bindings::{Gecko_IsLink, Gecko_IsRootElement, Gecko_IsTextNode};
use style::gecko_bindings::bindings::{Gecko_IsUnvisitedLink, Gecko_IsVisitedLink};
use style::gecko_bindings::bindings::{Gecko_LocalName, Gecko_Namespace, Gecko_NodeIsElement};
use style::gecko_bindings::bindings::Gecko_ClassOrClassList;
use style::gecko_bindings::bindings::Gecko_GetStyleContext;
use style::gecko_bindings::structs::{NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO, NODE_IS_DIRTY_FOR_SERVO};
use style::gecko_bindings::structs::{RawGeckoDocument, RawGeckoElement, RawGeckoNode};
use style::gecko_bindings::structs::{nsChangeHint, nsIAtom, nsStyleContext};
use style::gecko_bindings::structs::OpaqueStyleData;
use style::gecko_bindings::sugar::ownership::FFIArcHelpers;
use style::gecko_string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};
use glue::GeckoDeclarationBlock;
use libc::uintptr_t;
use selectors::Element;
use selectors::parser::{AttrSelector, NamespaceConstraint};
use snapshot::GeckoElementSnapshot;
use snapshot_helpers;
use std::fmt;
use std::ops::BitOr;
use std::ptr;
use std::sync::Arc;
use style::data::PrivateStyleData;
use style::dom::{LayoutIterator, NodeInfo, TDocument, TElement, TNode, TRestyleDamage, UnsafeNode};
use style::dom::{OpaqueNode, PresentationalHintsSynthetizer};
use style::element_state::ElementState;
use style::error_reporting::StdoutErrorReporter;
use style::parser::ParserContextExtraData;
use style::properties::{ComputedValues, parse_style_attribute};
use style::properties::PropertyDeclarationBlock;
use style::refcell::{Ref, RefCell, RefMut};
use style::selector_impl::ElementExt;
use style::selector_matching::ApplicableDeclarationBlock;
use style::sink::Push;
use style::gecko::selector_impl::{GeckoSelectorImpl, NonTSPseudoClass, PseudoElement};
use url::Url;
pub struct NonOpaqueStyleData(RefCell<PrivateStyleData>);
impl NonOpaqueStyleData {
pub fn new() -> Self {
NonOpaqueStyleData(RefCell::new(PrivateStyleData::new()))
}
}
// We can eliminate OpaqueStyleData when the bindings move into the style crate.
fn to_opaque_style_data(d: *mut NonOpaqueStyleData) -> *mut OpaqueStyleData {
d as *mut OpaqueStyleData
}
fn from_opaque_style_data(d: *mut OpaqueStyleData) -> *mut NonOpaqueStyleData {
d as *mut NonOpaqueStyleData
}
// Important: We don't currently refcount the DOM, because the wrapper lifetime
// magic guarantees that our LayoutFoo references won't outlive the root, and
// we don't mutate any of the references on the Gecko side during restyle. We
// could implement refcounting if need be (at a potentially non-trivial
// performance cost) by implementing Drop and making LayoutFoo non-Copy.
#[derive(Clone, Copy)]
pub struct GeckoNode<'ln>(pub &'ln RawGeckoNode);
impl<'ln> GeckoNode<'ln> {
fn get_node_data(&self) -> Option<&NonOpaqueStyleData> {
unsafe {
from_opaque_style_data(self.0.mServoData.get()).as_ref()
}
}
pub fn initialize_data(self) {
if self.get_node_data().is_none() {
let ptr = Box::new(NonOpaqueStyleData::new());
debug_assert!(self.0.mServoData.get().is_null());
self.0.mServoData.set(to_opaque_style_data(Box::into_raw(ptr)));
}
}
pub fn clear_data(self) {
if !self.get_node_data().is_none() {
let d = from_opaque_style_data(self.0.mServoData.get());
let _ = unsafe { Box::from_raw(d) };
self.0.mServoData.set(ptr::null_mut());
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct GeckoRestyleDamage(nsChangeHint);
impl TRestyleDamage for GeckoRestyleDamage {
type PreExistingComputedValues = nsStyleContext;
fn empty() -> Self {
use std::mem;
GeckoRestyleDamage(unsafe { mem::transmute(0u32) })
}
fn compute(source: &nsStyleContext,
new_style: &Arc<ComputedValues>) -> Self {
let context = source as *const nsStyleContext as *mut nsStyleContext;
let hint = unsafe { Gecko_CalcStyleDifference(context, new_style.as_borrowed()) };
GeckoRestyleDamage(hint)
}
fn rebuild_and_reflow() -> Self {
GeckoRestyleDamage(nsChangeHint::nsChangeHint_ReconstructFrame)
}
}
impl BitOr for GeckoRestyleDamage {
type Output = Self;
fn bitor(self, other: Self) -> Self {
use std::mem;
GeckoRestyleDamage(unsafe { mem::transmute(self.0 as u32 | other.0 as u32) })
}
}
impl<'ln> NodeInfo for GeckoNode<'ln> {
fn is_element(&self) -> bool {
unsafe {
Gecko_NodeIsElement(self.0)
}
}
fn is_text_node(&self) -> bool {
unsafe {
Gecko_IsTextNode(self.0)
}
}
}
impl<'ln> TNode for GeckoNode<'ln> {
type ConcreteDocument = GeckoDocument<'ln>;
type ConcreteElement = GeckoElement<'ln>;
type ConcreteRestyleDamage = GeckoRestyleDamage;
type ConcreteChildrenIterator = GeckoChildrenIterator<'ln>;
fn to_unsafe(&self) -> UnsafeNode {
(self.0 as *const _ as usize, 0)
}
unsafe fn from_unsafe(n: &UnsafeNode) -> Self {
GeckoNode(&*(n.0 as *mut RawGeckoNode))
}
fn dump(self) {
unimplemented!()
}
fn dump_style(self) {
unimplemented!()
}
fn children(self) -> LayoutIterator<GeckoChildrenIterator<'ln>> {
let maybe_iter = unsafe { Gecko_MaybeCreateStyleChildrenIterator(self.0) };
if let Some(iter) = maybe_iter.into_owned_opt() {
LayoutIterator(GeckoChildrenIterator::GeckoIterator(iter))
} else {
LayoutIterator(GeckoChildrenIterator::Current(self.first_child()))
}
}
fn opaque(&self) -> OpaqueNode {
let ptr: uintptr_t = self.0 as *const _ as uintptr_t;
OpaqueNode(ptr)
}
fn layout_parent_node(self, reflow_root: OpaqueNode) -> Option<GeckoNode<'ln>> {
if self.opaque() == reflow_root {
None
} else {
self.parent_node()
}
}
fn debug_id(self) -> usize {
unimplemented!()
}
fn as_element(&self) -> Option<GeckoElement<'ln>> {
if self.is_element() {
unsafe { Some(GeckoElement(&*(self.0 as *const _ as *const RawGeckoElement))) }
} else {
None
}
}
fn as_document(&self) -> Option<GeckoDocument<'ln>> {
unimplemented!()
}
// NOTE: This is not relevant for Gecko, since we get explicit restyle hints
// when a content has changed.
fn has_changed(&self) -> bool { false }
unsafe fn set_changed(&self, _value: bool) {
unimplemented!()
}
fn is_dirty(&self) -> bool {
// Return true unconditionally if we're not yet styled. This is a hack
// and should go away soon.
if self.get_node_data().is_none() {
return true;
}
let flags = unsafe { Gecko_GetNodeFlags(self.0) };
flags & (NODE_IS_DIRTY_FOR_SERVO as u32) != 0
}
unsafe fn set_dirty(&self, value: bool) {
if value {
Gecko_SetNodeFlags(self.0, NODE_IS_DIRTY_FOR_SERVO as u32)
} else {
Gecko_UnsetNodeFlags(self.0, NODE_IS_DIRTY_FOR_SERVO as u32)
}
}
fn has_dirty_descendants(&self) -> bool {
// Return true unconditionally if we're not yet styled. This is a hack
// and should go away soon.
if self.get_node_data().is_none() {
return true;
}
let flags = unsafe { Gecko_GetNodeFlags(self.0) };
flags & (NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO as u32) != 0
}
unsafe fn set_dirty_descendants(&self, value: bool) {
if value {
Gecko_SetNodeFlags(self.0, NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO as u32)
} else {
Gecko_UnsetNodeFlags(self.0, NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO as u32)
}
}
fn can_be_fragmented(&self) -> bool {
// FIXME(SimonSapin): Servo uses this to implement CSS multicol / fragmentation
// Maybe this isnt useful for Gecko?
false
}
unsafe fn set_can_be_fragmented(&self, _value: bool) {
// FIXME(SimonSapin): Servo uses this to implement CSS multicol / fragmentation
// Maybe this isnt useful for Gecko?
}
#[inline(always)]
unsafe fn borrow_data_unchecked(&self) -> Option<*const PrivateStyleData> {
self.get_node_data().as_ref().map(|d| d.0.as_unsafe_cell().get()
as *const PrivateStyleData)
}
#[inline(always)]
fn borrow_data(&self) -> Option<Ref<PrivateStyleData>> {
self.get_node_data().as_ref().map(|d| d.0.borrow())
}
#[inline(always)]
fn mutate_data(&self) -> Option<RefMut<PrivateStyleData>> {
self.get_node_data().as_ref().map(|d| d.0.borrow_mut())
}
fn restyle_damage(self) -> Self::ConcreteRestyleDamage {
// Not called from style, only for layout.
unimplemented!();
}
fn set_restyle_damage(self, damage: Self::ConcreteRestyleDamage) {
unsafe { Gecko_StoreStyleDifference(self.0, damage.0) }
}
fn parent_node(&self) -> Option<GeckoNode<'ln>> {
unsafe {
Gecko_GetParentNode(self.0).borrow_opt().map(|n| GeckoNode(n))
}
}
fn first_child(&self) -> Option<GeckoNode<'ln>> {
unsafe {
Gecko_GetFirstChild(self.0).borrow_opt().map(|n| GeckoNode(n))
}
}
fn last_child(&self) -> Option<GeckoNode<'ln>> {
unsafe {
Gecko_GetLastChild(self.0).borrow_opt().map(|n| GeckoNode(n))
}
}
fn prev_sibling(&self) -> Option<GeckoNode<'ln>> {
unsafe {
Gecko_GetPrevSibling(self.0).borrow_opt().map(|n| GeckoNode(n))
}
}
fn next_sibling(&self) -> Option<GeckoNode<'ln>> {
unsafe {
Gecko_GetNextSibling(self.0).borrow_opt().map(|n| GeckoNode(n))
}
}
fn existing_style_for_restyle_damage<'a>(&'a self,
current_cv: Option<&'a Arc<ComputedValues>>,
pseudo: Option<&PseudoElement>)
-> Option<&'a nsStyleContext> {
if current_cv.is_none() {
// Don't bother in doing an ffi call to get null back.
return None;
}
unsafe {
let atom_ptr = pseudo.map(|p| p.as_atom().as_ptr())
.unwrap_or(ptr::null_mut());
let context_ptr = Gecko_GetStyleContext(self.0, atom_ptr);
context_ptr.as_ref()
}
}
fn needs_dirty_on_viewport_size_changed(&self) -> bool {
// Gecko's node doesn't have the DIRTY_ON_VIEWPORT_SIZE_CHANGE flag,
// so we force them to be dirtied on viewport size change, regardless if
// they use viewport percentage size or not.
// TODO(shinglyu): implement this in Gecko: https://github.com/servo/servo/pull/11890
true
}
// TODO(shinglyu): implement this in Gecko: https://github.com/servo/servo/pull/11890
unsafe fn set_dirty_on_viewport_size_changed(&self) {}
}
// We generally iterate children by traversing the siblings of the first child
// like Servo does. However, for nodes with anonymous children, we use a custom
// (heavier-weight) Gecko-implemented iterator.
pub enum GeckoChildrenIterator<'a> {
Current(Option<GeckoNode<'a>>),
GeckoIterator(bindings::StyleChildrenIteratorOwned),
}
impl<'a> Drop for GeckoChildrenIterator<'a> {
fn drop(&mut self) {
if let GeckoChildrenIterator::GeckoIterator(ref it) = *self {
unsafe {
Gecko_DropStyleChildrenIterator(ptr::read(it as *const _));
}
}
}
}
impl<'a> Iterator for GeckoChildrenIterator<'a> {
type Item = GeckoNode<'a>;
fn next(&mut self) -> Option<GeckoNode<'a>> {
match *self {
GeckoChildrenIterator::Current(curr) => {
let next = curr.and_then(|node| node.next_sibling());
*self = GeckoChildrenIterator::Current(next);
curr
},
GeckoChildrenIterator::GeckoIterator(ref it) => unsafe {
Gecko_GetNextStyleChild(&it).borrow_opt().map(GeckoNode)
}
}
}
}
#[derive(Clone, Copy)]
pub struct GeckoDocument<'ld>(pub &'ld RawGeckoDocument);
impl<'ld> TDocument for GeckoDocument<'ld> {
type ConcreteNode = GeckoNode<'ld>;
type ConcreteElement = GeckoElement<'ld>;
fn as_node(&self) -> GeckoNode<'ld> {
unsafe { GeckoNode(&*(self.0 as *const _ as *const RawGeckoNode)) }
}
fn root_node(&self) -> Option<GeckoNode<'ld>> {
unsafe {
Gecko_GetDocumentElement(self.0).borrow_opt().map(|el| GeckoElement(el).as_node())
}
}
fn drain_modified_elements(&self) -> Vec<(GeckoElement<'ld>, GeckoElementSnapshot)> {
unimplemented!()
/*
let elements = unsafe { self.0.drain_modified_elements() };
elements.into_iter().map(|(el, snapshot)| (ServoLayoutElement::from_layout_js(el), snapshot)).collect()*/
}
fn will_paint(&self) { unimplemented!() }
fn needs_paint_from_layout(&self) { unimplemented!() }
}
#[derive(Clone, Copy)]
pub struct GeckoElement<'le>(pub &'le RawGeckoElement);
impl<'le> fmt::Debug for GeckoElement<'le> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
try!(write!(f, "<{}", self.get_local_name()));
if let Some(id) = self.get_id() {
try!(write!(f, " id={}", id));
}
write!(f, ">")
}
}
impl<'le> GeckoElement<'le> {
pub fn parse_style_attribute(value: &str) -> Option<PropertyDeclarationBlock> {
// FIXME(bholley): Real base URL and error reporter.
let base_url = &*DUMMY_BASE_URL;
// FIXME(heycam): Needs real ParserContextExtraData so that URLs parse
// properly.
let extra_data = ParserContextExtraData::default();
Some(parse_style_attribute(value, &base_url, Box::new(StdoutErrorReporter), extra_data))
}
}
lazy_static! {
pub static ref DUMMY_BASE_URL: Url = {
Url::parse("http://www.example.org").unwrap()
};
}
impl<'le> TElement for GeckoElement<'le> {
type ConcreteNode = GeckoNode<'le>;
type ConcreteDocument = GeckoDocument<'le>;
fn as_node(&self) -> Self::ConcreteNode {
unsafe { GeckoNode(&*(self.0 as *const _ as *const RawGeckoNode)) }
}
fn style_attribute(&self) -> Option<&Arc<PropertyDeclarationBlock>> {
let declarations = unsafe { Gecko_GetServoDeclarationBlock(self.0) };
if declarations.is_null() {
None
} else {
let declarations = declarations.as_arc::<GeckoDeclarationBlock>();
declarations.declarations.as_ref().map(|r| r as *const Arc<_>).map(|ptr| unsafe { &*ptr })
}
}
fn get_state(&self) -> ElementState {
unsafe {
ElementState::from_bits_truncate(Gecko_ElementState(self.0) as u16)
}
}
#[inline]
fn has_attr(&self, namespace: &Namespace, attr: &Atom) -> bool {
unsafe {
bindings::Gecko_HasAttr(self.0,
namespace.0.as_ptr(),
attr.as_ptr())
}
}
#[inline]
fn attr_equals(&self, namespace: &Namespace, attr: &Atom, val: &Atom) -> bool {
unsafe {
bindings::Gecko_AttrEquals(self.0,
namespace.0.as_ptr(),
attr.as_ptr(),
val.as_ptr(),
/* ignoreCase = */ false)
}
}
}
impl<'le> PartialEq for GeckoElement<'le> {
fn eq(&self, other: &Self) -> bool {
self.0 as *const _ == other.0 as *const _
}
}
impl<'le> PresentationalHintsSynthetizer for GeckoElement<'le> {
fn synthesize_presentational_hints_for_legacy_attributes<V>(&self, _hints: &mut V)
where V: Push<ApplicableDeclarationBlock>
{
// FIXME(bholley) - Need to implement this.
}
}
impl<'le> ::selectors::Element for GeckoElement<'le> {
fn parent_element(&self) -> Option<Self> {
unsafe {
Gecko_GetParentElement(self.0).borrow_opt().map(|el| GeckoElement(el))
}
}
fn first_child_element(&self) -> Option<Self> {
unsafe {
Gecko_GetFirstChildElement(self.0).borrow_opt().map(|el| GeckoElement(el))
}
}
fn last_child_element(&self) -> Option<Self> {
unsafe {
Gecko_GetLastChildElement(self.0).borrow_opt().map(|el| GeckoElement(el))
}
}
fn prev_sibling_element(&self) -> Option<Self> {
unsafe {
Gecko_GetPrevSiblingElement(self.0).borrow_opt().map(|el| GeckoElement(el))
}
}
fn next_sibling_element(&self) -> Option<Self> {
unsafe {
Gecko_GetNextSiblingElement(self.0).borrow_opt().map(|el| GeckoElement(el))
}
}
fn is_root(&self) -> bool {
unsafe {
Gecko_IsRootElement(self.0)
}
}
fn is_empty(&self) -> bool {
// XXX(emilio): Implement this properly.
false
}
fn get_local_name(&self) -> &WeakAtom {
unsafe {
WeakAtom::new(Gecko_LocalName(self.0))
}
}
fn get_namespace(&self) -> &WeakNamespace {
unsafe {
WeakNamespace::new(Gecko_Namespace(self.0))
}
}
fn match_non_ts_pseudo_class(&self, pseudo_class: NonTSPseudoClass) -> bool {
match pseudo_class {
// https://github.com/servo/servo/issues/8718
NonTSPseudoClass::AnyLink => unsafe { Gecko_IsLink(self.0) },
NonTSPseudoClass::Link => unsafe { Gecko_IsUnvisitedLink(self.0) },
NonTSPseudoClass::Visited => unsafe { Gecko_IsVisitedLink(self.0) },
NonTSPseudoClass::Active |
NonTSPseudoClass::Focus |
NonTSPseudoClass::Hover |
NonTSPseudoClass::Enabled |
NonTSPseudoClass::Disabled |
NonTSPseudoClass::Checked |
NonTSPseudoClass::ReadWrite |
NonTSPseudoClass::Indeterminate => {
self.get_state().contains(pseudo_class.state_flag())
},
NonTSPseudoClass::ReadOnly => {
!self.get_state().contains(pseudo_class.state_flag())
}
}
}
fn get_id(&self) -> Option<Atom> {
let ptr = unsafe {
bindings::Gecko_AtomAttrValue(self.0,
atom!("id").as_ptr())
};
if ptr.is_null() {
None
} else {
Some(Atom::from(ptr))
}
}
fn has_class(&self, name: &Atom) -> bool {
snapshot_helpers::has_class(self.0,
name,
Gecko_ClassOrClassList)
}
fn each_class<F>(&self, callback: F)
where F: FnMut(&Atom)
{
snapshot_helpers::each_class(self.0,
callback,
Gecko_ClassOrClassList)
}
fn is_html_element_in_html_document(&self) -> bool {
unsafe {
Gecko_IsHTMLElementInHTMLDocument(self.0)
}
}
}
pub trait AttrSelectorHelpers {
fn ns_or_null(&self) -> *mut nsIAtom;
fn select_name(&self, is_html_element_in_html_document: bool) -> *mut nsIAtom;
}
impl AttrSelectorHelpers for AttrSelector<GeckoSelectorImpl> {
fn ns_or_null(&self) -> *mut nsIAtom {
match self.namespace {
NamespaceConstraint::Any => ptr::null_mut(),
NamespaceConstraint::Specific(ref ns) => ns.url.0.as_ptr(),
}
}
fn select_name(&self, is_html_element_in_html_document: bool) -> *mut nsIAtom {
if is_html_element_in_html_document {
self.lower_name.as_ptr()
} else {
self.name.as_ptr()
}
}
}
impl<'le> ::selectors::MatchAttr for GeckoElement<'le> {
type Impl = GeckoSelectorImpl;
fn match_attr_has(&self, attr: &AttrSelector<Self::Impl>) -> bool {
unsafe {
bindings::Gecko_HasAttr(self.0,
attr.ns_or_null(),
attr.select_name(self.is_html_element_in_html_document()))
}
}
fn match_attr_equals(&self, attr: &AttrSelector<Self::Impl>, value: &Atom) -> bool {
unsafe {
bindings::Gecko_AttrEquals(self.0,
attr.ns_or_null(),
attr.select_name(self.is_html_element_in_html_document()),
value.as_ptr(),
/* ignoreCase = */ false)
}
}
fn match_attr_equals_ignore_ascii_case(&self, attr: &AttrSelector<Self::Impl>, value: &Atom) -> bool {
unsafe {
bindings::Gecko_AttrEquals(self.0,
attr.ns_or_null(),
attr.select_name(self.is_html_element_in_html_document()),
value.as_ptr(),
/* ignoreCase = */ false)
}
}
fn match_attr_includes(&self, attr: &AttrSelector<Self::Impl>, value: &Atom) -> bool {
unsafe {
bindings::Gecko_AttrIncludes(self.0,
attr.ns_or_null(),
attr.select_name(self.is_html_element_in_html_document()),
value.as_ptr())
}
}
fn match_attr_dash(&self, attr: &AttrSelector<Self::Impl>, value: &Atom) -> bool {
unsafe {
bindings::Gecko_AttrDashEquals(self.0,
attr.ns_or_null(),
attr.select_name(self.is_html_element_in_html_document()),
value.as_ptr())
}
}
fn match_attr_prefix(&self, attr: &AttrSelector<Self::Impl>, value: &Atom) -> bool {
unsafe {
bindings::Gecko_AttrHasPrefix(self.0,
attr.ns_or_null(),
attr.select_name(self.is_html_element_in_html_document()),
value.as_ptr())
}
}
fn match_attr_substring(&self, attr: &AttrSelector<Self::Impl>, value: &Atom) -> bool {
unsafe {
bindings::Gecko_AttrHasSubstring(self.0,
attr.ns_or_null(),
attr.select_name(self.is_html_element_in_html_document()),
value.as_ptr())
}
}
fn match_attr_suffix(&self, attr: &AttrSelector<Self::Impl>, value: &Atom) -> bool {
unsafe {
bindings::Gecko_AttrHasSuffix(self.0,
attr.ns_or_null(),
attr.select_name(self.is_html_element_in_html_document()),
value.as_ptr())
}
}
}
impl<'le> ElementExt for GeckoElement<'le> {
type Snapshot = GeckoElementSnapshot;
#[inline]
fn is_link(&self) -> bool {
self.match_non_ts_pseudo_class(NonTSPseudoClass::AnyLink)
}
}