If we always align the new scroll position with the previous scroll position, we can accumulate error and
gradually drift out of alignment with the layer pixels until we exceed the tolerance and are forced to
rerender everything.
Fix the size check in nsDisplayBackground::ShouldFixToViewport so that async
scrolling of fixed backgrounds works correctly when zoomed in on Firefox
Mobile. Also make IsFixedItem in nsDisplayList public and use it in
FrameLayerBuilder, so that fixed items are determined and treated consistently.
The underlying frame of an nsDisplayScrollLayer can change and end up returning
different values when finding the active scrolled root. Instead of relying on
display-item ordering/merging, get the scrolled frame from the item (which was
already storing it).
This changes the display-item storage to store layers against the underlying
frame and all merged frames of an item (and similarly, to retrieve them against
all frames of an item).
As well as storing the container layer against the underlying frame of the
container item, store it against its merged frames as well. In addition, check
for old container layers against merged frames when building a container layer.
This protects against losing the layer when the underlying frame of a container
item changes to either a new frame or an existing, merged frame.
Rather than relying on a particular ordering of layers in
ContainerState::Finish, use ContainerLayer::Reposition to more reliably remove
old, unused layers.
This changes the display-item storage to store layers against the underlying
frame and all merged frames of an item (and similarly, to retrieve them against
all frames of an item).
As well as storing the container layer against the underlying frame of the
container item, store it against its merged frames as well. In addition, check
for old container layers against merged frames when building a container layer.
This protects against losing the layer when the underlying frame of a container
item changes to either a new frame or an existing, merged frame.
Instead of forcing a full-layer invalidation when new frames appear, invalidate
only the bounds of the frame. Invalidating untrusted geometry still causes a
full-layer invalidation.
As clip items aren't processed, they have no associated layer entries. This is
a problem when a clip item's underlying frame is the same as one that gets
merged into a container layer, as display-item data will be created, but no
layer entry will be added. This will cause it to be removed on the next
layer-build, and cause a full invalidation.
Fix this by adding an 'mIsMergedFrame' entry to DisplayItemDataEntry and setting
it on all merged frames in BuildContainerLayerFor. This property stops the
entry from being removed when it gets updated.
Since Bug 758620, it's possible for an nsDisplayTransform to appear as the
child of a container layer. This caused problems when it was inactive, as the
invalidation would not be transformed in this case.
Fix this in FrameLayerBuilder by mandating that InvalidateThebesLayerContents
takes the untransformed invalidation and apply the transform in
BuildContainerLayer.
Change GetThebesLayerResolutionForFrame to GetThebesLayerScaleForFrame,
which just returns a scale. Ensure that the scale is as accurate as possible
even if dedicated layers for scrolled content (or any layers at all) have not
been created yet, by taking into account transforms that have not yet
generated layers. This makes the decisions made by
nsGfxScrollFrameInner::ScrollToImpl independent of whether there is
currently an active layer for the scrolled content (or much more nearly so).
In nsGfxScrollFrameInner::ScrollToImpl, do not use the current internal
fractional offset of the ThebesLayer, which is in a mostly unrelated
coordinate space to our scroll positions. Instead, just try to make sure
that the previous and next scroll position differ by a whole number of
layer pixels.
Change GetThebesLayerResolutionForFrame to GetThebesLayerScaleForFrame,
which just returns a scale. Ensure that the scale is as accurate as possible
even if dedicated layers for scrolled content (or any layers at all) have not
been created yet, by taking into account transforms that have not yet
generated layers. This makes the decisions made by
nsGfxScrollFrameInner::ScrollToImpl independent of whether there is
currently an active layer for the scrolled content (or much more nearly so).
In nsGfxScrollFrameInner::ScrollToImpl, do not use the current internal
fractional offset of the ThebesLayer, which is in a mostly unrelated
coordinate space to our scroll positions. Instead, just try to make sure
that the previous and next scroll position differ by a whole number of
layer pixels.
Change GetThebesLayerResolutionForFrame to GetThebesLayerScaleForFrame,
which just returns a scale. Ensure that the scale is as accurate as possible
even if dedicated layers for scrolled content (or any layers at all) have not
been created yet, by taking into account transforms that have not yet
generated layers. This makes the decisions made by
nsGfxScrollFrameInner::ScrollToImpl independent of whether there is
currently an active layer for the scrolled content (or much more nearly so).
In nsGfxScrollFrameInner::ScrollToImpl, do not use the current internal
fractional offset of the ThebesLayer, which is in a mostly unrelated
coordinate space to our scroll positions. Instead, just try to make sure
that the previous and next scroll position differ by a whole number of
layer pixels.
A comment in ApplyThebesLayerInvalidation says that it preserves the content
of ThebesLayerInvalidRegion, in case there are multiple container layers for
the same frame. SetHasContainerLayer, however, immediately clears said property.
This was causing invalidations to be lost since Bug 758620 on fixed-position
elements, as they were being separated out onto their own layers but were still
merged in the root scroll layer. This is tracked in Bug 769541.
This fixes the problem by storing the new invalid region in DisplayItemDataEntry
and clearing/setting the ThebesLayerInvalidRegion property in the
UpdateDisplayItemData callback from FrameLayerBuilder::WillEndTransaction.
* * *
Bug 753329. Followup: put ThebesLayerInvalidRegionProperty in display-list-builder coordinates so it can be shared by frames with different coordinate systems. r=mattwoodrow
This patch moves some stuff that needs to be done for every item up to the front of the function, then checks to
see if the item is being added in an area that's already opaque and visible. If so (and assuming the
color-layer optimization has already been disabled), none of the rest of the method would do anything so we
can exit early. In particular we avoid calling IsUniform and GetOpaqueRegion on the item, and we also avoid
some expensive region manipulation.
Currently we return an extra out parameter on GetOpaqueRegion. This is ugly and it's also going to be inefficient
because in a followup patch I'm going to avoid calls to GetOpaqueRegion, but we still need to know whether the item
needs a transparent surface. So this patch removes that out parameter. Instead, we rely on the fact that only
Windows' glass-window-background display item needs to force a transparent surface, and there can only be one
of those per window. So we store a reference to it in the nsDisplayListBuilder if there is one, and then we can
efficiently tell if any leaf display item is the one that forces a transparent surface. For display items that
wrap a list, we continue to store whether they need to force a transparent surface in a boolean in the list.
Previously we snapped the results of nsDisplayItem::GetBounds and
nsDisplayItem::GetOpaqueRegion internally. By tracking which display items were
inside transforms, we disabled snapping quite conservatively whenever an ancestor
had a transform, which is undesirable.
With this patch, we don't snap inside GetBounds or GetOpaqueRegion, but just return
a boolean flag indicating whether the item will draw with snapping or not. This flag
is conservative so that "true" means we will snap (if the graphics context has a transform
that allows snapping), but "false" means we might or might not snap (so it's always safe
to return false).
FrameLayerBuilder takes over responsibility for snapping item bounds. When it converts
display item bounds to layer pixel coordinates, it checks the snap flag returned from
the display item and checks whether the transform when we draw into the layer will be
a known scale (the ContainerParameters scale factors) plus integer translation. If both
are true, we snap the item bounds when converting to layer pixel coordinates. With
this approach, we can snap item bounds even when the items have ancestors with active
transforms.
While layers are active, scale factors are normalized to a power of 2 to
reduce the number of resolution changes during animations. This patch disables
the normalization when the scale factor on the layer is unchanged, or when
the layer was previously unscaled.
This lets us avoid clamping the scale in more situations. We should only clamp the scale when we think
the scale is changing due to a changing transform --- the goal of clamping is to not have to redraw the content
too often when the content is zooming in or out.
FrameLayerBuilder::BuildContainerLayerFor takes responsibility for resolution scaling. The ContainerParameters
passed in are added to any transform requested. Then we extract the scale part of the transform, round the scale
up to the nearest power of two if the transform may be actively animated (so we don't have to redraw layer contents
constantly), pass that scale down to be applied by each child and set the residual transform on the ContainerLayer.
For child layers built via BuildLayer, we just pass the requested scale factor in via the ContainerParameters.
If the returned layer is a ContainerLayer then BuildLayer is guaranteed to have already done necessary scaling.
If the returned layer is not a ContainerLayer then we apply the scale ourselves by adding the scale to the
child layer's transform.
For child ThebesLayers containing non-layer display items, we scale the drawing of those display items so that
the child ThebesLayers are simply larger or smaller (larger or smaller visible regions).
We have to scale all visible rects, clip rects etc that are in the coordinates of ThebesLayers or the parent
ContainerLayer. To keep things simple we do this whenever we convert from appunits to integer layer coordinates.
When a ThebesLayer's resolution changes we need to rerender the whole thing.
nsDisplayList::PaintForFrame needs to respect the presshell's resolution setting. We do that by building a layer tree
with a ContainerParameters requesting a scale up by the presshell resolution; once that layer tree is built, we
adjust the root layer transform to scale back down by the resolution.
This patch shouldn't change any behavior. It just passes the ContainerParameters around, which will contain scale factors that should have been
applied when BuildLayer returns a ContainerLayer.
This patch also adds an aTransform parameter to BuildContainerLayerFor, which nsDisplayTransform uses to set the
transform for the ContainerLayer. This way BuildContainerLayerFor knows what the container's transform is going to be
before constructing the children, which in the next patch will let us construct the children with the right resolution.
The basic idea is that whenever a layer transaction updates the window, we clear out the invalidation state for the canvas rendering context,
using a DidTransactionCallback registered on the layer(s) for the canvas, which calls MakeContextClean.
The DidTransactionCallbacks are directed to the user data attached to the Layer, which holds a strong reference to the canvas element. This
ensures that the element lives as long as the layer. Layers are destroyed when the presentation is torn down (including if the frame is destroyed),
so we can't have a leak here. The reference to the canvas element is only strong because the layer might briefly outlive the frame (the layer
won't be destroyed until the next paint of the window).
This patch moves responsibility for calling CanvasLayer::Updated and nsFrame::MarkLayersActive from the canvas context to nsHTMLCanvasElement::InvalidateFrame.
We call Updated on the retained CanvasLayer, if there is one; any other CanvasLayers created for this canvas would only be used once, and have Updated
called on them in BuildLayer when created.
The basic idea is that whenever a layer transaction updates the window, we clear out the invalidation state for the canvas rendering context,
using a DidTransactionCallback registered on the layer(s) for the canvas, which calls MakeContextClean.
The DidTransactionCallbacks are directed to the user data attached to the Layer, which holds a strong reference to the canvas element. This
ensures that the element lives as long as the layer. Layers are destroyed when the presentation is torn down (including if the frame is destroyed),
so we can't have a leak here. The reference to the canvas element is only strong because the layer might briefly outlive the frame (the layer
won't be destroyed until the next paint of the window).
This patch moves responsibility for calling CanvasLayer::Updated and nsFrame::MarkLayersActive from the canvas context to nsHTMLCanvasElement::InvalidateFrame.
We call Updated on the retained CanvasLayer, if there is one; any other CanvasLayers created for this canvas would only be used once, and have Updated
called on them in BuildLayer when created.
This avoids problems with FrameLayerBuilder making the visible region bigger than we expected, invalidating CONTENT_OPAQUE flags set on the layer.
In particular, we had been using TransformBounds to compute the new visible region, and for non-axis-aligned transforms this gives us a visible
region which contains areas not actually painted by the layer contents.
It's not correct to completely fill the destination context with the
mForcedBackgroundColor. FindOpaqueBackgroundColorFor only checks that
the bounds of the layer's visible region can be filled with the color.
Drawing outside the bounds of the visible region may not necessarily
be ignored (see comments on Layer::SetVisibleRegion).