mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
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:
commit
ee8c5c5a67
51 changed files with 2215 additions and 1840 deletions
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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<()>,
|
||||
}
|
999
components/script/layout_wrapper.rs
Normal file
999
components/script/layout_wrapper.rs
Normal 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>>> {}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue