Bug 1906475 - Improve scroll{Width,Height} implementation for overflow: visible frames. r=dholbert
Differential Revision: https://phabricator.services.mozilla.com/D218782
This commit is contained in:
@@ -15,8 +15,6 @@
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <initializer_list>
|
||||
#include <new>
|
||||
#include "DOMIntersectionObserver.h"
|
||||
#include "DOMMatrix.h"
|
||||
#include "ExpandedPrincipal.h"
|
||||
#include "PresShellInlines.h"
|
||||
@@ -929,24 +927,34 @@ int32_t Element::ScrollLeftMax() {
|
||||
|
||||
static nsSize GetScrollRectSizeForOverflowVisibleFrame(nsIFrame* aFrame) {
|
||||
if (!aFrame || aFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) {
|
||||
return nsSize(0, 0);
|
||||
return nsSize();
|
||||
}
|
||||
|
||||
nsRect paddingRect = aFrame->GetPaddingRectRelativeToSelf();
|
||||
// This matches WebKit and Blink, which in turn (apparently, according to
|
||||
// their source) matched old IE.
|
||||
const nsRect paddingRect = aFrame->GetPaddingRectRelativeToSelf();
|
||||
const nsRect overflowRect = [&] {
|
||||
OverflowAreas overflowAreas(paddingRect, paddingRect);
|
||||
// Add the scrollable overflow areas of children (if any) to the paddingRect.
|
||||
// It's important to start with the paddingRect, otherwise if there are no
|
||||
// children the overflow rect will be 0,0,0,0 which will force the point 0,0
|
||||
// to be included in the final rect.
|
||||
nsLayoutUtils::UnionChildOverflow(aFrame, overflowAreas);
|
||||
// Add the scrollable overflow areas of children (if any) to the
|
||||
// paddingRect, as if aFrame was a scrolled frame. It's important to start
|
||||
// with the paddingRect, otherwise if there are no children the overflow
|
||||
// rect will be 0,0,0,0 which will force the point 0,0 to be included in the
|
||||
// final rect.
|
||||
aFrame->UnionChildOverflow(overflowAreas, /* aAsIfScrolled = */ true);
|
||||
// Make sure that an empty padding-rect's edges are included, by adding
|
||||
// the padding-rect in again with UnionEdges.
|
||||
nsRect overflowRect =
|
||||
overflowAreas.ScrollableOverflow().UnionEdges(paddingRect);
|
||||
return nsLayoutUtils::GetScrolledRect(aFrame, overflowRect,
|
||||
paddingRect.Size(),
|
||||
aFrame->StyleVisibility()->mDirection)
|
||||
.Size();
|
||||
return overflowAreas.ScrollableOverflow().UnionEdges(paddingRect);
|
||||
}();
|
||||
|
||||
auto directions =
|
||||
ScrollContainerFrame::ComputePerAxisScrollDirections(aFrame);
|
||||
const nscoord height = directions.mToBottom
|
||||
? overflowRect.YMost() - paddingRect.Y()
|
||||
: paddingRect.YMost() - overflowRect.Y();
|
||||
const nscoord width = directions.mToRight
|
||||
? overflowRect.XMost() - paddingRect.X()
|
||||
: paddingRect.XMost() - overflowRect.X();
|
||||
return nsSize(width, height);
|
||||
}
|
||||
|
||||
nsSize Element::GetScrollSize() {
|
||||
|
||||
@@ -1378,94 +1378,6 @@ nsIFrame* nsLayoutUtils::GetNearestOverflowClipFrame(nsIFrame* aFrame) {
|
||||
});
|
||||
}
|
||||
|
||||
// static
|
||||
nsRect nsLayoutUtils::GetScrolledRect(nsIFrame* aScrolledFrame,
|
||||
const nsRect& aScrolledFrameOverflowArea,
|
||||
const nsSize& aScrollPortSize,
|
||||
StyleDirection aDirection) {
|
||||
WritingMode wm = aScrolledFrame->GetWritingMode();
|
||||
// Potentially override the frame's direction to use the direction found
|
||||
// by ScrollContainerFrame::GetScrolledFrameDir()
|
||||
wm.SetDirectionFromBidiLevel(aDirection == StyleDirection::Rtl
|
||||
? mozilla::intl::BidiEmbeddingLevel::RTL()
|
||||
: mozilla::intl::BidiEmbeddingLevel::LTR());
|
||||
|
||||
nscoord x1 = aScrolledFrameOverflowArea.x,
|
||||
x2 = aScrolledFrameOverflowArea.XMost(),
|
||||
y1 = aScrolledFrameOverflowArea.y,
|
||||
y2 = aScrolledFrameOverflowArea.YMost();
|
||||
|
||||
const bool isHorizontalWM = !wm.IsVertical();
|
||||
const bool isVerticalWM = wm.IsVertical();
|
||||
bool isInlineFlowFromTopOrLeft = !wm.IsInlineReversed();
|
||||
bool isBlockFlowFromTopOrLeft = isHorizontalWM || wm.IsVerticalLR();
|
||||
|
||||
if (aScrolledFrame->IsFlexContainerFrame()) {
|
||||
// In a flex container, the children flow (and overflow) along the flex
|
||||
// container's main axis and cross axis. These are analogous to the
|
||||
// inline/block axes, and by default they correspond exactly to those axes;
|
||||
// but the flex container's CSS (e.g. flex-direction: column-reverse) may
|
||||
// have swapped and/or reversed them, and we need to account for that here.
|
||||
FlexboxAxisInfo info(aScrolledFrame);
|
||||
if (info.mIsRowOriented) {
|
||||
// The flex container's inline axis is the main axis.
|
||||
isInlineFlowFromTopOrLeft =
|
||||
isInlineFlowFromTopOrLeft == !info.mIsMainAxisReversed;
|
||||
isBlockFlowFromTopOrLeft =
|
||||
isBlockFlowFromTopOrLeft == !info.mIsCrossAxisReversed;
|
||||
} else {
|
||||
// The flex container's block axis is the main axis.
|
||||
isBlockFlowFromTopOrLeft =
|
||||
isBlockFlowFromTopOrLeft == !info.mIsMainAxisReversed;
|
||||
isInlineFlowFromTopOrLeft =
|
||||
isInlineFlowFromTopOrLeft == !info.mIsCrossAxisReversed;
|
||||
}
|
||||
}
|
||||
|
||||
// Clamp the horizontal start-edge (x1 or x2, depending whether the logical
|
||||
// axis that corresponds to horizontal progresses from left-to-right or
|
||||
// right-to-left).
|
||||
if ((isHorizontalWM && isInlineFlowFromTopOrLeft) ||
|
||||
(isVerticalWM && isBlockFlowFromTopOrLeft)) {
|
||||
if (x1 < 0) {
|
||||
x1 = 0;
|
||||
}
|
||||
} else {
|
||||
if (x2 > aScrollPortSize.width) {
|
||||
x2 = aScrollPortSize.width;
|
||||
}
|
||||
// When the scrolled frame chooses a size larger than its available width
|
||||
// (because its padding alone is larger than the available width), we need
|
||||
// to keep the start-edge of the scroll frame anchored to the start-edge of
|
||||
// the scrollport.
|
||||
// When the scrolled frame is RTL, this means moving it in our left-based
|
||||
// coordinate system, so we need to compensate for its extra width here by
|
||||
// effectively repositioning the frame.
|
||||
nscoord extraWidth =
|
||||
std::max(0, aScrolledFrame->GetSize().width - aScrollPortSize.width);
|
||||
x2 += extraWidth;
|
||||
}
|
||||
|
||||
// Similarly, clamp the vertical start-edge (y1 or y2, depending whether the
|
||||
// logical axis that corresponds to vertical progresses from top-to-bottom or
|
||||
// buttom-to-top).
|
||||
if ((isHorizontalWM && isBlockFlowFromTopOrLeft) ||
|
||||
(isVerticalWM && isInlineFlowFromTopOrLeft)) {
|
||||
if (y1 < 0) {
|
||||
y1 = 0;
|
||||
}
|
||||
} else {
|
||||
if (y2 > aScrollPortSize.height) {
|
||||
y2 = aScrollPortSize.height;
|
||||
}
|
||||
nscoord extraHeight =
|
||||
std::max(0, aScrolledFrame->GetSize().height - aScrollPortSize.height);
|
||||
y2 += extraHeight;
|
||||
}
|
||||
|
||||
return nsRect(x1, y1, x2 - x1, y2 - y1);
|
||||
}
|
||||
|
||||
// static
|
||||
bool nsLayoutUtils::HasPseudoStyle(nsIContent* aContent,
|
||||
ComputedStyle* aComputedStyle,
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "mozilla/dom/DocumentInlines.h"
|
||||
#include "mozilla/gfx/gfxVars.h"
|
||||
#include "nsFontMetrics.h"
|
||||
#include "nsFlexContainerFrame.h"
|
||||
#include "mozilla/dom/NodeInfo.h"
|
||||
#include "nsScrollbarFrame.h"
|
||||
#include "nsINode.h"
|
||||
@@ -6893,24 +6894,107 @@ nsRect ScrollContainerFrame::GetScrolledRect() const {
|
||||
}
|
||||
|
||||
StyleDirection ScrollContainerFrame::GetScrolledFrameDir() const {
|
||||
return GetScrolledFrameDir(mScrolledFrame);
|
||||
}
|
||||
|
||||
StyleDirection ScrollContainerFrame::GetScrolledFrameDir(
|
||||
const nsIFrame* aScrolledFrame) {
|
||||
// If the scrolled frame has unicode-bidi: plaintext, the paragraph
|
||||
// direction set by the text content overrides the direction of the frame
|
||||
if (mScrolledFrame->StyleTextReset()->mUnicodeBidi ==
|
||||
if (aScrolledFrame->StyleTextReset()->mUnicodeBidi ==
|
||||
StyleUnicodeBidi::Plaintext) {
|
||||
if (nsIFrame* child = mScrolledFrame->PrincipalChildList().FirstChild()) {
|
||||
if (nsIFrame* child = aScrolledFrame->PrincipalChildList().FirstChild()) {
|
||||
return nsBidiPresUtils::ParagraphDirection(child) ==
|
||||
mozilla::intl::BidiDirection::LTR
|
||||
intl::BidiDirection::LTR
|
||||
? StyleDirection::Ltr
|
||||
: StyleDirection::Rtl;
|
||||
}
|
||||
}
|
||||
return IsBidiLTR() ? StyleDirection::Ltr : StyleDirection::Rtl;
|
||||
return aScrolledFrame->GetWritingMode().IsBidiLTR() ? StyleDirection::Ltr
|
||||
: StyleDirection::Rtl;
|
||||
}
|
||||
|
||||
auto ScrollContainerFrame::ComputePerAxisScrollDirections(
|
||||
const nsIFrame* aScrolledFrame) -> PerAxisScrollDirections {
|
||||
auto wm = aScrolledFrame->GetWritingMode();
|
||||
auto dir = GetScrolledFrameDir(aScrolledFrame);
|
||||
wm.SetDirectionFromBidiLevel(dir == StyleDirection::Rtl
|
||||
? intl::BidiEmbeddingLevel::RTL()
|
||||
: intl::BidiEmbeddingLevel::LTR());
|
||||
bool scrollToRight = wm.IsPhysicalLTR();
|
||||
bool scrollToBottom =
|
||||
!wm.IsVertical() || wm.GetInlineDir() == WritingMode::InlineDir::TTB;
|
||||
if (aScrolledFrame->IsFlexContainerFrame()) {
|
||||
// In a flex container, the children flow (and overflow) along the flex
|
||||
// container's main axis and cross axis. These are analogous to the
|
||||
// inline/block axes, and by default they correspond exactly to those axes;
|
||||
// but the flex container's CSS (e.g. flex-direction: column-reverse) may
|
||||
// have swapped and/or reversed them, and we need to account for that here.
|
||||
const FlexboxAxisInfo info(aScrolledFrame);
|
||||
const bool isMainAxisVertical = info.mIsRowOriented == wm.IsVertical();
|
||||
if (info.mIsMainAxisReversed) {
|
||||
if (isMainAxisVertical) {
|
||||
scrollToBottom = !scrollToBottom;
|
||||
} else {
|
||||
scrollToRight = !scrollToRight;
|
||||
}
|
||||
}
|
||||
if (info.mIsCrossAxisReversed) {
|
||||
if (isMainAxisVertical) {
|
||||
scrollToRight = !scrollToRight;
|
||||
} else {
|
||||
scrollToBottom = !scrollToBottom;
|
||||
}
|
||||
}
|
||||
}
|
||||
return {scrollToRight, scrollToBottom};
|
||||
}
|
||||
|
||||
nsRect ScrollContainerFrame::GetUnsnappedScrolledRectInternal(
|
||||
const nsRect& aScrolledOverflowArea, const nsSize& aScrollPortSize) const {
|
||||
return nsLayoutUtils::GetScrolledRect(mScrolledFrame, aScrolledOverflowArea,
|
||||
aScrollPortSize, GetScrolledFrameDir());
|
||||
nscoord x1 = aScrolledOverflowArea.x, x2 = aScrolledOverflowArea.XMost(),
|
||||
y1 = aScrolledOverflowArea.y, y2 = aScrolledOverflowArea.YMost();
|
||||
auto dirs = ComputePerAxisScrollDirections(mScrolledFrame);
|
||||
// Clamp the horizontal start-edge (x1 or x2, depending whether the logical
|
||||
// axis that corresponds to horizontal progresses from left-to-right or
|
||||
// right-to-left).
|
||||
if (dirs.mToRight) {
|
||||
if (x1 < 0) {
|
||||
x1 = 0;
|
||||
}
|
||||
} else {
|
||||
if (x2 > aScrollPortSize.width) {
|
||||
x2 = aScrollPortSize.width;
|
||||
}
|
||||
// When the scrolled frame chooses a size larger than its available width
|
||||
// (because its padding alone is larger than the available width), we need
|
||||
// to keep the start-edge of the scroll frame anchored to the start-edge of
|
||||
// the scrollport.
|
||||
// When the scrolled frame is RTL, this means moving it in our left-based
|
||||
// coordinate system, so we need to compensate for its extra width here by
|
||||
// effectively repositioning the frame.
|
||||
nscoord extraWidth =
|
||||
std::max(0, mScrolledFrame->GetSize().width - aScrollPortSize.width);
|
||||
x2 += extraWidth;
|
||||
}
|
||||
|
||||
// Similarly, clamp the vertical start-edge (y1 or y2, depending whether the
|
||||
// logical axis that corresponds to vertical progresses from top-to-bottom or
|
||||
// buttom-to-top).
|
||||
if (dirs.mToBottom) {
|
||||
if (y1 < 0) {
|
||||
y1 = 0;
|
||||
}
|
||||
} else {
|
||||
if (y2 > aScrollPortSize.height) {
|
||||
y2 = aScrollPortSize.height;
|
||||
}
|
||||
nscoord extraHeight =
|
||||
std::max(0, mScrolledFrame->GetSize().height - aScrollPortSize.height);
|
||||
y2 += extraHeight;
|
||||
}
|
||||
|
||||
return nsRect(x1, y1, x2 - x1, y2 - y1);
|
||||
}
|
||||
|
||||
nsMargin ScrollContainerFrame::GetActualScrollbarSizes(
|
||||
|
||||
@@ -195,6 +195,14 @@ class ScrollContainerFrame : public nsContainerFrame,
|
||||
return GetCurrentAnonymousContent().contains(GetNeededAnonymousContent());
|
||||
}
|
||||
|
||||
struct PerAxisScrollDirections {
|
||||
bool mToRight = false;
|
||||
bool mToBottom = false;
|
||||
};
|
||||
|
||||
static PerAxisScrollDirections ComputePerAxisScrollDirections(
|
||||
const nsIFrame* aScrolledFrame);
|
||||
|
||||
/**
|
||||
* Get the overscroll-behavior styles.
|
||||
*/
|
||||
@@ -1240,6 +1248,7 @@ class ScrollContainerFrame : public nsContainerFrame,
|
||||
bool HasPerspective() const { return ChildrenHavePerspective(); }
|
||||
bool HasBgAttachmentLocal() const;
|
||||
StyleDirection GetScrolledFrameDir() const;
|
||||
static StyleDirection GetScrolledFrameDir(const nsIFrame*);
|
||||
|
||||
// Ask APZ to smooth scroll to |aDestination|.
|
||||
// This method does not clamp the destination; callers should clamp it to
|
||||
|
||||
@@ -2538,7 +2538,8 @@ void nsBlockFrame::ComputeOverflowAreas(OverflowAreas& aOverflowAreas,
|
||||
#endif
|
||||
}
|
||||
|
||||
void nsBlockFrame::UnionChildOverflow(OverflowAreas& aOverflowAreas) {
|
||||
void nsBlockFrame::UnionChildOverflow(OverflowAreas& aOverflowAreas,
|
||||
bool aAsIfScrolled) {
|
||||
// We need to update the overflow areas of lines manually, as they
|
||||
// get cached and re-used otherwise. Lines aren't exposed as normal
|
||||
// frame children, so calling UnionChildOverflow alone will end up
|
||||
|
||||
@@ -630,7 +630,7 @@ class nsBlockFrame : public nsContainerFrame {
|
||||
|
||||
bool ComputeCustomOverflow(mozilla::OverflowAreas&) override;
|
||||
|
||||
void UnionChildOverflow(mozilla::OverflowAreas&) override;
|
||||
void UnionChildOverflow(mozilla::OverflowAreas&, bool aAsIfScrolled) override;
|
||||
|
||||
/**
|
||||
* Load all of aFrame's floats into the float manager iff aFrame is not a
|
||||
|
||||
@@ -4864,7 +4864,7 @@ Maybe<nscoord> nsFlexContainerFrame::GetNaturalBaselineBOffset(
|
||||
}
|
||||
|
||||
void nsFlexContainerFrame::UnionInFlowChildOverflow(
|
||||
OverflowAreas& aOverflowAreas) {
|
||||
OverflowAreas& aOverflowAreas, bool aAsIfScrolled) {
|
||||
// The CSS Overflow spec [1] requires that a scrollable container's
|
||||
// scrollable overflow should include the following areas.
|
||||
//
|
||||
@@ -4882,6 +4882,7 @@ void nsFlexContainerFrame::UnionInFlowChildOverflow(
|
||||
//
|
||||
// [1] https://drafts.csswg.org/css-overflow-3/#scrollable.
|
||||
const bool isScrolledContent =
|
||||
aAsIfScrolled ||
|
||||
Style()->GetPseudoType() == PseudoStyleType::scrolledContent;
|
||||
bool anyScrolledContentItem = false;
|
||||
// Union of normal-positioned margin boxes for all the items.
|
||||
@@ -4931,8 +4932,9 @@ void nsFlexContainerFrame::UnionInFlowChildOverflow(
|
||||
}
|
||||
}
|
||||
|
||||
void nsFlexContainerFrame::UnionChildOverflow(OverflowAreas& aOverflowAreas) {
|
||||
UnionInFlowChildOverflow(aOverflowAreas);
|
||||
void nsFlexContainerFrame::UnionChildOverflow(OverflowAreas& aOverflowAreas,
|
||||
bool aAsIfScrolled) {
|
||||
UnionInFlowChildOverflow(aOverflowAreas, aAsIfScrolled);
|
||||
// Union with child frames, skipping the principal list since we already
|
||||
// handled those above.
|
||||
nsLayoutUtils::UnionChildOverflow(this, aOverflowAreas,
|
||||
|
||||
@@ -159,10 +159,11 @@ class nsFlexContainerFrame final : public nsContainerFrame,
|
||||
BaselineExportContext) const override;
|
||||
|
||||
// Unions the child overflow from our in-flow children.
|
||||
void UnionInFlowChildOverflow(mozilla::OverflowAreas&);
|
||||
void UnionInFlowChildOverflow(mozilla::OverflowAreas&,
|
||||
bool aAsIfScrolled = false);
|
||||
|
||||
// Unions the child overflow from all our children, including out of flows.
|
||||
void UnionChildOverflow(mozilla::OverflowAreas&) final;
|
||||
void UnionChildOverflow(mozilla::OverflowAreas&, bool aAsIfScrolled) final;
|
||||
|
||||
// nsContainerFrame overrides
|
||||
bool DrainSelfOverflowList() override;
|
||||
|
||||
@@ -488,8 +488,8 @@ void nsHTMLCanvasFrame::AppendDirectlyOwnedAnonBoxes(
|
||||
aResult.AppendElement(OwnedAnonBox(mFrames.FirstChild()));
|
||||
}
|
||||
|
||||
void nsHTMLCanvasFrame::UnionChildOverflow(
|
||||
mozilla::OverflowAreas& aOverflowAreas) {
|
||||
void nsHTMLCanvasFrame::UnionChildOverflow(OverflowAreas& aOverflowAreas,
|
||||
bool) {
|
||||
// Our one child (the canvas content anon box) is unpainted and isn't relevant
|
||||
// for child-overflow purposes. So we need to provide our own trivial impl to
|
||||
// avoid receiving the child-considering impl that we would otherwise inherit.
|
||||
|
||||
@@ -57,7 +57,7 @@ class nsHTMLCanvasFrame final : public nsContainerFrame {
|
||||
virtual mozilla::IntrinsicSize GetIntrinsicSize() override;
|
||||
mozilla::AspectRatio GetIntrinsicRatio() const override;
|
||||
|
||||
void UnionChildOverflow(mozilla::OverflowAreas& aOverflowAreas) override;
|
||||
void UnionChildOverflow(mozilla::OverflowAreas&, bool aAsIfScrolled) override;
|
||||
|
||||
SizeComputationResult ComputeSize(
|
||||
gfxContext* aRenderingContext, mozilla::WritingMode aWM,
|
||||
|
||||
@@ -8045,8 +8045,9 @@ bool nsIFrame::DoesClipChildrenInBothAxes() const {
|
||||
}
|
||||
|
||||
/* virtual */
|
||||
void nsIFrame::UnionChildOverflow(OverflowAreas& aOverflowAreas) {
|
||||
if (!DoesClipChildrenInBothAxes()) {
|
||||
void nsIFrame::UnionChildOverflow(OverflowAreas& aOverflowAreas,
|
||||
bool aAsIfScrolled) {
|
||||
if (aAsIfScrolled || !DoesClipChildrenInBothAxes()) {
|
||||
nsLayoutUtils::UnionChildOverflow(this, aOverflowAreas);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3071,9 +3071,11 @@ class nsIFrame : public nsQueryFrame {
|
||||
|
||||
/**
|
||||
* Computes any overflow area created by children of this frame and
|
||||
* includes it into aOverflowAreas.
|
||||
* includes it into aOverflowAreas. If aAsIfScrolled is true, then it behaves
|
||||
* as if we were the scrolled content frame.
|
||||
*/
|
||||
virtual void UnionChildOverflow(mozilla::OverflowAreas& aOverflowAreas);
|
||||
virtual void UnionChildOverflow(mozilla::OverflowAreas& aOverflowAreas,
|
||||
bool aAsIfScrolled = false);
|
||||
|
||||
// Returns the applicable overflow-clip-margin values.
|
||||
nsSize OverflowClipMargin(mozilla::PhysicalAxes aClipAxes) const;
|
||||
|
||||
@@ -466,7 +466,8 @@ void SVGOuterSVGFrame::DidReflow(nsPresContext* aPresContext,
|
||||
}
|
||||
|
||||
/* virtual */
|
||||
void SVGOuterSVGFrame::UnionChildOverflow(OverflowAreas& aOverflowAreas) {
|
||||
void SVGOuterSVGFrame::UnionChildOverflow(OverflowAreas& aOverflowAreas,
|
||||
bool aAsIfScrolled) {
|
||||
// See the comments in Reflow above.
|
||||
|
||||
// WARNING!! Keep this in sync with Reflow above!
|
||||
|
||||
@@ -65,7 +65,8 @@ class SVGOuterSVGFrame final : public SVGDisplayContainerFrame,
|
||||
void DidReflow(nsPresContext* aPresContext,
|
||||
const ReflowInput* aReflowInput) override;
|
||||
|
||||
void UnionChildOverflow(mozilla::OverflowAreas& aOverflowAreas) override;
|
||||
void UnionChildOverflow(mozilla::OverflowAreas& aOverflowAreas,
|
||||
bool aAsIfScrolled) override;
|
||||
|
||||
void BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
||||
const nsDisplayListSet& aLists) override;
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
<!doctype html>
|
||||
<meta charset="utf-8">
|
||||
<title>scroll{Width,Height} with visible overflow and negative margins.</title>
|
||||
<link rel="help" href="https://drafts.csswg.org/cssom-view/#extension-to-the-element-interface">
|
||||
<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1906475">
|
||||
<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
|
||||
<link rel="author" title="Mozilla" href="https://mozilla.org">
|
||||
<script src=/resources/testharness.js></script>
|
||||
<script src=/resources/testharnessreport.js></script>
|
||||
<style>
|
||||
body { margin: 0 }
|
||||
.wrapper {
|
||||
width: 90px;
|
||||
border: 10px solid #d1d1d2;
|
||||
}
|
||||
.inner {
|
||||
margin: -10px;
|
||||
height: 100px;
|
||||
width: 100px;
|
||||
background-color: blue;
|
||||
}
|
||||
</style>
|
||||
<div class="wrapper" style="overflow: visible">
|
||||
<div class="inner"></div>
|
||||
</div>
|
||||
<div class="wrapper" style="overflow: hidden">
|
||||
<div class="inner"></div>
|
||||
</div>
|
||||
<div class="wrapper" style="overflow: auto">
|
||||
<div class="inner"></div>
|
||||
</div>
|
||||
<div class="wrapper" style="overflow: clip">
|
||||
<div class="inner"></div>
|
||||
</div>
|
||||
<script>
|
||||
for (let wrapper of document.querySelectorAll(".wrapper")) {
|
||||
test(function() {
|
||||
assert_equals(wrapper.scrollWidth, 90, "scrollWidth");
|
||||
assert_equals(wrapper.scrollHeight, 90, "scrollHeight");
|
||||
}, "scrollWidth/Height with negative margins: " + wrapper.style.cssText);
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,100 @@
|
||||
<!doctype html>
|
||||
<meta charset="utf-8">
|
||||
<title>scroll{Width,Height} with visible overflow and negative margins.</title>
|
||||
<link rel="help" href="https://drafts.csswg.org/cssom-view/#extension-to-the-element-interface">
|
||||
<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1906475">
|
||||
<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
|
||||
<link rel="author" title="Mozilla" href="https://mozilla.org">
|
||||
<script src=/resources/testharness.js></script>
|
||||
<script src=/resources/testharnessreport.js></script>
|
||||
<style>
|
||||
body { margin: 0 }
|
||||
.wrapper {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border: 1px solid #d1d1d2;
|
||||
padding: 1px 4px 8px 16px;
|
||||
border-width: 1px 2px 3px 4px;
|
||||
border-right-width: 50px;
|
||||
border-bottom-width: 40px;
|
||||
}
|
||||
.inner {
|
||||
margin: -100px;
|
||||
height: 300px;
|
||||
width: 300px;
|
||||
background-color: blue;
|
||||
}
|
||||
</style>
|
||||
<div class="wrapper">
|
||||
<div class="inner"></div>
|
||||
</div>
|
||||
<script>
|
||||
const wrapper = document.querySelector(".wrapper");
|
||||
const contentBox = {
|
||||
width: 80,
|
||||
height: 80,
|
||||
};
|
||||
const paddingBox = {
|
||||
width: contentBox.width + 4 + 16,
|
||||
height: contentBox.height + 1 + 8,
|
||||
};
|
||||
for (let display of ["block", "flex", "grid"]) {
|
||||
for (let flexDirection of ["row", "row-reverse", "column", "column-reverse"]) {
|
||||
if (flexDirection != "row" && display != "flex") {
|
||||
// Don't bother retesting with all flexDirection values unless we're actually a flex container
|
||||
continue;
|
||||
}
|
||||
for (let flexWrap of ["", "wrap-reverse"]) {
|
||||
if (flexWrap != "" && display != "flex") {
|
||||
// Don't bother retesting with all flexWrap values unless we're actually a flex container
|
||||
continue;
|
||||
}
|
||||
for (let direction of ["ltr", "rtl"]) {
|
||||
for (let writingMode of ["horizontal-tb", "vertical-lr", "vertical-rl"]) {
|
||||
for (let overflow of ["visible", "hidden", "auto", "clip", "scroll"]) {
|
||||
wrapper.style.display = display;
|
||||
wrapper.style.overflow = overflow;
|
||||
wrapper.style.direction = direction;
|
||||
wrapper.style.writingMode = writingMode;
|
||||
wrapper.style.flexDirection = flexDirection;
|
||||
wrapper.style.flexWrap = flexWrap;
|
||||
// Suppress scrollbars because they get added to the padding are
|
||||
// and would need to account for them in flexbox.
|
||||
wrapper.style.scrollbarWidth = display == "flex" ? "none" : "";
|
||||
let vertical = writingMode.startsWith("vertical");
|
||||
let scrollToTop = vertical && direction == "rtl";
|
||||
let scrollToLeft = (!vertical && direction == "rtl") || writingMode == "vertical-rl";
|
||||
let flexMainAxisIsVertical = flexDirection.startsWith("row") == vertical;
|
||||
if (display == "flex") {
|
||||
if (flexDirection.endsWith("-reverse")) {
|
||||
if (flexMainAxisIsVertical) {
|
||||
scrollToTop = !scrollToTop;
|
||||
} else {
|
||||
scrollToLeft = !scrollToLeft;
|
||||
}
|
||||
}
|
||||
if (flexWrap == "wrap-reverse") {
|
||||
if (flexMainAxisIsVertical) {
|
||||
scrollToLeft = !scrollToLeft;
|
||||
} else {
|
||||
scrollToTop = !scrollToTop;
|
||||
}
|
||||
}
|
||||
}
|
||||
test(function() {
|
||||
assert_greater_than_equal(wrapper.scrollWidth, paddingBox.width, "scrollWidth should be at least padding box width");
|
||||
let padding = display == "flex" && !flexMainAxisIsVertical ? paddingBox.width - contentBox.width : 0;
|
||||
assert_equals(wrapper.scrollWidth, (scrollToLeft ? 204 : 216) - padding, "scrollWidth");
|
||||
}, "scrollWidth with negative margins: " + wrapper.style.cssText);
|
||||
test(function() {
|
||||
assert_greater_than_equal(wrapper.scrollHeight, paddingBox.height, "scrollHeight should be at least padding box height");
|
||||
let padding = display == "flex" && flexMainAxisIsVertical ? paddingBox.width - contentBox.width : 0;
|
||||
assert_equals(wrapper.scrollHeight, (scrollToTop ? 208 : 201) - padding, "scrollHeight");
|
||||
}, "scrollHeight with negative margins: " + wrapper.style.cssText);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user