michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: // Main header first: michael@0: #include "nsSVGEffects.h" michael@0: michael@0: // Keep others in (case-insensitive) order: michael@0: #include "nsCSSFrameConstructor.h" michael@0: #include "nsISupportsImpl.h" michael@0: #include "nsSVGClipPathFrame.h" michael@0: #include "nsSVGPaintServerFrame.h" michael@0: #include "nsSVGPathGeometryElement.h" michael@0: #include "nsSVGFilterFrame.h" michael@0: #include "nsSVGMaskFrame.h" michael@0: #include "nsIReflowCallback.h" michael@0: #include "RestyleManager.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: michael@0: // nsSVGRenderingObserver impl michael@0: NS_IMPL_ISUPPORTS(nsSVGRenderingObserver, nsIMutationObserver) michael@0: michael@0: void michael@0: nsSVGRenderingObserver::StartListening() michael@0: { michael@0: Element* target = GetTarget(); michael@0: if (target) { michael@0: target->AddMutationObserver(this); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsSVGRenderingObserver::StopListening() michael@0: { michael@0: Element* target = GetTarget(); michael@0: michael@0: if (target) { michael@0: target->RemoveMutationObserver(this); michael@0: if (mInObserverList) { michael@0: nsSVGEffects::RemoveRenderingObserver(target, this); michael@0: mInObserverList = false; michael@0: } michael@0: } michael@0: NS_ASSERTION(!mInObserverList, "still in an observer list?"); michael@0: } michael@0: michael@0: michael@0: michael@0: /** michael@0: * Note that in the current setup there are two separate observer lists. michael@0: * michael@0: * In nsSVGIDRenderingObserver's ctor, the new object adds itself to the michael@0: * mutation observer list maintained by the referenced element. In this way the michael@0: * nsSVGIDRenderingObserver is notified if there are any attribute or content michael@0: * tree changes to the element or any of its *descendants*. michael@0: * michael@0: * In nsSVGIDRenderingObserver::GetReferencedElement() the michael@0: * nsSVGIDRenderingObserver object also adds itself to an michael@0: * nsSVGRenderingObserverList object belonging to the referenced michael@0: * element. michael@0: * michael@0: * XXX: it would be nice to have a clear and concise executive summary of the michael@0: * benefits/necessity of maintaining a second observer list. michael@0: */ michael@0: michael@0: nsSVGIDRenderingObserver::nsSVGIDRenderingObserver(nsIURI *aURI, michael@0: nsIFrame *aFrame, michael@0: bool aReferenceImage) michael@0: : mElement(MOZ_THIS_IN_INITIALIZER_LIST()), mFrame(aFrame), michael@0: mFramePresShell(aFrame->PresContext()->PresShell()) michael@0: { michael@0: // Start watching the target element michael@0: mElement.Reset(aFrame->GetContent(), aURI, true, aReferenceImage); michael@0: StartListening(); michael@0: } michael@0: michael@0: nsSVGIDRenderingObserver::~nsSVGIDRenderingObserver() michael@0: { michael@0: StopListening(); michael@0: } michael@0: michael@0: static nsSVGRenderingObserverList * michael@0: GetObserverList(Element *aElement) michael@0: { michael@0: return static_cast michael@0: (aElement->GetProperty(nsGkAtoms::renderingobserverlist)); michael@0: } michael@0: michael@0: Element* michael@0: nsSVGRenderingObserver::GetReferencedElement() michael@0: { michael@0: Element* target = GetTarget(); michael@0: #ifdef DEBUG michael@0: if (target) { michael@0: nsSVGRenderingObserverList *observerList = GetObserverList(target); michael@0: bool inObserverList = observerList && observerList->Contains(this); michael@0: NS_ASSERTION(inObserverList == mInObserverList, "failed to track whether we're in our referenced element's observer list!"); michael@0: } else { michael@0: NS_ASSERTION(!mInObserverList, "In whose observer list are we, then?"); michael@0: } michael@0: #endif michael@0: if (target && !mInObserverList) { michael@0: nsSVGEffects::AddRenderingObserver(target, this); michael@0: mInObserverList = true; michael@0: } michael@0: return target; michael@0: } michael@0: michael@0: nsIFrame* michael@0: nsSVGRenderingObserver::GetReferencedFrame() michael@0: { michael@0: Element* referencedElement = GetReferencedElement(); michael@0: return referencedElement ? referencedElement->GetPrimaryFrame() : nullptr; michael@0: } michael@0: michael@0: nsIFrame* michael@0: nsSVGRenderingObserver::GetReferencedFrame(nsIAtom* aFrameType, bool* aOK) michael@0: { michael@0: nsIFrame* frame = GetReferencedFrame(); michael@0: if (frame) { michael@0: if (frame->GetType() == aFrameType) michael@0: return frame; michael@0: if (aOK) { michael@0: *aOK = false; michael@0: } michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: void michael@0: nsSVGIDRenderingObserver::DoUpdate() michael@0: { michael@0: if (mFramePresShell->IsDestroying()) { michael@0: // mFrame is no longer valid. Bail out. michael@0: mFrame = nullptr; michael@0: return; michael@0: } michael@0: if (mElement.get() && mInObserverList) { michael@0: nsSVGEffects::RemoveRenderingObserver(mElement.get(), this); michael@0: mInObserverList = false; michael@0: } michael@0: if (mFrame && mFrame->IsFrameOfType(nsIFrame::eSVG)) { michael@0: // Changes should propagate out to things that might be observing michael@0: // the referencing frame or its ancestors. michael@0: nsSVGEffects::InvalidateRenderingObservers(mFrame); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsSVGRenderingObserver::InvalidateViaReferencedElement() michael@0: { michael@0: mInObserverList = false; michael@0: DoUpdate(); michael@0: } michael@0: michael@0: void michael@0: nsSVGRenderingObserver::NotifyEvictedFromRenderingObserverList() michael@0: { michael@0: mInObserverList = false; // We've been removed from rendering-obs. list. michael@0: StopListening(); // Remove ourselves from mutation-obs. list. michael@0: } michael@0: michael@0: void michael@0: nsSVGRenderingObserver::AttributeChanged(nsIDocument* aDocument, michael@0: dom::Element* aElement, michael@0: int32_t aNameSpaceID, michael@0: nsIAtom* aAttribute, michael@0: int32_t aModType) michael@0: { michael@0: // An attribute belonging to the element that we are observing *or one of its michael@0: // descendants* has changed. michael@0: // michael@0: // In the case of observing a gradient element, say, we want to know if any michael@0: // of its 'stop' element children change, but we don't actually want to do michael@0: // anything for changes to SMIL element children, for example. Maybe it's not michael@0: // worth having logic to optimize for that, but in most cases it could be a michael@0: // small check? michael@0: // michael@0: // XXXjwatt: do we really want to blindly break the link between our michael@0: // observers and ourselves for all attribute changes? For non-ID changes michael@0: // surely that is unnecessary. michael@0: michael@0: DoUpdate(); michael@0: } michael@0: michael@0: void michael@0: nsSVGRenderingObserver::ContentAppended(nsIDocument *aDocument, michael@0: nsIContent *aContainer, michael@0: nsIContent *aFirstNewContent, michael@0: int32_t /* unused */) michael@0: { michael@0: DoUpdate(); michael@0: } michael@0: michael@0: void michael@0: nsSVGRenderingObserver::ContentInserted(nsIDocument *aDocument, michael@0: nsIContent *aContainer, michael@0: nsIContent *aChild, michael@0: int32_t /* unused */) michael@0: { michael@0: DoUpdate(); michael@0: } michael@0: michael@0: void michael@0: nsSVGRenderingObserver::ContentRemoved(nsIDocument *aDocument, michael@0: nsIContent *aContainer, michael@0: nsIContent *aChild, michael@0: int32_t aIndexInContainer, michael@0: nsIContent *aPreviousSibling) michael@0: { michael@0: DoUpdate(); michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(nsSVGFilterProperty, nsISupports) michael@0: michael@0: nsSVGFilterProperty::nsSVGFilterProperty(const nsTArray &aFilters, michael@0: nsIFrame *aFilteredFrame) : michael@0: mFilters(aFilters) michael@0: { michael@0: for (uint32_t i = 0; i < mFilters.Length(); i++) { michael@0: if (mFilters[i].GetType() != NS_STYLE_FILTER_URL) michael@0: continue; michael@0: michael@0: nsSVGFilterReference *reference = michael@0: new nsSVGFilterReference(mFilters[i].GetURL(), aFilteredFrame); michael@0: NS_ADDREF(reference); michael@0: mReferences.AppendElement(reference); michael@0: } michael@0: } michael@0: michael@0: nsSVGFilterProperty::~nsSVGFilterProperty() michael@0: { michael@0: for (uint32_t i = 0; i < mReferences.Length(); i++) { michael@0: NS_RELEASE(mReferences[i]); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: nsSVGFilterProperty::ReferencesValidResources() michael@0: { michael@0: for (uint32_t i = 0; i < mReferences.Length(); i++) { michael@0: if (!mReferences[i]->ReferencesValidResource()) michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: nsSVGFilterProperty::IsInObserverLists() const michael@0: { michael@0: for (uint32_t i = 0; i < mReferences.Length(); i++) { michael@0: if (!mReferences[i]->IsInObserverList()) michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: nsSVGFilterProperty::Invalidate() michael@0: { michael@0: for (uint32_t i = 0; i < mReferences.Length(); i++) { michael@0: mReferences[i]->Invalidate(); michael@0: } michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS_INHERITED(nsSVGFilterReference, michael@0: nsSVGIDRenderingObserver, michael@0: nsISVGFilterReference); michael@0: michael@0: nsSVGFilterFrame * michael@0: nsSVGFilterReference::GetFilterFrame() michael@0: { michael@0: return static_cast michael@0: (GetReferencedFrame(nsGkAtoms::svgFilterFrame, nullptr)); michael@0: } michael@0: michael@0: static void michael@0: InvalidateAllContinuations(nsIFrame* aFrame) michael@0: { michael@0: for (nsIFrame* f = aFrame; f; michael@0: f = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(f)) { michael@0: f->InvalidateFrame(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsSVGFilterReference::DoUpdate() michael@0: { michael@0: nsSVGIDRenderingObserver::DoUpdate(); michael@0: if (!mFrame) michael@0: return; michael@0: michael@0: // Repaint asynchronously in case the filter frame is being torn down michael@0: nsChangeHint changeHint = michael@0: nsChangeHint(nsChangeHint_RepaintFrame); michael@0: michael@0: // Don't need to request UpdateOverflow if we're being reflowed. michael@0: if (!(mFrame->GetStateBits() & NS_FRAME_IN_REFLOW)) { michael@0: NS_UpdateHint(changeHint, nsChangeHint_UpdateOverflow); michael@0: } michael@0: mFramePresShell->GetPresContext()->RestyleManager()->PostRestyleEvent( michael@0: mFrame->GetContent()->AsElement(), nsRestyleHint(0), changeHint); michael@0: } michael@0: michael@0: void michael@0: nsSVGMarkerProperty::DoUpdate() michael@0: { michael@0: nsSVGIDRenderingObserver::DoUpdate(); michael@0: if (!mFrame) michael@0: return; michael@0: michael@0: NS_ASSERTION(mFrame->IsFrameOfType(nsIFrame::eSVG), "SVG frame expected"); michael@0: michael@0: // Repaint asynchronously in case the marker frame is being torn down michael@0: nsChangeHint changeHint = michael@0: nsChangeHint(nsChangeHint_RepaintFrame); michael@0: michael@0: // Don't need to request ReflowFrame if we're being reflowed. michael@0: if (!(mFrame->GetStateBits() & NS_FRAME_IN_REFLOW)) { michael@0: // XXXjwatt: We need to unify SVG into standard reflow so we can just use michael@0: // nsChangeHint_NeedReflow | nsChangeHint_NeedDirtyReflow here. michael@0: nsSVGEffects::InvalidateRenderingObservers(mFrame); michael@0: // XXXSDL KILL THIS!!! michael@0: nsSVGUtils::ScheduleReflowSVG(mFrame); michael@0: } michael@0: mFramePresShell->GetPresContext()->RestyleManager()->PostRestyleEvent( michael@0: mFrame->GetContent()->AsElement(), nsRestyleHint(0), changeHint); michael@0: } michael@0: michael@0: bool michael@0: nsSVGTextPathProperty::TargetIsValid() michael@0: { michael@0: Element* target = GetTarget(); michael@0: return target && target->IsSVG(nsGkAtoms::path); michael@0: } michael@0: michael@0: void michael@0: nsSVGTextPathProperty::DoUpdate() michael@0: { michael@0: nsSVGIDRenderingObserver::DoUpdate(); michael@0: if (!mFrame) michael@0: return; michael@0: michael@0: NS_ASSERTION(mFrame->IsFrameOfType(nsIFrame::eSVG) || mFrame->IsSVGText(), michael@0: "SVG frame expected"); michael@0: michael@0: // Avoid getting into an infinite loop of reflows if the is michael@0: // pointing to one of its ancestors. TargetIsValid returns true iff michael@0: // the target element is a element, and we would not have this michael@0: // nsSVGTextPathProperty if this were a descendant of the michael@0: // target . michael@0: // michael@0: // Note that we still have to post the restyle event when we michael@0: // change from being valid to invalid, so that mPositions on the michael@0: // SVGTextFrame gets updated, skipping the , ensuring michael@0: // that nothing gets painted for that element. michael@0: bool nowValid = TargetIsValid(); michael@0: if (!mValid && !nowValid) { michael@0: // Just return if we were previously invalid, and are still invalid. michael@0: return; michael@0: } michael@0: mValid = nowValid; michael@0: michael@0: // Repaint asynchronously in case the path frame is being torn down michael@0: nsChangeHint changeHint = michael@0: nsChangeHint(nsChangeHint_RepaintFrame | nsChangeHint_UpdateTextPath); michael@0: mFramePresShell->GetPresContext()->RestyleManager()->PostRestyleEvent( michael@0: mFrame->GetContent()->AsElement(), nsRestyleHint(0), changeHint); michael@0: } michael@0: michael@0: void michael@0: nsSVGPaintingProperty::DoUpdate() michael@0: { michael@0: nsSVGIDRenderingObserver::DoUpdate(); michael@0: if (!mFrame) michael@0: return; michael@0: michael@0: if (mFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) { michael@0: nsSVGEffects::InvalidateRenderingObservers(mFrame); michael@0: mFrame->InvalidateFrameSubtree(); michael@0: } else { michael@0: InvalidateAllContinuations(mFrame); michael@0: } michael@0: } michael@0: michael@0: static nsSVGRenderingObserver * michael@0: CreateMarkerProperty(nsIURI *aURI, nsIFrame *aFrame, bool aReferenceImage) michael@0: { return new nsSVGMarkerProperty(aURI, aFrame, aReferenceImage); } michael@0: michael@0: static nsSVGRenderingObserver * michael@0: CreateTextPathProperty(nsIURI *aURI, nsIFrame *aFrame, bool aReferenceImage) michael@0: { return new nsSVGTextPathProperty(aURI, aFrame, aReferenceImage); } michael@0: michael@0: static nsSVGRenderingObserver * michael@0: CreatePaintingProperty(nsIURI *aURI, nsIFrame *aFrame, bool aReferenceImage) michael@0: { return new nsSVGPaintingProperty(aURI, aFrame, aReferenceImage); } michael@0: michael@0: static nsSVGRenderingObserver * michael@0: GetEffectProperty(nsIURI *aURI, nsIFrame *aFrame, michael@0: const FramePropertyDescriptor *aProperty, michael@0: nsSVGRenderingObserver * (* aCreate)(nsIURI *, nsIFrame *, bool)) michael@0: { michael@0: if (!aURI) michael@0: return nullptr; michael@0: michael@0: FrameProperties props = aFrame->Properties(); michael@0: nsSVGRenderingObserver *prop = michael@0: static_cast(props.Get(aProperty)); michael@0: if (prop) michael@0: return prop; michael@0: prop = aCreate(aURI, aFrame, false); michael@0: if (!prop) michael@0: return nullptr; michael@0: NS_ADDREF(prop); michael@0: props.Set(aProperty, static_cast(prop)); michael@0: return prop; michael@0: } michael@0: michael@0: static nsSVGFilterProperty* michael@0: GetOrCreateFilterProperty(nsIFrame *aFrame) michael@0: { michael@0: const nsStyleSVGReset* style = aFrame->StyleSVGReset(); michael@0: if (!style->HasFilters()) michael@0: return nullptr; michael@0: michael@0: FrameProperties props = aFrame->Properties(); michael@0: nsSVGFilterProperty *prop = michael@0: static_cast(props.Get(nsSVGEffects::FilterProperty())); michael@0: if (prop) michael@0: return prop; michael@0: prop = new nsSVGFilterProperty(style->mFilters, aFrame); michael@0: if (!prop) michael@0: return nullptr; michael@0: NS_ADDREF(prop); michael@0: props.Set(nsSVGEffects::FilterProperty(), static_cast(prop)); michael@0: return prop; michael@0: } michael@0: michael@0: nsSVGMarkerProperty * michael@0: nsSVGEffects::GetMarkerProperty(nsIURI *aURI, nsIFrame *aFrame, michael@0: const FramePropertyDescriptor *aProp) michael@0: { michael@0: NS_ABORT_IF_FALSE(aFrame->GetType() == nsGkAtoms::svgPathGeometryFrame && michael@0: static_cast(aFrame->GetContent())->IsMarkable(), michael@0: "Bad frame"); michael@0: return static_cast( michael@0: GetEffectProperty(aURI, aFrame, aProp, CreateMarkerProperty)); michael@0: } michael@0: michael@0: nsSVGTextPathProperty * michael@0: nsSVGEffects::GetTextPathProperty(nsIURI *aURI, nsIFrame *aFrame, michael@0: const FramePropertyDescriptor *aProp) michael@0: { michael@0: return static_cast( michael@0: GetEffectProperty(aURI, aFrame, aProp, CreateTextPathProperty)); michael@0: } michael@0: michael@0: nsSVGPaintingProperty * michael@0: nsSVGEffects::GetPaintingProperty(nsIURI *aURI, nsIFrame *aFrame, michael@0: const FramePropertyDescriptor *aProp) michael@0: { michael@0: return static_cast( michael@0: GetEffectProperty(aURI, aFrame, aProp, CreatePaintingProperty)); michael@0: } michael@0: michael@0: static nsSVGRenderingObserver * michael@0: GetEffectPropertyForURI(nsIURI *aURI, nsIFrame *aFrame, michael@0: const FramePropertyDescriptor *aProperty, michael@0: nsSVGRenderingObserver * (* aCreate)(nsIURI *, nsIFrame *, bool)) michael@0: { michael@0: if (!aURI) michael@0: return nullptr; michael@0: michael@0: FrameProperties props = aFrame->Properties(); michael@0: nsSVGEffects::URIObserverHashtable *hashtable = michael@0: static_cast(props.Get(aProperty)); michael@0: if (!hashtable) { michael@0: hashtable = new nsSVGEffects::URIObserverHashtable(); michael@0: props.Set(aProperty, hashtable); michael@0: } michael@0: nsSVGRenderingObserver* prop = michael@0: static_cast(hashtable->GetWeak(aURI)); michael@0: if (!prop) { michael@0: bool watchImage = aProperty == nsSVGEffects::BackgroundImageProperty(); michael@0: prop = aCreate(aURI, aFrame, watchImage); michael@0: hashtable->Put(aURI, prop); michael@0: } michael@0: return prop; michael@0: } michael@0: michael@0: nsSVGPaintingProperty * michael@0: nsSVGEffects::GetPaintingPropertyForURI(nsIURI *aURI, nsIFrame *aFrame, michael@0: const FramePropertyDescriptor *aProp) michael@0: { michael@0: return static_cast( michael@0: GetEffectPropertyForURI(aURI, aFrame, aProp, CreatePaintingProperty)); michael@0: } michael@0: michael@0: nsSVGEffects::EffectProperties michael@0: nsSVGEffects::GetEffectProperties(nsIFrame *aFrame) michael@0: { michael@0: NS_ASSERTION(!aFrame->GetPrevContinuation(), "aFrame should be first continuation"); michael@0: michael@0: EffectProperties result; michael@0: const nsStyleSVGReset *style = aFrame->StyleSVGReset(); michael@0: result.mFilter = GetOrCreateFilterProperty(aFrame); michael@0: result.mClipPath = michael@0: GetPaintingProperty(style->mClipPath, aFrame, ClipPathProperty()); michael@0: result.mMask = michael@0: GetPaintingProperty(style->mMask, aFrame, MaskProperty()); michael@0: return result; michael@0: } michael@0: michael@0: nsSVGPaintServerFrame * michael@0: nsSVGEffects::GetPaintServer(nsIFrame *aTargetFrame, const nsStyleSVGPaint *aPaint, michael@0: const FramePropertyDescriptor *aType) michael@0: { michael@0: if (aPaint->mType != eStyleSVGPaintType_Server) michael@0: return nullptr; michael@0: michael@0: nsIFrame *frame = aTargetFrame->GetContent()->IsNodeOfType(nsINode::eTEXT) ? michael@0: aTargetFrame->GetParent() : aTargetFrame; michael@0: nsSVGPaintingProperty *property = michael@0: nsSVGEffects::GetPaintingProperty(aPaint->mPaint.mPaintServer, frame, aType); michael@0: if (!property) michael@0: return nullptr; michael@0: nsIFrame *result = property->GetReferencedFrame(); michael@0: if (!result) michael@0: return nullptr; michael@0: michael@0: nsIAtom *type = result->GetType(); michael@0: if (type != nsGkAtoms::svgLinearGradientFrame && michael@0: type != nsGkAtoms::svgRadialGradientFrame && michael@0: type != nsGkAtoms::svgPatternFrame) michael@0: return nullptr; michael@0: michael@0: return static_cast(result); michael@0: } michael@0: michael@0: nsSVGClipPathFrame * michael@0: nsSVGEffects::EffectProperties::GetClipPathFrame(bool *aOK) michael@0: { michael@0: if (!mClipPath) michael@0: return nullptr; michael@0: nsSVGClipPathFrame *frame = static_cast michael@0: (mClipPath->GetReferencedFrame(nsGkAtoms::svgClipPathFrame, aOK)); michael@0: if (frame && aOK && *aOK) { michael@0: *aOK = frame->IsValid(); michael@0: } michael@0: return frame; michael@0: } michael@0: michael@0: nsSVGMaskFrame * michael@0: nsSVGEffects::EffectProperties::GetMaskFrame(bool *aOK) michael@0: { michael@0: if (!mMask) michael@0: return nullptr; michael@0: return static_cast michael@0: (mMask->GetReferencedFrame(nsGkAtoms::svgMaskFrame, aOK)); michael@0: } michael@0: michael@0: void michael@0: nsSVGEffects::UpdateEffects(nsIFrame *aFrame) michael@0: { michael@0: NS_ASSERTION(aFrame->GetContent()->IsElement(), michael@0: "aFrame's content should be an element"); michael@0: michael@0: FrameProperties props = aFrame->Properties(); michael@0: props.Delete(FilterProperty()); michael@0: props.Delete(MaskProperty()); michael@0: props.Delete(ClipPathProperty()); michael@0: props.Delete(MarkerBeginProperty()); michael@0: props.Delete(MarkerMiddleProperty()); michael@0: props.Delete(MarkerEndProperty()); michael@0: props.Delete(FillProperty()); michael@0: props.Delete(StrokeProperty()); michael@0: props.Delete(BackgroundImageProperty()); michael@0: michael@0: // Ensure that the filter is repainted correctly michael@0: // We can't do that in DoUpdate as the referenced frame may not be valid michael@0: GetOrCreateFilterProperty(aFrame); michael@0: michael@0: if (aFrame->GetType() == nsGkAtoms::svgPathGeometryFrame && michael@0: static_cast(aFrame->GetContent())->IsMarkable()) { michael@0: // Set marker properties here to avoid reference loops michael@0: const nsStyleSVG *style = aFrame->StyleSVG(); michael@0: GetEffectProperty(style->mMarkerStart, aFrame, MarkerBeginProperty(), michael@0: CreateMarkerProperty); michael@0: GetEffectProperty(style->mMarkerMid, aFrame, MarkerMiddleProperty(), michael@0: CreateMarkerProperty); michael@0: GetEffectProperty(style->mMarkerEnd, aFrame, MarkerEndProperty(), michael@0: CreateMarkerProperty); michael@0: } michael@0: } michael@0: michael@0: nsSVGFilterProperty * michael@0: nsSVGEffects::GetFilterProperty(nsIFrame *aFrame) michael@0: { michael@0: NS_ASSERTION(!aFrame->GetPrevContinuation(), "aFrame should be first continuation"); michael@0: michael@0: if (!aFrame->StyleSVGReset()->HasFilters()) michael@0: return nullptr; michael@0: michael@0: return static_cast michael@0: (aFrame->Properties().Get(FilterProperty())); michael@0: } michael@0: michael@0: static PLDHashOperator michael@0: GatherEnumerator(nsPtrHashKey* aEntry, void* aArg) michael@0: { michael@0: nsTArray* array = michael@0: static_cast*>(aArg); michael@0: array->AppendElement(aEntry->GetKey()); michael@0: michael@0: return PL_DHASH_REMOVE; michael@0: } michael@0: michael@0: static PLDHashOperator michael@0: GatherEnumeratorForReflow(nsPtrHashKey* aEntry, void* aArg) michael@0: { michael@0: if (!aEntry->GetKey()->ObservesReflow()) { michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: nsTArray* array = michael@0: static_cast*>(aArg); michael@0: array->AppendElement(aEntry->GetKey()); michael@0: michael@0: return PL_DHASH_REMOVE; michael@0: } michael@0: michael@0: void michael@0: nsSVGRenderingObserverList::InvalidateAll() michael@0: { michael@0: if (mObservers.Count() == 0) michael@0: return; michael@0: michael@0: nsAutoTArray observers; michael@0: michael@0: // The PL_DHASH_REMOVE in GatherEnumerator drops all our observers here: michael@0: mObservers.EnumerateEntries(GatherEnumerator, &observers); michael@0: michael@0: for (uint32_t i = 0; i < observers.Length(); ++i) { michael@0: observers[i]->InvalidateViaReferencedElement(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsSVGRenderingObserverList::InvalidateAllForReflow() michael@0: { michael@0: if (mObservers.Count() == 0) michael@0: return; michael@0: michael@0: nsAutoTArray observers; michael@0: michael@0: // The PL_DHASH_REMOVE in GatherEnumerator drops all our observers here: michael@0: mObservers.EnumerateEntries(GatherEnumeratorForReflow, &observers); michael@0: michael@0: for (uint32_t i = 0; i < observers.Length(); ++i) { michael@0: observers[i]->InvalidateViaReferencedElement(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsSVGRenderingObserverList::RemoveAll() michael@0: { michael@0: nsAutoTArray observers; michael@0: michael@0: // The PL_DHASH_REMOVE in GatherEnumerator drops all our observers here: michael@0: mObservers.EnumerateEntries(GatherEnumerator, &observers); michael@0: michael@0: // Our list is now cleared. We need to notify the observers we've removed, michael@0: // so they can update their state & remove themselves as mutation-observers. michael@0: for (uint32_t i = 0; i < observers.Length(); ++i) { michael@0: observers[i]->NotifyEvictedFromRenderingObserverList(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsSVGEffects::AddRenderingObserver(Element *aElement, nsSVGRenderingObserver *aObserver) michael@0: { michael@0: nsSVGRenderingObserverList *observerList = GetObserverList(aElement); michael@0: if (!observerList) { michael@0: observerList = new nsSVGRenderingObserverList(); michael@0: if (!observerList) michael@0: return; michael@0: aElement->SetProperty(nsGkAtoms::renderingobserverlist, observerList, michael@0: nsINode::DeleteProperty); michael@0: } michael@0: aElement->SetHasRenderingObservers(true); michael@0: observerList->Add(aObserver); michael@0: } michael@0: michael@0: void michael@0: nsSVGEffects::RemoveRenderingObserver(Element *aElement, nsSVGRenderingObserver *aObserver) michael@0: { michael@0: nsSVGRenderingObserverList *observerList = GetObserverList(aElement); michael@0: if (observerList) { michael@0: NS_ASSERTION(observerList->Contains(aObserver), michael@0: "removing observer from an element we're not observing?"); michael@0: observerList->Remove(aObserver); michael@0: if (observerList->IsEmpty()) { michael@0: aElement->SetHasRenderingObservers(false); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsSVGEffects::RemoveAllRenderingObservers(Element *aElement) michael@0: { michael@0: nsSVGRenderingObserverList *observerList = GetObserverList(aElement); michael@0: if (observerList) { michael@0: observerList->RemoveAll(); michael@0: aElement->SetHasRenderingObservers(false); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsSVGEffects::InvalidateRenderingObservers(nsIFrame *aFrame) michael@0: { michael@0: NS_ASSERTION(!aFrame->GetPrevContinuation(), "aFrame must be first continuation"); michael@0: michael@0: if (!aFrame->GetContent()->IsElement()) michael@0: return; michael@0: michael@0: nsSVGRenderingObserverList *observerList = michael@0: GetObserverList(aFrame->GetContent()->AsElement()); michael@0: if (observerList) { michael@0: observerList->InvalidateAll(); michael@0: return; michael@0: } michael@0: michael@0: // Check ancestor SVG containers. The root frame cannot be of type michael@0: // eSVGContainer so we don't have to check f for null here. michael@0: for (nsIFrame *f = aFrame->GetParent(); michael@0: f->IsFrameOfType(nsIFrame::eSVGContainer); f = f->GetParent()) { michael@0: if (f->GetContent()->IsElement()) { michael@0: observerList = GetObserverList(f->GetContent()->AsElement()); michael@0: if (observerList) { michael@0: observerList->InvalidateAll(); michael@0: return; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsSVGEffects::InvalidateDirectRenderingObservers(Element *aElement, uint32_t aFlags /* = 0 */) michael@0: { michael@0: if (aElement->HasRenderingObservers()) { michael@0: nsSVGRenderingObserverList *observerList = GetObserverList(aElement); michael@0: if (observerList) { michael@0: if (aFlags & INVALIDATE_REFLOW) { michael@0: observerList->InvalidateAllForReflow(); michael@0: } else { michael@0: observerList->InvalidateAll(); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsSVGEffects::InvalidateDirectRenderingObservers(nsIFrame *aFrame, uint32_t aFlags /* = 0 */) michael@0: { michael@0: if (aFrame->GetContent() && aFrame->GetContent()->IsElement()) { michael@0: InvalidateDirectRenderingObservers(aFrame->GetContent()->AsElement(), aFlags); michael@0: } michael@0: }