зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1305957 part 8 - Suppress scroll offset adjustment when some layout affecting properties are changed on scroll anchor or its ancestors. r=hiro
This commit implements the first half of the heuristics to detect style changes that could lead to feedback loops with scroll anchoring. [1] When these style changes are made, a suppression flag is added to the anchor container to ignore any adjustments that would be made at the next layout flush and to invalidate the anchor at that time. [1] https://drafts.csswg.org/css-scroll-anchoring/#suppression-triggers Differential Revision: https://phabricator.services.mozilla.com/D13271 --HG-- extra : rebase_source : db0bf373e51325b7ef537c4c245ede4b4219d0d7 extra : source : d9445a5f3458b560fbdb7aee5faabb5a7a9327f3
This commit is contained in:
Родитель
43e96a0c82
Коммит
43b7ef66cb
|
@ -21,7 +21,8 @@ ScrollAnchorContainer::ScrollAnchorContainer(ScrollFrameHelper* aScrollFrame)
|
|||
mAnchorNode(nullptr),
|
||||
mLastAnchorPos(0, 0),
|
||||
mAnchorNodeIsDirty(true),
|
||||
mApplyingAnchorAdjustment(false) {}
|
||||
mApplyingAnchorAdjustment(false),
|
||||
mSuppressAnchorAdjustment(false) {}
|
||||
|
||||
ScrollAnchorContainer::~ScrollAnchorContainer() {}
|
||||
|
||||
|
|
|
@ -71,6 +71,12 @@ class ScrollAnchorContainer final {
|
|||
*/
|
||||
void ApplyAdjustments();
|
||||
|
||||
/**
|
||||
* Notify the scroll anchor container that it should suppress any scroll
|
||||
* adjustment that may happen after the next layout flush.
|
||||
*/
|
||||
void SuppressAdjustments();
|
||||
|
||||
/**
|
||||
* Notify this scroll anchor container that its anchor node should be
|
||||
* invalidated and recomputed at the next available opportunity.
|
||||
|
@ -135,6 +141,8 @@ class ScrollAnchorContainer final {
|
|||
bool mAnchorNodeIsDirty : 1;
|
||||
// True if we are applying a scroll anchor adjustment
|
||||
bool mApplyingAnchorAdjustment : 1;
|
||||
// True if we should suppress anchor adjustments
|
||||
bool mSuppressAnchorAdjustment : 1;
|
||||
};
|
||||
|
||||
} // namespace layout
|
||||
|
|
|
@ -1053,6 +1053,11 @@ void nsIFrame::MarkNeedsDisplayItemRebuild() {
|
|||
AddAndRemoveImageAssociations(this, oldLayers, newLayers);
|
||||
|
||||
if (aOldComputedStyle) {
|
||||
// Detect style changes that should trigger a scroll anchor adjustment
|
||||
// suppression.
|
||||
// https://drafts.csswg.org/css-scroll-anchoring/#suppression-triggers
|
||||
bool needAnchorSuppression = false;
|
||||
|
||||
// If we detect a change on margin, padding or border, we store the old
|
||||
// values on the frame itself between now and reflow, so if someone
|
||||
// calls GetUsed(Margin|Border|Padding)() before the next reflow, we
|
||||
|
@ -1062,17 +1067,21 @@ void nsIFrame::MarkNeedsDisplayItemRebuild() {
|
|||
nsMargin newValue(0, 0, 0, 0);
|
||||
const nsStyleMargin* oldMargin = aOldComputedStyle->PeekStyleMargin();
|
||||
if (oldMargin && oldMargin->GetMargin(oldValue)) {
|
||||
if ((!StyleMargin()->GetMargin(newValue) || oldValue != newValue) &&
|
||||
!HasProperty(UsedMarginProperty())) {
|
||||
AddProperty(UsedMarginProperty(), new nsMargin(oldValue));
|
||||
if (!StyleMargin()->GetMargin(newValue) || oldValue != newValue) {
|
||||
if (!HasProperty(UsedMarginProperty())) {
|
||||
AddProperty(UsedMarginProperty(), new nsMargin(oldValue));
|
||||
}
|
||||
needAnchorSuppression = true;
|
||||
}
|
||||
}
|
||||
|
||||
const nsStylePadding* oldPadding = aOldComputedStyle->PeekStylePadding();
|
||||
if (oldPadding && oldPadding->GetPadding(oldValue)) {
|
||||
if ((!StylePadding()->GetPadding(newValue) || oldValue != newValue) &&
|
||||
!HasProperty(UsedPaddingProperty())) {
|
||||
AddProperty(UsedPaddingProperty(), new nsMargin(oldValue));
|
||||
if (!StylePadding()->GetPadding(newValue) || oldValue != newValue) {
|
||||
if (!HasProperty(UsedPaddingProperty())) {
|
||||
AddProperty(UsedPaddingProperty(), new nsMargin(oldValue));
|
||||
}
|
||||
needAnchorSuppression = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1084,6 +1093,31 @@ void nsIFrame::MarkNeedsDisplayItemRebuild() {
|
|||
AddProperty(UsedBorderProperty(), new nsMargin(oldValue));
|
||||
}
|
||||
}
|
||||
|
||||
if (mInScrollAnchorChain) {
|
||||
const nsStylePosition* oldPosition =
|
||||
aOldComputedStyle->PeekStylePosition();
|
||||
if (oldPosition &&
|
||||
(oldPosition->mOffset != StylePosition()->mOffset ||
|
||||
oldPosition->mWidth != StylePosition()->mWidth ||
|
||||
oldPosition->mMinWidth != StylePosition()->mMinWidth ||
|
||||
oldPosition->mMaxWidth != StylePosition()->mMaxWidth ||
|
||||
oldPosition->mHeight != StylePosition()->mHeight ||
|
||||
oldPosition->mMinHeight != StylePosition()->mMinHeight ||
|
||||
oldPosition->mMaxHeight != StylePosition()->mMaxHeight)) {
|
||||
needAnchorSuppression = true;
|
||||
}
|
||||
|
||||
const nsStyleDisplay* oldDisp = aOldComputedStyle->PeekStyleDisplay();
|
||||
if (oldDisp && (oldDisp->mPosition != StyleDisplay()->mPosition ||
|
||||
oldDisp->TransformChanged(*StyleDisplay()))) {
|
||||
needAnchorSuppression = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (mInScrollAnchorChain && needAnchorSuppression) {
|
||||
ScrollAnchorContainer::FindFor(this)->SuppressAdjustments();
|
||||
}
|
||||
}
|
||||
|
||||
ImageLoader* imageLoader = PresContext()->Document()->StyleImageLoader();
|
||||
|
|
|
@ -3147,6 +3147,12 @@ void nsStyleDisplay::FinishStyle(nsPresContext* aPresContext,
|
|||
GenerateCombinedIndividualTransform();
|
||||
}
|
||||
|
||||
static inline bool TransformListChanged(
|
||||
const RefPtr<nsCSSValueSharedList>& aList,
|
||||
const RefPtr<nsCSSValueSharedList>& aNewList) {
|
||||
return !aList != !aNewList || (aList && *aList != *aNewList);
|
||||
}
|
||||
|
||||
static inline nsChangeHint CompareTransformValues(
|
||||
const RefPtr<nsCSSValueSharedList>& aList,
|
||||
const RefPtr<nsCSSValueSharedList>& aNewList) {
|
||||
|
@ -3425,6 +3431,11 @@ nsChangeHint nsStyleDisplay::CalcDifference(
|
|||
return hint;
|
||||
}
|
||||
|
||||
bool nsStyleDisplay::TransformChanged(const nsStyleDisplay& aNewData) const {
|
||||
return TransformListChanged(mSpecifiedTransform,
|
||||
aNewData.mSpecifiedTransform);
|
||||
}
|
||||
|
||||
void nsStyleDisplay::GenerateCombinedIndividualTransform() {
|
||||
// FIXME(emilio): This should probably be called from somewhere like what we
|
||||
// do for image layers, instead of FinishStyle.
|
||||
|
|
|
@ -1881,6 +1881,8 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleDisplay {
|
|||
|
||||
nsChangeHint CalcDifference(const nsStyleDisplay& aNewData) const;
|
||||
|
||||
bool TransformChanged(const nsStyleDisplay& aNewData) const;
|
||||
|
||||
// We guarantee that if mBinding is non-null, so are mBinding->GetURI() and
|
||||
// mBinding->mOriginPrincipal.
|
||||
RefPtr<mozilla::css::URLValue> mBinding;
|
||||
|
|
Загрузка…
Ссылка в новой задаче