Bug 1518631 - Invalidate scroll anchor container on changes to 'overflow-anchor' property and add tests. r=dholbert
authorRyan Hunt <rhunt@eqrion.net>
Tue, 08 Jan 2019 13:03:18 -0600
changeset 514215 24f969298ec5c1a7cbc444bfc8f3d77b07f46bbe
parent 514214 bd1415025d3e6b5fdf51803201ce06081beffd95
child 514238 ae73df344b934c73241e61809a8d42835c33952b
child 514253 ae2e40fee31665a104031f3ddd239919a6c9ab61
push id1953
push userffxbld-merge
push dateMon, 11 Mar 2019 12:10:20 +0000
treeherdermozilla-release@9c35dcbaa899 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdholbert
bugs1518631
milestone66.0a1
first release with
nightly linux32
24f969298ec5 / 66.0a1 / 20190117095319 / files
nightly linux64
24f969298ec5 / 66.0a1 / 20190117095319 / files
nightly mac
24f969298ec5 / 66.0a1 / 20190117095319 / files
nightly win32
24f969298ec5 / 66.0a1 / 20190117095319 / files
nightly win64
24f969298ec5 / 66.0a1 / 20190117095319 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1518631 - Invalidate scroll anchor container on changes to 'overflow-anchor' property and add tests. r=dholbert I believe that changes to 'overflow-anchor' should invalidate the current scroll anchor. This isn't in the spec, but there's an issue open for it [1]. [1] https://github.com/w3c/csswg-drafts/issues/3494 Differential Revision: https://phabricator.services.mozilla.com/D16273
layout/generic/nsFrame.cpp
testing/web-platform/tests/css/css-scroll-anchoring/opt-out-dynamic-scroller.html
testing/web-platform/tests/css/css-scroll-anchoring/opt-out-dynamic.html
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -1090,31 +1090,39 @@ void nsIFrame::MarkNeedsDisplayItemRebui
     if (oldBorder) {
       oldValue = oldBorder->GetComputedBorder();
       newValue = StyleBorder()->GetComputedBorder();
       if (oldValue != newValue && !HasProperty(UsedBorderProperty())) {
         AddProperty(UsedBorderProperty(), new nsMargin(oldValue));
       }
     }
 
+    const nsStyleDisplay* oldDisp = aOldComputedStyle->PeekStyleDisplay();
+    if (oldDisp &&
+        (oldDisp->mOverflowAnchor != StyleDisplay()->mOverflowAnchor)) {
+      ScrollAnchorContainer::FindFor(this)->InvalidateAnchor();
+      if (nsIScrollableFrame* scrollableFrame = do_QueryFrame(this)) {
+        scrollableFrame->GetAnchor()->InvalidateAnchor();
+      }
+    }
+
     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();
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-scroll-anchoring/opt-out-dynamic-scroller.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="help" href="https://drafts.csswg.org/css-scroll-anchoring-1/">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+
+#scroller {
+  overflow: scroll;
+  width: 300px;
+  height: 300px;
+}
+#before { height: 50px; }
+#content { margin-top: 100px; margin-bottom: 600px; }
+.no { overflow-anchor: none; }
+
+</style>
+<div id="scroller">
+  <div id="before"></div>
+  <div id="content">content</div>
+</div>
+<script>
+
+// Tests that dynamic styling 'overflow-anchor' on a scrolling element has the
+// same effect as initial styling
+
+test(() => {
+  let scroller = document.querySelector("#scroller");
+  let before = document.querySelector("#before");
+
+  // Scroll down so that #content is the first element in the viewport
+  scroller.scrollTop = 100;
+
+  // Change the height of #before to trigger a scroll adjustment. This ensures
+  // that #content was selected as a scroll anchor
+  before.style.height = "100px";
+  assert_equals(scroller.scrollTop, 150);
+
+  // Now set 'overflow-anchor: none' on #scroller. This should invalidate the
+  // scroll anchor, and #scroller shouldn't be able to select an anchor anymore
+  scroller.className = 'no';
+
+  // Change the height of #before and make sure we don't adjust. This ensures
+  // that #content is not a scroll anchor
+  before.style.height = "150px";
+  assert_equals(scroller.scrollTop, 150);
+}, "Dynamically styling 'overflow-anchor: none' on the scroller element should prevent scroll anchoring");
+
+</script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-scroll-anchoring/opt-out-dynamic.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="help" href="https://drafts.csswg.org/css-scroll-anchoring-1/">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+
+#scroller {
+  overflow: scroll;
+  width: 300px;
+  height: 300px;
+}
+#before { height: 50px; }
+#content { margin-top: 100px; margin-bottom: 600px; }
+.no { overflow-anchor: none; }
+
+</style>
+<div id="scroller">
+  <div id="before"></div>
+  <div id="content">content</div>
+</div>
+<script>
+
+// Tests that dynamic styling 'overflow-anchor' on an anchor node has the
+// same effect as initial styling
+
+test(() => {
+  let scroller = document.querySelector("#scroller");
+  let before = document.querySelector("#before");
+  let content = document.querySelector("#content");
+
+  // Scroll down so that #content is the first element in the viewport
+  scroller.scrollTop = 100;
+
+  // Change the height of #before to trigger a scroll adjustment. This ensures
+  // that #content was selected as a scroll anchor
+  before.style.height = "100px";
+  assert_equals(scroller.scrollTop, 150);
+
+  // Now set 'overflow-anchor: none' on #content. This should invalidate the
+  // scroll anchor, and #scroller should recalculate its anchor. There are no
+  // other valid anchors in the viewport, so there should be no anchor.
+  content.className = 'no';
+
+  // Change the height of #before and make sure we don't adjust. This ensures
+  // that #content was not selected as a scroll anchor
+  before.style.height = "150px";
+  assert_equals(scroller.scrollTop, 150);
+}, "Dynamically styling 'overflow-anchor: none' on the anchor node should prevent scroll anchoring");
+
+</script>