Gecko:Invalidation Handling

From MozillaWiki
Jump to: navigation, search

This is perhaps out of date and in need of updating, especially once views are removed and in light of dbaron's reflow branch.

Currently our rules for invalidation are quite inconsistent, in particular the rules for who is responsible for invalidating what during reflow.

Currently, I believe the rules include:

  • a frame that moves a child frame must repaint the union of the old and new bounds
  • a frame that resizes a child frame must paint the difference between the old and new bounds

We're inconsistent about whether 'bounds' means the actual frame bounds or the overflowArea.

Sometimes we suppress invalidation, e.g. during initial reflow, for performance reasons. This needs to be systematized.

Moving a view always repaints the union of the old and new view areas (which is the union of all frames contained by the view), and resizing a view always repaints the difference. I think we rely on this in some places to avoid frame invalidation. But it's not very efficient. For example if a frame has overflowing absolute frames that move around, then the frame's view will resize and we'll invalidate large horizontal and vertical strips. Graphically:

  +-------+ *********
  |Frame F| *********
  |       | *********
  |       | +--+ +--+
  |       | |A | |A'|
  |       | +--+ +--+
  |       | *********
  +-------+ *********

If A is an absolute child of F and it moves from A to A', then we'll repaint the area marked with asterisks when we only need to repaint the area covered by A and A'.

One thing we need to be careful about is that currently nsIFrame::Invalidate just issues an UpdateView against the nearest view containing the frame. The invalidation area is intersected with the view's bounds. So for example a frame with a view cannot invalidate an area bigger than its view without doing something tricky. For the rules below to work, we need to change this so nsIFrame::Invalidate will invalidate pixels even if they lie outside the frame's view. Imagine in the scenario above that frame F's reflow of its absolute children wants to perform the invalidation of A and A'. This will happen *before* F's view is resized, so currently the invalidation of A' would be lost.

Here are some rules that I think would be easy to maintain and also efficient:

  • View movement and resizing does not generate any invalidation.
  • If a frame moves its child frame (i.e., directly or indirectly calls SetRect on it) then it must invalidate the child's old and new overflowAreas by calling Invalidate on the parent. (Because the child frame's overflowing children will have moved too.)
  • A frame should only change size during reflow (by returning a different desiredSize than its current size). Frames that change size are responsible for invalidating themselves as necessary. Most frames (those that don't have an intrinsic rendering such as text or an image) will only need to invalidate on resize if they have styles that require rendering, such as background or borders. We can adapt nsFrame::CheckInvalidateSizeChange to perform all invalidation required by the frame's styles. Note for example that in this scheme a block that changes size and has no background, borders etc is not responsible for invalidating anything except any children it moved.
  • Frames with intrinsic rendering must invalidate themselves when that rendering changes during reflow or for any other reason.
  • Style changes do their own invalidation.
  • We should have a flag in the reflow state to indicate that no invalidation is required --- e.g. because it's an initial reflow and we plan to invalidate the entire window no matter what, or because painting is suppressed, or because we're incrementally reflowing and it's convenient to guarantee that we will invalidate the frame's old and new overflowAreas no matter what.

(Can't we suppress when unconstrained reflows are issued on child frames, as there is a promise that the frames will be reflown with a constrained size? Bernd) Gecko:Roc: I don't think so because the constrained-size reflow will only invalidate what's changed since the last reflow.