Bug 946502. Part 3: Extend FrameLayerBuilder to set fixed-pos metadata on layers created for background-attachment:fixed content. r=mattwoodrow

--HG--
extra : rebase_source : dbaab718d7a358bfaa40124913bcd5acf0255aa1
This commit is contained in:
Robert O'Callahan 2013-12-18 00:30:21 +13:00
parent 6136dbc17c
commit cac6eb1c2d
4 changed files with 145 additions and 80 deletions

View File

@ -607,6 +607,8 @@ protected:
* has a displayport. Updates *aVisibleRegion to be the intersection of
* aDrawRegion and the displayport, and updates *aIsSolidColorInVisibleRegion
* (if non-null) to false if the visible region grows.
* This can return the actual viewport frame for layers whose display items
* are directly on the viewport (e.g. background-attachment:fixed backgrounds).
*/
const nsIFrame* FindFixedPosFrameForLayerData(const nsIFrame* aAnimatedGeometryRoot,
const nsIntRegion& aDrawRegion,
@ -1661,31 +1663,44 @@ ContainerState::FindFixedPosFrameForLayerData(const nsIFrame* aAnimatedGeometryR
nsIntRegion* aVisibleRegion,
bool* aIsSolidColorInVisibleRegion)
{
nsIFrame *viewport = mContainerFrame->PresContext()->PresShell()->GetRootFrame();
// Viewports with no fixed-pos frames are not relevant.
if (!viewport->GetFirstChild(nsIFrame::kFixedList)) {
return nullptr;
}
nsPresContext* presContext = mContainerFrame->PresContext();
nsIFrame* viewport = presContext->PresShell()->GetRootFrame();
const nsIFrame* result = nullptr;
nsRect displayPort;
for (const nsIFrame* f = aAnimatedGeometryRoot; f; f = f->GetParent()) {
if (nsLayoutUtils::IsFixedPosFrameInDisplayPort(f, &displayPort)) {
// Display ports are relative to the viewport, convert it to be relative
// to our reference frame.
displayPort += viewport->GetOffsetToCrossDoc(mContainerReferenceFrame);
nsIntRegion newVisibleRegion;
newVisibleRegion.And(ScaleToOutsidePixels(displayPort, false),
aDrawRegion);
if (!aVisibleRegion->Contains(newVisibleRegion)) {
if (aIsSolidColorInVisibleRegion) {
*aIsSolidColorInVisibleRegion = false;
}
*aVisibleRegion = newVisibleRegion;
if (viewport == aAnimatedGeometryRoot &&
nsLayoutUtils::ViewportHasDisplayPort(presContext, &displayPort)) {
// Probably a background-attachment:fixed item
result = viewport;
} else {
// Viewports with no fixed-pos frames are not relevant.
if (!viewport->GetFirstChild(nsIFrame::kFixedList)) {
return nullptr;
}
for (const nsIFrame* f = aAnimatedGeometryRoot; f; f = f->GetParent()) {
if (nsLayoutUtils::IsFixedPosFrameInDisplayPort(f, &displayPort)) {
result = f;
break;
}
return f;
}
if (!result) {
return nullptr;
}
}
return nullptr;
// Display ports are relative to the viewport, convert it to be relative
// to our reference frame.
displayPort += viewport->GetOffsetToCrossDoc(mContainerReferenceFrame);
nsIntRegion newVisibleRegion;
newVisibleRegion.And(ScaleToOutsidePixels(displayPort, false),
aDrawRegion);
if (!aVisibleRegion->Contains(newVisibleRegion)) {
if (aIsSolidColorInVisibleRegion) {
*aIsSolidColorInVisibleRegion = false;
}
*aVisibleRegion = newVisibleRegion;
}
return result;
}
void
@ -1697,19 +1712,31 @@ ContainerState::SetFixedPositionLayerData(Layer* aLayer,
return;
}
nsIFrame* viewportFrame = aFixedPosFrame->GetParent();
nsPresContext* presContext = aFixedPosFrame->PresContext();
// Fixed position frames are reflowed into the scroll-port size if one has
// been set.
nsSize viewportSize = viewportFrame->GetSize();
if (presContext->PresShell()->IsScrollPositionClampingScrollPortSizeSet()) {
viewportSize = presContext->PresShell()->
GetScrollPositionClampingScrollPortSize();
const nsIFrame* viewportFrame = aFixedPosFrame->GetParent();
// anchorRect will be in the container's coordinate system (aLayer's parent layer).
// This is the same as the display items' reference frame.
nsRect anchorRect;
if (viewportFrame) {
// Fixed position frames are reflowed into the scroll-port size if one has
// been set.
if (presContext->PresShell()->IsScrollPositionClampingScrollPortSizeSet()) {
anchorRect.SizeTo(presContext->PresShell()->GetScrollPositionClampingScrollPortSize());
} else {
anchorRect.SizeTo(viewportFrame->GetSize());
}
} 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 = aFixedPosFrame;
}
// The anchorRect top-left is always the viewport top-left.
anchorRect.MoveTo(viewportFrame->GetOffsetToCrossDoc(mContainerReferenceFrame));
nsLayoutUtils::SetFixedPositionLayerData(aLayer,
viewportFrame, viewportSize, aFixedPosFrame, presContext, mParameters);
viewportFrame, anchorRect, aFixedPosFrame, presContext, mParameters);
}
static gfx3DMatrix
@ -2131,6 +2158,14 @@ ContainerState::FindThebesLayerFor(nsDisplayItem* aItem,
thebesLayerData->mLayer = layer;
thebesLayerData->mAnimatedGeometryRoot = aActiveScrolledRoot;
thebesLayerData->mReferenceFrame = aItem->ReferenceFrame();
if (!aActiveScrolledRoot->GetParent() &&
nsLayoutUtils::ViewportHasDisplayPort(aActiveScrolledRoot->PresContext())) {
// The active scrolled root is the viewport, so this is background-attachment:fixed
// or fixed-pos elements or something like that. Async scrolling may
// do magic things to move these layers, so don't allow any regular content
// to be pushed to layers below them; that might turn out to be incorrect.
thebesLayerData->SetAllDrawingAbove();
}
NS_ASSERTION(!mNewChildLayers.Contains(layer), "Layer already in list???");
*mNewChildLayers.AppendElement() = layer.forget();

View File

@ -3455,9 +3455,9 @@ nsDisplayStickyPosition::BuildLayer(nsDisplayListBuilder* aBuilder,
GetScrollPositionClampingScrollPortSize();
}
nsLayoutUtils::SetFixedPositionLayerData(layer, scrollFrame, scrollFrameSize,
mStickyPosFrame,
presContext, aContainerParameters);
nsLayoutUtils::SetFixedPositionLayerData(layer, scrollFrame,
nsRect(scrollFrame->GetOffsetToCrossDoc(ReferenceFrame()), scrollFrameSize),
mStickyPosFrame, presContext, aContainerParameters);
ViewID scrollId = nsLayoutUtils::FindOrCreateIDFor(
stickyScrollContainer->ScrollFrame()->GetScrolledFrame()->GetContent());

View File

@ -1203,7 +1203,7 @@ nsLayoutUtils::GetScrollableFrameFor(const nsIFrame *aScrolledFrame)
/* static */ void
nsLayoutUtils::SetFixedPositionLayerData(Layer* aLayer,
const nsIFrame* aViewportFrame,
nsSize aViewportSize,
const nsRect& aAnchorRect,
const nsIFrame* aFixedPosFrame,
nsPresContext* aPresContext,
const ContainerLayerParameters& aContainerParameters) {
@ -1211,41 +1211,31 @@ nsLayoutUtils::SetFixedPositionLayerData(Layer* aLayer,
// This, in conjunction with the container scale, will correspond to the
// coordinate-space of the built layer.
float factor = aPresContext->AppUnitsPerDevPixel();
nsPoint origin = aViewportFrame->GetOffsetToCrossDoc(aFixedPosFrame);
LayerRect anchorRect(NSAppUnitsToFloatPixels(origin.x, factor) *
aContainerParameters.mXScale,
NSAppUnitsToFloatPixels(origin.y, factor) *
aContainerParameters.mYScale,
NSAppUnitsToFloatPixels(aViewportSize.width, factor) *
aContainerParameters.mXScale,
NSAppUnitsToFloatPixels(aViewportSize.height, factor) *
aContainerParameters.mYScale);
Rect anchorRect(NSAppUnitsToFloatPixels(aAnchorRect.x, factor) *
aContainerParameters.mXScale,
NSAppUnitsToFloatPixels(aAnchorRect.y, factor) *
aContainerParameters.mYScale,
NSAppUnitsToFloatPixels(aAnchorRect.width, factor) *
aContainerParameters.mXScale,
NSAppUnitsToFloatPixels(aAnchorRect.height, factor) *
aContainerParameters.mYScale);
// Need to transform anchorRect from the container layer's coordinate system
// into aLayer's coordinate system.
Matrix transform2d;
if (aLayer->GetTransform().Is2D(&transform2d)) {
transform2d.Invert();
anchorRect = transform2d.TransformBounds(anchorRect);
} else {
NS_ERROR("3D transform found between fixedpos content and its viewport (should never happen)");
anchorRect = Rect(0,0,0,0);
}
// 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.
LayerPoint anchor = anchorRect.TopLeft();
const nsStylePosition* position = aFixedPosFrame->StylePosition();
if (position->mOffset.GetRightUnit() != eStyleUnit_Auto) {
if (position->mOffset.GetLeftUnit() != eStyleUnit_Auto) {
anchor.x = anchorRect.x + anchorRect.width / 2.f;
} else {
anchor.x = anchorRect.XMost();
}
}
if (position->mOffset.GetBottomUnit() != eStyleUnit_Auto) {
if (position->mOffset.GetTopUnit() != eStyleUnit_Auto) {
anchor.y = anchorRect.y + anchorRect.height / 2.f;
} else {
anchor.y = anchorRect.YMost();
}
}
aLayer->SetFixedPositionAnchor(anchor);
// Also make sure the layer is aware of any fixed position margins that have
LayerPoint anchor(anchorRect.x, anchorRect.y);
// Make sure the layer is aware of any fixed position margins that have
// been set.
nsMargin fixedMargins = aPresContext->PresShell()->GetContentDocumentFixedPositionMargins();
LayerMargin fixedLayerMargins(NSAppUnitsToFloatPixels(fixedMargins.top, factor) *
@ -1257,21 +1247,49 @@ nsLayoutUtils::SetFixedPositionLayerData(Layer* aLayer,
NSAppUnitsToFloatPixels(fixedMargins.left, factor) *
aContainerParameters.mXScale);
// If the frame is auto-positioned on either axis, set the top/left layer
// margins to -1, to indicate to the compositor that this layer is
// unaffected by fixed margins.
if (position->mOffset.GetLeftUnit() == eStyleUnit_Auto &&
position->mOffset.GetRightUnit() == eStyleUnit_Auto) {
fixedLayerMargins.left = -1;
}
if (position->mOffset.GetTopUnit() == eStyleUnit_Auto &&
position->mOffset.GetBottomUnit() == eStyleUnit_Auto) {
fixedLayerMargins.top = -1;
if (aFixedPosFrame != aViewportFrame) {
const nsStylePosition* position = aFixedPosFrame->StylePosition();
if (position->mOffset.GetRightUnit() != eStyleUnit_Auto) {
if (position->mOffset.GetLeftUnit() != eStyleUnit_Auto) {
anchor.x = anchorRect.x + anchorRect.width / 2.f;
} else {
anchor.x = anchorRect.XMost();
}
}
if (position->mOffset.GetBottomUnit() != eStyleUnit_Auto) {
if (position->mOffset.GetTopUnit() != eStyleUnit_Auto) {
anchor.y = anchorRect.y + anchorRect.height / 2.f;
} else {
anchor.y = anchorRect.YMost();
}
}
// If the frame is auto-positioned on either axis, set the top/left layer
// margins to -1, to indicate to the compositor that this layer is
// unaffected by fixed margins.
if (position->mOffset.GetLeftUnit() == eStyleUnit_Auto &&
position->mOffset.GetRightUnit() == eStyleUnit_Auto) {
fixedLayerMargins.left = -1;
}
if (position->mOffset.GetTopUnit() == eStyleUnit_Auto &&
position->mOffset.GetBottomUnit() == eStyleUnit_Auto) {
fixedLayerMargins.top = -1;
}
}
aLayer->SetFixedPositionAnchor(anchor);
aLayer->SetFixedPositionMargins(fixedLayerMargins);
}
bool
nsLayoutUtils::ViewportHasDisplayPort(nsPresContext* aPresContext, nsRect* aDisplayPort)
{
nsIFrame* rootScrollFrame =
aPresContext->PresShell()->GetRootScrollFrame();
return rootScrollFrame &&
nsLayoutUtils::GetDisplayPort(rootScrollFrame->GetContent(), aDisplayPort);
}
bool
nsLayoutUtils::IsFixedPosFrameInDisplayPort(const nsIFrame* aFrame, nsRect* aDisplayPort)
{
@ -1283,12 +1301,7 @@ nsLayoutUtils::IsFixedPosFrameInDisplayPort(const nsIFrame* aFrame, nsRect* aDis
aFrame->StyleDisplay()->mPosition != NS_STYLE_POSITION_FIXED) {
return false;
}
nsIFrame* rootScrollFrame =
aFrame->PresContext()->PresShell()->GetRootScrollFrame();
// Treat a fixed-pos frame as an animated geometry root if it belongs to
// a viewport which has a scrollframe and a displayport.
return rootScrollFrame &&
nsLayoutUtils::GetDisplayPort(rootScrollFrame->GetContent(), aDisplayPort);
return ViewportHasDisplayPort(aFrame->PresContext(), aDisplayPort);
}
nsIFrame*

View File

@ -375,12 +375,29 @@ public:
static bool IsAncestorFrameCrossDoc(const nsIFrame* aAncestorFrame, const nsIFrame* aFrame,
const nsIFrame* aCommonAncestor = nullptr);
/**
* Sets the fixed-pos metadata properties on aLayer.
* aAnchorRect is the basic anchor rectangle. If aFixedPosFrame is not a viewport
* frame, then we pick a corner of aAnchorRect to as the anchor point for the
* fixed-pos layer (i.e. the point to remain stable during zooming), based
* on which of the fixed-pos frame's CSS absolute positioning offset
* properties (top, left, right, bottom) are auto. aAnchorRect is in the
* coordinate space of aLayer's container layer (i.e. relative to the reference
* frame of the display item which is building aLayer's container layer).
*/
static void SetFixedPositionLayerData(Layer* aLayer, const nsIFrame* aViewportFrame,
nsSize aViewportSize,
const nsRect& aAnchorRect,
const nsIFrame* aFixedPosFrame,
nsPresContext* aPresContext,
const ContainerLayerParameters& aContainerParameters);
/**
* Return true if aPresContext's viewport has a displayport.
* Fills in aDisplayPort with the displayport rectangle if non-null.
*/
static bool ViewportHasDisplayPort(nsPresContext* aPresContext,
nsRect* aDisplayPort = nullptr);
/**
* Return true if aFrame is a fixed-pos frame and is a child of a viewport
* which has a displayport. These frames get special treatment from the compositor.