diff options
39 files changed, 321 insertions, 493 deletions
diff --git a/Cargo.lock b/Cargo.lock index 11c6ce403ad..4928ea1be00 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -853,9 +853,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.19" +version = "1.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362" +checksum = "04da6a0d40b948dfc4fa8f5bbf402b0fc1a64a28dbf7d12ffd683550f2c1b63a" dependencies = [ "jobserver", "libc", @@ -1074,7 +1074,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" dependencies = [ "lazy_static", - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] @@ -1232,7 +1232,7 @@ dependencies = [ [[package]] name = "content-security-policy" version = "0.5.4" -source = "git+https://github.com/servo/rust-content-security-policy/?branch=servo-csp#827eea44ec0f3d91457d1c0467881cb4f9752520" +source = "git+https://github.com/servo/rust-content-security-policy/?branch=servo-csp#81f95254fbfe98dd6e130260fd872cf950de9fcd" dependencies = [ "base64 0.22.1", "bitflags 2.9.0", @@ -4252,7 +4252,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] @@ -8785,7 +8785,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index cd2cbb54b63..21bc94bcfda 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -243,3 +243,7 @@ codegen-units = 1 # # [patch."https://github.com/servo/<repository>"] # <crate> = { path = "/path/to/local/checkout" } +# +# [patch."https://github.com/servo/rust-content-security-policy"] +# content-security-policy = { path = "../rust-content-security-policy/" } +# content-security-policy = { git = "https://github.com/timvdlippe/rust-content-security-policy/", branch = "fix-report-checks", features = ["serde"] } diff --git a/components/layout/fragment_tree/base_fragment.rs b/components/layout/fragment_tree/base_fragment.rs index 48d672a8547..0cf6ee511cb 100644 --- a/components/layout/fragment_tree/base_fragment.rs +++ b/components/layout/fragment_tree/base_fragment.rs @@ -132,11 +132,6 @@ impl Tag { Tag { node, pseudo } } - /// Returns true if this tag is for a pseudo element. - pub(crate) fn is_pseudo(&self) -> bool { - self.pseudo.is_some() - } - pub(crate) fn to_display_list_fragment_id(self) -> u64 { combine_id_with_fragment_type(self.node.id(), self.pseudo.into()) } diff --git a/components/layout/fragment_tree/box_fragment.rs b/components/layout/fragment_tree/box_fragment.rs index 0e83c0d71a6..e87826ec3ca 100644 --- a/components/layout/fragment_tree/box_fragment.rs +++ b/components/layout/fragment_tree/box_fragment.rs @@ -212,6 +212,10 @@ impl BoxFragment { ) } + pub(crate) fn set_containing_block(&mut self, containing_block: &PhysicalRect<Au>) { + self.cumulative_containing_block_rect = *containing_block; + } + pub fn offset_by_containing_block(&self, rect: &PhysicalRect<Au>) -> PhysicalRect<Au> { rect.translate(self.cumulative_containing_block_rect.origin.to_vector()) } @@ -405,8 +409,4 @@ impl BoxFragment { _ => CollapsedBlockMargins::zero(), } } - - pub(crate) fn set_containing_block(&mut self, containing_block: &PhysicalRect<Au>) { - self.cumulative_containing_block_rect = *containing_block; - } } diff --git a/components/layout/fragment_tree/fragment.rs b/components/layout/fragment_tree/fragment.rs index ceccd1ec304..7708b0893ee 100644 --- a/components/layout/fragment_tree/fragment.rs +++ b/components/layout/fragment_tree/fragment.rs @@ -134,7 +134,9 @@ impl Fragment { Fragment::Float(float_fragment) => float_fragment .borrow_mut() .set_containing_block(containing_block), - Fragment::Positioning(_) => {}, + Fragment::Positioning(positioning_fragment) => positioning_fragment + .borrow_mut() + .set_containing_block(containing_block), Fragment::AbsoluteOrFixedPositioned(hoisted_shared_fragment) => { if let Some(ref fragment) = hoisted_shared_fragment.borrow().fragment { fragment.set_containing_block(containing_block); @@ -191,13 +193,16 @@ impl Fragment { } } - pub(crate) fn cumulative_content_box_rect(&self) -> Option<PhysicalRect<Au>> { + pub(crate) fn cumulative_border_box_rect(&self) -> Option<PhysicalRect<Au>> { match self { Fragment::Box(fragment) | Fragment::Float(fragment) => { let fragment = fragment.borrow(); Some(fragment.offset_by_containing_block(&fragment.border_rect())) }, - Fragment::Positioning(_) | + Fragment::Positioning(fragment) => { + let fragment = fragment.borrow(); + Some(fragment.offset_by_containing_block(&fragment.rect)) + }, Fragment::Text(_) | Fragment::AbsoluteOrFixedPositioned(_) | Fragment::Image(_) | diff --git a/components/layout/fragment_tree/positioning_fragment.rs b/components/layout/fragment_tree/positioning_fragment.rs index 853caed6709..1fe968eb484 100644 --- a/components/layout/fragment_tree/positioning_fragment.rs +++ b/components/layout/fragment_tree/positioning_fragment.rs @@ -20,12 +20,17 @@ pub(crate) struct PositioningFragment { pub base: BaseFragment, pub rect: PhysicalRect<Au>, pub children: Vec<Fragment>, + /// The scrollable overflow of this anonymous fragment's children. pub scrollable_overflow: PhysicalRect<Au>, /// If this fragment was created with a style, the style of the fragment. #[conditional_malloc_size_of] pub style: Option<ServoArc<ComputedValues>>, + + /// This [`PositioningFragment`]'s containing block rectangle in coordinates relative to + /// the initial containing block, but not taking into account any transforms. + pub cumulative_containing_block_rect: PhysicalRect<Au>, } impl PositioningFragment { @@ -61,9 +66,18 @@ impl PositioningFragment { rect, children, scrollable_overflow, + cumulative_containing_block_rect: PhysicalRect::zero(), }) } + pub(crate) fn set_containing_block(&mut self, containing_block: &PhysicalRect<Au>) { + self.cumulative_containing_block_rect = *containing_block; + } + + pub fn offset_by_containing_block(&self, rect: &PhysicalRect<Au>) -> PhysicalRect<Au> { + rect.translate(self.cumulative_containing_block_rect.origin.to_vector()) + } + pub fn print(&self, tree: &mut PrintTree) { tree.new_level(format!( "PositioningFragment\ diff --git a/components/layout/layout_impl.rs b/components/layout/layout_impl.rs index 941fa641cc9..3110899d76e 100644 --- a/components/layout/layout_impl.rs +++ b/components/layout/layout_impl.rs @@ -300,8 +300,9 @@ impl Layout for LayoutThread { feature = "tracing", tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace") )] - fn query_offset_parent(&self, node: OpaqueNode) -> OffsetParentResponse { - process_offset_parent_query(node, self.fragment_tree.borrow().clone()) + fn query_offset_parent(&self, node: TrustedNodeAddress) -> OffsetParentResponse { + let node = unsafe { ServoLayoutNode::new(&node) }; + process_offset_parent_query(node).unwrap_or_default() } #[cfg_attr( diff --git a/components/layout/query.rs b/components/layout/query.rs index 3badff83672..e78acdd0ca8 100644 --- a/components/layout/query.rs +++ b/components/layout/query.rs @@ -7,7 +7,7 @@ use std::sync::Arc; use app_units::Au; use euclid::default::{Point2D, Rect}; -use euclid::{SideOffsets2D, Size2D, Vector2D}; +use euclid::{SideOffsets2D, Size2D}; use itertools::Itertools; use script_layout_interface::wrapper_traits::{ LayoutNode, ThreadSafeLayoutElement, ThreadSafeLayoutNode, @@ -38,19 +38,19 @@ use style::values::specified::GenericGridTemplateComponent; use style::values::specified::box_::DisplayInside; use style_traits::{ParsingMode, ToCss}; +use crate::ArcRefCell; use crate::dom::NodeExt; use crate::flow::inline::construct::{TextTransformation, WhitespaceCollapse}; use crate::fragment_tree::{ - BoxFragment, Fragment, FragmentFlags, FragmentTree, SpecificLayoutInfo, Tag, + BoxFragment, Fragment, FragmentFlags, FragmentTree, SpecificLayoutInfo, }; -use crate::geom::{PhysicalRect, PhysicalVec}; use crate::taffy::SpecificTaffyGridInfo; pub fn process_content_box_request<'dom>(node: impl LayoutNode<'dom> + 'dom) -> Option<Rect<Au>> { let rects: Vec<_> = node .fragments_for_pseudo(None) .iter() - .filter_map(Fragment::cumulative_content_box_rect) + .filter_map(Fragment::cumulative_border_box_rect) .collect(); if rects.is_empty() { return None; @@ -64,7 +64,7 @@ pub fn process_content_box_request<'dom>(node: impl LayoutNode<'dom> + 'dom) -> pub fn process_content_boxes_request<'dom>(node: impl LayoutNode<'dom> + 'dom) -> Vec<Rect<Au>> { node.fragments_for_pseudo(None) .iter() - .filter_map(Fragment::cumulative_content_box_rect) + .filter_map(Fragment::cumulative_border_box_rect) .map(|rect| rect.to_untyped()) .collect() } @@ -428,231 +428,157 @@ fn shorthand_to_css_string( } } -pub fn process_offset_parent_query( - node: OpaqueNode, - fragment_tree: Option<Arc<FragmentTree>>, -) -> OffsetParentResponse { - process_offset_parent_query_inner(node, fragment_tree).unwrap_or_default() +struct OffsetParentFragments { + parent: ArcRefCell<BoxFragment>, + grandparent: Option<Fragment>, } -#[inline] -fn process_offset_parent_query_inner( - node: OpaqueNode, - fragment_tree: Option<Arc<FragmentTree>>, -) -> Option<OffsetParentResponse> { - let fragment_tree = fragment_tree?; - - struct NodeOffsetBoxInfo { - border_box: Rect<Au>, - offset_parent_node_address: Option<OpaqueNode>, - is_static_body_element: bool, +/// <https://www.w3.org/TR/2016/WD-cssom-view-1-20160317/#dom-htmlelement-offsetparent> +fn offset_parent_fragments<'dom>( + node: impl LayoutNode<'dom> + 'dom, +) -> Option<OffsetParentFragments> { + // 1. If any of the following holds true return null and terminate this algorithm: + // * The element does not have an associated CSS layout box. + // * The element is the root element. + // * The element is the HTML body element. + // * The element’s computed value of the position property is fixed. + let fragment = node.fragments_for_pseudo(None).first().cloned()?; + let flags = fragment.base()?.flags; + if flags.intersects( + FragmentFlags::IS_ROOT_ELEMENT | FragmentFlags::IS_BODY_ELEMENT_OF_HTML_ELEMENT_ROOT, + ) { + return None; + } + if matches!( + fragment, Fragment::Box(fragment) if fragment.borrow().style.get_box().position == Position::Fixed + ) { + return None; } - // https://www.w3.org/TR/2016/WD-cssom-view-1-20160317/#extensions-to-the-htmlelement-interface - let mut parent_node_addresses: Vec<Option<(OpaqueNode, bool)>> = Vec::new(); - let tag_to_find = Tag::new(node); - let node_offset_box = fragment_tree.find(|fragment, level, containing_block| { - let base = fragment.base()?; - let is_body_element = base - .flags - .contains(FragmentFlags::IS_BODY_ELEMENT_OF_HTML_ELEMENT_ROOT); - - if fragment.tag() == Some(tag_to_find) { - // Only consider the first fragment of the node found as per a - // possible interpretation of the specification: "[...] return the - // y-coordinate of the top border edge of the first CSS layout box - // associated with the element [...]" - // - // FIXME: Browsers implement this all differently (e.g., [1]) - - // Firefox does returns the union of all layout elements of some - // sort. Chrome returns the first fragment for a block element (the - // same as ours) or the union of all associated fragments in the - // first containing block fragment for an inline element. We could - // implement Chrome's behavior, but our fragment tree currently - // provides insufficient information. - // - // [1]: https://github.com/w3c/csswg-drafts/issues/4541 - let fragment_relative_rect = match fragment { - Fragment::Box(fragment) | Fragment::Float(fragment) => fragment.borrow().border_rect(), - Fragment::Text(fragment) => fragment.borrow().rect, - Fragment::Positioning(fragment) => fragment.borrow().rect, - Fragment::AbsoluteOrFixedPositioned(_) | - Fragment::Image(_) | - Fragment::IFrame(_) => unreachable!(), + // 2. Return the nearest ancestor element of the element for which at least one of + // the following is true and terminate this algorithm if such an ancestor is found: + // * The computed value of the position property is not static. + // * It is the HTML body element. + // * The computed value of the position property of the element is static and the + // ancestor is one of the following HTML elements: td, th, or table. + let mut maybe_parent_node = node.parent_node(); + while let Some(parent_node) = maybe_parent_node { + maybe_parent_node = parent_node.parent_node(); + + if let Some(parent_fragment) = parent_node.fragments_for_pseudo(None).first() { + let parent_fragment = match parent_fragment { + Fragment::Box(box_fragment) | Fragment::Float(box_fragment) => box_fragment, + _ => continue, }; - let mut border_box = fragment_relative_rect.translate(containing_block.origin.to_vector()).to_untyped(); - - // "If any of the following holds true return null and terminate - // this algorithm: [...] The element’s computed value of the - // `position` property is `fixed`." - let is_fixed = matches!( - fragment, Fragment::Box(fragment) if fragment.borrow().style.get_box().position == Position::Fixed - ); + let grandparent_fragment = + maybe_parent_node.and_then(|node| node.fragments_for_pseudo(None).first().cloned()); - if is_body_element { - // "If the element is the HTML body element or [...] return zero - // and terminate this algorithm." - border_box.origin = Point2D::zero(); + if parent_fragment.borrow().style.get_box().position != Position::Static { + return Some(OffsetParentFragments { + parent: parent_fragment.clone(), + grandparent: grandparent_fragment, + }); } - let offset_parent_node = if is_fixed { - None - } else { - // Find the nearest ancestor element eligible as `offsetParent`. - parent_node_addresses[..level] - .iter() - .rev() - .cloned() - .find_map(std::convert::identity) - }; - - Some(NodeOffsetBoxInfo { - border_box, - offset_parent_node_address: offset_parent_node.map(|node| node.0), - is_static_body_element: offset_parent_node.is_some_and(|node| node.1), - }) - } else { - // Record the paths of the nodes being traversed. - let parent_node_address = match fragment { - Fragment::Box(fragment) | Fragment::Float(fragment) => { - let fragment = &*fragment.borrow(); - let is_eligible_parent = is_eligible_parent(fragment); - let is_static_body_element = is_body_element && - fragment.style.get_box().position == Position::Static; - match base.tag { - Some(tag) if is_eligible_parent && !tag.is_pseudo() => { - Some((tag.node, is_static_body_element)) - }, - _ => None, - } - }, - Fragment::AbsoluteOrFixedPositioned(_) | - Fragment::IFrame(_) | - Fragment::Image(_) | - Fragment::Positioning(_) | - Fragment::Text(_) => None, - }; - - while parent_node_addresses.len() <= level { - parent_node_addresses.push(None); + let flags = parent_fragment.borrow().base.flags; + if flags.intersects( + FragmentFlags::IS_BODY_ELEMENT_OF_HTML_ELEMENT_ROOT | + FragmentFlags::IS_TABLE_TH_OR_TD_ELEMENT, + ) { + return Some(OffsetParentFragments { + parent: parent_fragment.clone(), + grandparent: grandparent_fragment, + }); } - parent_node_addresses[level] = parent_node_address; - None } - }); + } - // Bail out if the element doesn't have an associated fragment. - // "If any of the following holds true return null and terminate this - // algorithm: [...] The element does not have an associated CSS layout box." - // (`offsetParent`) "If the element is the HTML body element [...] return - // zero and terminate this algorithm." (others) - let node_offset_box = node_offset_box?; + None +} - let offset_parent_padding_box_corner = if let Some(offset_parent_node_address) = - node_offset_box.offset_parent_node_address - { - // The spec (https://www.w3.org/TR/cssom-view-1/#extensions-to-the-htmlelement-interface) - // says that offsetTop/offsetLeft are always relative to the padding box of the offsetParent. - // However, in practice this is not true in major browsers in the case that the offsetParent is the body - // element and the body element is position:static. In that case offsetLeft/offsetTop are computed - // relative to the root node's border box. - if node_offset_box.is_static_body_element { - fn extract_box_fragment( - fragment: &Fragment, - containing_block: &PhysicalRect<Au>, - ) -> PhysicalVec<Au> { - let (Fragment::Box(fragment) | Fragment::Float(fragment)) = fragment else { - unreachable!(); - }; - // Again, take the *first* associated CSS layout box. - fragment.borrow().border_rect().origin.to_vector() + - containing_block.origin.to_vector() - } +#[inline] +pub fn process_offset_parent_query<'dom>( + node: impl LayoutNode<'dom> + 'dom, +) -> Option<OffsetParentResponse> { + // Only consider the first fragment of the node found as per a + // possible interpretation of the specification: "[...] return the + // y-coordinate of the top border edge of the first CSS layout box + // associated with the element [...]" + // + // FIXME: Browsers implement this all differently (e.g., [1]) - + // Firefox does returns the union of all layout elements of some + // sort. Chrome returns the first fragment for a block element (the + // same as ours) or the union of all associated fragments in the + // first containing block fragment for an inline element. We could + // implement Chrome's behavior, but our fragment tree currently + // provides insufficient information. + // + // [1]: https://github.com/w3c/csswg-drafts/issues/4541 + // > 1. If the element is the HTML body element or does not have any associated CSS + // layout box return zero and terminate this algorithm. + let fragment = node.fragments_for_pseudo(None).first().cloned()?; + let mut border_box = fragment.cumulative_border_box_rect()?; + + // 2. If the offsetParent of the element is null return the x-coordinate of the left + // border edge of the first CSS layout box associated with the element, relative to + // the initial containing block origin, ignoring any transforms that apply to the + // element and its ancestors, and terminate this algorithm. + let Some(offset_parent_fragment) = offset_parent_fragments(node) else { + return Some(OffsetParentResponse { + node_address: None, + rect: border_box.to_untyped(), + }); + }; - let containing_block = &fragment_tree.initial_containing_block; - let fragment = &fragment_tree.root_fragments[0]; - if let Fragment::AbsoluteOrFixedPositioned(shared_fragment) = fragment { - let shared_fragment = &*shared_fragment.borrow(); - let fragment = shared_fragment.fragment.as_ref().unwrap(); - extract_box_fragment(fragment, containing_block) - } else { - extract_box_fragment(fragment, containing_block) - } + let parent_fragment = offset_parent_fragment.parent.borrow(); + let parent_is_static_body_element = parent_fragment + .base + .flags + .contains(FragmentFlags::IS_BODY_ELEMENT_OF_HTML_ELEMENT_ROOT) && + parent_fragment.style.get_box().position == Position::Static; + + // For `offsetLeft`: + // 3. Return the result of subtracting the y-coordinate of the top padding edge of the + // first CSS layout box associated with the offsetParent of the element from the + // y-coordinate of the top border edge of the first CSS layout box associated with the + // element, relative to the initial containing block origin, ignoring any transforms + // that apply to the element and its ancestors. + // + // We generalize this for `offsetRight` as described in the specification. + let grandparent_box_fragment = || match offset_parent_fragment.grandparent { + Some(Fragment::Box(box_fragment)) | Some(Fragment::Float(box_fragment)) => { + Some(box_fragment) + }, + _ => None, + }; + + // The spec (https://www.w3.org/TR/cssom-view-1/#extensions-to-the-htmlelement-interface) + // says that offsetTop/offsetLeft are always relative to the padding box of the offsetParent. + // However, in practice this is not true in major browsers in the case that the offsetParent is the body + // element and the body element is position:static. In that case offsetLeft/offsetTop are computed + // relative to the root node's border box. + // + // See <https://github.com/w3c/csswg-drafts/issues/10549>. + let parent_offset_rect = if parent_is_static_body_element { + if let Some(grandparent_fragment) = grandparent_box_fragment() { + let grandparent_fragment = grandparent_fragment.borrow(); + grandparent_fragment.offset_by_containing_block(&grandparent_fragment.border_rect()) } else { - // Find the top and left padding edges of "the first CSS layout box - // associated with the `offsetParent` of the element". - // - // Since we saw `offset_parent_node_address` once, we should be able - // to find it again. - let offset_parent_node_tag = Tag::new(offset_parent_node_address); - fragment_tree - .find(|fragment, _, containing_block| { - match fragment { - Fragment::Box(fragment) | Fragment::Float(fragment) => { - let fragment = fragment.borrow(); - if fragment.base.tag == Some(offset_parent_node_tag) { - // Again, take the *first* associated CSS layout box. - let padding_box_corner = fragment.padding_rect().origin.to_vector() - + containing_block.origin.to_vector(); - Some(padding_box_corner) - } else { - None - } - }, - Fragment::AbsoluteOrFixedPositioned(_) - | Fragment::Text(_) - | Fragment::Image(_) - | Fragment::IFrame(_) - | Fragment::Positioning(_) => None, - } - }) - .unwrap() + parent_fragment.offset_by_containing_block(&parent_fragment.padding_rect()) } } else { - // "If the offsetParent of the element is null," subtract zero in the - // following step. - Vector2D::zero() + parent_fragment.offset_by_containing_block(&parent_fragment.padding_rect()) }; + border_box = border_box.translate(-parent_offset_rect.origin.to_vector()); + Some(OffsetParentResponse { - node_address: node_offset_box.offset_parent_node_address.map(Into::into), - // "Return the result of subtracting the x-coordinate of the left - // padding edge of the first CSS layout box associated with the - // `offsetParent` of the element from the x-coordinate of the left - // border edge of the first CSS layout box associated with the element, - // relative to the initial containing block origin, ignoring any - // transforms that apply to the element and its ancestors." (and vice - // versa for the top border edge) - rect: node_offset_box - .border_box - .translate(-offset_parent_padding_box_corner.to_untyped()), + node_address: parent_fragment.base.tag.map(|tag| tag.node.into()), + rect: border_box.to_untyped(), }) } -/// Returns whether or not the element with the given style and body element determination -/// is eligible to be a parent element for offset* queries. -/// -/// From <https://www.w3.org/TR/cssom-view-1/#dom-htmlelement-offsetparent>: -/// -/// > Return the nearest ancestor element of the element for which at least one of the following is -/// > true and terminate this algorithm if such an ancestor is found: -/// > 1. The computed value of the position property is not static. -/// > 2. It is the HTML body element. -/// > 3. The computed value of the position property of the element is static and the ancestor is -/// > one of the following HTML elements: td, th, or table. -fn is_eligible_parent(fragment: &BoxFragment) -> bool { - fragment - .base - .flags - .contains(FragmentFlags::IS_BODY_ELEMENT_OF_HTML_ELEMENT_ROOT) || - fragment.style.get_box().position != Position::Static || - fragment - .base - .flags - .contains(FragmentFlags::IS_TABLE_TH_OR_TD_ELEMENT) -} - /// <https://html.spec.whatwg.org/multipage/#get-the-text-steps> pub fn get_the_text_steps<'dom>(node: impl LayoutNode<'dom>) -> String { // Step 1: If element is not being rendered or if the user agent is a non-CSS user agent, then diff --git a/components/layout/table/layout.rs b/components/layout/table/layout.rs index 57b48ae0bca..2261f7d165c 100644 --- a/components/layout/table/layout.rs +++ b/components/layout/table/layout.rs @@ -2142,23 +2142,27 @@ impl<'a> TableLayout<'a> { for column_group in self.table.column_groups.iter() { let column_group = column_group.borrow(); if !column_group.is_empty() { - fragments.push(Fragment::Positioning(PositioningFragment::new_empty( + let fragment = Fragment::Positioning(PositioningFragment::new_empty( column_group.base.base_fragment_info, dimensions .get_column_group_rect(&column_group) .as_physical(None), column_group.base.style.clone(), - ))); + )); + column_group.base.set_fragment(fragment.clone()); + fragments.push(fragment); } } for (column_index, column) in self.table.columns.iter().enumerate() { let column = column.borrow(); - fragments.push(Fragment::Positioning(PositioningFragment::new_empty( + let fragment = Fragment::Positioning(PositioningFragment::new_empty( column.base.base_fragment_info, dimensions.get_column_rect(column_index).as_physical(None), column.base.style.clone(), - ))); + )); + column.base.set_fragment(fragment.clone()); + fragments.push(fragment); } } diff --git a/components/net/fetch/methods.rs b/components/net/fetch/methods.rs index 53bc2817292..b1ad01b81e0 100644 --- a/components/net/fetch/methods.rs +++ b/components/net/fetch/methods.rs @@ -23,8 +23,8 @@ use net_traits::http_status::HttpStatus; use net_traits::policy_container::{PolicyContainer, RequestPolicyContainer}; use net_traits::request::{ BodyChunkRequest, BodyChunkResponse, CredentialsMode, Destination, Initiator, - InsecureRequestsPolicy, Origin, RedirectMode, Referrer, Request, RequestMode, ResponseTainting, - Window, is_cors_safelisted_method, is_cors_safelisted_request_header, + InsecureRequestsPolicy, Origin, ParserMetadata, RedirectMode, Referrer, Request, RequestMode, + ResponseTainting, Window, is_cors_safelisted_method, is_cors_safelisted_request_header, }; use net_traits::response::{Response, ResponseBody, ResponseType}; use net_traits::{ @@ -169,6 +169,29 @@ pub async fn fetch_with_cors_cache( // TODO: We don't implement fetchParams as defined in the spec } +fn convert_request_to_csp_request(request: &Request, origin: &ImmutableOrigin) -> csp::Request { + csp::Request { + url: request.url().into_url(), + origin: origin.clone().into_url_origin(), + redirect_count: request.redirect_count, + destination: request.destination, + initiator: match request.initiator { + Initiator::Download => csp::Initiator::Download, + Initiator::ImageSet => csp::Initiator::ImageSet, + Initiator::Manifest => csp::Initiator::Manifest, + Initiator::Prefetch => csp::Initiator::Prefetch, + _ => csp::Initiator::None, + }, + nonce: request.cryptographic_nonce_metadata.clone(), + integrity_metadata: request.integrity_metadata.clone(), + parser_metadata: match request.parser_metadata { + ParserMetadata::ParserInserted => csp::ParserMetadata::ParserInserted, + ParserMetadata::NotParserInserted => csp::ParserMetadata::NotParserInserted, + ParserMetadata::Default => csp::ParserMetadata::None, + }, + } +} + /// <https://www.w3.org/TR/CSP/#should-block-request> pub fn should_request_be_blocked_by_csp( request: &Request, @@ -178,17 +201,7 @@ pub fn should_request_be_blocked_by_csp( Origin::Client => return (csp::CheckResult::Allowed, Vec::new()), Origin::Origin(origin) => origin, }; - - let csp_request = csp::Request { - url: request.url().into_url(), - origin: origin.clone().into_url_origin(), - redirect_count: request.redirect_count, - destination: request.destination, - initiator: csp::Initiator::None, - nonce: request.cryptographic_nonce_metadata.clone(), - integrity_metadata: request.integrity_metadata.clone(), - parser_metadata: csp::ParserMetadata::None, - }; + let csp_request = convert_request_to_csp_request(request, origin); policy_container .csp_list @@ -197,6 +210,24 @@ pub fn should_request_be_blocked_by_csp( .unwrap_or((csp::CheckResult::Allowed, Vec::new())) } +/// <https://www.w3.org/TR/CSP/#report-for-request> +pub fn report_violations_for_request_by_csp( + request: &Request, + policy_container: &PolicyContainer, +) -> Vec<csp::Violation> { + let origin = match &request.origin { + Origin::Client => return Vec::new(), + Origin::Origin(origin) => origin, + }; + let csp_request = convert_request_to_csp_request(request, origin); + + policy_container + .csp_list + .as_ref() + .map(|c| c.report_violations_for_request(&csp_request)) + .unwrap_or_default() +} + /// [Main fetch](https://fetch.spec.whatwg.org/#concept-main-fetch) pub async fn main_fetch( fetch_params: &mut FetchParams, @@ -232,9 +263,6 @@ pub async fn main_fetch( ))); } - // Step 2.2. - // TODO: Report violations. - // The request should have a valid policy_container associated with it. // TODO: This should not be `Client` here let policy_container = match &request.policy_container { @@ -242,6 +270,13 @@ pub async fn main_fetch( RequestPolicyContainer::PolicyContainer(container) => container.to_owned(), }; + // Step 2.2. + let violations = report_violations_for_request_by_csp(request, &policy_container); + + if !violations.is_empty() { + target.process_csp_violations(request, violations); + } + // Step 3. // TODO: handle request abort. diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs index 77d1ee37c03..b3345b90fc0 100644 --- a/components/script/dom/globalscope.rs +++ b/components/script/dom/globalscope.rs @@ -2422,7 +2422,8 @@ impl GlobalScope { headers: &Option<Serde<HeaderMap>>, ) -> Option<CspList> { // TODO: Implement step 1 (local scheme special case) - let mut csp = headers.as_ref()?.get_all("content-security-policy").iter(); + let headers = headers.as_ref()?; + let mut csp = headers.get_all("content-security-policy").iter(); // This silently ignores the CSP if it contains invalid Unicode. // We should probably report an error somewhere. let c = csp.next().and_then(|c| c.to_str().ok())?; @@ -2435,6 +2436,19 @@ impl GlobalScope { PolicyDisposition::Enforce, )); } + let csp_report = headers + .get_all("content-security-policy-report-only") + .iter(); + // This silently ignores the CSP if it contains invalid Unicode. + // We should probably report an error somewhere. + for c in csp_report { + let c = c.to_str().ok()?; + csp_list.append(CspList::parse( + c, + PolicySource::Header, + PolicyDisposition::Report, + )); + } Some(csp_list) } @@ -2822,36 +2836,16 @@ impl GlobalScope { })) } - #[allow(unsafe_code)] - pub(crate) fn is_js_evaluation_allowed(&self, cx: SafeJSContext) -> bool { + pub(crate) fn is_js_evaluation_allowed(&self, source: &str) -> bool { let Some(csp_list) = self.get_csp_list() else { return true; }; - let scripted_caller = unsafe { describe_scripted_caller(*cx) }.unwrap_or_default(); - let is_js_evaluation_allowed = csp_list.is_js_evaluation_allowed() == CheckResult::Allowed; - - if !is_js_evaluation_allowed { - // FIXME: Don't fire event if `script-src` and `default-src` - // were not passed. - for policy in csp_list.0 { - let report = CSPViolationReportBuilder::default() - .resource("eval".to_owned()) - .effective_directive("script-src".to_owned()) - .report_only(policy.disposition == PolicyDisposition::Report) - .source_file(scripted_caller.filename.clone()) - .line_number(scripted_caller.line) - .column_number(scripted_caller.col) - .build(self); - let task = CSPViolationReportTask::new(self, report); + let (is_js_evaluation_allowed, violations) = csp_list.is_js_evaluation_allowed(source); - self.task_manager() - .dom_manipulation_task_source() - .queue(task); - } - } + self.report_csp_violations(violations); - is_js_evaluation_allowed + is_js_evaluation_allowed == CheckResult::Allowed } pub(crate) fn create_image_bitmap( @@ -3464,10 +3458,13 @@ impl GlobalScope { unreachable!(); } + #[allow(unsafe_code)] pub(crate) fn report_csp_violations(&self, violations: Vec<Violation>) { + let scripted_caller = + unsafe { describe_scripted_caller(*GlobalScope::get_cx()) }.unwrap_or_default(); for violation in violations { let (sample, resource) = match violation.resource { - ViolationResource::Inline { .. } => (None, "inline".to_owned()), + ViolationResource::Inline { sample } => (sample, "inline".to_owned()), ViolationResource::Url(url) => (None, url.into()), ViolationResource::TrustedTypePolicy { sample } => { (Some(sample), "trusted-types-policy".to_owned()) @@ -3475,6 +3472,8 @@ impl GlobalScope { ViolationResource::TrustedTypeSink { sample } => { (Some(sample), "trusted-types-sink".to_owned()) }, + ViolationResource::Eval { sample } => (sample, "eval".to_owned()), + ViolationResource::WasmEval => (None, "wasm-eval".to_owned()), }; let report = CSPViolationReportBuilder::default() .resource(resource) @@ -3482,6 +3481,9 @@ impl GlobalScope { .effective_directive(violation.directive.name) .original_policy(violation.policy.to_string()) .report_only(violation.policy.disposition == PolicyDisposition::Report) + .source_file(scripted_caller.filename.clone()) + .line_number(scripted_caller.line) + .column_number(scripted_caller.col + 1) .build(self); let task = CSPViolationReportTask::new(self, report); self.task_manager() diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 418c737acd4..932a9ec7f2d 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -2392,7 +2392,10 @@ impl Window { return (None, Rect::zero()); } - let response = self.layout.borrow().query_offset_parent(node.to_opaque()); + let response = self + .layout + .borrow() + .query_offset_parent(node.to_trusted_node_address()); let element = response.node_address.and_then(|parent_node_address| { let node = unsafe { from_untrusted_node_address(parent_node_address) }; DomRoot::downcast(node) diff --git a/components/script/script_runtime.rs b/components/script/script_runtime.rs index d6832a644ec..1f05c15d74e 100644 --- a/components/script/script_runtime.rs +++ b/components/script/script_runtime.rs @@ -19,7 +19,7 @@ use std::time::{Duration, Instant}; use std::{os, ptr, thread}; use background_hang_monitor_api::ScriptHangAnnotation; -use content_security_policy::{CheckResult, PolicyDisposition}; +use content_security_policy::CheckResult; use js::conversions::jsstr_to_string; use js::glue::{ CollectServoSizes, CreateJobQueue, DeleteJobQueue, DispatchableRun, JobQueueTraps, @@ -45,7 +45,7 @@ pub(crate) use js::rust::ThreadSafeJSContext; use js::rust::wrappers::{GetPromiseIsHandled, JS_GetPromiseResult}; use js::rust::{ Handle, HandleObject as RustHandleObject, IntoHandle, JSEngine, JSEngineHandle, ParentRuntime, - Runtime as RustRuntime, describe_scripted_caller, + Runtime as RustRuntime, }; use malloc_size_of::MallocSizeOfOps; use malloc_size_of_derive::MallocSizeOf; @@ -82,7 +82,6 @@ use crate::microtask::{EnqueuedPromiseCallback, Microtask, MicrotaskQueue}; use crate::realms::{AlreadyInRealm, InRealm}; use crate::script_module::EnsureModuleHooksInitialized; use crate::script_thread::trace_thread; -use crate::security_manager::{CSPViolationReportBuilder, CSPViolationReportTask}; use crate::task_source::SendableTaskSource; static JOB_QUEUE_TRAPS: JobQueueTraps = JobQueueTraps { @@ -373,10 +372,6 @@ unsafe extern "C" fn content_security_policy_allows( let cx = JSContext::from_ptr(cx); wrap_panic(&mut || { // SpiderMonkey provides null pointer when executing webassembly. - let sample = match sample { - sample if !sample.is_null() => Some(jsstr_to_string(*cx, *sample)), - _ => None, - }; let in_realm_proof = AlreadyInRealm::assert_for_cx(cx); let global = GlobalScope::from_context(*cx, InRealm::Already(&in_realm_proof)); let Some(csp_list) = global.get_csp_list() else { @@ -384,43 +379,19 @@ unsafe extern "C" fn content_security_policy_allows( return; }; - let is_js_evaluation_allowed = csp_list.is_js_evaluation_allowed() == CheckResult::Allowed; - let is_wasm_evaluation_allowed = - csp_list.is_wasm_evaluation_allowed() == CheckResult::Allowed; - let scripted_caller = describe_scripted_caller(*cx).unwrap_or_default(); - - let resource = match runtime_code { - RuntimeCode::JS => "eval".to_owned(), - RuntimeCode::WASM => "wasm-eval".to_owned(), - }; - - allowed = match runtime_code { - RuntimeCode::JS if is_js_evaluation_allowed => true, - RuntimeCode::WASM if is_wasm_evaluation_allowed => true, - _ => false, + let (is_evaluation_allowed, violations) = match runtime_code { + RuntimeCode::JS => { + let source = match sample { + sample if !sample.is_null() => &jsstr_to_string(*cx, *sample), + _ => "", + }; + csp_list.is_js_evaluation_allowed(source) + }, + RuntimeCode::WASM => csp_list.is_wasm_evaluation_allowed(), }; - if !allowed { - // FIXME: Don't fire event if `script-src` and `default-src` - // were not passed. - for policy in csp_list.0 { - let report = CSPViolationReportBuilder::default() - .resource(resource.clone()) - .sample(sample.clone()) - .report_only(policy.disposition == PolicyDisposition::Report) - .source_file(scripted_caller.filename.clone()) - .line_number(scripted_caller.line) - .column_number(scripted_caller.col) - .effective_directive("script-src".to_owned()) - .build(&global); - let task = CSPViolationReportTask::new(&global, report); - - global - .task_manager() - .dom_manipulation_task_source() - .queue(task); - } - } + global.report_csp_violations(violations); + allowed = is_evaluation_allowed == CheckResult::Allowed; }); allowed } diff --git a/components/script/timers.rs b/components/script/timers.rs index 244aa2df4ed..0afc3da164a 100644 --- a/components/script/timers.rs +++ b/components/script/timers.rs @@ -421,8 +421,7 @@ impl JsTimers { ) -> i32 { let callback = match callback { TimerCallback::StringTimerCallback(code_str) => { - let cx = GlobalScope::get_cx(); - if global.is_js_evaluation_allowed(cx) { + if global.is_js_evaluation_allowed(code_str.as_ref()) { InternalTimerCallback::StringTimerCallback(code_str) } else { return 0; diff --git a/components/shared/script_layout/lib.rs b/components/shared/script_layout/lib.rs index 499d99753fe..a40b8c403c1 100644 --- a/components/shared/script_layout/lib.rs +++ b/components/shared/script_layout/lib.rs @@ -252,7 +252,7 @@ pub trait Layout { point: Point2D<f32>, query_type: NodesFromPointQueryType, ) -> Vec<UntrustedNodeAddress>; - fn query_offset_parent(&self, node: OpaqueNode) -> OffsetParentResponse; + fn query_offset_parent(&self, node: TrustedNodeAddress) -> OffsetParentResponse; fn query_resolved_style( &self, node: TrustedNodeAddress, diff --git a/ports/servoshell/Cargo.toml b/ports/servoshell/Cargo.toml index ed3a340e8d4..c77ea06fa15 100644 --- a/ports/servoshell/Cargo.toml +++ b/ports/servoshell/Cargo.toml @@ -22,7 +22,7 @@ bench = false # since build-scripts can't detect the cargo target os at build-time, we # must unconditionally add these dependencies. See https://github.com/rust-lang/cargo/issues/4932 [build-dependencies] -# MacOS only +# macOS only cc = "1.2" [target.'cfg(windows)'.build-dependencies] @@ -37,7 +37,7 @@ ProductName = "Servo" [features] crown = ["libservo/crown"] debugmozjs = ["libservo/debugmozjs"] -default = ["max_log_level", "webdriver", "webxr", "webgpu"] +default = ["max_log_level", "webdriver", "webgpu", "webxr"] jitspew = ["libservo/jitspew"] js_backtrace = ["libservo/js_backtrace"] max_log_level = ["log/release_max_level_info"] @@ -50,30 +50,30 @@ tracing-hitrace = ["tracing", "dep:hitrace"] tracing-perfetto = ["tracing", "dep:tracing-perfetto"] webdriver = ["libservo/webdriver"] webgl_backtrace = ["libservo/webgl_backtrace"] -webxr = ["libservo/webxr"] webgpu = ["libservo/webgpu"] +webxr = ["libservo/webxr"] [dependencies] +cfg-if = { workspace = true } dpi = { workspace = true } euclid = { workspace = true } -libc = { workspace = true } -libservo = { path = "../../components/servo", features = ["background_hang_monitor", "bluetooth"] } -cfg-if = { workspace = true } -keyboard-types = { workspace = true } -log = { workspace = true } getopts = { workspace = true } hitrace = { workspace = true, optional = true } +icu_locid = "1.5.0" image = { workspace = true } +keyboard-types = { workspace = true } +libc = { workspace = true } +libservo = { path = "../../components/servo", features = ["background_hang_monitor", "bluetooth"] } +log = { workspace = true } mime_guess = { workspace = true } -url = { workspace = true } raw-window-handle = { workspace = true } rustls = { workspace = true, features = ["aws-lc-rs"] } servo-tracing = { workspace = true } tokio = { workspace = true } tracing = { workspace = true, optional = true } -tracing-subscriber = { workspace = true, optional = true, features = ["env-filter"] } tracing-perfetto = { workspace = true, optional = true } -icu_locid = "1.5.0" +tracing-subscriber = { workspace = true, optional = true, features = ["env-filter"] } +url = { workspace = true } [target.'cfg(target_os = "android")'.dependencies] android_logger = "0.15" @@ -81,36 +81,34 @@ ipc-channel = { workspace = true } jni = "0.21.1" libloading = "0.8" - [target.'cfg(not(target_os = "android"))'.dependencies] backtrace = { workspace = true } [target.'cfg(target_env = "ohos")'.dependencies] env_filter = "0.1.3" euclid = { workspace = true } +hilog = "0.2.0" # force inprocess, until libc-rs 0.2.156 is released containing # https://github.com/rust-lang/libc/commit/9e248e212c5602cb4e98676e4c21ea0382663a12 ipc-channel = { workspace = true, features = ["force-inprocess"] } -hilog = "0.2.0" napi-derive-ohos = "1.0.4" napi-ohos = "1.0.4" -ohos-vsync = "0.1.3" -ohos-ime = { version = "0.2" } +ohos-ime = "0.2" ohos-ime-sys = "0.1.4" +ohos-vsync = "0.1.3" xcomponent-sys = { version = "0.3.1", features = ["api-12", "keyboard-types"] } [target.'cfg(any(target_os = "android", target_env = "ohos"))'.dependencies] nix = { workspace = true, features = ["fs"] } -surfman = { workspace = true, features = ["sm-angle-default"] } serde_json = { workspace = true } +surfman = { workspace = true, features = ["sm-angle-default"] } [target.'cfg(not(any(target_os = "android", target_env = "ohos")))'.dependencies] -# For optional feature servo_allocator/use-system-allocator -servo_allocator = { path = "../../components/allocator" } dirs = "5.0" -egui = { version = "0.31.1" } -egui_glow = { version = "0.31.1", features = ["winit"] } +egui = "0.31.1" +egui-file-dialog = "0.9.0" egui-winit = { version = "0.31.1", default-features = false, features = ["clipboard", "wayland"] } +egui_glow = { version = "0.31.1", features = ["winit"] } gilrs = "0.11.0" gleam = { workspace = true } glow = "0.16.0" @@ -119,17 +117,18 @@ http = { workspace = true } net = { path = "../../components/net" } net_traits = { workspace = true } serde_json = { workspace = true } +# For optional feature servo_allocator/use-system-allocator +servo_allocator = { path = "../../components/allocator" } shellwords = "1.0.0" -surfman = { workspace = true, features = ["sm-x11", "sm-raw-window-handle-06"] } -egui-file-dialog = "0.9.0" +surfman = { workspace = true, features = ["sm-raw-window-handle-06", "sm-x11"] } winit = "0.30.9" [target.'cfg(any(all(target_os = "linux", not(target_env = "ohos")), target_os = "macos"))'.dependencies] sig = "1.0" [target.'cfg(target_os = "windows")'.dependencies] -windows-sys = { workspace = true, features = ["Win32_Graphics_Gdi"] } libservo = { path = "../../components/servo", features = ["no-wgl"] } +windows-sys = { workspace = true, features = ["Win32_Graphics_Gdi"] } [target.'cfg(target_os = "macos")'.dependencies] objc2-app-kit = { version = "0.2.2", default-features = false, features = [ diff --git a/tests/wpt/meta/content-security-policy/default-src/default-src-sri_hash.sub.html.ini b/tests/wpt/meta/content-security-policy/default-src/default-src-sri_hash.sub.html.ini index b86623fcf06..ee237b70bc4 100644 --- a/tests/wpt/meta/content-security-policy/default-src/default-src-sri_hash.sub.html.ini +++ b/tests/wpt/meta/content-security-policy/default-src/default-src-sri_hash.sub.html.ini @@ -2,8 +2,11 @@ [multiple matching integrity] expected: FAIL - [partially matching integrity] + [matching integrity] expected: FAIL - [External script in a script tag with matching SRI hash should run.] + [matching integrity (case-insensitive algorithm)] + expected: FAIL + + [matching plus unsupported integrity] expected: FAIL diff --git a/tests/wpt/meta/content-security-policy/generic/304-response-should-update-csp.sub.html.ini b/tests/wpt/meta/content-security-policy/generic/304-response-should-update-csp.sub.html.ini deleted file mode 100644 index f4f10d1a85c..00000000000 --- a/tests/wpt/meta/content-security-policy/generic/304-response-should-update-csp.sub.html.ini +++ /dev/null @@ -1,6 +0,0 @@ -[304-response-should-update-csp.sub.html] - [Test that the first frame does not use nonce def] - expected: FAIL - - [Test that the second frame does not use nonce abc] - expected: FAIL diff --git a/tests/wpt/meta/content-security-policy/img-src/img-src-self-unique-origin.html.ini b/tests/wpt/meta/content-security-policy/img-src/img-src-self-unique-origin.html.ini deleted file mode 100644 index f5ccd49ccee..00000000000 --- a/tests/wpt/meta/content-security-policy/img-src/img-src-self-unique-origin.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[img-src-self-unique-origin.html] - expected: TIMEOUT - [Image's url must not match with 'self'. Image must be blocked.] - expected: TIMEOUT diff --git a/tests/wpt/meta/content-security-policy/resource-hints/prefetch-generate-directives.html.ini b/tests/wpt/meta/content-security-policy/resource-hints/prefetch-generate-directives.html.ini index 7843cf2984c..df10ea63fd6 100644 --- a/tests/wpt/meta/content-security-policy/resource-hints/prefetch-generate-directives.html.ini +++ b/tests/wpt/meta/content-security-policy/resource-hints/prefetch-generate-directives.html.ini @@ -1,19 +1,19 @@ [prefetch-generate-directives.html] expected: TIMEOUT [Test that script-src enabled with everything else disabled allows prefetching] - expected: FAIL + expected: TIMEOUT [Test that script-src enabled with default-src disabled allows prefetching] - expected: FAIL + expected: NOTRUN [Test that img-src enabled with everything else disabled allows prefetching] - expected: FAIL + expected: NOTRUN [Test that img-src enabled with default-src disabled allows prefetching] - expected: FAIL + expected: NOTRUN [Test that connect-src enabled with everything else disabled allows prefetching] - expected: TIMEOUT + expected: NOTRUN [Test that connect-src enabled with default-src disabled allows prefetching] expected: NOTRUN diff --git a/tests/wpt/meta/content-security-policy/script-src/script-src-report-only-policy-works-with-external-hash-policy.html.ini b/tests/wpt/meta/content-security-policy/script-src/script-src-report-only-policy-works-with-external-hash-policy.html.ini deleted file mode 100644 index 6ff412416ae..00000000000 --- a/tests/wpt/meta/content-security-policy/script-src/script-src-report-only-policy-works-with-external-hash-policy.html.ini +++ /dev/null @@ -1,6 +0,0 @@ -[script-src-report-only-policy-works-with-external-hash-policy.html] - [Should fire securitypolicyviolation event] - expected: FAIL - - [External script in a script tag with matching SRI hash should run.] - expected: FAIL diff --git a/tests/wpt/meta/content-security-policy/script-src/script-src-sri_hash.sub.html.ini b/tests/wpt/meta/content-security-policy/script-src/script-src-sri_hash.sub.html.ini index 7ce862f5a16..3324bf91bd7 100644 --- a/tests/wpt/meta/content-security-policy/script-src/script-src-sri_hash.sub.html.ini +++ b/tests/wpt/meta/content-security-policy/script-src/script-src-sri_hash.sub.html.ini @@ -2,8 +2,11 @@ [multiple matching integrity] expected: FAIL - [partially matching integrity] + [matching integrity] expected: FAIL - [External script in a script tag with matching SRI hash should run.] + [matching integrity (case-insensitive algorithm)] + expected: FAIL + + [matching plus unsupported integrity] expected: FAIL diff --git a/tests/wpt/meta/content-security-policy/script-src/script-src-strict_dynamic_discard_source_expressions.html.ini b/tests/wpt/meta/content-security-policy/script-src/script-src-strict_dynamic_discard_source_expressions.html.ini deleted file mode 100644 index 63cfa444854..00000000000 --- a/tests/wpt/meta/content-security-policy/script-src/script-src-strict_dynamic_discard_source_expressions.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[script-src-strict_dynamic_discard_source_expressions.html] - [Allowed scripts without a correct nonce are not permitted with `strict-dynamic`.] - expected: FAIL diff --git a/tests/wpt/meta/content-security-policy/script-src/script-src-strict_dynamic_double_policy_honor_source_expressions.sub.html.ini b/tests/wpt/meta/content-security-policy/script-src/script-src-strict_dynamic_double_policy_honor_source_expressions.sub.html.ini deleted file mode 100644 index c9d74462151..00000000000 --- a/tests/wpt/meta/content-security-policy/script-src/script-src-strict_dynamic_double_policy_honor_source_expressions.sub.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[script-src-strict_dynamic_double_policy_honor_source_expressions.sub.html] - [Non-allowed script injected via `appendChild` is not permitted with `strict-dynamic` + a nonce+allowed double policy.] - expected: FAIL diff --git a/tests/wpt/meta/content-security-policy/script-src/script-src-strict_dynamic_parser_inserted.html.ini b/tests/wpt/meta/content-security-policy/script-src/script-src-strict_dynamic_parser_inserted.html.ini index 44bc930f887..67a423f9b8a 100644 --- a/tests/wpt/meta/content-security-policy/script-src/script-src-strict_dynamic_parser_inserted.html.ini +++ b/tests/wpt/meta/content-security-policy/script-src/script-src-strict_dynamic_parser_inserted.html.ini @@ -1,29 +1,5 @@ [script-src-strict_dynamic_parser_inserted.html] expected: TIMEOUT - [Parser-inserted script via `document.write` without a correct nonce is not allowed with `strict-dynamic`.] - expected: FAIL - - [Parser-inserted script via `document.writeln` without a correct nonce is not allowed with `strict-dynamic`.] - expected: FAIL - - [Parser-inserted deferred script via `document.write` without a correct nonce is not allowed with `strict-dynamic`.] - expected: FAIL - - [Parser-inserted deferred script via `document.writeln` without a correct nonce is not allowed with `strict-dynamic`.] - expected: FAIL - - [Parser-inserted async script via `document.write` without a correct nonce is not allowed with `strict-dynamic`.] - expected: FAIL - - [Parser-inserted async script via `document.writeln` without a correct nonce is not allowed with `strict-dynamic`.] - expected: FAIL - - [Parser-inserted deferred async script via `document.write` without a correct nonce is not allowed with `strict-dynamic`.] - expected: FAIL - - [Parser-inserted deferred async script via `document.writeln` without a correct nonce is not allowed with `strict-dynamic`.] - expected: TIMEOUT - [Script injected via `innerHTML` is not allowed with `strict-dynamic`.] expected: TIMEOUT diff --git a/tests/wpt/meta/content-security-policy/securitypolicyviolation/upgrade-insecure-requests-reporting.https.html.ini b/tests/wpt/meta/content-security-policy/securitypolicyviolation/upgrade-insecure-requests-reporting.https.html.ini index da41ac13664..f84e31682d3 100644 --- a/tests/wpt/meta/content-security-policy/securitypolicyviolation/upgrade-insecure-requests-reporting.https.html.ini +++ b/tests/wpt/meta/content-security-policy/securitypolicyviolation/upgrade-insecure-requests-reporting.https.html.ini @@ -1,8 +1,5 @@ [upgrade-insecure-requests-reporting.https.html] expected: TIMEOUT - [Upgraded image is reported] - expected: TIMEOUT - [Upgraded iframe is reported] expected: TIMEOUT diff --git a/tests/wpt/meta/css/cssom-view/offsetParent-block-in-inline.html.ini b/tests/wpt/meta/css/cssom-view/offsetParent-block-in-inline.html.ini deleted file mode 100644 index aa1109f1600..00000000000 --- a/tests/wpt/meta/css/cssom-view/offsetParent-block-in-inline.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[offsetParent-block-in-inline.html] - [offsetParent-block-in-inline] - expected: FAIL diff --git a/tests/wpt/meta/css/filter-effects/backdrop-filter-edge-clipping-2.html.ini b/tests/wpt/meta/css/filter-effects/backdrop-filter-edge-clipping-2.html.ini new file mode 100644 index 00000000000..4a08b41acc2 --- /dev/null +++ b/tests/wpt/meta/css/filter-effects/backdrop-filter-edge-clipping-2.html.ini @@ -0,0 +1,2 @@ +[backdrop-filter-edge-clipping-2.html] + expected: FAIL diff --git a/tests/wpt/meta/css/filter-effects/backdrop-filter-edge-mirror.html.ini b/tests/wpt/meta/css/filter-effects/backdrop-filter-edge-mirror.html.ini new file mode 100644 index 00000000000..51318cbb1d2 --- /dev/null +++ b/tests/wpt/meta/css/filter-effects/backdrop-filter-edge-mirror.html.ini @@ -0,0 +1,2 @@ +[backdrop-filter-edge-mirror.html] + expected: FAIL diff --git a/tests/wpt/meta/css/filter-effects/backdrop-filter-edge-pixels-2.html.ini b/tests/wpt/meta/css/filter-effects/backdrop-filter-edge-pixels-2.html.ini new file mode 100644 index 00000000000..19d53f587ad --- /dev/null +++ b/tests/wpt/meta/css/filter-effects/backdrop-filter-edge-pixels-2.html.ini @@ -0,0 +1,2 @@ +[backdrop-filter-edge-pixels-2.html] + expected: FAIL diff --git a/tests/wpt/meta/shadow-dom/offsetParent-across-shadow-boundaries.html.ini b/tests/wpt/meta/shadow-dom/offsetParent-across-shadow-boundaries.html.ini deleted file mode 100644 index afd8ee2cf06..00000000000 --- a/tests/wpt/meta/shadow-dom/offsetParent-across-shadow-boundaries.html.ini +++ /dev/null @@ -1,24 +0,0 @@ -[offsetParent-across-shadow-boundaries.html] - [offsetParent must skip offset parents of an element when the context object is assigned to a slot in a shadow tree of open mode] - expected: FAIL - - [offsetParent must skip offset parents of an element when the context object is assigned to a slot in a shadow tree of closed mode] - expected: FAIL - - [offsetParent must skip multiple offset parents of an element when the context object is assigned to a slot in a shadow tree of open mode] - expected: FAIL - - [offsetParent must skip multiple offset parents of an element when the context object is assigned to a slot in a shadow tree of closed mode] - expected: FAIL - - [offsetParent must skip offset parents of an element when the context object is assigned to a slot in nested shadow trees of open mode] - expected: FAIL - - [offsetParent must skip offset parents of an element when the context object is assigned to a slot in nested shadow trees of closed mode] - expected: FAIL - - [offsetParent must find the first offset parent which is a shadow-including ancestor of the context object even some shadow tree of open mode did not have any offset parent] - expected: FAIL - - [offsetParent must find the first offset parent which is a shadow-including ancestor of the context object even some shadow tree of closed mode did not have any offset parent] - expected: FAIL diff --git a/tests/wpt/meta/shadow-dom/offsetTop-offsetLeft-across-shadow-boundaries.html.ini b/tests/wpt/meta/shadow-dom/offsetTop-offsetLeft-across-shadow-boundaries.html.ini index 88f45f3b6a7..5ebeb149de3 100644 --- a/tests/wpt/meta/shadow-dom/offsetTop-offsetLeft-across-shadow-boundaries.html.ini +++ b/tests/wpt/meta/shadow-dom/offsetTop-offsetLeft-across-shadow-boundaries.html.ini @@ -4,6 +4,3 @@ [Verifies that HTMLElement.offsetTop accounts for shadow boundaries when nested in multiple shadow roots.] expected: FAIL - - [Verifies that HTMLElement.offsetLeft accounts for shadow boundaries.] - expected: FAIL diff --git a/tests/wpt/meta/trusted-types/should-trusted-type-policy-creation-be-blocked-by-csp-001.html.ini b/tests/wpt/meta/trusted-types/should-trusted-type-policy-creation-be-blocked-by-csp-001.html.ini deleted file mode 100644 index 097af84d4ee..00000000000 --- a/tests/wpt/meta/trusted-types/should-trusted-type-policy-creation-be-blocked-by-csp-001.html.ini +++ /dev/null @@ -1,30 +0,0 @@ -[should-trusted-type-policy-creation-be-blocked-by-csp-001.html] - [single report-only policy with directive "trusted-type tt-policy-name"] - expected: FAIL - - [single report-only policy with directive "trusted-type *"] - expected: FAIL - - [single report-only policy with directive "trusted-type 'none'"] - expected: FAIL - - [single report-only policy with directive "trusted-type 'allow-duplicates'"] - expected: FAIL - - [single report-only policy with directive "trusted-type tt-policy-name 'allow-duplicates'"] - expected: FAIL - - [single report-only policy with directive "trusted-type 'none' 'allow-duplicates'"] - expected: FAIL - - [single report-only policy with directive "trusted-type 'none' tt-policy-name"] - expected: FAIL - - [single report-only policy with directive "trusted-type 'none' *"] - expected: FAIL - - [single report-only policy with directive "trusted-type tt-policy-name *"] - expected: FAIL - - [single report-only policy with directive "trusted-type tt-policy-name1 tt-policy-name2 tt-policy-name3"] - expected: FAIL diff --git a/tests/wpt/meta/trusted-types/should-trusted-type-policy-creation-be-blocked-by-csp-002.html.ini b/tests/wpt/meta/trusted-types/should-trusted-type-policy-creation-be-blocked-by-csp-002.html.ini index b42c980eebc..65aaf704101 100644 --- a/tests/wpt/meta/trusted-types/should-trusted-type-policy-creation-be-blocked-by-csp-002.html.ini +++ b/tests/wpt/meta/trusted-types/should-trusted-type-policy-creation-be-blocked-by-csp-002.html.ini @@ -1,8 +1,5 @@ [should-trusted-type-policy-creation-be-blocked-by-csp-002.html] expected: TIMEOUT - [invalid tt-policy-name name "policy name"] - expected: FAIL - [invalid tt-policy-name name "policy*name"] expected: FAIL diff --git a/tests/wpt/meta/trusted-types/should-trusted-type-policy-creation-be-blocked-by-csp-003.html.ini b/tests/wpt/meta/trusted-types/should-trusted-type-policy-creation-be-blocked-by-csp-003.html.ini deleted file mode 100644 index d18359a0c91..00000000000 --- a/tests/wpt/meta/trusted-types/should-trusted-type-policy-creation-be-blocked-by-csp-003.html.ini +++ /dev/null @@ -1,15 +0,0 @@ -[should-trusted-type-policy-creation-be-blocked-by-csp-003.html] - [Multiple report-only trusted-types directives.] - expected: FAIL - - [One violated report-only trusted-types directive followed by multiple enforce directives.] - expected: FAIL - - [One violated enforce trusted-types directive followed by multiple report-only directives.] - expected: FAIL - - [Mixing enforce and report-only policies with trusted-types directives] - expected: FAIL - - [Mixing enforce and report-only policies with trusted-types directives (duplicate policy)] - expected: FAIL diff --git a/tests/wpt/meta/trusted-types/should-trusted-type-policy-creation-be-blocked-by-csp-004-worker.html.ini b/tests/wpt/meta/trusted-types/should-trusted-type-policy-creation-be-blocked-by-csp-004-worker.html.ini deleted file mode 100644 index e9ae8caa720..00000000000 --- a/tests/wpt/meta/trusted-types/should-trusted-type-policy-creation-be-blocked-by-csp-004-worker.html.ini +++ /dev/null @@ -1,6 +0,0 @@ -[should-trusted-type-policy-creation-be-blocked-by-csp-004-worker.html] - [Exception and violations for CSP with multiple enforce and report-only policies.] - expected: FAIL - - [Location of trusted-types violations.] - expected: FAIL diff --git a/tests/wpt/meta/trusted-types/should-trusted-type-policy-creation-be-blocked-by-csp-005.html.ini b/tests/wpt/meta/trusted-types/should-trusted-type-policy-creation-be-blocked-by-csp-005.html.ini deleted file mode 100644 index 7a8b7095515..00000000000 --- a/tests/wpt/meta/trusted-types/should-trusted-type-policy-creation-be-blocked-by-csp-005.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[should-trusted-type-policy-creation-be-blocked-by-csp-005.html] - [Location of trusted-types violations.] - expected: FAIL diff --git a/tests/wpt/meta/trusted-types/trusted-types-eval-reporting-no-unsafe-eval.html.ini b/tests/wpt/meta/trusted-types/trusted-types-eval-reporting-no-unsafe-eval.html.ini index 927b135f628..d3440d323c2 100644 --- a/tests/wpt/meta/trusted-types/trusted-types-eval-reporting-no-unsafe-eval.html.ini +++ b/tests/wpt/meta/trusted-types/trusted-types-eval-reporting-no-unsafe-eval.html.ini @@ -4,6 +4,3 @@ [Trusted Type violation report: evaluating a Trusted Script violates script-src.] expected: FAIL - - [Trusted Type violation report: script-src restrictions apply after the default policy runs.] - expected: FAIL diff --git a/tests/wpt/meta/trusted-types/trusted-types-reporting.html.ini b/tests/wpt/meta/trusted-types/trusted-types-reporting.html.ini index 8347bf66f28..27c9a258d53 100644 --- a/tests/wpt/meta/trusted-types/trusted-types-reporting.html.ini +++ b/tests/wpt/meta/trusted-types/trusted-types-reporting.html.ini @@ -1,7 +1,4 @@ [trusted-types-reporting.html] - [Trusted Type violation report: creating a forbidden policy.] - expected: FAIL - [Trusted Type violation report: creating a report-only-forbidden policy.] expected: FAIL |