Bug 2056749 - Add -moz-scrollbar-inset-{block, inline} for improved scrollbar boundaries for rounded chrome corners. r=emilio,tabbrowser-reviewers,firefox-style-system-reviewers,layout-reviewers,sthompson
- Adds `-moz-scrollbar-inset-block` and `-moz-scrollbar-inset-inline`, chrome-only
`<non-negative-length>{1,2}` longhands. Each names the scrollbar's axis it
applies to. The two values are the axis' logical start and end
and flip with the writing mode.
- ScrollContainerFrame::ScrollbarInsets() serves as the one place to extract the inset from for
both nsScrollbarFrame and GetBorderRadii
- Applies inset to nsScrollbarFrame::Reflow similar to stepper buttons
- ScrollContainerFrame::GetBorderRadii no longer squares a corner off when the
inset already holds the scrollbar clear of the curve.
- Forward the <browser> embedder's inset to a root scrollbar for the content viewport
- Add reftests for css-scrollbars
- This commit does NOT implement the css property anywhere
Differential Revision: https://phabricator.services.mozilla.com/D313900
This commit is contained in:
committed by
mhynson@mozilla.com
parent
9c85b9b307
commit
19ded9cd7e
@@ -160,6 +160,8 @@ exports.ANIMATION_TYPE_FOR_LONGHANDS = [
|
||||
"scroll-snap-type",
|
||||
"shape-rendering",
|
||||
"scrollbar-gutter",
|
||||
"-moz-scrollbar-inset-block",
|
||||
"-moz-scrollbar-inset-inline",
|
||||
"scrollbar-width",
|
||||
"stroke-linecap",
|
||||
"stroke-linejoin",
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "mozilla/dom/BrowsingContext.h"
|
||||
|
||||
#include "ipc/IPCMessageUtils.h"
|
||||
#include "mozilla/GfxMessageUtils.h"
|
||||
|
||||
#ifdef ACCESSIBILITY
|
||||
# include "mozilla/a11y/DocAccessibleParent.h"
|
||||
@@ -3492,6 +3493,14 @@ void BrowsingContext::DidSet(FieldIndex<IDX_EmbedderColorSchemes>,
|
||||
PresContextAffectingFieldChanged();
|
||||
}
|
||||
|
||||
void BrowsingContext::DidSet(FieldIndex<IDX_EmbedderScrollbarInset>,
|
||||
LayoutDeviceIntMargin&& aOldValue) {
|
||||
if (GetEmbedderScrollbarInset() == aOldValue) {
|
||||
return;
|
||||
}
|
||||
PresContextAffectingFieldChanged();
|
||||
}
|
||||
|
||||
void BrowsingContext::DidSet(FieldIndex<IDX_PrefersColorSchemeOverride>,
|
||||
dom::PrefersColorSchemeOverride aOldValue) {
|
||||
MOZ_ASSERT(IsTop());
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <tuple>
|
||||
#include "GVAutoplayRequestUtils.h"
|
||||
#include "Units.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/HalScreenConfiguration.h"
|
||||
#include "mozilla/LinkedList.h"
|
||||
@@ -254,6 +255,10 @@ struct EmbedderColorSchemes {
|
||||
/* prefers-color-scheme override based on the color-scheme style of our \
|
||||
* <browser> embedder element. */ \
|
||||
FIELD(EmbedderColorSchemes, EmbedderColorSchemes) \
|
||||
/* Content-area scrollbar insets forwarded from the <browser> embedder's \
|
||||
* -moz-scrollbar-inset-{block,inline}, so the top-level content viewport \
|
||||
* scrollbars clear the rounded content-area corners. */ \
|
||||
FIELD(EmbedderScrollbarInset, LayoutDeviceIntMargin) \
|
||||
FIELD(DisplayMode, dom::DisplayMode) \
|
||||
/* The number of entries added to the session history because of this \
|
||||
* browsing context. */ \
|
||||
@@ -1332,6 +1337,11 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
|
||||
return CheckOnlyEmbedderCanSet(aSource);
|
||||
}
|
||||
|
||||
bool CanSet(FieldIndex<IDX_EmbedderScrollbarInset>,
|
||||
const LayoutDeviceIntMargin&, ContentParent* aSource) {
|
||||
return CheckOnlyEmbedderCanSet(aSource);
|
||||
}
|
||||
|
||||
bool CanSet(FieldIndex<IDX_PrefersColorSchemeOverride>,
|
||||
dom::PrefersColorSchemeOverride, ContentParent*) {
|
||||
return IsTop();
|
||||
@@ -1365,6 +1375,9 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
|
||||
void DidSet(FieldIndex<IDX_EmbedderColorSchemes>,
|
||||
EmbedderColorSchemes&& aOldValue);
|
||||
|
||||
void DidSet(FieldIndex<IDX_EmbedderScrollbarInset>,
|
||||
LayoutDeviceIntMargin&& aOldValue);
|
||||
|
||||
void DidSet(FieldIndex<IDX_PrefersColorSchemeOverride>,
|
||||
dom::PrefersColorSchemeOverride aOldValue);
|
||||
|
||||
|
||||
@@ -108,7 +108,7 @@ use.counter:
|
||||
send_in_pings:
|
||||
- use-counters
|
||||
|
||||
# Total of 2628 use counter metrics (excludes denominators).
|
||||
# Total of 2632 use counter metrics (excludes denominators).
|
||||
# Total of 416 'page' use counters.
|
||||
use.counter.page:
|
||||
svgsvgelement_getelementbyid:
|
||||
@@ -19843,7 +19843,7 @@ use.counter.deprecated_ops.doc:
|
||||
send_in_pings:
|
||||
- use-counters
|
||||
|
||||
# Total of 734 'CSS (page)' use counters.
|
||||
# Total of 736 'CSS (page)' use counters.
|
||||
use.counter.css.page:
|
||||
css_align_items:
|
||||
type: counter
|
||||
@@ -24860,6 +24860,40 @@ use.counter.css.page:
|
||||
send_in_pings:
|
||||
- use-counters
|
||||
|
||||
css_moz_scrollbar_inset_block:
|
||||
type: counter
|
||||
description: >
|
||||
Whether a page used the CSS property -moz-scrollbar-inset-block.
|
||||
Compare against `use.counter.top_level_content_documents_destroyed`
|
||||
to calculate the rate.
|
||||
bugs:
|
||||
- https://bugzilla.mozilla.org/show_bug.cgi?id=1852098
|
||||
data_reviews:
|
||||
- https://bugzilla.mozilla.org/show_bug.cgi?id=1852098
|
||||
notification_emails:
|
||||
- dom-core@mozilla.com
|
||||
- emilio@mozilla.com
|
||||
expires: never
|
||||
send_in_pings:
|
||||
- use-counters
|
||||
|
||||
css_moz_scrollbar_inset_inline:
|
||||
type: counter
|
||||
description: >
|
||||
Whether a page used the CSS property -moz-scrollbar-inset-inline.
|
||||
Compare against `use.counter.top_level_content_documents_destroyed`
|
||||
to calculate the rate.
|
||||
bugs:
|
||||
- https://bugzilla.mozilla.org/show_bug.cgi?id=1852098
|
||||
data_reviews:
|
||||
- https://bugzilla.mozilla.org/show_bug.cgi?id=1852098
|
||||
notification_emails:
|
||||
- dom-core@mozilla.com
|
||||
- emilio@mozilla.com
|
||||
expires: never
|
||||
send_in_pings:
|
||||
- use-counters
|
||||
|
||||
css_moz_window_transform:
|
||||
type: counter
|
||||
description: >
|
||||
@@ -28736,6 +28770,23 @@ use.counter.css.page:
|
||||
send_in_pings:
|
||||
- use-counters
|
||||
|
||||
css_moz_appearance:
|
||||
type: counter
|
||||
description: >
|
||||
Whether a page used the CSS property -moz-appearance.
|
||||
Compare against `use.counter.top_level_content_documents_destroyed`
|
||||
to calculate the rate.
|
||||
bugs:
|
||||
- https://bugzilla.mozilla.org/show_bug.cgi?id=1852098
|
||||
data_reviews:
|
||||
- https://bugzilla.mozilla.org/show_bug.cgi?id=1852098
|
||||
notification_emails:
|
||||
- dom-core@mozilla.com
|
||||
- emilio@mozilla.com
|
||||
expires: never
|
||||
send_in_pings:
|
||||
- use-counters
|
||||
|
||||
css_all:
|
||||
type: counter
|
||||
description: >
|
||||
@@ -29110,23 +29161,6 @@ use.counter.css.page:
|
||||
send_in_pings:
|
||||
- use-counters
|
||||
|
||||
css_moz_appearance:
|
||||
type: counter
|
||||
description: >
|
||||
Whether a page used the CSS property -moz-appearance.
|
||||
Compare against `use.counter.top_level_content_documents_destroyed`
|
||||
to calculate the rate.
|
||||
bugs:
|
||||
- https://bugzilla.mozilla.org/show_bug.cgi?id=1852098
|
||||
data_reviews:
|
||||
- https://bugzilla.mozilla.org/show_bug.cgi?id=1852098
|
||||
notification_emails:
|
||||
- dom-core@mozilla.com
|
||||
- emilio@mozilla.com
|
||||
expires: never
|
||||
send_in_pings:
|
||||
- use-counters
|
||||
|
||||
css_webkit_appearance:
|
||||
type: counter
|
||||
description: >
|
||||
@@ -32323,7 +32357,7 @@ use.counter.css.page:
|
||||
send_in_pings:
|
||||
- use-counters
|
||||
|
||||
# Total of 734 'CSS (document)' use counters.
|
||||
# Total of 736 'CSS (document)' use counters.
|
||||
use.counter.css.doc:
|
||||
css_align_items:
|
||||
type: counter
|
||||
@@ -37340,6 +37374,40 @@ use.counter.css.doc:
|
||||
send_in_pings:
|
||||
- use-counters
|
||||
|
||||
css_moz_scrollbar_inset_block:
|
||||
type: counter
|
||||
description: >
|
||||
Whether a document used the CSS property -moz-scrollbar-inset-block.
|
||||
Compare against `use.counter.content_documents_destroyed`
|
||||
to calculate the rate.
|
||||
bugs:
|
||||
- https://bugzilla.mozilla.org/show_bug.cgi?id=1852098
|
||||
data_reviews:
|
||||
- https://bugzilla.mozilla.org/show_bug.cgi?id=1852098
|
||||
notification_emails:
|
||||
- dom-core@mozilla.com
|
||||
- emilio@mozilla.com
|
||||
expires: never
|
||||
send_in_pings:
|
||||
- use-counters
|
||||
|
||||
css_moz_scrollbar_inset_inline:
|
||||
type: counter
|
||||
description: >
|
||||
Whether a document used the CSS property -moz-scrollbar-inset-inline.
|
||||
Compare against `use.counter.content_documents_destroyed`
|
||||
to calculate the rate.
|
||||
bugs:
|
||||
- https://bugzilla.mozilla.org/show_bug.cgi?id=1852098
|
||||
data_reviews:
|
||||
- https://bugzilla.mozilla.org/show_bug.cgi?id=1852098
|
||||
notification_emails:
|
||||
- dom-core@mozilla.com
|
||||
- emilio@mozilla.com
|
||||
expires: never
|
||||
send_in_pings:
|
||||
- use-counters
|
||||
|
||||
css_moz_window_transform:
|
||||
type: counter
|
||||
description: >
|
||||
@@ -41216,6 +41284,23 @@ use.counter.css.doc:
|
||||
send_in_pings:
|
||||
- use-counters
|
||||
|
||||
css_moz_appearance:
|
||||
type: counter
|
||||
description: >
|
||||
Whether a document used the CSS property -moz-appearance.
|
||||
Compare against `use.counter.content_documents_destroyed`
|
||||
to calculate the rate.
|
||||
bugs:
|
||||
- https://bugzilla.mozilla.org/show_bug.cgi?id=1852098
|
||||
data_reviews:
|
||||
- https://bugzilla.mozilla.org/show_bug.cgi?id=1852098
|
||||
notification_emails:
|
||||
- dom-core@mozilla.com
|
||||
- emilio@mozilla.com
|
||||
expires: never
|
||||
send_in_pings:
|
||||
- use-counters
|
||||
|
||||
css_all:
|
||||
type: counter
|
||||
description: >
|
||||
@@ -41590,23 +41675,6 @@ use.counter.css.doc:
|
||||
send_in_pings:
|
||||
- use-counters
|
||||
|
||||
css_moz_appearance:
|
||||
type: counter
|
||||
description: >
|
||||
Whether a document used the CSS property -moz-appearance.
|
||||
Compare against `use.counter.content_documents_destroyed`
|
||||
to calculate the rate.
|
||||
bugs:
|
||||
- https://bugzilla.mozilla.org/show_bug.cgi?id=1852098
|
||||
data_reviews:
|
||||
- https://bugzilla.mozilla.org/show_bug.cgi?id=1852098
|
||||
notification_emails:
|
||||
- dom-core@mozilla.com
|
||||
- emilio@mozilla.com
|
||||
expires: never
|
||||
send_in_pings:
|
||||
- use-counters
|
||||
|
||||
css_webkit_appearance:
|
||||
type: counter
|
||||
description: >
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include "mozilla/PresShellInlines.h"
|
||||
#include "mozilla/RestyleManager.h"
|
||||
#include "mozilla/SMILAnimationController.h"
|
||||
#include "mozilla/ScrollContainerFrame.h"
|
||||
#include "mozilla/ServoBindings.h"
|
||||
#include "mozilla/ServoStyleSet.h"
|
||||
#include "mozilla/StaticPrefs_bidi.h"
|
||||
@@ -896,6 +897,21 @@ void nsPresContext::SetLinkParametersOverride(
|
||||
RebuildAllStyleData(nsChangeHint(0), RestyleHint::RecascadeSubtree());
|
||||
}
|
||||
|
||||
void nsPresContext::SetEmbedderScrollbarInset(const nsMargin& aInset) {
|
||||
if (mEmbedderScrollbarInset == aInset) {
|
||||
return;
|
||||
}
|
||||
mEmbedderScrollbarInset = aInset;
|
||||
|
||||
// Read directly by nsScrollbarFrame::Reflow for the viewport scroll frame,
|
||||
// so nothing needs re-cascading; just reflow the scrollbars.
|
||||
if (mozilla::PresShell* presShell = GetPresShell()) {
|
||||
if (ScrollContainerFrame* sf = presShell->GetRootScrollContainerFrame()) {
|
||||
sf->MarkScrollbarsDirtyForReflow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void nsPresContext::UpdateAnimationsPlayBackRateMultiplier(double aMultiplier) {
|
||||
if (mAnimationsPlayBackRateMultiplier == aMultiplier) {
|
||||
return;
|
||||
@@ -939,6 +955,9 @@ void nsPresContext::RecomputeBrowsingContextDependentData() {
|
||||
return browsingContext->GetEmbedderColorSchemes().mPreferred;
|
||||
}());
|
||||
|
||||
SetEmbedderScrollbarInset(LayoutDevicePixel::ToAppUnits(
|
||||
browsingContext->GetEmbedderScrollbarInset(), AppUnitsPerDevPixel()));
|
||||
|
||||
UpdateForcedColors();
|
||||
|
||||
SetInRDMPane(top->GetInRDMPane());
|
||||
|
||||
@@ -601,6 +601,15 @@ class nsPresContext : public nsISupports,
|
||||
void SetLinkParametersOverride(
|
||||
const mozilla::StyleLinkParameters& aLinkParameters);
|
||||
|
||||
/**
|
||||
* Content-area scrollbar insets forwarded from the <browser> embedder, per
|
||||
* physical side, in app units.
|
||||
*/
|
||||
const nsMargin& EmbedderScrollbarInset() const {
|
||||
return mEmbedderScrollbarInset;
|
||||
}
|
||||
void SetEmbedderScrollbarInset(const nsMargin& aInset);
|
||||
|
||||
/**
|
||||
* Return the device's screen size in inches, for font size
|
||||
* inflation.
|
||||
@@ -1433,6 +1442,7 @@ class nsPresContext : public nsISupports,
|
||||
mozilla::dom::PrefersColorSchemeOverride mOverriddenOrEmbedderColorScheme;
|
||||
mozilla::StyleForcedColors mForcedColors;
|
||||
mozilla::StyleLinkParameters mLinkParameters;
|
||||
nsMargin mEmbedderScrollbarInset;
|
||||
|
||||
protected:
|
||||
virtual ~nsPresContext();
|
||||
|
||||
@@ -5882,6 +5882,11 @@ GetWebkitScrollbarWidthAndHeight(
|
||||
return {toSize(webkitScrollbarWidth), toSize(webkitScrollbarHeight)};
|
||||
}
|
||||
|
||||
static nsMargin StyleScrollbarInsets(const ComputedStyle& aStyle,
|
||||
const WritingMode aWm) {
|
||||
return aStyle.StyleDisplay()->GetScrollbarInset(aWm).GetPhysicalMargin(aWm);
|
||||
}
|
||||
|
||||
void ScrollContainerFrame::DidSetComputedStyle(
|
||||
ComputedStyle* aOldComputedStyle) {
|
||||
nsContainerFrame::DidSetComputedStyle(aOldComputedStyle);
|
||||
@@ -5932,10 +5937,15 @@ void ScrollContainerFrame::DidSetComputedStyle(
|
||||
}
|
||||
}
|
||||
|
||||
if (aOldComputedStyle && !mIsRoot &&
|
||||
StyleDisplay()->mScrollSnapType !=
|
||||
aOldComputedStyle->StyleDisplay()->mScrollSnapType) {
|
||||
PostPendingResnap();
|
||||
if (aOldComputedStyle && !mIsRoot) {
|
||||
if (StyleDisplay()->mScrollSnapType !=
|
||||
aOldComputedStyle->StyleDisplay()->mScrollSnapType) {
|
||||
PostPendingResnap();
|
||||
}
|
||||
if (ScrollbarInsets() !=
|
||||
StyleScrollbarInsets(*aOldComputedStyle, GetWritingMode())) {
|
||||
MarkScrollbarsDirtyForReflow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6969,6 +6979,13 @@ void ScrollContainerFrame::LayoutScrollbars(ScrollReflowInput& aState,
|
||||
}
|
||||
}
|
||||
|
||||
nsMargin ScrollContainerFrame::ScrollbarInsets() const {
|
||||
if (mIsRoot) {
|
||||
return PresContext()->EmbedderScrollbarInset();
|
||||
}
|
||||
return StyleScrollbarInsets(*Style(), GetWritingMode());
|
||||
}
|
||||
|
||||
static void ReduceRadii(nscoord aXBorder, nscoord aYBorder, nsSize& aRadius) {
|
||||
// In order to ensure that the inside edge of the border has no
|
||||
// curvature, we need at least one of its radii to be zero.
|
||||
@@ -6982,6 +6999,20 @@ static void ReduceRadii(nscoord aXBorder, nscoord aYBorder, nsSize& aRadius) {
|
||||
aRadius.width *= ratio;
|
||||
aRadius.height *= ratio;
|
||||
}
|
||||
// Whether a scrollbar on one side of a corner reaches that corner's curve, and
|
||||
// so forces it square.
|
||||
// aInset: the scrollbar's inset at the given corner
|
||||
// aRadius: the corner radius measured along the scrollbar's axis
|
||||
// aBorder: border width on the side the curve starts from
|
||||
static bool ScrollbarReachesCorner(bool aHasScrollbar, nscoord aInset,
|
||||
nscoord aRadius, nscoord aBorder) {
|
||||
if (!aHasScrollbar) {
|
||||
return false;
|
||||
}
|
||||
// Subtracting aBorder because ReduceRadii only squares the border's inner
|
||||
// edge; that also matches its early-out when the border swallows the radius.
|
||||
return aInset < aRadius - aBorder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement an override for nsIFrame::GetBorderRadii to ensure that
|
||||
@@ -7006,16 +7037,33 @@ bool ScrollContainerFrame::GetBorderRadii(const nsSize& aFrameSize,
|
||||
nsMargin sb = GetActualScrollbarSizes();
|
||||
nsMargin border = GetUsedBorder();
|
||||
|
||||
if (sb.left > 0 || sb.top > 0) {
|
||||
// A scrollbar with an inset holds clear of a corner leaves that corner's
|
||||
// radius alone. A vertical scrollbar is inset at the top and bottom, and a
|
||||
// horizontal one at the left and right.
|
||||
const nsMargin inset = ScrollbarInsets();
|
||||
|
||||
if (ScrollbarReachesCorner(sb.left, inset.top, aRadii.TopLeft().height,
|
||||
border.top) ||
|
||||
ScrollbarReachesCorner(sb.top, inset.left, aRadii.TopLeft().width,
|
||||
border.left)) {
|
||||
ReduceRadii(border.left, border.top, aRadii.TopLeft());
|
||||
}
|
||||
if (sb.top > 0 || sb.right > 0) {
|
||||
if (ScrollbarReachesCorner(sb.right, inset.top, aRadii.TopRight().height,
|
||||
border.top) ||
|
||||
ScrollbarReachesCorner(sb.top, inset.right, aRadii.TopRight().width,
|
||||
border.right)) {
|
||||
ReduceRadii(border.right, border.top, aRadii.TopRight());
|
||||
}
|
||||
if (sb.right > 0 || sb.bottom > 0) {
|
||||
if (ScrollbarReachesCorner(sb.right, inset.bottom,
|
||||
aRadii.BottomRight().height, border.bottom) ||
|
||||
ScrollbarReachesCorner(sb.bottom, inset.right, aRadii.BottomRight().width,
|
||||
border.right)) {
|
||||
ReduceRadii(border.right, border.bottom, aRadii.BottomRight());
|
||||
}
|
||||
if (sb.bottom > 0 || sb.left > 0) {
|
||||
if (ScrollbarReachesCorner(sb.left, inset.bottom, aRadii.BottomLeft().height,
|
||||
border.bottom) ||
|
||||
ScrollbarReachesCorner(sb.bottom, inset.left, aRadii.BottomLeft().width,
|
||||
border.left)) {
|
||||
ReduceRadii(border.left, border.bottom, aRadii.BottomLeft());
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -111,6 +111,8 @@ class ScrollContainerFrame : public nsContainerFrame,
|
||||
nsIFrame::Sides aSkipSides,
|
||||
nsRectCornerRadii&) const final;
|
||||
|
||||
nsMargin ScrollbarInsets() const;
|
||||
|
||||
nscoord IntrinsicISize(const IntrinsicSizeInput& aInput,
|
||||
IntrinsicISizeType aType) override;
|
||||
|
||||
|
||||
@@ -2314,6 +2314,14 @@ inline bool nsStyleMargin::HasAuto(
|
||||
: HasBlockAxisAuto(aWM, aParams);
|
||||
}
|
||||
|
||||
inline mozilla::LogicalMargin nsStyleDisplay::GetScrollbarInset(
|
||||
mozilla::WritingMode aWM) const {
|
||||
return mozilla::LogicalMargin(aWM, mScrollbarInsetBlock.start.ToAppUnits(),
|
||||
mScrollbarInsetInline.end.ToAppUnits(),
|
||||
mScrollbarInsetBlock.end.ToAppUnits(),
|
||||
mScrollbarInsetInline.start.ToAppUnits());
|
||||
}
|
||||
|
||||
inline AnchorResolvedMargin nsStyleMargin::GetMargin(
|
||||
mozilla::LogicalSide aSide, mozilla::WritingMode aWM,
|
||||
const AnchorPosResolutionParams& aParams) const {
|
||||
|
||||
@@ -177,6 +177,7 @@ void nsSubDocumentFrame::UpdateEmbeddedBrowsingContextDependentData() {
|
||||
}
|
||||
MaybeUpdateRemoteStyle();
|
||||
MaybeUpdateEmbedderColorScheme();
|
||||
MaybeUpdateEmbedderScrollbarInset();
|
||||
MaybeUpdateEmbedderZoom();
|
||||
PropagateIsUnderHiddenEmbedderElement(
|
||||
PresShell()->IsUnderHiddenEmbedderElement() ||
|
||||
@@ -775,6 +776,36 @@ void nsSubDocumentFrame::MaybeUpdateEmbedderColorScheme() {
|
||||
(void)bc->SetEmbedderColorSchemes(schemes);
|
||||
}
|
||||
|
||||
void nsSubDocumentFrame::MaybeUpdateEmbedderScrollbarInset() {
|
||||
nsFrameLoader* fl = mFrameLoader.get();
|
||||
if (!fl) {
|
||||
return;
|
||||
}
|
||||
|
||||
BrowsingContext* bc = fl->GetExtantBrowsingContext();
|
||||
if (!bc) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The content document can't express this in CSS: its viewport scroll frame's
|
||||
// style is a non-inheriting anonymous box. Forward our own
|
||||
// -moz-scrollbar-inset-{block,inline} instead, resolved to physical sides in
|
||||
// our writing mode and converted to device pixels, since the subdocument's
|
||||
// writing mode and zoom are both its own.
|
||||
const WritingMode wm = GetWritingMode();
|
||||
nsPresContext* pc = PresContext();
|
||||
const nsMargin physical =
|
||||
StyleDisplay()->GetScrollbarInset(wm).GetPhysicalMargin(wm);
|
||||
|
||||
const LayoutDeviceIntMargin inset = LayoutDevicePixel::FromAppUnitsRounded(
|
||||
physical, pc->AppUnitsPerDevPixel());
|
||||
if (bc->GetEmbedderScrollbarInset() == inset) {
|
||||
return;
|
||||
}
|
||||
|
||||
(void)bc->SetEmbedderScrollbarInset(inset);
|
||||
}
|
||||
|
||||
void nsSubDocumentFrame::MaybeUpdateEmbedderZoom() {
|
||||
nsFrameLoader* fl = mFrameLoader.get();
|
||||
if (!fl) {
|
||||
@@ -850,6 +881,7 @@ void nsSubDocumentFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle) {
|
||||
// If there's no old style, the call in Init() or ShowViewer() should have
|
||||
// us covered.
|
||||
MaybeUpdateEmbedderColorScheme();
|
||||
MaybeUpdateEmbedderScrollbarInset();
|
||||
MaybeUpdateRemoteStyle(aOldComputedStyle);
|
||||
if (aOldComputedStyle->EffectiveZoom() != Style()->EffectiveZoom()) {
|
||||
MaybeUpdateEmbedderZoom();
|
||||
|
||||
@@ -160,6 +160,7 @@ class nsSubDocumentFrame final : public nsAtomicContainerFrame,
|
||||
friend class AsyncFrameInit;
|
||||
|
||||
void MaybeUpdateEmbedderColorScheme();
|
||||
void MaybeUpdateEmbedderScrollbarInset();
|
||||
void MaybeUpdateEmbedderZoom();
|
||||
void MaybeUpdateRemoteStyle(ComputedStyle* aOldComputedStyle = nullptr);
|
||||
void PropagateIsUnderHiddenEmbedderElement(bool aValue);
|
||||
|
||||
@@ -1,3 +1,40 @@
|
||||
== viewport-scrollbar-color-change.html viewport-scrollbar-color-change-ref.html
|
||||
== scrollbar-thin-overflow-change.html scrollbar-thin-overflow-change-ref.html
|
||||
test-pref(layout.css.scrollbar-width-thin.disabled,true) == scrollbar-width-thin.html scrollbar-width-auto.html
|
||||
# -moz-scrollbar-inset-{block,inline} are chrome-only, so these load over
|
||||
# chrome://.
|
||||
# Overlay scrollbars are forced off so the thumb is actually painted.
|
||||
pref(ui.useOverlayScrollbars,0) == chrome://reftest/content/css-scrollbars/scrollbar-inset-change.html chrome://reftest/content/css-scrollbars/scrollbar-inset-change-ref.html
|
||||
# An embedder's inset must reach the subdocument's viewport scrollbar. The !=
|
||||
# assertion fails if the forwarding does nothing, since the two iframes are
|
||||
# otherwise identical.
|
||||
pref(ui.useOverlayScrollbars,0) != chrome://reftest/content/css-scrollbars/scrollbar-inset-forward.html chrome://reftest/content/css-scrollbars/scrollbar-inset-forward-notref.html
|
||||
pref(ui.useOverlayScrollbars,0) == chrome://reftest/content/css-scrollbars/scrollbar-inset-forward-change.html chrome://reftest/content/css-scrollbars/scrollbar-inset-forward.html
|
||||
# The inset is physical, so full-zooming the subdocument must not change it. The
|
||||
# fuzz is the thumb snapping to device pixels at a different app-unit
|
||||
# granularity; a CSS-px inset would be out by ~600 pixels, not 40. Verified: the
|
||||
# same test at fullZoom 1.0 matches exactly.
|
||||
pref(ui.useOverlayScrollbars,0) fuzzy(0-255,0-40) == chrome://reftest/content/css-scrollbars/scrollbar-inset-zoom.html chrome://reftest/content/css-scrollbars/scrollbar-inset-forward.html
|
||||
# -moz-scrollbar-inset-inline must reach the horizontal scrollbar at all...
|
||||
pref(ui.useOverlayScrollbars,0) != chrome://reftest/content/css-scrollbars/scrollbar-inset-inline.html chrome://reftest/content/css-scrollbars/scrollbar-inset-inline-notref.html
|
||||
# ...and the logical start must resolve per writing mode, so an RTL inline-end
|
||||
# inset matches an LTR inline-start one. Needs the != above to mean something.
|
||||
pref(ui.useOverlayScrollbars,0) == chrome://reftest/content/css-scrollbars/scrollbar-inset-rtl.html chrome://reftest/content/css-scrollbars/scrollbar-inset-inline.html
|
||||
# An inset that holds the slider clear of the corner means GetBorderRadii no
|
||||
# longer has to square that corner off, so the ring matches a container with no
|
||||
# scrollbar at all. The cover hides the padding box, leaving only the ring.
|
||||
pref(ui.useOverlayScrollbars,0) == chrome://reftest/content/css-scrollbars/scrollbar-inset-radii.html chrome://reftest/content/css-scrollbars/scrollbar-inset-radii-ref.html
|
||||
# The same, but applying the inset dynamically, so the corner has to go back to
|
||||
# being round after having been squared off.
|
||||
pref(ui.useOverlayScrollbars,0) == chrome://reftest/content/css-scrollbars/scrollbar-inset-radii-change.html chrome://reftest/content/css-scrollbars/scrollbar-inset-radii-ref.html
|
||||
# Without an inset the corner must still be squared off. This also catches the
|
||||
# == above passing for free if the scrollbar ever stops taking up layout space.
|
||||
pref(ui.useOverlayScrollbars,0) != chrome://reftest/content/css-scrollbars/scrollbar-inset-radii-notref.html chrome://reftest/content/css-scrollbars/scrollbar-inset-radii-ref.html
|
||||
# The corner GetBorderRadii spares must be picked per writing mode, so an RTL
|
||||
# inline-end inset spares the same physical corner as an LTR inline-start one.
|
||||
# Uses the inline axis because the block axis doesn't flip in RTL at all, and an
|
||||
# asymmetric inset because a symmetric one is its own mirror image.
|
||||
pref(ui.useOverlayScrollbars,0) == chrome://reftest/content/css-scrollbars/scrollbar-inset-radii-rtl.html chrome://reftest/content/css-scrollbars/scrollbar-inset-radii-inline.html
|
||||
# The two ends must be distinguishable for the == above to mean anything: swap
|
||||
# the inset and the opposite corner is spared instead.
|
||||
pref(ui.useOverlayScrollbars,0) != chrome://reftest/content/css-scrollbars/scrollbar-inset-radii-inline-notref.html chrome://reftest/content/css-scrollbars/scrollbar-inset-radii-inline.html
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<!DOCTYPE html>
|
||||
<style>
|
||||
#outer {
|
||||
overflow-y: scroll;
|
||||
width: 200px;
|
||||
height: 300px;
|
||||
background: white;
|
||||
-moz-scrollbar-inset-block: 40px 0;
|
||||
}
|
||||
#inner {
|
||||
height: 900px;
|
||||
}
|
||||
</style>
|
||||
<div id="outer">
|
||||
<div id="inner"></div>
|
||||
</div>
|
||||
@@ -0,0 +1,27 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- Loaded from chrome:// so the chrome-only -moz-scrollbar-inset-block parses.
|
||||
Applies the inset dynamically after a forced reflow; the reference declares
|
||||
the final value up front. If changing -moz-scrollbar-inset-block doesn't reflow
|
||||
the scrollbar, the thumb keeps its old travel range and this won't match. -->
|
||||
<style>
|
||||
#outer {
|
||||
overflow-y: scroll;
|
||||
width: 200px;
|
||||
height: 300px;
|
||||
background: white;
|
||||
}
|
||||
#outer.inset {
|
||||
-moz-scrollbar-inset-block: 40px 0;
|
||||
}
|
||||
#inner {
|
||||
height: 900px;
|
||||
}
|
||||
</style>
|
||||
<div id="outer">
|
||||
<div id="inner"></div>
|
||||
</div>
|
||||
<script>
|
||||
// Force a reflow so the un-inset scrollbar geometry is really computed.
|
||||
document.body.offsetHeight;
|
||||
document.getElementById("outer").classList.add("inset");
|
||||
</script>
|
||||
@@ -0,0 +1,29 @@
|
||||
<!DOCTYPE html>
|
||||
<html class="reftest-wait">
|
||||
<!-- Applies the embedder's inset after the subdocument has loaded and laid out.
|
||||
Matches the statically-inset reference only if the change propagates and
|
||||
reflows the subdocument's scrollbar, i.e. SetEmbedderScrollbarInset ->
|
||||
MarkScrollbarsDirtyForReflow. Uses a class toggle because CSSOM
|
||||
setProperty() is a silent no-op for chrome-only properties. -->
|
||||
<style>
|
||||
iframe {
|
||||
width: 200px;
|
||||
height: 300px;
|
||||
border: none;
|
||||
}
|
||||
iframe.inset {
|
||||
-moz-scrollbar-inset-block: 40px 0;
|
||||
}
|
||||
</style>
|
||||
<iframe src="scrollbar-inset-forward-inner.html"></iframe>
|
||||
<script>
|
||||
const iframe = document.querySelector("iframe");
|
||||
iframe.addEventListener("load", () => {
|
||||
// Force the un-inset scrollbar geometry to be computed first.
|
||||
iframe.contentDocument.documentElement.offsetHeight;
|
||||
iframe.classList.add("inset");
|
||||
requestAnimationFrame(() =>
|
||||
requestAnimationFrame(() =>
|
||||
document.documentElement.removeAttribute("class")));
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,17 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- Inner document for the -moz-scrollbar-inset-block forwarding tests. Scrolls
|
||||
vertically so its viewport scrollbar is present and painted. -->
|
||||
<style>
|
||||
html {
|
||||
background: white;
|
||||
scrollbar-color: black white;
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
/* Sized in vh, not px, so the scroll ratio - and therefore the thumb's
|
||||
size in device pixels - is the same at any full zoom. That leaves the
|
||||
inset as the only variable in the zoom test. */
|
||||
height: 300vh;
|
||||
}
|
||||
</style>
|
||||
<body></body>
|
||||
@@ -0,0 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<style>
|
||||
iframe {
|
||||
width: 200px;
|
||||
height: 300px;
|
||||
border: none;
|
||||
}
|
||||
</style>
|
||||
<iframe src="scrollbar-inset-forward-inner.html"></iframe>
|
||||
@@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- -moz-scrollbar-inset-block on an embedder must reach the subdocument's viewport
|
||||
scrollbar, which can't carry the property itself. Compared with != against
|
||||
an identical iframe that has no inset: if the forwarding through
|
||||
nsSubDocumentFrame -> BrowsingContext -> nsPresContext is broken, the two
|
||||
render identically and this fails. -->
|
||||
<style>
|
||||
iframe {
|
||||
width: 200px;
|
||||
height: 300px;
|
||||
border: none;
|
||||
-moz-scrollbar-inset-block: 40px 0;
|
||||
}
|
||||
</style>
|
||||
<iframe src="scrollbar-inset-forward-inner.html"></iframe>
|
||||
@@ -0,0 +1,16 @@
|
||||
<!DOCTYPE html>
|
||||
<style>
|
||||
#outer {
|
||||
overflow-x: scroll;
|
||||
overflow-y: hidden;
|
||||
width: 300px;
|
||||
height: 100px;
|
||||
background: white;
|
||||
scrollbar-color: black white;
|
||||
}
|
||||
#inner {
|
||||
width: 900px;
|
||||
height: 20px;
|
||||
}
|
||||
</style>
|
||||
<div id="outer"><div id="inner"></div></div>
|
||||
@@ -0,0 +1,19 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- -moz-scrollbar-inset-inline must reach the horizontal scrollbar. Compared
|
||||
with != against an identical container that has no inset. -->
|
||||
<style>
|
||||
#outer {
|
||||
overflow-x: scroll;
|
||||
overflow-y: hidden;
|
||||
width: 300px;
|
||||
height: 100px;
|
||||
background: white;
|
||||
scrollbar-color: black white;
|
||||
-moz-scrollbar-inset-inline: 40px 0;
|
||||
}
|
||||
#inner {
|
||||
width: 900px;
|
||||
height: 20px;
|
||||
}
|
||||
</style>
|
||||
<div id="outer"><div id="inner"></div></div>
|
||||
@@ -0,0 +1,48 @@
|
||||
<!DOCTYPE HTML>
|
||||
<title>Applying an inset dynamically un-squares the corner</title>
|
||||
<!-- Starts with a scrollbar and no inset, so GetBorderRadii squares the right
|
||||
corners off, then applies the inset after a forced reflow. Matches the
|
||||
no-scrollbar reference only if the corners go back to being round, which
|
||||
needs the radii to be recomputed and the result repainted. -->
|
||||
<style>
|
||||
|
||||
.contain { position: relative; height: 140px }
|
||||
|
||||
.test {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 200px;
|
||||
height: 100px;
|
||||
border: 10px solid blue;
|
||||
border-radius: 20px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.test.inset {
|
||||
-moz-scrollbar-inset-block: 30px;
|
||||
}
|
||||
|
||||
/* Covers the whole padding box, so only the border ring is compared and the
|
||||
scrollbar itself can't contribute pixels. */
|
||||
.cover {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
width: 200px;
|
||||
height: 100px;
|
||||
background: blue;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<div class="contain">
|
||||
<div class="test"></div>
|
||||
<div class="cover"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Force a reflow so the squared-off corners are really computed first.
|
||||
document.body.offsetHeight;
|
||||
document.querySelector(".test").classList.add("inset");
|
||||
</script>
|
||||
@@ -0,0 +1,36 @@
|
||||
<!DOCTYPE HTML>
|
||||
<title>The mirrored inset keeps the bottom-right corner instead</title>
|
||||
<style>
|
||||
|
||||
.contain { position: relative; height: 140px }
|
||||
|
||||
.test {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 200px;
|
||||
height: 100px;
|
||||
border: 10px solid blue;
|
||||
border-radius: 20px;
|
||||
overflow-x: scroll;
|
||||
overflow-y: hidden;
|
||||
-moz-scrollbar-inset-inline: 0 30px;
|
||||
}
|
||||
|
||||
/* Covers the whole padding box, so only the border ring is compared and the
|
||||
scrollbar itself can't contribute pixels. */
|
||||
.cover {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
width: 200px;
|
||||
height: 100px;
|
||||
background: blue;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<div class="contain">
|
||||
<div class="test"></div>
|
||||
<div class="cover"></div>
|
||||
</div>
|
||||
@@ -0,0 +1,36 @@
|
||||
<!DOCTYPE HTML>
|
||||
<title>An inline-start inset keeps the bottom-left corner's radius</title>
|
||||
<style>
|
||||
|
||||
.contain { position: relative; height: 140px }
|
||||
|
||||
.test {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 200px;
|
||||
height: 100px;
|
||||
border: 10px solid blue;
|
||||
border-radius: 20px;
|
||||
overflow-x: scroll;
|
||||
overflow-y: hidden;
|
||||
-moz-scrollbar-inset-inline: 30px 0;
|
||||
}
|
||||
|
||||
/* Covers the whole padding box, so only the border ring is compared and the
|
||||
scrollbar itself can't contribute pixels. */
|
||||
.cover {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
width: 200px;
|
||||
height: 100px;
|
||||
background: blue;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<div class="contain">
|
||||
<div class="test"></div>
|
||||
<div class="cover"></div>
|
||||
</div>
|
||||
@@ -0,0 +1,35 @@
|
||||
<!DOCTYPE HTML>
|
||||
<title>Without an inset the corner is squared off</title>
|
||||
<style>
|
||||
|
||||
.contain { position: relative; height: 140px }
|
||||
|
||||
.test {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 200px;
|
||||
height: 100px;
|
||||
border: 10px solid blue;
|
||||
border-radius: 20px;
|
||||
overflow-y: scroll;
|
||||
-moz-scrollbar-inset-block: 0;
|
||||
}
|
||||
|
||||
/* Covers the whole padding box, so only the border ring is compared and the
|
||||
scrollbar itself can't contribute pixels. */
|
||||
.cover {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
width: 200px;
|
||||
height: 100px;
|
||||
background: blue;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<div class="contain">
|
||||
<div class="test"></div>
|
||||
<div class="cover"></div>
|
||||
</div>
|
||||
@@ -0,0 +1,34 @@
|
||||
<!DOCTYPE HTML>
|
||||
<title>No scrollbar, so the radius is never reduced</title>
|
||||
<style>
|
||||
|
||||
.contain { position: relative; height: 140px }
|
||||
|
||||
.test {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 200px;
|
||||
height: 100px;
|
||||
border: 10px solid blue;
|
||||
border-radius: 20px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Covers the whole padding box, so only the border ring is compared and the
|
||||
scrollbar itself can't contribute pixels. */
|
||||
.cover {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
width: 200px;
|
||||
height: 100px;
|
||||
background: blue;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<div class="contain">
|
||||
<div class="test"></div>
|
||||
<div class="cover"></div>
|
||||
</div>
|
||||
@@ -0,0 +1,40 @@
|
||||
<!DOCTYPE HTML>
|
||||
<title>In RTL the inline-end inset is the one that keeps the bottom-left corner</title>
|
||||
<style>
|
||||
|
||||
.contain { position: relative; height: 140px }
|
||||
|
||||
/* Same physical result as -moz-scrollbar-inset-inline: 30px 0 in LTR, because
|
||||
RTL puts the inline end on the left. A second writing-mode swap anywhere
|
||||
between here and GetBorderRadii would protect the right corner instead. */
|
||||
.test {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 200px;
|
||||
height: 100px;
|
||||
border: 10px solid blue;
|
||||
border-radius: 20px;
|
||||
overflow-x: scroll;
|
||||
overflow-y: hidden;
|
||||
direction: rtl;
|
||||
-moz-scrollbar-inset-inline: 0 30px;
|
||||
}
|
||||
|
||||
/* Covers the whole padding box, so only the border ring is compared and the
|
||||
scrollbar itself can't contribute pixels. */
|
||||
.cover {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
width: 200px;
|
||||
height: 100px;
|
||||
background: blue;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<div class="contain">
|
||||
<div class="test"></div>
|
||||
<div class="cover"></div>
|
||||
</div>
|
||||
@@ -0,0 +1,35 @@
|
||||
<!DOCTYPE HTML>
|
||||
<title>An inset that clears the corner keeps the border-radius</title>
|
||||
<style>
|
||||
|
||||
.contain { position: relative; height: 140px }
|
||||
|
||||
.test {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 200px;
|
||||
height: 100px;
|
||||
border: 10px solid blue;
|
||||
border-radius: 20px;
|
||||
overflow-y: scroll;
|
||||
-moz-scrollbar-inset-block: 30px;
|
||||
}
|
||||
|
||||
/* Covers the whole padding box, so only the border ring is compared and the
|
||||
scrollbar itself can't contribute pixels. */
|
||||
.cover {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
width: 200px;
|
||||
height: 100px;
|
||||
background: blue;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<div class="contain">
|
||||
<div class="test"></div>
|
||||
<div class="cover"></div>
|
||||
</div>
|
||||
@@ -0,0 +1,32 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- The inset is logical, so in RTL the inline-start end is the physical right.
|
||||
This declares the inset on the inline-END, which in RTL resolves to the
|
||||
physical LEFT - the same physical result as the LTR reference declaring it on
|
||||
the inline-start. Matches only if nsScrollbarFrame maps the logical start onto
|
||||
a physical side and swaps the pair; without that swap the inset lands on the
|
||||
right here and this fails.
|
||||
|
||||
scrollLeft is normalised so both documents have the thumb at the same end;
|
||||
RTL otherwise starts scrolled to the opposite end from LTR. -->
|
||||
<style>
|
||||
#outer {
|
||||
direction: rtl;
|
||||
overflow-x: scroll;
|
||||
overflow-y: hidden;
|
||||
width: 300px;
|
||||
height: 100px;
|
||||
background: white;
|
||||
scrollbar-color: black white;
|
||||
-moz-scrollbar-inset-inline: 0 40px;
|
||||
}
|
||||
#inner {
|
||||
width: 900px;
|
||||
height: 20px;
|
||||
}
|
||||
</style>
|
||||
<div id="outer"><div id="inner"></div></div>
|
||||
<script>
|
||||
// Scroll fully to the physical left in both writing modes.
|
||||
const outer = document.getElementById("outer");
|
||||
outer.scrollLeft = -(outer.scrollWidth - outer.clientWidth);
|
||||
</script>
|
||||
@@ -0,0 +1,27 @@
|
||||
<!DOCTYPE html>
|
||||
<html class="reftest-wait">
|
||||
<!-- The forwarded inset must be a fixed physical size, so full-zooming the
|
||||
subdocument must not change it. The reference is the same iframe at zoom 1.
|
||||
|
||||
This only isolates the inset because the subdocument is sized in vh: the
|
||||
thumb's device-pixel size and the scrollbar's thickness are already
|
||||
zoom-invariant. If the inset were carried in CSS pixels instead of device
|
||||
pixels it would double at zoom 2 and the thumb would start lower. -->
|
||||
<style>
|
||||
iframe {
|
||||
width: 200px;
|
||||
height: 300px;
|
||||
border: none;
|
||||
-moz-scrollbar-inset-block: 40px 0;
|
||||
}
|
||||
</style>
|
||||
<iframe src="scrollbar-inset-forward-inner.html"></iframe>
|
||||
<script>
|
||||
const iframe = document.querySelector("iframe");
|
||||
iframe.addEventListener("load", () => {
|
||||
iframe.frameLoader.browsingContext.fullZoom = 2.0;
|
||||
requestAnimationFrame(() =>
|
||||
requestAnimationFrame(() =>
|
||||
document.documentElement.removeAttribute("class")));
|
||||
});
|
||||
</script>
|
||||
@@ -582,6 +582,8 @@ def gen_css_properties_js(output):
|
||||
"-moz-box-collapse", # chrome-only internal properties
|
||||
"-moz-line-scroll-amount", # chrome-only internal properties
|
||||
"-moz-image-decoding", # chrome-only internal properties
|
||||
"-moz-scrollbar-inset-block", # chrome-only internal properties
|
||||
"-moz-scrollbar-inset-inline", # chrome-only internal properties
|
||||
"-moz-subtree-hidden-only-visually", # chrome-only internal properties
|
||||
"-moz-user-focus", # chrome-only internal properties
|
||||
"-moz-window-input-region-margin", # chrome-only internal properties
|
||||
|
||||
@@ -444,6 +444,7 @@ cbindgen-types = [
|
||||
{ gecko = "StyleViewTransitionName", servo = "crate::values::computed::ViewTransitionName" },
|
||||
{ gecko = "StyleResize", servo = "crate::values::computed::Resize" },
|
||||
{ gecko = "StyleOverflowClipMargin", servo = "crate::values::computed::OverflowClipMargin" },
|
||||
{ gecko = "StyleScrollbarInset", servo = "crate::values::computed::ScrollbarInset" },
|
||||
{ gecko = "StyleFloat", servo = "crate::values::computed::Float" },
|
||||
{ gecko = "StyleClear", servo = "crate::values::computed::Clear" },
|
||||
{ gecko = "StyleOverscrollBehavior", servo = "crate::values::computed::OverscrollBehavior" },
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsCRTGlue.h"
|
||||
#include "nsCSSProps.h"
|
||||
#include "nsChangeHint.h"
|
||||
#include "nsContainerFrame.h"
|
||||
#include "nsDeviceContext.h"
|
||||
#include "nsIURI.h"
|
||||
@@ -2264,6 +2265,8 @@ nsStyleDisplay::nsStyleDisplay()
|
||||
mScrollSnapStop{StyleScrollSnapStop::Normal},
|
||||
mScrollSnapType{StyleScrollSnapAxis::Both,
|
||||
StyleScrollSnapStrictness::None},
|
||||
mScrollbarInsetBlock{StyleLength{0.}, StyleLength{0.}},
|
||||
mScrollbarInsetInline{StyleLength{0.}, StyleLength{0.}},
|
||||
mBackfaceVisibility(StyleBackfaceVisibility::Visible),
|
||||
mTransformStyle(StyleTransformStyle::Flat),
|
||||
mTransformBox(StyleTransformBox::ViewBox),
|
||||
@@ -2323,6 +2326,8 @@ nsStyleDisplay::nsStyleDisplay(const nsStyleDisplay& aSource)
|
||||
mScrollSnapAlign(aSource.mScrollSnapAlign),
|
||||
mScrollSnapStop(aSource.mScrollSnapStop),
|
||||
mScrollSnapType(aSource.mScrollSnapType),
|
||||
mScrollbarInsetBlock(aSource.mScrollbarInsetBlock),
|
||||
mScrollbarInsetInline(aSource.mScrollbarInsetInline),
|
||||
mBackfaceVisibility(aSource.mBackfaceVisibility),
|
||||
mTransformStyle(aSource.mTransformStyle),
|
||||
mTransformBox(aSource.mTransformBox),
|
||||
@@ -2736,6 +2741,10 @@ nsChangeHint nsStyleDisplay::CalcDifference(
|
||||
// container-query selection on descendants).
|
||||
// container-type / contain / content-visibility are handled by the
|
||||
// mEffectiveContainment check.
|
||||
//
|
||||
// scrollbar-inset changes are dealt with in
|
||||
// nsSubDocumentFrame::DidSetComputedStyle and
|
||||
// ScrollContainerFrame::DidSetComputedStyle.
|
||||
if (!hint && (mWillChange != aNewData.mWillChange ||
|
||||
mOverflowAnchor != aNewData.mOverflowAnchor ||
|
||||
mContentVisibility != aNewData.mContentVisibility ||
|
||||
@@ -2743,7 +2752,9 @@ nsChangeHint nsStyleDisplay::CalcDifference(
|
||||
mContain != aNewData.mContain ||
|
||||
mContainerName != aNewData.mContainerName ||
|
||||
mAnchorName != aNewData.mAnchorName ||
|
||||
mAnchorScope != aNewData.mAnchorScope)) {
|
||||
mAnchorScope != aNewData.mAnchorScope ||
|
||||
mScrollbarInsetBlock != aNewData.mScrollbarInsetBlock ||
|
||||
mScrollbarInsetInline != aNewData.mScrollbarInsetInline)) {
|
||||
hint |= nsChangeHint_NeutralChange;
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ class ComputedStyle;
|
||||
struct AnchorPosResolutionCache;
|
||||
class AnchorPosReferenceData;
|
||||
struct IntrinsicSize;
|
||||
class LogicalMargin;
|
||||
struct SizeComputationInput;
|
||||
|
||||
} // namespace mozilla
|
||||
@@ -1662,6 +1663,8 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleDisplay {
|
||||
mozilla::StyleScrollSnapAlign mScrollSnapAlign;
|
||||
mozilla::StyleScrollSnapStop mScrollSnapStop;
|
||||
mozilla::StyleScrollSnapType mScrollSnapType;
|
||||
mozilla::StyleScrollbarInset mScrollbarInsetBlock;
|
||||
mozilla::StyleScrollbarInset mScrollbarInsetInline;
|
||||
|
||||
mozilla::StyleBackfaceVisibility mBackfaceVisibility;
|
||||
mozilla::StyleTransformStyle mTransformStyle;
|
||||
@@ -1848,6 +1851,12 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleDisplay {
|
||||
mOverflowY == mozilla::StyleOverflow::Visible;
|
||||
}
|
||||
|
||||
// How much to shorten each scrollbar by at each of its ends. Each side holds
|
||||
// the inset measured from it, so the block-axis pair shortens the scrollbar
|
||||
// running along the block axis. Defined in WritingModes.h.
|
||||
inline mozilla::LogicalMargin GetScrollbarInset(
|
||||
mozilla::WritingMode aWM) const;
|
||||
|
||||
bool IsContainPaint() const {
|
||||
// Short circuit for no containment whatsoever
|
||||
if (!mEffectiveContainment) {
|
||||
|
||||
@@ -13899,6 +13899,24 @@ if (false) {
|
||||
"2px",
|
||||
],
|
||||
};
|
||||
|
||||
gCSSProperties["-moz-scrollbar-inset-block"] = {
|
||||
// domProp: "MozScrollbarInsetBlock",
|
||||
inherited: false,
|
||||
type: CSS_TYPE_LONGHAND,
|
||||
initial_values: ["0", "0 0"],
|
||||
other_values: ["1px 2px", "calc(2em + 3ex)", "1px calc(2em)"],
|
||||
invalid_values: ["auto", "20%", "-10px", "1px 2px 3px", "1px 2px 3px 4px"],
|
||||
};
|
||||
|
||||
gCSSProperties["-moz-scrollbar-inset-inline"] = {
|
||||
// domProp: "MozScrollbarInsetInline",
|
||||
inherited: false,
|
||||
type: CSS_TYPE_LONGHAND,
|
||||
initial_values: ["0", "0 0"],
|
||||
other_values: ["1px 2px", "calc(2em + 3ex)", "1px calc(2em)"],
|
||||
invalid_values: ["auto", "20%", "-10px", "1px 2px 3px", "1px 2px 3px 4px"],
|
||||
};
|
||||
}
|
||||
|
||||
gCSSProperties["scrollbar-color"] = {
|
||||
|
||||
@@ -9,6 +9,7 @@ reftest.jar:
|
||||
content/bugs (../../reftests/bugs/*)
|
||||
content/image (../../reftests/image/*)
|
||||
content/css-display (../../reftests/css-display/*)
|
||||
content/css-scrollbars (../../reftests/css-scrollbars/*)
|
||||
content/fonts (../../reftests/fonts/*)
|
||||
content/forms/input/file (../../reftests/forms/input/file/*)
|
||||
content/forms/input/text (../../reftests/forms/input/text/*)
|
||||
|
||||
@@ -124,6 +124,18 @@ void nsScrollbarFrame::Destroy(DestroyContext& aContext) {
|
||||
nsContainerFrame::Destroy(aContext);
|
||||
}
|
||||
|
||||
std::pair<nscoord, nscoord> nsScrollbarFrame::ScrollbarInset() const {
|
||||
// The scroll container owns the property and resolves the writing mode, so
|
||||
// the margin it hands back is already physical and needs no further mapping.
|
||||
ScrollContainerFrame* scrollContainerFrame = do_QueryFrame(GetParent());
|
||||
if (!scrollContainerFrame) {
|
||||
return {0, 0};
|
||||
}
|
||||
const nsMargin inset = scrollContainerFrame->ScrollbarInsets();
|
||||
return IsHorizontal() ? std::pair{inset.left, inset.right}
|
||||
: std::pair{inset.top, inset.bottom};
|
||||
}
|
||||
|
||||
void nsScrollbarFrame::Reflow(nsPresContext* aPresContext,
|
||||
ReflowOutput& aDesiredSize,
|
||||
const ReflowInput& aReflowInput,
|
||||
@@ -152,11 +164,22 @@ void nsScrollbarFrame::Reflow(nsPresContext* aPresContext,
|
||||
|
||||
const nsSize containerSize = aDesiredSize.PhysicalSize();
|
||||
const LogicalSize totalAvailSize = aDesiredSize.Size(wm);
|
||||
LogicalPoint nextKidPos(wm);
|
||||
|
||||
MOZ_ASSERT(!wm.IsVertical());
|
||||
const bool movesInInlineDirection = horizontal;
|
||||
|
||||
// We stay full-length so our track still spans the whole scrollport, and hold
|
||||
// the slider back at each end instead. Unlike a scrollbar button the two ends
|
||||
// are independent, so the symmetry assumption below doesn't apply to them.
|
||||
const auto [insetStart, insetEnd] = ScrollbarInset();
|
||||
|
||||
LogicalPoint nextKidPos(wm);
|
||||
if (movesInInlineDirection) {
|
||||
nextKidPos.I(wm) = insetStart;
|
||||
} else {
|
||||
nextKidPos.B(wm) = insetStart;
|
||||
}
|
||||
|
||||
// Layout our kids left to right / top to bottom.
|
||||
for (nsIFrame* kid : mFrames) {
|
||||
MOZ_ASSERT(!kid->GetWritingMode().IsOrthogonalTo(wm),
|
||||
@@ -164,16 +187,18 @@ void nsScrollbarFrame::Reflow(nsPresContext* aPresContext,
|
||||
const bool isSlider = kid->GetContent() == mSlider;
|
||||
LogicalSize availSize = totalAvailSize;
|
||||
{
|
||||
const nscoord consumed =
|
||||
movesInInlineDirection ? nextKidPos.I(wm) : nextKidPos.B(wm);
|
||||
// Assume we'll consume the same size before and after the slider. This is
|
||||
// not a technically correct assumption if we have weird scrollbar button
|
||||
// setups, but those will be going away, see bug 1824254.
|
||||
const int32_t factor = isSlider ? 2 : 1;
|
||||
const nscoord reserved =
|
||||
isSlider ? (consumed - insetStart) * 2 + insetStart + insetEnd
|
||||
: consumed;
|
||||
if (movesInInlineDirection) {
|
||||
availSize.ISize(wm) =
|
||||
std::max(0, totalAvailSize.ISize(wm) - nextKidPos.I(wm) * factor);
|
||||
availSize.ISize(wm) = std::max(0, totalAvailSize.ISize(wm) - reserved);
|
||||
} else {
|
||||
availSize.BSize(wm) =
|
||||
std::max(0, totalAvailSize.BSize(wm) - nextKidPos.B(wm) * factor);
|
||||
availSize.BSize(wm) = std::max(0, totalAvailSize.BSize(wm) - reserved);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -68,6 +68,8 @@ class nsScrollbarFrame final : public nsContainerFrame,
|
||||
nsSize ScrollbarMinSize() const;
|
||||
bool IsHorizontal() const;
|
||||
|
||||
std::pair<nscoord, nscoord> ScrollbarInset() const;
|
||||
|
||||
void Destroy(DestroyContext&) override;
|
||||
|
||||
void Init(nsIContent* aContent, nsContainerFrame* aParent,
|
||||
|
||||
@@ -4168,6 +4168,30 @@ animation_type = "discrete"
|
||||
affects = "layout"
|
||||
keyword = { values = ["auto", "thin", "none"] }
|
||||
|
||||
# Reserves space at each end of a vertical scrollbar.
|
||||
[-moz-scrollbar-inset-block]
|
||||
type = "ScrollbarInset"
|
||||
initial = "computed::ScrollbarInset::zero()"
|
||||
struct = "box"
|
||||
engine = "gecko"
|
||||
animation_type = "discrete"
|
||||
enabled_in = "chrome"
|
||||
gecko_ffi_name = "mScrollbarInsetBlock"
|
||||
spec = "None (Nonstandard Firefox-only property)"
|
||||
affects = "layout"
|
||||
|
||||
# Reserves space at each end of a horizontal scrollbar.
|
||||
[-moz-scrollbar-inset-inline]
|
||||
type = "ScrollbarInset"
|
||||
initial = "computed::ScrollbarInset::zero()"
|
||||
struct = "box"
|
||||
engine = "gecko"
|
||||
animation_type = "discrete"
|
||||
enabled_in = "chrome"
|
||||
gecko_ffi_name = "mScrollbarInsetInline"
|
||||
spec = "None (Nonstandard Firefox-only property)"
|
||||
affects = "layout"
|
||||
|
||||
[-moz-window-dragging]
|
||||
struct = "ui"
|
||||
engine = "gecko"
|
||||
|
||||
@@ -10,7 +10,7 @@ use crate::values::computed::length::{LengthPercentage, NonNegativeLength};
|
||||
use crate::values::computed::{Context, Integer, Number, ToComputedValue};
|
||||
use crate::values::generics::box_::{
|
||||
GenericBaselineShift, GenericContainIntrinsicSize, GenericLineClamp, GenericOverflowClipMargin,
|
||||
GenericPerspective,
|
||||
GenericPerspective, GenericScrollbarInset,
|
||||
};
|
||||
use crate::values::generics::GreaterThanOrEqualToOne;
|
||||
use crate::values::specified::box_ as specified;
|
||||
@@ -31,6 +31,9 @@ pub type BaselineShift = GenericBaselineShift<LengthPercentage>;
|
||||
/// A computed value for the `overflow-clip-margin` property.
|
||||
pub type OverflowClipMargin = GenericOverflowClipMargin<NonNegativeLength>;
|
||||
|
||||
/// A computed value for the `-moz-scrollbar-inset-block` / `-inline` properties.
|
||||
pub type ScrollbarInset = GenericScrollbarInset<NonNegativeLength>;
|
||||
|
||||
/// A computed value for the `contain-intrinsic-size` property.
|
||||
pub type ContainIntrinsicSize = GenericContainIntrinsicSize<NonNegativeLength>;
|
||||
|
||||
|
||||
@@ -61,8 +61,8 @@ pub use self::box_::{
|
||||
Contain, ContainIntrinsicSize, ContainerName, ContainerType, ContentVisibility, Display,
|
||||
DominantBaseline, Float, LineClamp, MarginTrim, Overflow, OverflowAnchor, OverflowClipMargin,
|
||||
OverscrollBehavior, Perspective, PositionProperty, Resize, ScrollSnapAlign, ScrollSnapAxis,
|
||||
ScrollSnapStop, ScrollSnapStrictness, ScrollSnapType, ScrollbarGutter, TouchAction, WillChange,
|
||||
WritingModeProperty, Zoom,
|
||||
ScrollSnapStop, ScrollSnapStrictness, ScrollSnapType, ScrollbarGutter, ScrollbarInset,
|
||||
TouchAction, WillChange, WritingModeProperty, Zoom,
|
||||
};
|
||||
pub use self::color::{
|
||||
Color, ColorOrAuto, ColorPropertyValue, ColorScheme, ForcedColorAdjust, PrintColorAdjust,
|
||||
|
||||
@@ -500,3 +500,53 @@ impl<L: Zero + ToCss> ToCss for OverflowClipMargin<L> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// The two insets of one scrollbar, for the chrome-only
|
||||
/// `-moz-scrollbar-inset-block` / `-moz-scrollbar-inset-inline`. Each property
|
||||
/// names the axis the scrollbar runs along, so `start` and `end` are the logical
|
||||
/// start and end of that axis and flip with the writing mode.
|
||||
#[derive(
|
||||
Clone,
|
||||
Copy,
|
||||
Debug,
|
||||
MallocSizeOf,
|
||||
PartialEq,
|
||||
SpecifiedValueInfo,
|
||||
ToComputedValue,
|
||||
ToResolvedValue,
|
||||
ToShmem,
|
||||
ToTyped,
|
||||
)]
|
||||
#[repr(C)]
|
||||
pub struct GenericScrollbarInset<L> {
|
||||
/// The inset at the logical start of the axis.
|
||||
pub start: L,
|
||||
/// The inset at the logical end of the axis.
|
||||
pub end: L,
|
||||
}
|
||||
|
||||
pub use self::GenericScrollbarInset as ScrollbarInset;
|
||||
|
||||
impl<L: Zero> ScrollbarInset<L> {
|
||||
/// Returns the initial, all-zero value.
|
||||
pub fn zero() -> Self {
|
||||
Self {
|
||||
start: Zero::zero(),
|
||||
end: Zero::zero(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<L: PartialEq + ToCss> ToCss for ScrollbarInset<L> {
|
||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
||||
where
|
||||
W: Write,
|
||||
{
|
||||
self.start.to_css(dest)?;
|
||||
if self.end != self.start {
|
||||
dest.write_char(' ')?;
|
||||
self.end.to_css(dest)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,8 @@ use crate::properties::{LonghandId, PropertyDeclarationId, PropertyId};
|
||||
pub use crate::typed_om::{KeywordValue, ToTyped, TypedValue};
|
||||
use crate::values::generics::box_::{
|
||||
BaselineShiftKeyword, BlockEllipsis, GenericBaselineShift, GenericContainIntrinsicSize,
|
||||
GenericLineClamp, GenericOverflowClipMargin, GenericPerspective, MaxLines,
|
||||
OverflowClipMarginBox,
|
||||
GenericLineClamp, GenericOverflowClipMargin, GenericPerspective, GenericScrollbarInset,
|
||||
MaxLines, OverflowClipMarginBox,
|
||||
};
|
||||
use crate::values::specified::length::{LengthPercentage, NonNegativeLength};
|
||||
use crate::values::specified::{AllowQuirks, NonNegativeNumberOrPercentage, PositiveInteger};
|
||||
@@ -117,6 +117,20 @@ impl Parse for OverflowClipMargin {
|
||||
}
|
||||
}
|
||||
|
||||
/// The specified value of `-moz-scrollbar-inset-block` / `-inline`.
|
||||
pub type ScrollbarInset = GenericScrollbarInset<NonNegativeLength>;
|
||||
|
||||
impl Parse for ScrollbarInset {
|
||||
// <length>{1,2}
|
||||
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
|
||||
let start = NonNegativeLength::parse(context, input)?;
|
||||
let end = input
|
||||
.try_parse(|i| NonNegativeLength::parse(context, i))
|
||||
.unwrap_or_else(|_| start.clone());
|
||||
Ok(Self { start, end })
|
||||
}
|
||||
}
|
||||
|
||||
/// Defines an element’s display type, which consists of
|
||||
/// the two basic qualities of how an element generates boxes
|
||||
/// <https://drafts.csswg.org/css-display/#propdef-display>
|
||||
|
||||
@@ -45,8 +45,8 @@ pub use self::box_::{
|
||||
Contain, ContainIntrinsicSize, ContainerName, ContainerType, ContentVisibility, Display,
|
||||
DominantBaseline, Float, LineClamp, MarginTrim, Overflow, OverflowAnchor, OverflowClipMargin,
|
||||
OverscrollBehavior, Perspective, PositionProperty, Resize, ScrollSnapAlign, ScrollSnapAxis,
|
||||
ScrollSnapStop, ScrollSnapStrictness, ScrollSnapType, ScrollbarGutter, TouchAction, WillChange,
|
||||
WillChangeBits, WritingModeProperty, Zoom,
|
||||
ScrollSnapStop, ScrollSnapStrictness, ScrollSnapType, ScrollbarGutter, ScrollbarInset,
|
||||
TouchAction, WillChange, WillChangeBits, WritingModeProperty, Zoom,
|
||||
};
|
||||
pub use self::calc::{CalcLengthPercentage, CalcNumeric};
|
||||
pub use self::color::{
|
||||
|
||||
@@ -190,6 +190,7 @@ include = [
|
||||
"ViewTimelineInset",
|
||||
"OverflowAnchor",
|
||||
"OverflowClipMargin",
|
||||
"ScrollbarInset",
|
||||
"Resize",
|
||||
"Overflow",
|
||||
"LengthPercentage",
|
||||
|
||||
Reference in New Issue
Block a user