mirror of
https://github.com/servo/servo.git
synced 2025-08-06 22:15:33 +01:00
Rename script_layout_interface
to layout_api
(#37591)
Now that we are standardizing on the `_traits` crates becoming `_api` and exposing the API of the crate that they get their name from [^1], `script_layout_interface` becomes `layout_api` as it exposes the API for `layout` that is used by `script` This brings the crate in line with the naming of the other ones in `shared`. [^1]: https://servo.zulipchat.com/#narrow/channel/263398-general/topic/Organizing.20*_traits.20crates/with/396893711 Testing: This should not change any behavior and thus is covered by existing tests. Signed-off-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
parent
85c849d843
commit
69ff4afa58
41 changed files with 102 additions and 113 deletions
41
components/shared/layout/Cargo.toml
Normal file
41
components/shared/layout/Cargo.toml
Normal file
|
@ -0,0 +1,41 @@
|
|||
[package]
|
||||
name = "layout_api"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
license.workspace = true
|
||||
edition.workspace = true
|
||||
publish.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
[lib]
|
||||
name = "layout_api"
|
||||
path = "lib.rs"
|
||||
|
||||
[dependencies]
|
||||
base = { workspace = true }
|
||||
app_units = { workspace = true }
|
||||
atomic_refcell = { workspace = true }
|
||||
compositing_traits = { workspace = true }
|
||||
constellation_traits = { workspace = true }
|
||||
embedder_traits = { workspace = true }
|
||||
euclid = { workspace = true }
|
||||
fnv = { workspace = true }
|
||||
fonts = { path = "../../fonts" }
|
||||
fonts_traits = { workspace = true }
|
||||
fxhash = { workspace = true }
|
||||
html5ever = { workspace = true }
|
||||
ipc-channel = { workspace = true }
|
||||
libc = { workspace = true }
|
||||
malloc_size_of = { workspace = true }
|
||||
malloc_size_of_derive = { workspace = true }
|
||||
net_traits = { workspace = true }
|
||||
pixels = { path = "../../pixels" }
|
||||
profile_traits = { workspace = true }
|
||||
range = { path = "../../range" }
|
||||
script_traits = { workspace = true }
|
||||
selectors = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
servo_arc = { workspace = true }
|
||||
servo_url = { path = "../../url" }
|
||||
stylo = { workspace = true }
|
||||
webrender_api = { workspace = true }
|
638
components/shared/layout/lib.rs
Normal file
638
components/shared/layout/lib.rs
Normal file
|
@ -0,0 +1,638 @@
|
|||
/* 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 https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
//! This module contains traits in script used generically in the rest of Servo.
|
||||
//! The traits are here instead of in script so that these modules won't have
|
||||
//! to depend on script.
|
||||
|
||||
#![deny(unsafe_code)]
|
||||
|
||||
pub mod wrapper_traits;
|
||||
|
||||
use std::any::Any;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicIsize, AtomicU64, Ordering};
|
||||
|
||||
use app_units::Au;
|
||||
use atomic_refcell::AtomicRefCell;
|
||||
use base::Epoch;
|
||||
use base::id::{BrowsingContextId, PipelineId, WebViewId};
|
||||
use compositing_traits::CrossProcessCompositorApi;
|
||||
use constellation_traits::LoadData;
|
||||
use embedder_traits::{Theme, UntrustedNodeAddress, ViewportDetails};
|
||||
use euclid::default::{Point2D, Rect};
|
||||
use fnv::FnvHashMap;
|
||||
use fonts::{FontContext, SystemFontServiceProxy};
|
||||
use fxhash::FxHashMap;
|
||||
use ipc_channel::ipc::IpcSender;
|
||||
use libc::c_void;
|
||||
use malloc_size_of::{MallocSizeOf as MallocSizeOfTrait, MallocSizeOfOps};
|
||||
use malloc_size_of_derive::MallocSizeOf;
|
||||
use net_traits::image_cache::{ImageCache, PendingImageId};
|
||||
use pixels::RasterImage;
|
||||
use profile_traits::mem::Report;
|
||||
use profile_traits::time;
|
||||
use script_traits::{InitialScriptState, Painter, ScriptThreadMessage};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use servo_arc::Arc as ServoArc;
|
||||
use servo_url::{ImmutableOrigin, ServoUrl};
|
||||
use style::Atom;
|
||||
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::PropertyId;
|
||||
use style::properties::style_structs::Font;
|
||||
use style::selector_parser::{PseudoElement, RestyleDamage, Snapshot};
|
||||
use style::stylesheets::Stylesheet;
|
||||
use webrender_api::units::{DeviceIntSize, LayoutVector2D};
|
||||
use webrender_api::{ExternalScrollId, ImageKey};
|
||||
|
||||
pub trait GenericLayoutDataTrait: Any + MallocSizeOfTrait {
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
}
|
||||
|
||||
pub type GenericLayoutData = dyn GenericLayoutDataTrait + Send + Sync;
|
||||
|
||||
#[derive(MallocSizeOf)]
|
||||
pub struct StyleData {
|
||||
/// Data that the style system associates with a node. When the
|
||||
/// style system is being used standalone, this is all that hangs
|
||||
/// off the node. This must be first to permit the various
|
||||
/// transmutations between ElementData and PersistentLayoutData.
|
||||
pub element_data: AtomicRefCell<ElementData>,
|
||||
|
||||
/// Information needed during parallel traversals.
|
||||
pub parallel: DomParallelInfo,
|
||||
}
|
||||
|
||||
impl Default for StyleData {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
element_data: AtomicRefCell::new(ElementData::default()),
|
||||
parallel: DomParallelInfo::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Information that we need stored in each DOM node.
|
||||
#[derive(Default, MallocSizeOf)]
|
||||
pub struct DomParallelInfo {
|
||||
/// The number of children remaining to process during bottom-up traversal.
|
||||
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,
|
||||
SVGImageElement,
|
||||
SVGSVGElement,
|
||||
}
|
||||
|
||||
pub struct HTMLCanvasData {
|
||||
pub source: Option<ImageKey>,
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
}
|
||||
|
||||
pub struct SVGSVGData {
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
}
|
||||
|
||||
/// The address of a node known to be valid. These are sent from script to layout.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub struct TrustedNodeAddress(pub *const c_void);
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
unsafe impl Send for TrustedNodeAddress {}
|
||||
|
||||
/// Whether the pending image needs to be fetched or is waiting on an existing fetch.
|
||||
#[derive(Debug)]
|
||||
pub enum PendingImageState {
|
||||
Unrequested(ServoUrl),
|
||||
PendingResponse,
|
||||
}
|
||||
|
||||
/// The data associated with an image that is not yet present in the image cache.
|
||||
/// Used by the script thread to hold on to DOM elements that need to be repainted
|
||||
/// when an image fetch is complete.
|
||||
#[derive(Debug)]
|
||||
pub struct PendingImage {
|
||||
pub state: PendingImageState,
|
||||
pub node: UntrustedNodeAddress,
|
||||
pub id: PendingImageId,
|
||||
pub origin: ImmutableOrigin,
|
||||
}
|
||||
|
||||
/// A data structure to tarck vector image that are fully loaded (i.e has a parsed SVG
|
||||
/// tree) but not yet rasterized to the size needed by layout. The rasterization is
|
||||
/// happening in the image cache.
|
||||
#[derive(Debug)]
|
||||
pub struct PendingRasterizationImage {
|
||||
pub node: UntrustedNodeAddress,
|
||||
pub id: PendingImageId,
|
||||
pub size: DeviceIntSize,
|
||||
}
|
||||
|
||||
#[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 webview_id: WebViewId,
|
||||
pub url: ServoUrl,
|
||||
pub is_iframe: bool,
|
||||
pub script_chan: IpcSender<ScriptThreadMessage>,
|
||||
pub image_cache: Arc<dyn ImageCache>,
|
||||
pub font_context: Arc<FontContext>,
|
||||
pub time_profiler_chan: time::ProfilerChan,
|
||||
pub compositor_api: CrossProcessCompositorApi,
|
||||
pub viewport_details: ViewportDetails,
|
||||
pub theme: Theme,
|
||||
}
|
||||
|
||||
pub trait LayoutFactory: Send + Sync {
|
||||
fn create(&self, config: LayoutConfig) -> Box<dyn Layout>;
|
||||
}
|
||||
|
||||
pub trait Layout {
|
||||
/// Get a reference to this Layout's Stylo `Device` used to handle media queries and
|
||||
/// resolve font metrics.
|
||||
fn device(&self) -> &Device;
|
||||
|
||||
/// The currently laid out Epoch that this Layout has finished.
|
||||
fn current_epoch(&self) -> Epoch;
|
||||
|
||||
/// Load all fonts from the given stylesheet, returning the number of fonts that
|
||||
/// need to be loaded.
|
||||
fn load_web_fonts_from_stylesheet(&self, stylesheet: ServoArc<Stylesheet>);
|
||||
|
||||
/// Add a stylesheet to this Layout. This will add it to the Layout's `Stylist` as well as
|
||||
/// loading all web fonts defined in the stylesheet. The second stylesheet is the insertion
|
||||
/// point (if it exists, the sheet needs to be inserted before it).
|
||||
fn add_stylesheet(
|
||||
&mut self,
|
||||
stylesheet: ServoArc<Stylesheet>,
|
||||
before_stylsheet: Option<ServoArc<Stylesheet>>,
|
||||
);
|
||||
|
||||
/// Inform the layout that its ScriptThread is about to exit.
|
||||
fn exit_now(&mut self);
|
||||
|
||||
/// Requests that layout measure its memory usage. The resulting reports are sent back
|
||||
/// via the supplied channel.
|
||||
fn collect_reports(&self, reports: &mut Vec<Report>, ops: &mut MallocSizeOfOps);
|
||||
|
||||
/// Sets quirks mode for the document, causing the quirks mode stylesheet to be used.
|
||||
fn set_quirks_mode(&mut self, quirks_mode: QuirksMode);
|
||||
|
||||
/// Removes a stylesheet from the Layout.
|
||||
fn remove_stylesheet(&mut self, stylesheet: ServoArc<Stylesheet>);
|
||||
|
||||
/// Requests a reflow.
|
||||
fn reflow(&mut self, reflow_request: ReflowRequest) -> Option<ReflowResult>;
|
||||
|
||||
/// Tells layout that script has added some paint worklet modules.
|
||||
fn register_paint_worklet_modules(
|
||||
&mut self,
|
||||
name: Atom,
|
||||
properties: Vec<Atom>,
|
||||
painter: Box<dyn Painter>,
|
||||
);
|
||||
|
||||
/// Set the scroll states of this layout after a compositor scroll.
|
||||
fn set_scroll_offsets_from_renderer(
|
||||
&mut self,
|
||||
scroll_states: &HashMap<ExternalScrollId, LayoutVector2D>,
|
||||
);
|
||||
|
||||
/// Get the scroll offset of the given scroll node with id of [`ExternalScrollId`] or `None` if it does
|
||||
/// not exist in the tree.
|
||||
fn scroll_offset(&self, id: ExternalScrollId) -> Option<LayoutVector2D>;
|
||||
|
||||
fn query_content_box(&self, node: TrustedNodeAddress) -> Option<Rect<Au>>;
|
||||
fn query_content_boxes(&self, node: TrustedNodeAddress) -> Vec<Rect<Au>>;
|
||||
fn query_client_rect(&self, node: TrustedNodeAddress) -> Rect<i32>;
|
||||
fn query_element_inner_outer_text(&self, node: TrustedNodeAddress) -> String;
|
||||
fn query_nodes_from_point(
|
||||
&self,
|
||||
point: Point2D<f32>,
|
||||
query_type: NodesFromPointQueryType,
|
||||
) -> Vec<UntrustedNodeAddress>;
|
||||
fn query_offset_parent(&self, node: TrustedNodeAddress) -> 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<TrustedNodeAddress>) -> Rect<i32>;
|
||||
fn query_text_indext(&self, node: OpaqueNode, point: Point2D<f32>) -> Option<usize>;
|
||||
}
|
||||
|
||||
/// This trait is part of `layout_api` because it depends on both `script_traits`
|
||||
/// and also `LayoutFactory` from this crate. If it was in `script_traits` there would be a
|
||||
/// circular dependency.
|
||||
pub trait ScriptThreadFactory {
|
||||
/// Create a `ScriptThread`.
|
||||
fn create(
|
||||
state: InitialScriptState,
|
||||
layout_factory: Arc<dyn LayoutFactory>,
|
||||
system_font_service: Arc<SystemFontServiceProxy>,
|
||||
load_data: LoadData,
|
||||
);
|
||||
}
|
||||
#[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,
|
||||
ScrollingAreaOrOffsetQuery,
|
||||
OffsetParentQuery,
|
||||
TextIndexQuery,
|
||||
NodesFromPointQuery,
|
||||
ResolvedStyleQuery,
|
||||
StyleQuery,
|
||||
ElementInnerOuterTextQuery,
|
||||
ResolvedFontStyleQuery,
|
||||
InnerWindowDimensionsQuery,
|
||||
}
|
||||
|
||||
/// The goal of a reflow request.
|
||||
///
|
||||
/// Please do not add any other types of reflows. In general, all reflow should
|
||||
/// go through the *update the rendering* step of the HTML specification. Exceptions
|
||||
/// should have careful review.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum ReflowGoal {
|
||||
/// A reflow has been requesting by the *update the rendering* step of the HTML
|
||||
/// event loop. This nominally driven by the display's VSync.
|
||||
UpdateTheRendering,
|
||||
|
||||
/// Script has done a layout query and this reflow ensurs that layout is up-to-date
|
||||
/// with the latest changes to the DOM.
|
||||
LayoutQuery(QueryMsg),
|
||||
|
||||
/// Tells layout about a single new scrolling offset from the script. The rest will
|
||||
/// remain untouched and layout won't forward this back to script.
|
||||
UpdateScrollNode(ExternalScrollId, LayoutVector2D),
|
||||
}
|
||||
|
||||
impl ReflowGoal {
|
||||
/// Returns true if the given ReflowQuery needs a full, up-to-date display list to
|
||||
/// be present or false if it only needs stacking-relative positions.
|
||||
pub fn needs_display_list(&self) -> bool {
|
||||
match *self {
|
||||
ReflowGoal::UpdateTheRendering | ReflowGoal::UpdateScrollNode(..) => true,
|
||||
ReflowGoal::LayoutQuery(ref querymsg) => match *querymsg {
|
||||
QueryMsg::ElementInnerOuterTextQuery |
|
||||
QueryMsg::InnerWindowDimensionsQuery |
|
||||
QueryMsg::NodesFromPointQuery |
|
||||
QueryMsg::ResolvedStyleQuery |
|
||||
QueryMsg::ScrollingAreaOrOffsetQuery |
|
||||
QueryMsg::TextIndexQuery => true,
|
||||
QueryMsg::ClientRectQuery |
|
||||
QueryMsg::ContentBox |
|
||||
QueryMsg::ContentBoxes |
|
||||
QueryMsg::OffsetParentQuery |
|
||||
QueryMsg::ResolvedFontStyleQuery |
|
||||
QueryMsg::StyleQuery => false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the given ReflowQuery needs its display list send to WebRender or
|
||||
/// false if a layout_thread display list is sufficient.
|
||||
pub fn needs_display(&self) -> bool {
|
||||
match *self {
|
||||
ReflowGoal::UpdateTheRendering | ReflowGoal::UpdateScrollNode(..) => true,
|
||||
ReflowGoal::LayoutQuery(ref querymsg) => match *querymsg {
|
||||
QueryMsg::NodesFromPointQuery |
|
||||
QueryMsg::TextIndexQuery |
|
||||
QueryMsg::ElementInnerOuterTextQuery => true,
|
||||
QueryMsg::ContentBox |
|
||||
QueryMsg::ContentBoxes |
|
||||
QueryMsg::ClientRectQuery |
|
||||
QueryMsg::ScrollingAreaOrOffsetQuery |
|
||||
QueryMsg::ResolvedStyleQuery |
|
||||
QueryMsg::ResolvedFontStyleQuery |
|
||||
QueryMsg::OffsetParentQuery |
|
||||
QueryMsg::InnerWindowDimensionsQuery |
|
||||
QueryMsg::StyleQuery => false,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, MallocSizeOf)]
|
||||
pub struct IFrameSize {
|
||||
pub browsing_context_id: BrowsingContextId,
|
||||
pub pipeline_id: PipelineId,
|
||||
pub viewport_details: ViewportDetails,
|
||||
}
|
||||
|
||||
pub type IFrameSizes = FnvHashMap<BrowsingContextId, IFrameSize>;
|
||||
|
||||
/// Information derived from a layout pass that needs to be returned to the script thread.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct ReflowResult {
|
||||
/// Whether or not this reflow produced a display list.
|
||||
pub built_display_list: bool,
|
||||
/// The list of images that were encountered that are in progress.
|
||||
pub pending_images: Vec<PendingImage>,
|
||||
/// The list of vector images that were encountered that still need to be rasterized.
|
||||
pub pending_rasterization_images: Vec<PendingRasterizationImage>,
|
||||
/// The list of iframes in this layout and their sizes, used in order
|
||||
/// to communicate them with the Constellation and also the `Window`
|
||||
/// element of their content pages.
|
||||
pub iframe_sizes: IFrameSizes,
|
||||
/// The mapping of node to animated image, need to be returned to ImageAnimationManager
|
||||
pub node_to_image_animation_map: FxHashMap<OpaqueNode, ImageAnimationState>,
|
||||
}
|
||||
|
||||
/// Information needed for a script-initiated reflow.
|
||||
#[derive(Debug)]
|
||||
pub struct ReflowRequest {
|
||||
/// The document node.
|
||||
pub document: TrustedNodeAddress,
|
||||
/// The dirty root from which to restyle.
|
||||
pub dirty_root: Option<TrustedNodeAddress>,
|
||||
/// Whether the document's stylesheets have changed since the last script reflow.
|
||||
pub stylesheets_changed: bool,
|
||||
/// The current [`ViewportDetails`] to use for this reflow.
|
||||
pub viewport_details: ViewportDetails,
|
||||
/// The goal of this reflow.
|
||||
pub reflow_goal: ReflowGoal,
|
||||
/// The number of objects in the dom #10110
|
||||
pub dom_count: u32,
|
||||
/// The current window origin
|
||||
pub origin: ImmutableOrigin,
|
||||
/// Restyle snapshot map.
|
||||
pub pending_restyles: Vec<(TrustedNodeAddress, PendingRestyle)>,
|
||||
/// The current animation timeline value.
|
||||
pub animation_timeline_value: f64,
|
||||
/// The set of animations for this document.
|
||||
pub animations: DocumentAnimationSet,
|
||||
/// The set of image animations.
|
||||
pub node_to_image_animation_map: FxHashMap<OpaqueNode, ImageAnimationState>,
|
||||
/// The theme for the window
|
||||
pub theme: Theme,
|
||||
/// The node highlighted by the devtools, if any
|
||||
pub highlighted_dom_node: Option<OpaqueNode>,
|
||||
}
|
||||
|
||||
/// A pending restyle.
|
||||
#[derive(Debug, Default, MallocSizeOf)]
|
||||
pub struct PendingRestyle {
|
||||
/// If this element had a state or attribute change since the last restyle, track
|
||||
/// the original condition of the element.
|
||||
pub snapshot: Option<Snapshot>,
|
||||
|
||||
/// Any explicit restyles hints that have been accumulated for this element.
|
||||
pub hint: RestyleHint,
|
||||
|
||||
/// Any explicit restyles damage that have been accumulated for this element.
|
||||
pub damage: RestyleDamage,
|
||||
}
|
||||
|
||||
/// The type of fragment that a scroll root is created for.
|
||||
///
|
||||
/// This can only ever grow to maximum 4 entries. That's because we cram the value of this enum
|
||||
/// into the lower 2 bits of the `ScrollRootId`, which otherwise contains a 32-bit-aligned
|
||||
/// heap address.
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
|
||||
pub enum FragmentType {
|
||||
/// A StackingContext for the fragment body itself.
|
||||
FragmentBody,
|
||||
/// A StackingContext created to contain ::before pseudo-element content.
|
||||
BeforePseudoContent,
|
||||
/// A StackingContext created to contain ::after pseudo-element content.
|
||||
AfterPseudoContent,
|
||||
}
|
||||
|
||||
impl From<Option<PseudoElement>> for FragmentType {
|
||||
fn from(value: Option<PseudoElement>) -> Self {
|
||||
match value {
|
||||
Some(PseudoElement::After) => FragmentType::AfterPseudoContent,
|
||||
Some(PseudoElement::Before) => FragmentType::BeforePseudoContent,
|
||||
_ => FragmentType::FragmentBody,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The next ID that will be used for a special scroll root id.
|
||||
///
|
||||
/// A special scroll root is a scroll root that is created for generated content.
|
||||
static NEXT_SPECIAL_SCROLL_ROOT_ID: AtomicU64 = AtomicU64::new(0);
|
||||
|
||||
/// If none of the bits outside this mask are set, the scroll root is a special scroll root.
|
||||
/// Note that we assume that the top 16 bits of the address space are unused on the platform.
|
||||
const SPECIAL_SCROLL_ROOT_ID_MASK: u64 = 0xffff;
|
||||
|
||||
/// Returns a new scroll root ID for a scroll root.
|
||||
fn next_special_id() -> u64 {
|
||||
// We shift this left by 2 to make room for the fragment type ID.
|
||||
((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
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, MallocSizeOf)]
|
||||
pub struct ImageAnimationState {
|
||||
#[ignore_malloc_size_of = "Arc is hard"]
|
||||
pub image: Arc<RasterImage>,
|
||||
pub active_frame: usize,
|
||||
last_update_time: f64,
|
||||
}
|
||||
|
||||
impl ImageAnimationState {
|
||||
pub fn new(image: Arc<RasterImage>, last_update_time: f64) -> Self {
|
||||
Self {
|
||||
image,
|
||||
active_frame: 0,
|
||||
last_update_time,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn image_key(&self) -> Option<ImageKey> {
|
||||
self.image.id
|
||||
}
|
||||
|
||||
pub fn time_to_next_frame(&self, now: f64) -> f64 {
|
||||
let frame_delay = self
|
||||
.image
|
||||
.frames
|
||||
.get(self.active_frame)
|
||||
.expect("Image frame should always be valid")
|
||||
.delay
|
||||
.map_or(0., |delay| delay.as_secs_f64());
|
||||
(frame_delay - now + self.last_update_time).max(0.0)
|
||||
}
|
||||
|
||||
/// check whether image active frame need to be updated given current time,
|
||||
/// return true if there are image that need to be updated.
|
||||
/// false otherwise.
|
||||
pub fn update_frame_for_animation_timeline_value(&mut self, now: f64) -> bool {
|
||||
if self.image.frames.len() <= 1 {
|
||||
return false;
|
||||
}
|
||||
let image = &self.image;
|
||||
let time_interval_since_last_update = now - self.last_update_time;
|
||||
let mut remain_time_interval = time_interval_since_last_update -
|
||||
image
|
||||
.frames
|
||||
.get(self.active_frame)
|
||||
.unwrap()
|
||||
.delay
|
||||
.unwrap()
|
||||
.as_secs_f64();
|
||||
let mut next_active_frame_id = self.active_frame;
|
||||
while remain_time_interval > 0.0 {
|
||||
next_active_frame_id = (next_active_frame_id + 1) % image.frames.len();
|
||||
remain_time_interval -= image
|
||||
.frames
|
||||
.get(next_active_frame_id)
|
||||
.unwrap()
|
||||
.delay
|
||||
.unwrap()
|
||||
.as_secs_f64();
|
||||
}
|
||||
if self.active_frame == next_active_frame_id {
|
||||
return false;
|
||||
}
|
||||
self.active_frame = next_active_frame_id;
|
||||
self.last_update_time = now;
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use ipc_channel::ipc::IpcSharedMemory;
|
||||
use pixels::{CorsStatus, ImageFrame, ImageMetadata, PixelFormat, RasterImage};
|
||||
|
||||
use crate::ImageAnimationState;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let image_frames: Vec<ImageFrame> = std::iter::repeat_with(|| ImageFrame {
|
||||
delay: Some(Duration::from_millis(100)),
|
||||
byte_range: 0..1,
|
||||
width: 100,
|
||||
height: 100,
|
||||
})
|
||||
.take(10)
|
||||
.collect();
|
||||
let image = RasterImage {
|
||||
metadata: ImageMetadata {
|
||||
width: 100,
|
||||
height: 100,
|
||||
},
|
||||
format: PixelFormat::BGRA8,
|
||||
id: None,
|
||||
bytes: IpcSharedMemory::from_byte(1, 1),
|
||||
frames: image_frames,
|
||||
cors_status: CorsStatus::Unsafe,
|
||||
};
|
||||
let mut image_animation_state = ImageAnimationState::new(Arc::new(image), 0.0);
|
||||
|
||||
assert_eq!(image_animation_state.active_frame, 0);
|
||||
assert_eq!(image_animation_state.last_update_time, 0.0);
|
||||
assert_eq!(
|
||||
image_animation_state.update_frame_for_animation_timeline_value(0.101),
|
||||
true
|
||||
);
|
||||
assert_eq!(image_animation_state.active_frame, 1);
|
||||
assert_eq!(image_animation_state.last_update_time, 0.101);
|
||||
assert_eq!(
|
||||
image_animation_state.update_frame_for_animation_timeline_value(0.116),
|
||||
false
|
||||
);
|
||||
assert_eq!(image_animation_state.active_frame, 1);
|
||||
assert_eq!(image_animation_state.last_update_time, 0.101);
|
||||
}
|
||||
}
|
383
components/shared/layout/wrapper_traits.rs
Normal file
383
components/shared/layout/wrapper_traits.rs
Normal file
|
@ -0,0 +1,383 @@
|
|||
/* 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 https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#![allow(unsafe_code)]
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::fmt::Debug;
|
||||
|
||||
use atomic_refcell::AtomicRef;
|
||||
use base::id::{BrowsingContextId, PipelineId};
|
||||
use fonts_traits::ByteIndex;
|
||||
use html5ever::{LocalName, Namespace};
|
||||
use net_traits::image_cache::Image;
|
||||
use pixels::ImageMetadata;
|
||||
use range::Range;
|
||||
use servo_arc::Arc;
|
||||
use servo_url::ServoUrl;
|
||||
use style::attr::AttrValue;
|
||||
use style::context::SharedStyleContext;
|
||||
use style::data::ElementData;
|
||||
use style::dom::{LayoutIterator, NodeInfo, OpaqueNode, TElement, TNode};
|
||||
use style::properties::ComputedValues;
|
||||
use style::selector_parser::{PseudoElement, PseudoElementCascadeType, SelectorImpl};
|
||||
use style::stylist::RuleInclusion;
|
||||
|
||||
use crate::{
|
||||
FragmentType, GenericLayoutData, GenericLayoutDataTrait, HTMLCanvasData, HTMLMediaData,
|
||||
LayoutNodeType, SVGSVGData, StyleData,
|
||||
};
|
||||
|
||||
pub trait LayoutDataTrait: GenericLayoutDataTrait + Default + Send + Sync + 'static {}
|
||||
|
||||
/// A wrapper so that layout can access only the methods that it should have access to. Layout must
|
||||
/// only ever see these and must never see instances of `LayoutDom`.
|
||||
/// FIXME(mrobinson): `Send + Sync` is required here for Layout 2020, but eventually it
|
||||
/// should stop sending LayoutNodes to other threads and rely on ThreadSafeLayoutNode
|
||||
/// or some other mechanism to ensure thread safety.
|
||||
pub trait LayoutNode<'dom>: Copy + Debug + TNode + Send + Sync {
|
||||
type ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode<'dom>;
|
||||
fn to_threadsafe(&self) -> Self::ConcreteThreadSafeLayoutNode;
|
||||
|
||||
/// Returns the type ID of this node.
|
||||
fn type_id(&self) -> LayoutNodeType;
|
||||
|
||||
/// Initialize this node with empty style and opaque layout data.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This method is unsafe because it modifies the given node during
|
||||
/// layout. Callers should ensure that no other layout thread is
|
||||
/// attempting to read or modify the opaque layout data of this node.
|
||||
unsafe fn initialize_style_and_layout_data<RequestedLayoutDataType: LayoutDataTrait>(&self);
|
||||
|
||||
/// Initialize this node with empty opaque layout data.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This method is unsafe because it modifies the given node during
|
||||
/// layout. Callers should ensure that no other layout thread is
|
||||
/// attempting to read or modify the opaque layout data of this node.
|
||||
fn initialize_layout_data<RequestedLayoutDataType: LayoutDataTrait>(&self);
|
||||
|
||||
/// Get the [`StyleData`] for this node. Returns None if the node is unstyled.
|
||||
fn style_data(&self) -> Option<&'dom StyleData>;
|
||||
|
||||
/// Get the layout data of this node, attempting to downcast it to the desired type.
|
||||
/// Returns None if there is no layout data or it isn't of the desired type.
|
||||
fn layout_data(&self) -> Option<&'dom GenericLayoutData>;
|
||||
|
||||
fn rev_children(self) -> LayoutIterator<ReverseChildrenIterator<Self>> {
|
||||
LayoutIterator(ReverseChildrenIterator {
|
||||
current: self.last_child(),
|
||||
})
|
||||
}
|
||||
|
||||
fn traverse_preorder(self) -> TreeIterator<Self> {
|
||||
TreeIterator::new(self)
|
||||
}
|
||||
|
||||
/// Returns whether the node is connected.
|
||||
fn is_connected(&self) -> bool;
|
||||
}
|
||||
|
||||
pub struct ReverseChildrenIterator<ConcreteNode> {
|
||||
current: Option<ConcreteNode>,
|
||||
}
|
||||
|
||||
impl<'dom, ConcreteNode> Iterator for ReverseChildrenIterator<ConcreteNode>
|
||||
where
|
||||
ConcreteNode: LayoutNode<'dom>,
|
||||
{
|
||||
type Item = ConcreteNode;
|
||||
fn next(&mut self) -> Option<ConcreteNode> {
|
||||
let node = self.current;
|
||||
self.current = node.and_then(|node| node.prev_sibling());
|
||||
node
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TreeIterator<ConcreteNode> {
|
||||
stack: Vec<ConcreteNode>,
|
||||
}
|
||||
|
||||
impl<'dom, ConcreteNode> TreeIterator<ConcreteNode>
|
||||
where
|
||||
ConcreteNode: LayoutNode<'dom>,
|
||||
{
|
||||
fn new(root: ConcreteNode) -> TreeIterator<ConcreteNode> {
|
||||
let stack = vec![root];
|
||||
TreeIterator { stack }
|
||||
}
|
||||
|
||||
pub fn next_skipping_children(&mut self) -> Option<ConcreteNode> {
|
||||
self.stack.pop()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'dom, ConcreteNode> Iterator for TreeIterator<ConcreteNode>
|
||||
where
|
||||
ConcreteNode: LayoutNode<'dom>,
|
||||
{
|
||||
type Item = ConcreteNode;
|
||||
fn next(&mut self) -> Option<ConcreteNode> {
|
||||
let ret = self.stack.pop();
|
||||
if let Some(node) = ret {
|
||||
self.stack.extend(node.rev_children())
|
||||
}
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
/// A thread-safe version of `LayoutNode`, used during flow construction. This type of layout
|
||||
/// node does not allow any parents or siblings of nodes to be accessed, to avoid races.
|
||||
pub trait ThreadSafeLayoutNode<'dom>: Clone + Copy + Debug + NodeInfo + PartialEq + Sized {
|
||||
type ConcreteNode: LayoutNode<'dom, ConcreteThreadSafeLayoutNode = Self>;
|
||||
type ConcreteElement: TElement;
|
||||
|
||||
type ConcreteThreadSafeLayoutElement: ThreadSafeLayoutElement<'dom, ConcreteThreadSafeLayoutNode = Self>
|
||||
+ ::selectors::Element<Impl = SelectorImpl>;
|
||||
type ChildrenIterator: Iterator<Item = Self> + Sized;
|
||||
|
||||
/// Converts self into an `OpaqueNode`.
|
||||
fn opaque(&self) -> OpaqueNode;
|
||||
|
||||
/// Returns the type ID of this node.
|
||||
/// Returns `None` if this is a pseudo-element; otherwise, returns `Some`.
|
||||
fn type_id(&self) -> Option<LayoutNodeType>;
|
||||
|
||||
/// Returns the style for a text node. This is computed on the fly from the
|
||||
/// parent style to avoid traversing text nodes in the style system.
|
||||
///
|
||||
/// Note that this does require accessing the parent, which this interface
|
||||
/// technically forbids. But accessing the parent is only unsafe insofar as
|
||||
/// it can be used to reach siblings and cousins. A simple immutable borrow
|
||||
/// of the parent data is fine, since the bottom-up traversal will not process
|
||||
/// the parent until all the children have been processed.
|
||||
fn parent_style(&self) -> Arc<ComputedValues>;
|
||||
|
||||
fn debug_id(self) -> usize;
|
||||
|
||||
/// Returns an iterator over this node's children.
|
||||
fn children(&self) -> LayoutIterator<Self::ChildrenIterator>;
|
||||
|
||||
/// Returns a ThreadSafeLayoutElement if this is an element, None otherwise.
|
||||
fn as_element(&self) -> Option<Self::ConcreteThreadSafeLayoutElement>;
|
||||
|
||||
/// Returns a ThreadSafeLayoutElement if this is an element in an HTML namespace, None otherwise.
|
||||
fn as_html_element(&self) -> Option<Self::ConcreteThreadSafeLayoutElement>;
|
||||
|
||||
/// Get the [`StyleData`] for this node. Returns None if the node is unstyled.
|
||||
fn style_data(&self) -> Option<&'dom StyleData>;
|
||||
|
||||
/// Get the layout data of this node, attempting to downcast it to the desired type.
|
||||
/// Returns None if there is no layout data or it isn't of the desired type.
|
||||
fn layout_data(&self) -> Option<&'dom GenericLayoutData>;
|
||||
|
||||
fn style(&self, context: &SharedStyleContext) -> Arc<ComputedValues> {
|
||||
if let Some(el) = self.as_element() {
|
||||
el.style(context)
|
||||
} else {
|
||||
// Text nodes are not styled during traversal,instead we simply
|
||||
// return parent style here and do cascading during layout.
|
||||
debug_assert!(self.is_text_node());
|
||||
self.parent_style()
|
||||
}
|
||||
}
|
||||
|
||||
fn selected_style(&self) -> Arc<ComputedValues> {
|
||||
if let Some(el) = self.as_element() {
|
||||
el.selected_style()
|
||||
} else {
|
||||
debug_assert!(self.is_text_node());
|
||||
// TODO(stshine): What should the selected style be for text?
|
||||
self.parent_style()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if this node contributes content. This is used in the implementation of
|
||||
/// `empty_cells` per CSS 2.1 § 17.6.1.1.
|
||||
fn is_content(&self) -> bool {
|
||||
self.type_id().is_some()
|
||||
}
|
||||
|
||||
/// Returns access to the underlying LayoutNode. This is breaks the abstraction
|
||||
/// barrier of ThreadSafeLayout wrapper layer, and can lead to races if not used
|
||||
/// carefully.
|
||||
///
|
||||
/// We need this because the implementation of some methods need to access the layout
|
||||
/// data flags, and we have this annoying trait separation between script and layout :-(
|
||||
fn unsafe_get(self) -> Self::ConcreteNode;
|
||||
|
||||
fn node_text_content(self) -> Cow<'dom, str>;
|
||||
|
||||
/// If selection intersects this node, return it. Otherwise, returns `None`.
|
||||
fn selection(&self) -> Option<Range<ByteIndex>>;
|
||||
|
||||
/// If this is an image element, returns its URL. If this is not an image element, fails.
|
||||
fn image_url(&self) -> Option<ServoUrl>;
|
||||
|
||||
/// If this is an image element, returns its current-pixel-density. If this is not an image element, fails.
|
||||
fn image_density(&self) -> Option<f64>;
|
||||
|
||||
/// If this is an image element, returns its image data. Otherwise, returns `None`.
|
||||
fn image_data(&self) -> Option<(Option<Image>, Option<ImageMetadata>)>;
|
||||
|
||||
fn canvas_data(&self) -> Option<HTMLCanvasData>;
|
||||
|
||||
fn svg_data(&self) -> Option<SVGSVGData>;
|
||||
|
||||
fn media_data(&self) -> Option<HTMLMediaData>;
|
||||
|
||||
/// If this node is an iframe element, returns its browsing context ID. If this node is
|
||||
/// not an iframe element, fails. Returns None if there is no nested browsing context.
|
||||
fn iframe_browsing_context_id(&self) -> Option<BrowsingContextId>;
|
||||
|
||||
/// If this node is an iframe element, returns its pipeline ID. If this node is
|
||||
/// not an iframe element, fails. Returns None if there is no nested browsing context.
|
||||
fn iframe_pipeline_id(&self) -> Option<PipelineId>;
|
||||
|
||||
fn get_span(&self) -> Option<u32>;
|
||||
fn get_colspan(&self) -> Option<u32>;
|
||||
fn get_rowspan(&self) -> Option<u32>;
|
||||
|
||||
fn pseudo_element(&self) -> Option<PseudoElement>;
|
||||
|
||||
fn fragment_type(&self) -> FragmentType {
|
||||
self.pseudo_element().into()
|
||||
}
|
||||
|
||||
fn with_pseudo(&self, pseudo_element_type: PseudoElement) -> Option<Self> {
|
||||
self.as_element()
|
||||
.and_then(|element| element.with_pseudo(pseudo_element_type))
|
||||
.as_ref()
|
||||
.map(ThreadSafeLayoutElement::as_node)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ThreadSafeLayoutElement<'dom>:
|
||||
Clone + Copy + Sized + Debug + ::selectors::Element<Impl = SelectorImpl>
|
||||
{
|
||||
type ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode<'dom, ConcreteThreadSafeLayoutElement = Self>;
|
||||
|
||||
/// This type alias is just a work-around to avoid writing
|
||||
///
|
||||
/// <Self::ConcreteThreadSafeLayoutNode as ThreadSafeLayoutNode>::ConcreteElement
|
||||
///
|
||||
type ConcreteElement: TElement;
|
||||
|
||||
fn as_node(&self) -> Self::ConcreteThreadSafeLayoutNode;
|
||||
|
||||
/// Creates a new `ThreadSafeLayoutElement` for the same `LayoutElement`
|
||||
/// with a different pseudo-element type.
|
||||
///
|
||||
/// Returns `None` if this pseudo doesn't apply to the given element for one of
|
||||
/// the following reasons:
|
||||
///
|
||||
/// 1. `pseudo` is eager and is not defined in the stylesheet. In this case, there
|
||||
/// is not reason to process the pseudo element at all.
|
||||
/// 2. `pseudo` is for `::servo-details-summary` or `::servo-details-content` and
|
||||
/// it doesn't apply to this element, either because it isn't a details or is
|
||||
/// in the wrong state.
|
||||
fn with_pseudo(&self, pseudo: PseudoElement) -> Option<Self>;
|
||||
|
||||
/// Returns the type ID of this node.
|
||||
/// Returns `None` if this is a pseudo-element; otherwise, returns `Some`.
|
||||
fn type_id(&self) -> Option<LayoutNodeType>;
|
||||
|
||||
/// Returns access to the underlying TElement. This is breaks the abstraction
|
||||
/// barrier of ThreadSafeLayout wrapper layer, and can lead to races if not used
|
||||
/// carefully.
|
||||
///
|
||||
/// We need this so that the functions defined on this trait can call
|
||||
/// lazily_compute_pseudo_element_style, which operates on TElement.
|
||||
fn unsafe_get(self) -> Self::ConcreteElement;
|
||||
|
||||
/// Get the local name of this element. See
|
||||
/// <https://dom.spec.whatwg.org/#concept-element-local-name>.
|
||||
fn get_local_name(&self) -> &LocalName;
|
||||
|
||||
fn get_attr(&self, namespace: &Namespace, name: &LocalName) -> Option<&str>;
|
||||
|
||||
fn get_attr_enum(&self, namespace: &Namespace, name: &LocalName) -> Option<&AttrValue>;
|
||||
|
||||
fn style_data(&self) -> AtomicRef<ElementData>;
|
||||
|
||||
fn pseudo_element(&self) -> Option<PseudoElement>;
|
||||
|
||||
/// Returns the style results for the given node. If CSS selector matching
|
||||
/// has not yet been performed, fails.
|
||||
///
|
||||
/// Unlike the version on TNode, this handles pseudo-elements.
|
||||
#[inline]
|
||||
fn style(&self, context: &SharedStyleContext) -> Arc<ComputedValues> {
|
||||
let data = self.style_data();
|
||||
match self.pseudo_element() {
|
||||
None => data.styles.primary().clone(),
|
||||
Some(style_pseudo) => {
|
||||
// Precompute non-eagerly-cascaded pseudo-element styles if not
|
||||
// cached before.
|
||||
match style_pseudo.cascade_type() {
|
||||
// Already computed during the cascade.
|
||||
PseudoElementCascadeType::Eager => self
|
||||
.style_data()
|
||||
.styles
|
||||
.pseudos
|
||||
.get(&style_pseudo)
|
||||
.unwrap()
|
||||
.clone(),
|
||||
PseudoElementCascadeType::Precomputed => context
|
||||
.stylist
|
||||
.precomputed_values_for_pseudo::<Self::ConcreteElement>(
|
||||
&context.guards,
|
||||
&style_pseudo,
|
||||
Some(data.styles.primary()),
|
||||
),
|
||||
PseudoElementCascadeType::Lazy => {
|
||||
context
|
||||
.stylist
|
||||
.lazily_compute_pseudo_element_style(
|
||||
&context.guards,
|
||||
self.unsafe_get(),
|
||||
&style_pseudo,
|
||||
RuleInclusion::All,
|
||||
data.styles.primary(),
|
||||
/* is_probe = */ false,
|
||||
/* matching_func = */ None,
|
||||
)
|
||||
.unwrap()
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn selected_style(&self) -> Arc<ComputedValues> {
|
||||
let data = self.style_data();
|
||||
data.styles
|
||||
.pseudos
|
||||
.get(&PseudoElement::Selection)
|
||||
.unwrap_or(data.styles.primary())
|
||||
.clone()
|
||||
}
|
||||
|
||||
fn is_shadow_host(&self) -> bool;
|
||||
|
||||
/// Returns whether this node is a body element of an html element root
|
||||
/// in an HTML element document.
|
||||
///
|
||||
/// Note that this does require accessing the parent, which this interface
|
||||
/// technically forbids. But accessing the parent is only unsafe insofar as
|
||||
/// it can be used to reach siblings and cousins. A simple immutable borrow
|
||||
/// of the parent data is fine, since the bottom-up traversal will not process
|
||||
/// the parent until all the children have been processed.
|
||||
fn is_body_element_of_html_element_root(&self) -> bool;
|
||||
|
||||
/// Returns whether this node is the root element in an HTML document element.
|
||||
///
|
||||
/// Note that, like `Self::is_body_element_of_html_element_root`, this accesses the parent.
|
||||
/// As in that case, since this is an immutable borrow, we do not violate thread safety.
|
||||
fn is_root(&self) -> bool;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue