Bug 1516377 - Use the layout viewport for position:fixed elements if the viewport is larger than. r=botond
authorHiroyuki Ikezoe <hikezoe@mozilla.com>
Thu, 07 Feb 2019 05:39:33 +0000
changeset 457582 96d77e9c3f2985c2b702c0910e0da686bee54105
parent 457581 4b74d76e55a819852c8fa925efd25c57fdf35c9d
child 457583 c948bb3bd63bd9ce9181d3e391aa6e03999581f0
push id35516
push userrmaries@mozilla.com
push dateFri, 08 Feb 2019 04:23:26 +0000
treeherdermozilla-central@d599d1a73a3a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbotond
bugs1516377
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1516377 - Use the layout viewport for position:fixed elements if the viewport is larger than. r=botond Both of reftests in this commit are based on an exmaple [1] in the Viewports Explainer written by David Bokan. position-fixed-out-of-view.html fails without the fix because the position:fixed element is rendered at the right edge of the visual viewport so that it's visible in the first place. position-fixed-on-minimum-scale-size.html does NOT fail without the fix either because the position:fixed element sticks at the right edge of the visual viewport so that it still be there even after the visual viewport offset has been changed. [1] https://github.com/bokand/bokand.github.io/blob/master/web_viewports_explainer.md#chrome-2 Differential Revision: https://phabricator.services.mozilla.com/D18797
layout/base/PresShell.cpp
layout/base/nsIPresShell.h
layout/generic/ViewportFrame.cpp
layout/painting/nsDisplayList.cpp
layout/reftests/meta-viewport/position-fixed-on-minimum-scale-size-ref.html
layout/reftests/meta-viewport/position-fixed-on-minimum-scale-size.html
layout/reftests/meta-viewport/position-fixed-out-of-view.html
layout/reftests/meta-viewport/reftest.list
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -10337,16 +10337,24 @@ nsPoint nsIPresShell::GetVisualViewportO
 nsPoint nsIPresShell::GetLayoutViewportOffset() const {
   nsPoint result;
   if (nsIScrollableFrame* sf = GetRootScrollFrameAsScrollable()) {
     result = sf->GetScrollPosition();
   }
   return result;
 }
 
+nsSize nsIPresShell::GetLayoutViewportSize() const {
+  nsSize result;
+  if (nsIScrollableFrame* sf = GetRootScrollFrameAsScrollable()) {
+    result = sf->GetScrollPortRect().Size();
+  }
+  return result;
+}
+
 void nsIPresShell::RecomputeFontSizeInflationEnabled() {
   mFontSizeInflationEnabled = DetermineFontSizeInflationState();
 
   float fontScale = nsLayoutUtils::SystemFontScale();
   if (fontScale == 0.0f) {
     return;
   }
 
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -1713,16 +1713,17 @@ class nsIPresShell : public nsStubDocume
     mPendingVisualScrollUpdate = mozilla::Nothing();
   }
   const mozilla::Maybe<VisualScrollUpdate>& GetPendingVisualScrollUpdate()
       const {
     return mPendingVisualScrollUpdate;
   }
 
   nsPoint GetLayoutViewportOffset() const;
+  nsSize GetLayoutViewportSize() const;
 
   virtual void WindowSizeMoveDone() = 0;
   virtual void SysColorChanged() = 0;
   virtual void ThemeChanged() = 0;
   virtual void BackingScaleFactorChanged() = 0;
 
   /**
    * Documents belonging to an invisible DocShell must not be painted ever.
--- a/layout/generic/ViewportFrame.cpp
+++ b/layout/generic/ViewportFrame.cpp
@@ -263,16 +263,21 @@ nsRect ViewportFrame::AdjustReflowInputA
   // computed size.
   nsRect rect(0, 0, aReflowInput->ComputedWidth(),
               aReflowInput->ComputedHeight());
   nsIPresShell* ps = PresShell();
   if (ps->IsVisualViewportSizeSet() &&
       rect.Size() < ps->GetVisualViewportSize()) {
     rect.SizeTo(ps->GetVisualViewportSize());
   }
+  // Expand the size to the layout viewport size if necessary.
+  const nsSize layoutViewportSize = ps->GetLayoutViewportSize();
+  if (rect.Size() < layoutViewportSize) {
+    rect.SizeTo(layoutViewportSize);
+  }
   return rect;
 }
 
 void ViewportFrame::Reflow(nsPresContext* aPresContext,
                            ReflowOutput& aDesiredSize,
                            const ReflowInput& aReflowInput,
                            nsReflowStatus& aStatus) {
   MarkInReflow();
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -950,17 +950,21 @@ nsDisplayListBuilder::OutOfFlowDisplayDa
     dirtyRectRelativeToDirtyFrame =
         nsRect(nsPoint(0, 0), aFrame->GetParent()->GetSize());
 
     nsIPresShell* ps = aFrame->PresShell();
     if (ps->IsVisualViewportSizeSet() &&
         dirtyRectRelativeToDirtyFrame.Size() < ps->GetVisualViewportSize()) {
       dirtyRectRelativeToDirtyFrame.SizeTo(ps->GetVisualViewportSize());
     }
-
+    // Expand the size to the layout viewport size if necessary.
+    const nsSize layoutViewportSize = ps->GetLayoutViewportSize();
+    if (dirtyRectRelativeToDirtyFrame.Size() < layoutViewportSize) {
+      dirtyRectRelativeToDirtyFrame.SizeTo(layoutViewportSize);
+    }
     visible = dirtyRectRelativeToDirtyFrame;
   }
 #endif
 
   *aOutDirtyRect = dirtyRectRelativeToDirtyFrame - aFrame->GetPosition();
   visible -= aFrame->GetPosition();
 
   nsRect overflowRect = aFrame->GetVisualOverflowRect();
@@ -6670,16 +6674,23 @@ already_AddRefed<Layer> nsDisplayFixedPo
   if (viewportFrame) {
     // Fixed position frames are reflowed into the scroll-port size if one has
     // been set.
     if (presContext->PresShell()->IsVisualViewportSizeSet()) {
       anchorRect.SizeTo(presContext->PresShell()->GetVisualViewportSize());
     } else {
       anchorRect.SizeTo(viewportFrame->GetSize());
     }
+
+    // Expand the size to the layout viewport size if necessary.
+    const nsSize layoutViewportSize =
+        presContext->PresShell()->GetLayoutViewportSize();
+    if (anchorRect.Size() < layoutViewportSize) {
+      anchorRect.SizeTo(layoutViewportSize);
+    }
   } else {
     // A display item directly attached to the viewport.
     // For background-attachment:fixed items, the anchor point is always the
     // top-left of the viewport currently.
     viewportFrame = fixedFrame;
   }
   // The anchorRect top-left is always the viewport top-left.
   anchorRect.MoveTo(viewportFrame->GetOffsetToCrossDoc(ReferenceFrame()));
new file mode 100644
--- /dev/null
+++ b/layout/reftests/meta-viewport/position-fixed-on-minimum-scale-size-ref.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html reftest-async-scroll
+      reftest-async-scroll-x="800"
+      reftest-async-scroll-y="0"
+      reftest-displayport-w="1600"
+      reftest-displayport-h="2000"
+      reftest-displayport-x="0"
+      reftest-displayport-y="0">
+<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
+<style>
+  html,body {
+    margin: 0;
+    width: 1600px;
+    height: 2000px;
+  }
+</style>
+<div style="position: relative; width: 100%; height: 100%;">
+  <div style="position: absolute; top: 0; right: 0; width: 100px; height: 100px; background-color: green"></div>
+</div>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/meta-viewport/position-fixed-on-minimum-scale-size.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html reftest-async-scroll
+      reftest-async-scroll-x="800"
+      reftest-async-scroll-y="0"
+      reftest-displayport-w="1600"
+      reftest-displayport-h="2000"
+      reftest-displayport-x="0"
+      reftest-displayport-y="0">
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<style>
+  html,body {
+    margin: 0;
+    width: 800px;
+    height: 1000px;
+  }
+</style>
+<div style="position:fixed; top: 0; right: 0; width: 100px; height: 100px; background-color: green"></div>
+<div style="width: 1600px; height: 2000px;"></div>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/meta-viewport/position-fixed-out-of-view.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<style>
+  html,body {
+    margin: 0;
+    width: 100%;
+    height: 100%;
+    scrollbar-width: none;
+  }
+</style>
+<!--
+  this green box should be attached at the right edge of the layout viewport so
+  it should not be visible in the initial viewport position(0,0).
+-->
+<div style="position:fixed; top:0; right: 0; width:100px; height: 100px; background-color: green"></div>
+<div style="width: 200%; height: 100%;"></div>
--- a/layout/reftests/meta-viewport/reftest.list
+++ b/layout/reftests/meta-viewport/reftest.list
@@ -5,16 +5,19 @@ default-preferences pref(dom.meta-viewpo
 == initial-scale-0.html initial-scale-0_5-ref.html
 == initial-scale-100.html initial-scale-0_5-ref.html
 == no-viewport.html initial-scale-0_5-ref.html
 == viewport-width.html initial-scale-0_5-ref.html
 == initial-scale-1.html no-zoom-ref.html
 == minimum-scale.html no-zoom-ref.html
 == clamped-by-default-minimum-scale.html initial-scale-0_25-ref.html
 
+skip-if(!Android) == position-fixed-on-minimum-scale-size.html position-fixed-on-minimum-scale-size-ref.html
+== position-fixed-out-of-view.html about:blank
+
 # Skip below tests on Windows (bug 1516322) on Webrender (bug 1520096)
 skip-if(winWidget||webrender) == overflow-region.html overflow-region-ref.html
 skip-if(winWidget||webrender) == overflow-hidden-region.html overflow-region-ref.html
 skip-if(winWidget||webrender) == overflow-hidden-region-with-negative-left-positioned-element.html overflow-region-ref.html
 skip-if(winWidget||webrender) fails == horizontal-overflow-hidden-region.html horizontal-overflow-hidden-region-ref.html # bug 1508177
 skip-if(winWidget||webrender) == vertical-overflow-hidden-region.html about:blank
 skip-if(winWidget||webrender) == scroll-to-unreachable-area.html scroll-to-unreachable-area-ref.html
 skip-if(winWidget||webrender) == wrapped-text-at-icb.html wrapped-text-at-icb-ref.html