aboutsummaryrefslogtreecommitdiffstats
path: root/components
diff options
context:
space:
mode:
Diffstat (limited to 'components')
-rw-r--r--components/config/prefs.rs2
-rw-r--r--components/layout/display_list/background.rs4
-rw-r--r--components/layout/display_list/clip.rs276
-rw-r--r--components/layout/display_list/clip_path.rs259
-rw-r--r--components/layout/display_list/mod.rs392
-rw-r--r--components/layout/display_list/stacking_context.rs441
-rw-r--r--components/layout/dom.rs14
-rw-r--r--components/layout/flexbox/mod.rs6
-rw-r--r--components/layout/flow/inline/construct.rs2
-rw-r--r--components/layout/flow/inline/mod.rs23
-rw-r--r--components/layout/flow/mod.rs22
-rw-r--r--components/layout/formatting_contexts.rs16
-rw-r--r--components/layout/fragment_tree/box_fragment.rs2
-rw-r--r--components/layout/fragment_tree/fragment_tree.rs11
-rw-r--r--components/layout/layout_impl.rs125
-rw-r--r--components/layout/table/mod.rs5
-rw-r--r--components/layout/taffy/mod.rs6
-rw-r--r--components/layout/traversal.rs5
-rw-r--r--components/net/Cargo.toml2
-rw-r--r--components/net/fetch/methods.rs29
-rw-r--r--components/net/hsts.rs106
-rw-r--r--components/net/resource_thread.rs24
-rw-r--r--components/net/storage_thread.rs42
-rw-r--r--components/net/tests/hsts.rs161
-rw-r--r--components/script/dom/bindings/structuredclone.rs8
-rw-r--r--components/script/dom/blob.rs71
-rw-r--r--components/script/dom/headers.rs73
-rw-r--r--components/script/dom/readablestream.rs46
-rw-r--r--components/script/dom/transformstream.rs104
-rw-r--r--components/script/dom/xmlhttprequest.rs155
-rw-r--r--components/script/stylesheet_loader.rs14
-rw-r--r--components/script_bindings/codegen/Bindings.conf6
-rw-r--r--components/script_bindings/webidls/Element.webidl2
-rw-r--r--components/script_bindings/webidls/ReadableStream.webidl4
-rw-r--r--components/shared/base/id.rs5
-rw-r--r--components/shared/compositing/display_list.rs154
-rw-r--r--components/shared/compositing/lib.rs2
-rw-r--r--components/shared/compositing/tests/compositor.rs43
-rw-r--r--components/shared/constellation/lib.rs2
-rw-r--r--components/shared/constellation/structured_data/mod.rs4
-rw-r--r--components/shared/constellation/structured_data/serializable.rs6
-rw-r--r--components/shared/constellation/structured_data/transferable.rs10
-rw-r--r--components/shared/embedder/resources.rs4
-rw-r--r--components/shared/net/Cargo.toml1
-rw-r--r--components/shared/net/fetch/headers.rs93
-rw-r--r--components/shared/net/pub_domains.rs8
-rw-r--r--components/shared/net/response.rs10
-rw-r--r--components/shared/net/storage_thread.rs4
-rw-r--r--components/webdriver_server/actions.rs185
49 files changed, 1668 insertions, 1321 deletions
diff --git a/components/config/prefs.rs b/components/config/prefs.rs
index a9ec112e3eb..64dd9659e56 100644
--- a/components/config/prefs.rs
+++ b/components/config/prefs.rs
@@ -99,7 +99,6 @@ pub struct Preferences {
pub dom_serviceworker_timeout_seconds: i64,
pub dom_servo_helpers_enabled: bool,
pub dom_servoparser_async_html_tokenizer_enabled: bool,
- pub dom_shadowdom_enabled: bool,
pub dom_svg_enabled: bool,
pub dom_testable_crash_enabled: bool,
pub dom_testbinding_enabled: bool,
@@ -277,7 +276,6 @@ impl Preferences {
dom_serviceworker_timeout_seconds: 60,
dom_servo_helpers_enabled: false,
dom_servoparser_async_html_tokenizer_enabled: false,
- dom_shadowdom_enabled: true,
dom_svg_enabled: false,
dom_testable_crash_enabled: false,
dom_testbinding_enabled: false,
diff --git a/components/layout/display_list/background.rs b/components/layout/display_list/background.rs
index f49ddfbe6ce..563bce28450 100644
--- a/components/layout/display_list/background.rs
+++ b/components/layout/display_list/background.rs
@@ -66,7 +66,7 @@ impl<'a> BackgroundPainter<'a> {
if &BackgroundAttachment::Fixed ==
get_cyclic(&background.background_attachment.0, layer_index)
{
- let viewport_size = builder.display_list.compositor_info.viewport_size;
+ let viewport_size = builder.compositor_info.viewport_size;
return units::LayoutRect::from_origin_and_size(Point2D::origin(), viewport_size);
}
@@ -121,7 +121,7 @@ impl<'a> BackgroundPainter<'a> {
if &BackgroundAttachment::Fixed ==
get_cyclic(&style.get_background().background_attachment.0, layer_index)
{
- common.spatial_id = builder.current_reference_frame_scroll_node_id.spatial_id;
+ common.spatial_id = builder.spatial_id(builder.current_reference_frame_scroll_node_id);
}
common
}
diff --git a/components/layout/display_list/clip.rs b/components/layout/display_list/clip.rs
new file mode 100644
index 00000000000..d5bd0f52b69
--- /dev/null
+++ b/components/layout/display_list/clip.rs
@@ -0,0 +1,276 @@
+/* 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/. */
+
+use app_units::Au;
+use base::id::ScrollTreeNodeId;
+use style::values::computed::basic_shape::{BasicShape, ClipPath};
+use style::values::computed::length_percentage::NonNegativeLengthPercentage;
+use style::values::computed::position::Position;
+use style::values::generics::basic_shape::{GenericShapeRadius, ShapeBox, ShapeGeometryBox};
+use style::values::generics::position::GenericPositionOrAuto;
+use webrender_api::BorderRadius;
+use webrender_api::units::{LayoutRect, LayoutSideOffsets, LayoutSize};
+
+use super::{BuilderForBoxFragment, compute_margin_box_radius, normalize_radii};
+
+/// An identifier for a clip used during StackingContextTree construction. This is a simple index in
+/// a [`ClipStore`]s vector of clips.
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub(crate) struct ClipId(pub usize);
+
+impl ClipId {
+ /// Equivalent to [`ClipChainId::INVALID`]. This means "no clip."
+ pub(crate) const INVALID: ClipId = ClipId(usize::MAX);
+}
+
+/// All the information needed to create a clip on a WebRender display list. These are created at
+/// two times: during `StackingContextTree` creation and during WebRender display list construction.
+/// Only the former are stored in a [`ClipStore`].
+#[derive(Clone)]
+pub(crate) struct Clip {
+ pub id: ClipId,
+ pub radii: BorderRadius,
+ pub rect: LayoutRect,
+ pub parent_scroll_node_id: ScrollTreeNodeId,
+ pub parent_clip_id: ClipId,
+}
+
+/// A simple vector of [`Clip`] that is built during `StackingContextTree` construction.
+/// These are later turned into WebRender clips and clip chains during WebRender display
+/// list construction.
+#[derive(Clone, Default)]
+pub(crate) struct StackingContextTreeClipStore(pub Vec<Clip>);
+
+impl StackingContextTreeClipStore {
+ pub(crate) fn add(
+ &mut self,
+ radii: webrender_api::BorderRadius,
+ rect: LayoutRect,
+ parent_scroll_node_id: ScrollTreeNodeId,
+ parent_clip_id: ClipId,
+ ) -> ClipId {
+ let id = ClipId(self.0.len());
+ self.0.push(Clip {
+ id,
+ radii,
+ rect,
+ parent_scroll_node_id,
+ parent_clip_id,
+ });
+ id
+ }
+
+ pub(super) fn add_for_clip_path(
+ &mut self,
+ clip_path: ClipPath,
+ parent_scroll_node_id: &ScrollTreeNodeId,
+ parent_clip_chain_id: &ClipId,
+ fragment_builder: BuilderForBoxFragment,
+ ) -> Option<ClipId> {
+ let geometry_box = match clip_path {
+ ClipPath::Shape(_, ShapeGeometryBox::ShapeBox(shape_box)) => shape_box,
+ ClipPath::Shape(_, ShapeGeometryBox::ElementDependent) => ShapeBox::BorderBox,
+ ClipPath::Box(ShapeGeometryBox::ShapeBox(shape_box)) => shape_box,
+ ClipPath::Box(ShapeGeometryBox::ElementDependent) => ShapeBox::BorderBox,
+ _ => return None,
+ };
+ let layout_rect = match geometry_box {
+ ShapeBox::BorderBox => fragment_builder.border_rect,
+ ShapeBox::ContentBox => *fragment_builder.content_rect(),
+ ShapeBox::PaddingBox => *fragment_builder.padding_rect(),
+ ShapeBox::MarginBox => *fragment_builder.margin_rect(),
+ };
+ if let ClipPath::Shape(shape, _) = clip_path {
+ match *shape {
+ BasicShape::Circle(_) | BasicShape::Ellipse(_) | BasicShape::Rect(_) => self
+ .add_for_basic_shape(
+ *shape,
+ layout_rect,
+ parent_scroll_node_id,
+ parent_clip_chain_id,
+ ),
+ BasicShape::Polygon(_) | BasicShape::PathOrShape(_) => None,
+ }
+ } else {
+ Some(self.add(
+ match geometry_box {
+ ShapeBox::MarginBox => compute_margin_box_radius(
+ fragment_builder.border_radius,
+ layout_rect.size(),
+ fragment_builder.fragment,
+ ),
+ _ => fragment_builder.border_radius,
+ },
+ layout_rect,
+ *parent_scroll_node_id,
+ *parent_clip_chain_id,
+ ))
+ }
+ }
+
+ #[cfg_attr(
+ feature = "tracing",
+ tracing::instrument(
+ name = "StackingContextClipStore::add_for_basic_shape",
+ skip_all,
+ fields(servo_profiling = true),
+ level = "trace",
+ )
+ )]
+ fn add_for_basic_shape(
+ &mut self,
+ shape: BasicShape,
+ layout_box: LayoutRect,
+ parent_scroll_node_id: &ScrollTreeNodeId,
+ parent_clip_chain_id: &ClipId,
+ ) -> Option<ClipId> {
+ match shape {
+ BasicShape::Rect(rect) => {
+ let box_height = Au::from_f32_px(layout_box.height());
+ let box_width = Au::from_f32_px(layout_box.width());
+ let insets = LayoutSideOffsets::new(
+ rect.rect.0.to_used_value(box_height).to_f32_px(),
+ rect.rect.1.to_used_value(box_width).to_f32_px(),
+ rect.rect.2.to_used_value(box_height).to_f32_px(),
+ rect.rect.3.to_used_value(box_width).to_f32_px(),
+ );
+
+ // `inner_rect()` will cause an assertion failure if the insets are larger than the
+ // rectangle dimension.
+ let shape_rect = if insets.left + insets.right >= layout_box.width() ||
+ insets.top + insets.bottom > layout_box.height()
+ {
+ LayoutRect::from_origin_and_size(layout_box.min, LayoutSize::zero())
+ } else {
+ layout_box.to_rect().inner_rect(insets).to_box2d()
+ };
+
+ let corner = |corner: &style::values::computed::BorderCornerRadius| {
+ LayoutSize::new(
+ corner.0.width.0.to_used_value(box_width).to_f32_px(),
+ corner.0.height.0.to_used_value(box_height).to_f32_px(),
+ )
+ };
+ let mut radii = webrender_api::BorderRadius {
+ top_left: corner(&rect.round.top_left),
+ top_right: corner(&rect.round.top_right),
+ bottom_left: corner(&rect.round.bottom_left),
+ bottom_right: corner(&rect.round.bottom_right),
+ };
+ normalize_radii(&layout_box, &mut radii);
+ Some(self.add(
+ radii,
+ shape_rect,
+ *parent_scroll_node_id,
+ *parent_clip_chain_id,
+ ))
+ },
+ BasicShape::Circle(circle) => {
+ let center = match circle.position {
+ GenericPositionOrAuto::Position(position) => position,
+ GenericPositionOrAuto::Auto => Position::center(),
+ };
+ let anchor_x = center
+ .horizontal
+ .to_used_value(Au::from_f32_px(layout_box.width()));
+ let anchor_y = center
+ .vertical
+ .to_used_value(Au::from_f32_px(layout_box.height()));
+ let center = layout_box
+ .min
+ .add_size(&LayoutSize::new(anchor_x.to_f32_px(), anchor_y.to_f32_px()));
+
+ let horizontal = compute_shape_radius(
+ center.x,
+ &circle.radius,
+ layout_box.min.x,
+ layout_box.max.x,
+ );
+ let vertical = compute_shape_radius(
+ center.y,
+ &circle.radius,
+ layout_box.min.y,
+ layout_box.max.y,
+ );
+
+ // If the value is `Length` then both values should be the same at this point.
+ let radius = match circle.radius {
+ GenericShapeRadius::FarthestSide => horizontal.max(vertical),
+ GenericShapeRadius::ClosestSide => horizontal.min(vertical),
+ GenericShapeRadius::Length(_) => horizontal,
+ };
+ let radius = LayoutSize::new(radius, radius);
+ let mut radii = webrender_api::BorderRadius {
+ top_left: radius,
+ top_right: radius,
+ bottom_left: radius,
+ bottom_right: radius,
+ };
+ let start = center.add_size(&-radius);
+ let rect = LayoutRect::from_origin_and_size(start, radius * 2.);
+ normalize_radii(&layout_box, &mut radii);
+ Some(self.add(radii, rect, *parent_scroll_node_id, *parent_clip_chain_id))
+ },
+ BasicShape::Ellipse(ellipse) => {
+ let center = match ellipse.position {
+ GenericPositionOrAuto::Position(position) => position,
+ GenericPositionOrAuto::Auto => Position::center(),
+ };
+ let anchor_x = center
+ .horizontal
+ .to_used_value(Au::from_f32_px(layout_box.width()));
+ let anchor_y = center
+ .vertical
+ .to_used_value(Au::from_f32_px(layout_box.height()));
+ let center = layout_box
+ .min
+ .add_size(&LayoutSize::new(anchor_x.to_f32_px(), anchor_y.to_f32_px()));
+
+ let width = compute_shape_radius(
+ center.x,
+ &ellipse.semiaxis_x,
+ layout_box.min.x,
+ layout_box.max.x,
+ );
+ let height = compute_shape_radius(
+ center.y,
+ &ellipse.semiaxis_y,
+ layout_box.min.y,
+ layout_box.max.y,
+ );
+
+ let mut radii = webrender_api::BorderRadius {
+ top_left: LayoutSize::new(width, height),
+ top_right: LayoutSize::new(width, height),
+ bottom_left: LayoutSize::new(width, height),
+ bottom_right: LayoutSize::new(width, height),
+ };
+ let size = LayoutSize::new(width, height);
+ let start = center.add_size(&-size);
+ let rect = LayoutRect::from_origin_and_size(start, size * 2.);
+ normalize_radii(&rect, &mut radii);
+ Some(self.add(radii, rect, *parent_scroll_node_id, *parent_clip_chain_id))
+ },
+ _ => None,
+ }
+ }
+}
+
+fn compute_shape_radius(
+ center: f32,
+ radius: &GenericShapeRadius<NonNegativeLengthPercentage>,
+ min_edge: f32,
+ max_edge: f32,
+) -> f32 {
+ let distance_from_min_edge = (min_edge - center).abs();
+ let distance_from_max_edge = (max_edge - center).abs();
+ match radius {
+ GenericShapeRadius::FarthestSide => distance_from_min_edge.max(distance_from_max_edge),
+ GenericShapeRadius::ClosestSide => distance_from_min_edge.min(distance_from_max_edge),
+ GenericShapeRadius::Length(length) => length
+ .0
+ .to_used_value(Au::from_f32_px(max_edge - min_edge))
+ .to_f32_px(),
+ }
+}
diff --git a/components/layout/display_list/clip_path.rs b/components/layout/display_list/clip_path.rs
deleted file mode 100644
index 419d15c3572..00000000000
--- a/components/layout/display_list/clip_path.rs
+++ /dev/null
@@ -1,259 +0,0 @@
-/* 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/. */
-
-use app_units::Au;
-use base::id::ScrollTreeNodeId;
-use style::values::computed::basic_shape::{BasicShape, ClipPath};
-use style::values::computed::length_percentage::NonNegativeLengthPercentage;
-use style::values::computed::position::Position;
-use style::values::generics::basic_shape::{GenericShapeRadius, ShapeBox, ShapeGeometryBox};
-use style::values::generics::position::GenericPositionOrAuto;
-use webrender_api::ClipChainId;
-use webrender_api::units::{LayoutRect, LayoutSideOffsets, LayoutSize};
-
-use super::{BuilderForBoxFragment, DisplayList, compute_margin_box_radius, normalize_radii};
-
-pub(super) fn build_clip_path_clip_chain_if_necessary(
- clip_path: ClipPath,
- display_list: &mut DisplayList,
- parent_scroll_node_id: &ScrollTreeNodeId,
- parent_clip_chain_id: &ClipChainId,
- fragment_builder: BuilderForBoxFragment,
-) -> Option<ClipChainId> {
- let geometry_box = match clip_path {
- ClipPath::Shape(_, ShapeGeometryBox::ShapeBox(shape_box)) => shape_box,
- ClipPath::Shape(_, ShapeGeometryBox::ElementDependent) => ShapeBox::BorderBox,
- ClipPath::Box(ShapeGeometryBox::ShapeBox(shape_box)) => shape_box,
- ClipPath::Box(ShapeGeometryBox::ElementDependent) => ShapeBox::BorderBox,
- _ => return None,
- };
- let layout_rect = match geometry_box {
- ShapeBox::BorderBox => fragment_builder.border_rect,
- ShapeBox::ContentBox => *fragment_builder.content_rect(),
- ShapeBox::PaddingBox => *fragment_builder.padding_rect(),
- ShapeBox::MarginBox => *fragment_builder.margin_rect(),
- };
- if let ClipPath::Shape(shape, _) = clip_path {
- match *shape {
- BasicShape::Circle(_) | BasicShape::Ellipse(_) | BasicShape::Rect(_) => {
- build_simple_shape(
- *shape,
- layout_rect,
- parent_scroll_node_id,
- parent_clip_chain_id,
- display_list,
- )
- },
- BasicShape::Polygon(_) | BasicShape::PathOrShape(_) => None,
- }
- } else {
- Some(create_rect_clip_chain(
- match geometry_box {
- ShapeBox::MarginBox => compute_margin_box_radius(
- fragment_builder.border_radius,
- layout_rect.size(),
- fragment_builder.fragment,
- ),
- _ => fragment_builder.border_radius,
- },
- layout_rect,
- parent_scroll_node_id,
- parent_clip_chain_id,
- display_list,
- ))
- }
-}
-
-#[cfg_attr(
- feature = "tracing",
- tracing::instrument(
- name = "display_list::build_simple_shape",
- skip_all,
- fields(servo_profiling = true),
- level = "trace",
- )
-)]
-fn build_simple_shape(
- shape: BasicShape,
- layout_box: LayoutRect,
- parent_scroll_node_id: &ScrollTreeNodeId,
- parent_clip_chain_id: &ClipChainId,
- display_list: &mut DisplayList,
-) -> Option<ClipChainId> {
- match shape {
- BasicShape::Rect(rect) => {
- let box_height = Au::from_f32_px(layout_box.height());
- let box_width = Au::from_f32_px(layout_box.width());
- let insets = LayoutSideOffsets::new(
- rect.rect.0.to_used_value(box_height).to_f32_px(),
- rect.rect.1.to_used_value(box_width).to_f32_px(),
- rect.rect.2.to_used_value(box_height).to_f32_px(),
- rect.rect.3.to_used_value(box_width).to_f32_px(),
- );
-
- // `inner_rect()` will cause an assertion failure if the insets are larger than the
- // rectangle dimension.
- let shape_rect = if insets.left + insets.right >= layout_box.width() ||
- insets.top + insets.bottom > layout_box.height()
- {
- LayoutRect::from_origin_and_size(layout_box.min, LayoutSize::zero())
- } else {
- layout_box.to_rect().inner_rect(insets).to_box2d()
- };
-
- let corner = |corner: &style::values::computed::BorderCornerRadius| {
- LayoutSize::new(
- corner.0.width.0.to_used_value(box_width).to_f32_px(),
- corner.0.height.0.to_used_value(box_height).to_f32_px(),
- )
- };
- let mut radii = webrender_api::BorderRadius {
- top_left: corner(&rect.round.top_left),
- top_right: corner(&rect.round.top_right),
- bottom_left: corner(&rect.round.bottom_left),
- bottom_right: corner(&rect.round.bottom_right),
- };
- normalize_radii(&layout_box, &mut radii);
- Some(create_rect_clip_chain(
- radii,
- shape_rect,
- parent_scroll_node_id,
- parent_clip_chain_id,
- display_list,
- ))
- },
- BasicShape::Circle(circle) => {
- let center = match circle.position {
- GenericPositionOrAuto::Position(position) => position,
- GenericPositionOrAuto::Auto => Position::center(),
- };
- let anchor_x = center
- .horizontal
- .to_used_value(Au::from_f32_px(layout_box.width()));
- let anchor_y = center
- .vertical
- .to_used_value(Au::from_f32_px(layout_box.height()));
- let center = layout_box
- .min
- .add_size(&LayoutSize::new(anchor_x.to_f32_px(), anchor_y.to_f32_px()));
-
- let horizontal =
- compute_shape_radius(center.x, &circle.radius, layout_box.min.x, layout_box.max.x);
- let vertical =
- compute_shape_radius(center.y, &circle.radius, layout_box.min.y, layout_box.max.y);
-
- // If the value is `Length` then both values should be the same at this point.
- let radius = match circle.radius {
- GenericShapeRadius::FarthestSide => horizontal.max(vertical),
- GenericShapeRadius::ClosestSide => horizontal.min(vertical),
- GenericShapeRadius::Length(_) => horizontal,
- };
- let radius = LayoutSize::new(radius, radius);
- let mut radii = webrender_api::BorderRadius {
- top_left: radius,
- top_right: radius,
- bottom_left: radius,
- bottom_right: radius,
- };
- let start = center.add_size(&-radius);
- let rect = LayoutRect::from_origin_and_size(start, radius * 2.);
- normalize_radii(&layout_box, &mut radii);
- Some(create_rect_clip_chain(
- radii,
- rect,
- parent_scroll_node_id,
- parent_clip_chain_id,
- display_list,
- ))
- },
- BasicShape::Ellipse(ellipse) => {
- let center = match ellipse.position {
- GenericPositionOrAuto::Position(position) => position,
- GenericPositionOrAuto::Auto => Position::center(),
- };
- let anchor_x = center
- .horizontal
- .to_used_value(Au::from_f32_px(layout_box.width()));
- let anchor_y = center
- .vertical
- .to_used_value(Au::from_f32_px(layout_box.height()));
- let center = layout_box
- .min
- .add_size(&LayoutSize::new(anchor_x.to_f32_px(), anchor_y.to_f32_px()));
-
- let width = compute_shape_radius(
- center.x,
- &ellipse.semiaxis_x,
- layout_box.min.x,
- layout_box.max.x,
- );
- let height = compute_shape_radius(
- center.y,
- &ellipse.semiaxis_y,
- layout_box.min.y,
- layout_box.max.y,
- );
-
- let mut radii = webrender_api::BorderRadius {
- top_left: LayoutSize::new(width, height),
- top_right: LayoutSize::new(width, height),
- bottom_left: LayoutSize::new(width, height),
- bottom_right: LayoutSize::new(width, height),
- };
- let size = LayoutSize::new(width, height);
- let start = center.add_size(&-size);
- let rect = LayoutRect::from_origin_and_size(start, size * 2.);
- normalize_radii(&rect, &mut radii);
- Some(create_rect_clip_chain(
- radii,
- rect,
- parent_scroll_node_id,
- parent_clip_chain_id,
- display_list,
- ))
- },
- _ => None,
- }
-}
-
-fn compute_shape_radius(
- center: f32,
- radius: &GenericShapeRadius<NonNegativeLengthPercentage>,
- min_edge: f32,
- max_edge: f32,
-) -> f32 {
- let distance_from_min_edge = (min_edge - center).abs();
- let distance_from_max_edge = (max_edge - center).abs();
- match radius {
- GenericShapeRadius::FarthestSide => distance_from_min_edge.max(distance_from_max_edge),
- GenericShapeRadius::ClosestSide => distance_from_min_edge.min(distance_from_max_edge),
- GenericShapeRadius::Length(length) => length
- .0
- .to_used_value(Au::from_f32_px(max_edge - min_edge))
- .to_f32_px(),
- }
-}
-fn create_rect_clip_chain(
- radii: webrender_api::BorderRadius,
- rect: LayoutRect,
- parent_scroll_node_id: &ScrollTreeNodeId,
- parent_clip_chain_id: &ClipChainId,
- display_list: &mut DisplayList,
-) -> ClipChainId {
- let new_clip_id = if radii.is_zero() {
- display_list
- .wr
- .define_clip_rect(parent_scroll_node_id.spatial_id, rect)
- } else {
- display_list.wr.define_clip_rounded_rect(
- parent_scroll_node_id.spatial_id,
- webrender_api::ComplexClipRegion {
- rect,
- radii,
- mode: webrender_api::ClipMode::Clip,
- },
- )
- };
- display_list.define_clip_chain(*parent_clip_chain_id, [new_clip_id])
-}
diff --git a/components/layout/display_list/mod.rs b/components/layout/display_list/mod.rs
index f017642908d..95689cf1186 100644
--- a/components/layout/display_list/mod.rs
+++ b/components/layout/display_list/mod.rs
@@ -8,13 +8,15 @@ use std::sync::Arc;
use app_units::{AU_PER_PX, Au};
use base::WebRenderEpochToU16;
use base::id::ScrollTreeNodeId;
-use compositing_traits::display_list::{AxesScrollSensitivity, CompositorDisplayListInfo};
+use clip::{Clip, ClipId};
+use compositing_traits::display_list::{CompositorDisplayListInfo, SpatialTreeNodeInfo};
use embedder_traits::Cursor;
use euclid::{Point2D, SideOffsets2D, Size2D, UnknownUnit};
use fonts::GlyphStore;
use gradient::WebRenderGradient;
use range::Range as ServoRange;
use servo_arc::Arc as ServoArc;
+use servo_config::opts::DebugOptions;
use servo_geometry::MaxRect;
use style::Zero;
use style::color::{AbsoluteColor, ColorSpace};
@@ -35,8 +37,9 @@ use style::values::specified::ui::CursorKind;
use style_traits::CSSPixel;
use webrender_api::units::{DevicePixel, LayoutPixel, LayoutRect, LayoutSize};
use webrender_api::{
- self as wr, BorderDetails, BoxShadowClipMode, ClipChainId, CommonItemProperties,
- ImageRendering, NinePatchBorder, NinePatchBorderSource, SpatialId, units,
+ self as wr, BorderDetails, BoxShadowClipMode, BuiltDisplayList, ClipChainId, ClipMode,
+ CommonItemProperties, ComplexClipRegion, ImageRendering, NinePatchBorder,
+ NinePatchBorderSource, PropertyBinding, SpatialId, SpatialTreeItemKey, units,
};
use wr::units::LayoutVector2D;
@@ -55,7 +58,7 @@ use crate::replaced::NaturalSizes;
use crate::style_ext::{BorderStyleColor, ComputedValuesExt};
mod background;
-mod clip_path;
+mod clip;
mod conversions;
mod gradient;
mod stacking_context;
@@ -74,68 +77,6 @@ type ItemTag = (u64, u16);
type HitInfo = Option<ItemTag>;
const INSERTION_POINT_LOGICAL_WIDTH: Au = Au(AU_PER_PX);
-/// Where the information that's used to build display lists is stored. This
-/// includes both a [wr::DisplayListBuilder] for building up WebRender-specific
-/// display list information and a [CompositorDisplayListInfo] used to store
-/// information used by the compositor, such as a compositor-side scroll tree.
-pub struct DisplayList {
- /// The [wr::DisplayListBuilder] used to collect display list items.
- pub wr: wr::DisplayListBuilder,
-
- /// The information about the WebRender display list that the compositor
- /// consumes. This curerntly contains the out-of-band hit testing information
- /// data structure that the compositor uses to map hit tests to information
- /// about the item hit.
- pub compositor_info: CompositorDisplayListInfo,
-
- /// A count of the number of SpatialTree nodes pushed to the WebRender display
- /// list. This is merely to ensure that the currently-unused SpatialTreeItemKey
- /// produced for every SpatialTree node is unique.
- pub spatial_tree_count: u64,
-}
-
-impl DisplayList {
- /// Create a new [DisplayList] given the dimensions of the layout and the WebRender
- /// pipeline id.
- pub fn new(
- viewport_size: units::LayoutSize,
- content_size: units::LayoutSize,
- pipeline_id: wr::PipelineId,
- epoch: wr::Epoch,
- viewport_scroll_sensitivity: AxesScrollSensitivity,
- first_reflow: bool,
- ) -> Self {
- Self {
- wr: wr::DisplayListBuilder::new(pipeline_id),
- compositor_info: CompositorDisplayListInfo::new(
- viewport_size,
- content_size,
- pipeline_id,
- epoch,
- viewport_scroll_sensitivity,
- first_reflow,
- ),
- spatial_tree_count: 0,
- }
- }
-
- pub fn define_clip_chain<I>(&mut self, parent: ClipChainId, clips: I) -> ClipChainId
- where
- I: IntoIterator<Item = wr::ClipId>,
- I::IntoIter: ExactSizeIterator + Clone,
- {
- // WebRender has two different ways of expressing "no clip." ClipChainId::INVALID should be
- // used for primitives, but `None` is used for stacking contexts and clip chains. We convert
- // to the `Option<ClipChainId>` representation here. Just passing Some(ClipChainId::INVALID)
- // leads to a crash.
- let parent = match parent {
- ClipChainId::INVALID => None,
- parent => Some(parent),
- };
- self.wr.define_clip_chain(parent, clips)
- }
-}
-
pub(crate) struct DisplayListBuilder<'a> {
/// The current [ScrollTreeNodeId] for this [DisplayListBuilder]. This
/// allows only passing the builder instead passing the containing
@@ -148,18 +89,21 @@ pub(crate) struct DisplayListBuilder<'a> {
/// `background-attachment: fixed` need to not scroll while the rest of the fragment does.
current_reference_frame_scroll_node_id: ScrollTreeNodeId,
- /// The current [wr::ClipId] for this [DisplayListBuilder]. This allows
+ /// The current [`ClipId`] for this [DisplayListBuilder]. This allows
/// only passing the builder instead passing the containing
/// [stacking_context::StackingContextContent::Fragment] as an argument to display
/// list building functions.
- current_clip_chain_id: ClipChainId,
+ current_clip_id: ClipId,
/// A [LayoutContext] used to get information about the device pixel ratio
/// and get handles to WebRender images.
pub context: &'a LayoutContext<'a>,
- /// The [DisplayList] used to collect display list items and metadata.
- pub display_list: &'a mut DisplayList,
+ /// The [`wr::DisplayListBuilder`] for this Servo [`DisplayListBuilder`].
+ pub webrender_display_list_builder: &'a mut wr::DisplayListBuilder,
+
+ /// The [`CompositorDisplayListInfo`] used to collect display list items and metadata.
+ pub compositor_info: &'a mut CompositorDisplayListInfo,
/// Data about the fragments that are highlighted by the inspector, if any.
///
@@ -171,6 +115,10 @@ pub(crate) struct DisplayListBuilder<'a> {
/// element inherits the `<body>`'s background to paint the page canvas background.
/// See <https://drafts.csswg.org/css-backgrounds/#body-background>.
paint_body_background: bool,
+
+ /// A mapping from [`ClipId`] To WebRender [`ClipChainId`] used when building this WebRender
+ /// display list.
+ clip_map: Vec<ClipChainId>,
}
struct InspectorHighlight {
@@ -207,45 +155,224 @@ impl InspectorHighlight {
}
}
-impl DisplayList {
- pub fn build(
- &mut self,
+impl DisplayListBuilder<'_> {
+ pub(crate) fn build(
context: &LayoutContext,
+ stacking_context_tree: &mut StackingContextTree,
fragment_tree: &FragmentTree,
- root_stacking_context: &StackingContext,
- ) {
+ debug: &DebugOptions,
+ ) -> BuiltDisplayList {
+ // Build the rest of the display list which inclues all of the WebRender primitives.
+ let compositor_info = &mut stacking_context_tree.compositor_info;
+ compositor_info.hit_test_info.clear();
+
+ let mut webrender_display_list_builder =
+ webrender_api::DisplayListBuilder::new(compositor_info.pipeline_id);
+ webrender_display_list_builder.begin();
+
+ // `dump_serialized_display_list` doesn't actually print anything. It sets up
+ // the display list for printing the serialized version when `finalize()` is called.
+ // We need to call this before adding any display items so that they are printed
+ // during `finalize()`.
+ if debug.dump_display_list {
+ webrender_display_list_builder.dump_serialized_display_list();
+ }
+
#[cfg(feature = "tracing")]
- let _span = tracing::trace_span!("display_list::build", servo_profiling = true).entered();
+ let _span =
+ tracing::trace_span!("DisplayListBuilder::build", servo_profiling = true).entered();
let mut builder = DisplayListBuilder {
- current_scroll_node_id: self.compositor_info.root_reference_frame_id,
- current_reference_frame_scroll_node_id: self.compositor_info.root_reference_frame_id,
- current_clip_chain_id: ClipChainId::INVALID,
+ current_scroll_node_id: compositor_info.root_reference_frame_id,
+ current_reference_frame_scroll_node_id: compositor_info.root_reference_frame_id,
+ current_clip_id: ClipId::INVALID,
context,
- display_list: self,
+ webrender_display_list_builder: &mut webrender_display_list_builder,
+ compositor_info,
inspector_highlight: context
.highlighted_dom_node
.map(InspectorHighlight::for_node),
paint_body_background: true,
+ clip_map: Default::default(),
};
- fragment_tree.build_display_list(&mut builder, root_stacking_context);
- if let Some(highlight) = builder
- .inspector_highlight
- .take()
- .and_then(|highlight| highlight.state)
- {
- builder.paint_dom_inspector_highlight(highlight);
+ builder.add_all_spatial_nodes();
+
+ for clip in stacking_context_tree.clip_store.0.iter() {
+ builder.add_clip_to_display_list(clip);
}
+
+ // Paint the canvas’ background (if any) before/under everything else
+ stacking_context_tree
+ .root_stacking_context
+ .build_canvas_background_display_list(&mut builder, fragment_tree);
+ stacking_context_tree
+ .root_stacking_context
+ .build_display_list(&mut builder);
+ builder.paint_dom_inspector_highlight();
+
+ webrender_display_list_builder.end().1
}
-}
-impl DisplayListBuilder<'_> {
fn wr(&mut self) -> &mut wr::DisplayListBuilder {
- &mut self.display_list.wr
+ self.webrender_display_list_builder
+ }
+
+ fn pipeline_id(&mut self) -> wr::PipelineId {
+ self.compositor_info.pipeline_id
}
fn mark_is_contentful(&mut self) {
- self.display_list.compositor_info.is_contentful = true;
+ self.compositor_info.is_contentful = true;
+ }
+
+ fn spatial_id(&self, id: ScrollTreeNodeId) -> SpatialId {
+ self.compositor_info.scroll_tree.webrender_id(&id)
+ }
+
+ fn clip_chain_id(&self, id: ClipId) -> ClipChainId {
+ match id {
+ ClipId::INVALID => ClipChainId::INVALID,
+ _ => *self
+ .clip_map
+ .get(id.0)
+ .expect("Should never try to get clip before adding it to WebRender display list"),
+ }
+ }
+
+ pub(crate) fn add_all_spatial_nodes(&mut self) {
+ // A count of the number of SpatialTree nodes pushed to the WebRender display
+ // list. This is merely to ensure that the currently-unused SpatialTreeItemKey
+ // produced for every SpatialTree node is unique.
+ let mut spatial_tree_count = 0;
+ let mut scroll_tree = std::mem::take(&mut self.compositor_info.scroll_tree);
+ let mut mapping = Vec::with_capacity(scroll_tree.nodes.len());
+
+ mapping.push(SpatialId::root_reference_frame(self.pipeline_id()));
+ mapping.push(SpatialId::root_scroll_node(self.pipeline_id()));
+
+ let pipeline_id = self.pipeline_id();
+ let pipeline_tag = ((pipeline_id.0 as u64) << 32) | pipeline_id.1 as u64;
+
+ for node in scroll_tree.nodes.iter().skip(2) {
+ let parent_scroll_node_id = node
+ .parent
+ .expect("Should have already added root reference frame");
+ let parent_spatial_node_id = mapping
+ .get(parent_scroll_node_id.index)
+ .expect("Should add spatial nodes to display list in order");
+
+ // Produce a new SpatialTreeItemKey. This is currently unused by WebRender,
+ // but has to be unique to the entire scene.
+ spatial_tree_count += 1;
+ let spatial_tree_item_key = SpatialTreeItemKey::new(pipeline_tag, spatial_tree_count);
+
+ mapping.push(match &node.info {
+ SpatialTreeNodeInfo::ReferenceFrame(info) => {
+ let spatial_id = self.wr().push_reference_frame(
+ info.origin,
+ *parent_spatial_node_id,
+ info.transform_style,
+ PropertyBinding::Value(info.transform),
+ info.kind,
+ spatial_tree_item_key,
+ );
+ self.wr().pop_reference_frame();
+ spatial_id
+ },
+ SpatialTreeNodeInfo::Scroll(info) => {
+ self.wr().define_scroll_frame(
+ *parent_spatial_node_id,
+ info.external_id,
+ info.content_rect,
+ info.clip_rect,
+ LayoutVector2D::zero(), /* external_scroll_offset */
+ 0, /* scroll_offset_generation */
+ wr::HasScrollLinkedEffect::No,
+ spatial_tree_item_key,
+ )
+ },
+ SpatialTreeNodeInfo::Sticky(info) => {
+ self.wr().define_sticky_frame(
+ *parent_spatial_node_id,
+ info.frame_rect,
+ info.margins,
+ info.vertical_offset_bounds,
+ info.horizontal_offset_bounds,
+ LayoutVector2D::zero(), /* previously_applied_offset */
+ spatial_tree_item_key,
+ None, /* transform */
+ )
+ },
+ });
+ }
+
+ scroll_tree.update_mapping(mapping);
+ self.compositor_info.scroll_tree = scroll_tree;
+ }
+
+ /// Add the given [`Clip`] to the WebRender display list and create a mapping from
+ /// its [`ClipId`] to a WebRender [`ClipChainId`]. This happens:
+ /// - When WebRender display list construction starts: All clips created during the
+ /// `StackingContextTree` construction are added in one batch. These clips are used
+ /// for things such as `overflow: scroll` elements.
+ /// - When a clip is added during WebRender display list construction for individual
+ /// items. In that case, this is called by [`Self::maybe_create_clip`].
+ pub(crate) fn add_clip_to_display_list(&mut self, clip: &Clip) -> ClipChainId {
+ assert_eq!(
+ clip.id.0,
+ self.clip_map.len(),
+ "Clips should be added in order"
+ );
+
+ let spatial_id = self.spatial_id(clip.parent_scroll_node_id);
+ let new_clip_id = if clip.radii.is_zero() {
+ self.wr().define_clip_rect(spatial_id, clip.rect)
+ } else {
+ self.wr().define_clip_rounded_rect(
+ spatial_id,
+ ComplexClipRegion {
+ rect: clip.rect,
+ radii: clip.radii,
+ mode: ClipMode::Clip,
+ },
+ )
+ };
+
+ // WebRender has two different ways of expressing "no clip." ClipChainId::INVALID should be
+ // used for primitives, but `None` is used for stacking contexts and clip chains. We convert
+ // to the `Option<ClipChainId>` representation here. Just passing Some(ClipChainId::INVALID)
+ // leads to a crash.
+ let parent_clip_chain_id = match self.clip_chain_id(clip.parent_clip_id) {
+ ClipChainId::INVALID => None,
+ parent => Some(parent),
+ };
+ let clip_chain_id = self
+ .wr()
+ .define_clip_chain(parent_clip_chain_id, [new_clip_id]);
+ self.clip_map.push(clip_chain_id);
+ clip_chain_id
+ }
+
+ /// Add a new clip to the WebRender display list being built. This only happens during
+ /// WebRender display list building and these clips should be added after all clips
+ /// from the `StackingContextTree` have already been processed.
+ fn maybe_create_clip(
+ &mut self,
+ radii: wr::BorderRadius,
+ rect: units::LayoutRect,
+ force_clip_creation: bool,
+ ) -> Option<ClipChainId> {
+ if radii.is_zero() && !force_clip_creation {
+ return None;
+ }
+
+ Some(self.add_clip_to_display_list(&Clip {
+ id: ClipId(self.clip_map.len()),
+ radii,
+ rect,
+ parent_scroll_node_id: self.current_scroll_node_id,
+ parent_clip_id: self.current_clip_id,
+ }))
}
fn common_properties(
@@ -258,8 +385,8 @@ impl DisplayListBuilder<'_> {
// for fragments that paint their entire border rectangle.
wr::CommonItemProperties {
clip_rect,
- spatial_id: self.current_scroll_node_id.spatial_id,
- clip_chain_id: self.current_clip_chain_id,
+ spatial_id: self.spatial_id(self.current_scroll_node_id),
+ clip_chain_id: self.clip_chain_id(self.current_clip_id),
flags: style.get_webrender_primitive_flags(),
}
}
@@ -277,19 +404,24 @@ impl DisplayListBuilder<'_> {
return None;
}
- let hit_test_index = self.display_list.compositor_info.add_hit_test_info(
+ let hit_test_index = self.compositor_info.add_hit_test_info(
tag?.node.0 as u64,
Some(cursor(inherited_ui.cursor.keyword, auto_cursor)),
self.current_scroll_node_id,
);
- Some((
- hit_test_index as u64,
- self.display_list.compositor_info.epoch.as_u16(),
- ))
+ Some((hit_test_index as u64, self.compositor_info.epoch.as_u16()))
}
/// Draw highlights around the node that is currently hovered in the devtools.
- fn paint_dom_inspector_highlight(&mut self, highlight: HighlightTraversalState) {
+ fn paint_dom_inspector_highlight(&mut self) {
+ let Some(highlight) = self
+ .inspector_highlight
+ .take()
+ .and_then(|highlight| highlight.state)
+ else {
+ return;
+ };
+
const CONTENT_BOX_HIGHLIGHT_COLOR: webrender_api::ColorF = webrender_api::ColorF {
r: 0.23,
g: 0.7,
@@ -327,8 +459,7 @@ impl DisplayListBuilder<'_> {
flags: wr::PrimitiveFlags::default(),
};
- self.display_list
- .wr
+ self.wr()
.push_rect(&properties, content_box, CONTENT_BOX_HIGHLIGHT_COLOR);
// Highlight margin, border and padding
@@ -442,12 +573,14 @@ impl Fragment {
is_hit_test_for_scrollable_overflow: bool,
is_collapsed_table_borders: bool,
) {
+ let spatial_id = builder.spatial_id(builder.current_scroll_node_id);
+ let clip_chain_id = builder.clip_chain_id(builder.current_clip_id);
if let Some(inspector_highlight) = &mut builder.inspector_highlight {
if self.tag() == Some(inspector_highlight.tag) {
inspector_highlight.register_fragment_of_highlighted_dom_node(
self,
- builder.current_scroll_node_id.spatial_id,
- builder.current_clip_chain_id,
+ spatial_id,
+ clip_chain_id,
containing_block,
);
}
@@ -574,8 +707,8 @@ impl Fragment {
None => return,
};
- let clip_chain_id = builder.current_clip_chain_id;
- let spatial_id = builder.current_scroll_node_id.spatial_id;
+ let clip_chain_id = builder.clip_chain_id(builder.current_clip_id);
+ let spatial_id = builder.spatial_id(builder.current_scroll_node_id);
builder.wr().push_hit_test(
rect.to_webrender(),
clip_chain_id,
@@ -765,8 +898,9 @@ impl Fragment {
if text_decoration_style == ComputedTextDecorationStyle::MozNone {
return;
}
- builder.display_list.wr.push_line(
- &builder.common_properties(rect, parent_style),
+ let common_properties = builder.common_properties(rect, parent_style);
+ builder.wr().push_line(
+ &common_properties,
&rect,
wavy_line_thickness,
wr::LineOrientation::Horizontal,
@@ -878,12 +1012,8 @@ impl<'a> BuilderForBoxFragment<'a> {
return Some(clip);
}
- let maybe_clip = create_clip_chain(
- self.border_radius,
- self.border_rect,
- builder,
- force_clip_creation,
- );
+ let maybe_clip =
+ builder.maybe_create_clip(self.border_radius, self.border_rect, force_clip_creation);
*self.border_edge_clip_chain_id.borrow_mut() = maybe_clip;
maybe_clip
}
@@ -899,7 +1029,7 @@ impl<'a> BuilderForBoxFragment<'a> {
let radii = inner_radii(self.border_radius, self.fragment.border.to_webrender());
let maybe_clip =
- create_clip_chain(radii, *self.padding_rect(), builder, force_clip_creation);
+ builder.maybe_create_clip(radii, *self.padding_rect(), force_clip_creation);
*self.padding_edge_clip_chain_id.borrow_mut() = maybe_clip;
maybe_clip
}
@@ -918,7 +1048,7 @@ impl<'a> BuilderForBoxFragment<'a> {
(self.fragment.border + self.fragment.padding).to_webrender(),
);
let maybe_clip =
- create_clip_chain(radii, *self.content_rect(), builder, force_clip_creation);
+ builder.maybe_create_clip(radii, *self.content_rect(), force_clip_creation);
*self.content_edge_clip_chain_id.borrow_mut() = maybe_clip;
maybe_clip
}
@@ -1553,38 +1683,6 @@ fn offset_radii(mut radii: wr::BorderRadius, offset: f32) -> wr::BorderRadius {
radii
}
-fn create_clip_chain(
- radii: wr::BorderRadius,
- rect: units::LayoutRect,
- builder: &mut DisplayListBuilder,
- force_clip_creation: bool,
-) -> Option<ClipChainId> {
- if radii.is_zero() && !force_clip_creation {
- return None;
- }
-
- let spatial_id = builder.current_scroll_node_id.spatial_id;
- let parent_clip_chain_id = builder.current_clip_chain_id;
- let new_clip_id = if radii.is_zero() {
- builder.wr().define_clip_rect(spatial_id, rect)
- } else {
- builder.wr().define_clip_rounded_rect(
- spatial_id,
- wr::ComplexClipRegion {
- rect,
- radii,
- mode: wr::ClipMode::Clip,
- },
- )
- };
-
- Some(
- builder
- .display_list
- .define_clip_chain(parent_clip_chain_id, [new_clip_id]),
- )
-}
-
/// Resolve the WebRender border-image outset area from the style values.
fn resolve_border_image_outset(
outset: BorderImageOutset,
diff --git a/components/layout/display_list/stacking_context.rs b/components/layout/display_list/stacking_context.rs
index 27fa73a680c..bcd882e3fcc 100644
--- a/components/layout/display_list/stacking_context.rs
+++ b/components/layout/display_list/stacking_context.rs
@@ -9,18 +9,19 @@ use std::mem;
use app_units::Au;
use base::id::ScrollTreeNodeId;
use base::print_tree::PrintTree;
-use compositing_traits::display_list::{AxesScrollSensitivity, ScrollableNodeInfo};
+use compositing_traits::display_list::{
+ AxesScrollSensitivity, CompositorDisplayListInfo, ReferenceFrameNodeInfo, ScrollableNodeInfo,
+ SpatialTreeNodeInfo, StickyNodeInfo,
+};
use euclid::SideOffsets2D;
use euclid::default::{Point2D, Rect, Size2D};
use log::warn;
-use servo_arc::Arc as ServoArc;
use servo_config::opts::DebugOptions;
use style::Zero;
use style::computed_values::float::T as ComputedFloat;
use style::computed_values::mix_blend_mode::T as ComputedMixBlendMode;
use style::computed_values::overflow_x::T as ComputedOverflow;
use style::computed_values::position::T as ComputedPosition;
-use style::properties::ComputedValues;
use style::values::computed::angle::Angle;
use style::values::computed::basic_shape::ClipPath;
use style::values::computed::{ClipRectOrAuto, Length};
@@ -29,11 +30,12 @@ use style::values::generics::transform::{self, GenericRotate, GenericScale, Gene
use style::values::specified::box_::DisplayOutside;
use webrender_api::units::{LayoutPoint, LayoutRect, LayoutTransform, LayoutVector2D};
use webrender_api::{self as wr, BorderRadius};
+use wr::StickyOffsetBounds;
use wr::units::{LayoutPixel, LayoutSize};
-use wr::{ClipChainId, SpatialTreeItemKey, StickyOffsetBounds};
-use super::DisplayList;
-use super::clip_path::build_clip_path_clip_chain_if_necessary;
+use super::ClipId;
+use super::clip::StackingContextTreeClipStore;
+use crate::ArcRefCell;
use crate::display_list::conversions::{FilterToWebRender, ToWebRender};
use crate::display_list::{BuilderForBoxFragment, DisplayListBuilder, offset_radii};
use crate::fragment_tree::{
@@ -54,9 +56,8 @@ pub(crate) struct ContainingBlock {
/// frame and sticky positioning isn't taken into account.
scroll_frame_size: Option<LayoutSize>,
- /// The WebRender ClipId to use for this children of this containing
- /// block.
- clip_chain_id: wr::ClipChainId,
+ /// The [`ClipId`] to use for the children of this containing block.
+ clip_id: ClipId,
/// The physical rect of this containing block.
rect: PhysicalRect<Au>,
@@ -67,12 +68,12 @@ impl ContainingBlock {
rect: PhysicalRect<Au>,
scroll_node_id: ScrollTreeNodeId,
scroll_frame_size: Option<LayoutSize>,
- clip_chain_id: wr::ClipChainId,
+ clip_id: ClipId,
) -> Self {
ContainingBlock {
scroll_node_id,
scroll_frame_size,
- clip_chain_id,
+ clip_id,
rect,
}
}
@@ -95,40 +96,56 @@ pub(crate) enum StackingContextSection {
Outline,
}
-impl DisplayList {
- /// Produce a new SpatialTreeItemKey. This is currently unused by WebRender,
- /// but has to be unique to the entire scene.
- fn get_next_spatial_tree_item_key(&mut self) -> SpatialTreeItemKey {
- self.spatial_tree_count += 1;
- let pipeline_tag = ((self.wr.pipeline_id.0 as u64) << 32) | self.wr.pipeline_id.1 as u64;
- SpatialTreeItemKey::new(pipeline_tag, self.spatial_tree_count)
- }
+pub(crate) struct StackingContextTree {
+ /// The root stacking context of this [`StackingContextTree`].
+ pub root_stacking_context: StackingContext,
- #[cfg_attr(
- feature = "tracing",
- tracing::instrument(
- name = "display_list::build_stacking_context_tree",
- skip_all,
- fields(servo_profiling = true),
- level = "trace",
- )
- )]
- pub fn build_stacking_context_tree(
- &mut self,
+ /// The information about the WebRender display list that the compositor
+ /// consumes. This curerntly contains the out-of-band hit testing information
+ /// data structure that the compositor uses to map hit tests to information
+ /// about the item hit.
+ pub compositor_info: CompositorDisplayListInfo,
+
+ /// All of the clips collected for this [`StackingContextTree`]. These are added
+ /// for things like `overflow`. More clips may be created later during WebRender
+ /// display list construction, but they are never added here.
+ pub clip_store: StackingContextTreeClipStore,
+}
+
+impl StackingContextTree {
+ /// Create a new [DisplayList] given the dimensions of the layout and the WebRender
+ /// pipeline id.
+ pub fn new(
fragment_tree: &FragmentTree,
+ viewport_size: LayoutSize,
+ content_size: LayoutSize,
+ pipeline_id: wr::PipelineId,
+ viewport_scroll_sensitivity: AxesScrollSensitivity,
+ first_reflow: bool,
debug: &DebugOptions,
- ) -> StackingContext {
+ ) -> Self {
+ let compositor_info = CompositorDisplayListInfo::new(
+ viewport_size,
+ content_size,
+ pipeline_id,
+ // This epoch is set when the WebRender display list is built. For now use a dummy value.
+ wr::Epoch(0),
+ viewport_scroll_sensitivity,
+ first_reflow,
+ );
+
+ let root_scroll_node_id = compositor_info.root_scroll_node_id;
let cb_for_non_fixed_descendants = ContainingBlock::new(
fragment_tree.initial_containing_block,
- self.compositor_info.root_scroll_node_id,
- Some(self.compositor_info.viewport_size),
- ClipChainId::INVALID,
+ root_scroll_node_id,
+ Some(compositor_info.viewport_size),
+ ClipId::INVALID,
);
let cb_for_fixed_descendants = ContainingBlock::new(
fragment_tree.initial_containing_block,
- self.compositor_info.root_reference_frame_id,
+ compositor_info.root_reference_frame_id,
None,
- ClipChainId::INVALID,
+ ClipId::INVALID,
);
// We need to specify all three containing blocks here, because absolute
@@ -143,17 +160,31 @@ impl DisplayList {
for_absolute_and_fixed_descendants: &cb_for_fixed_descendants,
};
- let mut root_stacking_context = StackingContext::create_root(&self.wr, debug);
+ let mut stacking_context_tree = Self {
+ // This is just a temporary value that will be replaced once we have finished building the tree.
+ root_stacking_context: StackingContext::create_root(root_scroll_node_id, debug),
+ compositor_info,
+ clip_store: Default::default(),
+ };
+
+ let mut root_stacking_context = StackingContext::create_root(root_scroll_node_id, debug);
for fragment in &fragment_tree.root_fragments {
fragment.build_stacking_context_tree(
- self,
+ &mut stacking_context_tree,
&containing_block_info,
&mut root_stacking_context,
StackingContextBuildMode::SkipHoisted,
);
}
root_stacking_context.sort();
- root_stacking_context
+
+ if debug.dump_stacking_context_tree {
+ root_stacking_context.debug_print();
+ }
+
+ stacking_context_tree.root_stacking_context = root_stacking_context;
+
+ stacking_context_tree
}
fn push_reference_frame(
@@ -161,53 +192,20 @@ impl DisplayList {
origin: LayoutPoint,
parent_scroll_node_id: &ScrollTreeNodeId,
transform_style: wr::TransformStyle,
- transform: wr::PropertyBinding<LayoutTransform>,
+ transform: LayoutTransform,
kind: wr::ReferenceFrameKind,
) -> ScrollTreeNodeId {
- let spatial_tree_item_key = self.get_next_spatial_tree_item_key();
- let new_spatial_id = self.wr.push_reference_frame(
- origin,
- parent_scroll_node_id.spatial_id,
- transform_style,
- transform,
- kind,
- spatial_tree_item_key,
- );
self.compositor_info.scroll_tree.add_scroll_tree_node(
Some(parent_scroll_node_id),
- new_spatial_id,
- None,
+ SpatialTreeNodeInfo::ReferenceFrame(ReferenceFrameNodeInfo {
+ origin,
+ transform_style,
+ transform,
+ kind,
+ }),
)
}
- fn pop_reference_frame(&mut self) {
- self.wr.pop_reference_frame();
- }
-
- fn clip_overflow_frame(
- &mut self,
- parent_scroll_node_id: &ScrollTreeNodeId,
- parent_clip_id: &ClipChainId,
- clip_rect: LayoutRect,
- radii: wr::BorderRadius,
- ) -> ClipChainId {
- let new_clip_id = if radii.is_zero() {
- self.wr
- .define_clip_rect(parent_scroll_node_id.spatial_id, clip_rect)
- } else {
- self.wr.define_clip_rounded_rect(
- parent_scroll_node_id.spatial_id,
- webrender_api::ComplexClipRegion {
- rect: clip_rect,
- radii,
- mode: webrender_api::ClipMode::Clip,
- },
- )
- };
-
- self.define_clip_chain(*parent_clip_id, [new_clip_id])
- }
-
fn define_scroll_frame(
&mut self,
parent_scroll_node_id: &ScrollTreeNodeId,
@@ -216,25 +214,12 @@ impl DisplayList {
clip_rect: LayoutRect,
scroll_sensitivity: AxesScrollSensitivity,
) -> ScrollTreeNodeId {
- let spatial_tree_item_key = self.get_next_spatial_tree_item_key();
-
- let new_spatial_id = self.wr.define_scroll_frame(
- parent_scroll_node_id.spatial_id,
- external_id,
- content_rect,
- clip_rect,
- LayoutVector2D::zero(), /* external_scroll_offset */
- 0, /* scroll_offset_generation */
- wr::HasScrollLinkedEffect::No,
- spatial_tree_item_key,
- );
-
self.compositor_info.scroll_tree.add_scroll_tree_node(
Some(parent_scroll_node_id),
- new_spatial_id,
- Some(ScrollableNodeInfo {
+ SpatialTreeNodeInfo::Scroll(ScrollableNodeInfo {
external_id,
- scrollable_size: content_rect.size() - clip_rect.size(),
+ content_rect,
+ clip_rect,
scroll_sensitivity,
offset: LayoutVector2D::zero(),
}),
@@ -249,21 +234,14 @@ impl DisplayList {
vertical_offset_bounds: StickyOffsetBounds,
horizontal_offset_bounds: StickyOffsetBounds,
) -> ScrollTreeNodeId {
- let spatial_tree_item_key = self.get_next_spatial_tree_item_key();
- let new_spatial_id = self.wr.define_sticky_frame(
- parent_scroll_node_id.spatial_id,
- frame_rect,
- margins,
- vertical_offset_bounds,
- horizontal_offset_bounds,
- LayoutVector2D::zero(), /* previously_applied_offset */
- spatial_tree_item_key,
- None, /* transform */
- );
self.compositor_info.scroll_tree.add_scroll_tree_node(
Some(parent_scroll_node_id),
- new_spatial_id,
- None,
+ SpatialTreeNodeInfo::Sticky(StickyNodeInfo {
+ frame_rect,
+ margins,
+ vertical_offset_bounds,
+ horizontal_offset_bounds,
+ }),
)
}
}
@@ -277,7 +255,7 @@ pub(crate) enum StackingContextContent {
Fragment {
scroll_node_id: ScrollTreeNodeId,
reference_frame_scroll_node_id: ScrollTreeNodeId,
- clip_chain_id: wr::ClipChainId,
+ clip_id: ClipId,
section: StackingContextSection,
containing_block: PhysicalRect<Au>,
fragment: Fragment,
@@ -308,7 +286,7 @@ impl StackingContextContent {
Self::Fragment {
scroll_node_id,
reference_frame_scroll_node_id,
- clip_chain_id,
+ clip_id,
section,
containing_block,
fragment,
@@ -317,7 +295,7 @@ impl StackingContextContent {
} => {
builder.current_scroll_node_id = *scroll_node_id;
builder.current_reference_frame_scroll_node_id = *reference_frame_scroll_node_id;
- builder.current_clip_chain_id = *clip_chain_id;
+ builder.current_clip_id = *clip_id;
fragment.build_display_list(
builder,
containing_block,
@@ -349,16 +327,14 @@ pub(crate) enum StackingContextType {
pub struct StackingContext {
/// The spatial id of this fragment. This is used to properly handle
/// things like preserve-3d.
- spatial_id: wr::SpatialId,
+ scroll_tree_node_id: ScrollTreeNodeId,
/// The clip chain id of this stacking context if it has one. Used for filter clipping.
- clip_chain_id: Option<wr::ClipChainId>,
-
- /// The style of the fragment that established this stacking context.
- initializing_fragment_style: Option<ServoArc<ComputedValues>>,
+ clip_id: Option<ClipId>,
- /// The [`FragmentFlags`] of the [`Fragment`] that established this stacking context.
- initializing_fragment_flags: FragmentFlags,
+ /// The [`BoxFragment`] that established this stacking context. We store the fragment here
+ /// rather than just the style, so that incremental layout can automatically update the style.
+ initializing_fragment: Option<ArcRefCell<BoxFragment>>,
/// The type of this stacking context. Used for collecting and sorting.
context_type: StackingContextType,
@@ -415,25 +391,23 @@ pub enum DebugPrintField {
impl StackingContext {
fn create_descendant(
&self,
- spatial_id: wr::SpatialId,
- clip_chain_id: wr::ClipChainId,
- initializing_fragment_style: ServoArc<ComputedValues>,
- initializing_fragment_flags: FragmentFlags,
+ spatial_id: ScrollTreeNodeId,
+ clip_id: ClipId,
+ initializing_fragment: ArcRefCell<BoxFragment>,
context_type: StackingContextType,
) -> Self {
// WebRender has two different ways of expressing "no clip." ClipChainId::INVALID should be
// used for primitives, but `None` is used for stacking contexts and clip chains. We convert
- // to the `Option<ClipChainId>` representation here. Just passing Some(ClipChainId::INVALID)
+ // to the `Option<ClipId>` representation here. Just passing Some(ClipChainId::INVALID)
// leads to a crash.
- let clip_chain_id: Option<ClipChainId> = match clip_chain_id {
- ClipChainId::INVALID => None,
- clip_chain_id => Some(clip_chain_id),
+ let clip_id = match clip_id {
+ ClipId::INVALID => None,
+ clip_id => Some(clip_id),
};
Self {
- spatial_id,
- clip_chain_id,
- initializing_fragment_style: Some(initializing_fragment_style),
- initializing_fragment_flags,
+ scroll_tree_node_id: spatial_id,
+ clip_id,
+ initializing_fragment: Some(initializing_fragment),
context_type,
contents: vec![],
real_stacking_contexts_and_positioned_stacking_containers: vec![],
@@ -443,12 +417,11 @@ impl StackingContext {
}
}
- pub(crate) fn create_root(wr: &wr::DisplayListBuilder, debug: &DebugOptions) -> Self {
+ fn create_root(root_scroll_node_id: ScrollTreeNodeId, debug: &DebugOptions) -> Self {
Self {
- spatial_id: wr::SpaceAndClipInfo::root_scroll(wr.pipeline_id).spatial_id,
- clip_chain_id: None,
- initializing_fragment_style: None,
- initializing_fragment_flags: FragmentFlags::empty(),
+ scroll_tree_node_id: root_scroll_node_id,
+ clip_id: None,
+ initializing_fragment: None,
context_type: StackingContextType::RealStackingContext,
contents: vec![],
real_stacking_contexts_and_positioned_stacking_containers: vec![],
@@ -476,11 +449,10 @@ impl StackingContext {
}
fn z_index(&self) -> i32 {
- self.initializing_fragment_style
- .as_ref()
- .map_or(0, |style| {
- style.effective_z_index(self.initializing_fragment_flags)
- })
+ self.initializing_fragment.as_ref().map_or(0, |fragment| {
+ let fragment = fragment.borrow();
+ fragment.style.effective_z_index(fragment.base.flags)
+ })
}
pub(crate) fn sort(&mut self) {
@@ -519,13 +491,14 @@ impl StackingContext {
&self,
builder: &mut DisplayListBuilder,
) -> bool {
- let style = match self.initializing_fragment_style.as_ref() {
- Some(style) => style,
+ let fragment = match self.initializing_fragment.as_ref() {
+ Some(fragment) => fragment.borrow(),
None => return false,
};
// WebRender only uses the stacking context to apply certain effects. If we don't
// actually need to create a stacking context, just avoid creating one.
+ let style = &fragment.style;
let effects = style.get_effects();
if effects.filter.0.is_empty() &&
effects.opacity == 1.0 &&
@@ -557,11 +530,13 @@ impl StackingContext {
// This will require additional tracking during layout
// before we start collecting stacking contexts so that
// information will be available when we reach this point.
+ let spatial_id = builder.spatial_id(self.scroll_tree_node_id);
+ let clip_chain_id = self.clip_id.map(|clip_id| builder.clip_chain_id(clip_id));
builder.wr().push_stacking_context(
LayoutPoint::zero(), // origin
- self.spatial_id,
+ spatial_id,
style.get_webrender_primitive_flags(),
- self.clip_chain_id,
+ clip_chain_id,
style.get_used_transform_style().to_webrender(),
effects.mix_blend_mode.to_webrender(),
&filters,
@@ -635,10 +610,7 @@ impl StackingContext {
if background_color.alpha > 0.0 {
let common = builder.common_properties(painting_area, &source_style);
let color = super::rgba(background_color);
- builder
- .display_list
- .wr
- .push_rect(&common, painting_area, color)
+ builder.wr().push_rect(&common, painting_area, color)
}
let mut fragment_builder = BuilderForBoxFragment::new(
@@ -741,7 +713,7 @@ impl StackingContext {
}
if pushed_context {
- builder.display_list.wr.pop_stacking_context();
+ builder.wr().pop_stacking_context();
}
}
@@ -824,7 +796,7 @@ pub(crate) enum StackingContextBuildMode {
impl Fragment {
pub(crate) fn build_stacking_context_tree(
&self,
- display_list: &mut DisplayList,
+ stacking_context_tree: &mut StackingContextTree,
containing_block_info: &ContainingBlockInfo,
stacking_context: &mut StackingContext,
mode: StackingContextBuildMode,
@@ -852,7 +824,7 @@ impl Fragment {
fragment.build_stacking_context_tree(
fragment_clone,
- display_list,
+ stacking_context_tree,
containing_block,
containing_block_info,
stacking_context,
@@ -866,7 +838,7 @@ impl Fragment {
};
fragment_ref.build_stacking_context_tree(
- display_list,
+ stacking_context_tree,
containing_block_info,
stacking_context,
StackingContextBuildMode::IncludeHoisted,
@@ -875,7 +847,7 @@ impl Fragment {
Fragment::Positioning(fragment) => {
let fragment = fragment.borrow();
fragment.build_stacking_context_tree(
- display_list,
+ stacking_context_tree,
containing_block,
containing_block_info,
stacking_context,
@@ -890,7 +862,7 @@ impl Fragment {
reference_frame_scroll_node_id: containing_block_info
.for_absolute_and_fixed_descendants
.scroll_node_id,
- clip_chain_id: containing_block.clip_chain_id,
+ clip_id: containing_block.clip_id,
containing_block: containing_block.rect,
fragment: fragment_clone,
is_hit_test_for_scrollable_overflow: false,
@@ -912,7 +884,7 @@ struct ScrollFrameData {
}
struct OverflowFrameData {
- clip_chain_id: wr::ClipChainId,
+ clip_id: ClipId,
scroll_frame_data: Option<ScrollFrameData>,
}
@@ -953,14 +925,14 @@ impl BoxFragment {
fn build_stacking_context_tree(
&self,
fragment: Fragment,
- display_list: &mut DisplayList,
+ stacking_context_tree: &mut StackingContextTree,
containing_block: &ContainingBlock,
containing_block_info: &ContainingBlockInfo,
parent_stacking_context: &mut StackingContext,
) {
self.build_stacking_context_tree_maybe_creating_reference_frame(
fragment,
- display_list,
+ stacking_context_tree,
containing_block,
containing_block_info,
parent_stacking_context,
@@ -970,7 +942,7 @@ impl BoxFragment {
fn build_stacking_context_tree_maybe_creating_reference_frame(
&self,
fragment: Fragment,
- display_list: &mut DisplayList,
+ stacking_context_tree: &mut StackingContextTree,
containing_block: &ContainingBlock,
containing_block_info: &ContainingBlockInfo,
parent_stacking_context: &mut StackingContext,
@@ -981,7 +953,7 @@ impl BoxFragment {
None => {
return self.build_stacking_context_tree_maybe_creating_stacking_context(
fragment,
- display_list,
+ stacking_context_tree,
containing_block,
containing_block_info,
parent_stacking_context,
@@ -989,11 +961,11 @@ impl BoxFragment {
},
};
- let new_spatial_id = display_list.push_reference_frame(
+ let new_spatial_id = stacking_context_tree.push_reference_frame(
reference_frame_data.origin.to_webrender(),
&containing_block.scroll_node_id,
self.style.get_box().transform_style.to_webrender(),
- wr::PropertyBinding::Value(reference_frame_data.transform),
+ reference_frame_data.transform,
reference_frame_data.kind,
);
@@ -1016,26 +988,24 @@ impl BoxFragment {
.translate(-reference_frame_data.origin.to_vector()),
new_spatial_id,
None,
- containing_block.clip_chain_id,
+ containing_block.clip_id,
);
let new_containing_block_info =
containing_block_info.new_for_non_absolute_descendants(&adjusted_containing_block);
self.build_stacking_context_tree_maybe_creating_stacking_context(
fragment,
- display_list,
+ stacking_context_tree,
&adjusted_containing_block,
&new_containing_block_info,
parent_stacking_context,
);
-
- display_list.pop_reference_frame();
}
fn build_stacking_context_tree_maybe_creating_stacking_context(
&self,
fragment: Fragment,
- display_list: &mut DisplayList,
+ stacking_context_tree: &mut StackingContextTree,
containing_block: &ContainingBlock,
containing_block_info: &ContainingBlockInfo,
parent_stacking_context: &mut StackingContext,
@@ -1045,7 +1015,7 @@ impl BoxFragment {
None => {
self.build_stacking_context_tree_for_children(
fragment,
- display_list,
+ stacking_context_tree,
containing_block,
containing_block_info,
parent_stacking_context,
@@ -1068,30 +1038,37 @@ impl BoxFragment {
// `clip-path` needs to be applied before filters and creates a stacking context, so it can be
// applied directly to the stacking context itself.
// before
- let stacking_context_clip_chain_id = build_clip_path_clip_chain_if_necessary(
- self.style.clone_clip_path(),
- display_list,
- &containing_block.scroll_node_id,
- &containing_block.clip_chain_id,
- BuilderForBoxFragment::new(
- self,
- &containing_block.rect,
- false, /* is_hit_test_for_scrollable_overflow */
- false, /* is_collapsed_table_borders */
- ),
- )
- .unwrap_or(containing_block.clip_chain_id);
+ let stacking_context_clip_id = stacking_context_tree
+ .clip_store
+ .add_for_clip_path(
+ self.style.clone_clip_path(),
+ &containing_block.scroll_node_id,
+ &containing_block.clip_id,
+ BuilderForBoxFragment::new(
+ self,
+ &containing_block.rect,
+ false, /* is_hit_test_for_scrollable_overflow */
+ false, /* is_collapsed_table_borders */
+ ),
+ )
+ .unwrap_or(containing_block.clip_id);
+
+ let box_fragment = match fragment {
+ Fragment::Box(ref box_fragment) | Fragment::Float(ref box_fragment) => {
+ box_fragment.clone()
+ },
+ _ => unreachable!("Should never try to make stacking context for non-BoxFragment"),
+ };
let mut child_stacking_context = parent_stacking_context.create_descendant(
- containing_block.scroll_node_id.spatial_id,
- stacking_context_clip_chain_id,
- self.style.clone(),
- self.base.flags,
+ containing_block.scroll_node_id,
+ stacking_context_clip_id,
+ box_fragment,
context_type,
);
self.build_stacking_context_tree_for_children(
fragment,
- display_list,
+ stacking_context_tree,
containing_block,
containing_block_info,
&mut child_stacking_context,
@@ -1116,19 +1093,19 @@ impl BoxFragment {
fn build_stacking_context_tree_for_children(
&self,
fragment: Fragment,
- display_list: &mut DisplayList,
+ stacking_context_tree: &mut StackingContextTree,
containing_block: &ContainingBlock,
containing_block_info: &ContainingBlockInfo,
stacking_context: &mut StackingContext,
) {
let mut new_scroll_node_id = containing_block.scroll_node_id;
- let mut new_clip_chain_id = containing_block.clip_chain_id;
+ let mut new_clip_id = containing_block.clip_id;
let mut new_scroll_frame_size = containing_block_info
.for_non_absolute_descendants
.scroll_frame_size;
if let Some(scroll_node_id) = self.build_sticky_frame_if_necessary(
- display_list,
+ stacking_context_tree,
&new_scroll_node_id,
&containing_block.rect,
&new_scroll_frame_size,
@@ -1136,20 +1113,19 @@ impl BoxFragment {
new_scroll_node_id = scroll_node_id;
}
- if let Some(clip_chain_id) = self.build_clip_frame_if_necessary(
- display_list,
+ if let Some(clip_id) = self.build_clip_frame_if_necessary(
+ stacking_context_tree,
&new_scroll_node_id,
- &new_clip_chain_id,
+ new_clip_id,
&containing_block.rect,
) {
- new_clip_chain_id = clip_chain_id;
+ new_clip_id = clip_id;
}
- if let Some(clip_chain_id) = build_clip_path_clip_chain_if_necessary(
+ if let Some(clip_id) = stacking_context_tree.clip_store.add_for_clip_path(
self.style.clone_clip_path(),
- display_list,
&new_scroll_node_id,
- &new_clip_chain_id,
+ &new_clip_id,
BuilderForBoxFragment::new(
self,
&containing_block.rect,
@@ -1157,7 +1133,7 @@ impl BoxFragment {
false, /* is_collapsed_table_borders */
),
) {
- new_clip_chain_id = clip_chain_id;
+ new_clip_id = clip_id;
}
let establishes_containing_block_for_all_descendants = self
@@ -1182,7 +1158,7 @@ impl BoxFragment {
.push(StackingContextContent::Fragment {
scroll_node_id: new_scroll_node_id,
reference_frame_scroll_node_id: reference_frame_scroll_node_id_for_fragments,
- clip_chain_id: new_clip_chain_id,
+ clip_id: new_clip_id,
section,
containing_block: containing_block.rect,
fragment: fragment.clone(),
@@ -1200,12 +1176,12 @@ impl BoxFragment {
// We want to build the scroll frame after the background and border, because
// they shouldn't scroll with the rest of the box content.
if let Some(overflow_frame_data) = self.build_overflow_frame_if_necessary(
- display_list,
+ stacking_context_tree,
&new_scroll_node_id,
- &new_clip_chain_id,
+ new_clip_id,
&containing_block.rect,
) {
- new_clip_chain_id = overflow_frame_data.clip_chain_id;
+ new_clip_id = overflow_frame_data.clip_id;
if let Some(scroll_frame_data) = overflow_frame_data.scroll_frame_data {
new_scroll_node_id = scroll_frame_data.scroll_tree_node_id;
new_scroll_frame_size = Some(scroll_frame_data.scroll_frame_rect.size());
@@ -1216,7 +1192,7 @@ impl BoxFragment {
scroll_node_id: new_scroll_node_id,
reference_frame_scroll_node_id:
reference_frame_scroll_node_id_for_fragments,
- clip_chain_id: new_clip_chain_id,
+ clip_id: new_clip_id,
section,
containing_block: containing_block.rect,
fragment: fragment.clone(),
@@ -1237,13 +1213,13 @@ impl BoxFragment {
padding_rect,
new_scroll_node_id,
new_scroll_frame_size,
- new_clip_chain_id,
+ new_clip_id,
);
let for_non_absolute_descendants = ContainingBlock::new(
content_rect,
new_scroll_node_id,
new_scroll_frame_size,
- new_clip_chain_id,
+ new_clip_id,
);
// Create a new `ContainingBlockInfo` for descendants depending on
@@ -1265,7 +1241,7 @@ impl BoxFragment {
for child in &self.children {
child.build_stacking_context_tree(
- display_list,
+ stacking_context_tree,
&new_containing_block_info,
stacking_context,
StackingContextBuildMode::SkipHoisted,
@@ -1281,7 +1257,7 @@ impl BoxFragment {
.push(StackingContextContent::Fragment {
scroll_node_id: new_scroll_node_id,
reference_frame_scroll_node_id: reference_frame_scroll_node_id_for_fragments,
- clip_chain_id: new_clip_chain_id,
+ clip_id: new_clip_id,
section,
containing_block: containing_block.rect,
fragment: fragment.clone(),
@@ -1293,11 +1269,11 @@ impl BoxFragment {
fn build_clip_frame_if_necessary(
&self,
- display_list: &mut DisplayList,
+ stacking_context_tree: &mut StackingContextTree,
parent_scroll_node_id: &ScrollTreeNodeId,
- parent_clip_chain_id: &wr::ClipChainId,
+ parent_clip_id: ClipId,
containing_block_rect: &PhysicalRect<Au>,
- ) -> Option<wr::ClipChainId> {
+ ) -> Option<ClipId> {
let position = self.style.get_box().position;
// https://drafts.csswg.org/css2/#clipping
// The clip property applies only to absolutely positioned elements
@@ -1316,18 +1292,19 @@ impl BoxFragment {
.for_border_rect(border_rect)
.translate(containing_block_rect.origin.to_vector())
.to_webrender();
-
- let clip_id = display_list
- .wr
- .define_clip_rect(parent_scroll_node_id.spatial_id, clip_rect);
- Some(display_list.define_clip_chain(*parent_clip_chain_id, [clip_id]))
+ Some(stacking_context_tree.clip_store.add(
+ BorderRadius::zero(),
+ clip_rect,
+ *parent_scroll_node_id,
+ parent_clip_id,
+ ))
}
fn build_overflow_frame_if_necessary(
&self,
- display_list: &mut DisplayList,
+ stacking_context_tree: &mut StackingContextTree,
parent_scroll_node_id: &ScrollTreeNodeId,
- parent_clip_chain_id: &wr::ClipChainId,
+ parent_clip_id: ClipId,
containing_block_rect: &PhysicalRect<Au>,
) -> Option<OverflowFrameData> {
let overflow = self.style.effective_overflow(self.base.flags);
@@ -1367,15 +1344,15 @@ impl BoxFragment {
radii = BorderRadius::zero();
}
- let clip_chain_id = display_list.clip_overflow_frame(
- parent_scroll_node_id,
- parent_clip_chain_id,
- overflow_clip_rect,
+ let clip_id = stacking_context_tree.clip_store.add(
radii,
+ overflow_clip_rect,
+ *parent_scroll_node_id,
+ parent_clip_id,
);
return Some(OverflowFrameData {
- clip_chain_id,
+ clip_id,
scroll_frame_data: None,
});
}
@@ -1404,17 +1381,17 @@ impl BoxFragment {
.translate(containing_block_rect.origin.to_vector())
.to_webrender();
- let clip_chain_id = display_list.clip_overflow_frame(
- parent_scroll_node_id,
- parent_clip_chain_id,
- scroll_frame_rect,
+ let clip_id = stacking_context_tree.clip_store.add(
BuilderForBoxFragment::new(self, containing_block_rect, false, false).border_radius,
+ scroll_frame_rect,
+ *parent_scroll_node_id,
+ parent_clip_id,
);
let tag = self.base.tag?;
let external_id = wr::ExternalScrollId(
tag.to_display_list_fragment_id(),
- display_list.wr.pipeline_id,
+ stacking_context_tree.compositor_info.pipeline_id,
);
let sensitivity = AxesScrollSensitivity {
@@ -1424,7 +1401,7 @@ impl BoxFragment {
let content_rect = self.reachable_scrollable_overflow_region().to_webrender();
- let scroll_tree_node_id = display_list.define_scroll_frame(
+ let scroll_tree_node_id = stacking_context_tree.define_scroll_frame(
parent_scroll_node_id,
external_id,
content_rect,
@@ -1433,7 +1410,7 @@ impl BoxFragment {
);
Some(OverflowFrameData {
- clip_chain_id,
+ clip_id,
scroll_frame_data: Some(ScrollFrameData {
scroll_tree_node_id,
scroll_frame_rect,
@@ -1443,7 +1420,7 @@ impl BoxFragment {
fn build_sticky_frame_if_necessary(
&self,
- display_list: &mut DisplayList,
+ stacking_context_tree: &mut StackingContextTree,
parent_scroll_node_id: &ScrollTreeNodeId,
containing_block_rect: &PhysicalRect<Au>,
scroll_frame_size: &Option<LayoutSize>,
@@ -1456,7 +1433,7 @@ impl BoxFragment {
Some(size) => size,
None => {
// This is a direct descendant of a reference frame.
- &display_list.compositor_info.viewport_size
+ &stacking_context_tree.compositor_info.viewport_size
},
};
@@ -1513,7 +1490,7 @@ impl BoxFragment {
offsets.left.non_auto().map(|v| v.to_f32_px()),
);
- let sticky_node_id = display_list.define_sticky_frame(
+ let sticky_node_id = stacking_context_tree.define_sticky_frame(
parent_scroll_node_id,
frame_rect,
margins,
@@ -1665,7 +1642,7 @@ impl BoxFragment {
impl PositioningFragment {
fn build_stacking_context_tree(
&self,
- display_list: &mut DisplayList,
+ stacking_context_tree: &mut StackingContextTree,
containing_block: &ContainingBlock,
containing_block_info: &ContainingBlockInfo,
stacking_context: &mut StackingContext,
@@ -1679,7 +1656,7 @@ impl PositioningFragment {
for child in &self.children {
child.build_stacking_context_tree(
- display_list,
+ stacking_context_tree,
&new_containing_block_info,
stacking_context,
StackingContextBuildMode::SkipHoisted,
diff --git a/components/layout/dom.rs b/components/layout/dom.rs
index e3a22eb5197..88176ffbbb0 100644
--- a/components/layout/dom.rs
+++ b/components/layout/dom.rs
@@ -126,15 +126,15 @@ impl LayoutBox {
.repair_style(context, node, new_style);
}
},
- LayoutBox::FlexLevel(flex_level_box) => {
- flex_level_box.borrow_mut().repair_style(context, new_style)
- },
+ LayoutBox::FlexLevel(flex_level_box) => flex_level_box
+ .borrow_mut()
+ .repair_style(context, node, new_style),
LayoutBox::TableLevelBox(table_level_box) => {
- table_level_box.repair_style(context, new_style)
- },
- LayoutBox::TaffyItemBox(taffy_item_box) => {
- taffy_item_box.borrow_mut().repair_style(context, new_style)
+ table_level_box.repair_style(context, node, new_style)
},
+ LayoutBox::TaffyItemBox(taffy_item_box) => taffy_item_box
+ .borrow_mut()
+ .repair_style(context, node, new_style),
}
}
}
diff --git a/components/layout/flexbox/mod.rs b/components/layout/flexbox/mod.rs
index 91a12b31812..96a311ee2b5 100644
--- a/components/layout/flexbox/mod.rs
+++ b/components/layout/flexbox/mod.rs
@@ -4,6 +4,7 @@
use geom::{FlexAxis, MainStartCrossStart};
use malloc_size_of_derive::MallocSizeOf;
+use script::layout_dom::ServoLayoutNode;
use servo_arc::Arc as ServoArc;
use style::context::SharedStyleContext;
use style::logical_geometry::WritingMode;
@@ -154,16 +155,17 @@ impl FlexLevelBox {
pub(crate) fn repair_style(
&mut self,
context: &SharedStyleContext,
+ node: &ServoLayoutNode,
new_style: &ServoArc<ComputedValues>,
) {
match self {
FlexLevelBox::FlexItem(flex_item_box) => flex_item_box
.independent_formatting_context
- .repair_style(context, new_style),
+ .repair_style(context, node, new_style),
FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => positioned_box
.borrow_mut()
.context
- .repair_style(context, new_style),
+ .repair_style(context, node, new_style),
}
}
diff --git a/components/layout/flow/inline/construct.rs b/components/layout/flow/inline/construct.rs
index a99de1679a4..600da9b721a 100644
--- a/components/layout/flow/inline/construct.rs
+++ b/components/layout/flow/inline/construct.rs
@@ -31,7 +31,7 @@ pub(crate) struct InlineFormattingContextBuilder {
/// inline box stack, and importantly, one for every `display: contents` element that we are
/// currently processing. Normally `display: contents` elements don't affect the structure of
/// the [`InlineFormattingContext`], but the styles they provide do style their children.
- shared_inline_styles_stack: Vec<SharedInlineStyles>,
+ pub shared_inline_styles_stack: Vec<SharedInlineStyles>,
/// The collection of text strings that make up this [`InlineFormattingContext`] under
/// construction.
diff --git a/components/layout/flow/inline/mod.rs b/components/layout/flow/inline/mod.rs
index 7e69aa1aaae..74d42ca6fb4 100644
--- a/components/layout/flow/inline/mod.rs
+++ b/components/layout/flow/inline/mod.rs
@@ -91,6 +91,7 @@ use line_breaker::LineBreaker;
use malloc_size_of_derive::MallocSizeOf;
use range::Range;
use script::layout_dom::ServoLayoutNode;
+use script_layout_interface::wrapper_traits::{LayoutNode, ThreadSafeLayoutNode};
use servo_arc::Arc;
use style::Zero;
use style::computed_values::text_wrap_mode::T as TextWrapMode;
@@ -158,6 +159,10 @@ pub(crate) struct InlineFormattingContext {
/// context in order to avoid duplicating this information.
pub font_metrics: Vec<FontKeyAndMetrics>,
+ /// The [`SharedInlineStyles`] for the root of this [`InlineFormattingContext`] that are used to
+ /// share styles with all [`TextRun`] children.
+ pub(super) shared_inline_styles: SharedInlineStyles,
+
pub(super) text_decoration_line: TextDecorationLine,
/// Whether this IFC contains the 1st formatted line of an element:
@@ -237,12 +242,14 @@ impl InlineItem {
InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => positioned_box
.borrow_mut()
.context
- .repair_style(context, new_style),
+ .repair_style(context, node, new_style),
InlineItem::OutOfFlowFloatBox(float_box) => float_box
.borrow_mut()
.contents
- .repair_style(context, new_style),
- InlineItem::Atomic(atomic, ..) => atomic.borrow_mut().repair_style(context, new_style),
+ .repair_style(context, node, new_style),
+ InlineItem::Atomic(atomic, ..) => {
+ atomic.borrow_mut().repair_style(context, node, new_style)
+ },
}
}
@@ -1699,6 +1706,11 @@ impl InlineFormattingContext {
inline_items: builder.inline_items,
inline_boxes: builder.inline_boxes,
font_metrics,
+ shared_inline_styles: builder
+ .shared_inline_styles_stack
+ .last()
+ .expect("Should have at least one SharedInlineStyle for the root of an IFC")
+ .clone(),
text_decoration_line: propagated_data.text_decoration,
has_first_formatted_line,
contains_floats: builder.contains_floats,
@@ -1707,6 +1719,11 @@ impl InlineFormattingContext {
}
}
+ pub(crate) fn repair_style(&self, node: &ServoLayoutNode, new_style: &Arc<ComputedValues>) {
+ *self.shared_inline_styles.style.borrow_mut() = new_style.clone();
+ *self.shared_inline_styles.selected.borrow_mut() = node.to_threadsafe().selected_style();
+ }
+
pub(super) fn layout(
&self,
layout_context: &LayoutContext,
diff --git a/components/layout/flow/mod.rs b/components/layout/flow/mod.rs
index 99b84d088e5..4776b65771c 100644
--- a/components/layout/flow/mod.rs
+++ b/components/layout/flow/mod.rs
@@ -78,6 +78,15 @@ impl BlockContainer {
BlockContainer::InlineFormattingContext(context) => context.contains_floats,
}
}
+
+ pub(crate) fn repair_style(&mut self, node: &ServoLayoutNode, new_style: &Arc<ComputedValues>) {
+ match self {
+ BlockContainer::BlockLevelBoxes(..) => {},
+ BlockContainer::InlineFormattingContext(inline_formatting_context) => {
+ inline_formatting_context.repair_style(node, new_style)
+ },
+ }
+ }
}
#[derive(Debug, MallocSizeOf)]
@@ -106,20 +115,21 @@ impl BlockLevelBox {
match self {
BlockLevelBox::Independent(independent_formatting_context) => {
- independent_formatting_context.repair_style(context, new_style)
+ independent_formatting_context.repair_style(context, node, new_style)
},
BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => positioned_box
.borrow_mut()
.context
- .repair_style(context, new_style),
+ .repair_style(context, node, new_style),
BlockLevelBox::OutOfFlowFloatBox(float_box) => {
- float_box.contents.repair_style(context, new_style)
+ float_box.contents.repair_style(context, node, new_style)
},
BlockLevelBox::OutsideMarker(outside_marker) => {
outside_marker.repair_style(context, node, new_style)
},
- BlockLevelBox::SameFormattingContextBlock { base, .. } => {
+ BlockLevelBox::SameFormattingContextBlock { base, contents, .. } => {
base.repair_style(new_style);
+ contents.repair_style(node, new_style);
},
}
}
@@ -477,6 +487,10 @@ impl BlockFormattingContext {
pub(crate) fn layout_style<'a>(&self, base: &'a LayoutBoxBase) -> LayoutStyle<'a> {
LayoutStyle::Default(&base.style)
}
+
+ pub(crate) fn repair_style(&mut self, node: &ServoLayoutNode, new_style: &Arc<ComputedValues>) {
+ self.contents.repair_style(node, new_style);
+ }
}
/// Finds the min/max-content inline size of the block-level children of a block container.
diff --git a/components/layout/formatting_contexts.rs b/components/layout/formatting_contexts.rs
index a489df2b663..2b242c00361 100644
--- a/components/layout/formatting_contexts.rs
+++ b/components/layout/formatting_contexts.rs
@@ -4,7 +4,7 @@
use app_units::Au;
use malloc_size_of_derive::MallocSizeOf;
-use script::layout_dom::ServoLayoutElement;
+use script::layout_dom::{ServoLayoutElement, ServoLayoutNode};
use servo_arc::Arc;
use style::context::SharedStyleContext;
use style::properties::ComputedValues;
@@ -223,12 +223,13 @@ impl IndependentFormattingContext {
pub(crate) fn repair_style(
&mut self,
context: &SharedStyleContext,
+ node: &ServoLayoutNode,
new_style: &Arc<ComputedValues>,
) {
self.base.repair_style(new_style);
match &mut self.contents {
IndependentFormattingContextContents::NonReplaced(content) => {
- content.repair_style(context, new_style);
+ content.repair_style(context, node, new_style);
},
IndependentFormattingContextContents::Replaced(..) => {},
}
@@ -356,9 +357,16 @@ impl IndependentNonReplacedContents {
matches!(self, Self::Table(_))
}
- fn repair_style(&mut self, context: &SharedStyleContext, new_style: &Arc<ComputedValues>) {
+ fn repair_style(
+ &mut self,
+ context: &SharedStyleContext,
+ node: &ServoLayoutNode,
+ new_style: &Arc<ComputedValues>,
+ ) {
match self {
- IndependentNonReplacedContents::Flow(..) => {},
+ IndependentNonReplacedContents::Flow(block_formatting_context) => {
+ block_formatting_context.repair_style(node, new_style);
+ },
IndependentNonReplacedContents::Flex(flex_container) => {
flex_container.repair_style(new_style)
},
diff --git a/components/layout/fragment_tree/box_fragment.rs b/components/layout/fragment_tree/box_fragment.rs
index 9b96b1c4fb4..b7c3a2a3524 100644
--- a/components/layout/fragment_tree/box_fragment.rs
+++ b/components/layout/fragment_tree/box_fragment.rs
@@ -92,7 +92,7 @@ pub(crate) struct BoxFragment {
pub scrollable_overflow_from_children: PhysicalRect<Au>,
/// The resolved box insets if this box is `position: sticky`. These are calculated
- /// during stacking context tree construction because they rely on the size of the
+ /// during `StackingContextTree` construction because they rely on the size of the
/// scroll container.
pub(crate) resolved_sticky_insets: AtomicRefCell<Option<PhysicalSides<AuOrAuto>>>,
diff --git a/components/layout/fragment_tree/fragment_tree.rs b/components/layout/fragment_tree/fragment_tree.rs
index 979bd0090fc..ba03a72ac21 100644
--- a/components/layout/fragment_tree/fragment_tree.rs
+++ b/components/layout/fragment_tree/fragment_tree.rs
@@ -14,7 +14,6 @@ use webrender_api::units;
use super::{BoxFragment, ContainingBlockManager, Fragment};
use crate::ArcRefCell;
use crate::context::LayoutContext;
-use crate::display_list::StackingContext;
use crate::geom::PhysicalRect;
#[derive(MallocSizeOf)]
@@ -91,16 +90,6 @@ impl FragmentTree {
fragment_tree
}
- pub(crate) fn build_display_list(
- &self,
- builder: &mut crate::display_list::DisplayListBuilder,
- root_stacking_context: &StackingContext,
- ) {
- // Paint the canvas’ background (if any) before/under everything else
- root_stacking_context.build_canvas_background_display_list(builder, self);
- root_stacking_context.build_display_list(builder);
- }
-
pub fn print(&self) {
let mut print_tree = PrintTree::new("Fragment Tree".to_string());
for fragment in &self.root_fragments {
diff --git a/components/layout/layout_impl.rs b/components/layout/layout_impl.rs
index fcf658036b2..b490b4a0506 100644
--- a/components/layout/layout_impl.rs
+++ b/components/layout/layout_impl.rs
@@ -77,7 +77,7 @@ use webrender_api::units::{DevicePixel, DevicePoint, LayoutPixel, LayoutPoint, L
use webrender_api::{ExternalScrollId, HitTestFlags};
use crate::context::LayoutContext;
-use crate::display_list::{DisplayList, WebRenderImageInfo};
+use crate::display_list::{DisplayListBuilder, StackingContextTree, WebRenderImageInfo};
use crate::query::{
get_the_text_steps, process_client_rect_request, process_content_box_request,
process_content_boxes_request, process_node_scroll_area_request, process_offset_parent_query,
@@ -144,6 +144,9 @@ pub struct LayoutThread {
/// The fragment tree.
fragment_tree: RefCell<Option<Arc<FragmentTree>>>,
+ /// The [`StackingContextTree`] cached from previous layouts.
+ stacking_context_tree: RefCell<Option<StackingContextTree>>,
+
/// A counter for epoch messages
epoch: Cell<Epoch>,
@@ -521,6 +524,7 @@ impl LayoutThread {
first_reflow: Cell::new(true),
box_tree: Default::default(),
fragment_tree: Default::default(),
+ stacking_context_tree: Default::default(),
// Epoch starts at 1 because of the initial display list for epoch 0 that we send to WR
epoch: Cell::new(Epoch(1)),
viewport_size: Size2D::new(
@@ -649,15 +653,17 @@ impl LayoutThread {
highlighted_dom_node: reflow_request.highlighted_dom_node,
};
- self.restyle_and_build_trees(
+ let did_reflow = self.restyle_and_build_trees(
&reflow_request,
root_element,
rayon_pool,
&mut layout_context,
);
+
+ self.build_stacking_context_tree(&reflow_request, did_reflow);
self.build_display_list(&reflow_request, &mut layout_context);
- self.first_reflow.set(false);
+ self.first_reflow.set(false);
if let ReflowGoal::UpdateScrollNode(scroll_state) = reflow_request.reflow_goal {
self.update_scroll_node_state(&scroll_state);
}
@@ -666,6 +672,7 @@ impl LayoutThread {
let iframe_sizes = std::mem::take(&mut *layout_context.iframe_sizes.lock());
let node_to_image_animation_map =
std::mem::take(&mut *layout_context.node_image_animation_map.write());
+
Some(ReflowResult {
pending_images,
iframe_sizes,
@@ -742,7 +749,7 @@ impl LayoutThread {
root_element: ServoLayoutElement<'_>,
rayon_pool: Option<&ThreadPool>,
layout_context: &mut LayoutContext<'_>,
- ) {
+ ) -> bool {
let dirty_root = unsafe {
ServoLayoutNode::new(&reflow_request.dirty_root.unwrap())
.as_element()
@@ -758,7 +765,7 @@ impl LayoutThread {
if !token.should_traverse() {
layout_context.style_context.stylist.rule_tree().maybe_gc();
- return;
+ return false;
}
let dirty_root: ServoLayoutNode =
@@ -768,7 +775,7 @@ impl LayoutThread {
let damage = compute_damage_and_repair_style(layout_context.shared_context(), root_node);
if damage == RestyleDamage::REPAINT {
layout_context.style_context.stylist.rule_tree().maybe_gc();
- return;
+ return false;
}
let mut box_tree = self.box_tree.borrow_mut();
@@ -803,8 +810,15 @@ impl LayoutThread {
run_layout()
});
+ if self.debug.dump_flow_tree {
+ fragment_tree.print();
+ }
*self.fragment_tree.borrow_mut() = Some(fragment_tree);
+ // The FragmentTree has been updated, so any existing StackingContext tree that layout
+ // had is now out of date and should be rebuilt.
+ *self.stacking_context_tree.borrow_mut() = None;
+
if self.debug.dump_style_tree {
println!(
"{:?}",
@@ -822,75 +836,80 @@ impl LayoutThread {
// GC the rule tree if some heuristics are met.
layout_context.style_context.stylist.rule_tree().maybe_gc();
+ true
}
- fn build_display_list(
- &self,
- reflow_request: &ReflowRequest,
- layout_context: &mut LayoutContext<'_>,
- ) {
+ fn build_stacking_context_tree(&self, reflow_request: &ReflowRequest, did_reflow: bool) {
+ if !reflow_request.reflow_goal.needs_display_list() &&
+ !reflow_request.reflow_goal.needs_display()
+ {
+ return;
+ }
let Some(fragment_tree) = &*self.fragment_tree.borrow() else {
return;
};
- if !reflow_request.reflow_goal.needs_display_list() {
+ if !did_reflow && self.stacking_context_tree.borrow().is_some() {
return;
}
- let mut epoch = self.epoch.get();
- epoch.next();
- self.epoch.set(epoch);
-
let viewport_size = LayoutSize::from_untyped(Size2D::new(
self.viewport_size.width.to_f32_px(),
self.viewport_size.height.to_f32_px(),
));
- let mut display_list = DisplayList::new(
+
+ // Build the StackingContextTree. This turns the `FragmentTree` into a
+ // tree of fragments in CSS painting order and also creates all
+ // applicable spatial and clip nodes.
+ *self.stacking_context_tree.borrow_mut() = Some(StackingContextTree::new(
+ fragment_tree,
viewport_size,
fragment_tree.scrollable_overflow(),
self.id.into(),
- epoch.into(),
fragment_tree.viewport_scroll_sensitivity,
self.first_reflow.get(),
- );
- display_list.wr.begin();
-
- // `dump_serialized_display_list` doesn't actually print anything. It sets up
- // the display list for printing the serialized version when `finalize()` is called.
- // We need to call this before adding any display items so that they are printed
- // during `finalize()`.
- if self.debug.dump_display_list {
- display_list.wr.dump_serialized_display_list();
- }
+ &self.debug,
+ ));
+ }
- // Build the root stacking context. This turns the `FragmentTree` into a
- // tree of fragments in CSS painting order and also creates all
- // applicable spatial and clip nodes.
- let root_stacking_context =
- display_list.build_stacking_context_tree(fragment_tree, &self.debug);
+ fn build_display_list(
+ &self,
+ reflow_request: &ReflowRequest,
+ layout_context: &mut LayoutContext<'_>,
+ ) {
+ if !reflow_request.reflow_goal.needs_display() {
+ return;
+ }
+ let Some(fragment_tree) = &*self.fragment_tree.borrow() else {
+ return;
+ };
- // Build the rest of the display list which inclues all of the WebRender primitives.
- display_list.build(layout_context, fragment_tree, &root_stacking_context);
+ let mut stacking_context_tree = self.stacking_context_tree.borrow_mut();
+ let Some(stacking_context_tree) = stacking_context_tree.as_mut() else {
+ return;
+ };
- if self.debug.dump_flow_tree {
- fragment_tree.print();
- }
- if self.debug.dump_stacking_context_tree {
- root_stacking_context.debug_print();
- }
+ let mut epoch = self.epoch.get();
+ epoch.next();
+ self.epoch.set(epoch);
+ stacking_context_tree.compositor_info.epoch = epoch.into();
- if reflow_request.reflow_goal.needs_display() {
- self.compositor_api.send_display_list(
- self.webview_id,
- display_list.compositor_info,
- display_list.wr.end().1,
- );
+ let built_display_list = DisplayListBuilder::build(
+ layout_context,
+ stacking_context_tree,
+ fragment_tree,
+ &self.debug,
+ );
+ self.compositor_api.send_display_list(
+ self.webview_id,
+ &stacking_context_tree.compositor_info,
+ built_display_list,
+ );
- let (keys, instance_keys) = self
- .font_context
- .collect_unused_webrender_resources(false /* all */);
- self.compositor_api
- .remove_unused_font_resources(keys, instance_keys)
- }
+ let (keys, instance_keys) = self
+ .font_context
+ .collect_unused_webrender_resources(false /* all */);
+ self.compositor_api
+ .remove_unused_font_resources(keys, instance_keys)
}
fn update_scroll_node_state(&self, state: &ScrollState) {
diff --git a/components/layout/table/mod.rs b/components/layout/table/mod.rs
index 72b67863e7d..78884c377e9 100644
--- a/components/layout/table/mod.rs
+++ b/components/layout/table/mod.rs
@@ -76,7 +76,7 @@ pub(crate) use construct::AnonymousTableContent;
pub use construct::TableBuilder;
use euclid::{Point2D, Size2D, UnknownUnit, Vector2D};
use malloc_size_of_derive::MallocSizeOf;
-use script::layout_dom::ServoLayoutElement;
+use script::layout_dom::{ServoLayoutElement, ServoLayoutNode};
use servo_arc::Arc;
use style::context::SharedStyleContext;
use style::properties::ComputedValues;
@@ -425,13 +425,14 @@ impl TableLevelBox {
pub(crate) fn repair_style(
&self,
context: &SharedStyleContext<'_>,
+ node: &ServoLayoutNode,
new_style: &Arc<ComputedValues>,
) {
match self {
TableLevelBox::Caption(caption) => caption
.borrow_mut()
.context
- .repair_style(context, new_style),
+ .repair_style(context, node, new_style),
TableLevelBox::Cell(cell) => cell.borrow_mut().repair_style(new_style),
TableLevelBox::TrackGroup(track_group) => {
track_group.borrow_mut().repair_style(new_style);
diff --git a/components/layout/taffy/mod.rs b/components/layout/taffy/mod.rs
index ba80824fa99..2bc7a598d08 100644
--- a/components/layout/taffy/mod.rs
+++ b/components/layout/taffy/mod.rs
@@ -7,6 +7,7 @@ use std::fmt;
use app_units::Au;
use malloc_size_of_derive::MallocSizeOf;
+use script::layout_dom::ServoLayoutNode;
use servo_arc::Arc;
use style::context::SharedStyleContext;
use style::properties::ComputedValues;
@@ -152,17 +153,18 @@ impl TaffyItemBox {
pub(crate) fn repair_style(
&mut self,
context: &SharedStyleContext,
+ node: &ServoLayoutNode,
new_style: &Arc<ComputedValues>,
) {
self.style = new_style.clone();
match &mut self.taffy_level_box {
TaffyItemBoxInner::InFlowBox(independent_formatting_context) => {
- independent_formatting_context.repair_style(context, new_style)
+ independent_formatting_context.repair_style(context, node, new_style)
},
TaffyItemBoxInner::OutOfFlowAbsolutelyPositionedBox(positioned_box) => positioned_box
.borrow_mut()
.context
- .repair_style(context, new_style),
+ .repair_style(context, node, new_style),
}
}
}
diff --git a/components/layout/traversal.rs b/components/layout/traversal.rs
index 17c3d0b1c20..d05deb24bfa 100644
--- a/components/layout/traversal.rs
+++ b/components/layout/traversal.rs
@@ -112,15 +112,14 @@ pub(crate) fn compute_damage_and_repair_style_inner(
.element_data
.borrow_mut();
+ original_damage = std::mem::take(&mut element_data.damage);
if let Some(ref style) = element_data.styles.primary {
if style.get_box().display == Display::None {
return parent_restyle_damage;
}
}
- original_damage = std::mem::take(&mut element_data.damage);
- element_data.damage |= parent_restyle_damage;
- element_data.damage
+ original_damage | parent_restyle_damage
};
let mut propagated_damage = damage;
diff --git a/components/net/Cargo.toml b/components/net/Cargo.toml
index 9af310f32bc..1945165b8c1 100644
--- a/components/net/Cargo.toml
+++ b/components/net/Cargo.toml
@@ -29,6 +29,7 @@ crossbeam-channel = { workspace = true }
data-url = { workspace = true }
devtools_traits = { workspace = true }
embedder_traits = { workspace = true }
+fst = "0.4"
futures = { version = "0.3", package = "futures" }
futures-core = { version = "0.3.30", default-features = false }
futures-util = { version = "0.3.30", default-features = false }
@@ -77,6 +78,7 @@ webrender_api = { workspace = true }
[dev-dependencies]
embedder_traits = { workspace = true, features = ["baked-default-resources"] }
flate2 = "1"
+fst = "0.4"
futures = { version = "0.3", features = ["compat"] }
hyper = { workspace = true, features = ["full"] }
hyper-util = { workspace = true, features = ["server-graceful"] }
diff --git a/components/net/fetch/methods.rs b/components/net/fetch/methods.rs
index 65c173fce3b..33d0da952fb 100644
--- a/components/net/fetch/methods.rs
+++ b/components/net/fetch/methods.rs
@@ -18,6 +18,7 @@ use http::{HeaderValue, Method, StatusCode};
use ipc_channel::ipc;
use log::{debug, trace, warn};
use mime::{self, Mime};
+use net_traits::fetch::headers::extract_mime_type_as_mime;
use net_traits::filemanager_thread::{FileTokenCheck, RelativePos};
use net_traits::http_status::HttpStatus;
use net_traits::policy_container::{PolicyContainer, RequestPolicyContainer};
@@ -886,7 +887,7 @@ pub fn should_be_blocked_due_to_nosniff(
// Step 2
// Note: an invalid MIME type will produce a `None`.
- let content_type_header = response_headers.typed_get::<ContentType>();
+ let mime_type = extract_mime_type_as_mime(response_headers);
/// <https://html.spec.whatwg.org/multipage/#scriptingLanguages>
#[inline]
@@ -915,16 +916,12 @@ pub fn should_be_blocked_due_to_nosniff(
.any(|mime| mime.type_() == mime_type.type_() && mime.subtype() == mime_type.subtype())
}
- match content_type_header {
+ match mime_type {
// Step 4
- Some(ref ct) if destination.is_script_like() => {
- !is_javascript_mime_type(&ct.clone().into())
- },
-
+ Some(ref mime_type) if destination.is_script_like() => !is_javascript_mime_type(mime_type),
// Step 5
- Some(ref ct) if destination == Destination::Style => {
- let m: mime::Mime = ct.clone().into();
- m.type_() != mime::TEXT && m.subtype() != mime::CSS
+ Some(ref mime_type) if destination == Destination::Style => {
+ mime_type.type_() != mime::TEXT && mime_type.subtype() != mime::CSS
},
None if destination == Destination::Style || destination.is_script_like() => true,
@@ -938,18 +935,22 @@ fn should_be_blocked_due_to_mime_type(
destination: Destination,
response_headers: &HeaderMap,
) -> bool {
- // Step 1
- let mime_type: mime::Mime = match response_headers.typed_get::<ContentType>() {
- Some(header) => header.into(),
+ // Step 1: Let mimeType be the result of extracting a MIME type from response’s header list.
+ let mime_type: mime::Mime = match extract_mime_type_as_mime(response_headers) {
+ Some(mime_type) => mime_type,
+ // Step 2: If mimeType is failure, then return allowed.
None => return false,
};
- // Step 2-3
+ // Step 3: Let destination be request’s destination.
+ // Step 4: If destination is script-like and one of the following is true, then return blocked:
+ // - mimeType’s essence starts with "audio/", "image/", or "video/".
+ // - mimeType’s essence is "text/csv".
+ // Step 5: Return allowed.
destination.is_script_like() &&
match mime_type.type_() {
mime::AUDIO | mime::VIDEO | mime::IMAGE => true,
mime::TEXT if mime_type.subtype() == mime::CSV => true,
- // Step 4
_ => false,
}
}
diff --git a/components/net/hsts.rs b/components/net/hsts.rs
index be955980d2b..c50f3304142 100644
--- a/components/net/hsts.rs
+++ b/components/net/hsts.rs
@@ -9,9 +9,11 @@ use std::sync::LazyLock;
use std::time::Duration;
use embedder_traits::resources::{self, Resource};
+use fst::{Map, MapBuilder};
use headers::{HeaderMapExt, StrictTransportSecurity};
use http::HeaderMap;
use log::{debug, error, info};
+use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use malloc_size_of_derive::MallocSizeOf;
use net_traits::IncludeSubdomains;
use net_traits::pub_domains::reg_suffix;
@@ -85,99 +87,67 @@ pub struct HstsList {
/// it is split out to allow sharing between the private and public http state
/// as well as potentially swpaping out the underlying type to something immutable
/// and more efficient like FSTs or DAFSA/DAWGs.
-#[derive(Clone, Debug, Default, Deserialize, MallocSizeOf, Serialize)]
-pub struct HstsPreloadList {
- pub entries_map: HashMap<String, Vec<HstsEntry>>,
+/// To generate a new version of the FST map file run `./mach update-hsts-preload`
+#[derive(Clone, Debug)]
+pub struct HstsPreloadList(pub fst::Map<Vec<u8>>);
+
+impl MallocSizeOf for HstsPreloadList {
+ #[allow(unsafe_code)]
+ fn size_of(&self, ops: &mut malloc_size_of::MallocSizeOfOps) -> usize {
+ unsafe { ops.malloc_size_of(self.0.as_fst().as_inner().as_ptr()) }
+ }
}
-pub static PRELOAD_LIST_ENTRIES: LazyLock<HstsPreloadList> =
+static PRELOAD_LIST_ENTRIES: LazyLock<HstsPreloadList> =
LazyLock::new(HstsPreloadList::from_servo_preload);
+pub fn hsts_preload_size_of(ops: &mut MallocSizeOfOps) -> usize {
+ PRELOAD_LIST_ENTRIES.size_of(ops)
+}
+
impl HstsPreloadList {
/// Create an `HstsList` from the bytes of a JSON preload file.
- pub fn from_preload(preload_content: &str) -> Option<HstsPreloadList> {
- #[derive(Deserialize)]
- struct HstsEntries {
- entries: Vec<HstsEntry>,
- }
-
- let hsts_entries: Option<HstsEntries> = serde_json::from_str(preload_content).ok();
-
- hsts_entries.map(|hsts_entries| {
- let mut hsts_list: HstsPreloadList = HstsPreloadList::default();
-
- for hsts_entry in hsts_entries.entries {
- hsts_list.push(hsts_entry);
- }
-
- hsts_list
- })
+ pub fn from_preload(preload_content: Vec<u8>) -> Option<HstsPreloadList> {
+ Map::new(preload_content).map(HstsPreloadList).ok()
}
pub fn from_servo_preload() -> HstsPreloadList {
debug!("Intializing HSTS Preload list");
- let list = resources::read_string(Resource::HstsPreloadList);
- HstsPreloadList::from_preload(&list).unwrap_or_else(|| {
+ let map_bytes = resources::read_bytes(Resource::HstsPreloadList);
+ HstsPreloadList::from_preload(map_bytes).unwrap_or_else(|| {
error!("HSTS preload file is invalid. Setting HSTS list to default values");
- HstsPreloadList {
- entries_map: Default::default(),
- }
+ HstsPreloadList(MapBuilder::memory().into_map())
})
}
pub fn is_host_secure(&self, host: &str) -> bool {
let base_domain = reg_suffix(host);
- self.entries_map.get(base_domain).is_some_and(|entries| {
- // No need to check for expiration in the preload list
- entries.iter().any(|e| {
- if e.include_subdomains {
- e.matches_subdomain(host) || e.matches_domain(host)
- } else {
- e.matches_domain(host)
- }
- })
- })
- }
+ let parts = host[..host.len() - base_domain.len()].rsplit_terminator('.');
+ let mut domain_to_test = base_domain.to_owned();
- pub fn has_domain(&self, host: &str, base_domain: &str) -> bool {
- self.entries_map
- .get(base_domain)
- .is_some_and(|entries| entries.iter().any(|e| e.matches_domain(host)))
- }
-
- pub fn has_subdomain(&self, host: &str, base_domain: &str) -> bool {
- self.entries_map.get(base_domain).is_some_and(|entries| {
- entries
- .iter()
- .any(|e| e.include_subdomains && e.matches_subdomain(host))
- })
- }
-
- pub fn push(&mut self, entry: HstsEntry) {
- let host = entry.host.clone();
- let base_domain = reg_suffix(&host);
- let have_domain = self.has_domain(&entry.host, base_domain);
- let have_subdomain = self.has_subdomain(&entry.host, base_domain);
+ if self.0.get(&domain_to_test).is_some_and(|id| {
+ // The FST map ids were constructed such that the parity represents the includeSubdomain flag
+ id % 2 == 1 || domain_to_test == host
+ }) {
+ return true;
+ }
- let entries = self.entries_map.entry(base_domain.to_owned()).or_default();
- if !have_domain && !have_subdomain {
- entries.push(entry);
- } else if !have_subdomain {
- for e in entries {
- if e.matches_domain(&entry.host) {
- e.include_subdomains = entry.include_subdomains;
- // TODO(sebsebmc): We could shrink the the HSTS preload memory use further by using a type
- // that doesn't store an expiry since all preload entries should be "forever"
- e.expires_at = entry.expires_at;
- }
+ // Check all further subdomains up to the passed host
+ for part in parts {
+ domain_to_test = format!("{}.{}", part, domain_to_test);
+ if self.0.get(&domain_to_test).is_some_and(|id| {
+ // The FST map ids were constructed such that the parity represents the includeSubdomain flag
+ id % 2 == 1 || domain_to_test == host
+ }) {
+ return true;
}
}
+ false
}
}
impl HstsList {
pub fn is_host_secure(&self, host: &str) -> bool {
- debug!("HSTS: is {host} secure?");
if PRELOAD_LIST_ENTRIES.is_host_secure(host) {
info!("{host} is in the preload list");
return true;
diff --git a/components/net/resource_thread.rs b/components/net/resource_thread.rs
index 4077256d6ca..94592d19bed 100644
--- a/components/net/resource_thread.rs
+++ b/components/net/resource_thread.rs
@@ -21,9 +21,9 @@ use embedder_traits::EmbedderProxy;
use hyper_serde::Serde;
use ipc_channel::ipc::{self, IpcReceiver, IpcReceiverSet, IpcSender};
use log::{debug, trace, warn};
-use malloc_size_of::MallocSizeOf;
use net_traits::blob_url_store::parse_blob_url;
use net_traits::filemanager_thread::FileTokenCheck;
+use net_traits::pub_domains::public_suffix_list_size_of;
use net_traits::request::{Destination, RequestBuilder, RequestId};
use net_traits::response::{Response, ResponseInit};
use net_traits::storage_thread::StorageThreadMsg;
@@ -97,14 +97,15 @@ pub fn new_resource_threads(
let (public_core, private_core) = new_core_resource_thread(
devtools_sender,
time_profiler_chan,
- mem_profiler_chan,
+ mem_profiler_chan.clone(),
embedder_proxy,
config_dir.clone(),
ca_certificates,
ignore_certificate_errors,
protocols,
);
- let storage: IpcSender<StorageThreadMsg> = StorageThreadFactory::new(config_dir);
+ let storage: IpcSender<StorageThreadMsg> =
+ StorageThreadFactory::new(config_dir, mem_profiler_chan);
(
ResourceThreads::new(public_core, storage.clone()),
ResourceThreads::new(private_core, storage),
@@ -287,11 +288,18 @@ impl ResourceChannelManager {
perform_memory_report(|ops| {
let mut reports = public_http_state.memory_reports("public", ops);
reports.extend(private_http_state.memory_reports("private", ops));
- reports.push(Report {
- path: path!["hsts-preload-list"],
- kind: ReportKind::ExplicitJemallocHeapSize,
- size: hsts::PRELOAD_LIST_ENTRIES.size_of(ops),
- });
+ reports.extend(vec![
+ Report {
+ path: path!["hsts-preload-list"],
+ kind: ReportKind::ExplicitJemallocHeapSize,
+ size: hsts::hsts_preload_size_of(ops),
+ },
+ Report {
+ path: path!["public-suffix-list"],
+ kind: ReportKind::ExplicitJemallocHeapSize,
+ size: public_suffix_list_size_of(ops),
+ },
+ ]);
msg.send(ProcessReports::new(reports));
})
}
diff --git a/components/net/storage_thread.rs b/components/net/storage_thread.rs
index dd058f17170..899214a450c 100644
--- a/components/net/storage_thread.rs
+++ b/components/net/storage_thread.rs
@@ -8,7 +8,12 @@ use std::path::PathBuf;
use std::thread;
use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
+use malloc_size_of::MallocSizeOf;
use net_traits::storage_thread::{StorageThreadMsg, StorageType};
+use profile_traits::mem::{
+ ProcessReports, ProfilerChan as MemProfilerChan, Report, ReportKind, perform_memory_report,
+};
+use profile_traits::path;
use servo_url::ServoUrl;
use crate::resource_thread;
@@ -16,17 +21,26 @@ use crate::resource_thread;
const QUOTA_SIZE_LIMIT: usize = 5 * 1024 * 1024;
pub trait StorageThreadFactory {
- fn new(config_dir: Option<PathBuf>) -> Self;
+ fn new(config_dir: Option<PathBuf>, mem_profiler_chan: MemProfilerChan) -> Self;
}
impl StorageThreadFactory for IpcSender<StorageThreadMsg> {
/// Create a storage thread
- fn new(config_dir: Option<PathBuf>) -> IpcSender<StorageThreadMsg> {
+ fn new(
+ config_dir: Option<PathBuf>,
+ mem_profiler_chan: MemProfilerChan,
+ ) -> IpcSender<StorageThreadMsg> {
let (chan, port) = ipc::channel().unwrap();
+ let chan2 = chan.clone();
thread::Builder::new()
.name("StorageManager".to_owned())
.spawn(move || {
- StorageManager::new(port, config_dir).start();
+ mem_profiler_chan.run_with_memory_reporting(
+ || StorageManager::new(port, config_dir).start(),
+ String::from("storage-reporter"),
+ chan2,
+ StorageThreadMsg::CollectMemoryReport,
+ );
})
.expect("Thread spawning failed");
chan
@@ -83,6 +97,10 @@ impl StorageManager {
self.clear(sender, url, storage_type);
self.save_state()
},
+ StorageThreadMsg::CollectMemoryReport(sender) => {
+ let reports = self.collect_memory_reports();
+ sender.send(ProcessReports::new(reports));
+ },
StorageThreadMsg::Exit(sender) => {
// Nothing to do since we save localstorage set eagerly.
let _ = sender.send(());
@@ -92,6 +110,24 @@ impl StorageManager {
}
}
+ fn collect_memory_reports(&self) -> Vec<Report> {
+ let mut reports = vec![];
+ perform_memory_report(|ops| {
+ reports.push(Report {
+ path: path!["storage", "local"],
+ kind: ReportKind::ExplicitJemallocHeapSize,
+ size: self.local_data.size_of(ops),
+ });
+
+ reports.push(Report {
+ path: path!["storage", "session"],
+ kind: ReportKind::ExplicitJemallocHeapSize,
+ size: self.session_data.size_of(ops),
+ });
+ });
+ reports
+ }
+
fn save_state(&self) {
if let Some(ref config_dir) = self.config_dir {
resource_thread::write_json_to_file(&self.local_data, config_dir, "local_data.json");
diff --git a/components/net/tests/hsts.rs b/components/net/tests/hsts.rs
index e1e754beb3c..3598b232111 100644
--- a/components/net/tests/hsts.rs
+++ b/components/net/tests/hsts.rs
@@ -6,13 +6,14 @@ use std::collections::HashMap;
use std::num::NonZeroU64;
use std::time::Duration as StdDuration;
+use base64::Engine;
use net::hsts::{HstsEntry, HstsList, HstsPreloadList};
use net_traits::IncludeSubdomains;
#[test]
fn test_hsts_entry_is_not_expired_when_it_has_no_expires_at() {
let entry = HstsEntry {
- host: "mozilla.org".to_owned(),
+ host: "example.com".to_owned(),
include_subdomains: false,
expires_at: None,
};
@@ -23,7 +24,7 @@ fn test_hsts_entry_is_not_expired_when_it_has_no_expires_at() {
#[test]
fn test_hsts_entry_is_expired_when_it_has_reached_its_max_age() {
let entry = HstsEntry {
- host: "mozilla.org".to_owned(),
+ host: "example.com".to_owned(),
include_subdomains: false,
expires_at: Some(NonZeroU64::new(1).unwrap()),
};
@@ -59,7 +60,7 @@ fn test_base_domain_in_entries_map() {
list.push(
HstsEntry::new(
- "servo.mozilla.org".to_owned(),
+ "servo.example.com".to_owned(),
IncludeSubdomains::NotIncluded,
None,
)
@@ -67,7 +68,7 @@ fn test_base_domain_in_entries_map() {
);
list.push(
HstsEntry::new(
- "firefox.mozilla.org".to_owned(),
+ "firefox.example.com".to_owned(),
IncludeSubdomains::NotIncluded,
None,
)
@@ -75,7 +76,7 @@ fn test_base_domain_in_entries_map() {
);
list.push(
HstsEntry::new(
- "bugzilla.org".to_owned(),
+ "example.org".to_owned(),
IncludeSubdomains::NotIncluded,
None,
)
@@ -83,17 +84,17 @@ fn test_base_domain_in_entries_map() {
);
assert_eq!(list.entries_map.len(), 2);
- assert_eq!(list.entries_map.get("mozilla.org").unwrap().len(), 2);
+ assert_eq!(list.entries_map.get("example.com").unwrap().len(), 2);
}
#[test]
fn test_push_entry_with_0_max_age_is_not_secure() {
let mut entries_map = HashMap::new();
entries_map.insert(
- "mozilla.org".to_owned(),
+ "example.com".to_owned(),
vec![
HstsEntry::new(
- "mozilla.org".to_owned(),
+ "example.com".to_owned(),
IncludeSubdomains::NotIncluded,
Some(StdDuration::from_secs(500000)),
)
@@ -106,23 +107,23 @@ fn test_push_entry_with_0_max_age_is_not_secure() {
list.push(
HstsEntry::new(
- "mozilla.org".to_owned(),
+ "example.com".to_owned(),
IncludeSubdomains::NotIncluded,
Some(StdDuration::ZERO),
)
.unwrap(),
);
- assert_eq!(list.is_host_secure("mozilla.org"), false)
+ assert_eq!(list.is_host_secure("example.com"), false)
}
fn test_push_entry_with_0_max_age_evicts_entry_from_list() {
let mut entries_map = HashMap::new();
entries_map.insert(
- "mozilla.org".to_owned(),
+ "example.com".to_owned(),
vec![
HstsEntry::new(
- "mozilla.org".to_owned(),
+ "example.com".to_owned(),
IncludeSubdomains::NotIncluded,
Some(StdDuration::from_secs(500000)),
)
@@ -133,25 +134,25 @@ fn test_push_entry_with_0_max_age_evicts_entry_from_list() {
entries_map: entries_map,
};
- assert_eq!(list.entries_map.get("mozilla.org").unwrap().len(), 1);
+ assert_eq!(list.entries_map.get("example.com").unwrap().len(), 1);
list.push(
HstsEntry::new(
- "mozilla.org".to_owned(),
+ "example.com".to_owned(),
IncludeSubdomains::NotIncluded,
Some(StdDuration::ZERO),
)
.unwrap(),
);
- assert_eq!(list.entries_map.get("mozilla.org").unwrap().len(), 0);
+ assert_eq!(list.entries_map.get("example.com").unwrap().len(), 0);
}
#[test]
fn test_push_entry_to_hsts_list_should_not_add_subdomains_whose_superdomain_is_already_matched() {
let mut entries_map = HashMap::new();
entries_map.insert(
- "mozilla.org".to_owned(),
- vec![HstsEntry::new("mozilla.org".to_owned(), IncludeSubdomains::Included, None).unwrap()],
+ "example.com".to_owned(),
+ vec![HstsEntry::new("example.com".to_owned(), IncludeSubdomains::Included, None).unwrap()],
);
let mut list = HstsList {
entries_map: entries_map,
@@ -159,24 +160,24 @@ fn test_push_entry_to_hsts_list_should_not_add_subdomains_whose_superdomain_is_a
list.push(
HstsEntry::new(
- "servo.mozilla.org".to_owned(),
+ "servo.example.com".to_owned(),
IncludeSubdomains::NotIncluded,
None,
)
.unwrap(),
);
- assert_eq!(list.entries_map.get("mozilla.org").unwrap().len(), 1)
+ assert_eq!(list.entries_map.get("example.com").unwrap().len(), 1)
}
#[test]
fn test_push_entry_to_hsts_list_should_add_subdomains_whose_superdomain_doesnt_include() {
let mut entries_map = HashMap::new();
entries_map.insert(
- "mozilla.org".to_owned(),
+ "example.com".to_owned(),
vec![
HstsEntry::new(
- "mozilla.org".to_owned(),
+ "example.com".to_owned(),
IncludeSubdomains::NotIncluded,
None,
)
@@ -189,49 +190,49 @@ fn test_push_entry_to_hsts_list_should_add_subdomains_whose_superdomain_doesnt_i
list.push(
HstsEntry::new(
- "servo.mozilla.org".to_owned(),
+ "servo.example.com".to_owned(),
IncludeSubdomains::NotIncluded,
None,
)
.unwrap(),
);
- assert_eq!(list.entries_map.get("mozilla.org").unwrap().len(), 2)
+ assert_eq!(list.entries_map.get("example.com").unwrap().len(), 2)
}
#[test]
fn test_push_entry_to_hsts_list_should_update_existing_domain_entrys_include_subdomains() {
let mut entries_map = HashMap::new();
entries_map.insert(
- "mozilla.org".to_owned(),
- vec![HstsEntry::new("mozilla.org".to_owned(), IncludeSubdomains::Included, None).unwrap()],
+ "example.com".to_owned(),
+ vec![HstsEntry::new("example.com".to_owned(), IncludeSubdomains::Included, None).unwrap()],
);
let mut list = HstsList {
entries_map: entries_map,
};
- assert!(list.is_host_secure("servo.mozilla.org"));
+ assert!(list.is_host_secure("servo.example.com"));
list.push(
HstsEntry::new(
- "mozilla.org".to_owned(),
+ "example.com".to_owned(),
IncludeSubdomains::NotIncluded,
None,
)
.unwrap(),
);
- assert!(!list.is_host_secure("servo.mozilla.org"))
+ assert!(!list.is_host_secure("servo.example.com"))
}
#[test]
fn test_push_entry_to_hsts_list_should_not_create_duplicate_entry() {
let mut entries_map = HashMap::new();
entries_map.insert(
- "mozilla.org".to_owned(),
+ "example.com".to_owned(),
vec![
HstsEntry::new(
- "mozilla.org".to_owned(),
+ "example.com".to_owned(),
IncludeSubdomains::NotIncluded,
None,
)
@@ -244,14 +245,14 @@ fn test_push_entry_to_hsts_list_should_not_create_duplicate_entry() {
list.push(
HstsEntry::new(
- "mozilla.org".to_owned(),
+ "example.com".to_owned(),
IncludeSubdomains::NotIncluded,
None,
)
.unwrap(),
);
- assert_eq!(list.entries_map.get("mozilla.org").unwrap().len(), 1)
+ assert_eq!(list.entries_map.get("example.com").unwrap().len(), 1)
}
#[test]
@@ -260,16 +261,14 @@ fn test_push_multiple_entrie_to_hsts_list_should_add_them_all() {
entries_map: HashMap::new(),
};
- assert!(!list.is_host_secure("mozilla.org"));
- assert!(!list.is_host_secure("bugzilla.org"));
+ assert!(!list.is_host_secure("example.com"));
+ assert!(!list.is_host_secure("example.org"));
- list.push(HstsEntry::new("mozilla.org".to_owned(), IncludeSubdomains::Included, None).unwrap());
- list.push(
- HstsEntry::new("bugzilla.org".to_owned(), IncludeSubdomains::Included, None).unwrap(),
- );
+ list.push(HstsEntry::new("example.com".to_owned(), IncludeSubdomains::Included, None).unwrap());
+ list.push(HstsEntry::new("example.org".to_owned(), IncludeSubdomains::Included, None).unwrap());
- assert!(list.is_host_secure("mozilla.org"));
- assert!(list.is_host_secure("bugzilla.org"));
+ assert!(list.is_host_secure("example.com"));
+ assert!(list.is_host_secure("example.org"));
}
#[test]
@@ -278,25 +277,16 @@ fn test_push_entry_to_hsts_list_should_add_an_entry() {
entries_map: HashMap::new(),
};
- assert!(!list.is_host_secure("mozilla.org"));
+ assert!(!list.is_host_secure("example.com"));
- list.push(HstsEntry::new("mozilla.org".to_owned(), IncludeSubdomains::Included, None).unwrap());
+ list.push(HstsEntry::new("example.com".to_owned(), IncludeSubdomains::Included, None).unwrap());
- assert!(list.is_host_secure("mozilla.org"));
+ assert!(list.is_host_secure("example.com"));
}
#[test]
fn test_parse_hsts_preload_should_return_none_when_json_invalid() {
- let mock_preload_content = "derp";
- assert!(
- HstsPreloadList::from_preload(mock_preload_content).is_none(),
- "invalid preload list should not have parsed"
- )
-}
-
-#[test]
-fn test_parse_hsts_preload_should_return_none_when_json_contains_no_entries_map_key() {
- let mock_preload_content = "{\"nothing\": \"to see here\"}";
+ let mock_preload_content = "derp".as_bytes().to_vec();
assert!(
HstsPreloadList::from_preload(mock_preload_content).is_none(),
"invalid preload list should not have parsed"
@@ -305,20 +295,17 @@ fn test_parse_hsts_preload_should_return_none_when_json_contains_no_entries_map_
#[test]
fn test_parse_hsts_preload_should_decode_host_and_includes_subdomains() {
- let mock_preload_content = "{\
- \"entries\": [\
- {\"host\": \"mozilla.org\",\
- \"include_subdomains\": false}\
- ]\
- }";
- let hsts_list = HstsPreloadList::from_preload(mock_preload_content);
- let entries_map = hsts_list.unwrap().entries_map;
-
- assert_eq!(
- entries_map.get("mozilla.org").unwrap()[0].host,
- "mozilla.org"
- );
- assert!(!entries_map.get("mozilla.org").unwrap()[0].include_subdomains);
+ // Generated with `fst map --sorted` on a csv of "example.com,0\nexample.org,3"
+ let mock_preload_content = base64::engine::general_purpose::STANDARD
+ .decode("AwAAAAAAAAAAAAAAAAAAAAAQkMQAEJfHAwABBW9jEQLNws/J0MXqwgIAAAAAAAAAJwAAAAAAAADVOFe6")
+ .unwrap();
+ let hsts_list = HstsPreloadList::from_preload(mock_preload_content).unwrap();
+
+ assert_eq!(hsts_list.is_host_secure("derp"), false);
+ assert_eq!(hsts_list.is_host_secure("example.com"), true);
+ assert_eq!(hsts_list.is_host_secure("servo.example.com"), false);
+ assert_eq!(hsts_list.is_host_secure("example.org"), true);
+ assert_eq!(hsts_list.is_host_secure("servo.example.org"), true);
}
#[test]
@@ -327,17 +314,17 @@ fn test_hsts_list_with_no_entries_map_does_not_is_host_secure() {
entries_map: HashMap::new(),
};
- assert!(!hsts_list.is_host_secure("mozilla.org"));
+ assert!(!hsts_list.is_host_secure("example.com"));
}
#[test]
fn test_hsts_list_with_exact_domain_entry_is_is_host_secure() {
let mut entries_map = HashMap::new();
entries_map.insert(
- "mozilla.org".to_owned(),
+ "example.com".to_owned(),
vec![
HstsEntry::new(
- "mozilla.org".to_owned(),
+ "example.com".to_owned(),
IncludeSubdomains::NotIncluded,
None,
)
@@ -349,31 +336,31 @@ fn test_hsts_list_with_exact_domain_entry_is_is_host_secure() {
entries_map: entries_map,
};
- assert!(hsts_list.is_host_secure("mozilla.org"));
+ assert!(hsts_list.is_host_secure("example.com"));
}
#[test]
fn test_hsts_list_with_subdomain_when_include_subdomains_is_true_is_is_host_secure() {
let mut entries_map = HashMap::new();
entries_map.insert(
- "mozilla.org".to_owned(),
- vec![HstsEntry::new("mozilla.org".to_owned(), IncludeSubdomains::Included, None).unwrap()],
+ "example.com".to_owned(),
+ vec![HstsEntry::new("example.com".to_owned(), IncludeSubdomains::Included, None).unwrap()],
);
let hsts_list = HstsList {
entries_map: entries_map,
};
- assert!(hsts_list.is_host_secure("servo.mozilla.org"));
+ assert!(hsts_list.is_host_secure("servo.example.com"));
}
#[test]
fn test_hsts_list_with_subdomain_when_include_subdomains_is_false_is_not_is_host_secure() {
let mut entries_map = HashMap::new();
entries_map.insert(
- "mozilla.org".to_owned(),
+ "example.com".to_owned(),
vec![
HstsEntry::new(
- "mozilla.org".to_owned(),
+ "example.com".to_owned(),
IncludeSubdomains::NotIncluded,
None,
)
@@ -384,44 +371,44 @@ fn test_hsts_list_with_subdomain_when_include_subdomains_is_false_is_not_is_host
entries_map: entries_map,
};
- assert!(!hsts_list.is_host_secure("servo.mozilla.org"));
+ assert!(!hsts_list.is_host_secure("servo.example.com"));
}
#[test]
fn test_hsts_list_with_subdomain_when_host_is_not_a_subdomain_is_not_is_host_secure() {
let mut entries_map = HashMap::new();
entries_map.insert(
- "mozilla.org".to_owned(),
- vec![HstsEntry::new("mozilla.org".to_owned(), IncludeSubdomains::Included, None).unwrap()],
+ "example.com".to_owned(),
+ vec![HstsEntry::new("example.com".to_owned(), IncludeSubdomains::Included, None).unwrap()],
);
let hsts_list = HstsList {
entries_map: entries_map,
};
- assert!(!hsts_list.is_host_secure("servo-mozilla.org"));
+ assert!(!hsts_list.is_host_secure("servo-example.com"));
}
#[test]
fn test_hsts_list_with_subdomain_when_host_is_exact_match_is_is_host_secure() {
let mut entries_map = HashMap::new();
entries_map.insert(
- "mozilla.org".to_owned(),
- vec![HstsEntry::new("mozilla.org".to_owned(), IncludeSubdomains::Included, None).unwrap()],
+ "example.com".to_owned(),
+ vec![HstsEntry::new("example.com".to_owned(), IncludeSubdomains::Included, None).unwrap()],
);
let hsts_list = HstsList {
entries_map: entries_map,
};
- assert!(hsts_list.is_host_secure("mozilla.org"));
+ assert!(hsts_list.is_host_secure("example.com"));
}
#[test]
fn test_hsts_list_with_expired_entry_is_not_is_host_secure() {
let mut entries_map = HashMap::new();
entries_map.insert(
- "mozilla.org".to_owned(),
+ "example.com".to_owned(),
vec![HstsEntry {
- host: "mozilla.org".to_owned(),
+ host: "example.com".to_owned(),
include_subdomains: false,
expires_at: Some(NonZeroU64::new(1).unwrap()),
}],
@@ -430,11 +417,11 @@ fn test_hsts_list_with_expired_entry_is_not_is_host_secure() {
entries_map: entries_map,
};
- assert!(!hsts_list.is_host_secure("mozilla.org"));
+ assert!(!hsts_list.is_host_secure("example.com"));
}
#[test]
fn test_preload_hsts_domains_well_formed() {
let hsts_list = HstsPreloadList::from_servo_preload();
- assert!(!hsts_list.entries_map.is_empty());
+ assert_ne!(hsts_list.0.len(), 0);
}
diff --git a/components/script/dom/bindings/structuredclone.rs b/components/script/dom/bindings/structuredclone.rs
index 70638238123..c23156817cb 100644
--- a/components/script/dom/bindings/structuredclone.rs
+++ b/components/script/dom/bindings/structuredclone.rs
@@ -14,7 +14,7 @@ use base::id::{
};
use constellation_traits::{
BlobImpl, DomException, DomPoint, MessagePortImpl, Serializable as SerializableInterface,
- StructuredSerializedData, Transferrable as TransferrableInterface,
+ StructuredSerializedData, Transferrable as TransferrableInterface, TransformStreamData,
};
use js::gc::RootedVec;
use js::glue::{
@@ -517,6 +517,8 @@ pub(crate) struct StructuredDataReader<'a> {
/// used as part of the "transfer-receiving" steps of ports,
/// to produce the DOM ports stored in `message_ports` above.
pub(crate) port_impls: Option<HashMap<MessagePortId, MessagePortImpl>>,
+ /// A map of transform stream implementations,
+ pub(crate) transform_streams_port_impls: Option<HashMap<MessagePortId, TransformStreamData>>,
/// A map of blob implementations,
/// used as part of the "deserialize" steps of blobs,
/// to produce the DOM blobs stored in `blobs` above.
@@ -535,6 +537,8 @@ pub(crate) struct StructuredDataWriter {
pub(crate) errors: DOMErrorRecord,
/// Transferred ports.
pub(crate) ports: Option<HashMap<MessagePortId, MessagePortImpl>>,
+ /// Transferred transform streams.
+ pub(crate) transform_streams_port: Option<HashMap<MessagePortId, TransformStreamData>>,
/// Serialized points.
pub(crate) points: Option<HashMap<DomPointId, DomPoint>>,
/// Serialized exceptions.
@@ -591,6 +595,7 @@ pub(crate) fn write(
let data = StructuredSerializedData {
serialized: data,
ports: sc_writer.ports.take(),
+ transform_streams: sc_writer.transform_streams_port.take(),
points: sc_writer.points.take(),
exceptions: sc_writer.exceptions.take(),
blobs: sc_writer.blobs.take(),
@@ -613,6 +618,7 @@ pub(crate) fn read(
let mut sc_reader = StructuredDataReader {
roots,
port_impls: data.ports.take(),
+ transform_streams_port_impls: data.transform_streams.take(),
blob_impls: data.blobs.take(),
points: data.points.take(),
exceptions: data.exceptions.take(),
diff --git a/components/script/dom/blob.rs b/components/script/dom/blob.rs
index c5c5c480707..18e968aaa70 100644
--- a/components/script/dom/blob.rs
+++ b/components/script/dom/blob.rs
@@ -12,11 +12,10 @@ use dom_struct::dom_struct;
use encoding_rs::UTF_8;
use js::jsapi::JSObject;
use js::rust::HandleObject;
-use js::typedarray::Uint8;
+use js::typedarray::{ArrayBufferU8, Uint8};
use net_traits::filemanager_thread::RelativePos;
use uuid::Uuid;
-use crate::body::{FetchedData, run_array_buffer_data_algorithm};
use crate::dom::bindings::buffer_source::create_buffer_source;
use crate::dom::bindings::codegen::Bindings::BlobBinding;
use crate::dom::bindings::codegen::Bindings::BlobBinding::BlobMethods;
@@ -226,7 +225,7 @@ impl BlobMethods<crate::DomTypeHolder> for Blob {
Blob::new(&global, blob_impl, can_gc)
}
- // https://w3c.github.io/FileAPI/#text-method-algo
+ /// <https://w3c.github.io/FileAPI/#text-method-algo>
fn Text(&self, can_gc: CanGc) -> Rc<Promise> {
let global = self.global();
let in_realm_proof = AlreadyInRealm::assert::<crate::DomTypeHolder>();
@@ -250,35 +249,51 @@ impl BlobMethods<crate::DomTypeHolder> for Blob {
}
// https://w3c.github.io/FileAPI/#arraybuffer-method-algo
- fn ArrayBuffer(&self, can_gc: CanGc) -> Rc<Promise> {
- let global = self.global();
- let in_realm_proof = AlreadyInRealm::assert::<crate::DomTypeHolder>();
- let p = Promise::new_in_current_realm(InRealm::Already(&in_realm_proof), can_gc);
+ fn ArrayBuffer(&self, in_realm: InRealm, can_gc: CanGc) -> Rc<Promise> {
+ let cx = GlobalScope::get_cx();
+ let global = GlobalScope::from_safe_context(cx, in_realm);
+ let promise = Promise::new_in_current_realm(in_realm, can_gc);
- let id = self.get_blob_url_id();
+ // 1. Let stream be the result of calling get stream on this.
+ let stream = self.get_stream(can_gc);
- global.read_file_async(
- id,
- p.clone(),
- Box::new(|promise, bytes| {
- match bytes {
- Ok(b) => {
- let cx = GlobalScope::get_cx();
- let result = run_array_buffer_data_algorithm(cx, b, CanGc::note());
-
- match result {
- Ok(FetchedData::ArrayBuffer(a)) => {
- promise.resolve_native(&a, CanGc::note())
- },
- Err(e) => promise.reject_error(e, CanGc::note()),
- _ => panic!("Unexpected result from run_array_buffer_data_algorithm"),
- }
- },
- Err(e) => promise.reject_error(e, CanGc::note()),
- };
+ // 2. Let reader be the result of getting a reader from stream.
+ // If that threw an exception, return a new promise rejected with that exception.
+ let reader = match stream.and_then(|s| s.acquire_default_reader(can_gc)) {
+ Ok(reader) => reader,
+ Err(error) => {
+ promise.reject_error(error, can_gc);
+ return promise;
+ },
+ };
+
+ // 3. Let promise be the result of reading all bytes from stream with reader.
+ let success_promise = promise.clone();
+ let failure_promise = promise.clone();
+ reader.read_all_bytes(
+ cx,
+ &global,
+ Rc::new(move |bytes| {
+ rooted!(in(*cx) let mut js_object = ptr::null_mut::<JSObject>());
+ // 4. Return the result of transforming promise by a fulfillment handler that returns a new
+ // [ArrayBuffer]
+ let array_buffer = create_buffer_source::<ArrayBufferU8>(
+ cx,
+ bytes,
+ js_object.handle_mut(),
+ can_gc,
+ )
+ .expect("Converting input to ArrayBufferU8 should never fail");
+ success_promise.resolve_native(&array_buffer, can_gc);
}),
+ Rc::new(move |cx, value| {
+ failure_promise.reject(cx, value, can_gc);
+ }),
+ in_realm,
+ can_gc,
);
- p
+
+ promise
}
/// <https://w3c.github.io/FileAPI/#dom-blob-bytes>
diff --git a/components/script/dom/headers.rs b/components/script/dom/headers.rs
index 10a8be731bf..0e8dcf92ccd 100644
--- a/components/script/dom/headers.rs
+++ b/components/script/dom/headers.rs
@@ -5,12 +5,12 @@
use std::cell::Cell;
use std::str::{self, FromStr};
-use data_url::mime::Mime as DataUrlMime;
use dom_struct::dom_struct;
use http::header::{HeaderMap as HyperHeaders, HeaderName, HeaderValue};
use js::rust::HandleObject;
use net_traits::fetch::headers::{
- get_decode_and_split_header_value, get_value_from_header_list, is_forbidden_method,
+ extract_mime_type, get_decode_and_split_header_value, get_value_from_header_list,
+ is_forbidden_method,
};
use net_traits::request::is_cors_safelisted_request_header;
@@ -564,72 +564,3 @@ pub(crate) fn is_vchar(x: u8) -> bool {
pub(crate) fn is_obs_text(x: u8) -> bool {
matches!(x, 0x80..=0xFF)
}
-
-// https://fetch.spec.whatwg.org/#concept-header-extract-mime-type
-// This function uses data_url::Mime to parse the MIME Type because
-// mime::Mime does not provide a parser following the Fetch spec
-// see https://github.com/hyperium/mime/issues/106
-pub(crate) fn extract_mime_type(headers: &HyperHeaders) -> Option<Vec<u8>> {
- let mut charset: Option<String> = None;
- let mut essence: String = "".to_string();
- let mut mime_type: Option<DataUrlMime> = None;
-
- // Step 4
- let headers_values = headers.get_all(http::header::CONTENT_TYPE).iter();
-
- // Step 5
- if headers_values.size_hint() == (0, Some(0)) {
- return None;
- }
-
- // Step 6
- for header_value in headers_values {
- // Step 6.1
- match DataUrlMime::from_str(header_value.to_str().unwrap_or("")) {
- // Step 6.2
- Err(_) => continue,
- Ok(temp_mime) => {
- let temp_essence = format!("{}/{}", temp_mime.type_, temp_mime.subtype);
-
- // Step 6.2
- if temp_essence == "*/*" {
- continue;
- }
-
- let temp_charset = &temp_mime.get_parameter("charset");
-
- // Step 6.3
- mime_type = Some(DataUrlMime {
- type_: temp_mime.type_.to_string(),
- subtype: temp_mime.subtype.to_string(),
- parameters: temp_mime.parameters.clone(),
- });
-
- // Step 6.4
- if temp_essence != essence {
- charset = temp_charset.map(|c| c.to_string());
- temp_essence.clone_into(&mut essence);
- } else {
- // Step 6.5
- if temp_charset.is_none() && charset.is_some() {
- let DataUrlMime {
- type_: t,
- subtype: st,
- parameters: p,
- } = mime_type.unwrap();
- let mut params = p;
- params.push(("charset".to_string(), charset.clone().unwrap()));
- mime_type = Some(DataUrlMime {
- type_: t.to_string(),
- subtype: st.to_string(),
- parameters: params,
- })
- }
- }
- },
- }
- }
-
- // Step 7, 8
- mime_type.map(|m| format!("{}", m).into_bytes())
-}
diff --git a/components/script/dom/readablestream.rs b/components/script/dom/readablestream.rs
index d631a01e1e7..d2c1d853f86 100644
--- a/components/script/dom/readablestream.rs
+++ b/components/script/dom/readablestream.rs
@@ -24,7 +24,7 @@ use js::typedarray::ArrayBufferViewU8;
use crate::dom::bindings::codegen::Bindings::QueuingStrategyBinding::QueuingStrategy;
use crate::dom::bindings::codegen::Bindings::ReadableStreamBinding::{
ReadableStreamGetReaderOptions, ReadableStreamMethods, ReadableStreamReaderMode,
- StreamPipeOptions,
+ ReadableWritablePair, StreamPipeOptions,
};
use script_bindings::str::DOMString;
@@ -2006,6 +2006,50 @@ impl ReadableStreamMethods<crate::DomTypeHolder> for ReadableStream {
can_gc,
)
}
+
+ /// <https://streams.spec.whatwg.org/#rs-pipe-through>
+ fn PipeThrough(
+ &self,
+ transform: &ReadableWritablePair,
+ options: &StreamPipeOptions,
+ realm: InRealm,
+ can_gc: CanGc,
+ ) -> Fallible<DomRoot<ReadableStream>> {
+ let global = self.global();
+ let cx = GlobalScope::get_cx();
+
+ // If ! IsReadableStreamLocked(this) is true, throw a TypeError exception.
+ if self.is_locked() {
+ return Err(Error::Type("Source stream is locked".to_owned()));
+ }
+
+ // If ! IsWritableStreamLocked(transform["writable"]) is true, throw a TypeError exception.
+ if transform.writable.is_locked() {
+ return Err(Error::Type("Destination stream is locked".to_owned()));
+ }
+
+ // Let signal be options["signal"] if it exists, or undefined otherwise.
+ // TODO: implement AbortSignal.
+
+ // Let promise be ! ReadableStreamPipeTo(this, transform["writable"],
+ // options["preventClose"], options["preventAbort"], options["preventCancel"], signal).
+ let promise = self.pipe_to(
+ cx,
+ &global,
+ &transform.writable,
+ options.preventAbort,
+ options.preventCancel,
+ options.preventClose,
+ realm,
+ can_gc,
+ );
+
+ // Set promise.[[PromiseIsHandled]] to true.
+ promise.set_promise_is_handled();
+
+ // Return transform["readable"].
+ Ok(transform.readable.clone())
+ }
}
#[allow(unsafe_code)]
diff --git a/components/script/dom/transformstream.rs b/components/script/dom/transformstream.rs
index 0251498980d..446bf71f172 100644
--- a/components/script/dom/transformstream.rs
+++ b/components/script/dom/transformstream.rs
@@ -8,7 +8,7 @@ use std::ptr::{self};
use std::rc::Rc;
use base::id::{MessagePortId, MessagePortIndex};
-use constellation_traits::MessagePortImpl;
+use constellation_traits::TransformStreamData;
use dom_struct::dom_struct;
use js::jsapi::{Heap, IsPromiseObject, JSObject};
use js::jsval::{JSVal, ObjectValue, UndefinedValue};
@@ -1007,9 +1007,9 @@ impl TransformStreamMethods<crate::DomTypeHolder> for TransformStream {
/// <https://streams.spec.whatwg.org/#ts-transfer>
impl Transferable for TransformStream {
type Index = MessagePortIndex;
- type Data = MessagePortImpl;
+ type Data = TransformStreamData;
- fn transfer(&self) -> Result<(MessagePortId, MessagePortImpl), ()> {
+ fn transfer(&self) -> Result<(MessagePortId, TransformStreamData), ()> {
let global = self.global();
let realm = enter_realm(&*global);
let comp = InRealm::Entered(&realm);
@@ -1023,73 +1023,85 @@ impl Transferable for TransformStream {
let writable = self.get_writable();
// If ! IsReadableStreamLocked(readable) is true, throw a "DataCloneError" DOMException.
- if readable.is_locked() {
- return Err(());
- }
-
// If ! IsWritableStreamLocked(writable) is true, throw a "DataCloneError" DOMException.
- if writable.is_locked() {
+ if readable.is_locked() || writable.is_locked() {
return Err(());
}
- // Create the shared port pair
- let port_1 = MessagePort::new(&global, can_gc);
- global.track_message_port(&port_1, None);
- let port_2 = MessagePort::new(&global, can_gc);
- global.track_message_port(&port_2, None);
- global.entangle_ports(*port_1.message_port_id(), *port_2.message_port_id());
+ // First port pair (readable → proxy writable)
+ let port1 = MessagePort::new(&global, can_gc);
+ global.track_message_port(&port1, None);
+ let port1_peer = MessagePort::new(&global, can_gc);
+ global.track_message_port(&port1_peer, None);
+ global.entangle_ports(*port1.message_port_id(), *port1_peer.message_port_id());
+
+ let proxy_readable = ReadableStream::new_with_proto(&global, None, can_gc);
+ proxy_readable.setup_cross_realm_transform_readable(cx, &port1, can_gc);
+ proxy_readable
+ .pipe_to(cx, &global, &writable, false, false, false, comp, can_gc)
+ .set_promise_is_handled();
+
+ // Second port pair (proxy readable → writable)
+ let port2 = MessagePort::new(&global, can_gc);
+ global.track_message_port(&port2, None);
+ let port2_peer = MessagePort::new(&global, can_gc);
+ global.track_message_port(&port2_peer, None);
+ global.entangle_ports(*port2.message_port_id(), *port2_peer.message_port_id());
- // Create a proxy WritableStream wired to port_1
let proxy_writable = WritableStream::new_with_proto(&global, None, can_gc);
- proxy_writable.setup_cross_realm_transform_writable(cx, &port_1, can_gc);
+ proxy_writable.setup_cross_realm_transform_writable(cx, &port2, can_gc);
// Pipe readable into the proxy writable (→ port_1)
- let pipe1 = readable.pipe_to(
- cx,
- &global,
- &proxy_writable,
- false,
- false,
- false,
- comp,
- can_gc,
- );
- pipe1.set_promise_is_handled();
-
- // Create a proxy ReadableStream wired to port_1
- let proxy_readable = ReadableStream::new_with_proto(&global, None, can_gc);
- proxy_readable.setup_cross_realm_transform_readable(cx, &port_1, can_gc);
-
- // Pipe proxy readable (← port_1) into writable
- let pipe2 =
- proxy_readable.pipe_to(cx, &global, &writable, false, false, false, comp, can_gc);
- pipe2.set_promise_is_handled();
+ readable
+ .pipe_to(
+ cx,
+ &global,
+ &proxy_writable,
+ false,
+ false,
+ false,
+ comp,
+ can_gc,
+ )
+ .set_promise_is_handled();
// Set dataHolder.[[readable]] to ! StructuredSerializeWithTransfer(readable, « readable »).
// Set dataHolder.[[writable]] to ! StructuredSerializeWithTransfer(writable, « writable »).
- port_2.transfer()
+ Ok((
+ *port1_peer.message_port_id(),
+ TransformStreamData {
+ readable: port1_peer.transfer()?,
+ writable: port2_peer.transfer()?,
+ },
+ ))
}
fn transfer_receive(
owner: &GlobalScope,
- id: MessagePortId,
- port_impl: MessagePortImpl,
+ _id: MessagePortId,
+ data: TransformStreamData,
) -> Result<DomRoot<Self>, ()> {
let can_gc = CanGc::note();
+ let cx = GlobalScope::get_cx();
+
+ let port1 = MessagePort::transfer_receive(owner, data.readable.0, data.readable.1)?;
+ let port2 = MessagePort::transfer_receive(owner, data.writable.0, data.writable.1)?;
// Let readableRecord be ! StructuredDeserializeWithTransfer(dataHolder.[[readable]], the current Realm).
// Set value.[[readable]] to readableRecord.[[Deserialized]].
- let readable = ReadableStream::transfer_receive(owner, id, port_impl.clone())?;
-
// Let writableRecord be ! StructuredDeserializeWithTransfer(dataHolder.[[writable]], the current Realm).
- let writable = WritableStream::transfer_receive(owner, id, port_impl)?;
+ let proxy_readable = ReadableStream::new_with_proto(owner, None, can_gc);
+ proxy_readable.setup_cross_realm_transform_readable(cx, &port2, can_gc);
+
+ let proxy_writable = WritableStream::new_with_proto(owner, None, can_gc);
+ proxy_writable.setup_cross_realm_transform_writable(cx, &port1, can_gc);
// Set value.[[readable]] to readableRecord.[[Deserialized]].
// Set value.[[writable]] to writableRecord.[[Deserialized]].
// Set value.[[backpressure]], value.[[backpressureChangePromise]], and value.[[controller]] to undefined.
let stream = TransformStream::new_with_proto(owner, None, can_gc);
- stream.readable.set(Some(&readable));
- stream.writable.set(Some(&writable));
+ stream.readable.set(Some(&proxy_readable));
+ stream.writable.set(Some(&proxy_writable));
Ok(stream)
}
@@ -1098,8 +1110,8 @@ impl Transferable for TransformStream {
data: StructuredData<'a, '_>,
) -> &'a mut Option<HashMap<MessagePortId, Self::Data>> {
match data {
- StructuredData::Reader(r) => &mut r.port_impls,
- StructuredData::Writer(w) => &mut w.ports,
+ StructuredData::Reader(r) => &mut r.transform_streams_port_impls,
+ StructuredData::Writer(w) => &mut w.transform_streams_port,
}
}
}
diff --git a/components/script/dom/xmlhttprequest.rs b/components/script/dom/xmlhttprequest.rs
index ca5bb72a1dc..4e7c136f42b 100644
--- a/components/script/dom/xmlhttprequest.rs
+++ b/components/script/dom/xmlhttprequest.rs
@@ -26,6 +26,7 @@ use js::jsval::{JSVal, NullValue};
use js::rust::wrappers::JS_ParseJSON;
use js::rust::{HandleObject, MutableHandleValue};
use js::typedarray::{ArrayBuffer, ArrayBufferU8};
+use net_traits::fetch::headers::extract_mime_type_as_dataurl_mime;
use net_traits::http_status::HttpStatus;
use net_traits::request::{CredentialsMode, Referrer, RequestBuilder, RequestId, RequestMode};
use net_traits::{
@@ -59,7 +60,7 @@ use crate::dom::document::{Document, DocumentSource, HasBrowsingContext, IsHTMLD
use crate::dom::event::{Event, EventBubbles, EventCancelable};
use crate::dom::eventtarget::EventTarget;
use crate::dom::globalscope::GlobalScope;
-use crate::dom::headers::{extract_mime_type, is_forbidden_request_header};
+use crate::dom::headers::is_forbidden_request_header;
use crate::dom::node::Node;
use crate::dom::performanceresourcetiming::InitiatorType;
use crate::dom::progressevent::ProgressEvent;
@@ -1324,11 +1325,7 @@ impl XMLHttpRequest {
return response;
}
// Step 2
- let mime = self
- .final_mime_type()
- .as_ref()
- .map(|m| normalize_type_string(&m.to_string()))
- .unwrap_or("".to_owned());
+ let mime = normalize_type_string(&self.final_mime_type().to_string());
// Step 3, 4
let bytes = self.response.borrow().to_vec();
@@ -1366,64 +1363,77 @@ impl XMLHttpRequest {
return response;
}
- // Step 1
+ // Step 1: If xhr’s response’s body is null, then return.
if self.response_status.get().is_err() {
return None;
}
- // Step 2
- let mime_type = self.final_mime_type();
- // Step 5.3, 7
- let charset = self.final_charset().unwrap_or(UTF_8);
- let temp_doc: DomRoot<Document>;
- match mime_type {
- Some(ref mime) if mime.matches(TEXT, HTML) => {
- // Step 4
- if self.response_type.get() == XMLHttpRequestResponseType::_empty {
- return None;
- } else {
- // TODO Step 5.2 "If charset is null, prescan the first 1024 bytes of xhr’s received bytes"
- // Step 5
- temp_doc = self.document_text_html(can_gc);
- }
- },
- // Step 7
- None => {
- temp_doc = self.handle_xml(can_gc);
- // Not sure it the parser should throw an error for this case
- // The specification does not indicates this test,
- // but for now we check the document has no child nodes
- let has_no_child_nodes = temp_doc.upcast::<Node>().children().next().is_none();
- if has_no_child_nodes {
- return None;
- }
- },
- Some(ref mime)
- if mime.matches(TEXT, XML) ||
- mime.matches(APPLICATION, XML) ||
- mime.has_suffix(XML) =>
- {
- temp_doc = self.handle_xml(can_gc);
- // Not sure it the parser should throw an error for this case
- // The specification does not indicates this test,
- // but for now we check the document has no child nodes
- let has_no_child_nodes = temp_doc.upcast::<Node>().children().next().is_none();
- if has_no_child_nodes {
- return None;
- }
- },
- // Step 3
- _ => {
+ // Step 2: Let finalMIME be the result of get a final MIME type for xhr.
+ let final_mime = self.final_mime_type();
+
+ // Step 3: If finalMIME is not an HTML MIME type or an XML MIME type, then return.
+ let is_xml_mime_type = final_mime.matches(TEXT, XML) ||
+ final_mime.matches(APPLICATION, XML) ||
+ final_mime.has_suffix(XML);
+ if !final_mime.matches(TEXT, HTML) && !is_xml_mime_type {
+ return None;
+ }
+
+ // Step 4: If xhr’s response type is the empty string and finalMIME is an HTML MIME
+ // type, then return.
+ let charset;
+ let temp_doc;
+ if final_mime.matches(TEXT, HTML) {
+ if self.response_type.get() == XMLHttpRequestResponseType::_empty {
return None;
- },
+ }
+
+ // Step 5: If finalMIME is an HTML MIME type, then:
+ // Step 5.1: Let charset be the result of get a final encoding for xhr.
+ // Step 5.2: If charset is null, prescan the first 1024 bytes of xhr’s received bytes
+ // and if that does not terminate unsuccessfully then let charset be the return value.
+ // TODO: This isn't happening right now.
+ // Step 5.3. If charset is null, then set charset to UTF-8.
+ charset = Some(self.final_charset().unwrap_or(UTF_8));
+
+ // Step 5.4: Let document be a document that represents the result parsing xhr’s
+ // received bytes following the rules set forth in the HTML Standard for an HTML parser
+ // with scripting disabled and a known definite encoding charset. [HTML]
+ temp_doc = self.document_text_html(can_gc);
+ } else {
+ assert!(is_xml_mime_type);
+
+ // Step 6: Otherwise, let document be a document that represents the result of running
+ // the XML parser with XML scripting support disabled on xhr’s received bytes. If that
+ // fails (unsupported character encoding, namespace well-formedness error, etc.), then
+ // return null. [HTML]
+ //
+ // TODO: The spec seems to suggest the charset should come from the XML parser here.
+ temp_doc = self.handle_xml(can_gc);
+ charset = self.final_charset();
+
+ // Not sure it the parser should throw an error for this case
+ // The specification does not indicates this test,
+ // but for now we check the document has no child nodes
+ let has_no_child_nodes = temp_doc.upcast::<Node>().children().next().is_none();
+ if has_no_child_nodes {
+ return None;
+ }
}
- // Step 8
+
+ // Step 7: If charset is null, then set charset to UTF-8.
+ let charset = charset.unwrap_or(UTF_8);
+
+ // Step 8: Set document’s encoding to charset.
temp_doc.set_encoding(charset);
- // Step 9 to 11
- // Done by handle_text_html and handle_xml
+ // Step 9: Set document’s content type to finalMIME.
+ // Step 10: Set document’s URL to xhr’s response’s URL.
+ // Step 11: Set document’s origin to xhr’s relevant settings object’s origin.
+ //
+ // Done by `handle_text_html()` and `handle_xml()`.
- // Step 12
+ // Step 12: Set xhr’s response object to document.
self.response_xml.set(Some(&temp_doc));
self.response_xml.get()
}
@@ -1507,7 +1517,7 @@ impl XMLHttpRequest {
Ok(parsed) => Some(parsed),
Err(_) => None, // Step 7
};
- let content_type = self.final_mime_type();
+ let content_type = Some(self.final_mime_type());
Document::new(
win,
HasBrowsingContext::No,
@@ -1598,14 +1608,16 @@ impl XMLHttpRequest {
// 3. If responseMIME’s parameters["charset"] exists, then set label to it.
let response_charset = self
.response_mime_type()
- .and_then(|mime| mime.get_parameter(CHARSET).map(|c| c.to_string()));
+ .get_parameter(CHARSET)
+ .map(ToString::to_string);
// 4. If xhr’s override MIME type’s parameters["charset"] exists, then set label to it.
let override_charset = self
.override_mime_type
.borrow()
.as_ref()
- .and_then(|mime| mime.get_parameter(CHARSET).map(|c| c.to_string()));
+ .and_then(|mime| mime.get_parameter(CHARSET))
+ .map(ToString::to_string);
// 5. If label is null, then return null.
// 6. Let encoding be the result of getting an encoding from label.
@@ -1617,23 +1629,22 @@ impl XMLHttpRequest {
}
/// <https://xhr.spec.whatwg.org/#response-mime-type>
- fn response_mime_type(&self) -> Option<Mime> {
- return extract_mime_type(&self.response_headers.borrow())
- .and_then(|mime_as_bytes| {
- String::from_utf8(mime_as_bytes)
- .unwrap_or_default()
- .parse()
- .ok()
- })
- .or(Some(Mime::new(TEXT, XML)));
+ fn response_mime_type(&self) -> Mime {
+ // 1. Let mimeType be the result of extracting a MIME type from xhr’s response’s
+ // header list.
+ // 2. If mimeType is failure, then set mimeType to text/xml.
+ // 3. Return mimeType.
+ extract_mime_type_as_dataurl_mime(&self.response_headers.borrow())
+ .unwrap_or_else(|| Mime::new(TEXT, XML))
}
/// <https://xhr.spec.whatwg.org/#final-mime-type>
- fn final_mime_type(&self) -> Option<Mime> {
- match *self.override_mime_type.borrow() {
- Some(ref override_mime) => Some(override_mime.clone()),
- None => self.response_mime_type(),
- }
+ fn final_mime_type(&self) -> Mime {
+ self.override_mime_type
+ .borrow()
+ .as_ref()
+ .map(MimeExt::clone)
+ .unwrap_or_else(|| self.response_mime_type())
}
}
diff --git a/components/script/stylesheet_loader.rs b/components/script/stylesheet_loader.rs
index a18d63e323b..5efaf78e542 100644
--- a/components/script/stylesheet_loader.rs
+++ b/components/script/stylesheet_loader.rs
@@ -163,7 +163,8 @@ impl FetchResponseListener for StylesheetContext {
Some(meta) => meta,
None => return,
};
- let is_css = metadata.content_type.is_some_and(|ct| {
+
+ let mut is_css = metadata.content_type.is_some_and(|ct| {
let mime: Mime = ct.into_inner().into();
mime.type_() == mime::TEXT && mime.subtype() == mime::CSS
}) || (
@@ -177,6 +178,17 @@ impl FetchResponseListener for StylesheetContext {
document.origin().immutable().clone() == metadata.final_url.origin()
);
+ // From <https://html.spec.whatwg.org/multipage/#link-type-stylesheet>:
+ // > Quirk: If the document has been set to quirks mode, has the same origin as
+ // > the URL of the external resource, and the Content-Type metadata of the
+ // > external resource is not a supported style sheet type, the user agent must
+ // > instead assume it to be text/css.
+ if document.quirks_mode() == QuirksMode::Quirks &&
+ document.url().origin() == self.url.origin()
+ {
+ is_css = true;
+ }
+
let data = if is_css {
let data = std::mem::take(&mut self.data);
self.unminify_css(data, metadata.final_url.clone())
diff --git a/components/script_bindings/codegen/Bindings.conf b/components/script_bindings/codegen/Bindings.conf
index 92871bc54aa..7cc092e574e 100644
--- a/components/script_bindings/codegen/Bindings.conf
+++ b/components/script_bindings/codegen/Bindings.conf
@@ -39,7 +39,7 @@ DOMInterfaces = {
'Blob': {
'weakReferenceable': True,
'canGc': ['Slice', 'Text', 'ArrayBuffer', 'Stream', 'Bytes'],
- 'inRealms': ['Bytes'],
+ 'inRealms': ['Bytes', 'ArrayBuffer'],
},
'Bluetooth': {
@@ -721,8 +721,8 @@ DOMInterfaces = {
},
'ReadableStream': {
- 'canGc': ['GetReader', 'Cancel', 'PipeTo', 'Tee'],
- 'inRealms': ['PipeTo'],
+ 'canGc': ['GetReader', 'Cancel', 'PipeTo', 'PipeThrough', 'Tee'],
+ 'inRealms': ['PipeTo', 'PipeThrough'],
},
"ReadableStreamDefaultController": {
diff --git a/components/script_bindings/webidls/Element.webidl b/components/script_bindings/webidls/Element.webidl
index 42733b91929..4545b18d058 100644
--- a/components/script_bindings/webidls/Element.webidl
+++ b/components/script_bindings/webidls/Element.webidl
@@ -84,7 +84,7 @@ interface Element : Node {
[CEReactions, Throws]
undefined insertAdjacentHTML(DOMString position, (TrustedHTML or DOMString) string);
- [Throws, Pref="dom_shadowdom_enabled"] ShadowRoot attachShadow(ShadowRootInit init);
+ [Throws] ShadowRoot attachShadow(ShadowRootInit init);
readonly attribute ShadowRoot? shadowRoot;
};
diff --git a/components/script_bindings/webidls/ReadableStream.webidl b/components/script_bindings/webidls/ReadableStream.webidl
index 12798262953..4ec190bd911 100644
--- a/components/script_bindings/webidls/ReadableStream.webidl
+++ b/components/script_bindings/webidls/ReadableStream.webidl
@@ -20,8 +20,8 @@ interface _ReadableStream {
[Throws]
ReadableStreamReader getReader(optional ReadableStreamGetReaderOptions options = {});
- // [Throws]
- // ReadableStream pipeThrough(ReadableWritablePair transform, optional StreamPipeOptions options = {});
+ [Throws]
+ ReadableStream pipeThrough(ReadableWritablePair transform, optional StreamPipeOptions options = {});
[NewObject]
Promise<undefined> pipeTo(WritableStream destination, optional StreamPipeOptions options = {});
diff --git a/components/shared/base/id.rs b/components/shared/base/id.rs
index cc4ad947494..b6ad1b3de9b 100644
--- a/components/shared/base/id.rs
+++ b/components/shared/base/id.rs
@@ -17,7 +17,7 @@ use malloc_size_of::MallocSizeOfOps;
use malloc_size_of_derive::MallocSizeOf;
use parking_lot::Mutex;
use serde::{Deserialize, Serialize};
-use webrender_api::{ExternalScrollId, PipelineId as WebRenderPipelineId, SpatialId};
+use webrender_api::{ExternalScrollId, PipelineId as WebRenderPipelineId};
/// Asserts the size of a type at compile time.
macro_rules! size_of_test {
@@ -397,7 +397,4 @@ pub const TEST_WEBVIEW_ID: WebViewId = WebViewId(TEST_BROWSING_CONTEXT_ID);
pub struct ScrollTreeNodeId {
/// The index of this scroll tree node in the tree's array of nodes.
pub index: usize,
-
- /// The WebRender spatial id of this scroll tree node.
- pub spatial_id: SpatialId,
}
diff --git a/components/shared/compositing/display_list.rs b/components/shared/compositing/display_list.rs
index 6aa822cb145..4fb0d5e94db 100644
--- a/components/shared/compositing/display_list.rs
+++ b/components/shared/compositing/display_list.rs
@@ -6,11 +6,17 @@
use base::id::ScrollTreeNodeId;
use embedder_traits::Cursor;
+use euclid::SideOffsets2D;
use malloc_size_of_derive::MallocSizeOf;
use serde::{Deserialize, Serialize};
use style::values::specified::Overflow;
-use webrender_api::units::{LayoutSize, LayoutVector2D};
-use webrender_api::{Epoch, ExternalScrollId, PipelineId, ScrollLocation, SpatialId};
+use webrender_api::units::{
+ LayoutPixel, LayoutPoint, LayoutRect, LayoutSize, LayoutTransform, LayoutVector2D,
+};
+use webrender_api::{
+ Epoch, ExternalScrollId, PipelineId, ReferenceFrameKind, ScrollLocation, SpatialId,
+ StickyOffsetBounds, TransformStyle,
+};
/// The scroll sensitivity of a scroll node in a particular axis ie whether it can be scrolled due to
/// input events and script events or only script events.
@@ -56,6 +62,29 @@ pub struct HitTestInfo {
pub scroll_tree_node: ScrollTreeNodeId,
}
+#[derive(Debug, Deserialize, Serialize)]
+pub enum SpatialTreeNodeInfo {
+ ReferenceFrame(ReferenceFrameNodeInfo),
+ Scroll(ScrollableNodeInfo),
+ Sticky(StickyNodeInfo),
+}
+
+#[derive(Debug, Deserialize, Serialize)]
+pub struct StickyNodeInfo {
+ pub frame_rect: LayoutRect,
+ pub margins: SideOffsets2D<Option<f32>, LayoutPixel>,
+ pub vertical_offset_bounds: StickyOffsetBounds,
+ pub horizontal_offset_bounds: StickyOffsetBounds,
+}
+
+#[derive(Debug, Deserialize, Serialize)]
+pub struct ReferenceFrameNodeInfo {
+ pub origin: LayoutPoint,
+ pub transform_style: TransformStyle,
+ pub transform: LayoutTransform,
+ pub kind: ReferenceFrameKind,
+}
+
/// Data stored for nodes in the [ScrollTree] that actually scroll,
/// as opposed to reference frames and sticky nodes which do not.
#[derive(Debug, Deserialize, Serialize)]
@@ -64,8 +93,11 @@ pub struct ScrollableNodeInfo {
/// it between successive re-layouts.
pub external_id: ExternalScrollId,
- /// Amount that this `ScrollableNode` can scroll in both directions.
- pub scrollable_size: LayoutSize,
+ /// The content rectangle for this scroll node;
+ pub content_rect: LayoutRect,
+
+ /// The clip rectange for this scroll node.
+ pub clip_rect: LayoutRect,
/// Whether this `ScrollableNode` is sensitive to input events.
pub scroll_sensitivity: AxesScrollSensitivity,
@@ -74,6 +106,12 @@ pub struct ScrollableNodeInfo {
pub offset: LayoutVector2D,
}
+impl ScrollableNodeInfo {
+ fn scrollable_size(&self) -> LayoutSize {
+ self.content_rect.size() - self.clip_rect.size()
+ }
+}
+
#[derive(Debug, Deserialize, Serialize)]
/// A node in a tree of scroll nodes. This may either be a scrollable
/// node which responds to scroll events or a non-scrollable one.
@@ -82,35 +120,51 @@ pub struct ScrollTreeNode {
/// None then this is the root node.
pub parent: Option<ScrollTreeNodeId>,
- /// Scrolling data which will not be None if this is a scrolling node.
- pub scroll_info: Option<ScrollableNodeInfo>,
+ /// The WebRender id, which is filled in when this tree is serialiezd
+ /// into a WebRender display list.
+ pub webrender_id: Option<SpatialId>,
+
+ /// Specific information about this node, depending on whether it is a scroll node
+ /// or a reference frame.
+ pub info: SpatialTreeNodeInfo,
}
impl ScrollTreeNode {
+ /// Get the WebRender [`SpatialId`] for the given [`ScrollNodeId`]. This will
+ /// panic if [`ScrollTree::build_display_list`] has not been called yet.
+ pub fn webrender_id(&self) -> SpatialId {
+ self.webrender_id
+ .expect("Should have called ScrollTree::build_display_list before querying SpatialId")
+ }
+
/// Get the external id of this node.
pub fn external_id(&self) -> Option<ExternalScrollId> {
- self.scroll_info.as_ref().map(|info| info.external_id)
+ match self.info {
+ SpatialTreeNodeInfo::Scroll(ref info) => Some(info.external_id),
+ _ => None,
+ }
}
/// Get the offset id of this node if it applies.
pub fn offset(&self) -> Option<LayoutVector2D> {
- self.scroll_info.as_ref().map(|info| info.offset)
+ match self.info {
+ SpatialTreeNodeInfo::Scroll(ref info) => Some(info.offset),
+ _ => None,
+ }
}
/// Set the offset for this node, returns false if this was a
/// non-scrolling node for which you cannot set the offset.
pub fn set_offset(&mut self, new_offset: LayoutVector2D) -> bool {
- match self.scroll_info {
- Some(ref mut info) => {
- let scrollable_width = info.scrollable_size.width;
- let scrollable_height = info.scrollable_size.height;
-
- if scrollable_width > 0. {
- info.offset.x = (new_offset.x).min(0.0).max(-scrollable_width);
+ match self.info {
+ SpatialTreeNodeInfo::Scroll(ref mut info) => {
+ let scrollable_size = info.scrollable_size();
+ if scrollable_size.width > 0. {
+ info.offset.x = (new_offset.x).min(0.0).max(-scrollable_size.width);
}
- if scrollable_height > 0. {
- info.offset.y = (new_offset.y).min(0.0).max(-scrollable_height);
+ if scrollable_size.height > 0. {
+ info.offset.y = (new_offset.y).min(0.0).max(-scrollable_size.height);
}
true
},
@@ -125,9 +179,9 @@ impl ScrollTreeNode {
&mut self,
scroll_location: ScrollLocation,
) -> Option<(ExternalScrollId, LayoutVector2D)> {
- let info = match self.scroll_info {
- Some(ref mut data) => data,
- None => return None,
+ let info = match self.info {
+ SpatialTreeNodeInfo::Scroll(ref mut info) => info,
+ _ => return None,
};
if info.scroll_sensitivity.x != ScrollSensitivity::ScriptAndInputEvents &&
@@ -148,7 +202,7 @@ impl ScrollTreeNode {
return Some((info.external_id, info.offset));
},
ScrollLocation::End => {
- let end_pos = -info.scrollable_size.height;
+ let end_pos = -info.scrollable_size().height;
if info.offset.y.round() <= end_pos {
// Nothing to do on this layer.
return None;
@@ -159,20 +213,23 @@ impl ScrollTreeNode {
},
};
- let scrollable_width = info.scrollable_size.width;
- let scrollable_height = info.scrollable_size.height;
+ let scrollable_size = info.scrollable_size();
let original_layer_scroll_offset = info.offset;
- if scrollable_width > 0. &&
+ if scrollable_size.width > 0. &&
info.scroll_sensitivity.x == ScrollSensitivity::ScriptAndInputEvents
{
- info.offset.x = (info.offset.x + delta.x).min(0.0).max(-scrollable_width);
+ info.offset.x = (info.offset.x + delta.x)
+ .min(0.0)
+ .max(-scrollable_size.width);
}
- if scrollable_height > 0. &&
+ if scrollable_size.height > 0. &&
info.scroll_sensitivity.y == ScrollSensitivity::ScriptAndInputEvents
{
- info.offset.y = (info.offset.y + delta.y).min(0.0).max(-scrollable_height);
+ info.offset.y = (info.offset.y + delta.y)
+ .min(0.0)
+ .max(-scrollable_size.height);
}
if info.offset != original_layer_scroll_offset {
@@ -199,16 +256,23 @@ impl ScrollTree {
pub fn add_scroll_tree_node(
&mut self,
parent: Option<&ScrollTreeNodeId>,
- spatial_id: SpatialId,
- scroll_info: Option<ScrollableNodeInfo>,
+ info: SpatialTreeNodeInfo,
) -> ScrollTreeNodeId {
self.nodes.push(ScrollTreeNode {
parent: parent.cloned(),
- scroll_info,
+ webrender_id: None,
+ info,
});
ScrollTreeNodeId {
index: self.nodes.len() - 1,
- spatial_id,
+ }
+ }
+
+ /// Once WebRender display list construction is complete for this [`ScrollTree`], update
+ /// the mapping of nodes to WebRender [`SpatialId`]s.
+ pub fn update_mapping(&mut self, mapping: Vec<SpatialId>) {
+ for (spatial_id, node) in mapping.into_iter().zip(self.nodes.iter_mut()) {
+ node.webrender_id = Some(spatial_id);
}
}
@@ -218,10 +282,16 @@ impl ScrollTree {
}
/// Get an immutable reference to the node with the given index.
- pub fn get_node(&mut self, id: &ScrollTreeNodeId) -> &ScrollTreeNode {
+ pub fn get_node(&self, id: &ScrollTreeNodeId) -> &ScrollTreeNode {
&self.nodes[id.index]
}
+ /// Get the WebRender [`SpatialId`] for the given [`ScrollNodeId`]. This will
+ /// panic if [`ScrollTree::build_display_list`] has not been called yet.
+ pub fn webrender_id(&self, id: &ScrollTreeNodeId) -> SpatialId {
+ self.get_node(id).webrender_id()
+ }
+
/// Scroll the given scroll node on this scroll tree. If the node cannot be scrolled,
/// because it isn't a scrollable node or it's already scrolled to the maximum scroll
/// extent, try to scroll an ancestor of this node. Returns the node scrolled and the
@@ -251,8 +321,10 @@ impl ScrollTree {
offset: LayoutVector2D,
) -> bool {
for node in self.nodes.iter_mut() {
- match node.scroll_info {
- Some(ref mut scroll_info) if scroll_info.external_id == external_scroll_id => {
+ match node.info {
+ SpatialTreeNodeInfo::Scroll(ref mut scroll_info)
+ if scroll_info.external_id == external_scroll_id =>
+ {
scroll_info.offset = offset;
return true;
},
@@ -320,15 +392,19 @@ impl CompositorDisplayListInfo {
let mut scroll_tree = ScrollTree::default();
let root_reference_frame_id = scroll_tree.add_scroll_tree_node(
None,
- SpatialId::root_reference_frame(pipeline_id),
- None,
+ SpatialTreeNodeInfo::ReferenceFrame(ReferenceFrameNodeInfo {
+ origin: Default::default(),
+ transform_style: TransformStyle::Flat,
+ transform: LayoutTransform::identity(),
+ kind: ReferenceFrameKind::default(),
+ }),
);
let root_scroll_node_id = scroll_tree.add_scroll_tree_node(
Some(&root_reference_frame_id),
- SpatialId::root_scroll_node(pipeline_id),
- Some(ScrollableNodeInfo {
+ SpatialTreeNodeInfo::Scroll(ScrollableNodeInfo {
external_id: ExternalScrollId(0, pipeline_id),
- scrollable_size: content_size - viewport_size,
+ content_rect: LayoutRect::from_origin_and_size(LayoutPoint::zero(), content_size),
+ clip_rect: LayoutRect::from_origin_and_size(LayoutPoint::zero(), viewport_size),
scroll_sensitivity: viewport_scroll_sensitivity,
offset: LayoutVector2D::zero(),
}),
diff --git a/components/shared/compositing/lib.rs b/components/shared/compositing/lib.rs
index a6701ca2b52..061dfe023df 100644
--- a/components/shared/compositing/lib.rs
+++ b/components/shared/compositing/lib.rs
@@ -236,7 +236,7 @@ impl CrossProcessCompositorApi {
pub fn send_display_list(
&self,
webview_id: WebViewId,
- display_list_info: CompositorDisplayListInfo,
+ display_list_info: &CompositorDisplayListInfo,
list: BuiltDisplayList,
) {
let (display_list_data, display_list_descriptor) = list.into_data();
diff --git a/components/shared/compositing/tests/compositor.rs b/components/shared/compositing/tests/compositor.rs
index 4d2ecfd99c9..e04f1770964 100644
--- a/components/shared/compositing/tests/compositor.rs
+++ b/components/shared/compositing/tests/compositor.rs
@@ -4,11 +4,12 @@
use base::id::ScrollTreeNodeId;
use compositing_traits::display_list::{
- AxesScrollSensitivity, ScrollSensitivity, ScrollTree, ScrollableNodeInfo,
+ AxesScrollSensitivity, ScrollSensitivity, ScrollTree, ScrollableNodeInfo, SpatialTreeNodeInfo,
+ StickyNodeInfo,
};
-use euclid::Size2D;
+use euclid::{SideOffsets2D, Size2D};
use webrender_api::units::LayoutVector2D;
-use webrender_api::{ExternalScrollId, PipelineId, ScrollLocation, SpatialId};
+use webrender_api::{ExternalScrollId, PipelineId, ScrollLocation, StickyOffsetBounds};
fn add_mock_scroll_node(tree: &mut ScrollTree) -> ScrollTreeNodeId {
let pipeline_id = PipelineId(0, 0);
@@ -16,7 +17,6 @@ fn add_mock_scroll_node(tree: &mut ScrollTree) -> ScrollTreeNodeId {
let parent = if num_nodes > 0 {
Some(ScrollTreeNodeId {
index: num_nodes - 1,
- spatial_id: SpatialId::new(num_nodes - 1, pipeline_id),
})
} else {
None
@@ -24,10 +24,10 @@ fn add_mock_scroll_node(tree: &mut ScrollTree) -> ScrollTreeNodeId {
tree.add_scroll_tree_node(
parent.as_ref(),
- SpatialId::new(num_nodes, pipeline_id),
- Some(ScrollableNodeInfo {
+ SpatialTreeNodeInfo::Scroll(ScrollableNodeInfo {
external_id: ExternalScrollId(num_nodes as u64, pipeline_id),
- scrollable_size: Size2D::new(100.0, 100.0),
+ content_rect: Size2D::new(200.0, 200.0).into(),
+ clip_rect: Size2D::new(100.0, 100.0).into(),
scroll_sensitivity: AxesScrollSensitivity {
x: ScrollSensitivity::ScriptAndInputEvents,
y: ScrollSensitivity::ScriptAndInputEvents,
@@ -78,8 +78,15 @@ fn test_scroll_tree_simple_scroll_chaining() {
let pipeline_id = PipelineId(0, 0);
let parent_id = add_mock_scroll_node(&mut scroll_tree);
- let unscrollable_child_id =
- scroll_tree.add_scroll_tree_node(Some(&parent_id), SpatialId::new(1, pipeline_id), None);
+ let unscrollable_child_id = scroll_tree.add_scroll_tree_node(
+ Some(&parent_id),
+ SpatialTreeNodeInfo::Sticky(StickyNodeInfo {
+ frame_rect: Size2D::new(100.0, 100.0).into(),
+ margins: SideOffsets2D::default(),
+ vertical_offset_bounds: StickyOffsetBounds::new(0.0, 0.0),
+ horizontal_offset_bounds: StickyOffsetBounds::new(0.0, 0.0),
+ }),
+ );
let (scrolled_id, offset) = scroll_tree
.scroll_node_or_ancestor(
@@ -157,16 +164,14 @@ fn test_scroll_tree_chain_through_overflow_hidden() {
let pipeline_id = PipelineId(0, 0);
let parent_id = add_mock_scroll_node(&mut scroll_tree);
let overflow_hidden_id = add_mock_scroll_node(&mut scroll_tree);
- scroll_tree
- .get_node_mut(&overflow_hidden_id)
- .scroll_info
- .as_mut()
- .map(|info| {
- info.scroll_sensitivity = AxesScrollSensitivity {
- x: ScrollSensitivity::Script,
- y: ScrollSensitivity::Script,
- };
- });
+ let node = scroll_tree.get_node_mut(&overflow_hidden_id);
+
+ if let SpatialTreeNodeInfo::Scroll(ref mut scroll_node_info) = node.info {
+ scroll_node_info.scroll_sensitivity = AxesScrollSensitivity {
+ x: ScrollSensitivity::Script,
+ y: ScrollSensitivity::Script,
+ };
+ }
let (scrolled_id, offset) = scroll_tree
.scroll_node_or_ancestor(
diff --git a/components/shared/constellation/lib.rs b/components/shared/constellation/lib.rs
index d85fbe31bdf..005295000c9 100644
--- a/components/shared/constellation/lib.rs
+++ b/components/shared/constellation/lib.rs
@@ -152,7 +152,7 @@ pub enum TraversalDirection {
}
/// A task on the <https://html.spec.whatwg.org/multipage/#port-message-queue>
-#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
+#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
pub struct PortMessageTask {
/// The origin of this task.
pub origin: ImmutableOrigin,
diff --git a/components/shared/constellation/structured_data/mod.rs b/components/shared/constellation/structured_data/mod.rs
index 3fb9d0c5f67..81e3849e476 100644
--- a/components/shared/constellation/structured_data/mod.rs
+++ b/components/shared/constellation/structured_data/mod.rs
@@ -20,7 +20,7 @@ pub use transferable::*;
/// A data-holder for serialized data and transferred objects.
/// <https://html.spec.whatwg.org/multipage/#structuredserializewithtransfer>
-#[derive(Clone, Debug, Default, Deserialize, MallocSizeOf, Serialize)]
+#[derive(Debug, Default, Deserialize, MallocSizeOf, Serialize)]
pub struct StructuredSerializedData {
/// Data serialized by SpiderMonkey.
pub serialized: Vec<u8>,
@@ -32,6 +32,8 @@ pub struct StructuredSerializedData {
pub exceptions: Option<HashMap<DomExceptionId, DomException>>,
/// Transferred objects.
pub ports: Option<HashMap<MessagePortId, MessagePortImpl>>,
+ /// Transform streams transferred objects.
+ pub transform_streams: Option<HashMap<MessagePortId, TransformStreamData>>,
}
impl StructuredSerializedData {
diff --git a/components/shared/constellation/structured_data/serializable.rs b/components/shared/constellation/structured_data/serializable.rs
index 194f0567c51..22370087665 100644
--- a/components/shared/constellation/structured_data/serializable.rs
+++ b/components/shared/constellation/structured_data/serializable.rs
@@ -88,7 +88,7 @@ impl Clone for BroadcastMsg {
}
/// File-based blob
-#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
+#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
pub struct FileBlob {
#[ignore_malloc_size_of = "Uuid are hard(not really)"]
id: Uuid,
@@ -164,7 +164,7 @@ impl BroadcastClone for BlobImpl {
}
/// The data backing a DOM Blob.
-#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
+#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
pub struct BlobImpl {
/// UUID of the blob.
blob_id: BlobId,
@@ -177,7 +177,7 @@ pub struct BlobImpl {
}
/// Different backends of Blob
-#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
+#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
pub enum BlobData {
/// File-based blob, whose content lives in the net process
File(FileBlob),
diff --git a/components/shared/constellation/structured_data/transferable.rs b/components/shared/constellation/structured_data/transferable.rs
index 528c1e79e65..3210a41a538 100644
--- a/components/shared/constellation/structured_data/transferable.rs
+++ b/components/shared/constellation/structured_data/transferable.rs
@@ -15,6 +15,12 @@ use strum::EnumIter;
use crate::PortMessageTask;
+#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
+pub struct TransformStreamData {
+ pub readable: (MessagePortId, MessagePortImpl),
+ pub writable: (MessagePortId, MessagePortImpl),
+}
+
/// All the DOM interfaces that can be transferred.
#[derive(Clone, Copy, Debug, EnumIter)]
pub enum Transferrable {
@@ -28,7 +34,7 @@ pub enum Transferrable {
TransformStream,
}
-#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
+#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
enum MessagePortState {
/// <https://html.spec.whatwg.org/multipage/#detached>
Detached,
@@ -42,7 +48,7 @@ enum MessagePortState {
Disabled(bool),
}
-#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
+#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
/// The data and logic backing the DOM managed MessagePort.
pub struct MessagePortImpl {
/// The current state of the port.
diff --git a/components/shared/embedder/resources.rs b/components/shared/embedder/resources.rs
index 830a403698e..28464a95d1a 100644
--- a/components/shared/embedder/resources.rs
+++ b/components/shared/embedder/resources.rs
@@ -116,7 +116,7 @@ impl Resource {
match self {
Resource::BluetoothBlocklist => "gatt_blocklist.txt",
Resource::DomainList => "public_domains.txt",
- Resource::HstsPreloadList => "hsts_preload.json",
+ Resource::HstsPreloadList => "hsts_preload.fstmap",
Resource::BadCertHTML => "badcert.html",
Resource::NetErrorHTML => "neterror.html",
Resource::RippyPNG => "rippy.png",
@@ -155,7 +155,7 @@ fn resources_for_tests() -> Box<dyn ResourceReaderMethods + Sync + Send> {
&include_bytes!("../../../resources/public_domains.txt")[..]
},
Resource::HstsPreloadList => {
- &include_bytes!("../../../resources/hsts_preload.json")[..]
+ &include_bytes!("../../../resources/hsts_preload.fstmap")[..]
},
Resource::BadCertHTML => &include_bytes!("../../../resources/badcert.html")[..],
Resource::NetErrorHTML => &include_bytes!("../../../resources/neterror.html")[..],
diff --git a/components/shared/net/Cargo.toml b/components/shared/net/Cargo.toml
index 79ea936a688..0dfad486455 100644
--- a/components/shared/net/Cargo.toml
+++ b/components/shared/net/Cargo.toml
@@ -19,6 +19,7 @@ compositing_traits = { workspace = true }
content-security-policy = { workspace = true }
cookie = { workspace = true }
crossbeam-channel = { workspace = true }
+data-url = { workspace = true }
embedder_traits = { workspace = true }
headers = { workspace = true }
http = { workspace = true }
diff --git a/components/shared/net/fetch/headers.rs b/components/shared/net/fetch/headers.rs
index 11bb68d5d0a..5ffd537adf8 100644
--- a/components/shared/net/fetch/headers.rs
+++ b/components/shared/net/fetch/headers.rs
@@ -3,8 +3,9 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::iter::Peekable;
-use std::str::Chars;
+use std::str::{Chars, FromStr};
+use data_url::mime::Mime as DataUrlMime;
use headers::HeaderMap;
/// <https://fetch.spec.whatwg.org/#http-tab-or-space>
@@ -184,3 +185,93 @@ fn collect_http_quoted_string(position: &mut Peekable<Chars>, extract_value: boo
// Step 6, 7
value
}
+
+/// <https://fetch.spec.whatwg.org/#concept-header-extract-mime-type>
+/// This function uses data_url::Mime to parse the MIME Type because
+/// mime::Mime does not provide a parser following the Fetch spec
+/// see <https://github.com/hyperium/mime/issues/106>
+pub fn extract_mime_type_as_dataurl_mime(headers: &HeaderMap) -> Option<DataUrlMime> {
+ // > 1: Let charset be null.
+ let mut charset = None;
+ // > 2: Let essence be null.
+ let mut essence = String::new();
+ // > 3: Let mimeType be null.
+ let mut mime_type = None;
+
+ // > 4: Let values be the result of getting, decoding, and splitting `Content-Type`
+ // from headers.
+ // > 5: If values is null, then return failure.
+ let headers_values = get_decode_and_split_header_name("content-type", headers)?;
+
+ // > 6: For each value of values:
+ for header_value in headers_values.iter() {
+ // > 6.1: Let temporaryMimeType be the result of parsing value.
+ match DataUrlMime::from_str(header_value) {
+ // > 6.2: If temporaryMimeType is failure or its essence is "*/*", then continue.
+ Err(_) => continue,
+ Ok(temp_mime) => {
+ let temp_essence = format!("{}/{}", temp_mime.type_, temp_mime.subtype);
+
+ // > 6.2: If temporaryMimeType is failure or its essence is "*/*", then
+ // continue.
+ if temp_essence == "*/*" {
+ continue;
+ }
+
+ // > 6.3: Set mimeType to temporaryMimeType.
+ mime_type = Some(DataUrlMime {
+ type_: temp_mime.type_.to_string(),
+ subtype: temp_mime.subtype.to_string(),
+ parameters: temp_mime.parameters.clone(),
+ });
+
+ // > 6.4: If mimeType’s essence is not essence, then:
+ let temp_charset = &temp_mime.get_parameter("charset");
+ if temp_essence != essence {
+ // > 6.4.1: Set charset to null.
+ // > 6.4.2: If mimeType’s parameters["charset"] exists, then set
+ // charset to mimeType’s parameters["charset"].
+ charset = temp_charset.map(|c| c.to_string());
+ // > 6.4.3: Set essence to mimeType’s essence.
+ essence = temp_essence.to_owned();
+ } else {
+ // > 6.5: Otherwise, if mimeType’s parameters["charset"] does not exist,
+ // and charset is non-null, set mimeType’s parameters["charset"] to charset.
+ if temp_charset.is_none() && charset.is_some() {
+ let DataUrlMime {
+ type_: t,
+ subtype: st,
+ parameters: p,
+ } = mime_type.unwrap();
+ let mut params = p;
+ params.push(("charset".to_string(), charset.clone().unwrap()));
+ mime_type = Some(DataUrlMime {
+ type_: t.to_string(),
+ subtype: st.to_string(),
+ parameters: params,
+ })
+ }
+ }
+ },
+ }
+ }
+
+ // > 7: If mimeType is null, then return failure.
+ // > 8: Return mimeType.
+ mime_type
+}
+
+pub fn extract_mime_type(headers: &HeaderMap) -> Option<Vec<u8>> {
+ extract_mime_type_as_dataurl_mime(headers).map(|m| format!("{}", m).into_bytes())
+}
+
+pub fn extract_mime_type_as_mime(headers: &HeaderMap) -> Option<mime::Mime> {
+ extract_mime_type_as_dataurl_mime(headers).and_then(|mime: DataUrlMime| {
+ // Try to transform a data-url::mime::Mime into a mime::Mime
+ let mut mime_as_str = format!("{}/{}", mime.type_, mime.subtype);
+ for p in mime.parameters {
+ mime_as_str.push_str(format!("; {}={}", p.0, p.1).as_str());
+ }
+ mime_as_str.parse().ok()
+ })
+}
diff --git a/components/shared/net/pub_domains.rs b/components/shared/net/pub_domains.rs
index cbbb2b465b2..6e6f883cd2f 100644
--- a/components/shared/net/pub_domains.rs
+++ b/components/shared/net/pub_domains.rs
@@ -19,9 +19,11 @@ use std::iter::FromIterator;
use std::sync::LazyLock;
use embedder_traits::resources::{self, Resource};
+use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
+use malloc_size_of_derive::MallocSizeOf;
use servo_url::{Host, ImmutableOrigin, ServoUrl};
-#[derive(Clone, Debug, Default)]
+#[derive(Clone, Debug, Default, MallocSizeOf)]
pub struct PubDomainRules {
rules: HashSet<String>,
wildcards: HashSet<String>,
@@ -30,6 +32,10 @@ pub struct PubDomainRules {
static PUB_DOMAINS: LazyLock<PubDomainRules> = LazyLock::new(load_pub_domains);
+pub fn public_suffix_list_size_of(ops: &mut MallocSizeOfOps) -> usize {
+ PUB_DOMAINS.size_of(ops)
+}
+
impl<'a> FromIterator<&'a str> for PubDomainRules {
fn from_iter<T>(iter: T) -> Self
where
diff --git a/components/shared/net/response.rs b/components/shared/net/response.rs
index f91993ddccb..9a01fbbf965 100644
--- a/components/shared/net/response.rs
+++ b/components/shared/net/response.rs
@@ -7,7 +7,6 @@
use std::sync::Mutex;
use std::sync::atomic::AtomicBool;
-use headers::{ContentType, HeaderMapExt};
use http::HeaderMap;
use hyper_serde::Serde;
use malloc_size_of_derive::MallocSizeOf;
@@ -15,6 +14,7 @@ use serde::{Deserialize, Serialize};
use servo_arc::Arc;
use servo_url::ServoUrl;
+use crate::fetch::headers::extract_mime_type_as_mime;
use crate::http_status::HttpStatus;
use crate::{
FetchMetadata, FilteredMetadata, Metadata, NetworkError, ReferrerPolicy, ResourceFetchTiming,
@@ -300,13 +300,7 @@ impl Response {
pub fn metadata(&self) -> Result<FetchMetadata, NetworkError> {
fn init_metadata(response: &Response, url: &ServoUrl) -> Metadata {
let mut metadata = Metadata::default(url.clone());
- metadata.set_content_type(
- response
- .headers
- .typed_get::<ContentType>()
- .map(|v| v.into())
- .as_ref(),
- );
+ metadata.set_content_type(extract_mime_type_as_mime(&response.headers).as_ref());
metadata.location_url.clone_from(&response.location_url);
metadata.headers = Some(Serde(response.headers.clone()));
metadata.status.clone_from(&response.status);
diff --git a/components/shared/net/storage_thread.rs b/components/shared/net/storage_thread.rs
index 0253603016e..2ba0aa12445 100644
--- a/components/shared/net/storage_thread.rs
+++ b/components/shared/net/storage_thread.rs
@@ -4,6 +4,7 @@
use ipc_channel::ipc::IpcSender;
use malloc_size_of_derive::MallocSizeOf;
+use profile_traits::mem::ReportsChan;
use serde::{Deserialize, Serialize};
use servo_url::ServoUrl;
@@ -45,4 +46,7 @@ pub enum StorageThreadMsg {
/// send a reply when done cleaning up thread resources and then shut it down
Exit(IpcSender<()>),
+
+ /// Measure memory used by this thread and send the report over the provided channel.
+ CollectMemoryReport(ReportsChan),
}
diff --git a/components/webdriver_server/actions.rs b/components/webdriver_server/actions.rs
index 43dd0e183dc..7965120b0fd 100644
--- a/components/webdriver_server/actions.rs
+++ b/components/webdriver_server/actions.rs
@@ -33,6 +33,9 @@ pub(crate) enum InputSourceState {
}
// https://w3c.github.io/webdriver/#dfn-pointer-input-source
+// TODO: subtype is used for https://w3c.github.io/webdriver/#dfn-get-a-pointer-id
+// Need to add pointer-id to the following struct
+#[allow(dead_code)]
pub(crate) struct PointerInputState {
subtype: PointerType,
pressed: HashSet<u64>,
@@ -142,7 +145,22 @@ impl Handler {
.or_insert(InputSourceState::Key(KeyInputState::new()));
match action {
KeyAction::Down(action) => {
- self.dispatch_keydown_action(source_id, action)
+ self.dispatch_keydown_action(source_id, action);
+ // Step 9. If subtype is "keyDown", append a copy of action
+ // object with the subtype property changed to "keyUp" to
+ // input state's input cancel list.
+ self.session_mut().unwrap().input_cancel_list.push(
+ ActionSequence {
+ id: source_id.into(),
+ actions: ActionsType::Key {
+ actions: vec![KeyActionItem::Key(KeyAction::Up(
+ KeyUpAction {
+ value: action.value.clone(),
+ },
+ ))],
+ },
+ },
+ );
},
KeyAction::Up(action) => {
self.dispatch_keyup_action(source_id, action)
@@ -172,7 +190,27 @@ impl Handler {
match action {
PointerAction::Cancel => (),
PointerAction::Down(action) => {
- self.dispatch_pointerdown_action(source_id, action)
+ self.dispatch_pointerdown_action(source_id, action);
+
+ // Step 10. If subtype is "pointerDown", append a copy of action
+ // object with the subtype property changed to "pointerUp" to
+ // input state's input cancel list.
+ self.session_mut().unwrap().input_cancel_list.push(
+ ActionSequence {
+ id: source_id.into(),
+ actions: ActionsType::Pointer {
+ parameters: PointerActionParameters {
+ pointer_type: parameters.pointer_type,
+ },
+ actions: vec![PointerActionItem::Pointer(
+ PointerAction::Up(PointerUpAction {
+ button: action.button,
+ ..Default::default()
+ }),
+ )],
+ },
+ },
+ );
},
PointerAction::Move(action) => self.dispatch_pointermove_action(
source_id,
@@ -215,26 +253,18 @@ impl Handler {
// https://w3c.github.io/webdriver/#dfn-dispatch-a-keydown-action
fn dispatch_keydown_action(&mut self, source_id: &str, action: &KeyDownAction) {
- let session = self.session.as_mut().unwrap();
-
+ // Step 1
let raw_key = action.value.chars().next().unwrap();
- let key_input_state = match session.input_state_table.get_mut(source_id).unwrap() {
- InputSourceState::Key(key_input_state) => key_input_state,
- _ => unreachable!(),
- };
-
- session.input_cancel_list.push(ActionSequence {
- id: source_id.into(),
- actions: ActionsType::Key {
- actions: vec![KeyActionItem::Key(KeyAction::Up(KeyUpAction {
- value: action.value.clone(),
- }))],
- },
- });
+ let key_input_state = self.get_key_input_state_mut(source_id);
+ // Step 2 - 11. Done by `keyboard-types` crate.
let keyboard_event = key_input_state.dispatch_keydown(raw_key);
- let cmd_msg =
- WebDriverCommandMsg::KeyboardAction(session.browsing_context_id, keyboard_event);
+
+ // Step 12
+ let cmd_msg = WebDriverCommandMsg::KeyboardAction(
+ self.session().unwrap().browsing_context_id,
+ keyboard_event,
+ );
self.constellation_chan
.send(EmbedderToConstellationMessage::WebDriverCommand(cmd_msg))
.unwrap();
@@ -242,71 +272,57 @@ impl Handler {
// https://w3c.github.io/webdriver/#dfn-dispatch-a-keyup-action
fn dispatch_keyup_action(&mut self, source_id: &str, action: &KeyUpAction) {
- let session = self.session.as_mut().unwrap();
-
+ // Step 1
let raw_key = action.value.chars().next().unwrap();
- let key_input_state = match session.input_state_table.get_mut(source_id).unwrap() {
- InputSourceState::Key(key_input_state) => key_input_state,
- _ => unreachable!(),
- };
-
- session.input_cancel_list.push(ActionSequence {
- id: source_id.into(),
- actions: ActionsType::Key {
- actions: vec![KeyActionItem::Key(KeyAction::Up(KeyUpAction {
- value: action.value.clone(),
- }))],
- },
- });
+ let key_input_state = self.get_key_input_state_mut(source_id);
+ // Step 2 - 11. Done by `keyboard-types` crate.
if let Some(keyboard_event) = key_input_state.dispatch_keyup(raw_key) {
- let cmd_msg =
- WebDriverCommandMsg::KeyboardAction(session.browsing_context_id, keyboard_event);
+ // Step 12
+ let cmd_msg = WebDriverCommandMsg::KeyboardAction(
+ self.session().unwrap().browsing_context_id,
+ keyboard_event,
+ );
self.constellation_chan
.send(EmbedderToConstellationMessage::WebDriverCommand(cmd_msg))
.unwrap();
}
}
+ fn get_pointer_input_state_mut(&mut self, source_id: &str) -> &mut PointerInputState {
+ let session = self.session_mut().unwrap();
+ let pointer_input_state = match session.input_state_table.get_mut(source_id).unwrap() {
+ InputSourceState::Pointer(pointer_input_state) => pointer_input_state,
+ _ => unreachable!(),
+ };
+ pointer_input_state
+ }
+
+ fn get_key_input_state_mut(&mut self, source_id: &str) -> &mut KeyInputState {
+ let session = self.session_mut().unwrap();
+ let key_input_state = match session.input_state_table.get_mut(source_id).unwrap() {
+ InputSourceState::Key(key_input_state) => key_input_state,
+ _ => unreachable!(),
+ };
+ key_input_state
+ }
+
// https://w3c.github.io/webdriver/#dfn-dispatch-a-pointerdown-action
pub(crate) fn dispatch_pointerdown_action(
&mut self,
source_id: &str,
action: &PointerDownAction,
) {
- let session = self.session.as_mut().unwrap();
-
- let pointer_input_state = match session.input_state_table.get_mut(source_id).unwrap() {
- InputSourceState::Pointer(pointer_input_state) => pointer_input_state,
- _ => unreachable!(),
- };
+ let webview_id = self.session().unwrap().webview_id;
+ let pointer_input_state = self.get_pointer_input_state_mut(source_id);
if pointer_input_state.pressed.contains(&action.button) {
return;
}
pointer_input_state.pressed.insert(action.button);
- session.input_cancel_list.push(ActionSequence {
- id: source_id.into(),
- actions: ActionsType::Pointer {
- parameters: PointerActionParameters {
- pointer_type: match pointer_input_state.subtype {
- PointerType::Mouse => PointerType::Mouse,
- PointerType::Pen => PointerType::Pen,
- PointerType::Touch => PointerType::Touch,
- },
- },
- actions: vec![PointerActionItem::Pointer(PointerAction::Up(
- PointerUpAction {
- button: action.button,
- ..Default::default()
- },
- ))],
- },
- });
-
let cmd_msg = WebDriverCommandMsg::MouseButtonAction(
- session.webview_id,
+ webview_id,
MouseButtonAction::Down,
action.button.into(),
pointer_input_state.x as f32,
@@ -319,39 +335,16 @@ impl Handler {
// https://w3c.github.io/webdriver/#dfn-dispatch-a-pointerup-action
pub(crate) fn dispatch_pointerup_action(&mut self, source_id: &str, action: &PointerUpAction) {
- let session = self.session.as_mut().unwrap();
-
- let pointer_input_state = match session.input_state_table.get_mut(source_id).unwrap() {
- InputSourceState::Pointer(pointer_input_state) => pointer_input_state,
- _ => unreachable!(),
- };
+ let webview_id = self.session().unwrap().webview_id;
+ let pointer_input_state = self.get_pointer_input_state_mut(source_id);
if !pointer_input_state.pressed.contains(&action.button) {
return;
}
pointer_input_state.pressed.remove(&action.button);
- session.input_cancel_list.push(ActionSequence {
- id: source_id.into(),
- actions: ActionsType::Pointer {
- parameters: PointerActionParameters {
- pointer_type: match pointer_input_state.subtype {
- PointerType::Mouse => PointerType::Mouse,
- PointerType::Pen => PointerType::Pen,
- PointerType::Touch => PointerType::Touch,
- },
- },
- actions: vec![PointerActionItem::Pointer(PointerAction::Down(
- PointerDownAction {
- button: action.button,
- ..Default::default()
- },
- ))],
- },
- });
-
let cmd_msg = WebDriverCommandMsg::MouseButtonAction(
- session.webview_id,
+ webview_id,
MouseButtonAction::Up,
action.button.into(),
pointer_input_state.x as f32,
@@ -432,12 +425,9 @@ impl Handler {
target_y: f64,
tick_start: Instant,
) {
- let session = self.session.as_mut().unwrap();
- let pointer_input_state = match session.input_state_table.get_mut(source_id).unwrap() {
- InputSourceState::Pointer(pointer_input_state) => pointer_input_state,
- _ => unreachable!(),
- };
-
+ let webview_id = self.session().unwrap().webview_id;
+ let constellation_chan = self.constellation_chan.clone();
+ let pointer_input_state = self.get_pointer_input_state_mut(source_id);
loop {
// Step 1
let time_delta = tick_start.elapsed().as_millis();
@@ -469,10 +459,9 @@ impl Handler {
// Step 7
if x != current_x || y != current_y {
// Step 7.2
- let cmd_msg =
- WebDriverCommandMsg::MouseMoveAction(session.webview_id, x as f32, y as f32);
+ let cmd_msg = WebDriverCommandMsg::MouseMoveAction(webview_id, x as f32, y as f32);
//TODO: Need Synchronization here before updating `pointer_input_state`
- self.constellation_chan
+ constellation_chan
.send(EmbedderToConstellationMessage::WebDriverCommand(cmd_msg))
.unwrap();
// Step 7.3
@@ -567,7 +556,7 @@ impl Handler {
mut curr_delta_y: i64,
tick_start: Instant,
) {
- let session = self.session.as_mut().unwrap();
+ let session = self.session_mut().unwrap();
// Step 1
let time_delta = tick_start.elapsed().as_millis();