Auto merge of #22156 - jdm:wr-scroll, r=pcwalton

Add a transparent hit-testing rectangle for scrollable block content.

This implements the strategy described at https://github.com/servo/webrender/issues/3261#issuecomment-435106998 and which Gecko uses as well. Adding a transparent rectangle below the content and above the background ensures that hit testing correctly associates the background area with the right scrolling node.

---
- [x] `./mach build -d` does not report any errors
- [x] `./mach test-tidy` does not report any errors
- [x] These changes fix #22073
- [x] These changes do not require tests because the affected codepath requires user input, and we don't support webdriver tests yet.

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/22156)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2018-11-09 20:27:22 -05:00 committed by GitHub
commit b60006ae11
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -33,7 +33,7 @@ use crate::inline::{InlineFlow, InlineFragmentNodeFlags};
use crate::list_item::ListItemFlow;
use crate::model::MaybeAuto;
use crate::table_cell::CollapsedBordersForCell;
use euclid::{rect, Point2D, Rect, SideOffsets2D, Size2D, TypedSize2D, Vector2D};
use euclid::{rect, Point2D, Rect, SideOffsets2D, Size2D, TypedRect, TypedSize2D, Vector2D};
use fnv::FnvHashMap;
use gfx::text::glyph::ByteIndex;
use gfx::text::TextRun;
@ -724,6 +724,9 @@ pub trait FragmentDisplayListBuilding {
/// under construction and other metadata useful for constructing it.
/// * `dirty`: The dirty rectangle in the coordinate system of the owning flow.
/// * `clip`: The region to clip the display items to.
/// * `overflow_content_size`: The size of content associated with this fragment
/// that must have overflow handling applied to it. For a scrollable block
/// flow, it is expected that this is the size of the child boxes.
fn build_display_list(
&mut self,
state: &mut DisplayListBuildState,
@ -731,6 +734,7 @@ pub trait FragmentDisplayListBuilding {
border_painting_mode: BorderPaintingMode,
display_list_section: DisplayListSection,
clip: Rect<Au>,
overflow_content_size: Option<Size2D<Au>>,
);
/// build_display_list, but don't update the restyle damage
@ -743,6 +747,7 @@ pub trait FragmentDisplayListBuilding {
border_painting_mode: BorderPaintingMode,
display_list_section: DisplayListSection,
clip: Rect<Au>,
overflow_content_size: Option<Size2D<Au>>,
);
/// Builds the display items necessary to paint the selection and/or caret for this fragment,
@ -1701,6 +1706,7 @@ impl FragmentDisplayListBuilding for Fragment {
border_painting_mode: BorderPaintingMode,
display_list_section: DisplayListSection,
clip: Rect<Au>,
overflow_content_size: Option<Size2D<Au>>,
) {
let previous_clipping_and_scrolling = state.current_clipping_and_scrolling;
if let Some(index) = self.established_reference_frame {
@ -1714,6 +1720,7 @@ impl FragmentDisplayListBuilding for Fragment {
border_painting_mode,
display_list_section,
clip,
overflow_content_size,
);
state.current_clipping_and_scrolling = previous_clipping_and_scrolling;
@ -1726,6 +1733,7 @@ impl FragmentDisplayListBuilding for Fragment {
border_painting_mode: BorderPaintingMode,
display_list_section: DisplayListSection,
clip: Rect<Au>,
overflow_content_size: Option<Size2D<Au>>,
) {
if self.style().get_inherited_box().visibility != Visibility::Visible {
return;
@ -1838,6 +1846,29 @@ impl FragmentDisplayListBuilding for Fragment {
debug!("Fragment::build_display_list: intersected. Adding display item...");
if let Some(content_size) = overflow_content_size {
// Create a transparent rectangle for hit-testing purposes that exists in front
// of this fragment's background but behind its content. This ensures that any
// hit tests inside the content box but not on actual content target the current
// scrollable ancestor.
let content_size = TypedRect::from_size(content_size);
let base = state.create_base_display_item(
content_size,
content_size,
self.node,
self.style
.get_cursor(CursorKind::Default)
.or_else(|| Some(CursorKind::Default)),
DisplayListSection::Content,
);
state.add_display_item(DisplayItem::Rectangle(CommonDisplayItem::new(
base,
webrender_api::RectangleDisplayItem {
color: ColorF::TRANSPARENT,
},
)));
}
// Create special per-fragment-type display items.
state.clipping_and_scrolling_scope(|state| {
self.build_fragment_type_specific_display_items(
@ -2947,6 +2978,14 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
let background_border_section = self.background_border_section();
state.processing_scrolling_overflow_element = self.has_scrolling_overflow();
let content_size = if state.processing_scrolling_overflow_element {
let content_size = self.base.overflow.scroll.origin + self.base.overflow.scroll.size;
Some(Size2D::new(content_size.x, content_size.y))
} else {
None
};
let stacking_relative_border_box = self
.base
.stacking_relative_border_box_for_display_list(&self.fragment);
@ -2957,6 +2996,7 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
border_painting_mode,
background_border_section,
self.base.clip,
content_size,
);
self.base
@ -3098,6 +3138,7 @@ impl InlineFlowDisplayListBuilding for InlineFlow {
BorderPaintingMode::Separate,
DisplayListSection::Content,
self.base.clip,
None,
);
}
@ -3156,6 +3197,7 @@ impl ListItemFlowDisplayListBuilding for ListItemFlow {
BorderPaintingMode::Separate,
DisplayListSection::Content,
self.block_flow.base.clip,
None,
);
}