mirror of
https://github.com/servo/servo.git
synced 2025-08-05 13:40:08 +01:00
Store pointer to Content in JSContext private slot; add methods for pure folding over a flow's boxes; Set up layout query protocol; Hook up HTMLImageElement.width to use content->layout blocking queries.
This commit is contained in:
parent
0b42b7f537
commit
b9b3895a8a
6 changed files with 248 additions and 109 deletions
|
@ -3,9 +3,10 @@ The content task is the main task that runs JavaScript and spawns layout
|
||||||
tasks.
|
tasks.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export ContentTask;
|
export Content, ContentTask;
|
||||||
export ControlMsg, ExecuteMsg, ParseMsg, ExitMsg, Timer;
|
export ControlMsg, ExecuteMsg, ParseMsg, ExitMsg, Timer;
|
||||||
export PingMsg, PongMsg;
|
export PingMsg, PongMsg;
|
||||||
|
export task_from_context;
|
||||||
|
|
||||||
use std::arc::{ARC, clone};
|
use std::arc::{ARC, clone};
|
||||||
use comm::{Port, Chan, listen, select2};
|
use comm::{Port, Chan, listen, select2};
|
||||||
|
@ -43,8 +44,8 @@ use task::{task, SingleThreaded};
|
||||||
|
|
||||||
use js::glue::bindgen::RUST_JSVAL_TO_OBJECT;
|
use js::glue::bindgen::RUST_JSVAL_TO_OBJECT;
|
||||||
use js::JSVAL_NULL;
|
use js::JSVAL_NULL;
|
||||||
use js::jsapi::jsval;
|
use js::jsapi::{JSContext, jsval};
|
||||||
use js::jsapi::bindgen::JS_CallFunctionValue;
|
use js::jsapi::bindgen::{JS_CallFunctionValue, JS_GetContextPrivate};
|
||||||
use ptr::null;
|
use ptr::null;
|
||||||
|
|
||||||
enum ControlMsg {
|
enum ControlMsg {
|
||||||
|
@ -65,25 +66,13 @@ fn ContentTask<S: Compositor Send Copy>(layout_task: LayoutTask,
|
||||||
resource_task: ResourceTask,
|
resource_task: ResourceTask,
|
||||||
img_cache_task: ImageCacheTask) -> ContentTask {
|
img_cache_task: ImageCacheTask) -> ContentTask {
|
||||||
do task().sched_mode(SingleThreaded).spawn_listener::<ControlMsg> |from_master| {
|
do task().sched_mode(SingleThreaded).spawn_listener::<ControlMsg> |from_master| {
|
||||||
Content(layout_task, compositor, from_master, resource_task, img_cache_task).start();
|
let content = Content(layout_task, from_master, resource_task, img_cache_task);
|
||||||
|
compositor.add_event_listener(content.event_port.chan());
|
||||||
|
content.start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends a ping to layout and waits for the response
|
struct Content {
|
||||||
#[allow(non_implicitly_copyable_typarams)]
|
|
||||||
fn join_layout(scope: NodeScope, layout_task: LayoutTask) {
|
|
||||||
|
|
||||||
if scope.is_reader_forked() {
|
|
||||||
listen(|response_from_layout| {
|
|
||||||
layout_task.send(layout_task::PingMsg(response_from_layout));
|
|
||||||
response_from_layout.recv();
|
|
||||||
});
|
|
||||||
scope.reader_joined();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Content<C:Compositor> {
|
|
||||||
compositor: C,
|
|
||||||
layout_task: LayoutTask,
|
layout_task: LayoutTask,
|
||||||
image_cache_task: ImageCacheTask,
|
image_cache_task: ImageCacheTask,
|
||||||
from_master: comm::Port<ControlMsg>,
|
from_master: comm::Port<ControlMsg>,
|
||||||
|
@ -102,29 +91,26 @@ struct Content<C:Compositor> {
|
||||||
compartment: Option<compartment>,
|
compartment: Option<compartment>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Content<C:Compositor>(layout_task: LayoutTask,
|
fn Content(layout_task: LayoutTask,
|
||||||
compositor: C,
|
|
||||||
from_master: Port<ControlMsg>,
|
from_master: Port<ControlMsg>,
|
||||||
resource_task: ResourceTask,
|
resource_task: ResourceTask,
|
||||||
img_cache_task: ImageCacheTask) -> Content<C> {
|
img_cache_task: ImageCacheTask) -> Content {
|
||||||
|
|
||||||
let jsrt = jsrt();
|
let jsrt = jsrt();
|
||||||
let cx = jsrt.cx();
|
let cx = jsrt.cx();
|
||||||
let event_port = Port();
|
let event_port = Port();
|
||||||
|
|
||||||
compositor.add_event_listener(event_port.chan());
|
|
||||||
|
|
||||||
cx.set_default_options_and_version();
|
cx.set_default_options_and_version();
|
||||||
cx.set_logging_error_reporter();
|
cx.set_logging_error_reporter();
|
||||||
|
|
||||||
let compartment = match cx.new_compartment(global_class) {
|
let compartment = match cx.new_compartment(global_class) {
|
||||||
Ok(c) => Some(c),
|
Ok(c) => Some(c),
|
||||||
Err(()) => None
|
Err(()) => None
|
||||||
};
|
};
|
||||||
|
|
||||||
Content {
|
let content = Content {
|
||||||
layout_task : layout_task,
|
layout_task : layout_task,
|
||||||
image_cache_task : img_cache_task,
|
image_cache_task : img_cache_task,
|
||||||
compositor : compositor,
|
|
||||||
from_master : from_master,
|
from_master : from_master,
|
||||||
event_port : event_port,
|
event_port : event_port,
|
||||||
|
|
||||||
|
@ -138,10 +124,18 @@ fn Content<C:Compositor>(layout_task: LayoutTask,
|
||||||
|
|
||||||
resource_task : resource_task,
|
resource_task : resource_task,
|
||||||
compartment : compartment
|
compartment : compartment
|
||||||
}
|
};
|
||||||
|
|
||||||
|
cx.set_cx_private(ptr::to_unsafe_ptr(&content) as *());
|
||||||
|
|
||||||
|
content
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C:Compositor> Content<C> {
|
fn task_from_context(cx: *JSContext) -> &Content unsafe {
|
||||||
|
cast::reinterpret_cast(&JS_GetContextPrivate(cx))
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Content {
|
||||||
|
|
||||||
fn start() {
|
fn start() {
|
||||||
while self.handle_msg(select2(self.from_master, self.event_port)) {
|
while self.handle_msg(select2(self.from_master, self.event_port)) {
|
||||||
|
@ -159,7 +153,7 @@ impl<C:Compositor> Content<C> {
|
||||||
fn handle_control_msg(control_msg: ControlMsg) -> bool {
|
fn handle_control_msg(control_msg: ControlMsg) -> bool {
|
||||||
match control_msg {
|
match control_msg {
|
||||||
ParseMsg(url) => {
|
ParseMsg(url) => {
|
||||||
#debug["content: Received url `%s` to parse", url_to_str(copy url)];
|
debug!("content: Received url `%s` to parse", url_to_str(copy url));
|
||||||
|
|
||||||
// Note: we can parse the next document in parallel
|
// Note: we can parse the next document in parallel
|
||||||
// with any previous documents.
|
// with any previous documents.
|
||||||
|
@ -174,9 +168,9 @@ impl<C:Compositor> Content<C> {
|
||||||
let js_scripts = result.js_port.recv();
|
let js_scripts = result.js_port.recv();
|
||||||
|
|
||||||
// Apply the css rules to the dom tree:
|
// Apply the css rules to the dom tree:
|
||||||
#debug["css_rules: %?", css_rules];
|
debug!("css_rules: %?", css_rules);
|
||||||
|
|
||||||
#debug["js_scripts: %?", js_scripts];
|
debug!("js_scripts: %?", js_scripts);
|
||||||
|
|
||||||
let document = Document(root, self.scope, css_rules);
|
let document = Document(root, self.scope, css_rules);
|
||||||
let window = Window(self.from_master);
|
let window = Window(self.from_master);
|
||||||
|
@ -215,11 +209,11 @@ impl<C:Compositor> Content<C> {
|
||||||
|
|
||||||
|
|
||||||
ExecuteMsg(url) => {
|
ExecuteMsg(url) => {
|
||||||
#debug["content: Received url `%s` to execute", url_to_str(copy url)];
|
debug!("content: Received url `%s` to execute", url_to_str(copy url));
|
||||||
|
|
||||||
match read_whole_file(&Path(url.path)) {
|
match read_whole_file(&Path(url.path)) {
|
||||||
Err(msg) => {
|
Err(msg) => {
|
||||||
println(#fmt["Error opening %s: %s", url_to_str(copy url), msg]);
|
println(fmt!("Error opening %s: %s", url_to_str(copy url), msg));
|
||||||
}
|
}
|
||||||
Ok(bytes) => {
|
Ok(bytes) => {
|
||||||
let compartment = option::expect(self.compartment, ~"TODO error checking");
|
let compartment = option::expect(self.compartment, ~"TODO error checking");
|
||||||
|
@ -237,12 +231,31 @@ impl<C:Compositor> Content<C> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Sends a ping to layout and waits for the response (i.e., it has finished any
|
||||||
|
pending layout request messages).
|
||||||
|
*/
|
||||||
|
fn join_layout() {
|
||||||
|
if self.scope.is_reader_forked() {
|
||||||
|
listen(|response_from_layout| {
|
||||||
|
self.layout_task.send(layout_task::PingMsg(response_from_layout));
|
||||||
|
response_from_layout.recv();
|
||||||
|
});
|
||||||
|
self.scope.reader_joined();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
This method will wait until the layout task has completed its current action,
|
||||||
|
join the layout task, and then request a new layout run. It won't wait for the
|
||||||
|
new layout computation to finish.
|
||||||
|
*/
|
||||||
fn relayout(document: Document, doc_url: &Url) {
|
fn relayout(document: Document, doc_url: &Url) {
|
||||||
#debug("content: performing relayout");
|
debug!("content: performing relayout");
|
||||||
|
|
||||||
// Now, join the layout so that they will see the latest
|
// Now, join the layout so that they will see the latest
|
||||||
// changes we have made.
|
// changes we have made.
|
||||||
join_layout(self.scope, self.layout_task);
|
self.join_layout();
|
||||||
|
|
||||||
// Send new document and relevant styles to layout
|
// Send new document and relevant styles to layout
|
||||||
// FIXME: Put CSS rules in an arc or something.
|
// FIXME: Put CSS rules in an arc or something.
|
||||||
|
@ -253,10 +266,23 @@ impl<C:Compositor> Content<C> {
|
||||||
self.scope.reader_forked();
|
self.scope.reader_forked();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn query_layout(query: layout_task::LayoutQuery) -> layout_task::LayoutQueryResponse {
|
||||||
|
self.relayout(*self.document.get(), &self.doc_url.get());
|
||||||
|
self.join_layout();
|
||||||
|
|
||||||
|
let response_port = Port();
|
||||||
|
self.layout_task.send(layout_task::QueryMsg(query, response_port.chan()));
|
||||||
|
return response_port.recv()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
This is the main entry point for receiving and dispatching DOM events.
|
||||||
|
*/
|
||||||
|
// TODO: actually perform DOM event dispatch.
|
||||||
fn handle_event(event: Event) -> bool {
|
fn handle_event(event: Event) -> bool {
|
||||||
match event {
|
match event {
|
||||||
ResizeEvent(new_width, new_height) => {
|
ResizeEvent(new_width, new_height) => {
|
||||||
#debug("content got resize event: %d, %d", new_width, new_height);
|
debug!("content got resize event: %d, %d", new_width, new_height);
|
||||||
match copy self.document {
|
match copy self.document {
|
||||||
None => {
|
None => {
|
||||||
// Nothing to do.
|
// Nothing to do.
|
||||||
|
@ -269,7 +295,7 @@ impl<C:Compositor> Content<C> {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
ReflowEvent => {
|
ReflowEvent => {
|
||||||
#debug("content got reflow event");
|
debug!("content got reflow event");
|
||||||
match copy self.document {
|
match copy self.document {
|
||||||
None => {
|
None => {
|
||||||
// Nothing to do.
|
// Nothing to do.
|
||||||
|
|
|
@ -6,11 +6,13 @@ use js::{JS_ARGV, JSCLASS_HAS_RESERVED_SLOTS, JSPROP_ENUMERATE, JSPROP_SHARED, J
|
||||||
use js::jsapi::{JSContext, jsval, JSObject, JSBool, jsid, JSClass, JSFreeOp, JSPropertySpec};
|
use js::jsapi::{JSContext, jsval, JSObject, JSBool, jsid, JSClass, JSFreeOp, JSPropertySpec};
|
||||||
use js::jsapi::bindgen::{JS_ValueToString, JS_GetStringCharsZAndLength, JS_ReportError,
|
use js::jsapi::bindgen::{JS_ValueToString, JS_GetStringCharsZAndLength, JS_ReportError,
|
||||||
JS_GetReservedSlot, JS_SetReservedSlot, JS_NewStringCopyN,
|
JS_GetReservedSlot, JS_SetReservedSlot, JS_NewStringCopyN,
|
||||||
JS_DefineFunctions, JS_DefineProperty, JS_GetContextPrivate};
|
JS_DefineFunctions, JS_DefineProperty};
|
||||||
use js::jsapi::bindgen::*;
|
use js::jsapi::bindgen::*;
|
||||||
use js::glue::bindgen::*;
|
use js::glue::bindgen::*;
|
||||||
use js::crust::{JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, JS_ConvertStub};
|
use js::crust::{JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, JS_ConvertStub};
|
||||||
|
|
||||||
|
use content::content_task::{Content, task_from_context};
|
||||||
|
use layout::layout_task;
|
||||||
use dom::node::{Node, NodeScope, Element};
|
use dom::node::{Node, NodeScope, Element};
|
||||||
use dom::element::*;
|
use dom::element::*;
|
||||||
use node::NodeBundle;
|
use node::NodeBundle;
|
||||||
|
@ -70,20 +72,20 @@ extern fn HTMLImageElement_getWidth(cx: *JSContext, _argc: c_uint, vp: *mut jsva
|
||||||
}
|
}
|
||||||
|
|
||||||
let bundle = unwrap(obj);
|
let bundle = unwrap(obj);
|
||||||
let width = (*bundle).payload.scope.write((*bundle).payload.node, |nd| {
|
let node = (*bundle).payload.node;
|
||||||
|
let scope = (*bundle).payload.scope;
|
||||||
|
let width = scope.write(node, |nd| {
|
||||||
match nd.kind {
|
match nd.kind {
|
||||||
~Element(ed) => {
|
~Element(ed) => {
|
||||||
match ed.kind {
|
match ed.kind {
|
||||||
~HTMLImageElement(*) => {
|
~HTMLImageElement(*) => {
|
||||||
// TODO: this should actually come from rendered dimensions!
|
let content : &Content = task_from_context(cx);
|
||||||
match ed.get_attr(~"width") {
|
match content.query_layout(layout_task::ContentBox(node)) {
|
||||||
Some(s) => match int::from_str(s) {
|
Ok(rect) => rect.width,
|
||||||
Some(w) => au::from_px(w),
|
Err(()) => 0,
|
||||||
None => au(0) /* failed to parse a number */
|
}
|
||||||
},
|
// TODO: if nothing is being rendered(?), return zero dimensions
|
||||||
None => au(0) /* no width attr. */
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
_ => fail ~"why is this not an image element?"
|
_ => fail ~"why is this not an image element?"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -91,7 +93,7 @@ extern fn HTMLImageElement_getWidth(cx: *JSContext, _argc: c_uint, vp: *mut jsva
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
*vp = RUST_INT_TO_JSVAL(
|
*vp = RUST_INT_TO_JSVAL(
|
||||||
(au::to_px(width) & (i32::max_value as int)) as libc::c_int);
|
(width & (i32::max_value as int)) as libc::c_int);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ pub struct ImageHolder {
|
||||||
// occasionally while get_image is being called
|
// occasionally while get_image is being called
|
||||||
mut url : Option<Url>,
|
mut url : Option<Url>,
|
||||||
mut image : Option<ARC<~Image>>,
|
mut image : Option<ARC<~Image>>,
|
||||||
|
mut cached_size: Size2D<int>,
|
||||||
image_cache_task: ImageCacheTask,
|
image_cache_task: ImageCacheTask,
|
||||||
reflow_cb: fn~(),
|
reflow_cb: fn~(),
|
||||||
|
|
||||||
|
@ -23,6 +24,7 @@ fn ImageHolder(url : &Url, image_cache_task: ImageCacheTask, cb: fn~()) -> Image
|
||||||
let holder = ImageHolder {
|
let holder = ImageHolder {
|
||||||
url : Some(copy *url),
|
url : Some(copy *url),
|
||||||
image : None,
|
image : None,
|
||||||
|
cached_size : Size2D(0,0),
|
||||||
image_cache_task : image_cache_task,
|
image_cache_task : image_cache_task,
|
||||||
reflow_cb : copy cb,
|
reflow_cb : copy cb,
|
||||||
};
|
};
|
||||||
|
@ -39,13 +41,26 @@ fn ImageHolder(url : &Url, image_cache_task: ImageCacheTask, cb: fn~()) -> Image
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ImageHolder {
|
impl ImageHolder {
|
||||||
|
/**
|
||||||
|
This version doesn't perform any computation, but may be stale w.r.t.
|
||||||
|
newly-available image data that determines size.
|
||||||
|
|
||||||
|
The intent is that the impure version is used during layout when
|
||||||
|
dimensions are used for computing layout.
|
||||||
|
*/
|
||||||
|
pure fn size() -> Size2D<int> {
|
||||||
|
self.cached_size
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Query and update current image size */
|
||||||
fn get_size() -> Option<Size2D<int>> {
|
fn get_size() -> Option<Size2D<int>> {
|
||||||
debug!("get_size() %?", self.url);
|
debug!("get_size() %?", self.url);
|
||||||
match self.get_image() {
|
match self.get_image() {
|
||||||
Some(img) => {
|
Some(img) => {
|
||||||
let img_ref = get(&img);
|
let img_ref = get(&img);
|
||||||
Some(Size2D(img_ref.width as int,
|
self.cached_size = Size2D(img_ref.width as int,
|
||||||
img_ref.height as int))
|
img_ref.height as int);
|
||||||
|
Some(copy self.cached_size)
|
||||||
},
|
},
|
||||||
None => None
|
None => None
|
||||||
}
|
}
|
||||||
|
|
|
@ -183,10 +183,10 @@ impl @RenderBox {
|
||||||
|
|
||||||
/* The box formed by the content edge, as defined in CSS 2.1 Section 8.1.
|
/* The box formed by the content edge, as defined in CSS 2.1 Section 8.1.
|
||||||
Coordinates are relative to the owning flow. */
|
Coordinates are relative to the owning flow. */
|
||||||
fn content_box() -> Rect<au> {
|
pure fn content_box() -> Rect<au> {
|
||||||
match self.kind {
|
match self.kind {
|
||||||
ImageBox(i) => {
|
ImageBox(i) => {
|
||||||
let size = i.get_size().get_default(Size2D(0,0));
|
let size = i.size();
|
||||||
Rect {
|
Rect {
|
||||||
origin: copy self.data.position.origin,
|
origin: copy self.data.position.origin,
|
||||||
size: Size2D(au::from_px(size.width),
|
size: Size2D(au::from_px(size.width),
|
||||||
|
|
|
@ -140,7 +140,25 @@ impl @FlowContext {
|
||||||
|
|
||||||
// Actual methods that do not require much flow-specific logic
|
// Actual methods that do not require much flow-specific logic
|
||||||
impl @FlowContext {
|
impl @FlowContext {
|
||||||
fn iter_boxes_for_node<T>(node: Node, cb: pure fn&(@RenderBox) -> T) {
|
pure fn foldl_boxes_for_node<B: Copy>(node: Node, seed: B, blk: pure fn&(B,@RenderBox) -> B) -> B {
|
||||||
|
match self.kind {
|
||||||
|
RootFlow(d) => match d.box {
|
||||||
|
Some(box) if box.node == node => { blk(seed, box) },
|
||||||
|
_ => seed
|
||||||
|
},
|
||||||
|
BlockFlow(d) => match d.box {
|
||||||
|
Some(box) if box.node == node => { blk(seed, box) },
|
||||||
|
_ => seed
|
||||||
|
},
|
||||||
|
InlineFlow(d) => do d.boxes.foldl(seed) |acc, box| {
|
||||||
|
if box.node == node { blk(acc, box) }
|
||||||
|
else { acc }
|
||||||
|
},
|
||||||
|
_ => fail fmt!("Don't know how to iterate node's RenderBoxes for %?", self.kind)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pure fn iter_boxes_for_node<T>(node: Node, cb: pure fn&(@RenderBox) -> T) {
|
||||||
match self.kind {
|
match self.kind {
|
||||||
RootFlow(d) => match d.box {
|
RootFlow(d) => match d.box {
|
||||||
Some(box) if box.node == node => { cb(box); },
|
Some(box) if box.node == node => { cb(box); },
|
||||||
|
|
|
@ -19,6 +19,7 @@ use gfx::render_task;
|
||||||
use layout::box::RenderBox;
|
use layout::box::RenderBox;
|
||||||
use layout::box_builder::LayoutTreeBuilder;
|
use layout::box_builder::LayoutTreeBuilder;
|
||||||
use layout::context::LayoutContext;
|
use layout::context::LayoutContext;
|
||||||
|
use opt = core::option;
|
||||||
use render_task::RenderTask;
|
use render_task::RenderTask;
|
||||||
use resource::image_cache_task::ImageCacheTask;
|
use resource::image_cache_task::ImageCacheTask;
|
||||||
use servo_text::font_cache::FontCache;
|
use servo_text::font_cache::FontCache;
|
||||||
|
@ -31,43 +32,118 @@ use task::*;
|
||||||
|
|
||||||
type LayoutTask = Chan<Msg>;
|
type LayoutTask = Chan<Msg>;
|
||||||
|
|
||||||
|
enum LayoutQuery {
|
||||||
|
ContentBox(Node)
|
||||||
|
}
|
||||||
|
|
||||||
|
type LayoutQueryResponse = Result<LayoutQueryResponse_, ()>;
|
||||||
|
|
||||||
|
enum LayoutQueryResponse_ {
|
||||||
|
ContentSize(Size2D<int>)
|
||||||
|
}
|
||||||
|
|
||||||
enum Msg {
|
enum Msg {
|
||||||
BuildMsg(Node, ARC<Stylesheet>, Url, Chan<Event>),
|
BuildMsg(Node, ARC<Stylesheet>, Url, Chan<Event>),
|
||||||
PingMsg(Chan<content_task::PingMsg>),
|
PingMsg(Chan<content_task::PingMsg>),
|
||||||
|
QueryMsg(LayoutQuery, Chan<LayoutQueryResponse>),
|
||||||
ExitMsg
|
ExitMsg
|
||||||
}
|
}
|
||||||
|
|
||||||
fn LayoutTask(render_task: RenderTask, image_cache_task: ImageCacheTask) -> LayoutTask {
|
fn LayoutTask(render_task: RenderTask,
|
||||||
do spawn_listener::<Msg>|request| {
|
img_cache_task: ImageCacheTask) -> LayoutTask {
|
||||||
|
do spawn_listener::<Msg>|from_content| {
|
||||||
|
Layout(render_task, img_cache_task, from_content).start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// This just keeps our dom aux objects alive
|
struct Layout {
|
||||||
let layout_data_refs = DVec();
|
render_task: RenderTask,
|
||||||
let font_cache = FontCache();
|
image_cache_task: ImageCacheTask,
|
||||||
|
from_content: comm::Port<Msg>,
|
||||||
|
|
||||||
loop {
|
font_cache: @FontCache,
|
||||||
|
// This is used to root auxilliary RCU reader data
|
||||||
|
layout_refs: DVec<@LayoutData>
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Layout(render_task: RenderTask,
|
||||||
|
image_cache_task: ImageCacheTask,
|
||||||
|
from_content: comm::Port<Msg>) -> Layout {
|
||||||
|
|
||||||
|
Layout {
|
||||||
|
render_task: render_task,
|
||||||
|
image_cache_task: image_cache_task,
|
||||||
|
from_content: from_content,
|
||||||
|
font_cache: FontCache(),
|
||||||
|
layout_refs: DVec()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Layout {
|
||||||
|
|
||||||
|
fn handle_query(query: LayoutQuery,
|
||||||
|
reply_chan: comm::Chan<LayoutQueryResponse>) {
|
||||||
|
match query {
|
||||||
|
ContentBox(node) => {
|
||||||
|
// TODO: extract me to a method when I get sibling arms
|
||||||
|
let response = match node.aux(|a| a).flow {
|
||||||
|
None => Err(()),
|
||||||
|
Some(flow) => {
|
||||||
|
let start_val : Option<Rect<au>> = None;
|
||||||
|
let rect = do flow.foldl_boxes_for_node(node, start_val) |acc, box| {
|
||||||
|
match acc {
|
||||||
|
Some(acc) => Some(acc.union(&box.content_box())),
|
||||||
|
None => Some(box.content_box())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match rect {
|
||||||
|
None => Err(()),
|
||||||
|
Some(rect) => {
|
||||||
|
let size = Size2D(au::to_px(rect.size.width),
|
||||||
|
au::to_px(rect.size.height));
|
||||||
|
Ok(ContentSize(move size))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
reply_chan.send(response)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start() {
|
||||||
|
while self.handle_request(self.from_content) {
|
||||||
|
// loop indefinitely
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_request(request: comm::Port<Msg>) -> bool {
|
||||||
match request.recv() {
|
match request.recv() {
|
||||||
PingMsg(ping_channel) => ping_channel.send(content_task::PongMsg),
|
PingMsg(ping_channel) => ping_channel.send(content_task::PongMsg),
|
||||||
|
QueryMsg(query, chan) => self.handle_query(query, chan),
|
||||||
ExitMsg => {
|
ExitMsg => {
|
||||||
debug!("layout: ExitMsg received");
|
debug!("layout: ExitMsg received");
|
||||||
break;
|
return false
|
||||||
}
|
},
|
||||||
BuildMsg(node, styles, doc_url, event_chan) => {
|
BuildMsg(node, styles, doc_url, to_content) => {
|
||||||
debug!("layout: received layout request for: %s", doc_url.to_str());
|
debug!("layout: received layout request for: %s", doc_url.to_str());
|
||||||
debug!("layout: parsed Node tree");
|
debug!("layout: parsed Node tree");
|
||||||
node.dump();
|
node.dump();
|
||||||
|
|
||||||
let layout_ctx = LayoutContext {
|
let layout_ctx = LayoutContext {
|
||||||
image_cache: image_cache_task,
|
image_cache: self.image_cache_task,
|
||||||
font_cache: font_cache,
|
font_cache: self.font_cache,
|
||||||
doc_url: doc_url,
|
doc_url: doc_url,
|
||||||
reflow_cb: || event_chan.send(ReflowEvent),
|
reflow_cb: || to_content.send(ReflowEvent),
|
||||||
// TODO: obtain screen size from a real data source
|
// TODO: obtain screen size from a real data source
|
||||||
screen_size: Rect(Point2D(au(0), au(0)), Size2D(au::from_px(800), au::from_px(600)))
|
screen_size: Rect(Point2D(au(0), au(0)), Size2D(au::from_px(800), au::from_px(600)))
|
||||||
};
|
};
|
||||||
|
|
||||||
do util::time::time(~"layout") {
|
do util::time::time(~"layout") {
|
||||||
// TODO: this is dumb. we don't need 3 separate traversals.
|
// TODO: this is dumb. we don't need 3 separate traversals.
|
||||||
node.initialize_style_for_subtree(&layout_ctx, &layout_data_refs);
|
node.initialize_style_for_subtree(&layout_ctx, &self.layout_refs);
|
||||||
node.recompute_style_for_subtree(&layout_ctx, styles);
|
node.recompute_style_for_subtree(&layout_ctx, styles);
|
||||||
/* resolve styles (convert relative values) down the node tree */
|
/* resolve styles (convert relative values) down the node tree */
|
||||||
apply_style(&layout_ctx, node, layout_ctx.reflow_cb);
|
apply_style(&layout_ctx, node, layout_ctx.reflow_cb);
|
||||||
|
@ -93,10 +169,12 @@ fn LayoutTask(render_task: RenderTask, image_cache_task: ImageCacheTask) -> Layo
|
||||||
// TODO: set options on the builder before building
|
// TODO: set options on the builder before building
|
||||||
// TODO: be smarter about what needs painting
|
// TODO: be smarter about what needs painting
|
||||||
layout_root.build_display_list(&builder, © layout_root.data.position, &dlist);
|
layout_root.build_display_list(&builder, © layout_root.data.position, &dlist);
|
||||||
render_task.send(render_task::RenderMsg(dlist));
|
self.render_task.send(render_task::RenderMsg(dlist));
|
||||||
}
|
} // time(layout)
|
||||||
}
|
} // BuildMsg
|
||||||
}
|
} // match
|
||||||
}
|
|
||||||
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue