mirror of
https://github.com/servo/servo.git
synced 2025-08-09 23:45:35 +01:00
Add getBoundingClientRect, and make it and getClientRects synchronously 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.
This commit is contained in:
parent
71df18a839
commit
5bade7b0fb
12 changed files with 219 additions and 59 deletions
|
@ -228,7 +228,8 @@ pub impl Content {
|
||||||
debug!("js_scripts: %?", js_scripts);
|
debug!("js_scripts: %?", js_scripts);
|
||||||
|
|
||||||
let window = Window(self.control_chan.clone(),
|
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));
|
let document = Document(root, Some(window));
|
||||||
|
|
||||||
do root.with_mut_node |node| {
|
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 {
|
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();
|
self.join_layout();
|
||||||
|
|
||||||
let (response_port, response_chan) = comm::stream();
|
let (response_port, response_chan) = comm::stream();
|
||||||
|
|
|
@ -86,6 +86,11 @@ pub fn init(compartment: @mut Compartment) {
|
||||||
nargs: 0,
|
nargs: 0,
|
||||||
flags: 0,
|
flags: 0,
|
||||||
selfHostedName: null()},
|
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"),
|
JSFunctionSpec {name: compartment.add_name(~"setAttribute"),
|
||||||
call: JSNativeWrapper {op: setAttribute, info: null()},
|
call: JSNativeWrapper {op: setAttribute, info: null()},
|
||||||
nargs: 0,
|
nargs: 0,
|
||||||
|
@ -137,7 +142,27 @@ extern fn getClientRects(cx: *JSContext, _argc: c_uint, vp: *JSVal) -> JSBool {
|
||||||
JS_SET_RVAL(cx, vp, JSVAL_NULL);
|
JS_SET_RVAL(cx, vp, JSVAL_NULL);
|
||||||
} else {
|
} else {
|
||||||
let cache = node.get_wrappercache();
|
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(),
|
assert!(WrapNewBindingObject(cx, cache.get_wrapper(),
|
||||||
rval,
|
rval,
|
||||||
cast::transmute(vp)));
|
cast::transmute(vp)));
|
||||||
|
@ -192,7 +217,12 @@ extern fn HTMLImageElement_getWidth(cx: *JSContext, _argc: c_uint, vp: *mut JSVa
|
||||||
ElementNodeTypeId(HTMLImageElementTypeId) => {
|
ElementNodeTypeId(HTMLImageElementTypeId) => {
|
||||||
let content = task_from_context(cx);
|
let content = task_from_context(cx);
|
||||||
match (*content).query_layout(layout_task::ContentBox(node)) {
|
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
|
Err(()) => 0
|
||||||
}
|
}
|
||||||
// TODO: if nothing is being rendered(?), return zero dimensions
|
// TODO: if nothing is being rendered(?), return zero dimensions
|
||||||
|
|
|
@ -3,14 +3,14 @@ use dom::bindings::utils::WrapperCache;
|
||||||
|
|
||||||
pub struct ClientRectList {
|
pub struct ClientRectList {
|
||||||
wrapper: WrapperCache,
|
wrapper: WrapperCache,
|
||||||
rects: ~[(f32, f32, f32, f32)]
|
rects: ~[@mut ClientRect]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub impl ClientRectList {
|
pub impl ClientRectList {
|
||||||
fn new() -> @mut ClientRectList {
|
fn new(rects: ~[@mut ClientRect]) -> @mut ClientRectList {
|
||||||
let list = @mut ClientRectList {
|
let list = @mut ClientRectList {
|
||||||
wrapper: WrapperCache::new(),
|
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.init_wrapper();
|
||||||
list
|
list
|
||||||
|
@ -22,8 +22,7 @@ pub impl ClientRectList {
|
||||||
|
|
||||||
fn Item(&self, index: u32) -> Option<@mut ClientRect> {
|
fn Item(&self, index: u32) -> Option<@mut ClientRect> {
|
||||||
if index < self.rects.len() as u32 {
|
if index < self.rects.len() as u32 {
|
||||||
let (top, bottom, left, right) = self.rects[index];
|
Some(self.rects[index])
|
||||||
Some(ClientRect::new(top, bottom, left, right))
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -33,4 +32,4 @@ pub impl ClientRectList {
|
||||||
*found = index < self.rects.len() as u32;
|
*found = index < self.rects.len() as u32;
|
||||||
self.Item(index)
|
self.Item(index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,9 +7,12 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
use dom::node::{ElementNodeTypeId, Node};
|
use dom::node::{ElementNodeTypeId, Node};
|
||||||
|
use dom::clientrect::ClientRect;
|
||||||
use dom::clientrectlist::ClientRectList;
|
use dom::clientrectlist::ClientRectList;
|
||||||
use dom::bindings::utils::DOMString;
|
use dom::bindings::utils::DOMString;
|
||||||
|
|
||||||
|
use layout::layout_task;
|
||||||
|
|
||||||
use core::str::eq_slice;
|
use core::str::eq_slice;
|
||||||
use core::cell::Cell;
|
use core::cell::Cell;
|
||||||
use std::net::url::Url;
|
use std::net::url::Url;
|
||||||
|
@ -157,7 +160,80 @@ pub impl<'self> Element {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn getClientRects(&self) -> Option<@mut ClientRectList> {
|
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,
|
wrapper: WrapperCache,
|
||||||
type_id: NodeTypeId,
|
type_id: NodeTypeId,
|
||||||
|
|
||||||
|
abstract: Option<AbstractNode>,
|
||||||
|
|
||||||
parent_node: Option<AbstractNode>,
|
parent_node: Option<AbstractNode>,
|
||||||
first_child: Option<AbstractNode>,
|
first_child: Option<AbstractNode>,
|
||||||
last_child: Option<AbstractNode>,
|
last_child: Option<AbstractNode>,
|
||||||
|
@ -238,15 +240,27 @@ pub impl AbstractNode {
|
||||||
|
|
||||||
fn transmute<T, R>(self, f: &fn(&T) -> R) -> R {
|
fn transmute<T, R>(self, f: &fn(&T) -> R) -> R {
|
||||||
unsafe {
|
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);
|
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 {
|
fn transmute_mut<T, R>(self, f: &fn(&mut T) -> R) -> R {
|
||||||
unsafe {
|
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);
|
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(),
|
wrapper: WrapperCache::new(),
|
||||||
type_id: type_id,
|
type_id: type_id,
|
||||||
|
|
||||||
|
abstract: None,
|
||||||
|
|
||||||
parent_node: None,
|
parent_node: None,
|
||||||
first_child: None,
|
first_child: None,
|
||||||
last_child: None,
|
last_child: None,
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* 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::utils::WrapperCache;
|
||||||
use dom::bindings::window;
|
use dom::bindings::window;
|
||||||
use dom::event::Event;
|
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
|
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 {
|
pub struct Window {
|
||||||
timer_chan: Chan<TimerControlMsg>,
|
timer_chan: Chan<TimerControlMsg>,
|
||||||
dom_event_chan: SharedChan<Event>,
|
dom_event_chan: SharedChan<Event>,
|
||||||
|
content_task: *mut Content,
|
||||||
wrapper: WrapperCache
|
wrapper: WrapperCache
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +84,8 @@ pub impl Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn Window(content_chan: comm::SharedChan<ControlMsg>,
|
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 {
|
let win = @mut Window {
|
||||||
wrapper: WrapperCache::new(),
|
wrapper: WrapperCache::new(),
|
||||||
|
@ -96,7 +100,8 @@ pub fn Window(content_chan: comm::SharedChan<ControlMsg>,
|
||||||
TimerMessage_TriggerExit => content_chan.send(ExitMsg)
|
TimerMessage_TriggerExit => content_chan.send(ExitMsg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
content_task: content_task
|
||||||
};
|
};
|
||||||
let compartment = global_content().compartment.get();
|
let compartment = global_content().compartment.get();
|
||||||
window::create(compartment, win);
|
window::create(compartment, win);
|
||||||
|
|
|
@ -230,16 +230,17 @@ impl BuilderContext {
|
||||||
|
|
||||||
priv fn create_child_flow_of_type(&self,
|
priv fn create_child_flow_of_type(&self,
|
||||||
flow_type: FlowContextType,
|
flow_type: FlowContextType,
|
||||||
builder: &mut LayoutTreeBuilder) -> BuilderContext {
|
builder: &mut LayoutTreeBuilder,
|
||||||
let new_flow = builder.make_flow(flow_type);
|
node: AbstractNode) -> BuilderContext {
|
||||||
|
let new_flow = builder.make_flow(flow_type, node);
|
||||||
self.attach_child_flow(new_flow);
|
self.attach_child_flow(new_flow);
|
||||||
|
|
||||||
BuilderContext::new(@mut BoxGenerator::new(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");
|
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);
|
let new_generator = @mut BoxGenerator::new(new_flow);
|
||||||
|
|
||||||
self.inline_collector = Some(new_generator);
|
self.inline_collector = Some(new_generator);
|
||||||
|
@ -248,10 +249,10 @@ impl BuilderContext {
|
||||||
BuilderContext::new(new_generator)
|
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 {
|
match copy self.inline_collector {
|
||||||
Some(collector) => BuilderContext::new(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
|
// If this is the root node, then use the root flow's
|
||||||
// context. Otherwise, make a child block context.
|
// context. Otherwise, make a child block context.
|
||||||
match node.parent_node() {
|
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() },
|
None => { self.clone() },
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(CSSDisplayBlock, @BlockFlow(*)) => {
|
(CSSDisplayBlock, @BlockFlow(*)) => {
|
||||||
self.clear_inline_collector();
|
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(),
|
(CSSDisplayInline, @InlineFlow(*)) => self.clone(),
|
||||||
(CSSDisplayInlineBlock, @InlineFlow(*)) => self.clone(),
|
(CSSDisplayInlineBlock, @InlineFlow(*)) => self.clone(),
|
||||||
(CSSDisplayInline, @BlockFlow(*)) => self.get_inline_collector(builder),
|
(CSSDisplayInline, @BlockFlow(*)) => self.get_inline_collector(builder, node),
|
||||||
(CSSDisplayInlineBlock, @BlockFlow(*)) => self.get_inline_collector(builder),
|
(CSSDisplayInlineBlock, @BlockFlow(*)) => self.get_inline_collector(builder, node),
|
||||||
_ => self.clone()
|
_ => self.clone()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -332,10 +333,9 @@ pub impl LayoutTreeBuilder {
|
||||||
// nodes and FlowContexts should not change during layout.
|
// nodes and FlowContexts should not change during layout.
|
||||||
let flow = &mut this_ctx.default_collector.flow;
|
let flow = &mut this_ctx.default_collector.flow;
|
||||||
for tree::each_child(&FlowTree, flow) |child_flow: &@mut FlowContext| {
|
for tree::each_child(&FlowTree, flow) |child_flow: &@mut FlowContext| {
|
||||||
for (copy child_flow.d().node).each |node| {
|
let node = child_flow.d().node;
|
||||||
assert!(node.has_layout_data());
|
assert!(node.has_layout_data());
|
||||||
node.layout_data().flow = Some(*child_flow);
|
node.layout_data().flow = Some(*child_flow);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -406,7 +406,7 @@ pub impl LayoutTreeBuilder {
|
||||||
called on root DOM element. */
|
called on root DOM element. */
|
||||||
fn construct_trees(&mut self, layout_ctx: &LayoutContext, root: AbstractNode)
|
fn construct_trees(&mut self, layout_ctx: &LayoutContext, root: AbstractNode)
|
||||||
-> Result<@mut FlowContext, ()> {
|
-> 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 new_generator = @mut BoxGenerator::new(new_flow);
|
||||||
let mut root_ctx = BuilderContext::new(new_generator);
|
let mut root_ctx = BuilderContext::new(new_generator);
|
||||||
|
|
||||||
|
@ -415,8 +415,8 @@ pub impl LayoutTreeBuilder {
|
||||||
return Ok(new_flow)
|
return Ok(new_flow)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_flow(&mut self, ty: FlowContextType) -> @mut FlowContext {
|
fn make_flow(&mut self, ty: FlowContextType, node: AbstractNode) -> @mut FlowContext {
|
||||||
let data = FlowData(self.next_flow_id());
|
let data = FlowData(self.next_flow_id(), node);
|
||||||
let ret = match ty {
|
let ret = match ty {
|
||||||
Flow_Absolute => @mut AbsoluteFlow(data),
|
Flow_Absolute => @mut AbsoluteFlow(data),
|
||||||
Flow_Block => @mut BlockFlow(data, BlockFlowData()),
|
Flow_Block => @mut BlockFlow(data, BlockFlowData()),
|
||||||
|
|
|
@ -70,7 +70,7 @@ pub enum FlowContextType {
|
||||||
/* A particular kind of layout context. It manages the positioning of
|
/* A particular kind of layout context. It manages the positioning of
|
||||||
render boxes within the context. */
|
render boxes within the context. */
|
||||||
pub struct FlowData {
|
pub struct FlowData {
|
||||||
node: Option<AbstractNode>,
|
node: AbstractNode,
|
||||||
/* reference to parent, children flow contexts */
|
/* reference to parent, children flow contexts */
|
||||||
tree: tree::Tree<@mut FlowContext>,
|
tree: tree::Tree<@mut FlowContext>,
|
||||||
/* TODO (Issue #87): debug only */
|
/* TODO (Issue #87): debug only */
|
||||||
|
@ -84,9 +84,9 @@ pub struct FlowData {
|
||||||
position: Rect<Au>,
|
position: Rect<Au>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn FlowData(id: int) -> FlowData {
|
pub fn FlowData(id: int, node: AbstractNode) -> FlowData {
|
||||||
FlowData {
|
FlowData {
|
||||||
node: None,
|
node: node,
|
||||||
tree: tree::empty(),
|
tree: tree::empty(),
|
||||||
id: id,
|
id: id,
|
||||||
|
|
||||||
|
|
|
@ -40,13 +40,15 @@ use std::net::url::Url;
|
||||||
pub type LayoutTask = SharedChan<Msg>;
|
pub type LayoutTask = SharedChan<Msg>;
|
||||||
|
|
||||||
pub enum LayoutQuery {
|
pub enum LayoutQuery {
|
||||||
ContentBox(AbstractNode)
|
ContentBox(AbstractNode),
|
||||||
|
ContentBoxes(AbstractNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type LayoutQueryResponse = Result<LayoutQueryResponse_, ()>;
|
pub type LayoutQueryResponse = Result<LayoutQueryResponse_, ()>;
|
||||||
|
|
||||||
enum LayoutQueryResponse_ {
|
pub enum LayoutQueryResponse_ {
|
||||||
ContentSize(Size2D<int>)
|
ContentRect(Rect<Au>),
|
||||||
|
ContentRects(~[Rect<Au>])
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Msg {
|
pub enum Msg {
|
||||||
|
@ -256,7 +258,10 @@ impl Layout {
|
||||||
match query {
|
match query {
|
||||||
ContentBox(node) => {
|
ContentBox(node) => {
|
||||||
let response = match node.layout_data().flow {
|
let response = match node.layout_data().flow {
|
||||||
None => Err(()),
|
None => {
|
||||||
|
error!("no flow present");
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
Some(flow) => {
|
Some(flow) => {
|
||||||
let start_val: Option<Rect<Au>> = None;
|
let start_val: Option<Rect<Au>> = None;
|
||||||
let rect = do flow.foldl_boxes_for_node(node, start_val) |acc, box| {
|
let rect = do flow.foldl_boxes_for_node(node, start_val) |acc, box| {
|
||||||
|
@ -267,16 +272,30 @@ impl Layout {
|
||||||
};
|
};
|
||||||
|
|
||||||
match rect {
|
match rect {
|
||||||
None => Err(()),
|
None => {
|
||||||
Some(rect) => {
|
error!("no boxes for node");
|
||||||
let size = Size2D(rect.size.width.to_px(),
|
Err(())
|
||||||
rect.size.height.to_px());
|
|
||||||
Ok(ContentSize(size))
|
|
||||||
}
|
}
|
||||||
|
Some(rect) => Ok(ContentRect(rect))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
reply_chan.send(response)
|
||||||
|
}
|
||||||
|
ContentBoxes(node) => {
|
||||||
|
let response = match node.layout_data().flow {
|
||||||
|
None => Err(()),
|
||||||
|
Some(flow) => {
|
||||||
|
let mut boxes = ~[];
|
||||||
|
for flow.iter_boxes_for_node(node) |box| {
|
||||||
|
boxes.push(box.content_box());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(ContentRects(boxes))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
reply_chan.send(response)
|
reply_chan.send(response)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,9 @@
|
||||||
<script src="test_bindings.js"></script>
|
<script src="test_bindings.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="first"></div>
|
<div id="first">fffff<br><br><br><br>fffffffffffffffff</div>
|
||||||
<div id="second"></div>
|
<div id="second">ggg</div>
|
||||||
<span id="third"></div>
|
<span id="third">hhhhhhhh</span>
|
||||||
<div id="fourth"></div>
|
<div id="fourth">iiiiiiiiiiiiiiiiiii</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,16 +1,24 @@
|
||||||
//window.alert(ClientRect);
|
//window.alert(ClientRect);
|
||||||
//window.alert(ClientRectList);
|
//window.alert(ClientRectList);
|
||||||
|
window.alert("==1==");
|
||||||
window.alert("1");
|
let elem = document.getElementsByTagName('div')[0];
|
||||||
let elem = document.documentElement;
|
|
||||||
window.alert(elem.nodeType);
|
window.alert(elem.nodeType);
|
||||||
window.alert(elem);
|
window.alert(elem);
|
||||||
window.alert("2");
|
window.alert("==1.5==");
|
||||||
|
var rect = elem.getBoundingClientRect();
|
||||||
|
window.alert(rect);
|
||||||
|
window.alert(rect.top);
|
||||||
|
window.alert(rect.bottom);
|
||||||
|
window.alert(rect.left);
|
||||||
|
window.alert(rect.right);
|
||||||
|
window.alert(rect.width);
|
||||||
|
window.alert(rect.height);
|
||||||
|
window.alert("==2==");
|
||||||
var rects = elem.getClientRects();
|
var rects = elem.getClientRects();
|
||||||
window.alert("3");
|
window.alert("==3==");
|
||||||
window.alert(rects);
|
window.alert(rects);
|
||||||
window.alert(rects.length);
|
window.alert(rects.length);
|
||||||
window.alert("4");
|
window.alert("==4==");
|
||||||
let rect = rects[0];
|
let rect = rects[0];
|
||||||
window.alert(rect);
|
window.alert(rect);
|
||||||
/*window.alert(Object.prototype.toString.call(rect.__proto__));
|
/*window.alert(Object.prototype.toString.call(rect.__proto__));
|
||||||
|
@ -40,4 +48,4 @@ window.alert("DOMParser:");
|
||||||
window.alert(DOMParser);
|
window.alert(DOMParser);
|
||||||
let parser = new DOMParser();
|
let parser = new DOMParser();
|
||||||
window.alert(parser);
|
window.alert(parser);
|
||||||
window.alert(parser.parseFromString("<html></html>", "text/html"));
|
//window.alert(parser.parseFromString("<html></html>", "text/html"));
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
var divs = document.getElementsByTagName("div");
|
var divs = document.getElementsByTagName("div");
|
||||||
var div = divs[0];
|
var div = divs[0];
|
||||||
for (var i = 0; i < 1000000; i++) {
|
|
||||||
div.setAttribute('id', 'styled');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
var count = 1000000;
|
||||||
|
var start = new Date();
|
||||||
|
for (var i = 0; i < count; i++) {
|
||||||
|
div.setAttribute('id', 'styled');
|
||||||
|
div.getBoundingClientRect();
|
||||||
|
window.alert(i);
|
||||||
|
}
|
||||||
|
var stop = new Date();
|
||||||
|
window.alert((stop - start) / count * 1e6);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue