diff options
author | Jack Moffitt <jack@metajack.im> | 2014-08-28 09:34:23 -0600 |
---|---|---|
committer | Jack Moffitt <jack@metajack.im> | 2014-09-08 20:21:42 -0600 |
commit | c6ab60dbfc6da7b4f800c9e40893c8b58413960c (patch) | |
tree | d1d74076cf7fa20e4f77ec7cb82cae98b67362cb /components/script/layout_interface.rs | |
parent | db2f642c32fc5bed445bb6f2e45b0f6f0b4342cf (diff) | |
download | servo-c6ab60dbfc6da7b4f800c9e40893c8b58413960c.tar.gz servo-c6ab60dbfc6da7b4f800c9e40893c8b58413960c.zip |
Cargoify servo
Diffstat (limited to 'components/script/layout_interface.rs')
-rw-r--r-- | components/script/layout_interface.rs | 204 |
1 files changed, 204 insertions, 0 deletions
diff --git a/components/script/layout_interface.rs b/components/script/layout_interface.rs new file mode 100644 index 00000000000..1e5e23f9c9a --- /dev/null +++ b/components/script/layout_interface.rs @@ -0,0 +1,204 @@ +/* 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 dom::bindings::js::JS; +use dom::node::{Node, LayoutDataRef}; + +use geom::point::Point2D; +use geom::rect::Rect; +use libc::c_void; +use script_traits::{ScriptControlChan, OpaqueScriptLayoutChannel}; +use servo_msg::constellation_msg::WindowSizeData; +use servo_util::geometry::Au; +use std::any::{Any, AnyRefExt}; +use std::cmp; +use std::comm::{channel, Receiver, Sender}; +use std::owned::BoxAny; +use style::Stylesheet; +use url::Url; + +use serialize::{Encodable, Encoder}; + +/// Asynchronous messages that script can send to layout. +pub enum Msg { + /// Adds the given stylesheet to the document. + AddStylesheetMsg(Stylesheet), + + /// Requests a reflow. + ReflowMsg(Box<Reflow>), + + /// Get an RPC interface. + GetRPCMsg(Sender<Box<LayoutRPC + Send>>), + + /// Destroys layout data associated with a DOM node. + /// + /// TODO(pcwalton): Maybe think about batching to avoid message traffic. + ReapLayoutDataMsg(LayoutDataRef), + + /// Requests that the layout task 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. + PrepareToExitMsg(Sender<()>), + + /// Requests that the layout task immediately shut down. There must be no more nodes left after + /// this, or layout will crash. + ExitNowMsg, +} + +/// 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 LayoutTaskData, +/// 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, node: TrustedNodeAddress) -> ContentBoxResponse; + /// Requests the dimensions of all the content boxes, as in the `getClientRects()` call. + fn content_boxes(&self, node: TrustedNodeAddress) -> ContentBoxesResponse; + /// Requests the node containing the point of interest + fn hit_test(&self, node: TrustedNodeAddress, point: Point2D<f32>) -> Result<HitTestResponse, ()>; + fn mouse_over(&self, node: TrustedNodeAddress, point: Point2D<f32>) -> Result<MouseOverResponse, ()>; +} + +/// The address of a node known to be valid. These must only be sent from content -> layout, +/// because we do not trust layout. +pub struct TrustedNodeAddress(pub *const c_void); + +impl<S: Encoder<E>, E> Encodable<S, E> for TrustedNodeAddress { + fn encode(&self, s: &mut S) -> Result<(), E> { + let TrustedNodeAddress(addr) = *self; + let node = addr as *const Node; + unsafe { + JS::from_raw(node).encode(s) + } + } +} + +/// The address of a node. Layout sends these back. They must be validated via +/// `from_untrusted_node_address` before they can be used, because we do not trust layout. +pub type UntrustedNodeAddress = *const c_void; + +pub struct ContentBoxResponse(pub Rect<Au>); +pub struct ContentBoxesResponse(pub Vec<Rect<Au>>); +pub struct HitTestResponse(pub UntrustedNodeAddress); +pub struct MouseOverResponse(pub Vec<UntrustedNodeAddress>); + +/// Determines which part of the +#[deriving(PartialEq, PartialOrd, Eq, Ord, Encodable)] +pub enum DocumentDamageLevel { + /// Reflow, but do not perform CSS selector matching. + ReflowDocumentDamage, + /// Perform CSS selector matching and reflow. + MatchSelectorsDocumentDamage, + /// Content changed; set full style damage and do the above. + ContentChangedDocumentDamage, +} + +impl DocumentDamageLevel { + /// Sets this damage to the maximum of this damage and the given damage. + pub fn add(&mut self, new_damage: DocumentDamageLevel) { + *self = cmp::max(*self, new_damage); + } +} + +/// What parts of the document have changed, as far as the script task can tell. +/// +/// Note that this is fairly coarse-grained and is separate from layout's notion of the document +#[deriving(Encodable)] +pub struct DocumentDamage { + /// The topmost node in the tree that has changed. + pub root: TrustedNodeAddress, + /// The amount of damage that occurred. + pub level: DocumentDamageLevel, +} + +/// Why we're doing reflow. +#[deriving(PartialEq)] +pub enum ReflowGoal { + /// We're reflowing in order to send a display list to the screen. + ReflowForDisplay, + /// We're reflowing in order to satisfy a script query. No display list will be created. + ReflowForScriptQuery, +} + +/// Information needed for a reflow. +pub struct Reflow { + /// The document node. + pub document_root: TrustedNodeAddress, + /// The style changes that need to be done. + pub damage: DocumentDamage, + /// The goal of reflow: either to render to the screen or to flush layout info for script. + pub goal: ReflowGoal, + /// The URL of the page. + pub url: Url, + /// The channel through which messages can be sent back to the script task. + pub script_chan: ScriptControlChan, + /// The current window size. + pub window_size: WindowSizeData, + /// The channel that we send a notification to. + pub script_join_chan: Sender<()>, + /// Unique identifier + pub id: uint +} + +/// Encapsulates a channel to the layout task. +#[deriving(Clone)] +pub struct LayoutChan(pub Sender<Msg>); + +impl LayoutChan { + pub fn new() -> (Receiver<Msg>, LayoutChan) { + let (chan, port) = channel(); + (port, LayoutChan(chan)) + } +} + +/// A trait to manage opaque references to script<->layout channels without needing +/// to expose the message type to crates that don't need to know about them. +pub trait ScriptLayoutChan { + fn new(sender: Sender<Msg>, receiver: Receiver<Msg>) -> Self; + fn sender(&self) -> Sender<Msg>; + fn receiver(self) -> Receiver<Msg>; +} + +impl ScriptLayoutChan for OpaqueScriptLayoutChannel { + fn new(sender: Sender<Msg>, receiver: Receiver<Msg>) -> OpaqueScriptLayoutChannel { + let inner = (box sender as Box<Any+Send>, box receiver as Box<Any+Send>); + OpaqueScriptLayoutChannel(inner) + } + + fn sender(&self) -> Sender<Msg> { + let &OpaqueScriptLayoutChannel((ref sender, _)) = self; + (*sender.downcast_ref::<Sender<Msg>>().unwrap()).clone() + } + + fn receiver(self) -> Receiver<Msg> { + let OpaqueScriptLayoutChannel((_, receiver)) = self; + *receiver.downcast::<Receiver<Msg>>().unwrap() + } +} + +#[test] +fn test_add_damage() { + fn assert_add(mut a: DocumentDamageLevel, b: DocumentDamageLevel, + result: DocumentDamageLevel) { + a.add(b); + assert!(a == result); + } + + assert_add(ReflowDocumentDamage, ReflowDocumentDamage, ReflowDocumentDamage); + assert_add(ContentChangedDocumentDamage, ContentChangedDocumentDamage, ContentChangedDocumentDamage); + assert_add(ReflowDocumentDamage, MatchSelectorsDocumentDamage, MatchSelectorsDocumentDamage); + assert_add(MatchSelectorsDocumentDamage, ReflowDocumentDamage, MatchSelectorsDocumentDamage); + assert_add(ReflowDocumentDamage, ContentChangedDocumentDamage, ContentChangedDocumentDamage); + assert_add(ContentChangedDocumentDamage, ReflowDocumentDamage, ContentChangedDocumentDamage); + assert_add(MatchSelectorsDocumentDamage, ContentChangedDocumentDamage, ContentChangedDocumentDamage); + assert_add(ContentChangedDocumentDamage, MatchSelectorsDocumentDamage, ContentChangedDocumentDamage); +} |