Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lots of work on bindings #397

Merged
merged 10 commits into from Apr 24, 2013

Add getBoundingClientRect, and make it and getClientRects synchronous…

…ly query layout. Associate flows with DOM nodes to allow this querying to occur. Alleviate the problem of Element objects not having access to the original AbstractNode by adding a transient field to Node that is non-null while a node downcast is taking place.
  • Loading branch information
jdm committed Apr 23, 2013
commit 5bade7b0fb093c7eac7473f41fc95e78e000b325
@@ -228,7 +228,8 @@ pub impl Content {
debug!("js_scripts: %?", js_scripts);

let window = Window(self.control_chan.clone(),
self.event_chan.clone());
self.event_chan.clone(),
ptr::to_mut_unsafe_ptr(&mut *self)); //FIXME store this safely
let document = Document(root, Some(window));

do root.with_mut_node |node| {
@@ -344,7 +345,7 @@ pub impl Content {
}

fn query_layout(&mut self, query: layout_task::LayoutQuery) -> layout_task::LayoutQueryResponse {
self.relayout(self.document.get(), &(copy self.doc_url).get());
//self.relayout(self.document.get(), &(copy self.doc_url).get());
self.join_layout();

let (response_port, response_chan) = comm::stream();
@@ -86,6 +86,11 @@ pub fn init(compartment: @mut Compartment) {
nargs: 0,
flags: 0,
selfHostedName: null()},
JSFunctionSpec {name: compartment.add_name(~"getBoundingClientRect"),
call: JSNativeWrapper {op: getBoundingClientRect, info: null()},
nargs: 0,
flags: 0,
selfHostedName: null()},
JSFunctionSpec {name: compartment.add_name(~"setAttribute"),
call: JSNativeWrapper {op: setAttribute, info: null()},
nargs: 0,
@@ -137,7 +142,27 @@ extern fn getClientRects(cx: *JSContext, _argc: c_uint, vp: *JSVal) -> JSBool {
JS_SET_RVAL(cx, vp, JSVAL_NULL);
} else {
let cache = node.get_wrappercache();
let rval = rval.get() as @mut CacheableWrapper;
let rval = rval.get() as @mut CacheableWrapper;
assert!(WrapNewBindingObject(cx, cache.get_wrapper(),
rval,
cast::transmute(vp)));
}
return 1;
}
}

extern fn getBoundingClientRect(cx: *JSContext, _argc: c_uint, vp: *JSVal) -> JSBool {
unsafe {
let obj = JS_THIS_OBJECT(cx, vp);
let mut node = unwrap(obj);
let rval = do node.with_imm_element |elem| {
elem.getBoundingClientRect()
};
if rval.is_none() {
JS_SET_RVAL(cx, vp, JSVAL_NULL);
} else {
let cache = node.get_wrappercache();
let rval = rval.get() as @mut CacheableWrapper;
assert!(WrapNewBindingObject(cx, cache.get_wrapper(),
rval,
cast::transmute(vp)));
@@ -192,7 +217,12 @@ extern fn HTMLImageElement_getWidth(cx: *JSContext, _argc: c_uint, vp: *mut JSVa
ElementNodeTypeId(HTMLImageElementTypeId) => {
let content = task_from_context(cx);
match (*content).query_layout(layout_task::ContentBox(node)) {
Ok(rect) => rect.width,
Ok(rect) => {
match rect {
layout_task::ContentRect(rect) => rect.size.width.to_px(),
_ => fail!(~"unexpected layout reply")
}
}
Err(()) => 0
}
// TODO: if nothing is being rendered(?), return zero dimensions
@@ -3,14 +3,14 @@ use dom::bindings::utils::WrapperCache;

pub struct ClientRectList {
wrapper: WrapperCache,
rects: ~[(f32, f32, f32, f32)]
rects: ~[@mut ClientRect]
}

pub impl ClientRectList {
fn new() -> @mut ClientRectList {
fn new(rects: ~[@mut ClientRect]) -> @mut ClientRectList {
let list = @mut ClientRectList {
wrapper: WrapperCache::new(),
rects: ~[(5.6, 80.2, 3.7, 4.8), (800.1, 8001.1, -50.000001, -45.01)]
rects: rects
};
list.init_wrapper();
list
@@ -22,8 +22,7 @@ pub impl ClientRectList {

fn Item(&self, index: u32) -> Option<@mut ClientRect> {
if index < self.rects.len() as u32 {
let (top, bottom, left, right) = self.rects[index];
Some(ClientRect::new(top, bottom, left, right))
Some(self.rects[index])
} else {
None
}
@@ -33,4 +32,4 @@ pub impl ClientRectList {
*found = index < self.rects.len() as u32;
self.Item(index)
}
}
}
@@ -7,9 +7,12 @@
//

use dom::node::{ElementNodeTypeId, Node};
use dom::clientrect::ClientRect;
use dom::clientrectlist::ClientRectList;
use dom::bindings::utils::DOMString;

use layout::layout_task;

use core::str::eq_slice;
use core::cell::Cell;
use std::net::url::Url;
@@ -157,7 +160,80 @@ pub impl<'self> Element {
}

fn getClientRects(&self) -> Option<@mut ClientRectList> {
Some(ClientRectList::new())
let rects = match self.parent.owner_doc {
Some(doc) => {
match doc.window {
Some(win) => {
let node = self.parent.abstract.get();
assert!(node.is_element());
let content = unsafe { &mut *win.content_task };
match content.query_layout(layout_task::ContentBoxes(node)) {
Ok(rects) => match rects {
layout_task::ContentRects(rects) =>
do rects.map |r| {
ClientRect::new(
r.origin.y.to_f32(),
(r.origin.y + r.size.height).to_f32(),
r.origin.x.to_f32(),
(r.origin.x + r.size.width).to_f32())
},
_ => fail!(~"unexpected layout reply")
},
Err(()) => {
debug!("layout query error");
~[]
}
}
}
None => {
debug!("no window");
~[]
}
}
}
None => {
debug!("no document");
~[]
}
};
Some(ClientRectList::new(rects))
}

fn getBoundingClientRect(&self) -> Option<@mut ClientRect> {
match self.parent.owner_doc {
Some(doc) => {
match doc.window {
Some(win) => {
let node = self.parent.abstract.get();
assert!(node.is_element());
let content = unsafe { &mut *win.content_task };
match content.query_layout(layout_task::ContentBox(node)) {
Ok(rect) => match rect {
layout_task::ContentRect(rect) =>
Some(ClientRect::new(
rect.origin.y.to_f32(),
(rect.origin.y + rect.size.height).to_f32(),
rect.origin.x.to_f32(),
(rect.origin.x + rect.size.width).to_f32())),
_ => fail!(~"unexpected layout result")
},
Err(()) => {
debug!("error querying layout");
None
}
}
}
None => {
debug!("no window");
None
}
}
}
None => {
debug!("no document");
None
}
}
}
}

@@ -43,6 +43,8 @@ pub struct Node {
wrapper: WrapperCache,
type_id: NodeTypeId,

abstract: Option<AbstractNode>,

parent_node: Option<AbstractNode>,
first_child: Option<AbstractNode>,
last_child: Option<AbstractNode>,
@@ -238,15 +240,27 @@ pub impl AbstractNode {

fn transmute<T, R>(self, f: &fn(&T) -> R) -> R {
unsafe {
let node_box: *mut bindings::utils::rust_box<Node> = transmute(self.obj);
let node = &mut (*node_box).payload;
let old = node.abstract;
node.abstract = Some(self);
let box: *bindings::utils::rust_box<T> = transmute(self.obj);
f(&(*box).payload)
let rv = f(&(*box).payload);
node.abstract = old;
rv
}
}

fn transmute_mut<T, R>(self, f: &fn(&mut T) -> R) -> R {
unsafe {
let node_box: *mut bindings::utils::rust_box<Node> = transmute(self.obj);
let node = &mut (*node_box).payload;
let old = node.abstract;
node.abstract = Some(self);
let box: *bindings::utils::rust_box<T> = transmute(self.obj);
f(cast::transmute(&(*box).payload))
let rv = f(cast::transmute(&(*box).payload));
node.abstract = old;
rv
}
}

@@ -381,6 +395,8 @@ impl Node {
wrapper: WrapperCache::new(),
type_id: type_id,

abstract: None,

parent_node: None,
first_child: None,
last_child: None,
@@ -2,7 +2,7 @@
* 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 content::content_task::{ControlMsg, Timer, ExitMsg, global_content};
use content::content_task::{ControlMsg, Timer, ExitMsg, global_content, Content};
use dom::bindings::utils::WrapperCache;
use dom::bindings::window;
use dom::event::Event;
@@ -19,9 +19,12 @@ pub enum TimerControlMsg {
TimerMessage_TriggerExit //XXXjdm this is just a quick hack to talk to the content task
}

//FIXME If we're going to store the content task, find a way to do so safely. Currently it's
// only used for querying layout from arbitrary content.
pub struct Window {
timer_chan: Chan<TimerControlMsg>,
dom_event_chan: SharedChan<Event>,
content_task: *mut Content,
wrapper: WrapperCache
}

@@ -81,7 +84,8 @@ pub impl Window {
}

pub fn Window(content_chan: comm::SharedChan<ControlMsg>,
dom_event_chan: comm::SharedChan<Event>) -> @mut Window {
dom_event_chan: comm::SharedChan<Event>,
content_task: *mut Content) -> @mut Window {

let win = @mut Window {
wrapper: WrapperCache::new(),
@@ -96,7 +100,8 @@ pub fn Window(content_chan: comm::SharedChan<ControlMsg>,
TimerMessage_TriggerExit => content_chan.send(ExitMsg)
}
}
}
},
content_task: content_task
};
let compartment = global_content().compartment.get();
window::create(compartment, win);
@@ -230,16 +230,17 @@ impl BuilderContext {

priv fn create_child_flow_of_type(&self,
flow_type: FlowContextType,
builder: &mut LayoutTreeBuilder) -> BuilderContext {
let new_flow = builder.make_flow(flow_type);
builder: &mut LayoutTreeBuilder,
node: AbstractNode) -> BuilderContext {
let new_flow = builder.make_flow(flow_type, node);
self.attach_child_flow(new_flow);

BuilderContext::new(@mut BoxGenerator::new(new_flow))
}

priv fn make_inline_collector(&mut self, builder: &mut LayoutTreeBuilder) -> BuilderContext {
priv fn make_inline_collector(&mut self, builder: &mut LayoutTreeBuilder, node: AbstractNode) -> BuilderContext {
debug!("BuilderContext: making new inline collector flow");
let new_flow = builder.make_flow(Flow_Inline);
let new_flow = builder.make_flow(Flow_Inline, node);
let new_generator = @mut BoxGenerator::new(new_flow);

self.inline_collector = Some(new_generator);
@@ -248,10 +249,10 @@ impl BuilderContext {
BuilderContext::new(new_generator)
}

priv fn get_inline_collector(&mut self, builder: &mut LayoutTreeBuilder) -> BuilderContext {
priv fn get_inline_collector(&mut self, builder: &mut LayoutTreeBuilder, node: AbstractNode) -> BuilderContext {
match copy self.inline_collector {
Some(collector) => BuilderContext::new(collector),
None => self.make_inline_collector(builder)
None => self.make_inline_collector(builder, node)
}
}

@@ -278,18 +279,18 @@ impl BuilderContext {
// If this is the root node, then use the root flow's
// context. Otherwise, make a child block context.
match node.parent_node() {
Some(_) => { self.create_child_flow_of_type(Flow_Block, builder) }
Some(_) => { self.create_child_flow_of_type(Flow_Block, builder, node) }
None => { self.clone() },
}
},
(CSSDisplayBlock, @BlockFlow(*)) => {
self.clear_inline_collector();
self.create_child_flow_of_type(Flow_Block, builder)
self.create_child_flow_of_type(Flow_Block, builder, node)
},
(CSSDisplayInline, @InlineFlow(*)) => self.clone(),
(CSSDisplayInlineBlock, @InlineFlow(*)) => self.clone(),
(CSSDisplayInline, @BlockFlow(*)) => self.get_inline_collector(builder),
(CSSDisplayInlineBlock, @BlockFlow(*)) => self.get_inline_collector(builder),
(CSSDisplayInline, @BlockFlow(*)) => self.get_inline_collector(builder, node),
(CSSDisplayInlineBlock, @BlockFlow(*)) => self.get_inline_collector(builder, node),
_ => self.clone()
};

@@ -332,10 +333,9 @@ pub impl LayoutTreeBuilder {
// nodes and FlowContexts should not change during layout.
let flow = &mut this_ctx.default_collector.flow;
for tree::each_child(&FlowTree, flow) |child_flow: &@mut FlowContext| {
for (copy child_flow.d().node).each |node| {
assert!(node.has_layout_data());
node.layout_data().flow = Some(*child_flow);
}
let node = child_flow.d().node;
assert!(node.has_layout_data());
node.layout_data().flow = Some(*child_flow);
}
}

@@ -406,7 +406,7 @@ pub impl LayoutTreeBuilder {
called on root DOM element. */
fn construct_trees(&mut self, layout_ctx: &LayoutContext, root: AbstractNode)
-> Result<@mut FlowContext, ()> {
let new_flow = self.make_flow(Flow_Root);
let new_flow = self.make_flow(Flow_Root, root);
let new_generator = @mut BoxGenerator::new(new_flow);
let mut root_ctx = BuilderContext::new(new_generator);

@@ -415,8 +415,8 @@ pub impl LayoutTreeBuilder {
return Ok(new_flow)
}

fn make_flow(&mut self, ty: FlowContextType) -> @mut FlowContext {
let data = FlowData(self.next_flow_id());
fn make_flow(&mut self, ty: FlowContextType, node: AbstractNode) -> @mut FlowContext {
let data = FlowData(self.next_flow_id(), node);
let ret = match ty {
Flow_Absolute => @mut AbsoluteFlow(data),
Flow_Block => @mut BlockFlow(data, BlockFlowData()),
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.