Add link following and refactor the profiler.

This commit is contained in:
Tim Kuehn 2013-06-06 15:12:02 -07:00 committed by Patrick Walton
parent e5c0021299
commit a53a7f689d
18 changed files with 325 additions and 107 deletions

View file

@ -18,6 +18,8 @@ use azure::scaled_font::ScaledFont;
use azure::azure_hl::{BackendType, ColorPattern}; use azure::azure_hl::{BackendType, ColorPattern};
use geom::{Point2D, Rect, Size2D}; use geom::{Point2D, Rect, Size2D};
use servo_util::time::ProfilerChan;
// FontHandle encapsulates access to the platform's font API, // FontHandle encapsulates access to the platform's font API,
// e.g. quartz, FreeType. It provides access to metrics and tables // e.g. quartz, FreeType. It provides access to metrics and tables
// needed by the text shaper as well as access to the underlying font // needed by the text shaper as well as access to the underlying font
@ -210,13 +212,15 @@ pub struct Font {
style: UsedFontStyle, style: UsedFontStyle,
metrics: FontMetrics, metrics: FontMetrics,
backend: BackendType, backend: BackendType,
profiler_chan: ProfilerChan,
} }
pub impl Font { pub impl Font {
fn new_from_buffer(ctx: &FontContext, fn new_from_buffer(ctx: &FontContext,
buffer: ~[u8], buffer: ~[u8],
style: &SpecifiedFontStyle, style: &SpecifiedFontStyle,
backend: BackendType) backend: BackendType,
profiler_chan: ProfilerChan)
-> Result<@mut Font, ()> { -> Result<@mut Font, ()> {
let handle = FontHandleMethods::new_from_buffer(&ctx.handle, buffer, style); let handle = FontHandleMethods::new_from_buffer(&ctx.handle, buffer, style);
let handle: FontHandle = if handle.is_ok() { let handle: FontHandle = if handle.is_ok() {
@ -235,11 +239,13 @@ pub impl Font {
style: copy *style, style: copy *style,
metrics: metrics, metrics: metrics,
backend: backend, backend: backend,
profiler_chan: profiler_chan,
}); });
} }
fn new_from_adopted_handle(_fctx: &FontContext, handle: FontHandle, fn new_from_adopted_handle(_fctx: &FontContext, handle: FontHandle,
style: &SpecifiedFontStyle, backend: BackendType) -> @mut Font { style: &SpecifiedFontStyle, backend: BackendType,
profiler_chan: ProfilerChan) -> @mut Font {
let metrics = handle.get_metrics(); let metrics = handle.get_metrics();
@mut Font { @mut Font {
@ -249,11 +255,13 @@ pub impl Font {
style: copy *style, style: copy *style,
metrics: metrics, metrics: metrics,
backend: backend, backend: backend,
profiler_chan: profiler_chan,
} }
} }
fn new_from_existing_handle(fctx: &FontContext, handle: &FontHandle, fn new_from_existing_handle(fctx: &FontContext, handle: &FontHandle,
style: &SpecifiedFontStyle, backend: BackendType) -> Result<@mut Font,()> { style: &SpecifiedFontStyle, backend: BackendType,
profiler_chan: ProfilerChan) -> Result<@mut Font,()> {
// TODO(Issue #179): convert between specified and used font style here? // TODO(Issue #179): convert between specified and used font style here?
let styled_handle = match handle.clone_with_style(&fctx.handle, style) { let styled_handle = match handle.clone_with_style(&fctx.handle, style) {
@ -261,7 +269,7 @@ pub impl Font {
Err(()) => return Err(()) Err(()) => return Err(())
}; };
return Ok(Font::new_from_adopted_handle(fctx, styled_handle, style, backend)); return Ok(Font::new_from_adopted_handle(fctx, styled_handle, style, backend, profiler_chan));
} }
priv fn get_shaper(@mut self) -> @Shaper { priv fn get_shaper(@mut self) -> @Shaper {

View file

@ -40,17 +40,18 @@ pub struct FontContext {
handle: FontContextHandle, handle: FontContextHandle,
backend: BackendType, backend: BackendType,
generic_fonts: HashMap<~str,~str>, generic_fonts: HashMap<~str,~str>,
profiler_chan: ProfilerChan,
} }
#[allow(non_implicitly_copyable_typarams)] #[allow(non_implicitly_copyable_typarams)]
pub impl<'self> FontContext { pub impl<'self> FontContext {
fn new(backend: BackendType, fn new(backend: BackendType,
needs_font_list: bool, needs_font_list: bool,
prof_chan: ProfilerChan) profiler_chan: ProfilerChan)
-> FontContext { -> FontContext {
let handle = FontContextHandle::new(); let handle = FontContextHandle::new();
let font_list = if needs_font_list { let font_list = if needs_font_list {
Some(FontList::new(&handle, prof_chan.clone())) } Some(FontList::new(&handle, profiler_chan.clone())) }
else { None }; else { None };
// TODO: Allow users to specify these. // TODO: Allow users to specify these.
@ -69,6 +70,7 @@ pub impl<'self> FontContext {
handle: handle, handle: handle,
backend: backend, backend: backend,
generic_fonts: generic_fonts, generic_fonts: generic_fonts,
profiler_chan: profiler_chan,
} }
} }
@ -125,7 +127,8 @@ pub impl<'self> FontContext {
for result.each |font_entry| { for result.each |font_entry| {
found = true; found = true;
// TODO(Issue #203): route this instantion through FontContext's Font instance cache. // TODO(Issue #203): route this instantion through FontContext's Font instance cache.
let instance = Font::new_from_existing_handle(self, &font_entry.handle, style, self.backend); let instance = Font::new_from_existing_handle(self, &font_entry.handle, style, self.backend,
self.profiler_chan.clone());
do result::iter(&instance) |font: &@mut Font| { fonts.push(*font); } do result::iter(&instance) |font: &@mut Font| { fonts.push(*font); }
}; };
@ -163,7 +166,8 @@ pub impl<'self> FontContext {
Ok(Font::new_from_adopted_handle(self, Ok(Font::new_from_adopted_handle(self,
handle, handle,
&desc.style, &desc.style,
self.backend)) self.backend,
self.profiler_chan.clone()))
}) })
} }
}; };

View file

@ -13,6 +13,7 @@ pub struct Opts {
render_backend: BackendType, render_backend: BackendType,
n_render_threads: uint, n_render_threads: uint,
tile_size: uint, tile_size: uint,
profiler_period: Option<f64>,
} }
#[allow(non_implicitly_copyable_typarams)] #[allow(non_implicitly_copyable_typarams)]
@ -26,13 +27,13 @@ pub fn from_cmdline_args(args: &[~str]) -> Opts {
getopts::optopt(~"r"), // rendering backend getopts::optopt(~"r"), // rendering backend
getopts::optopt(~"s"), // size of tiles getopts::optopt(~"s"), // size of tiles
getopts::optopt(~"t"), // threads to render with getopts::optopt(~"t"), // threads to render with
getopts::optflagopt(~"p"), // profiler flag and output interval
]; ];
let opt_match = match getopts::getopts(args, opts) { let opt_match = match getopts::getopts(args, opts) {
result::Ok(m) => { copy m } result::Ok(m) => { copy m }
result::Err(f) => { fail!(getopts::fail_str(copy f)) } result::Err(f) => { fail!(getopts::fail_str(copy f)) }
}; };
let urls = if opt_match.free.is_empty() { let urls = if opt_match.free.is_empty() {
fail!(~"servo asks that you provide 1 or more URLs") fail!(~"servo asks that you provide 1 or more URLs")
} else { } else {
@ -68,10 +69,18 @@ pub fn from_cmdline_args(args: &[~str]) -> Opts {
None => 1, // FIXME: Number of cores. None => 1, // FIXME: Number of cores.
}; };
let profiler_period: Option<f64> =
// if only flag is present, default to 5 second period
match getopts::opt_default(&opt_match, ~"p", ~"5") {
Some(period) => Some(f64::from_str(period).get()),
None => None,
};
Opts { Opts {
urls: urls, urls: urls,
render_backend: render_backend, render_backend: render_backend,
n_render_threads: n_render_threads, n_render_threads: n_render_threads,
tile_size: tile_size, tile_size: tile_size,
profiler_period: profiler_period,
} }
} }

View file

@ -18,9 +18,7 @@ use core::task::SingleThreaded;
use std::task_pool::TaskPool; use std::task_pool::TaskPool;
use servo_net::util::spawn_listener; use servo_net::util::spawn_listener;
use servo_util::time::ProfilerChan; use servo_util::time::{ProfilerChan, profile};
use servo_util::time::profile;
use servo_util::time::time;
use servo_util::time; use servo_util::time;
pub enum Msg { pub enum Msg {
@ -124,7 +122,7 @@ impl<C: Compositor + Owned> Renderer<C> {
fn render(&mut self, render_layer: RenderLayer) { fn render(&mut self, render_layer: RenderLayer) {
debug!("renderer: rendering"); debug!("renderer: rendering");
do time("rendering") { do profile(time::RenderingCategory, self.profiler_chan.clone()) {
let layer_buffer_set = do render_layers(&render_layer, let layer_buffer_set = do render_layers(&render_layer,
&self.opts, &self.opts,
self.profiler_chan.clone()) |render_layer_ref, self.profiler_chan.clone()) |render_layer_ref,

View file

@ -6,6 +6,8 @@ use font_context::FontContext;
use geometry::Au; use geometry::Au;
use text::glyph::{BreakTypeNormal, GlyphStore}; use text::glyph::{BreakTypeNormal, GlyphStore};
use font::{Font, FontDescriptor, RunMetrics}; use font::{Font, FontDescriptor, RunMetrics};
use servo_util::time;
use servo_util::time::profile;
use servo_util::range::Range; use servo_util::range::Range;
/// A text run. /// A text run.
@ -44,7 +46,9 @@ pub impl<'self> TextRun {
fn new(font: @mut Font, text: ~str, underline: bool) -> TextRun { fn new(font: @mut Font, text: ~str, underline: bool) -> TextRun {
let mut glyph_store = GlyphStore::new(str::char_len(text)); let mut glyph_store = GlyphStore::new(str::char_len(text));
TextRun::compute_potential_breaks(text, &mut glyph_store); TextRun::compute_potential_breaks(text, &mut glyph_store);
font.shape_text(text, &mut glyph_store); do profile(time::LayoutShapingCategory, font.profiler_chan.clone()) {
font.shape_text(text, &mut glyph_store);
}
let run = TextRun { let run = TextRun {
text: text, text: text,

View file

@ -5,8 +5,10 @@
use compositing::resize_rate_limiter::ResizeRateLimiter; use compositing::resize_rate_limiter::ResizeRateLimiter;
use platform::{Application, Window}; use platform::{Application, Window};
use script::script_task::{LoadMsg, ScriptMsg, SendEventMsg}; use script::script_task::{LoadMsg, ScriptMsg, SendEventMsg};
use windowing::{ApplicationMethods, WindowMethods}; use windowing::{ApplicationMethods, WindowMethods, WindowMouseEvent, WindowClickEvent};
use script::dom::event::ClickEvent; use windowing::{WindowMouseDownEvent, WindowMouseUpEvent};
use script::dom::event::{Event, ClickEvent, MouseDownEvent, MouseUpEvent};
use azure::azure_hl::{DataSourceSurface, DrawTarget, SourceSurfaceMethods}; use azure::azure_hl::{DataSourceSurface, DrawTarget, SourceSurfaceMethods};
use core::cell::Cell; use core::cell::Cell;
@ -233,12 +235,24 @@ fn run_main_loop(port: Port<Msg>,
let script_chan_clone = script_chan.clone(); let script_chan_clone = script_chan.clone();
// When the user clicks, perform hit testing // When the user triggers a mouse event, perform appropriate hit testing
do window.set_click_callback |layer_click_point| { do window.set_mouse_callback |window_mouse_event: WindowMouseEvent| {
let world_click_point = layer_click_point + *world_offset; let event: Event;
debug!("osmain: clicked at %?", world_click_point); let world_mouse_point = |layer_mouse_point: Point2D<f32>| {
layer_mouse_point + *world_offset
script_chan_clone.send(SendEventMsg(ClickEvent(world_click_point))); };
match window_mouse_event {
WindowClickEvent(button, layer_mouse_point) => {
event = ClickEvent(button, world_mouse_point(layer_mouse_point));
}
WindowMouseDownEvent(button, layer_mouse_point) => {
event = MouseDownEvent(button, world_mouse_point(layer_mouse_point));
}
WindowMouseUpEvent(button, layer_mouse_point) => {
event = MouseUpEvent(button, world_mouse_point(layer_mouse_point));
}
}
script_chan_clone.send(SendEventMsg(event));
} }
// When the user scrolls, move the layer around. // When the user scrolls, move the layer around.

View file

@ -24,6 +24,16 @@ fn with_node_name<R>(node: AbstractNode<LayoutView>, f: &fn(&str) -> R) -> R {
} }
impl SelectHandler<AbstractNode<LayoutView>> for NodeSelectHandler { impl SelectHandler<AbstractNode<LayoutView>> for NodeSelectHandler {
// FIXME(tkuehn): placeholder to get servo to compile
fn node_has_class(&self, node: &AbstractNode<LayoutView>, s: &str) -> bool {
true
}
// FIXME(tkuehn): placeholder to get servo to compile
fn with_node_classes<R>(&self, node: &AbstractNode<LayoutView>, f: &fn(Option<&str>) -> R) -> R {
f(None)
}
fn with_node_name<R>(&self, node: &AbstractNode<LayoutView>, f: &fn(&str) -> R) -> R { fn with_node_name<R>(&self, node: &AbstractNode<LayoutView>, f: &fn(&str) -> R) -> R {
with_node_name(*node, f) with_node_name(*node, f)
} }

View file

@ -18,7 +18,7 @@ use script::script_task;
use servo_net::image_cache_task::{ImageCacheTask, ImageCacheTaskClient}; use servo_net::image_cache_task::{ImageCacheTask, ImageCacheTaskClient};
use servo_net::resource_task::ResourceTask; use servo_net::resource_task::ResourceTask;
use servo_net::resource_task; use servo_net::resource_task;
use servo_util::time::{ProfilerChan, ProfilerPort, ProfilerTask}; use servo_util::time::{profiler_force_print, ProfilerChan, ProfilerPort, ProfilerTask};
use std::net::url::Url; use std::net::url::Url;
pub type EngineTask = Chan<Msg>; pub type EngineTask = Chan<Msg>;
@ -39,6 +39,12 @@ pub struct Engine {
profiler_task: ProfilerTask, profiler_task: ProfilerTask,
} }
impl Drop for Engine {
fn finalize(&self) {
profiler_force_print(self.profiler_task.chan.clone());
}
}
impl Engine { impl Engine {
pub fn start(compositor: CompositorTask, pub fn start(compositor: CompositorTask,
opts: &Opts, opts: &Opts,
@ -58,9 +64,12 @@ impl Engine {
opts.with_ref(|o| copy *o), opts.with_ref(|o| copy *o),
profiler_chan.clone()); profiler_chan.clone());
let profiler_task = ProfilerTask::new(profiler_port.take(), profiler_chan.clone());
let opts = opts.take(); let opts = opts.take();
let profiler_task = ProfilerTask::new(profiler_port.take(),
profiler_chan.clone(),
opts.profiler_period);
let layout_task = layout_task::create_layout_task(render_task.clone(), let layout_task = layout_task::create_layout_task(render_task.clone(),
image_cache_task.clone(), image_cache_task.clone(),
opts, opts,

View file

@ -342,13 +342,14 @@ impl Layout {
flow.build_display_list(&builder, flow.build_display_list(&builder,
&flow.position(), &flow.position(),
display_list); display_list);
// iterate in reverse to ensure we have the most recently painted render box
let (x, y) = (Au::from_frac_px(point.x as float), let (x, y) = (Au::from_frac_px(point.x as float),
Au::from_frac_px(point.y as float)); Au::from_frac_px(point.y as float));
let mut resp = Err(()); let mut resp = Err(());
let display_list = &display_list.take().list; let display_list = &display_list.take().list;
// iterate in reverse to ensure we have the most recently painted render box
for display_list.each_reverse |display_item| { for display_list.each_reverse |display_item| {
let bounds = display_item.bounds(); let bounds = display_item.bounds();
// TODO this check should really be performed by a method of DisplayItem
if x <= bounds.origin.x + bounds.size.width && if x <= bounds.origin.x + bounds.size.width &&
bounds.origin.x <= x && bounds.origin.x <= x &&
y < bounds.origin.y + bounds.size.height && y < bounds.origin.y + bounds.size.height &&

View file

@ -7,8 +7,9 @@
/// GLUT is a very old and bare-bones toolkit. However, it has good cross-platform support, at /// GLUT is a very old and bare-bones toolkit. However, it has good cross-platform support, at
/// least on desktops. It is designed for testing Servo without the need of a UI. /// least on desktops. It is designed for testing Servo without the need of a UI.
use windowing::{ApplicationMethods, CompositeCallback, LoadUrlCallback, ClickCallback}; use windowing::{ApplicationMethods, CompositeCallback, LoadUrlCallback, MouseCallback};
use windowing::{ResizeCallback, ScrollCallback, WindowMethods}; use windowing::{ResizeCallback, ScrollCallback, WindowMethods, WindowMouseEvent, WindowClickEvent};
use windowing::{WindowMouseDownEvent, WindowMouseUpEvent};
use alert::{Alert, AlertMethods}; use alert::{Alert, AlertMethods};
use core::libc::c_int; use core::libc::c_int;
@ -36,10 +37,12 @@ pub struct Window {
composite_callback: Option<CompositeCallback>, composite_callback: Option<CompositeCallback>,
resize_callback: Option<ResizeCallback>, resize_callback: Option<ResizeCallback>,
load_url_callback: Option<LoadUrlCallback>, load_url_callback: Option<LoadUrlCallback>,
click_callback: Option<ClickCallback>, mouse_callback: Option<MouseCallback>,
scroll_callback: Option<ScrollCallback>, scroll_callback: Option<ScrollCallback>,
drag_origin: Point2D<c_int>, drag_origin: Point2D<c_int>,
mouse_down_button: @mut c_int,
mouse_down_point: @mut Point2D<c_int>,
} }
impl WindowMethods<Application> for Window { impl WindowMethods<Application> for Window {
@ -56,10 +59,12 @@ impl WindowMethods<Application> for Window {
composite_callback: None, composite_callback: None,
resize_callback: None, resize_callback: None,
load_url_callback: None, load_url_callback: None,
click_callback: None, mouse_callback: None,
scroll_callback: None, scroll_callback: None,
drag_origin: Point2D(0, 0), drag_origin: Point2D(0, 0),
mouse_down_button: @mut 0,
mouse_down_point: @mut Point2D(0, 0),
}; };
// Register event handlers. // Register event handlers.
@ -79,9 +84,9 @@ impl WindowMethods<Application> for Window {
do glut::keyboard_func |key, _, _| { do glut::keyboard_func |key, _, _| {
window.handle_key(key) window.handle_key(key)
} }
do glut::mouse_func |button, _, x, y| { do glut::mouse_func |button, state, x, y| {
if button < 3 { if button < 3 {
window.handle_click(x, y); window.handle_mouse(button, state, x, y);
} else { } else {
window.handle_scroll(if button == 4 { -30.0 } else { 30.0 }); window.handle_scroll(if button == 4 { -30.0 } else { 30.0 });
} }
@ -117,9 +122,9 @@ impl WindowMethods<Application> for Window {
self.load_url_callback = Some(new_load_url_callback) self.load_url_callback = Some(new_load_url_callback)
} }
/// Registers a callback to be run when a click event occurs. /// Registers a callback to be run when a mouse event occurs.
pub fn set_click_callback(&mut self, new_click_callback: ClickCallback) { pub fn set_mouse_callback(&mut self, new_mouse_callback: MouseCallback) {
self.click_callback = Some(new_click_callback) self.mouse_callback = Some(new_mouse_callback)
} }
/// Registers a callback to be run when the user scrolls. /// Registers a callback to be run when the user scrolls.
@ -148,10 +153,36 @@ impl Window {
} }
/// Helper function to handle a click /// Helper function to handle a click
fn handle_click(&self, x: c_int, y: c_int) { fn handle_mouse(&self, button: c_int, state: c_int, x: c_int, y: c_int) {
match self.click_callback { // FIXME(tkuehn): max pixel dist should be based on pixel density
let max_pixel_dist = 10f;
match self.mouse_callback {
None => {} None => {}
Some(callback) => callback(Point2D(x as f32, y as f32)), Some(callback) => {
let event: WindowMouseEvent;
match state {
glut::MOUSE_DOWN => {
event = WindowMouseDownEvent(button as uint, Point2D(x as f32, y as f32));
*self.mouse_down_point = Point2D(x, y);
*self.mouse_down_button = button;
}
glut::MOUSE_UP => {
event = WindowMouseUpEvent(button as uint, Point2D(x as f32, y as f32));
if *self.mouse_down_button == button {
let pixel_dist = *self.mouse_down_point - Point2D(x, y);
let pixel_dist = ((pixel_dist.x * pixel_dist.x +
pixel_dist.y * pixel_dist.y) as float).sqrt();
if pixel_dist < max_pixel_dist {
let click_event = WindowClickEvent(button as uint,
Point2D(x as f32, y as f32));
callback(click_event);
}
}
}
_ => fail!("I cannot recognize the type of mouse action that occured. :-(")
};
callback(event);
}
} }
} }

View file

@ -7,6 +7,12 @@
use geom::point::Point2D; use geom::point::Point2D;
use geom::size::Size2D; use geom::size::Size2D;
pub enum WindowMouseEvent {
WindowClickEvent(uint, Point2D<f32>),
WindowMouseDownEvent(uint, Point2D<f32>),
WindowMouseUpEvent(uint, Point2D<f32>),
}
/// Type of the function that is called when the screen is to be redisplayed. /// Type of the function that is called when the screen is to be redisplayed.
pub type CompositeCallback = @fn(); pub type CompositeCallback = @fn();
@ -16,9 +22,8 @@ pub type ResizeCallback = @fn(uint, uint);
/// Type of the function that is called when a new URL is to be loaded. /// Type of the function that is called when a new URL is to be loaded.
pub type LoadUrlCallback = @fn(&str); pub type LoadUrlCallback = @fn(&str);
/// Type of the function that is called when hit testing is to be performed. /// Type of the function that is called when a mouse hit test is to be performed.
/// FIXME this currently does not discriminate between left and right clicks or any modifiers pub type MouseCallback = @fn(WindowMouseEvent);
pub type ClickCallback = @fn(Point2D<f32>);
/// Type of the function that is called when the user scrolls. /// Type of the function that is called when the user scrolls.
pub type ScrollCallback = @fn(Point2D<f32>); pub type ScrollCallback = @fn(Point2D<f32>);
@ -43,7 +48,7 @@ pub trait WindowMethods<A> {
/// Registers a callback to run when a new URL is to be loaded. /// Registers a callback to run when a new URL is to be loaded.
pub fn set_load_url_callback(&mut self, new_load_url_callback: LoadUrlCallback); pub fn set_load_url_callback(&mut self, new_load_url_callback: LoadUrlCallback);
/// Registers a callback to run when the user clicks. /// Registers a callback to run when the user clicks.
pub fn set_click_callback(&mut self, new_click_callback: ClickCallback); pub fn set_mouse_callback(&mut self, new_mouse_callback: MouseCallback);
/// Registers a callback to run when the user scrolls. /// Registers a callback to run when the user scrolls.
pub fn set_scroll_callback(&mut self, new_scroll_callback: ScrollCallback); pub fn set_scroll_callback(&mut self, new_scroll_callback: ScrollCallback);

View file

@ -12,7 +12,9 @@ use geom::point::Point2D;
pub enum Event { pub enum Event {
ResizeEvent(uint, uint, comm::Chan<()>), ResizeEvent(uint, uint, comm::Chan<()>),
ReflowEvent, ReflowEvent,
ClickEvent(Point2D<f32>), ClickEvent(uint, Point2D<f32>),
MouseDownEvent(uint, Point2D<f32>),
MouseUpEvent(uint, Point2D<f32>),
} }
pub struct Event_ { pub struct Event_ {

View file

@ -7,7 +7,8 @@
use dom::bindings::utils::GlobalStaticData; use dom::bindings::utils::GlobalStaticData;
use dom::document::Document; use dom::document::Document;
use dom::event::{Event, ResizeEvent, ReflowEvent, ClickEvent}; use dom::element::Element;
use dom::event::{Event, ResizeEvent, ReflowEvent, ClickEvent, MouseDownEvent, MouseUpEvent};
use dom::node::{AbstractNode, ScriptView, define_bindings}; use dom::node::{AbstractNode, ScriptView, define_bindings};
use dom::window::Window; use dom::window::Window;
use layout_interface::{AddStylesheetMsg, DocumentDamage, DocumentDamageLevel, HitTestQuery}; use layout_interface::{AddStylesheetMsg, DocumentDamage, DocumentDamageLevel, HitTestQuery};
@ -37,7 +38,7 @@ use js;
use servo_net::image_cache_task::ImageCacheTask; use servo_net::image_cache_task::ImageCacheTask;
use servo_net::resource_task::ResourceTask; use servo_net::resource_task::ResourceTask;
use servo_util::tree::TreeNodeRef; use servo_util::tree::TreeNodeRef;
use std::net::url::Url; use std::net::url::{Url, from_str};
use std::net::url; use std::net::url;
/// Messages used to control the script task. /// Messages used to control the script task.
@ -503,7 +504,7 @@ impl ScriptContext {
} }
} }
ClickEvent(point) => { ClickEvent(button, point) => {
debug!("ClickEvent: clicked at %?", point); debug!("ClickEvent: clicked at %?", point);
let root = match self.root_frame { let root = match self.root_frame {
Some(ref frame) => frame.document.root, Some(ref frame) => frame.document.root,
@ -511,14 +512,51 @@ impl ScriptContext {
}; };
match self.query_layout(HitTestQuery(root, point)) { match self.query_layout(HitTestQuery(root, point)) {
Ok(node) => match node { Ok(node) => match node {
HitTestResponse(node) => debug!("clicked on %?", node.debug_str()), HitTestResponse(node) => {
debug!("clicked on %?", node.debug_str());
let mut node = node;
// traverse node generations until a node that is an element is found
while !node.is_element() {
match node.parent_node() {
Some(parent) => {
node = parent;
}
None => break
}
}
if node.is_element() {
do node.with_imm_element |element| {
match element.tag_name {
~"a" => self.load_url_from_element(element),
_ => {}
}
}
}
}
_ => fail!(~"unexpected layout reply") _ => fail!(~"unexpected layout reply")
}, },
Err(()) => { Err(()) => {
println(fmt!("layout query error")); debug!(fmt!("layout query error"));
} }
}; };
} }
MouseDownEvent(*) => {}
MouseUpEvent(*) => {}
}
}
priv fn load_url_from_element(&self, element: &Element) {
// if the node's element is "a," load url from href attr
for element.attrs.each |attr| {
if attr.name == ~"href" {
debug!("clicked on link to %?", attr.value);
let url = from_str(attr.value);
match url {
Ok(url) => self.script_chan.send(LoadMsg(url)),
Err(msg) => debug!(msg)
};
break;
}
} }
} }
} }

View file

@ -6,62 +6,129 @@
use std::time::precise_time_ns; use std::time::precise_time_ns;
use core::cell::Cell; use core::cell::Cell;
use core::comm::{Port, SharedChan}; use core::comm::{Port, SharedChan};
use core::os::getenv; use std::sort::tim_sort;
#[deriving(Eq)]
pub enum ProfilerCategory { pub enum ProfilerCategory {
CompositingCategory, CompositingCategory,
LayoutPerformCategory,
LayoutQueryCategory, LayoutQueryCategory,
LayoutPerformCategory,
LayoutAuxInitCategory, LayoutAuxInitCategory,
LayoutSelectorMatchCategory, LayoutSelectorMatchCategory,
LayoutTreeBuilderCategory, LayoutTreeBuilderCategory,
LayoutMainCategory, LayoutMainCategory,
LayoutShapingCategory,
LayoutDispListBuildCategory, LayoutDispListBuildCategory,
GfxRegenAvailableFontsCategory, GfxRegenAvailableFontsCategory,
RenderingPrepBuffCategory, RenderingPrepBuffCategory,
RenderingWaitSubtasksCategory, RenderingWaitSubtasksCategory,
RenderingCategory, RenderingCategory,
// hackish but helps prevent errors when adding new categories
NUM_BUCKETS,
} }
// change this whenever buckets are added/rm'd
static NUM_BUCKETS: uint = 12;
pub type ProfilerChan = SharedChan<(ProfilerCategory, uint)>; impl ProfilerCategory {
pub type ProfilerPort = Port<(ProfilerCategory, uint)>;
// convenience function to not have to cast every time
pub fn num_buckets() -> uint {
NUM_BUCKETS as uint
}
// enumeration of all ProfilerCategory types
// FIXME(tkuehn): this is ugly and error-prone,
// but currently we lack better alternatives without an enum enumeration
priv fn empty_buckets() -> ~[(ProfilerCategory, ~[f64])] {
let mut vec = ~[];
vec.push((CompositingCategory, ~[]));
vec.push((LayoutQueryCategory, ~[]));
vec.push((LayoutPerformCategory, ~[]));
vec.push((LayoutAuxInitCategory, ~[]));
vec.push((LayoutSelectorMatchCategory, ~[]));
vec.push((LayoutTreeBuilderCategory, ~[]));
vec.push((LayoutMainCategory, ~[]));
vec.push((LayoutShapingCategory, ~[]));
vec.push((LayoutDispListBuildCategory, ~[]));
vec.push((GfxRegenAvailableFontsCategory, ~[]));
vec.push((RenderingPrepBuffCategory, ~[]));
vec.push((RenderingWaitSubtasksCategory, ~[]));
vec.push((RenderingCategory, ~[]));
ProfilerCategory::check_order(vec);
vec
}
priv fn check_order(vec: &[(ProfilerCategory, ~[f64])]) {
for vec.each |&(category, _)| {
if category != vec[category as uint].first() {
fail!("Enum category does not match bucket index. This is a bug.");
}
}
}
// some categories are subcategories of LayoutPerformCategory
// and should be printed to indicate this
pub fn format(self) -> ~str {
let padding = match self {
LayoutAuxInitCategory | LayoutSelectorMatchCategory | LayoutTreeBuilderCategory |
LayoutMainCategory | LayoutDispListBuildCategory | LayoutShapingCategory=> " - ",
_ => ""
};
fmt!("%s%?", padding, self)
}
}
// FIXME(#5873) this should be initialized by a NUM_BUCKETS cast,
static BUCKETS: uint = 13;
pub enum ProfilerMsg {
// Normal message used for reporting time
TimeMsg(ProfilerCategory, f64),
// Message used to force print the profiling metrics
ForcePrintMsg,
}
pub type ProfilerChan = SharedChan<ProfilerMsg>;
pub type ProfilerPort = Port<ProfilerMsg>;
pub struct ProfilerTask { pub struct ProfilerTask {
chan: ProfilerChan, chan: ProfilerChan,
} }
impl ProfilerTask { impl ProfilerTask {
pub fn new(prof_port: ProfilerPort, pub fn new(profiler_port: ProfilerPort,
prof_chan: ProfilerChan) profiler_chan: ProfilerChan,
period: Option<f64>)
-> ProfilerTask { -> ProfilerTask {
let prof_port = Cell(prof_port); let profiler_port = Cell(profiler_port);
do spawn { do spawn {
let mut profiler_context = ProfilerContext::new(prof_port.take()); let mut profiler_context = ProfilerContext::new(profiler_port.take(), period);
profiler_context.start(); profiler_context.start();
} }
ProfilerTask { ProfilerTask {
chan: prof_chan chan: profiler_chan
} }
} }
} }
pub struct ProfilerContext { pub struct ProfilerContext {
port: ProfilerPort, port: ProfilerPort,
buckets: [~[uint], ..NUM_BUCKETS], buckets: ~[(ProfilerCategory, ~[f64])],
verbose: Option<~str>, verbose: bool,
mut last_print: u64, period: f64,
mut last_print: f64,
} }
impl ProfilerContext { impl ProfilerContext {
pub fn new(port: ProfilerPort) -> ProfilerContext { pub fn new(port: ProfilerPort, period: Option<f64>) -> ProfilerContext {
let (verbose, period) = match period {
Some(period) => (true, period),
None => (false, 0f64)
};
ProfilerContext { ProfilerContext {
port: port, port: port,
buckets: [~[], ..NUM_BUCKETS], buckets: ProfilerCategory::empty_buckets(),
verbose: getenv("SERVO_PROFILER"), verbose: verbose,
last_print: 0, period: period,
last_print: 0f64,
} }
} }
@ -72,62 +139,76 @@ impl ProfilerContext {
} }
} }
priv fn handle_msg(&mut self, msg: (ProfilerCategory, uint)) { priv fn handle_msg(&mut self, msg: ProfilerMsg) {
let (prof_msg, t) = msg; match msg {
self.buckets[prof_msg as uint].push(t); TimeMsg(category, t) => {
if self.verbose.is_some() { // FIXME(#3874): this should be a let (cat, ref mut bucket) = ...,
let cur_time = precise_time_ns() / 1000000000u64; // not a match
if cur_time - self.last_print > 5 { match self.buckets[category as uint] {
self.last_print = cur_time; (_, ref mut data) => {
let mut i = 0; data.push(t);
for self.buckets.each |bucket| { tim_sort(*data);
let prof_msg = match i { }
// must be in same order as ProfilerCategory
0 => CompositingCategory,
1 => LayoutPerformCategory,
2 => LayoutQueryCategory,
3 => LayoutAuxInitCategory,
4 => LayoutSelectorMatchCategory,
5 => LayoutTreeBuilderCategory,
6 => LayoutMainCategory,
7 => LayoutDispListBuildCategory,
8 => GfxRegenAvailableFontsCategory,
9 => RenderingPrepBuffCategory,
10 => RenderingWaitSubtasksCategory,
11 => RenderingCategory,
_ => fail!()
};
io::println(fmt!("%?: %f", prof_msg,
(bucket.foldl(0 as uint, |a, b| a + *b) as float) /
(bucket.len() as float)));
i += 1;
} }
io::println("");
if self.verbose {
let cur_time = precise_time_ns() as f64 / 1000000000f64;
if cur_time - self.last_print > self.period {
self.last_print = cur_time;
self.print_buckets();
}
}
}
ForcePrintMsg => self.print_buckets(),
};
}
priv fn print_buckets(&mut self) {
println(fmt!("%31s %15s %15s %15s %15s %15s",
"_category (ms)_", "_mean (ms)_", "_median (ms)_",
"_min (ms)_", "_max (ms)_", "_bucket size_"));
for self.buckets.each |bucket| {
let &(category, data) = bucket;
let data_len = data.len();
if data_len > 0 {
let (mean, median, min, max) =
(data.foldl(0f64, |a, b| a + *b) / (data_len as f64),
data[data_len / 2],
data.min(),
data.max());
println(fmt!("%-30s: %15.4? %15.4? %15.4? %15.4? %15u",
category.format(), mean, median, min, max, data_len));
} }
} }
println("");
} }
} }
pub fn profile<T>(cat: ProfilerCategory,
prof_chan: ProfilerChan, pub fn profile<T>(category: ProfilerCategory,
profiler_chan: ProfilerChan,
callback: &fn() -> T) callback: &fn() -> T)
-> T { -> T {
let start_time = precise_time_ns(); let start_time = precise_time_ns();
let val = callback(); let val = callback();
let end_time = precise_time_ns(); let end_time = precise_time_ns();
let ms = ((end_time - start_time) / 1000000u64) as uint; let ms = ((end_time - start_time) as f64 / 1000000f64);
prof_chan.send((cat, ms)); profiler_chan.send(TimeMsg(category, ms));
return val; return val;
} }
pub fn profiler_force_print(profiler_chan: ProfilerChan) {
profiler_chan.send(ForcePrintMsg);
}
pub fn time<T>(msg: &str, callback: &fn() -> T) -> T{ pub fn time<T>(msg: &str, callback: &fn() -> T) -> T{
let start_time = precise_time_ns(); let start_time = precise_time_ns();
let val = callback(); let val = callback();
let end_time = precise_time_ns(); let end_time = precise_time_ns();
let ms = ((end_time - start_time) / 1000000u64) as uint; let ms = ((end_time - start_time) as f64 / 1000000f64);
if ms >= 5 { if ms >= 5f64 {
debug!("%s took %u ms", msg, ms); debug!("%s took %? ms", msg, ms);
} }
return val; return val;
} }

@ -1 +1 @@
Subproject commit 865f539114383a021822583801e8362faf916699 Subproject commit 09d2db847c11bcab7f1832d5daf5947a7c1384ee

@ -1 +1 @@
Subproject commit 453bf81e021008f5eba29b135f07f4529e6c8b2e Subproject commit 7db24a19d25fbedca2898381ae0b13b723c14135

@ -1 +1 @@
Subproject commit 325cd5197ed953f5c7c9317111b20ec1599eaffe Subproject commit 8fc7400ed332c3d9edf358c2a18dd047ebde09a6

View file

@ -12,3 +12,7 @@ for (var i = 0; i < count; i++) {
} }
var stop = new Date(); var stop = new Date();
window.alert((stop - start) / count * 1e6 + " ns/layout"); window.alert((stop - start) / count * 1e6 + " ns/layout");
<<<<<<< HEAD
=======
window.close();
>>>>>>> 0560988... Add link following and refactor the profiler.