Bug 615870. Part 2: Track per-display-root-frame 'update layer tree' state bit. r=tnikkel a=b-f
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -6072,21 +6072,32 @@ PresShell::Paint(nsIView* aDis
AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Paint);
NS_ASSERTION(!mIsDestroying, "painting a destroyed PresShell");
NS_ASSERTION(aDisplayRoot, "null view");
NS_ASSERTION(aViewToPaint, "null view");
NS_ASSERTION(aWidgetToPaint, "Can't paint without a widget");
- nscolor bgcolor = ComputeBackstopColor(aDisplayRoot);
-
nsIFrame* frame = aPaintDefaultBackground
? nsnull : static_cast<nsIFrame*>(aDisplayRoot->GetClientData());
+ LayerManager* layerManager = aWidgetToPaint->GetLayerManager();
+ NS_ASSERTION(layerManager, "Must be in paint event");
+
+ if (frame) {
+ if (!(frame->GetStateBits() & NS_FRAME_UPDATE_LAYER_TREE)) {
+ if (layerManager->DoEmptyTransaction())
+ return NS_OK;
+ }
+ frame->RemoveStateBits(NS_FRAME_UPDATE_LAYER_TREE);
+ }
+
+ nscolor bgcolor = ComputeBackstopColor(aDisplayRoot);
+
if (frame && aViewToPaint == aDisplayRoot) {
// Defer invalidates that are triggered during painting, and discard
// invalidates of areas that are already being repainted.
// The layer system can trigger invalidates during painting
// (see FrameLayerBuilder).
frame->BeginDeferringInvalidatesForDisplayRoot(aDirtyRegion);
// We can paint directly into the widget using its layer manager.
@@ -6101,19 +6112,16 @@ PresShell::Paint(nsIView* aDis
}
if (frame) {
// Defer invalidates that are triggered during painting, and discard
// invalidates of areas that are already being repainted.
frame->BeginDeferringInvalidatesForDisplayRoot(aDirtyRegion);
}
- LayerManager* layerManager = aWidgetToPaint->GetLayerManager();
- NS_ASSERTION(layerManager, "Must be in paint event");
-
layerManager->BeginTransaction();
nsRefPtr<ThebesLayer> root = layerManager->CreateThebesLayer();
if (root) {
root->SetVisibleRegion(aIntDirtyRegion);
layerManager->SetRoot(root);
}
if (!frame) {
bgcolor = NS_ComposeColors(bgcolor, mCanvasBackgroundColor);
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -3945,17 +3945,23 @@ nsIFrame::InvalidateLayer(const nsRect&
{
NS_ASSERTION(aDisplayItemKey > 0, "Need a key");
if (!FrameLayerBuilder::HasDedicatedLayer(this, aDisplayItemKey)) {
Invalidate(aDamageRect);
return;
}
- InvalidateWithFlags(aDamageRect, INVALIDATE_NO_THEBES_LAYERS);
+ PRUint32 flags = INVALIDATE_NO_THEBES_LAYERS;
+ if (aDisplayItemKey == nsDisplayItem::TYPE_VIDEO ||
+ aDisplayItemKey == nsDisplayItem::TYPE_PLUGIN) {
+ flags = INVALIDATE_NO_UPDATE_LAYER_TREE;
+ }
+
+ InvalidateWithFlags(aDamageRect, flags);
}
class LayerActivity {
public:
LayerActivity(nsIFrame* aFrame) : mFrame(aFrame) {}
~LayerActivity();
nsExpirationState* GetExpirationState() { return &mState; }
@@ -4251,16 +4257,20 @@ nsIFrame::InvalidateRoot(const nsRect& a
nsRegion r;
r.Sub(rect, *excludeRegion);
if (r.IsEmpty())
return;
rect = r.GetBounds();
}
}
+ if (!(aFlags & INVALIDATE_NO_UPDATE_LAYER_TREE)) {
+ AddStateBits(NS_FRAME_UPDATE_LAYER_TREE);
+ }
+
nsIView* view = GetView();
NS_ASSERTION(view, "This can only be called on frames with views");
view->GetViewManager()->UpdateViewNoSuppression(view, rect, flags);
}
void
nsIFrame::BeginDeferringInvalidatesForDisplayRoot(const nsRegion& aExcludeRegion)
{
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -268,16 +268,22 @@ typedef PRUint64 nsFrameState;
// Frame or one of its (cross-doc) descendants may have the
// NS_FRAME_HAS_CONTAINER_LAYER bit.
#define NS_FRAME_HAS_CONTAINER_LAYER_DESCENDANT NS_FRAME_STATE_BIT(34)
// Frame's overflow area was clipped by the 'clip' property.
#define NS_FRAME_HAS_CLIP NS_FRAME_STATE_BIT(35)
+// Frame is a display root and the retained layer tree needs to be updated
+// at the next paint via display list construction.
+// Only meaningful for display roots, so we don't really need a global state
+// bit; we could free up this bit with a little extra complexity.
+#define NS_FRAME_UPDATE_LAYER_TREE NS_FRAME_STATE_BIT(36)
+
// The lower 20 bits and upper 32 bits of the frame state are reserved
// by this API.
#define NS_FRAME_RESERVED ~NS_FRAME_IMPL_RESERVED
// Box layout bits
#define NS_STATE_IS_HORIZONTAL NS_FRAME_STATE_BIT(22)
#define NS_STATE_IS_DIRECTION_NORMAL NS_FRAME_STATE_BIT(31)
@@ -2046,27 +2052,32 @@ public:
* then invalidation in the current paint region is simply discarded.
* Use this flag if areas that are being painted do not need
* to be invalidated. By default, when this flag is not specified,
* areas that are invalidated while currently being painted will be repainted
* again.
* This flag is useful when, during painting, FrameLayerBuilder discovers that
* a region of the window needs to be drawn differently, and that region
* may or may not be contained in the currently painted region.
+ * @param aFlags INVALIDATE_NO_UPDATE_LAYER_TREE: display lists and the
+ * layer tree do not need to be updated. This can be used when the layer
+ * tree has already been updated outside a transaction, e.g. via
+ * ImageContainer::SetCurrentImage.
*/
enum {
INVALIDATE_IMMEDIATE = 0x01,
INVALIDATE_CROSS_DOC = 0x02,
INVALIDATE_REASON_SCROLL_BLIT = 0x04,
INVALIDATE_REASON_SCROLL_REPAINT = 0x08,
INVALIDATE_REASON_MASK = INVALIDATE_REASON_SCROLL_BLIT |
INVALIDATE_REASON_SCROLL_REPAINT,
INVALIDATE_NO_THEBES_LAYERS = 0x10,
INVALIDATE_ONLY_THEBES_LAYERS = 0x20,
- INVALIDATE_EXCLUDE_CURRENT_PAINT = 0x40
+ INVALIDATE_EXCLUDE_CURRENT_PAINT = 0x40,
+ INVALIDATE_NO_UPDATE_LAYER_TREE = 0x80
};
virtual void InvalidateInternal(const nsRect& aDamageRect,
nscoord aOffsetX, nscoord aOffsetY,
nsIFrame* aForChild, PRUint32 aFlags);
/**
* Helper function that funnels an InvalidateInternal request up to the
* parent. This function is used so that if MOZ_SVG is not defined, we still