mirror of
https://github.com/servo/servo.git
synced 2025-06-26 18:14:34 +01:00
servo: Implement stacking contexts and allow multiple layers per
pipeline. This handles fixed positioning mostly correctly.
This commit is contained in:
parent
f8e3e50db5
commit
cd9d824c21
20 changed files with 1726 additions and 1142 deletions
|
@ -18,64 +18,145 @@ use color::Color;
|
||||||
use render_context::RenderContext;
|
use render_context::RenderContext;
|
||||||
use text::TextRun;
|
use text::TextRun;
|
||||||
|
|
||||||
use geom::{Point2D, Rect, Size2D, SideOffsets2D};
|
use geom::{Point2D, Rect, SideOffsets2D, Size2D};
|
||||||
use servo_net::image::base::Image;
|
use servo_net::image::base::Image;
|
||||||
use servo_util::geometry::Au;
|
use servo_util::geometry::Au;
|
||||||
use servo_util::range::Range;
|
use servo_util::range::Range;
|
||||||
|
use servo_util::smallvec::{SmallVec, SmallVec0, SmallVecIterator};
|
||||||
use std::cast::transmute_region;
|
use std::cast::transmute_region;
|
||||||
|
use std::cast;
|
||||||
|
use std::libc::uintptr_t;
|
||||||
|
use std::mem;
|
||||||
use std::vec::Items;
|
use std::vec::Items;
|
||||||
use style::computed_values::border_style;
|
use style::computed_values::border_style;
|
||||||
use sync::Arc;
|
use sync::Arc;
|
||||||
|
|
||||||
pub struct DisplayListCollection<E> {
|
/// An opaque handle to a node. The only safe operation that can be performed on this node is to
|
||||||
lists: ~[DisplayList<E>]
|
/// compare it to another opaque handle or to another node.
|
||||||
|
///
|
||||||
|
/// Because the script task's GC does not trace layout, node data cannot be safely stored in layout
|
||||||
|
/// data structures. Also, layout code tends to be faster when the DOM is not being accessed, for
|
||||||
|
/// locality reasons. Using `OpaqueNode` enforces this invariant.
|
||||||
|
#[deriving(Clone, Eq)]
|
||||||
|
pub struct OpaqueNode(uintptr_t);
|
||||||
|
|
||||||
|
impl OpaqueNode {
|
||||||
|
/// Returns the address of this node, for debugging purposes.
|
||||||
|
pub fn id(&self) -> uintptr_t {
|
||||||
|
unsafe {
|
||||||
|
cast::transmute_copy(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E> DisplayListCollection<E> {
|
/// A stacking context. See CSS 2.1 § E.2. "Steps" below refer to steps in that section of the
|
||||||
pub fn new() -> DisplayListCollection<E> {
|
/// specification.
|
||||||
DisplayListCollection {
|
///
|
||||||
lists: ~[]
|
/// TODO(pcwalton): Outlines.
|
||||||
|
pub struct StackingContext {
|
||||||
|
/// The border and backgrounds for the root of this stacking context: steps 1 and 2.
|
||||||
|
background_and_borders: DisplayList,
|
||||||
|
/// Borders and backgrounds for block-level descendants: step 4.
|
||||||
|
block_backgrounds_and_borders: DisplayList,
|
||||||
|
/// Floats: step 5. These are treated as pseudo-stacking contexts.
|
||||||
|
floats: DisplayList,
|
||||||
|
/// All other content.
|
||||||
|
content: DisplayList,
|
||||||
|
|
||||||
|
/// Positioned descendant stacking contexts, along with their `z-index` levels.
|
||||||
|
///
|
||||||
|
/// TODO(pcwalton): `z-index` should be the actual CSS property value in order to handle
|
||||||
|
/// `auto`, not just an integer. In this case we should store an actual stacking context, not
|
||||||
|
/// a flattened display list.
|
||||||
|
positioned_descendants: SmallVec0<(int, DisplayList)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StackingContext {
|
||||||
|
pub fn new() -> StackingContext {
|
||||||
|
StackingContext {
|
||||||
|
background_and_borders: DisplayList::new(),
|
||||||
|
block_backgrounds_and_borders: DisplayList::new(),
|
||||||
|
floats: DisplayList::new(),
|
||||||
|
content: DisplayList::new(),
|
||||||
|
positioned_descendants: SmallVec0::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn iter<'a>(&'a self) -> DisplayListIterator<'a,E> {
|
pub fn list_for_background_and_border_level<'a>(
|
||||||
ParentDisplayListIterator(self.lists.iter())
|
&'a mut self,
|
||||||
}
|
level: BackgroundAndBorderLevel)
|
||||||
|
-> &'a mut DisplayList {
|
||||||
pub fn add_list(&mut self, list: DisplayList<E>) {
|
match level {
|
||||||
self.lists.push(list);
|
RootOfStackingContextLevel => &mut self.background_and_borders,
|
||||||
}
|
BlockLevel => &mut self.block_backgrounds_and_borders,
|
||||||
|
ContentLevel => &mut self.content,
|
||||||
pub fn draw_lists_into_context(&self, render_context: &mut RenderContext) {
|
|
||||||
for list in self.lists.iter() {
|
|
||||||
list.draw_into_context(render_context);
|
|
||||||
}
|
|
||||||
debug!("{:?}", self.dump());
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dump(&self) {
|
|
||||||
let mut index = 0;
|
|
||||||
for list in self.lists.iter() {
|
|
||||||
debug!("dumping display list {:d}:", index);
|
|
||||||
list.dump();
|
|
||||||
index = index + 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Flattens a stacking context into a display list according to the steps in CSS 2.1 § E.2.
|
||||||
|
pub fn flatten(self) -> DisplayList {
|
||||||
|
// Steps 1 and 2: Borders and background for the root.
|
||||||
|
let StackingContext {
|
||||||
|
background_and_borders: mut result,
|
||||||
|
block_backgrounds_and_borders,
|
||||||
|
floats,
|
||||||
|
content,
|
||||||
|
positioned_descendants: mut positioned_descendants
|
||||||
|
} = self;
|
||||||
|
|
||||||
|
// TODO(pcwalton): Sort positioned children according to z-index.
|
||||||
|
|
||||||
|
// Step 3: Positioned descendants with negative z-indices.
|
||||||
|
for &(ref mut z_index, ref mut list) in positioned_descendants.mut_iter() {
|
||||||
|
if *z_index < 0 {
|
||||||
|
result.push_all_move(mem::replace(list, DisplayList::new()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 4: Block backgrounds and borders.
|
||||||
|
result.push_all_move(block_backgrounds_and_borders);
|
||||||
|
|
||||||
|
// Step 5: Floats.
|
||||||
|
result.push_all_move(floats);
|
||||||
|
|
||||||
|
// TODO(pcwalton): Step 6: Inlines that generate stacking contexts.
|
||||||
|
|
||||||
|
// Step 7: Content.
|
||||||
|
result.push_all_move(content);
|
||||||
|
|
||||||
|
// Steps 8 and 9: Positioned descendants with nonnegative z-indices.
|
||||||
|
for &(ref mut z_index, ref mut list) in positioned_descendants.mut_iter() {
|
||||||
|
if *z_index >= 0 {
|
||||||
|
result.push_all_move(mem::replace(list, DisplayList::new()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(pcwalton): Step 10: Outlines.
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Which level to place backgrounds and borders in.
|
||||||
|
pub enum BackgroundAndBorderLevel {
|
||||||
|
RootOfStackingContextLevel,
|
||||||
|
BlockLevel,
|
||||||
|
ContentLevel,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A list of rendering operations to be performed.
|
/// A list of rendering operations to be performed.
|
||||||
pub struct DisplayList<E> {
|
pub struct DisplayList {
|
||||||
list: ~[DisplayItem<E>]
|
list: SmallVec0<DisplayItem>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum DisplayListIterator<'a,E> {
|
pub enum DisplayListIterator<'a> {
|
||||||
EmptyDisplayListIterator,
|
EmptyDisplayListIterator,
|
||||||
ParentDisplayListIterator(Items<'a,DisplayList<E>>),
|
ParentDisplayListIterator(Items<'a,DisplayList>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a,E> Iterator<&'a DisplayList<E>> for DisplayListIterator<'a,E> {
|
impl<'a> Iterator<&'a DisplayList> for DisplayListIterator<'a> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn next(&mut self) -> Option<&'a DisplayList<E>> {
|
fn next(&mut self) -> Option<&'a DisplayList> {
|
||||||
match *self {
|
match *self {
|
||||||
EmptyDisplayListIterator => None,
|
EmptyDisplayListIterator => None,
|
||||||
ParentDisplayListIterator(ref mut subiterator) => subiterator.next(),
|
ParentDisplayListIterator(ref mut subiterator) => subiterator.next(),
|
||||||
|
@ -83,11 +164,11 @@ impl<'a,E> Iterator<&'a DisplayList<E>> for DisplayListIterator<'a,E> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E> DisplayList<E> {
|
impl DisplayList {
|
||||||
/// Creates a new display list.
|
/// Creates a new display list.
|
||||||
pub fn new() -> DisplayList<E> {
|
pub fn new() -> DisplayList {
|
||||||
DisplayList {
|
DisplayList {
|
||||||
list: ~[]
|
list: SmallVec0::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,60 +179,62 @@ impl<E> DisplayList<E> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Appends the given item to the display list.
|
/// Appends the given item to the display list.
|
||||||
pub fn append_item(&mut self, item: DisplayItem<E>) {
|
pub fn push(&mut self, item: DisplayItem) {
|
||||||
// FIXME(Issue #150): crashes
|
|
||||||
//debug!("Adding display item {:u}: {}", self.len(), item);
|
|
||||||
self.list.push(item)
|
self.list.push(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Appends the given display list to this display list, consuming the other display list in
|
||||||
|
/// the process.
|
||||||
|
pub fn push_all_move(&mut self, other: DisplayList) {
|
||||||
|
self.list.push_all_move(other.list)
|
||||||
|
}
|
||||||
|
|
||||||
/// Draws the display list into the given render context.
|
/// Draws the display list into the given render context.
|
||||||
pub fn draw_into_context(&self, render_context: &mut RenderContext) {
|
pub fn draw_into_context(&self, render_context: &mut RenderContext) {
|
||||||
debug!("Beginning display list.");
|
debug!("Beginning display list.");
|
||||||
for item in self.list.iter() {
|
for item in self.list.iter() {
|
||||||
// FIXME(Issue #150): crashes
|
|
||||||
//debug!("drawing {}", *item);
|
|
||||||
item.draw_into_context(render_context)
|
item.draw_into_context(render_context)
|
||||||
}
|
}
|
||||||
debug!("Ending display list.");
|
debug!("Ending display list.");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a preorder iterator over the given display list.
|
/// Returns a preorder iterator over the given display list.
|
||||||
pub fn iter<'a>(&'a self) -> DisplayItemIterator<'a,E> {
|
pub fn iter<'a>(&'a self) -> DisplayItemIterator<'a> {
|
||||||
ParentDisplayItemIterator(self.list.iter())
|
ParentDisplayItemIterator(self.list.iter())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// One drawing command in the list.
|
/// One drawing command in the list.
|
||||||
pub enum DisplayItem<E> {
|
pub enum DisplayItem {
|
||||||
SolidColorDisplayItemClass(~SolidColorDisplayItem<E>),
|
SolidColorDisplayItemClass(~SolidColorDisplayItem),
|
||||||
TextDisplayItemClass(~TextDisplayItem<E>),
|
TextDisplayItemClass(~TextDisplayItem),
|
||||||
ImageDisplayItemClass(~ImageDisplayItem<E>),
|
ImageDisplayItemClass(~ImageDisplayItem),
|
||||||
BorderDisplayItemClass(~BorderDisplayItem<E>),
|
BorderDisplayItemClass(~BorderDisplayItem),
|
||||||
LineDisplayItemClass(~LineDisplayItem<E>),
|
LineDisplayItemClass(~LineDisplayItem),
|
||||||
ClipDisplayItemClass(~ClipDisplayItem<E>)
|
ClipDisplayItemClass(~ClipDisplayItem)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Information common to all display items.
|
/// Information common to all display items.
|
||||||
pub struct BaseDisplayItem<E> {
|
pub struct BaseDisplayItem {
|
||||||
/// The boundaries of the display item.
|
/// The boundaries of the display item.
|
||||||
///
|
///
|
||||||
/// TODO: Which coordinate system should this use?
|
/// TODO: Which coordinate system should this use?
|
||||||
bounds: Rect<Au>,
|
bounds: Rect<Au>,
|
||||||
|
|
||||||
/// Extra data: either the originating flow (for hit testing) or nothing (for rendering).
|
/// The originating DOM node.
|
||||||
extra: E,
|
node: OpaqueNode,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Renders a solid color.
|
/// Renders a solid color.
|
||||||
pub struct SolidColorDisplayItem<E> {
|
pub struct SolidColorDisplayItem {
|
||||||
base: BaseDisplayItem<E>,
|
base: BaseDisplayItem,
|
||||||
color: Color,
|
color: Color,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Renders text.
|
/// Renders text.
|
||||||
pub struct TextDisplayItem<E> {
|
pub struct TextDisplayItem {
|
||||||
/// Fields common to all display items.
|
/// Fields common to all display items.
|
||||||
base: BaseDisplayItem<E>,
|
base: BaseDisplayItem,
|
||||||
|
|
||||||
/// The text run.
|
/// The text run.
|
||||||
text_run: Arc<~TextRun>,
|
text_run: Arc<~TextRun>,
|
||||||
|
@ -188,14 +271,19 @@ bitfield!(TextDisplayItemFlags, override_overline, set_override_overline, 0x02)
|
||||||
bitfield!(TextDisplayItemFlags, override_line_through, set_override_line_through, 0x04)
|
bitfield!(TextDisplayItemFlags, override_line_through, set_override_line_through, 0x04)
|
||||||
|
|
||||||
/// Renders an image.
|
/// Renders an image.
|
||||||
pub struct ImageDisplayItem<E> {
|
pub struct ImageDisplayItem {
|
||||||
base: BaseDisplayItem<E>,
|
base: BaseDisplayItem,
|
||||||
image: Arc<~Image>,
|
image: Arc<~Image>,
|
||||||
|
|
||||||
|
/// The dimensions to which the image display item should be stretched. If this is smaller than
|
||||||
|
/// the bounds of this display item, then the image will be repeated in the appropriate
|
||||||
|
/// direction to tile the entire bounds.
|
||||||
|
stretch_size: Size2D<Au>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Renders a border.
|
/// Renders a border.
|
||||||
pub struct BorderDisplayItem<E> {
|
pub struct BorderDisplayItem {
|
||||||
base: BaseDisplayItem<E>,
|
base: BaseDisplayItem,
|
||||||
|
|
||||||
/// The border widths
|
/// The border widths
|
||||||
border: SideOffsets2D<Au>,
|
border: SideOffsets2D<Au>,
|
||||||
|
@ -207,31 +295,31 @@ pub struct BorderDisplayItem<E> {
|
||||||
style: SideOffsets2D<border_style::T>
|
style: SideOffsets2D<border_style::T>
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Renders a line segment
|
/// Renders a line segment.
|
||||||
pub struct LineDisplayItem<E> {
|
pub struct LineDisplayItem {
|
||||||
base: BaseDisplayItem<E>,
|
base: BaseDisplayItem,
|
||||||
|
|
||||||
/// The line segment color.
|
/// The line segment color.
|
||||||
color: Color,
|
color: Color,
|
||||||
|
|
||||||
/// The line segemnt style.
|
/// The line segment style.
|
||||||
style: border_style::T
|
style: border_style::T
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ClipDisplayItem<E> {
|
pub struct ClipDisplayItem {
|
||||||
base: BaseDisplayItem<E>,
|
base: BaseDisplayItem,
|
||||||
child_list: ~[DisplayItem<E>],
|
child_list: SmallVec0<DisplayItem>,
|
||||||
need_clip: bool
|
need_clip: bool
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum DisplayItemIterator<'a,E> {
|
pub enum DisplayItemIterator<'a> {
|
||||||
EmptyDisplayItemIterator,
|
EmptyDisplayItemIterator,
|
||||||
ParentDisplayItemIterator(Items<'a,DisplayItem<E>>),
|
ParentDisplayItemIterator(SmallVecIterator<'a,DisplayItem>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a,E> Iterator<&'a DisplayItem<E>> for DisplayItemIterator<'a,E> {
|
impl<'a> Iterator<&'a DisplayItem> for DisplayItemIterator<'a> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn next(&mut self) -> Option<&'a DisplayItem<E>> {
|
fn next(&mut self) -> Option<&'a DisplayItem> {
|
||||||
match *self {
|
match *self {
|
||||||
EmptyDisplayItemIterator => None,
|
EmptyDisplayItemIterator => None,
|
||||||
ParentDisplayItemIterator(ref mut subiterator) => subiterator.next(),
|
ParentDisplayItemIterator(ref mut subiterator) => subiterator.next(),
|
||||||
|
@ -239,7 +327,7 @@ impl<'a,E> Iterator<&'a DisplayItem<E>> for DisplayItemIterator<'a,E> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E> DisplayItem<E> {
|
impl DisplayItem {
|
||||||
/// Renders this display item into the given render context.
|
/// Renders this display item into the given render context.
|
||||||
fn draw_into_context(&self, render_context: &mut RenderContext) {
|
fn draw_into_context(&self, render_context: &mut RenderContext) {
|
||||||
match *self {
|
match *self {
|
||||||
|
@ -306,7 +394,22 @@ impl<E> DisplayItem<E> {
|
||||||
ImageDisplayItemClass(ref image_item) => {
|
ImageDisplayItemClass(ref image_item) => {
|
||||||
debug!("Drawing image at {:?}.", image_item.base.bounds);
|
debug!("Drawing image at {:?}.", image_item.base.bounds);
|
||||||
|
|
||||||
render_context.draw_image(image_item.base.bounds, image_item.image.clone())
|
let mut y_offset = Au(0);
|
||||||
|
while y_offset < image_item.base.bounds.size.height {
|
||||||
|
let mut x_offset = Au(0);
|
||||||
|
while x_offset < image_item.base.bounds.size.width {
|
||||||
|
let mut bounds = image_item.base.bounds;
|
||||||
|
bounds.origin.x = bounds.origin.x + x_offset;
|
||||||
|
bounds.origin.y = bounds.origin.y + y_offset;
|
||||||
|
bounds.size = image_item.stretch_size;
|
||||||
|
|
||||||
|
render_context.draw_image(bounds, image_item.image.clone());
|
||||||
|
|
||||||
|
x_offset = x_offset + image_item.stretch_size.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
y_offset = y_offset + image_item.stretch_size.height;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BorderDisplayItemClass(ref border) => {
|
BorderDisplayItemClass(ref border) => {
|
||||||
|
@ -324,7 +427,7 @@ impl<E> DisplayItem<E> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn base<'a>(&'a self) -> &'a BaseDisplayItem<E> {
|
pub fn base<'a>(&'a self) -> &'a BaseDisplayItem {
|
||||||
// FIXME(tkuehn): Workaround for Rust region bug.
|
// FIXME(tkuehn): Workaround for Rust region bug.
|
||||||
unsafe {
|
unsafe {
|
||||||
match *self {
|
match *self {
|
||||||
|
@ -342,7 +445,7 @@ impl<E> DisplayItem<E> {
|
||||||
self.base().bounds
|
self.base().bounds
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn children<'a>(&'a self) -> DisplayItemIterator<'a,E> {
|
pub fn children<'a>(&'a self) -> DisplayItemIterator<'a> {
|
||||||
match *self {
|
match *self {
|
||||||
ClipDisplayItemClass(ref clip) => ParentDisplayItemIterator(clip.child_list.iter()),
|
ClipDisplayItemClass(ref clip) => ParentDisplayItemIterator(clip.child_list.iter()),
|
||||||
SolidColorDisplayItemClass(..) |
|
SolidColorDisplayItemClass(..) |
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
use font_context::FontContext;
|
use font_context::FontContext;
|
||||||
use style::computed_values::border_style;
|
use style::computed_values::border_style;
|
||||||
|
|
||||||
use azure::azure_hl::{B8G8R8A8, Color, ColorPattern, DrawOptions};
|
use azure::azure_hl::{B8G8R8A8, Color, ColorPattern, DrawOptions, DrawSurfaceOptions, DrawTarget};
|
||||||
use azure::azure_hl::{DrawSurfaceOptions, DrawTarget, Linear, StrokeOptions};
|
use azure::azure_hl::{Linear, SourceOp, StrokeOptions};
|
||||||
use azure::AZ_CAP_BUTT;
|
use azure::AZ_CAP_BUTT;
|
||||||
use azure::AzFloat;
|
use azure::AzFloat;
|
||||||
use geom::point::Point2D;
|
use geom::point::Point2D;
|
||||||
|
@ -45,7 +45,7 @@ impl<'a> RenderContext<'a> {
|
||||||
|
|
||||||
pub fn draw_solid_color(&self, bounds: &Rect<Au>, color: Color) {
|
pub fn draw_solid_color(&self, bounds: &Rect<Au>, color: Color) {
|
||||||
self.draw_target.make_current();
|
self.draw_target.make_current();
|
||||||
self.draw_target.fill_rect(&bounds.to_azure_rect(), &ColorPattern(color));
|
self.draw_target.fill_rect(&bounds.to_azure_rect(), &ColorPattern(color), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_border(&self,
|
pub fn draw_border(&self,
|
||||||
|
@ -121,13 +121,15 @@ impl<'a> RenderContext<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear(&self) {
|
pub fn clear(&self) {
|
||||||
let pattern = ColorPattern(Color(1.0, 1.0, 1.0, 1.0));
|
let pattern = ColorPattern(Color(0.0, 0.0, 0.0, 0.0));
|
||||||
let rect = Rect(Point2D(self.page_rect.origin.x as AzFloat,
|
let rect = Rect(Point2D(self.page_rect.origin.x as AzFloat,
|
||||||
self.page_rect.origin.y as AzFloat),
|
self.page_rect.origin.y as AzFloat),
|
||||||
Size2D(self.screen_rect.size.width as AzFloat,
|
Size2D(self.screen_rect.size.width as AzFloat,
|
||||||
self.screen_rect.size.height as AzFloat));
|
self.screen_rect.size.height as AzFloat));
|
||||||
|
let mut draw_options = DrawOptions(1.0, 0);
|
||||||
|
draw_options.set_composition_op(SourceOp);
|
||||||
self.draw_target.make_current();
|
self.draw_target.make_current();
|
||||||
self.draw_target.fill_rect(&rect, &pattern);
|
self.draw_target.fill_rect(&rect, &pattern, Some(&draw_options));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_border_segment(&self, direction: Direction, bounds: &Rect<Au>, border: SideOffsets2D<f32>, color: SideOffsets2D<Color>, style: SideOffsets2D<border_style::T>) {
|
fn draw_border_segment(&self, direction: Direction, bounds: &Rect<Au>, border: SideOffsets2D<f32>, color: SideOffsets2D<Color>, style: SideOffsets2D<border_style::T>) {
|
||||||
|
|
|
@ -2,7 +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/. */
|
||||||
|
|
||||||
// The task that handles all rendering/painting.
|
//! The task that handles all rendering/painting.
|
||||||
|
|
||||||
|
use buffer_map::BufferMap;
|
||||||
|
use display_list::DisplayList;
|
||||||
|
use font_context::{FontContext, FontContextInfo};
|
||||||
|
use render_context::RenderContext;
|
||||||
|
|
||||||
use azure::azure_hl::{B8G8R8A8, Color, DrawTarget, StolenGLResources};
|
use azure::azure_hl::{B8G8R8A8, Color, DrawTarget, StolenGLResources};
|
||||||
use azure::AzFloat;
|
use azure::AzFloat;
|
||||||
|
@ -12,12 +17,14 @@ use geom::size::Size2D;
|
||||||
use layers::platform::surface::{NativePaintingGraphicsContext, NativeSurface};
|
use layers::platform::surface::{NativePaintingGraphicsContext, NativeSurface};
|
||||||
use layers::platform::surface::{NativeSurfaceMethods};
|
use layers::platform::surface::{NativeSurfaceMethods};
|
||||||
use layers;
|
use layers;
|
||||||
use servo_msg::compositor_msg::{Epoch, IdleRenderState, LayerBuffer, LayerBufferSet};
|
use servo_msg::compositor_msg::{Epoch, IdleRenderState, LayerBuffer};
|
||||||
use servo_msg::compositor_msg::{RenderListener, RenderingRenderState};
|
use servo_msg::compositor_msg::{LayerBufferSet, LayerId, LayerMetadata, RenderListener};
|
||||||
|
use servo_msg::compositor_msg::{RenderingRenderState, ScrollPolicy};
|
||||||
use servo_msg::constellation_msg::{ConstellationChan, PipelineId, RendererReadyMsg};
|
use servo_msg::constellation_msg::{ConstellationChan, PipelineId, RendererReadyMsg};
|
||||||
use servo_msg::constellation_msg::{Failure, FailureMsg};
|
use servo_msg::constellation_msg::{Failure, FailureMsg};
|
||||||
use servo_msg::platform::surface::NativeSurfaceAzureMethods;
|
use servo_msg::platform::surface::NativeSurfaceAzureMethods;
|
||||||
use servo_util::opts::Opts;
|
use servo_util::opts::Opts;
|
||||||
|
use servo_util::smallvec::{SmallVec, SmallVec1};
|
||||||
use servo_util::time::{ProfilerChan, profile};
|
use servo_util::time::{ProfilerChan, profile};
|
||||||
use servo_util::time;
|
use servo_util::time;
|
||||||
use servo_util::task::send_on_failure;
|
use servo_util::task::send_on_failure;
|
||||||
|
@ -26,20 +33,23 @@ use std::comm::{Chan, Port};
|
||||||
use std::task;
|
use std::task;
|
||||||
use sync::Arc;
|
use sync::Arc;
|
||||||
|
|
||||||
use buffer_map::BufferMap;
|
/// Information about a layer that layout sends to the painting task.
|
||||||
use display_list::DisplayListCollection;
|
pub struct RenderLayer {
|
||||||
use font_context::{FontContext, FontContextInfo};
|
/// A per-pipeline ID describing this layer that should be stable across reflows.
|
||||||
use render_context::RenderContext;
|
id: LayerId,
|
||||||
|
/// The display list describing the contents of this layer.
|
||||||
pub struct RenderLayer<T> {
|
display_list: Arc<DisplayList>,
|
||||||
display_list_collection: Arc<DisplayListCollection<T>>,
|
/// The position of the layer in pixels.
|
||||||
size: Size2D<uint>,
|
rect: Rect<uint>,
|
||||||
color: Color
|
/// The color of the background in this layer. Used for unrendered content.
|
||||||
|
color: Color,
|
||||||
|
/// The scrolling policy of this layer.
|
||||||
|
scroll_policy: ScrollPolicy,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Msg<T> {
|
pub enum Msg {
|
||||||
RenderMsg(RenderLayer<T>),
|
RenderMsg(SmallVec1<RenderLayer>),
|
||||||
ReRenderMsg(~[BufferRequest], f32, Epoch),
|
ReRenderMsg(~[BufferRequest], f32, LayerId, Epoch),
|
||||||
UnusedBufferMsg(~[~LayerBuffer]),
|
UnusedBufferMsg(~[~LayerBuffer]),
|
||||||
PaintPermissionGranted,
|
PaintPermissionGranted,
|
||||||
PaintPermissionRevoked,
|
PaintPermissionRevoked,
|
||||||
|
@ -63,22 +73,21 @@ pub fn BufferRequest(screen_rect: Rect<uint>, page_rect: Rect<f32>) -> BufferReq
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME(rust#9155): this should be a newtype struct, but
|
// FIXME(pcwalton): This should be a newtype struct.
|
||||||
// generic newtypes ICE when compiled cross-crate
|
pub struct RenderChan {
|
||||||
pub struct RenderChan<T> {
|
chan: Chan<Msg>,
|
||||||
chan: Chan<Msg<T>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Send> Clone for RenderChan<T> {
|
impl Clone for RenderChan {
|
||||||
fn clone(&self) -> RenderChan<T> {
|
fn clone(&self) -> RenderChan {
|
||||||
RenderChan {
|
RenderChan {
|
||||||
chan: self.chan.clone(),
|
chan: self.chan.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Send> RenderChan<T> {
|
impl RenderChan {
|
||||||
pub fn new() -> (Port<Msg<T>>, RenderChan<T>) {
|
pub fn new() -> (Port<Msg>, RenderChan) {
|
||||||
let (port, chan) = Chan::new();
|
let (port, chan) = Chan::new();
|
||||||
let render_chan = RenderChan {
|
let render_chan = RenderChan {
|
||||||
chan: chan,
|
chan: chan,
|
||||||
|
@ -86,11 +95,11 @@ impl<T: Send> RenderChan<T> {
|
||||||
(port, render_chan)
|
(port, render_chan)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send(&self, msg: Msg<T>) {
|
pub fn send(&self, msg: Msg) {
|
||||||
assert!(self.try_send(msg), "RenderChan.send: render port closed")
|
assert!(self.try_send(msg), "RenderChan.send: render port closed")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn try_send(&self, msg: Msg<T>) -> bool {
|
pub fn try_send(&self, msg: Msg) -> bool {
|
||||||
self.chan.try_send(msg)
|
self.chan.try_send(msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,9 +111,9 @@ enum GraphicsContext {
|
||||||
GpuGraphicsContext,
|
GpuGraphicsContext,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RenderTask<C,T> {
|
pub struct RenderTask<C> {
|
||||||
id: PipelineId,
|
id: PipelineId,
|
||||||
port: Port<Msg<T>>,
|
port: Port<Msg>,
|
||||||
compositor: C,
|
compositor: C,
|
||||||
constellation_chan: ConstellationChan,
|
constellation_chan: ConstellationChan,
|
||||||
font_ctx: ~FontContext,
|
font_ctx: ~FontContext,
|
||||||
|
@ -119,8 +128,8 @@ pub struct RenderTask<C,T> {
|
||||||
/// The native graphics context.
|
/// The native graphics context.
|
||||||
native_graphics_context: Option<NativePaintingGraphicsContext>,
|
native_graphics_context: Option<NativePaintingGraphicsContext>,
|
||||||
|
|
||||||
/// The layer to be rendered
|
/// The layers to be rendered.
|
||||||
render_layer: Option<RenderLayer<T>>,
|
render_layers: SmallVec1<RenderLayer>,
|
||||||
|
|
||||||
/// Permission to send paint messages to the compositor
|
/// Permission to send paint messages to the compositor
|
||||||
paint_permission: bool,
|
paint_permission: bool,
|
||||||
|
@ -140,9 +149,25 @@ macro_rules! native_graphics_context(
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
|
fn initialize_layers<C:RenderListener>(
|
||||||
|
compositor: &mut C,
|
||||||
|
pipeline_id: PipelineId,
|
||||||
|
epoch: Epoch,
|
||||||
|
render_layers: &[RenderLayer]) {
|
||||||
|
let metadata = render_layers.iter().map(|render_layer| {
|
||||||
|
LayerMetadata {
|
||||||
|
id: render_layer.id,
|
||||||
|
rect: render_layer.rect,
|
||||||
|
color: render_layer.color,
|
||||||
|
scroll_policy: render_layer.scroll_policy,
|
||||||
|
}
|
||||||
|
}).collect();
|
||||||
|
compositor.initialize_layers_for_pipeline(pipeline_id, metadata, epoch);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: RenderListener + Send> RenderTask<C> {
|
||||||
pub fn create(id: PipelineId,
|
pub fn create(id: PipelineId,
|
||||||
port: Port<Msg<T>>,
|
port: Port<Msg>,
|
||||||
compositor: C,
|
compositor: C,
|
||||||
constellation_chan: ConstellationChan,
|
constellation_chan: ConstellationChan,
|
||||||
failure_msg: Failure,
|
failure_msg: Failure,
|
||||||
|
@ -181,7 +206,7 @@ impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
|
||||||
|
|
||||||
native_graphics_context: native_graphics_context,
|
native_graphics_context: native_graphics_context,
|
||||||
|
|
||||||
render_layer: None,
|
render_layers: SmallVec1::new(),
|
||||||
|
|
||||||
paint_permission: false,
|
paint_permission: false,
|
||||||
epoch: Epoch(0),
|
epoch: Epoch(0),
|
||||||
|
@ -207,20 +232,25 @@ impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
match self.port.recv() {
|
match self.port.recv() {
|
||||||
RenderMsg(render_layer) => {
|
RenderMsg(render_layers) => {
|
||||||
if self.paint_permission {
|
self.epoch.next();
|
||||||
self.epoch.next();
|
self.render_layers = render_layers;
|
||||||
self.compositor.set_layer_page_size_and_color(self.id, render_layer.size, self.epoch, render_layer.color);
|
|
||||||
} else {
|
if !self.paint_permission {
|
||||||
debug!("render_task: render ready msg");
|
debug!("render_task: render ready msg");
|
||||||
let ConstellationChan(ref mut c) = self.constellation_chan;
|
let ConstellationChan(ref mut c) = self.constellation_chan;
|
||||||
c.send(RendererReadyMsg(self.id));
|
c.send(RendererReadyMsg(self.id));
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
self.render_layer = Some(render_layer);
|
|
||||||
|
initialize_layers(&mut self.compositor,
|
||||||
|
self.id,
|
||||||
|
self.epoch,
|
||||||
|
self.render_layers.as_slice());
|
||||||
}
|
}
|
||||||
ReRenderMsg(tiles, scale, epoch) => {
|
ReRenderMsg(tiles, scale, layer_id, epoch) => {
|
||||||
if self.epoch == epoch {
|
if self.epoch == epoch {
|
||||||
self.render(tiles, scale);
|
self.render(tiles, scale, layer_id);
|
||||||
} else {
|
} else {
|
||||||
debug!("renderer epoch mismatch: {:?} != {:?}", self.epoch, epoch);
|
debug!("renderer epoch mismatch: {:?} != {:?}", self.epoch, epoch);
|
||||||
}
|
}
|
||||||
|
@ -233,12 +263,16 @@ impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
|
||||||
}
|
}
|
||||||
PaintPermissionGranted => {
|
PaintPermissionGranted => {
|
||||||
self.paint_permission = true;
|
self.paint_permission = true;
|
||||||
match self.render_layer {
|
|
||||||
Some(ref render_layer) => {
|
// Here we assume that the main layer—the layer responsible for the page size—
|
||||||
self.epoch.next();
|
// is the first layer. This is a pretty fragile assumption. It will be fixed
|
||||||
self.compositor.set_layer_page_size_and_color(self.id, render_layer.size, self.epoch, render_layer.color);
|
// once we use the layers-based scrolling infrastructure for all scrolling.
|
||||||
}
|
if self.render_layers.len() > 1 {
|
||||||
None => {}
|
self.epoch.next();
|
||||||
|
initialize_layers(&mut self.compositor,
|
||||||
|
self.id,
|
||||||
|
self.epoch,
|
||||||
|
self.render_layers.as_slice());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PaintPermissionRevoked => {
|
PaintPermissionRevoked => {
|
||||||
|
@ -253,138 +287,151 @@ impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(&mut self, tiles: ~[BufferRequest], scale: f32) {
|
/// Renders one layer and sends the tiles back to the layer.
|
||||||
if self.render_layer.is_none() {
|
///
|
||||||
return
|
/// FIXME(pcwalton): We will probably want to eventually send all layers belonging to a page in
|
||||||
}
|
/// one transaction, to avoid the user seeing inconsistent states.
|
||||||
|
fn render(&mut self, tiles: ~[BufferRequest], scale: f32, layer_id: LayerId) {
|
||||||
self.compositor.set_render_state(RenderingRenderState);
|
|
||||||
time::profile(time::RenderingCategory, self.profiler_chan.clone(), || {
|
time::profile(time::RenderingCategory, self.profiler_chan.clone(), || {
|
||||||
// FIXME: Try not to create a new array here.
|
// FIXME: Try not to create a new array here.
|
||||||
let mut new_buffers = ~[];
|
let mut new_buffers = ~[];
|
||||||
|
|
||||||
// Divide up the layer into tiles.
|
// Find the appropriate render layer.
|
||||||
time::profile(time::RenderingPrepBuffCategory, self.profiler_chan.clone(), || {
|
let mut render_layer = None;
|
||||||
for tile in tiles.iter() {
|
for layer in self.render_layers.iter() {
|
||||||
let width = tile.screen_rect.size.width;
|
if layer.id == layer_id {
|
||||||
let height = tile.screen_rect.size.height;
|
render_layer = Some(layer);
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let render_layer = match render_layer {
|
||||||
|
Some(render_layer) => render_layer,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
|
||||||
let size = Size2D(width as i32, height as i32);
|
self.compositor.set_render_state(RenderingRenderState);
|
||||||
let draw_target = match self.graphics_context {
|
|
||||||
CpuGraphicsContext => {
|
// Divide up the layer into tiles.
|
||||||
DrawTarget::new(self.opts.render_backend, size, B8G8R8A8)
|
for tile in tiles.iter() {
|
||||||
}
|
let width = tile.screen_rect.size.width;
|
||||||
GpuGraphicsContext => {
|
let height = tile.screen_rect.size.height;
|
||||||
// FIXME(pcwalton): Cache the components of draw targets
|
|
||||||
// (texture color buffer, renderbuffers) instead of recreating them.
|
let size = Size2D(width as i32, height as i32);
|
||||||
let draw_target =
|
let draw_target = match self.graphics_context {
|
||||||
DrawTarget::new_with_fbo(self.opts.render_backend,
|
CpuGraphicsContext => {
|
||||||
native_graphics_context!(self),
|
DrawTarget::new(self.opts.render_backend, size, B8G8R8A8)
|
||||||
size,
|
}
|
||||||
B8G8R8A8);
|
GpuGraphicsContext => {
|
||||||
draw_target.make_current();
|
// FIXME(pcwalton): Cache the components of draw targets
|
||||||
draw_target
|
// (texture color buffer, renderbuffers) instead of recreating them.
|
||||||
}
|
let draw_target =
|
||||||
|
DrawTarget::new_with_fbo(self.opts.render_backend,
|
||||||
|
native_graphics_context!(self),
|
||||||
|
size,
|
||||||
|
B8G8R8A8);
|
||||||
|
draw_target.make_current();
|
||||||
|
draw_target
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
// Build the render context.
|
||||||
|
let mut ctx = RenderContext {
|
||||||
|
draw_target: &draw_target,
|
||||||
|
font_ctx: &mut self.font_ctx,
|
||||||
|
opts: &self.opts,
|
||||||
|
page_rect: tile.page_rect,
|
||||||
|
screen_rect: tile.screen_rect,
|
||||||
};
|
};
|
||||||
|
|
||||||
{
|
// Apply the translation to render the tile we want.
|
||||||
// Build the render context.
|
let matrix: Matrix2D<AzFloat> = Matrix2D::identity();
|
||||||
let mut ctx = RenderContext {
|
let matrix = matrix.scale(scale as AzFloat, scale as AzFloat);
|
||||||
draw_target: &draw_target,
|
let matrix = matrix.translate(-(tile.page_rect.origin.x) as AzFloat,
|
||||||
font_ctx: &mut self.font_ctx,
|
-(tile.page_rect.origin.y) as AzFloat);
|
||||||
opts: &self.opts,
|
let matrix = matrix.translate(-(render_layer.rect.origin.x as AzFloat),
|
||||||
page_rect: tile.page_rect,
|
-(render_layer.rect.origin.y as AzFloat));
|
||||||
screen_rect: tile.screen_rect,
|
|
||||||
|
ctx.draw_target.set_transform(&matrix);
|
||||||
|
|
||||||
|
// Clear the buffer.
|
||||||
|
ctx.clear();
|
||||||
|
|
||||||
|
// Draw the display list.
|
||||||
|
profile(time::RenderingDrawingCategory, self.profiler_chan.clone(), || {
|
||||||
|
render_layer.display_list.get().draw_into_context(&mut ctx);
|
||||||
|
ctx.draw_target.flush();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract the texture from the draw target and place it into its slot in the
|
||||||
|
// buffer. If using CPU rendering, upload it first.
|
||||||
|
//
|
||||||
|
// FIXME(pcwalton): We should supply the texture and native surface *to* the
|
||||||
|
// draw target in GPU rendering mode, so that it doesn't have to recreate it.
|
||||||
|
let buffer = match self.graphics_context {
|
||||||
|
CpuGraphicsContext => {
|
||||||
|
let buffer = match self.buffer_map.find(tile.screen_rect.size) {
|
||||||
|
Some(buffer) => {
|
||||||
|
let mut buffer = buffer;
|
||||||
|
buffer.rect = tile.page_rect;
|
||||||
|
buffer.screen_pos = tile.screen_rect;
|
||||||
|
buffer.resolution = scale;
|
||||||
|
buffer.native_surface.mark_wont_leak();
|
||||||
|
buffer
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
// Create an empty native surface. We mark it as not leaking
|
||||||
|
// in case it dies in transit to the compositor task.
|
||||||
|
let mut native_surface: NativeSurface =
|
||||||
|
layers::platform::surface::NativeSurfaceMethods::new(
|
||||||
|
native_graphics_context!(self),
|
||||||
|
Size2D(width as i32, height as i32),
|
||||||
|
width as i32 * 4);
|
||||||
|
native_surface.mark_wont_leak();
|
||||||
|
|
||||||
|
~LayerBuffer {
|
||||||
|
native_surface: native_surface,
|
||||||
|
rect: tile.page_rect,
|
||||||
|
screen_pos: tile.screen_rect,
|
||||||
|
resolution: scale,
|
||||||
|
stride: (width * 4) as uint
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Apply the translation to render the tile we want.
|
draw_target.snapshot().get_data_surface().with_data(|data| {
|
||||||
let matrix: Matrix2D<AzFloat> = Matrix2D::identity();
|
buffer.native_surface.upload(native_graphics_context!(self), data);
|
||||||
let matrix = matrix.scale(scale as AzFloat, scale as AzFloat);
|
debug!("RENDERER uploading to native surface {:d}",
|
||||||
let matrix = matrix.translate(-(tile.page_rect.origin.x) as AzFloat,
|
buffer.native_surface.get_id() as int);
|
||||||
-(tile.page_rect.origin.y) as AzFloat);
|
|
||||||
|
|
||||||
ctx.draw_target.set_transform(&matrix);
|
|
||||||
|
|
||||||
// Clear the buffer.
|
|
||||||
ctx.clear();
|
|
||||||
|
|
||||||
// Draw the display list.
|
|
||||||
profile(time::RenderingDrawingCategory, self.profiler_chan.clone(), || {
|
|
||||||
let render_layer = self.render_layer.as_ref().unwrap();
|
|
||||||
render_layer.display_list_collection.get().draw_lists_into_context(&mut ctx);
|
|
||||||
ctx.draw_target.flush();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
buffer
|
||||||
}
|
}
|
||||||
|
GpuGraphicsContext => {
|
||||||
|
draw_target.make_current();
|
||||||
|
let StolenGLResources {
|
||||||
|
surface: native_surface
|
||||||
|
} = draw_target.steal_gl_resources().unwrap();
|
||||||
|
|
||||||
// Extract the texture from the draw target and place it into its slot in the
|
// We mark the native surface as not leaking in case the surfaces
|
||||||
// buffer. If using CPU rendering, upload it first.
|
// die on their way to the compositor task.
|
||||||
//
|
let mut native_surface: NativeSurface =
|
||||||
// FIXME(pcwalton): We should supply the texture and native surface *to* the
|
NativeSurfaceAzureMethods::from_azure_surface(native_surface);
|
||||||
// draw target in GPU rendering mode, so that it doesn't have to recreate it.
|
native_surface.mark_wont_leak();
|
||||||
let buffer = match self.graphics_context {
|
|
||||||
CpuGraphicsContext => {
|
|
||||||
let buffer = match self.buffer_map.find(tile.screen_rect.size) {
|
|
||||||
Some(buffer) => {
|
|
||||||
let mut buffer = buffer;
|
|
||||||
buffer.rect = tile.page_rect;
|
|
||||||
buffer.screen_pos = tile.screen_rect;
|
|
||||||
buffer.resolution = scale;
|
|
||||||
buffer.native_surface.mark_wont_leak();
|
|
||||||
buffer
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
// Create an empty native surface. We mark it as not leaking
|
|
||||||
// in case it dies in transit to the compositor task.
|
|
||||||
let mut native_surface: NativeSurface =
|
|
||||||
layers::platform::surface::NativeSurfaceMethods::new(
|
|
||||||
native_graphics_context!(self),
|
|
||||||
Size2D(width as i32, height as i32),
|
|
||||||
width as i32 * 4);
|
|
||||||
native_surface.mark_wont_leak();
|
|
||||||
|
|
||||||
~LayerBuffer {
|
~LayerBuffer {
|
||||||
native_surface: native_surface,
|
native_surface: native_surface,
|
||||||
rect: tile.page_rect,
|
rect: tile.page_rect,
|
||||||
screen_pos: tile.screen_rect,
|
screen_pos: tile.screen_rect,
|
||||||
resolution: scale,
|
resolution: scale,
|
||||||
stride: (width * 4) as uint
|
stride: (width * 4) as uint
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
draw_target.snapshot().get_data_surface().with_data(|data| {
|
|
||||||
buffer.native_surface.upload(native_graphics_context!(self), data);
|
|
||||||
debug!("RENDERER uploading to native surface {:d}",
|
|
||||||
buffer.native_surface.get_id() as int);
|
|
||||||
});
|
|
||||||
|
|
||||||
buffer
|
|
||||||
}
|
}
|
||||||
GpuGraphicsContext => {
|
}
|
||||||
draw_target.make_current();
|
};
|
||||||
let StolenGLResources {
|
|
||||||
surface: native_surface
|
new_buffers.push(buffer);
|
||||||
} = draw_target.steal_gl_resources().unwrap();
|
}
|
||||||
|
|
||||||
// We mark the native surface as not leaking in case the surfaces
|
|
||||||
// die on their way to the compositor task.
|
|
||||||
let mut native_surface: NativeSurface =
|
|
||||||
NativeSurfaceAzureMethods::from_azure_surface(native_surface);
|
|
||||||
native_surface.mark_wont_leak();
|
|
||||||
|
|
||||||
~LayerBuffer {
|
|
||||||
native_surface: native_surface,
|
|
||||||
rect: tile.page_rect,
|
|
||||||
screen_pos: tile.screen_rect,
|
|
||||||
resolution: scale,
|
|
||||||
stride: (width * 4) as uint
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
new_buffers.push(buffer);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let layer_buffer_set = ~LayerBufferSet {
|
let layer_buffer_set = ~LayerBufferSet {
|
||||||
buffers: new_buffers,
|
buffers: new_buffers,
|
||||||
|
@ -392,7 +439,7 @@ impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
|
||||||
|
|
||||||
debug!("render_task: returning surface");
|
debug!("render_task: returning surface");
|
||||||
if self.paint_permission {
|
if self.paint_permission {
|
||||||
self.compositor.paint(self.id, layer_buffer_set, self.epoch);
|
self.compositor.paint(self.id, render_layer.id, layer_buffer_set, self.epoch);
|
||||||
} else {
|
} else {
|
||||||
debug!("render_task: RendererReadyMsg send");
|
debug!("render_task: RendererReadyMsg send");
|
||||||
let ConstellationChan(ref mut c) = self.constellation_chan;
|
let ConstellationChan(ref mut c) = self.constellation_chan;
|
||||||
|
|
|
@ -5,16 +5,13 @@
|
||||||
use constellation::SendableFrameTree;
|
use constellation::SendableFrameTree;
|
||||||
use compositing::compositor_layer::CompositorLayer;
|
use compositing::compositor_layer::CompositorLayer;
|
||||||
use compositing::*;
|
use compositing::*;
|
||||||
|
use pipeline::CompositionPipeline;
|
||||||
use platform::{Application, Window};
|
use platform::{Application, Window};
|
||||||
|
use windowing::{FinishedWindowEvent, IdleWindowEvent, LoadUrlWindowEvent, MouseWindowClickEvent};
|
||||||
use windowing::{WindowEvent, WindowMethods,
|
use windowing::{MouseWindowEvent, MouseWindowEventClass, MouseWindowMouseDownEvent};
|
||||||
WindowNavigateMsg,
|
use windowing::{MouseWindowMouseUpEvent, MouseWindowMoveEventClass, NavigationWindowEvent};
|
||||||
IdleWindowEvent, RefreshWindowEvent, ResizeWindowEvent, LoadUrlWindowEvent,
|
use windowing::{QuitWindowEvent, RefreshWindowEvent, ResizeWindowEvent, ScrollWindowEvent};
|
||||||
MouseWindowEventClass, MouseWindowMoveEventClass,ScrollWindowEvent, ZoomWindowEvent, NavigationWindowEvent,
|
use windowing::{WindowEvent, WindowMethods, WindowNavigateMsg, ZoomWindowEvent};
|
||||||
FinishedWindowEvent, QuitWindowEvent,
|
|
||||||
MouseWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEvent, MouseWindowMouseUpEvent};
|
|
||||||
|
|
||||||
|
|
||||||
use azure::azure_hl::{SourceSurfaceMethods, Color};
|
use azure::azure_hl::{SourceSurfaceMethods, Color};
|
||||||
use azure::azure_hl;
|
use azure::azure_hl;
|
||||||
|
@ -29,8 +26,10 @@ use layers::rendergl::RenderContext;
|
||||||
use layers::scene::Scene;
|
use layers::scene::Scene;
|
||||||
use opengles::gl2;
|
use opengles::gl2;
|
||||||
use png;
|
use png;
|
||||||
use servo_msg::compositor_msg::{Blank, Epoch, FinishedLoading, IdleRenderState, LayerBufferSet, ReadyState, RenderState};
|
use servo_msg::compositor_msg::{Blank, Epoch, FinishedLoading, IdleRenderState, LayerBufferSet};
|
||||||
use servo_msg::constellation_msg::{ConstellationChan, ExitMsg, NavigateMsg, ResizedWindowMsg, LoadUrlMsg, PipelineId};
|
use servo_msg::compositor_msg::{LayerId, ReadyState, RenderState, ScrollPolicy, Scrollable};
|
||||||
|
use servo_msg::constellation_msg::{ConstellationChan, ExitMsg, LoadUrlMsg, NavigateMsg};
|
||||||
|
use servo_msg::constellation_msg::{PipelineId, ResizedWindowMsg};
|
||||||
use servo_msg::constellation_msg;
|
use servo_msg::constellation_msg;
|
||||||
use servo_util::opts::Opts;
|
use servo_util::opts::Opts;
|
||||||
use servo_util::time::{profile, ProfilerChan, Timer};
|
use servo_util::time::{profile, ProfilerChan, Timer};
|
||||||
|
@ -55,6 +54,9 @@ pub struct IOCompositor {
|
||||||
/// The root ContainerLayer.
|
/// The root ContainerLayer.
|
||||||
root_layer: Rc<ContainerLayer>,
|
root_layer: Rc<ContainerLayer>,
|
||||||
|
|
||||||
|
/// The root pipeline.
|
||||||
|
root_pipeline: Option<CompositionPipeline>,
|
||||||
|
|
||||||
/// The canvas to paint a page.
|
/// The canvas to paint a page.
|
||||||
scene: Scene,
|
scene: Scene,
|
||||||
|
|
||||||
|
@ -110,7 +112,6 @@ pub struct IOCompositor {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IOCompositor {
|
impl IOCompositor {
|
||||||
|
|
||||||
pub fn new(app: &Application,
|
pub fn new(app: &Application,
|
||||||
opts: Opts,
|
opts: Opts,
|
||||||
port: Port<Msg>,
|
port: Port<Msg>,
|
||||||
|
@ -131,6 +132,7 @@ impl IOCompositor {
|
||||||
opts: opts,
|
opts: opts,
|
||||||
context: rendergl::init_render_context(),
|
context: rendergl::init_render_context(),
|
||||||
root_layer: root_layer.clone(),
|
root_layer: root_layer.clone(),
|
||||||
|
root_pipeline: None,
|
||||||
scene: Scene(ContainerLayerKind(root_layer), window_size, identity()),
|
scene: Scene(ContainerLayerKind(root_layer), window_size, identity()),
|
||||||
window_size: Size2D(window_size.width as uint, window_size.height as uint),
|
window_size: Size2D(window_size.width as uint, window_size.height as uint),
|
||||||
graphics_context: CompositorTask::create_graphics_context(),
|
graphics_context: CompositorTask::create_graphics_context(),
|
||||||
|
@ -253,11 +255,10 @@ impl IOCompositor {
|
||||||
self.change_render_state(render_state);
|
self.change_render_state(render_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
(Data(SetUnRenderedColor(_id, color)), false) => {
|
(Data(SetUnRenderedColor(pipeline_id, layer_id, color)), false) => {
|
||||||
self.set_unrendered_color(_id, color);
|
self.set_unrendered_color(pipeline_id, layer_id, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
(Data(SetIds(frame_tree, response_chan, new_constellation_chan)), _) => {
|
(Data(SetIds(frame_tree, response_chan, new_constellation_chan)), _) => {
|
||||||
self.set_ids(frame_tree, response_chan, new_constellation_chan);
|
self.set_ids(frame_tree, response_chan, new_constellation_chan);
|
||||||
}
|
}
|
||||||
|
@ -266,32 +267,44 @@ impl IOCompositor {
|
||||||
chan.send(Some(azure_hl::current_graphics_metadata()));
|
chan.send(Some(azure_hl::current_graphics_metadata()));
|
||||||
}
|
}
|
||||||
|
|
||||||
(Data(NewLayer(_id, new_size)), false) => {
|
(Data(CreateRootCompositorLayerIfNecessary(pipeline_id, layer_id, size)),
|
||||||
self.create_new_layer(_id, new_size);
|
false) => {
|
||||||
|
self.create_root_compositor_layer_if_necessary(pipeline_id, layer_id, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
(Data(SetLayerPageSize(id, new_size, epoch)), false) => {
|
(Data(CreateDescendantCompositorLayerIfNecessary(pipeline_id,
|
||||||
self.set_layer_page_size(id, new_size, epoch);
|
layer_id,
|
||||||
|
rect,
|
||||||
|
scroll_behavior)),
|
||||||
|
false) => {
|
||||||
|
self.create_descendant_compositor_layer_if_necessary(pipeline_id,
|
||||||
|
layer_id,
|
||||||
|
rect,
|
||||||
|
scroll_behavior);
|
||||||
}
|
}
|
||||||
|
|
||||||
(Data(SetLayerClipRect(id, new_rect)), false) => {
|
(Data(SetLayerPageSize(pipeline_id, layer_id, new_size, epoch)), false) => {
|
||||||
self.set_layer_clip_rect(id, new_rect);
|
self.set_layer_page_size(pipeline_id, layer_id, new_size, epoch);
|
||||||
}
|
}
|
||||||
|
|
||||||
(Data(DeleteLayer(id)), _) => {
|
(Data(SetLayerClipRect(pipeline_id, layer_id, new_rect)), false) => {
|
||||||
|
self.set_layer_clip_rect(pipeline_id, layer_id, new_rect);
|
||||||
|
}
|
||||||
|
|
||||||
|
(Data(DeleteLayerGroup(id)), _) => {
|
||||||
self.delete_layer(id);
|
self.delete_layer(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
(Data(Paint(id, new_layer_buffer_set, epoch)), false) => {
|
(Data(Paint(pipeline_id, layer_id, new_layer_buffer_set, epoch)), false) => {
|
||||||
self.paint(id, new_layer_buffer_set, epoch);
|
self.paint(pipeline_id, layer_id, new_layer_buffer_set, epoch);
|
||||||
}
|
}
|
||||||
|
|
||||||
(Data(InvalidateRect(id, rect)), false) => {
|
(Data(InvalidateRect(pipeline_id, layer_id, rect)), false) => {
|
||||||
self.invalidate_rect(id, rect);
|
self.invalidate_rect(pipeline_id, layer_id, rect);
|
||||||
}
|
}
|
||||||
|
|
||||||
(Data(ScrollFragmentPoint(id, point)), false) => {
|
(Data(ScrollFragmentPoint(pipeline_id, layer_id, point)), false) => {
|
||||||
self.scroll_fragment_to_point(id, point);
|
self.scroll_fragment_to_point(pipeline_id, layer_id, point);
|
||||||
}
|
}
|
||||||
|
|
||||||
(Data(LoadComplete(..)), false) => {
|
(Data(LoadComplete(..)), false) => {
|
||||||
|
@ -313,11 +326,10 @@ impl IOCompositor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_unrendered_color(&mut self, _id: PipelineId, color: Color) {
|
// FIXME(pcwalton): Take the pipeline ID and layer ID into account.
|
||||||
|
fn set_unrendered_color(&mut self, _: PipelineId, _: LayerId, color: Color) {
|
||||||
match self.compositor_layer {
|
match self.compositor_layer {
|
||||||
Some(ref mut layer) => {
|
Some(ref mut layer) => layer.unrendered_color = color,
|
||||||
layer.unrendered_color = color;
|
|
||||||
}
|
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -328,31 +340,7 @@ impl IOCompositor {
|
||||||
new_constellation_chan: ConstellationChan) {
|
new_constellation_chan: ConstellationChan) {
|
||||||
response_chan.send(());
|
response_chan.send(());
|
||||||
|
|
||||||
// This assumes there is at most one child, which should be the case.
|
self.root_pipeline = Some(frame_tree.pipeline.clone());
|
||||||
// NOTE: work around borrowchk
|
|
||||||
{
|
|
||||||
let tmp = self.root_layer.borrow().first_child.borrow();
|
|
||||||
match *tmp.get() {
|
|
||||||
Some(ref old_layer) => ContainerLayer::remove_child(self.root_layer.clone(),
|
|
||||||
old_layer.clone()),
|
|
||||||
None => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let layer = CompositorLayer::from_frame_tree(frame_tree,
|
|
||||||
self.opts.tile_size,
|
|
||||||
Some(10000000u),
|
|
||||||
self.opts.cpu_painting);
|
|
||||||
ContainerLayer::add_child_start(self.root_layer.clone(),
|
|
||||||
ContainerLayerKind(layer.root_layer.clone()));
|
|
||||||
|
|
||||||
// If there's already a root layer, destroy it cleanly.
|
|
||||||
match self.compositor_layer {
|
|
||||||
None => {}
|
|
||||||
Some(ref mut compositor_layer) => compositor_layer.clear_all(),
|
|
||||||
}
|
|
||||||
|
|
||||||
self.compositor_layer = Some(layer);
|
|
||||||
|
|
||||||
// Initialize the new constellation channel by sending it the root window size.
|
// Initialize the new constellation channel by sending it the root window size.
|
||||||
let window_size = self.window.borrow().size();
|
let window_size = self.window.borrow().size();
|
||||||
|
@ -366,39 +354,81 @@ impl IOCompositor {
|
||||||
self.constellation_chan = new_constellation_chan;
|
self.constellation_chan = new_constellation_chan;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_new_layer(&mut self, _id: PipelineId, new_size: Size2D<f32>) {
|
// FIXME(pcwalton): Take the pipeline ID into account.
|
||||||
// FIXME: This should create an additional layer instead of replacing the current one.
|
fn create_root_compositor_layer_if_necessary(&mut self,
|
||||||
// Once ResizeLayer messages are set up, we can switch to the new functionality.
|
_: PipelineId,
|
||||||
|
layer_id: LayerId,
|
||||||
|
size: Size2D<f32>) {
|
||||||
|
let (root_pipeline, root_layer_id) = match self.compositor_layer {
|
||||||
|
Some(ref compositor_layer) => {
|
||||||
|
(compositor_layer.pipeline.clone(), compositor_layer.id_of_first_child())
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
match self.root_pipeline {
|
||||||
|
Some(ref root_pipeline) => (root_pipeline.clone(), LayerId::null()),
|
||||||
|
None => fail!("Compositor: Received new layer without initialized pipeline"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let p = match self.compositor_layer {
|
if layer_id != root_layer_id {
|
||||||
Some(ref compositor_layer) => compositor_layer.pipeline.clone(),
|
let root_pipeline_id = root_pipeline.id;
|
||||||
|
let mut new_layer = CompositorLayer::new_root(root_pipeline,
|
||||||
|
size,
|
||||||
|
self.opts.tile_size,
|
||||||
|
self.opts.cpu_painting);
|
||||||
|
|
||||||
|
{
|
||||||
|
let current_child = self.root_layer.borrow().first_child.borrow();
|
||||||
|
match *current_child.get() {
|
||||||
|
None => {}
|
||||||
|
Some(ref old_layer) => {
|
||||||
|
ContainerLayer::remove_child(self.root_layer.clone(), old_layer.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(new_layer.add_child_if_necessary(self.root_layer.clone(),
|
||||||
|
root_pipeline_id,
|
||||||
|
new_layer.id,
|
||||||
|
layer_id,
|
||||||
|
Rect(Point2D(0f32, 0f32), size),
|
||||||
|
size,
|
||||||
|
Scrollable));
|
||||||
|
|
||||||
|
ContainerLayer::add_child_start(self.root_layer.clone(),
|
||||||
|
ContainerLayerKind(new_layer.root_layer.clone()));
|
||||||
|
self.compositor_layer = Some(new_layer);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.ask_for_tiles();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_descendant_compositor_layer_if_necessary(&mut self,
|
||||||
|
pipeline_id: PipelineId,
|
||||||
|
layer_id: LayerId,
|
||||||
|
rect: Rect<f32>,
|
||||||
|
scroll_policy: ScrollPolicy) {
|
||||||
|
match self.compositor_layer {
|
||||||
|
Some(ref mut compositor_layer) => {
|
||||||
|
assert!(compositor_layer.add_child_if_necessary(self.root_layer.clone(),
|
||||||
|
pipeline_id,
|
||||||
|
compositor_layer.id,
|
||||||
|
layer_id,
|
||||||
|
rect,
|
||||||
|
compositor_layer.page_size
|
||||||
|
.unwrap(),
|
||||||
|
scroll_policy))
|
||||||
|
}
|
||||||
None => fail!("Compositor: Received new layer without initialized pipeline"),
|
None => fail!("Compositor: Received new layer without initialized pipeline"),
|
||||||
};
|
};
|
||||||
let page_size = Size2D(new_size.width as f32, new_size.height as f32);
|
|
||||||
let new_layer = CompositorLayer::new(p,
|
|
||||||
Some(page_size),
|
|
||||||
self.opts.tile_size,
|
|
||||||
Some(10000000u),
|
|
||||||
self.opts.cpu_painting);
|
|
||||||
|
|
||||||
{
|
|
||||||
let current_child = self.root_layer.borrow().first_child.borrow();
|
|
||||||
// This assumes there is at most one child, which should be the case.
|
|
||||||
match *current_child.get() {
|
|
||||||
Some(ref old_layer) => ContainerLayer::remove_child(self.root_layer.clone(),
|
|
||||||
old_layer.clone()),
|
|
||||||
None => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ContainerLayer::add_child_start(self.root_layer.clone(),
|
|
||||||
ContainerLayerKind(new_layer.root_layer.clone()));
|
|
||||||
self.compositor_layer = Some(new_layer);
|
|
||||||
|
|
||||||
self.ask_for_tiles();
|
self.ask_for_tiles();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_layer_page_size(&mut self,
|
fn set_layer_page_size(&mut self,
|
||||||
id: PipelineId,
|
pipeline_id: PipelineId,
|
||||||
|
layer_id: LayerId,
|
||||||
new_size: Size2D<f32>,
|
new_size: Size2D<f32>,
|
||||||
epoch: Epoch) {
|
epoch: Epoch) {
|
||||||
let (ask, move): (bool, bool) = match self.compositor_layer {
|
let (ask, move): (bool, bool) = match self.compositor_layer {
|
||||||
|
@ -407,8 +437,10 @@ impl IOCompositor {
|
||||||
let world_zoom = self.world_zoom;
|
let world_zoom = self.world_zoom;
|
||||||
let page_window = Size2D(window_size.width as f32 / world_zoom,
|
let page_window = Size2D(window_size.width as f32 / world_zoom,
|
||||||
window_size.height as f32 / world_zoom);
|
window_size.height as f32 / world_zoom);
|
||||||
layer.resize(id, new_size, page_window, epoch);
|
layer.resize(pipeline_id, layer_id, new_size, page_window, epoch);
|
||||||
let move = self.fragment_point.take().map_or(false, |point| layer.move(point, page_window));
|
let move = self.fragment_point.take().map_or(false, |point| {
|
||||||
|
layer.move(pipeline_id, layer_id, point, page_window)
|
||||||
|
});
|
||||||
|
|
||||||
(true, move)
|
(true, move)
|
||||||
}
|
}
|
||||||
|
@ -421,10 +453,13 @@ impl IOCompositor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_layer_clip_rect(&mut self, id: PipelineId, new_rect: Rect<f32>) {
|
fn set_layer_clip_rect(&mut self,
|
||||||
|
pipeline_id: PipelineId,
|
||||||
|
layer_id: LayerId,
|
||||||
|
new_rect: Rect<f32>) {
|
||||||
let ask: bool = match self.compositor_layer {
|
let ask: bool = match self.compositor_layer {
|
||||||
Some(ref mut layer) => {
|
Some(ref mut layer) => {
|
||||||
assert!(layer.set_clipping_rect(id, new_rect));
|
assert!(layer.set_clipping_rect(pipeline_id, layer_id, new_rect));
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
|
@ -454,10 +489,11 @@ impl IOCompositor {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint(&mut self,
|
fn paint(&mut self,
|
||||||
id: PipelineId,
|
pipeline_id: PipelineId,
|
||||||
|
layer_id: LayerId,
|
||||||
new_layer_buffer_set: ~LayerBufferSet,
|
new_layer_buffer_set: ~LayerBufferSet,
|
||||||
epoch: Epoch) {
|
epoch: Epoch) {
|
||||||
debug!("osmain: received new frame");
|
debug!("compositor received new frame");
|
||||||
|
|
||||||
// From now on, if we destroy the buffers, they will leak.
|
// From now on, if we destroy the buffers, they will leak.
|
||||||
let mut new_layer_buffer_set = new_layer_buffer_set;
|
let mut new_layer_buffer_set = new_layer_buffer_set;
|
||||||
|
@ -466,27 +502,29 @@ impl IOCompositor {
|
||||||
match self.compositor_layer {
|
match self.compositor_layer {
|
||||||
Some(ref mut layer) => {
|
Some(ref mut layer) => {
|
||||||
assert!(layer.add_buffers(&self.graphics_context,
|
assert!(layer.add_buffers(&self.graphics_context,
|
||||||
id,
|
pipeline_id,
|
||||||
|
layer_id,
|
||||||
new_layer_buffer_set,
|
new_layer_buffer_set,
|
||||||
epoch).is_none());
|
epoch).is_none());
|
||||||
self.recomposite = true;
|
self.recomposite = true;
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
fail!("Compositor: given paint command with no CompositorLayer initialized");
|
fail!("compositor given paint command with no CompositorLayer initialized");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Recycle the old buffers; send them back to the renderer to reuse if
|
// TODO: Recycle the old buffers; send them back to the renderer to reuse if
|
||||||
// it wishes.
|
// it wishes.
|
||||||
}
|
}
|
||||||
|
|
||||||
fn invalidate_rect(&mut self, id: PipelineId, rect: Rect<uint>) {
|
fn invalidate_rect(&mut self, pipeline_id: PipelineId, layer_id: LayerId, rect: Rect<uint>) {
|
||||||
let ask: bool = match self.compositor_layer {
|
let ask: bool = match self.compositor_layer {
|
||||||
Some(ref mut layer) => {
|
Some(ref mut layer) => {
|
||||||
let point = Point2D(rect.origin.x as f32,
|
let point = Point2D(rect.origin.x as f32,
|
||||||
rect.origin.y as f32);
|
rect.origin.y as f32);
|
||||||
let size = Size2D(rect.size.width as f32,
|
let size = Size2D(rect.size.width as f32,
|
||||||
rect.size.height as f32);
|
rect.size.height as f32);
|
||||||
layer.invalidate_rect(id, Rect(point, size));
|
layer.invalidate_rect(pipeline_id, layer_id, Rect(point, size));
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
|
@ -500,14 +538,18 @@ impl IOCompositor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scroll_fragment_to_point(&mut self, id: PipelineId, point: Point2D<f32>) {
|
fn scroll_fragment_to_point(&mut self,
|
||||||
|
pipeline_id: PipelineId,
|
||||||
|
layer_id: LayerId,
|
||||||
|
point: Point2D<f32>) {
|
||||||
let world_zoom = self.world_zoom;
|
let world_zoom = self.world_zoom;
|
||||||
let page_window = Size2D(self.window_size.width as f32 / world_zoom,
|
let page_window = Size2D(self.window_size.width as f32 / world_zoom,
|
||||||
self.window_size.height as f32 / world_zoom);
|
self.window_size.height as f32 / world_zoom);
|
||||||
let (ask, move): (bool, bool) = match self.compositor_layer {
|
|
||||||
Some(ref mut layer) if layer.pipeline.id == id && !layer.hidden => {
|
|
||||||
|
|
||||||
(true, layer.move(point, page_window))
|
let (ask, move): (bool, bool) = match self.compositor_layer {
|
||||||
|
Some(ref mut layer) if layer.pipeline.id == pipeline_id && !layer.hidden => {
|
||||||
|
|
||||||
|
(true, layer.move(pipeline_id, layer_id, point, page_window))
|
||||||
}
|
}
|
||||||
Some(_) | None => {
|
Some(_) | None => {
|
||||||
self.fragment_point = Some(point);
|
self.fragment_point = Some(point);
|
||||||
|
@ -630,7 +672,7 @@ impl IOCompositor {
|
||||||
self.window_size.height as f32 / world_zoom);
|
self.window_size.height as f32 / world_zoom);
|
||||||
let mut scroll = false;
|
let mut scroll = false;
|
||||||
for layer in self.compositor_layer.mut_iter() {
|
for layer in self.compositor_layer.mut_iter() {
|
||||||
scroll = layer.scroll(page_delta, page_cursor, page_window) || scroll;
|
scroll = layer.handle_scroll_event(page_delta, page_cursor, page_window) || scroll;
|
||||||
}
|
}
|
||||||
self.recomposite_if(scroll);
|
self.recomposite_if(scroll);
|
||||||
self.ask_for_tiles();
|
self.ask_for_tiles();
|
||||||
|
@ -656,7 +698,7 @@ impl IOCompositor {
|
||||||
let page_window = Size2D(window_size.width as f32 / world_zoom,
|
let page_window = Size2D(window_size.width as f32 / world_zoom,
|
||||||
window_size.height as f32 / world_zoom);
|
window_size.height as f32 / world_zoom);
|
||||||
for layer in self.compositor_layer.mut_iter() {
|
for layer in self.compositor_layer.mut_iter() {
|
||||||
layer.scroll(page_delta, page_cursor, page_window);
|
layer.handle_scroll_event(page_delta, page_cursor, page_window);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.recomposite = true;
|
self.recomposite = true;
|
||||||
|
@ -679,7 +721,9 @@ impl IOCompositor {
|
||||||
for layer in self.compositor_layer.mut_iter() {
|
for layer in self.compositor_layer.mut_iter() {
|
||||||
if !layer.hidden {
|
if !layer.hidden {
|
||||||
let rect = Rect(Point2D(0f32, 0f32), window_size_page);
|
let rect = Rect(Point2D(0f32, 0f32), window_size_page);
|
||||||
let recomposite = layer.get_buffer_request(&self.graphics_context, rect, world_zoom) ||
|
let recomposite = layer.get_buffer_request(&self.graphics_context,
|
||||||
|
rect,
|
||||||
|
world_zoom) ||
|
||||||
self.recomposite;
|
self.recomposite;
|
||||||
self.recomposite = recomposite;
|
self.recomposite = recomposite;
|
||||||
} else {
|
} else {
|
||||||
|
@ -756,3 +800,4 @@ impl IOCompositor {
|
||||||
self.recomposite = result || self.recomposite;
|
self.recomposite = result || self.recomposite;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,42 +3,56 @@
|
||||||
* 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 compositing::quadtree::{Quadtree, Normal, Invalid, Hidden};
|
use compositing::quadtree::{Quadtree, Normal, Invalid, Hidden};
|
||||||
use constellation::{SendableChildFrameTree, SendableFrameTree};
|
use pipeline::CompositionPipeline;
|
||||||
|
use windowing::{MouseWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEvent};
|
||||||
|
use windowing::{MouseWindowMouseUpEvent};
|
||||||
|
|
||||||
|
use azure::azure_hl::Color;
|
||||||
use geom::matrix::identity;
|
use geom::matrix::identity;
|
||||||
use geom::point::Point2D;
|
use geom::point::Point2D;
|
||||||
use geom::rect::Rect;
|
use geom::rect::Rect;
|
||||||
use geom::size::Size2D;
|
use geom::size::Size2D;
|
||||||
use gfx::render_task::{ReRenderMsg, UnusedBufferMsg};
|
use gfx::render_task::{ReRenderMsg, UnusedBufferMsg};
|
||||||
|
use gfx;
|
||||||
use layers::layers::{ContainerLayerKind, ContainerLayer, Flip, NoFlip, TextureLayer};
|
use layers::layers::{ContainerLayerKind, ContainerLayer, Flip, NoFlip, TextureLayer};
|
||||||
use layers::layers::TextureLayerKind;
|
use layers::layers::TextureLayerKind;
|
||||||
#[cfg(target_os="macos")]
|
|
||||||
#[cfg(target_os="android")]
|
|
||||||
use layers::layers::VerticalFlip;
|
|
||||||
use layers::platform::surface::{NativeCompositingGraphicsContext, NativeSurfaceMethods};
|
use layers::platform::surface::{NativeCompositingGraphicsContext, NativeSurfaceMethods};
|
||||||
use layers::texturegl::{Texture, TextureTarget};
|
use layers::texturegl::{Texture, TextureTarget};
|
||||||
#[cfg(target_os="macos")] use layers::texturegl::TextureTargetRectangle;
|
|
||||||
use pipeline::CompositionPipeline;
|
|
||||||
use script::dom::event::{ClickEvent, MouseDownEvent, MouseMoveEvent, MouseUpEvent};
|
use script::dom::event::{ClickEvent, MouseDownEvent, MouseMoveEvent, MouseUpEvent};
|
||||||
use script::script_task::{ScriptChan, SendEventMsg};
|
use script::script_task::{ScriptChan, SendEventMsg};
|
||||||
use servo_msg::compositor_msg::{LayerBuffer, LayerBufferSet, Epoch, Tile};
|
use servo_msg::compositor_msg::{Epoch, FixedPosition, LayerBuffer, LayerBufferSet, LayerId};
|
||||||
|
use servo_msg::compositor_msg::{ScrollPolicy, Tile};
|
||||||
use servo_msg::constellation_msg::PipelineId;
|
use servo_msg::constellation_msg::PipelineId;
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use windowing::{MouseWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEvent};
|
|
||||||
use windowing::{MouseWindowMouseUpEvent};
|
|
||||||
use azure::azure_hl::Color;
|
|
||||||
use gfx;
|
|
||||||
|
|
||||||
|
#[cfg(target_os="macos")]
|
||||||
|
#[cfg(target_os="android")]
|
||||||
|
use layers::layers::VerticalFlip;
|
||||||
#[cfg(not(target_os="macos"))]
|
#[cfg(not(target_os="macos"))]
|
||||||
use layers::texturegl::TextureTarget2D;
|
use layers::texturegl::TextureTarget2D;
|
||||||
|
#[cfg(target_os="macos")]
|
||||||
|
use layers::texturegl::TextureTargetRectangle;
|
||||||
|
|
||||||
|
/// The amount of memory usage allowed per layer.
|
||||||
|
static MAX_TILE_MEMORY_PER_LAYER: uint = 10000000;
|
||||||
|
|
||||||
/// The CompositorLayer represents an element on a page that has a unique scroll
|
/// The CompositorLayer represents an element on a page that has a unique scroll
|
||||||
/// or animation behavior. This can include absolute positioned elements, iframes, etc.
|
/// or animation behavior. This can include absolute positioned elements, iframes, etc.
|
||||||
/// Each layer can also have child layers.
|
/// Each layer can also have child layers.
|
||||||
|
///
|
||||||
|
/// FIXME(pcwalton): This should be merged with the concept of a layer in `rust-layers` and
|
||||||
|
/// ultimately removed, except as a set of helper methods on `rust-layers` layers.
|
||||||
pub struct CompositorLayer {
|
pub struct CompositorLayer {
|
||||||
/// This layer's pipeline. BufferRequests and mouse events will be sent through this.
|
/// This layer's pipeline. BufferRequests and mouse events will be sent through this.
|
||||||
pipeline: CompositionPipeline,
|
pipeline: CompositionPipeline,
|
||||||
|
|
||||||
|
/// The ID of this layer within the pipeline.
|
||||||
|
id: LayerId,
|
||||||
|
|
||||||
|
/// The bounds of this layer in terms of its parent (a.k.a. the scissor box).
|
||||||
|
bounds: Rect<f32>,
|
||||||
|
|
||||||
/// The size of the underlying page in page coordinates. This is an option
|
/// The size of the underlying page in page coordinates. This is an option
|
||||||
/// because we may not know the size of the page until layout is finished completely.
|
/// because we may not know the size of the page until layout is finished completely.
|
||||||
/// if we have no size yet, the layer is hidden until a size message is recieved.
|
/// if we have no size yet, the layer is hidden until a size message is recieved.
|
||||||
|
@ -70,17 +84,20 @@ pub struct CompositorLayer {
|
||||||
epoch: Epoch,
|
epoch: Epoch,
|
||||||
|
|
||||||
/// The behavior of this layer when a scroll message is received.
|
/// The behavior of this layer when a scroll message is received.
|
||||||
scroll_behavior: ScrollBehavior,
|
wants_scroll_events: WantsScrollEventsFlag,
|
||||||
|
|
||||||
|
/// Whether an ancestor layer that receives scroll events moves this layer.
|
||||||
|
scroll_policy: ScrollPolicy,
|
||||||
|
|
||||||
/// True if CPU rendering is enabled, false if we're using GPU rendering.
|
/// True if CPU rendering is enabled, false if we're using GPU rendering.
|
||||||
cpu_painting: bool,
|
cpu_painting: bool,
|
||||||
|
|
||||||
/// The color to use for the unrendered-content void
|
/// The color to use for the unrendered-content void
|
||||||
unrendered_color: Color
|
unrendered_color: Color,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper struct for keeping CompositorLayer children organized.
|
/// Helper struct for keeping CompositorLayer children organized.
|
||||||
struct CompositorLayerChild {
|
pub struct CompositorLayerChild {
|
||||||
/// The child itself.
|
/// The child itself.
|
||||||
child: ~CompositorLayer,
|
child: ~CompositorLayer,
|
||||||
/// A ContainerLayer managed by the parent node. This deals with clipping and
|
/// A ContainerLayer managed by the parent node. This deals with clipping and
|
||||||
|
@ -95,13 +112,28 @@ enum MaybeQuadtree {
|
||||||
NoTree(uint, Option<uint>),
|
NoTree(uint, Option<uint>),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determines the behavior of the layer when a scroll message is recieved.
|
impl MaybeQuadtree {
|
||||||
enum ScrollBehavior {
|
fn tile_size(&self) -> uint {
|
||||||
/// Normal scrolling behavior.
|
match *self {
|
||||||
Scroll,
|
Tree(ref quadtree) => quadtree.max_tile_size,
|
||||||
/// Scrolling messages targeted at this layer are ignored, but can be
|
NoTree(tile_size, _) => tile_size,
|
||||||
/// passed on to child layers.
|
}
|
||||||
FixedPosition,
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deriving(Eq, Clone)]
|
||||||
|
pub enum WantsScrollEventsFlag {
|
||||||
|
WantsScrollEvents,
|
||||||
|
DoesntWantScrollEvents,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_container_layer_from_rect(rect: Rect<f32>) -> Rc<ContainerLayer> {
|
||||||
|
let container = Rc::new(ContainerLayer());
|
||||||
|
container.borrow().scissor.set(Some(rect));
|
||||||
|
container.borrow().common.with_mut(|common| {
|
||||||
|
common.transform = identity().translate(rect.origin.x, rect.origin.y, 0f32);
|
||||||
|
});
|
||||||
|
container
|
||||||
}
|
}
|
||||||
|
|
||||||
trait Clampable {
|
trait Clampable {
|
||||||
|
@ -124,79 +156,155 @@ impl Clampable for f32 {
|
||||||
|
|
||||||
|
|
||||||
impl CompositorLayer {
|
impl CompositorLayer {
|
||||||
/// Creates a new CompositorLayer with an optional page size. If no page size is given,
|
/// Creates a new `CompositorLayer`.
|
||||||
/// the layer is initially hidden and initialized without a quadtree.
|
fn new(pipeline: CompositionPipeline,
|
||||||
pub fn new(pipeline: CompositionPipeline,
|
layer_id: LayerId,
|
||||||
page_size: Option<Size2D<f32>>,
|
bounds: Rect<f32>,
|
||||||
tile_size: uint,
|
page_size: Option<Size2D<f32>>,
|
||||||
max_mem: Option<uint>,
|
tile_size: uint,
|
||||||
cpu_painting: bool)
|
cpu_painting: bool,
|
||||||
-> CompositorLayer {
|
wants_scroll_events: WantsScrollEventsFlag,
|
||||||
|
scroll_policy: ScrollPolicy)
|
||||||
|
-> CompositorLayer {
|
||||||
CompositorLayer {
|
CompositorLayer {
|
||||||
pipeline: pipeline,
|
pipeline: pipeline,
|
||||||
|
id: layer_id,
|
||||||
|
bounds: bounds,
|
||||||
page_size: page_size,
|
page_size: page_size,
|
||||||
scroll_offset: Point2D(0f32, 0f32),
|
scroll_offset: Point2D(0f32, 0f32),
|
||||||
children: ~[],
|
children: ~[],
|
||||||
quadtree: match page_size {
|
quadtree: match page_size {
|
||||||
None => NoTree(tile_size, max_mem),
|
None => NoTree(tile_size, Some(MAX_TILE_MEMORY_PER_LAYER)),
|
||||||
Some(page_size) => Tree(Quadtree::new(Size2D(page_size.width as uint,
|
Some(page_size) => {
|
||||||
page_size.height as uint),
|
Tree(Quadtree::new(Size2D(page_size.width as uint, page_size.height as uint),
|
||||||
tile_size,
|
tile_size,
|
||||||
max_mem)),
|
Some(MAX_TILE_MEMORY_PER_LAYER)))
|
||||||
|
}
|
||||||
},
|
},
|
||||||
root_layer: Rc::new(ContainerLayer()),
|
root_layer: Rc::new(ContainerLayer()),
|
||||||
hidden: true,
|
hidden: true,
|
||||||
epoch: Epoch(0),
|
epoch: Epoch(0),
|
||||||
scroll_behavior: Scroll,
|
wants_scroll_events: wants_scroll_events,
|
||||||
|
scroll_policy: scroll_policy,
|
||||||
cpu_painting: cpu_painting,
|
cpu_painting: cpu_painting,
|
||||||
unrendered_color: gfx::color::rgba(0.0, 0.0, 0.0, 0.0),
|
unrendered_color: gfx::color::rgba(0.0, 0.0, 0.0, 0.0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constructs a CompositorLayer tree from a frame tree.
|
/// Creates a new root `CompositorLayer` bound to a composition pipeline with an optional page
|
||||||
pub fn from_frame_tree(frame_tree: SendableFrameTree,
|
/// size. If no page size is given, the layer is initially hidden and initialized without a
|
||||||
tile_size: uint,
|
/// quadtree.
|
||||||
max_mem: Option<uint>,
|
pub fn new_root(pipeline: CompositionPipeline,
|
||||||
cpu_painting: bool)
|
page_size: Size2D<f32>,
|
||||||
-> CompositorLayer {
|
tile_size: uint,
|
||||||
let SendableFrameTree { pipeline, children } = frame_tree;
|
cpu_painting: bool)
|
||||||
let mut layer = CompositorLayer::new(pipeline, None, tile_size, max_mem, cpu_painting);
|
-> CompositorLayer {
|
||||||
layer.children = (children.move_iter().map(|child| {
|
CompositorLayer {
|
||||||
let SendableChildFrameTree { frame_tree, rect } = child;
|
pipeline: pipeline,
|
||||||
let container = Rc::new(ContainerLayer());
|
id: LayerId::null(),
|
||||||
match rect {
|
bounds: Rect(Point2D(0f32, 0f32), page_size),
|
||||||
Some(rect) => {
|
page_size: Some(page_size),
|
||||||
container.borrow().scissor.set(Some(rect));
|
scroll_offset: Point2D(0f32, 0f32),
|
||||||
container.borrow().common.with_mut(|common| common.transform = identity().translate(rect.origin.x,
|
children: ~[],
|
||||||
rect.origin.y,
|
quadtree: NoTree(tile_size, Some(MAX_TILE_MEMORY_PER_LAYER)),
|
||||||
0f32));
|
root_layer: Rc::new(ContainerLayer()),
|
||||||
}
|
hidden: false,
|
||||||
None => {}
|
epoch: Epoch(0),
|
||||||
}
|
wants_scroll_events: WantsScrollEvents,
|
||||||
|
scroll_policy: FixedPosition,
|
||||||
|
cpu_painting: cpu_painting,
|
||||||
|
unrendered_color: gfx::color::rgba(0.0, 0.0, 0.0, 0.0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a child layer to the layer with the given ID and the given pipeline, if it doesn't
|
||||||
|
/// exist yet. The child layer will have the same pipeline, tile size, memory limit, and CPU
|
||||||
|
/// painting status as its parent.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// * True if the layer was added;
|
||||||
|
/// * True if the layer was not added because it already existed;
|
||||||
|
/// * False if the layer could not be added because no suitable parent layer with the given
|
||||||
|
/// ID and pipeline could be found.
|
||||||
|
pub fn add_child_if_necessary(&mut self,
|
||||||
|
container_layer: Rc<ContainerLayer>,
|
||||||
|
pipeline_id: PipelineId,
|
||||||
|
parent_layer_id: LayerId,
|
||||||
|
child_layer_id: LayerId,
|
||||||
|
rect: Rect<f32>,
|
||||||
|
page_size: Size2D<f32>,
|
||||||
|
scroll_policy: ScrollPolicy)
|
||||||
|
-> bool {
|
||||||
|
if self.pipeline.id != pipeline_id || self.id != parent_layer_id {
|
||||||
|
return self.children.mut_iter().any(|kid_holder| {
|
||||||
|
kid_holder.child.add_child_if_necessary(kid_holder.container.clone(),
|
||||||
|
pipeline_id,
|
||||||
|
parent_layer_id,
|
||||||
|
child_layer_id,
|
||||||
|
rect,
|
||||||
|
page_size,
|
||||||
|
scroll_policy)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
let child_layer = ~CompositorLayer::from_frame_tree(frame_tree,
|
// See if we've already made this child layer.
|
||||||
tile_size,
|
for kid_holder in self.children.iter() {
|
||||||
max_mem,
|
if kid_holder.child.pipeline.id == pipeline_id &&
|
||||||
cpu_painting);
|
kid_holder.child.id == child_layer_id {
|
||||||
ContainerLayer::add_child_start(container.clone(), ContainerLayerKind(child_layer.root_layer.clone()));
|
return true
|
||||||
|
|
||||||
CompositorLayerChild {
|
|
||||||
child: child_layer,
|
|
||||||
container: container,
|
|
||||||
}
|
}
|
||||||
})).collect();
|
}
|
||||||
layer.set_occlusions();
|
|
||||||
layer
|
let mut kid = ~CompositorLayer::new(self.pipeline.clone(),
|
||||||
|
child_layer_id,
|
||||||
|
rect,
|
||||||
|
Some(page_size),
|
||||||
|
self.quadtree.tile_size(),
|
||||||
|
self.cpu_painting,
|
||||||
|
DoesntWantScrollEvents,
|
||||||
|
scroll_policy);
|
||||||
|
|
||||||
|
kid.hidden = false;
|
||||||
|
|
||||||
|
// Place the kid's layer in a container...
|
||||||
|
let kid_container = create_container_layer_from_rect(rect);
|
||||||
|
ContainerLayer::add_child_start(kid_container.clone(),
|
||||||
|
ContainerLayerKind(kid.root_layer.clone()));
|
||||||
|
|
||||||
|
// ...and add *that* container as a child of the container passed in.
|
||||||
|
ContainerLayer::add_child_end(container_layer,
|
||||||
|
ContainerLayerKind(kid_container.clone()));
|
||||||
|
|
||||||
|
self.children.push(CompositorLayerChild {
|
||||||
|
child: kid,
|
||||||
|
container: kid_container,
|
||||||
|
});
|
||||||
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move the layer by as relative specified amount in page coordinates. Does not change
|
/// Move the layer's descendants that don't want scroll events and scroll by a relative
|
||||||
// the position of the layer relative to its parent. This also takes in a cursor position
|
/// specified amount in page coordinates. This also takes in a cursor position to see if the
|
||||||
// to see if the mouse is over child layers first. If a layer successfully scrolled, returns
|
/// mouse is over child layers first. If a layer successfully scrolled, returns true; otherwise
|
||||||
// true; otherwise returns false, so a parent layer can scroll instead.
|
/// returns false, so a parent layer can scroll instead.
|
||||||
pub fn scroll(&mut self, delta: Point2D<f32>, cursor: Point2D<f32>, window_size: Size2D<f32>)
|
pub fn handle_scroll_event(&mut self,
|
||||||
-> bool {
|
delta: Point2D<f32>,
|
||||||
|
cursor: Point2D<f32>,
|
||||||
|
window_size: Size2D<f32>)
|
||||||
|
-> bool {
|
||||||
|
// If this layer is hidden, neither it nor its children will scroll.
|
||||||
|
if self.hidden {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this layer doesn't want scroll events, neither it nor its children can handle scroll
|
||||||
|
// events.
|
||||||
|
if self.wants_scroll_events != WantsScrollEvents {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow children to scroll.
|
||||||
let cursor = cursor - self.scroll_offset;
|
let cursor = cursor - self.scroll_offset;
|
||||||
for child in self.children.mut_iter().filter(|x| !x.child.hidden) {
|
for child in self.children.mut_iter() {
|
||||||
// NOTE: work around borrowchk
|
// NOTE: work around borrowchk
|
||||||
let tmp = child.container.borrow().scissor.borrow();
|
let tmp = child.container.borrow().scissor.borrow();
|
||||||
match *tmp.get() {
|
match *tmp.get() {
|
||||||
|
@ -206,42 +314,82 @@ impl CompositorLayer {
|
||||||
Some(rect) => {
|
Some(rect) => {
|
||||||
if cursor.x >= rect.origin.x && cursor.x < rect.origin.x + rect.size.width
|
if cursor.x >= rect.origin.x && cursor.x < rect.origin.x + rect.size.width
|
||||||
&& cursor.y >= rect.origin.y && cursor.y < rect.origin.y + rect.size.height
|
&& cursor.y >= rect.origin.y && cursor.y < rect.origin.y + rect.size.height
|
||||||
&& child.child.scroll(delta, cursor - rect.origin, rect.size) {
|
&& child.child.handle_scroll_event(delta,
|
||||||
return true;
|
cursor - rect.origin,
|
||||||
|
rect.size) {
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This scroll event is mine!
|
// This scroll event is mine!
|
||||||
match self.scroll_behavior {
|
// Scroll this layer!
|
||||||
Scroll => {
|
let old_origin = self.scroll_offset;
|
||||||
// Scroll this layer!
|
self.scroll_offset = self.scroll_offset + delta;
|
||||||
let old_origin = self.scroll_offset;
|
|
||||||
self.scroll_offset = self.scroll_offset + delta;
|
|
||||||
|
|
||||||
// bounds checking
|
// bounds checking
|
||||||
let page_size = match self.page_size {
|
let page_size = match self.page_size {
|
||||||
Some(size) => size,
|
Some(size) => size,
|
||||||
None => fail!("CompositorLayer: tried to scroll with no page size set"),
|
None => fail!("CompositorLayer: tried to scroll with no page size set"),
|
||||||
};
|
};
|
||||||
let min_x = cmp::min(window_size.width - page_size.width, 0.0);
|
let min_x = cmp::min(window_size.width - page_size.width, 0.0);
|
||||||
self.scroll_offset.x = self.scroll_offset.x.clamp(&min_x, &0.0);
|
self.scroll_offset.x = self.scroll_offset.x.clamp(&min_x, &0.0);
|
||||||
let min_y = cmp::min(window_size.height - page_size.height, 0.0);
|
let min_y = cmp::min(window_size.height - page_size.height, 0.0);
|
||||||
self.scroll_offset.y = self.scroll_offset.y.clamp(&min_y, &0.0);
|
self.scroll_offset.y = self.scroll_offset.y.clamp(&min_y, &0.0);
|
||||||
|
|
||||||
// check to see if we scrolled
|
if old_origin - self.scroll_offset == Point2D(0f32, 0f32) {
|
||||||
if old_origin - self.scroll_offset == Point2D(0f32, 0f32) {
|
return false
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.root_layer.borrow().common.with_mut(|common| common.set_transform(identity().translate(self.scroll_offset.x,
|
|
||||||
self.scroll_offset.y,
|
|
||||||
0.0)));
|
|
||||||
true
|
|
||||||
}
|
|
||||||
FixedPosition => false, // Ignore this scroll event.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.scroll(self.scroll_offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn dump_layer_tree(&self, layer: Rc<ContainerLayer>, indent: ~str) {
|
||||||
|
{
|
||||||
|
let scissor = layer.borrow().scissor.borrow();
|
||||||
|
println!("{}scissor {:?}", indent, *scissor.get());
|
||||||
|
}
|
||||||
|
for kid in layer.borrow().children() {
|
||||||
|
match kid {
|
||||||
|
ContainerLayerKind(ref container_layer) => {
|
||||||
|
self.dump_layer_tree((*container_layer).clone(), indent + " ");
|
||||||
|
}
|
||||||
|
TextureLayerKind(_) => {
|
||||||
|
println!("{} (texture layer)", indent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Actually scrolls the descendants of a layer that scroll. This is called by
|
||||||
|
/// `handle_scroll_event` above when it determines that a layer wants to scroll.
|
||||||
|
fn scroll(&mut self, scroll_offset: Point2D<f32>) -> bool {
|
||||||
|
let mut result = false;
|
||||||
|
|
||||||
|
// Only scroll this layer if it's not fixed-positioned.
|
||||||
|
if self.scroll_policy != FixedPosition {
|
||||||
|
// Scroll this layer!
|
||||||
|
self.scroll_offset = scroll_offset;
|
||||||
|
|
||||||
|
self.root_layer
|
||||||
|
.borrow()
|
||||||
|
.common
|
||||||
|
.with_mut(|common| {
|
||||||
|
common.set_transform(identity().translate(self.scroll_offset.x,
|
||||||
|
self.scroll_offset.y,
|
||||||
|
0.0))
|
||||||
|
});
|
||||||
|
|
||||||
|
result = true
|
||||||
|
}
|
||||||
|
|
||||||
|
for kid_holder in self.children.mut_iter() {
|
||||||
|
result = kid_holder.child.scroll(scroll_offset) || result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
// Takes in a MouseWindowEvent, determines if it should be passed to children, and
|
// Takes in a MouseWindowEvent, determines if it should be passed to children, and
|
||||||
|
@ -282,43 +430,49 @@ impl CompositorLayer {
|
||||||
chan.try_send(SendEventMsg(self.pipeline.id.clone(), message));
|
chan.try_send(SendEventMsg(self.pipeline.id.clone(), message));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Given the current window size, determine which tiles need to be (re)rendered
|
// Given the current window size, determine which tiles need to be (re-)rendered and sends them
|
||||||
// and sends them off the the appropriate renderer.
|
// off the the appropriate renderer. Returns true if and only if the scene should be repainted.
|
||||||
// Returns a bool that is true if the scene should be repainted.
|
|
||||||
pub fn get_buffer_request(&mut self,
|
pub fn get_buffer_request(&mut self,
|
||||||
graphics_context: &NativeCompositingGraphicsContext,
|
graphics_context: &NativeCompositingGraphicsContext,
|
||||||
window_rect: Rect<f32>,
|
window_rect: Rect<f32>,
|
||||||
scale: f32)
|
scale: f32)
|
||||||
-> bool {
|
-> bool {
|
||||||
let rect = Rect(Point2D(-self.scroll_offset.x + window_rect.origin.x,
|
let mut redisplay = false;
|
||||||
-self.scroll_offset.y + window_rect.origin.y),
|
match self.quadtree {
|
||||||
window_rect.size);
|
NoTree(..) => {}
|
||||||
let mut redisplay: bool;
|
Tree(ref mut quadtree) => {
|
||||||
{ // block here to prevent double mutable borrow of self
|
let (request, unused) = quadtree.get_tile_rects_page(window_rect, scale);
|
||||||
let quadtree = match self.quadtree {
|
|
||||||
NoTree(..) => fail!("CompositorLayer: cannot get buffer request for {:?},
|
// Workaround to make redisplay visible outside block.
|
||||||
no quadtree initialized", self.pipeline.id),
|
redisplay = !unused.is_empty();
|
||||||
Tree(ref mut quadtree) => quadtree,
|
if redisplay {
|
||||||
};
|
// Send back unused tiles.
|
||||||
let (request, unused) = quadtree.get_tile_rects_page(rect, scale);
|
self.pipeline.render_chan.try_send(UnusedBufferMsg(unused));
|
||||||
redisplay = !unused.is_empty(); // workaround to make redisplay visible outside block
|
}
|
||||||
if redisplay { // send back unused tiles
|
if !request.is_empty() {
|
||||||
self.pipeline.render_chan.try_send(UnusedBufferMsg(unused));
|
// Ask for tiles.
|
||||||
|
//
|
||||||
|
// FIXME(pcwalton): We may want to batch these up in the case in which one
|
||||||
|
// page has multiple layers, to avoid the user seeing inconsistent states.
|
||||||
|
let msg = ReRenderMsg(request, scale, self.id, self.epoch);
|
||||||
|
self.pipeline.render_chan.try_send(msg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if !request.is_empty() { // ask for tiles
|
};
|
||||||
self.pipeline.render_chan.try_send(ReRenderMsg(request, scale, self.epoch));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if redisplay {
|
if redisplay {
|
||||||
self.build_layer_tree(graphics_context);
|
self.build_layer_tree(graphics_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
let transform = |x: &mut CompositorLayerChild| -> bool {
|
let transform = |x: &mut CompositorLayerChild| -> bool {
|
||||||
// NOTE: work around borrowchk
|
// NOTE: work around borrowchk
|
||||||
let tmp = x.container.borrow().scissor.borrow();
|
let tmp = x.container.borrow().scissor.borrow();
|
||||||
match *tmp.get() {
|
match *tmp.get() {
|
||||||
Some(scissor) => {
|
Some(scissor) => {
|
||||||
let new_rect = rect.intersection(&scissor);
|
let mut new_rect = window_rect;
|
||||||
match new_rect {
|
new_rect.origin.x = new_rect.origin.x - x.child.scroll_offset.x;
|
||||||
|
new_rect.origin.y = new_rect.origin.y - x.child.scroll_offset.y;
|
||||||
|
match new_rect.intersection(&scissor) {
|
||||||
Some(new_rect) => {
|
Some(new_rect) => {
|
||||||
// Child layers act as if they are rendered at (0,0), so we
|
// Child layers act as if they are rendered at (0,0), so we
|
||||||
// subtract the layer's (x,y) coords in its containing page
|
// subtract the layer's (x,y) coords in its containing page
|
||||||
|
@ -328,13 +482,11 @@ impl CompositorLayer {
|
||||||
x.child.get_buffer_request(graphics_context, child_rect, scale)
|
x.child.get_buffer_request(graphics_context, child_rect, scale)
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
false //Layer is offscreen
|
false // Layer is offscreen
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => fail!("child layer not clipped!"),
|
||||||
fail!("CompositorLayer: Child layer not clipped");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
self.children.mut_iter().filter(|x| !x.child.hidden)
|
self.children.mut_iter().filter(|x| !x.child.hidden)
|
||||||
|
@ -347,9 +499,18 @@ impl CompositorLayer {
|
||||||
// and clip the layer to the specified size in page coordinates.
|
// and clip the layer to the specified size in page coordinates.
|
||||||
// If the layer is hidden and has a defined page size, unhide it.
|
// If the layer is hidden and has a defined page size, unhide it.
|
||||||
// This method returns false if the specified layer is not found.
|
// This method returns false if the specified layer is not found.
|
||||||
pub fn set_clipping_rect(&mut self, pipeline_id: PipelineId, new_rect: Rect<f32>) -> bool {
|
pub fn set_clipping_rect(&mut self,
|
||||||
match self.children.iter().position(|x| pipeline_id == x.child.pipeline.id) {
|
pipeline_id: PipelineId,
|
||||||
|
layer_id: LayerId,
|
||||||
|
new_rect: Rect<f32>)
|
||||||
|
-> bool {
|
||||||
|
debug!("compositor_layer: starting set_clipping_rect()");
|
||||||
|
match self.children.iter().position(|kid_holder| {
|
||||||
|
pipeline_id == kid_holder.child.pipeline.id &&
|
||||||
|
layer_id == kid_holder.child.id
|
||||||
|
}) {
|
||||||
Some(i) => {
|
Some(i) => {
|
||||||
|
debug!("compositor_layer: node found for set_clipping_rect()");
|
||||||
let child_node = &mut self.children[i];
|
let child_node = &mut self.children[i];
|
||||||
child_node.container.borrow().common.with_mut(|common|
|
child_node.container.borrow().common.with_mut(|common|
|
||||||
common.set_transform(identity().translate(new_rect.origin.x,
|
common.set_transform(identity().translate(new_rect.origin.x,
|
||||||
|
@ -360,6 +521,7 @@ impl CompositorLayer {
|
||||||
let tmp = child_node.container.borrow().scissor.borrow();
|
let tmp = child_node.container.borrow().scissor.borrow();
|
||||||
tmp.get().clone()
|
tmp.get().clone()
|
||||||
};
|
};
|
||||||
|
|
||||||
child_node.container.borrow().scissor.set(Some(new_rect));
|
child_node.container.borrow().scissor.set(Some(new_rect));
|
||||||
match self.quadtree {
|
match self.quadtree {
|
||||||
NoTree(..) => {} // Nothing to do
|
NoTree(..) => {} // Nothing to do
|
||||||
|
@ -382,72 +544,94 @@ impl CompositorLayer {
|
||||||
None => {
|
None => {
|
||||||
// ID does not match any of our immediate children, so recurse on
|
// ID does not match any of our immediate children, so recurse on
|
||||||
// descendents (including hidden children)
|
// descendents (including hidden children)
|
||||||
self.children.mut_iter().map(|x| &mut x.child).any(|x| x.set_clipping_rect(pipeline_id, new_rect))
|
self.children
|
||||||
|
.mut_iter()
|
||||||
|
.map(|kid_holder| &mut kid_holder.child)
|
||||||
|
.any(|kid| kid.set_clipping_rect(pipeline_id, layer_id, new_rect))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Set the layer's page size. This signals that the renderer is ready for BufferRequests.
|
// Set the layer's page size. This signals that the renderer is ready for BufferRequests.
|
||||||
// If the layer is hidden and has a defined clipping rect, unhide it.
|
// If the layer is hidden and has a defined clipping rect, unhide it.
|
||||||
// This method returns false if the specified layer is not found.
|
// This method returns false if the specified layer is not found.
|
||||||
pub fn resize(&mut self, pipeline_id: PipelineId, new_size: Size2D<f32>, window_size: Size2D<f32>, epoch: Epoch) -> bool {
|
pub fn resize(&mut self,
|
||||||
if self.pipeline.id == pipeline_id {
|
pipeline_id: PipelineId,
|
||||||
self.epoch = epoch;
|
layer_id: LayerId,
|
||||||
self.page_size = Some(new_size);
|
new_size: Size2D<f32>,
|
||||||
match self.quadtree {
|
window_size: Size2D<f32>,
|
||||||
Tree(ref mut quadtree) => {
|
epoch: Epoch)
|
||||||
self.pipeline.render_chan.try_send(UnusedBufferMsg(quadtree.resize(new_size.width as uint,
|
-> bool {
|
||||||
new_size.height as uint)));
|
debug!("compositor_layer: starting resize()");
|
||||||
}
|
if self.pipeline.id != pipeline_id || self.id != layer_id {
|
||||||
NoTree(tile_size, max_mem) => {
|
return self.resize_helper(pipeline_id, layer_id, new_size, epoch)
|
||||||
self.quadtree = Tree(Quadtree::new(Size2D(new_size.width as uint,
|
|
||||||
new_size.height as uint),
|
|
||||||
tile_size,
|
|
||||||
max_mem))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Call scroll for bounds checking if the page shrunk. Use (-1, -1) as the cursor position
|
|
||||||
// to make sure the scroll isn't propagated downwards.
|
|
||||||
self.scroll(Point2D(0f32, 0f32), Point2D(-1f32, -1f32), window_size);
|
|
||||||
self.hidden = false;
|
|
||||||
self.set_occlusions();
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
self.resize_helper(pipeline_id, new_size, epoch)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debug!("compositor_layer: layer found for resize()");
|
||||||
|
self.epoch = epoch;
|
||||||
|
self.page_size = Some(new_size);
|
||||||
|
match self.quadtree {
|
||||||
|
Tree(ref mut quadtree) => {
|
||||||
|
self.pipeline
|
||||||
|
.render_chan
|
||||||
|
.try_send(UnusedBufferMsg(quadtree.resize(new_size.width as uint,
|
||||||
|
new_size.height as uint)));
|
||||||
|
}
|
||||||
|
NoTree(tile_size, max_mem) => {
|
||||||
|
self.quadtree = Tree(Quadtree::new(Size2D(new_size.width as uint,
|
||||||
|
new_size.height as uint),
|
||||||
|
tile_size,
|
||||||
|
max_mem))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Call scroll for bounds checking if the page shrunk. Use (-1, -1) as the cursor position
|
||||||
|
// to make sure the scroll isn't propagated downwards.
|
||||||
|
self.handle_scroll_event(Point2D(0f32, 0f32), Point2D(-1f32, -1f32), window_size);
|
||||||
|
self.hidden = false;
|
||||||
|
self.set_occlusions();
|
||||||
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn move(&mut self, origin: Point2D<f32>, window_size: Size2D<f32>) -> bool {
|
pub fn move(&mut self,
|
||||||
match self.scroll_behavior {
|
pipeline_id: PipelineId,
|
||||||
Scroll => {
|
layer_id: LayerId,
|
||||||
// Scroll this layer!
|
origin: Point2D<f32>,
|
||||||
let old_origin = self.scroll_offset;
|
window_size: Size2D<f32>)
|
||||||
self.scroll_offset = Point2D(0f32, 0f32) - origin;
|
-> bool {
|
||||||
|
// Search children for the right layer to move.
|
||||||
// bounds checking
|
if self.pipeline.id != pipeline_id || self.id != layer_id {
|
||||||
let page_size = match self.page_size {
|
for kid_holder in self.children.mut_iter() {
|
||||||
Some(size) => size,
|
if kid_holder.child.move(pipeline_id, layer_id, origin, window_size) {
|
||||||
None => fail!("CompositorLayer: tried to scroll with no page size set"),
|
return true
|
||||||
};
|
|
||||||
let min_x = cmp::min(window_size.width - page_size.width, 0.0);
|
|
||||||
self.scroll_offset.x = self.scroll_offset.x.clamp(&min_x, &0.0);
|
|
||||||
let min_y = cmp::min(window_size.height - page_size.height, 0.0);
|
|
||||||
self.scroll_offset.y = self.scroll_offset.y.clamp(&min_y, &0.0);
|
|
||||||
|
|
||||||
// check to see if we scrolled
|
|
||||||
if old_origin - self.scroll_offset == Point2D(0f32, 0f32) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.root_layer.borrow().common.with_mut(|common|
|
|
||||||
common.set_transform(identity().translate(self.scroll_offset.x,
|
|
||||||
self.scroll_offset.y,
|
|
||||||
0.0)));
|
|
||||||
true
|
|
||||||
}
|
}
|
||||||
FixedPosition => false // Ignore this scroll event.
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.wants_scroll_events != WantsScrollEvents {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scroll this layer!
|
||||||
|
let old_origin = self.scroll_offset;
|
||||||
|
self.scroll_offset = Point2D(0f32, 0f32) - origin;
|
||||||
|
|
||||||
|
// bounds checking
|
||||||
|
let page_size = match self.page_size {
|
||||||
|
Some(size) => size,
|
||||||
|
None => fail!("CompositorLayer: tried to scroll with no page size set"),
|
||||||
|
};
|
||||||
|
let min_x = cmp::min(window_size.width - page_size.width, 0.0);
|
||||||
|
self.scroll_offset.x = self.scroll_offset.x.clamp(&min_x, &0.0);
|
||||||
|
let min_y = cmp::min(window_size.height - page_size.height, 0.0);
|
||||||
|
self.scroll_offset.y = self.scroll_offset.y.clamp(&min_y, &0.0);
|
||||||
|
|
||||||
|
// check to see if we scrolled
|
||||||
|
if old_origin - self.scroll_offset == Point2D(0f32, 0f32) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.scroll(self.scroll_offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns whether the layer should be vertically flipped.
|
// Returns whether the layer should be vertically flipped.
|
||||||
|
@ -479,9 +663,19 @@ impl CompositorLayer {
|
||||||
}
|
}
|
||||||
|
|
||||||
// A helper method to resize sublayers.
|
// A helper method to resize sublayers.
|
||||||
fn resize_helper(&mut self, pipeline_id: PipelineId, new_size: Size2D<f32>, epoch: Epoch) -> bool {
|
fn resize_helper(&mut self,
|
||||||
let found = match self.children.iter().position(|x| pipeline_id == x.child.pipeline.id) {
|
pipeline_id: PipelineId,
|
||||||
|
layer_id: LayerId,
|
||||||
|
new_size: Size2D<f32>,
|
||||||
|
epoch: Epoch)
|
||||||
|
-> bool {
|
||||||
|
debug!("compositor_layer: starting resize_helper()");
|
||||||
|
let found = match self.children.iter().position(|kid_holder| {
|
||||||
|
pipeline_id == kid_holder.child.pipeline.id &&
|
||||||
|
layer_id == kid_holder.child.id
|
||||||
|
}) {
|
||||||
Some(i) => {
|
Some(i) => {
|
||||||
|
debug!("compositor_layer: layer found for resize_helper()");
|
||||||
let child_node = &mut self.children[i];
|
let child_node = &mut self.children[i];
|
||||||
let child = &mut child_node.child;
|
let child = &mut child_node.child;
|
||||||
child.epoch = epoch;
|
child.epoch = epoch;
|
||||||
|
@ -502,9 +696,11 @@ impl CompositorLayer {
|
||||||
let tmp = child_node.container.borrow().scissor.borrow();
|
let tmp = child_node.container.borrow().scissor.borrow();
|
||||||
match *tmp.get() {
|
match *tmp.get() {
|
||||||
Some(scissor) => {
|
Some(scissor) => {
|
||||||
// Call scroll for bounds checking if the page shrunk. Use (-1, -1) as the cursor position
|
// Call scroll for bounds checking if the page shrunk. Use (-1, -1) as the
|
||||||
// to make sure the scroll isn't propagated downwards.
|
// cursor position to make sure the scroll isn't propagated downwards.
|
||||||
child.scroll(Point2D(0f32, 0f32), Point2D(-1f32, -1f32), scissor.size);
|
child.handle_scroll_event(Point2D(0f32, 0f32),
|
||||||
|
Point2D(-1f32, -1f32),
|
||||||
|
scissor.size);
|
||||||
child.hidden = false;
|
child.hidden = false;
|
||||||
}
|
}
|
||||||
None => {} // Nothing to do
|
None => {} // Nothing to do
|
||||||
|
@ -516,11 +712,15 @@ impl CompositorLayer {
|
||||||
|
|
||||||
if found { // Boolean flag to get around double borrow of self
|
if found { // Boolean flag to get around double borrow of self
|
||||||
self.set_occlusions();
|
self.set_occlusions();
|
||||||
true
|
return true
|
||||||
} else {
|
|
||||||
// ID does not match ours, so recurse on descendents (including hidden children)
|
|
||||||
self.children.mut_iter().map(|x| &mut x.child).any(|x| x.resize_helper(pipeline_id, new_size, epoch))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we got here, the layer's ID does not match ours, so recurse on descendents (including
|
||||||
|
// hidden children).
|
||||||
|
self.children
|
||||||
|
.mut_iter()
|
||||||
|
.map(|kid_holder| &mut kid_holder.child)
|
||||||
|
.any(|kid_holder| kid_holder.resize_helper(pipeline_id, layer_id, new_size, epoch))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collect buffers from the quadtree. This method IS NOT recursive, so child CompositorLayers
|
// Collect buffers from the quadtree. This method IS NOT recursive, so child CompositorLayers
|
||||||
|
@ -577,7 +777,9 @@ impl CompositorLayer {
|
||||||
buffer.native_surface.bind_to_texture(graphics_context, &texture, size);
|
buffer.native_surface.bind_to_texture(graphics_context, &texture, size);
|
||||||
|
|
||||||
// Make a texture layer and add it.
|
// Make a texture layer and add it.
|
||||||
texture_layer = Rc::new(TextureLayer::new(texture, buffer.screen_pos.size, flip));
|
texture_layer = Rc::new(TextureLayer::new(texture,
|
||||||
|
buffer.screen_pos.size,
|
||||||
|
flip));
|
||||||
ContainerLayer::add_child_end(self.root_layer.clone(),
|
ContainerLayer::add_child_end(self.root_layer.clone(),
|
||||||
TextureLayerKind(texture_layer.clone()));
|
TextureLayerKind(texture_layer.clone()));
|
||||||
None
|
None
|
||||||
|
@ -632,57 +834,66 @@ impl CompositorLayer {
|
||||||
pub fn add_buffers(&mut self,
|
pub fn add_buffers(&mut self,
|
||||||
graphics_context: &NativeCompositingGraphicsContext,
|
graphics_context: &NativeCompositingGraphicsContext,
|
||||||
pipeline_id: PipelineId,
|
pipeline_id: PipelineId,
|
||||||
|
layer_id: LayerId,
|
||||||
mut new_buffers: ~LayerBufferSet,
|
mut new_buffers: ~LayerBufferSet,
|
||||||
epoch: Epoch)
|
epoch: Epoch)
|
||||||
-> Option<~LayerBufferSet> {
|
-> Option<~LayerBufferSet> {
|
||||||
if self.pipeline.id == pipeline_id {
|
debug!("compositor_layer: starting add_buffers()");
|
||||||
if self.epoch != epoch {
|
if self.pipeline.id != pipeline_id || self.id != layer_id {
|
||||||
debug!("compositor epoch mismatch: {:?} != {:?}, id: {:?}",
|
// ID does not match ours, so recurse on descendents (including hidden children).
|
||||||
self.epoch,
|
for child_layer in self.children.mut_iter() {
|
||||||
epoch,
|
match child_layer.child.add_buffers(graphics_context,
|
||||||
self.pipeline.id);
|
pipeline_id,
|
||||||
self.pipeline.render_chan.try_send(UnusedBufferMsg(new_buffers.buffers));
|
layer_id,
|
||||||
return None;
|
new_buffers,
|
||||||
|
epoch) {
|
||||||
|
None => return None,
|
||||||
|
Some(buffers) => new_buffers = buffers,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
// Not found. Give the caller the buffers back.
|
||||||
// Block here to prevent double mutable borrow of self.
|
return Some(new_buffers)
|
||||||
let quadtree = match self.quadtree {
|
|
||||||
NoTree(..) => fail!("CompositorLayer: cannot add buffers, no quadtree initialized"),
|
|
||||||
Tree(ref mut quadtree) => quadtree,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut unused_tiles = ~[];
|
|
||||||
// move_rev_iter is more efficient
|
|
||||||
for buffer in new_buffers.buffers.move_rev_iter() {
|
|
||||||
unused_tiles.push_all_move(quadtree.add_tile_pixel(buffer.screen_pos.origin.x,
|
|
||||||
buffer.screen_pos.origin.y,
|
|
||||||
buffer.resolution, buffer));
|
|
||||||
}
|
|
||||||
if !unused_tiles.is_empty() { // send back unused buffers
|
|
||||||
self.pipeline.render_chan.try_send(UnusedBufferMsg(unused_tiles));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.build_layer_tree(graphics_context);
|
|
||||||
return None;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ID does not match ours, so recurse on descendents (including hidden children).
|
debug!("compositor_layer: layers found for add_buffers()");
|
||||||
for child_layer in self.children.mut_iter() {
|
|
||||||
match child_layer.child.add_buffers(graphics_context,
|
if self.epoch != epoch {
|
||||||
pipeline_id,
|
debug!("add_buffers: compositor epoch mismatch: {:?} != {:?}, id: {:?}",
|
||||||
new_buffers,
|
self.epoch,
|
||||||
epoch) {
|
epoch,
|
||||||
None => return None,
|
self.pipeline.id);
|
||||||
Some(buffers) => new_buffers = buffers,
|
self.pipeline.render_chan.try_send(UnusedBufferMsg(new_buffers.buffers));
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let quadtree = match self.quadtree {
|
||||||
|
NoTree(..) => {
|
||||||
|
fail!("CompositorLayer: cannot add buffers, no quadtree initialized")
|
||||||
|
}
|
||||||
|
Tree(ref mut quadtree) => quadtree,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut unused_tiles = ~[];
|
||||||
|
// `move_rev_iter` is more efficient than `.move_iter().reverse()`.
|
||||||
|
for buffer in new_buffers.buffers.move_rev_iter() {
|
||||||
|
unused_tiles.push_all_move(quadtree.add_tile_pixel(buffer.screen_pos.origin.x,
|
||||||
|
buffer.screen_pos.origin.y,
|
||||||
|
buffer.resolution,
|
||||||
|
buffer));
|
||||||
|
}
|
||||||
|
if !unused_tiles.is_empty() { // send back unused buffers
|
||||||
|
self.pipeline.render_chan.try_send(UnusedBufferMsg(unused_tiles));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not found. Give the caller the buffers back.
|
self.build_layer_tree(graphics_context);
|
||||||
Some(new_buffers)
|
return None
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deletes a specified sublayer, including hidden children. Returns false if the layer is not found.
|
// Deletes a specified sublayer, including hidden children. Returns false if the layer is not
|
||||||
|
// found.
|
||||||
pub fn delete(&mut self,
|
pub fn delete(&mut self,
|
||||||
graphics_context: &NativeCompositingGraphicsContext,
|
graphics_context: &NativeCompositingGraphicsContext,
|
||||||
pipeline_id: PipelineId)
|
pipeline_id: PipelineId)
|
||||||
|
@ -717,8 +928,11 @@ impl CompositorLayer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn invalidate_rect(&mut self, pipeline_id: PipelineId, rect: Rect<f32>) -> bool {
|
pub fn invalidate_rect(&mut self, pipeline_id: PipelineId, layer_id: LayerId, rect: Rect<f32>)
|
||||||
if self.pipeline.id == pipeline_id {
|
-> bool {
|
||||||
|
debug!("compositor_layer: starting invalidate_rect()");
|
||||||
|
if self.pipeline.id == pipeline_id && layer_id == self.id {
|
||||||
|
debug!("compositor_layer: layer found for invalidate_rect()");
|
||||||
let quadtree = match self.quadtree {
|
let quadtree = match self.quadtree {
|
||||||
NoTree(..) => return true, // Nothing to do
|
NoTree(..) => return true, // Nothing to do
|
||||||
Tree(ref mut quadtree) => quadtree,
|
Tree(ref mut quadtree) => quadtree,
|
||||||
|
@ -727,7 +941,10 @@ impl CompositorLayer {
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
// ID does not match ours, so recurse on descendents (including hidden children).
|
// ID does not match ours, so recurse on descendents (including hidden children).
|
||||||
self.children.mut_iter().map(|x| &mut x.child).any(|x| x.invalidate_rect(pipeline_id, rect))
|
self.children
|
||||||
|
.mut_iter()
|
||||||
|
.map(|kid_holder| &mut kid_holder.child)
|
||||||
|
.any(|kid| kid.invalidate_rect(pipeline_id, layer_id, rect))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -773,16 +990,6 @@ impl CompositorLayer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Destroys all quadtree tiles of all layers, including child layers, sending the buffers
|
|
||||||
/// back to the renderer to be destroyed or reused.
|
|
||||||
pub fn clear_all(&mut self) {
|
|
||||||
self.clear();
|
|
||||||
|
|
||||||
for kid in self.children.mut_iter() {
|
|
||||||
kid.child.clear_all()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Destroys all tiles of all layers, including children, *without* sending them back to the
|
/// Destroys all tiles of all layers, including children, *without* sending them back to the
|
||||||
/// renderer. You must call this only when the render task is destined to be going down;
|
/// renderer. You must call this only when the render task is destined to be going down;
|
||||||
/// otherwise, you will leak tiles.
|
/// otherwise, you will leak tiles.
|
||||||
|
@ -804,5 +1011,12 @@ impl CompositorLayer {
|
||||||
kid.child.forget_all_tiles();
|
kid.child.forget_all_tiles();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn id_of_first_child(&self) -> LayerId {
|
||||||
|
for kid_holder in self.children.iter() {
|
||||||
|
return kid_holder.child.id
|
||||||
|
}
|
||||||
|
fail!("no first child!");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,8 +13,8 @@ use geom::point::Point2D;
|
||||||
use geom::rect::Rect;
|
use geom::rect::Rect;
|
||||||
use geom::size::Size2D;
|
use geom::size::Size2D;
|
||||||
use layers::platform::surface::{NativeCompositingGraphicsContext, NativeGraphicsMetadata};
|
use layers::platform::surface::{NativeCompositingGraphicsContext, NativeGraphicsMetadata};
|
||||||
use servo_msg::compositor_msg::{Epoch, RenderListener, LayerBufferSet, RenderState, ReadyState};
|
use servo_msg::compositor_msg::{Epoch, LayerBufferSet, LayerId, LayerMetadata, ReadyState};
|
||||||
use servo_msg::compositor_msg::ScriptListener;
|
use servo_msg::compositor_msg::{RenderListener, RenderState, ScriptListener, ScrollPolicy};
|
||||||
use servo_msg::constellation_msg::{ConstellationChan, PipelineId};
|
use servo_msg::constellation_msg::{ConstellationChan, PipelineId};
|
||||||
use servo_util::opts::Opts;
|
use servo_util::opts::Opts;
|
||||||
use servo_util::time::ProfilerChan;
|
use servo_util::time::ProfilerChan;
|
||||||
|
@ -45,12 +45,15 @@ impl ScriptListener for CompositorChan {
|
||||||
self.chan.send(msg);
|
self.chan.send(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn invalidate_rect(&self, id: PipelineId, rect: Rect<uint>) {
|
fn invalidate_rect(&self, pipeline_id: PipelineId, layer_id: LayerId, rect: Rect<uint>) {
|
||||||
self.chan.send(InvalidateRect(id, rect));
|
self.chan.send(InvalidateRect(pipeline_id, layer_id, rect));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scroll_fragment_point(&self, id: PipelineId, point: Point2D<f32>) {
|
fn scroll_fragment_point(&self,
|
||||||
self.chan.send(ScrollFragmentPoint(id, point));
|
pipeline_id: PipelineId,
|
||||||
|
layer_id: LayerId,
|
||||||
|
point: Point2D<f32>) {
|
||||||
|
self.chan.send(ScrollFragmentPoint(pipeline_id, layer_id, point));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn close(&self) {
|
fn close(&self) {
|
||||||
|
@ -72,30 +75,64 @@ impl RenderListener for CompositorChan {
|
||||||
port.recv()
|
port.recv()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint(&self, id: PipelineId, layer_buffer_set: ~LayerBufferSet, epoch: Epoch) {
|
fn paint(&self,
|
||||||
self.chan.send(Paint(id, layer_buffer_set, epoch))
|
pipeline_id: PipelineId,
|
||||||
|
layer_id: LayerId,
|
||||||
|
layer_buffer_set: ~LayerBufferSet,
|
||||||
|
epoch: Epoch) {
|
||||||
|
self.chan.send(Paint(pipeline_id, layer_id, layer_buffer_set, epoch))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_layer(&self, id: PipelineId, page_size: Size2D<uint>) {
|
fn create_layer_group_for_pipeline(&self, id: PipelineId, page_size: Size2D<uint>) {
|
||||||
let Size2D { width, height } = page_size;
|
let Size2D { width, height } = page_size;
|
||||||
self.chan.send(NewLayer(id, Size2D(width as f32, height as f32)))
|
self.chan.send(CreateRootCompositorLayerIfNecessary(id,
|
||||||
}
|
LayerId::null(),
|
||||||
fn set_layer_page_size_and_color(&self, id: PipelineId, page_size: Size2D<uint>, epoch: Epoch, color: Color) {
|
Size2D(width as f32, height as f32)))
|
||||||
let Size2D { width, height } = page_size;
|
|
||||||
self.chan.send(SetUnRenderedColor(id, color));
|
|
||||||
self.chan.send(SetLayerPageSize(id, Size2D(width as f32, height as f32), epoch))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_layer_clip_rect(&self, id: PipelineId, new_rect: Rect<uint>) {
|
fn initialize_layers_for_pipeline(&self,
|
||||||
|
pipeline_id: PipelineId,
|
||||||
|
metadata: ~[LayerMetadata],
|
||||||
|
epoch: Epoch) {
|
||||||
|
// FIXME(pcwalton): This assumes that the first layer determines the page size, and that
|
||||||
|
// all other layers are immediate children of it. This is sufficient to handle
|
||||||
|
// `position: fixed` but will not be sufficient to handle `overflow: scroll` or transforms.
|
||||||
|
let mut first = true;
|
||||||
|
for metadata in metadata.iter() {
|
||||||
|
let origin = Point2D(metadata.rect.origin.x as f32, metadata.rect.origin.y as f32);
|
||||||
|
let size = Size2D(metadata.rect.size.width as f32, metadata.rect.size.height as f32);
|
||||||
|
let rect = Rect(origin, size);
|
||||||
|
if first {
|
||||||
|
self.chan.send(CreateRootCompositorLayerIfNecessary(pipeline_id,
|
||||||
|
metadata.id,
|
||||||
|
size));
|
||||||
|
first = false
|
||||||
|
} else {
|
||||||
|
self.chan
|
||||||
|
.send(CreateDescendantCompositorLayerIfNecessary(pipeline_id,
|
||||||
|
metadata.id,
|
||||||
|
rect,
|
||||||
|
metadata.scroll_policy));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.chan.send(SetUnRenderedColor(pipeline_id, metadata.id, metadata.color));
|
||||||
|
self.chan.send(SetLayerPageSize(pipeline_id, metadata.id, size, epoch));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_layer_clip_rect(&self,
|
||||||
|
pipeline_id: PipelineId,
|
||||||
|
layer_id: LayerId,
|
||||||
|
new_rect: Rect<uint>) {
|
||||||
let new_rect = Rect(Point2D(new_rect.origin.x as f32,
|
let new_rect = Rect(Point2D(new_rect.origin.x as f32,
|
||||||
new_rect.origin.y as f32),
|
new_rect.origin.y as f32),
|
||||||
Size2D(new_rect.size.width as f32,
|
Size2D(new_rect.size.width as f32,
|
||||||
new_rect.size.height as f32));
|
new_rect.size.height as f32));
|
||||||
self.chan.send(SetLayerClipRect(id, new_rect))
|
self.chan.send(SetLayerClipRect(pipeline_id, layer_id, new_rect))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn delete_layer(&self, id: PipelineId) {
|
fn delete_layer_group(&self, id: PipelineId) {
|
||||||
self.chan.send(DeleteLayer(id))
|
self.chan.send(DeleteLayerGroup(id))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_render_state(&self, render_state: RenderState) {
|
fn set_render_state(&self, render_state: RenderState) {
|
||||||
|
@ -133,29 +170,32 @@ pub enum Msg {
|
||||||
/// The headless compositor returns `None`.
|
/// The headless compositor returns `None`.
|
||||||
GetGraphicsMetadata(Chan<Option<NativeGraphicsMetadata>>),
|
GetGraphicsMetadata(Chan<Option<NativeGraphicsMetadata>>),
|
||||||
|
|
||||||
/// Alerts the compositor that there is a new layer to be rendered.
|
/// Tells the compositor to create the root layer for a pipeline if necessary (i.e. if no layer
|
||||||
NewLayer(PipelineId, Size2D<f32>),
|
/// with that ID exists).
|
||||||
/// Alerts the compositor that the specified layer's page has changed size.
|
CreateRootCompositorLayerIfNecessary(PipelineId, LayerId, Size2D<f32>),
|
||||||
SetLayerPageSize(PipelineId, Size2D<f32>, Epoch),
|
/// Tells the compositor to create a descendant layer for a pipeline if necessary (i.e. if no
|
||||||
|
/// layer with that ID exists).
|
||||||
|
CreateDescendantCompositorLayerIfNecessary(PipelineId, LayerId, Rect<f32>, ScrollPolicy),
|
||||||
|
/// Alerts the compositor that the specified layer has changed size.
|
||||||
|
SetLayerPageSize(PipelineId, LayerId, Size2D<f32>, Epoch),
|
||||||
/// Alerts the compositor that the specified layer's clipping rect has changed.
|
/// Alerts the compositor that the specified layer's clipping rect has changed.
|
||||||
SetLayerClipRect(PipelineId, Rect<f32>),
|
SetLayerClipRect(PipelineId, LayerId, Rect<f32>),
|
||||||
/// Alerts the compositor that the specified layer has been deleted.
|
/// Alerts the compositor that the specified pipeline has been deleted.
|
||||||
DeleteLayer(PipelineId),
|
DeleteLayerGroup(PipelineId),
|
||||||
/// Invalidate a rect for a given layer
|
/// Invalidate a rect for a given layer
|
||||||
InvalidateRect(PipelineId, Rect<uint>),
|
InvalidateRect(PipelineId, LayerId, Rect<uint>),
|
||||||
/// Scroll a page in a window
|
/// Scroll a page in a window
|
||||||
ScrollFragmentPoint(PipelineId, Point2D<f32>),
|
ScrollFragmentPoint(PipelineId, LayerId, Point2D<f32>),
|
||||||
/// Requests that the compositor paint the given layer buffer set for the given page size.
|
/// Requests that the compositor paint the given layer buffer set for the given page size.
|
||||||
Paint(PipelineId, ~LayerBufferSet, Epoch),
|
Paint(PipelineId, LayerId, ~LayerBufferSet, Epoch),
|
||||||
/// Alerts the compositor to the current status of page loading.
|
/// Alerts the compositor to the current status of page loading.
|
||||||
ChangeReadyState(ReadyState),
|
ChangeReadyState(ReadyState),
|
||||||
/// Alerts the compositor to the current status of rendering.
|
/// Alerts the compositor to the current status of rendering.
|
||||||
ChangeRenderState(RenderState),
|
ChangeRenderState(RenderState),
|
||||||
/// Sets the channel to the current layout and render tasks, along with their id
|
/// Sets the channel to the current layout and render tasks, along with their id
|
||||||
SetIds(SendableFrameTree, Chan<()>, ConstellationChan),
|
SetIds(SendableFrameTree, Chan<()>, ConstellationChan),
|
||||||
|
/// Sets the color of unrendered content for a layer.
|
||||||
SetUnRenderedColor(PipelineId, Color),
|
SetUnRenderedColor(PipelineId, LayerId, Color),
|
||||||
|
|
||||||
/// The load of a page for a given URL has completed.
|
/// The load of a page for a given URL has completed.
|
||||||
LoadComplete(PipelineId, Url),
|
LoadComplete(PipelineId, Url),
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,10 +77,11 @@ impl NullCompositor {
|
||||||
// we'll notice and think about whether it needs a response, like
|
// we'll notice and think about whether it needs a response, like
|
||||||
// SetIds.
|
// SetIds.
|
||||||
|
|
||||||
NewLayer(..) | SetLayerPageSize(..) | SetLayerClipRect(..) | DeleteLayer(..) |
|
CreateRootCompositorLayerIfNecessary(..) |
|
||||||
Paint(..) | InvalidateRect(..) | ChangeReadyState(..) | ChangeRenderState(..)|
|
CreateDescendantCompositorLayerIfNecessary(..) | SetLayerPageSize(..) |
|
||||||
ScrollFragmentPoint(..) | SetUnRenderedColor(..) | LoadComplete(..)
|
SetLayerClipRect(..) | DeleteLayerGroup(..) | Paint(..) | InvalidateRect(..) |
|
||||||
=> ()
|
ChangeReadyState(..) | ChangeRenderState(..) | ScrollFragmentPoint(..) |
|
||||||
|
SetUnRenderedColor(..) | LoadComplete(..) => ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -460,9 +460,13 @@ impl<T: Tile> QuadtreeNode<T> {
|
||||||
/// treated as invalid as well.
|
/// treated as invalid as well.
|
||||||
/// NOTE: this method will sometimes modify the tree by deleting tiles.
|
/// NOTE: this method will sometimes modify the tree by deleting tiles.
|
||||||
/// See the QuadTree function description for more details.
|
/// See the QuadTree function description for more details.
|
||||||
fn get_tile_rects(&mut self, window: Rect<f32>, clip: Size2D<f32>, scale: f32, tile_size: f32, override: bool) ->
|
fn get_tile_rects(&mut self,
|
||||||
(~[BufferRequest], ~[T], int) {
|
window: Rect<f32>,
|
||||||
|
clip: Size2D<f32>,
|
||||||
|
scale: f32,
|
||||||
|
tile_size: f32,
|
||||||
|
override: bool)
|
||||||
|
-> (~[BufferRequest], ~[T], int) {
|
||||||
let w_x = window.origin.x;
|
let w_x = window.origin.x;
|
||||||
let w_y = window.origin.y;
|
let w_y = window.origin.y;
|
||||||
let w_width = window.size.width;
|
let w_width = window.size.width;
|
||||||
|
@ -470,11 +474,11 @@ impl<T: Tile> QuadtreeNode<T> {
|
||||||
let s_x = self.origin.x;
|
let s_x = self.origin.x;
|
||||||
let s_y = self.origin.y;
|
let s_y = self.origin.y;
|
||||||
let s_size = self.size;
|
let s_size = self.size;
|
||||||
|
|
||||||
// if window is outside of visible region, nothing to do
|
// if window is outside of visible region, nothing to do
|
||||||
if w_x + w_width < s_x || w_x > s_x + s_size
|
if w_x + w_width < s_x || w_x > s_x + s_size
|
||||||
|| w_y + w_height < s_y || w_y > s_y + s_size
|
|| w_y + w_height < s_y || w_y > s_y + s_size
|
||||||
|| w_x >= clip.width || w_y >= clip.height {
|
|| w_x >= clip.width || w_y >= clip.height {
|
||||||
return (~[], ~[], 0);
|
return (~[], ~[], 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ use script::script_task::{ResizeMsg, ResizeInactiveMsg, ExitPipelineMsg};
|
||||||
use script::layout_interface;
|
use script::layout_interface;
|
||||||
use script::layout_interface::LayoutChan;
|
use script::layout_interface::LayoutChan;
|
||||||
use script::script_task::ScriptChan;
|
use script::script_task::ScriptChan;
|
||||||
|
use servo_msg::compositor_msg::LayerId;
|
||||||
use servo_msg::constellation_msg::{ConstellationChan, ExitMsg, FailureMsg, Failure, FrameRectMsg};
|
use servo_msg::constellation_msg::{ConstellationChan, ExitMsg, FailureMsg, Failure, FrameRectMsg};
|
||||||
use servo_msg::constellation_msg::{IFrameSandboxState, IFrameUnsandboxed, InitLoadUrlMsg};
|
use servo_msg::constellation_msg::{IFrameSandboxState, IFrameUnsandboxed, InitLoadUrlMsg};
|
||||||
use servo_msg::constellation_msg::{LoadCompleteMsg, LoadIframeUrlMsg, LoadUrlMsg, Msg, NavigateMsg};
|
use servo_msg::constellation_msg::{LoadCompleteMsg, LoadIframeUrlMsg, LoadUrlMsg, Msg, NavigateMsg};
|
||||||
|
@ -98,15 +99,6 @@ pub struct SendableChildFrameTree {
|
||||||
rect: Option<Rect<f32>>,
|
rect: Option<Rect<f32>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// impl SendableFrameTree {
|
|
||||||
// fn contains(&self, id: PipelineId) -> bool {
|
|
||||||
// self.pipeline.id == id ||
|
|
||||||
// self.children.iter().any(|&SendableChildFrameTree { frame_tree: ref frame_tree, .. }| {
|
|
||||||
// frame_tree.contains(id)
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
enum ReplaceResult {
|
enum ReplaceResult {
|
||||||
ReplacedNode(Rc<FrameTree>),
|
ReplacedNode(Rc<FrameTree>),
|
||||||
OriginalNode(Rc<FrameTree>),
|
OriginalNode(Rc<FrameTree>),
|
||||||
|
@ -511,11 +503,12 @@ impl Constellation {
|
||||||
== subpage_id
|
== subpage_id
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let frames = self.find_all(pipeline_id);
|
||||||
|
|
||||||
{
|
{
|
||||||
// Update a child's frame rect and inform its script task of the change,
|
// Update a child's frame rect and inform its script task of the change,
|
||||||
// if it hasn't been already. Optionally inform the compositor if
|
// if it hasn't been already. Optionally inform the compositor if
|
||||||
// resize happens immediately.
|
// resize happens immediately.
|
||||||
let compositor_chan = self.compositor_chan.clone();
|
|
||||||
let update_child_rect = |child_frame_tree: &mut ChildFrameTree, is_active: bool| {
|
let update_child_rect = |child_frame_tree: &mut ChildFrameTree, is_active: bool| {
|
||||||
child_frame_tree.rect = Some(rect.clone());
|
child_frame_tree.rect = Some(rect.clone());
|
||||||
// NOTE: work around borrowchk issues
|
// NOTE: work around borrowchk issues
|
||||||
|
@ -524,21 +517,19 @@ impl Constellation {
|
||||||
let Size2D { width, height } = rect.size;
|
let Size2D { width, height } = rect.size;
|
||||||
if is_active {
|
if is_active {
|
||||||
let pipeline = pipeline.get().borrow();
|
let pipeline = pipeline.get().borrow();
|
||||||
let ScriptChan(ref chan) = pipeline.script_chan;
|
let ScriptChan(ref script_chan) = pipeline.script_chan;
|
||||||
chan.send(ResizeMsg(pipeline.id, Size2D {
|
script_chan.send(ResizeMsg(pipeline.id, Size2D {
|
||||||
width: width as uint,
|
width: width as uint,
|
||||||
height: height as uint
|
height: height as uint
|
||||||
}));
|
}));
|
||||||
compositor_chan.send(SetLayerClipRect(pipeline.id, rect));
|
self.compositor_chan.send(SetLayerClipRect(pipeline.id,
|
||||||
|
LayerId::null(),
|
||||||
|
rect));
|
||||||
} else {
|
} else {
|
||||||
let pipeline = pipeline.get().borrow();
|
let pipeline = pipeline.get().borrow();
|
||||||
let ScriptChan(ref chan) = pipeline.script_chan;
|
already_sent.insert(pipeline.id);
|
||||||
chan.send(ResizeInactiveMsg(pipeline.id,
|
|
||||||
Size2D(width as uint, height as uint)));
|
|
||||||
}
|
}
|
||||||
let pipeline = pipeline.get().borrow();
|
};
|
||||||
already_sent.insert(pipeline.id);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// If the subframe is in the current frame tree, the compositor needs the new size
|
// If the subframe is in the current frame tree, the compositor needs the new size
|
||||||
|
@ -554,7 +545,6 @@ impl Constellation {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update all frames with matching pipeline- and subpage-ids
|
// Update all frames with matching pipeline- and subpage-ids
|
||||||
let frames = self.find_all(pipeline_id);
|
|
||||||
for frame_tree in frames.iter() {
|
for frame_tree in frames.iter() {
|
||||||
// NOTE: work around borrowchk issues
|
// NOTE: work around borrowchk issues
|
||||||
let mut tmp = frame_tree.borrow().children.borrow_mut();
|
let mut tmp = frame_tree.borrow().children.borrow_mut();
|
||||||
|
|
|
@ -330,6 +330,36 @@ enum BlockType {
|
||||||
FloatNonReplacedType,
|
FloatNonReplacedType,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[deriving(Clone, Eq)]
|
||||||
|
pub enum MarginsMayCollapseFlag {
|
||||||
|
MarginsMayCollapse,
|
||||||
|
MarginsMayNotCollapse,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Propagates the `layers_needed_for_descendants` flag appropriately from a child. This is called
|
||||||
|
// as part of height assignment.
|
||||||
|
//
|
||||||
|
// If any fixed descendants of kids are present, this kid needs a layer.
|
||||||
|
//
|
||||||
|
// FIXME(pcwalton): This is too layer-happy. Like WebKit, we shouldn't do this unless
|
||||||
|
// the positioned descendants are actually on top of the fixed kids.
|
||||||
|
//
|
||||||
|
// TODO(pcwalton): Do this for CSS transforms and opacity too, at least if they're
|
||||||
|
// animating.
|
||||||
|
fn propagate_layer_flag_from_child(layers_needed_for_descendants: &mut bool, kid: &mut Flow) {
|
||||||
|
if kid.is_absolute_containing_block() {
|
||||||
|
let kid_base = flow::mut_base(kid);
|
||||||
|
if kid_base.flags_info.flags.needs_layer() {
|
||||||
|
*layers_needed_for_descendants = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let kid_base = flow::mut_base(kid);
|
||||||
|
if kid_base.flags_info.flags.layers_needed_for_descendants() {
|
||||||
|
*layers_needed_for_descendants = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// A block formatting context.
|
// A block formatting context.
|
||||||
pub struct BlockFlow {
|
pub struct BlockFlow {
|
||||||
/// Data common to all flows.
|
/// Data common to all flows.
|
||||||
|
@ -1180,104 +1210,37 @@ impl BlockFlow {
|
||||||
///
|
///
|
||||||
/// Set the absolute position for children after doing any offsetting for
|
/// Set the absolute position for children after doing any offsetting for
|
||||||
/// position: relative.
|
/// position: relative.
|
||||||
pub fn build_display_list_block<E:ExtraDisplayListData>(
|
pub fn build_display_list_block(&mut self,
|
||||||
&mut self,
|
stacking_context: &mut StackingContext,
|
||||||
builder: &DisplayListBuilder,
|
builder: &mut DisplayListBuilder,
|
||||||
container_block_size: &Size2D<Au>,
|
info: &DisplayListBuildingInfo) {
|
||||||
absolute_cb_abs_position: Point2D<Au>,
|
|
||||||
dirty: &Rect<Au>,
|
|
||||||
index: uint,
|
|
||||||
lists: &RefCell<DisplayListCollection<E>>)
|
|
||||||
-> uint {
|
|
||||||
|
|
||||||
if self.is_float() {
|
if self.is_float() {
|
||||||
self.build_display_list_float(builder, container_block_size, dirty, index, lists);
|
// TODO(pcwalton): This is a pseudo-stacking context. We need to merge `z-index: auto`
|
||||||
return index;
|
// kids into the parent stacking context, when that is supported.
|
||||||
|
self.build_display_list_float(stacking_context, builder, info)
|
||||||
} else if self.is_absolutely_positioned() {
|
} else if self.is_absolutely_positioned() {
|
||||||
return self.build_display_list_abs(builder, container_block_size,
|
self.build_display_list_abs(stacking_context, builder, info)
|
||||||
absolute_cb_abs_position,
|
} else {
|
||||||
dirty, index, lists);
|
self.build_display_list_block_common(stacking_context,
|
||||||
|
builder,
|
||||||
|
info,
|
||||||
|
Point2D(Au(0), Au(0)),
|
||||||
|
BlockLevel)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Shouldn't this be the abs_rect _after_ relative positioning?
|
|
||||||
let abs_rect = Rect(self.base.abs_position, self.base.position.size);
|
|
||||||
if !abs_rect.intersects(dirty) {
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
debug!("build_display_list_block: adding display element");
|
|
||||||
|
|
||||||
let rel_offset = match self.box_ {
|
|
||||||
Some(ref box_) => {
|
|
||||||
box_.relative_position(container_block_size)
|
|
||||||
},
|
|
||||||
None => {
|
|
||||||
Point2D {
|
|
||||||
x: Au::new(0),
|
|
||||||
y: Au::new(0),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// add box that starts block context
|
|
||||||
for box_ in self.box_.iter() {
|
|
||||||
box_.build_display_list(builder, dirty, self.base.abs_position + rel_offset,
|
|
||||||
(&*self) as &Flow, index, lists);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: handle any out-of-flow elements
|
|
||||||
let this_position = self.base.abs_position;
|
|
||||||
for child in self.base.child_iter() {
|
|
||||||
let child_base = flow::mut_base(child);
|
|
||||||
child_base.abs_position = this_position + child_base.position.origin + rel_offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
index
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_display_list_float<E:ExtraDisplayListData>(
|
pub fn build_display_list_float(&mut self,
|
||||||
&mut self,
|
parent_stacking_context: &mut StackingContext,
|
||||||
builder: &DisplayListBuilder,
|
builder: &mut DisplayListBuilder,
|
||||||
container_block_size: &Size2D<Au>,
|
info: &DisplayListBuildingInfo) {
|
||||||
dirty: &Rect<Au>,
|
let mut stacking_context = StackingContext::new();
|
||||||
index: uint,
|
let float_offset = self.float.get_ref().rel_pos;
|
||||||
lists: &RefCell<DisplayListCollection<E>>)
|
self.build_display_list_block_common(&mut stacking_context,
|
||||||
-> bool {
|
builder,
|
||||||
let abs_rect = Rect(self.base.abs_position, self.base.position.size);
|
info,
|
||||||
if !abs_rect.intersects(dirty) {
|
float_offset,
|
||||||
return true;
|
RootOfStackingContextLevel);
|
||||||
}
|
parent_stacking_context.floats.push_all_move(stacking_context.flatten())
|
||||||
|
|
||||||
// position:relative
|
|
||||||
let rel_offset = match self.box_ {
|
|
||||||
Some(ref box_) => {
|
|
||||||
box_.relative_position(container_block_size)
|
|
||||||
},
|
|
||||||
None => {
|
|
||||||
Point2D {
|
|
||||||
x: Au::new(0),
|
|
||||||
y: Au::new(0),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
let offset = self.base.abs_position + self.float.get_ref().rel_pos + rel_offset;
|
|
||||||
// add box that starts block context
|
|
||||||
for box_ in self.box_.iter() {
|
|
||||||
box_.build_display_list(builder, dirty, offset, (&*self) as &Flow, index, lists);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// TODO: handle any out-of-flow elements
|
|
||||||
|
|
||||||
// go deeper into the flow tree
|
|
||||||
for child in self.base.child_iter() {
|
|
||||||
let child_base = flow::mut_base(child);
|
|
||||||
child_base.abs_position = offset + child_base.position.origin + rel_offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate and set the height, offsets, etc. for absolutely positioned flow.
|
/// Calculate and set the height, offsets, etc. for absolutely positioned flow.
|
||||||
|
@ -1351,63 +1314,72 @@ impl BlockFlow {
|
||||||
box_.margin.set(margin);
|
box_.margin.set(margin);
|
||||||
|
|
||||||
let mut position = box_.border_box.get();
|
let mut position = box_.border_box.get();
|
||||||
position.origin.y = box_.margin.get().top;
|
position.origin.y = Au(0);
|
||||||
// Border box height
|
// Border box height
|
||||||
let border_and_padding = box_.noncontent_height();
|
let border_and_padding = box_.noncontent_height();
|
||||||
position.size.height = solution.height + border_and_padding;
|
position.size.height = solution.height + border_and_padding;
|
||||||
box_.border_box.set(position);
|
box_.border_box.set(position);
|
||||||
|
|
||||||
self.base.position.origin.y = solution.top;
|
self.base.position.origin.y = solution.top + margin.top;
|
||||||
self.base.position.size.height = solution.height + border_and_padding
|
self.base.position.size.height = solution.height + border_and_padding;
|
||||||
+ solution.margin_top + solution.margin_bottom;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add display items for Absolutely Positioned flow.
|
/// Add display items for Absolutely Positioned flow.
|
||||||
pub fn build_display_list_abs<E:ExtraDisplayListData>(
|
pub fn build_display_list_abs(&mut self,
|
||||||
&mut self,
|
parent_stacking_context: &mut StackingContext,
|
||||||
builder: &DisplayListBuilder,
|
builder: &mut DisplayListBuilder,
|
||||||
_: &Size2D<Au>,
|
info: &DisplayListBuildingInfo) {
|
||||||
absolute_cb_abs_position: Point2D<Au>,
|
let mut stacking_context = StackingContext::new();
|
||||||
dirty: &Rect<Au>,
|
let mut info = *info;
|
||||||
mut index: uint,
|
|
||||||
lists: &RefCell<DisplayListCollection<E>>)
|
info.absolute_containing_block_position = if self.is_fixed() {
|
||||||
-> uint {
|
|
||||||
let flow_origin = if self.is_fixed() {
|
|
||||||
// The viewport is initially at (0, 0).
|
// The viewport is initially at (0, 0).
|
||||||
self.base.position.origin
|
self.base.position.origin
|
||||||
} else {
|
} else {
|
||||||
// Absolute position of Containing Block + position of absolute flow
|
// Absolute position of Containing Block + position of absolute flow
|
||||||
// wrt Containing Block
|
// wrt Containing Block
|
||||||
absolute_cb_abs_position + self.base.position.origin
|
info.absolute_containing_block_position + self.base.position.origin
|
||||||
};
|
};
|
||||||
|
|
||||||
if self.is_fixed() {
|
|
||||||
lists.with_mut(|lists| {
|
|
||||||
index = lists.lists.len();
|
|
||||||
lists.add_list(DisplayList::<E>::new());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the absolute position, which will be passed down later as part
|
// Set the absolute position, which will be passed down later as part
|
||||||
// of containing block details for absolute descendants.
|
// of containing block details for absolute descendants.
|
||||||
self.base.abs_position = flow_origin;
|
self.base.abs_position = info.absolute_containing_block_position;
|
||||||
let abs_rect = Rect(flow_origin, self.base.position.size);
|
|
||||||
if !abs_rect.intersects(dirty) {
|
self.build_display_list_block_common(&mut stacking_context,
|
||||||
return index;
|
builder,
|
||||||
|
&info,
|
||||||
|
Point2D(Au(0), Au(0)),
|
||||||
|
RootOfStackingContextLevel);
|
||||||
|
|
||||||
|
if !info.layers_needed_for_positioned_flows && !self.base.flags_info.flags.needs_layer() {
|
||||||
|
// We didn't need a layer.
|
||||||
|
//
|
||||||
|
// TODO(pcwalton): `z-index`.
|
||||||
|
parent_stacking_context.positioned_descendants.push((0, stacking_context.flatten()));
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for box_ in self.box_.iter() {
|
// If we got here, then we need a new layer.
|
||||||
box_.build_display_list(builder, dirty, flow_origin, (&*self) as &Flow, index, lists);
|
//
|
||||||
}
|
// FIXME(pcwalton): The color is wrong!
|
||||||
|
let size = Size2D(self.base.position.size.width.to_nearest_px() as uint,
|
||||||
// Go deeper into the flow tree.
|
self.base.position.size.height.to_nearest_px() as uint);
|
||||||
for child in self.base.child_iter() {
|
let origin = Point2D(info.absolute_containing_block_position.x.to_nearest_px() as uint,
|
||||||
let child_base = flow::mut_base(child);
|
info.absolute_containing_block_position.y.to_nearest_px() as uint);
|
||||||
child_base.abs_position = flow_origin + child_base.position.origin;
|
let scroll_policy = if self.is_fixed() {
|
||||||
}
|
FixedPosition
|
||||||
|
} else {
|
||||||
index
|
Scrollable
|
||||||
|
};
|
||||||
|
let new_layer = RenderLayer {
|
||||||
|
id: self.layer_id(0),
|
||||||
|
display_list: Arc::new(stacking_context.flatten()),
|
||||||
|
rect: Rect(origin, size),
|
||||||
|
color: color::rgba(255.0, 255.0, 255.0, 0.0),
|
||||||
|
scroll_policy: scroll_policy,
|
||||||
|
};
|
||||||
|
builder.layers.push(new_layer)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the top outer edge of the Hypothetical Box for an absolute flow.
|
/// Return the top outer edge of the Hypothetical Box for an absolute flow.
|
||||||
|
@ -1663,6 +1635,18 @@ impl Flow for BlockFlow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn layer_id(&self, fragment_index: uint) -> LayerId {
|
||||||
|
// FIXME(pcwalton): This is a hack and is totally bogus in the presence of pseudo-elements.
|
||||||
|
// But until we have incremental reflow we can't do better--we recreate the flow for every
|
||||||
|
// DOM node so otherwise we nuke layers on every reflow.
|
||||||
|
match self.box_ {
|
||||||
|
Some(ref box_) => {
|
||||||
|
LayerId(box_.node.id(), fragment_index)
|
||||||
|
}
|
||||||
|
None => fail!("can't make a layer ID for a flow with no box"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn debug_str(&self) -> ~str {
|
fn debug_str(&self) -> ~str {
|
||||||
let txt = if self.is_float() {
|
let txt = if self.is_float() {
|
||||||
~"FloatFlow: "
|
~"FloatFlow: "
|
||||||
|
|
|
@ -951,12 +951,10 @@ impl Box {
|
||||||
(Au::new(0), Au::new(0))
|
(Au::new(0), Au::new(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn paint_inline_background_border_if_applicable<E:ExtraDisplayListData>(
|
pub fn paint_inline_background_border_if_applicable(&self,
|
||||||
&self,
|
list: &mut DisplayList,
|
||||||
index: uint,
|
absolute_bounds: &Rect<Au>,
|
||||||
lists: &RefCell<DisplayListCollection<E>>,
|
offset: &Point2D<Au>) {
|
||||||
absolute_bounds: &Rect<Au>,
|
|
||||||
offset: &Point2D<Au>) {
|
|
||||||
// FIXME: This causes a lot of background colors to be displayed when they are clearly not
|
// FIXME: This causes a lot of background colors to be displayed when they are clearly not
|
||||||
// needed. We could use display list optimization to clean this up, but it still seems
|
// needed. We could use display list optimization to clean this up, but it still seems
|
||||||
// inefficient. What we really want is something like "nearest ancestor element that
|
// inefficient. What we really want is something like "nearest ancestor element that
|
||||||
|
@ -965,7 +963,7 @@ impl Box {
|
||||||
match info.get() {
|
match info.get() {
|
||||||
&Some(ref box_info) => {
|
&Some(ref box_info) => {
|
||||||
let mut bg_rect = absolute_bounds.clone();
|
let mut bg_rect = absolute_bounds.clone();
|
||||||
for info in box_info.parent_info.rev_iter() {
|
for info in box_info.parent_info.as_slice().rev_iter() {
|
||||||
// TODO (ksh8281) compute vertical-align, line-height
|
// TODO (ksh8281) compute vertical-align, line-height
|
||||||
bg_rect.origin.y = box_info.baseline + offset.y - info.font_ascent;
|
bg_rect.origin.y = box_info.baseline + offset.y - info.font_ascent;
|
||||||
bg_rect.size.height = info.font_ascent + info.font_descent;
|
bg_rect.size.height = info.font_ascent + info.font_descent;
|
||||||
|
@ -973,23 +971,24 @@ impl Box {
|
||||||
info.style.get().Background.get().background_color);
|
info.style.get().Background.get().background_color);
|
||||||
|
|
||||||
if !background_color.alpha.approx_eq(&0.0) {
|
if !background_color.alpha.approx_eq(&0.0) {
|
||||||
lists.with_mut(|lists| {
|
let solid_color_display_item = ~SolidColorDisplayItem {
|
||||||
let solid_color_display_item = ~SolidColorDisplayItem {
|
base: BaseDisplayItem {
|
||||||
base: BaseDisplayItem {
|
bounds: bg_rect.clone(),
|
||||||
bounds: bg_rect.clone(),
|
node: self.node,
|
||||||
extra: ExtraDisplayListData::new(self),
|
},
|
||||||
},
|
color: background_color.to_gfx_color(),
|
||||||
color: background_color.to_gfx_color(),
|
};
|
||||||
};
|
|
||||||
|
|
||||||
lists.lists[index].append_item(SolidColorDisplayItemClass(solid_color_display_item))
|
list.push(SolidColorDisplayItemClass(solid_color_display_item))
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let border = &info.border;
|
let border = &info.border;
|
||||||
|
|
||||||
// Fast path.
|
// Fast path.
|
||||||
if border.is_zero() {
|
if border.is_zero() {
|
||||||
continue;
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
bg_rect.origin.y = bg_rect.origin.y - border.top;
|
bg_rect.origin.y = bg_rect.origin.y - border.top;
|
||||||
bg_rect.size.height = bg_rect.size.height + border.top + border.bottom;
|
bg_rect.size.height = bg_rect.size.height + border.top + border.bottom;
|
||||||
|
|
||||||
|
@ -1003,26 +1002,20 @@ impl Box {
|
||||||
let bottom_style = style.Border.get().border_bottom_style;
|
let bottom_style = style.Border.get().border_bottom_style;
|
||||||
let left_style = style.Border.get().border_left_style;
|
let left_style = style.Border.get().border_left_style;
|
||||||
|
|
||||||
|
let border_display_item = ~BorderDisplayItem {
|
||||||
|
base: BaseDisplayItem {
|
||||||
|
bounds: bg_rect,
|
||||||
|
node: self.node,
|
||||||
|
},
|
||||||
|
border: border.clone(),
|
||||||
|
color: SideOffsets2D::new(top_color.to_gfx_color(),
|
||||||
|
right_color.to_gfx_color(),
|
||||||
|
bottom_color.to_gfx_color(),
|
||||||
|
left_color.to_gfx_color()),
|
||||||
|
style: SideOffsets2D::new(top_style, right_style, bottom_style, left_style)
|
||||||
|
};
|
||||||
|
|
||||||
lists.with_mut(|lists| {
|
list.push(BorderDisplayItemClass(border_display_item));
|
||||||
let border_display_item = ~BorderDisplayItem {
|
|
||||||
base: BaseDisplayItem {
|
|
||||||
bounds: bg_rect,
|
|
||||||
extra: ExtraDisplayListData::new(self),
|
|
||||||
},
|
|
||||||
border: border.clone(),
|
|
||||||
color: SideOffsets2D::new(top_color.to_gfx_color(),
|
|
||||||
right_color.to_gfx_color(),
|
|
||||||
bottom_color.to_gfx_color(),
|
|
||||||
left_color.to_gfx_color()),
|
|
||||||
style: SideOffsets2D::new(top_style,
|
|
||||||
right_style,
|
|
||||||
bottom_style,
|
|
||||||
left_style)
|
|
||||||
};
|
|
||||||
|
|
||||||
lists.lists[index].append_item(BorderDisplayItemClass(border_display_item))
|
|
||||||
});
|
|
||||||
|
|
||||||
bg_rect.origin.x = bg_rect.origin.x + border.left;
|
bg_rect.origin.x = bg_rect.origin.x + border.left;
|
||||||
bg_rect.size.width = bg_rect.size.width - border.left - border.right;
|
bg_rect.size.width = bg_rect.size.width - border.left - border.right;
|
||||||
|
@ -1033,11 +1026,9 @@ impl Box {
|
||||||
}
|
}
|
||||||
/// Adds the display items necessary to paint the background of this box to the display list if
|
/// Adds the display items necessary to paint the background of this box to the display list if
|
||||||
/// necessary.
|
/// necessary.
|
||||||
pub fn paint_background_if_applicable<E:ExtraDisplayListData>(
|
pub fn paint_background_if_applicable(&self,
|
||||||
&self,
|
list: &mut DisplayList,
|
||||||
builder: &DisplayListBuilder,
|
builder: &DisplayListBuilder,
|
||||||
index: uint,
|
|
||||||
lists: &RefCell<DisplayListCollection<E>>,
|
|
||||||
absolute_bounds: &Rect<Au>) {
|
absolute_bounds: &Rect<Au>) {
|
||||||
// FIXME: This causes a lot of background colors to be displayed when they are clearly not
|
// FIXME: This causes a lot of background colors to be displayed when they are clearly not
|
||||||
// needed. We could use display list optimization to clean this up, but it still seems
|
// needed. We could use display list optimization to clean this up, but it still seems
|
||||||
|
@ -1046,17 +1037,15 @@ impl Box {
|
||||||
let style = self.style();
|
let style = self.style();
|
||||||
let background_color = style.resolve_color(style.Background.get().background_color);
|
let background_color = style.resolve_color(style.Background.get().background_color);
|
||||||
if !background_color.alpha.approx_eq(&0.0) {
|
if !background_color.alpha.approx_eq(&0.0) {
|
||||||
lists.with_mut(|lists| {
|
let display_item = ~SolidColorDisplayItem {
|
||||||
let solid_color_display_item = ~SolidColorDisplayItem {
|
base: BaseDisplayItem {
|
||||||
base: BaseDisplayItem {
|
bounds: *absolute_bounds,
|
||||||
bounds: *absolute_bounds,
|
node: self.node,
|
||||||
extra: ExtraDisplayListData::new(self),
|
},
|
||||||
},
|
color: background_color.to_gfx_color(),
|
||||||
color: background_color.to_gfx_color(),
|
};
|
||||||
};
|
|
||||||
|
|
||||||
lists.lists[index].append_item(SolidColorDisplayItemClass(solid_color_display_item))
|
list.push(SolidColorDisplayItemClass(display_item))
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The background image is painted on top of the background color.
|
// The background image is painted on top of the background color.
|
||||||
|
@ -1064,22 +1053,81 @@ impl Box {
|
||||||
// http://www.w3.org/TR/CSS21/colors.html#background
|
// http://www.w3.org/TR/CSS21/colors.html#background
|
||||||
match style.Background.get().background_image {
|
match style.Background.get().background_image {
|
||||||
Some(ref image_url) => {
|
Some(ref image_url) => {
|
||||||
let mut holder = ImageHolder::new(image_url.clone(), builder.ctx.image_cache.clone());
|
let mut holder = ImageHolder::new(image_url.clone(),
|
||||||
|
builder.ctx.image_cache.clone());
|
||||||
match holder.get_image() {
|
match holder.get_image() {
|
||||||
Some(image) => {
|
Some(image) => {
|
||||||
debug!("(building display list) building background image");
|
debug!("(building display list) building background image");
|
||||||
|
|
||||||
// Place the image into the display list.
|
// Adjust bounds for `background-position` and `background-attachment`.
|
||||||
lists.with_mut(|lists| {
|
let mut bounds = *absolute_bounds;
|
||||||
let image_display_item = ~ImageDisplayItem {
|
let horizontal_position = model::specified(
|
||||||
base: BaseDisplayItem {
|
style.Background.get().background_position.horizontal,
|
||||||
bounds: *absolute_bounds,
|
bounds.size.width);
|
||||||
extra: ExtraDisplayListData::new(self),
|
let vertical_position = model::specified(
|
||||||
},
|
style.Background.get().background_position.vertical,
|
||||||
image: image.clone(),
|
bounds.size.height);
|
||||||
};
|
|
||||||
lists.lists[index].append_item(ImageDisplayItemClass(image_display_item));
|
let clip_display_item;
|
||||||
|
match style.Background.get().background_attachment {
|
||||||
|
background_attachment::scroll => {
|
||||||
|
clip_display_item = None;
|
||||||
|
bounds.origin.x = bounds.origin.x + horizontal_position;
|
||||||
|
bounds.origin.y = bounds.origin.y + vertical_position;
|
||||||
|
bounds.size.width = bounds.size.width - horizontal_position;
|
||||||
|
bounds.size.height = bounds.size.height - vertical_position;
|
||||||
|
}
|
||||||
|
background_attachment::fixed => {
|
||||||
|
clip_display_item = Some(~ClipDisplayItem {
|
||||||
|
base: BaseDisplayItem {
|
||||||
|
bounds: bounds,
|
||||||
|
node: self.node,
|
||||||
|
},
|
||||||
|
child_list: SmallVec0::new(),
|
||||||
|
need_clip: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
bounds = Rect {
|
||||||
|
origin: Point2D(horizontal_position, vertical_position),
|
||||||
|
size: Size2D(bounds.origin.x + bounds.size.width,
|
||||||
|
bounds.origin.y + bounds.size.height),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Adjust sizes for `background-repeat`.
|
||||||
|
match style.Background.get().background_repeat {
|
||||||
|
background_repeat::no_repeat => {
|
||||||
|
bounds.size.width = Au::from_px(image.get().width as int);
|
||||||
|
bounds.size.height = Au::from_px(image.get().height as int)
|
||||||
|
}
|
||||||
|
background_repeat::repeat_x => {
|
||||||
|
bounds.size.height = Au::from_px(image.get().height as int)
|
||||||
|
}
|
||||||
|
background_repeat::repeat_y => {
|
||||||
|
bounds.size.width = Au::from_px(image.get().width as int)
|
||||||
|
}
|
||||||
|
background_repeat::repeat => {}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Create the image display item.
|
||||||
|
let image_display_item = ImageDisplayItemClass(~ImageDisplayItem {
|
||||||
|
base: BaseDisplayItem {
|
||||||
|
bounds: bounds,
|
||||||
|
node: self.node,
|
||||||
|
},
|
||||||
|
image: image.clone(),
|
||||||
|
stretch_size: Size2D(Au::from_px(image.get().width as int),
|
||||||
|
Au::from_px(image.get().height as int)),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
match clip_display_item {
|
||||||
|
None => list.push(image_display_item),
|
||||||
|
Some(mut clip_display_item) => {
|
||||||
|
clip_display_item.child_list.push(image_display_item);
|
||||||
|
list.push(ClipDisplayItemClass(clip_display_item))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
// No image data at all? Do nothing.
|
// No image data at all? Do nothing.
|
||||||
|
@ -1095,11 +1143,7 @@ impl Box {
|
||||||
|
|
||||||
/// Adds the display items necessary to paint the borders of this box to a display list if
|
/// Adds the display items necessary to paint the borders of this box to a display list if
|
||||||
/// necessary.
|
/// necessary.
|
||||||
pub fn paint_borders_if_applicable<E:ExtraDisplayListData>(
|
pub fn paint_borders_if_applicable(&self, list: &mut DisplayList, abs_bounds: &Rect<Au>) {
|
||||||
&self,
|
|
||||||
index: uint,
|
|
||||||
lists: &RefCell<DisplayListCollection<E>>,
|
|
||||||
abs_bounds: &Rect<Au>) {
|
|
||||||
// Fast path.
|
// Fast path.
|
||||||
let border = self.border.get();
|
let border = self.border.get();
|
||||||
if border.is_zero() {
|
if border.is_zero() {
|
||||||
|
@ -1122,75 +1166,142 @@ impl Box {
|
||||||
- self.noncontent_inline_right();
|
- self.noncontent_inline_right();
|
||||||
|
|
||||||
// Append the border to the display list.
|
// Append the border to the display list.
|
||||||
lists.with_mut(|lists| {
|
let border_display_item = ~BorderDisplayItem {
|
||||||
let border_display_item = ~BorderDisplayItem {
|
base: BaseDisplayItem {
|
||||||
base: BaseDisplayItem {
|
bounds: abs_bounds,
|
||||||
bounds: abs_bounds,
|
node: self.node,
|
||||||
extra: ExtraDisplayListData::new(self),
|
},
|
||||||
},
|
border: border,
|
||||||
border: border,
|
color: SideOffsets2D::new(top_color.to_gfx_color(),
|
||||||
color: SideOffsets2D::new(top_color.to_gfx_color(),
|
right_color.to_gfx_color(),
|
||||||
right_color.to_gfx_color(),
|
bottom_color.to_gfx_color(),
|
||||||
bottom_color.to_gfx_color(),
|
left_color.to_gfx_color()),
|
||||||
left_color.to_gfx_color()),
|
style: SideOffsets2D::new(top_style,
|
||||||
style: SideOffsets2D::new(top_style,
|
right_style,
|
||||||
right_style,
|
bottom_style,
|
||||||
bottom_style,
|
left_style)
|
||||||
left_style)
|
};
|
||||||
};
|
|
||||||
|
|
||||||
lists.lists[index].append_item(BorderDisplayItemClass(border_display_item))
|
list.push(BorderDisplayItemClass(border_display_item))
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds the display items for this box to the given display list.
|
fn build_debug_borders_around_text_boxes(&self,
|
||||||
|
stacking_context: &mut StackingContext,
|
||||||
|
flow_origin: Point2D<Au>,
|
||||||
|
text_box: &ScannedTextBoxInfo) {
|
||||||
|
let box_bounds = self.border_box.get();
|
||||||
|
let absolute_box_bounds = box_bounds.translate(&flow_origin);
|
||||||
|
|
||||||
|
// Compute the text box bounds and draw a border surrounding them.
|
||||||
|
let debug_border = SideOffsets2D::new_all_same(Au::from_px(1));
|
||||||
|
|
||||||
|
let border_display_item = ~BorderDisplayItem {
|
||||||
|
base: BaseDisplayItem {
|
||||||
|
bounds: absolute_box_bounds,
|
||||||
|
node: self.node,
|
||||||
|
},
|
||||||
|
border: debug_border,
|
||||||
|
color: SideOffsets2D::new_all_same(rgb(0, 0, 200)),
|
||||||
|
style: SideOffsets2D::new_all_same(border_style::solid)
|
||||||
|
|
||||||
|
};
|
||||||
|
stacking_context.content.push(BorderDisplayItemClass(border_display_item));
|
||||||
|
|
||||||
|
// Draw a rectangle representing the baselines.
|
||||||
|
let ascent = text_box.run.get().metrics_for_range(&text_box.range).ascent;
|
||||||
|
let baseline = Rect(absolute_box_bounds.origin + Point2D(Au(0), ascent),
|
||||||
|
Size2D(absolute_box_bounds.size.width, Au(0)));
|
||||||
|
|
||||||
|
let line_display_item = ~LineDisplayItem {
|
||||||
|
base: BaseDisplayItem {
|
||||||
|
bounds: baseline,
|
||||||
|
node: self.node,
|
||||||
|
},
|
||||||
|
color: rgb(0, 200, 0),
|
||||||
|
style: border_style::dashed,
|
||||||
|
|
||||||
|
};
|
||||||
|
stacking_context.content.push(LineDisplayItemClass(line_display_item))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_debug_borders_around_box(&self,
|
||||||
|
stacking_context: &mut StackingContext,
|
||||||
|
flow_origin: Point2D<Au>) {
|
||||||
|
let box_bounds = self.border_box.get();
|
||||||
|
let absolute_box_bounds = box_bounds.translate(&flow_origin);
|
||||||
|
|
||||||
|
// This prints a debug border around the border of this box.
|
||||||
|
let debug_border = SideOffsets2D::new_all_same(Au::from_px(1));
|
||||||
|
|
||||||
|
let border_display_item = ~BorderDisplayItem {
|
||||||
|
base: BaseDisplayItem {
|
||||||
|
bounds: absolute_box_bounds,
|
||||||
|
node: self.node,
|
||||||
|
},
|
||||||
|
border: debug_border,
|
||||||
|
color: SideOffsets2D::new_all_same(rgb(0, 0, 200)),
|
||||||
|
style: SideOffsets2D::new_all_same(border_style::solid)
|
||||||
|
|
||||||
|
};
|
||||||
|
stacking_context.content.push(BorderDisplayItemClass(border_display_item))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds the display items for this box to the given stacking context.
|
||||||
///
|
///
|
||||||
/// Arguments:
|
/// Arguments:
|
||||||
|
///
|
||||||
|
/// * `stacking_context`: The stacking context to add display items to.
|
||||||
/// * `builder`: The display list builder, which manages the coordinate system and options.
|
/// * `builder`: The display list builder, which manages the coordinate system and options.
|
||||||
/// * `dirty`: The dirty rectangle in the coordinate system of the owning flow.
|
/// * `dirty`: The dirty rectangle in the coordinate system of the owning flow.
|
||||||
/// * `flow_origin`: Position of the origin of the owning flow wrt the display list root flow.
|
/// * `flow_origin`: Position of the origin of the owning flow wrt the display list root flow.
|
||||||
/// box.
|
/// box.
|
||||||
/// * `list`: The display list to which items should be appended.
|
/// * `flow`: The flow that this box belongs to.
|
||||||
///
|
pub fn build_display_list(&self,
|
||||||
/// TODO: To implement stacking contexts correctly, we need to create a set of display lists,
|
stacking_context: &mut StackingContext,
|
||||||
/// one per layer of the stacking context (CSS 2.1 § 9.9.1). Each box is passed the list set
|
|
||||||
/// representing the box's stacking context. When asked to construct its constituent display
|
|
||||||
/// items, each box puts its display items into the correct stack layer according to CSS 2.1
|
|
||||||
/// Appendix E. Finally, the builder flattens the list.
|
|
||||||
pub fn build_display_list<E:ExtraDisplayListData>(
|
|
||||||
&self,
|
|
||||||
builder: &DisplayListBuilder,
|
builder: &DisplayListBuilder,
|
||||||
dirty: &Rect<Au>,
|
_: &DisplayListBuildingInfo,
|
||||||
flow_origin: Point2D<Au>,
|
flow_origin: Point2D<Au>,
|
||||||
flow: &Flow,
|
flow: &Flow,
|
||||||
index: uint,
|
background_and_border_level: BackgroundAndBorderLevel) {
|
||||||
lists: &RefCell<DisplayListCollection<E>>) {
|
|
||||||
// Box position wrt to the owning flow.
|
// Box position wrt to the owning flow.
|
||||||
let box_bounds = self.border_box.get();
|
let box_bounds = self.border_box.get();
|
||||||
let absolute_box_bounds = box_bounds.translate(&flow_origin);
|
let absolute_box_bounds = box_bounds.translate(&flow_origin);
|
||||||
debug!("Box::build_display_list at rel={}, abs={}: {:s}",
|
debug!("Box::build_display_list at rel={}, abs={}: {:s}",
|
||||||
box_bounds, absolute_box_bounds, self.debug_str());
|
box_bounds,
|
||||||
debug!("Box::build_display_list: dirty={}, flow_origin={}", *dirty, flow_origin);
|
absolute_box_bounds,
|
||||||
|
self.debug_str());
|
||||||
|
debug!("Box::build_display_list: dirty={}, flow_origin={}", builder.dirty, flow_origin);
|
||||||
|
|
||||||
if self.style().InheritedBox.get().visibility != visibility::visible {
|
if self.style().InheritedBox.get().visibility != visibility::visible {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if absolute_box_bounds.intersects(dirty) {
|
if !absolute_box_bounds.intersects(&builder.dirty) {
|
||||||
debug!("Box::build_display_list: intersected. Adding display item...");
|
|
||||||
} else {
|
|
||||||
debug!("Box::build_display_list: Did not intersect...");
|
debug!("Box::build_display_list: Did not intersect...");
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
self.paint_inline_background_border_if_applicable(index, lists, &absolute_box_bounds, &flow_origin);
|
debug!("Box::build_display_list: intersected. Adding display item...");
|
||||||
// Add the background to the list, if applicable.
|
|
||||||
self.paint_background_if_applicable(builder, index, lists, &absolute_box_bounds);
|
|
||||||
|
|
||||||
// Add a border, if applicable.
|
{
|
||||||
//
|
let list =
|
||||||
// TODO: Outlines.
|
stacking_context.list_for_background_and_border_level(background_and_border_level);
|
||||||
self.paint_borders_if_applicable(index, lists, &absolute_box_bounds);
|
|
||||||
|
// Add a background to the list, if this is an inline.
|
||||||
|
//
|
||||||
|
// FIXME(pcwalton): This is kind of ugly; merge with the call below?
|
||||||
|
self.paint_inline_background_border_if_applicable(list,
|
||||||
|
&absolute_box_bounds,
|
||||||
|
&flow_origin);
|
||||||
|
|
||||||
|
// Add the background to the list, if applicable.
|
||||||
|
self.paint_background_if_applicable(list, builder, &absolute_box_bounds);
|
||||||
|
|
||||||
|
// Add a border, if applicable.
|
||||||
|
//
|
||||||
|
// TODO: Outlines.
|
||||||
|
self.paint_borders_if_applicable(list, &absolute_box_bounds);
|
||||||
|
}
|
||||||
|
|
||||||
match self.specific {
|
match self.specific {
|
||||||
UnscannedTextBox(_) => fail!("Shouldn't see unscanned boxes here."),
|
UnscannedTextBox(_) => fail!("Shouldn't see unscanned boxes here."),
|
||||||
|
@ -1224,100 +1335,45 @@ impl Box {
|
||||||
- self.noncontent_inline_right();
|
- self.noncontent_inline_right();
|
||||||
|
|
||||||
// Create the text box.
|
// Create the text box.
|
||||||
lists.with_mut(|lists| {
|
let text_display_item = ~TextDisplayItem {
|
||||||
let text_display_item = ~TextDisplayItem {
|
base: BaseDisplayItem {
|
||||||
base: BaseDisplayItem {
|
bounds: bounds,
|
||||||
bounds: bounds,
|
node: self.node,
|
||||||
extra: ExtraDisplayListData::new(self),
|
},
|
||||||
},
|
text_run: text_box.run.clone(),
|
||||||
text_run: text_box.run.clone(),
|
range: text_box.range,
|
||||||
range: text_box.range,
|
text_color: text_color,
|
||||||
text_color: text_color,
|
overline_color: flow_flags.overline_color(text_color),
|
||||||
overline_color: flow_flags.overline_color(text_color),
|
underline_color: flow_flags.underline_color(text_color),
|
||||||
underline_color: flow_flags.underline_color(text_color),
|
line_through_color: flow_flags.line_through_color(text_color),
|
||||||
line_through_color: flow_flags.line_through_color(text_color),
|
flags: text_flags,
|
||||||
flags: text_flags,
|
};
|
||||||
};
|
|
||||||
|
|
||||||
lists.lists[index].append_item(TextDisplayItemClass(text_display_item));
|
stacking_context.content.push(TextDisplayItemClass(text_display_item));
|
||||||
});
|
|
||||||
|
|
||||||
// Draw debug frames for text bounds.
|
// Draw debug frames for text bounds.
|
||||||
//
|
//
|
||||||
// FIXME(pcwalton): This is a bit of an abuse of the logging infrastructure. We
|
// FIXME(pcwalton): This is a bit of an abuse of the logging infrastructure. We
|
||||||
// should have a real `SERVO_DEBUG` system.
|
// should have a real `SERVO_DEBUG` system.
|
||||||
debug!("{:?}", {
|
debug!("{:?}", self.build_debug_borders_around_text_boxes(stacking_context,
|
||||||
// Compute the text box bounds and draw a border surrounding them.
|
flow_origin,
|
||||||
let debug_border = SideOffsets2D::new_all_same(Au::from_px(1));
|
text_box))
|
||||||
|
|
||||||
lists.with_mut(|lists| {
|
|
||||||
let border_display_item = ~BorderDisplayItem {
|
|
||||||
base: BaseDisplayItem {
|
|
||||||
bounds: absolute_box_bounds,
|
|
||||||
extra: ExtraDisplayListData::new(self),
|
|
||||||
},
|
|
||||||
border: debug_border,
|
|
||||||
color: SideOffsets2D::new_all_same(rgb(0, 0, 200)),
|
|
||||||
style: SideOffsets2D::new_all_same(border_style::solid)
|
|
||||||
|
|
||||||
};
|
|
||||||
lists.lists[index].append_item(BorderDisplayItemClass(border_display_item));
|
|
||||||
});
|
|
||||||
|
|
||||||
// Draw a rectangle representing the baselines.
|
|
||||||
let ascent = text_box.run.get().metrics_for_range(
|
|
||||||
&text_box.range).ascent;
|
|
||||||
let baseline = Rect(absolute_box_bounds.origin + Point2D(Au(0), ascent),
|
|
||||||
Size2D(absolute_box_bounds.size.width, Au(0)));
|
|
||||||
|
|
||||||
lists.with_mut(|lists| {
|
|
||||||
let line_display_item = ~LineDisplayItem {
|
|
||||||
base: BaseDisplayItem {
|
|
||||||
bounds: baseline,
|
|
||||||
extra: ExtraDisplayListData::new(self),
|
|
||||||
},
|
|
||||||
color: rgb(0, 200, 0),
|
|
||||||
style: border_style::dashed
|
|
||||||
|
|
||||||
};
|
|
||||||
lists.lists[index].append_item(LineDisplayItemClass(line_display_item));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
GenericBox | IframeBox(..) | TableBox | TableCellBox | TableRowBox |
|
GenericBox | IframeBox(..) | TableBox | TableCellBox | TableRowBox |
|
||||||
TableWrapperBox => {
|
TableWrapperBox => {
|
||||||
lists.with_mut(|lists| {
|
let item = ~ClipDisplayItem {
|
||||||
let item = ~ClipDisplayItem {
|
base: BaseDisplayItem {
|
||||||
base: BaseDisplayItem {
|
bounds: absolute_box_bounds,
|
||||||
bounds: absolute_box_bounds,
|
node: self.node,
|
||||||
extra: ExtraDisplayListData::new(self),
|
},
|
||||||
},
|
child_list: SmallVec0::new(),
|
||||||
child_list: ~[],
|
need_clip: self.needs_clip()
|
||||||
need_clip: self.needs_clip()
|
};
|
||||||
};
|
stacking_context.content.push(ClipDisplayItemClass(item));
|
||||||
lists.lists[index].append_item(ClipDisplayItemClass(item));
|
|
||||||
});
|
|
||||||
|
|
||||||
// FIXME(pcwalton): This is a bit of an abuse of the logging infrastructure. We
|
// FIXME(pcwalton): This is a bit of an abuse of the logging infrastructure. We
|
||||||
// should have a real `SERVO_DEBUG` system.
|
// should have a real `SERVO_DEBUG` system.
|
||||||
debug!("{:?}", {
|
debug!("{:?}", self.build_debug_borders_around_box(stacking_context, flow_origin))
|
||||||
// This prints a debug border around the border of this box.
|
|
||||||
let debug_border = SideOffsets2D::new_all_same(Au::from_px(1));
|
|
||||||
|
|
||||||
lists.with_mut(|lists| {
|
|
||||||
let border_display_item = ~BorderDisplayItem {
|
|
||||||
base: BaseDisplayItem {
|
|
||||||
bounds: absolute_box_bounds,
|
|
||||||
extra: ExtraDisplayListData::new(self),
|
|
||||||
},
|
|
||||||
border: debug_border,
|
|
||||||
color: SideOffsets2D::new_all_same(rgb(0, 0, 200)),
|
|
||||||
style: SideOffsets2D::new_all_same(border_style::solid)
|
|
||||||
|
|
||||||
};
|
|
||||||
lists.lists[index].append_item(BorderDisplayItemClass(border_display_item));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
ImageBox(ref image_box) => {
|
ImageBox(ref image_box) => {
|
||||||
let mut image_ref = image_box.image.borrow_mut();
|
let mut image_ref = image_box.image.borrow_mut();
|
||||||
|
@ -1335,16 +1391,15 @@ impl Box {
|
||||||
debug!("(building display list) building image box");
|
debug!("(building display list) building image box");
|
||||||
|
|
||||||
// Place the image into the display list.
|
// Place the image into the display list.
|
||||||
lists.with_mut(|lists| {
|
let image_display_item = ~ImageDisplayItem {
|
||||||
let image_display_item = ~ImageDisplayItem {
|
base: BaseDisplayItem {
|
||||||
base: BaseDisplayItem {
|
bounds: bounds,
|
||||||
bounds: bounds,
|
node: self.node,
|
||||||
extra: ExtraDisplayListData::new(self),
|
},
|
||||||
},
|
image: image.clone(),
|
||||||
image: image.clone(),
|
stretch_size: bounds.size,
|
||||||
};
|
};
|
||||||
lists.lists[index].append_item(ImageDisplayItemClass(image_display_item));
|
stacking_context.content.push(ImageDisplayItemClass(image_display_item))
|
||||||
});
|
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
// No image data at all? Do nothing.
|
// No image data at all? Do nothing.
|
||||||
|
@ -1355,24 +1410,7 @@ impl Box {
|
||||||
}
|
}
|
||||||
// FIXME(pcwalton): This is a bit of an abuse of the logging infrastructure. We
|
// FIXME(pcwalton): This is a bit of an abuse of the logging infrastructure. We
|
||||||
// should have a real `SERVO_DEBUG` system.
|
// should have a real `SERVO_DEBUG` system.
|
||||||
debug!("{:?}", {
|
debug!("{:?}", self.build_debug_borders_around_box(stacking_context, flow_origin))
|
||||||
let debug_border = SideOffsets2D::new_all_same(Au::from_px(1));
|
|
||||||
|
|
||||||
lists.with_mut(|lists| {
|
|
||||||
let border_display_item = ~BorderDisplayItem {
|
|
||||||
base: BaseDisplayItem {
|
|
||||||
bounds: absolute_box_bounds,
|
|
||||||
extra: ExtraDisplayListData::new(self),
|
|
||||||
},
|
|
||||||
border: debug_border,
|
|
||||||
color: SideOffsets2D::new_all_same(rgb(0, 0, 200)),
|
|
||||||
style: SideOffsets2D::new_all_same(border_style::solid)
|
|
||||||
|
|
||||||
};
|
|
||||||
lists.lists[index].append_item(BorderDisplayItemClass(border_display_item))
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1440,8 +1478,8 @@ impl Box {
|
||||||
UnscannedTextBox(_) => fail!("Unscanned text boxes should have been scanned by now!"),
|
UnscannedTextBox(_) => fail!("Unscanned text boxes should have been scanned by now!"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns, and computes, the height of this box.
|
/// Returns, and computes, the height of this box.
|
||||||
///
|
|
||||||
pub fn content_height(&self) -> Au {
|
pub fn content_height(&self) -> Au {
|
||||||
match self.specific {
|
match self.specific {
|
||||||
GenericBox | IframeBox(_) | TableBox | TableCellBox | TableRowBox |
|
GenericBox | IframeBox(_) | TableBox | TableCellBox | TableRowBox |
|
||||||
|
|
|
@ -5,10 +5,10 @@
|
||||||
//! Data needed by the layout task.
|
//! Data needed by the layout task.
|
||||||
|
|
||||||
use css::matching::{ApplicableDeclarationsCache, StyleSharingCandidateCache};
|
use css::matching::{ApplicableDeclarationsCache, StyleSharingCandidateCache};
|
||||||
use layout::util::OpaqueNode;
|
|
||||||
|
|
||||||
use extra::url::Url;
|
use extra::url::Url;
|
||||||
use geom::size::Size2D;
|
use geom::size::Size2D;
|
||||||
|
use gfx::display_list::OpaqueNode;
|
||||||
use gfx::font_context::{FontContext, FontContextInfo};
|
use gfx::font_context::{FontContext, FontContextInfo};
|
||||||
use green::task::GreenTask;
|
use green::task::GreenTask;
|
||||||
use script::layout_interface::LayoutChan;
|
use script::layout_interface::LayoutChan;
|
||||||
|
|
|
@ -4,39 +4,34 @@
|
||||||
|
|
||||||
//! Constructs display lists from boxes.
|
//! Constructs display lists from boxes.
|
||||||
|
|
||||||
use layout::box_::Box;
|
|
||||||
use layout::context::LayoutContext;
|
use layout::context::LayoutContext;
|
||||||
use layout::util::OpaqueNode;
|
|
||||||
|
|
||||||
|
use geom::{Point2D, Rect, Size2D};
|
||||||
|
use gfx::render_task::RenderLayer;
|
||||||
use gfx;
|
use gfx;
|
||||||
|
use servo_util::geometry::Au;
|
||||||
|
use servo_util::smallvec::SmallVec0;
|
||||||
use style;
|
use style;
|
||||||
|
|
||||||
pub trait ExtraDisplayListData {
|
/// Manages the information needed to construct the display list.
|
||||||
fn new(box_: &Box) -> Self;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type Nothing = ();
|
|
||||||
|
|
||||||
impl ExtraDisplayListData for OpaqueNode {
|
|
||||||
fn new(box_: &Box) -> OpaqueNode {
|
|
||||||
box_.node
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ExtraDisplayListData for Nothing {
|
|
||||||
fn new(_: &Box) -> Nothing {
|
|
||||||
()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A builder object that manages display list builder should mainly hold information about the
|
|
||||||
/// initial request and desired result--for example, whether the `DisplayList` is to be used for
|
|
||||||
/// painting or hit testing. This can affect which boxes are created.
|
|
||||||
///
|
|
||||||
/// Right now, the builder isn't used for much, but it establishes the pattern we'll need once we
|
|
||||||
/// support display-list-based hit testing and so forth.
|
|
||||||
pub struct DisplayListBuilder<'a> {
|
pub struct DisplayListBuilder<'a> {
|
||||||
ctx: &'a LayoutContext,
|
ctx: &'a LayoutContext,
|
||||||
|
|
||||||
|
/// A list of render layers that we've built up, root layer not included.
|
||||||
|
layers: SmallVec0<RenderLayer>,
|
||||||
|
|
||||||
|
/// The dirty rect.
|
||||||
|
dirty: Rect<Au>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Information needed at each step of the display list building traversal.
|
||||||
|
pub struct DisplayListBuildingInfo {
|
||||||
|
/// The size of the containing block.
|
||||||
|
containing_block_size: Size2D<Au>,
|
||||||
|
/// The absolute position of the absolute containing block.
|
||||||
|
absolute_containing_block_position: Point2D<Au>,
|
||||||
|
/// Whether the absolute containing block forces positioned descendants to be layerized.
|
||||||
|
layers_needed_for_positioned_flows: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
|
@ -219,6 +219,14 @@ pub trait Flow {
|
||||||
fail!("this is not the CB-generating flow you're looking for")
|
fail!("this is not the CB-generating flow you're looking for")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a layer ID for the given fragment.
|
||||||
|
fn layer_id(&self, fragment_id: uint) -> LayerId {
|
||||||
|
unsafe {
|
||||||
|
let pointer: uint = cast::transmute(self);
|
||||||
|
LayerId(pointer, fragment_id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns a debugging string describing this flow.
|
/// Returns a debugging string describing this flow.
|
||||||
fn debug_str(&self) -> ~str {
|
fn debug_str(&self) -> ~str {
|
||||||
~"???"
|
~"???"
|
||||||
|
@ -336,16 +344,11 @@ pub trait MutableFlowUtils {
|
||||||
/// Computes the overflow region for this flow.
|
/// Computes the overflow region for this flow.
|
||||||
fn store_overflow(self, _: &mut LayoutContext);
|
fn store_overflow(self, _: &mut LayoutContext);
|
||||||
|
|
||||||
/// builds the display lists
|
/// Builds the display lists for this flow and its descendants.
|
||||||
fn build_display_lists<E:ExtraDisplayListData>(
|
fn build_display_list(self,
|
||||||
self,
|
stacking_context: &mut StackingContext,
|
||||||
builder: &DisplayListBuilder,
|
builder: &mut DisplayListBuilder,
|
||||||
container_block_size: &Size2D<Au>,
|
info: &DisplayListBuildingInfo);
|
||||||
absolute_cb_abs_position: Point2D<Au>,
|
|
||||||
dirty: &Rect<Au>,
|
|
||||||
index: uint,
|
|
||||||
mut list: &RefCell<DisplayListCollection<E>>)
|
|
||||||
-> bool;
|
|
||||||
|
|
||||||
/// Destroys the flow.
|
/// Destroys the flow.
|
||||||
fn destroy(self);
|
fn destroy(self);
|
||||||
|
@ -606,6 +609,17 @@ bitfield!(FlowFlags, override_overline, set_override_overline, 0b0000_0100)
|
||||||
// NB: If you update this, you need to update TEXT_DECORATION_OVERRIDE_BITMASK.
|
// NB: If you update this, you need to update TEXT_DECORATION_OVERRIDE_BITMASK.
|
||||||
bitfield!(FlowFlags, override_line_through, set_override_line_through, 0b0000_1000)
|
bitfield!(FlowFlags, override_line_through, set_override_line_through, 0b0000_1000)
|
||||||
|
|
||||||
|
// Whether this flow contains a flow that has its own layer within the same absolute containing
|
||||||
|
// block.
|
||||||
|
bitfield!(FlowFlags,
|
||||||
|
layers_needed_for_descendants,
|
||||||
|
set_layers_needed_for_descendants,
|
||||||
|
0b0100_0000)
|
||||||
|
|
||||||
|
// Whether this flow must have its own layer. Even if this flag is not set, it might get its own
|
||||||
|
// layer if it's deemed to be likely to overlap flows with their own layer.
|
||||||
|
bitfield!(FlowFlags, needs_layer, set_needs_layer, 0b1000_0000)
|
||||||
|
|
||||||
// The text alignment for this flow.
|
// The text alignment for this flow.
|
||||||
impl FlowFlags {
|
impl FlowFlags {
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
@ -3,21 +3,23 @@
|
||||||
* 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 css::node_style::StyledNode;
|
use css::node_style::StyledNode;
|
||||||
use layout::box_::{Box, CannotSplit, GenericBox, IframeBox, ImageBox, ScannedTextBox, SplitDidFit};
|
use layout::box_::{Box, CannotSplit, GenericBox, IframeBox, ImageBox, InlineInfo, ScannedTextBox};
|
||||||
use layout::box_::{SplitDidNotFit, UnscannedTextBox, InlineInfo};
|
use layout::box_::{SplitDidFit, SplitDidNotFit, TableBox, TableCellBox, TableColumnBox};
|
||||||
use layout::box_::{TableColumnBox, TableRowBox, TableWrapperBox, TableCellBox, TableBox};
|
use layout::box_::{TableRowBox, TableWrapperBox, UnscannedTextBox};
|
||||||
use layout::context::LayoutContext;
|
use layout::context::LayoutContext;
|
||||||
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
|
use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo};
|
||||||
use layout::floats::{FloatLeft, Floats, PlacementInfo};
|
use layout::floats::{FloatLeft, Floats, PlacementInfo};
|
||||||
use layout::flow::{BaseFlow, FlowClass, Flow, InlineFlowClass};
|
use layout::flow::{BaseFlow, FlowClass, Flow, InlineFlowClass};
|
||||||
use layout::flow;
|
use layout::flow;
|
||||||
|
use layout::model::IntrinsicWidths;
|
||||||
use layout::util::ElementMapping;
|
use layout::util::ElementMapping;
|
||||||
use layout::wrapper::ThreadSafeLayoutNode;
|
use layout::wrapper::ThreadSafeLayoutNode;
|
||||||
|
|
||||||
use collections::{Deque, RingBuf};
|
use collections::{Deque, RingBuf};
|
||||||
use geom::{Point2D, Rect, Size2D};
|
use geom::{Point2D, Rect, Size2D};
|
||||||
use gfx::display_list::DisplayListCollection;
|
use gfx::display_list::{ContentLevel, StackingContext};
|
||||||
use servo_util::geometry::Au;
|
use servo_util::geometry::Au;
|
||||||
|
use servo_util::geometry;
|
||||||
use servo_util::range::Range;
|
use servo_util::range::Range;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
@ -480,17 +482,13 @@ impl InlineFlow {
|
||||||
self.boxes = ~[];
|
self.boxes = ~[];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_display_list_inline<E:ExtraDisplayListData>(
|
pub fn build_display_list_inline(&self,
|
||||||
&self,
|
stacking_context: &mut StackingContext,
|
||||||
builder: &DisplayListBuilder,
|
builder: &DisplayListBuilder,
|
||||||
container_block_size: &Size2D<Au>,
|
info: &DisplayListBuildingInfo) {
|
||||||
dirty: &Rect<Au>,
|
|
||||||
index: uint,
|
|
||||||
lists: &RefCell<DisplayListCollection<E>>)
|
|
||||||
-> uint {
|
|
||||||
let abs_rect = Rect(self.base.abs_position, self.base.position.size);
|
let abs_rect = Rect(self.base.abs_position, self.base.position.size);
|
||||||
if !abs_rect.intersects(dirty) {
|
if !abs_rect.intersects(&builder.dirty) {
|
||||||
return index;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(#228): Once we form line boxes and have their cached bounds, we can be smarter and
|
// TODO(#228): Once we form line boxes and have their cached bounds, we can be smarter and
|
||||||
|
@ -498,15 +496,19 @@ impl InlineFlow {
|
||||||
debug!("Flow: building display list for {:u} inline boxes", self.boxes.len());
|
debug!("Flow: building display list for {:u} inline boxes", self.boxes.len());
|
||||||
|
|
||||||
for box_ in self.boxes.iter() {
|
for box_ in self.boxes.iter() {
|
||||||
let rel_offset: Point2D<Au> = box_.relative_position(container_block_size);
|
let rel_offset: Point2D<Au> = box_.relative_position(&info.containing_block_size);
|
||||||
box_.build_display_list(builder, dirty, self.base.abs_position + rel_offset, (&*self) as &Flow, index, lists);
|
box_.build_display_list(stacking_context,
|
||||||
|
builder,
|
||||||
|
info,
|
||||||
|
self.base.abs_position + rel_offset,
|
||||||
|
(&*self) as &Flow,
|
||||||
|
ContentLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(#225): Should `inline-block` elements have flows as children of the inline flow or
|
// TODO(#225): Should `inline-block` elements have flows as children of the inline flow or
|
||||||
// should the flow be nested inside the box somehow?
|
// should the flow be nested inside the box somehow?
|
||||||
|
|
||||||
// For now, don't traverse the subtree rooted here
|
// For now, don't traverse the subtree rooted here.
|
||||||
index
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the relative offset from the baseline for this box, taking into account the value
|
/// Returns the relative offset from the baseline for this box, taking into account the value
|
||||||
|
@ -696,6 +698,7 @@ impl Flow for InlineFlow {
|
||||||
//
|
//
|
||||||
// TODO(pcwalton): Cache the linebox scanner?
|
// TODO(pcwalton): Cache the linebox scanner?
|
||||||
debug!("assign_height_inline: floats in: {:?}", self.base.floats);
|
debug!("assign_height_inline: floats in: {:?}", self.base.floats);
|
||||||
|
|
||||||
// assign height for inline boxes
|
// assign height for inline boxes
|
||||||
for box_ in self.boxes.iter() {
|
for box_ in self.boxes.iter() {
|
||||||
box_.assign_replaced_height_if_necessary();
|
box_.assign_replaced_height_if_necessary();
|
||||||
|
|
|
@ -11,22 +11,22 @@ use css::select::new_stylist;
|
||||||
use css::node_style::StyledNode;
|
use css::node_style::StyledNode;
|
||||||
use layout::construct::{FlowConstructionResult, NoConstructionResult};
|
use layout::construct::{FlowConstructionResult, NoConstructionResult};
|
||||||
use layout::context::LayoutContext;
|
use layout::context::LayoutContext;
|
||||||
use layout::display_list_builder::{DisplayListBuilder, ToGfxColor};
|
use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo, ToGfxColor};
|
||||||
use layout::flow::{Flow, ImmutableFlowUtils, MutableFlowUtils, MutableOwnedFlowUtils};
|
use layout::flow::{Flow, ImmutableFlowUtils, MutableFlowUtils, MutableOwnedFlowUtils};
|
||||||
use layout::flow::{PreorderFlowTraversal, PostorderFlowTraversal};
|
use layout::flow::{PreorderFlowTraversal, PostorderFlowTraversal};
|
||||||
use layout::flow;
|
use layout::flow;
|
||||||
use layout::incremental::RestyleDamage;
|
use layout::incremental::RestyleDamage;
|
||||||
use layout::parallel::PaddedUnsafeFlow;
|
use layout::parallel::PaddedUnsafeFlow;
|
||||||
use layout::parallel;
|
use layout::parallel;
|
||||||
use layout::util::{LayoutDataAccess, OpaqueNode, LayoutDataWrapper};
|
use layout::util::{LayoutDataAccess, LayoutDataWrapper, OpaqueNodeMethods};
|
||||||
use layout::wrapper::{LayoutNode, TLayoutNode, ThreadSafeLayoutNode};
|
use layout::wrapper::{LayoutNode, TLayoutNode, ThreadSafeLayoutNode};
|
||||||
|
|
||||||
use extra::url::Url;
|
use extra::url::Url;
|
||||||
use geom::point::Point2D;
|
use geom::point::Point2D;
|
||||||
use geom::rect::Rect;
|
use geom::rect::Rect;
|
||||||
use geom::size::Size2D;
|
use geom::size::Size2D;
|
||||||
use gfx::display_list::{ClipDisplayItemClass, DisplayItem, DisplayItemIterator};
|
use gfx::display_list::{ClipDisplayItemClass, DisplayItem, DisplayItemIterator, DisplayList};
|
||||||
use gfx::display_list::{DisplayList, DisplayListCollection};
|
use gfx::display_list::{OpaqueNode, StackingContext};
|
||||||
use gfx::font_context::{FontContext, FontContextInfo};
|
use gfx::font_context::{FontContext, FontContextInfo};
|
||||||
use gfx::render_task::{RenderMsg, RenderChan, RenderLayer};
|
use gfx::render_task::{RenderMsg, RenderChan, RenderLayer};
|
||||||
use gfx::{render_task, color};
|
use gfx::{render_task, color};
|
||||||
|
@ -41,18 +41,20 @@ use script::layout_interface::{ContentChangedDocumentDamage, LayoutChan, Msg, Pr
|
||||||
use script::layout_interface::{QueryMsg, ReapLayoutDataMsg, Reflow, UntrustedNodeAddress};
|
use script::layout_interface::{QueryMsg, ReapLayoutDataMsg, Reflow, UntrustedNodeAddress};
|
||||||
use script::layout_interface::{ReflowForDisplay, ReflowMsg};
|
use script::layout_interface::{ReflowForDisplay, ReflowMsg};
|
||||||
use script::script_task::{ReflowCompleteMsg, ScriptChan, SendEventMsg};
|
use script::script_task::{ReflowCompleteMsg, ScriptChan, SendEventMsg};
|
||||||
|
use servo_msg::compositor_msg::Scrollable;
|
||||||
use servo_msg::constellation_msg::{ConstellationChan, PipelineId, Failure, FailureMsg};
|
use servo_msg::constellation_msg::{ConstellationChan, PipelineId, Failure, FailureMsg};
|
||||||
use servo_net::image_cache_task::{ImageCacheTask, ImageResponseMsg};
|
use servo_net::image_cache_task::{ImageCacheTask, ImageResponseMsg};
|
||||||
use servo_net::local_image_cache::{ImageResponder, LocalImageCache};
|
use servo_net::local_image_cache::{ImageResponder, LocalImageCache};
|
||||||
use servo_util::geometry::Au;
|
use servo_util::geometry::Au;
|
||||||
|
use servo_util::geometry;
|
||||||
use servo_util::opts::Opts;
|
use servo_util::opts::Opts;
|
||||||
|
use servo_util::smallvec::{SmallVec, SmallVec0, SmallVec1};
|
||||||
use servo_util::time::{ProfilerChan, profile};
|
use servo_util::time::{ProfilerChan, profile};
|
||||||
use servo_util::time;
|
use servo_util::time;
|
||||||
use servo_util::task::send_on_failure;
|
use servo_util::task::send_on_failure;
|
||||||
use servo_util::workqueue::WorkQueue;
|
use servo_util::workqueue::WorkQueue;
|
||||||
use std::cast::transmute;
|
use std::cast::transmute;
|
||||||
use std::cast;
|
use std::cast;
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::comm::Port;
|
use std::comm::Port;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
@ -79,7 +81,7 @@ pub struct LayoutTask {
|
||||||
script_chan: ScriptChan,
|
script_chan: ScriptChan,
|
||||||
|
|
||||||
/// The channel on which messages can be sent to the painting task.
|
/// The channel on which messages can be sent to the painting task.
|
||||||
render_chan: RenderChan<OpaqueNode>,
|
render_chan: RenderChan,
|
||||||
|
|
||||||
/// The channel on which messages can be sent to the image cache.
|
/// The channel on which messages can be sent to the image cache.
|
||||||
image_cache_task: ImageCacheTask,
|
image_cache_task: ImageCacheTask,
|
||||||
|
@ -91,7 +93,7 @@ pub struct LayoutTask {
|
||||||
screen_size: Size2D<Au>,
|
screen_size: Size2D<Au>,
|
||||||
|
|
||||||
/// A cached display list.
|
/// A cached display list.
|
||||||
display_list_collection: Option<Arc<DisplayListCollection<OpaqueNode>>>,
|
display_list: Option<Arc<DisplayList>>,
|
||||||
|
|
||||||
stylist: ~Stylist,
|
stylist: ~Stylist,
|
||||||
|
|
||||||
|
@ -251,7 +253,7 @@ impl LayoutTask {
|
||||||
constellation_chan: ConstellationChan,
|
constellation_chan: ConstellationChan,
|
||||||
failure_msg: Failure,
|
failure_msg: Failure,
|
||||||
script_chan: ScriptChan,
|
script_chan: ScriptChan,
|
||||||
render_chan: RenderChan<OpaqueNode>,
|
render_chan: RenderChan,
|
||||||
img_cache_task: ImageCacheTask,
|
img_cache_task: ImageCacheTask,
|
||||||
opts: Opts,
|
opts: Opts,
|
||||||
profiler_chan: ProfilerChan,
|
profiler_chan: ProfilerChan,
|
||||||
|
@ -282,7 +284,7 @@ impl LayoutTask {
|
||||||
chan: LayoutChan,
|
chan: LayoutChan,
|
||||||
constellation_chan: ConstellationChan,
|
constellation_chan: ConstellationChan,
|
||||||
script_chan: ScriptChan,
|
script_chan: ScriptChan,
|
||||||
render_chan: RenderChan<OpaqueNode>,
|
render_chan: RenderChan,
|
||||||
image_cache_task: ImageCacheTask,
|
image_cache_task: ImageCacheTask,
|
||||||
opts: &Opts,
|
opts: &Opts,
|
||||||
profiler_chan: ProfilerChan)
|
profiler_chan: ProfilerChan)
|
||||||
|
@ -306,7 +308,7 @@ impl LayoutTask {
|
||||||
local_image_cache: local_image_cache,
|
local_image_cache: local_image_cache,
|
||||||
screen_size: screen_size,
|
screen_size: screen_size,
|
||||||
|
|
||||||
display_list_collection: None,
|
display_list: None,
|
||||||
stylist: ~new_stylist(),
|
stylist: ~new_stylist(),
|
||||||
initial_css_values: Arc::new(style::initial_values()),
|
initial_css_values: Arc::new(style::initial_values()),
|
||||||
parallel_traversal: parallel_traversal,
|
parallel_traversal: parallel_traversal,
|
||||||
|
@ -339,7 +341,7 @@ impl LayoutTask {
|
||||||
stylist: &*self.stylist,
|
stylist: &*self.stylist,
|
||||||
initial_css_values: self.initial_css_values.clone(),
|
initial_css_values: self.initial_css_values.clone(),
|
||||||
url: (*url).clone(),
|
url: (*url).clone(),
|
||||||
reflow_root: OpaqueNode::from_layout_node(reflow_root),
|
reflow_root: OpaqueNodeMethods::from_layout_node(reflow_root),
|
||||||
opts: self.opts.clone(),
|
opts: self.opts.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -627,23 +629,27 @@ impl LayoutTask {
|
||||||
// Build the display list if necessary, and send it to the renderer.
|
// Build the display list if necessary, and send it to the renderer.
|
||||||
if data.goal == ReflowForDisplay {
|
if data.goal == ReflowForDisplay {
|
||||||
profile(time::LayoutDispListBuildCategory, self.profiler_chan.clone(), || {
|
profile(time::LayoutDispListBuildCategory, self.profiler_chan.clone(), || {
|
||||||
let root_size = flow::base(layout_root).position.size;
|
let mut root_stacking_context = StackingContext::new();
|
||||||
let root_abs_position = Point2D(Au::new(0), Au::new(0));
|
let mut display_list_builder = DisplayListBuilder {
|
||||||
let mut display_list_collection = DisplayListCollection::new();
|
|
||||||
display_list_collection.add_list(DisplayList::<OpaqueNode>::new());
|
|
||||||
let display_list_collection = ~RefCell::new(display_list_collection);
|
|
||||||
let dirty = flow::base(layout_root).position.clone();
|
|
||||||
let display_list_builder = DisplayListBuilder {
|
|
||||||
ctx: &layout_ctx,
|
ctx: &layout_ctx,
|
||||||
|
layers: SmallVec0::new(),
|
||||||
|
dirty: flow::base(layout_root).position.clone(),
|
||||||
|
};
|
||||||
|
let display_list_building_info = DisplayListBuildingInfo {
|
||||||
|
containing_block_size: flow::base(layout_root).position.size,
|
||||||
|
absolute_containing_block_position: Point2D(Au(0), Au(0)),
|
||||||
|
layers_needed_for_positioned_flows: false,
|
||||||
};
|
};
|
||||||
layout_root.build_display_lists(&display_list_builder, &root_size,
|
|
||||||
root_abs_position,
|
|
||||||
&dirty, 0u, display_list_collection);
|
|
||||||
|
|
||||||
let display_list_collection = Arc::new(display_list_collection.unwrap());
|
layout_root.build_display_list(&mut root_stacking_context,
|
||||||
|
&mut display_list_builder,
|
||||||
|
&display_list_building_info);
|
||||||
|
|
||||||
|
let display_list = Arc::new(root_stacking_context.flatten());
|
||||||
|
|
||||||
|
// FIXME(pcwalton): This is really ugly and can't handle overflow: scroll. Refactor
|
||||||
|
// it with extreme prejudice.
|
||||||
let mut color = color::rgba(255.0, 255.0, 255.0, 255.0);
|
let mut color = color::rgba(255.0, 255.0, 255.0, 255.0);
|
||||||
|
|
||||||
for child in node.traverse_preorder() {
|
for child in node.traverse_preorder() {
|
||||||
if child.type_id() == ElementNodeTypeId(HTMLHtmlElementTypeId) ||
|
if child.type_id() == ElementNodeTypeId(HTMLHtmlElementTypeId) ||
|
||||||
child.type_id() == ElementNodeTypeId(HTMLBodyElementTypeId) {
|
child.type_id() == ElementNodeTypeId(HTMLBodyElementTypeId) {
|
||||||
|
@ -668,18 +674,33 @@ impl LayoutTask {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let root_size = Size2D(display_list_building_info.containing_block_size
|
||||||
|
.width
|
||||||
|
.to_nearest_px() as uint,
|
||||||
|
display_list_building_info.containing_block_size
|
||||||
|
.height
|
||||||
|
.to_nearest_px() as uint);
|
||||||
let render_layer = RenderLayer {
|
let render_layer = RenderLayer {
|
||||||
display_list_collection: display_list_collection.clone(),
|
id: layout_root.layer_id(0),
|
||||||
size: Size2D(root_size.width.to_nearest_px() as uint,
|
display_list: display_list.clone(),
|
||||||
root_size.height.to_nearest_px() as uint),
|
rect: Rect(Point2D(0u, 0u), root_size),
|
||||||
color: color
|
color: color,
|
||||||
|
scroll_policy: Scrollable,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.display_list_collection = Some(display_list_collection.clone());
|
self.display_list = Some(display_list.clone());
|
||||||
|
|
||||||
|
let mut layers = SmallVec1::new();
|
||||||
|
layers.push(render_layer);
|
||||||
|
let DisplayListBuilder {
|
||||||
|
layers: sublayers,
|
||||||
|
..
|
||||||
|
} = display_list_builder;
|
||||||
|
layers.push_all_move(sublayers);
|
||||||
|
|
||||||
debug!("Layout done!");
|
debug!("Layout done!");
|
||||||
|
|
||||||
self.render_chan.send(RenderMsg(render_layer));
|
self.render_chan.send(RenderMsg(layers));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -701,15 +722,14 @@ impl LayoutTask {
|
||||||
// The neat thing here is that in order to answer the following two queries we only
|
// The neat thing here is that in order to answer the following two queries we only
|
||||||
// need to compare nodes for equality. Thus we can safely work only with `OpaqueNode`.
|
// need to compare nodes for equality. Thus we can safely work only with `OpaqueNode`.
|
||||||
ContentBoxQuery(node, reply_chan) => {
|
ContentBoxQuery(node, reply_chan) => {
|
||||||
let node = OpaqueNode::from_script_node(node);
|
let node: OpaqueNode = OpaqueNodeMethods::from_script_node(node);
|
||||||
|
|
||||||
fn union_boxes_for_node<'a>(
|
fn union_boxes_for_node(accumulator: &mut Option<Rect<Au>>,
|
||||||
accumulator: &mut Option<Rect<Au>>,
|
mut iter: DisplayItemIterator,
|
||||||
mut iter: DisplayItemIterator<'a,OpaqueNode>,
|
|
||||||
node: OpaqueNode) {
|
node: OpaqueNode) {
|
||||||
for item in iter {
|
for item in iter {
|
||||||
union_boxes_for_node(accumulator, item.children(), node);
|
union_boxes_for_node(accumulator, item.children(), node);
|
||||||
if item.base().extra == node {
|
if item.base().node == node {
|
||||||
match *accumulator {
|
match *accumulator {
|
||||||
None => *accumulator = Some(item.base().bounds),
|
None => *accumulator = Some(item.base().bounds),
|
||||||
Some(ref mut acc) => *acc = acc.union(&item.base().bounds),
|
Some(ref mut acc) => *acc = acc.union(&item.base().bounds),
|
||||||
|
@ -719,41 +739,49 @@ impl LayoutTask {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut rect = None;
|
let mut rect = None;
|
||||||
for display_list in self.display_list_collection.as_ref().unwrap().get().iter() {
|
match self.display_list {
|
||||||
union_boxes_for_node(&mut rect, display_list.iter(), node);
|
None => fail!("no display list!"),
|
||||||
|
Some(ref display_list) => {
|
||||||
|
union_boxes_for_node(&mut rect, display_list.get().iter(), node)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
reply_chan.send(ContentBoxResponse(rect.unwrap_or(Au::zero_rect())))
|
reply_chan.send(ContentBoxResponse(rect.unwrap_or(Au::zero_rect())))
|
||||||
}
|
}
|
||||||
ContentBoxesQuery(node, reply_chan) => {
|
ContentBoxesQuery(node, reply_chan) => {
|
||||||
let node = OpaqueNode::from_script_node(node);
|
let node: OpaqueNode = OpaqueNodeMethods::from_script_node(node);
|
||||||
|
|
||||||
fn add_boxes_for_node<'a>(
|
fn add_boxes_for_node(accumulator: &mut ~[Rect<Au>],
|
||||||
accumulator: &mut ~[Rect<Au>],
|
mut iter: DisplayItemIterator,
|
||||||
mut iter: DisplayItemIterator<'a,OpaqueNode>,
|
|
||||||
node: OpaqueNode) {
|
node: OpaqueNode) {
|
||||||
for item in iter {
|
for item in iter {
|
||||||
add_boxes_for_node(accumulator, item.children(), node);
|
add_boxes_for_node(accumulator, item.children(), node);
|
||||||
if item.base().extra == node {
|
if item.base().node == node {
|
||||||
accumulator.push(item.base().bounds)
|
accumulator.push(item.base().bounds)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut boxes = ~[];
|
let mut boxes = ~[];
|
||||||
for display_list in self.display_list_collection.as_ref().unwrap().get().iter() {
|
match self.display_list {
|
||||||
add_boxes_for_node(&mut boxes, display_list.iter(), node);
|
None => fail!("no display list!"),
|
||||||
|
Some(ref display_list) => {
|
||||||
|
add_boxes_for_node(&mut boxes, display_list.get().iter(), node)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
reply_chan.send(ContentBoxesResponse(boxes))
|
reply_chan.send(ContentBoxesResponse(boxes))
|
||||||
}
|
}
|
||||||
HitTestQuery(_, point, reply_chan) => {
|
HitTestQuery(_, point, reply_chan) => {
|
||||||
fn hit_test(x: Au, y: Au, list: &[DisplayItem<OpaqueNode>])
|
fn hit_test(x: Au, y: Au, list: &[DisplayItem])
|
||||||
-> Option<HitTestResponse> {
|
-> Option<HitTestResponse> {
|
||||||
for item in list.rev_iter() {
|
for item in list.rev_iter() {
|
||||||
match *item {
|
match *item {
|
||||||
ClipDisplayItemClass(ref cc) => {
|
ClipDisplayItemClass(ref cc) => {
|
||||||
let ret = hit_test(x, y, cc.child_list);
|
if !cc.need_clip || geometry::rect_contains_point(cc.base.bounds,
|
||||||
if !ret.is_none() {
|
Point2D(x, y)) {
|
||||||
return ret;
|
let ret = hit_test(x, y, cc.child_list.as_slice());
|
||||||
|
if !ret.is_none() {
|
||||||
|
return ret
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
@ -774,31 +802,35 @@ impl LayoutTask {
|
||||||
y < bounds.origin.y + bounds.size.height &&
|
y < bounds.origin.y + bounds.size.height &&
|
||||||
bounds.origin.y <= y {
|
bounds.origin.y <= y {
|
||||||
return Some(HitTestResponse(item.base()
|
return Some(HitTestResponse(item.base()
|
||||||
.extra
|
.node
|
||||||
.to_untrusted_node_address()))
|
.to_untrusted_node_address()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let ret: Option<HitTestResponse> = None;
|
let ret: Option<HitTestResponse> = None;
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
for display_list in self.display_list_collection.as_ref().unwrap().get().lists.rev_iter() {
|
let (x, y) = (Au::from_frac_px(point.x as f64),
|
||||||
let (x, y) = (Au::from_frac_px(point.x as f64),
|
Au::from_frac_px(point.y as f64));
|
||||||
Au::from_frac_px(point.y as f64));
|
let resp = match self.display_list {
|
||||||
let resp = hit_test(x,y,display_list.list);
|
None => fail!("no display list!"),
|
||||||
if resp.is_some() {
|
Some(ref display_list) => hit_test(x, y, display_list.get().list.as_slice()),
|
||||||
reply_chan.send(Ok(resp.unwrap()));
|
};
|
||||||
return
|
if resp.is_some() {
|
||||||
}
|
reply_chan.send(Ok(resp.unwrap()));
|
||||||
|
return
|
||||||
}
|
}
|
||||||
reply_chan.send(Err(()));
|
reply_chan.send(Err(()));
|
||||||
|
|
||||||
}
|
}
|
||||||
MouseOverQuery(_, point, reply_chan) => {
|
MouseOverQuery(_, point, reply_chan) => {
|
||||||
fn mouse_over_test(x: Au, y: Au, list: &[DisplayItem<OpaqueNode>], result: &mut ~[UntrustedNodeAddress]) {
|
fn mouse_over_test(x: Au,
|
||||||
|
y: Au,
|
||||||
|
list: &[DisplayItem],
|
||||||
|
result: &mut ~[UntrustedNodeAddress]) {
|
||||||
for item in list.rev_iter() {
|
for item in list.rev_iter() {
|
||||||
match *item {
|
match *item {
|
||||||
ClipDisplayItemClass(ref cc) => {
|
ClipDisplayItemClass(ref cc) => {
|
||||||
mouse_over_test(x, y, cc.child_list, result);
|
mouse_over_test(x, y, cc.child_list.as_slice(), result);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
@ -814,18 +846,24 @@ impl LayoutTask {
|
||||||
y < bounds.origin.y + bounds.size.height &&
|
y < bounds.origin.y + bounds.size.height &&
|
||||||
bounds.origin.y <= y {
|
bounds.origin.y <= y {
|
||||||
result.push(item.base()
|
result.push(item.base()
|
||||||
.extra
|
.node
|
||||||
.to_untrusted_node_address());
|
.to_untrusted_node_address());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut mouse_over_list:~[UntrustedNodeAddress] = ~[];
|
let mut mouse_over_list:~[UntrustedNodeAddress] = ~[];
|
||||||
for display_list in self.display_list_collection.as_ref().unwrap().get().lists.rev_iter() {
|
let (x, y) = (Au::from_frac_px(point.x as f64),
|
||||||
let (x, y) = (Au::from_frac_px(point.x as f64),
|
Au::from_frac_px(point.y as f64));
|
||||||
Au::from_frac_px(point.y as f64));
|
match self.display_list {
|
||||||
mouse_over_test(x,y,display_list.list, &mut mouse_over_list);
|
None => fail!("no display list!"),
|
||||||
}
|
Some(ref display_list) => {
|
||||||
|
mouse_over_test(x,
|
||||||
|
y,
|
||||||
|
display_list.get().list.as_slice(),
|
||||||
|
&mut mouse_over_list);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if mouse_over_list.is_empty() {
|
if mouse_over_list.is_empty() {
|
||||||
reply_chan.send(Err(()));
|
reply_chan.send(Err(()));
|
||||||
|
|
|
@ -9,7 +9,6 @@ use extra::url::Url;
|
||||||
use geom::size::Size2D;
|
use geom::size::Size2D;
|
||||||
use gfx::render_task::{PaintPermissionGranted, PaintPermissionRevoked};
|
use gfx::render_task::{PaintPermissionGranted, PaintPermissionRevoked};
|
||||||
use gfx::render_task::{RenderChan, RenderTask};
|
use gfx::render_task::{RenderChan, RenderTask};
|
||||||
use layout::util::OpaqueNode;
|
|
||||||
use script::layout_interface::LayoutChan;
|
use script::layout_interface::LayoutChan;
|
||||||
use script::script_task::LoadMsg;
|
use script::script_task::LoadMsg;
|
||||||
use script::script_task::{AttachLayoutMsg, NewLayoutInfo, ScriptTask, ScriptChan};
|
use script::script_task::{AttachLayoutMsg, NewLayoutInfo, ScriptTask, ScriptChan};
|
||||||
|
@ -28,7 +27,7 @@ pub struct Pipeline {
|
||||||
subpage_id: Option<SubpageId>,
|
subpage_id: Option<SubpageId>,
|
||||||
script_chan: ScriptChan,
|
script_chan: ScriptChan,
|
||||||
layout_chan: LayoutChan,
|
layout_chan: LayoutChan,
|
||||||
render_chan: RenderChan<OpaqueNode>,
|
render_chan: RenderChan,
|
||||||
layout_shutdown_port: Port<()>,
|
layout_shutdown_port: Port<()>,
|
||||||
render_shutdown_port: Port<()>,
|
render_shutdown_port: Port<()>,
|
||||||
/// The most recently loaded url
|
/// The most recently loaded url
|
||||||
|
@ -40,7 +39,7 @@ pub struct Pipeline {
|
||||||
pub struct CompositionPipeline {
|
pub struct CompositionPipeline {
|
||||||
id: PipelineId,
|
id: PipelineId,
|
||||||
script_chan: ScriptChan,
|
script_chan: ScriptChan,
|
||||||
render_chan: RenderChan<OpaqueNode>,
|
render_chan: RenderChan,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pipeline {
|
impl Pipeline {
|
||||||
|
@ -171,7 +170,7 @@ impl Pipeline {
|
||||||
subpage_id: Option<SubpageId>,
|
subpage_id: Option<SubpageId>,
|
||||||
script_chan: ScriptChan,
|
script_chan: ScriptChan,
|
||||||
layout_chan: LayoutChan,
|
layout_chan: LayoutChan,
|
||||||
render_chan: RenderChan<OpaqueNode>,
|
render_chan: RenderChan,
|
||||||
layout_shutdown_port: Port<()>,
|
layout_shutdown_port: Port<()>,
|
||||||
render_shutdown_port: Port<()>)
|
render_shutdown_port: Port<()>)
|
||||||
-> Pipeline {
|
-> Pipeline {
|
||||||
|
|
|
@ -2,17 +2,18 @@
|
||||||
* 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 azure::azure_hl::Color;
|
||||||
use geom::point::Point2D;
|
use geom::point::Point2D;
|
||||||
use geom::rect::Rect;
|
use geom::rect::Rect;
|
||||||
use geom::size::Size2D;
|
use geom::size::Size2D;
|
||||||
use azure::azure_hl::Color;
|
|
||||||
use layers::platform::surface::{NativeGraphicsMetadata, NativePaintingGraphicsContext};
|
use layers::platform::surface::{NativeGraphicsMetadata, NativePaintingGraphicsContext};
|
||||||
use layers::platform::surface::{NativeSurface, NativeSurfaceMethods};
|
use layers::platform::surface::{NativeSurface, NativeSurfaceMethods};
|
||||||
|
use serialize::{Encoder, Encodable};
|
||||||
|
use std::fmt::{Formatter, Show};
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
use constellation_msg::PipelineId;
|
use constellation_msg::PipelineId;
|
||||||
|
|
||||||
use serialize::{Encoder, Encodable};
|
|
||||||
|
|
||||||
pub struct LayerBuffer {
|
pub struct LayerBuffer {
|
||||||
/// The native surface which can be shared between threads or processes. On Mac this is an
|
/// The native surface which can be shared between threads or processes. On Mac this is an
|
||||||
/// `IOSurface`; on Linux this is an X Pixmap; on Android this is an `EGLImageKHR`.
|
/// `IOSurface`; on Linux this is an X Pixmap; on Android this is an `EGLImageKHR`.
|
||||||
|
@ -76,15 +77,73 @@ impl Epoch {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[deriving(Clone, Eq)]
|
||||||
|
pub struct LayerId(uint, uint);
|
||||||
|
|
||||||
|
impl Show for LayerId {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
let LayerId(a, b) = *self;
|
||||||
|
write!(f.buf, "Layer({}, {})", a, b);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LayerId {
|
||||||
|
/// FIXME(pcwalton): This is unfortunate. Maybe remove this in the future.
|
||||||
|
pub fn null() -> LayerId {
|
||||||
|
LayerId(0, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The scrolling policy of a layer.
|
||||||
|
#[deriving(Eq)]
|
||||||
|
pub enum ScrollPolicy {
|
||||||
|
/// These layers scroll when the parent receives a scrolling message.
|
||||||
|
Scrollable,
|
||||||
|
/// These layers do not scroll when the parent receives a scrolling message.
|
||||||
|
FixedPosition,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// All layer-specific information that the painting task sends to the compositor other than the
|
||||||
|
/// buffer contents of the layer itself.
|
||||||
|
pub struct LayerMetadata {
|
||||||
|
/// An opaque ID. This is usually the address of the flow and index of the box within it.
|
||||||
|
id: LayerId,
|
||||||
|
/// The position and size of the layer in pixels.
|
||||||
|
rect: Rect<uint>,
|
||||||
|
/// The background color of the layer.
|
||||||
|
color: Color,
|
||||||
|
/// The scrolling policy of this layer.
|
||||||
|
scroll_policy: ScrollPolicy,
|
||||||
|
}
|
||||||
|
|
||||||
/// The interface used by the renderer to acquire draw targets for each render frame and
|
/// The interface used by the renderer to acquire draw targets for each render frame and
|
||||||
/// submit them to be drawn to the display.
|
/// submit them to be drawn to the display.
|
||||||
pub trait RenderListener {
|
pub trait RenderListener {
|
||||||
fn get_graphics_metadata(&self) -> Option<NativeGraphicsMetadata>;
|
fn get_graphics_metadata(&self) -> Option<NativeGraphicsMetadata>;
|
||||||
fn new_layer(&self, PipelineId, Size2D<uint>);
|
fn create_layer_group_for_pipeline(&self, PipelineId, Size2D<uint>);
|
||||||
fn set_layer_page_size_and_color(&self, PipelineId, Size2D<uint>, Epoch, Color);
|
|
||||||
fn set_layer_clip_rect(&self, PipelineId, Rect<uint>);
|
/// Informs the compositor of the layers for the given pipeline. The compositor responds by
|
||||||
fn delete_layer(&self, PipelineId);
|
/// creating and/or destroying render layers as necessary.
|
||||||
fn paint(&self, id: PipelineId, layer_buffer_set: ~LayerBufferSet, Epoch);
|
fn initialize_layers_for_pipeline(&self,
|
||||||
|
pipeline_id: PipelineId,
|
||||||
|
metadata: ~[LayerMetadata],
|
||||||
|
epoch: Epoch);
|
||||||
|
|
||||||
|
fn set_layer_clip_rect(&self,
|
||||||
|
pipeline_id: PipelineId,
|
||||||
|
layer_id: LayerId,
|
||||||
|
new_rect: Rect<uint>);
|
||||||
|
|
||||||
|
fn delete_layer_group(&self, PipelineId);
|
||||||
|
|
||||||
|
/// Sends new tiles for the given layer to the compositor.
|
||||||
|
fn paint(&self,
|
||||||
|
pipeline_id: PipelineId,
|
||||||
|
layer_id: LayerId,
|
||||||
|
layer_buffer_set: ~LayerBufferSet,
|
||||||
|
epoch: Epoch);
|
||||||
|
|
||||||
fn set_render_state(&self, render_state: RenderState);
|
fn set_render_state(&self, render_state: RenderState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,8 +151,11 @@ pub trait RenderListener {
|
||||||
/// which is used in displaying the appropriate message in the window's title.
|
/// which is used in displaying the appropriate message in the window's title.
|
||||||
pub trait ScriptListener : Clone {
|
pub trait ScriptListener : Clone {
|
||||||
fn set_ready_state(&self, ReadyState);
|
fn set_ready_state(&self, ReadyState);
|
||||||
fn invalidate_rect(&self, PipelineId, Rect<uint>);
|
fn invalidate_rect(&self, pipeline_id: PipelineId, layer_id: LayerId, rect: Rect<uint>);
|
||||||
fn scroll_fragment_point(&self, PipelineId, Point2D<f32>);
|
fn scroll_fragment_point(&self,
|
||||||
|
pipeline_id: PipelineId,
|
||||||
|
layer_id: LayerId,
|
||||||
|
point: Point2D<f32>);
|
||||||
fn close(&self);
|
fn close(&self);
|
||||||
fn dup(&self) -> ~ScriptListener;
|
fn dup(&self) -> ~ScriptListener;
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,8 @@ use js::jsapi::{JSObject, JS_InhibitGC, JS_AllowGC, JS_CallFunctionValue};
|
||||||
use js::jsval::NullValue;
|
use js::jsval::NullValue;
|
||||||
use js::rust::{Compartment, Cx, CxUtils, RtUtils};
|
use js::rust::{Compartment, Cx, CxUtils, RtUtils};
|
||||||
use js;
|
use js;
|
||||||
use servo_msg::compositor_msg::{FinishedLoading, Loading, PerformingLayout, ScriptListener};
|
use servo_msg::compositor_msg::{FinishedLoading, LayerId, Loading, PerformingLayout};
|
||||||
|
use servo_msg::compositor_msg::{ScriptListener};
|
||||||
use servo_msg::constellation_msg::{ConstellationChan, IFrameSandboxed, IFrameUnsandboxed};
|
use servo_msg::constellation_msg::{ConstellationChan, IFrameSandboxed, IFrameUnsandboxed};
|
||||||
use servo_msg::constellation_msg::{LoadIframeUrlMsg, LoadCompleteMsg, LoadUrlMsg, NavigationDirection};
|
use servo_msg::constellation_msg::{LoadIframeUrlMsg, LoadCompleteMsg, LoadUrlMsg, NavigationDirection};
|
||||||
use servo_msg::constellation_msg::{PipelineId, SubpageId, Failure, FailureMsg};
|
use servo_msg::constellation_msg::{PipelineId, SubpageId, Failure, FailureMsg};
|
||||||
|
@ -929,10 +930,14 @@ impl ScriptTask {
|
||||||
fn scroll_fragment_point(&self, pipeline_id: PipelineId, page: &Page, node: JS<Element>) {
|
fn scroll_fragment_point(&self, pipeline_id: PipelineId, page: &Page, node: JS<Element>) {
|
||||||
let (port, chan) = Chan::new();
|
let (port, chan) = Chan::new();
|
||||||
let node: JS<Node> = NodeCast::from(&node);
|
let node: JS<Node> = NodeCast::from(&node);
|
||||||
let ContentBoxResponse(rect) = page.query_layout(ContentBoxQuery(node.to_trusted_node_address(), chan), port);
|
let ContentBoxResponse(rect) =
|
||||||
|
page.query_layout(ContentBoxQuery(node.to_trusted_node_address(), chan), port);
|
||||||
let point = Point2D(to_frac_px(rect.origin.x).to_f32().unwrap(),
|
let point = Point2D(to_frac_px(rect.origin.x).to_f32().unwrap(),
|
||||||
to_frac_px(rect.origin.y).to_f32().unwrap());
|
to_frac_px(rect.origin.y).to_f32().unwrap());
|
||||||
self.compositor.scroll_fragment_point(pipeline_id, point);
|
// FIXME(pcwalton): This is pretty bogus when multiple layers are involved. Really
|
||||||
|
// what needs to happen is that this needs to go through layout to ask which layer
|
||||||
|
// the element belongs to, and have it send the scroll message to the compositor.
|
||||||
|
self.compositor.scroll_fragment_point(pipeline_id, LayerId::null(), point);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This is the main entry point for receiving and dispatching DOM events.
|
/// This is the main entry point for receiving and dispatching DOM events.
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 400a01307515def5dd6874ec44ed8a017ada36fd
|
Subproject commit 79e405fa59c052ff78d7a2527a92474a32ac9b4d
|
Loading…
Add table
Add a link
Reference in a new issue