When you mouse over a <select> element that displays as a combobox, or a scrolling list, we invalidate an area that is the size of all the child frames as if they were visible. This can cause the invalidate to hit other frames, which may be expensive-to-redraw things like plugins etc. The reason seems to be some code in UpdateViewsForTree() (in nsCSSFrameConstuctor) that naively walks the child frames of the frame whose state has changed (the combobox frame), doing a union of the bounds of the child frames. In the case of combox boxes, those child frames might not be visble, either because their containing view (the dropdown) is hidden, or because they are part of a scrolling view, and scrolled out of view.
Solving the case when we're clipped by a scrollframe could be tough... But shouldn't we just ignore invalidates on views that are hidden? In particular, I think we should bail out of nsViewManager::UpdateView if the view is hidden, and when doing UpdateViewsForTree we should only do out-of-flows after we hit a hidden view (I _think_ that should be ok, right?). Alternately, should setting a view's visibility to kHide somehow mark all its descendant views so we can easily tell that we're in a non-painting subtree?
Yeah, in theory we need not paint any view that is hidden or has a hidden view ancestor.
We have some code to that effect in nsViewManager::SetViewVisibility, but it doesn't set the visibility of descendant views with frames.... should it?
I don't think we should recursively change the visibility status of child views. It's OK for hiding but not when you make the parent view visible again. Instead we should just check ancestors on the fly when we make visibility decisions.
The problem with checking ancestors is that we're trying to optimize based on visibility, and checking ancestors may end up more expensive...
View trees are usually quite shallow.