Bug 572689 - Make nsSVGRenderingObservers observe elements instead of frames. r=roc

This commit is contained in:
Markus Stange 2010-08-13 15:31:31 +02:00
parent fae48f3bbd
commit eaba422dce
7 changed files with 142 additions and 120 deletions

View File

@ -320,7 +320,8 @@ public:
mFlagsOrSlots(NODE_DOESNT_HAVE_SLOTS),
mNextSibling(nsnull),
mPreviousSibling(nsnull),
mFirstChild(nsnull)
mFirstChild(nsnull),
mNodeHasRenderingObservers(false)
{
}
@ -1147,6 +1148,10 @@ public:
NS_NOTREACHED("How did we get here?");
}
bool HasRenderingObservers() { return mNodeHasRenderingObservers; }
void SetHasRenderingObservers(bool aValue)
{ mNodeHasRenderingObservers = aValue; }
// Optimized way to get classinfo. May return null.
virtual nsXPCClassInfo* GetClassInfo() = 0;
protected:
@ -1279,6 +1284,9 @@ protected:
nsIContent* mNextSibling;
nsIContent* mPreviousSibling;
nsIContent* mFirstChild;
// More flags
bool mNodeHasRenderingObservers : 1;
};

View File

@ -794,6 +794,7 @@ GK_ATOM(refresh, "refresh")
GK_ATOM(rel, "rel")
GK_ATOM(rem, "rem")
GK_ATOM(removeelement, "removeelement")
GK_ATOM(renderingobserverlist, "renderingobserverlist")
GK_ATOM(repeat, "repeat")
GK_ATOM(replace, "replace")
GK_ATOM(reset, "reset")

View File

@ -350,7 +350,7 @@ nsFrame::Init(nsIContent* aContent,
mState |= state & (NS_FRAME_SELECTED_CONTENT |
NS_FRAME_INDEPENDENT_SELECTION |
NS_FRAME_IS_SPECIAL |
NS_FRAME_MAY_BE_TRANSFORMED_OR_HAVE_RENDERING_OBSERVERS);
NS_FRAME_MAY_BE_TRANSFORMED);
}
if (mParent) {
nsFrameState state = mParent->GetStateBits();
@ -362,7 +362,7 @@ nsFrame::Init(nsIContent* aContent,
if (GetStyleDisplay()->HasTransform()) {
// The frame gets reconstructed if we toggle the -moz-transform
// property, so we can set this bit here and then ignore it.
mState |= NS_FRAME_MAY_BE_TRANSFORMED_OR_HAVE_RENDERING_OBSERVERS;
mState |= NS_FRAME_MAY_BE_TRANSFORMED;
}
DidSetStyleContext(nsnull);
@ -721,7 +721,7 @@ nsIFrame::GetPaddingRect() const
PRBool
nsIFrame::IsTransformed() const
{
return (mState & NS_FRAME_MAY_BE_TRANSFORMED_OR_HAVE_RENDERING_OBSERVERS) &&
return (mState & NS_FRAME_MAY_BE_TRANSFORMED) &&
GetStyleDisplay()->HasTransform();
}
@ -1259,7 +1259,7 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
/* If we're being transformed, we need to invert the matrix transform so that we don't
* grab points in the wrong coordinate system!
*/
if ((mState & NS_FRAME_MAY_BE_TRANSFORMED_OR_HAVE_RENDERING_OBSERVERS) &&
if ((mState & NS_FRAME_MAY_BE_TRANSFORMED) &&
disp->HasTransform()) {
dirtyRect = nsDisplayTransform::UntransformRect(dirtyRect, this, nsPoint(0, 0));
inTransform = PR_TRUE;
@ -1382,7 +1382,7 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
/* If we're going to apply a transformation, wrap everything in an
* nsDisplayTransform.
*/
if ((mState & NS_FRAME_MAY_BE_TRANSFORMED_OR_HAVE_RENDERING_OBSERVERS) &&
if ((mState & NS_FRAME_MAY_BE_TRANSFORMED) &&
disp->HasTransform()) {
rv = resultList.AppendNewToTop(
new (aBuilder) nsDisplayTransform(aBuilder, this, &resultList));
@ -1494,9 +1494,7 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
// Child is composited if it's transformed, partially transparent, or has
// SVG effects.
PRBool isComposited = disp->mOpacity != 1.0f ||
((aChild->mState & NS_FRAME_MAY_BE_TRANSFORMED_OR_HAVE_RENDERING_OBSERVERS) &&
aChild->GetStyleDisplay()->HasTransform())
PRBool isComposited = disp->mOpacity != 1.0f || aChild->IsTransformed()
#ifdef MOZ_SVG
|| nsSVGIntegrationUtils::UsingEffectsForFrame(aChild)
#endif
@ -3878,8 +3876,7 @@ nsIFrame::InvalidateInternalAfterResize(const nsRect& aDamageRect, nscoord aX,
// Don't need to invalidate any more Thebes layers
aFlags |= INVALIDATE_NO_THEBES_LAYERS;
}
if ((mState & NS_FRAME_MAY_BE_TRANSFORMED_OR_HAVE_RENDERING_OBSERVERS) &&
GetStyleDisplay()->HasTransform()) {
if (IsTransformed()) {
nsRect newDamageRect;
newDamageRect.UnionRect(nsDisplayTransform::TransformRect
(aDamageRect, this, nsPoint(-aX, -aY)), aDamageRect);
@ -4171,8 +4168,7 @@ nsIFrame::GetOverflowRectRelativeToParent() const
nsRect
nsIFrame::GetOverflowRectRelativeToSelf() const
{
if ((mState & NS_FRAME_MAY_BE_TRANSFORMED_OR_HAVE_RENDERING_OBSERVERS) &&
GetStyleDisplay()->HasTransform()) {
if (IsTransformed()) {
nsRect* preTransformBBox = static_cast<nsRect*>
(Properties().Get(PreTransformBBoxProperty()));
if (preTransformBBox)
@ -5870,9 +5866,7 @@ nsIFrame::FinishAndStoreOverflow(nsRect* aOverflowArea, nsSize aNewSize)
&hasOutlineOrEffects);
/* If we're transformed, transform the overflow rect by the current transformation. */
PRBool hasTransform =
(mState & NS_FRAME_MAY_BE_TRANSFORMED_OR_HAVE_RENDERING_OBSERVERS) &&
GetStyleDisplay()->HasTransform();
PRBool hasTransform = IsTransformed();
if (hasTransform) {
Properties().
Set(nsIFrame::PreTransformBBoxProperty(), new nsRect(*aOverflowArea));

View File

@ -235,10 +235,7 @@ typedef PRUint64 nsFrameState;
// to its coordinate system (e.g. CSS transform, SVG foreignObject).
// This is used primarily in GetTransformMatrix to optimize for the
// common case.
// ALSO, if this bit is set, the frame's first-continuation may
// have an associated nsSVGRenderingObserverList.
#define NS_FRAME_MAY_BE_TRANSFORMED_OR_HAVE_RENDERING_OBSERVERS \
NS_FRAME_STATE_BIT(16)
#define NS_FRAME_MAY_BE_TRANSFORMED NS_FRAME_STATE_BIT(16)
#ifdef IBMBIDI
// If this bit is set, the frame itself is a bidi continuation,

View File

@ -46,18 +46,19 @@
#include "nsFrameManager.h"
using namespace mozilla;
using namespace mozilla::dom;
/**
* Note that in the current setup there are two separate observer lists.
*
* In nsSVGRenderingObserver's ctor, the new object adds itself to the mutation
* observer list maintained by the referenced *element*. In this way the
* observer list maintained by the referenced element. In this way the
* nsSVGRenderingObserver is notified if there are any attribute or content
* tree changes to the element or any of its *descendants*.
*
* In nsSVGRenderingObserver::GetReferencedFrame() the nsSVGRenderingObserver
* In nsSVGRenderingObserver::GetReferencedElement() the nsSVGRenderingObserver
* object also adds itself to an nsSVGRenderingObserverList object belonging
* to the nsIFrame corresponding to the referenced element.
* to the referenced element.
*
* XXX: it would be nice to have a clear and concise executive summary of the
* benefits/necessity of maintaining a second observer list.
@ -76,57 +77,73 @@ nsSVGRenderingObserver::nsSVGRenderingObserver(nsIURI *aURI,
nsIFrame *aFrame)
: mElement(this), mFrame(aFrame),
mFramePresShell(aFrame->PresContext()->PresShell()),
mReferencedFrame(nsnull),
mReferencedFramePresShell(nsnull)
mInObserverList(PR_FALSE)
#ifdef _MSC_VER
#pragma warning(pop)
#endif
{
// Start watching the target element
mElement.Reset(aFrame->GetContent(), aURI);
StartListening();
}
nsSVGRenderingObserver::~nsSVGRenderingObserver()
{
StopListening();
}
void
nsSVGRenderingObserver::StartListening()
{
if (mElement.get()) {
mElement.get()->AddMutationObserver(this);
}
}
nsSVGRenderingObserver::~nsSVGRenderingObserver()
void
nsSVGRenderingObserver::StopListening()
{
if (mElement.get()) {
mElement.get()->RemoveMutationObserver(this);
if (mInObserverList) {
nsSVGEffects::RemoveRenderingObserver(mElement.get(), this);
mInObserverList = PR_FALSE;
}
}
if (mReferencedFrame && !mReferencedFramePresShell->IsDestroying()) {
nsSVGEffects::RemoveRenderingObserver(mReferencedFrame, this);
NS_ASSERTION(!mInObserverList, "still in an observer list?");
}
static nsSVGRenderingObserverList *
GetObserverList(Element *aElement)
{
return static_cast<nsSVGRenderingObserverList*>
(aElement->GetProperty(nsGkAtoms::renderingobserverlist));
}
Element*
nsSVGRenderingObserver::GetReferencedElement()
{
#ifdef DEBUG
if (mElement.get()) {
nsSVGRenderingObserverList *observerList = GetObserverList(mElement.get());
PRBool inObserverList = observerList && observerList->Contains(this);
NS_ASSERTION(inObserverList == mInObserverList, "failed to track whether we're in our referenced element's observer list!");
} else {
NS_ASSERTION(!mInObserverList, "In whose observer list are we, then?");
}
#endif
if (mElement.get() && !mInObserverList) {
nsSVGEffects::AddRenderingObserver(mElement.get(), this);
mInObserverList = PR_TRUE;
}
return mElement.get();
}
nsIFrame*
nsSVGRenderingObserver::GetReferencedFrame()
{
if (mReferencedFrame && !mReferencedFramePresShell->IsDestroying()) {
// Don't test this assertion if it's not a good time to call
// GetPrimaryFrame
if (!mReferencedFramePresShell->FrameManager()->IsDestroyingFrames()) {
NS_ASSERTION(mElement.get() &&
static_cast<nsGenericElement*>(mElement.get())->GetPrimaryFrame() == mReferencedFrame,
"Cached frame is incorrect!");
}
return mReferencedFrame;
}
if (mElement.get()) {
nsIDocument* doc = mElement.get()->GetCurrentDoc();
nsIPresShell* shell = doc ? doc->GetShell() : nsnull;
if (shell && !shell->FrameManager()->IsDestroyingFrames()) {
nsIFrame* frame = mElement.get()->GetPrimaryFrame();
if (frame) {
mReferencedFrame = frame;
mReferencedFramePresShell = shell;
nsSVGEffects::AddRenderingObserver(mReferencedFrame, this);
return frame;
}
}
}
return nsnull;
Element* referencedElement = GetReferencedElement();
return referencedElement ? referencedElement->GetPrimaryFrame() : nsnull;
}
nsIFrame*
@ -149,10 +166,9 @@ nsSVGRenderingObserver::DoUpdate()
mFrame = nsnull;
return;
}
if (mReferencedFrame) {
nsSVGEffects::RemoveRenderingObserver(mReferencedFrame, this);
mReferencedFrame = nsnull;
mReferencedFramePresShell = nsnull;
if (mElement.get() && mInObserverList) {
nsSVGEffects::RemoveRenderingObserver(mElement.get(), this);
mInObserverList = PR_FALSE;
}
if (mFrame && mFrame->IsFrameOfType(nsIFrame::eSVG)) {
// Changes should propagate out to things that might be observing
@ -162,12 +178,9 @@ nsSVGRenderingObserver::DoUpdate()
}
void
nsSVGRenderingObserver::InvalidateViaReferencedFrame()
nsSVGRenderingObserver::InvalidateViaReferencedElement()
{
// Clear mReferencedFrame since the referenced frame has already
// dropped its reference back to us
mReferencedFrame = nsnull;
mReferencedFramePresShell = nsnull;
mInObserverList = PR_FALSE;
DoUpdate();
}
@ -511,56 +524,42 @@ nsSVGRenderingObserverList::InvalidateAll()
mObservers.EnumerateEntries(GatherEnumerator, &observers);
for (PRUint32 i = 0; i < observers.Length(); ++i) {
observers[i]->InvalidateViaReferencedFrame();
observers[i]->InvalidateViaReferencedElement();
}
}
static void
DestroyObservers(void* aPropertyValue)
DestroyObservers(void *aObject, nsIAtom *aPropertyName,
void *aPropertyValue, void *aData)
{
delete static_cast<nsSVGRenderingObserverList*>(aPropertyValue);
}
NS_DECLARE_FRAME_PROPERTY(ObserversProperty, DestroyObservers)
static nsSVGRenderingObserverList *
GetObserverList(nsIFrame *aFrame)
{
if (!(aFrame->GetStateBits() & NS_FRAME_MAY_BE_TRANSFORMED_OR_HAVE_RENDERING_OBSERVERS))
return nsnull;
return static_cast<nsSVGRenderingObserverList*>
(aFrame->Properties().Get(ObserversProperty()));
}
void
nsSVGEffects::AddRenderingObserver(nsIFrame *aFrame, nsSVGRenderingObserver *aObserver)
nsSVGEffects::AddRenderingObserver(Element *aElement, nsSVGRenderingObserver *aObserver)
{
NS_ASSERTION(!aFrame->GetPrevContinuation(), "aFrame must be first continuation");
nsSVGRenderingObserverList *observerList = GetObserverList(aFrame);
nsSVGRenderingObserverList *observerList = GetObserverList(aElement);
if (!observerList) {
observerList = new nsSVGRenderingObserverList();
if (!observerList)
return;
for (nsIFrame* f = aFrame; f; f = f->GetNextContinuation()) {
f->AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED_OR_HAVE_RENDERING_OBSERVERS);
}
aFrame->Properties().Set(ObserversProperty(), observerList);
aElement->SetProperty(nsGkAtoms::renderingobserverlist, observerList, DestroyObservers);
}
aElement->SetHasRenderingObservers(true);
observerList->Add(aObserver);
}
void
nsSVGEffects::RemoveRenderingObserver(nsIFrame *aFrame, nsSVGRenderingObserver *aObserver)
nsSVGEffects::RemoveRenderingObserver(Element *aElement, nsSVGRenderingObserver *aObserver)
{
NS_ASSERTION(!aFrame->GetPrevContinuation(), "aFrame must be first continuation");
nsSVGRenderingObserverList *observerList = GetObserverList(aFrame);
nsSVGRenderingObserverList *observerList = GetObserverList(aElement);
if (observerList) {
NS_ASSERTION(observerList->Contains(aObserver),
"removing observer from an element we're not observing?");
observerList->Remove(aObserver);
// Don't remove the property even if the observer list is empty.
// This might not be a good time to modify the frame property
// hashtables.
if (observerList->IsEmpty()) {
aElement->SetHasRenderingObservers(false);
}
}
}
@ -569,7 +568,11 @@ nsSVGEffects::InvalidateRenderingObservers(nsIFrame *aFrame)
{
NS_ASSERTION(!aFrame->GetPrevContinuation(), "aFrame must be first continuation");
nsSVGRenderingObserverList *observerList = GetObserverList(aFrame);
if (!aFrame->GetContent()->IsElement())
return;
nsSVGRenderingObserverList *observerList =
GetObserverList(aFrame->GetContent()->AsElement());
if (observerList) {
observerList->InvalidateAll();
return;
@ -579,10 +582,23 @@ nsSVGEffects::InvalidateRenderingObservers(nsIFrame *aFrame)
// eSVGContainer so we don't have to check f for null here.
for (nsIFrame *f = aFrame->GetParent();
f->IsFrameOfType(nsIFrame::eSVGContainer); f = f->GetParent()) {
observerList = GetObserverList(f);
if (f->GetContent()->IsElement()) {
observerList = GetObserverList(f->GetContent()->AsElement());
if (observerList) {
observerList->InvalidateAll();
return;
}
}
}
}
void
nsSVGEffects::InvalidateDirectRenderingObservers(Element *aElement)
{
if (aElement->HasRenderingObservers()) {
nsSVGRenderingObserverList *observerList = GetObserverList(aElement);
if (observerList) {
observerList->InvalidateAll();
return;
}
}
}
@ -590,8 +606,7 @@ nsSVGEffects::InvalidateRenderingObservers(nsIFrame *aFrame)
void
nsSVGEffects::InvalidateDirectRenderingObservers(nsIFrame *aFrame)
{
nsSVGRenderingObserverList *observerList = GetObserverList(aFrame);
if (observerList) {
observerList->InvalidateAll();
if (aFrame->GetContent() && aFrame->GetContent()->IsElement()) {
InvalidateDirectRenderingObservers(aFrame->GetContent()->AsElement());
}
}

View File

@ -62,6 +62,7 @@ class nsSVGMaskFrame;
*/
class nsSVGRenderingObserver : public nsStubMutationObserver {
public:
typedef mozilla::dom::Element Element;
nsSVGRenderingObserver(nsIURI* aURI, nsIFrame *aFrame);
virtual ~nsSVGRenderingObserver();
@ -74,8 +75,9 @@ public:
NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
void InvalidateViaReferencedFrame();
void InvalidateViaReferencedElement();
Element* GetReferencedElement();
nsIFrame* GetReferencedFrame();
/**
* @param aOK this is only for the convenience of callers. We set *aOK to false
@ -86,19 +88,17 @@ public:
protected:
// This is called when the referenced resource changes.
virtual void DoUpdate();
void StartListening();
void StopListening();
class SourceReference : public nsReferencedElement {
public:
SourceReference(nsSVGRenderingObserver* aContainer) : mContainer(aContainer) {}
protected:
virtual void ElementChanged(Element* aFrom, Element* aTo) {
if (aFrom) {
aFrom->RemoveMutationObserver(mContainer);
}
mContainer->StopListening();
nsReferencedElement::ElementChanged(aFrom, aTo);
if (aTo) {
aTo->AddMutationObserver(mContainer);
}
mContainer->StartListening();
mContainer->DoUpdate();
}
/**
@ -119,8 +119,8 @@ protected:
// we test the presshell to see if it's destroying itself. If it is,
// then the frame pointer is not valid and we know the frame has gone away.
nsIPresShell *mFramePresShell;
nsIFrame *mReferencedFrame;
nsIPresShell *mReferencedFramePresShell;
// Whether we're in our referenced element's observer list at this time.
PRPackedBool mInObserverList;
};
class nsSVGFilterProperty :
@ -177,11 +177,11 @@ protected:
* nsSVGRenderingObservers can be added or removed. They are not strongly
* referenced so an observer must be removed before it dies.
* When InvalidateAll is called, all outstanding references get
* InvalidateViaReferencedFrame()
* InvalidateViaReferencedElement()
* called on them and the list is cleared. The intent is that
* the observer will force repainting of whatever part of the document
* is needed, and then at paint time the observer will do a clean lookup
* of the referenced frame and [re-]add itself to the frame's observer list.
* of the referenced element and [re-]add itself to the element's observer list.
*
* InvalidateAll must be called before this object is destroyed, i.e.
* before the referenced frame is destroyed. This should normally happen
@ -204,6 +204,12 @@ public:
{ mObservers.PutEntry(aObserver); }
void Remove(nsSVGRenderingObserver* aObserver)
{ mObservers.RemoveEntry(aObserver); }
#ifdef DEBUG
PRBool Contains(nsSVGRenderingObserver* aObserver)
{ return (mObservers.GetEntry(aObserver) != nsnull); }
#endif
PRBool IsEmpty()
{ return mObservers.Count() == 0; }
/**
* Drop all our observers, and notify them that we have changed and dropped
@ -217,6 +223,7 @@ private:
class nsSVGEffects {
public:
typedef mozilla::dom::Element Element;
typedef mozilla::FramePropertyDescriptor FramePropertyDescriptor;
typedef nsInterfaceHashtable<nsURIHashKey, nsIMutationObserver>
URIObserverHashtable;
@ -299,31 +306,32 @@ public:
/**
* @param aFrame must be a first-continuation.
*/
static void AddRenderingObserver(nsIFrame *aFrame, nsSVGRenderingObserver *aObserver);
static void AddRenderingObserver(Element *aElement, nsSVGRenderingObserver *aObserver);
/**
* @param aFrame must be a first-continuation.
*/
static void RemoveRenderingObserver(nsIFrame *aFrame, nsSVGRenderingObserver *aObserver);
static void RemoveRenderingObserver(Element *aElement, nsSVGRenderingObserver *aObserver);
/**
* This can be called on any first-continuation frame. We invalidate aFrame's
* observers, if any, or else walk up to the nearest observable SVG parent
* This can be called on any frame. We invalidate the observers of aFrame's
* element, if any, or else walk up to the nearest observable SVG parent
* frame with observers and invalidate them instead.
*
* Note that this method is very different to e.g.
* nsNodeUtils::AttributeChanged which walks up the content node tree (not
* the frame tree) all the way to the root node (not stopping if it
* encounters a non-container SVG node) invalidating all mutation observers
* (not just nsSVGRenderingObservers) on all nodes along the way (not just
* the first node it finds with observers). In other words, by doing all the
* nsNodeUtils::AttributeChanged which walks up the content node tree all the
* way to the root node (not stopping if it encounters a non-container SVG
* node) invalidating all mutation observers (not just
* nsSVGRenderingObservers) on all nodes along the way (not just the first
* node it finds with observers). In other words, by doing all the
* things in parentheses in the preceding sentence, this method uses
* knowledge about our implementation and what can be affected by SVG effects
* to make invalidation relatively lightweight when an SVG effect changes.
*/
static void InvalidateRenderingObservers(nsIFrame *aFrame);
/**
* This can be called on any frame. Only direct observers of this frame, if
* any, are invalidated.
* This can be called on any element or frame. Only direct observers of this
* (frame's) element, if any, are invalidated.
*/
static void InvalidateDirectRenderingObservers(Element *aElement);
static void InvalidateDirectRenderingObservers(nsIFrame *aFrame);
/**

View File

@ -70,8 +70,7 @@ nsSVGForeignObjectFrame::nsSVGForeignObjectFrame(nsStyleContext* aContext)
: nsSVGForeignObjectFrameBase(aContext),
mInReflow(PR_FALSE)
{
AddStateBits(NS_FRAME_REFLOW_ROOT |
NS_FRAME_MAY_BE_TRANSFORMED_OR_HAVE_RENDERING_OBSERVERS);
AddStateBits(NS_FRAME_REFLOW_ROOT | NS_FRAME_MAY_BE_TRANSFORMED);
}
//----------------------------------------------------------------------