Bug 926292 - Put overlay scrollbars on top of the topmost positioned descendant of the scrolled frame. r=mats

This commit is contained in:
Markus Stange 2014-03-20 10:12:46 +08:00
parent 79968503a5
commit da8c6a19ca
14 changed files with 306 additions and 55 deletions

View File

@ -1637,9 +1637,7 @@ 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,
@ -1699,6 +1697,20 @@ nsDisplayItem::MaxActiveLayers()
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) {
@ -2958,7 +2970,9 @@ nsDisplayBoxShadowInner::ComputeVisibility(nsDisplayListBuilder* aBuilder,
nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder* aBuilder,
nsIFrame* aFrame, nsDisplayList* aList)
: nsDisplayItem(aBuilder, aFrame) {
: nsDisplayItem(aBuilder, aFrame)
, mOverrideZIndex(0)
{
mList.AppendToTop(aList);
UpdateBounds(aBuilder);
@ -3000,7 +3014,9 @@ nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder* aBuilder,
nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder* aBuilder,
nsIFrame* aFrame, nsDisplayItem* aItem)
: nsDisplayItem(aBuilder, aFrame) {
: nsDisplayItem(aBuilder, aFrame)
, mOverrideZIndex(0)
{
mList.AppendToTop(aItem);
UpdateBounds(aBuilder);
@ -3026,7 +3042,9 @@ 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);
}

View File

@ -840,6 +840,12 @@ public:
* 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
@ -2447,7 +2453,7 @@ public:
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.
@ -2506,6 +2512,16 @@ public:
}
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
@ -2549,6 +2565,8 @@ protected:
// this item's own frame.
nsTArray<nsIFrame*> mMergedFrames;
nsRect mBounds;
// Overrides the ZIndex of our frame if > 0.
int32_t mOverrideZIndex;
};
/**

View File

@ -2496,20 +2496,6 @@ nsLayoutUtils::PaintFrame(nsRenderingContext* aRenderingContext, nsIFrame* aFram
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

View File

@ -841,12 +841,6 @@ public:
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

View File

@ -2024,13 +2024,10 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
// 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;
}

View File

@ -2092,19 +2092,39 @@ ScrollFrameHelper::ScrollToImpl(nsPoint aPt, const nsRect& aRange, nsIAtom* aOri
}
}
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);
}
}
@ -2163,14 +2183,6 @@ ScrollFrameHelper::AppendScrollPartsTo(nsDisplayListBuilder* aBuilder,
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;
@ -2181,9 +2193,9 @@ ScrollFrameHelper::AppendScrollPartsTo(nsDisplayListBuilder* aBuilder,
// 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);
}
}
@ -2601,8 +2613,6 @@ ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder,
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
@ -2610,8 +2620,9 @@ ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder,
// 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

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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