This commit is contained in:
eschweic 2013-06-20 10:06:25 -07:00
commit 0dad4d138c
36 changed files with 506 additions and 390 deletions

View file

@ -49,6 +49,7 @@ endif
export CFG_RUSTC export CFG_RUSTC
export CFG_RUSTC_FLAGS export CFG_RUSTC_FLAGS
export CFG_LOCAL_RUSTC export CFG_LOCAL_RUSTC
export CFG_ENABLE_DEBUG
export RUSTC=$(CFG_RUSTC) export RUSTC=$(CFG_RUSTC)
export RUSTFLAGS=$(CFG_RUSTC_FLAGS) export RUSTFLAGS=$(CFG_RUSTC_FLAGS)
@ -185,28 +186,35 @@ DONE_net = $(B)src/components/net/libnet.dummy
DEPS_net = $(CRATE_net) $(SRC_net) $(DONE_SUBMODULES) $(DONE_util) DEPS_net = $(CRATE_net) $(SRC_net) $(DONE_SUBMODULES) $(DONE_util)
RFLAGS_gfx = $(strip $(CFG_RUSTC_FLAGS)) $(addprefix -L $(B)src/,$(DEPS_SUBMODULES)) -L $(B)src/components/util -L $(B)src/components/net RFLAGS_msg = $(strip $(CFG_RUSTC_FLAGS)) $(addprefix -L $(B)src/,$(DEPS_SUBMODULES))
SRC_msg = $(call rwildcard,$(S)src/components/msg/,*.rs)
CRATE_msg = $(S)src/components/msg/msg.rc
DONE_msg = $(B)src/components/msg/libmsg.dummy
DEPS_msg = $(CRATE_msg) $(SRC_msg) $(DONE_SUBMODULES)
RFLAGS_gfx = $(strip $(CFG_RUSTC_FLAGS)) $(addprefix -L $(B)src/,$(DEPS_SUBMODULES)) -L $(B)src/components/util -L $(B)src/components/net -L $(B)src/components/msg
SRC_gfx = $(call rwildcard,$(S)src/components/gfx/,*.rs) SRC_gfx = $(call rwildcard,$(S)src/components/gfx/,*.rs)
CRATE_gfx = $(S)src/components/gfx/gfx.rc CRATE_gfx = $(S)src/components/gfx/gfx.rc
DONE_gfx = $(B)src/components/gfx/libgfx.dummy DONE_gfx = $(B)src/components/gfx/libgfx.dummy
DEPS_gfx = $(CRATE_gfx) $(SRC_gfx) $(DONE_SUBMODULES) $(DONE_util) $(DONE_net) DEPS_gfx = $(CRATE_gfx) $(SRC_gfx) $(DONE_SUBMODULES) $(DONE_util) $(DONE_net) $(DONE_msg)
RFLAGS_script = $(strip $(CFG_RUSTC_FLAGS)) $(addprefix -L $(B)src/,$(DEPS_SUBMODULES)) -L $(B)src/components/util -L $(B)src/components/net -L $(B)src/components/gfx RFLAGS_script = $(strip $(CFG_RUSTC_FLAGS)) $(addprefix -L $(B)src/,$(DEPS_SUBMODULES)) -L $(B)src/components/util -L $(B)src/components/net -L $(B)src/components/gfx -L $(B)src/components/msg
WEBIDL_script = $(call rwildcard,$(S)src/components/script/,*.webidl) WEBIDL_script = $(call rwildcard,$(S)src/components/script/,*.webidl)
AUTOGEN_SRC_script = $(patsubst %.webidl, %Binding.rs, $(WEBIDL_script)) AUTOGEN_SRC_script = $(patsubst %.webidl, %Binding.rs, $(WEBIDL_script))
SRC_script = $(call rwildcard,$(S)src/components/script/,*.rs) $(AUTOGEN_SRC_script) SRC_script = $(call rwildcard,$(S)src/components/script/,*.rs) $(AUTOGEN_SRC_script)
CRATE_script = $(S)src/components/script/script.rc CRATE_script = $(S)src/components/script/script.rc
DONE_script = $(B)src/components/script/libscript.dummy DONE_script = $(B)src/components/script/libscript.dummy
DEPS_script = $(CRATE_script) $(SRC_script) $(DONE_SUBMODULES) $(DONE_util) $(DONE_net) $(DONE_gfx) DEPS_script = $(CRATE_script) $(SRC_script) $(DONE_SUBMODULES) $(DONE_util) $(DONE_net) $(DONE_gfx) $(DONE_msg)
RFLAGS_servo = $(strip $(CFG_RUSTC_FLAGS)) $(addprefix -L $(B)src/,$(DEPS_SUBMODULES)) -L $(B)src/components/gfx -L $(B)src/components/util -L $(B)src/components/net -L $(B)src/components/script RFLAGS_servo = $(strip $(CFG_RUSTC_FLAGS)) $(addprefix -L $(B)src/,$(DEPS_SUBMODULES)) -L $(B)src/components/gfx -L $(B)src/components/util -L $(B)src/components/net -L $(B)src/components/script -L $(B)src/components/msg
SRC_servo = $(call rwildcard,$(S)src/components/main/,*.rs) SRC_servo = $(call rwildcard,$(S)src/components/main/,*.rs)
CRATE_servo = $(S)src/components/main/servo.rc CRATE_servo = $(S)src/components/main/servo.rc
DEPS_servo = $(CRATE_servo) $(SRC_servo) $(DONE_SUBMODULES) $(DONE_util) $(DONE_gfx) $(DONE_script) $(DONE_net) DEPS_servo = $(CRATE_servo) $(SRC_servo) $(DONE_SUBMODULES) $(DONE_util) $(DONE_gfx) $(DONE_script) $(DONE_net) $(DONE_msg)
# rules that depend on having correct meta-target vars (DEPS_CLEAN, DEPS_servo, etc) # rules that depend on having correct meta-target vars (DEPS_CLEAN, DEPS_servo, etc)
include $(S)mk/check.mk include $(S)mk/check.mk
@ -224,6 +232,9 @@ $(DONE_util): $(DEPS_util)
$(DONE_net): $(DEPS_net) $(DONE_net): $(DEPS_net)
$(RUSTC) $(RFLAGS_net) -o $@ $< && touch $@ $(RUSTC) $(RFLAGS_net) -o $@ $< && touch $@
$(DONE_msg): $(DEPS_msg)
$(RUSTC) $(RFLAGS_msg) -o $@ $< && touch $@
$(DONE_gfx): $(DEPS_gfx) $(DONE_gfx): $(DEPS_gfx)
$(RUSTC) $(RFLAGS_gfx) -o $@ $< && touch $@ $(RUSTC) $(RFLAGS_gfx) -o $@ $< && touch $@

1
configure vendored
View file

@ -452,6 +452,7 @@ do
done done
make_dir ${CFG_BUILD_DIR}src/components/util make_dir ${CFG_BUILD_DIR}src/components/util
make_dir ${CFG_BUILD_DIR}src/components/msg
make_dir ${CFG_BUILD_DIR}src/components/net make_dir ${CFG_BUILD_DIR}src/components/net
make_dir ${CFG_BUILD_DIR}src/components/gfx make_dir ${CFG_BUILD_DIR}src/components/gfx
make_dir ${CFG_BUILD_DIR}src/components/script make_dir ${CFG_BUILD_DIR}src/components/script

View file

@ -28,6 +28,9 @@ clean-fast: $(DEPS_CLEAN_TARGETS_FAST) clean-servo
clean-util: clean-util:
cd $(B)/src/components/util/ && rm -rf libutil*.dylib $(DONE_util) cd $(B)/src/components/util/ && rm -rf libutil*.dylib $(DONE_util)
clean-msg:
cd $(B)/src/components/msg/ && rm -rf libmsg*.dylib $(DONE_msg)
clean-net: clean-net:
cd $(B)/src/components/net/ && rm -rf libnet*.dylib $(DONE_net) cd $(B)/src/components/net/ && rm -rf libnet*.dylib $(DONE_net)

View file

@ -24,7 +24,6 @@ use geom::{Point2D, Rect, Size2D};
use servo_net::image::base::Image; use servo_net::image::base::Image;
use servo_util::range::Range; use servo_util::range::Range;
use std::arc::ARC; use std::arc::ARC;
use std::arc;
/// A list of rendering operations to be performed. /// A list of rendering operations to be performed.
pub struct DisplayList<E> { pub struct DisplayList<E> {

View file

@ -2,11 +2,12 @@
* 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 font::{Font, FontDescriptor, FontGroup, FontStyle, SelectorPlatformIdentifier}; use font::{Font, FontDescriptor, FontGroup, FontHandleMethods, FontStyle,
SelectorPlatformIdentifier};
use font::{SpecifiedFontStyle, UsedFontStyle}; use font::{SpecifiedFontStyle, UsedFontStyle};
use font_list::FontList; use font_list::FontList;
use servo_util::cache::Cache; use servo_util::cache::Cache;
use servo_util::cache::MonoCache; use servo_util::cache::LRUCache;
use servo_util::time::ProfilerChan; use servo_util::time::ProfilerChan;
use platform::font::FontHandle; use platform::font::FontHandle;
@ -35,8 +36,9 @@ pub trait FontContextHandleMethods {
#[allow(non_implicitly_copyable_typarams)] #[allow(non_implicitly_copyable_typarams)]
pub struct FontContext { pub struct FontContext {
instance_cache: MonoCache<FontDescriptor, @mut Font>, instance_cache: LRUCache<FontDescriptor, @mut Font>,
font_list: Option<FontList>, // only needed by layout font_list: Option<FontList>, // only needed by layout
group_cache: LRUCache<SpecifiedFontStyle, @FontGroup>,
handle: FontContextHandle, handle: FontContextHandle,
backend: BackendType, backend: BackendType,
generic_fonts: HashMap<~str,~str>, generic_fonts: HashMap<~str,~str>,
@ -63,10 +65,9 @@ pub impl<'self> FontContext {
generic_fonts.insert(~"monospace", ~"Menlo"); generic_fonts.insert(~"monospace", ~"Menlo");
FontContext { FontContext {
// TODO(Rust #3902): remove extraneous type parameters once they are inferred correctly. instance_cache: LRUCache::new(10),
instance_cache:
Cache::new::<FontDescriptor,@mut Font,MonoCache<FontDescriptor,@mut Font>>(10),
font_list: font_list, font_list: font_list,
group_cache: LRUCache::new(10),
handle: handle, handle: handle,
backend: backend, backend: backend,
generic_fonts: generic_fonts, generic_fonts: generic_fonts,
@ -78,15 +79,29 @@ pub impl<'self> FontContext {
self.font_list.get_ref() self.font_list.get_ref()
} }
fn get_resolved_font_for_style(@mut self, style: &SpecifiedFontStyle) -> @FontGroup { fn get_resolved_font_for_style(&mut self, style: &SpecifiedFontStyle) -> @FontGroup {
// TODO(Issue #178, E): implement a cache of FontGroup instances. match self.group_cache.find(style) {
self.create_font_group(style) Some(fg) => {
debug!("font group cache hit");
fg
},
None => {
debug!("font group cache miss");
let fg = self.create_font_group(style);
self.group_cache.insert(style, fg);
fg
}
}
} }
fn get_font_by_descriptor(&mut self, desc: &FontDescriptor) -> Result<@mut Font, ()> { fn get_font_by_descriptor(&mut self, desc: &FontDescriptor) -> Result<@mut Font, ()> {
match self.instance_cache.find(desc) { match self.instance_cache.find(desc) {
Some(f) => Ok(f), Some(f) => {
debug!("font cache hit");
Ok(f)
},
None => { None => {
debug!("font cache miss");
let result = self.create_font_instance(desc); let result = self.create_font_instance(desc);
match result { match result {
Ok(font) => { Ok(font) => {
@ -108,27 +123,34 @@ pub impl<'self> FontContext {
} }
} }
// TODO:(Issue #196): cache font groups on the font context. priv fn create_font_group(&mut self, style: &SpecifiedFontStyle) -> @FontGroup {
priv fn create_font_group(@mut self, style: &SpecifiedFontStyle) -> @FontGroup {
let mut fonts = ~[]; let mut fonts = ~[];
debug!("(create font group) --- starting ---"); debug!("(create font group) --- starting ---");
let list = self.get_font_list();
// TODO(Issue #193): make iteration over 'font-family' more robust. // TODO(Issue #193): make iteration over 'font-family' more robust.
for str::each_split_char(style.families, ',') |family| { for str::each_split_char(style.families, ',') |family| {
let family_name = str::trim(family); let family_name = str::trim(family);
let transformed_family_name = self.transform_family(family_name); let transformed_family_name = self.transform_family(family_name);
debug!("(create font group) transformed family is `%s`", transformed_family_name); debug!("(create font group) transformed family is `%s`", transformed_family_name);
let result = list.find_font_in_family(transformed_family_name, style); let result = match self.font_list {
Some(ref fl) => {
fl.find_font_in_family(transformed_family_name, style)
},
None => None,
};
let mut found = false; let mut found = false;
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.
let instance = Font::new_from_existing_handle(self, &font_entry.handle, style, self.backend, let font_id =
self.profiler_chan.clone()); SelectorPlatformIdentifier(font_entry.handle.face_identifier());
let font_desc = FontDescriptor::new(copy *style, font_id);
let instance = self.get_font_by_descriptor(&font_desc);
do result::iter(&instance) |font: &@mut Font| { fonts.push(*font); } do result::iter(&instance) |font: &@mut Font| { fonts.push(*font); }
}; };
@ -140,13 +162,20 @@ pub impl<'self> FontContext {
let last_resort = FontList::get_last_resort_font_families(); let last_resort = FontList::get_last_resort_font_families();
for last_resort.each |family| { for last_resort.each |family| {
let result = list.find_font_in_family(*family,style); let result = match self.font_list {
Some(ref fl) => {
fl.find_font_in_family(*family, style)
},
None => None,
};
for result.each |font_entry| { for result.each |font_entry| {
let instance = Font::new_from_existing_handle(self, let font_id =
&font_entry.handle, SelectorPlatformIdentifier(font_entry.handle.face_identifier());
style, let font_desc = FontDescriptor::new(copy *style, font_id);
self.backend,
self.profiler_chan.clone()); let instance = self.get_font_by_descriptor(&font_desc);
do result::iter(&instance) |font: &@mut Font| { do result::iter(&instance) |font: &@mut Font| {
fonts.push(*font); fonts.push(*font);
} }

View file

@ -112,9 +112,8 @@ pub impl Au {
Rect(Point2D(z, z), Size2D(z, z)) Rect(Point2D(z, z), Size2D(z, z))
} }
// assumes 72 points per inch, and 96 px per inch
pub fn from_pt(f: float) -> Au { pub fn from_pt(f: float) -> Au {
from_px((f / 72f * 96f) as int) from_px(pt_to_px(f) as int)
} }
pub fn from_frac_px(f: float) -> Au { pub fn from_frac_px(f: float) -> Au {
@ -125,6 +124,16 @@ pub impl Au {
pub fn max(x: Au, y: Au) -> Au { if *x > *y { x } else { y } } pub fn max(x: Au, y: Au) -> Au { if *x > *y { x } else { y } }
} }
// assumes 72 points per inch, and 96 px per inch
pub fn pt_to_px(f: float) -> float {
f / 72f * 96f
}
// assumes 72 points per inch, and 96 px per inch
pub fn px_to_pt(f: float) -> float {
f / 96f * 72f
}
pub fn zero_rect() -> Rect<Au> { pub fn zero_rect() -> Rect<Au> {
let z = Au(0); let z = Au(0);
Rect(Point2D(z, z), Size2D(z, z)) Rect(Point2D(z, z), Size2D(z, z))

View file

@ -15,6 +15,7 @@ extern mod stb_image;
extern mod std; extern mod std;
extern mod servo_net (name = "net"); extern mod servo_net (name = "net");
extern mod servo_util (name = "util"); extern mod servo_util (name = "util");
extern mod servo_msg (name = "msg");
// Eventually we would like the shaper to be pluggable, as many operating systems have their own // Eventually we would like the shaper to be pluggable, as many operating systems have their own
// shapers. For now, however, this is a hard dependency. // shapers. For now, however, this is a hard dependency.
@ -39,10 +40,8 @@ priv mod render_context;
// Rendering // Rendering
pub mod color; pub mod color;
pub mod compositor;
pub mod display_list; pub mod display_list;
pub mod geometry; pub mod geometry;
pub mod render_layers;
pub mod render_task; pub mod render_task;
pub mod surface; pub mod surface;

View file

@ -219,7 +219,7 @@ impl FontHandleMethods for FontHandle {
x_height: geometry::from_pt(0.0), //FIXME x_height: geometry::from_pt(0.0), //FIXME
em_size: em_size, em_size: em_size,
ascent: ascent, ascent: ascent,
descent: descent, descent: -descent, // linux font's seem to use the opposite sign from mac
max_advance: max_advance max_advance: max_advance
} }
} }

View file

@ -12,7 +12,7 @@ use font::{CSSFontWeight, FontHandleMethods, FontMetrics, FontTableMethods};
use font::{FontTableTag, FontWeight100, FontWeight200, FontWeight300, FontWeight400}; use font::{FontTableTag, FontWeight100, FontWeight200, FontWeight300, FontWeight400};
use font::{FontWeight500, FontWeight600, FontWeight700, FontWeight800, FontWeight900}; use font::{FontWeight500, FontWeight600, FontWeight700, FontWeight800, FontWeight900};
use font::{FractionalPixel, SpecifiedFontStyle}; use font::{FractionalPixel, SpecifiedFontStyle};
use geometry::Au; use geometry::{Au, px_to_pt};
use platform::macos::font_context::FontContextHandle; use platform::macos::font_context::FontContextHandle;
use text::glyph::GlyphIndex; use text::glyph::GlyphIndex;
@ -157,6 +157,9 @@ impl FontHandleMethods for FontHandle {
let bounding_rect: CGRect = self.ctfont.bounding_box(); let bounding_rect: CGRect = self.ctfont.bounding_box();
let ascent = Au::from_pt(self.ctfont.ascent() as float); let ascent = Au::from_pt(self.ctfont.ascent() as float);
let descent = Au::from_pt(self.ctfont.descent() as float); let descent = Au::from_pt(self.ctfont.descent() as float);
let em_size = Au::from_frac_px(self.ctfont.pt_size() as float);
let scale = px_to_pt(self.ctfont.pt_size() as float) / (self.ctfont.ascent() as float + self.ctfont.descent() as float);
let metrics = FontMetrics { let metrics = FontMetrics {
underline_size: Au::from_pt(self.ctfont.underline_thickness() as float), underline_size: Au::from_pt(self.ctfont.underline_thickness() as float),
@ -168,9 +171,9 @@ impl FontHandleMethods for FontHandle {
underline_offset: Au::from_pt(self.ctfont.underline_position() as float), underline_offset: Au::from_pt(self.ctfont.underline_position() as float),
leading: Au::from_pt(self.ctfont.leading() as float), leading: Au::from_pt(self.ctfont.leading() as float),
x_height: Au::from_pt(self.ctfont.x_height() as float), x_height: Au::from_pt(self.ctfont.x_height() as float),
em_size: ascent + descent, em_size: em_size,
ascent: ascent, ascent: ascent.scale_by(scale),
descent: descent, descent: descent.scale_by(scale),
max_advance: Au::from_pt(bounding_rect.size.width as float) max_advance: Au::from_pt(bounding_rect.size.width as float)
}; };

View file

@ -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 compositor::LayerBuffer; use servo_msg::compositor::LayerBuffer;
use font_context::FontContext; use font_context::FontContext;
use geometry::Au; use geometry::Au;
use opts::Opts; use opts::Opts;

View file

@ -1,113 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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 compositor::{LayerBuffer, LayerBufferSet};
use display_list::DisplayList;
use opts::Opts;
use servo_util::time;
use servo_util::time::ProfilerChan;
use azure::azure_hl::{B8G8R8A8, DrawTarget};
use azure::azure::{AzGLContext};
use core::comm::Chan;
use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D;
/// The type representing the lack of extra display list data. This is used when sending display
/// list data off to be rendered.
pub type Nothing = ();
pub struct RenderLayer {
display_list: DisplayList<Nothing>,
size: Size2D<uint>
}
type RenderFn<'self> = &'self fn(layer: *RenderLayer,
buffer: LayerBuffer,
return_buffer: Chan<LayerBuffer>);
/// Given a layer and a buffer, either reuses the buffer (if it's of the right size and format)
/// or creates a new buffer (if it's not of the appropriate size and format) and invokes the
/// given callback with the render layer and the buffer. Returns the resulting layer buffer (which
/// might be the old layer buffer if it had the appropriate size and format).
pub fn render_layers(layer_ref: *RenderLayer,
opts: &Opts,
prof_chan: ProfilerChan,
share_gl_context: AzGLContext,
f: RenderFn)
-> LayerBufferSet {
let tile_size = opts.tile_size;
let scale = opts.zoom;
// FIXME: Try not to create a new array here.
let mut new_buffer_ports = ~[];
// Divide up the layer into tiles.
do time::profile(time::RenderingPrepBuffCategory, prof_chan.clone()) {
let layer: &RenderLayer = unsafe { cast::transmute(layer_ref) };
let mut y = 0;
while y < layer.size.height * scale {
let mut x = 0;
while x < layer.size.width * scale {
// Figure out the dimension of this tile.
let right = uint::min(x + tile_size, layer.size.width * scale);
let bottom = uint::min(y + tile_size, layer.size.height * scale);
let width = right - x;
let height = bottom - y;
let tile_rect = Rect(Point2D(x / scale, y / scale), Size2D(width, height)); //change this
let screen_rect = Rect(Point2D(x, y), Size2D(width, height)); //change this
let buffer;
// FIXME: Try harder to search for a matching tile.
// FIXME: Don't use shift; it's bad for perf. Maybe reverse and pop.
/*if buffers.len() != 0 && buffers[0].rect == tile_rect {
debug!("reusing tile, (%u, %u)", x, y);
buffer = buffers.shift();
} else {*/
// Create a new buffer.
debug!("creating tile, (%u, %u)", x, y);
// FIXME: This may not be always true.
let stride = width * 4;
buffer = LayerBuffer {
draw_target: DrawTarget::new_with_fbo(opts.render_backend,
share_gl_context,
Size2D(width as i32, height as i32),
B8G8R8A8),
rect: tile_rect,
screen_pos: screen_rect,
stride: stride as uint
};
//}
// Create a port and channel pair to receive the new buffer.
let (new_buffer_port, new_buffer_chan) = comm::stream();
// Send the buffer to the child.
f(layer_ref, buffer, new_buffer_chan);
// Enqueue the port.
new_buffer_ports.push(new_buffer_port);
x += tile_size;
}
y += tile_size;
}
}
let mut new_buffers = ~[];
do time::profile(time::RenderingWaitSubtasksCategory, prof_chan.clone()) {
for new_buffer_ports.each |new_buffer_port| {
new_buffers.push(new_buffer_port.recv());
}
}
LayerBufferSet {
buffers: new_buffers,
}
}

View file

@ -5,23 +5,29 @@
// The task that handles all rendering/painting. // The task that handles all rendering/painting.
use azure::{AzFloat, AzGLContext}; use azure::{AzFloat, AzGLContext};
use compositor::{RenderListener, IdleRenderState, RenderingRenderState}; use azure::azure_hl::{B8G8R8A8, DrawTarget};
use display_list::DisplayList;
use servo_msg::compositor::{RenderListener, IdleRenderState, RenderingRenderState, LayerBuffer};
use servo_msg::compositor::LayerBufferSet;
use font_context::FontContext; use font_context::FontContext;
use geom::matrix2d::Matrix2D; use geom::matrix2d::Matrix2D;
use geom::point::Point2D;
use geom::size::Size2D;
use geom::rect::Rect;
use opts::Opts; use opts::Opts;
use render_context::RenderContext; use render_context::RenderContext;
use render_layers::{RenderLayer, render_layers};
use core::cell::Cell; use core::cell::Cell;
use core::comm::{Chan, Port, SharedChan}; use core::comm::{Chan, Port, SharedChan};
use core::task::SingleThreaded;
use std::task_pool::TaskPool;
use servo_net::util::spawn_listener;
use servo_util::time::{ProfilerChan, profile}; use servo_util::time::{ProfilerChan, profile};
use servo_util::time; use servo_util::time;
pub struct RenderLayer {
display_list: DisplayList<()>,
size: Size2D<uint>
}
pub enum Msg<C> { pub enum Msg<C> {
AttachCompositorMsg(C), AttachCompositorMsg(C),
RenderMsg(RenderLayer), RenderMsg(RenderLayer),
@ -62,38 +68,17 @@ pub fn create_render_task<C: RenderListener + Owned>(port: Port<Msg<C>>,
do spawn { do spawn {
let compositor = compositor_cell.take(); let compositor = compositor_cell.take();
let share_gl_context = compositor.get_gl_context(); let share_gl_context = compositor.get_gl_context();
// FIXME: Annoying three-cell dance here. We need one-shot closures.
let opts = opts_cell.with_ref(|o| copy *o); let opts = opts_cell.with_ref(|o| copy *o);
let n_threads = opts.n_render_threads;
let new_opts_cell = Cell(opts);
let profiler_chan = profiler_chan.clone(); let profiler_chan = profiler_chan.clone();
let profiler_chan_copy = profiler_chan.clone(); let profiler_chan_copy = profiler_chan.clone();
let thread_pool = do TaskPool::new(n_threads, Some(SingleThreaded)) {
let opts_cell = Cell(new_opts_cell.with_ref(|o| copy *o));
let profiler_chan = Cell(profiler_chan.clone());
let f: ~fn(uint) -> ThreadRenderContext = |thread_index| {
let opts = opts_cell.with_ref(|opts| copy *opts);
ThreadRenderContext {
thread_index: thread_index,
font_ctx: @mut FontContext::new(opts.render_backend,
false,
profiler_chan.take()),
opts: opts,
}
};
f
};
// FIXME: rust/#5967 // FIXME: rust/#5967
let mut renderer = Renderer { let mut renderer = Renderer {
port: port.take(), port: port.take(),
compositor: compositor, compositor: compositor,
thread_pool: thread_pool, font_ctx: @mut FontContext::new(opts.render_backend,
false,
profiler_chan),
opts: opts_cell.take(), opts: opts_cell.take(),
profiler_chan: profiler_chan_copy, profiler_chan: profiler_chan_copy,
share_gl_context: share_gl_context, share_gl_context: share_gl_context,
@ -103,17 +88,10 @@ pub fn create_render_task<C: RenderListener + Owned>(port: Port<Msg<C>>,
} }
} }
/// Data that needs to be kept around for each render thread.
priv struct ThreadRenderContext {
thread_index: uint,
font_ctx: @mut FontContext,
opts: Opts,
}
priv struct Renderer<C> { priv struct Renderer<C> {
port: Port<Msg<C>>, port: Port<Msg<C>>,
compositor: C, compositor: C,
thread_pool: TaskPool<ThreadRenderContext>, font_ctx: @mut FontContext,
opts: Opts, opts: Opts,
/// A channel to the profiler. /// A channel to the profiler.
@ -142,48 +120,74 @@ impl<C: RenderListener + Owned> Renderer<C> {
debug!("renderer: rendering"); debug!("renderer: rendering");
self.compositor.set_render_state(RenderingRenderState); self.compositor.set_render_state(RenderingRenderState);
do profile(time::RenderingCategory, self.profiler_chan.clone()) { do profile(time::RenderingCategory, self.profiler_chan.clone()) {
let layer_buffer_set = do render_layers(&render_layer, let tile_size = self.opts.tile_size;
&self.opts, let scale = self.opts.zoom;
self.profiler_chan.clone(),
self.share_gl_context) |render_layer_ref, // FIXME: Try not to create a new array here.
layer_buffer, let mut new_buffers = ~[];
buffer_chan| {
let layer_buffer_cell = Cell(layer_buffer); // Divide up the layer into tiles.
do self.thread_pool.execute |thread_render_context| { do time::profile(time::RenderingPrepBuffCategory, self.profiler_chan.clone()) {
do layer_buffer_cell.with_ref |layer_buffer| { let mut y = 0;
// Build the render context. while y < render_layer.size.height * scale {
let ctx = RenderContext { let mut x = 0;
canvas: layer_buffer, while x < render_layer.size.width * scale {
font_ctx: thread_render_context.font_ctx, // Figure out the dimension of this tile.
opts: &thread_render_context.opts let right = uint::min(x + tile_size, render_layer.size.width * scale);
let bottom = uint::min(y + tile_size, render_layer.size.height * scale);
let width = right - x;
let height = bottom - y;
let tile_rect = Rect(Point2D(x / scale, y / scale), Size2D(width, height)); //change this
let screen_rect = Rect(Point2D(x, y), Size2D(width, height)); //change this
let buffer = LayerBuffer {
draw_target: DrawTarget::new_with_fbo(self.opts.render_backend,
self.share_gl_context,
Size2D(width as i32, height as i32),
B8G8R8A8),
rect: tile_rect,
screen_pos: screen_rect,
stride: (width * 4) as uint
}; };
// Apply the translation to render the tile we want. {
let matrix: Matrix2D<AzFloat> = Matrix2D::identity(); // Build the render context.
let scale = thread_render_context.opts.zoom as f32; let ctx = RenderContext {
canvas: &buffer,
font_ctx: self.font_ctx,
opts: &self.opts
};
let matrix = matrix.scale(scale as AzFloat, scale as AzFloat); // Apply the translation to render the tile we want.
let matrix = matrix.translate(-(layer_buffer.rect.origin.x as f32) as AzFloat, let matrix: Matrix2D<AzFloat> = Matrix2D::identity();
-(layer_buffer.rect.origin.y as f32) as AzFloat); let matrix = matrix.scale(scale as AzFloat, scale as AzFloat);
let matrix = matrix.translate(-(buffer.rect.origin.x as f32) as AzFloat,
-(buffer.rect.origin.y as f32) as AzFloat);
layer_buffer.draw_target.set_transform(&matrix); ctx.canvas.draw_target.set_transform(&matrix);
// Clear the buffer. // Clear the buffer.
ctx.clear(); ctx.clear();
// Draw the display list. // Draw the display list.
let render_layer: &RenderLayer = unsafe { do profile(time::RenderingDrawingCategory, self.profiler_chan.clone()) {
cast::transmute(render_layer_ref) render_layer.display_list.draw_into_context(&ctx);
}; ctx.canvas.draw_target.flush();
}
render_layer.display_list.draw_into_context(&ctx); }
ctx.canvas.draw_target.flush();
new_buffers.push(buffer);
x += tile_size;
} }
// Send back the buffer. y += tile_size;
buffer_chan.send(layer_buffer_cell.take());
} }
}
let layer_buffer_set = LayerBufferSet {
buffers: new_buffers,
}; };
debug!("renderer: returning surface"); debug!("renderer: returning surface");

View file

@ -28,15 +28,15 @@ impl Eq for CompressionMode {
// //
// High level TODOs: // High level TODOs:
// //
// * Issue #113: consider incoming text state (preceding spaces, arabic, etc) // * Issue #113: consider incoming text state (arabic, etc)
// and propogate outgoing text state (dual of above) // and propogate outgoing text state (dual of above)
// //
// * Issue #114: record skipped and kept chars for mapping original to new text // * Issue #114: record skipped and kept chars for mapping original to new text
// //
// * Untracked: various edge cases for bidi, CJK, etc. // * Untracked: various edge cases for bidi, CJK, etc.
pub fn transform_text(text: &str, mode: CompressionMode) -> ~str { pub fn transform_text(text: &str, mode: CompressionMode, incoming_whitespace: bool) -> (~str, bool) {
let mut out_str: ~str = ~""; let mut out_str: ~str = ~"";
match mode { let out_whitespace = match mode {
CompressNone | DiscardNewline => { CompressNone | DiscardNewline => {
for str::each_char(text) |ch: char| { for str::each_char(text) |ch: char| {
if is_discardable_char(ch, mode) { if is_discardable_char(ch, mode) {
@ -49,18 +49,14 @@ pub fn transform_text(text: &str, mode: CompressionMode) -> ~str {
str::push_char(&mut out_str, ch); str::push_char(&mut out_str, ch);
} }
} }
text.len() > 0 && is_in_whitespace(text.char_at_reverse(0), mode)
}, },
CompressWhitespace | CompressWhitespaceNewline => { CompressWhitespace | CompressWhitespaceNewline => {
let mut in_whitespace: bool = false; let mut in_whitespace: bool = incoming_whitespace;
for str::each_char(text) |ch: char| { for str::each_char(text) |ch: char| {
// TODO: discard newlines between CJK chars // TODO: discard newlines between CJK chars
let mut next_in_whitespace: bool = match (ch, mode) { let mut next_in_whitespace: bool = is_in_whitespace(ch, mode);
(' ', _) => true,
('\t', _) => true,
('\n', CompressWhitespaceNewline) => true,
(_, _) => false
};
if !next_in_whitespace { if !next_in_whitespace {
if is_always_discardable_char(ch) { if is_always_discardable_char(ch) {
@ -82,10 +78,20 @@ pub fn transform_text(text: &str, mode: CompressionMode) -> ~str {
// save whitespace context for next char // save whitespace context for next char
in_whitespace = next_in_whitespace; in_whitespace = next_in_whitespace;
} /* /for str::each_char */ } /* /for str::each_char */
in_whitespace
} }
} };
return out_str; return (out_str, out_whitespace);
fn is_in_whitespace(ch: char, mode: CompressionMode) -> bool {
match (ch, mode) {
(' ', _) => true,
('\t', _) => true,
('\n', CompressWhitespaceNewline) => true,
(_, _) => false
}
}
fn is_discardable_char(ch: char, mode: CompressionMode) -> bool { fn is_discardable_char(ch: char, mode: CompressionMode) -> bool {
if is_always_discardable_char(ch) { if is_always_discardable_char(ch) {
@ -143,7 +149,8 @@ fn test_transform_compress_none() {
let mode = CompressNone; let mode = CompressNone;
for uint::range(0, test_strs.len()) |i| { for uint::range(0, test_strs.len()) |i| {
assert!(transform_text(test_strs[i], mode) == test_strs[i]); (trimmed_str, _out) = transform_text(test_strs[i], mode, true);
assert!(trimmed_str == test_strs[i])
} }
} }
@ -170,7 +177,8 @@ fn test_transform_discard_newline() {
let mode = DiscardNewline; let mode = DiscardNewline;
for uint::range(0, test_strs.len()) |i| { for uint::range(0, test_strs.len()) |i| {
assert!(transform_text(test_strs[i], mode) == oracle_strs[i]); (trimmed_str, _out) = transform_text(test_strs[i], mode, true);
assert!(trimmed_str == oracle_strs[i])
} }
} }
@ -196,7 +204,8 @@ fn test_transform_compress_whitespace() {
let mode = CompressWhitespace; let mode = CompressWhitespace;
for uint::range(0, test_strs.len()) |i| { for uint::range(0, test_strs.len()) |i| {
assert!(transform_text(test_strs[i], mode) == oracle_strs[i]); (trimmed_str, _out) = transform_text(test_strs[i], mode, true);
assert!(trimmed_str == oracle_strs[i])
} }
} }
@ -210,7 +219,7 @@ fn test_transform_compress_whitespace_newline() {
~"foo bar baz", ~"foo bar baz",
~"foobarbaz\n\n"]; ~"foobarbaz\n\n"];
let oracle_strs : ~[~str] = ~[~" foo bar", let oracle_strs : ~[~str] = ~[~"foo bar",
~"foo bar ", ~"foo bar ",
~"foo bar", ~"foo bar",
~"foo bar", ~"foo bar",
@ -222,6 +231,36 @@ fn test_transform_compress_whitespace_newline() {
let mode = CompressWhitespaceNewline; let mode = CompressWhitespaceNewline;
for uint::range(0, test_strs.len()) |i| { for uint::range(0, test_strs.len()) |i| {
assert!(transform_text(test_strs[i], mode) == oracle_strs[i]); (trimmed_str, _out) = transform_text(test_strs[i], mode, true);
assert!(trimmed_str == oracle_strs[i])
}
}
#[test]
fn test_transform_compress_whitespace_newline() {
let test_strs : ~[~str] = ~[~" foo bar",
~"\nfoo bar",
~"foo bar ",
~"foo\n bar",
~"foo \nbar",
~" foo bar \nbaz",
~"foo bar baz",
~"foobarbaz\n\n"];
let oracle_strs : ~[~str] = ~[~" foo bar",
~" foo bar",
~"foo bar ",
~"foo bar",
~"foo bar",
~" foo bar baz",
~"foo bar baz",
~"foobarbaz "];
assert!(test_strs.len() == oracle_strs.len());
let mode = CompressWhitespaceNewline;
for uint::range(0, test_strs.len()) |i| {
(trimmed_str, _out) = transform_text(test_strs[i], mode, false);
assert!(trimmed_str == oracle_strs[i])
} }
} }

View file

@ -3,14 +3,13 @@
* 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 platform::{Application, Window}; use platform::{Application, Window};
use script::dom::event::{Event, ClickEvent, MouseDownEvent, MouseUpEvent, ResizeEvent};
use script::script_task::{LoadMsg, SendEventMsg}; use script::script_task::{LoadMsg, SendEventMsg};
use script::layout_interface::{LayoutChan, RouteScriptMsg};
use windowing::{ApplicationMethods, WindowMethods, WindowMouseEvent, WindowClickEvent}; use windowing::{ApplicationMethods, WindowMethods, WindowMouseEvent, WindowClickEvent};
use windowing::{WindowMouseDownEvent, WindowMouseUpEvent}; use windowing::{WindowMouseDownEvent, WindowMouseUpEvent};
use servo_msg::compositor::{RenderListener, LayerBufferSet, RenderState};
use script::dom::event::{Event, ClickEvent, MouseDownEvent, MouseUpEvent, ResizeEvent}; use servo_msg::compositor::{ReadyState, ScriptListener};
use script::compositor_interface::{ReadyState, ScriptListener};
use script::script_task::{ScriptChan, SendEventMsg};
use script::layout_interface::{LayoutChan, RouteScriptMsg};
use azure::azure_hl::{DataSourceSurface, DrawTarget, SourceSurfaceMethods, current_gl_context}; use azure::azure_hl::{DataSourceSurface, DrawTarget, SourceSurfaceMethods, current_gl_context};
use azure::azure::AzGLContext; use azure::azure::AzGLContext;
@ -21,7 +20,6 @@ use core::util;
use geom::matrix::identity; use geom::matrix::identity;
use geom::point::Point2D; use geom::point::Point2D;
use geom::size::Size2D; use geom::size::Size2D;
use gfx::compositor::{RenderListener, LayerBufferSet, RenderState};
use layers::layers::{ARGB32Format, ContainerLayer, ContainerLayerKind, Format}; use layers::layers::{ARGB32Format, ContainerLayer, ContainerLayerKind, Format};
use layers::layers::{ImageData, WithDataFn}; use layers::layers::{ImageData, WithDataFn};
use layers::layers::{TextureLayerKind, TextureLayer, TextureManager}; use layers::layers::{TextureLayerKind, TextureLayer, TextureManager};

View file

@ -10,8 +10,8 @@ use core::comm::Port;
use gfx::opts::Opts; use gfx::opts::Opts;
use gfx::render_task::RenderChan; use gfx::render_task::RenderChan;
use gfx::render_task; use gfx::render_task;
use script::compositor_interface::{ScriptListener, ReadyState}; use servo_msg::compositor::{ScriptListener, ReadyState};
use script::engine_interface::{EngineChan, ExitMsg, LoadUrlMsg, Msg}; use servo_msg::engine::{EngineChan, ExitMsg, LoadUrlMsg, Msg};
use script::layout_interface::LayoutChan; use script::layout_interface::LayoutChan;
use script::layout_interface; use script::layout_interface;
use script::script_task::{ExecuteMsg, LoadMsg, ScriptMsg, ScriptContext, ScriptChan}; use script::script_task::{ExecuteMsg, LoadMsg, ScriptMsg, ScriptContext, ScriptChan};

View file

@ -42,19 +42,19 @@ pub trait LayoutAuxMethods {
impl LayoutAuxMethods for AbstractNode<LayoutView> { impl LayoutAuxMethods for AbstractNode<LayoutView> {
// FIXME (Rust #3080): These unsafe blocks are *not* unused! // FIXME (Rust #3080): These unsafe blocks are *not* unused!
pub fn layout_data(self) -> @mut LayoutData { pub fn layout_data(self) -> @mut LayoutData {
unsafe { /*unsafe {*/
self.unsafe_layout_data() self.unsafe_layout_data()
} /*}*/
} }
pub fn has_layout_data(self) -> bool { pub fn has_layout_data(self) -> bool {
unsafe { /*unsafe {*/
self.unsafe_has_layout_data() self.unsafe_has_layout_data()
} /*}*/
} }
pub fn set_layout_data(self, data: @mut LayoutData) { pub fn set_layout_data(self, data: @mut LayoutData) {
unsafe { /*unsafe {*/
self.unsafe_set_layout_data(data) self.unsafe_set_layout_data(data)
} /*}*/
} }
/// If none exists, creates empty layout data for the node (the reader-auxiliary /// If none exists, creates empty layout data for the node (the reader-auxiliary

View file

@ -21,14 +21,14 @@ use gfx::display_list::{DisplayList, ImageDisplayItem, ImageDisplayItemClass};
use gfx::display_list::{SolidColorDisplayItem, SolidColorDisplayItemClass, TextDisplayItem}; use gfx::display_list::{SolidColorDisplayItem, SolidColorDisplayItemClass, TextDisplayItem};
use gfx::display_list::{TextDisplayItemClass}; use gfx::display_list::{TextDisplayItemClass};
use gfx::font::{FontStyle, FontWeight300}; use gfx::font::{FontStyle, FontWeight300};
use gfx::geometry::Au; use gfx::geometry::{Au, pt_to_px};
use gfx::text::text_run::TextRun; use gfx::text::text_run::TextRun;
use newcss::color::rgb; use newcss::color::rgb;
use newcss::complete::CompleteStyle; use newcss::complete::CompleteStyle;
use newcss::units::{Cursive, Em, Fantasy, Monospace, Pt, Px, SansSerif, Serif}; use newcss::units::{Cursive, Em, Fantasy, Monospace, Pt, Px, SansSerif, Serif};
use newcss::values::{CSSFontFamilyFamilyName, CSSFontFamilyGenericFamily}; use newcss::values::{CSSFontFamilyFamilyName, CSSFontFamilyGenericFamily};
use newcss::values::{CSSFontSizeLength, CSSFontStyleItalic, CSSFontStyleNormal}; use newcss::values::{CSSFontSizeLength, CSSFontStyleItalic, CSSFontStyleNormal};
use newcss::values::{CSSFontStyleOblique, CSSTextAlign, CSSTextDecoration}; use newcss::values::{CSSFontStyleOblique, CSSTextAlign, CSSTextDecoration, CSSLineHeight};
use newcss::values::{CSSTextDecorationNone, CSSFloatNone, CSSPositionStatic}; use newcss::values::{CSSTextDecorationNone, CSSFloatNone, CSSPositionStatic};
use newcss::values::{CSSDisplayInlineBlock, CSSDisplayInlineTable}; use newcss::values::{CSSDisplayInlineBlock, CSSDisplayInlineTable};
use script::dom::node::{AbstractNode, LayoutView}; use script::dom::node::{AbstractNode, LayoutView};
@ -559,9 +559,6 @@ pub impl RenderBox {
return; return;
} }
// Add the background to the list, if applicable.
self.paint_background_if_applicable(list, &absolute_box_bounds);
match *self { match *self {
UnscannedTextRenderBoxClass(*) => fail!(~"Shouldn't see unscanned boxes here."), UnscannedTextRenderBoxClass(*) => fail!(~"Shouldn't see unscanned boxes here."),
TextRenderBoxClass(text_box) => { TextRenderBoxClass(text_box) => {
@ -625,26 +622,35 @@ pub impl RenderBox {
() ()
}); });
}, },
GenericRenderBoxClass(_) => { GenericRenderBoxClass(_) => {
// FIXME(pcwalton): This is somewhat of an abuse of the logging system.
debug!("%?", { // Add the background to the list, if applicable.
// Compute the text box bounds and draw a border surrounding them. self.paint_background_if_applicable(list, &absolute_box_bounds);
// FIXME(pcwalton): This is a bit of an abuse of the logging infrastructure. We
// should have a real `SERVO_DEBUG` system.
debug!("%?", {
do list.with_mut_ref |list| { do list.with_mut_ref |list| {
let border_display_item = ~BorderDisplayItem { let border_display_item = ~BorderDisplayItem {
base: BaseDisplayItem { base: BaseDisplayItem {
bounds: absolute_box_bounds, bounds: absolute_box_bounds,
extra: ExtraDisplayListData::new(*self), extra: ExtraDisplayListData::new(*self),
}, },
width: Au::from_px(1), width: Au::from_px(1),
color: rgb(0, 0, 0).to_gfx_color(), color: rgb(0, 0, 200).to_gfx_color(),
}; };
list.append_item(BorderDisplayItemClass(border_display_item)) list.append_item(BorderDisplayItemClass(border_display_item))
} }
()
}); });
}
},
ImageRenderBoxClass(image_box) => { ImageRenderBoxClass(image_box) => {
// Add the background to the list, if applicable.
self.paint_background_if_applicable(list, &absolute_box_bounds);
match image_box.image.get_image() { match image_box.image.get_image() {
Some(image) => { Some(image) => {
debug!("(building display list) building image box"); debug!("(building display list) building image box");
@ -708,6 +714,9 @@ pub impl RenderBox {
fn font_style(&self) -> FontStyle { fn font_style(&self) -> FontStyle {
let my_style = self.nearest_ancestor_element().style(); let my_style = self.nearest_ancestor_element().style();
debug!("(font style) start: %?", self.nearest_ancestor_element().type_id());
self.dump();
// FIXME: Too much allocation here. // FIXME: Too much allocation here.
let font_families = do my_style.font_family().map |family| { let font_families = do my_style.font_family().map |family| {
match *family { match *family {
@ -723,12 +732,13 @@ pub impl RenderBox {
debug!("(font style) font families: `%s`", font_families); debug!("(font style) font families: `%s`", font_families);
let font_size = match my_style.font_size() { let font_size = match my_style.font_size() {
CSSFontSizeLength(Px(length)) | CSSFontSizeLength(Px(length)) => length,
CSSFontSizeLength(Pt(length)) | CSSFontSizeLength(Pt(length)) => pt_to_px(length),
CSSFontSizeLength(Em(length)) => length, // todo: this is based on a hard coded font size, should be the parent element's font size
_ => 16.0 CSSFontSizeLength(Em(length)) => length * 16f,
_ => 16f // px units
}; };
debug!("(font style) font size: `%f`", font_size); debug!("(font style) font size: `%fpx`", font_size);
let (italic, oblique) = match my_style.font_style() { let (italic, oblique) = match my_style.font_style() {
CSSFontStyleNormal => (false, false), CSSFontStyleNormal => (false, false),
@ -751,6 +761,10 @@ pub impl RenderBox {
self.nearest_ancestor_element().style().text_align() self.nearest_ancestor_element().style().text_align()
} }
fn line_height(&self) -> CSSLineHeight {
self.nearest_ancestor_element().style().line_height()
}
/// Returns the text decoration of the computed style of the nearest `Element` node /// Returns the text decoration of the computed style of the nearest `Element` node
fn text_decoration(&self) -> CSSTextDecoration { fn text_decoration(&self) -> CSSTextDecoration {
/// Computes the propagated value of text-decoration, as specified in CSS 2.1 § 16.3.1 /// Computes the propagated value of text-decoration, as specified in CSS 2.1 § 16.3.1

View file

@ -20,6 +20,9 @@ use gfx::text::util::*;
use newcss::values::{CSSTextAlignCenter, CSSTextAlignJustify, CSSTextAlignLeft}; use newcss::values::{CSSTextAlignCenter, CSSTextAlignJustify, CSSTextAlignLeft};
use newcss::values::{CSSTextAlignRight, CSSTextDecoration, CSSTextDecorationUnderline}; use newcss::values::{CSSTextAlignRight, CSSTextDecoration, CSSTextDecorationUnderline};
use script::dom::node::{AbstractNode, LayoutView}; use script::dom::node::{AbstractNode, LayoutView};
use newcss::units::{Em, Px, Pt};
use newcss::values::{CSSLineHeightNormal, CSSLineHeightNumber, CSSLineHeightLength, CSSLineHeightPercentage};
use servo_util::range::Range; use servo_util::range::Range;
use std::deque::Deque; use std::deque::Deque;
@ -191,17 +194,18 @@ impl TextRunScanner {
assert!(inline.boxes.len() > 0); assert!(inline.boxes.len() > 0);
debug!("TextRunScanner: scanning %u boxes for text runs...", inline.boxes.len()); debug!("TextRunScanner: scanning %u boxes for text runs...", inline.boxes.len());
let mut last_whitespace = true;
let mut out_boxes = ~[]; let mut out_boxes = ~[];
for uint::range(0, flow.inline().boxes.len()) |box_i| { for uint::range(0, flow.inline().boxes.len()) |box_i| {
debug!("TextRunScanner: considering box: %?", flow.inline().boxes[box_i].debug_str()); debug!("TextRunScanner: considering box: %?", flow.inline().boxes[box_i].debug_str());
if box_i > 0 && !can_coalesce_text_nodes(flow.inline().boxes, box_i-1, box_i) { if box_i > 0 && !can_coalesce_text_nodes(flow.inline().boxes, box_i-1, box_i) {
self.flush_clump_to_list(ctx, flow, &mut out_boxes); last_whitespace = self.flush_clump_to_list(ctx, flow, last_whitespace, &mut out_boxes);
} }
self.clump.extend_by(1); self.clump.extend_by(1);
} }
// handle remaining clumps // handle remaining clumps
if self.clump.length() > 0 { if self.clump.length() > 0 {
self.flush_clump_to_list(ctx, flow, &mut out_boxes); self.flush_clump_to_list(ctx, flow, last_whitespace, &mut out_boxes);
} }
debug!("TextRunScanner: swapping out boxes."); debug!("TextRunScanner: swapping out boxes.");
@ -238,7 +242,8 @@ impl TextRunScanner {
fn flush_clump_to_list(&mut self, fn flush_clump_to_list(&mut self,
ctx: &mut LayoutContext, ctx: &mut LayoutContext,
flow: FlowContext, flow: FlowContext,
out_boxes: &mut ~[RenderBox]) { last_whitespace: bool,
out_boxes: &mut ~[RenderBox]) -> bool {
let inline = &mut *flow.inline(); let inline = &mut *flow.inline();
let in_boxes = &inline.boxes; let in_boxes = &inline.boxes;
@ -258,6 +263,8 @@ impl TextRunScanner {
_ => false _ => false
}; };
let mut new_whitespace = last_whitespace;
match (is_singleton, is_text_clump) { match (is_singleton, is_text_clump) {
(false, false) => { (false, false) => {
fail!(~"WAT: can't coalesce non-text nodes in flush_clump_to_list()!") fail!(~"WAT: can't coalesce non-text nodes in flush_clump_to_list()!")
@ -275,13 +282,15 @@ impl TextRunScanner {
// TODO(#115): Use the actual CSS `white-space` property of the relevant style. // TODO(#115): Use the actual CSS `white-space` property of the relevant style.
let compression = CompressWhitespaceNewline; let compression = CompressWhitespaceNewline;
let transformed_text = transform_text(text, compression); let (transformed_text, whitespace) = transform_text(text, compression, last_whitespace);
new_whitespace = whitespace;
// TODO(#177): Text run creation must account for the renderability of text by if transformed_text.len() > 0 {
// font group fonts. This is probably achieved by creating the font group above // TODO(#177): Text run creation must account for the renderability of text by
// and then letting `FontGroup` decide which `Font` to stick into the text run. // font group fonts. This is probably achieved by creating the font group above
let fontgroup = ctx.font_ctx.get_resolved_font_for_style(&font_style); // and then letting `FontGroup` decide which `Font` to stick into the text run.
let run = @fontgroup.create_textrun(transformed_text, underline); let fontgroup = ctx.font_ctx.get_resolved_font_for_style(&font_style);
let run = @fontgroup.create_textrun(transformed_text, underline);
debug!("TextRunScanner: pushing single text box in range: %?", self.clump); debug!("TextRunScanner: pushing single text box in range: %?", self.clump);
let new_box = do old_box.with_base |old_box_base| { let new_box = do old_box.with_base |old_box_base| {
@ -289,21 +298,26 @@ impl TextRunScanner {
@mut adapt_textbox_with_range(*old_box_base, run, range) @mut adapt_textbox_with_range(*old_box_base, run, range)
}; };
out_boxes.push(TextRenderBoxClass(new_box)); out_boxes.push(TextRenderBoxClass(new_box));
}
}, },
(false, true) => { (false, true) => {
// TODO(#115): Use the actual CSS `white-space` property of the relevant style. // TODO(#115): Use the actual CSS `white-space` property of the relevant style.
let compression = CompressWhitespaceNewline; let compression = CompressWhitespaceNewline;
// First, transform/compress text of all the nodes. // First, transform/compress text of all the nodes.
let mut last_whitespace = true;
let transformed_strs: ~[~str] = do vec::from_fn(self.clump.length()) |i| { let transformed_strs: ~[~str] = do vec::from_fn(self.clump.length()) |i| {
// TODO(#113): We should be passing the compression context between calls to // TODO(#113): We should be passing the compression context between calls to
// `transform_text`, so that boxes starting and/or ending with whitespace can // `transform_text`, so that boxes starting and/or ending with whitespace can
// be compressed correctly with respect to the text run. // be compressed correctly with respect to the text run.
let idx = i + self.clump.begin(); let idx = i + self.clump.begin();
transform_text(in_boxes[idx].raw_text(), compression) let (new_str, new_whitespace) = transform_text(in_boxes[idx].raw_text(), compression, last_whitespace);
last_whitespace = new_whitespace;
new_str
}; };
new_whitespace = last_whitespace;
// Next, concatenate all of the transformed strings together, saving the new // Next, concatenate all of the transformed strings together, saving the new
// character indices. // character indices.
let mut run_str: ~str = ~""; let mut run_str: ~str = ~"";
@ -328,7 +342,7 @@ impl TextRunScanner {
// TextRuns contain a cycle which is usually resolved by the teardown // TextRuns contain a cycle which is usually resolved by the teardown
// sequence. If no clump takes ownership, however, it will leak. // sequence. If no clump takes ownership, however, it will leak.
let clump = self.clump; let clump = self.clump;
let run = if clump.length() != 0 { let run = if clump.length() != 0 && run_str.len() > 0 {
Some(@TextRun::new(fontgroup.fonts[0], run_str, underline)) Some(@TextRun::new(fontgroup.fonts[0], run_str, underline))
} else { } else {
None None
@ -373,6 +387,8 @@ impl TextRunScanner {
let end = self.clump.end(); // FIXME: borrow checker workaround let end = self.clump.end(); // FIXME: borrow checker workaround
self.clump.reset(end, 0); self.clump.reset(end, 0);
new_whitespace
} // End of `flush_clump_to_list`. } // End of `flush_clump_to_list`.
} }
@ -748,7 +764,6 @@ impl InlineFlowData {
// TODO(#226): Get the CSS `line-height` property from each non-replaced inline element to // TODO(#226): Get the CSS `line-height` property from each non-replaced inline element to
// determine its height for computing linebox height. // determine its height for computing linebox height.
let line_height = Au::from_px(20);
let mut cur_y = Au(0); let mut cur_y = Au(0);
for self.lines.eachi |i, line_span| { for self.lines.eachi |i, line_span| {
@ -756,64 +771,65 @@ impl InlineFlowData {
// These coordinates are relative to the left baseline. // These coordinates are relative to the left baseline.
let mut linebox_bounding_box = Au::zero_rect(); let mut linebox_bounding_box = Au::zero_rect();
let mut linebox_height = Au(0);
let mut baseline_offset = Au(0);
let boxes = &mut self.boxes; let boxes = &mut self.boxes;
for line_span.eachi |box_i| { for line_span.eachi |box_i| {
let cur_box = boxes[box_i]; // FIXME: borrow checker workaround let cur_box = boxes[box_i]; // FIXME: borrow checker workaround
// Compute the height of each box. // Compute the height and bounding box of each box.
match cur_box { let bounding_box = match cur_box {
ImageRenderBoxClass(image_box) => { ImageRenderBoxClass(image_box) => {
let size = image_box.image.get_size(); let size = image_box.image.get_size();
let height = Au::from_px(size.get_or_default(Size2D(0, 0)).height); let height = Au::from_px(size.get_or_default(Size2D(0, 0)).height);
image_box.base.position.size.height = height; image_box.base.position.size.height = height;
image_box.base.position.translate(&Point2D(Au(0), -height))
} }
TextRenderBoxClass(*) => { TextRenderBoxClass(text_box) => {
// Text boxes are preinitialized.
let range = &text_box.range;
let run = &text_box.run;
// Compute the height based on the line-height and font size
let text_bounds = run.metrics_for_range(range).bounding_box;
let em_size = text_bounds.size.height;
let line_height = match cur_box.line_height() {
// use the font's leading for normal
CSSLineHeightNormal => text_box.run.font.metrics.leading,
CSSLineHeightNumber(l) => em_size.scale_by(l),
CSSLineHeightLength(Em(l)) => em_size.scale_by(l),
CSSLineHeightLength(Px(l)) => Au::from_frac_px(l),
CSSLineHeightLength(Pt(l)) => Au::from_pt(l),
CSSLineHeightPercentage(p) => em_size.scale_by(p / 100.0f)
};
// If this is the current tallest box then use it for baseline
// calculations.
// TODO: this will need to take into account type of line-height
// and the vertical-align value.
if line_height > linebox_height {
linebox_height = line_height;
// Offset from the top of the linebox is 1/2 of the leading + ascent
baseline_offset = text_box.run.font.metrics.ascent +
(linebox_height - em_size).scale_by(0.5f);
}
text_bounds.translate(&Point2D(text_box.base.position.origin.x, Au(0)))
} }
GenericRenderBoxClass(generic_box) => { GenericRenderBoxClass(generic_box) => {
// TODO(Issue #225): There will be different cases here for `inline-block` // TODO(Issue #225): There will be different cases here for `inline-block`
// and other replaced content. // and other replaced content.
// FIXME(pcwalton): This seems clownshoes; can we remove? // FIXME(pcwalton): This seems clownshoes; can we remove?
generic_box.position.size.height = Au::from_px(30); generic_box.position.size.height = Au::from_px(30);
generic_box.position
} }
// FIXME(pcwalton): This isn't very type safe! // FIXME(pcwalton): This isn't very type safe!
_ => { _ => {
fail!(fmt!("Tried to assign height to unknown Box variant: %s", fail!(fmt!("Tried to assign height to unknown Box variant: %s",
cur_box.debug_str())) cur_box.debug_str()))
} }
}
// Compute the bounding rect with the left baseline as origin. Determining line box
// height is a matter of lining up ideal baselines and then taking the union of all
// these rects.
let bounding_box = match cur_box {
// Adjust to baseline coordinates.
//
// TODO(#227): Use left/right margins, border, padding for nonreplaced content,
// and also use top/bottom margins, border, padding for replaced or
// inline-block content.
//
// TODO(#225): Use height, width for `inline-block` and other replaced content.
ImageRenderBoxClass(*) | GenericRenderBoxClass(*) => {
let height = cur_box.position().size.height;
cur_box.position().translate(&Point2D(Au(0), -height))
},
// Adjust the bounding box metric to the box's horizontal offset.
//
// TODO: We can use font metrics directly instead of re-measuring for the
// bounding box.
TextRenderBoxClass(text_box) => {
let range = &text_box.range;
let run = &text_box.run;
let text_bounds = run.metrics_for_range(range).bounding_box;
text_bounds.translate(&Point2D(text_box.base.position.origin.x, Au(0)))
},
_ => {
fail!(fmt!("Tried to compute bounding box of unknown Box variant: %s",
cur_box.debug_str()))
}
}; };
debug!("assign_height_inline: bounding box for box b%d = %?", debug!("assign_height_inline: bounding box for box b%d = %?",
@ -825,9 +841,6 @@ impl InlineFlowData {
debug!("assign_height_inline: linebox bounding box = %?", linebox_bounding_box); debug!("assign_height_inline: linebox bounding box = %?", linebox_bounding_box);
} }
let linebox_height = linebox_bounding_box.size.height;
let baseline_offset = -linebox_bounding_box.origin.y;
// Now go back and adjust the Y coordinates to match the baseline we determined. // Now go back and adjust the Y coordinates to match the baseline we determined.
for line_span.eachi |box_i| { for line_span.eachi |box_i| {
let cur_box = boxes[box_i]; let cur_box = boxes[box_i];
@ -835,30 +848,19 @@ impl InlineFlowData {
// TODO(#226): This is completely wrong. We need to use the element's `line-height` // TODO(#226): This is completely wrong. We need to use the element's `line-height`
// when calculating line box height. Then we should go back over and set Y offsets // when calculating line box height. Then we should go back over and set Y offsets
// according to the `vertical-align` property of the containing block. // according to the `vertical-align` property of the containing block.
let halfleading = match cur_box { let offset = match cur_box {
TextRenderBoxClass(text_box) => { TextRenderBoxClass(text_box) => {
//ad is the AD height as defined by CSS 2.1 § 10.8.1 baseline_offset - text_box.run.font.metrics.ascent
let ad = text_box.run.font.metrics.ascent + text_box.run.font.metrics.descent;
(line_height - ad).scale_by(0.5)
}, },
_ => Au(0), _ => Au(0),
}; };
//FIXME: when line-height is set on an inline element, the half leading
//distance can be negative.
let halfleading = Au::max(halfleading, Au(0));
let height = match cur_box {
TextRenderBoxClass(text_box) => text_box.run.font.metrics.ascent,
_ => cur_box.position().size.height
};
do cur_box.with_mut_base |base| { do cur_box.with_mut_base |base| {
base.position.origin.y = cur_y + halfleading + baseline_offset - height; base.position.origin.y = offset + cur_y;
} }
} }
cur_y += Au::max(line_height, linebox_height); cur_y += linebox_height;
} // End of `lines.each` loop. } // End of `lines.each` loop.
self.common.position.size.height = cur_y; self.common.position.size.height = cur_y;

View file

@ -25,8 +25,7 @@ use gfx::display_list::DisplayList;
use gfx::font_context::FontContext; use gfx::font_context::FontContext;
use gfx::geometry::Au; use gfx::geometry::Au;
use gfx::opts::Opts; use gfx::opts::Opts;
use gfx::render_layers::RenderLayer; use gfx::render_task::{RenderMsg, RenderChan, RenderLayer};
use gfx::render_task::{RenderMsg, RenderChan};
use newcss::select::SelectCtx; use newcss::select::SelectCtx;
use newcss::stylesheet::Stylesheet; use newcss::stylesheet::Stylesheet;
use newcss::types::OriginAuthor; use newcss::types::OriginAuthor;

View file

@ -15,11 +15,11 @@ use alert::{Alert, AlertMethods};
use core::libc::c_int; use core::libc::c_int;
use geom::point::Point2D; use geom::point::Point2D;
use geom::size::Size2D; use geom::size::Size2D;
use gfx::compositor::{IdleRenderState, RenderState, RenderingRenderState}; use servo_msg::compositor::{IdleRenderState, RenderState, RenderingRenderState};
use servo_msg::compositor::{FinishedLoading, Loading, PerformingLayout, ReadyState};
use glut::glut::{ACTIVE_CTRL, DOUBLE, HAVE_PRECISE_MOUSE_WHEEL, WindowHeight, WindowWidth}; use glut::glut::{ACTIVE_CTRL, DOUBLE, HAVE_PRECISE_MOUSE_WHEEL, WindowHeight, WindowWidth};
use glut::glut; use glut::glut;
use glut::machack; use glut::machack;
use script::compositor_interface::{FinishedLoading, Loading, PerformingLayout, ReadyState};
static THROBBER: [char, ..8] = [ '⣾', '⣽', '⣻', '⢿', '⡿', '⣟', '⣯', '⣷' ]; static THROBBER: [char, ..8] = [ '⣾', '⣽', '⣻', '⢿', '⡿', '⣟', '⣯', '⣷' ];
@ -59,7 +59,8 @@ impl WindowMethods<Application> for Window {
/// Creates a new window. /// Creates a new window.
pub fn new(_: &Application) -> @mut Window { pub fn new(_: &Application) -> @mut Window {
// Create the GLUT window. // Create the GLUT window.
unsafe { glut::bindgen::glutInitWindowSize(800, 600); } // FIXME (Rust #3080): These unsafe blocks are *not* unused!
/*unsafe { */glut::bindgen::glutInitWindowSize(800, 600);/* }*/
let glut_window = glut::create_window(~"Servo"); let glut_window = glut::create_window(~"Servo");
// Create our window object. // Create our window object.
@ -117,7 +118,7 @@ impl WindowMethods<Application> for Window {
window.handle_mouse(button, state, x, y); window.handle_mouse(button, state, x, y);
} }
} }
do glut::mouse_wheel_func |wheel, direction, x, y| { do glut::mouse_wheel_func |wheel, direction, _x, _y| {
let delta = if HAVE_PRECISE_MOUSE_WHEEL { let delta = if HAVE_PRECISE_MOUSE_WHEEL {
(direction as f32) / 10000.0 (direction as f32) / 10000.0
} else { } else {

View file

@ -23,6 +23,7 @@ extern mod newcss (name = "css");
extern mod opengles; extern mod opengles;
extern mod script; extern mod script;
extern mod servo_net (name = "net"); extern mod servo_net (name = "net");
extern mod servo_msg (name = "msg");
extern mod servo_util (name = "util"); extern mod servo_util (name = "util");
extern mod sharegl; extern mod sharegl;
extern mod stb_image; extern mod stb_image;
@ -35,7 +36,7 @@ extern mod core_text;
use compositing::{CompositorChan, CompositorTask}; use compositing::{CompositorChan, CompositorTask};
use engine::Engine; use engine::Engine;
use script::engine_interface::{ExitMsg, LoadUrlMsg}; use servo_msg::engine::{ExitMsg, LoadUrlMsg};
use gfx::opts; use gfx::opts;
use servo_net::image_cache_task::ImageCacheTask; use servo_net::image_cache_task::ImageCacheTask;

View file

@ -6,8 +6,7 @@
use geom::point::Point2D; use geom::point::Point2D;
use geom::size::Size2D; use geom::size::Size2D;
use gfx::compositor::RenderState; use servo_msg::compositor::{ReadyState, RenderState};
use script::compositor_interface::ReadyState;
pub enum WindowMouseEvent { pub enum WindowMouseEvent {
WindowClickEvent(uint, Point2D<f32>), WindowClickEvent(uint, Point2D<f32>),

View file

@ -40,3 +40,17 @@ pub trait RenderListener {
fn set_render_state(&self, render_state: RenderState); fn set_render_state(&self, render_state: RenderState);
} }
pub enum ReadyState {
/// Informs the compositor that a page is loading. Used for setting status
Loading,
/// Informs the compositor that a page is performing layout. Used for setting status
PerformingLayout,
/// Informs the compositor that a page is finished loading. Used for setting status
FinishedLoading,
}
/// The interface used by the script task to tell the compositor to update its ready state,
/// which is used in displaying the appropriate message in the window's title.
pub trait ScriptListener : Clone {
fn set_ready_state(&self, ReadyState);
}

18
src/components/msg/msg.rc Normal file
View file

@ -0,0 +1,18 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
#[link(name = "msg",
vers = "0.1",
uuid = "4c6054e4-2a7b-4fae-b0c8-6d04416b2bf2",
url = "http://servo.org/")];
#[crate_type = "lib"];
extern mod azure;
extern mod core;
extern mod geom;
extern mod std;
pub mod compositor;
pub mod engine;

View file

@ -1,21 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
//! The high-level interface from script to compositor. Using this abstract interface helps reduce
/// coupling between these two components
pub enum ReadyState {
/// Informs the compositor that a page is loading. Used for setting status
Loading,
/// Informs the compositor that a page is performing layout. Used for setting status
PerformingLayout,
/// Informs the compositor that a page is finished loading. Used for setting status
FinishedLoading,
}
/// The interface used by the script task to tell the compositor to update its ready state,
/// which is used in displaying the appropriate message in the window's title.
pub trait ScriptListener : Clone {
fn set_ready_state(&self, ReadyState);
}

View file

@ -19,6 +19,7 @@ extern mod netsurfcss;
extern mod newcss (name = "css"); extern mod newcss (name = "css");
extern mod servo_net (name = "net"); extern mod servo_net (name = "net");
extern mod servo_util (name = "util"); extern mod servo_util (name = "util");
extern mod servo_msg (name = "msg");
extern mod std; extern mod std;
pub mod dom { pub mod dom {
@ -64,8 +65,6 @@ pub mod html {
pub mod hubbub_html_parser; pub mod hubbub_html_parser;
} }
pub mod compositor_interface;
pub mod engine_interface;
pub mod layout_interface; pub mod layout_interface;
pub mod script_task; pub mod script_task;

View file

@ -5,7 +5,7 @@
/// The script task is the task that owns the DOM in memory, runs JavaScript, and spawns parsing /// The script task is the task that owns the DOM in memory, runs JavaScript, and spawns parsing
/// and layout tasks. /// and layout tasks.
use compositor_interface::{ReadyState, Loading, PerformingLayout, FinishedLoading}; use servo_msg::compositor::{ReadyState, Loading, PerformingLayout, FinishedLoading};
use dom::bindings::utils::GlobalStaticData; use dom::bindings::utils::GlobalStaticData;
use dom::document::Document; use dom::document::Document;
use dom::element::Element; use dom::element::Element;
@ -17,7 +17,7 @@ use layout_interface::{HitTestResponse, LayoutQuery, LayoutResponse, LayoutChan}
use layout_interface::{MatchSelectorsDocumentDamage, QueryMsg, Reflow, ReflowDocumentDamage}; use layout_interface::{MatchSelectorsDocumentDamage, QueryMsg, Reflow, ReflowDocumentDamage};
use layout_interface::{ReflowForDisplay, ReflowForScriptQuery, ReflowGoal, ReflowMsg}; use layout_interface::{ReflowForDisplay, ReflowForScriptQuery, ReflowGoal, ReflowMsg};
use layout_interface; use layout_interface;
use engine_interface::{EngineChan, LoadUrlMsg}; use servo_msg::engine::{EngineChan, LoadUrlMsg};
use core::cast::transmute; use core::cast::transmute;
use core::cell::Cell; use core::cell::Cell;
@ -546,7 +546,7 @@ impl ScriptContext {
} }
} }
ClickEvent(button, 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,

View file

@ -3,9 +3,8 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
pub trait Cache<K: Copy + Eq, V: Copy> { pub trait Cache<K: Copy + Eq, V: Copy> {
fn new(size: uint) -> Self;
fn insert(&mut self, key: &K, value: V); fn insert(&mut self, key: &K, value: V);
fn find(&self, key: &K) -> Option<V>; fn find(&mut self, key: &K) -> Option<V>;
fn find_or_create(&mut self, key: &K, blk: &fn(&K) -> V) -> V; fn find_or_create(&mut self, key: &K, blk: &fn(&K) -> V) -> V;
fn evict_all(&mut self); fn evict_all(&mut self);
} }
@ -14,16 +13,18 @@ pub struct MonoCache<K, V> {
entry: Option<(K,V)>, entry: Option<(K,V)>,
} }
impl<K: Copy + Eq, V: Copy> Cache<K,V> for MonoCache<K,V> { pub impl<K: Copy + Eq, V: Copy> MonoCache<K,V> {
fn new(_size: uint) -> MonoCache<K,V> { fn new(_size: uint) -> MonoCache<K,V> {
MonoCache { entry: None } MonoCache { entry: None }
} }
}
impl<K: Copy + Eq, V: Copy> Cache<K,V> for MonoCache<K,V> {
fn insert(&mut self, key: &K, value: V) { fn insert(&mut self, key: &K, value: V) {
self.entry = Some((copy *key, value)); self.entry = Some((copy *key, value));
} }
fn find(&self, key: &K) -> Option<V> { fn find(&mut self, key: &K) -> Option<V> {
match self.entry { match self.entry {
None => None, None => None,
Some((ref k,v)) => if *k == *key { Some(v) } else { None } Some((ref k,v)) => if *k == *key { Some(v) } else { None }
@ -47,8 +48,7 @@ impl<K: Copy + Eq, V: Copy> Cache<K,V> for MonoCache<K,V> {
#[test] #[test]
fn test_monocache() { fn test_monocache() {
// TODO: this is hideous because of Rust Issue #3902 let cache = MonoCache::new(10);
let cache = cache::new::<uint, @str, MonoCache<uint, @str>>(10);
let one = @"one"; let one = @"one";
let two = @"two"; let two = @"two";
cache.insert(&1, one); cache.insert(&1, one);
@ -59,3 +59,91 @@ fn test_monocache() {
assert!(cache.find(&2).is_some()); assert!(cache.find(&2).is_some());
assert!(cache.find(&1).is_none()); assert!(cache.find(&1).is_none());
} }
pub struct LRUCache<K, V> {
entries: ~[(K, V)],
cache_size: uint,
}
pub impl<K: Copy + Eq, V: Copy> LRUCache<K,V> {
fn new(size: uint) -> LRUCache<K, V> {
LRUCache {
entries: ~[],
cache_size: size,
}
}
fn touch(&mut self, pos: uint) -> V {
let (key, val) = copy self.entries[pos];
if pos != self.cache_size {
self.entries.remove(pos);
self.entries.push((key, val));
}
val
}
}
impl<K: Copy + Eq, V: Copy> Cache<K,V> for LRUCache<K,V> {
fn insert(&mut self, key: &K, val: V) {
if self.entries.len() == self.cache_size {
self.entries.remove(0);
}
self.entries.push((copy *key, val));
}
fn find(&mut self, key: &K) -> Option<V> {
match self.entries.position(|&(k, _)| k == *key) {
Some(pos) => Some(self.touch(pos)),
None => None,
}
}
fn find_or_create(&mut self, key: &K, blk: &fn(&K) -> V) -> V {
match self.entries.position(|&(k, _)| k == *key) {
Some(pos) => self.touch(pos),
None => {
let val = blk(key);
self.insert(key, val);
val
}
}
}
fn evict_all(&mut self) {
self.entries.clear();
}
}
#[test]
fn test_lru_cache() {
let one = @"one";
let two = @"two";
let three = @"three";
let four = @"four";
// Test normal insertion.
let cache = LRUCache::new(2); // (_, _) (cache is empty)
cache.insert(&1, one); // (1, _)
cache.insert(&2, two); // (1, 2)
cache.insert(&3, three); // (2, 3)
assert!(cache.find(&1).is_none()); // (2, 3) (no change)
assert!(cache.find(&3).is_some()); // (2, 3)
assert!(cache.find(&2).is_some()); // (3, 2)
// Test that LRU works (this insertion should replace 3, not 2).
cache.insert(&4, four); // (2, 4)
assert!(cache.find(&1).is_none()); // (2, 4) (no change)
assert!(cache.find(&2).is_some()); // (4, 2)
assert!(cache.find(&3).is_none()); // (4, 2) (no change)
assert!(cache.find(&4).is_some()); // (2, 4) (no change)
// Test find_or_create.
do cache.find_or_create(&1) |_| { one } // (4, 1)
assert!(cache.find(&1).is_some()); // (4, 1) (no change)
assert!(cache.find(&2).is_none()); // (4, 1) (no change)
assert!(cache.find(&3).is_none()); // (4, 1) (no change)
assert!(cache.find(&4).is_some()); // (1, 4)
}

View file

@ -37,8 +37,8 @@ pub enum ProfilerCategory {
LayoutShapingCategory, LayoutShapingCategory,
LayoutDispListBuildCategory, LayoutDispListBuildCategory,
GfxRegenAvailableFontsCategory, GfxRegenAvailableFontsCategory,
RenderingDrawingCategory,
RenderingPrepBuffCategory, RenderingPrepBuffCategory,
RenderingWaitSubtasksCategory,
RenderingCategory, RenderingCategory,
// hackish but helps prevent errors when adding new categories // hackish but helps prevent errors when adding new categories
NUM_BUCKETS, NUM_BUCKETS,
@ -81,8 +81,8 @@ impl ProfilerCategory {
vec.push((LayoutShapingCategory, ~[])); vec.push((LayoutShapingCategory, ~[]));
vec.push((LayoutDispListBuildCategory, ~[])); vec.push((LayoutDispListBuildCategory, ~[]));
vec.push((GfxRegenAvailableFontsCategory, ~[])); vec.push((GfxRegenAvailableFontsCategory, ~[]));
vec.push((RenderingDrawingCategory, ~[]));
vec.push((RenderingPrepBuffCategory, ~[])); vec.push((RenderingPrepBuffCategory, ~[]));
vec.push((RenderingWaitSubtasksCategory, ~[]));
vec.push((RenderingCategory, ~[])); vec.push((RenderingCategory, ~[]));
ProfilerCategory::check_order(vec); ProfilerCategory::check_order(vec);

@ -1 +1 @@
Subproject commit 04976e3fae0ef0332036082ab8770fb1ad2c10ca Subproject commit cf5641f5b0b7720e3841518ab7fbfafb4d2443a4

@ -1 +1 @@
Subproject commit d722188de3876ed748382965eb4f300fc1b78bf8 Subproject commit da248d3f5b3ed6d9e804c543563be8e34baf1673

@ -1 +1 @@
Subproject commit 8ba903a1af20ce461d4f4033ec1092a229bc7482 Subproject commit 0824ca2895668b6ac1751871d7360bc484afb930

@ -1 +1 @@
Subproject commit 7415c2829e77812411a2ae9cf448c0e288035463 Subproject commit 3f41e98925b9d4c0d4074046388e73f0e0032ab9

View file

@ -0,0 +1,10 @@
#larger1 {
font-size: 20px;
line-height: 2;
}
#larger2 {
font-size: 30px;
line-height: 1;
}

View file

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="lineheight-simple.css"/>
</head>
<body>
<div>Regular font <span id="larger1">Even larger with line-height 2</span></div>
<div id="larger2">Large line 2!</div>
</body>
</html>