mirror of
https://github.com/servo/servo.git
synced 2025-09-27 23:30:08 +01:00
script: Move keyboard scrolling to script (#39371)
Instead of having every single embedder implement keyboard scrolling, handle it in script in the default key event handler. This allows properly targeting the scroll events to their scroll containers as well as appropriately sizing "page up" and "page down" scroll deltas. This change means that when you use the keyboard to scroll, the focused or most recently clicked `<iframe>` or overflow scroll container is scrolled, rather than the main frame. In addition, when a particular scroll frame is larger than its content in the axis of the scroll, the scrolling operation is chained to the parent (as in other browsers). One exception is for `<iframe>`s, which will be implemented in a followup change. Testing: automated tests runnable locally with `mach test-wpt --product servodriver` Signed-off-by: Martin Robinson <mrobinson@igalia.com> Co-authored-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
parent
99fbd36b5d
commit
ac8895c3ae
19 changed files with 540 additions and 185 deletions
|
@ -8,7 +8,7 @@ use compositing_traits::display_list::AxesScrollSensitivity;
|
|||
use euclid::Rect;
|
||||
use euclid::default::Size2D as UntypedSize2D;
|
||||
use layout_api::wrapper_traits::{LayoutNode, ThreadSafeLayoutElement, ThreadSafeLayoutNode};
|
||||
use layout_api::{LayoutElementType, LayoutNodeType};
|
||||
use layout_api::{AxesOverflow, LayoutElementType, LayoutNodeType};
|
||||
use malloc_size_of_derive::MallocSizeOf;
|
||||
use script::layout_dom::{ServoLayoutNode, ServoThreadSafeLayoutNode};
|
||||
use servo_arc::Arc;
|
||||
|
@ -31,7 +31,7 @@ use crate::fragment_tree::{FragmentFlags, FragmentTree};
|
|||
use crate::geom::{LogicalVec2, PhysicalSize};
|
||||
use crate::positioned::{AbsolutelyPositionedBox, PositioningContext};
|
||||
use crate::replaced::ReplacedContents;
|
||||
use crate::style_ext::{AxesOverflow, Display, DisplayGeneratingBox, DisplayInside};
|
||||
use crate::style_ext::{Display, DisplayGeneratingBox, DisplayInside};
|
||||
use crate::taffy::{TaffyItemBox, TaffyItemBoxInner};
|
||||
use crate::{DefiniteContainingBlock, PropagatedBoxTreeData};
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ use layout_api::{
|
|||
BoxAreaType, IFrameSizes, Layout, LayoutConfig, LayoutDamage, LayoutFactory,
|
||||
OffsetParentResponse, PropertyRegistration, QueryMsg, ReflowGoal, ReflowPhasesRun,
|
||||
ReflowRequest, ReflowRequestRestyle, ReflowResult, RegisterPropertyError,
|
||||
ScrollContainerQueryType, ScrollContainerResponse, TrustedNodeAddress,
|
||||
ScrollContainerQueryFlags, ScrollContainerResponse, TrustedNodeAddress,
|
||||
};
|
||||
use log::{debug, error, warn};
|
||||
use malloc_size_of::{MallocConditionalSizeOf, MallocSizeOf, MallocSizeOfOps};
|
||||
|
@ -334,10 +334,10 @@ impl Layout for LayoutThread {
|
|||
fn query_scroll_container(
|
||||
&self,
|
||||
node: TrustedNodeAddress,
|
||||
query_type: ScrollContainerQueryType,
|
||||
flags: ScrollContainerQueryFlags,
|
||||
) -> Option<ScrollContainerResponse> {
|
||||
let node = unsafe { ServoLayoutNode::new(&node) };
|
||||
process_scroll_container_query(node, query_type)
|
||||
process_scroll_container_query(node, flags)
|
||||
}
|
||||
|
||||
#[servo_tracing::instrument(skip_all)]
|
||||
|
|
|
@ -12,8 +12,8 @@ use euclid::{SideOffsets2D, Size2D};
|
|||
use itertools::Itertools;
|
||||
use layout_api::wrapper_traits::{LayoutNode, ThreadSafeLayoutElement, ThreadSafeLayoutNode};
|
||||
use layout_api::{
|
||||
BoxAreaType, LayoutElementType, LayoutNodeType, OffsetParentResponse, ScrollContainerQueryType,
|
||||
ScrollContainerResponse,
|
||||
BoxAreaType, LayoutElementType, LayoutNodeType, OffsetParentResponse,
|
||||
ScrollContainerQueryFlags, ScrollContainerResponse,
|
||||
};
|
||||
use script::layout_dom::{ServoLayoutNode, ServoThreadSafeLayoutNode};
|
||||
use servo_arc::Arc as ServoArc;
|
||||
|
@ -677,7 +677,7 @@ pub fn process_offset_parent_query(
|
|||
#[inline]
|
||||
pub(crate) fn process_scroll_container_query(
|
||||
node: ServoLayoutNode<'_>,
|
||||
query_type: ScrollContainerQueryType,
|
||||
query_flags: ScrollContainerQueryFlags,
|
||||
) -> Option<ScrollContainerResponse> {
|
||||
let layout_data = node.to_threadsafe().inner_layout_data()?;
|
||||
|
||||
|
@ -686,15 +686,15 @@ pub(crate) fn process_scroll_container_query(
|
|||
let layout_box = layout_data.self_box.borrow();
|
||||
let layout_box = layout_box.as_ref()?;
|
||||
|
||||
let (mut current_position_value, flags) = layout_box
|
||||
.with_first_base(|base| (base.style.clone_position(), base.base_fragment_info.flags))?;
|
||||
let (style, flags) =
|
||||
layout_box.with_first_base(|base| (base.style.clone(), base.base_fragment_info.flags))?;
|
||||
|
||||
// - The element is the root element.
|
||||
// - The element is the body element.
|
||||
//
|
||||
// Note: We only do this for `scrollParent`, which needs to be null. But `scrollIntoView` on the
|
||||
// `<body>` or root element should still bring it into view by scrolling the viewport.
|
||||
if query_type == ScrollContainerQueryType::ForScrollParent &&
|
||||
if query_flags.contains(ScrollContainerQueryFlags::ForScrollParent) &&
|
||||
flags.intersects(
|
||||
FragmentFlags::IS_ROOT_ELEMENT | FragmentFlags::IS_BODY_ELEMENT_OF_HTML_ELEMENT_ROOT,
|
||||
)
|
||||
|
@ -702,6 +702,15 @@ pub(crate) fn process_scroll_container_query(
|
|||
return None;
|
||||
}
|
||||
|
||||
if query_flags.contains(ScrollContainerQueryFlags::Inclusive) &&
|
||||
style.establishes_scroll_container(flags)
|
||||
{
|
||||
return Some(ScrollContainerResponse::Element(
|
||||
node.opaque().into(),
|
||||
style.effective_overflow(flags),
|
||||
));
|
||||
}
|
||||
|
||||
// - The element’s computed value of the position property is fixed and no ancestor
|
||||
// establishes a fixed position containing block.
|
||||
//
|
||||
|
@ -721,6 +730,7 @@ pub(crate) fn process_scroll_container_query(
|
|||
// Notes: We don't follow the specification exactly below, but we follow the spirit.
|
||||
//
|
||||
// TODO: Handle the situation where the ancestor is "closed-shadow-hidden" from the element.
|
||||
let mut current_position_value = style.clone_position();
|
||||
let mut current_ancestor = node.as_element()?;
|
||||
while let Some(ancestor) = current_ancestor.traversal_parent() {
|
||||
current_ancestor = ancestor;
|
||||
|
@ -757,6 +767,7 @@ pub(crate) fn process_scroll_container_query(
|
|||
if ancestor_style.establishes_scroll_container(ancestor_flags) {
|
||||
return Some(ScrollContainerResponse::Element(
|
||||
ancestor.as_node().opaque().into(),
|
||||
ancestor_style.effective_overflow(ancestor_flags),
|
||||
));
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use app_units::Au;
|
||||
use layout_api::AxesOverflow;
|
||||
use malloc_size_of_derive::MallocSizeOf;
|
||||
use style::Zero;
|
||||
use style::color::AbsoluteColor;
|
||||
|
@ -58,30 +59,6 @@ pub(crate) enum DisplayGeneratingBox {
|
|||
/// <https://drafts.csswg.org/css-display-3/#layout-specific-display>
|
||||
LayoutInternal(DisplayLayoutInternal),
|
||||
}
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct AxesOverflow {
|
||||
pub x: Overflow,
|
||||
pub y: Overflow,
|
||||
}
|
||||
|
||||
impl Default for AxesOverflow {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
x: Overflow::Visible,
|
||||
y: Overflow::Visible,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&ComputedValues> for AxesOverflow {
|
||||
fn from(style: &ComputedValues) -> Self {
|
||||
Self {
|
||||
x: style.clone_overflow_x(),
|
||||
y: style.clone_overflow_y(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DisplayGeneratingBox {
|
||||
pub(crate) fn display_inside(&self) -> DisplayInside {
|
||||
match *self {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue