Bug 758620 - Force fixed-position frames to have their own layers. r=roc

Introduce a new display-list item 'nsDisplayFixedPosition' that represents
fixed-position elements. This item cannot be merged, which forces fixed
position elements to have their own layer, and has a BuildLayer implementation
that sets the necessary metadata on a Layer to be able to maintain its
position correctly during composition when asynchronously panning and zooming.
This commit is contained in:
Chris Lord 2012-06-27 16:43:57 +01:00
parent 1163c9e9c6
commit 9fd49ed93f
8 changed files with 127 additions and 2 deletions

View File

@ -731,6 +731,15 @@ public:
*/
void SetIsFixedPosition(bool aFixedPosition) { mIsFixedPosition = aFixedPosition; }
/**
* CONSTRUCTION PHASE ONLY
* If a layer is "fixed position", this determines which point on the layer
* is considered the "anchor" point, that is, the point which remains in the
* same position when compositing the layer tree with a transformation
* (such as when asynchronously scrolling and zooming).
*/
void SetFixedPositionAnchor(const gfxPoint& aAnchor) { mAnchor = aAnchor; }
// These getters can be used anytime.
float GetOpacity() { return mOpacity; }
const nsIntRect* GetClipRect() { return mUseClipRect ? &mClipRect : nsnull; }
@ -743,6 +752,7 @@ public:
virtual Layer* GetLastChild() { return nsnull; }
const gfx3DMatrix& GetTransform() { return mTransform; }
bool GetIsFixedPosition() { return mIsFixedPosition; }
gfxPoint GetFixedPositionAnchor() { return mAnchor; }
Layer* GetMaskLayer() { return mMaskLayer; }
/**
@ -992,6 +1002,7 @@ protected:
bool mUseClipRect;
bool mUseTileSourceRect;
bool mIsFixedPosition;
gfxPoint mAnchor;
DebugOnly<PRUint32> mDebugColorIndex;
};

View File

@ -12,6 +12,7 @@ include protocol PRenderFrame;
include "gfxipc/ShadowLayerUtils.h";
using gfx3DMatrix;
using gfxPoint;
using gfxRGBA;
using nsIntPoint;
using nsIntRect;
@ -85,6 +86,7 @@ struct CommonLayerAttributes {
bool useClipRect;
nsIntRect clipRect;
bool isFixedPosition;
gfxPoint fixedPositionAnchor;
nullable PLayer maskLayer;
};

View File

@ -291,6 +291,7 @@ ShadowLayerForwarder::EndTransaction(InfallibleTArray<EditReply>* aReplies)
common.clipRect() = (common.useClipRect() ?
*mutant->GetClipRect() : nsIntRect());
common.isFixedPosition() = mutant->GetIsFixedPosition();
common.fixedPositionAnchor() = mutant->GetFixedPositionAnchor();
if (Layer* maskLayer = mutant->GetMaskLayer()) {
common.maskLayerChild() = Shadow(maskLayer->AsShadowableLayer());
} else {

View File

@ -207,6 +207,7 @@ ShadowLayersParent::RecvUpdate(const InfallibleTArray<Edit>& cset,
static bool fixedPositionLayersEnabled = getenv("MOZ_ENABLE_FIXED_POSITION_LAYERS") != 0;
if (fixedPositionLayersEnabled) {
layer->SetIsFixedPosition(common.isFixedPosition());
layer->SetFixedPositionAnchor(common.fixedPositionAnchor());
}
if (PLayerParent* maskLayer = common.maskLayerParent()) {
layer->SetMaskLayer(cast(maskLayer)->AsLayer());

View File

@ -427,6 +427,27 @@ struct ParamTraits<gfxMatrix>
}
};
template<>
struct ParamTraits<gfxPoint>
{
typedef gfxPoint paramType;
static void Write(Message* aMsg, const paramType& aParam)
{
WriteParam(aMsg, aParam.x);
WriteParam(aMsg, aParam.y);
}
static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
{
if (ReadParam(aMsg, aIter, &aResult->x) &&
ReadParam(aMsg, aIter, &aResult->y))
return true;
return false;
}
};
template<>
struct ParamTraits<gfxSize>
{

View File

@ -2002,6 +2002,68 @@ nsDisplayOwnLayer::BuildLayer(nsDisplayListBuilder* aBuilder,
return layer.forget();
}
nsDisplayFixedPosition::nsDisplayFixedPosition(nsDisplayListBuilder* aBuilder,
nsIFrame* aFrame,
nsDisplayList* aList)
: nsDisplayOwnLayer(aBuilder, aFrame, aList) {
MOZ_COUNT_CTOR(nsDisplayFixedPosition);
}
#ifdef NS_BUILD_REFCNT_LOGGING
nsDisplayFixedPosition::~nsDisplayFixedPosition() {
MOZ_COUNT_DTOR(nsDisplayFixedPosition);
}
#endif
already_AddRefed<Layer>
nsDisplayFixedPosition::BuildLayer(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
const ContainerParameters& aContainerParameters) {
nsRefPtr<Layer> layer =
nsDisplayOwnLayer::BuildLayer(aBuilder, aManager, aContainerParameters);
// Work out the anchor point for this fixed position layer. We assume that
// any positioning set (left/top/right/bottom) indicates that the
// corresponding side of its container should be the anchor point,
// defaulting to top-left.
nsIFrame* viewportFrame = mFrame->GetParent();
nsPresContext *presContext = viewportFrame->PresContext();
// Fixed position frames are reflowed into the scroll-port size if one has
// been set.
nsSize containingBlockSize = viewportFrame->GetSize();
if (presContext->PresShell()->IsScrollPositionClampingScrollPortSizeSet()) {
containingBlockSize = presContext->PresShell()->
GetScrollPositionClampingScrollPortSize();
}
// Find out the rect of the viewport frame relative to the reference frame.
// This, in conjunction with the container scale, will correspond to the
// coordinate-space of the built layer.
float factor = presContext->AppUnitsPerDevPixel();
nsPoint origin = aBuilder->ToReferenceFrame(viewportFrame);
gfxRect anchorRect(NSAppUnitsToFloatPixels(origin.x, factor) *
aContainerParameters.mXScale,
NSAppUnitsToFloatPixels(origin.y, factor) *
aContainerParameters.mYScale,
NSAppUnitsToFloatPixels(containingBlockSize.width, factor) *
aContainerParameters.mXScale,
NSAppUnitsToFloatPixels(containingBlockSize.height, factor) *
aContainerParameters.mYScale);
gfxPoint anchor(anchorRect.x, anchorRect.y);
const nsStylePosition* position = mFrame->GetStylePosition();
if (position->mOffset.GetRightUnit() != eStyleUnit_Auto)
anchor.x = anchorRect.XMost();
if (position->mOffset.GetBottomUnit() != eStyleUnit_Auto)
anchor.y = anchorRect.YMost();
layer->SetFixedPositionAnchor(anchor);
return layer.forget();
}
nsDisplayScrollLayer::nsDisplayScrollLayer(nsDisplayListBuilder* aBuilder,
nsDisplayList* aList,
nsIFrame* aForFrame,

View File

@ -1909,6 +1909,25 @@ public:
NS_DISPLAY_DECL_NAME("OwnLayer", TYPE_OWN_LAYER)
};
/**
* A display item used to represent fixed position elements. This will ensure
* the contents gets its own layer, and that the built layer will have
* position-related metadata set on it.
*/
class nsDisplayFixedPosition : public nsDisplayOwnLayer {
public:
nsDisplayFixedPosition(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
nsDisplayList* aList);
#ifdef NS_BUILD_REFCNT_LOGGING
virtual ~nsDisplayFixedPosition();
#endif
virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
const ContainerParameters& aContainerParameters);
NS_DISPLAY_DECL_NAME("FixedPosition", TYPE_OWN_LAYER)
};
/**
* This potentially creates a layer for the given list of items, whose
* visibility is determined by the displayport for the given frame instead of

View File

@ -2168,8 +2168,16 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
// Genuine stacking contexts, and positioned pseudo-stacking-contexts,
// go in this level.
if (!list.IsEmpty()) {
rv = aLists.PositionedDescendants()->AppendNewToTop(new (aBuilder)
nsDisplayWrapList(aBuilder, child, &list));
// Make sure the root of a fixed position frame sub-tree gets the
// correct displaylist item type.
nsDisplayItem* item;
if (!child->GetParent()->GetParent() &&
disp->mPosition == NS_STYLE_POSITION_FIXED) {
item = new (aBuilder) nsDisplayFixedPosition(aBuilder, child, &list);
} else {
item = new (aBuilder) nsDisplayWrapList(aBuilder, child, &list);
}
rv = aLists.PositionedDescendants()->AppendNewToTop(item);
NS_ENSURE_SUCCESS(rv, rv);
}
} else if (disp->IsFloating()) {