mirror of
https://github.com/servo/servo.git
synced 2025-08-05 05:30:08 +01:00
Implement keyword sizes on floats (#33666)
Adds support for min-content, max-content, fit-content and stretch, for floated elements. Signed-off-by: Oriol Brufau <obrufau@igalia.com>
This commit is contained in:
parent
39133a5478
commit
c1b744b2b2
4 changed files with 290 additions and 41 deletions
|
@ -27,10 +27,10 @@ use crate::dom::NodeExt;
|
||||||
use crate::dom_traversal::{Contents, NodeAndStyleInfo};
|
use crate::dom_traversal::{Contents, NodeAndStyleInfo};
|
||||||
use crate::formatting_contexts::IndependentFormattingContext;
|
use crate::formatting_contexts::IndependentFormattingContext;
|
||||||
use crate::fragment_tree::{BoxFragment, CollapsedBlockMargins, CollapsedMargin};
|
use crate::fragment_tree::{BoxFragment, CollapsedBlockMargins, CollapsedMargin};
|
||||||
use crate::geom::{LogicalRect, LogicalVec2, PhysicalPoint, PhysicalRect, ToLogical};
|
use crate::geom::{LogicalRect, LogicalVec2, PhysicalPoint, PhysicalRect, Size, ToLogical};
|
||||||
use crate::positioned::{relative_adjustement, PositioningContext};
|
use crate::positioned::{relative_adjustement, PositioningContext};
|
||||||
use crate::style_ext::{Clamp, ComputedValuesExt, DisplayInside, PaddingBorderMargin};
|
use crate::style_ext::{Clamp, ComputedValuesExt, DisplayInside, PaddingBorderMargin};
|
||||||
use crate::{ContainingBlock, IndefiniteContainingBlock};
|
use crate::{AuOrAuto, ContainingBlock, IndefiniteContainingBlock};
|
||||||
|
|
||||||
/// A floating box.
|
/// A floating box.
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
|
@ -914,43 +914,61 @@ impl FloatBox {
|
||||||
// Calculate inline size.
|
// Calculate inline size.
|
||||||
// https://drafts.csswg.org/css2/#float-width
|
// https://drafts.csswg.org/css2/#float-width
|
||||||
let style = non_replaced.style.clone();
|
let style = non_replaced.style.clone();
|
||||||
let box_size = style
|
let box_size = style.content_box_size(containing_block, &pbm);
|
||||||
.content_box_size_deprecated(containing_block, &pbm)
|
let max_box_size = style.content_max_box_size(containing_block, &pbm);
|
||||||
.map(|v| v.map(Au::from));
|
let min_box_size = style.content_min_box_size(containing_block, &pbm);
|
||||||
let max_box_size = style
|
let available_inline_size =
|
||||||
.content_max_box_size_deprecated(containing_block, &pbm)
|
containing_block.inline_size - pbm_sums.inline_sum();
|
||||||
.map(|v| v.map(Au::from));
|
let available_block_size = containing_block
|
||||||
let min_box_size = style
|
.block_size
|
||||||
.content_min_box_size_deprecated(containing_block, &pbm)
|
.non_auto()
|
||||||
.map(|v| v.map(Au::from))
|
.map(|block_size| block_size - pbm_sums.block_sum());
|
||||||
.auto_is(Au::zero);
|
let tentative_block_size = box_size
|
||||||
|
.block
|
||||||
let block_size = box_size.block.map(|size| {
|
.maybe_resolve_extrinsic(available_block_size)
|
||||||
size.clamp_between_extremums(min_box_size.block, max_box_size.block)
|
.map(|size| {
|
||||||
});
|
let min_block_size = min_box_size
|
||||||
let tentative_inline_size = box_size.inline.auto_is(|| {
|
.block
|
||||||
let available_size =
|
.maybe_resolve_extrinsic(available_block_size)
|
||||||
containing_block.inline_size - pbm_sums.inline_sum();
|
.unwrap_or_default();
|
||||||
|
let max_block_size = max_box_size
|
||||||
|
.block
|
||||||
|
.maybe_resolve_extrinsic(available_block_size);
|
||||||
|
size.clamp_between_extremums(min_block_size, max_block_size)
|
||||||
|
})
|
||||||
|
.map_or(AuOrAuto::Auto, AuOrAuto::LengthPercentage);
|
||||||
|
let mut get_content_size = || {
|
||||||
let containing_block_for_children =
|
let containing_block_for_children =
|
||||||
IndefiniteContainingBlock::new_for_style_and_block_size(
|
IndefiniteContainingBlock::new_for_style_and_block_size(
|
||||||
&style, block_size,
|
&style,
|
||||||
|
tentative_block_size,
|
||||||
);
|
);
|
||||||
non_replaced
|
non_replaced.inline_content_sizes(
|
||||||
.inline_content_sizes(
|
layout_context,
|
||||||
layout_context,
|
&containing_block_for_children,
|
||||||
&containing_block_for_children,
|
)
|
||||||
)
|
};
|
||||||
.shrink_to_fit(available_size)
|
let tentative_inline_size = box_size.inline.resolve(
|
||||||
});
|
Size::fit_content,
|
||||||
|
available_inline_size,
|
||||||
|
&mut get_content_size,
|
||||||
|
);
|
||||||
|
let min_inline_size = min_box_size
|
||||||
|
.inline
|
||||||
|
.resolve_non_initial(available_inline_size, &mut get_content_size)
|
||||||
|
.unwrap_or_default();
|
||||||
|
let max_inline_size = max_box_size
|
||||||
|
.inline
|
||||||
|
.resolve_non_initial(available_inline_size, &mut get_content_size);
|
||||||
let inline_size = tentative_inline_size
|
let inline_size = tentative_inline_size
|
||||||
.clamp_between_extremums(min_box_size.inline, max_box_size.inline);
|
.clamp_between_extremums(min_inline_size, max_inline_size);
|
||||||
|
|
||||||
// Calculate block size.
|
// Calculate block size.
|
||||||
// https://drafts.csswg.org/css2/#block-root-margin
|
// https://drafts.csswg.org/css2/#block-root-margin
|
||||||
// FIXME(pcwalton): Is a tree rank of zero correct here?
|
// FIXME(pcwalton): Is a tree rank of zero correct here?
|
||||||
let containing_block_for_children = ContainingBlock {
|
let containing_block_for_children = ContainingBlock {
|
||||||
inline_size,
|
inline_size,
|
||||||
block_size,
|
block_size: tentative_block_size,
|
||||||
style: &non_replaced.style,
|
style: &non_replaced.style,
|
||||||
};
|
};
|
||||||
let independent_layout = non_replaced.layout(
|
let independent_layout = non_replaced.layout(
|
||||||
|
@ -964,17 +982,23 @@ impl FloatBox {
|
||||||
Some(inline_size) => {
|
Some(inline_size) => {
|
||||||
(independent_layout.content_block_size, inline_size)
|
(independent_layout.content_block_size, inline_size)
|
||||||
},
|
},
|
||||||
None => (
|
None => {
|
||||||
block_size.auto_is(|| {
|
let stretch_size = available_block_size
|
||||||
independent_layout
|
.unwrap_or(independent_layout.content_block_size);
|
||||||
.content_block_size
|
let mut get_content_size =
|
||||||
.clamp_between_extremums(
|
|| independent_layout.content_block_size.into();
|
||||||
min_box_size.block,
|
let min_block_size = min_box_size
|
||||||
max_box_size.block,
|
.block
|
||||||
)
|
.resolve_non_initial(stretch_size, &mut get_content_size)
|
||||||
}),
|
.unwrap_or_default();
|
||||||
inline_size,
|
let max_block_size = max_box_size
|
||||||
),
|
.block
|
||||||
|
.resolve_non_initial(stretch_size, &mut get_content_size);
|
||||||
|
let block_size = tentative_block_size
|
||||||
|
.auto_is(|| independent_layout.content_block_size)
|
||||||
|
.clamp_between_extremums(min_block_size, max_block_size);
|
||||||
|
(block_size, inline_size)
|
||||||
|
},
|
||||||
};
|
};
|
||||||
content_size = LogicalVec2 {
|
content_size = LogicalVec2 {
|
||||||
inline: inline_size,
|
inline: inline_size,
|
||||||
|
|
|
@ -16,6 +16,7 @@ use style::values::generics::length::GenericLengthPercentageOrAuto as AutoOr;
|
||||||
use style::Zero;
|
use style::Zero;
|
||||||
use style_traits::CSSPixel;
|
use style_traits::CSSPixel;
|
||||||
|
|
||||||
|
use crate::sizing::ContentSizes;
|
||||||
use crate::ContainingBlock;
|
use crate::ContainingBlock;
|
||||||
|
|
||||||
pub type PhysicalPoint<U> = euclid::Point2D<U, CSSPixel>;
|
pub type PhysicalPoint<U> = euclid::Point2D<U, CSSPixel>;
|
||||||
|
@ -656,12 +657,24 @@ impl<T> Default for Size<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Clone> Size<T> {
|
impl<T> Size<T> {
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn fit_content() -> Self {
|
||||||
|
Self::Keyword(SizeKeyword::FitContent)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn is_keyword(&self) -> bool {
|
pub(crate) fn is_keyword(&self) -> bool {
|
||||||
matches!(self, Self::Keyword(_))
|
matches!(self, Self::Keyword(_))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn is_initial(&self) -> bool {
|
||||||
|
matches!(self, Self::Keyword(SizeKeyword::Initial))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Clone> Size<T> {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn to_numeric(&self) -> Option<T> {
|
pub(crate) fn to_numeric(&self) -> Option<T> {
|
||||||
match self {
|
match self {
|
||||||
|
@ -761,3 +774,66 @@ impl LogicalVec2<Size<LengthPercentage>> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Size<Au> {
|
||||||
|
/// Resolves any size into a numerical value.
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn resolve(
|
||||||
|
&self,
|
||||||
|
get_initial_behavior: impl Fn() -> Self,
|
||||||
|
stretch_size: Au,
|
||||||
|
get_content_size: &mut impl FnMut() -> ContentSizes,
|
||||||
|
) -> Au {
|
||||||
|
if self.is_initial() {
|
||||||
|
let initial_behavior = get_initial_behavior();
|
||||||
|
assert!(!initial_behavior.is_initial());
|
||||||
|
initial_behavior.resolve_non_initial(stretch_size, get_content_size)
|
||||||
|
} else {
|
||||||
|
self.resolve_non_initial(stretch_size, get_content_size)
|
||||||
|
}
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resolves a non-initial size into a numerical value.
|
||||||
|
/// Returns `None` if the size is the initial one.
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn resolve_non_initial(
|
||||||
|
&self,
|
||||||
|
stretch_size: Au,
|
||||||
|
get_content_size: &mut impl FnMut() -> ContentSizes,
|
||||||
|
) -> Option<Au> {
|
||||||
|
match self {
|
||||||
|
Self::Keyword(SizeKeyword::Initial) => None,
|
||||||
|
Self::Keyword(SizeKeyword::MinContent) => Some(get_content_size().min_content),
|
||||||
|
Self::Keyword(SizeKeyword::MaxContent) => Some(get_content_size().max_content),
|
||||||
|
Self::Keyword(SizeKeyword::FitContent) => {
|
||||||
|
Some(get_content_size().shrink_to_fit(stretch_size))
|
||||||
|
},
|
||||||
|
Self::Keyword(SizeKeyword::Stretch) => Some(stretch_size),
|
||||||
|
Self::Numeric(numeric) => Some(*numeric),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tries to resolve an extrinsic size into a numerical value.
|
||||||
|
/// Extrinsic sizes are those based on the context of an element, without regard for its contents.
|
||||||
|
/// <https://drafts.csswg.org/css-sizing-3/#extrinsic>
|
||||||
|
///
|
||||||
|
/// Returns `None` if either:
|
||||||
|
/// - The size is intrinsic.
|
||||||
|
/// - The size is the initial one.
|
||||||
|
/// TODO: should we allow it to behave as `stretch` instead of assuming it's intrinsic?
|
||||||
|
/// - The provided `stretch_size` is `None` but we need its value.
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn maybe_resolve_extrinsic(&self, stretch_size: Option<Au>) -> Option<Au> {
|
||||||
|
match self {
|
||||||
|
Self::Keyword(keyword) => match keyword {
|
||||||
|
SizeKeyword::Initial |
|
||||||
|
SizeKeyword::MinContent |
|
||||||
|
SizeKeyword::MaxContent |
|
||||||
|
SizeKeyword::FitContent => None,
|
||||||
|
SizeKeyword::Stretch => stretch_size,
|
||||||
|
},
|
||||||
|
Self::Numeric(numeric) => Some(*numeric),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
7
tests/wpt/meta/MANIFEST.json
vendored
7
tests/wpt/meta/MANIFEST.json
vendored
|
@ -565459,6 +565459,13 @@
|
||||||
{}
|
{}
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
|
"keyword-sizes-on-floated-element.html": [
|
||||||
|
"adfffba8061aa983800822746a0cd5fc8f6f0fa8",
|
||||||
|
[
|
||||||
|
null,
|
||||||
|
{}
|
||||||
|
]
|
||||||
|
],
|
||||||
"min-max-content-orthogonal-flow-crash-001.html": [
|
"min-max-content-orthogonal-flow-crash-001.html": [
|
||||||
"d2617f8aa2d1c966e394abb1d1617c012ea4648e",
|
"d2617f8aa2d1c966e394abb1d1617c012ea4648e",
|
||||||
[
|
[
|
||||||
|
|
142
tests/wpt/tests/css/css-sizing/keyword-sizes-on-floated-element.html
vendored
Normal file
142
tests/wpt/tests/css/css-sizing/keyword-sizes-on-floated-element.html
vendored
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<title>Keyword sizes on floated element</title>
|
||||||
|
<link rel="author" title="Oriol Brufau" href="obrufau@igalia.com">
|
||||||
|
<link rel="help" href="https://drafts.csswg.org/css-sizing-3/#sizing-values">
|
||||||
|
<link rel="help" href="https://drafts.csswg.org/css-sizing-4/#sizing-values">
|
||||||
|
<link rel="help" href="https://drafts.csswg.org/css2/#floats">
|
||||||
|
<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/11006">
|
||||||
|
<meta assert="The various keyword sizes work as expected on floats.">
|
||||||
|
<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.test {
|
||||||
|
float: left;
|
||||||
|
margin: 5px;
|
||||||
|
border: 3px solid;
|
||||||
|
padding: 2px;
|
||||||
|
font: 20px/1 Ahem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the preferred size to small amount, to test that the min size works */
|
||||||
|
.test.min-width { width: 0px }
|
||||||
|
.test.min-height { height: 0px }
|
||||||
|
|
||||||
|
/* Set the preferred size to big amount, to test that the max size works */
|
||||||
|
.test.max-width { width: 500px }
|
||||||
|
.test.max-height { height: 500px }
|
||||||
|
|
||||||
|
/* stretch isn't widely supported, fall back to vendor-prefixed alternatives */
|
||||||
|
.width.stretch { width: -moz-available; width: -webkit-fill-available; width: stretch }
|
||||||
|
.min-width.stretch { min-width: -moz-available; min-width: -webkit-fill-available; min-width: stretch }
|
||||||
|
.max-width.stretch { max-width: -moz-available; max-width: -webkit-fill-available; max-width: stretch }
|
||||||
|
.height.stretch { height: -moz-available; height: -webkit-fill-available; height: stretch }
|
||||||
|
.min-height.stretch { min-height: -moz-available; min-height: -webkit-fill-available; min-height: stretch }
|
||||||
|
.max-height.stretch { max-height: -moz-available; max-height: -webkit-fill-available; max-height: stretch }
|
||||||
|
</style>
|
||||||
|
<div id="log"></div>
|
||||||
|
|
||||||
|
<!-- Intrinsic keywords -->
|
||||||
|
<div style="width: 100px; height: 100px">
|
||||||
|
<div class="test width" style="width: min-content" data-expected-width="30">X X</div>
|
||||||
|
<div class="test width" style="width: fit-content" data-expected-width="70">X X</div>
|
||||||
|
<div class="test width" style="width: max-content" data-expected-width="70">X X</div>
|
||||||
|
|
||||||
|
<div class="test width" style="width: min-content" data-expected-width="70">XXX XXX</div>
|
||||||
|
<div class="test width" style="width: fit-content" data-expected-width="90">XXX XXX</div>
|
||||||
|
<div class="test width" style="width: max-content" data-expected-width="150">XXX XXX</div>
|
||||||
|
|
||||||
|
<div class="test width" style="width: min-content" data-expected-width="110">XXXXX XXXXX</div>
|
||||||
|
<div class="test width" style="width: fit-content" data-expected-width="110">XXXXX XXXXX</div>
|
||||||
|
<div class="test width" style="width: max-content" data-expected-width="230">XXXXX XXXXX</div>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<div class="test min-width" style="min-width: min-content" data-expected-width="30">X X</div>
|
||||||
|
<div class="test min-width" style="min-width: fit-content" data-expected-width="70">X X</div>
|
||||||
|
<div class="test min-width" style="min-width: max-content" data-expected-width="70">X X</div>
|
||||||
|
|
||||||
|
<div class="test min-width" style="min-width: min-content" data-expected-width="70">XXX XXX</div>
|
||||||
|
<div class="test min-width" style="min-width: fit-content" data-expected-width="90">XXX XXX</div>
|
||||||
|
<div class="test min-width" style="min-width: max-content" data-expected-width="150">XXX XXX</div>
|
||||||
|
|
||||||
|
<div class="test min-width" style="min-width: min-content" data-expected-width="110">XXXXX XXXXX</div>
|
||||||
|
<div class="test min-width" style="min-width: fit-content" data-expected-width="110">XXXXX XXXXX</div>
|
||||||
|
<div class="test min-width" style="min-width: max-content" data-expected-width="230">XXXXX XXXXX</div>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<div class="test max-width" style="max-width: min-content" data-expected-width="30">X X</div>
|
||||||
|
<div class="test max-width" style="max-width: fit-content" data-expected-width="70">X X</div>
|
||||||
|
<div class="test max-width" style="max-width: max-content" data-expected-width="70">X X</div>
|
||||||
|
|
||||||
|
<div class="test max-width" style="max-width: min-content" data-expected-width="70">XXX XXX</div>
|
||||||
|
<div class="test max-width" style="max-width: fit-content" data-expected-width="90">XXX XXX</div>
|
||||||
|
<div class="test max-width" style="max-width: max-content" data-expected-width="150">XXX XXX</div>
|
||||||
|
|
||||||
|
<div class="test max-width" style="max-width: min-content" data-expected-width="110">XXXXX XXXXX</div>
|
||||||
|
<div class="test max-width" style="max-width: fit-content" data-expected-width="110">XXXXX XXXXX</div>
|
||||||
|
<div class="test max-width" style="max-width: max-content" data-expected-width="230">XXXXX XXXXX</div>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<div class="test height" style="height: min-content" data-expected-height="30">X X</div>
|
||||||
|
<div class="test height" style="height: fit-content" data-expected-height="30">X X</div>
|
||||||
|
<div class="test height" style="height: max-content" data-expected-height="30">X X</div>
|
||||||
|
|
||||||
|
<div class="test min-height" style="min-height: min-content" data-expected-height="30">X X</div>
|
||||||
|
<div class="test min-height" style="min-height: fit-content" data-expected-height="30">X X</div>
|
||||||
|
<div class="test min-height" style="min-height: max-content" data-expected-height="30">X X</div>
|
||||||
|
|
||||||
|
<div class="test max-height" style="max-height: min-content" data-expected-height="30">X X</div>
|
||||||
|
<div class="test max-height" style="max-height: fit-content" data-expected-height="30">X X</div>
|
||||||
|
<div class="test max-height" style="max-height: max-content" data-expected-height="30">X X</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Definite stretch -->
|
||||||
|
<div style="width: 100px; height: 100px">
|
||||||
|
<div class="test width stretch" data-expected-width="90">X X</div>
|
||||||
|
<div class="test width stretch" data-expected-width="90">XXX XXX</div>
|
||||||
|
<div class="test width stretch" data-expected-width="90">XXXXX XXXXX</div>
|
||||||
|
|
||||||
|
<div class="test min-width stretch" data-expected-width="90">X X</div>
|
||||||
|
<div class="test min-width stretch" data-expected-width="90">XXX XXX</div>
|
||||||
|
<div class="test min-width stretch" data-expected-width="90">XXXXX XXXXX</div>
|
||||||
|
|
||||||
|
<div class="test max-width stretch" data-expected-width="90">X X</div>
|
||||||
|
<div class="test max-width stretch" data-expected-width="90">XXX XXX</div>
|
||||||
|
<div class="test max-width stretch" data-expected-width="90">XXXXX XXXXX</div>
|
||||||
|
|
||||||
|
<div class="test height stretch" data-expected-height="90">X X</div>
|
||||||
|
<div class="test height stretch" data-expected-height="90">XXX XXX<</div>
|
||||||
|
<div class="test height stretch" data-expected-height="90">XXXXX XXXXX</div>
|
||||||
|
|
||||||
|
<div class="test min-height stretch" data-expected-height="90">X X</div>
|
||||||
|
<div class="test min-height stretch" data-expected-height="90">XXX XXX</div>
|
||||||
|
<div class="test min-height stretch" data-expected-height="90">XXXXX XXXXX</div>
|
||||||
|
|
||||||
|
<div class="test max-height stretch" data-expected-height="90">X X</div>
|
||||||
|
<div class="test max-height stretch" data-expected-height="90">XXX XXX</div>
|
||||||
|
<div class="test max-height stretch" data-expected-height="90">XXXXX XXXXX</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Indefinite stretch -->
|
||||||
|
<div style="width: 100px; max-height: 100px">
|
||||||
|
<div class="test height stretch indefinite" data-expected-height="30">X X</div>
|
||||||
|
<div class="test height stretch indefinite" data-expected-height="50">XXX XXX</div>
|
||||||
|
<div class="test height stretch indefinite" data-expected-height="50">XXXXX XXXXX</div>
|
||||||
|
|
||||||
|
<div class="test min-height stretch indefinite" data-expected-height="30">X X</div>
|
||||||
|
<div class="test min-height stretch indefinite" data-expected-height="50">XXX XXX</div>
|
||||||
|
<div class="test min-height stretch indefinite" data-expected-height="50">XXXXX XXXXX</div>
|
||||||
|
|
||||||
|
<div class="test max-height stretch indefinite" data-expected-height="30">X X</div>
|
||||||
|
<div class="test max-height stretch indefinite" data-expected-height="50">XXX XXX</div>
|
||||||
|
<div class="test max-height stretch indefinite" data-expected-height="50">XXXXX XXXXX</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
<script src="/resources/check-layout-th.js"></script>
|
||||||
|
<script>
|
||||||
|
checkLayout(".test");
|
||||||
|
</script>
|
Loading…
Add table
Add a link
Reference in a new issue