#![deny(unsafe_code)]
pub mod wrapper_traits;
use std::any::Any;
use std::borrow::Cow;
use std::sync::atomic::{AtomicIsize, AtomicU64, Ordering};
use std::sync::Arc;
use app_units::Au;
use atomic_refcell::AtomicRefCell;
use base::cross_process_instant::CrossProcessInstant;
use base::id::{BrowsingContextId, PipelineId};
use base::Epoch;
use canvas_traits::canvas::{CanvasId, CanvasMsg};
use crossbeam_channel::Sender;
use euclid::default::{Point2D, Rect};
use euclid::Size2D;
use fonts::SystemFontServiceProxy;
use ipc_channel::ipc::IpcSender;
use libc::c_void;
use malloc_size_of_derive::MallocSizeOf;
use metrics::PaintTimeMetrics;
use net_traits::image_cache::{ImageCache, PendingImageId};
use net_traits::ResourceThreads;
use profile_traits::mem::Report;
use profile_traits::time;
use script_traits::{
ConstellationControlMsg, InitialScriptState, LayoutMsg, LoadData, Painter, ScrollState,
UntrustedNodeAddress, WindowSizeData,
};
use serde::{Deserialize, Serialize};
use servo_arc::Arc as ServoArc;
use servo_url::{ImmutableOrigin, ServoUrl};
use style::animation::DocumentAnimationSet;
use style::context::QuirksMode;
use style::data::ElementData;
use style::dom::OpaqueNode;
use style::invalidation::element::restyle_hints::RestyleHint;
use style::media_queries::Device;
use style::properties::style_structs::Font;
use style::properties::PropertyId;
use style::selector_parser::{PseudoElement, RestyleDamage, Snapshot};
use style::stylesheets::Stylesheet;
use style::Atom;
use style_traits::CSSPixel;
use webrender_api::ImageKey;
use webrender_traits::CrossProcessCompositorApi;
pub type GenericLayoutData = dyn Any + Send + Sync;
#[derive(MallocSizeOf)]
pub struct StyleData {
#[ignore_malloc_size_of = "This probably should not be ignored"]
pub element_data: AtomicRefCell<ElementData>,
pub parallel: DomParallelInfo,
}
impl Default for StyleData {
fn default() -> Self {
Self {
element_data: AtomicRefCell::new(ElementData::default()),
parallel: DomParallelInfo::default(),
}
}
}
#[derive(Default, MallocSizeOf)]
pub struct DomParallelInfo {
pub children_to_process: AtomicIsize,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum LayoutNodeType {
Element(LayoutElementType),
Text,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum LayoutElementType {
Element,
HTMLBodyElement,
HTMLBRElement,
HTMLCanvasElement,
HTMLHtmlElement,
HTMLIFrameElement,
HTMLImageElement,
HTMLInputElement,
HTMLMediaElement,
HTMLObjectElement,
HTMLOptGroupElement,
HTMLOptionElement,
HTMLParagraphElement,
HTMLPreElement,
HTMLSelectElement,
HTMLTableCellElement,
HTMLTableColElement,
HTMLTableElement,
HTMLTableRowElement,
HTMLTableSectionElement,
HTMLTextAreaElement,
SVGSVGElement,
}
pub enum HTMLCanvasDataSource {
WebGL(ImageKey),
Image(IpcSender<CanvasMsg>),
WebGPU(ImageKey),
Empty,
}
pub struct HTMLCanvasData {
pub source: HTMLCanvasDataSource,
pub width: u32,
pub height: u32,
pub canvas_id: CanvasId,
}
pub struct SVGSVGData {
pub width: u32,
pub height: u32,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct TrustedNodeAddress(pub *const c_void);
#[allow(unsafe_code)]
unsafe impl Send for TrustedNodeAddress {}
#[derive(Debug)]
pub enum PendingImageState {
Unrequested(ServoUrl),
PendingResponse,
}
#[derive(Debug)]
pub struct PendingImage {
pub state: PendingImageState,
pub node: UntrustedNodeAddress,
pub id: PendingImageId,
pub origin: ImmutableOrigin,
}
#[derive(Clone, Copy, Debug)]
pub struct MediaFrame {
pub image_key: webrender_api::ImageKey,
pub width: i32,
pub height: i32,
}
pub struct MediaMetadata {
pub width: u32,
pub height: u32,
}
pub struct HTMLMediaData {
pub current_frame: Option<MediaFrame>,
pub metadata: Option<MediaMetadata>,
}
pub struct LayoutConfig {
pub id: PipelineId,
pub url: ServoUrl,
pub is_iframe: bool,
pub constellation_chan: IpcSender<LayoutMsg>,
pub script_chan: IpcSender<ConstellationControlMsg>,
pub image_cache: Arc<dyn ImageCache>,
pub resource_threads: ResourceThreads,
pub system_font_service: Arc<SystemFontServiceProxy>,
pub time_profiler_chan: time::ProfilerChan,
pub compositor_api: CrossProcessCompositorApi,
pub paint_time_metrics: PaintTimeMetrics,
pub window_size: WindowSizeData,
}
pub trait LayoutFactory: Send + Sync {
fn create(&self, config: LayoutConfig) -> Box<dyn Layout>;
}
pub trait Layout {
fn device(&self) -> &Device;
fn waiting_for_web_fonts_to_load(&self) -> bool;
fn current_epoch(&self) -> Epoch;
fn load_web_fonts_from_stylesheet(&self, stylesheet: ServoArc<Stylesheet>);
fn add_stylesheet(
&mut self,
stylesheet: ServoArc<Stylesheet>,
before_stylsheet: Option<ServoArc<Stylesheet>>,
);
fn exit_now(&mut self);
fn collect_reports(&self, reports: &mut Vec<Report>);
fn set_quirks_mode(&mut self, quirks_mode: QuirksMode);
fn remove_stylesheet(&mut self, stylesheet: ServoArc<Stylesheet>);
fn reflow(&mut self, script_reflow: ScriptReflow);
fn register_paint_worklet_modules(
&mut self,
name: Atom,
properties: Vec<Atom>,
painter: Box<dyn Painter>,
);
fn set_scroll_states(&mut self, scroll_states: &[ScrollState]);
fn set_epoch_paint_time(&mut self, epoch: Epoch, paint_time: CrossProcessInstant);
fn query_content_box(&self, node: OpaqueNode) -> Option<Rect<Au>>;
fn query_content_boxes(&self, node: OpaqueNode) -> Vec<Rect<Au>>;
fn query_client_rect(&self, node: OpaqueNode) -> Rect<i32>;
fn query_element_inner_outer_text(&self, node: TrustedNodeAddress) -> String;
fn query_inner_window_dimension(
&self,
context: BrowsingContextId,
) -> Option<Size2D<f32, CSSPixel>>;
fn query_nodes_from_point(
&self,
point: Point2D<f32>,
query_type: NodesFromPointQueryType,
) -> Vec<UntrustedNodeAddress>;
fn query_offset_parent(&self, node: OpaqueNode) -> OffsetParentResponse;
fn query_resolved_style(
&self,
node: TrustedNodeAddress,
pseudo: Option<PseudoElement>,
property_id: PropertyId,
animations: DocumentAnimationSet,
animation_timeline_value: f64,
) -> String;
fn query_resolved_font_style(
&self,
node: TrustedNodeAddress,
value: &str,
animations: DocumentAnimationSet,
animation_timeline_value: f64,
) -> Option<ServoArc<Font>>;
fn query_scrolling_area(&self, node: Option<OpaqueNode>) -> Rect<i32>;
fn query_text_indext(&self, node: OpaqueNode, point: Point2D<f32>) -> Option<usize>;
}
pub trait ScriptThreadFactory {
fn create(
state: InitialScriptState,
layout_factory: Arc<dyn LayoutFactory>,
system_font_service: Arc<SystemFontServiceProxy>,
load_data: LoadData,
user_agent: Cow<'static, str>,
);
}
#[derive(Clone, Default)]
pub struct OffsetParentResponse {
pub node_address: Option<UntrustedNodeAddress>,
pub rect: Rect<Au>,
}
#[derive(Debug, PartialEq)]
pub enum NodesFromPointQueryType {
All,
Topmost,
}
#[derive(Debug, PartialEq)]
pub enum QueryMsg {
ContentBox,
ContentBoxes,
ClientRectQuery,
ScrollingAreaQuery,
OffsetParentQuery,
TextIndexQuery,
NodesFromPointQuery,
ResolvedStyleQuery,
StyleQuery,
ElementInnerOuterTextQuery,
ResolvedFontStyleQuery,
InnerWindowDimensionsQuery,
}
#[derive(Debug, PartialEq)]
pub enum ReflowGoal {
Full,
TickAnimations,
LayoutQuery(QueryMsg),
UpdateScrollNode(ScrollState),
}
impl ReflowGoal {
pub fn needs_display_list(&self) -> bool {
match *self {
ReflowGoal::Full | ReflowGoal::TickAnimations | ReflowGoal::UpdateScrollNode(_) => true,
ReflowGoal::LayoutQuery(ref querymsg) => match *querymsg {
QueryMsg::ElementInnerOuterTextQuery |
QueryMsg::InnerWindowDimensionsQuery |
QueryMsg::NodesFromPointQuery |
QueryMsg::ResolvedStyleQuery |
QueryMsg::TextIndexQuery => true,
QueryMsg::ClientRectQuery |
QueryMsg::ContentBox |
QueryMsg::ContentBoxes |
QueryMsg::OffsetParentQuery |
QueryMsg::ResolvedFontStyleQuery |
QueryMsg::ScrollingAreaQuery |
QueryMsg::StyleQuery => false,
},
}
}
pub fn needs_display(&self) -> bool {
match *self {
ReflowGoal::Full | ReflowGoal::TickAnimations | ReflowGoal::UpdateScrollNode(_) => true,
ReflowGoal::LayoutQuery(ref querymsg) => match *querymsg {
QueryMsg::NodesFromPointQuery |
QueryMsg::TextIndexQuery |
QueryMsg::ElementInnerOuterTextQuery => true,
QueryMsg::ContentBox |
QueryMsg::ContentBoxes |
QueryMsg::ClientRectQuery |
QueryMsg::ScrollingAreaQuery |
QueryMsg::ResolvedStyleQuery |
QueryMsg::ResolvedFontStyleQuery |
QueryMsg::OffsetParentQuery |
QueryMsg::InnerWindowDimensionsQuery |
QueryMsg::StyleQuery => false,
},
}
}
}
#[derive(Debug)]
pub struct Reflow {
pub page_clip_rect: Rect<Au>,
}
#[derive(Debug, Default)]
pub struct ReflowComplete {
pub pending_images: Vec<PendingImage>,
}
#[derive(Debug)]
pub struct ScriptReflow {
pub reflow_info: Reflow,
pub document: TrustedNodeAddress,
pub dirty_root: Option<TrustedNodeAddress>,
pub stylesheets_changed: bool,
pub window_size: WindowSizeData,
pub script_join_chan: Sender<ReflowComplete>,
pub reflow_goal: ReflowGoal,
pub dom_count: u32,
pub origin: ImmutableOrigin,
pub pending_restyles: Vec<(TrustedNodeAddress, PendingRestyle)>,
pub animation_timeline_value: f64,
pub animations: DocumentAnimationSet,
}
#[derive(Debug, Default, MallocSizeOf)]
pub struct PendingRestyle {
pub snapshot: Option<Snapshot>,
pub hint: RestyleHint,
pub damage: RestyleDamage,
}
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
pub enum FragmentType {
FragmentBody,
BeforePseudoContent,
AfterPseudoContent,
}
static NEXT_SPECIAL_SCROLL_ROOT_ID: AtomicU64 = AtomicU64::new(0);
const SPECIAL_SCROLL_ROOT_ID_MASK: u64 = 0xffff;
fn next_special_id() -> u64 {
((NEXT_SPECIAL_SCROLL_ROOT_ID.fetch_add(1, Ordering::SeqCst) + 1) << 2) &
SPECIAL_SCROLL_ROOT_ID_MASK
}
pub fn combine_id_with_fragment_type(id: usize, fragment_type: FragmentType) -> u64 {
debug_assert_eq!(id & (fragment_type as usize), 0);
if fragment_type == FragmentType::FragmentBody {
id as u64
} else {
next_special_id() | (fragment_type as u64)
}
}
pub fn node_id_from_scroll_id(id: usize) -> Option<usize> {
if (id as u64 & !SPECIAL_SCROLL_ROOT_ID_MASK) != 0 {
return Some(id & !3);
}
None
}