Last Comment Bug 689546 - Simplify attribute updates to svg elements
: Simplify attribute updates to svg elements
Status: RESOLVED FIXED
:
Product: Core
Classification: Components
Component: SVG (show other bugs)
: Trunk
: x86_64 Windows 7
: -- normal (vote)
: mozilla10
Assigned To: Robert Longson
:
:
Mentors:
Depends on: 722882
Blocks: 608161
  Show dependency treegraph
 
Reported: 2011-09-27 06:09 PDT by Robert Longson
Modified: 2012-01-31 14:45 PST (History)
2 users (show)
longsonr: in‑testsuite-
See Also:
Crash Signature:
(edit)
QA Whiteboard:
Iteration: ---
Points: ---
Has Regression Range: ---
Has STR: ---


Attachments
patch (9.96 KB, patch)
2011-09-27 06:09 PDT, Robert Longson
no flags Details | Diff | Splinter Review
updated patch (11.12 KB, patch)
2011-09-29 01:53 PDT, Robert Longson
no flags Details | Diff | Splinter Review
updated again (12.34 KB, patch)
2011-09-29 02:45 PDT, Robert Longson
no flags Details | Diff | Splinter Review
without the reftest changes this time (10.74 KB, patch)
2011-09-29 02:51 PDT, Robert Longson
no flags Details | Diff | Splinter Review
updated (10.75 KB, patch)
2011-09-29 05:54 PDT, Robert Longson
jwatt: review+
Details | Diff | Splinter Review

Description Robert Longson 2011-09-27 06:09:31 PDT
Created attachment 562740 [details] [diff] [review]
patch

Currently we override DidSetXXX rather than just picking up the change in AttributeChanged.
Comment 1 Jonathan Watt [:jwatt] (back in October - email directly if necessary) 2011-09-27 09:19:27 PDT
Comment on attachment 562740 [details] [diff] [review]
patch

It seems to me that the AttributeChanged implementations should be sending the COORD_CONTEXT_CHANGED flag down to their children if the viewBox changed, or if the width/height changed and there is no viewBox.

> nsSVGInnerSVGFrame::AttributeChanged(PRInt32  aNameSpaceID,
>                                      nsIAtom* aAttribute,
>                                      PRInt32  aModType)
> {
>   if (aNameSpaceID == kNameSpaceID_None) {
>     if (aAttribute == nsGkAtoms::width ||
>-        aAttribute == nsGkAtoms::height ||
>-        aAttribute == nsGkAtoms::preserveAspectRatio ||
>-        aAttribute == nsGkAtoms::viewBox) {
>+        aAttribute == nsGkAtoms::height) {
>       nsSVGUtils::UpdateGraphic(this);
>     } else if (aAttribute == nsGkAtoms::transform ||
>+               aAttribute == nsGkAtoms::preserveAspectRatio ||
>+               aAttribute == nsGkAtoms::viewBox ||
>                aAttribute == nsGkAtoms::x ||
>                aAttribute == nsGkAtoms::y) {
>       // make sure our cached transform matrix gets (lazily) updated
>       mCanvasTM = nsnull;
> 
>       nsSVGUtils::NotifyChildrenOfSVGChange(this, TRANSFORM_CHANGED);
>     }
>   }

It also seems like we still want the InvalidateRenderingObservers, UpdateAndInvalidateCoveredRegion and NotifyAncestorsOfFilterRegionChange calls in UpdateGraphic when viewBox/preserveAspectRatio change (and x and y actually), or does that happen elsewhere?
Comment 2 Robert Longson 2011-09-27 09:27:20 PDT
(In reply to Jonathan Watt [:jwatt] from comment #1)
> 
> It seems to me that the AttributeChanged implementations should be sending
> the COORD_CONTEXT_CHANGED flag down to their children if the viewBox
> changed, or if the width/height changed and there is no viewBox.

OK, I'll update the patch for that.

> It also seems like we still want the InvalidateRenderingObservers,
> UpdateAndInvalidateCoveredRegion and NotifyAncestorsOfFilterRegionChange
> calls in UpdateGraphic when viewBox/preserveAspectRatio change (and x and y
> actually), or does that happen elsewhere?

when NotifySVGChanged propagates to leaf frames (e.g. nsSVGGlyphFrame) it calls nsSVGUtils:UpdateGraphic which calls InvalidateRenderingObservers,  UpdateAndInvalidateCoveredRegion and NotifyAncestorsOfFilterRegionChange so yes, that happens elsewhere.
Comment 3 Jonathan Watt [:jwatt] (back in October - email directly if necessary) 2011-09-27 11:33:55 PDT
Ah, I see.

Did you consider removing NotifyViewportChange from nsISVGSVGFrame and nsSVGInnerSVGFrame? In nsSVGSVGElement::InvalidateTransformNotifyFrame() you could check !IsInner() and cast to nsSVGOuterSVGFrame...or I think you can QI to nsSVGOuterSVGFrame.

Hmm...I'm thinking probably we should be returning a "not implemented" exception from nsSVGSVGElement::SetCurrentScaleTranslate if !IsRoot() though.

I'll review your next patch.
Comment 4 Robert Longson 2011-09-29 01:53:42 PDT
Created attachment 563340 [details] [diff] [review]
updated patch

(In reply to Jonathan Watt [:jwatt] (away 1-9 Oct, inc.) from comment #3)
> 
> Did you consider removing NotifyViewportChange from nsISVGSVGFrame and
> nsSVGInnerSVGFrame? In nsSVGSVGElement::InvalidateTransformNotifyFrame() you
> could check !IsInner() and cast to nsSVGOuterSVGFrame...or I think you can
> QI to nsSVGOuterSVGFrame.
> 

No, that seems overly complicated now that most callers of NotifyViewportChange have gone. You'd need to either resize the browser window or zoom. Before it would be called whenver you'd animate an <svg> attribute so it's much less animation performance sensitive now.

I've better performance fish to fry next as I intend to create a state bit for redraw suspended in my next bug and propagate that so that update graphic doesn't need to find the outer frame if redraw is suspended. I might even be able to get rid of the traversal to the outer svg frame on redraws altogether.
Comment 5 Jonathan Watt [:jwatt] (back in October - email directly if necessary) 2011-09-29 02:10:36 PDT
I was suggesting removing NotifyViewportChange from nsISVGSVGFrame to simplify the code, and as a step to getting rid of that interface. Anyway, it's not a big deal.
Comment 6 Robert Longson 2011-09-29 02:14:54 PDT
I'll look into it as part of bug 413960
Comment 7 Jonathan Watt [:jwatt] (back in October - email directly if necessary) 2011-09-29 02:34:16 PDT
Comment on attachment 563340 [details] [diff] [review]
updated patch

>+  // we'll get notified by the outerSVGFrame anyway

This comment seems wrong. nsSVGInnerSVGFrame::NotifyViewportChange is called by the nsSVGSVGElement mContent. It's not that the outerSVGFrame will notify us, but rather that our AttributeChanged method will be called by our own nsSVGSVGElement, and we'll handle the changes there.

>+    } else if (aAttribute == nsGkAtoms::width ||
>+               aAttribute == nsGkAtoms::height) {
>+
>+        PRBool hasValidViewBox =
>+                 static_cast<nsSVGSVGElement*>(mContent)->mViewBox.IsValid();
>+
>+        if (hasValidViewBox) {
>+          // make sure our cached transform matrix gets (lazily) updated
>+          mCanvasTM = nsnull;
>+        }
>+        nsSVGUtils::NotifyChildrenOfSVGChange(
>+           this, hasValidViewBox ? TRANSFORM_CHANGED : COORD_CONTEXT_CHANGED);
>+
>+        nsIFrame* embeddingFrame;
>+        if (IsRootOfReplacedElementSubDoc(&embeddingFrame) && embeddingFrame) {
>+          if (DependsOnIntrinsicSize(embeddingFrame)) {
>+            // Tell embeddingFrame's presShell it needs to be reflowed (which takes
>+            // care of reflowing us too).
>+            embeddingFrame->PresContext()->PresShell()->
>+              FrameNeedsReflow(embeddingFrame, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
>+          }
>+          // else our width and height is overridden - don't reflow anything
>+        } else {
>+          // We are not embedded by reference, so our 'width' and 'height'
>+          // attributes are not overridden - we need to reflow.
>+          PresContext()->PresShell()->
>+            FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
>+        }

When we get our reflow, our nsSVGOuterSVGFrame::Reflow will call nsSVGOuterSVGFrame::NotifyViewportChange, which will do the nulling out of mCanvasTM and calling of NotifyChildrenOfSVGChange, so doing that here is just extra work.
Comment 8 Robert Longson 2011-09-29 02:45:49 PDT
Created attachment 563352 [details] [diff] [review]
updated again

(In reply to Jonathan Watt [:jwatt] (away 1-9 Oct, inc.) from comment #7)
> 
> >+  // we'll get notified by the outerSVGFrame anyway
> 
> This comment seems wrong. nsSVGInnerSVGFrame::NotifyViewportChange is called
> by the nsSVGSVGElement mContent. It's not that the outerSVGFrame will notify
> us, but rather that our AttributeChanged method will be called by our own
> nsSVGSVGElement, and we'll handle the changes there.

I meant that the outer SVG element will pick up the viewport change and call nsSVGOuterSVGFrame::NotifyViewportChange which will call nsSVGUtils::NotifyChildrenOfSVGChange which will call nsSVGInnerSVGFrame::NotifySVGChanged and that's where the inner svg frame processes the viewport change. So it doesn't need to do so in nsSVGInnerSVGFrame::NotifyViewportChange as well.

> 
> When we get our reflow, our nsSVGOuterSVGFrame::Reflow will call
> nsSVGOuterSVGFrame::NotifyViewportChange, which will do the nulling out of
> mCanvasTM and calling of NotifyChildrenOfSVGChange, so doing that here is
> just extra work.

Fixed.
Comment 9 Robert Longson 2011-09-29 02:51:40 PDT
Created attachment 563354 [details] [diff] [review]
without the reftest changes this time
Comment 10 Jonathan Watt [:jwatt] (back in October - email directly if necessary) 2011-09-29 02:58:14 PDT
(In reply to Robert Longson from comment #8)
> I meant that the outer SVG element will pick up the viewport change and call
> nsSVGOuterSVGFrame::NotifyViewportChange which will call
> nsSVGUtils::NotifyChildrenOfSVGChange which will call
> nsSVGInnerSVGFrame::NotifySVGChanged and that's where the inner svg frame
> processes the viewport change. So it doesn't need to do so in
> nsSVGInnerSVGFrame::NotifyViewportChange as well.

I think there's some confusion here. The viewport change in question is the change in the SVG viewport defined by an inner-<svg> for its children, not a change in the window dimensions or something. When the width/height of that inner-<svg> changes, probably the outer-<svg> isn't notified about that change (other than to invalidate an area of the canvas), so I'm not sure what you mean by "outer SVG element will pick up the viewport change and...". I think my comment about that comment still stands - the reason we don't need to do anything in nsSVGInnerSVGFrame::NotifyViewportChange is because we handle the "viewport" change in our AttributeChanged method.
Comment 11 Jonathan Watt [:jwatt] (back in October - email directly if necessary) 2011-09-29 02:58:40 PDT
Comment on attachment 563354 [details] [diff] [review]
without the reftest changes this time

> nsSVGInnerSVGFrame::AttributeChanged(PRInt32  aNameSpaceID,
>                                      nsIAtom* aAttribute,
>                                      PRInt32  aModType)
> {
>   if (aNameSpaceID == kNameSpaceID_None) {
>     if (aAttribute == nsGkAtoms::width ||
>-        aAttribute == nsGkAtoms::height ||
>-        aAttribute == nsGkAtoms::preserveAspectRatio ||
>-        aAttribute == nsGkAtoms::viewBox) {
>-      nsSVGUtils::UpdateGraphic(this);
>+        aAttribute == nsGkAtoms::height) {
>+
>+        PRBool hasValidViewBox =
>+                 static_cast<nsSVGSVGElement*>(mContent)->mViewBox.IsValid();
>+
>+        if (hasValidViewBox) {
>+          // make sure our cached transform matrix gets (lazily) updated
>+          mCanvasTM = nsnull;
>+        }
>+        nsSVGUtils::NotifyChildrenOfSVGChange(
>+           this, hasValidViewBox ? TRANSFORM_CHANGED : COORD_CONTEXT_CHANGED);
>+

Rather than having two conditionals on whether there is a viewBox rect, I think this would be clearer to have just one:

      if (static_cast<nsSVGSVGElement*>(mContent)->mViewBox.IsValid()) {
        mCanvasTM = nsnull;
        nsSVGUtils::NotifyChildrenOfSVGChange(this, TRANSFORM_CHANGED);
      } else {
        nsSVGUtils::NotifyChildrenOfSVGChange(this, COORD_CONTEXT_CHANGED);
      }

Other than that, can you please rename hasValidViewBox to hasViewBoxRect in the other part of your patch? (To me "none" is a valid attribute value, but what's of interest here is whether the viewBox defines a *rect*. I'd like us to change the name of IsValid to HasValidRect or something, but that's for another patch I guess.)
Comment 12 Jonathan Watt [:jwatt] (back in October - email directly if necessary) 2011-09-29 03:05:10 PDT
Other than that, the patch looks great.
Comment 13 Robert Longson 2011-09-29 05:54:25 PDT
Created attachment 563381 [details] [diff] [review]
updated

(In reply to Jonathan Watt [:jwatt] (away 1-9 Oct, inc.) from comment #11)
> Comment on attachment 563354 [details] [diff] [review] [diff] [details] [review]
> without the reftest changes this time
> 
> Rather than having two conditionals on whether there is a viewBox rect, I
> think this would be clearer to have just one:

OK.

> Other than that, can you please rename hasValidViewBox to hasViewBoxRect in
> the other part of your patch? (To me "none" is a valid attribute value, but
> what's of interest here is whether the viewBox defines a *rect*. I'd like us
> to change the name of IsValid to HasValidRect or something, but that's for
> another patch I guess.)

That's the ony place it occurred in this patch and it's gone now with the above change.


(In reply to Jonathan Watt [:jwatt] (away 1-9 Oct, inc.) from comment #10)
> 
> I think there's some confusion here. The viewport change in question is the
> change in the SVG viewport defined by an inner-<svg> for its children, not a
> change in the window dimensions or something. When the width/height of that
> inner-<svg> changes, probably the outer-<svg> isn't notified about that
> change (other than to invalidate an area of the canvas), so I'm not sure
> what you mean by "outer SVG element will pick up the viewport change
> and...". I think my comment about that comment still stands - the reason we
> don't need to do anything in nsSVGInnerSVGFrame::NotifyViewportChange is
> because we handle the "viewport" change in our AttributeChanged method.

It's never called any more so I added an NS_ERROR to that effect. I might try to remove entirely it in bug 413960
Comment 14 Jonathan Watt [:jwatt] (back in October - email directly if necessary) 2011-09-29 06:04:17 PDT
Comment on attachment 563381 [details] [diff] [review]
updated

Thanks! r=jwatt

Note You need to log in before you can comment on or make changes to this bug.