SVG:SVG-display-lists
Contents
- 1 Intro
- 2 Project Completion Schedule
- 3 Task breakdown
- 3.1 2 days; DONE: Get basic rect working
- 3.2 Make SVG more compatible with display lists
- 3.3 Get transforms working
- 3.3.1 3 days; DONE-ish: Get <rect> with transform working
- 3.3.2 1 day: Get <image> with transform working
- 3.3.3 3 days: Get <rect> inside a <g> with a transforms working
- 3.3.4 Get outer-<svg> transforms working
- 3.3.5 3 days: Get inner-<svg> transforms working
- 3.3.6 Workweek checkpoint
- 3.3.7 5 days: Get text working
- 3.3.8 1 day: Get textPath working
- 3.4 Get clipping working
- 3.5 3 days: Get group opacity working, with the SVG optimization!
- 3.6 Investigate next steps more thoroughly
- 3.7 2 days: Get reftests to use svg.display-lists.painting.enabled
- 3.8 Implementing SVG effects
- 3.9 2 days: Integrate foreignObject painting
- 3.10 X days: Resolve conflicts with bug 539356
- 3.11 X days: Debug and fix reftest failures
- 4 Follow-up work post-completion
- 5 Some background on display lists
- 6 Concerns
- 7 Some thoughts on invalidation
- 8 Some thoughts on hit-testing
- 9 Some thoughts on clipping
- 10 Checklist
- 11 Old stuff
Intro
This page is about a project to overhaul how the SVG code paints and hit tests by implementing display lists for SVG. In the process, the way we invalidate will also change significantly.
The main bug for this project is bug 614732, and a mercurial patch queue of the current coding progress is also maintained.
This project's goals are:
- major performance wins required for us to catch up with the competition on some high profile SVG demos
- we'll be able to use GFX Layers to avoid invalidation/repainting in important scenarios
- essential for DL-based invalidation in SVG (once that's generally implemented) which will solve pathological invalidation cases
- to enable z-index to be implemented in SVG
Project Completion Schedule
As we are nearing the completion of this project, remaining work items and milestone dates will be tracked in this section.
- Investigate clipPath, filters and mask (May 21 - May 23)
- Enable clipPath support (May 24 - May 29)
- Enable filters support (May 30 - Jun 5)
- feGaussianBlur, 1 day
- other filters except feImage, 1 days
- feImage painting referenced content, 3 days
- Enable mask support (Jun 6 - Jun 8)
- Create a mechanism to allow the existing code paths to be invoked under DLs, 2 days
- Get basic masking working, 1 day
- Get complex masking working, 2 days
- Enable markers support (Jun 11 - Jun 15)
- Markers without effects, 1 day
- Markers with effects, 4 days
- Enable patterns support (Jun 18 - Jun 22)
- Basic patterns, 1 day
- Patterns with effects, 2 days
- Patterns that reference non-SVG, 2 days
- Enable gradients support (Jun 22 - Jun 25)
- Enable foreignObject support (Jun 26 - Jun 27)
- Enable symbols support (Jun 28 - Jun 29)
- Project Completion Review (On or before July 20)
Task breakdown
The following sections break down the various tasks that need to be completed. (It is fully expected that unexpected difficulties will arise as the project progresses and the work required becomes clearer, so new tasks will likely be added and existing tasks may change.)
Hit-testing requires a subset of the SVG features needed for painting, since hit-testing ignores most SVG effects like masking, filters and markers. The plan is to get the features needed for hit-testing implemented using display lists first, then add support for the other features later. This would also allow us to initially ship with display list hit-testing turned on, and painting turned off.
2 days; DONE: Get basic rect working
Start by getting a simple rect with no transforms or even x/y offsets working.
This works by adding appropriate BuildDisplayList methods to various SVG frame classes, and creating a new WIP nsDisplaySVGGeometry class.
Make SVG more compatible with display lists
Either we need to change the SVG code so that it doesn't violate the invariants that the display list infrastructure relies on, or we need to change the display list infrastructure so that it handles SVG specially. Doing the latter would end up making both the display list and SVG code the code much gnarlier. Doing the former will result in cleaner code and give us performance wins orthogonal to the wins we get from using display lists (e.g. fixing bug 309782).
nsDisplayTransform, or whatever we end up using for particular transform sources, also needs to have access to the appropriate transform information and we need to make sure it uses it in the appropriate places and in the appropriate ways.
2 days; DONE: Allow transforms consumers to specify which transforms they want
Right now in SVG we have PrependLocalTransformsTo which prepends _all_ sources of transforms that an element provides. We're going to need to be able to request the transforms for only some of the sources though. Bug 660216 covers the work to allow PrependLocalTransformsTo callers to specify which transforms they want.
GetBounds and GetVisualOverflowRect
The display list infrastructure depends on the nsDisplayItem method GetBounds and the nsFrame method GetVisualOverflowRect returning sane values in order to work. The SVG code currently doesn't have any mechanism to determine and keep up to date the necessary state on its frames to support these methods though. This needs to be fixed so that the display list infrastructure will create display list items for frames when we need it to (and not create lots of extra items when we do not).
5 days; DONE: Kill covered regions
Currently mRect for SVG leaf frames contains the frame's "covered region", which is its bounds in its nsSVGOuterSVGFrame's content area, in app units. (This is very different to the CSS box model frames, where mRect is relative to the frame's geometric parent frame (and is the frame's border rect, in app units).) Since the display list code transforms the dirty rect at it's passed down the display list tree, SVG really needs to provide that code with a local rect for intersect checking with dirty rect.
One step towards satisfying GetBounds is to change what SVG frames store in mRect. This will require switching how we do invalidation though.
An alternative would be to have GetBounds() use GetBBoxContribution for SVG frames, but that requires painting the leaf each time, so we'd want to cache the result somewhere (which implies invalidating that cached value when needed). Since GetBounds() needs to work on SVG container frames too (it's called on nsDisplayTransform, for example), this would mean painting each leaf n times, where n is the number of container nsDisplayItems!! Clearly it would be preferable to store the rect GetBounds() needs, and the obvious way to do this is to get rid of covered regions and make mRect for leaves contain the frame's userspace bounds.
One of the consequences of doing this is that invalidation would need to recurse to all leaves (as before) and, for each leaf, pass mRect up the parent chain, adjusting x/y and/or transforming it with nsDisplayTransform::TransformRectOut at each parent. (If we store mRect on containers, then this would be the same but without descending to the leaves.)
Also note that nsDisplayTransform::GetBounds and nsDisplayTransform::GetFrameBoundsForTransform depend on the given frame's mRect. We're going to need to use a helper function to specialize for SVG. We can use GetBBoxContribution if we've killed covered regions, since the performance of it then won't be terrible.
Implement some sort of ReflowSVG mechanism
Right now nothing is stored in mRect or the overflow rect for SVG container frames. We'll need some sort of lightweight version of reflow for SVG to store and update this information when appropriate.
This is probably going to involve changing the way we invalidate, so that instead of using UpdateCoveredRegion, we will use the reflow mechanism to invalidate the old area, update this state, and then invalidate the new area.
3 days: Design some ReflowSVG infrastructure
Basically we'll need a ReflowSVG method for SVG frames that reflows the frame's children, then update the frame's mRect and overflow rect. (The latter by calling nsIFrame::FinishAndStoreOverflow preferably.) We'll also need some way of marking frames as "dirty", and need to make sure that the various points throughout the code that need to mark them as such, do so. The invalidation code will also need to be updated to work with this new way of doing things.
2 days: Support ReflowSVG on the different frame types
We'll need to work through the various SVG frames making sure that they handle ReflowSVG correctly. No doubt some time will also be needed to work through TBPL test failures.
Get transforms working
There are lots of sources of transforms on different SVG elements, such as 'viewBox', 'transform', 'currentScale', 'currentTranslate', 'x'/'y' offsets, etc.
3 days; DONE-ish: Get <rect> with transform working
This is pretty much working now, except that hit-testing has some bugs due to the SVG frames not having their overflow rect set. (Causing the display list infrastructure to fail to create display list items when the pointer is at certain positions when it should create them.) Once the ReflowSVG work (above) is done, then the hit-testing bugs should be fixed.
1 day: Get <image> with transform working
This work should be fairly similar to the work done for nsSVGPathGeometryFrame. No complications are expected.
3 days: Get <rect> inside a <g> with a transforms working
This depends on the ReflowSVG work, then should be a simple case of using nsDisplayTransform for the transforming, and working in the mRect offsets at the appropriate points. Given that this will be the first container element with a transform to be supported using display lists there may be some unforeseen issues, but nothing major is anticipated.
Get outer-<svg> transforms working
In addition to CSS transforms, outer-<svg> can have transforms as a result of viewBox, currentScale and currentTranslate.
The unique problem posed by viewBox, currentScale and currentTranslate is that they're on the leaf-side of mRect, not the root-side like most other transforms. Unfortunately this violates one of the invariants that is deeply ingrained into nsDisplayTransform and its consumers. For example, these transforms must not have an affect on what GetBounds etc. on the frame with the viewBox returns.
3 days: Create an nsDisplayViewBox class
Accounting for viewBox, currentScale and currentTranslate transforms would be much easier if we could just account for them in the nsDisplayTransform, but a new display list item class will be needed to handle them specially.
2 days: Unexpected issues
nsDisplayTransform and its integration with its consumers is pretty complex, and creating our own variant for viewBox that does some things completely differently and integrating it is probably not going to be straightforward. Some time is likely to be needed to sort out unexpected problems.
3 days: Get inner-<svg> transforms working
Inner-<svg> has x/y as an additional source of transforms, and these have to come between 'transform' and 'viewBox', but unlike viewBox must be included in nsDisplayTransform bounds calculations, etc.
We can still account for the x/y offsets in an nsDisplayTransform item (meaning that mRect x/y will always be zero) so that we should get smooth animations of x/y for free. We should probably always create an nsDisplayTransform for <svg> since x and/or y will almost always be set, and it would be good to be consistent about always creating a stacking context for <svg>. It does mean that we will always have the overhead of nsDisplayTransform for all <svg> elements though, regardless of whether they have a viewBox. Accounting for x/y in an nsDisplayTransform is probably makes the implementation less intuitive for someone looking at the SVG code for the first time though, and e.g. expecting mRect to contain x/y offsets.
Workweek checkpoint
5 days: Get text working
SVG text has some really funky code for munging transforms and the offset of different tspans and glyph spans. It may not take too long to figure out how to rework things for display list handling, but equally it may turn out to be really nasty and take much longer than expected.
1 day: Get textPath working
We have some special transform handling in the textPath code which will need fixed up to work with display lists. Should be fairly easy.
Get clipping working
3 days: SVG element "viewport" clipping
In SVG, clipping for the various SVG container elements that clip is currently done on an ad-hoc basis, but in the display list world it has to be done vie a special display list item. (Also important for avoiding creating unnecessary display list items.) SVG clipping can't be done using the existing nsDisplayClip class since that uses an nsRect and we need to avoid the implied nscoord rounding, but we should be able to create a similar class.
A bit or research into how the CSS world currently does clipping will also be required. There are complications in the CSS world that don't exist for SVG clipping. For example, in the CSS world clipping must NOT create a stacking context, which involves jumping through a bunch of hoops to distribute multiple clip display items across various parts of the display list tree. It's not clear if it's practical or even desirable to do that for SVG, or if the existing code for doing that will interact badly in an SVG world.
1 day: The 'clip' property
We should be able to reuse the "SVG viewport element" display item, in which case it should hopefully be pretty easy to also implement the 'clip' property on SVG elements.
3 days: Get group opacity working, with the SVG optimization!
Ideally we'll use nsDisplayOpacity for group opacity.
One complication is that it's very common for authors to use group opacity on leaf elements that have only one of stroke/fill set. It's critical that we maintain our SVG optimization for this somehow when converting to use nsDisplayOpacity.
Investigate next steps more thoroughly
- Get a simple gradient demo with white to black working
- Simple filter testcase
2 days: Get reftests to use svg.display-lists.painting.enabled
In order to thoroughly test display list painting, we're going to need display lists to paint tests with SDL turned on, but paint the references with it turned off. I'll need to figure out how the reftest harness works, and get it to flip this pref as it flips between painting the tests and references.
Implementing SVG effects
We're probably going to need hack on nsDisplaySVGEffects to get it to work on SVG as well as HTML type frames.
Investigate next items
Break down the following items into smaller parts.
1 day: gradients
For some reason gradients are not painting correctly for transformed rect. We must have some transform dependent code in there somewhere. Shouldn't be hard to fix.
5 days: filters
I'm not clear on what will have to be done differently. Some investigation work will be required, but hopefully that and the implementation work will not take more than a week.
Filters do some funky things with transforms to try and prevent pixelation when filtered content is zoomed, which depends on passing around various transforms to different "filter", "user" and "device" spaces, and creating offscreen surfaces of sizes that take into account the right one. The code is a bit hard to follow, so I'm a bit concerned it will take longer to understand and fix than one might expect.
5 days: clipPath
Adding support for clipPath in a display list world is going to involve creating a distinct display list for the clipPath descendants. This is less that straightforward since we will now no longer have an <svg> element at the root, and a lot of the SVG code assumes that that's always the case. We're going to have to do something special for clipPath to make sure everything works correctly.
We should probably treat clipPath the same as normal CSS clipping, with regards to trying to make it NOT create a stacking context. It's unclear what that will involve though, and whether the performance hit will be acceptable.
5 days: mask
Implementing clipPath should teach us a lot that will be useful for implementing mask, since the idea of painting into an offscreen surface and then using that to paint the frame with the affect is similar.
5 days: markers
We should teach nsLayoutUtils::PaintFrame to paint the marks. That will create a temporary display list for the contents of the marker.
One complication is that ToReferenceFrame on marker children will look up through the marker, but we'd need it to look up through the frame that's being marked. Maybe we can make the marker frame itself a reference frame?? Also what are we going to do about mRect and the visible overflow region for frames inside a marker?? Can their values depend on the frame that is being marked, or are they always going to be constant?
X days: symbol and pattern
These both have drawable contents. If those drawable contents are supposed to draw via display lists, then we are going to have to do some magic to get them to draw.
2 days: Integrate foreignObject painting
X days: Resolve conflicts with bug 539356
The work in bug 539356 to use display list comparison for invalidation is changing the display list code. That work will rot the patches for this project, and may make architectural changes that will require things to be reworked. Hopefully not...
X days: Debug and fix reftest failures
Among the thousands of SVG reftests we have some really funky tests with complicated and weird combinations of SVG effects. It's a virtual certainty that there will be failures and some tricky problems to debug.
Follow-up work post-completion
10 days: Map the 'transform' attribute into style
The problem with doing this is that no other SMIL animatable attributes are mapped into style. There's also the lack of a concept of animVal/baseVal for CSS properties. Should SMIL animation values override explicit CSS values? Should the CSS computed value be derived from/be affected by the SMIL animated value? Mapping transform into style places a big question mark over element.transform.baseVal/.animVal.
It seems ridiculous that we're ending up with so many versions of a property:
- CSS animated value
- CSS computed value
- CSS specified value
- animated attribute value
- base (attribute) value
- initial CSS value
5 days: Implementing GetOpaqueRegion on DL items to get occlusion culling (likely perf win)
5 days: Get z-index working in SVG
See bug 360148.
Make sure that 'clip' property clipping works
Make sure that 'clip' property clipping works properly on SVG, both for painting and hit testing. nsIFrame::FinishAndStoreOverflow uses it, so make sure we're doing the right thing there too.
Other stuff
Does http://www.croczilla.com/bits_and_pieces/svg/samples/xulsvg1/xulsvg1.xul crash still?
Instead of using IsScrollableOverflow, add an inline method nsStyleDisplay::IsOverflowVisible() that just checks |mOverflowX == NS_STYLE_OVERFLOW_VISIBLE| and use that instead.
Check GetCanvasTM callers, since a lot of uses should be redundant after DLs.
Need to make sure the spec says to handle things such as markers as atomic units that cannot have things inserted between their component parts.
Is anyone going to be unhappy that transforms create a stacking context? Are there any use cases for wanting items under a transform to be moved out of the transformed element?
SetIgnoreScrollFrame - for painting the whole document without window clipping or strollbars. Could this be given a better name indicating that that's what it's for? SetIgnoreScrollFrameClipping? SetClipPaintingToViewport? SetIgnoreViewportForPainting? SetPaintWholeDoc?
pseudoStackingContext - grr, change this to either just stackingContext or to full/realOrPseudoStackingContext otherwise I'm going to keep thinking it's only pseudo.
GetResultingTransformMatrix probably needs a better name now.
Document nsDisplayTransform::HitTest explaining that as the rect is passed down towards the leaves we transform it into local frame space. We don't transform the frame's bounds into global space.
Find cases of SVG where there are a lot of elements on the screen. How common is this?
We transform dirty rects from root space into the local space of the element. See |dirtyRect = nsDisplayTransform::UntransformRect| in nsIFrame::BuildDisplayListForStackingContext. When there are a chain of transforms with rotation, we could end up expanding the dirty rect quite a bit more than necessary. It may be better to replace the four value rect (x,y,w,h) with a six value parallelogram (x,y of three points).
I think nsSVGUtils::GetCTM is misusing PrependLocalTransformsTo - surely it should not be including anything after the userspace on the element. Testing whether getCTM on an 'svg' with a viewBox include the x,y and viewBox, unfortunately it seems that FF, Op and Ch all do include these sadly. Not sure about IE.
Similarly, I think GetBBoxContribution is also doing the wrong thing for the element on which getBBox is called, in that it should only get the transform from the coordinate space established for the element's children, to the element's userspace. In other words, on a <g> element, getBBox should get a bounds in the g's userspace, not in the space established by its parent. Probably the reason we've not noticed this problem is that it's actually quite unlikely to notice. This is because only the elements <svg>, <foreignObject> and <use> have transforms after their userspace. Since <svg> and <foreignObject> are kind of tricky cases (getBBox on <svg> doesn't really work with overflow:visible ATM, and for <foreignObject> it's kinda unclear what should happen. However, for <use> the problem is visible. The way to fix this is probably to have GetBBoxContribution to recognise a flag in aFlags that says "you are the root element", and have it use PrependLocalTransformsTo with a special arg to get that part of the transform from the element to include it. It's not possible to have nsSVGUtils::GetBBox get this transform since for <svg> and <foreignObject> we only want to use that transform for the descendants, not the x,y,width,height of the element itself!
Some background on display lists
See https://mxr.mozilla.org/mozilla-central/source/layout/base/nsDisplayList.h#69 for an intro to display lists.
The basic idea is that we'll create a nsDisplaySVGGraphic subclass of nsDisplayItem that will paint SVG graphics. We won't create special display items for SVG containers, although we will create nsDisplayTransform and nsDisplaySVGEffects items etc. as necessary to take account of transforms and SVG effects on containers.
We will probably still need an nsDisplaySVG for nsSVGOuterSVGFrame for some special handling. E.g. to transform from userspace into nsSVGOuterSVGFrame content box space.
Elements that should probably establish a new stacking context: 'image', 'use', 'marker', 'mask', 'pattern' and 'symbol'? The latter four are not used directly, so how will we deal with them? We'll need to create separate display lists for them in order to paint their content, but it's not clear what that will involve.
nsDisplayTransform
Currently the plan is to account all coordinate system changes on SVG *container* elements - including x/y offsets - using an nsDisplayTransform. This is because it saves us creating another display list item type for these offsets, and because PrependLocalTransformsTo (which it seems natural to use to get the transform for nsDisplayTransform) includes these offsets.
Instead of adding a GetLocalTransform method to SVG frames, perhaps handle SVG in nsDisplayTransform::GetResultingTransformMatrix using PrependLocalTransformsTo?
nsDisplayTransform::GetFrameBoundsForTransform depends on mRect->Size() - make sure this function works with SVG.
nsDisplayTransform::GetBounds depends on frame->mRect->GetSize()
Sources of translation:
- x/y on svg, use, image, text, tspan and foreignObject
- currentTranslate on svg
Sources of more complex transforms:
- transform attribute/property on most elements
- viewBox on svg and use
- currentScale on svg
Concerns
Nothing major, but it's worth noting the following about implementing SVG display lists:
- SVG tends to have more elements on screen than HTML; sometimes considerably more. We may find that creating/destroying the bigger display lists that SVG will sometimes require will create its own performance issues for hit-testing and painting.
- When we come to implement display list based invalidation, it's maybe worth noting that it's in the case where there's nothing to invalidate that we'll do the most work when comparing the two display lists. (Since when nothing has changed we'll need to compare every single item in the lists, and won't stop at differing container items.)
Some thoughts on invalidation
CSS box model invalidation
In the CSS box model layout (BML) world, invalidation is done during reflow. When a frame is moved, its parent is responsible for invalidating the old and new overflow areas; when a frame is resized, the frame itself is responsible for invalidating the dirty area. (This split is just how the code ended up; there's no reason to have it split this way.) Invalidate() is called for each frame that is moved/resized during a reflow, so there can be a lot of these calls.
The rect passed to Invalidate is relative to the frame itself. As this rect is passed up to its parent in InvalidateInternalAfterResize (nsIFrame::Invalidate calls nsIFrame::InvalidateWithFlags, calls nsIFrame::InvalidateInternal, calls nsIFrame::InvalidateInternalAfterResize), the x/y offset is adjusted by adding each frame's mRect.x/.y, and transforms are taken into account by adjusting the rect using the static method nsDisplayTransform::TransformRectOut.
Maintaining mRect and overflow/scroll rects on containers has its pros and cons. Changes to the area of a frame are more expensive because, typically, the rects of its ancestors and descendants then also need to be updated. However, non-move/resize invalidation is typically cheaper since there's no need to look at an element's descendants to figure out the dirty area.
Pre-SDL SVG invalidation
When an element needs to be invalidated its frame is passed to nsSVGOuterSVGFrame::InvalidateCoveredRegion or nsSVGOuterSVGFrame::UpdateAndInvalidateCoveredRegion. These methods recurse to the leaves to obtain the union of all the leaves' covered regions (mRect - the bounds of the leaf relative to the nsSVGOuterSVGFrame's content area) and calls Invalidate() with that area. In the case of UpdateAndInvalidateCoveredRegion the covered regions are then updated by calling UpdateCoveredRegion() on the frame, which recurses to the leaves where each element calls GetCanvasTM() as part of calculating its new nsSVGOuterSVGFrame relative covered region. UpdateAndInvalidateCoveredRegion then recurses to the leaves a second time to obtain the new union of all the leaves' covered regions to Invalidate().
Advantages: the area invalidated in the end is typically smaller when we're not passing dirty areas up the parent chain taking bounds-of-transformed-bounds as we go.
Comparison
One of the advantages of the old SVG way of doing invalidation was that the area invalidated was the tight bounds in the nsSVGOuterSVGFrame's space. The new system takes the bounds in the leaf element's space, and passes it up the parent chain, expanding it as it does. If it continues to do this, then the new system will almost inevitably end up invalidating much more of the screen than the old system. It's not impossible that we could avoid the expansion while passing the dirty rect up though. We could instead have a function called GetInvalidationParentAndTransform, which would in one call walk the parent chain and return the nsSVGOuterSVGFrame and the transform to it, so we can just do a single transform of mRect for invalidation.
One thing to note about killing covered regions is that we get a perf win for moving of containers. This is because to update each leaf's mRect we need to paint it, but if mRect doesn't include transforms on ancestors and thus doesn't need to update when ancestors change, then we avoid the painting of leaves when containers move.
Some thoughts on hit-testing
In pre-SDL SVG, nsDisplaySVG::HitTest would convert the rect (point) it was given to a point in app units relative to the top left of its nsSVGOuterSVGFrame's content rect. It would pass this point to nsSVGUtils::HitTestChildren, under which the point would be passed, unmodified, down to the leaf elements, with some container elements checking along the way whether the point was within their clip and overflow bounds. The leaf elements store their "covered region" - their app unit bounds in nsSVGOuterSVGFrame content box space - in mRect, making it convenient for an early return check. If the point is in their bounds, then the leafs generally convert the point from outer-<svg> content rect app unit space to local user space for a more accurate check.
In CSS box model layout, hit testing is done somewhat differently.
nsDisplayItem::GetBounds() gets the rect with ToReferenceFrame() offset and nsIFrame::GetSize() size.
Ignoring transforms, aRect for nsDisplayList::HitTest and nsDisplayItem::HitTest is relative to ToReferenceFrame(). XXX right-ish?
DL hit testing checks for intersection with GetBounds, so items need to return bounds in the coord space that the hit point is in. The hit point is in the coords of ToReferenceFrame, except with transforms undone. If nsDisplayTransform is going to convert the coords of the point, then GetBounds would have to ignore the transforms.
New world: the point arrives relative to ToReferenceFrame (ignoring transforms). All changes in coordinate context between the leaf and the outer-svg are accounted for by nsDisplayTransform, so all we need to do is hit test against the userspace bounds, offset it the outer-svg's offset from ToReferenceFrame.
nsDisplaySVGEffects::HitTest uses nsSVGIntegrationUtils::HitTestFrameForEffects which uses nsSVGUtils::HitTestClip
Search for all the places checking z-index. If is-positioned is checked, then we also need do || if-svg.
nsDisplayList::HitTest() requires intersection with item->GetBounds for the item to hit.
nsRect nsDisplayItem::GetBounds(nsDisplayListBuilder* aBuilder) { return nsRect(ToReferenceFrame(), GetUnderlyingFrame()->GetSize()); }
Some thoughts on clipping
In BuildDisplayListForChild, ApplyOverflowClipping does not create a new stacking context. Seems like we should do the same for 'svg' and 'foreignObject', but that's more work.
Checklist
Make sure the SVG opacity optimization of flattening group opacity into just fill/stroke opacity when possible is implemented.
Old stuff
Killing covered regions
For SVG frames pre-SDL, mRect is only set on leaf frames (nsSVGPathGeometryFrame, nsSVGForeignObjectFrame, nsSVGImageFrame and nsSVGGlyphFrame). It's set by UpdateCoveredRegion. This "covered region" that's stored in mRect is an incomplete, partial calculation of the leaf's bounds in app units in the nsSVGOuterSVGFrame's content rect space. It is incomplete because it only takes into account fill, stroke and markers on the leaf. It does not take into account any SVG affects, either on the leaf or on its parents. The actual area painted by the leaf may be much bigger or smaller than the covered region due to SVG affects. The contribution of the stroke bounds may also be overestimated since it's obtained from nsSVGUtils::PathExtentsToMaxStrokeExtents.
What it's used for:
- nsSVGImageFrame::PaintSVG - depends on mRect being pre-SDL
- nsSVGPathGeometryFrame::GetFrameForPoint - early return
- nsSVGForeignObjectFrame::PaintSVG - early return if dirty rect doesn't intersect
- nsSVGForeignObjectFrame::InvalidateDirtyRect - crop the size of invalidation rects coming out from its contents, maybe returning early
- nsSVGForeignObjectFrame::Reflow - sets the reflow metrics width, height and overflow areas to GetSize(). See email "Question about foreignObject reflow".
- nsSVGUtils::PaintFrameWithEffects - skips painting of leaves for which the dirty rect doesn't intersect leaf-> GetRect()
- Callers of GetCoveredRegion, including:
- nsSVGOuterSVGFrame::InvalidateCoveredRegion
- nsSVGOuterSVGFrame::UpdateAndInvalidateCoveredRegion
Note that nothing needs to be done for nsSVGForeignObjectFrame::Reflow when changing what mRect contains, since we can store whatever we like in mRect there.
Problem with killing covered regions
When an element moves, we need to invalidate its old and new area. Currently SVG doesn't separate these two steps very strongly; both steps happen in UpdateAndInvalidateCoveredRegion _after_ the attribute/property in question has changed. We get away with this only because the bounds we store in mRect are relative to the frame's nsSVGOuterSVGFrame. Thus when UpdateAndInvalidateCoveredRegion is called we can still get the old nsSVGOuterSVGFrame relative bounds to invalidate it, update the bounds, then invalidate the new bounds. The problem is, if we make the bounds stored by mRect relative to its userspace instead of the nsSVGOuterSVGFrame, we can't reliably get the old bounds relative to the nsSVGOuterSVGFrame. Transforming the old userspace relative bounds by passing it up the parent chain is wrong, since the elements transform attribute or a transform anywhere along the parent chain could be the thing that has changed.