aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/compositing/compositor.rs8
-rw-r--r--components/compositing/compositor_task.rs6
-rw-r--r--components/compositing/constellation.rs14
-rw-r--r--components/compositing/headless.rs16
-rw-r--r--components/compositing/windowing.rs4
-rw-r--r--components/gfx/display_list/mod.rs105
-rw-r--r--components/layout/display_list_builder.rs85
-rw-r--r--components/layout/layout_task.rs156
-rw-r--r--components/layout/parallel.rs16
-rw-r--r--components/msg/Cargo.toml3
-rw-r--r--components/msg/constellation_msg.rs3
-rw-r--r--components/script/script_task.rs21
-rw-r--r--components/servo/Cargo.lock1
-rw-r--r--components/style/properties/mod.rs.mako135
-rw-r--r--components/util/cursor.rs46
-rw-r--r--components/util/lib.rs1
-rw-r--r--components/util/time.rs4
-rw-r--r--ports/cef/Cargo.lock1
-rw-r--r--ports/cef/browser_host.rs7
-rw-r--r--ports/cef/window.rs64
-rw-r--r--ports/glfw/window.rs36
-rw-r--r--resources/servo.css1
-rw-r--r--resources/user-agent.css2
23 files changed, 564 insertions, 171 deletions
diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs
index 0b18124f53a..5b265d2ec15 100644
--- a/components/compositing/compositor.rs
+++ b/components/compositing/compositor.rs
@@ -3,8 +3,8 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use compositor_layer::{CompositorData, CompositorLayer, WantsScrollEventsFlag};
-use compositor_task::{CompositorEventListener, CompositorProxy, CompositorReceiver, CompositorTask};
-use compositor_task::{LayerProperties, Msg};
+use compositor_task::{CompositorEventListener, CompositorProxy, CompositorReceiver};
+use compositor_task::{CompositorTask, LayerProperties, Msg};
use constellation::{FrameId, FrameTreeDiff, SendableFrameTree};
use pipeline::CompositionPipeline;
use scrolling::ScrollingTimerProxy;
@@ -338,6 +338,10 @@ impl<Window: WindowMethods> IOCompositor<Window> {
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
// such as Paint that may crash because we have begun tearing down
// the rest of our resources.
diff --git a/components/compositing/compositor_task.rs b/components/compositing/compositor_task.rs
index 0fc99cc1884..c8e8932b956 100644
--- a/components/compositing/compositor_task.rs
+++ b/components/compositing/compositor_task.rs
@@ -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::constellation_msg::{ConstellationChan, LoadData, PipelineId};
use servo_msg::constellation_msg::{Key, KeyState, KeyModifiers, Pressed};
+use servo_util::cursor::Cursor;
use servo_util::memory::MemoryProfilerChan;
use servo_util::time::TimeProfilerChan;
use std::comm::{channel, Sender, Receiver};
@@ -213,6 +214,8 @@ pub enum Msg {
ScrollTimeout(u64),
/// Sends an unconsumed key event back to the compositor.
KeyEvent(Key, KeyModifiers),
+ /// Changes the cursor.
+ SetCursor(Cursor),
}
impl Show for Msg {
@@ -231,11 +234,12 @@ impl Show for Msg {
Msg::ChangePageTitle(..) => write!(f, "ChangePageTitle"),
Msg::ChangePageLoadData(..) => write!(f, "ChangePageLoadData"),
Msg::PaintMsgDiscarded(..) => write!(f, "PaintMsgDiscarded"),
+ Msg::FrameTreeUpdate(..) => write!(f, "FrameTreeUpdate"),
Msg::SetIds(..) => write!(f, "SetIds"),
- Msg::FrameTreeUpdate(..) => write!(f, "FrameTreeUpdateMsg"),
Msg::LoadComplete => write!(f, "LoadComplete"),
Msg::ScrollTimeout(..) => write!(f, "ScrollTimeout"),
Msg::KeyEvent(..) => write!(f, "KeyEvent"),
+ Msg::SetCursor(..) => write!(f, "SetCursor"),
}
}
}
diff --git a/components/compositing/constellation.rs b/components/compositing/constellation.rs
index fe756a5c7b9..118f33bc3f3 100644
--- a/components/compositing/constellation.rs
+++ b/components/compositing/constellation.rs
@@ -18,20 +18,21 @@ use libc;
use script_traits::{mod, GetTitleMsg, ResizeMsg, ResizeInactiveMsg, ExitPipelineMsg, SendEventMsg};
use script_traits::{ScriptControlChan, ScriptTaskFactory};
use servo_msg::compositor_msg::LayerId;
-use servo_msg::constellation_msg::{ConstellationChan, ExitMsg, FailureMsg, Failure, FrameRectMsg};
-use servo_msg::constellation_msg::{GetPipelineTitleMsg};
+use servo_msg::constellation_msg::{mod, ConstellationChan, ExitMsg, FailureMsg, Failure};
+use servo_msg::constellation_msg::{FrameRectMsg, GetPipelineTitleMsg};
use servo_msg::constellation_msg::{IFrameSandboxState, IFrameUnsandboxed, InitLoadUrlMsg};
use servo_msg::constellation_msg::{KeyEvent, Key, KeyState, KeyModifiers, LoadCompleteMsg};
use servo_msg::constellation_msg::{LoadData, LoadUrlMsg, NavigateMsg, NavigationType};
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;
use servo_net::image_cache_task::{ImageCacheTask, ImageCacheTaskClient};
use servo_net::resource_task::ResourceTask;
use servo_net::resource_task;
use servo_net::storage_task::StorageTask;
use servo_net::storage_task;
+use servo_util::cursor::Cursor;
use servo_util::geometry::{PagePx, ViewportPx};
use servo_util::opts;
use servo_util::task::spawn_named;
@@ -472,6 +473,7 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
subpage_id,
sandbox);
}
+ SetCursorMsg(cursor) => self.handle_set_cursor_msg(cursor),
// 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;
// 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);
}
+ 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) {
let url = load_data.url.to_string();
debug!("Constellation: received message to load {:s}", url);
diff --git a/components/compositing/headless.rs b/components/compositing/headless.rs
index 4a9ed6d5c56..9651ec7de78 100644
--- a/components/compositing/headless.rs
+++ b/components/compositing/headless.rs
@@ -99,10 +99,18 @@ impl CompositorEventListener for NullCompositor {
Msg::CreateOrUpdateRootLayer(..) |
Msg::CreateOrUpdateDescendantLayer(..) |
- Msg::SetLayerOrigin(..) | Msg::Paint(..) |
- Msg::ChangeReadyState(..) | Msg::ChangePaintState(..) | Msg::ScrollFragmentPoint(..) |
- Msg::LoadComplete | Msg::PaintMsgDiscarded(..) | Msg::ScrollTimeout(..) | Msg::ChangePageTitle(..) |
- Msg::ChangePageLoadData(..) | Msg::KeyEvent(..) => ()
+ Msg::SetLayerOrigin(..) |
+ Msg::Paint(..) |
+ Msg::ChangeReadyState(..) |
+ Msg::ChangePaintState(..) |
+ Msg::ScrollFragmentPoint(..) |
+ Msg::LoadComplete |
+ Msg::PaintMsgDiscarded(..) |
+ Msg::ScrollTimeout(..) |
+ Msg::ChangePageTitle(..) |
+ Msg::ChangePageLoadData(..) |
+ Msg::KeyEvent(..) |
+ Msg::SetCursor(..) => {}
}
true
}
diff --git a/components/compositing/windowing.rs b/components/compositing/windowing.rs
index e33a5f9fe75..54fc96897d0 100644
--- a/components/compositing/windowing.rs
+++ b/components/compositing/windowing.rs
@@ -13,6 +13,7 @@ use layers::geometry::DevicePixel;
use layers::platform::surface::NativeGraphicsMetadata;
use servo_msg::compositor_msg::{PaintState, ReadyState};
use servo_msg::constellation_msg::{Key, KeyState, KeyModifiers, LoadData};
+use servo_util::cursor::Cursor;
use servo_util::geometry::ScreenPx;
use std::fmt::{FormatError, Formatter, Show};
use std::rc::Rc;
@@ -124,6 +125,9 @@ pub trait WindowMethods {
/// proceed and false if it should not.
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.
fn handle_key(&self, key: Key, mods: KeyModifiers);
}
diff --git a/components/gfx/display_list/mod.rs b/components/gfx/display_list/mod.rs
index b0cb1e609a6..1d81f39f612 100644
--- a/components/gfx/display_list/mod.rs
+++ b/components/gfx/display_list/mod.rs
@@ -14,12 +14,13 @@
//! They are therefore not exactly analogous to constructs like Skia pictures, which consist of
//! low-level drawing primitives.
-use self::DisplayItem::*;
-use self::DisplayItemIterator::*;
+#![deny(unsafe_blocks)]
use color::Color;
use display_list::optimizer::DisplayListOptimizer;
use paint_context::{PaintContext, ToAzureRect};
+use self::DisplayItem::*;
+use self::DisplayItemIterator::*;
use text::glyph::CharIndex;
use text::TextRun;
@@ -28,17 +29,18 @@ use collections::dlist::{mod, DList};
use geom::{Point2D, Rect, SideOffsets2D, Size2D, Matrix2D};
use libc::uintptr_t;
use paint_task::PaintLayer;
-use script_traits::UntrustedNodeAddress;
use servo_msg::compositor_msg::LayerId;
use servo_net::image::base::Image;
+use servo_util::cursor::Cursor;
use servo_util::dlist as servo_dlist;
use servo_util::geometry::{mod, Au, ZERO_POINT};
use servo_util::range::Range;
use servo_util::smallvec::{SmallVec, SmallVec8};
use std::fmt;
-use std::mem;
use std::slice::Items;
+use style::ComputedValues;
use style::computed_values::border_style;
+use style::computed_values::cursor::{AutoCursor, SpecifiedCursor};
use sync::Arc;
// 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.
pub fn hit_test(&self,
point: Point2D<Au>,
- result: &mut Vec<UntrustedNodeAddress>,
+ result: &mut Vec<DisplayItemMetadata>,
topmost_only: bool) {
fn hit_test_in_list<'a,I>(point: Point2D<Au>,
- result: &mut Vec<UntrustedNodeAddress>,
+ result: &mut Vec<DisplayItemMetadata>,
topmost_only: bool,
mut iterator: I)
where I: Iterator<&'a DisplayItem> {
for item in iterator {
- if geometry::rect_contains_point(item.base().clip_rect, point) &&
- geometry::rect_contains_point(item.bounds(), point) {
- result.push(item.base().node.to_untrusted_node_address());
- if topmost_only {
- return
+ if !geometry::rect_contains_point(item.base().clip_rect, point) {
+ // Clipped out.
+ continue
+ }
+ 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.
pub bounds: Rect<Au>,
- /// The originating DOM node.
- pub node: OpaqueNode,
+ /// Metadata attached to this display item.
+ pub metadata: DisplayItemMetadata,
/// The rectangle to clip to.
///
@@ -459,15 +486,45 @@ pub struct BaseDisplayItem {
impl BaseDisplayItem {
#[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 {
bounds: bounds,
- node: node,
+ metadata: metadata,
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.
#[deriving(Clone)]
pub struct SolidColorDisplayItem {
@@ -738,24 +795,8 @@ impl fmt::Show for DisplayItem {
BoxShadowDisplayItemClass(_) => "BoxShadow",
},
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
- }
- }
-}
diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs
index 33f40f8b188..27c59072191 100644
--- a/components/layout/display_list_builder.rs
+++ b/components/layout/display_list_builder.rs
@@ -24,7 +24,7 @@ use geom::{Point2D, Rect, Size2D, SideOffsets2D};
use gfx::color;
use gfx::display_list::{BOX_SHADOW_INFLATION_FACTOR, BaseDisplayItem, BorderDisplayItem};
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::{GradientStop, ImageDisplayItem, ImageDisplayItemClass, LineDisplayItem};
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::constellation_msg::{ConstellationChan, FrameRectMsg};
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::logical_geometry::{LogicalRect, WritingMode};
use servo_util::opts;
@@ -132,6 +133,7 @@ pub trait FragmentDisplayListBuilding {
clip_rect: &Rect<Au>);
fn build_debug_borders_around_text_fragments(&self,
+ style: &ComputedValues,
display_list: &mut DisplayList,
flow_origin: Point2D<Au>,
text_fragment: &ScannedTextFragmentInfo,
@@ -197,7 +199,11 @@ impl FragmentDisplayListBuilding for Fragment {
let background_color = style.resolve_color(style.get_background().background_color);
if !background_color.alpha.approx_eq(&0.0) {
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(),
}), level);
}
@@ -309,7 +315,9 @@ impl FragmentDisplayListBuilding for Fragment {
// Create the image display item.
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(),
stretch_size: Size2D(Au::from_px(image.width 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);
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,
end_point: center + delta,
stops: stops,
@@ -441,7 +451,11 @@ impl FragmentDisplayListBuilding for Fragment {
absolute_bounds.translate(&Point2D(box_shadow.offset_x, box_shadow.offset_y))
.inflate(inflation, inflation);
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,
color: style.resolve_color(box_shadow.color).to_gfx_color(),
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.
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),
color: SideOffsets2D::new(top_color.to_gfx_color(),
right_color.to_gfx_color(),
@@ -510,7 +526,9 @@ impl FragmentDisplayListBuilding for Fragment {
// Append the outline to the display list.
let color = style.resolve_color(style.get_outline().outline_color).to_gfx_color();
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),
color: SideOffsets2D::new_all_same(color),
style: SideOffsets2D::new_all_same(outline_style),
@@ -519,6 +537,7 @@ impl FragmentDisplayListBuilding for Fragment {
}
fn build_debug_borders_around_text_fragments(&self,
+ style: &ComputedValues,
display_list: &mut DisplayList,
flow_origin: Point2D<Au>,
text_fragment: &ScannedTextFragmentInfo,
@@ -533,7 +552,9 @@ impl FragmentDisplayListBuilding for Fragment {
// Compute the text fragment bounds and draw a border surrounding them.
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)),
color: SideOffsets2D::new_all_same(color::rgb(0, 0, 200)),
style: SideOffsets2D::new_all_same(border_style::solid),
@@ -549,7 +570,9 @@ impl FragmentDisplayListBuilding for Fragment {
baseline.origin = baseline.origin + flow_origin;
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),
style: border_style::dashed,
};
@@ -570,7 +593,11 @@ impl FragmentDisplayListBuilding for Fragment {
// This prints a debug border around the border of this fragment.
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)),
color: SideOffsets2D::new_all_same(color::rgb(0, 0, 200)),
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::ScannedText(ref text_fragment) => {
// 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() {
- SidewaysLeft
+ (SidewaysLeft, VerticalTextCursor)
} else {
- SidewaysRight
+ (SidewaysRight, VerticalTextCursor)
}
} else {
- Upright
+ (Upright, TextCursor)
};
let metrics = &text_fragment.run.font_metrics;
@@ -750,7 +777,11 @@ impl FragmentDisplayListBuilding for Fragment {
};
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(),
range: text_fragment.range,
text_color: self.style().get_color().color.to_gfx_color(),
@@ -760,14 +791,21 @@ impl FragmentDisplayListBuilding for Fragment {
// 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 {
None => {}
Some(color) => {
let bounds = rect_to_absolute(self.style.writing_mode, rect());
display_list.content.push_back(SolidColorDisplayItemClass(
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(),
}))
}
@@ -776,20 +814,20 @@ impl FragmentDisplayListBuilding for Fragment {
let text_decorations =
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();
rect.start.b = rect.start.b + metrics.ascent - metrics.underline_offset;
rect.size.block = metrics.underline_size;
rect
});
- line(text_decorations.overline, || {
+ line(text_decorations.overline, self.style(), || {
let mut rect = content_box.clone();
rect.size.block = metrics.underline_size;
rect
});
- line(text_decorations.line_through, || {
+ line(text_decorations.line_through, self.style(), || {
let mut rect = content_box.clone();
rect.start.b = rect.start.b + metrics.ascent - metrics.strikeout_offset;
rect.size.block = metrics.strikeout_size;
@@ -798,7 +836,8 @@ impl FragmentDisplayListBuilding for Fragment {
}
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,
&**text_fragment,
clip_rect);
@@ -822,7 +861,9 @@ impl FragmentDisplayListBuilding for Fragment {
// Place the image into the display list.
display_list.content.push_back(ImageDisplayItemClass(box ImageDisplayItem {
base: BaseDisplayItem::new(absolute_content_box,
- self.node,
+ DisplayItemMetadata::new(self.node,
+ &*self.style,
+ DefaultCursor),
*clip_rect),
image: image.clone(),
stretch_size: absolute_content_box.size,
diff --git a/components/layout/layout_task.rs b/components/layout/layout_task.rs
index 4d8885cf8ed..48941a0d336 100644
--- a/components/layout/layout_task.rs
+++ b/components/layout/layout_task.rs
@@ -13,8 +13,7 @@ use flow_ref::FlowRef;
use fragment::{Fragment, FragmentBoundsIterator};
use incremental::{LayoutDamageComputation, REFLOW, REFLOW_ENTIRE_DOCUMENT, REPAINT};
use layout_debug;
-use parallel::UnsafeFlow;
-use parallel;
+use parallel::{mod, UnsafeFlow};
use sequential;
use util::{LayoutDataAccess, LayoutDataWrapper, OpaqueNodeMethods, ToGfxColor};
use wrapper::{LayoutNode, TLayoutNode, ThreadSafeLayoutNode};
@@ -26,7 +25,7 @@ use geom::rect::Rect;
use geom::size::Size2D;
use geom::scale_factor::ScaleFactor;
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::paint_task::{mod, PaintInitMsg, PaintChan, PaintLayer};
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::{MouseOverResponse, Msg, NoQuery, PrepareToExitMsg};
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::{ScriptControlChan, UntrustedNodeAddress};
use servo_msg::compositor_msg::Scrollable;
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::local_image_cache::{ImageResponder, LocalImageCache};
use servo_net::resource_task::{ResourceTask, load_bytes_iter};
+use servo_util::cursor::DefaultCursor;
use servo_util::geometry::Au;
use servo_util::logical_geometry::LogicalPoint;
use servo_util::opts;
use servo_util::smallvec::{SmallVec, SmallVec1, VecLike};
use servo_util::task::spawn_named_with_send_on_failure;
use servo_util::task_state;
-use servo_util::time::{TimeProfilerChan, profile, TimerMetadataFrameType, TimerMetadataReflowType};
-use servo_util::time;
+use servo_util::time::{mod, ProfilerMetadata, TimeProfilerChan, TimerMetadataFrameType};
+use servo_util::time::{TimerMetadataReflowType, profile};
use servo_util::workqueue::WorkQueue;
use std::cell::Cell;
use std::comm::{channel, Sender, Receiver, Select};
@@ -73,6 +75,9 @@ pub struct LayoutTaskData {
/// The local image cache.
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.
pub screen_size: Size2D<Au>,
@@ -112,9 +117,6 @@ pub struct LayoutTask {
//// The channel to send messages to ourself.
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.
pub script_chan: ScriptControlChan,
@@ -262,7 +264,6 @@ impl LayoutTask {
port: port,
pipeline_port: pipeline_port,
chan: chan,
- constellation_chan: constellation_chan,
script_chan: script_chan,
paint_chan: paint_chan,
time_profiler_chan: time_profiler_chan,
@@ -273,6 +274,7 @@ impl LayoutTask {
rw_data: Arc::new(Mutex::new(
LayoutTaskData {
local_image_cache: local_image_cache,
+ constellation_chan: constellation_chan,
screen_size: screen_size,
stacking_context: None,
stylist: box Stylist::new(device),
@@ -302,7 +304,7 @@ impl LayoutTask {
SharedLayoutContext {
image_cache: rw_data.local_image_cache.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(),
font_cache_task: self.font_cache_task.clone(),
stylist: &*rw_data.stylist,
@@ -397,9 +399,7 @@ impl LayoutTask {
},
ReflowMsg(data) => {
profile(time::LayoutPerformCategory,
- Some((&data.url,
- if data.iframe { TimerMetadataFrameType::IFrame } else { TimerMetadataFrameType::RootWindow },
- if self.first_reflow.get() { TimerMetadataReflowType::FirstReflow } else { TimerMetadataReflowType::Incremental })),
+ self.profiler_metadata(&*data),
self.time_profiler_chan.clone(),
|| 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
// operation out.
parallel::traverse_flow_tree_preorder(layout_root,
- &data.url,
- if data.iframe { TimerMetadataFrameType::IFrame } else { TimerMetadataFrameType::RootWindow },
- if self.first_reflow.get() { TimerMetadataReflowType::FirstReflow } else { TimerMetadataReflowType::Incremental },
+ self.profiler_metadata(data),
self.time_profiler_chan.clone(),
shared_layout_context,
traversal);
@@ -624,16 +622,14 @@ impl LayoutTask {
data: &Reflow,
node: &mut LayoutNode,
layout_root: &mut FlowRef,
- shared_layout_ctx: &mut SharedLayoutContext,
+ shared_layout_context: &mut SharedLayoutContext,
rw_data: &mut RWGuard<'a>) {
let writing_mode = flow::base(&**layout_root).writing_mode;
profile(time::LayoutDispListBuildCategory,
- Some((&data.url,
- if data.iframe { TimerMetadataFrameType::IFrame } else { TimerMetadataFrameType::RootWindow },
- if self.first_reflow.get() { TimerMetadataReflowType::FirstReflow } else { TimerMetadataReflowType::Incremental })),
- self.time_profiler_chan.clone(),
- || {
- shared_layout_ctx.dirty =
+ self.profiler_metadata(data),
+ self.time_profiler_chan.clone(),
+ || {
+ shared_layout_context.dirty =
flow::base(&**layout_root).position.to_physical(writing_mode,
rw_data.screen_size);
flow::mut_base(&mut **layout_root).stacking_relative_position =
@@ -645,15 +641,13 @@ impl LayoutTask {
let rw_data = rw_data.deref_mut();
match rw_data.parallel_traversal {
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) => {
parallel::build_display_list_for_subtree(layout_root,
- &data.url,
- if data.iframe { TimerMetadataFrameType::IFrame } else { TimerMetadataFrameType::RootWindow },
- if self.first_reflow.get() { TimerMetadataReflowType::FirstReflow } else { TimerMetadataReflowType::Incremental },
+ self.profiler_metadata(data),
self.time_profiler_chan.clone(),
- shared_layout_ctx,
+ shared_layout_context,
traversal);
}
}
@@ -746,9 +740,9 @@ impl LayoutTask {
rw_data.screen_size = current_screen_size;
// Create a layout context for use throughout the following passes.
- let mut shared_layout_ctx = self.build_shared_layout_context(rw_data.deref(),
- node,
- &data.url);
+ let mut shared_layout_context = self.build_shared_layout_context(rw_data.deref(),
+ node,
+ &data.url);
// Handle conditions where the entire flow tree is invalid.
let screen_size_changed = current_screen_size != old_screen_size;
@@ -775,19 +769,17 @@ impl LayoutTask {
}
let mut layout_root = profile(time::LayoutStyleRecalcCategory,
- Some((&data.url,
- if data.iframe { TimerMetadataFrameType::IFrame } else { TimerMetadataFrameType::RootWindow },
- if self.first_reflow.get() { TimerMetadataReflowType::FirstReflow } else { TimerMetadataReflowType::Incremental })),
+ self.profiler_metadata(data),
self.time_profiler_chan.clone(),
|| {
// Perform CSS selector matching and flow construction.
let rw_data = rw_data.deref_mut();
match rw_data.parallel_traversal {
None => {
- sequential::traverse_dom_preorder(*node, &shared_layout_ctx);
+ sequential::traverse_dom_preorder(*node, &shared_layout_context);
}
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,
- Some((&data.url,
- if data.iframe { TimerMetadataFrameType::IFrame } else { TimerMetadataFrameType::RootWindow },
- if self.first_reflow.get() { TimerMetadataReflowType::FirstReflow } else { TimerMetadataReflowType::Incremental })),
+ self.profiler_metadata(data),
self.time_profiler_chan.clone(),
|| {
- if opts::get().nonincremental_layout ||
- layout_root.deref_mut().compute_layout_damage().contains(REFLOW_ENTIRE_DOCUMENT) {
+ if opts::get().nonincremental_layout || layout_root.deref_mut()
+ .compute_layout_damage()
+ .contains(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
// the boxes.
profile(time::LayoutMainCategory,
- Some((&data.url,
- if data.iframe { TimerMetadataFrameType::IFrame } else { TimerMetadataFrameType::RootWindow },
- if self.first_reflow.get() { TimerMetadataReflowType::FirstReflow } else { TimerMetadataReflowType::Incremental })),
+ self.profiler_metadata(data),
self.time_profiler_chan.clone(),
|| {
let rw_data = rw_data.deref_mut();
match rw_data.parallel_traversal {
None => {
// Sequential mode.
- self.solve_constraints(&mut layout_root, &shared_layout_ctx)
+ self.solve_constraints(&mut layout_root, &shared_layout_context)
}
Some(_) => {
// Parallel mode.
self.solve_constraints_parallel(data,
rw_data,
&mut layout_root,
- &mut shared_layout_ctx);
+ &mut shared_layout_context);
}
}
});
// Build the display list if necessary, and send it to the painter.
- if data.goal == ReflowForDisplay {
- self.build_display_list_for_reflow(data,
- node,
- &mut layout_root,
- &mut shared_layout_ctx,
- &mut rw_data);
+ match data.goal {
+ ReflowForDisplay => {
+ self.build_display_list_for_reflow(data,
+ node,
+ &mut layout_root,
+ &mut shared_layout_context,
+ &mut rw_data);
+ }
+ ReflowForScriptQuery => {}
}
match data.query_type {
- ContentBoxQuery(node) =>
- 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),
- NoQuery => {},
+ ContentBoxQuery(node) => {
+ 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)
+ }
+ NoQuery => {}
}
self.first_reflow.set(false);
@@ -896,11 +890,13 @@ impl LayoutTask {
}
}
- // When images can't be loaded in time to display they trigger
- // this callback in some task somewhere. This will send a message
- // 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
- // the script task for this.
+ /// When images can't be loaded in time to display they trigger
+ /// this callback in some task somewhere. This will send a message
+ /// 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
+ /// the script task for this.
+ ///
+ /// FIXME(pcwalton): Rewrite all of this.
fn make_on_image_available_cb(&self) -> Box<ImageResponder<UntrustedNodeAddress>+Send> {
// This has a crazy signature because the image cache needs to
// make multiple copies of the callback, and the dom event
@@ -919,6 +915,21 @@ impl LayoutTask {
let _: Option<LayoutDataWrapper> = mem::transmute(
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>>);
@@ -951,7 +962,7 @@ impl LayoutRPC for LayoutRPCImpl {
let mut result = Vec::new();
stacking_context.hit_test(point, &mut result, true);
if !result.is_empty() {
- Some(HitTestResponse(result[0]))
+ Some(HitTestResponse(result[0].node.to_untrusted_node_address()))
} else {
None
}
@@ -967,7 +978,7 @@ impl LayoutRPC for LayoutRPCImpl {
fn mouse_over(&self, _: TrustedNodeAddress, point: Point2D<f32>)
-> 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 &LayoutRPCImpl(ref rw_data) = self;
@@ -978,12 +989,25 @@ impl LayoutRPC for LayoutRPCImpl {
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() {
Err(())
} 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))
}
}
}
diff --git a/components/layout/parallel.rs b/components/layout/parallel.rs
index 6d1ce6643c8..5537ba95998 100644
--- a/components/layout/parallel.rs
+++ b/components/layout/parallel.rs
@@ -13,15 +13,13 @@ use flow_ref::FlowRef;
use traversal::{RecalcStyleForNode, ConstructFlows};
use traversal::{BubbleISizes, AssignISizes, AssignBSizesAndStoreOverflow};
use traversal::{ComputeAbsolutePositions, BuildDisplayList};
-use url::Url;
use util::{LayoutDataAccess, LayoutDataWrapper};
use wrapper::{layout_node_to_unsafe_layout_node, layout_node_from_unsafe_layout_node, LayoutNode};
use wrapper::{PostorderNodeMutTraversal, UnsafeLayoutNode};
use wrapper::{PreorderDomTraversal, PostorderDomTraversal};
use servo_util::opts;
-use servo_util::time::{TimeProfilerChan, profile, TimerMetadataFrameType, TimerMetadataReflowType};
-use servo_util::time;
+use servo_util::time::{mod, ProfilerMetadata, TimeProfilerChan, profile};
use servo_util::workqueue::{WorkQueue, WorkUnit, WorkerProxy};
use std::mem;
use std::ptr;
@@ -422,9 +420,7 @@ pub fn traverse_dom_preorder(root: LayoutNode,
}
pub fn traverse_flow_tree_preorder(root: &mut FlowRef,
- url: &Url,
- iframe: TimerMetadataFrameType,
- reflow_type: TimerMetadataReflowType,
+ profiler_metadata: ProfilerMetadata,
time_profiler_chan: TimeProfilerChan,
shared_layout_context: &SharedLayoutContext,
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 _;
- profile(time::LayoutParallelWarmupCategory, Some((url, iframe, reflow_type)), time_profiler_chan, || {
+ profile(time::LayoutParallelWarmupCategory, profiler_metadata, time_profiler_chan, || {
queue.push(WorkUnit {
fun: assign_inline_sizes,
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,
- url: &Url,
- iframe: TimerMetadataFrameType,
- reflow_type: TimerMetadataReflowType,
+ profiler_metadata: ProfilerMetadata,
time_profiler_chan: TimeProfilerChan,
shared_layout_context: &SharedLayoutContext,
queue: &mut WorkQueue<*const SharedLayoutContext,UnsafeFlow>) {
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 {
fun: compute_absolute_positions,
data: mut_owned_flow_to_unsafe_flow(root),
diff --git a/components/msg/Cargo.toml b/components/msg/Cargo.toml
index 21312f5d481..460a277a7a7 100644
--- a/components/msg/Cargo.toml
+++ b/components/msg/Cargo.toml
@@ -8,6 +8,9 @@ authors = ["The Servo Project Developers"]
name = "msg"
path = "lib.rs"
+[dependencies.style]
+path = "../style"
+
[dependencies.util]
path = "../util"
diff --git a/components/msg/constellation_msg.rs b/components/msg/constellation_msg.rs
index 531b39360e5..78cda641aef 100644
--- a/components/msg/constellation_msg.rs
+++ b/components/msg/constellation_msg.rs
@@ -11,6 +11,7 @@ use geom::scale_factor::ScaleFactor;
use hyper::header::Headers;
use hyper::method::{Method, Get};
use layers::geometry::DevicePixel;
+use servo_util::cursor::Cursor;
use servo_util::geometry::{PagePx, ViewportPx};
use std::comm::{channel, Sender, Receiver};
use url::Url;
@@ -208,6 +209,8 @@ pub enum Msg {
/// Requests that the constellation inform the compositor of the title of the pipeline
/// immediately.
GetPipelineTitleMsg(PipelineId),
+ /// Requests that the constellation inform the compositor of the a cursor change.
+ SetCursorMsg(Cursor),
}
/// Similar to net::resource_task::LoadData
diff --git a/components/script/script_task.rs b/components/script/script_task.rs
index 3fc455d588a..dfb4e7d267a 100644
--- a/components/script/script_task.rs
+++ b/components/script/script_task.rs
@@ -1150,7 +1150,6 @@ impl ScriptTask {
let page = get_page(&*self.page.borrow(), pipeline_id);
match page.get_nodes_under_mouse(&point) {
Some(node_address) => {
-
let mut target_list = vec!();
let mut target_compare = false;
@@ -1166,23 +1165,19 @@ impl ScriptTask {
}
for node_address in node_address.iter() {
-
let temp_node =
- node::from_untrusted_node_address(
- self.js_runtime.ptr, *node_address);
+ node::from_untrusted_node_address(self.js_runtime.ptr, *node_address);
let maybe_node = temp_node.root().ancestors().find(|node| node.is_element());
match maybe_node {
Some(node) => {
node.set_hover_state(true);
-
match *mouse_over_targets {
- Some(ref mouse_over_targets) => {
- if !target_compare {
- target_compare = !mouse_over_targets.contains(&JS::from_rooted(node));
- }
+ Some(ref mouse_over_targets) if !target_compare => {
+ target_compare =
+ !mouse_over_targets.contains(&JS::from_rooted(node));
}
- None => {}
+ _ => {}
}
target_list.push(JS::from_rooted(node));
}
@@ -1192,15 +1187,15 @@ impl ScriptTask {
match *mouse_over_targets {
Some(ref mouse_over_targets) => {
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 mouse_over_targets.is_some() {
- self.force_reflow(&*page);
+ self.force_reflow(&*page)
}
*mouse_over_targets = Some(target_list);
}
diff --git a/components/servo/Cargo.lock b/components/servo/Cargo.lock
index bca176f29cb..716539140c9 100644
--- a/components/servo/Cargo.lock
+++ b/components/servo/Cargo.lock
@@ -501,6 +501,7 @@ dependencies = [
"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)",
"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)",
"util 0.0.1",
]
diff --git a/components/style/properties/mod.rs.mako b/components/style/properties/mod.rs.mako
index 3541677e0d9..f82c821a8bb 100644
--- a/components/style/properties/mod.rs.mako
+++ b/components/style/properties/mod.rs.mako
@@ -32,7 +32,7 @@ import re
def to_rust_ident(name):
name = name.replace("-", "_")
- if name in ["static", "super", "box"]: # Rust keywords
+ if name in ["static", "super", "box", "move"]: # Rust keywords
name += "_"
return name
@@ -1351,6 +1351,139 @@ pub mod longhands {
${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.
${new_style_struct("Effects", is_inherited=False)}
diff --git a/components/util/cursor.rs b/components/util/cursor.rs
new file mode 100644
index 00000000000..d295487ac9b
--- /dev/null
+++ b/components/util/cursor.rs
@@ -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,
+}
+
diff --git a/components/util/lib.rs b/components/util/lib.rs
index b0e4cff8c62..02daa6be76f 100644
--- a/components/util/lib.rs
+++ b/components/util/lib.rs
@@ -39,6 +39,7 @@ use std::sync::Arc;
pub mod bloom;
pub mod cache;
+pub mod cursor;
pub mod debug_utils;
pub mod dlist;
pub mod fnv;
diff --git a/components/util/time.rs b/components/util/time.rs
index 16155710737..c4a994bc832 100644
--- a/components/util/time.rs
+++ b/components/util/time.rs
@@ -259,8 +259,10 @@ pub enum TimerMetadataReflowType {
FirstReflow,
}
+pub type ProfilerMetadata<'a> = Option<(&'a Url, TimerMetadataFrameType, TimerMetadataReflowType)>;
+
pub fn profile<T>(category: TimeProfilerCategory,
- meta: Option<(&Url, TimerMetadataFrameType, TimerMetadataReflowType)>,
+ meta: ProfilerMetadata,
time_profiler_chan: TimeProfilerChan,
callback: || -> T)
-> T {
diff --git a/ports/cef/Cargo.lock b/ports/cef/Cargo.lock
index c79b3716831..9079fd27a9c 100644
--- a/ports/cef/Cargo.lock
+++ b/ports/cef/Cargo.lock
@@ -469,6 +469,7 @@ dependencies = [
"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)",
"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)",
"util 0.0.1",
]
diff --git a/ports/cef/browser_host.rs b/ports/cef/browser_host.rs
index 9fea6a6fa9c..e1248403e2f 100644
--- a/ports/cef/browser_host.rs
+++ b/ports/cef/browser_host.rs
@@ -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,
event: *const cef_mouse_event,
delta_x: c_int,
diff --git a/ports/cef/window.rs b/ports/cef/window.rs
index 56851533767..807b72b83cd 100644
--- a/ports/cef/window.rs
+++ b/ports/cef/window.rs
@@ -10,7 +10,7 @@
use eutil::Downcast;
use interfaces::CefBrowser;
use render_handler::CefRenderHandlerExtensions;
-use types::cef_rect_t;
+use types::{cef_cursor_handle_t, cef_rect_t};
use wrappers::Utf16Encoder;
use compositing::compositor_task::{mod, CompositorProxy, CompositorReceiver};
@@ -25,11 +25,23 @@ use servo_msg::constellation_msg::{Key, KeyModifiers};
use servo_msg::compositor_msg::{Blank, FinishedLoading, Loading, PerformingLayout, PaintState};
use servo_msg::compositor_msg::{ReadyState};
use servo_msg::constellation_msg::LoadData;
+use servo_util::cursor::Cursor;
use servo_util::geometry::ScreenPx;
use std::cell::RefCell;
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")]
use std::ptr;
/// The type of an off-screen window.
@@ -87,6 +99,42 @@ impl Window {
pub fn wait_events(&self) -> WindowEvent {
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 {
@@ -265,6 +313,20 @@ impl WindowMethods for Window {
fn handle_key(&self, _: Key, _: KeyModifiers) {
// 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 {
diff --git a/ports/glfw/window.rs b/ports/glfw/window.rs
index 05a4b68a0e0..377bb2839b6 100644
--- a/ports/glfw/window.rs
+++ b/ports/glfw/window.rs
@@ -32,6 +32,7 @@ use std::comm::Receiver;
use std::num::Float;
use std::rc::Rc;
use time::{mod, Timespec};
+use util::cursor::Cursor;
use util::geometry::ScreenPx;
/// The type of a window.
@@ -241,6 +242,12 @@ impl WindowMethods for Window {
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 set_page_title(&self, _: Option<String>) {}
@@ -269,14 +276,7 @@ impl Window {
self.event_queue.borrow_mut().push(Refresh);
},
glfw::MouseButtonEvent(button, action, _mods) => {
- let (x, y) = window.get_cursor_pos();
- //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;
-
+ let cursor_position = self.cursor_position();
match button {
glfw::MouseButton::Button5 => { // Back button (might be different per platform)
self.event_queue.borrow_mut().push(Navigation(Back));
@@ -285,14 +285,18 @@ impl Window {
self.event_queue.borrow_mut().push(Navigation(Forward));
},
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) => {
- self.event_queue.borrow_mut().push(
- MouseWindowMoveEventClass(TypedPoint2D(xpos as f32, ypos as f32)));
+ glfw::CursorPosEvent(..) => {
+ self.event_queue
+ .borrow_mut()
+ .push(MouseWindowMoveEventClass(self.cursor_position()));
},
glfw::ScrollEvent(xpos, ypos) => {
match (window.get_key(glfw::Key::LeftControl),
@@ -393,6 +397,14 @@ impl Window {
};
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 {
diff --git a/resources/servo.css b/resources/servo.css
index eec446505d4..e62ca9837bb 100644
--- a/resources/servo.css
+++ b/resources/servo.css
@@ -18,3 +18,4 @@ input[type="radio"]:checked::before { content: "(●)"; }
td[align="left"] { text-align: left; }
td[align="center"] { text-align: center; }
td[align="right"] { text-align: right; }
+
diff --git a/resources/user-agent.css b/resources/user-agent.css
index 8ae8f5a5960..46c5787783a 100644
--- a/resources/user-agent.css
+++ b/resources/user-agent.css
@@ -89,7 +89,7 @@ rt { display: ruby-text; }
:link { color: #0000EE; }
:visited { color: #551A8B; }
-:link, :visited { text-decoration: underline; }
+:link, :visited { text-decoration: underline; cursor: pointer; }
a:link[rel~=help], a:visited[rel~=help],
area:link[rel~=help], area:visited[rel~=help] { cursor: help; }