aboutsummaryrefslogtreecommitdiffstats
path: root/components
diff options
context:
space:
mode:
Diffstat (limited to 'components')
-rw-r--r--components/constellation/constellation.rs2
-rw-r--r--components/layout/flexbox/layout.rs25
-rw-r--r--components/layout/flow/inline/line.rs2
-rw-r--r--components/layout/flow/inline/mod.rs4
-rw-r--r--components/layout/flow/mod.rs5
-rw-r--r--components/layout/flow/root.rs3
-rw-r--r--components/layout/formatting_contexts.rs5
-rw-r--r--components/layout/positioned.rs339
-rw-r--r--components/layout/style_ext.rs14
-rw-r--r--components/layout/table/layout.rs47
-rw-r--r--components/layout/taffy/layout.rs72
-rw-r--r--components/layout/taffy/mod.rs5
-rw-r--r--components/script/canvas_context.rs4
-rw-r--r--components/script/dom/gamepad.rs41
-rw-r--r--components/script/dom/gamepadbutton.rs6
-rw-r--r--components/script/dom/gamepadbuttonlist.rs44
-rw-r--r--components/script/dom/gamepadhapticactuator.rs20
-rw-r--r--components/script/dom/globalscope.rs171
-rw-r--r--components/script/dom/gpucanvascontext.rs1
-rw-r--r--components/script/dom/htmliframeelement.rs7
-rw-r--r--components/script/dom/webxr/xrhittestsource.rs6
-rw-r--r--components/script/dom/webxr/xrinputsource.rs13
-rw-r--r--components/script/dom/webxr/xrinputsourcearray.rs10
-rw-r--r--components/script/dom/webxr/xrrenderstate.rs8
-rw-r--r--components/script/dom/webxr/xrsession.rs12
-rw-r--r--components/script/dom/webxr/xrsystem.rs8
-rw-r--r--components/script/dom/window.rs137
-rw-r--r--components/script/script_thread.rs11
-rw-r--r--components/servo/Cargo.toml9
-rw-r--r--components/servo/tests/common/mod.rs114
-rw-r--r--components/servo/tests/servo.rs24
-rw-r--r--components/servo/tests/webview.rs49
-rw-r--r--components/webdriver_server/actions.rs7
-rw-r--r--components/webdriver_server/capabilities.rs14
-rw-r--r--components/webdriver_server/lib.rs88
35 files changed, 630 insertions, 697 deletions
diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs
index ad89c435717..f3a15d7708d 100644
--- a/components/constellation/constellation.rs
+++ b/components/constellation/constellation.rs
@@ -371,6 +371,7 @@ pub struct Constellation<STF, SWF> {
mem_profiler_chan: mem::ProfilerChan,
/// A single WebRender document the constellation operates on.
+ #[cfg(feature = "webgpu")]
webrender_document: DocumentId,
/// Webrender related objects required by WebGPU threads
@@ -715,6 +716,7 @@ where
phantom: PhantomData,
webdriver: WebDriverData::new(),
document_states: HashMap::new(),
+ #[cfg(feature = "webgpu")]
webrender_document: state.webrender_document,
#[cfg(feature = "webgpu")]
webrender_wgpu,
diff --git a/components/layout/flexbox/layout.rs b/components/layout/flexbox/layout.rs
index a5540123681..e69b792e272 100644
--- a/components/layout/flexbox/layout.rs
+++ b/components/layout/flexbox/layout.rs
@@ -49,7 +49,6 @@ use crate::{
struct FlexContext<'a> {
config: FlexContainerConfig,
layout_context: &'a LayoutContext<'a>,
- positioning_context: &'a mut PositioningContext,
containing_block: &'a ContainingBlock<'a>, // For items
container_inner_size_constraint: FlexRelativeVec2<SizeConstraint>,
}
@@ -657,7 +656,6 @@ impl FlexContainer {
let mut flex_context = FlexContext {
config: self.config.clone(),
layout_context,
- positioning_context,
containing_block,
// https://drafts.csswg.org/css-flexbox/#definite-sizes
container_inner_size_constraint: self.config.flex_axis.vec2_to_flex_relative(
@@ -1775,16 +1773,7 @@ impl FlexItem<'_> {
) -> Option<FlexItemLayoutResult> {
let containing_block = flex_context.containing_block;
let independent_formatting_context = &self.box_.independent_formatting_context;
- let mut positioning_context = independent_formatting_context
- .new_positioning_context()
- .unwrap_or_else(|| {
- PositioningContext::new_for_subtree(
- flex_context
- .positioning_context
- .collects_for_nearest_positioned_ancestor(),
- )
- });
-
+ let mut positioning_context = PositioningContext::default();
let item_writing_mode = independent_formatting_context.style().writing_mode;
let item_is_horizontal = item_writing_mode.is_horizontal();
let flex_axis = flex_context.config.flex_axis;
@@ -2617,17 +2606,7 @@ impl FlexItemBox {
cross_size_stretches_to_container_size: bool,
intrinsic_sizing_mode: IntrinsicSizingMode,
) -> Au {
- let mut positioning_context = self
- .independent_formatting_context
- .new_positioning_context()
- .unwrap_or_else(|| {
- PositioningContext::new_for_subtree(
- flex_context
- .positioning_context
- .collects_for_nearest_positioned_ancestor(),
- )
- });
-
+ let mut positioning_context = PositioningContext::default();
let style = self.independent_formatting_context.style();
match &self.independent_formatting_context.contents {
IndependentFormattingContextContents::Replaced(replaced) => {
diff --git a/components/layout/flow/inline/line.rs b/components/layout/flow/inline/line.rs
index e65eaed2367..80bab1080ed 100644
--- a/components/layout/flow/inline/line.rs
+++ b/components/layout/flow/inline/line.rs
@@ -331,7 +331,7 @@ impl LineItemLayout<'_, '_> {
self.calculate_inline_box_block_start(inline_box_state, space_above_baseline);
let positioning_context_or_start_offset_in_parent =
- match inline_box.base.new_positioning_context() {
+ match PositioningContext::new_for_layout_box_base(&inline_box.base) {
Some(positioning_context) => Either::Left(positioning_context),
None => Either::Right(self.current_positioning_context_mut().len()),
};
diff --git a/components/layout/flow/inline/mod.rs b/components/layout/flow/inline/mod.rs
index 25fbaa324b1..2023f4e7174 100644
--- a/components/layout/flow/inline/mod.rs
+++ b/components/layout/flow/inline/mod.rs
@@ -2004,9 +2004,7 @@ impl IndependentFormattingContext {
bidi_level: Level,
) {
// We need to know the inline size of the atomic before deciding whether to do the line break.
- let mut child_positioning_context = self
- .new_positioning_context()
- .unwrap_or_else(|| PositioningContext::new_for_subtree(true));
+ let mut child_positioning_context = PositioningContext::default();
let IndependentFloatOrAtomicLayoutResult {
mut fragment,
baselines,
diff --git a/components/layout/flow/mod.rs b/components/layout/flow/mod.rs
index 983282dc389..772b150ae1c 100644
--- a/components/layout/flow/mod.rs
+++ b/components/layout/flow/mod.rs
@@ -689,16 +689,13 @@ fn layout_block_level_children_in_parallel(
placement_state: &mut PlacementState,
ignore_block_margins_for_stretch: LogicalSides1D<bool>,
) -> Vec<Fragment> {
- let collects_for_nearest_positioned_ancestor =
- positioning_context.collects_for_nearest_positioned_ancestor();
let mut layout_results: Vec<(Fragment, PositioningContext)> =
Vec::with_capacity(child_boxes.len());
child_boxes
.par_iter()
.map(|child_box| {
- let mut child_positioning_context =
- PositioningContext::new_for_subtree(collects_for_nearest_positioned_ancestor);
+ let mut child_positioning_context = PositioningContext::default();
let fragment = child_box.borrow().layout(
layout_context,
&mut child_positioning_context,
diff --git a/components/layout/flow/root.rs b/components/layout/flow/root.rs
index c6498eeed63..bb9ff1d337a 100644
--- a/components/layout/flow/root.rs
+++ b/components/layout/flow/root.rs
@@ -385,8 +385,7 @@ impl BoxTree {
style,
};
- let mut positioning_context =
- PositioningContext::new_for_containing_block_for_all_descendants();
+ let mut positioning_context = PositioningContext::default();
let independent_layout = self.root.layout(
layout_context,
&mut positioning_context,
diff --git a/components/layout/formatting_contexts.rs b/components/layout/formatting_contexts.rs
index 4661c44592c..4982d0dae1a 100644
--- a/components/layout/formatting_contexts.rs
+++ b/components/layout/formatting_contexts.rs
@@ -295,10 +295,7 @@ impl IndependentNonReplacedContents {
);
}
- let mut child_positioning_context = PositioningContext::new_for_subtree(
- positioning_context.collects_for_nearest_positioned_ancestor(),
- );
-
+ let mut child_positioning_context = PositioningContext::default();
let result = self.layout_without_caching(
layout_context,
&mut child_positioning_context,
diff --git a/components/layout/positioned.rs b/components/layout/positioned.rs
index 6bfe2af87ef..ff361396fa3 100644
--- a/components/layout/positioned.rs
+++ b/components/layout/positioned.rs
@@ -43,16 +43,6 @@ pub(crate) struct AbsolutelyPositionedBox {
}
#[derive(Clone, MallocSizeOf)]
-pub(crate) struct PositioningContext {
- for_nearest_positioned_ancestor: Option<Vec<HoistedAbsolutelyPositionedBox>>,
-
- // For nearest `containing block for all descendants` as defined by the CSS transforms
- // spec.
- // https://www.w3.org/TR/css-transforms-1/#containing-block-for-all-descendants
- for_nearest_containing_block_for_all_descendants: Vec<HoistedAbsolutelyPositionedBox>,
-}
-
-#[derive(Clone, MallocSizeOf)]
pub(crate) struct HoistedAbsolutelyPositionedBox {
absolutely_positioned_box: ArcRefCell<AbsolutelyPositionedBox>,
@@ -104,55 +94,26 @@ impl AbsolutelyPositionedBox {
}
}
-impl IndependentFormattingContext {
- #[inline]
- pub(crate) fn new_positioning_context(&self) -> Option<PositioningContext> {
- self.base.new_positioning_context()
- }
-}
-
-impl LayoutBoxBase {
- #[inline]
- pub(crate) fn new_positioning_context(&self) -> Option<PositioningContext> {
- PositioningContext::new_for_style(&self.style, &self.base_fragment_info.flags)
- }
+#[derive(Clone, Default, MallocSizeOf)]
+pub(crate) struct PositioningContext {
+ absolutes: Vec<HoistedAbsolutelyPositionedBox>,
}
impl PositioningContext {
- pub(crate) fn new_for_containing_block_for_all_descendants() -> Self {
- Self {
- for_nearest_positioned_ancestor: None,
- for_nearest_containing_block_for_all_descendants: Vec::new(),
- }
- }
-
- /// Create a [PositioningContext] to use for laying out a subtree. The idea is that
- /// when subtree layout is finished, the newly hoisted boxes can be processed
- /// (normally adjusting their static insets) and then appended to the parent
- /// [PositioningContext].
- pub(crate) fn new_for_subtree(collects_for_nearest_positioned_ancestor: bool) -> Self {
- Self {
- for_nearest_positioned_ancestor: if collects_for_nearest_positioned_ancestor {
- Some(Vec::new())
- } else {
- None
- },
- for_nearest_containing_block_for_all_descendants: Vec::new(),
- }
- }
-
- pub(crate) fn collects_for_nearest_positioned_ancestor(&self) -> bool {
- self.for_nearest_positioned_ancestor.is_some()
+ #[inline]
+ pub(crate) fn new_for_layout_box_base(layout_box_base: &LayoutBoxBase) -> Option<Self> {
+ Self::new_for_style_and_fragment_flags(
+ &layout_box_base.style,
+ &layout_box_base.base_fragment_info.flags,
+ )
}
- fn new_for_style(style: &ComputedValues, flags: &FragmentFlags) -> Option<Self> {
- if style.establishes_containing_block_for_all_descendants(*flags) {
- Some(Self::new_for_containing_block_for_all_descendants())
- } else if style.establishes_containing_block_for_absolute_descendants(*flags) {
- Some(Self {
- for_nearest_positioned_ancestor: Some(Vec::new()),
- for_nearest_containing_block_for_all_descendants: Vec::new(),
- })
+ fn new_for_style_and_fragment_flags(
+ style: &ComputedValues,
+ flags: &FragmentFlags,
+ ) -> Option<Self> {
+ if style.establishes_containing_block_for_absolute_descendants(*flags) {
+ Some(Self::default())
} else {
None
}
@@ -195,20 +156,9 @@ impl PositioningContext {
offset: &PhysicalVec<Au>,
index: PositioningContextLength,
) {
- if let Some(hoisted_boxes) = self.for_nearest_positioned_ancestor.as_mut() {
- hoisted_boxes
- .iter_mut()
- .skip(index.for_nearest_positioned_ancestor)
- .for_each(|hoisted_fragment| {
- hoisted_fragment
- .fragment
- .borrow_mut()
- .adjust_offsets(offset)
- })
- }
- self.for_nearest_containing_block_for_all_descendants
+ self.absolutes
.iter_mut()
- .skip(index.for_nearest_containing_block_for_all_descendants)
+ .skip(index.0)
.for_each(|hoisted_fragment| {
hoisted_fragment
.fragment
@@ -227,19 +177,23 @@ impl PositioningContext {
base: &LayoutBoxBase,
fragment_layout_fn: impl FnOnce(&mut Self) -> BoxFragment,
) -> BoxFragment {
- // Try to create a context, but if one isn't necessary, simply create the fragment
- // using the given closure and the current `PositioningContext`.
- let mut new_context = match base.new_positioning_context() {
- Some(new_context) => new_context,
- None => return fragment_layout_fn(self),
- };
+ // If a new `PositioningContext` isn't necessary, simply create the fragment using
+ // the given closure and the current `PositioningContext`.
+ let establishes_containing_block_for_absolutes = base
+ .style
+ .establishes_containing_block_for_absolute_descendants(base.base_fragment_info.flags);
+ if !establishes_containing_block_for_absolutes {
+ return fragment_layout_fn(self);
+ }
+ let mut new_context = PositioningContext::default();
let mut new_fragment = fragment_layout_fn(&mut new_context);
- new_context.layout_collected_children(layout_context, &mut new_fragment);
- // If the new context has any hoisted boxes for the nearest containing block for
- // pass them up the tree.
+ // Lay out all of the absolutely positioned children for this fragment, and, if it
+ // isn't a containing block for fixed elements, then pass those up to the parent.
+ new_context.layout_collected_children(layout_context, &mut new_fragment);
self.append(new_context);
+
if base.style.clone_position() == Position::Relative {
new_fragment.content_rect.origin += relative_adjustement(&base.style, containing_block)
.to_physical_vector(containing_block.style.writing_mode)
@@ -248,13 +202,61 @@ impl PositioningContext {
new_fragment
}
+ fn take_boxes_for_fragment(
+ &mut self,
+ new_fragment: &BoxFragment,
+ boxes_to_layout_out: &mut Vec<HoistedAbsolutelyPositionedBox>,
+ boxes_to_continue_hoisting_out: &mut Vec<HoistedAbsolutelyPositionedBox>,
+ ) {
+ debug_assert!(
+ new_fragment
+ .style
+ .establishes_containing_block_for_absolute_descendants(new_fragment.base.flags)
+ );
+
+ if new_fragment
+ .style
+ .establishes_containing_block_for_all_descendants(new_fragment.base.flags)
+ {
+ boxes_to_layout_out.append(&mut self.absolutes);
+ return;
+ }
+
+ // TODO: This could potentially use `extract_if` when that is stabilized.
+ let (mut boxes_to_layout, mut boxes_to_continue_hoisting) = self
+ .absolutes
+ .drain(..)
+ .partition(|hoisted_box| hoisted_box.position() != Position::Fixed);
+ boxes_to_layout_out.append(&mut boxes_to_layout);
+ boxes_to_continue_hoisting_out.append(&mut boxes_to_continue_hoisting);
+ }
+
// Lay out the hoisted boxes collected into this `PositioningContext` and add them
// to the given `BoxFragment`.
- pub fn layout_collected_children(
+ pub(crate) fn layout_collected_children(
&mut self,
layout_context: &LayoutContext,
new_fragment: &mut BoxFragment,
) {
+ if self.absolutes.is_empty() {
+ return;
+ }
+
+ // Sometimes we create temporary PositioningContexts just to collect hoisted absolutes and
+ // then these are processed later. In that case and if this fragment doesn't establish a
+ // containing block for absolutes at all, we just do nothing. All hoisted fragments will
+ // later be passed up to a parent PositioningContext.
+ //
+ // Handling this case here, when the PositioningContext is completely ineffectual other than
+ // as a temporary container for hoisted boxes, means that callers can execute less conditional
+ // code.
+ if !new_fragment
+ .style
+ .establishes_containing_block_for_absolute_descendants(new_fragment.base.flags)
+ {
+ return;
+ }
+
let padding_rect = PhysicalRect::new(
// Ignore the content rect’s position in its own containing block:
PhysicalPoint::origin(),
@@ -268,83 +270,58 @@ impl PositioningContext {
style: &new_fragment.style,
};
- let take_hoisted_boxes_pending_layout =
- |context: &mut Self| match context.for_nearest_positioned_ancestor.as_mut() {
- Some(fragments) => mem::take(fragments),
- None => mem::take(&mut context.for_nearest_containing_block_for_all_descendants),
- };
+ let mut fixed_position_boxes_to_hoist = Vec::new();
+ let mut boxes_to_layout = Vec::new();
+ self.take_boxes_for_fragment(
+ new_fragment,
+ &mut boxes_to_layout,
+ &mut fixed_position_boxes_to_hoist,
+ );
- // Loop because it’s possible that we discover (the static position of)
- // more absolutely-positioned boxes while doing layout for others.
- let mut hoisted_boxes = take_hoisted_boxes_pending_layout(self);
- let mut laid_out_child_fragments = Vec::new();
- while !hoisted_boxes.is_empty() {
+ // Laying out a `position: absolute` child (which only establishes a containing block for
+ // `position: absolute` descendants) can result in more `position: fixed` descendants
+ // collecting in `self.absolutes`. We need to loop here in order to keep either laying them
+ // out or putting them into `fixed_position_boxes_to_hoist`. We know there aren't any more
+ // when `self.absolutes` is empty.
+ while !boxes_to_layout.is_empty() {
HoistedAbsolutelyPositionedBox::layout_many(
layout_context,
- &mut hoisted_boxes,
- &mut laid_out_child_fragments,
- &mut self.for_nearest_containing_block_for_all_descendants,
+ std::mem::take(&mut boxes_to_layout),
+ &mut new_fragment.children,
+ &mut self.absolutes,
&containing_block,
new_fragment.padding,
);
- hoisted_boxes = take_hoisted_boxes_pending_layout(self);
- }
- new_fragment.children.extend(laid_out_child_fragments);
- }
-
- pub(crate) fn push(&mut self, box_: HoistedAbsolutelyPositionedBox) {
- if let Some(nearest) = &mut self.for_nearest_positioned_ancestor {
- let position = box_
- .absolutely_positioned_box
- .borrow()
- .context
- .style()
- .clone_position();
- match position {
- Position::Fixed => {}, // fall through
- Position::Absolute => return nearest.push(box_),
- Position::Static | Position::Relative | Position::Sticky => unreachable!(),
- }
+ self.take_boxes_for_fragment(
+ new_fragment,
+ &mut boxes_to_layout,
+ &mut fixed_position_boxes_to_hoist,
+ );
}
- self.for_nearest_containing_block_for_all_descendants
- .push(box_)
+
+ // We replace here instead of simply preserving these in `take_boxes_for_fragment`
+ // so that we don't have to continually re-iterate over them when laying out in the
+ // loop above.
+ self.absolutes = fixed_position_boxes_to_hoist;
}
- pub(crate) fn is_empty(&self) -> bool {
- self.for_nearest_containing_block_for_all_descendants
- .is_empty() &&
- self.for_nearest_positioned_ancestor
- .as_ref()
- .is_none_or(|vector| vector.is_empty())
+ pub(crate) fn push(&mut self, hoisted_box: HoistedAbsolutelyPositionedBox) {
+ debug_assert!(matches!(
+ hoisted_box.position(),
+ Position::Absolute | Position::Fixed
+ ));
+ self.absolutes.push(hoisted_box);
}
- pub(crate) fn append(&mut self, other: Self) {
- if other.is_empty() {
+ pub(crate) fn append(&mut self, mut other: Self) {
+ if other.absolutes.is_empty() {
return;
}
-
- vec_append_owned(
- &mut self.for_nearest_containing_block_for_all_descendants,
- other.for_nearest_containing_block_for_all_descendants,
- );
-
- match (
- self.for_nearest_positioned_ancestor.as_mut(),
- other.for_nearest_positioned_ancestor,
- ) {
- (Some(us), Some(them)) => vec_append_owned(us, them),
- (None, Some(them)) => {
- // This is the case where we have laid out the absolute children in a containing
- // block for absolutes and we then are passing up the fixed-position descendants
- // to the containing block for all descendants.
- vec_append_owned(
- &mut self.for_nearest_containing_block_for_all_descendants,
- them,
- );
- },
- (None, None) => {},
- _ => unreachable!(),
+ if self.absolutes.is_empty() {
+ self.absolutes = other.absolutes;
+ } else {
+ self.absolutes.append(&mut other.absolutes)
}
}
@@ -354,19 +331,16 @@ impl PositioningContext {
initial_containing_block: &DefiniteContainingBlock,
fragments: &mut Vec<Fragment>,
) {
- debug_assert!(self.for_nearest_positioned_ancestor.is_none());
-
- // Loop because it’s possible that we discover (the static position of)
- // more absolutely-positioned boxes while doing layout for others.
- while !self
- .for_nearest_containing_block_for_all_descendants
- .is_empty()
- {
+ // Laying out a `position: absolute` child (which only establishes a containing block for
+ // `position: absolute` descendants) can result in more `position: fixed` descendants
+ // collecting in `self.absolutes`. We need to loop here in order to keep laying them out. We
+ // know there aren't any more when `self.absolutes` is empty.
+ while !self.absolutes.is_empty() {
HoistedAbsolutelyPositionedBox::layout_many(
layout_context,
- &mut mem::take(&mut self.for_nearest_containing_block_for_all_descendants),
+ mem::take(&mut self.absolutes),
fragments,
- &mut self.for_nearest_containing_block_for_all_descendants,
+ &mut self.absolutes,
initial_containing_block,
Default::default(),
)
@@ -375,58 +349,46 @@ impl PositioningContext {
/// Get the length of this [PositioningContext].
pub(crate) fn len(&self) -> PositioningContextLength {
- PositioningContextLength {
- for_nearest_positioned_ancestor: self
- .for_nearest_positioned_ancestor
- .as_ref()
- .map_or(0, |vec| vec.len()),
- for_nearest_containing_block_for_all_descendants: self
- .for_nearest_containing_block_for_all_descendants
- .len(),
- }
+ PositioningContextLength(self.absolutes.len())
}
/// Truncate this [PositioningContext] to the given [PositioningContextLength]. This
/// is useful for "unhoisting" boxes in this context and returning it to the state at
/// the time that [`PositioningContext::len()`] was called.
pub(crate) fn truncate(&mut self, length: &PositioningContextLength) {
- if let Some(vec) = self.for_nearest_positioned_ancestor.as_mut() {
- vec.truncate(length.for_nearest_positioned_ancestor);
- }
- self.for_nearest_containing_block_for_all_descendants
- .truncate(length.for_nearest_containing_block_for_all_descendants);
+ self.absolutes.truncate(length.0)
}
}
/// A data structure which stores the size of a positioning context.
#[derive(Clone, Copy, Debug, PartialEq)]
-pub(crate) struct PositioningContextLength {
- /// The number of boxes that will be hoisted the the nearest positioned ancestor for
- /// layout.
- for_nearest_positioned_ancestor: usize,
- /// The number of boxes that will be hoisted the the nearest ancestor which
- /// establishes a containing block for all descendants for layout.
- for_nearest_containing_block_for_all_descendants: usize,
-}
+pub(crate) struct PositioningContextLength(usize);
impl Zero for PositioningContextLength {
fn zero() -> Self {
- PositioningContextLength {
- for_nearest_positioned_ancestor: 0,
- for_nearest_containing_block_for_all_descendants: 0,
- }
+ Self(0)
}
fn is_zero(&self) -> bool {
- self.for_nearest_positioned_ancestor == 0 &&
- self.for_nearest_containing_block_for_all_descendants == 0
+ self.0.is_zero()
}
}
impl HoistedAbsolutelyPositionedBox {
+ fn position(&self) -> Position {
+ let position = self
+ .absolutely_positioned_box
+ .borrow()
+ .context
+ .style()
+ .clone_position();
+ assert!(position == Position::Fixed || position == Position::Absolute);
+ position
+ }
+
pub(crate) fn layout_many(
layout_context: &LayoutContext,
- boxes: &mut [Self],
+ mut boxes: Vec<Self>,
fragments: &mut Vec<Fragment>,
for_nearest_containing_block_for_all_descendants: &mut Vec<HoistedAbsolutelyPositionedBox>,
containing_block: &DefiniteContainingBlock,
@@ -473,7 +435,7 @@ impl HoistedAbsolutelyPositionedBox {
pub(crate) fn layout(
&mut self,
layout_context: &LayoutContext,
- for_nearest_containing_block_for_all_descendants: &mut Vec<HoistedAbsolutelyPositionedBox>,
+ hoisted_absolutes_from_children: &mut Vec<HoistedAbsolutelyPositionedBox>,
containing_block: &DefiniteContainingBlock,
containing_block_padding: PhysicalSides<Au>,
) -> Fragment {
@@ -596,7 +558,7 @@ impl HoistedAbsolutelyPositionedBox {
.sizes
}));
- let mut positioning_context = context.new_positioning_context().unwrap();
+ let mut positioning_context = PositioningContext::default();
let mut new_fragment = {
let content_size: LogicalVec2<Au>;
let fragments;
@@ -709,6 +671,10 @@ impl HoistedAbsolutelyPositionedBox {
)
.with_specific_layout_info(specific_layout_info)
};
+
+ // This is an absolutely positioned element, which means it also establishes a
+ // containing block for absolutes. We lay out any absolutely positioned children
+ // here and pass the rest to `hoisted_absolutes_from_children.`
positioning_context.layout_collected_children(layout_context, &mut new_fragment);
// Any hoisted boxes that remain in this positioning context are going to be hoisted
@@ -721,8 +687,7 @@ impl HoistedAbsolutelyPositionedBox {
PositioningContextLength::zero(),
);
- for_nearest_containing_block_for_all_descendants
- .extend(positioning_context.for_nearest_containing_block_for_all_descendants);
+ hoisted_absolutes_from_children.extend(positioning_context.absolutes);
let fragment = Fragment::Box(ArcRefCell::new(new_fragment));
context.base.set_fragment(fragment.clone());
@@ -1024,14 +989,6 @@ impl AbsoluteAxisSolver<'_> {
}
}
-fn vec_append_owned<T>(a: &mut Vec<T>, mut b: Vec<T>) {
- if a.is_empty() {
- *a = b
- } else {
- a.append(&mut b)
- }
-}
-
/// <https://drafts.csswg.org/css2/visuren.html#relative-positioning>
pub(crate) fn relative_adjustement(
style: &ComputedValues,
diff --git a/components/layout/style_ext.rs b/components/layout/style_ext.rs
index 023db6b07f1..68a4481a2be 100644
--- a/components/layout/style_ext.rs
+++ b/components/layout/style_ext.rs
@@ -712,15 +712,19 @@ impl ComputedValuesExt for ComputedValues {
// From <https://www.w3.org/TR/css-transforms-1/#transform-rendering>
// > For elements whose layout is governed by the CSS box model, any value other than
// > `none` for the `transform` property results in the creation of a stacking context.
+ //
+ // From <https://www.w3.org/TR/css-transforms-2/#individual-transforms>
+ // > all other values […] create a stacking context and containing block for all
+ // > descendants, per usual for transforms.
+ //
+ // From <https://www.w3.org/TR/css-transforms-2/#perspective-property>
+ // > any value other than none establishes a stacking context.
+ //
// From <https://www.w3.org/TR/css-transforms-2/#transform-style-property>
// > A computed value of `preserve-3d` for `transform-style` on a transformable element
// > establishes both a stacking context and a containing block for all descendants.
- // From <https://www.w3.org/TR/css-transforms-2/#perspective-property>
- // > any value other than none establishes a stacking context.
- // TODO: handle individual transform properties (`translate`, `scale` and `rotate`).
- // <https://www.w3.org/TR/css-transforms-2/#individual-transforms>
if self.is_transformable(fragment_flags) &&
- (!self.get_box().transform.0.is_empty() ||
+ (self.has_transform_or_perspective_style() ||
self.get_box().transform_style == ComputedTransformStyle::Preserve3d ||
will_change_bits
.intersects(WillChangeBits::TRANSFORM | WillChangeBits::PERSPECTIVE))
diff --git a/components/layout/table/layout.rs b/components/layout/table/layout.rs
index 0cbe3e9ca76..2efe339837e 100644
--- a/components/layout/table/layout.rs
+++ b/components/layout/table/layout.rs
@@ -1068,7 +1068,6 @@ impl<'a> TableLayout<'a> {
&mut self,
layout_context: &LayoutContext,
containing_block_for_table: &ContainingBlock,
- parent_positioning_context: &mut PositioningContext,
) {
self.cells_laid_out = self
.table
@@ -1076,30 +1075,6 @@ impl<'a> TableLayout<'a> {
.par_iter()
.enumerate()
.map(|(row_index, row_slots)| {
- // When building the PositioningContext for this cell, we want it to have the same
- // configuration for whatever PositioningContext the contents are ultimately added to.
- let collect_for_nearest_positioned_ancestor = parent_positioning_context
- .collects_for_nearest_positioned_ancestor() ||
- self.table.rows.get(row_index).is_some_and(|row| {
- let row = row.borrow();
- let row_group_collects_for_nearest_positioned_ancestor =
- row.group_index.is_some_and(|group_index| {
- self.table.row_groups[group_index]
- .borrow()
- .base
- .style
- .establishes_containing_block_for_absolute_descendants(
- FragmentFlags::empty(),
- )
- });
- row_group_collects_for_nearest_positioned_ancestor ||
- row.base
- .style
- .establishes_containing_block_for_absolute_descendants(
- FragmentFlags::empty(),
- )
- });
-
row_slots
.par_iter()
.enumerate()
@@ -1141,10 +1116,7 @@ impl<'a> TableLayout<'a> {
style: &cell.base.style,
};
- let mut positioning_context = PositioningContext::new_for_subtree(
- collect_for_nearest_positioned_ancestor,
- );
-
+ let mut positioning_context = PositioningContext::default();
let layout = cell.contents.layout(
layout_context,
&mut positioning_context,
@@ -1503,7 +1475,6 @@ impl<'a> TableLayout<'a> {
layout_context: &LayoutContext,
parent_positioning_context: &mut PositioningContext,
) -> BoxFragment {
- let mut positioning_context = caption.context.new_positioning_context();
let containing_block = &ContainingBlock {
size: ContainingBlockSize {
inline: self.table_width + self.pbm.padding_border_sums.inline,
@@ -1517,6 +1488,8 @@ impl<'a> TableLayout<'a> {
// stretch block size. https://drafts.csswg.org/css-sizing-4/#stretch-fit-sizing
let ignore_block_margins_for_stretch = LogicalSides1D::new(false, false);
+ let mut positioning_context =
+ PositioningContext::new_for_layout_box_base(&caption.context.base);
let mut box_fragment = caption.context.layout_in_flow_block_level(
layout_context,
positioning_context
@@ -1769,11 +1742,7 @@ impl<'a> TableLayout<'a> {
) -> BoxFragment {
self.distributed_column_widths =
Self::distribute_width_to_columns(self.assignable_width, &self.columns);
- self.layout_cells_in_row(
- layout_context,
- containing_block_for_children,
- positioning_context,
- );
+ self.layout_cells_in_row(layout_context, containing_block_for_children);
let table_writing_mode = containing_block_for_children.style.writing_mode;
let first_layout_row_heights = self.do_first_row_layout(table_writing_mode);
self.compute_table_height_and_final_row_heights(
@@ -2325,7 +2294,7 @@ impl<'a> RowFragmentLayout<'a> {
Self {
row: table_row,
rect,
- positioning_context: table_row.base.new_positioning_context(),
+ positioning_context: PositioningContext::new_for_layout_box_base(&table_row.base),
containing_block,
fragments: Vec::new(),
}
@@ -2379,11 +2348,11 @@ impl<'a> RowFragmentLayout<'a> {
if let Some(mut row_positioning_context) = self.positioning_context.take() {
row_positioning_context.layout_collected_children(layout_context, &mut row_fragment);
- let positioning_context = row_group_fragment_layout
+ let parent_positioning_context = row_group_fragment_layout
.as_mut()
.and_then(|layout| layout.positioning_context.as_mut())
.unwrap_or(table_positioning_context);
- positioning_context.append(row_positioning_context);
+ parent_positioning_context.append(row_positioning_context);
}
let fragment = Fragment::Box(ArcRefCell::new(row_fragment));
@@ -2410,7 +2379,7 @@ impl RowGroupFragmentLayout {
let row_group = row_group.borrow();
(
dimensions.get_row_group_rect(&row_group),
- row_group.base.new_positioning_context(),
+ PositioningContext::new_for_layout_box_base(&row_group.base),
)
};
Self {
diff --git a/components/layout/taffy/layout.rs b/components/layout/taffy/layout.rs
index 3777c902053..a5838c1bd65 100644
--- a/components/layout/taffy/layout.rs
+++ b/components/layout/taffy/layout.rs
@@ -29,7 +29,7 @@ use crate::geom::{
use crate::layout_box_base::CacheableLayoutResult;
use crate::positioned::{AbsolutelyPositionedBox, PositioningContext, PositioningContextLength};
use crate::sizing::{ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult};
-use crate::style_ext::{ComputedValuesExt, LayoutStyle};
+use crate::style_ext::LayoutStyle;
use crate::{ConstraintSpace, ContainingBlock, ContainingBlockSize};
const DUMMY_NODE_ID: taffy::NodeId = taffy::NodeId::new(u64::MAX);
@@ -250,29 +250,15 @@ impl taffy::LayoutPartialTree for TaffyContainerContext<'_> {
},
style,
};
- let layout = {
- let mut child_positioning_context = independent_context
- .new_positioning_context()
- .unwrap_or_else(|| {
- PositioningContext::new_for_subtree(
- self.positioning_context
- .collects_for_nearest_positioned_ancestor(),
- )
- });
-
- let layout = non_replaced.layout_without_caching(
- self.layout_context,
- &mut child_positioning_context,
- &content_box_size_override,
- containing_block,
- false, /* depends_on_block_constraints */
- );
- // Store layout data on child for later access
- child.positioning_context = child_positioning_context;
-
- layout
- };
+ child.positioning_context = PositioningContext::default();
+ let layout = non_replaced.layout_without_caching(
+ self.layout_context,
+ &mut child.positioning_context,
+ &content_box_size_override,
+ containing_block,
+ false, /* depends_on_block_constraints */
+ );
child.child_fragments = layout.fragments;
self.child_specific_layout_infos[usize::from(node_id)] =
@@ -373,8 +359,7 @@ impl ComputeInlineContentSizes for TaffyContainer {
let mut grid_context = TaffyContainerContext {
layout_context,
- positioning_context:
- &mut PositioningContext::new_for_containing_block_for_all_descendants(),
+ positioning_context: &mut PositioningContext::default(),
content_box_size_override: containing_block,
style,
source_child_nodes: &self.children,
@@ -540,17 +525,6 @@ impl TaffyContainer {
let child_specific_layout_info: Option<SpecificLayoutInfo> =
std::mem::take(&mut container_ctx.child_specific_layout_infos[child_id]);
- let establishes_containing_block_for_absolute_descendants =
- if let TaffyItemBoxInner::InFlowBox(independent_box) = &child.taffy_level_box {
- child
- .style
- .establishes_containing_block_for_absolute_descendants(
- independent_box.base_fragment_info().flags,
- )
- } else {
- false
- };
-
let fragment = match &mut child.taffy_level_box {
TaffyItemBoxInner::InFlowBox(independent_box) => {
let mut fragment_info = independent_box.base_fragment_info();
@@ -573,29 +547,21 @@ impl TaffyContainer {
})
.with_specific_layout_info(child_specific_layout_info);
- if establishes_containing_block_for_absolute_descendants {
- child.positioning_context.layout_collected_children(
- container_ctx.layout_context,
- &mut box_fragment,
- );
- }
-
- let fragment = Fragment::Box(ArcRefCell::new(box_fragment));
-
+ child.positioning_context.layout_collected_children(
+ container_ctx.layout_context,
+ &mut box_fragment,
+ );
child
.positioning_context
- .adjust_static_position_of_hoisted_fragments(
- &fragment,
+ .adjust_static_position_of_hoisted_fragments_with_offset(
+ &box_fragment.content_rect.origin.to_vector(),
PositioningContextLength::zero(),
);
- let child_positioning_context = std::mem::replace(
- &mut child.positioning_context,
- PositioningContext::new_for_containing_block_for_all_descendants(),
- );
container_ctx
.positioning_context
- .append(child_positioning_context);
- fragment
+ .append(std::mem::take(&mut child.positioning_context));
+
+ Fragment::Box(ArcRefCell::new(box_fragment))
},
TaffyItemBoxInner::OutOfFlowAbsolutelyPositionedBox(abs_pos_box) => {
fn resolve_alignment(value: AlignFlags, auto: AlignFlags) -> AlignFlags {
diff --git a/components/layout/taffy/mod.rs b/components/layout/taffy/mod.rs
index 55a678cd89a..b1ff753ea78 100644
--- a/components/layout/taffy/mod.rs
+++ b/components/layout/taffy/mod.rs
@@ -110,7 +110,7 @@ impl TaffyItemBox {
Self {
taffy_layout: Default::default(),
child_fragments: Vec::new(),
- positioning_context: PositioningContext::new_for_containing_block_for_all_descendants(),
+ positioning_context: PositioningContext::default(),
style,
taffy_level_box: inner,
}
@@ -118,8 +118,7 @@ impl TaffyItemBox {
pub(crate) fn invalidate_cached_fragment(&mut self) {
self.taffy_layout = Default::default();
- self.positioning_context =
- PositioningContext::new_for_containing_block_for_all_descendants();
+ self.positioning_context = PositioningContext::default();
match self.taffy_level_box {
TaffyItemBoxInner::InFlowBox(ref independent_formatting_context) => {
independent_formatting_context
diff --git a/components/script/canvas_context.rs b/components/script/canvas_context.rs
index 0a7545e9594..7dfdf48e3f5 100644
--- a/components/script/canvas_context.rs
+++ b/components/script/canvas_context.rs
@@ -14,8 +14,10 @@ use crate::dom::bindings::codegen::UnionTypes::HTMLCanvasElementOrOffscreenCanva
use crate::dom::bindings::inheritance::Castable;
use crate::dom::htmlcanvaselement::HTMLCanvasElement;
use crate::dom::node::{Node, NodeDamage};
+#[cfg(feature = "webgpu")]
+use crate::dom::types::GPUCanvasContext;
use crate::dom::types::{
- CanvasRenderingContext2D, GPUCanvasContext, OffscreenCanvas, OffscreenCanvasRenderingContext2D,
+ CanvasRenderingContext2D, OffscreenCanvas, OffscreenCanvasRenderingContext2D,
WebGL2RenderingContext, WebGLRenderingContext,
};
diff --git a/components/script/dom/gamepad.rs b/components/script/dom/gamepad.rs
index dcafc58bcd9..baf3af7466f 100644
--- a/components/script/dom/gamepad.rs
+++ b/components/script/dom/gamepad.rs
@@ -13,7 +13,7 @@ use crate::dom::bindings::codegen::Bindings::GamepadBinding::{GamepadHand, Gamep
use crate::dom::bindings::codegen::Bindings::GamepadButtonListBinding::GamepadButtonListMethods;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::num::Finite;
-use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object_with_proto};
+use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::bindings::str::DOMString;
use crate::dom::event::Event;
@@ -23,6 +23,7 @@ use crate::dom::gamepadevent::{GamepadEvent, GamepadEventType};
use crate::dom::gamepadhapticactuator::GamepadHapticActuator;
use crate::dom::gamepadpose::GamepadPose;
use crate::dom::globalscope::GlobalScope;
+use crate::dom::window::Window;
use crate::script_runtime::{CanGc, JSContext};
// This value is for determining when to consider a gamepad as having a user gesture
@@ -88,39 +89,14 @@ impl Gamepad {
}
}
- #[allow(clippy::too_many_arguments)]
- pub(crate) fn new(
- global: &GlobalScope,
- gamepad_id: u32,
- id: String,
- mapping_type: String,
- axis_bounds: (f64, f64),
- button_bounds: (f64, f64),
- supported_haptic_effects: GamepadSupportedHapticEffects,
- xr: bool,
- can_gc: CanGc,
- ) -> DomRoot<Gamepad> {
- Self::new_with_proto(
- global,
- gamepad_id,
- id,
- mapping_type,
- axis_bounds,
- button_bounds,
- supported_haptic_effects,
- xr,
- can_gc,
- )
- }
-
/// When we construct a new gamepad, we initialize the number of buttons and
/// axes corresponding to the "standard" gamepad mapping.
/// The spec says UAs *may* do this for fingerprint mitigation, and it also
/// happens to simplify implementation
/// <https://www.w3.org/TR/gamepad/#fingerprinting-mitigation>
#[allow(clippy::too_many_arguments)]
- fn new_with_proto(
- global: &GlobalScope,
+ pub(crate) fn new(
+ window: &Window,
gamepad_id: u32,
id: String,
mapping_type: String,
@@ -130,11 +106,11 @@ impl Gamepad {
xr: bool,
can_gc: CanGc,
) -> DomRoot<Gamepad> {
- let button_list = GamepadButtonList::init_buttons(global, can_gc);
+ let button_list = GamepadButtonList::init_buttons(window, can_gc);
let vibration_actuator =
- GamepadHapticActuator::new(global, gamepad_id, supported_haptic_effects, can_gc);
+ GamepadHapticActuator::new(window, gamepad_id, supported_haptic_effects, can_gc);
let index = if xr { -1 } else { 0 };
- let gamepad = reflect_dom_object_with_proto(
+ let gamepad = reflect_dom_object(
Box::new(Gamepad::new_inherited(
gamepad_id,
id,
@@ -149,8 +125,7 @@ impl Gamepad {
button_bounds,
&vibration_actuator,
)),
- global,
- None,
+ window,
can_gc,
);
gamepad.init_axes(can_gc);
diff --git a/components/script/dom/gamepadbutton.rs b/components/script/dom/gamepadbutton.rs
index fead990ccd3..3588ba775ca 100644
--- a/components/script/dom/gamepadbutton.rs
+++ b/components/script/dom/gamepadbutton.rs
@@ -10,7 +10,7 @@ use crate::dom::bindings::codegen::Bindings::GamepadButtonBinding::GamepadButton
use crate::dom::bindings::num::Finite;
use crate::dom::bindings::reflector::{Reflector, reflect_dom_object};
use crate::dom::bindings::root::DomRoot;
-use crate::dom::globalscope::GlobalScope;
+use crate::dom::window::Window;
use crate::script_runtime::CanGc;
#[dom_struct]
@@ -32,14 +32,14 @@ impl GamepadButton {
}
pub(crate) fn new(
- global: &GlobalScope,
+ window: &Window,
pressed: bool,
touched: bool,
can_gc: CanGc,
) -> DomRoot<GamepadButton> {
reflect_dom_object(
Box::new(GamepadButton::new_inherited(pressed, touched)),
- global,
+ window,
can_gc,
)
}
diff --git a/components/script/dom/gamepadbuttonlist.rs b/components/script/dom/gamepadbuttonlist.rs
index 50d9c8172bc..7e540ab56bb 100644
--- a/components/script/dom/gamepadbuttonlist.rs
+++ b/components/script/dom/gamepadbuttonlist.rs
@@ -8,7 +8,7 @@ use crate::dom::bindings::codegen::Bindings::GamepadButtonListBinding::GamepadBu
use crate::dom::bindings::reflector::{Reflector, reflect_dom_object};
use crate::dom::bindings::root::{Dom, DomRoot, DomSlice};
use crate::dom::gamepadbutton::GamepadButton;
-use crate::dom::globalscope::GlobalScope;
+use crate::dom::window::Window;
use crate::script_runtime::CanGc;
// https://w3c.github.io/gamepad/#gamepadbutton-interface
@@ -28,13 +28,13 @@ impl GamepadButtonList {
}
pub(crate) fn new(
- global: &GlobalScope,
+ window: &Window,
list: &[&GamepadButton],
can_gc: CanGc,
) -> DomRoot<GamepadButtonList> {
reflect_dom_object(
Box::new(GamepadButtonList::new_inherited(list)),
- global,
+ window,
can_gc,
)
}
@@ -62,27 +62,27 @@ impl GamepadButtonListMethods<crate::DomTypeHolder> for GamepadButtonList {
impl GamepadButtonList {
/// Initialize the number of buttons in the "standard" gamepad mapping.
/// <https://www.w3.org/TR/gamepad/#dfn-initializing-buttons>
- pub(crate) fn init_buttons(global: &GlobalScope, can_gc: CanGc) -> DomRoot<GamepadButtonList> {
+ pub(crate) fn init_buttons(window: &Window, can_gc: CanGc) -> DomRoot<GamepadButtonList> {
let standard_buttons = &[
- GamepadButton::new(global, false, false, can_gc), // Bottom button in right cluster
- GamepadButton::new(global, false, false, can_gc), // Right button in right cluster
- GamepadButton::new(global, false, false, can_gc), // Left button in right cluster
- GamepadButton::new(global, false, false, can_gc), // Top button in right cluster
- GamepadButton::new(global, false, false, can_gc), // Top left front button
- GamepadButton::new(global, false, false, can_gc), // Top right front button
- GamepadButton::new(global, false, false, can_gc), // Bottom left front button
- GamepadButton::new(global, false, false, can_gc), // Bottom right front button
- GamepadButton::new(global, false, false, can_gc), // Left button in center cluster
- GamepadButton::new(global, false, false, can_gc), // Right button in center cluster
- GamepadButton::new(global, false, false, can_gc), // Left stick pressed button
- GamepadButton::new(global, false, false, can_gc), // Right stick pressed button
- GamepadButton::new(global, false, false, can_gc), // Top button in left cluster
- GamepadButton::new(global, false, false, can_gc), // Bottom button in left cluster
- GamepadButton::new(global, false, false, can_gc), // Left button in left cluster
- GamepadButton::new(global, false, false, can_gc), // Right button in left cluster
- GamepadButton::new(global, false, false, can_gc), // Center button in center cluster
+ GamepadButton::new(window, false, false, can_gc), // Bottom button in right cluster
+ GamepadButton::new(window, false, false, can_gc), // Right button in right cluster
+ GamepadButton::new(window, false, false, can_gc), // Left button in right cluster
+ GamepadButton::new(window, false, false, can_gc), // Top button in right cluster
+ GamepadButton::new(window, false, false, can_gc), // Top left front button
+ GamepadButton::new(window, false, false, can_gc), // Top right front button
+ GamepadButton::new(window, false, false, can_gc), // Bottom left front button
+ GamepadButton::new(window, false, false, can_gc), // Bottom right front button
+ GamepadButton::new(window, false, false, can_gc), // Left button in center cluster
+ GamepadButton::new(window, false, false, can_gc), // Right button in center cluster
+ GamepadButton::new(window, false, false, can_gc), // Left stick pressed button
+ GamepadButton::new(window, false, false, can_gc), // Right stick pressed button
+ GamepadButton::new(window, false, false, can_gc), // Top button in left cluster
+ GamepadButton::new(window, false, false, can_gc), // Bottom button in left cluster
+ GamepadButton::new(window, false, false, can_gc), // Left button in left cluster
+ GamepadButton::new(window, false, false, can_gc), // Right button in left cluster
+ GamepadButton::new(window, false, false, can_gc), // Center button in center cluster
];
rooted_vec!(let buttons <- standard_buttons.iter().map(DomRoot::as_traced));
- Self::new(global, buttons.r(), can_gc)
+ Self::new(window, buttons.r(), can_gc)
}
}
diff --git a/components/script/dom/gamepadhapticactuator.rs b/components/script/dom/gamepadhapticactuator.rs
index d19db6d1279..ddea21b97ee 100644
--- a/components/script/dom/gamepadhapticactuator.rs
+++ b/components/script/dom/gamepadhapticactuator.rs
@@ -18,12 +18,12 @@ use crate::dom::bindings::codegen::Bindings::GamepadHapticActuatorBinding::{
use crate::dom::bindings::codegen::Bindings::WindowBinding::Window_Binding::WindowMethods;
use crate::dom::bindings::error::Error;
use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
-use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object_with_proto};
+use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::DOMString;
use crate::dom::bindings::utils::to_frozen_array;
-use crate::dom::globalscope::GlobalScope;
use crate::dom::promise::Promise;
+use crate::dom::window::Window;
use crate::realms::InRealm;
use crate::script_runtime::{CanGc, JSContext};
use crate::task_source::SendableTaskSource;
@@ -98,27 +98,17 @@ impl GamepadHapticActuator {
}
pub(crate) fn new(
- global: &GlobalScope,
+ window: &Window,
gamepad_index: u32,
supported_haptic_effects: GamepadSupportedHapticEffects,
can_gc: CanGc,
) -> DomRoot<GamepadHapticActuator> {
- Self::new_with_proto(global, gamepad_index, supported_haptic_effects, can_gc)
- }
-
- fn new_with_proto(
- global: &GlobalScope,
- gamepad_index: u32,
- supported_haptic_effects: GamepadSupportedHapticEffects,
- can_gc: CanGc,
- ) -> DomRoot<GamepadHapticActuator> {
- reflect_dom_object_with_proto(
+ reflect_dom_object(
Box::new(GamepadHapticActuator::new_inherited(
gamepad_index,
supported_haptic_effects,
)),
- global,
- None,
+ window,
can_gc,
)
}
diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs
index efa9a9a97ab..527d03eed4e 100644
--- a/components/script/dom/globalscope.rs
+++ b/components/script/dom/globalscope.rs
@@ -18,18 +18,17 @@ use base::id::{
ServiceWorkerId, ServiceWorkerRegistrationId, WebViewId,
};
use constellation_traits::{
- BlobData, BlobImpl, BroadcastMsg, FileBlob, MessagePortImpl, MessagePortMsg, PortMessageTask,
- ScriptToConstellationChan, ScriptToConstellationMessage,
+ BlobData, BlobImpl, BroadcastMsg, FileBlob, LoadData, LoadOrigin, MessagePortImpl,
+ MessagePortMsg, PortMessageTask, ScriptToConstellationChan, ScriptToConstellationMessage,
};
use content_security_policy::{
- CheckResult, CspList, PolicyDisposition, PolicySource, Violation, ViolationResource,
+ CheckResult, CspList, Destination, Initiator, NavigationCheckType, ParserMetadata,
+ PolicyDisposition, PolicySource, Request, Violation, ViolationResource,
};
use crossbeam_channel::Sender;
use devtools_traits::{PageError, ScriptToDevtoolsControlMsg};
use dom_struct::dom_struct;
-use embedder_traits::{
- EmbedderMsg, GamepadEvent, GamepadSupportedHapticEffects, GamepadUpdateType,
-};
+use embedder_traits::EmbedderMsg;
use http::HeaderMap;
use hyper_serde::Serde;
use ipc_channel::ipc::{self, IpcSender};
@@ -64,6 +63,7 @@ use profile_traits::{ipc as profile_ipc, mem as profile_mem, time as profile_tim
use script_bindings::interfaces::GlobalScopeHelpers;
use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl};
use timers::{TimerEventId, TimerEventRequest, TimerSource};
+use url::Origin;
use uuid::Uuid;
#[cfg(feature = "webgpu")]
use webgpu_traits::{DeviceLostReason, WebGPUDevice};
@@ -81,9 +81,7 @@ use crate::dom::bindings::codegen::Bindings::FunctionBinding::Function;
use crate::dom::bindings::codegen::Bindings::ImageBitmapBinding::{
ImageBitmapOptions, ImageBitmapSource,
};
-use crate::dom::bindings::codegen::Bindings::NavigatorBinding::NavigatorMethods;
use crate::dom::bindings::codegen::Bindings::NotificationBinding::NotificationPermissionCallback;
-use crate::dom::bindings::codegen::Bindings::PerformanceBinding::Performance_Binding::PerformanceMethods;
use crate::dom::bindings::codegen::Bindings::PermissionStatusBinding::{
PermissionName, PermissionState,
};
@@ -113,8 +111,6 @@ use crate::dom::event::{Event, EventBubbles, EventCancelable, EventStatus};
use crate::dom::eventsource::EventSource;
use crate::dom::eventtarget::EventTarget;
use crate::dom::file::File;
-use crate::dom::gamepad::{Gamepad, contains_user_gesture};
-use crate::dom::gamepadevent::GamepadEventType;
use crate::dom::htmlscriptelement::{ScriptId, SourceCode};
use crate::dom::imagebitmap::ImageBitmap;
use crate::dom::messageevent::MessageEvent;
@@ -2957,6 +2953,33 @@ impl GlobalScope {
is_js_evaluation_allowed == CheckResult::Allowed
}
+ pub(crate) fn should_navigation_request_be_blocked(&self, load_data: &LoadData) -> bool {
+ let Some(csp_list) = self.get_csp_list() else {
+ return false;
+ };
+ let request = Request {
+ url: load_data.url.clone().into_url(),
+ origin: match &load_data.load_origin {
+ LoadOrigin::Script(immutable_origin) => immutable_origin.clone().into_url_origin(),
+ _ => Origin::new_opaque(),
+ },
+ // TODO: populate this field correctly
+ redirect_count: 0,
+ destination: Destination::None,
+ initiator: Initiator::None,
+ nonce: "".to_owned(),
+ integrity_metadata: "".to_owned(),
+ parser_metadata: ParserMetadata::None,
+ };
+ // TODO: set correct navigation check type for form submission if applicable
+ let (result, violations) =
+ csp_list.should_navigation_request_be_blocked(&request, NavigationCheckType::Other);
+
+ self.report_csp_violations(violations);
+
+ result == CheckResult::Blocked
+ }
+
pub(crate) fn create_image_bitmap(
&self,
image: ImageBitmapSource,
@@ -3287,134 +3310,6 @@ impl GlobalScope {
}
}
- pub(crate) fn handle_gamepad_event(&self, gamepad_event: GamepadEvent) {
- match gamepad_event {
- GamepadEvent::Connected(index, name, bounds, supported_haptic_effects) => {
- self.handle_gamepad_connect(
- index.0,
- name,
- bounds.axis_bounds,
- bounds.button_bounds,
- supported_haptic_effects,
- );
- },
- GamepadEvent::Disconnected(index) => {
- self.handle_gamepad_disconnect(index.0);
- },
- GamepadEvent::Updated(index, update_type) => {
- self.receive_new_gamepad_button_or_axis(index.0, update_type);
- },
- };
- }
-
- /// <https://www.w3.org/TR/gamepad/#dfn-gamepadconnected>
- fn handle_gamepad_connect(
- &self,
- // As the spec actually defines how to set the gamepad index, the GilRs index
- // is currently unused, though in practice it will almost always be the same.
- // More infra is currently needed to track gamepads across windows.
- _index: usize,
- name: String,
- axis_bounds: (f64, f64),
- button_bounds: (f64, f64),
- supported_haptic_effects: GamepadSupportedHapticEffects,
- ) {
- // TODO: 2. If document is not null and is not allowed to use the "gamepad" permission,
- // then abort these steps.
- let this = Trusted::new(self);
- self.task_manager()
- .gamepad_task_source()
- .queue(task!(gamepad_connected: move || {
- let global = this.root();
-
- if let Some(window) = global.downcast::<Window>() {
- let navigator = window.Navigator();
- let selected_index = navigator.select_gamepad_index();
- let gamepad = Gamepad::new(
- &global,
- selected_index,
- name,
- "standard".into(),
- axis_bounds,
- button_bounds,
- supported_haptic_effects,
- false,
- CanGc::note(),
- );
- navigator.set_gamepad(selected_index as usize, &gamepad, CanGc::note());
- }
- }));
- }
-
- /// <https://www.w3.org/TR/gamepad/#dfn-gamepaddisconnected>
- pub(crate) fn handle_gamepad_disconnect(&self, index: usize) {
- let this = Trusted::new(self);
- self.task_manager()
- .gamepad_task_source()
- .queue(task!(gamepad_disconnected: move || {
- let global = this.root();
- if let Some(window) = global.downcast::<Window>() {
- let navigator = window.Navigator();
- if let Some(gamepad) = navigator.get_gamepad(index) {
- if window.Document().is_fully_active() {
- gamepad.update_connected(false, gamepad.exposed(), CanGc::note());
- navigator.remove_gamepad(index);
- }
- }
- }
- }));
- }
-
- /// <https://www.w3.org/TR/gamepad/#receiving-inputs>
- pub(crate) fn receive_new_gamepad_button_or_axis(
- &self,
- index: usize,
- update_type: GamepadUpdateType,
- ) {
- let this = Trusted::new(self);
-
- // <https://w3c.github.io/gamepad/#dfn-update-gamepad-state>
- self.task_manager().gamepad_task_source().queue(
- task!(update_gamepad_state: move || {
- let global = this.root();
- if let Some(window) = global.downcast::<Window>() {
- let navigator = window.Navigator();
- if let Some(gamepad) = navigator.get_gamepad(index) {
- let current_time = global.performance().Now();
- gamepad.update_timestamp(*current_time);
- match update_type {
- GamepadUpdateType::Axis(index, value) => {
- gamepad.map_and_normalize_axes(index, value);
- },
- GamepadUpdateType::Button(index, value) => {
- gamepad.map_and_normalize_buttons(index, value);
- }
- };
- if !navigator.has_gamepad_gesture() && contains_user_gesture(update_type) {
- navigator.set_has_gamepad_gesture(true);
- navigator.GetGamepads()
- .iter()
- .filter_map(|g| g.as_ref())
- .for_each(|gamepad| {
- gamepad.set_exposed(true);
- gamepad.update_timestamp(*current_time);
- let new_gamepad = Trusted::new(&**gamepad);
- if window.Document().is_fully_active() {
- global.task_manager().gamepad_task_source().queue(
- task!(update_gamepad_connect: move || {
- let gamepad = new_gamepad.root();
- gamepad.notify_event(GamepadEventType::Connected, CanGc::note());
- })
- );
- }
- });
- }
- }
- }
- })
- );
- }
-
pub(crate) fn current_group_label(&self) -> Option<DOMString> {
self.console_group_stack
.borrow()
diff --git a/components/script/dom/gpucanvascontext.rs b/components/script/dom/gpucanvascontext.rs
index 2bdabf3e0ab..f47e1dfddd1 100644
--- a/components/script/dom/gpucanvascontext.rs
+++ b/components/script/dom/gpucanvascontext.rs
@@ -3,6 +3,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use dom_struct::dom_struct;
+use webrender_api::ImageKey;
use crate::dom::bindings::codegen::Bindings::GPUCanvasContextBinding::GPUCanvasContextMethods;
use crate::dom::bindings::codegen::UnionTypes;
diff --git a/components/script/dom/htmliframeelement.rs b/components/script/dom/htmliframeelement.rs
index c5194c4527f..da7a53bbf0b 100644
--- a/components/script/dom/htmliframeelement.rs
+++ b/components/script/dom/htmliframeelement.rs
@@ -162,8 +162,13 @@ impl HTMLIFrameElement {
if load_data.url.scheme() == "javascript" {
let window_proxy = self.GetContentWindow();
if let Some(window_proxy) = window_proxy {
+ if document
+ .global()
+ .should_navigation_request_be_blocked(&load_data)
+ {
+ return;
+ }
// Important re security. See https://github.com/servo/servo/issues/23373
- // TODO: check according to https://w3c.github.io/webappsec-csp/#should-block-navigation-request
if ScriptThread::check_load_origin(&load_data.load_origin, &document.url().origin())
{
ScriptThread::eval_js_url(&window_proxy.global(), &mut load_data, can_gc);
diff --git a/components/script/dom/webxr/xrhittestsource.rs b/components/script/dom/webxr/xrhittestsource.rs
index 0ec9560db6e..f73f8f79655 100644
--- a/components/script/dom/webxr/xrhittestsource.rs
+++ b/components/script/dom/webxr/xrhittestsource.rs
@@ -8,7 +8,7 @@ use webxr_api::HitTestId;
use crate::dom::bindings::codegen::Bindings::XRHitTestSourceBinding::XRHitTestSourceMethods;
use crate::dom::bindings::reflector::{Reflector, reflect_dom_object};
use crate::dom::bindings::root::{Dom, DomRoot};
-use crate::dom::globalscope::GlobalScope;
+use crate::dom::window::Window;
use crate::dom::xrsession::XRSession;
use crate::script_runtime::CanGc;
@@ -31,14 +31,14 @@ impl XRHitTestSource {
}
pub(crate) fn new(
- global: &GlobalScope,
+ window: &Window,
id: HitTestId,
session: &XRSession,
can_gc: CanGc,
) -> DomRoot<XRHitTestSource> {
reflect_dom_object(
Box::new(XRHitTestSource::new_inherited(id, session)),
- global,
+ window,
can_gc,
)
}
diff --git a/components/script/dom/webxr/xrinputsource.rs b/components/script/dom/webxr/xrinputsource.rs
index 009b210646a..e454e785424 100644
--- a/components/script/dom/webxr/xrinputsource.rs
+++ b/components/script/dom/webxr/xrinputsource.rs
@@ -17,6 +17,7 @@ use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
use crate::dom::gamepad::Gamepad;
use crate::dom::globalscope::GlobalScope;
+use crate::dom::window::Window;
use crate::dom::xrhand::XRHand;
use crate::dom::xrsession::XRSession;
use crate::dom::xrspace::XRSpace;
@@ -40,14 +41,14 @@ pub(crate) struct XRInputSource {
impl XRInputSource {
pub(crate) fn new_inherited(
- global: &GlobalScope,
+ window: &Window,
session: &XRSession,
info: InputSource,
can_gc: CanGc,
) -> XRInputSource {
// <https://www.w3.org/TR/webxr-gamepads-module-1/#gamepad-differences>
let gamepad = Gamepad::new(
- global,
+ window,
0,
"".into(),
"xr-standard".into(),
@@ -74,18 +75,18 @@ impl XRInputSource {
#[allow(unsafe_code)]
pub(crate) fn new(
- global: &GlobalScope,
+ window: &Window,
session: &XRSession,
info: InputSource,
can_gc: CanGc,
) -> DomRoot<XRInputSource> {
let source = reflect_dom_object(
- Box::new(XRInputSource::new_inherited(global, session, info, can_gc)),
- global,
+ Box::new(XRInputSource::new_inherited(window, session, info, can_gc)),
+ window,
can_gc,
);
- let _ac = enter_realm(global);
+ let _ac = enter_realm(window);
let cx = GlobalScope::get_cx();
unsafe {
rooted!(in(*cx) let mut profiles = UndefinedValue());
diff --git a/components/script/dom/webxr/xrinputsourcearray.rs b/components/script/dom/webxr/xrinputsourcearray.rs
index d7dcdfcbb6d..26a2c42f598 100644
--- a/components/script/dom/webxr/xrinputsourcearray.rs
+++ b/components/script/dom/webxr/xrinputsourcearray.rs
@@ -11,7 +11,7 @@ use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::event::Event;
-use crate::dom::globalscope::GlobalScope;
+use crate::dom::window::Window;
use crate::dom::xrinputsource::XRInputSource;
use crate::dom::xrinputsourceschangeevent::XRInputSourcesChangeEvent;
use crate::dom::xrsession::XRSession;
@@ -31,10 +31,10 @@ impl XRInputSourceArray {
}
}
- pub(crate) fn new(global: &GlobalScope, can_gc: CanGc) -> DomRoot<XRInputSourceArray> {
+ pub(crate) fn new(window: &Window, can_gc: CanGc) -> DomRoot<XRInputSourceArray> {
reflect_dom_object(
Box::new(XRInputSourceArray::new_inherited()),
- global,
+ window,
can_gc,
)
}
@@ -60,7 +60,7 @@ impl XRInputSourceArray {
.any(|i| i.id() == info.id),
"Should never add a duplicate input id!"
);
- let input = XRInputSource::new(&global, session, info.clone(), can_gc);
+ let input = XRInputSource::new(window, session, info.clone(), can_gc);
self.input_sources.borrow_mut().push(Dom::from_ref(&input));
added.push(input);
}
@@ -121,7 +121,7 @@ impl XRInputSourceArray {
&[]
};
self.input_sources.borrow_mut().retain(|i| i.id() != id);
- let input = XRInputSource::new(&global, session, info, can_gc);
+ let input = XRInputSource::new(window, session, info, can_gc);
self.input_sources.borrow_mut().push(Dom::from_ref(&input));
let added = [input];
diff --git a/components/script/dom/webxr/xrrenderstate.rs b/components/script/dom/webxr/xrrenderstate.rs
index 3f546c2353d..d114020e16e 100644
--- a/components/script/dom/webxr/xrrenderstate.rs
+++ b/components/script/dom/webxr/xrrenderstate.rs
@@ -14,7 +14,7 @@ use crate::dom::bindings::num::Finite;
use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
use crate::dom::bindings::utils::to_frozen_array;
-use crate::dom::globalscope::GlobalScope;
+use crate::dom::window::Window;
use crate::dom::xrlayer::XRLayer;
use crate::dom::xrwebgllayer::XRWebGLLayer;
use crate::script_runtime::{CanGc, JSContext};
@@ -49,7 +49,7 @@ impl XRRenderState {
}
pub(crate) fn new(
- global: &GlobalScope,
+ window: &Window,
depth_near: f64,
depth_far: f64,
inline_vertical_fov: Option<f64>,
@@ -65,14 +65,14 @@ impl XRRenderState {
layer,
layers,
)),
- global,
+ window,
can_gc,
)
}
pub(crate) fn clone_object(&self) -> DomRoot<Self> {
XRRenderState::new(
- &self.global(),
+ self.global().as_window(),
self.depth_near.get(),
self.depth_far.get(),
self.inline_vertical_fov.get(),
diff --git a/components/script/dom/webxr/xrsession.rs b/components/script/dom/webxr/xrsession.rs
index a171a769b71..6ead8f65445 100644
--- a/components/script/dom/webxr/xrsession.rs
+++ b/components/script/dom/webxr/xrsession.rs
@@ -54,8 +54,8 @@ use crate::dom::bindings::root::{Dom, DomRoot, MutDom, MutNullableDom};
use crate::dom::bindings::utils::to_frozen_array;
use crate::dom::event::Event;
use crate::dom::eventtarget::EventTarget;
-use crate::dom::globalscope::GlobalScope;
use crate::dom::promise::Promise;
+use crate::dom::window::Window;
use crate::dom::xrboundedreferencespace::XRBoundedReferenceSpace;
use crate::dom::xrframe::XRFrame;
use crate::dom::xrhittestsource::XRHitTestSource;
@@ -152,7 +152,7 @@ impl XRSession {
}
pub(crate) fn new(
- global: &GlobalScope,
+ window: &Window,
session: Session,
mode: XRSessionMode,
frame_receiver: IpcReceiver<Frame>,
@@ -163,8 +163,8 @@ impl XRSession {
} else {
None
};
- let render_state = XRRenderState::new(global, 0.1, 1000.0, ivfov, None, Vec::new(), can_gc);
- let input_sources = XRInputSourceArray::new(global, can_gc);
+ let render_state = XRRenderState::new(window, 0.1, 1000.0, ivfov, None, Vec::new(), can_gc);
+ let input_sources = XRInputSourceArray::new(window, can_gc);
let ret = reflect_dom_object(
Box::new(XRSession::new_inherited(
session,
@@ -172,7 +172,7 @@ impl XRSession {
&input_sources,
mode,
)),
- global,
+ window,
can_gc,
);
ret.attach_event_handler();
@@ -587,7 +587,7 @@ impl XRSession {
FrameUpdateEvent::HitTestSourceAdded(id) => {
if let Some(promise) = self.pending_hit_test_promises.borrow_mut().remove(&id) {
promise.resolve_native(
- &XRHitTestSource::new(&self.global(), id, self, can_gc),
+ &XRHitTestSource::new(self.global().as_window(), id, self, can_gc),
can_gc,
);
} else {
diff --git a/components/script/dom/webxr/xrsystem.rs b/components/script/dom/webxr/xrsystem.rs
index eabe7a72119..9963d92fa59 100644
--- a/components/script/dom/webxr/xrsystem.rs
+++ b/components/script/dom/webxr/xrsystem.rs
@@ -297,7 +297,13 @@ impl XRSystem {
return;
},
};
- let session = XRSession::new(&self.global(), session, mode, frame_receiver, CanGc::note());
+ let session = XRSession::new(
+ self.global().as_window(),
+ session,
+ mode,
+ frame_receiver,
+ CanGc::note(),
+ );
if mode == XRSessionMode::Inline {
self.active_inline_sessions
.borrow_mut()
diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs
index a685bbb25f2..b115add8611 100644
--- a/components/script/dom/window.rs
+++ b/components/script/dom/window.rs
@@ -32,8 +32,9 @@ use devtools_traits::{ScriptToDevtoolsControlMsg, TimelineMarker, TimelineMarker
use dom_struct::dom_struct;
use embedder_traits::user_content_manager::{UserContentManager, UserScript};
use embedder_traits::{
- AlertResponse, ConfirmResponse, EmbedderMsg, PromptResponse, SimpleDialog, Theme,
- ViewportDetails, WebDriverJSError, WebDriverJSResult,
+ AlertResponse, ConfirmResponse, EmbedderMsg, GamepadEvent, GamepadSupportedHapticEffects,
+ GamepadUpdateType, PromptResponse, SimpleDialog, Theme, ViewportDetails, WebDriverJSError,
+ WebDriverJSResult,
};
use euclid::default::{Point2D as UntypedPoint2D, Rect as UntypedRect};
use euclid::{Point2D, Rect, Scale, Size2D, Vector2D};
@@ -61,6 +62,8 @@ use num_traits::ToPrimitive;
use profile_traits::ipc as ProfiledIpc;
use profile_traits::mem::ProfilerChan as MemProfilerChan;
use profile_traits::time::ProfilerChan as TimeProfilerChan;
+use script_bindings::codegen::GenericBindings::NavigatorBinding::NavigatorMethods;
+use script_bindings::codegen::GenericBindings::PerformanceBinding::PerformanceMethods;
use script_bindings::interfaces::WindowHelpers;
use script_layout_interface::{
FragmentType, Layout, PendingImageState, QueryMsg, Reflow, ReflowGoal, ReflowRequest,
@@ -125,6 +128,8 @@ use crate::dom::document::{AnimationFrameCallback, Document, ReflowTriggerCondit
use crate::dom::element::Element;
use crate::dom::event::{Event, EventBubbles, EventCancelable, EventStatus};
use crate::dom::eventtarget::EventTarget;
+use crate::dom::gamepad::{Gamepad, contains_user_gesture};
+use crate::dom::gamepadevent::GamepadEventType;
use crate::dom::globalscope::GlobalScope;
use crate::dom::hashchangeevent::HashChangeEvent;
use crate::dom::history::History;
@@ -642,6 +647,126 @@ impl Window {
pub(crate) fn font_context(&self) -> &Arc<FontContext> {
&self.font_context
}
+
+ pub(crate) fn handle_gamepad_event(&self, gamepad_event: GamepadEvent) {
+ match gamepad_event {
+ GamepadEvent::Connected(index, name, bounds, supported_haptic_effects) => {
+ self.handle_gamepad_connect(
+ index.0,
+ name,
+ bounds.axis_bounds,
+ bounds.button_bounds,
+ supported_haptic_effects,
+ );
+ },
+ GamepadEvent::Disconnected(index) => {
+ self.handle_gamepad_disconnect(index.0);
+ },
+ GamepadEvent::Updated(index, update_type) => {
+ self.receive_new_gamepad_button_or_axis(index.0, update_type);
+ },
+ };
+ }
+
+ /// <https://www.w3.org/TR/gamepad/#dfn-gamepadconnected>
+ fn handle_gamepad_connect(
+ &self,
+ // As the spec actually defines how to set the gamepad index, the GilRs index
+ // is currently unused, though in practice it will almost always be the same.
+ // More infra is currently needed to track gamepads across windows.
+ _index: usize,
+ name: String,
+ axis_bounds: (f64, f64),
+ button_bounds: (f64, f64),
+ supported_haptic_effects: GamepadSupportedHapticEffects,
+ ) {
+ // TODO: 2. If document is not null and is not allowed to use the "gamepad" permission,
+ // then abort these steps.
+ let this = Trusted::new(self);
+ self.upcast::<GlobalScope>()
+ .task_manager()
+ .gamepad_task_source()
+ .queue(task!(gamepad_connected: move || {
+ let window = this.root();
+
+ let navigator = window.Navigator();
+ let selected_index = navigator.select_gamepad_index();
+ let gamepad = Gamepad::new(
+ &window,
+ selected_index,
+ name,
+ "standard".into(),
+ axis_bounds,
+ button_bounds,
+ supported_haptic_effects,
+ false,
+ CanGc::note(),
+ );
+ navigator.set_gamepad(selected_index as usize, &gamepad, CanGc::note());
+ }));
+ }
+
+ /// <https://www.w3.org/TR/gamepad/#dfn-gamepaddisconnected>
+ fn handle_gamepad_disconnect(&self, index: usize) {
+ let this = Trusted::new(self);
+ self.upcast::<GlobalScope>()
+ .task_manager()
+ .gamepad_task_source()
+ .queue(task!(gamepad_disconnected: move || {
+ let window = this.root();
+ let navigator = window.Navigator();
+ if let Some(gamepad) = navigator.get_gamepad(index) {
+ if window.Document().is_fully_active() {
+ gamepad.update_connected(false, gamepad.exposed(), CanGc::note());
+ navigator.remove_gamepad(index);
+ }
+ }
+ }));
+ }
+
+ /// <https://www.w3.org/TR/gamepad/#receiving-inputs>
+ fn receive_new_gamepad_button_or_axis(&self, index: usize, update_type: GamepadUpdateType) {
+ let this = Trusted::new(self);
+
+ // <https://w3c.github.io/gamepad/#dfn-update-gamepad-state>
+ self.upcast::<GlobalScope>().task_manager().gamepad_task_source().queue(
+ task!(update_gamepad_state: move || {
+ let window = this.root();
+ let navigator = window.Navigator();
+ if let Some(gamepad) = navigator.get_gamepad(index) {
+ let current_time = window.Performance().Now();
+ gamepad.update_timestamp(*current_time);
+ match update_type {
+ GamepadUpdateType::Axis(index, value) => {
+ gamepad.map_and_normalize_axes(index, value);
+ },
+ GamepadUpdateType::Button(index, value) => {
+ gamepad.map_and_normalize_buttons(index, value);
+ }
+ };
+ if !navigator.has_gamepad_gesture() && contains_user_gesture(update_type) {
+ navigator.set_has_gamepad_gesture(true);
+ navigator.GetGamepads()
+ .iter()
+ .filter_map(|g| g.as_ref())
+ .for_each(|gamepad| {
+ gamepad.set_exposed(true);
+ gamepad.update_timestamp(*current_time);
+ let new_gamepad = Trusted::new(&**gamepad);
+ if window.Document().is_fully_active() {
+ window.upcast::<GlobalScope>().task_manager().gamepad_task_source().queue(
+ task!(update_gamepad_connect: move || {
+ let gamepad = new_gamepad.root();
+ gamepad.notify_event(GamepadEventType::Connected, CanGc::note());
+ })
+ );
+ }
+ });
+ }
+ }
+ })
+ );
+ }
}
// https://html.spec.whatwg.org/multipage/#atob
@@ -1246,7 +1371,7 @@ impl WindowMethods<crate::DomTypeHolder> for Window {
let rv = jsval_to_webdriver(cx, &self.globalscope, val, realm, can_gc);
let opt_chan = self.webdriver_script_chan.borrow_mut().take();
if let Some(chan) = opt_chan {
- chan.send(rv).unwrap();
+ let _ = chan.send(rv);
}
}
@@ -1255,9 +1380,9 @@ impl WindowMethods<crate::DomTypeHolder> for Window {
let opt_chan = self.webdriver_script_chan.borrow_mut().take();
if let Some(chan) = opt_chan {
if let Ok(rv) = rv {
- chan.send(Err(WebDriverJSError::JSException(rv))).unwrap();
+ let _ = chan.send(Err(WebDriverJSError::JSException(rv)));
} else {
- chan.send(rv).unwrap();
+ let _ = chan.send(rv);
}
}
}
@@ -1265,7 +1390,7 @@ impl WindowMethods<crate::DomTypeHolder> for Window {
fn WebdriverTimeout(&self) {
let opt_chan = self.webdriver_script_chan.borrow_mut().take();
if let Some(chan) = opt_chan {
- chan.send(Err(WebDriverJSError::Timeout)).unwrap();
+ let _ = chan.send(Err(WebDriverJSError::Timeout));
}
}
diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs
index 2129979ad42..bd4de9d893b 100644
--- a/components/script/script_thread.rs
+++ b/components/script/script_thread.rs
@@ -598,7 +598,7 @@ impl ScriptThread {
with_script_thread(|script_thread| {
let is_javascript = load_data.url.scheme() == "javascript";
// If resource is a request whose url's scheme is "javascript"
- // https://html.spec.whatwg.org/multipage/#javascript-protocol
+ // https://html.spec.whatwg.org/multipage/#navigate-to-a-javascript:-url
if is_javascript {
let window = match script_thread.documents.borrow().find_window(pipeline_id) {
None => return,
@@ -612,8 +612,12 @@ impl ScriptThread {
.clone();
let task = task!(navigate_javascript: move || {
// Important re security. See https://github.com/servo/servo/issues/23373
- // TODO: check according to https://w3c.github.io/webappsec-csp/#should-block-navigation-request
if let Some(window) = trusted_global.root().downcast::<Window>() {
+ // Step 5: If the result of should navigation request of type be blocked by
+ // Content Security Policy? given request and cspNavigationType is "Blocked", then return. [CSP]
+ if trusted_global.root().should_navigation_request_be_blocked(&load_data) {
+ return;
+ }
if ScriptThread::check_load_origin(&load_data.load_origin, &window.get_url().origin()) {
ScriptThread::eval_js_url(&trusted_global.root(), &mut load_data, CanGc::note());
sender
@@ -622,6 +626,7 @@ impl ScriptThread {
}
}
});
+ // Step 19 of <https://html.spec.whatwg.org/multipage/#navigate>
global
.task_manager()
.dom_manipulation_task_source()
@@ -1126,7 +1131,7 @@ impl ScriptThread {
document.dispatch_ime_event(ime_event, can_gc);
},
InputEvent::Gamepad(gamepad_event) => {
- window.as_global_scope().handle_gamepad_event(gamepad_event);
+ window.handle_gamepad_event(gamepad_event);
},
InputEvent::EditingAction(editing_action_event) => {
document.handle_editing_action(editing_action_event, can_gc);
diff --git a/components/servo/Cargo.toml b/components/servo/Cargo.toml
index b49f60e742a..fa64638fa4c 100644
--- a/components/servo/Cargo.toml
+++ b/components/servo/Cargo.toml
@@ -141,3 +141,12 @@ libservo = { path = ".", features = ["tracing"] }
rustls = { version = "0.23", default-features = false, features = ["aws-lc-rs"] }
tracing = { workspace = true }
winit = "0.30.8"
+
+
+[[test]]
+name = "webview"
+harness = false
+
+[[test]]
+name = "servo"
+harness = false
diff --git a/components/servo/tests/common/mod.rs b/components/servo/tests/common/mod.rs
index 8c00826a0d8..de71361e9be 100644
--- a/components/servo/tests/common/mod.rs
+++ b/components/servo/tests/common/mod.rs
@@ -3,18 +3,52 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::rc::Rc;
+use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
-use std::sync::{Arc, OnceLock};
use std::time::Duration;
use anyhow::Error;
use compositing_traits::rendering_context::{RenderingContext, SoftwareRenderingContext};
-use crossbeam_channel::{Receiver, Sender, unbounded};
use dpi::PhysicalSize;
use embedder_traits::EventLoopWaker;
-use parking_lot::Mutex;
use servo::{Servo, ServoBuilder};
+macro_rules! run_api_tests {
+ ($($test_function:ident), +) => {
+ let mut failed = false;
+
+ // Be sure that `servo_test` is dropped before exiting early.
+ {
+ let servo_test = ServoTest::new();
+ $(
+ common::run_test($test_function, stringify!($test_function), &servo_test, &mut failed);
+ )+
+ }
+
+ if failed {
+ std::process::exit(1);
+ }
+ }
+}
+
+pub(crate) use run_api_tests;
+
+pub(crate) fn run_test(
+ test_function: fn(&ServoTest) -> Result<(), Error>,
+ test_name: &str,
+ servo_test: &ServoTest,
+ failed: &mut bool,
+) {
+ match test_function(servo_test) {
+ Ok(_) => println!(" ✅ {test_name}"),
+ Err(error) => {
+ *failed = true;
+ println!(" ❌ {test_name}");
+ println!("{}", format!("\n{error:?}").replace("\n", "\n "));
+ },
+ }
+}
+
pub struct ServoTest {
servo: Servo,
}
@@ -30,7 +64,7 @@ impl Drop for ServoTest {
}
impl ServoTest {
- fn new() -> Self {
+ pub(crate) fn new() -> Self {
let rendering_context = Rc::new(
SoftwareRenderingContext::new(PhysicalSize {
width: 500,
@@ -63,58 +97,28 @@ impl ServoTest {
&self.servo
}
- /// Run a Servo test. All tests are run in a `ServoTestThread` and serially. Currently
- /// Servo does not support launching concurrent instances, in order to ensure
- /// isolation and allow for more than a single test per instance.
- pub fn run(
- test_function: impl FnOnce(&ServoTest) -> Result<(), anyhow::Error> + Send + Sync + 'static,
- ) {
- static SERVO_TEST_THREAD: Mutex<OnceLock<ServoTestThread>> = Mutex::new(OnceLock::new());
- let test_thread = SERVO_TEST_THREAD.lock();
- test_thread
- .get_or_init(ServoTestThread::new)
- .run_test(Box::new(test_function));
- }
-}
-
-type TestFunction =
- Box<dyn FnOnce(&ServoTest) -> Result<(), anyhow::Error> + Send + Sync + 'static>;
-
-struct ServoTestThread {
- test_function_sender: Sender<TestFunction>,
- result_receiver: Receiver<Result<(), Error>>,
-}
-
-impl ServoTestThread {
- fn new() -> Self {
- let (result_sender, result_receiver) = unbounded();
- let (test_function_sender, test_function_receiver) = unbounded();
-
- // Defined here rather than at the end of this method in order to take advantage
- // of Rust type inference.
- let thread = Self {
- test_function_sender,
- result_receiver,
- };
-
- let _ = std::thread::spawn(move || {
- let servo_test = ServoTest::new();
- while let Ok(incoming_test_function) = test_function_receiver.recv() {
- let _ = result_sender.send(incoming_test_function(&servo_test));
+ /// Spin the Servo event loop until one of:
+ /// - The given callback returns `Ok(false)`.
+ /// - The given callback returns an `Error`, in which case the `Error` will be returned.
+ /// - Servo has indicated that shut down is complete and we cannot spin the event loop
+ /// any longer.
+ // The dead code exception here is because not all test suites that use `common` also
+ // use `spin()`.
+ #[allow(dead_code)]
+ pub fn spin(&self, callback: impl Fn() -> Result<bool, Error> + 'static) -> Result<(), Error> {
+ let mut keep_going = true;
+ while keep_going {
+ std::thread::sleep(Duration::from_millis(1));
+ if !self.servo.spin_event_loop() {
+ return Ok(());
+ }
+ let result = callback();
+ match result {
+ Ok(result) => keep_going = result,
+ Err(error) => return Err(error),
}
- });
-
- thread
- }
-
- fn run_test(&self, test_function: TestFunction) {
- let _ = self.test_function_sender.send(Box::new(test_function));
- let result = self
- .result_receiver
- .recv()
- .expect("Servo test thread should always return a result.");
- if let Err(result) = result {
- unreachable!("{result}");
}
+
+ Ok(())
}
}
diff --git a/components/servo/tests/servo.rs b/components/servo/tests/servo.rs
index 2f71e909bee..6333b0af50a 100644
--- a/components/servo/tests/servo.rs
+++ b/components/servo/tests/servo.rs
@@ -4,7 +4,7 @@
//! Servo API unit tests.
//!
-//! Since all Servo tests must rust serially on the same thread, it is important
+//! Since all Servo tests must run serially on the same thread, it is important
//! that tests never panic. In order to ensure this, use `anyhow::ensure!` instead
//! of `assert!` for test assertions. `ensure!` will produce a `Result::Err` in
//! place of panicking.
@@ -12,21 +12,15 @@
mod common;
use anyhow::ensure;
-use common::*;
-use servo::WebViewBuilder;
+use common::{ServoTest, run_api_tests};
-#[test]
-fn test_simple_servo_is_not_animating_by_default() {
- ServoTest::run(|servo_test| {
- ensure!(!servo_test.servo().animating());
- Ok(())
- });
+fn test_simple_servo_is_not_animating_by_default(
+ servo_test: &ServoTest,
+) -> Result<(), anyhow::Error> {
+ ensure!(!servo_test.servo().animating());
+ Ok(())
}
-#[test]
-fn test_simple_servo_construct_webview() {
- ServoTest::run(|servo_test| {
- WebViewBuilder::new(servo_test.servo()).build();
- Ok(())
- });
+fn main() {
+ run_api_tests!(test_simple_servo_is_not_animating_by_default);
}
diff --git a/components/servo/tests/webview.rs b/components/servo/tests/webview.rs
new file mode 100644
index 00000000000..4ed06e412da
--- /dev/null
+++ b/components/servo/tests/webview.rs
@@ -0,0 +1,49 @@
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+//! WebView API unit tests.
+//!
+//! Since all Servo tests must run serially on the same thread, it is important
+//! that tests never panic. In order to ensure this, use `anyhow::ensure!` instead
+//! of `assert!` for test assertions. `ensure!` will produce a `Result::Err` in
+//! place of panicking.
+
+mod common;
+
+use std::cell::Cell;
+use std::rc::Rc;
+
+use anyhow::ensure;
+use common::{ServoTest, run_api_tests};
+use servo::{WebViewBuilder, WebViewDelegate};
+
+#[derive(Default)]
+struct WebViewDelegateImpl {
+ url_changed: Cell<bool>,
+}
+
+impl WebViewDelegate for WebViewDelegateImpl {
+ fn notify_url_changed(&self, _webview: servo::WebView, _url: url::Url) {
+ self.url_changed.set(true);
+ }
+}
+
+fn test_create_webview(servo_test: &ServoTest) -> Result<(), anyhow::Error> {
+ let delegate = Rc::new(WebViewDelegateImpl::default());
+ let webview = WebViewBuilder::new(servo_test.servo())
+ .delegate(delegate.clone())
+ .build();
+
+ servo_test.spin(move || Ok(!delegate.url_changed.get()))?;
+
+ let url = webview.url();
+ ensure!(url.is_some());
+ ensure!(url.unwrap().to_string() == "about:blank");
+
+ Ok(())
+}
+
+fn main() {
+ run_api_tests!(test_create_webview);
+}
diff --git a/components/webdriver_server/actions.rs b/components/webdriver_server/actions.rs
index f69a09a2577..fbede5b5887 100644
--- a/components/webdriver_server/actions.rs
+++ b/components/webdriver_server/actions.rs
@@ -76,7 +76,7 @@ fn compute_tick_duration(tick_actions: &ActionSequence) -> u64 {
}
},
ActionsType::Key { actions: _ } => (),
- ActionsType::Wheel { .. } => todo!("Not implemented."),
+ ActionsType::Wheel { .. } => log::error!("not implemented"),
}
duration
}
@@ -176,7 +176,10 @@ impl Handler {
}
}
},
- ActionsType::Wheel { .. } => todo!("Not implemented."),
+ ActionsType::Wheel { .. } => {
+ log::error!("not yet implemented");
+ return Err(ErrorStatus::UnsupportedOperation);
+ },
}
Ok(())
diff --git a/components/webdriver_server/capabilities.rs b/components/webdriver_server/capabilities.rs
index 477a3bfd34c..32596f5275a 100644
--- a/components/webdriver_server/capabilities.rs
+++ b/components/webdriver_server/capabilities.rs
@@ -4,7 +4,7 @@
use serde_json::{Map, Value};
use webdriver::capabilities::{BrowserCapabilities, Capabilities};
-use webdriver::error::{WebDriverError, WebDriverResult};
+use webdriver::error::{ErrorStatus, WebDriverError, WebDriverResult};
pub struct ServoCapabilities {
pub browser_name: String,
@@ -79,42 +79,42 @@ impl BrowserCapabilities for ServoCapabilities {
&mut self,
_: &serde_json::Map<std::string::String, Value>,
) -> Result<bool, WebDriverError> {
- todo!()
+ Err(WebDriverError::new(ErrorStatus::UnsupportedOperation, ""))
}
fn webauthn_virtual_authenticators(
&mut self,
_: &serde_json::Map<std::string::String, Value>,
) -> Result<bool, WebDriverError> {
- todo!()
+ Err(WebDriverError::new(ErrorStatus::UnsupportedOperation, ""))
}
fn webauthn_extension_uvm(
&mut self,
_: &serde_json::Map<std::string::String, Value>,
) -> Result<bool, WebDriverError> {
- todo!()
+ Err(WebDriverError::new(ErrorStatus::UnsupportedOperation, ""))
}
fn webauthn_extension_prf(
&mut self,
_: &serde_json::Map<std::string::String, Value>,
) -> Result<bool, WebDriverError> {
- todo!()
+ Err(WebDriverError::new(ErrorStatus::UnsupportedOperation, ""))
}
fn webauthn_extension_large_blob(
&mut self,
_: &serde_json::Map<std::string::String, Value>,
) -> Result<bool, WebDriverError> {
- todo!()
+ Err(WebDriverError::new(ErrorStatus::UnsupportedOperation, ""))
}
fn webauthn_extension_cred_blob(
&mut self,
_: &serde_json::Map<std::string::String, Value>,
) -> Result<bool, WebDriverError> {
- todo!()
+ Err(WebDriverError::new(ErrorStatus::UnsupportedOperation, ""))
}
}
diff --git a/components/webdriver_server/lib.rs b/components/webdriver_server/lib.rs
index ce83a8f3cc1..d003ebf8adb 100644
--- a/components/webdriver_server/lib.rs
+++ b/components/webdriver_server/lib.rs
@@ -29,7 +29,7 @@ use embedder_traits::{
use euclid::{Rect, Size2D};
use http::method::Method;
use image::{DynamicImage, ImageFormat, RgbaImage};
-use ipc_channel::ipc::{self, IpcSender};
+use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
use ipc_channel::router::ROUTER;
use keyboard_types::webdriver::send_keys;
use log::{debug, info};
@@ -678,7 +678,7 @@ impl Handler {
let (sender, receiver) = ipc::channel().unwrap();
self.top_level_script_command(WebDriverScriptCommand::GetUrl(sender))?;
- let url = receiver.recv().unwrap();
+ let url = wait_for_script_response(receiver)?;
Ok(WebDriverResponse::Generic(ValueResponse(
serde_json::to_value(url.as_str())?,
@@ -694,7 +694,7 @@ impl Handler {
.send(EmbedderToConstellationMessage::WebDriverCommand(cmd_msg))
.unwrap();
- let window_size = receiver.recv().unwrap();
+ let window_size = wait_for_script_response(receiver)?;
let window_size_response = WindowRectResponse {
x: 0,
y: 0,
@@ -738,7 +738,7 @@ impl Handler {
.unwrap();
});
- let window_size = receiver.recv().unwrap();
+ let window_size = wait_for_script_response(receiver)?;
let window_size_response = WindowRectResponse {
x: 0,
y: 0,
@@ -756,7 +756,7 @@ impl Handler {
sender,
))?;
- match receiver.recv().unwrap() {
+ match wait_for_script_response(receiver)? {
Ok(is_enabled) => Ok(WebDriverResponse::Generic(ValueResponse(
serde_json::to_value(is_enabled)?,
))),
@@ -772,7 +772,7 @@ impl Handler {
sender,
))?;
- match receiver.recv().unwrap() {
+ match wait_for_script_response(receiver)? {
Ok(is_selected) => Ok(WebDriverResponse::Generic(ValueResponse(
serde_json::to_value(is_selected)?,
))),
@@ -812,7 +812,7 @@ impl Handler {
self.top_level_script_command(WebDriverScriptCommand::GetTitle(sender))?;
- let value = receiver.recv().unwrap();
+ let value = wait_for_script_response(receiver)?;
Ok(WebDriverResponse::Generic(ValueResponse(
serde_json::to_value(value)?,
)))
@@ -874,7 +874,7 @@ impl Handler {
},
}
- match receiver.recv().unwrap() {
+ match wait_for_script_response(receiver)? {
Ok(value) => {
let value_resp = serde_json::to_value(
value.map(|x| serde_json::to_value(WebElement(x)).unwrap()),
@@ -1005,7 +1005,7 @@ impl Handler {
let cmd = WebDriverScriptCommand::GetBrowsingContextId(frame_id, sender);
self.browsing_context_script_command(cmd)?;
- match receiver.recv().unwrap() {
+ match wait_for_script_response(receiver)? {
Ok(browsing_context_id) => {
self.session_mut()?.browsing_context_id = browsing_context_id;
Ok(WebDriverResponse::Void)
@@ -1047,7 +1047,7 @@ impl Handler {
},
}
- match receiver.recv().unwrap() {
+ match wait_for_script_response(receiver)? {
Ok(value) => {
let resp_value: Vec<Value> = value
.into_iter()
@@ -1103,7 +1103,7 @@ impl Handler {
},
}
- match receiver.recv().unwrap() {
+ match wait_for_script_response(receiver)? {
Ok(value) => {
let value_resp = serde_json::to_value(
value.map(|x| serde_json::to_value(WebElement(x)).unwrap()),
@@ -1156,7 +1156,7 @@ impl Handler {
},
}
- match receiver.recv().unwrap() {
+ match wait_for_script_response(receiver)? {
Ok(value) => {
let resp_value: Vec<Value> = value
.into_iter()
@@ -1175,7 +1175,7 @@ impl Handler {
let (sender, receiver) = ipc::channel().unwrap();
let cmd = WebDriverScriptCommand::GetElementRect(element.to_string(), sender);
self.browsing_context_script_command(cmd)?;
- match receiver.recv().unwrap() {
+ match wait_for_script_response(receiver)? {
Ok(rect) => {
let response = ElementRectResponse {
x: rect.origin.x,
@@ -1193,7 +1193,7 @@ impl Handler {
let (sender, receiver) = ipc::channel().unwrap();
let cmd = WebDriverScriptCommand::GetElementText(element.to_string(), sender);
self.browsing_context_script_command(cmd)?;
- match receiver.recv().unwrap() {
+ match wait_for_script_response(receiver)? {
Ok(value) => Ok(WebDriverResponse::Generic(ValueResponse(
serde_json::to_value(value)?,
))),
@@ -1205,9 +1205,7 @@ impl Handler {
let (sender, receiver) = ipc::channel().unwrap();
let cmd = WebDriverScriptCommand::GetActiveElement(sender);
self.browsing_context_script_command(cmd)?;
- let value = receiver
- .recv()
- .unwrap()
+ let value = wait_for_script_response(receiver)?
.map(|x| serde_json::to_value(WebElement(x)).unwrap());
Ok(WebDriverResponse::Generic(ValueResponse(
serde_json::to_value(value)?,
@@ -1218,7 +1216,7 @@ impl Handler {
let (sender, receiver) = ipc::channel().unwrap();
let cmd = WebDriverScriptCommand::GetComputedRole(element.to_string(), sender);
self.browsing_context_script_command(cmd)?;
- match receiver.recv().unwrap() {
+ match wait_for_script_response(receiver)? {
Ok(value) => Ok(WebDriverResponse::Generic(ValueResponse(
serde_json::to_value(value)?,
))),
@@ -1230,7 +1228,7 @@ impl Handler {
let (sender, receiver) = ipc::channel().unwrap();
let cmd = WebDriverScriptCommand::GetElementTagName(element.to_string(), sender);
self.browsing_context_script_command(cmd)?;
- match receiver.recv().unwrap() {
+ match wait_for_script_response(receiver)? {
Ok(value) => Ok(WebDriverResponse::Generic(ValueResponse(
serde_json::to_value(value)?,
))),
@@ -1250,7 +1248,7 @@ impl Handler {
sender,
);
self.browsing_context_script_command(cmd)?;
- match receiver.recv().unwrap() {
+ match wait_for_script_response(receiver)? {
Ok(value) => Ok(WebDriverResponse::Generic(ValueResponse(
serde_json::to_value(value)?,
))),
@@ -1272,7 +1270,7 @@ impl Handler {
);
self.browsing_context_script_command(cmd)?;
- match receiver.recv().unwrap() {
+ match wait_for_script_response(receiver)? {
Ok(value) => Ok(WebDriverResponse::Generic(ValueResponse(
serde_json::to_value(SendableWebDriverJSValue(value))?,
))),
@@ -1289,7 +1287,7 @@ impl Handler {
let cmd =
WebDriverScriptCommand::GetElementCSS(element.to_string(), name.to_owned(), sender);
self.browsing_context_script_command(cmd)?;
- match receiver.recv().unwrap() {
+ match wait_for_script_response(receiver)? {
Ok(value) => Ok(WebDriverResponse::Generic(ValueResponse(
serde_json::to_value(value)?,
))),
@@ -1301,7 +1299,7 @@ impl Handler {
let (sender, receiver) = ipc::channel().unwrap();
let cmd = WebDriverScriptCommand::GetCookies(sender);
self.browsing_context_script_command(cmd)?;
- let cookies = receiver.recv().unwrap();
+ let cookies = wait_for_script_response(receiver)?;
let response = cookies
.into_iter()
.map(|cookie| cookie_msg_to_cookie(cookie.into_inner()))
@@ -1313,12 +1311,14 @@ impl Handler {
let (sender, receiver) = ipc::channel().unwrap();
let cmd = WebDriverScriptCommand::GetCookie(name, sender);
self.browsing_context_script_command(cmd)?;
- let cookies = receiver.recv().unwrap();
- let response = cookies
+ let cookies = wait_for_script_response(receiver)?;
+ let Some(response) = cookies
.into_iter()
.map(|cookie| cookie_msg_to_cookie(cookie.into_inner()))
.next()
- .unwrap();
+ else {
+ return Err(WebDriverError::new(ErrorStatus::NoSuchCookie, ""));
+ };
Ok(WebDriverResponse::Cookie(CookieResponse(response)))
}
@@ -1342,7 +1342,7 @@ impl Handler {
let cmd = WebDriverScriptCommand::AddCookie(cookie_builder.build(), sender);
self.browsing_context_script_command(cmd)?;
- match receiver.recv().unwrap() {
+ match wait_for_script_response(receiver)? {
Ok(_) => Ok(WebDriverResponse::Void),
Err(response) => match response {
WebDriverCookieError::InvalidDomain => Err(WebDriverError::new(
@@ -1361,7 +1361,7 @@ impl Handler {
let (sender, receiver) = ipc::channel().unwrap();
let cmd = WebDriverScriptCommand::DeleteCookie(name, sender);
self.browsing_context_script_command(cmd)?;
- match receiver.recv().unwrap() {
+ match wait_for_script_response(receiver)? {
Ok(_) => Ok(WebDriverResponse::Void),
Err(error) => Err(WebDriverError::new(error, "")),
}
@@ -1371,7 +1371,7 @@ impl Handler {
let (sender, receiver) = ipc::channel().unwrap();
let cmd = WebDriverScriptCommand::DeleteCookies(sender);
self.browsing_context_script_command(cmd)?;
- match receiver.recv().unwrap() {
+ match wait_for_script_response(receiver)? {
Ok(_) => Ok(WebDriverResponse::Void),
Err(error) => Err(WebDriverError::new(error, "")),
}
@@ -1426,7 +1426,7 @@ impl Handler {
let cmd = WebDriverScriptCommand::GetPageSource(sender);
self.browsing_context_script_command(cmd)?;
- match receiver.recv().unwrap() {
+ match wait_for_script_response(receiver)? {
Ok(source) => Ok(WebDriverResponse::Generic(ValueResponse(
serde_json::to_value(source)?,
))),
@@ -1487,9 +1487,7 @@ impl Handler {
let (sender, receiver) = ipc::channel().unwrap();
let command = WebDriverScriptCommand::ExecuteScript(script, sender);
self.browsing_context_script_command(command)?;
- let result = receiver
- .recv()
- .unwrap_or(Err(WebDriverJSError::BrowsingContextNotFound));
+ let result = wait_for_script_response(receiver)?;
self.postprocess_js_result(result)
}
@@ -1533,9 +1531,7 @@ impl Handler {
let (sender, receiver) = ipc::channel().unwrap();
let command = WebDriverScriptCommand::ExecuteAsyncScript(script, sender);
self.browsing_context_script_command(command)?;
- let result = receiver
- .recv()
- .unwrap_or(Err(WebDriverJSError::BrowsingContextNotFound));
+ let result = wait_for_script_response(receiver)?;
self.postprocess_js_result(result)
}
@@ -1589,10 +1585,7 @@ impl Handler {
.unwrap();
// TODO: distinguish the not found and not focusable cases
- receiver
- .recv()
- .unwrap()
- .map_err(|error| WebDriverError::new(error, ""))?;
+ wait_for_script_response(receiver)?.map_err(|error| WebDriverError::new(error, ""))?;
let input_events = send_keys(&keys.text);
@@ -1615,7 +1608,7 @@ impl Handler {
let command = WebDriverScriptCommand::ElementClick(element.to_string(), sender);
self.browsing_context_script_command(command)?;
- match receiver.recv().unwrap() {
+ match wait_for_script_response(receiver)? {
Ok(element_id) => match element_id {
Some(element_id) => {
let id = Uuid::new_v4().to_string();
@@ -1688,7 +1681,7 @@ impl Handler {
.send(EmbedderToConstellationMessage::WebDriverCommand(cmd_msg))
.unwrap();
- if let Some(x) = receiver.recv().unwrap() {
+ if let Some(x) = wait_for_script_response(receiver)? {
img = Some(x);
break;
};
@@ -1739,7 +1732,7 @@ impl Handler {
let command = WebDriverScriptCommand::GetBoundingClientRect(element.to_string(), sender);
self.browsing_context_script_command(command)?;
- match receiver.recv().unwrap() {
+ match wait_for_script_response(receiver)? {
Ok(rect) => {
let encoded = self.take_screenshot(Some(Rect::from_untyped(&rect)))?;
@@ -1944,3 +1937,12 @@ fn webdriver_value_to_js_argument(v: &Value) -> String {
},
}
}
+
+fn wait_for_script_response<T>(receiver: IpcReceiver<T>) -> Result<T, WebDriverError>
+where
+ T: for<'de> Deserialize<'de> + Serialize,
+{
+ receiver
+ .recv()
+ .map_err(|_| WebDriverError::new(ErrorStatus::NoSuchWindow, ""))
+}