mirror of
https://github.com/servo/servo.git
synced 2025-08-08 23:15:33 +01:00
Move most of geckolib into style::gecko
This commit is contained in:
parent
bc9cbc87ba
commit
b2e592b121
27 changed files with 167 additions and 157 deletions
51
components/style/gecko/context.rs
Normal file
51
components/style/gecko/context.rs
Normal file
|
@ -0,0 +1,51 @@
|
|||
/* 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::{LocalStyleContext, StyleContext, SharedStyleContext};
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
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
|
||||
}
|
||||
}
|
|
@ -107,12 +107,12 @@ impl From<nsStyleCoord_CalcValue> for LengthOrPercentage {
|
|||
|
||||
pub mod basic_shape {
|
||||
use euclid::size::Size2D;
|
||||
use gecko::values::GeckoStyleCoordConvertible;
|
||||
use gecko_bindings::structs;
|
||||
use gecko_bindings::structs::{StyleBasicShape, StyleBasicShapeType, StyleFillRule};
|
||||
use gecko_bindings::structs::{nsStyleCoord, nsStyleCorners};
|
||||
use gecko_bindings::structs::StyleClipPathGeometryBox;
|
||||
use gecko_bindings::sugar::ns_style_coord::{CoordDataMut, CoordDataValue};
|
||||
use gecko::values::GeckoStyleCoordConvertible;
|
||||
use std::borrow::Borrow;
|
||||
use values::computed::{BorderRadiusSize, LengthOrPercentage};
|
||||
use values::computed::basic_shape::*;
|
||||
|
|
105
components/style/gecko/data.rs
Normal file
105
components/style/gecko/data.rs
Normal file
|
@ -0,0 +1,105 @@
|
|||
/* 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 animation::Animation;
|
||||
use context::SharedStyleContext;
|
||||
use dom::OpaqueNode;
|
||||
use euclid::size::TypedSize2D;
|
||||
use gecko_bindings::bindings::RawServoStyleSet;
|
||||
use gecko_bindings::sugar::ownership::{HasBoxFFI, HasFFI, HasSimpleFFI};
|
||||
use media_queries::{Device, MediaType};
|
||||
use num_cpus;
|
||||
use parallel::WorkQueueData;
|
||||
use selector_matching::Stylist;
|
||||
use std::cmp;
|
||||
use std::collections::HashMap;
|
||||
use std::env;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::sync::mpsc::{Receiver, Sender, channel};
|
||||
use style_traits::ViewportPx;
|
||||
use stylesheets::Stylesheet;
|
||||
use thread_state;
|
||||
use workqueue::WorkQueue;
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
280
components/style/gecko/generated/gecko_pseudo_element_helper.rs
Normal file
280
components/style/gecko/generated/gecko_pseudo_element_helper.rs
Normal file
|
@ -0,0 +1,280 @@
|
|||
/* 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/. */
|
||||
|
||||
/* Autogenerated file, DO NOT EDIT DIRECTLY */
|
||||
|
||||
/*
|
||||
* This file contains a helper macro invocation to aid Gecko's style system
|
||||
* pseudo-element integration.
|
||||
*
|
||||
* This file is NOT INTENDED to be compiled as a standalone module.
|
||||
*
|
||||
* Also, it guarantees the property that normal pseudo-elements are processed
|
||||
* before anonymous boxes.
|
||||
*
|
||||
* Expected usage is as follows:
|
||||
*
|
||||
* ```
|
||||
* fn have_to_use_pseudo_elements() {
|
||||
* macro_rules pseudo_element! {
|
||||
* ($pseudo_str_with_colon:expr, $pseudo_atom:expr, $is_anon_box:true) => {{
|
||||
* // Stuff stuff stuff.
|
||||
* }}
|
||||
* }
|
||||
* include!("path/to/helper.rs")
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
*/
|
||||
{
|
||||
pseudo_element!(":after",
|
||||
atom!(":after"),
|
||||
false);
|
||||
pseudo_element!(":before",
|
||||
atom!(":before"),
|
||||
false);
|
||||
pseudo_element!(":backdrop",
|
||||
atom!(":backdrop"),
|
||||
false);
|
||||
pseudo_element!(":first-letter",
|
||||
atom!(":first-letter"),
|
||||
false);
|
||||
pseudo_element!(":first-line",
|
||||
atom!(":first-line"),
|
||||
false);
|
||||
pseudo_element!(":-moz-selection",
|
||||
atom!(":-moz-selection"),
|
||||
false);
|
||||
pseudo_element!(":-moz-focus-inner",
|
||||
atom!(":-moz-focus-inner"),
|
||||
false);
|
||||
pseudo_element!(":-moz-focus-outer",
|
||||
atom!(":-moz-focus-outer"),
|
||||
false);
|
||||
pseudo_element!(":-moz-list-bullet",
|
||||
atom!(":-moz-list-bullet"),
|
||||
false);
|
||||
pseudo_element!(":-moz-list-number",
|
||||
atom!(":-moz-list-number"),
|
||||
false);
|
||||
pseudo_element!(":-moz-math-anonymous",
|
||||
atom!(":-moz-math-anonymous"),
|
||||
false);
|
||||
pseudo_element!(":-moz-number-wrapper",
|
||||
atom!(":-moz-number-wrapper"),
|
||||
false);
|
||||
pseudo_element!(":-moz-number-text",
|
||||
atom!(":-moz-number-text"),
|
||||
false);
|
||||
pseudo_element!(":-moz-number-spin-box",
|
||||
atom!(":-moz-number-spin-box"),
|
||||
false);
|
||||
pseudo_element!(":-moz-number-spin-up",
|
||||
atom!(":-moz-number-spin-up"),
|
||||
false);
|
||||
pseudo_element!(":-moz-number-spin-down",
|
||||
atom!(":-moz-number-spin-down"),
|
||||
false);
|
||||
pseudo_element!(":-moz-progress-bar",
|
||||
atom!(":-moz-progress-bar"),
|
||||
false);
|
||||
pseudo_element!(":-moz-range-track",
|
||||
atom!(":-moz-range-track"),
|
||||
false);
|
||||
pseudo_element!(":-moz-range-progress",
|
||||
atom!(":-moz-range-progress"),
|
||||
false);
|
||||
pseudo_element!(":-moz-range-thumb",
|
||||
atom!(":-moz-range-thumb"),
|
||||
false);
|
||||
pseudo_element!(":-moz-meter-bar",
|
||||
atom!(":-moz-meter-bar"),
|
||||
false);
|
||||
pseudo_element!(":-moz-placeholder",
|
||||
atom!(":-moz-placeholder"),
|
||||
false);
|
||||
pseudo_element!(":placeholder",
|
||||
atom!(":placeholder"),
|
||||
false);
|
||||
pseudo_element!(":-moz-color-swatch",
|
||||
atom!(":-moz-color-swatch"),
|
||||
false);
|
||||
pseudo_element!(":-moz-text",
|
||||
atom!(":-moz-text"),
|
||||
true);
|
||||
pseudo_element!(":-moz-other-non-element",
|
||||
atom!(":-moz-other-non-element"),
|
||||
true);
|
||||
pseudo_element!(":-moz-anonymous-block",
|
||||
atom!(":-moz-anonymous-block"),
|
||||
true);
|
||||
pseudo_element!(":-moz-anonymous-positioned-block",
|
||||
atom!(":-moz-anonymous-positioned-block"),
|
||||
true);
|
||||
pseudo_element!(":-moz-mathml-anonymous-block",
|
||||
atom!(":-moz-mathml-anonymous-block"),
|
||||
true);
|
||||
pseudo_element!(":-moz-xul-anonymous-block",
|
||||
atom!(":-moz-xul-anonymous-block"),
|
||||
true);
|
||||
pseudo_element!(":-moz-hframeset-border",
|
||||
atom!(":-moz-hframeset-border"),
|
||||
true);
|
||||
pseudo_element!(":-moz-vframeset-border",
|
||||
atom!(":-moz-vframeset-border"),
|
||||
true);
|
||||
pseudo_element!(":-moz-line-frame",
|
||||
atom!(":-moz-line-frame"),
|
||||
true);
|
||||
pseudo_element!(":-moz-button-content",
|
||||
atom!(":-moz-button-content"),
|
||||
true);
|
||||
pseudo_element!(":-moz-buttonlabel",
|
||||
atom!(":-moz-buttonlabel"),
|
||||
true);
|
||||
pseudo_element!(":-moz-cell-content",
|
||||
atom!(":-moz-cell-content"),
|
||||
true);
|
||||
pseudo_element!(":-moz-dropdown-list",
|
||||
atom!(":-moz-dropdown-list"),
|
||||
true);
|
||||
pseudo_element!(":-moz-fieldset-content",
|
||||
atom!(":-moz-fieldset-content"),
|
||||
true);
|
||||
pseudo_element!(":-moz-frameset-blank",
|
||||
atom!(":-moz-frameset-blank"),
|
||||
true);
|
||||
pseudo_element!(":-moz-display-comboboxcontrol-frame",
|
||||
atom!(":-moz-display-comboboxcontrol-frame"),
|
||||
true);
|
||||
pseudo_element!(":-moz-html-canvas-content",
|
||||
atom!(":-moz-html-canvas-content"),
|
||||
true);
|
||||
pseudo_element!(":-moz-inline-table",
|
||||
atom!(":-moz-inline-table"),
|
||||
true);
|
||||
pseudo_element!(":-moz-table",
|
||||
atom!(":-moz-table"),
|
||||
true);
|
||||
pseudo_element!(":-moz-table-cell",
|
||||
atom!(":-moz-table-cell"),
|
||||
true);
|
||||
pseudo_element!(":-moz-table-column-group",
|
||||
atom!(":-moz-table-column-group"),
|
||||
true);
|
||||
pseudo_element!(":-moz-table-column",
|
||||
atom!(":-moz-table-column"),
|
||||
true);
|
||||
pseudo_element!(":-moz-table-wrapper",
|
||||
atom!(":-moz-table-wrapper"),
|
||||
true);
|
||||
pseudo_element!(":-moz-table-row-group",
|
||||
atom!(":-moz-table-row-group"),
|
||||
true);
|
||||
pseudo_element!(":-moz-table-row",
|
||||
atom!(":-moz-table-row"),
|
||||
true);
|
||||
pseudo_element!(":-moz-canvas",
|
||||
atom!(":-moz-canvas"),
|
||||
true);
|
||||
pseudo_element!(":-moz-pagebreak",
|
||||
atom!(":-moz-pagebreak"),
|
||||
true);
|
||||
pseudo_element!(":-moz-page",
|
||||
atom!(":-moz-page"),
|
||||
true);
|
||||
pseudo_element!(":-moz-pagecontent",
|
||||
atom!(":-moz-pagecontent"),
|
||||
true);
|
||||
pseudo_element!(":-moz-page-sequence",
|
||||
atom!(":-moz-page-sequence"),
|
||||
true);
|
||||
pseudo_element!(":-moz-scrolled-content",
|
||||
atom!(":-moz-scrolled-content"),
|
||||
true);
|
||||
pseudo_element!(":-moz-scrolled-canvas",
|
||||
atom!(":-moz-scrolled-canvas"),
|
||||
true);
|
||||
pseudo_element!(":-moz-scrolled-page-sequence",
|
||||
atom!(":-moz-scrolled-page-sequence"),
|
||||
true);
|
||||
pseudo_element!(":-moz-column-content",
|
||||
atom!(":-moz-column-content"),
|
||||
true);
|
||||
pseudo_element!(":-moz-viewport",
|
||||
atom!(":-moz-viewport"),
|
||||
true);
|
||||
pseudo_element!(":-moz-viewport-scroll",
|
||||
atom!(":-moz-viewport-scroll"),
|
||||
true);
|
||||
pseudo_element!(":-moz-anonymous-flex-item",
|
||||
atom!(":-moz-anonymous-flex-item"),
|
||||
true);
|
||||
pseudo_element!(":-moz-anonymous-grid-item",
|
||||
atom!(":-moz-anonymous-grid-item"),
|
||||
true);
|
||||
pseudo_element!(":-moz-ruby",
|
||||
atom!(":-moz-ruby"),
|
||||
true);
|
||||
pseudo_element!(":-moz-ruby-base",
|
||||
atom!(":-moz-ruby-base"),
|
||||
true);
|
||||
pseudo_element!(":-moz-ruby-base-container",
|
||||
atom!(":-moz-ruby-base-container"),
|
||||
true);
|
||||
pseudo_element!(":-moz-ruby-text",
|
||||
atom!(":-moz-ruby-text"),
|
||||
true);
|
||||
pseudo_element!(":-moz-ruby-text-container",
|
||||
atom!(":-moz-ruby-text-container"),
|
||||
true);
|
||||
pseudo_element!(":-moz-tree-column",
|
||||
atom!(":-moz-tree-column"),
|
||||
true);
|
||||
pseudo_element!(":-moz-tree-row",
|
||||
atom!(":-moz-tree-row"),
|
||||
true);
|
||||
pseudo_element!(":-moz-tree-separator",
|
||||
atom!(":-moz-tree-separator"),
|
||||
true);
|
||||
pseudo_element!(":-moz-tree-cell",
|
||||
atom!(":-moz-tree-cell"),
|
||||
true);
|
||||
pseudo_element!(":-moz-tree-indentation",
|
||||
atom!(":-moz-tree-indentation"),
|
||||
true);
|
||||
pseudo_element!(":-moz-tree-line",
|
||||
atom!(":-moz-tree-line"),
|
||||
true);
|
||||
pseudo_element!(":-moz-tree-twisty",
|
||||
atom!(":-moz-tree-twisty"),
|
||||
true);
|
||||
pseudo_element!(":-moz-tree-image",
|
||||
atom!(":-moz-tree-image"),
|
||||
true);
|
||||
pseudo_element!(":-moz-tree-cell-text",
|
||||
atom!(":-moz-tree-cell-text"),
|
||||
true);
|
||||
pseudo_element!(":-moz-tree-checkbox",
|
||||
atom!(":-moz-tree-checkbox"),
|
||||
true);
|
||||
pseudo_element!(":-moz-tree-progressmeter",
|
||||
atom!(":-moz-tree-progressmeter"),
|
||||
true);
|
||||
pseudo_element!(":-moz-tree-drop-feedback",
|
||||
atom!(":-moz-tree-drop-feedback"),
|
||||
true);
|
||||
pseudo_element!(":-moz-svg-marker-anon-child",
|
||||
atom!(":-moz-svg-marker-anon-child"),
|
||||
true);
|
||||
pseudo_element!(":-moz-svg-outer-svg-anon-child",
|
||||
atom!(":-moz-svg-outer-svg-anon-child"),
|
||||
true);
|
||||
pseudo_element!(":-moz-svg-foreign-content",
|
||||
atom!(":-moz-svg-foreign-content"),
|
||||
true);
|
||||
pseudo_element!(":-moz-svg-text",
|
||||
atom!(":-moz-svg-text"),
|
||||
true);
|
||||
}
|
15
components/style/gecko/mod.rs
Normal file
15
components/style/gecko/mod.rs
Normal file
|
@ -0,0 +1,15 @@
|
|||
/* 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/. */
|
||||
|
||||
|
||||
pub mod context;
|
||||
pub mod data;
|
||||
pub mod snapshot;
|
||||
pub mod snapshot_helpers;
|
||||
pub mod traversal;
|
||||
pub mod wrapper;
|
||||
|
||||
pub mod conversions;
|
||||
pub mod selector_impl;
|
||||
pub mod values;
|
|
@ -70,7 +70,7 @@ impl PseudoElement {
|
|||
}}
|
||||
}
|
||||
|
||||
include!("../generated/gecko_pseudo_element_helper.rs");
|
||||
include!("generated/gecko_pseudo_element_helper.rs");
|
||||
|
||||
None
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ impl PseudoElement {
|
|||
}}
|
||||
}
|
||||
|
||||
include!("../generated/gecko_pseudo_element_helper.rs");
|
||||
include!("generated/gecko_pseudo_element_helper.rs");
|
||||
|
||||
None
|
||||
}
|
||||
|
@ -238,7 +238,7 @@ impl GeckoSelectorImpl {
|
|||
}}
|
||||
}
|
||||
|
||||
include!("../generated/gecko_pseudo_element_helper.rs")
|
||||
include!("generated/gecko_pseudo_element_helper.rs")
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
151
components/style/gecko/snapshot.rs
Normal file
151
components/style/gecko/snapshot.rs
Normal file
|
@ -0,0 +1,151 @@
|
|||
/* 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 element_state::ElementState;
|
||||
use gecko::snapshot_helpers;
|
||||
use gecko::wrapper::AttrSelectorHelpers;
|
||||
use gecko_bindings::bindings;
|
||||
use gecko_bindings::structs::ServoElementSnapshot;
|
||||
use gecko_bindings::structs::ServoElementSnapshotFlags as Flags;
|
||||
use restyle_hints::ElementSnapshot;
|
||||
use selector_impl::TheSelectorImpl;
|
||||
use selectors::parser::AttrSelector;
|
||||
use string_cache::Atom;
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
53
components/style/gecko/snapshot_helpers.rs
Normal file
53
components/style/gecko/snapshot_helpers.rs
Normal file
|
@ -0,0 +1,53 @@
|
|||
/* 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 gecko_bindings::structs::nsIAtom;
|
||||
use std::{ptr, slice};
|
||||
use string_cache::Atom;
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
49
components/style/gecko/traversal.rs
Normal file
49
components/style/gecko/traversal.rs
Normal file
|
@ -0,0 +1,49 @@
|
|||
/* 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::{LocalStyleContext, SharedStyleContext, StyleContext};
|
||||
use dom::OpaqueNode;
|
||||
use gecko::context::StandaloneStyleContext;
|
||||
use gecko::wrapper::GeckoNode;
|
||||
use std::mem;
|
||||
use traversal::{DomTraversalContext, recalc_style_at};
|
||||
use traversal::RestyleResult;
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
728
components/style/gecko/wrapper.rs
Normal file
728
components/style/gecko/wrapper.rs
Normal file
|
@ -0,0 +1,728 @@
|
|||
/* 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 data::PrivateStyleData;
|
||||
use dom::{LayoutIterator, NodeInfo, TDocument, TElement, TNode, TRestyleDamage, UnsafeNode};
|
||||
use dom::{OpaqueNode, PresentationalHintsSynthetizer};
|
||||
use element_state::ElementState;
|
||||
use error_reporting::StdoutErrorReporter;
|
||||
use gecko::selector_impl::{GeckoSelectorImpl, NonTSPseudoClass, PseudoElement};
|
||||
use gecko::snapshot::GeckoElementSnapshot;
|
||||
use gecko::snapshot_helpers;
|
||||
use gecko_bindings::bindings;
|
||||
use gecko_bindings::bindings::{Gecko_CalcStyleDifference, Gecko_StoreStyleDifference};
|
||||
use gecko_bindings::bindings::{Gecko_DropStyleChildrenIterator, Gecko_MaybeCreateStyleChildrenIterator};
|
||||
use gecko_bindings::bindings::{Gecko_ElementState, Gecko_GetDocumentElement};
|
||||
use gecko_bindings::bindings::{Gecko_GetFirstChild, Gecko_GetFirstChildElement};
|
||||
use gecko_bindings::bindings::{Gecko_GetLastChild, Gecko_GetLastChildElement};
|
||||
use gecko_bindings::bindings::{Gecko_GetNextSibling, Gecko_GetNextSiblingElement, Gecko_GetNextStyleChild};
|
||||
use gecko_bindings::bindings::{Gecko_GetNodeFlags, Gecko_SetNodeFlags, Gecko_UnsetNodeFlags};
|
||||
use gecko_bindings::bindings::{Gecko_GetParentElement, Gecko_GetParentNode};
|
||||
use gecko_bindings::bindings::{Gecko_GetPrevSibling, Gecko_GetPrevSiblingElement};
|
||||
use gecko_bindings::bindings::{Gecko_GetServoDeclarationBlock, Gecko_IsHTMLElementInHTMLDocument};
|
||||
use gecko_bindings::bindings::{Gecko_IsLink, Gecko_IsRootElement, Gecko_IsTextNode};
|
||||
use gecko_bindings::bindings::{Gecko_IsUnvisitedLink, Gecko_IsVisitedLink};
|
||||
use gecko_bindings::bindings::{Gecko_LocalName, Gecko_Namespace, Gecko_NodeIsElement};
|
||||
use gecko_bindings::bindings::Gecko_ClassOrClassList;
|
||||
use gecko_bindings::bindings::Gecko_GetStyleContext;
|
||||
use gecko_bindings::structs::{NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO, NODE_IS_DIRTY_FOR_SERVO};
|
||||
use gecko_bindings::structs::{RawGeckoDocument, RawGeckoElement, RawGeckoNode};
|
||||
use gecko_bindings::structs::{nsChangeHint, nsIAtom, nsStyleContext};
|
||||
use gecko_bindings::structs::OpaqueStyleData;
|
||||
use gecko_bindings::sugar::ownership::{FFIArcHelpers, HasArcFFI, HasFFI};
|
||||
use libc::uintptr_t;
|
||||
use parser::ParserContextExtraData;
|
||||
use properties::{ComputedValues, parse_style_attribute};
|
||||
use properties::PropertyDeclarationBlock;
|
||||
use refcell::{Ref, RefCell, RefMut};
|
||||
use selector_impl::ElementExt;
|
||||
use selector_matching::ApplicableDeclarationBlock;
|
||||
use selectors::Element;
|
||||
use selectors::parser::{AttrSelector, NamespaceConstraint};
|
||||
use sink::Push;
|
||||
use std::fmt;
|
||||
use std::ops::BitOr;
|
||||
use std::ptr;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicBool, AtomicPtr};
|
||||
use string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};
|
||||
use url::Url;
|
||||
|
||||
pub struct NonOpaqueStyleData(RefCell<PrivateStyleData>);
|
||||
|
||||
impl NonOpaqueStyleData {
|
||||
pub fn new() -> Self {
|
||||
NonOpaqueStyleData(RefCell::new(PrivateStyleData::new()))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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<bindings::nsHTMLCSSStyleSheet>,
|
||||
pub immutable: AtomicBool,
|
||||
}
|
||||
|
||||
unsafe impl HasFFI for GeckoDeclarationBlock {
|
||||
type FFIType = bindings::ServoDeclarationBlock;
|
||||
}
|
||||
unsafe impl HasArcFFI for GeckoDeclarationBlock {}
|
||||
|
||||
|
||||
// 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 isn’t 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 isn’t 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)
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue