Skip to content

Commit

Permalink
Completed implementation of devtools' getLayout.
Browse files Browse the repository at this point in the history
  • Loading branch information
benschulz authored and jdm committed Feb 24, 2016
1 parent 9ab2da3 commit 0785d91
Show file tree
Hide file tree
Showing 13 changed files with 222 additions and 39 deletions.
1 change: 1 addition & 0 deletions components/devtools/Cargo.toml
Expand Up @@ -27,5 +27,6 @@ hyper = { version = "0.7", features = [ "serde-serialization" ] }
log = "0.3"
rustc-serialize = "0.3"
serde = "0.6"
serde_json = "0.6"
serde_macros = "0.6"
time = "0.1"
106 changes: 78 additions & 28 deletions components/devtools/actors/inspector.rs
Expand Up @@ -12,7 +12,8 @@ use devtools_traits::{ComputedNodeLayout, DevtoolScriptControlMsg, NodeInfo};
use ipc_channel::ipc::{self, IpcSender};
use msg::constellation_msg::PipelineId;
use protocol::JsonPacketStream;
use rustc_serialize::json::{self, Json, ToJson};
use rustc_serialize::json::{self, Json};
use serde_json;
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::net::TcpStream;
Expand Down Expand Up @@ -397,21 +398,49 @@ struct AppliedSheet {
ruleCount: usize,
}

#[derive(RustcEncodable)]
#[derive(Serialize)]
struct GetLayoutReply {
width: i32,
height: i32,
autoMargins: Json,
from: String,
}

#[derive(RustcEncodable)]
#[allow(dead_code)]
struct AutoMargins {
top: String,
bottom: String,
left: String,
right: String,
display: String,
position: String,
#[serde(rename = "z-index")]
zIndex: String,
#[serde(rename = "box-sizing")]
boxSizing: String,

// Would be nice to use a proper struct, blocked by
// https://github.com/serde-rs/serde/issues/43
autoMargins: serde_json::value::Value,
#[serde(rename = "margin-top")]
marginTop: String,
#[serde(rename = "margin-right")]
marginRight: String,
#[serde(rename = "margin-bottom")]
marginBottom: String,
#[serde(rename = "margin-left")]
marginLeft: String,

#[serde(rename = "border-top-width")]
borderTopWidth: String,
#[serde(rename = "border-right-width")]
borderRightWidth: String,
#[serde(rename = "border-bottom-width")]
borderBottomWidth: String,
#[serde(rename = "border-left-width")]
borderLeftWidth: String,

#[serde(rename = "padding-top")]
paddingTop: String,
#[serde(rename = "padding-right")]
paddingRight: String,
#[serde(rename = "padding-bottom")]
paddingBottom: String,
#[serde(rename = "padding-left")]
paddingLeft: String,

width: f32,
height: f32,
}

impl Actor for PageStyleActor {
Expand Down Expand Up @@ -455,31 +484,52 @@ impl Actor for PageStyleActor {
registry.actor_to_script(target.to_owned()),
tx))
.unwrap();
let ComputedNodeLayout { width, height } = rx.recv().unwrap();
let ComputedNodeLayout {
display, position, zIndex, boxSizing,
autoMargins, marginTop, marginRight, marginBottom, marginLeft,
borderTopWidth, borderRightWidth, borderBottomWidth, borderLeftWidth,
paddingTop, paddingRight, paddingBottom, paddingLeft,
width, height,
} = rx.recv().unwrap();

let auto_margins = msg.get("autoMargins")
.and_then(&Json::as_boolean).unwrap_or(false);

//TODO: the remaining layout properties (margin, border, padding, position)
// as specified in getLayout in
// http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/server/actors/styles.js
// http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/server/actors/styles.js
let msg = GetLayoutReply {
width: width.round() as i32,
height: height.round() as i32,
from: self.name(),
display: display,
position: position,
zIndex: zIndex,
boxSizing: boxSizing,
autoMargins: if auto_margins {
//TODO: real values like processMargins in
// http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/server/actors/styles.js
let mut m = BTreeMap::new();
m.insert("top".to_owned(), "auto".to_owned().to_json());
m.insert("bottom".to_owned(), "auto".to_owned().to_json());
m.insert("left".to_owned(), "auto".to_owned().to_json());
m.insert("right".to_owned(), "auto".to_owned().to_json());
Json::Object(m)
let auto = serde_json::value::Value::String("auto".to_owned());
if autoMargins.top { m.insert("top".to_owned(), auto.clone()); }
if autoMargins.right { m.insert("right".to_owned(), auto.clone()); }
if autoMargins.bottom { m.insert("bottom".to_owned(), auto.clone()); }
if autoMargins.left { m.insert("left".to_owned(), auto.clone()); }
serde_json::value::Value::Object(m)
} else {
Json::Null
serde_json::value::Value::Null
},
from: self.name(),
marginTop: marginTop,
marginRight: marginRight,
marginBottom: marginBottom,
marginLeft: marginLeft,
borderTopWidth: borderTopWidth,
borderRightWidth: borderRightWidth,
borderBottomWidth: borderBottomWidth,
borderLeftWidth: borderLeftWidth,
paddingTop: paddingTop,
paddingRight: paddingRight,
paddingBottom: paddingBottom,
paddingLeft: paddingLeft,
width: width,
height: height,
};
let msg = &serde_json::to_string(&msg).unwrap();
let msg = Json::from_str(msg).unwrap();
stream.write_json_packet(&msg);
ActorMessageStatus::Processed
}
Expand Down
2 changes: 2 additions & 0 deletions components/devtools/lib.rs
Expand Up @@ -11,6 +11,7 @@
#![crate_type = "rlib"]

#![feature(box_syntax)]
#![feature(custom_attribute)]
#![feature(custom_derive)]
#![feature(plugin)]
#![plugin(serde_macros)]
Expand All @@ -27,6 +28,7 @@ extern crate log;
extern crate msg;
extern crate rustc_serialize;
extern crate serde;
extern crate serde_json;
extern crate time;
extern crate util;

Expand Down
29 changes: 29 additions & 0 deletions components/devtools_traits/lib.rs
Expand Up @@ -157,10 +157,39 @@ pub enum TimelineMarkerType {
/// The properties of a DOM node as computed by layout.
#[derive(Deserialize, Serialize)]
pub struct ComputedNodeLayout {
pub display: String,
pub position: String,
pub zIndex: String,
pub boxSizing: String,

pub autoMargins: AutoMargins,
pub marginTop: String,
pub marginRight: String,
pub marginBottom: String,
pub marginLeft: String,

pub borderTopWidth: String,
pub borderRightWidth: String,
pub borderBottomWidth: String,
pub borderLeftWidth: String,

pub paddingTop: String,
pub paddingRight: String,
pub paddingBottom: String,
pub paddingLeft: String,

pub width: f32,
pub height: f32,
}

#[derive(Deserialize, Serialize)]
pub struct AutoMargins {
pub top: bool,
pub right: bool,
pub bottom: bool,
pub left: bool,
}

/// Messages to process in a particular script thread, as instructed by a devtools client.
#[derive(Deserialize, Serialize)]
pub enum DevtoolScriptControlMsg {
Expand Down
16 changes: 14 additions & 2 deletions components/layout/layout_thread.rs
Expand Up @@ -42,9 +42,10 @@ use profile_traits::mem::{self, Report, ReportKind, ReportsChan};
use profile_traits::time::{TimerMetadataFrameType, TimerMetadataReflowType};
use profile_traits::time::{self, TimerMetadata, profile};
use query::{LayoutRPCImpl, process_content_box_request, process_content_boxes_request};
use query::{process_node_geometry_request, process_offset_parent_query, process_resolved_style_request};
use query::{process_node_geometry_request, process_offset_parent_query};
use query::{process_resolved_style_request, process_margin_style_query};
use script::dom::node::OpaqueStyleAndLayoutData;
use script::layout_interface::{LayoutRPC, OffsetParentResponse};
use script::layout_interface::{LayoutRPC, OffsetParentResponse, MarginStyleResponse};
use script::layout_interface::{Msg, NewLayoutThreadInfo, Reflow, ReflowQueryType};
use script::layout_interface::{ScriptLayoutChan, ScriptReflow};
use script::reporter::CSSErrorReporter;
Expand Down Expand Up @@ -117,6 +118,9 @@ pub struct LayoutThreadData {

/// A queued response for the offset parent/rect of a node.
pub offset_parent_response: OffsetParentResponse,

/// A queued response for the offset parent/rect of a node.
pub margin_style_response: MarginStyleResponse,
}

/// Information needed by the layout thread.
Expand Down Expand Up @@ -455,6 +459,7 @@ impl LayoutThread {
client_rect_response: Rect::zero(),
resolved_style_response: None,
offset_parent_response: OffsetParentResponse::empty(),
margin_style_response: MarginStyleResponse::empty(),
})),
error_reporter: CSSErrorReporter {
pipelineid: id,
Expand Down Expand Up @@ -986,6 +991,9 @@ impl LayoutThread {
ReflowQueryType::OffsetParentQuery(_) => {
rw_data.offset_parent_response = OffsetParentResponse::empty();
},
ReflowQueryType::MarginStyleQuery(_) => {
rw_data.margin_style_response = MarginStyleResponse::empty();
},
ReflowQueryType::NoQuery => {}
}
return;
Expand Down Expand Up @@ -1129,6 +1137,10 @@ impl LayoutThread {
let node = unsafe { ServoLayoutNode::new(&node) };
rw_data.offset_parent_response = process_offset_parent_query(node, &mut root_flow);
},
ReflowQueryType::MarginStyleQuery(node) => {
let node = unsafe { ServoLayoutNode::new(&node) };
rw_data.margin_style_response = process_margin_style_query(node);
},
ReflowQueryType::NoQuery => {}
}
}
Expand Down
22 changes: 21 additions & 1 deletion components/layout/query.rs
Expand Up @@ -17,7 +17,7 @@ use msg::constellation_msg::ConstellationChan;
use opaque_node::OpaqueNodeMethods;
use script::layout_interface::{ContentBoxResponse, ContentBoxesResponse, NodeGeometryResponse};
use script::layout_interface::{HitTestResponse, LayoutRPC, MouseOverResponse, OffsetParentResponse};
use script::layout_interface::{ResolvedStyleResponse, ScriptLayoutChan};
use script::layout_interface::{ResolvedStyleResponse, ScriptLayoutChan, MarginStyleResponse};
use script_traits::LayoutMsg as ConstellationMsg;
use sequential;
use std::ops::Deref;
Expand Down Expand Up @@ -131,6 +131,12 @@ impl LayoutRPC for LayoutRPCImpl {
let rw_data = rw_data.lock().unwrap();
rw_data.offset_parent_response.clone()
}

fn margin_style(&self) -> MarginStyleResponse {
let &LayoutRPCImpl(ref rw_data) = self;
let rw_data = rw_data.lock().unwrap();
rw_data.margin_style_response.clone()
}
}

struct UnioningFragmentBorderBoxIterator {
Expand Down Expand Up @@ -575,3 +581,17 @@ pub fn process_offset_parent_query<'ln, N: LayoutNode<'ln>>(requested_node: N, l
}
}
}

pub fn process_margin_style_query<'ln, N: LayoutNode<'ln>>(requested_node: N)
-> MarginStyleResponse {
let layout_node = requested_node.to_threadsafe();
let style = &*layout_node.style();
let margin = style.get_margin();

MarginStyleResponse {
top: margin.margin_top,
right: margin.margin_right,
bottom: margin.margin_bottom,
left: margin.margin_left,
}
}
49 changes: 43 additions & 6 deletions components/script/devtools.rs
Expand Up @@ -2,18 +2,22 @@
* 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 devtools_traits::{CONSOLE_API, CachedConsoleMessage, CachedConsoleMessageTypes, PAGE_ERROR};
use devtools_traits::TimelineMarkerType;
use devtools_traits::{AutoMargins, CONSOLE_API, CachedConsoleMessage, CachedConsoleMessageTypes};
use devtools_traits::{ComputedNodeLayout, ConsoleAPI, PageError, ScriptToDevtoolsControlMsg};
use devtools_traits::{EvaluateJSReply, Modification, NodeInfo, TimelineMarker, TimelineMarkerType};
use devtools_traits::{EvaluateJSReply, Modification, NodeInfo, PAGE_ERROR, TimelineMarker};
use dom::bindings::codegen::Bindings::CSSStyleDeclarationBinding::CSSStyleDeclarationMethods;
use dom::bindings::codegen::Bindings::DOMRectBinding::DOMRectMethods;
use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
use dom::bindings::codegen::Bindings::ElementBinding::ElementMethods;
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
use dom::bindings::conversions::{FromJSValConvertible, jsstring_to_str};
use dom::bindings::global::GlobalRef;
use dom::bindings::inheritance::Castable;
use dom::bindings::js::Root;
use dom::element::Element;
use dom::node::Node;
use dom::window::Window;
use ipc_channel::ipc::IpcSender;
use js::jsapi::{ObjectClassName, RootedObject, RootedValue};
use js::jsval::UndefinedValue;
Expand All @@ -23,6 +27,7 @@ use script_thread::get_page;
use std::ffi::CStr;
use std::rc::Rc;
use std::str;
use style::properties::longhands::{margin_top, margin_right, margin_bottom, margin_left};
use util::str::DOMString;
use uuid::Uuid;

Expand Down Expand Up @@ -110,15 +115,47 @@ pub fn handle_get_layout(page: &Rc<Page>,
node_id: String,
reply: IpcSender<ComputedNodeLayout>) {
let node = find_node_by_unique_id(&*page, pipeline, node_id);

let elem = node.downcast::<Element>().expect("should be getting layout of element");
let rect = elem.GetBoundingClientRect();
let width = rect.Width() as f32;
let height = rect.Height() as f32;

let window = page.window();
let elem = node.downcast::<Element>().expect("should be getting layout of element");
let computed_style = window.r().GetComputedStyle(elem, None);

reply.send(ComputedNodeLayout {
width: width,
height: height,
})
.unwrap();
display: String::from(computed_style.Display()),
position: String::from(computed_style.Position()),
zIndex: String::from(computed_style.ZIndex()),
boxSizing: String::from(computed_style.BoxSizing()),
autoMargins: determine_auto_margins(&window, &*node),
marginTop: String::from(computed_style.MarginTop()),
marginRight: String::from(computed_style.MarginRight()),
marginBottom: String::from(computed_style.MarginBottom()),
marginLeft: String::from(computed_style.MarginLeft()),
borderTopWidth: String::from(computed_style.BorderTopWidth()),
borderRightWidth: String::from(computed_style.BorderRightWidth()),
borderBottomWidth: String::from(computed_style.BorderBottomWidth()),
borderLeftWidth: String::from(computed_style.BorderLeftWidth()),
paddingTop: String::from(computed_style.PaddingTop()),
paddingRight: String::from(computed_style.PaddingRight()),
paddingBottom: String::from(computed_style.PaddingBottom()),
paddingLeft: String::from(computed_style.PaddingLeft()),
width: width,
height: height,
}).unwrap();
}

fn determine_auto_margins(window: &Window, node: &Node) -> AutoMargins {
let margin = window.margin_style_query(node.to_trusted_node_address());
AutoMargins {
top: margin.top == margin_top::computed_value::T::Auto,
right: margin.right == margin_right::computed_value::T::Auto,
bottom: margin.bottom == margin_bottom::computed_value::T::Auto,
left: margin.left == margin_left::computed_value::T::Auto,
}
}

pub fn handle_get_cached_messages(_pipeline_id: PipelineId,
Expand Down
1 change: 0 additions & 1 deletion components/script/dom/node.rs
Expand Up @@ -185,7 +185,6 @@ 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) {
Expand Down

0 comments on commit 0785d91

Please sign in to comment.