Bug 926292 - Put overlay scrollbars on top of the topmost positioned descendant of the scrolled frame. r=mats
authorMarkus Stange <mstange@themasta.com>
Thu, 20 Mar 2014 10:12:46 +0800
changeset 174413 d5acc6457aaa2f28db65018a413b75dcd83ca109
parent 174412 8052705f985a81974c86df38b8184d5ede0633fe
child 174414 44bea44e2f610342c1040db15dab36726cf2b4ab
push id41281
push usermstange@themasta.com
push dateThu, 20 Mar 2014 02:13:03 +0000
treeherdermozilla-inbound@d5acc6457aaa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmats
bugs926292
milestone31.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 926292 - Put overlay scrollbars on top of the topmost positioned descendant of the scrolled frame. r=mats
layout/base/nsDisplayList.cpp
layout/base/nsDisplayList.h
layout/base/nsLayoutUtils.cpp
layout/base/nsLayoutUtils.h
layout/generic/nsFrame.cpp
layout/generic/nsGfxScrollFrame.cpp
layout/reftests/z-index/overlayscrollbar-sorting-1.html
layout/reftests/z-index/overlayscrollbar-sorting-2.html
layout/reftests/z-index/overlayscrollbar-sorting-3.html
layout/reftests/z-index/overlayscrollbar-sorting-4.html
layout/reftests/z-index/overlayscrollbar-sorting-5.html
layout/reftests/z-index/overlayscrollbar-sorting-ref-hidden.html
layout/reftests/z-index/overlayscrollbar-sorting-ref-visible.html
layout/reftests/z-index/reftest.list
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -1632,19 +1632,17 @@ static bool IsContentLEQ(nsDisplayItem* 
   }
   return nsLayoutUtils::CompareTreePosition(content1, content2, commonAncestor) <= 0;
 }
 
 static bool IsZOrderLEQ(nsDisplayItem* aItem1, nsDisplayItem* aItem2,
                         void* aClosure) {
   // Note that we can't just take the difference of the two
   // z-indices here, because that might overflow a 32-bit int.
-  int32_t index1 = nsLayoutUtils::GetZIndex(aItem1->Frame());
-  int32_t index2 = nsLayoutUtils::GetZIndex(aItem2->Frame());
-  return index1 <= index2;
+  return aItem1->ZIndex() <= aItem2->ZIndex();
 }
 
 void nsDisplayList::SortByZOrder(nsDisplayListBuilder* aBuilder,
                                  nsIContent* aCommonAncestor) {
   Sort(aBuilder, IsZOrderLEQ, aCommonAncestor);
 }
 
 void nsDisplayList::SortByContentOrder(nsDisplayListBuilder* aBuilder,
@@ -1694,16 +1692,30 @@ nsDisplayItem::MaxActiveLayers()
   if (!sMaxLayersCached) {
     Preferences::AddIntVarCache(&sMaxLayers, "layers.max-active", -1);
     sMaxLayersCached = true;
   }
 
   return sMaxLayers;
 }
 
+int32_t
+nsDisplayItem::ZIndex() const
+{
+  if (!mFrame->IsPositioned() && !mFrame->IsFlexItem())
+    return 0;
+
+  const nsStylePosition* position = mFrame->StylePosition();
+  if (position->mZIndex.GetUnit() == eStyleUnit_Integer)
+    return position->mZIndex.GetIntValue();
+
+  // sort the auto and 0 elements together
+  return 0;
+}
+
 bool
 nsDisplayItem::RecomputeVisibility(nsDisplayListBuilder* aBuilder,
                                    nsRegion* aVisibleRegion) {
   nsRect bounds = GetClippedBounds(aBuilder);
 
   nsRegion itemVisible;
   itemVisible.And(*aVisibleRegion, bounds);
   mVisibleRect = itemVisible.GetBounds();
@@ -2953,17 +2965,19 @@ nsDisplayBoxShadowInner::ComputeVisibili
 
   // Store the actual visible region
   mVisibleRegion.And(*aVisibleRegion, mVisibleRect);
   return true;
 }
 
 nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder* aBuilder,
                                      nsIFrame* aFrame, nsDisplayList* aList)
-  : nsDisplayItem(aBuilder, aFrame) {
+  : nsDisplayItem(aBuilder, aFrame)
+  , mOverrideZIndex(0)
+{
   mList.AppendToTop(aList);
   UpdateBounds(aBuilder);
 
   if (!aFrame || !aFrame->IsTransformed()) {
     return;
   }
 
   // If the frame is a preserve-3d parent, then we will create transforms
@@ -2995,17 +3009,19 @@ nsDisplayWrapList::nsDisplayWrapList(nsD
       i->Frame() == mFrame) {
     mReferenceFrame = i->ReferenceFrame();
     mToReferenceFrame = i->ToReferenceFrame();
   }
 }
 
 nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder* aBuilder,
                                      nsIFrame* aFrame, nsDisplayItem* aItem)
-  : nsDisplayItem(aBuilder, aFrame) {
+  : nsDisplayItem(aBuilder, aFrame)
+  , mOverrideZIndex(0)
+{
   mList.AppendToTop(aItem);
   UpdateBounds(aBuilder);
   
   if (!aFrame || !aFrame->IsTransformed()) {
     return;
   }
 
   if (aFrame->Preserves3DChildren()) {
@@ -3021,17 +3037,19 @@ nsDisplayWrapList::nsDisplayWrapList(nsD
     mToReferenceFrame = aItem->ToReferenceFrame();
   }
 }
 
 nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder* aBuilder,
                                      nsIFrame* aFrame, nsDisplayItem* aItem,
                                      const nsIFrame* aReferenceFrame,
                                      const nsPoint& aToReferenceFrame)
-  : nsDisplayItem(aBuilder, aFrame, aReferenceFrame, aToReferenceFrame) {
+  : nsDisplayItem(aBuilder, aFrame, aReferenceFrame, aToReferenceFrame)
+  , mOverrideZIndex(0)
+{
   mList.AppendToTop(aItem);
   mBounds = mList.GetBounds(aBuilder);
 }
 
 nsDisplayWrapList::~nsDisplayWrapList() {
   mList.DeleteAll();
 }
 
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -836,16 +836,22 @@ public:
                        HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) {}
   /**
    * @return the frame that this display item is based on. This is used to sort
    * items by z-index and content order and for some other uses. Never
    * returns null.
    */
   inline nsIFrame* Frame() const { return mFrame; }
   /**
+   * Compute the used z-index of our frame; returns zero for elements to which
+   * z-index does not apply, and for z-index:auto.
+   * @note This can be overridden, @see nsDisplayWrapList::SetOverrideZIndex.
+   */
+  virtual int32_t ZIndex() const;
+  /**
    * The default bounds is the frame border rect.
    * @param aSnap *aSnap is set to true if the returned rect will be
    * snapped to nearest device pixel edges during actual drawing.
    * It might be set to false and snap anyway, so code computing the set of
    * pixels affected by this display item needs to round outwards to pixel
    * boundaries when *aSnap is set to false.
    * This does not take the item's clipping into account.
    * @return a rectangle relative to aBuilder->ReferenceFrame() that
@@ -2442,17 +2448,17 @@ public:
    */
   nsDisplayWrapList(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                     nsDisplayList* aList);
   nsDisplayWrapList(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                     nsDisplayItem* aItem);
   nsDisplayWrapList(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                     nsDisplayItem* aItem, const nsIFrame* aReferenceFrame, const nsPoint& aToReferenceFrame);
   nsDisplayWrapList(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
-    : nsDisplayItem(aBuilder, aFrame) {}
+    : nsDisplayItem(aBuilder, aFrame), mOverrideZIndex(0) {}
   virtual ~nsDisplayWrapList();
   /**
    * Call this if the wrapped list is changed.
    */
   virtual void UpdateBounds(nsDisplayListBuilder* aBuilder) MOZ_OVERRIDE
   {
     mBounds = mList.GetBounds(aBuilder);
   }
@@ -2501,16 +2507,26 @@ public:
     NS_ASSERTION(mList.IsEmpty() || !ReferenceFrame() ||
                  !mList.GetBottom()->ReferenceFrame() ||
                  mList.GetBottom()->ReferenceFrame() == ReferenceFrame(),
                  "Children must have same reference frame");
     return &mList;
   }
   virtual nsDisplayList* GetChildren() MOZ_OVERRIDE { return &mList; }
 
+  virtual int32_t ZIndex() const MOZ_OVERRIDE
+  {
+    return (mOverrideZIndex > 0) ? mOverrideZIndex : nsDisplayItem::ZIndex();
+  }
+
+  void SetOverrideZIndex(int32_t aZIndex)
+  {
+    mOverrideZIndex = aZIndex;
+  }
+
   /**
    * This creates a copy of this item, but wrapping aItem instead of
    * our existing list. Only gets called if this item returned nullptr
    * for GetUnderlyingFrame(). aItem is guaranteed to return non-null from
    * GetUnderlyingFrame().
    */
   virtual nsDisplayWrapList* WrapWithClone(nsDisplayListBuilder* aBuilder,
                                            nsDisplayItem* aItem) {
@@ -2544,16 +2560,18 @@ protected:
     mMergedFrames.MoveElementsFrom(aOther->mMergedFrames);
   }
 
   nsDisplayList mList;
   // The frames from items that have been merged into this item, excluding
   // this item's own frame.
   nsTArray<nsIFrame*> mMergedFrames;
   nsRect mBounds;
+  // Overrides the ZIndex of our frame if > 0.
+  int32_t mOverrideZIndex;
 };
 
 /**
  * We call WrapDisplayList on the in-flow lists: BorderBackground(),
  * BlockBorderBackgrounds() and Content().
  * We call WrapDisplayItem on each item of Outlines(), PositionedDescendants(),
  * and Floats(). This is done to support special wrapping processing for frames
  * that may not be in-flow descendants of the current frame.
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -2491,30 +2491,16 @@ nsLayoutUtils::PaintFrame(nsRenderingCon
   }
 
 
   // Flush the list so we don't trigger the IsEmpty-on-destruction assertion
   list.DeleteAll();
   return NS_OK;
 }
 
-int32_t
-nsLayoutUtils::GetZIndex(nsIFrame* aFrame) {
-  if (!aFrame->IsPositioned() && !aFrame->IsFlexItem())
-    return 0;
-
-  const nsStylePosition* position =
-    aFrame->StylePosition();
-  if (position->mZIndex.GetUnit() == eStyleUnit_Integer)
-    return position->mZIndex.GetIntValue();
-
-  // sort the auto and 0 elements together
-  return 0;
-}
-
 /**
  * Uses a binary search for find where the cursor falls in the line of text
  * It also keeps track of the part of the string that has already been measured
  * so it doesn't have to keep measuring the same text over and over
  *
  * @param "aBaseWidth" contains the width in twips of the portion
  * of the text that has already been measured, and aBaseInx contains
  * the index of the text that has already been measured.
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -837,22 +837,16 @@ public:
    * necessarily correspond to what's visible in the window; we don't
    * want to mess up the widget's layer tree.
    */
   static nsresult PaintFrame(nsRenderingContext* aRenderingContext, nsIFrame* aFrame,
                              const nsRegion& aDirtyRegion, nscolor aBackstop,
                              uint32_t aFlags = 0);
 
   /**
-   * Compute the used z-index of aFrame; returns zero for elements to which
-   * z-index does not apply, and for z-index:auto
-   */
-  static int32_t GetZIndex(nsIFrame* aFrame);
-
-  /**
    * Uses a binary search for find where the cursor falls in the line of text
    * It also keeps track of the part of the string that has already been measured
    * so it doesn't have to keep measuring the same text over and over
    *
    * @param "aBaseWidth" contains the width in twips of the portion
    * of the text that has already been measured, and aBaseInx contains
    * the index of the text that has already been measured.
    *
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -2019,23 +2019,20 @@ nsIFrame::BuildDisplayListForStackingCon
   
   nsDisplayList resultList;
   // Now follow the rules of http://www.w3.org/TR/CSS21/zindex.html
   // 1,2: backgrounds and borders
   resultList.AppendToTop(set.BorderBackground());
   // 3: negative z-index children.
   for (;;) {
     nsDisplayItem* item = set.PositionedDescendants()->GetBottom();
-    if (item) {
-      nsIFrame* f = item->Frame();
-      if (nsLayoutUtils::GetZIndex(f) < 0) {
-        set.PositionedDescendants()->RemoveBottom();
-        resultList.AppendToTop(item);
-        continue;
-      }
+    if (item && item->ZIndex() < 0) {
+      set.PositionedDescendants()->RemoveBottom();
+      resultList.AppendToTop(item);
+      continue;
     }
     break;
   }
   // 4: block backgrounds
   resultList.AppendToTop(set.BlockBorderBackgrounds());
   // 5: floats
   resultList.AppendToTop(set.Floats());
   // 7: general content
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -2087,29 +2087,49 @@ ScrollFrameHelper::ScrollToImpl(nsPoint 
   }
 
   nsCOMPtr<nsIDocShell> docShell = presContext->GetDocShell();
   if (docShell) {
     docShell->NotifyScrollObservers();
   }
 }
 
+static int32_t
+MaxZIndexInList(nsDisplayList* aList, nsDisplayListBuilder* aBuilder)
+{
+  int32_t maxZIndex = 0;
+  for (nsDisplayItem* item = aList->GetBottom(); item; item = item->GetAbove()) {
+    maxZIndex = std::max(maxZIndex, item->ZIndex());
+  }
+  return maxZIndex;
+}
+
 static void
-AppendToTop(nsDisplayListBuilder* aBuilder, nsDisplayList* aDest,
+AppendToTop(nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists,
             nsDisplayList* aSource, nsIFrame* aSourceFrame, bool aOwnLayer,
-            uint32_t aFlags, mozilla::layers::FrameMetrics::ViewID aScrollTargetId)
+            uint32_t aFlags, mozilla::layers::FrameMetrics::ViewID aScrollTargetId,
+            bool aPositioned)
 {
   if (aSource->IsEmpty())
     return;
-  if (aOwnLayer) {
-    aDest->AppendNewToTop(
-        new (aBuilder) nsDisplayOwnLayer(aBuilder, aSourceFrame, aSource,
-                                         aFlags, aScrollTargetId));
+
+  nsDisplayWrapList* newItem = aOwnLayer?
+    new (aBuilder) nsDisplayOwnLayer(aBuilder, aSourceFrame, aSource,
+                                     aFlags, aScrollTargetId) :
+    new (aBuilder) nsDisplayWrapList(aBuilder, aSourceFrame, aSource);
+
+  nsDisplayList* positionedDescendants = aLists.PositionedDescendants();
+  if (aPositioned && !positionedDescendants->IsEmpty()) {
+    // We want overlay scrollbars to always be on top of the scrolled content,
+    // but we don't want them to unnecessarily cover overlapping elements from
+    // outside our scroll frame.
+    newItem->SetOverrideZIndex(MaxZIndexInList(positionedDescendants, aBuilder));
+    positionedDescendants->AppendNewToTop(newItem);
   } else {
-    aDest->AppendToTop(aSource);
+    aLists.BorderBackground()->AppendNewToTop(newItem);
   }
 }
 
 struct HoveredStateComparator
 {
   bool Equals(nsIFrame* A, nsIFrame* B) const {
     bool aHovered = A->GetContent()->HasAttr(kNameSpaceID_None,
                                              nsGkAtoms::hover);
@@ -2158,37 +2178,29 @@ ScrollFrameHelper::AppendScrollPartsTo(n
   scrollParts.Sort(HoveredStateComparator());
 
   for (uint32_t i = 0; i < scrollParts.Length(); ++i) {
     nsDisplayListCollection partList;
     mOuter->BuildDisplayListForChild(
       aBuilder, scrollParts[i], aDirtyRect, partList,
       nsIFrame::DISPLAY_CHILD_FORCE_STACKING_CONTEXT);
 
-    // Don't append textarea resizers to the positioned descendants because
-    // we don't want them to float on top of overlapping elements.
-    bool appendToPositioned = aPositioned &&
-                              !(scrollParts[i] == mResizerBox && !mIsRoot);
-
-    nsDisplayList* dest = appendToPositioned ?
-      aLists.PositionedDescendants() : aLists.BorderBackground();
-
     uint32_t flags = 0;
     if (scrollParts[i] == mVScrollbarBox) {
       flags |= nsDisplayOwnLayer::VERTICAL_SCROLLBAR;
     }
     if (scrollParts[i] == mHScrollbarBox) {
       flags |= nsDisplayOwnLayer::HORIZONTAL_SCROLLBAR;
     }
 
     // DISPLAY_CHILD_FORCE_STACKING_CONTEXT put everything into
     // partList.PositionedDescendants().
-    ::AppendToTop(aBuilder, dest,
+    ::AppendToTop(aBuilder, aLists,
                   partList.PositionedDescendants(), scrollParts[i],
-                  aCreateLayer, flags, scrollTargetId);
+                  aCreateLayer, flags, scrollTargetId, aPositioned);
   }
 }
 
 class ScrollLayerWrapper : public nsDisplayWrapper
 {
 public:
   ScrollLayerWrapper(nsIFrame* aScrollFrame, nsIFrame* aScrolledFrame)
     : mCount(0)
@@ -2596,27 +2608,26 @@ ScrollFrameHelper::BuildDisplayList(nsDi
 
     // In case we are not using displayport or the nsDisplayScrollLayers are
     // flattened during visibility computation, we still need to export the
     // metadata about this scroll box to the compositor process.
     nsDisplayScrollInfoLayer* layerItem = new (aBuilder) nsDisplayScrollInfoLayer(
       aBuilder, mScrolledFrame, mOuter);
     scrolledContent.BorderBackground()->AppendNewToBottom(layerItem);
   }
-  scrolledContent.MoveTo(aLists);
-
   // Now display overlay scrollbars and the resizer, if we have one.
 #ifdef MOZ_WIDGET_GONK
   // TODO: only layerize the overlay scrollbars if this scrollframe can be
   // panned asynchronously. For now just always layerize on B2G because.
   // that's where we want the layerized scrollbars
   createLayersForScrollbars = true;
 #endif
-  AppendScrollPartsTo(aBuilder, aDirtyRect, aLists, createLayersForScrollbars,
-                      true);
+  AppendScrollPartsTo(aBuilder, aDirtyRect, scrolledContent,
+                      createLayersForScrollbars, true);
+  scrolledContent.MoveTo(aLists);
 }
 
 bool
 ScrollFrameHelper::IsRectNearlyVisible(const nsRect& aRect) const
 {
   // Use the right rect depending on if a display port is set.
   nsRect displayPort;
   bool usingDisplayport = nsLayoutUtils::GetDisplayPort(mOuter->GetContent(), &displayPort);
new file mode 100644
--- /dev/null
+++ b/layout/reftests/z-index/overlayscrollbar-sorting-1.html
@@ -0,0 +1,26 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Test that overlay scrollbars are on top of positioned content</title>
+
+<style>
+
+#outer {
+  width: 200px;
+  height: 200px;
+  overflow: auto;
+}
+
+#content {
+  height: 400px;
+  background: cyan;
+  position: relative;
+  z-index: 1;
+}
+
+</style>
+
+<div id="outer"><div id="content"></div></div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/z-index/overlayscrollbar-sorting-2.html
@@ -0,0 +1,32 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Test that overlay scrollbars are covered by overlapping non-positioned siblings</title>
+
+<style>
+
+#outer {
+  width: 200px;
+  height: 200px;
+  overflow: auto;
+  background: cyan;
+}
+
+#content {
+  height: 400px;
+}
+
+#cover {
+  margin-top: -200px;
+  width: 200px;
+  height: 200px;
+  background: cyan;
+}
+
+</style>
+
+<div id="outer"><div id="content"></div></div>
+<div id="cover"></div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/z-index/overlayscrollbar-sorting-3.html
@@ -0,0 +1,33 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Test that overlay scrollbars are covered by overlapping positioned siblings</title>
+
+<style>
+
+#outer {
+  width: 200px;
+  height: 200px;
+  overflow: auto;
+  background: cyan;
+}
+
+#content {
+  height: 400px;
+}
+
+#cover {
+  margin-top: -200px;
+  width: 200px;
+  height: 200px;
+  background: cyan;
+  position: relative;
+}
+
+</style>
+
+<div id="outer"><div id="content"></div></div>
+<div id="cover"></div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/z-index/overlayscrollbar-sorting-4.html
@@ -0,0 +1,35 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Test that overlay scrollbars are covered by positioned siblings with higher z-index even when the scrollable frame has a positioned descendant</title>
+
+<style>
+
+#outer {
+  width: 200px;
+  height: 200px;
+  overflow: auto;
+}
+
+#content {
+  height: 400px;
+  background: cyan;
+  position: relative;
+  z-index: 1;
+}
+
+#cover {
+  width: 200px;
+  height: 200px;
+  background: cyan;
+  position: absolute;
+  z-index: 2;
+}
+
+</style>
+
+<div id="cover"></div>
+<div id="outer"><div id="content"></div></div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/z-index/overlayscrollbar-sorting-5.html
@@ -0,0 +1,43 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Test that overlay scrollbars are on top of positioned siblings when the scrollable frame has a positioned descendant that's higher than the sibling</title>
+
+<style>
+
+#outer {
+  width: 200px;
+  height: 200px;
+  overflow: auto;
+}
+
+#content {
+  height: 400px;
+  background: cyan;
+  position: relative;
+  z-index: 3;
+}
+
+/* This test has different behavior depending on the type of scrollbar used.
+ * We want the scrollbar to be visible. When overlay scrollbars are used,
+ * they should be visible even when #cover gets between #outer and #content,
+ * but for non-overlay scrollbars, that would cover them so we disable the
+ * cover.
+ */
+@media all and (-moz-overlay-scrollbars) {
+  #cover {
+    width: 200px;
+    height: 200px;
+    background: cyan;
+    position: absolute;
+    z-index: 2;
+  }
+}
+
+</style>
+
+<div id="cover"></div>
+<div id="outer"><div id="content"></div></div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/z-index/overlayscrollbar-sorting-ref-hidden.html
@@ -0,0 +1,24 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Reference without scrollbar</title>
+
+<style>
+
+#outer {
+  width: 200px;
+  height: 200px;
+  overflow: auto;
+  background: cyan;
+}
+
+#content {
+  height: 0px;
+}
+
+</style>
+
+<div id="outer"><div id="content"></div></div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/z-index/overlayscrollbar-sorting-ref-visible.html
@@ -0,0 +1,24 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Reference with scrollbar</title>
+
+<style>
+
+#outer {
+  width: 200px;
+  height: 200px;
+  overflow: auto;
+  background: cyan;
+}
+
+#content {
+  height: 400px;
+}
+
+</style>
+
+<div id="outer"><div id="content"></div></div>
--- a/layout/reftests/z-index/reftest.list
+++ b/layout/reftests/z-index/reftest.list
@@ -1,5 +1,15 @@
+# Make overlay scrollbars never fade out
+default-preferences pref(layout.testing.overlay-scrollbars.always-visible,true)
+
 == 480053-1.html 480053-1-ref.html
 == z-index-1.html z-index-1-ref.html
 != stacking-context-yes.html stacking-context-no.html
 == stacking-context-perspective.html stacking-context-yes.html
 == stacking-context-backface-visibility.html stacking-context-no.html
+
+fails-if(Android) != overlayscrollbar-sorting-ref-visible.html overlayscrollbar-sorting-ref-hidden.html
+== overlayscrollbar-sorting-1.html overlayscrollbar-sorting-ref-visible.html
+== overlayscrollbar-sorting-2.html overlayscrollbar-sorting-ref-hidden.html
+== overlayscrollbar-sorting-3.html overlayscrollbar-sorting-ref-hidden.html
+== overlayscrollbar-sorting-4.html overlayscrollbar-sorting-ref-hidden.html
+== overlayscrollbar-sorting-5.html overlayscrollbar-sorting-ref-visible.html