Auto merge of #11754 - Ms2ger:wrapper-traits-prep2, r=nox

Move ServoLayoutNode and related structs to script.

<!-- Reviewable:start -->
This change is [<img src="https://reviewable.io/review_button.svg" height="35" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/11754)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2016-06-20 12:54:20 -05:00 committed by GitHub
commit ee8c5c5a67
51 changed files with 2215 additions and 1840 deletions

View file

@ -49,10 +49,12 @@ phf_macros = "0.7.13"
plugins = {path = "../plugins"}
profile_traits = {path = "../profile_traits"}
rand = "0.3"
range = {path = "../range"}
ref_filter_map = "1.0"
ref_slice = "1.0"
regex = "0.1.43"
rustc-serialize = "0.3"
script_layout_interface = {path = "../script_layout_interface"}
script_traits = {path = "../script_traits"}
selectors = {version = "0.6", features = ["heap_size"]}
serde = "0.7"

View file

@ -33,7 +33,7 @@ use dom::node::Node;
use heapsize::HeapSizeOf;
use js::jsapi::{Heap, JSObject, JSTracer};
use js::jsval::JSVal;
use layout_interface::TrustedNodeAddress;
use script_layout_interface::TrustedNodeAddress;
use script_thread::STACK_ROOTS;
use std::cell::UnsafeCell;
use std::default::Default;

View file

@ -55,7 +55,6 @@ use js::glue::{CallObjectTracer, CallUnbarrieredObjectTracer, CallValueTracer};
use js::jsapi::{GCTraceKindToAscii, Heap, TraceKind, JSObject, JSTracer};
use js::jsval::JSVal;
use js::rust::Runtime;
use layout_interface::LayoutRPC;
use libc;
use msg::constellation_msg::{FrameType, PipelineId, SubpageId, WindowSizeData, WindowSizeType, ReferrerPolicy};
use net_traits::filemanager_thread::SelectedFileId;
@ -67,6 +66,9 @@ use net_traits::{Metadata, NetworkError, ResourceThreads};
use offscreen_gl_context::GLLimits;
use profile_traits::mem::ProfilerChan as MemProfilerChan;
use profile_traits::time::ProfilerChan as TimeProfilerChan;
use script_layout_interface::OpaqueStyleAndLayoutData;
use script_layout_interface::reporter::CSSErrorReporter;
use script_layout_interface::rpc::LayoutRPC;
use script_runtime::ScriptChan;
use script_traits::{TimerEventId, TimerSource, TouchpadPressurePhase, UntrustedNodeAddress};
use serde::{Deserialize, Serialize};
@ -328,6 +330,8 @@ no_jsmanaged_fields!(ReferrerPolicy);
no_jsmanaged_fields!(ResourceThreads);
no_jsmanaged_fields!(SystemTime);
no_jsmanaged_fields!(SelectedFileId);
no_jsmanaged_fields!(OpaqueStyleAndLayoutData);
no_jsmanaged_fields!(CSSErrorReporter);
impl JSTraceable for Box<ScriptChan + Send> {
#[inline]

View file

@ -92,7 +92,6 @@ use html5ever::tree_builder::{LimitedQuirks, NoQuirks, Quirks, QuirksMode};
use ipc_channel::ipc::{self, IpcSender};
use js::jsapi::JS_GetRuntime;
use js::jsapi::{JSContext, JSObject, JSRuntime};
use layout_interface::{Msg, ReflowQueryType};
use msg::constellation_msg::{ALT, CONTROL, SHIFT, SUPER};
use msg::constellation_msg::{Key, KeyModifiers, KeyState};
use msg::constellation_msg::{PipelineId, ReferrerPolicy, SubpageId};
@ -103,6 +102,7 @@ use net_traits::{AsyncResponseTarget, PendingAsyncLoad, IpcSend};
use num_traits::ToPrimitive;
use origin::Origin;
use parse::{ParserRoot, ParserRef, MutNullableParserField};
use script_layout_interface::message::{Msg, ReflowQueryType};
use script_thread::{MainThreadScriptMsg, Runnable};
use script_traits::UntrustedNodeAddress;
use script_traits::{AnimationState, MouseButton, MouseEventType, MozBrowserEvent};

View file

@ -30,6 +30,7 @@ use ipc_channel::ipc::{self, IpcSender};
use js::jsapi::{HandleValue, JSContext};
use offscreen_gl_context::GLContextAttributes;
use rustc_serialize::base64::{STANDARD, ToBase64};
use script_layout_interface::HTMLCanvasData;
use std::iter::repeat;
use string_cache::Atom;
use style::attr::AttrValue;
@ -92,12 +93,6 @@ impl HTMLCanvasElement {
}
}
pub struct HTMLCanvasData {
pub ipc_renderer: Option<IpcSender<CanvasMsg>>,
pub width: u32,
pub height: u32,
}
pub trait LayoutHTMLCanvasElementHelpers {
fn data(&self) -> HTMLCanvasData;
}

View file

@ -38,9 +38,9 @@ use dom::window::{ReflowReason, Window};
use ipc_channel::ipc;
use js::jsapi::{JSAutoCompartment, RootedValue, JSContext, MutableHandleValue};
use js::jsval::{UndefinedValue, NullValue};
use layout_interface::ReflowQueryType;
use msg::constellation_msg::{FrameType, LoadData, NavigationDirection, PipelineId, SubpageId};
use net_traits::response::HttpsState;
use script_layout_interface::message::ReflowQueryType;
use script_traits::IFrameSandboxState::{IFrameSandboxed, IFrameUnsandboxed};
use script_traits::{IFrameLoadInfo, MozBrowserEvent, ScriptMsg as ConstellationMsg};
use std::cell::Cell;

View file

@ -25,9 +25,9 @@ use hyper::header::ContentType;
use hyper::mime::{Mime, TopLevel, SubLevel};
use ipc_channel::ipc;
use ipc_channel::router::ROUTER;
use layout_interface::Msg;
use net_traits::{AsyncResponseListener, AsyncResponseTarget, Metadata, NetworkError};
use network_listener::{NetworkListener, PreInvoke};
use script_layout_interface::message::Msg;
use script_traits::{MozBrowserEvent, ScriptMsg as ConstellationMsg};
use std::ascii::AsciiExt;
use std::borrow::ToOwned;

View file

@ -68,11 +68,6 @@ impl<'a> ProcessDataURL for &'a HTMLObjectElement {
}
}
pub fn is_image_data(uri: &str) -> bool {
static TYPES: &'static [&'static str] = &["data:image/png", "data:image/gif", "data:image/jpeg"];
TYPES.iter().any(|&type_| uri.starts_with(type_))
}
impl HTMLObjectElementMethods for HTMLObjectElement {
// https://html.spec.whatwg.org/multipage/#dom-cva-validity
fn Validity(&self) -> Root<ValidityState> {

View file

@ -14,7 +14,7 @@ use dom::element::Element;
use dom::htmlelement::HTMLElement;
use dom::node::{ChildrenMutation, Node, document_from_node, window_from_node};
use dom::virtualmethods::VirtualMethods;
use layout_interface::Msg;
use script_layout_interface::message::Msg;
use std::sync::Arc;
use string_cache::Atom;
use style::media_queries::parse_media_query_list;

View file

@ -5,7 +5,6 @@
//! The core DOM types. Defines the basic DOM hierarchy as well as all the HTML elements.
use app_units::Au;
use core::nonzero::NonZero;
use devtools_traits::NodeInfo;
use document_loader::DocumentLoader;
use dom::attr::Attr;
@ -22,8 +21,8 @@ use dom::bindings::codegen::UnionTypes::NodeOrString;
use dom::bindings::conversions::{self, DerivedFrom};
use dom::bindings::error::{Error, ErrorResult, Fallible};
use dom::bindings::global::GlobalRef;
use dom::bindings::inheritance::{Castable, CharacterDataTypeId};
use dom::bindings::inheritance::{EventTargetTypeId, NodeTypeId};
use dom::bindings::inheritance::{Castable, CharacterDataTypeId, ElementTypeId};
use dom::bindings::inheritance::{EventTargetTypeId, HTMLElementTypeId, NodeTypeId};
use dom::bindings::js::Root;
use dom::bindings::js::RootedReference;
use dom::bindings::js::{JS, LayoutJS, MutNullableHeap};
@ -38,7 +37,7 @@ use dom::documenttype::DocumentType;
use dom::element::{Element, ElementCreator};
use dom::eventtarget::EventTarget;
use dom::htmlbodyelement::HTMLBodyElement;
use dom::htmlcanvaselement::{LayoutHTMLCanvasElementHelpers, HTMLCanvasData};
use dom::htmlcanvaselement::LayoutHTMLCanvasElementHelpers;
use dom::htmlcollection::HTMLCollection;
use dom::htmlelement::HTMLElement;
use dom::htmliframeelement::{HTMLIFrameElement, HTMLIFrameElementLayoutMethods};
@ -57,11 +56,13 @@ use euclid::size::Size2D;
use heapsize::{HeapSizeOf, heap_size_of};
use html5ever::tree_builder::QuirksMode;
use js::jsapi::{JSContext, JSObject, JSRuntime};
use layout_interface::Msg;
use libc::{self, c_void, uintptr_t};
use msg::constellation_msg::PipelineId;
use parse::html::parse_html_fragment;
use ref_slice::ref_slice;
use script_layout_interface::message::Msg;
use script_layout_interface::{HTMLCanvasData, OpaqueStyleAndLayoutData};
use script_layout_interface::{LayoutNodeType, LayoutElementType, TrustedNodeAddress};
use script_traits::UntrustedNodeAddress;
use selectors::matching::matches;
use selectors::parser::Selector;
@ -74,6 +75,7 @@ use std::iter::{self, FilterMap, Peekable};
use std::mem;
use std::ops::Range;
use string_cache::{Atom, Namespace, QualName};
use style::dom::OpaqueNode;
use style::selector_impl::ServoSelectorImpl;
use url::Url;
use util::thread_state;
@ -171,7 +173,7 @@ impl NodeFlags {
impl Drop for Node {
#[allow(unsafe_code)]
fn drop(&mut self) {
self.style_and_layout_data.get().map(|d| d.dispose(self));
self.style_and_layout_data.get().map(|d| self.dispose(d));
}
}
@ -184,29 +186,15 @@ enum SuppressObserver {
Unsuppressed
}
#[derive(Copy, Clone, HeapSizeOf)]
pub struct OpaqueStyleAndLayoutData {
#[ignore_heap_size_of = "TODO(#6910) Box value that should be counted but \
the type lives in layout"]
pub ptr: NonZero<*mut ()>
}
#[allow(unsafe_code)]
unsafe impl Send for OpaqueStyleAndLayoutData {}
no_jsmanaged_fields!(OpaqueStyleAndLayoutData);
impl OpaqueStyleAndLayoutData {
/// Sends the style and layout data, if any, back to the layout thread to be destroyed.
pub fn dispose(self, node: &Node) {
debug_assert!(thread_state::get().is_script());
let win = window_from_node(node);
node.style_and_layout_data.set(None);
win.layout_chan().send(Msg::ReapStyleAndLayoutData(self)).unwrap();
}
}
impl Node {
/// Sends the style and layout data, if any, back to the layout thread to be destroyed.
pub fn dispose(&self, data: OpaqueStyleAndLayoutData) {
debug_assert!(thread_state::get().is_script());
let win = window_from_node(self);
self.style_and_layout_data.set(None);
win.layout_chan().send(Msg::ReapStyleAndLayoutData(data)).unwrap();
}
/// Adds a new child to the end of this node's list of children.
///
/// Fails unless `new_child` is disconnected from the tree.
@ -292,7 +280,7 @@ impl Node {
for node in child.traverse_preorder() {
node.set_flag(IS_IN_DOC, false);
vtable_for(&&*node).unbind_from_tree(&context);
node.style_and_layout_data.get().map(|d| d.dispose(&node));
node.style_and_layout_data.get().map(|d| node.dispose(d));
}
self.owner_doc().content_and_heritage_changed(self, NodeDamage::OtherNodeDamage);
@ -340,7 +328,7 @@ impl<'a> Iterator for QuerySelectorIterator {
impl Node {
pub fn teardown(&self) {
self.style_and_layout_data.get().map(|d| d.dispose(self));
self.style_and_layout_data.get().map(|d| self.dispose(d));
for kid in self.children() {
kid.teardown();
}
@ -974,6 +962,7 @@ pub trait LayoutNodeHelpers {
fn image_url(&self) -> Option<Url>;
fn canvas_data(&self) -> Option<HTMLCanvasData>;
fn iframe_pipeline_id(&self) -> PipelineId;
fn opaque(&self) -> OpaqueNode;
}
impl LayoutNodeHelpers for LayoutJS<Node> {
@ -1114,6 +1103,13 @@ impl LayoutNodeHelpers for LayoutJS<Node> {
.expect("not an iframe element!");
iframe_element.pipeline_id().unwrap()
}
#[allow(unsafe_code)]
fn opaque(&self) -> OpaqueNode {
unsafe {
OpaqueNode(self.get_jsobject() as usize)
}
}
}
@ -2413,17 +2409,6 @@ impl NodeMethods for Node {
}
}
/// The address of a node known to be valid. These are sent from script to layout,
/// and are also used in the HTML parser interface.
#[derive(Clone, PartialEq, Eq, Copy)]
pub struct TrustedNodeAddress(pub *const c_void);
#[allow(unsafe_code)]
unsafe impl Send for TrustedNodeAddress {}
pub fn document_from_node<T: DerivedFrom<Node> + Reflectable>(derived: &T) -> Root<Document> {
derived.upcast().owner_doc()
}
@ -2648,3 +2633,56 @@ impl UniqueId {
}
}
}
impl Into<LayoutNodeType> for NodeTypeId {
#[inline(always)]
fn into(self) -> LayoutNodeType {
match self {
NodeTypeId::CharacterData(CharacterDataTypeId::Comment) =>
LayoutNodeType::Comment,
NodeTypeId::Document(..) =>
LayoutNodeType::Document,
NodeTypeId::DocumentFragment =>
LayoutNodeType::DocumentFragment,
NodeTypeId::DocumentType =>
LayoutNodeType::DocumentType,
NodeTypeId::Element(e) =>
LayoutNodeType::Element(e.into()),
NodeTypeId::CharacterData(CharacterDataTypeId::ProcessingInstruction) =>
LayoutNodeType::ProcessingInstruction,
NodeTypeId::CharacterData(CharacterDataTypeId::Text) =>
LayoutNodeType::Text,
}
}
}
impl Into<LayoutElementType> for ElementTypeId {
#[inline(always)]
fn into(self) -> LayoutElementType {
match self {
ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLCanvasElement) =>
LayoutElementType::HTMLCanvasElement,
ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLIFrameElement) =>
LayoutElementType::HTMLIFrameElement,
ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLImageElement) =>
LayoutElementType::HTMLImageElement,
ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLInputElement) =>
LayoutElementType::HTMLInputElement,
ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLObjectElement) =>
LayoutElementType::HTMLObjectElement,
ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableCellElement(_)) =>
LayoutElementType::HTMLTableCellElement,
ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableColElement) =>
LayoutElementType::HTMLTableColElement,
ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableElement) =>
LayoutElementType::HTMLTableElement,
ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableRowElement) =>
LayoutElementType::HTMLTableRowElement,
ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableSectionElement) =>
LayoutElementType::HTMLTableSectionElement,
ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTextAreaElement) =>
LayoutElementType::HTMLTextAreaElement,
_ => LayoutElementType::Element,
}
}
}

View file

@ -31,7 +31,7 @@ use dom::element::Element;
use dom::eventtarget::EventTarget;
use dom::location::Location;
use dom::navigator::Navigator;
use dom::node::{Node, TrustedNodeAddress, from_untrusted_node_address, window_from_node};
use dom::node::{Node, from_untrusted_node_address, window_from_node};
use dom::performance::Performance;
use dom::screen::Screen;
use dom::storage::Storage;
@ -42,8 +42,6 @@ use js::jsapi::{Evaluate2, HandleObject, HandleValue, JSAutoCompartment, JSConte
use js::jsapi::{JS_GetRuntime, JS_GC, MutableHandleValue, SetWindowProxy};
use js::rust::CompileOptionsWrapper;
use js::rust::Runtime;
use layout_interface::{ContentBoxResponse, ContentBoxesResponse, ResolvedStyleResponse, ScriptReflow};
use layout_interface::{LayoutRPC, Msg, Reflow, ReflowQueryType, MarginStyleResponse};
use libc;
use msg::constellation_msg::{FrameType, LoadData, PanicMsg, PipelineId, SubpageId};
use msg::constellation_msg::{WindowSizeData, WindowSizeType};
@ -57,8 +55,12 @@ use open;
use profile_traits::mem;
use profile_traits::time::{ProfilerCategory, TimerMetadata, TimerMetadataFrameType};
use profile_traits::time::{ProfilerChan, TimerMetadataReflowType, profile};
use reporter::CSSErrorReporter;
use rustc_serialize::base64::{FromBase64, STANDARD, ToBase64};
use script_layout_interface::TrustedNodeAddress;
use script_layout_interface::message::{Msg, Reflow, ReflowQueryType, ScriptReflow};
use script_layout_interface::reporter::CSSErrorReporter;
use script_layout_interface::rpc::{ContentBoxResponse, ContentBoxesResponse, LayoutRPC};
use script_layout_interface::rpc::{MarginStyleResponse, ResolvedStyleResponse};
use script_runtime::{ScriptChan, ScriptPort};
use script_thread::SendableMainThreadScriptChan;
use script_thread::{MainThreadScriptChan, MainThreadScriptMsg, RunnableWrapper};

View file

@ -1,249 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//! The high-level interface from script to layout. Using this abstract
//! interface helps reduce coupling between these two components, and enables
//! the DOM to be placed in a separate crate from layout.
use app_units::Au;
use euclid::point::Point2D;
use euclid::rect::Rect;
use gfx_traits::{Epoch, LayerId};
use ipc_channel::ipc::{IpcReceiver, IpcSender};
use msg::constellation_msg::{PanicMsg, PipelineId, WindowSizeData};
use net_traits::image_cache_thread::ImageCacheThread;
use profile_traits::mem::ReportsChan;
use script_traits::{ConstellationControlMsg, LayoutControlMsg, LayoutMsg as ConstellationMsg};
use script_traits::{StackingContextScrollState, UntrustedNodeAddress};
use std::sync::Arc;
use std::sync::mpsc::{Receiver, Sender};
use string_cache::Atom;
use style::context::ReflowGoal;
use style::properties::longhands::{margin_top, margin_right, margin_bottom, margin_left, overflow_x};
use style::selector_impl::PseudoElement;
use style::servo::Stylesheet;
use url::Url;
use util::ipc::OptionalOpaqueIpcSender;
pub use dom::bindings::inheritance::{CharacterDataTypeId, ElementTypeId};
pub use dom::bindings::inheritance::{HTMLElementTypeId, NodeTypeId};
pub use dom::bindings::js::LayoutJS;
pub use dom::characterdata::LayoutCharacterDataHelpers;
pub use dom::document::{Document, LayoutDocumentHelpers};
pub use dom::element::{Element, LayoutElementHelpers, RawLayoutElementHelpers};
pub use dom::htmlcanvaselement::HTMLCanvasData;
pub use dom::htmlobjectelement::is_image_data;
pub use dom::node::{CAN_BE_FRAGMENTED, HAS_CHANGED, HAS_DIRTY_DESCENDANTS, IS_DIRTY};
pub use dom::node::LayoutNodeHelpers;
pub use dom::node::Node;
pub use dom::node::OpaqueStyleAndLayoutData;
pub use dom::node::TrustedNodeAddress;
pub use dom::text::Text;
/// Asynchronous messages that script can send to layout.
pub enum Msg {
/// Adds the given stylesheet to the document.
AddStylesheet(Arc<Stylesheet>),
/// Puts a document into quirks mode, causing the quirks mode stylesheet to be loaded.
SetQuirksMode,
/// Requests a reflow.
Reflow(ScriptReflow),
/// Get an RPC interface.
GetRPC(Sender<Box<LayoutRPC + Send>>),
/// Requests that the layout thread render the next frame of all animations.
TickAnimations,
/// Requests that the layout thread reflow with a newly-loaded Web font.
ReflowWithNewlyLoadedWebFont,
/// Updates the layout visible rects, affecting the area that display lists will be constructed
/// for.
SetVisibleRects(Vec<(LayerId, Rect<Au>)>),
/// Destroys layout data associated with a DOM node.
///
/// TODO(pcwalton): Maybe think about batching to avoid message traffic.
ReapStyleAndLayoutData(OpaqueStyleAndLayoutData),
/// Requests that the layout thread measure its memory usage. The resulting reports are sent back
/// via the supplied channel.
CollectReports(ReportsChan),
/// Requests that the layout thread enter a quiescent state in which no more messages are
/// accepted except `ExitMsg`. A response message will be sent on the supplied channel when
/// this happens.
PrepareToExit(Sender<()>),
/// Requests that the layout thread immediately shut down. There must be no more nodes left after
/// this, or layout will crash.
ExitNow,
/// Get the last epoch counter for this layout thread.
GetCurrentEpoch(IpcSender<Epoch>),
/// Asks the layout thread whether any Web fonts have yet to load (if true, loads are pending;
/// false otherwise).
GetWebFontLoadState(IpcSender<bool>),
/// Creates a new layout thread.
///
/// This basically exists to keep the script-layout dependency one-way.
CreateLayoutThread(NewLayoutThreadInfo),
/// Set the final Url.
SetFinalUrl(Url),
/// Tells layout about the new scrolling offsets of each scrollable stacking context.
SetStackingContextScrollStates(Vec<StackingContextScrollState>),
}
/// Synchronous messages that script can send to layout.
///
/// In general, you should use messages to talk to Layout. Use the RPC interface
/// if and only if the work is
///
/// 1) read-only with respect to LayoutThreadData,
/// 2) small,
/// 3) and really needs to be fast.
pub trait LayoutRPC {
/// Requests the dimensions of the content box, as in the `getBoundingClientRect()` call.
fn content_box(&self) -> ContentBoxResponse;
/// Requests the dimensions of all the content boxes, as in the `getClientRects()` call.
fn content_boxes(&self) -> ContentBoxesResponse;
/// Requests the geometry of this node. Used by APIs such as `clientTop`.
fn node_geometry(&self) -> NodeGeometryResponse;
/// Requests the overflow-x and overflow-y of this node. Used by `scrollTop` etc.
fn node_overflow(&self) -> NodeOverflowResponse;
/// Requests the scroll geometry of this node. Used by APIs such as `scrollTop`.
fn node_scroll_area(&self) -> NodeGeometryResponse;
/// Requests the layer id of this node. Used by APIs such as `scrollTop`
fn node_layer_id(&self) -> NodeLayerIdResponse;
/// Requests the node containing the point of interest
fn hit_test(&self) -> HitTestResponse;
/// Query layout for the resolved value of a given CSS property
fn resolved_style(&self) -> ResolvedStyleResponse;
fn offset_parent(&self) -> OffsetParentResponse;
/// Query layout for the resolve values of the margin properties for an element.
fn margin_style(&self) -> MarginStyleResponse;
fn nodes_from_point(&self, point: Point2D<f32>) -> Vec<UntrustedNodeAddress>;
}
#[derive(Clone)]
pub struct MarginStyleResponse {
pub top: margin_top::computed_value::T,
pub right: margin_right::computed_value::T,
pub bottom: margin_bottom::computed_value::T,
pub left: margin_left::computed_value::T,
}
impl MarginStyleResponse {
pub fn empty() -> MarginStyleResponse {
MarginStyleResponse {
top: margin_top::computed_value::T::Auto,
right: margin_right::computed_value::T::Auto,
bottom: margin_bottom::computed_value::T::Auto,
left: margin_left::computed_value::T::Auto,
}
}
}
pub struct NodeOverflowResponse(pub Option<Point2D<overflow_x::computed_value::T>>);
pub struct ContentBoxResponse(pub Rect<Au>);
pub struct ContentBoxesResponse(pub Vec<Rect<Au>>);
pub struct HitTestResponse {
pub node_address: Option<UntrustedNodeAddress>,
}
pub struct NodeGeometryResponse {
pub client_rect: Rect<i32>,
}
pub struct NodeLayerIdResponse {
pub layer_id: LayerId,
}
pub struct ResolvedStyleResponse(pub Option<String>);
#[derive(Clone)]
pub struct OffsetParentResponse {
pub node_address: Option<UntrustedNodeAddress>,
pub rect: Rect<Au>,
}
impl OffsetParentResponse {
pub fn empty() -> OffsetParentResponse {
OffsetParentResponse {
node_address: None,
rect: Rect::zero(),
}
}
}
/// Any query to perform with this reflow.
#[derive(PartialEq)]
pub enum ReflowQueryType {
NoQuery,
ContentBoxQuery(TrustedNodeAddress),
ContentBoxesQuery(TrustedNodeAddress),
NodeOverflowQuery(TrustedNodeAddress),
HitTestQuery(Point2D<f32>, bool),
NodeGeometryQuery(TrustedNodeAddress),
NodeLayerIdQuery(TrustedNodeAddress),
NodeScrollGeometryQuery(TrustedNodeAddress),
ResolvedStyleQuery(TrustedNodeAddress, Option<PseudoElement>, Atom),
OffsetParentQuery(TrustedNodeAddress),
MarginStyleQuery(TrustedNodeAddress),
}
/// Information needed for a reflow.
pub struct Reflow {
/// The goal of reflow: either to render to the screen or to flush layout info for script.
pub goal: ReflowGoal,
/// A clipping rectangle for the page, an enlarged rectangle containing the viewport.
pub page_clip_rect: Rect<Au>,
}
/// Information needed for a script-initiated reflow.
pub struct ScriptReflow {
/// General reflow data.
pub reflow_info: Reflow,
/// The document node.
pub document: TrustedNodeAddress,
/// The document's list of stylesheets.
pub document_stylesheets: Vec<Arc<Stylesheet>>,
/// Whether the document's stylesheets have changed since the last script reflow.
pub stylesheets_changed: bool,
/// The current window size.
pub window_size: WindowSizeData,
/// The channel that we send a notification to.
pub script_join_chan: Sender<()>,
/// The type of query if any to perform during this reflow.
pub query_type: ReflowQueryType,
}
impl Drop for ScriptReflow {
fn drop(&mut self) {
self.script_join_chan.send(()).unwrap();
}
}
pub struct NewLayoutThreadInfo {
pub id: PipelineId,
pub url: Url,
pub is_parent: bool,
pub layout_pair: (Sender<Msg>, Receiver<Msg>),
pub pipeline_port: IpcReceiver<LayoutControlMsg>,
pub constellation_chan: IpcSender<ConstellationMsg>,
pub panic_chan: IpcSender<PanicMsg>,
pub script_chan: IpcSender<ConstellationControlMsg>,
pub image_cache_thread: ImageCacheThread,
pub paint_chan: OptionalOpaqueIpcSender,
pub content_process_shutdown_chan: IpcSender<()>,
}

View file

@ -0,0 +1,999 @@
/* 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/. */
//! A safe wrapper for DOM nodes that prevents layout from mutating the DOM, from letting DOM nodes
//! escape, and from generally doing anything that it isn't supposed to. This is accomplished via
//! a simple whitelist of allowed operations, along with some lifetime magic to prevent nodes from
//! escaping.
//!
//! As a security wrapper is only as good as its whitelist, be careful when adding operations to
//! this list. The cardinal rules are:
//!
//! 1. Layout is not allowed to mutate the DOM.
//!
//! 2. Layout is not allowed to see anything with `LayoutJS` in the name, because it could hang
//! onto these objects and cause use-after-free.
//!
//! When implementing wrapper functions, be careful that you do not touch the borrow flags, or you
//! will race and cause spurious thread failure. (Note that I do not believe these races are
//! exploitable, but they'll result in brokenness nonetheless.)
//!
//! Rules of the road for this file:
//!
//! * Do not call any methods on DOM nodes without checking to see whether they use borrow flags.
//!
//! o Instead of `get_attr()`, use `.get_attr_val_for_layout()`.
//!
//! o Instead of `html_element_in_html_document()`, use
//! `html_element_in_html_document_for_layout()`.
#![allow(unsafe_code)]
use dom::bindings::inheritance::{CharacterDataTypeId, ElementTypeId};
use dom::bindings::inheritance::{HTMLElementTypeId, NodeTypeId};
use dom::bindings::js::LayoutJS;
use dom::characterdata::LayoutCharacterDataHelpers;
use dom::document::{Document, LayoutDocumentHelpers};
use dom::element::{Element, LayoutElementHelpers, RawLayoutElementHelpers};
use dom::node::{CAN_BE_FRAGMENTED, HAS_CHANGED, HAS_DIRTY_DESCENDANTS, IS_DIRTY};
use dom::node::{Node, LayoutNodeHelpers};
use dom::text::Text;
use gfx_traits::ByteIndex;
use msg::constellation_msg::PipelineId;
use range::Range;
use script_layout_interface::restyle_damage::RestyleDamage;
use script_layout_interface::wrapper_traits::{DangerousThreadSafeLayoutNode, LayoutNode, PseudoElementType};
use script_layout_interface::wrapper_traits::{ThreadSafeLayoutNode, ThreadSafeLayoutElement};
use script_layout_interface::{HTMLCanvasData, LayoutNodeType, TrustedNodeAddress};
use script_layout_interface::{OpaqueStyleAndLayoutData, PartialStyleAndLayoutData};
use selectors::matching::{DeclarationBlock, ElementFlags};
use selectors::parser::{AttrSelector, NamespaceConstraint};
use smallvec::VecLike;
use std::cell::{Ref, RefCell, RefMut};
use std::marker::PhantomData;
use std::mem::{transmute, transmute_copy};
use string_cache::{Atom, BorrowedAtom, BorrowedNamespace, Namespace};
use style::attr::AttrValue;
use style::computed_values::display;
use style::dom::{PresentationalHintsSynthetizer, OpaqueNode, TDocument, TElement, TNode, UnsafeNode};
use style::element_state::*;
use style::properties::{ComputedValues, ServoComputedValues};
use style::properties::{PropertyDeclaration, PropertyDeclarationBlock};
use style::restyle_hints::ElementSnapshot;
use style::selector_impl::{NonTSPseudoClass, ServoSelectorImpl};
use style::servo::{PrivateStyleData, SharedStyleContext};
use url::Url;
use util::str::is_whitespace;
#[derive(Copy, Clone)]
pub struct ServoLayoutNode<'a> {
/// The wrapped node.
node: LayoutJS<Node>,
/// Being chained to a PhantomData prevents `LayoutNode`s from escaping.
chain: PhantomData<&'a ()>,
}
impl<'a> PartialEq for ServoLayoutNode<'a> {
#[inline]
fn eq(&self, other: &ServoLayoutNode) -> bool {
self.node == other.node
}
}
impl<'ln> ServoLayoutNode<'ln> {
fn from_layout_js(n: LayoutJS<Node>) -> ServoLayoutNode<'ln> {
ServoLayoutNode {
node: n,
chain: PhantomData,
}
}
pub unsafe fn new(address: &TrustedNodeAddress) -> ServoLayoutNode {
ServoLayoutNode::from_layout_js(LayoutJS::from_trusted_node_address(*address))
}
/// Creates a new layout node with the same lifetime as this layout node.
pub unsafe fn new_with_this_lifetime(&self, node: &LayoutJS<Node>) -> ServoLayoutNode<'ln> {
ServoLayoutNode {
node: *node,
chain: self.chain,
}
}
fn script_type_id(&self) -> NodeTypeId {
unsafe {
self.node.type_id_for_layout()
}
}
}
impl<'ln> TNode for ServoLayoutNode<'ln> {
type ConcreteComputedValues = ServoComputedValues;
type ConcreteElement = ServoLayoutElement<'ln>;
type ConcreteDocument = ServoLayoutDocument<'ln>;
type ConcreteRestyleDamage = RestyleDamage;
fn to_unsafe(&self) -> UnsafeNode {
unsafe {
let ptr: usize = transmute_copy(self);
(ptr, 0)
}
}
unsafe fn from_unsafe(n: &UnsafeNode) -> Self {
let (node, _) = *n;
transmute(node)
}
fn is_text_node(&self) -> bool {
self.script_type_id() == NodeTypeId::CharacterData(CharacterDataTypeId::Text)
}
fn is_element(&self) -> bool {
unsafe {
self.node.is_element_for_layout()
}
}
fn dump(self) {
self.dump_indent(0);
}
fn opaque(&self) -> OpaqueNode {
unsafe { self.get_jsmanaged().opaque() }
}
fn layout_parent_node(self, reflow_root: OpaqueNode) -> Option<ServoLayoutNode<'ln>> {
if self.opaque() == reflow_root {
None
} else {
self.parent_node()
}
}
fn debug_id(self) -> usize {
self.opaque().0
}
fn children_count(&self) -> u32 {
unsafe { self.node.children_count() }
}
fn as_element(&self) -> Option<ServoLayoutElement<'ln>> {
as_element(self.node)
}
fn as_document(&self) -> Option<ServoLayoutDocument<'ln>> {
self.node.downcast().map(ServoLayoutDocument::from_layout_js)
}
fn has_changed(&self) -> bool {
unsafe { self.node.get_flag(HAS_CHANGED) }
}
unsafe fn set_changed(&self, value: bool) {
self.node.set_flag(HAS_CHANGED, value)
}
fn is_dirty(&self) -> bool {
unsafe { self.node.get_flag(IS_DIRTY) }
}
unsafe fn set_dirty(&self, value: bool) {
self.node.set_flag(IS_DIRTY, value)
}
fn has_dirty_descendants(&self) -> bool {
unsafe { self.node.get_flag(HAS_DIRTY_DESCENDANTS) }
}
unsafe fn set_dirty_descendants(&self, value: bool) {
self.node.set_flag(HAS_DIRTY_DESCENDANTS, value)
}
fn can_be_fragmented(&self) -> bool {
unsafe { self.node.get_flag(CAN_BE_FRAGMENTED) }
}
unsafe fn set_can_be_fragmented(&self, value: bool) {
self.node.set_flag(CAN_BE_FRAGMENTED, value)
}
unsafe fn borrow_data_unchecked(&self) -> Option<*const PrivateStyleData> {
self.get_style_data().map(|d| {
&(*d.as_unsafe_cell().get()).style_data as *const _
})
}
fn borrow_data(&self) -> Option<Ref<PrivateStyleData>> {
self.get_style_data().map(|d| {
Ref::map(d.borrow(), |d| &d.style_data)
})
}
fn mutate_data(&self) -> Option<RefMut<PrivateStyleData>> {
self.get_style_data().map(|d| {
RefMut::map(d.borrow_mut(), |d| &mut d.style_data)
})
}
fn restyle_damage(self) -> RestyleDamage {
self.get_style_data().unwrap().borrow().restyle_damage
}
fn set_restyle_damage(self, damage: RestyleDamage) {
self.get_style_data().unwrap().borrow_mut().restyle_damage = damage;
}
fn parent_node(&self) -> Option<ServoLayoutNode<'ln>> {
unsafe {
self.node.parent_node_ref().map(|node| self.new_with_this_lifetime(&node))
}
}
fn first_child(&self) -> Option<ServoLayoutNode<'ln>> {
unsafe {
self.node.first_child_ref().map(|node| self.new_with_this_lifetime(&node))
}
}
fn last_child(&self) -> Option<ServoLayoutNode<'ln>> {
unsafe {
self.node.last_child_ref().map(|node| self.new_with_this_lifetime(&node))
}
}
fn prev_sibling(&self) -> Option<ServoLayoutNode<'ln>> {
unsafe {
self.node.prev_sibling_ref().map(|node| self.new_with_this_lifetime(&node))
}
}
fn next_sibling(&self) -> Option<ServoLayoutNode<'ln>> {
unsafe {
self.node.next_sibling_ref().map(|node| self.new_with_this_lifetime(&node))
}
}
}
impl<'ln> LayoutNode for ServoLayoutNode<'ln> {
type ConcreteThreadSafeLayoutNode = ServoThreadSafeLayoutNode<'ln>;
fn to_threadsafe(&self) -> Self::ConcreteThreadSafeLayoutNode {
ServoThreadSafeLayoutNode::new(self)
}
fn type_id(&self) -> LayoutNodeType {
self.script_type_id().into()
}
fn get_style_data(&self) -> Option<&RefCell<PartialStyleAndLayoutData>> {
unsafe {
self.get_jsmanaged().get_style_and_layout_data().map(|d| {
&**d.ptr
})
}
}
fn init_style_and_layout_data(&self, data: OpaqueStyleAndLayoutData) {
unsafe {
self.get_jsmanaged().init_style_and_layout_data(data);
}
}
fn get_style_and_layout_data(&self) -> Option<OpaqueStyleAndLayoutData> {
unsafe {
self.get_jsmanaged().get_style_and_layout_data()
}
}
}
impl<'ln> ServoLayoutNode<'ln> {
fn dump_indent(self, indent: u32) {
let mut s = String::new();
for _ in 0..indent {
s.push_str(" ");
}
s.push_str(&self.debug_str());
println!("{}", s);
for kid in self.children() {
kid.dump_indent(indent + 1);
}
}
fn debug_str(self) -> String {
format!("{:?}: changed={} dirty={} dirty_descendants={}",
self.script_type_id(), self.has_changed(), self.is_dirty(), self.has_dirty_descendants())
}
/// Returns the interior of this node as a `LayoutJS`. This is highly unsafe for layout to
/// call and as such is marked `unsafe`.
unsafe fn get_jsmanaged(&self) -> &LayoutJS<Node> {
&self.node
}
}
// A wrapper around documents that ensures ayout can only ever access safe properties.
#[derive(Copy, Clone)]
pub struct ServoLayoutDocument<'ld> {
document: LayoutJS<Document>,
chain: PhantomData<&'ld ()>,
}
impl<'ld> TDocument for ServoLayoutDocument<'ld> {
type ConcreteNode = ServoLayoutNode<'ld>;
type ConcreteElement = ServoLayoutElement<'ld>;
fn as_node(&self) -> ServoLayoutNode<'ld> {
ServoLayoutNode::from_layout_js(self.document.upcast())
}
fn root_node(&self) -> Option<ServoLayoutNode<'ld>> {
self.as_node().children().find(ServoLayoutNode::is_element)
}
fn drain_modified_elements(&self) -> Vec<(ServoLayoutElement<'ld>, ElementSnapshot)> {
let elements = unsafe { self.document.drain_modified_elements() };
elements.into_iter().map(|(el, snapshot)| (ServoLayoutElement::from_layout_js(el), snapshot)).collect()
}
}
impl<'ld> ServoLayoutDocument<'ld> {
fn from_layout_js(doc: LayoutJS<Document>) -> ServoLayoutDocument<'ld> {
ServoLayoutDocument {
document: doc,
chain: PhantomData,
}
}
}
/// A wrapper around elements that ensures layout can only ever access safe properties.
#[derive(Copy, Clone)]
pub struct ServoLayoutElement<'le> {
element: LayoutJS<Element>,
chain: PhantomData<&'le ()>,
}
impl<'le> PresentationalHintsSynthetizer for ServoLayoutElement<'le> {
fn synthesize_presentational_hints_for_legacy_attributes<V>(&self, hints: &mut V)
where V: VecLike<DeclarationBlock<Vec<PropertyDeclaration>>>
{
unsafe {
self.element.synthesize_presentational_hints_for_legacy_attributes(hints);
}
}
}
impl<'le> TElement for ServoLayoutElement<'le> {
type ConcreteNode = ServoLayoutNode<'le>;
type ConcreteDocument = ServoLayoutDocument<'le>;
fn as_node(&self) -> ServoLayoutNode<'le> {
ServoLayoutNode::from_layout_js(self.element.upcast())
}
fn style_attribute(&self) -> &Option<PropertyDeclarationBlock> {
unsafe {
&*self.element.style_attribute()
}
}
fn get_state(&self) -> ElementState {
self.element.get_state_for_layout()
}
#[inline]
fn get_attr(&self, namespace: &Namespace, name: &Atom) -> Option<&str> {
unsafe {
(*self.element.unsafe_get()).get_attr_val_for_layout(namespace, name)
}
}
#[inline]
fn get_attrs(&self, name: &Atom) -> Vec<&str> {
unsafe {
(*self.element.unsafe_get()).get_attr_vals_for_layout(name)
}
}
}
impl<'le> ServoLayoutElement<'le> {
fn from_layout_js(el: LayoutJS<Element>) -> ServoLayoutElement<'le> {
ServoLayoutElement {
element: el,
chain: PhantomData,
}
}
}
fn as_element<'le>(node: LayoutJS<Node>) -> Option<ServoLayoutElement<'le>> {
node.downcast().map(ServoLayoutElement::from_layout_js)
}
impl<'le> ::selectors::Element for ServoLayoutElement<'le> {
type Impl = ServoSelectorImpl;
fn parent_element(&self) -> Option<ServoLayoutElement<'le>> {
unsafe {
self.element.upcast().parent_node_ref().and_then(as_element)
}
}
fn first_child_element(&self) -> Option<ServoLayoutElement<'le>> {
self.as_node().children().filter_map(|n| n.as_element()).next()
}
fn last_child_element(&self) -> Option<ServoLayoutElement<'le>> {
self.as_node().rev_children().filter_map(|n| n.as_element()).next()
}
fn prev_sibling_element(&self) -> Option<ServoLayoutElement<'le>> {
let mut node = self.as_node();
while let Some(sibling) = node.prev_sibling() {
if let Some(element) = sibling.as_element() {
return Some(element)
}
node = sibling;
}
None
}
fn next_sibling_element(&self) -> Option<ServoLayoutElement<'le>> {
let mut node = self.as_node();
while let Some(sibling) = node.next_sibling() {
if let Some(element) = sibling.as_element() {
return Some(element)
}
node = sibling;
}
None
}
fn is_root(&self) -> bool {
match self.as_node().parent_node() {
None => false,
Some(node) => {
match node.script_type_id() {
NodeTypeId::Document(_) => true,
_ => false
}
},
}
}
fn is_empty(&self) -> bool {
self.as_node().children().all(|node| match node.script_type_id() {
NodeTypeId::Element(..) => false,
NodeTypeId::CharacterData(CharacterDataTypeId::Text) => unsafe {
node.node.downcast().unwrap().data_for_layout().is_empty()
},
_ => true
})
}
#[inline]
fn get_local_name<'a>(&'a self) -> BorrowedAtom<'a> {
BorrowedAtom(self.element.local_name())
}
#[inline]
fn get_namespace<'a>(&'a self) -> BorrowedNamespace<'a> {
BorrowedNamespace(self.element.namespace())
}
fn match_non_ts_pseudo_class(&self, pseudo_class: NonTSPseudoClass) -> bool {
match pseudo_class {
// https://github.com/servo/servo/issues/8718
NonTSPseudoClass::Link |
NonTSPseudoClass::AnyLink => unsafe {
match self.as_node().script_type_id() {
// https://html.spec.whatwg.org/multipage/#selector-link
NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLAnchorElement)) |
NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLAreaElement)) |
NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLLinkElement)) =>
(*self.element.unsafe_get()).get_attr_val_for_layout(&ns!(), &atom!("href")).is_some(),
_ => false,
}
},
NonTSPseudoClass::Visited => false,
NonTSPseudoClass::ServoNonZeroBorder => unsafe {
match (*self.element.unsafe_get()).get_attr_for_layout(&ns!(), &atom!("border")) {
None | Some(&AttrValue::UInt(_, 0)) => false,
_ => true,
}
},
NonTSPseudoClass::ReadOnly =>
!self.element.get_state_for_layout().contains(pseudo_class.state_flag()),
NonTSPseudoClass::Active |
NonTSPseudoClass::Focus |
NonTSPseudoClass::Hover |
NonTSPseudoClass::Enabled |
NonTSPseudoClass::Disabled |
NonTSPseudoClass::Checked |
NonTSPseudoClass::Indeterminate |
NonTSPseudoClass::ReadWrite |
NonTSPseudoClass::PlaceholderShown =>
self.element.get_state_for_layout().contains(pseudo_class.state_flag())
}
}
#[inline]
fn get_id(&self) -> Option<Atom> {
unsafe {
(*self.element.id_attribute()).clone()
}
}
#[inline]
fn has_class(&self, name: &Atom) -> bool {
unsafe {
self.element.has_class_for_layout(name)
}
}
#[inline(always)]
fn each_class<F>(&self, mut callback: F) where F: FnMut(&Atom) {
unsafe {
if let Some(ref classes) = self.element.get_classes_for_layout() {
for class in *classes {
callback(class)
}
}
}
}
fn match_attr<F>(&self, attr: &AttrSelector, test: F) -> bool where F: Fn(&str) -> bool {
let name = if self.is_html_element_in_html_document() {
&attr.lower_name
} else {
&attr.name
};
match attr.namespace {
NamespaceConstraint::Specific(ref ns) => {
self.get_attr(ns, name).map_or(false, |attr| test(attr))
},
NamespaceConstraint::Any => {
self.get_attrs(name).iter().any(|attr| test(*attr))
}
}
}
fn is_html_element_in_html_document(&self) -> bool {
unsafe {
self.element.html_element_in_html_document_for_layout()
}
}
fn insert_flags(&self, flags: ElementFlags) {
self.element.insert_atomic_flags(flags);
}
}
#[derive(Copy, Clone)]
pub struct ServoThreadSafeLayoutNode<'ln> {
/// The wrapped node.
node: ServoLayoutNode<'ln>,
/// The pseudo-element type, with (optionally),
/// an specified display value to override the stylesheet.
pseudo: PseudoElementType<Option<display::T>>,
}
impl<'a> PartialEq for ServoThreadSafeLayoutNode<'a> {
#[inline]
fn eq(&self, other: &ServoThreadSafeLayoutNode<'a>) -> bool {
self.node == other.node
}
}
impl<'ln> DangerousThreadSafeLayoutNode for ServoThreadSafeLayoutNode<'ln> {
unsafe fn dangerous_first_child(&self) -> Option<Self> {
self.get_jsmanaged().first_child_ref()
.map(|node| self.new_with_this_lifetime(&node))
}
unsafe fn dangerous_next_sibling(&self) -> Option<Self> {
self.get_jsmanaged().next_sibling_ref()
.map(|node| self.new_with_this_lifetime(&node))
}
}
impl<'ln> ServoThreadSafeLayoutNode<'ln> {
/// Creates a new layout node with the same lifetime as this layout node.
pub unsafe fn new_with_this_lifetime(&self, node: &LayoutJS<Node>) -> ServoThreadSafeLayoutNode<'ln> {
ServoThreadSafeLayoutNode {
node: self.node.new_with_this_lifetime(node),
pseudo: PseudoElementType::Normal,
}
}
/// Creates a new `ServoThreadSafeLayoutNode` from the given `ServoLayoutNode`.
pub fn new<'a>(node: &ServoLayoutNode<'a>) -> ServoThreadSafeLayoutNode<'a> {
ServoThreadSafeLayoutNode {
node: node.clone(),
pseudo: PseudoElementType::Normal,
}
}
/// Returns the interior of this node as a `LayoutJS`. This is highly unsafe for layout to
/// call and as such is marked `unsafe`.
unsafe fn get_jsmanaged(&self) -> &LayoutJS<Node> {
self.node.get_jsmanaged()
}
}
impl<'ln> ThreadSafeLayoutNode for ServoThreadSafeLayoutNode<'ln> {
type ConcreteThreadSafeLayoutElement = ServoThreadSafeLayoutElement<'ln>;
type ChildrenIterator = ThreadSafeLayoutNodeChildrenIterator<Self>;
fn with_pseudo(&self,
pseudo: PseudoElementType<Option<display::T>>) -> ServoThreadSafeLayoutNode<'ln> {
ServoThreadSafeLayoutNode {
node: self.node.clone(),
pseudo: pseudo,
}
}
fn opaque(&self) -> OpaqueNode {
unsafe { self.get_jsmanaged().opaque() }
}
fn type_id(&self) -> Option<LayoutNodeType> {
if self.pseudo != PseudoElementType::Normal {
return None
}
Some(self.node.type_id())
}
#[inline]
fn type_id_without_excluding_pseudo_elements(&self) -> LayoutNodeType {
self.node.type_id()
}
fn debug_id(self) -> usize {
self.node.debug_id()
}
fn children(&self) -> Self::ChildrenIterator {
ThreadSafeLayoutNodeChildrenIterator::new(*self)
}
fn as_element(&self) -> ServoThreadSafeLayoutElement<'ln> {
unsafe {
let element = match self.get_jsmanaged().downcast() {
Some(e) => e.unsafe_get(),
None => panic!("not an element")
};
// FIXME(pcwalton): Workaround until Rust gets multiple lifetime parameters on
// implementations.
ServoThreadSafeLayoutElement {
element: &*element,
}
}
}
fn get_pseudo_element_type(&self) -> PseudoElementType<Option<display::T>> {
self.pseudo
}
fn get_style_and_layout_data(&self) -> Option<OpaqueStyleAndLayoutData> {
self.node.get_style_and_layout_data()
}
fn is_ignorable_whitespace(&self, context: &SharedStyleContext) -> bool {
unsafe {
let text: LayoutJS<Text> = match self.get_jsmanaged().downcast() {
Some(text) => text,
None => return false
};
if !is_whitespace(text.upcast().data_for_layout()) {
return false
}
// NB: See the rules for `white-space` here:
//
// http://www.w3.org/TR/CSS21/text.html#propdef-white-space
//
// If you implement other values for this property, you will almost certainly
// want to update this check.
!self.style(context).get_inheritedtext().white_space.preserve_newlines()
}
}
fn restyle_damage(self) -> RestyleDamage {
self.node.restyle_damage()
}
fn set_restyle_damage(self, damage: RestyleDamage) {
self.node.set_restyle_damage(damage)
}
fn can_be_fragmented(&self) -> bool {
self.node.can_be_fragmented()
}
fn node_text_content(&self) -> String {
let this = unsafe { self.get_jsmanaged() };
return this.text_content();
}
fn selection(&self) -> Option<Range<ByteIndex>> {
let this = unsafe { self.get_jsmanaged() };
this.selection().map(|range| {
Range::new(ByteIndex(range.start as isize),
ByteIndex(range.len() as isize))
})
}
fn image_url(&self) -> Option<Url> {
let this = unsafe { self.get_jsmanaged() };
this.image_url()
}
fn canvas_data(&self) -> Option<HTMLCanvasData> {
let this = unsafe { self.get_jsmanaged() };
this.canvas_data()
}
fn iframe_pipeline_id(&self) -> PipelineId {
let this = unsafe { self.get_jsmanaged() };
this.iframe_pipeline_id()
}
fn get_colspan(&self) -> u32 {
unsafe {
self.get_jsmanaged().downcast::<Element>().unwrap().get_colspan()
}
}
fn get_style_data(&self) -> Option<&RefCell<PartialStyleAndLayoutData>> {
self.node.get_style_data()
}
}
pub struct ThreadSafeLayoutNodeChildrenIterator<ConcreteNode: ThreadSafeLayoutNode> {
current_node: Option<ConcreteNode>,
parent_node: ConcreteNode,
}
impl<ConcreteNode> ThreadSafeLayoutNodeChildrenIterator<ConcreteNode>
where ConcreteNode: DangerousThreadSafeLayoutNode {
pub fn new(parent: ConcreteNode) -> Self {
let first_child: Option<ConcreteNode> = match parent.get_pseudo_element_type() {
PseudoElementType::Normal => {
parent.get_before_pseudo().or_else(|| parent.get_details_summary_pseudo()).or_else(|| {
unsafe { parent.dangerous_first_child() }
})
},
PseudoElementType::DetailsContent(_) | PseudoElementType::DetailsSummary(_) => {
unsafe { parent.dangerous_first_child() }
},
_ => None,
};
ThreadSafeLayoutNodeChildrenIterator {
current_node: first_child,
parent_node: parent,
}
}
}
impl<ConcreteNode> Iterator for ThreadSafeLayoutNodeChildrenIterator<ConcreteNode>
where ConcreteNode: DangerousThreadSafeLayoutNode {
type Item = ConcreteNode;
fn next(&mut self) -> Option<ConcreteNode> {
match self.parent_node.get_pseudo_element_type() {
PseudoElementType::Before(_) | PseudoElementType::After(_) => None,
PseudoElementType::DetailsSummary(_) => {
let mut current_node = self.current_node.clone();
loop {
let next_node = if let Some(ref node) = current_node {
if node.is_element() &&
node.as_element().get_local_name() == atom!("summary") &&
node.as_element().get_namespace() == ns!(html) {
self.current_node = None;
return Some(node.clone());
}
unsafe { node.dangerous_next_sibling() }
} else {
self.current_node = None;
return None
};
current_node = next_node;
}
}
PseudoElementType::DetailsContent(_) => {
let node = self.current_node.clone();
let node = node.and_then(|node| {
if node.is_element() &&
node.as_element().get_local_name() == atom!("summary") &&
node.as_element().get_namespace() == ns!(html) {
unsafe { node.dangerous_next_sibling() }
} else {
Some(node)
}
});
self.current_node = node.and_then(|node| unsafe { node.dangerous_next_sibling() });
node
}
PseudoElementType::Normal => {
let node = self.current_node.clone();
if let Some(ref node) = node {
self.current_node = match node.get_pseudo_element_type() {
PseudoElementType::Before(_) => {
let first = self.parent_node.get_details_summary_pseudo().or_else(|| unsafe {
self.parent_node.dangerous_first_child()
});
match first {
Some(first) => Some(first),
None => self.parent_node.get_after_pseudo(),
}
},
PseudoElementType::Normal => {
match unsafe { node.dangerous_next_sibling() } {
Some(next) => Some(next),
None => self.parent_node.get_after_pseudo(),
}
},
PseudoElementType::DetailsSummary(_) => self.parent_node.get_details_content_pseudo(),
PseudoElementType::DetailsContent(_) => self.parent_node.get_after_pseudo(),
PseudoElementType::After(_) => {
None
},
};
}
node
}
}
}
}
/// A wrapper around elements that ensures layout can only
/// ever access safe properties and cannot race on elements.
#[derive(Copy, Clone)]
pub struct ServoThreadSafeLayoutElement<'le> {
element: &'le Element,
}
impl<'le> ThreadSafeLayoutElement for ServoThreadSafeLayoutElement<'le> {
type ConcreteThreadSafeLayoutNode = ServoThreadSafeLayoutNode<'le>;
fn get_attr<'a>(&'a self, namespace: &Namespace, name: &Atom) -> Option<&'a str> {
unsafe {
self.element.get_attr_val_for_layout(namespace, name)
}
}
#[inline]
fn get_local_name<'a>(&'a self) -> BorrowedAtom<'a> {
BorrowedAtom(self.element.local_name())
}
#[inline]
fn get_namespace<'a>(&'a self) -> BorrowedNamespace<'a> {
BorrowedNamespace(self.element.namespace())
}
}
/// This implementation of `::selectors::Element` is used for implementing lazy
/// pseudo-elements.
///
/// Lazy pseudo-elements in Servo only allows selectors using safe properties,
/// i.e., local_name, attributes, so they can only be used for **private**
/// pseudo-elements (like `::-servo-details-content`).
///
/// Probably a few more of this functions can be implemented (like `has_class`,
/// `each_class`, etc), but they have no use right now.
///
/// Note that the element implementation is needed only for selector matching,
/// not for inheritance (styles are inherited appropiately).
impl <'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> {
type Impl = ServoSelectorImpl;
fn parent_element(&self) -> Option<Self> {
warn!("ServoThreadSafeLayoutElement::parent_element called");
None
}
fn first_child_element(&self) -> Option<Self> {
warn!("ServoThreadSafeLayoutElement::first_child_element called");
None
}
// Skips non-element nodes
fn last_child_element(&self) -> Option<Self> {
warn!("ServoThreadSafeLayoutElement::last_child_element called");
None
}
// Skips non-element nodes
fn prev_sibling_element(&self) -> Option<Self> {
warn!("ServoThreadSafeLayoutElement::prev_sibling_element called");
None
}
// Skips non-element nodes
fn next_sibling_element(&self) -> Option<Self> {
warn!("ServoThreadSafeLayoutElement::next_sibling_element called");
None
}
fn is_html_element_in_html_document(&self) -> bool {
debug!("ServoThreadSafeLayoutElement::is_html_element_in_html_document called");
true
}
#[inline]
fn get_local_name<'a>(&'a self) -> BorrowedAtom<'a> {
ThreadSafeLayoutElement::get_local_name(self)
}
#[inline]
fn get_namespace<'a>(&'a self) -> BorrowedNamespace<'a> {
ThreadSafeLayoutElement::get_namespace(self)
}
fn match_non_ts_pseudo_class(&self, _: NonTSPseudoClass) -> bool {
// NB: This could maybe be implemented
warn!("ServoThreadSafeLayoutElement::match_non_ts_pseudo_class called");
false
}
fn get_id(&self) -> Option<Atom> {
debug!("ServoThreadSafeLayoutElement::get_id called");
None
}
fn has_class(&self, _name: &Atom) -> bool {
debug!("ServoThreadSafeLayoutElement::has_class called");
false
}
fn match_attr<F>(&self, attr: &AttrSelector, test: F) -> bool
where F: Fn(&str) -> bool {
match attr.namespace {
NamespaceConstraint::Specific(ref ns) => {
self.get_attr(ns, &attr.name).map_or(false, |attr| test(attr))
},
NamespaceConstraint::Any => {
unsafe {
self.element.get_attr_vals_for_layout(&attr.name).iter()
.any(|attr| test(*attr))
}
}
}
}
fn is_empty(&self) -> bool {
warn!("ServoThreadSafeLayoutElement::is_empty called");
false
}
fn is_root(&self) -> bool {
warn!("ServoThreadSafeLayoutElement::is_root called");
false
}
fn each_class<F>(&self, _callback: F)
where F: FnMut(&Atom) {
warn!("ServoThreadSafeLayoutElement::each_class called");
}
}
impl<'le> PresentationalHintsSynthetizer for ServoThreadSafeLayoutElement<'le> {
fn synthesize_presentational_hints_for_legacy_attributes<V>(&self, _hints: &mut V)
where V: VecLike<DeclarationBlock<Vec<PropertyDeclaration>>> {}
}

View file

@ -65,10 +65,12 @@ extern crate phf;
#[macro_use]
extern crate profile_traits;
extern crate rand;
extern crate range;
extern crate ref_filter_map;
extern crate ref_slice;
extern crate regex;
extern crate rustc_serialize;
extern crate script_layout_interface;
extern crate script_traits;
extern crate selectors;
extern crate serde;
@ -93,12 +95,11 @@ mod devtools;
pub mod document_loader;
#[macro_use]
pub mod dom;
pub mod layout_interface;
pub mod layout_wrapper;
mod mem;
mod network_listener;
pub mod origin;
pub mod parse;
pub mod reporter;
pub mod script_runtime;
#[allow(unsafe_code)]
pub mod script_thread;

View file

@ -1,44 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use cssparser::{Parser, SourcePosition};
use ipc_channel::ipc::IpcSender;
use log;
use msg::constellation_msg::PipelineId;
use script_traits::ConstellationControlMsg;
use std::sync::{Mutex, Arc};
use style::error_reporting::ParseErrorReporter;
#[derive(JSTraceable, HeapSizeOf)]
pub struct CSSErrorReporter {
pub pipelineid: PipelineId,
// Arc+Mutex combo is necessary to make this struct Sync,
// which is necessary to fulfill the bounds required by the
// uses of the ParseErrorReporter trait.
#[ignore_heap_size_of = "Arc is defined in libstd"]
pub script_chan: Arc<Mutex<IpcSender<ConstellationControlMsg>>>,
}
impl ParseErrorReporter for CSSErrorReporter {
fn report_error(&self, input: &mut Parser, position: SourcePosition, message: &str) {
let location = input.source_location(position);
if log_enabled!(log::LogLevel::Info) {
info!("{}:{} {}", location.line, location.column, message)
}
//TODO: report a real filename
let _ = self.script_chan.lock().unwrap().send(
ConstellationControlMsg::ReportCSSError(self.pipelineid,
"".to_owned(),
location.line,
location.column,
message.to_owned()));
}
fn clone(&self) -> Box<ParseErrorReporter + Send + Sync> {
box CSSErrorReporter {
pipelineid: self.pipelineid,
script_chan: self.script_chan.clone(),
}
}
}

View file

@ -61,7 +61,6 @@ use js::jsapi::{JSAutoCompartment, JSContext, JS_SetWrapObjectCallbacks};
use js::jsapi::{JSTracer, SetWindowProxyClass};
use js::jsval::UndefinedValue;
use js::rust::Runtime;
use layout_interface::{self, NewLayoutThreadInfo, ReflowQueryType};
use mem::heap_size_of_self_and_children;
use msg::constellation_msg::{FrameType, LoadData, PanicMsg, PipelineId, PipelineNamespace};
use msg::constellation_msg::{SubpageId, WindowSizeData, WindowSizeType};
@ -77,6 +76,7 @@ use parse::html::{ParseContext, parse_html};
use parse::xml::{self, parse_xml};
use profile_traits::mem::{self, OpaqueSender, Report, ReportKind, ReportsChan};
use profile_traits::time::{self, ProfilerCategory, profile};
use script_layout_interface::message::{self, NewLayoutThreadInfo, ReflowQueryType};
use script_runtime::{CommonScriptMsg, ScriptChan, ScriptThreadEventCategory};
use script_runtime::{ScriptPort, StackRootTLS, new_rt_and_cx, get_reports};
use script_traits::CompositorEvent::{KeyEvent, MouseButtonEvent, MouseMoveEvent, ResizeEvent};
@ -135,7 +135,7 @@ struct InProgressLoad {
/// The current window size associated with this pipeline.
window_size: Option<WindowSizeData>,
/// Channel to the layout thread associated with this pipeline.
layout_chan: Sender<layout_interface::Msg>,
layout_chan: Sender<message::Msg>,
/// The current viewport clipping rectangle applying to this pipeline, if any.
clip_rect: Option<Rect<f32>>,
/// Window is frozen (navigated away while loading for example).
@ -150,7 +150,7 @@ impl InProgressLoad {
/// Create a new InProgressLoad object.
fn new(id: PipelineId,
parent_info: Option<(PipelineId, SubpageId, FrameType)>,
layout_chan: Sender<layout_interface::Msg>,
layout_chan: Sender<message::Msg>,
window_size: Option<WindowSizeData>,
url: Url) -> InProgressLoad {
InProgressLoad {
@ -438,11 +438,11 @@ impl<'a> Drop for ScriptMemoryFailsafe<'a> {
}
impl ScriptThreadFactory for ScriptThread {
type Message = layout_interface::Msg;
type Message = message::Msg;
fn create(state: InitialScriptState,
load_data: LoadData)
-> (Sender<layout_interface::Msg>, Receiver<layout_interface::Msg>) {
-> (Sender<message::Msg>, Receiver<message::Msg>) {
let panic_chan = state.panic_chan.clone();
let (script_chan, script_port) = channel();
@ -1184,7 +1184,7 @@ impl ScriptThread {
// Tell layout to actually spawn the thread.
parent_window.layout_chan()
.send(layout_interface::Msg::CreateLayoutThread(layout_creation_info))
.send(message::Msg::CreateLayoutThread(layout_creation_info))
.unwrap();
// Kick off the fetch for the new resource.
@ -1462,10 +1462,10 @@ impl ScriptThread {
// processed this message.
let (response_chan, response_port) = channel();
let chan = &load.layout_chan;
if chan.send(layout_interface::Msg::PrepareToExit(response_chan)).is_ok() {
if chan.send(message::Msg::PrepareToExit(response_chan)).is_ok() {
debug!("shutting down layout for page {:?}", id);
response_port.recv().unwrap();
chan.send(layout_interface::Msg::ExitNow).ok();
chan.send(message::Msg::ExitNow).ok();
}
let has_pending_loads = self.incomplete_loads.borrow().len() > 0;
@ -1523,7 +1523,7 @@ impl ScriptThread {
{
// send the final url to the layout thread.
incomplete.layout_chan
.send(layout_interface::Msg::SetFinalUrl(final_url.clone()))
.send(message::Msg::SetFinalUrl(final_url.clone()))
.unwrap();
// update the pipeline url
@ -2126,7 +2126,7 @@ fn shut_down_layout(context_tree: &BrowsingContext) {
let (response_chan, response_port) = channel();
let window = context.active_window();
let chan = window.layout_chan().clone();
if chan.send(layout_interface::Msg::PrepareToExit(response_chan)).is_ok() {
if chan.send(message::Msg::PrepareToExit(response_chan)).is_ok() {
channels.push(chan);
response_port.recv().unwrap();
}
@ -2143,7 +2143,7 @@ fn shut_down_layout(context_tree: &BrowsingContext) {
// Destroy the layout thread. If there were node leaks, layout will now crash safely.
for chan in channels {
chan.send(layout_interface::Msg::ExitNow).ok();
chan.send(message::Msg::ExitNow).ok();
}
}