compositing: Implement cursor per CSS3-UI § 8.1.1 in the CEF/Mac port.

I'm not sure how we want to handle Linux cursors, and GLFW has no
ability to set cursors (short of disabling it and managing it yourself).
This commit is contained in:
Patrick Walton 2014-12-12 07:18:16 -08:00
parent 636641f905
commit 7371e0b8e3
23 changed files with 564 additions and 171 deletions

View file

@ -3,8 +3,8 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use compositor_layer::{CompositorData, CompositorLayer, WantsScrollEventsFlag}; use compositor_layer::{CompositorData, CompositorLayer, WantsScrollEventsFlag};
use compositor_task::{CompositorEventListener, CompositorProxy, CompositorReceiver, CompositorTask}; use compositor_task::{CompositorEventListener, CompositorProxy, CompositorReceiver};
use compositor_task::{LayerProperties, Msg}; use compositor_task::{CompositorTask, LayerProperties, Msg};
use constellation::{FrameId, FrameTreeDiff, SendableFrameTree}; use constellation::{FrameId, FrameTreeDiff, SendableFrameTree};
use pipeline::CompositionPipeline; use pipeline::CompositionPipeline;
use scrolling::ScrollingTimerProxy; use scrolling::ScrollingTimerProxy;
@ -338,6 +338,10 @@ impl<Window: WindowMethods> IOCompositor<Window> {
self.window.handle_key(key, modified); self.window.handle_key(key, modified);
} }
(Msg::SetCursor(cursor), ShutdownState::NotShuttingDown) => {
self.window.set_cursor(cursor)
}
// When we are shutting_down, we need to avoid performing operations // When we are shutting_down, we need to avoid performing operations
// such as Paint that may crash because we have begun tearing down // such as Paint that may crash because we have begun tearing down
// the rest of our resources. // the rest of our resources.

View file

@ -21,6 +21,7 @@ use servo_msg::compositor_msg::{Epoch, LayerId, LayerMetadata, ReadyState};
use servo_msg::compositor_msg::{PaintListener, PaintState, ScriptListener, ScrollPolicy}; use servo_msg::compositor_msg::{PaintListener, PaintState, ScriptListener, ScrollPolicy};
use servo_msg::constellation_msg::{ConstellationChan, LoadData, PipelineId}; use servo_msg::constellation_msg::{ConstellationChan, LoadData, PipelineId};
use servo_msg::constellation_msg::{Key, KeyState, KeyModifiers, Pressed}; use servo_msg::constellation_msg::{Key, KeyState, KeyModifiers, Pressed};
use servo_util::cursor::Cursor;
use servo_util::memory::MemoryProfilerChan; use servo_util::memory::MemoryProfilerChan;
use servo_util::time::TimeProfilerChan; use servo_util::time::TimeProfilerChan;
use std::comm::{channel, Sender, Receiver}; use std::comm::{channel, Sender, Receiver};
@ -213,6 +214,8 @@ pub enum Msg {
ScrollTimeout(u64), ScrollTimeout(u64),
/// Sends an unconsumed key event back to the compositor. /// Sends an unconsumed key event back to the compositor.
KeyEvent(Key, KeyModifiers), KeyEvent(Key, KeyModifiers),
/// Changes the cursor.
SetCursor(Cursor),
} }
impl Show for Msg { impl Show for Msg {
@ -231,11 +234,12 @@ impl Show for Msg {
Msg::ChangePageTitle(..) => write!(f, "ChangePageTitle"), Msg::ChangePageTitle(..) => write!(f, "ChangePageTitle"),
Msg::ChangePageLoadData(..) => write!(f, "ChangePageLoadData"), Msg::ChangePageLoadData(..) => write!(f, "ChangePageLoadData"),
Msg::PaintMsgDiscarded(..) => write!(f, "PaintMsgDiscarded"), Msg::PaintMsgDiscarded(..) => write!(f, "PaintMsgDiscarded"),
Msg::FrameTreeUpdate(..) => write!(f, "FrameTreeUpdate"),
Msg::SetIds(..) => write!(f, "SetIds"), Msg::SetIds(..) => write!(f, "SetIds"),
Msg::FrameTreeUpdate(..) => write!(f, "FrameTreeUpdateMsg"),
Msg::LoadComplete => write!(f, "LoadComplete"), Msg::LoadComplete => write!(f, "LoadComplete"),
Msg::ScrollTimeout(..) => write!(f, "ScrollTimeout"), Msg::ScrollTimeout(..) => write!(f, "ScrollTimeout"),
Msg::KeyEvent(..) => write!(f, "KeyEvent"), Msg::KeyEvent(..) => write!(f, "KeyEvent"),
Msg::SetCursor(..) => write!(f, "SetCursor"),
} }
} }
} }

View file

@ -18,20 +18,21 @@ use libc;
use script_traits::{mod, GetTitleMsg, ResizeMsg, ResizeInactiveMsg, ExitPipelineMsg, SendEventMsg}; use script_traits::{mod, GetTitleMsg, ResizeMsg, ResizeInactiveMsg, ExitPipelineMsg, SendEventMsg};
use script_traits::{ScriptControlChan, ScriptTaskFactory}; use script_traits::{ScriptControlChan, ScriptTaskFactory};
use servo_msg::compositor_msg::LayerId; use servo_msg::compositor_msg::LayerId;
use servo_msg::constellation_msg::{ConstellationChan, ExitMsg, FailureMsg, Failure, FrameRectMsg}; use servo_msg::constellation_msg::{mod, ConstellationChan, ExitMsg, FailureMsg, Failure};
use servo_msg::constellation_msg::{GetPipelineTitleMsg}; use servo_msg::constellation_msg::{FrameRectMsg, GetPipelineTitleMsg};
use servo_msg::constellation_msg::{IFrameSandboxState, IFrameUnsandboxed, InitLoadUrlMsg}; use servo_msg::constellation_msg::{IFrameSandboxState, IFrameUnsandboxed, InitLoadUrlMsg};
use servo_msg::constellation_msg::{KeyEvent, Key, KeyState, KeyModifiers, LoadCompleteMsg}; use servo_msg::constellation_msg::{KeyEvent, Key, KeyState, KeyModifiers, LoadCompleteMsg};
use servo_msg::constellation_msg::{LoadData, LoadUrlMsg, NavigateMsg, NavigationType}; use servo_msg::constellation_msg::{LoadData, LoadUrlMsg, NavigateMsg, NavigationType};
use servo_msg::constellation_msg::{PainterReadyMsg, PipelineId, ResizedWindowMsg}; use servo_msg::constellation_msg::{PainterReadyMsg, PipelineId, ResizedWindowMsg};
use servo_msg::constellation_msg::{ScriptLoadedURLInIFrameMsg, SubpageId, WindowSizeData}; use servo_msg::constellation_msg::{ScriptLoadedURLInIFrameMsg, SetCursorMsg, SubpageId};
use servo_msg::constellation_msg::{WindowSizeData};
use servo_msg::constellation_msg::Msg as ConstellationMsg; use servo_msg::constellation_msg::Msg as ConstellationMsg;
use servo_msg::constellation_msg;
use servo_net::image_cache_task::{ImageCacheTask, ImageCacheTaskClient}; use servo_net::image_cache_task::{ImageCacheTask, ImageCacheTaskClient};
use servo_net::resource_task::ResourceTask; use servo_net::resource_task::ResourceTask;
use servo_net::resource_task; use servo_net::resource_task;
use servo_net::storage_task::StorageTask; use servo_net::storage_task::StorageTask;
use servo_net::storage_task; use servo_net::storage_task;
use servo_util::cursor::Cursor;
use servo_util::geometry::{PagePx, ViewportPx}; use servo_util::geometry::{PagePx, ViewportPx};
use servo_util::opts; use servo_util::opts;
use servo_util::task::spawn_named; use servo_util::task::spawn_named;
@ -472,6 +473,7 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
subpage_id, subpage_id,
sandbox); sandbox);
} }
SetCursorMsg(cursor) => self.handle_set_cursor_msg(cursor),
// Load a new page, usually -- but not always -- from a mouse click or typed url // Load a new page, usually -- but not always -- from a mouse click or typed url
// If there is already a pending page (self.pending_frames), it will not be overridden; // If there is already a pending page (self.pending_frames), it will not be overridden;
// However, if the id is not encompassed by another change, it will be. // However, if the id is not encompassed by another change, it will be.
@ -754,6 +756,10 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
self.pipelines.insert(pipeline.id, pipeline); self.pipelines.insert(pipeline.id, pipeline);
} }
fn handle_set_cursor_msg(&mut self, cursor: Cursor) {
self.compositor_proxy.send(CompositorMsg::SetCursor(cursor))
}
fn handle_load_url_msg(&mut self, source_id: PipelineId, load_data: LoadData) { fn handle_load_url_msg(&mut self, source_id: PipelineId, load_data: LoadData) {
let url = load_data.url.to_string(); let url = load_data.url.to_string();
debug!("Constellation: received message to load {:s}", url); debug!("Constellation: received message to load {:s}", url);

View file

@ -99,10 +99,18 @@ impl CompositorEventListener for NullCompositor {
Msg::CreateOrUpdateRootLayer(..) | Msg::CreateOrUpdateRootLayer(..) |
Msg::CreateOrUpdateDescendantLayer(..) | Msg::CreateOrUpdateDescendantLayer(..) |
Msg::SetLayerOrigin(..) | Msg::Paint(..) | Msg::SetLayerOrigin(..) |
Msg::ChangeReadyState(..) | Msg::ChangePaintState(..) | Msg::ScrollFragmentPoint(..) | Msg::Paint(..) |
Msg::LoadComplete | Msg::PaintMsgDiscarded(..) | Msg::ScrollTimeout(..) | Msg::ChangePageTitle(..) | Msg::ChangeReadyState(..) |
Msg::ChangePageLoadData(..) | Msg::KeyEvent(..) => () Msg::ChangePaintState(..) |
Msg::ScrollFragmentPoint(..) |
Msg::LoadComplete |
Msg::PaintMsgDiscarded(..) |
Msg::ScrollTimeout(..) |
Msg::ChangePageTitle(..) |
Msg::ChangePageLoadData(..) |
Msg::KeyEvent(..) |
Msg::SetCursor(..) => {}
} }
true true
} }

View file

@ -13,6 +13,7 @@ use layers::geometry::DevicePixel;
use layers::platform::surface::NativeGraphicsMetadata; use layers::platform::surface::NativeGraphicsMetadata;
use servo_msg::compositor_msg::{PaintState, ReadyState}; use servo_msg::compositor_msg::{PaintState, ReadyState};
use servo_msg::constellation_msg::{Key, KeyState, KeyModifiers, LoadData}; use servo_msg::constellation_msg::{Key, KeyState, KeyModifiers, LoadData};
use servo_util::cursor::Cursor;
use servo_util::geometry::ScreenPx; use servo_util::geometry::ScreenPx;
use std::fmt::{FormatError, Formatter, Show}; use std::fmt::{FormatError, Formatter, Show};
use std::rc::Rc; use std::rc::Rc;
@ -124,6 +125,9 @@ pub trait WindowMethods {
/// proceed and false if it should not. /// proceed and false if it should not.
fn prepare_for_composite(&self) -> bool; fn prepare_for_composite(&self) -> bool;
/// Sets the cursor to be used in the window.
fn set_cursor(&self, cursor: Cursor);
/// Process a key event. /// Process a key event.
fn handle_key(&self, key: Key, mods: KeyModifiers); fn handle_key(&self, key: Key, mods: KeyModifiers);
} }

View file

@ -14,12 +14,13 @@
//! They are therefore not exactly analogous to constructs like Skia pictures, which consist of //! They are therefore not exactly analogous to constructs like Skia pictures, which consist of
//! low-level drawing primitives. //! low-level drawing primitives.
use self::DisplayItem::*; #![deny(unsafe_blocks)]
use self::DisplayItemIterator::*;
use color::Color; use color::Color;
use display_list::optimizer::DisplayListOptimizer; use display_list::optimizer::DisplayListOptimizer;
use paint_context::{PaintContext, ToAzureRect}; use paint_context::{PaintContext, ToAzureRect};
use self::DisplayItem::*;
use self::DisplayItemIterator::*;
use text::glyph::CharIndex; use text::glyph::CharIndex;
use text::TextRun; use text::TextRun;
@ -28,17 +29,18 @@ use collections::dlist::{mod, DList};
use geom::{Point2D, Rect, SideOffsets2D, Size2D, Matrix2D}; use geom::{Point2D, Rect, SideOffsets2D, Size2D, Matrix2D};
use libc::uintptr_t; use libc::uintptr_t;
use paint_task::PaintLayer; use paint_task::PaintLayer;
use script_traits::UntrustedNodeAddress;
use servo_msg::compositor_msg::LayerId; use servo_msg::compositor_msg::LayerId;
use servo_net::image::base::Image; use servo_net::image::base::Image;
use servo_util::cursor::Cursor;
use servo_util::dlist as servo_dlist; use servo_util::dlist as servo_dlist;
use servo_util::geometry::{mod, Au, ZERO_POINT}; use servo_util::geometry::{mod, Au, ZERO_POINT};
use servo_util::range::Range; use servo_util::range::Range;
use servo_util::smallvec::{SmallVec, SmallVec8}; use servo_util::smallvec::{SmallVec, SmallVec8};
use std::fmt; use std::fmt;
use std::mem;
use std::slice::Items; use std::slice::Items;
use style::ComputedValues;
use style::computed_values::border_style; use style::computed_values::border_style;
use style::computed_values::cursor::{AutoCursor, SpecifiedCursor};
use sync::Arc; use sync::Arc;
// It seems cleaner to have layout code not mention Azure directly, so let's just reexport this for // It seems cleaner to have layout code not mention Azure directly, so let's just reexport this for
@ -338,20 +340,45 @@ impl StackingContext {
/// upon entry to this function. /// upon entry to this function.
pub fn hit_test(&self, pub fn hit_test(&self,
point: Point2D<Au>, point: Point2D<Au>,
result: &mut Vec<UntrustedNodeAddress>, result: &mut Vec<DisplayItemMetadata>,
topmost_only: bool) { topmost_only: bool) {
fn hit_test_in_list<'a,I>(point: Point2D<Au>, fn hit_test_in_list<'a,I>(point: Point2D<Au>,
result: &mut Vec<UntrustedNodeAddress>, result: &mut Vec<DisplayItemMetadata>,
topmost_only: bool, topmost_only: bool,
mut iterator: I) mut iterator: I)
where I: Iterator<&'a DisplayItem> { where I: Iterator<&'a DisplayItem> {
for item in iterator { for item in iterator {
if geometry::rect_contains_point(item.base().clip_rect, point) && if !geometry::rect_contains_point(item.base().clip_rect, point) {
geometry::rect_contains_point(item.bounds(), point) { // Clipped out.
result.push(item.base().node.to_untrusted_node_address()); continue
if topmost_only { }
return if !geometry::rect_contains_point(item.bounds(), point) {
// Can't possibly hit.
continue
}
match *item {
BorderDisplayItemClass(ref border) => {
// If the point is inside the border, it didn't hit the border!
let interior_rect =
Rect(Point2D(border.base.bounds.origin.x + border.border_widths.left,
border.base.bounds.origin.y + border.border_widths.top),
Size2D(border.base.bounds.size.width -
(border.border_widths.left +
border.border_widths.right),
border.base.bounds.size.height -
(border.border_widths.top +
border.border_widths.bottom)));
if geometry::rect_contains_point(interior_rect, point) {
continue
}
} }
_ => {}
}
// We found a hit!
result.push(item.base().metadata);
if topmost_only {
return
} }
} }
} }
@ -447,8 +474,8 @@ pub struct BaseDisplayItem {
/// The boundaries of the display item, in layer coordinates. /// The boundaries of the display item, in layer coordinates.
pub bounds: Rect<Au>, pub bounds: Rect<Au>,
/// The originating DOM node. /// Metadata attached to this display item.
pub node: OpaqueNode, pub metadata: DisplayItemMetadata,
/// The rectangle to clip to. /// The rectangle to clip to.
/// ///
@ -459,15 +486,45 @@ pub struct BaseDisplayItem {
impl BaseDisplayItem { impl BaseDisplayItem {
#[inline(always)] #[inline(always)]
pub fn new(bounds: Rect<Au>, node: OpaqueNode, clip_rect: Rect<Au>) -> BaseDisplayItem { pub fn new(bounds: Rect<Au>, metadata: DisplayItemMetadata, clip_rect: Rect<Au>)
-> BaseDisplayItem {
BaseDisplayItem { BaseDisplayItem {
bounds: bounds, bounds: bounds,
node: node, metadata: metadata,
clip_rect: clip_rect, clip_rect: clip_rect,
} }
} }
} }
/// Metadata attached to each display item. This is useful for performing auxiliary tasks with
/// the display list involving hit testing: finding the originating DOM node and determining the
/// cursor to use when the element is hovered over.
#[deriving(Clone)]
pub struct DisplayItemMetadata {
/// The DOM node from which this display item originated.
pub node: OpaqueNode,
/// The value of the `cursor` property when the mouse hovers over this display item.
pub cursor: Cursor,
}
impl DisplayItemMetadata {
/// Creates a new set of display metadata for a display item constributed by a DOM node.
/// `default_cursor` specifies the cursor to use if `cursor` is `auto`. Typically, this will
/// be `PointerCursor`, but for text display items it may be `TextCursor` or
/// `VerticalTextCursor`.
#[inline]
pub fn new(node: OpaqueNode, style: &ComputedValues, default_cursor: Cursor)
-> DisplayItemMetadata {
DisplayItemMetadata {
node: node,
cursor: match style.get_pointing().cursor {
AutoCursor => default_cursor,
SpecifiedCursor(cursor) => cursor,
},
}
}
}
/// Paints a solid color. /// Paints a solid color.
#[deriving(Clone)] #[deriving(Clone)]
pub struct SolidColorDisplayItem { pub struct SolidColorDisplayItem {
@ -738,24 +795,8 @@ impl fmt::Show for DisplayItem {
BoxShadowDisplayItemClass(_) => "BoxShadow", BoxShadowDisplayItemClass(_) => "BoxShadow",
}, },
self.base().bounds, self.base().bounds,
self.base().node.id() self.base().metadata.node.id()
) )
} }
} }
pub trait OpaqueNodeMethods {
/// Converts this node to an `UntrustedNodeAddress`. An `UntrustedNodeAddress` is just the type
/// of node that script expects to receive in a hit test.
fn to_untrusted_node_address(&self) -> UntrustedNodeAddress;
}
impl OpaqueNodeMethods for OpaqueNode {
fn to_untrusted_node_address(&self) -> UntrustedNodeAddress {
unsafe {
let OpaqueNode(addr) = *self;
let addr: UntrustedNodeAddress = mem::transmute(addr);
addr
}
}
}

View file

@ -24,7 +24,7 @@ use geom::{Point2D, Rect, Size2D, SideOffsets2D};
use gfx::color; use gfx::color;
use gfx::display_list::{BOX_SHADOW_INFLATION_FACTOR, BaseDisplayItem, BorderDisplayItem}; use gfx::display_list::{BOX_SHADOW_INFLATION_FACTOR, BaseDisplayItem, BorderDisplayItem};
use gfx::display_list::{BorderDisplayItemClass, BorderRadii, BoxShadowDisplayItem}; use gfx::display_list::{BorderDisplayItemClass, BorderRadii, BoxShadowDisplayItem};
use gfx::display_list::{BoxShadowDisplayItemClass, DisplayItem, DisplayList}; use gfx::display_list::{BoxShadowDisplayItemClass, DisplayItem, DisplayItemMetadata, DisplayList};
use gfx::display_list::{GradientDisplayItem, GradientDisplayItemClass}; use gfx::display_list::{GradientDisplayItem, GradientDisplayItemClass};
use gfx::display_list::{GradientStop, ImageDisplayItem, ImageDisplayItemClass, LineDisplayItem}; use gfx::display_list::{GradientStop, ImageDisplayItem, ImageDisplayItemClass, LineDisplayItem};
use gfx::display_list::{LineDisplayItemClass, SidewaysLeft}; use gfx::display_list::{LineDisplayItemClass, SidewaysLeft};
@ -34,6 +34,7 @@ use gfx::paint_task::PaintLayer;
use servo_msg::compositor_msg::{FixedPosition, Scrollable}; use servo_msg::compositor_msg::{FixedPosition, Scrollable};
use servo_msg::constellation_msg::{ConstellationChan, FrameRectMsg}; use servo_msg::constellation_msg::{ConstellationChan, FrameRectMsg};
use servo_net::image::holder::ImageHolder; use servo_net::image::holder::ImageHolder;
use servo_util::cursor::{DefaultCursor, TextCursor, VerticalTextCursor};
use servo_util::geometry::{mod, Au, ZERO_POINT, ZERO_RECT}; use servo_util::geometry::{mod, Au, ZERO_POINT, ZERO_RECT};
use servo_util::logical_geometry::{LogicalRect, WritingMode}; use servo_util::logical_geometry::{LogicalRect, WritingMode};
use servo_util::opts; use servo_util::opts;
@ -132,6 +133,7 @@ pub trait FragmentDisplayListBuilding {
clip_rect: &Rect<Au>); clip_rect: &Rect<Au>);
fn build_debug_borders_around_text_fragments(&self, fn build_debug_borders_around_text_fragments(&self,
style: &ComputedValues,
display_list: &mut DisplayList, display_list: &mut DisplayList,
flow_origin: Point2D<Au>, flow_origin: Point2D<Au>,
text_fragment: &ScannedTextFragmentInfo, text_fragment: &ScannedTextFragmentInfo,
@ -197,7 +199,11 @@ impl FragmentDisplayListBuilding for Fragment {
let background_color = style.resolve_color(style.get_background().background_color); let background_color = style.resolve_color(style.get_background().background_color);
if !background_color.alpha.approx_eq(&0.0) { if !background_color.alpha.approx_eq(&0.0) {
display_list.push(SolidColorDisplayItemClass(box SolidColorDisplayItem { display_list.push(SolidColorDisplayItemClass(box SolidColorDisplayItem {
base: BaseDisplayItem::new(*absolute_bounds, self.node, *clip_rect), base: BaseDisplayItem::new(*absolute_bounds,
DisplayItemMetadata::new(self.node,
style,
DefaultCursor),
*clip_rect),
color: background_color.to_gfx_color(), color: background_color.to_gfx_color(),
}), level); }), level);
} }
@ -309,7 +315,9 @@ impl FragmentDisplayListBuilding for Fragment {
// Create the image display item. // Create the image display item.
display_list.push(ImageDisplayItemClass(box ImageDisplayItem { display_list.push(ImageDisplayItemClass(box ImageDisplayItem {
base: BaseDisplayItem::new(bounds, self.node, clip_rect), base: BaseDisplayItem::new(bounds,
DisplayItemMetadata::new(self.node, style, DefaultCursor),
clip_rect),
image: image.clone(), image: image.clone(),
stretch_size: Size2D(Au::from_px(image.width as int), stretch_size: Size2D(Au::from_px(image.width as int),
Au::from_px(image.height as int)), Au::from_px(image.height as int)),
@ -417,7 +425,9 @@ impl FragmentDisplayListBuilding for Fragment {
absolute_bounds.origin.y + absolute_bounds.size.height / 2); absolute_bounds.origin.y + absolute_bounds.size.height / 2);
let gradient_display_item = GradientDisplayItemClass(box GradientDisplayItem { let gradient_display_item = GradientDisplayItemClass(box GradientDisplayItem {
base: BaseDisplayItem::new(*absolute_bounds, self.node, clip_rect), base: BaseDisplayItem::new(*absolute_bounds,
DisplayItemMetadata::new(self.node, style, DefaultCursor),
clip_rect),
start_point: center - delta, start_point: center - delta,
end_point: center + delta, end_point: center + delta,
stops: stops, stops: stops,
@ -441,7 +451,11 @@ impl FragmentDisplayListBuilding for Fragment {
absolute_bounds.translate(&Point2D(box_shadow.offset_x, box_shadow.offset_y)) absolute_bounds.translate(&Point2D(box_shadow.offset_x, box_shadow.offset_y))
.inflate(inflation, inflation); .inflate(inflation, inflation);
list.push(BoxShadowDisplayItemClass(box BoxShadowDisplayItem { list.push(BoxShadowDisplayItemClass(box BoxShadowDisplayItem {
base: BaseDisplayItem::new(bounds, self.node, *clip_rect), base: BaseDisplayItem::new(bounds,
DisplayItemMetadata::new(self.node,
style,
DefaultCursor),
*clip_rect),
box_bounds: *absolute_bounds, box_bounds: *absolute_bounds,
color: style.resolve_color(box_shadow.color).to_gfx_color(), color: style.resolve_color(box_shadow.color).to_gfx_color(),
offset: Point2D(box_shadow.offset_x, box_shadow.offset_y), offset: Point2D(box_shadow.offset_x, box_shadow.offset_y),
@ -470,7 +484,9 @@ impl FragmentDisplayListBuilding for Fragment {
// Append the border to the display list. // Append the border to the display list.
display_list.push(BorderDisplayItemClass(box BorderDisplayItem { display_list.push(BorderDisplayItemClass(box BorderDisplayItem {
base: BaseDisplayItem::new(*abs_bounds, self.node, *clip_rect), base: BaseDisplayItem::new(*abs_bounds,
DisplayItemMetadata::new(self.node, style, DefaultCursor),
*clip_rect),
border_widths: border.to_physical(style.writing_mode), border_widths: border.to_physical(style.writing_mode),
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(),
@ -510,7 +526,9 @@ impl FragmentDisplayListBuilding for Fragment {
// Append the outline to the display list. // Append the outline to the display list.
let color = style.resolve_color(style.get_outline().outline_color).to_gfx_color(); let color = style.resolve_color(style.get_outline().outline_color).to_gfx_color();
display_list.outlines.push_back(BorderDisplayItemClass(box BorderDisplayItem { display_list.outlines.push_back(BorderDisplayItemClass(box BorderDisplayItem {
base: BaseDisplayItem::new(bounds, self.node, *clip_rect), base: BaseDisplayItem::new(bounds,
DisplayItemMetadata::new(self.node, style, DefaultCursor),
*clip_rect),
border_widths: SideOffsets2D::new_all_same(width), border_widths: SideOffsets2D::new_all_same(width),
color: SideOffsets2D::new_all_same(color), color: SideOffsets2D::new_all_same(color),
style: SideOffsets2D::new_all_same(outline_style), style: SideOffsets2D::new_all_same(outline_style),
@ -519,6 +537,7 @@ impl FragmentDisplayListBuilding for Fragment {
} }
fn build_debug_borders_around_text_fragments(&self, fn build_debug_borders_around_text_fragments(&self,
style: &ComputedValues,
display_list: &mut DisplayList, display_list: &mut DisplayList,
flow_origin: Point2D<Au>, flow_origin: Point2D<Au>,
text_fragment: &ScannedTextFragmentInfo, text_fragment: &ScannedTextFragmentInfo,
@ -533,7 +552,9 @@ impl FragmentDisplayListBuilding for Fragment {
// Compute the text fragment bounds and draw a border surrounding them. // Compute the text fragment bounds and draw a border surrounding them.
display_list.content.push_back(BorderDisplayItemClass(box BorderDisplayItem { display_list.content.push_back(BorderDisplayItemClass(box BorderDisplayItem {
base: BaseDisplayItem::new(absolute_fragment_bounds, self.node, *clip_rect), base: BaseDisplayItem::new(absolute_fragment_bounds,
DisplayItemMetadata::new(self.node, style, DefaultCursor),
*clip_rect),
border_widths: SideOffsets2D::new_all_same(Au::from_px(1)), border_widths: SideOffsets2D::new_all_same(Au::from_px(1)),
color: SideOffsets2D::new_all_same(color::rgb(0, 0, 200)), color: SideOffsets2D::new_all_same(color::rgb(0, 0, 200)),
style: SideOffsets2D::new_all_same(border_style::solid), style: SideOffsets2D::new_all_same(border_style::solid),
@ -549,7 +570,9 @@ impl FragmentDisplayListBuilding for Fragment {
baseline.origin = baseline.origin + flow_origin; baseline.origin = baseline.origin + flow_origin;
let line_display_item = box LineDisplayItem { let line_display_item = box LineDisplayItem {
base: BaseDisplayItem::new(baseline, self.node, *clip_rect), base: BaseDisplayItem::new(baseline,
DisplayItemMetadata::new(self.node, style, DefaultCursor),
*clip_rect),
color: color::rgb(0, 200, 0), color: color::rgb(0, 200, 0),
style: border_style::dashed, style: border_style::dashed,
}; };
@ -570,7 +593,11 @@ impl FragmentDisplayListBuilding for Fragment {
// This prints a debug border around the border of this fragment. // This prints a debug border around the border of this fragment.
display_list.content.push_back(BorderDisplayItemClass(box BorderDisplayItem { display_list.content.push_back(BorderDisplayItemClass(box BorderDisplayItem {
base: BaseDisplayItem::new(absolute_fragment_bounds, self.node, *clip_rect), base: BaseDisplayItem::new(absolute_fragment_bounds,
DisplayItemMetadata::new(self.node,
&*self.style,
DefaultCursor),
*clip_rect),
border_widths: SideOffsets2D::new_all_same(Au::from_px(1)), border_widths: SideOffsets2D::new_all_same(Au::from_px(1)),
color: SideOffsets2D::new_all_same(color::rgb(0, 0, 200)), color: SideOffsets2D::new_all_same(color::rgb(0, 0, 200)),
style: SideOffsets2D::new_all_same(border_style::solid), style: SideOffsets2D::new_all_same(border_style::solid),
@ -731,14 +758,14 @@ impl FragmentDisplayListBuilding for Fragment {
SpecificFragmentInfo::TableColumn(_) => panic!("Shouldn't see table column fragments here."), SpecificFragmentInfo::TableColumn(_) => panic!("Shouldn't see table column fragments here."),
SpecificFragmentInfo::ScannedText(ref text_fragment) => { SpecificFragmentInfo::ScannedText(ref text_fragment) => {
// Create the text display item. // Create the text display item.
let orientation = if self.style.writing_mode.is_vertical() { let (orientation, cursor) = if self.style.writing_mode.is_vertical() {
if self.style.writing_mode.is_sideways_left() { if self.style.writing_mode.is_sideways_left() {
SidewaysLeft (SidewaysLeft, VerticalTextCursor)
} else { } else {
SidewaysRight (SidewaysRight, VerticalTextCursor)
} }
} else { } else {
Upright (Upright, TextCursor)
}; };
let metrics = &text_fragment.run.font_metrics; let metrics = &text_fragment.run.font_metrics;
@ -750,7 +777,11 @@ impl FragmentDisplayListBuilding for Fragment {
}; };
display_list.content.push_back(TextDisplayItemClass(box TextDisplayItem { display_list.content.push_back(TextDisplayItemClass(box TextDisplayItem {
base: BaseDisplayItem::new(absolute_content_box, self.node, *clip_rect), base: BaseDisplayItem::new(absolute_content_box,
DisplayItemMetadata::new(self.node,
self.style(),
cursor),
*clip_rect),
text_run: text_fragment.run.clone(), text_run: text_fragment.run.clone(),
range: text_fragment.range, range: text_fragment.range,
text_color: self.style().get_color().color.to_gfx_color(), text_color: self.style().get_color().color.to_gfx_color(),
@ -760,14 +791,21 @@ impl FragmentDisplayListBuilding for Fragment {
// Create display items for text decoration // Create display items for text decoration
{ {
let line = |maybe_color: Option<RGBA>, rect: || -> LogicalRect<Au>| { let line = |maybe_color: Option<RGBA>,
style: &ComputedValues,
rect: || -> LogicalRect<Au>| {
match maybe_color { match maybe_color {
None => {} None => {}
Some(color) => { Some(color) => {
let bounds = rect_to_absolute(self.style.writing_mode, rect()); let bounds = rect_to_absolute(self.style.writing_mode, rect());
display_list.content.push_back(SolidColorDisplayItemClass( display_list.content.push_back(SolidColorDisplayItemClass(
box SolidColorDisplayItem { box SolidColorDisplayItem {
base: BaseDisplayItem::new(bounds, self.node, *clip_rect), base: BaseDisplayItem::new(
bounds,
DisplayItemMetadata::new(self.node,
style,
DefaultCursor),
*clip_rect),
color: color.to_gfx_color(), color: color.to_gfx_color(),
})) }))
} }
@ -776,20 +814,20 @@ impl FragmentDisplayListBuilding for Fragment {
let text_decorations = let text_decorations =
self.style().get_inheritedtext()._servo_text_decorations_in_effect; self.style().get_inheritedtext()._servo_text_decorations_in_effect;
line(text_decorations.underline, || { line(text_decorations.underline, self.style(), || {
let mut rect = content_box.clone(); let mut rect = content_box.clone();
rect.start.b = rect.start.b + metrics.ascent - metrics.underline_offset; rect.start.b = rect.start.b + metrics.ascent - metrics.underline_offset;
rect.size.block = metrics.underline_size; rect.size.block = metrics.underline_size;
rect rect
}); });
line(text_decorations.overline, || { line(text_decorations.overline, self.style(), || {
let mut rect = content_box.clone(); let mut rect = content_box.clone();
rect.size.block = metrics.underline_size; rect.size.block = metrics.underline_size;
rect rect
}); });
line(text_decorations.line_through, || { line(text_decorations.line_through, self.style(), || {
let mut rect = content_box.clone(); let mut rect = content_box.clone();
rect.start.b = rect.start.b + metrics.ascent - metrics.strikeout_offset; rect.start.b = rect.start.b + metrics.ascent - metrics.strikeout_offset;
rect.size.block = metrics.strikeout_size; rect.size.block = metrics.strikeout_size;
@ -798,7 +836,8 @@ impl FragmentDisplayListBuilding for Fragment {
} }
if opts::get().show_debug_fragment_borders { if opts::get().show_debug_fragment_borders {
self.build_debug_borders_around_text_fragments(display_list, self.build_debug_borders_around_text_fragments(self.style(),
display_list,
flow_origin, flow_origin,
&**text_fragment, &**text_fragment,
clip_rect); clip_rect);
@ -822,7 +861,9 @@ impl FragmentDisplayListBuilding for Fragment {
// Place the image into the display list. // Place the image into the display list.
display_list.content.push_back(ImageDisplayItemClass(box ImageDisplayItem { display_list.content.push_back(ImageDisplayItemClass(box ImageDisplayItem {
base: BaseDisplayItem::new(absolute_content_box, base: BaseDisplayItem::new(absolute_content_box,
self.node, DisplayItemMetadata::new(self.node,
&*self.style,
DefaultCursor),
*clip_rect), *clip_rect),
image: image.clone(), image: image.clone(),
stretch_size: absolute_content_box.size, stretch_size: absolute_content_box.size,

View file

@ -13,8 +13,7 @@ use flow_ref::FlowRef;
use fragment::{Fragment, FragmentBoundsIterator}; use fragment::{Fragment, FragmentBoundsIterator};
use incremental::{LayoutDamageComputation, REFLOW, REFLOW_ENTIRE_DOCUMENT, REPAINT}; use incremental::{LayoutDamageComputation, REFLOW, REFLOW_ENTIRE_DOCUMENT, REPAINT};
use layout_debug; use layout_debug;
use parallel::UnsafeFlow; use parallel::{mod, UnsafeFlow};
use parallel;
use sequential; use sequential;
use util::{LayoutDataAccess, LayoutDataWrapper, OpaqueNodeMethods, ToGfxColor}; use util::{LayoutDataAccess, LayoutDataWrapper, OpaqueNodeMethods, ToGfxColor};
use wrapper::{LayoutNode, TLayoutNode, ThreadSafeLayoutNode}; use wrapper::{LayoutNode, TLayoutNode, ThreadSafeLayoutNode};
@ -26,7 +25,7 @@ use geom::rect::Rect;
use geom::size::Size2D; use geom::size::Size2D;
use geom::scale_factor::ScaleFactor; use geom::scale_factor::ScaleFactor;
use gfx::color; use gfx::color;
use gfx::display_list::{DisplayList, OpaqueNode, StackingContext}; use gfx::display_list::{DisplayItemMetadata, DisplayList, OpaqueNode, StackingContext};
use gfx::font_cache_task::FontCacheTask; use gfx::font_cache_task::FontCacheTask;
use gfx::paint_task::{mod, PaintInitMsg, PaintChan, PaintLayer}; use gfx::paint_task::{mod, PaintInitMsg, PaintChan, PaintLayer};
use layout_traits; use layout_traits;
@ -40,22 +39,25 @@ use script::layout_interface::{ContentBoxesQuery, ContentBoxQuery, ExitNowMsg, G
use script::layout_interface::{HitTestResponse, LayoutChan, LayoutRPC, LoadStylesheetMsg}; use script::layout_interface::{HitTestResponse, LayoutChan, LayoutRPC, LoadStylesheetMsg};
use script::layout_interface::{MouseOverResponse, Msg, NoQuery, PrepareToExitMsg}; use script::layout_interface::{MouseOverResponse, Msg, NoQuery, PrepareToExitMsg};
use script::layout_interface::{ReapLayoutDataMsg, Reflow, ReflowForDisplay, ReflowMsg}; use script::layout_interface::{ReapLayoutDataMsg, Reflow, ReflowForDisplay, ReflowMsg};
use script::layout_interface::{ScriptLayoutChan, SetQuirksModeMsg, TrustedNodeAddress}; use script::layout_interface::{ReflowForScriptQuery, ScriptLayoutChan, SetQuirksModeMsg};
use script::layout_interface::{TrustedNodeAddress};
use script_traits::{SendEventMsg, ReflowEvent, ReflowCompleteMsg, OpaqueScriptLayoutChannel}; use script_traits::{SendEventMsg, ReflowEvent, ReflowCompleteMsg, OpaqueScriptLayoutChannel};
use script_traits::{ScriptControlChan, UntrustedNodeAddress}; use script_traits::{ScriptControlChan, UntrustedNodeAddress};
use servo_msg::compositor_msg::Scrollable; 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_msg::constellation_msg::{SetCursorMsg};
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_net::resource_task::{ResourceTask, load_bytes_iter}; use servo_net::resource_task::{ResourceTask, load_bytes_iter};
use servo_util::cursor::DefaultCursor;
use servo_util::geometry::Au; use servo_util::geometry::Au;
use servo_util::logical_geometry::LogicalPoint; use servo_util::logical_geometry::LogicalPoint;
use servo_util::opts; use servo_util::opts;
use servo_util::smallvec::{SmallVec, SmallVec1, VecLike}; use servo_util::smallvec::{SmallVec, SmallVec1, VecLike};
use servo_util::task::spawn_named_with_send_on_failure; use servo_util::task::spawn_named_with_send_on_failure;
use servo_util::task_state; use servo_util::task_state;
use servo_util::time::{TimeProfilerChan, profile, TimerMetadataFrameType, TimerMetadataReflowType}; use servo_util::time::{mod, ProfilerMetadata, TimeProfilerChan, TimerMetadataFrameType};
use servo_util::time; use servo_util::time::{TimerMetadataReflowType, profile};
use servo_util::workqueue::WorkQueue; use servo_util::workqueue::WorkQueue;
use std::cell::Cell; use std::cell::Cell;
use std::comm::{channel, Sender, Receiver, Select}; use std::comm::{channel, Sender, Receiver, Select};
@ -73,6 +75,9 @@ pub struct LayoutTaskData {
/// The local image cache. /// The local image cache.
pub local_image_cache: Arc<Mutex<LocalImageCache<UntrustedNodeAddress>>>, pub local_image_cache: Arc<Mutex<LocalImageCache<UntrustedNodeAddress>>>,
/// The channel on which messages can be sent to the constellation.
pub constellation_chan: ConstellationChan,
/// The size of the viewport. /// The size of the viewport.
pub screen_size: Size2D<Au>, pub screen_size: Size2D<Au>,
@ -112,9 +117,6 @@ pub struct LayoutTask {
//// The channel to send messages to ourself. //// The channel to send messages to ourself.
pub chan: LayoutChan, pub chan: LayoutChan,
/// The channel on which messages can be sent to the constellation.
pub constellation_chan: ConstellationChan,
/// The channel on which messages can be sent to the script task. /// The channel on which messages can be sent to the script task.
pub script_chan: ScriptControlChan, pub script_chan: ScriptControlChan,
@ -262,7 +264,6 @@ impl LayoutTask {
port: port, port: port,
pipeline_port: pipeline_port, pipeline_port: pipeline_port,
chan: chan, chan: chan,
constellation_chan: constellation_chan,
script_chan: script_chan, script_chan: script_chan,
paint_chan: paint_chan, paint_chan: paint_chan,
time_profiler_chan: time_profiler_chan, time_profiler_chan: time_profiler_chan,
@ -273,6 +274,7 @@ impl LayoutTask {
rw_data: Arc::new(Mutex::new( rw_data: Arc::new(Mutex::new(
LayoutTaskData { LayoutTaskData {
local_image_cache: local_image_cache, local_image_cache: local_image_cache,
constellation_chan: constellation_chan,
screen_size: screen_size, screen_size: screen_size,
stacking_context: None, stacking_context: None,
stylist: box Stylist::new(device), stylist: box Stylist::new(device),
@ -302,7 +304,7 @@ impl LayoutTask {
SharedLayoutContext { SharedLayoutContext {
image_cache: rw_data.local_image_cache.clone(), image_cache: rw_data.local_image_cache.clone(),
screen_size: rw_data.screen_size.clone(), screen_size: rw_data.screen_size.clone(),
constellation_chan: self.constellation_chan.clone(), constellation_chan: rw_data.constellation_chan.clone(),
layout_chan: self.chan.clone(), layout_chan: self.chan.clone(),
font_cache_task: self.font_cache_task.clone(), font_cache_task: self.font_cache_task.clone(),
stylist: &*rw_data.stylist, stylist: &*rw_data.stylist,
@ -397,9 +399,7 @@ impl LayoutTask {
}, },
ReflowMsg(data) => { ReflowMsg(data) => {
profile(time::LayoutPerformCategory, profile(time::LayoutPerformCategory,
Some((&data.url, self.profiler_metadata(&*data),
if data.iframe { TimerMetadataFrameType::IFrame } else { TimerMetadataFrameType::RootWindow },
if self.first_reflow.get() { TimerMetadataReflowType::FirstReflow } else { TimerMetadataReflowType::Incremental })),
self.time_profiler_chan.clone(), self.time_profiler_chan.clone(),
|| self.handle_reflow(&*data, possibly_locked_rw_data)); || self.handle_reflow(&*data, possibly_locked_rw_data));
}, },
@ -573,9 +573,7 @@ impl LayoutTask {
// NOTE: this currently computes borders, so any pruning should separate that // NOTE: this currently computes borders, so any pruning should separate that
// operation out. // operation out.
parallel::traverse_flow_tree_preorder(layout_root, parallel::traverse_flow_tree_preorder(layout_root,
&data.url, self.profiler_metadata(data),
if data.iframe { TimerMetadataFrameType::IFrame } else { TimerMetadataFrameType::RootWindow },
if self.first_reflow.get() { TimerMetadataReflowType::FirstReflow } else { TimerMetadataReflowType::Incremental },
self.time_profiler_chan.clone(), self.time_profiler_chan.clone(),
shared_layout_context, shared_layout_context,
traversal); traversal);
@ -624,16 +622,14 @@ impl LayoutTask {
data: &Reflow, data: &Reflow,
node: &mut LayoutNode, node: &mut LayoutNode,
layout_root: &mut FlowRef, layout_root: &mut FlowRef,
shared_layout_ctx: &mut SharedLayoutContext, shared_layout_context: &mut SharedLayoutContext,
rw_data: &mut RWGuard<'a>) { rw_data: &mut RWGuard<'a>) {
let writing_mode = flow::base(&**layout_root).writing_mode; let writing_mode = flow::base(&**layout_root).writing_mode;
profile(time::LayoutDispListBuildCategory, profile(time::LayoutDispListBuildCategory,
Some((&data.url, self.profiler_metadata(data),
if data.iframe { TimerMetadataFrameType::IFrame } else { TimerMetadataFrameType::RootWindow }, self.time_profiler_chan.clone(),
if self.first_reflow.get() { TimerMetadataReflowType::FirstReflow } else { TimerMetadataReflowType::Incremental })), || {
self.time_profiler_chan.clone(), shared_layout_context.dirty =
|| {
shared_layout_ctx.dirty =
flow::base(&**layout_root).position.to_physical(writing_mode, flow::base(&**layout_root).position.to_physical(writing_mode,
rw_data.screen_size); rw_data.screen_size);
flow::mut_base(&mut **layout_root).stacking_relative_position = flow::mut_base(&mut **layout_root).stacking_relative_position =
@ -645,15 +641,13 @@ impl LayoutTask {
let rw_data = rw_data.deref_mut(); let rw_data = rw_data.deref_mut();
match rw_data.parallel_traversal { match rw_data.parallel_traversal {
None => { None => {
sequential::build_display_list_for_subtree(layout_root, shared_layout_ctx); sequential::build_display_list_for_subtree(layout_root, shared_layout_context);
} }
Some(ref mut traversal) => { Some(ref mut traversal) => {
parallel::build_display_list_for_subtree(layout_root, parallel::build_display_list_for_subtree(layout_root,
&data.url, self.profiler_metadata(data),
if data.iframe { TimerMetadataFrameType::IFrame } else { TimerMetadataFrameType::RootWindow },
if self.first_reflow.get() { TimerMetadataReflowType::FirstReflow } else { TimerMetadataReflowType::Incremental },
self.time_profiler_chan.clone(), self.time_profiler_chan.clone(),
shared_layout_ctx, shared_layout_context,
traversal); traversal);
} }
} }
@ -746,9 +740,9 @@ impl LayoutTask {
rw_data.screen_size = current_screen_size; rw_data.screen_size = current_screen_size;
// Create a layout context for use throughout the following passes. // Create a layout context for use throughout the following passes.
let mut shared_layout_ctx = self.build_shared_layout_context(rw_data.deref(), let mut shared_layout_context = self.build_shared_layout_context(rw_data.deref(),
node, node,
&data.url); &data.url);
// Handle conditions where the entire flow tree is invalid. // Handle conditions where the entire flow tree is invalid.
let screen_size_changed = current_screen_size != old_screen_size; let screen_size_changed = current_screen_size != old_screen_size;
@ -775,19 +769,17 @@ impl LayoutTask {
} }
let mut layout_root = profile(time::LayoutStyleRecalcCategory, let mut layout_root = profile(time::LayoutStyleRecalcCategory,
Some((&data.url, self.profiler_metadata(data),
if data.iframe { TimerMetadataFrameType::IFrame } else { TimerMetadataFrameType::RootWindow },
if self.first_reflow.get() { TimerMetadataReflowType::FirstReflow } else { TimerMetadataReflowType::Incremental })),
self.time_profiler_chan.clone(), self.time_profiler_chan.clone(),
|| { || {
// Perform CSS selector matching and flow construction. // Perform CSS selector matching and flow construction.
let rw_data = rw_data.deref_mut(); let rw_data = rw_data.deref_mut();
match rw_data.parallel_traversal { match rw_data.parallel_traversal {
None => { None => {
sequential::traverse_dom_preorder(*node, &shared_layout_ctx); sequential::traverse_dom_preorder(*node, &shared_layout_context);
} }
Some(ref mut traversal) => { Some(ref mut traversal) => {
parallel::traverse_dom_preorder(*node, &shared_layout_ctx, traversal) parallel::traverse_dom_preorder(*node, &shared_layout_context, traversal)
} }
} }
@ -795,13 +787,12 @@ impl LayoutTask {
}); });
profile(time::LayoutRestyleDamagePropagation, profile(time::LayoutRestyleDamagePropagation,
Some((&data.url, self.profiler_metadata(data),
if data.iframe { TimerMetadataFrameType::IFrame } else { TimerMetadataFrameType::RootWindow },
if self.first_reflow.get() { TimerMetadataReflowType::FirstReflow } else { TimerMetadataReflowType::Incremental })),
self.time_profiler_chan.clone(), self.time_profiler_chan.clone(),
|| { || {
if opts::get().nonincremental_layout || if opts::get().nonincremental_layout || layout_root.deref_mut()
layout_root.deref_mut().compute_layout_damage().contains(REFLOW_ENTIRE_DOCUMENT) { .compute_layout_damage()
.contains(REFLOW_ENTIRE_DOCUMENT) {
layout_root.deref_mut().reflow_entire_document() layout_root.deref_mut().reflow_entire_document()
} }
}); });
@ -818,42 +809,45 @@ impl LayoutTask {
// Perform the primary layout passes over the flow tree to compute the locations of all // Perform the primary layout passes over the flow tree to compute the locations of all
// the boxes. // the boxes.
profile(time::LayoutMainCategory, profile(time::LayoutMainCategory,
Some((&data.url, self.profiler_metadata(data),
if data.iframe { TimerMetadataFrameType::IFrame } else { TimerMetadataFrameType::RootWindow },
if self.first_reflow.get() { TimerMetadataReflowType::FirstReflow } else { TimerMetadataReflowType::Incremental })),
self.time_profiler_chan.clone(), self.time_profiler_chan.clone(),
|| { || {
let rw_data = rw_data.deref_mut(); let rw_data = rw_data.deref_mut();
match rw_data.parallel_traversal { match rw_data.parallel_traversal {
None => { None => {
// Sequential mode. // Sequential mode.
self.solve_constraints(&mut layout_root, &shared_layout_ctx) self.solve_constraints(&mut layout_root, &shared_layout_context)
} }
Some(_) => { Some(_) => {
// Parallel mode. // Parallel mode.
self.solve_constraints_parallel(data, self.solve_constraints_parallel(data,
rw_data, rw_data,
&mut layout_root, &mut layout_root,
&mut shared_layout_ctx); &mut shared_layout_context);
} }
} }
}); });
// Build the display list if necessary, and send it to the painter. // Build the display list if necessary, and send it to the painter.
if data.goal == ReflowForDisplay { match data.goal {
self.build_display_list_for_reflow(data, ReflowForDisplay => {
node, self.build_display_list_for_reflow(data,
&mut layout_root, node,
&mut shared_layout_ctx, &mut layout_root,
&mut rw_data); &mut shared_layout_context,
&mut rw_data);
}
ReflowForScriptQuery => {}
} }
match data.query_type { match data.query_type {
ContentBoxQuery(node) => ContentBoxQuery(node) => {
self.process_content_box_request(node, &mut layout_root, &mut rw_data), self.process_content_box_request(node, &mut layout_root, &mut rw_data)
ContentBoxesQuery(node) => }
self.process_content_boxes_request(node, &mut layout_root, &mut rw_data), ContentBoxesQuery(node) => {
NoQuery => {}, self.process_content_boxes_request(node, &mut layout_root, &mut rw_data)
}
NoQuery => {}
} }
self.first_reflow.set(false); self.first_reflow.set(false);
@ -896,11 +890,13 @@ impl LayoutTask {
} }
} }
// When images can't be loaded in time to display they trigger /// When images can't be loaded in time to display they trigger
// this callback in some task somewhere. This will send a message /// this callback in some task somewhere. This will send a message
// to the script task, and ultimately cause the image to be /// to the script task, and ultimately cause the image to be
// re-requested. We probably don't need to go all the way back to /// re-requested. We probably don't need to go all the way back to
// the script task for this. /// the script task for this.
///
/// FIXME(pcwalton): Rewrite all of this.
fn make_on_image_available_cb(&self) -> Box<ImageResponder<UntrustedNodeAddress>+Send> { fn make_on_image_available_cb(&self) -> Box<ImageResponder<UntrustedNodeAddress>+Send> {
// This has a crazy signature because the image cache needs to // This has a crazy signature because the image cache needs to
// make multiple copies of the callback, and the dom event // make multiple copies of the callback, and the dom event
@ -919,6 +915,21 @@ impl LayoutTask {
let _: Option<LayoutDataWrapper> = mem::transmute( let _: Option<LayoutDataWrapper> = mem::transmute(
mem::replace(&mut *layout_data_ref, None)); mem::replace(&mut *layout_data_ref, None));
} }
/// Returns profiling information which is passed to the time profiler.
fn profiler_metadata<'a>(&self, data: &'a Reflow) -> ProfilerMetadata<'a> {
Some((&data.url,
if data.iframe {
TimerMetadataFrameType::IFrame
} else {
TimerMetadataFrameType::RootWindow
},
if self.first_reflow.get() {
TimerMetadataReflowType::FirstReflow
} else {
TimerMetadataReflowType::Incremental
}))
}
} }
struct LayoutRPCImpl(Arc<Mutex<LayoutTaskData>>); struct LayoutRPCImpl(Arc<Mutex<LayoutTaskData>>);
@ -951,7 +962,7 @@ impl LayoutRPC for LayoutRPCImpl {
let mut result = Vec::new(); let mut result = Vec::new();
stacking_context.hit_test(point, &mut result, true); stacking_context.hit_test(point, &mut result, true);
if !result.is_empty() { if !result.is_empty() {
Some(HitTestResponse(result[0])) Some(HitTestResponse(result[0].node.to_untrusted_node_address()))
} else { } else {
None None
} }
@ -967,7 +978,7 @@ impl LayoutRPC for LayoutRPCImpl {
fn mouse_over(&self, _: TrustedNodeAddress, point: Point2D<f32>) fn mouse_over(&self, _: TrustedNodeAddress, point: Point2D<f32>)
-> Result<MouseOverResponse, ()> { -> Result<MouseOverResponse, ()> {
let mut mouse_over_list: Vec<UntrustedNodeAddress> = vec!(); let mut mouse_over_list: Vec<DisplayItemMetadata> = vec!();
let point = Point2D(Au::from_frac_px(point.x as f64), Au::from_frac_px(point.y as f64)); let point = Point2D(Au::from_frac_px(point.x as f64), Au::from_frac_px(point.y as f64));
{ {
let &LayoutRPCImpl(ref rw_data) = self; let &LayoutRPCImpl(ref rw_data) = self;
@ -978,12 +989,25 @@ impl LayoutRPC for LayoutRPCImpl {
stacking_context.hit_test(point, &mut mouse_over_list, false); stacking_context.hit_test(point, &mut mouse_over_list, false);
} }
} }
// Compute the new cursor.
let cursor = if !mouse_over_list.is_empty() {
mouse_over_list[0].cursor
} else {
DefaultCursor
};
let ConstellationChan(ref constellation_chan) = rw_data.constellation_chan;
constellation_chan.send(SetCursorMsg(cursor));
} }
if mouse_over_list.is_empty() { if mouse_over_list.is_empty() {
Err(()) Err(())
} else { } else {
Ok(MouseOverResponse(mouse_over_list)) let response_list =
mouse_over_list.iter()
.map(|metadata| metadata.node.to_untrusted_node_address())
.collect();
Ok(MouseOverResponse(response_list))
} }
} }
} }

View file

@ -13,15 +13,13 @@ use flow_ref::FlowRef;
use traversal::{RecalcStyleForNode, ConstructFlows}; use traversal::{RecalcStyleForNode, ConstructFlows};
use traversal::{BubbleISizes, AssignISizes, AssignBSizesAndStoreOverflow}; use traversal::{BubbleISizes, AssignISizes, AssignBSizesAndStoreOverflow};
use traversal::{ComputeAbsolutePositions, BuildDisplayList}; use traversal::{ComputeAbsolutePositions, BuildDisplayList};
use url::Url;
use util::{LayoutDataAccess, LayoutDataWrapper}; use util::{LayoutDataAccess, LayoutDataWrapper};
use wrapper::{layout_node_to_unsafe_layout_node, layout_node_from_unsafe_layout_node, LayoutNode}; use wrapper::{layout_node_to_unsafe_layout_node, layout_node_from_unsafe_layout_node, LayoutNode};
use wrapper::{PostorderNodeMutTraversal, UnsafeLayoutNode}; use wrapper::{PostorderNodeMutTraversal, UnsafeLayoutNode};
use wrapper::{PreorderDomTraversal, PostorderDomTraversal}; use wrapper::{PreorderDomTraversal, PostorderDomTraversal};
use servo_util::opts; use servo_util::opts;
use servo_util::time::{TimeProfilerChan, profile, TimerMetadataFrameType, TimerMetadataReflowType}; use servo_util::time::{mod, ProfilerMetadata, TimeProfilerChan, profile};
use servo_util::time;
use servo_util::workqueue::{WorkQueue, WorkUnit, WorkerProxy}; use servo_util::workqueue::{WorkQueue, WorkUnit, WorkerProxy};
use std::mem; use std::mem;
use std::ptr; use std::ptr;
@ -422,9 +420,7 @@ pub fn traverse_dom_preorder(root: LayoutNode,
} }
pub fn traverse_flow_tree_preorder(root: &mut FlowRef, pub fn traverse_flow_tree_preorder(root: &mut FlowRef,
url: &Url, profiler_metadata: ProfilerMetadata,
iframe: TimerMetadataFrameType,
reflow_type: TimerMetadataReflowType,
time_profiler_chan: TimeProfilerChan, time_profiler_chan: TimeProfilerChan,
shared_layout_context: &SharedLayoutContext, shared_layout_context: &SharedLayoutContext,
queue: &mut WorkQueue<*const SharedLayoutContext,UnsafeFlow>) { queue: &mut WorkQueue<*const SharedLayoutContext,UnsafeFlow>) {
@ -436,7 +432,7 @@ pub fn traverse_flow_tree_preorder(root: &mut FlowRef,
queue.data = shared_layout_context as *const _; queue.data = shared_layout_context as *const _;
profile(time::LayoutParallelWarmupCategory, Some((url, iframe, reflow_type)), time_profiler_chan, || { profile(time::LayoutParallelWarmupCategory, profiler_metadata, time_profiler_chan, || {
queue.push(WorkUnit { queue.push(WorkUnit {
fun: assign_inline_sizes, fun: assign_inline_sizes,
data: mut_owned_flow_to_unsafe_flow(root), data: mut_owned_flow_to_unsafe_flow(root),
@ -449,15 +445,13 @@ pub fn traverse_flow_tree_preorder(root: &mut FlowRef,
} }
pub fn build_display_list_for_subtree(root: &mut FlowRef, pub fn build_display_list_for_subtree(root: &mut FlowRef,
url: &Url, profiler_metadata: ProfilerMetadata,
iframe: TimerMetadataFrameType,
reflow_type: TimerMetadataReflowType,
time_profiler_chan: TimeProfilerChan, time_profiler_chan: TimeProfilerChan,
shared_layout_context: &SharedLayoutContext, shared_layout_context: &SharedLayoutContext,
queue: &mut WorkQueue<*const SharedLayoutContext,UnsafeFlow>) { queue: &mut WorkQueue<*const SharedLayoutContext,UnsafeFlow>) {
queue.data = shared_layout_context as *const _; queue.data = shared_layout_context as *const _;
profile(time::LayoutParallelWarmupCategory, Some((url, iframe, reflow_type)), time_profiler_chan, || { profile(time::LayoutParallelWarmupCategory, profiler_metadata, time_profiler_chan, || {
queue.push(WorkUnit { queue.push(WorkUnit {
fun: compute_absolute_positions, fun: compute_absolute_positions,
data: mut_owned_flow_to_unsafe_flow(root), data: mut_owned_flow_to_unsafe_flow(root),

View file

@ -8,6 +8,9 @@ authors = ["The Servo Project Developers"]
name = "msg" name = "msg"
path = "lib.rs" path = "lib.rs"
[dependencies.style]
path = "../style"
[dependencies.util] [dependencies.util]
path = "../util" path = "../util"

View file

@ -11,6 +11,7 @@ use geom::scale_factor::ScaleFactor;
use hyper::header::Headers; use hyper::header::Headers;
use hyper::method::{Method, Get}; use hyper::method::{Method, Get};
use layers::geometry::DevicePixel; use layers::geometry::DevicePixel;
use servo_util::cursor::Cursor;
use servo_util::geometry::{PagePx, ViewportPx}; use servo_util::geometry::{PagePx, ViewportPx};
use std::comm::{channel, Sender, Receiver}; use std::comm::{channel, Sender, Receiver};
use url::Url; use url::Url;
@ -208,6 +209,8 @@ pub enum Msg {
/// Requests that the constellation inform the compositor of the title of the pipeline /// Requests that the constellation inform the compositor of the title of the pipeline
/// immediately. /// immediately.
GetPipelineTitleMsg(PipelineId), GetPipelineTitleMsg(PipelineId),
/// Requests that the constellation inform the compositor of the a cursor change.
SetCursorMsg(Cursor),
} }
/// Similar to net::resource_task::LoadData /// Similar to net::resource_task::LoadData

View file

@ -1150,7 +1150,6 @@ impl ScriptTask {
let page = get_page(&*self.page.borrow(), pipeline_id); let page = get_page(&*self.page.borrow(), pipeline_id);
match page.get_nodes_under_mouse(&point) { match page.get_nodes_under_mouse(&point) {
Some(node_address) => { Some(node_address) => {
let mut target_list = vec!(); let mut target_list = vec!();
let mut target_compare = false; let mut target_compare = false;
@ -1166,23 +1165,19 @@ impl ScriptTask {
} }
for node_address in node_address.iter() { for node_address in node_address.iter() {
let temp_node = let temp_node =
node::from_untrusted_node_address( node::from_untrusted_node_address(self.js_runtime.ptr, *node_address);
self.js_runtime.ptr, *node_address);
let maybe_node = temp_node.root().ancestors().find(|node| node.is_element()); let maybe_node = temp_node.root().ancestors().find(|node| node.is_element());
match maybe_node { match maybe_node {
Some(node) => { Some(node) => {
node.set_hover_state(true); node.set_hover_state(true);
match *mouse_over_targets { match *mouse_over_targets {
Some(ref mouse_over_targets) => { Some(ref mouse_over_targets) if !target_compare => {
if !target_compare { target_compare =
target_compare = !mouse_over_targets.contains(&JS::from_rooted(node)); !mouse_over_targets.contains(&JS::from_rooted(node));
}
} }
None => {} _ => {}
} }
target_list.push(JS::from_rooted(node)); target_list.push(JS::from_rooted(node));
} }
@ -1192,15 +1187,15 @@ impl ScriptTask {
match *mouse_over_targets { match *mouse_over_targets {
Some(ref mouse_over_targets) => { Some(ref mouse_over_targets) => {
if mouse_over_targets.len() != target_list.len() { if mouse_over_targets.len() != target_list.len() {
target_compare = true; target_compare = true
} }
} }
None => { target_compare = true; } None => target_compare = true,
} }
if target_compare { if target_compare {
if mouse_over_targets.is_some() { if mouse_over_targets.is_some() {
self.force_reflow(&*page); self.force_reflow(&*page)
} }
*mouse_over_targets = Some(target_list); *mouse_over_targets = Some(target_list);
} }

View file

@ -501,6 +501,7 @@ dependencies = [
"hyper 0.0.1 (git+https://github.com/servo/hyper?ref=servo)", "hyper 0.0.1 (git+https://github.com/servo/hyper?ref=servo)",
"io_surface 0.1.0 (git+https://github.com/servo/rust-io-surface)", "io_surface 0.1.0 (git+https://github.com/servo/rust-io-surface)",
"layers 0.1.0 (git+https://github.com/servo/rust-layers)", "layers 0.1.0 (git+https://github.com/servo/rust-layers)",
"style 0.0.1",
"url 0.1.0 (git+https://github.com/servo/rust-url)", "url 0.1.0 (git+https://github.com/servo/rust-url)",
"util 0.0.1", "util 0.0.1",
] ]

View file

@ -32,7 +32,7 @@ import re
def to_rust_ident(name): def to_rust_ident(name):
name = name.replace("-", "_") name = name.replace("-", "_")
if name in ["static", "super", "box"]: # Rust keywords if name in ["static", "super", "box", "move"]: # Rust keywords
name += "_" name += "_"
return name return name
@ -1349,6 +1349,139 @@ pub mod longhands {
${single_keyword("box-sizing", "content-box border-box")} ${single_keyword("box-sizing", "content-box border-box")}
${new_style_struct("Pointing", is_inherited=True)}
<%self:single_component_value name="cursor">
use servo_util::cursor as util_cursor;
pub use super::computed_as_specified as to_computed_value;
pub mod computed_value {
use servo_util::cursor::Cursor;
#[deriving(Clone, PartialEq, Show)]
pub enum T {
AutoCursor,
SpecifiedCursor(Cursor),
}
}
pub type SpecifiedValue = computed_value::T;
#[inline]
pub fn get_initial_value() -> computed_value::T {
computed_value::T::AutoCursor
}
pub fn from_component_value(value: &ComponentValue, _: &Url)
-> Result<SpecifiedValue,()> {
match value {
&Ident(ref value) if value.eq_ignore_ascii_case("auto") => Ok(T::AutoCursor),
&Ident(ref value) if value.eq_ignore_ascii_case("none") => {
Ok(T::SpecifiedCursor(util_cursor::NoCursor))
}
&Ident(ref value) if value.eq_ignore_ascii_case("default") => {
Ok(T::SpecifiedCursor(util_cursor::DefaultCursor))
}
&Ident(ref value) if value.eq_ignore_ascii_case("pointer") => {
Ok(T::SpecifiedCursor(util_cursor::PointerCursor))
}
&Ident(ref value) if value.eq_ignore_ascii_case("context-menu") => {
Ok(T::SpecifiedCursor(util_cursor::ContextMenuCursor))
}
&Ident(ref value) if value.eq_ignore_ascii_case("help") => {
Ok(T::SpecifiedCursor(util_cursor::HelpCursor))
}
&Ident(ref value) if value.eq_ignore_ascii_case("progress") => {
Ok(T::SpecifiedCursor(util_cursor::ProgressCursor))
}
&Ident(ref value) if value.eq_ignore_ascii_case("wait") => {
Ok(T::SpecifiedCursor(util_cursor::WaitCursor))
}
&Ident(ref value) if value.eq_ignore_ascii_case("cell") => {
Ok(T::SpecifiedCursor(util_cursor::CellCursor))
}
&Ident(ref value) if value.eq_ignore_ascii_case("crosshair") => {
Ok(T::SpecifiedCursor(util_cursor::CrosshairCursor))
}
&Ident(ref value) if value.eq_ignore_ascii_case("text") => {
Ok(T::SpecifiedCursor(util_cursor::TextCursor))
}
&Ident(ref value) if value.eq_ignore_ascii_case("vertical-text") => {
Ok(T::SpecifiedCursor(util_cursor::VerticalTextCursor))
}
&Ident(ref value) if value.eq_ignore_ascii_case("alias") => {
Ok(T::SpecifiedCursor(util_cursor::AliasCursor))
}
&Ident(ref value) if value.eq_ignore_ascii_case("copy") => {
Ok(T::SpecifiedCursor(util_cursor::CopyCursor))
}
&Ident(ref value) if value.eq_ignore_ascii_case("move") => {
Ok(T::SpecifiedCursor(util_cursor::MoveCursor))
}
&Ident(ref value) if value.eq_ignore_ascii_case("no-drop") => {
Ok(T::SpecifiedCursor(util_cursor::NoDropCursor))
}
&Ident(ref value) if value.eq_ignore_ascii_case("not-allowed") => {
Ok(T::SpecifiedCursor(util_cursor::NotAllowedCursor))
}
&Ident(ref value) if value.eq_ignore_ascii_case("grab") => {
Ok(T::SpecifiedCursor(util_cursor::GrabCursor))
}
&Ident(ref value) if value.eq_ignore_ascii_case("grabbing") => {
Ok(T::SpecifiedCursor(util_cursor::GrabbingCursor))
}
&Ident(ref value) if value.eq_ignore_ascii_case("e-resize") => {
Ok(T::SpecifiedCursor(util_cursor::EResizeCursor))
}
&Ident(ref value) if value.eq_ignore_ascii_case("n-resize") => {
Ok(T::SpecifiedCursor(util_cursor::NResizeCursor))
}
&Ident(ref value) if value.eq_ignore_ascii_case("ne-resize") => {
Ok(T::SpecifiedCursor(util_cursor::NeResizeCursor))
}
&Ident(ref value) if value.eq_ignore_ascii_case("nw-resize") => {
Ok(T::SpecifiedCursor(util_cursor::NwResizeCursor))
}
&Ident(ref value) if value.eq_ignore_ascii_case("s-resize") => {
Ok(T::SpecifiedCursor(util_cursor::SResizeCursor))
}
&Ident(ref value) if value.eq_ignore_ascii_case("se-resize") => {
Ok(T::SpecifiedCursor(util_cursor::SeResizeCursor))
}
&Ident(ref value) if value.eq_ignore_ascii_case("sw-resize") => {
Ok(T::SpecifiedCursor(util_cursor::SwResizeCursor))
}
&Ident(ref value) if value.eq_ignore_ascii_case("w-resize") => {
Ok(T::SpecifiedCursor(util_cursor::WResizeCursor))
}
&Ident(ref value) if value.eq_ignore_ascii_case("ew-resize") => {
Ok(T::SpecifiedCursor(util_cursor::EwResizeCursor))
}
&Ident(ref value) if value.eq_ignore_ascii_case("ns-resize") => {
Ok(T::SpecifiedCursor(util_cursor::NsResizeCursor))
}
&Ident(ref value) if value.eq_ignore_ascii_case("nesw-resize") => {
Ok(T::SpecifiedCursor(util_cursor::NeswResizeCursor))
}
&Ident(ref value) if value.eq_ignore_ascii_case("nwse-resize") => {
Ok(T::SpecifiedCursor(util_cursor::NwseResizeCursor))
}
&Ident(ref value) if value.eq_ignore_ascii_case("col-resize") => {
Ok(T::SpecifiedCursor(util_cursor::ColResizeCursor))
}
&Ident(ref value) if value.eq_ignore_ascii_case("row-resize") => {
Ok(T::SpecifiedCursor(util_cursor::RowResizeCursor))
}
&Ident(ref value) if value.eq_ignore_ascii_case("all-scroll") => {
Ok(T::SpecifiedCursor(util_cursor::AllScrollCursor))
}
&Ident(ref value) if value.eq_ignore_ascii_case("zoom-in") => {
Ok(T::SpecifiedCursor(util_cursor::ZoomInCursor))
}
&Ident(ref value) if value.eq_ignore_ascii_case("zoom-out") => {
Ok(T::SpecifiedCursor(util_cursor::ZoomOutCursor))
}
_ => Err(())
}
}
</%self:single_component_value>
// Box-shadow, etc. // Box-shadow, etc.
${new_style_struct("Effects", is_inherited=False)} ${new_style_struct("Effects", is_inherited=False)}

46
components/util/cursor.rs Normal file
View file

@ -0,0 +1,46 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//! A list of common mouse cursors per CSS3-UI § 8.1.1.
#[deriving(Clone, PartialEq, FromPrimitive, Show)]
#[repr(u8)]
pub enum Cursor {
NoCursor = 0,
DefaultCursor = 1,
PointerCursor = 2,
ContextMenuCursor = 3,
HelpCursor = 4,
ProgressCursor = 5,
WaitCursor = 6,
CellCursor = 7,
CrosshairCursor = 8,
TextCursor = 9,
VerticalTextCursor = 10,
AliasCursor = 11,
CopyCursor = 12,
MoveCursor = 13,
NoDropCursor = 14,
NotAllowedCursor = 15,
GrabCursor = 16,
GrabbingCursor = 17,
EResizeCursor = 18,
NResizeCursor = 19,
NeResizeCursor = 20,
NwResizeCursor = 21,
SResizeCursor = 22,
SeResizeCursor = 23,
SwResizeCursor = 24,
WResizeCursor = 25,
EwResizeCursor = 26,
NsResizeCursor = 27,
NeswResizeCursor = 28,
NwseResizeCursor = 29,
ColResizeCursor = 30,
RowResizeCursor = 31,
AllScrollCursor = 32,
ZoomInCursor = 33,
ZoomOutCursor = 34,
}

View file

@ -39,6 +39,7 @@ use std::sync::Arc;
pub mod bloom; pub mod bloom;
pub mod cache; pub mod cache;
pub mod cursor;
pub mod debug_utils; pub mod debug_utils;
pub mod dlist; pub mod dlist;
pub mod fnv; pub mod fnv;

View file

@ -259,8 +259,10 @@ pub enum TimerMetadataReflowType {
FirstReflow, FirstReflow,
} }
pub type ProfilerMetadata<'a> = Option<(&'a Url, TimerMetadataFrameType, TimerMetadataReflowType)>;
pub fn profile<T>(category: TimeProfilerCategory, pub fn profile<T>(category: TimeProfilerCategory,
meta: Option<(&Url, TimerMetadataFrameType, TimerMetadataReflowType)>, meta: ProfilerMetadata,
time_profiler_chan: TimeProfilerChan, time_profiler_chan: TimeProfilerChan,
callback: || -> T) callback: || -> T)
-> T { -> T {

1
ports/cef/Cargo.lock generated
View file

@ -469,6 +469,7 @@ dependencies = [
"hyper 0.0.1 (git+https://github.com/servo/hyper?ref=servo)", "hyper 0.0.1 (git+https://github.com/servo/hyper?ref=servo)",
"io_surface 0.1.0 (git+https://github.com/servo/rust-io-surface)", "io_surface 0.1.0 (git+https://github.com/servo/rust-io-surface)",
"layers 0.1.0 (git+https://github.com/servo/rust-layers)", "layers 0.1.0 (git+https://github.com/servo/rust-layers)",
"style 0.0.1",
"url 0.1.0 (git+https://github.com/servo/rust-url)", "url 0.1.0 (git+https://github.com/servo/rust-url)",
"util 0.0.1", "util 0.0.1",
] ]

View file

@ -109,6 +109,13 @@ cef_class_impl! {
} }
} }
fn send_mouse_move_event(&_this, event: *const cef_mouse_event, _mouse_exited: c_int)
-> () {
let event: &cef_mouse_event = event;
let point = TypedPoint2D((*event).x as f32, (*event).y as f32);
core::send_window_event(WindowEvent::MouseWindowMoveEventClass(point))
}
fn send_mouse_wheel_event(&_this, fn send_mouse_wheel_event(&_this,
event: *const cef_mouse_event, event: *const cef_mouse_event,
delta_x: c_int, delta_x: c_int,

View file

@ -10,7 +10,7 @@
use eutil::Downcast; use eutil::Downcast;
use interfaces::CefBrowser; use interfaces::CefBrowser;
use render_handler::CefRenderHandlerExtensions; use render_handler::CefRenderHandlerExtensions;
use types::cef_rect_t; use types::{cef_cursor_handle_t, cef_rect_t};
use wrappers::Utf16Encoder; use wrappers::Utf16Encoder;
use compositing::compositor_task::{mod, CompositorProxy, CompositorReceiver}; use compositing::compositor_task::{mod, CompositorProxy, CompositorReceiver};
@ -25,10 +25,22 @@ use servo_msg::constellation_msg::{Key, KeyModifiers};
use servo_msg::compositor_msg::{Blank, FinishedLoading, Loading, PerformingLayout, PaintState}; use servo_msg::compositor_msg::{Blank, FinishedLoading, Loading, PerformingLayout, PaintState};
use servo_msg::compositor_msg::{ReadyState}; use servo_msg::compositor_msg::{ReadyState};
use servo_msg::constellation_msg::LoadData; use servo_msg::constellation_msg::LoadData;
use servo_util::cursor::Cursor;
use servo_util::geometry::ScreenPx; use servo_util::geometry::ScreenPx;
use std::cell::RefCell; use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
#[cfg(target_os="macos")]
use servo_util::cursor::{AliasCursor, AllScrollCursor, ColResizeCursor, ContextMenuCursor};
#[cfg(target_os="macos")]
use servo_util::cursor::{CopyCursor, CrosshairCursor, EResizeCursor, EwResizeCursor};
#[cfg(target_os="macos")]
use servo_util::cursor::{GrabCursor, GrabbingCursor, NResizeCursor, NoCursor, NoDropCursor};
#[cfg(target_os="macos")]
use servo_util::cursor::{NsResizeCursor, NotAllowedCursor, PointerCursor, RowResizeCursor};
#[cfg(target_os="macos")]
use servo_util::cursor::{SResizeCursor, TextCursor, VerticalTextCursor, WResizeCursor};
#[cfg(target_os="macos")] #[cfg(target_os="macos")]
use std::ptr; use std::ptr;
@ -87,6 +99,42 @@ impl Window {
pub fn wait_events(&self) -> WindowEvent { pub fn wait_events(&self) -> WindowEvent {
WindowEvent::Idle WindowEvent::Idle
} }
/// Returns the Cocoa cursor for a CSS cursor. These match Firefox, except where Firefox
/// bundles custom resources (which we don't yet do).
#[cfg(target_os="macos")]
fn cursor_handle_for_cursor(&self, cursor: Cursor) -> cef_cursor_handle_t {
use cocoa::base::{class, msg_send, selector};
let cocoa_name = match cursor {
NoCursor => return 0 as cef_cursor_handle_t,
ContextMenuCursor => "contextualMenuCursor",
GrabbingCursor => "closedHandCursor",
CrosshairCursor => "crosshairCursor",
CopyCursor => "dragCopyCursor",
AliasCursor => "dragLinkCursor",
TextCursor => "IBeamCursor",
GrabCursor | AllScrollCursor => "openHandCursor",
NoDropCursor | NotAllowedCursor => "operationNotAllowedCursor",
PointerCursor => "pointingHandCursor",
SResizeCursor => "resizeDownCursor",
WResizeCursor => "resizeLeftCursor",
EwResizeCursor | ColResizeCursor => "resizeLeftRightCursor",
EResizeCursor => "resizeRightCursor",
NResizeCursor => "resizeUpCursor",
NsResizeCursor | RowResizeCursor => "resizeUpDownCursor",
VerticalTextCursor => "IBeamCursorForVerticalLayout",
_ => "arrowCursor",
};
unsafe {
msg_send()(class("NSCursor"), selector(cocoa_name))
}
}
#[cfg(not(target_os="macos"))]
fn cursor_handle_for_cursor(&self, _: Cursor) -> cef_cursor_handle_t {
0
}
} }
impl WindowMethods for Window { impl WindowMethods for Window {
@ -265,6 +313,20 @@ impl WindowMethods for Window {
fn handle_key(&self, _: Key, _: KeyModifiers) { fn handle_key(&self, _: Key, _: KeyModifiers) {
// TODO(negge) // TODO(negge)
} }
fn set_cursor(&self, cursor: Cursor) {
let browser = self.cef_browser.borrow();
match *browser {
None => {}
Some(ref browser) => {
let cursor_handle = self.cursor_handle_for_cursor(cursor);
browser.get_host()
.get_client()
.get_render_handler()
.on_cursor_change(browser.clone(), cursor_handle)
}
}
}
} }
struct CefCompositorProxy { struct CefCompositorProxy {

View file

@ -32,6 +32,7 @@ use std::comm::Receiver;
use std::num::Float; use std::num::Float;
use std::rc::Rc; use std::rc::Rc;
use time::{mod, Timespec}; use time::{mod, Timespec};
use util::cursor::Cursor;
use util::geometry::ScreenPx; use util::geometry::ScreenPx;
/// The type of a window. /// The type of a window.
@ -241,6 +242,12 @@ impl WindowMethods for Window {
true true
} }
fn set_cursor(&self, _: Cursor) {
// No-op. We could take over mouse handling ourselves and draw the cursor as an extra
// layer with our own custom bitmaps or something, but it doesn't seem worth the
// trouble.
}
fn load_end(&self) {} fn load_end(&self) {}
fn set_page_title(&self, _: Option<String>) {} fn set_page_title(&self, _: Option<String>) {}
@ -269,14 +276,7 @@ impl Window {
self.event_queue.borrow_mut().push(Refresh); self.event_queue.borrow_mut().push(Refresh);
}, },
glfw::MouseButtonEvent(button, action, _mods) => { glfw::MouseButtonEvent(button, action, _mods) => {
let (x, y) = window.get_cursor_pos(); let cursor_position = self.cursor_position();
//handle hidpi displays, since GLFW returns non-hi-def coordinates.
let (backing_size, _) = window.get_framebuffer_size();
let (window_size, _) = window.get_size();
let hidpi = (backing_size as f32) / (window_size as f32);
let x = x as f32 * hidpi;
let y = y as f32 * hidpi;
match button { match button {
glfw::MouseButton::Button5 => { // Back button (might be different per platform) glfw::MouseButton::Button5 => { // Back button (might be different per platform)
self.event_queue.borrow_mut().push(Navigation(Back)); self.event_queue.borrow_mut().push(Navigation(Back));
@ -285,14 +285,18 @@ impl Window {
self.event_queue.borrow_mut().push(Navigation(Forward)); self.event_queue.borrow_mut().push(Navigation(Forward));
}, },
glfw::MouseButtonLeft | glfw::MouseButtonRight => { glfw::MouseButtonLeft | glfw::MouseButtonRight => {
self.handle_mouse(button, action, x as i32, y as i32); self.handle_mouse(button,
action,
cursor_position.x.get() as i32,
cursor_position.y.get() as i32);
} }
_ => {} _ => {}
} }
}, },
glfw::CursorPosEvent(xpos, ypos) => { glfw::CursorPosEvent(..) => {
self.event_queue.borrow_mut().push( self.event_queue
MouseWindowMoveEventClass(TypedPoint2D(xpos as f32, ypos as f32))); .borrow_mut()
.push(MouseWindowMoveEventClass(self.cursor_position()));
}, },
glfw::ScrollEvent(xpos, ypos) => { glfw::ScrollEvent(xpos, ypos) => {
match (window.get_key(glfw::Key::LeftControl), match (window.get_key(glfw::Key::LeftControl),
@ -393,6 +397,14 @@ impl Window {
}; };
self.event_queue.borrow_mut().push(MouseWindowEventClass(event)); self.event_queue.borrow_mut().push(MouseWindowEventClass(event));
} }
/// Returns the cursor position, properly accounting for HiDPI.
fn cursor_position(&self) -> TypedPoint2D<DevicePixel,f32> {
// Handle hidpi displays, since GLFW returns non-hi-def coordinates.
let (x, y) = self.glfw_window.get_cursor_pos();
let hidpi_factor = self.hidpi_factor();
Point2D::from_untyped(&Point2D(x as f32, y as f32)) * hidpi_factor
}
} }
struct GlfwCompositorProxy { struct GlfwCompositorProxy {

View file

@ -18,3 +18,4 @@ input[type="radio"]:checked::before { content: "(●)"; }
td[align="left"] { text-align: left; } td[align="left"] { text-align: left; }
td[align="center"] { text-align: center; } td[align="center"] { text-align: center; }
td[align="right"] { text-align: right; } td[align="right"] { text-align: right; }

View file

@ -89,7 +89,7 @@ rt { display: ruby-text; }
:link { color: #0000EE; } :link { color: #0000EE; }
:visited { color: #551A8B; } :visited { color: #551A8B; }
:link, :visited { text-decoration: underline; } :link, :visited { text-decoration: underline; cursor: pointer; }
a:link[rel~=help], a:visited[rel~=help], a:link[rel~=help], a:visited[rel~=help],
area:link[rel~=help], area:visited[rel~=help] { cursor: help; } area:link[rel~=help], area:visited[rel~=help] { cursor: help; }