Backed out changeset 2960ea3e50ca (bug 1881157) for causing crashtest assertion failu...
[gecko.git] / layout / svg / SVGObserverUtils.cpp
blob551e7c67b7a61d6499b08da5039d7765b4629579
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 // Main header first:
8 #include "SVGObserverUtils.h"
10 // Keep others in (case-insensitive) order:
11 #include "mozilla/css/ImageLoader.h"
12 #include "mozilla/dom/CanvasRenderingContext2D.h"
13 #include "mozilla/dom/ReferrerInfo.h"
14 #include "mozilla/dom/SVGGeometryElement.h"
15 #include "mozilla/dom/SVGMPathElement.h"
16 #include "mozilla/dom/SVGTextPathElement.h"
17 #include "mozilla/dom/SVGUseElement.h"
18 #include "mozilla/PresShell.h"
19 #include "mozilla/RestyleManager.h"
20 #include "mozilla/SVGClipPathFrame.h"
21 #include "mozilla/SVGGeometryFrame.h"
22 #include "mozilla/SVGMaskFrame.h"
23 #include "mozilla/SVGTextFrame.h"
24 #include "mozilla/SVGUtils.h"
25 #include "nsCSSFrameConstructor.h"
26 #include "nsCycleCollectionParticipant.h"
27 #include "nsHashKeys.h"
28 #include "nsIContent.h"
29 #include "nsIContentInlines.h"
30 #include "nsInterfaceHashtable.h"
31 #include "nsIReflowCallback.h"
32 #include "nsISupportsImpl.h"
33 #include "nsLayoutUtils.h"
34 #include "nsNetUtil.h"
35 #include "nsTHashtable.h"
36 #include "nsURIHashKey.h"
37 #include "SVGFilterFrame.h"
38 #include "SVGMarkerFrame.h"
39 #include "SVGPaintServerFrame.h"
41 using namespace mozilla::dom;
43 namespace mozilla {
45 bool URLAndReferrerInfo::operator==(const URLAndReferrerInfo& aRHS) const {
46 bool uriEqual = false, referrerEqual = false;
47 this->mURI->Equals(aRHS.mURI, &uriEqual);
48 this->mReferrerInfo->Equals(aRHS.mReferrerInfo, &referrerEqual);
50 return uriEqual && referrerEqual;
53 class URLAndReferrerInfoHashKey : public PLDHashEntryHdr {
54 public:
55 using KeyType = const URLAndReferrerInfo*;
56 using KeyTypePointer = const URLAndReferrerInfo*;
58 explicit URLAndReferrerInfoHashKey(const URLAndReferrerInfo* aKey) noexcept
59 : mKey(aKey) {
60 MOZ_COUNT_CTOR(URLAndReferrerInfoHashKey);
62 URLAndReferrerInfoHashKey(URLAndReferrerInfoHashKey&& aToMove) noexcept
63 : PLDHashEntryHdr(std::move(aToMove)), mKey(std::move(aToMove.mKey)) {
64 MOZ_COUNT_CTOR(URLAndReferrerInfoHashKey);
66 MOZ_COUNTED_DTOR(URLAndReferrerInfoHashKey)
68 const URLAndReferrerInfo* GetKey() const { return mKey; }
70 bool KeyEquals(const URLAndReferrerInfo* aKey) const {
71 if (!mKey) {
72 return !aKey;
74 return *mKey == *aKey;
77 static const URLAndReferrerInfo* KeyToPointer(
78 const URLAndReferrerInfo* aKey) {
79 return aKey;
82 static PLDHashNumber HashKey(const URLAndReferrerInfo* aKey) {
83 if (!aKey) {
84 // If the key is null, return hash for empty string.
85 return HashString(""_ns);
87 nsAutoCString urlSpec, referrerSpec;
88 // nsURIHashKey ignores GetSpec() failures, so we do too:
89 Unused << aKey->GetURI()->GetSpec(urlSpec);
90 return AddToHash(
91 HashString(urlSpec),
92 static_cast<ReferrerInfo*>(aKey->GetReferrerInfo())->Hash());
95 enum { ALLOW_MEMMOVE = true };
97 protected:
98 RefPtr<const URLAndReferrerInfo> mKey;
102 * Return a baseURL for resolving a local-ref URL.
104 * @param aContent an element which uses a local-ref property. Here are some
105 * examples:
106 * <rect fill=url(#foo)>
107 * <circle clip-path=url(#foo)>
108 * <use xlink:href="#foo">
110 static already_AddRefed<nsIURI> GetBaseURLForLocalRef(nsIContent* content,
111 nsIURI* aURI) {
112 MOZ_ASSERT(content);
114 // Content is in a shadow tree. If this URL was specified in the subtree
115 // referenced by the <use>, element, and that subtree came from a separate
116 // resource document, then we want the fragment-only URL to resolve to an
117 // element from the resource document. Otherwise, the URL was specified
118 // somewhere in the document with the <use> element, and we want the
119 // fragment-only URL to resolve to an element in that document.
120 if (SVGUseElement* use = content->GetContainingSVGUseShadowHost()) {
121 if (nsIURI* originalURI = use->GetSourceDocURI()) {
122 bool isEqualsExceptRef = false;
123 aURI->EqualsExceptRef(originalURI, &isEqualsExceptRef);
124 if (isEqualsExceptRef) {
125 return do_AddRef(originalURI);
130 // For a local-reference URL, resolve that fragment against the current
131 // document that relative URLs are resolved against.
132 return do_AddRef(content->OwnerDoc()->GetDocumentURI());
135 static already_AddRefed<URLAndReferrerInfo> ResolveURLUsingLocalRef(
136 nsIFrame* aFrame, const StyleComputedImageUrl& aURL) {
137 MOZ_ASSERT(aFrame);
139 nsCOMPtr<nsIURI> uri = aURL.GetURI();
141 if (aURL.IsLocalRef()) {
142 uri = GetBaseURLForLocalRef(aFrame->GetContent(), uri);
143 uri = aURL.ResolveLocalRef(uri);
146 if (!uri) {
147 return nullptr;
150 return do_AddRef(new URLAndReferrerInfo(uri, aURL.ExtraData()));
153 static already_AddRefed<URLAndReferrerInfo> ResolveURLUsingLocalRef(
154 nsIContent* aContent, const nsAString& aURL) {
155 // Like GetBaseURLForLocalRef, we want to resolve the
156 // URL against any <use> element shadow tree's source document.
158 // Unlike GetBaseURLForLocalRef, we are assuming that the URL was specified
159 // directly on mFrame's content (because this ResolveURLUsingLocalRef
160 // overload is used for href="" attributes and not CSS URL values), so there
161 // is no need to check whether the URL was specified / inherited from
162 // outside the shadow tree.
163 nsIURI* base = nullptr;
164 const Encoding* encoding = nullptr;
165 if (SVGUseElement* use = aContent->GetContainingSVGUseShadowHost()) {
166 base = use->GetSourceDocURI();
167 encoding = use->GetSourceDocCharacterSet();
170 if (!base) {
171 base = aContent->OwnerDoc()->GetDocumentURI();
172 encoding = aContent->OwnerDoc()->GetDocumentCharacterSet();
175 nsCOMPtr<nsIURI> uri;
176 Unused << NS_NewURI(getter_AddRefs(uri), aURL, WrapNotNull(encoding), base);
178 if (!uri) {
179 return nullptr;
182 // There's no clear refererer policy spec about non-CSS SVG resource
183 // references Bug 1415044 to investigate which referrer we should use
184 nsIReferrerInfo* referrerInfo =
185 aContent->OwnerDoc()->ReferrerInfoForInternalCSSAndSVGResources();
187 return do_AddRef(new URLAndReferrerInfo(uri, referrerInfo));
190 class SVGFilterObserverList;
193 * A class used as a member of the "observer" classes below to help them
194 * avoid dereferencing their frame during presshell teardown when their frame
195 * may have been destroyed (leaving their pointer to their frame dangling).
197 * When a presshell is torn down, the properties for each frame may not be
198 * deleted until after the frames are destroyed. "Observer" objects (attached
199 * as frame properties) must therefore check whether the presshell is being
200 * torn down before using their pointer to their frame.
202 * mFramePresShell may be null, but when mFrame is non-null, mFramePresShell
203 * is guaranteed to be non-null, too.
205 struct SVGFrameReferenceFromProperty {
206 explicit SVGFrameReferenceFromProperty(nsIFrame* aFrame)
207 : mFrame(aFrame), mFramePresShell(aFrame->PresShell()) {}
209 // Clear our reference to the frame.
210 void Detach() {
211 mFrame = nullptr;
212 mFramePresShell = nullptr;
215 // null if the frame has become invalid
216 nsIFrame* Get() {
217 if (mFramePresShell && mFramePresShell->IsDestroying()) {
218 Detach(); // mFrame is no longer valid.
220 return mFrame;
223 private:
224 // The frame that our property is attached to (may be null).
225 nsIFrame* mFrame;
226 PresShell* mFramePresShell;
229 void SVGRenderingObserver::StartObserving() {
230 Element* target = GetReferencedElementWithoutObserving();
231 if (target) {
232 target->AddMutationObserver(this);
236 void SVGRenderingObserver::StopObserving() {
237 Element* target = GetReferencedElementWithoutObserving();
239 if (target) {
240 target->RemoveMutationObserver(this);
241 if (mInObserverSet) {
242 SVGObserverUtils::RemoveRenderingObserver(target, this);
243 mInObserverSet = false;
246 NS_ASSERTION(!mInObserverSet, "still in an observer set?");
249 Element* SVGRenderingObserver::GetAndObserveReferencedElement() {
250 #ifdef DEBUG
251 DebugObserverSet();
252 #endif
253 Element* referencedElement = GetReferencedElementWithoutObserving();
254 if (referencedElement && !mInObserverSet) {
255 SVGObserverUtils::AddRenderingObserver(referencedElement, this);
256 mInObserverSet = true;
258 return referencedElement;
261 nsIFrame* SVGRenderingObserver::GetAndObserveReferencedFrame() {
262 Element* referencedElement = GetAndObserveReferencedElement();
263 return referencedElement ? referencedElement->GetPrimaryFrame() : nullptr;
266 nsIFrame* SVGRenderingObserver::GetAndObserveReferencedFrame(
267 LayoutFrameType aFrameType, bool* aOK) {
268 nsIFrame* frame = GetAndObserveReferencedFrame();
269 if (frame) {
270 if (frame->Type() == aFrameType) {
271 return frame;
273 if (aOK) {
274 *aOK = false;
277 return nullptr;
280 void SVGRenderingObserver::OnNonDOMMutationRenderingChange() {
281 OnRenderingChange();
284 void SVGRenderingObserver::NotifyEvictedFromRenderingObserverSet() {
285 mInObserverSet = false; // We've been removed from rendering-obs. set.
286 StopObserving(); // Stop observing mutations too.
289 void SVGRenderingObserver::AttributeChanged(dom::Element* aElement,
290 int32_t aNameSpaceID,
291 nsAtom* aAttribute,
292 int32_t aModType,
293 const nsAttrValue* aOldValue) {
294 if (aElement->IsInNativeAnonymousSubtree()) {
295 // Don't observe attribute changes in native-anonymous subtrees like
296 // scrollbars.
297 return;
300 // An attribute belonging to the element that we are observing *or one of its
301 // descendants* has changed.
303 // In the case of observing a gradient element, say, we want to know if any
304 // of its 'stop' element children change, but we don't actually want to do
305 // anything for changes to SMIL element children, for example. Maybe it's not
306 // worth having logic to optimize for that, but in most cases it could be a
307 // small check?
309 // XXXjwatt: do we really want to blindly break the link between our
310 // observers and ourselves for all attribute changes? For non-ID changes
311 // surely that is unnecessary.
313 OnRenderingChange();
316 void SVGRenderingObserver::ContentAppended(nsIContent* aFirstNewContent) {
317 OnRenderingChange();
320 void SVGRenderingObserver::ContentInserted(nsIContent* aChild) {
321 OnRenderingChange();
324 void SVGRenderingObserver::ContentRemoved(nsIContent* aChild,
325 nsIContent* aPreviousSibling) {
326 OnRenderingChange();
330 * SVG elements reference supporting resources by element ID. We need to
331 * track when those resources change and when the document changes in ways
332 * that affect which element is referenced by a given ID (e.g., when
333 * element IDs change). The code here is responsible for that.
335 * When a frame references a supporting resource, we create a property
336 * object derived from SVGIDRenderingObserver to manage the relationship. The
337 * property object is attached to the referencing frame.
339 class SVGIDRenderingObserver : public SVGRenderingObserver {
340 public:
341 // Callback for checking if the element being observed is valid for this
342 // observer. Note that this may be called during construction, before the
343 // deriving class is fully constructed.
344 using TargetIsValidCallback = bool (*)(const Element&);
345 SVGIDRenderingObserver(
346 URLAndReferrerInfo* aURI, nsIContent* aObservingContent,
347 bool aReferenceImage,
348 uint32_t aCallbacks = kAttributeChanged | kContentAppended |
349 kContentInserted | kContentRemoved,
350 TargetIsValidCallback aTargetIsValidCallback = nullptr);
352 void Traverse(nsCycleCollectionTraversalCallback* aCB);
354 protected:
355 virtual ~SVGIDRenderingObserver() {
356 // This needs to call our GetReferencedElementWithoutObserving override,
357 // so must be called here rather than in our base class's dtor.
358 StopObserving();
361 void TargetChanged() {
362 mTargetIsValid = ([this] {
363 Element* observed = mObservedElementTracker.get();
364 if (!observed) {
365 return false;
367 // If the content is observing an ancestor, then return the target is not
368 // valid.
370 // TODO(emilio): Should we allow content observing its own descendants?
371 // That seems potentially-bad as well.
372 if (observed->OwnerDoc() == mObservingContent->OwnerDoc() &&
373 nsContentUtils::ContentIsHostIncludingDescendantOf(mObservingContent,
374 observed)) {
375 return false;
377 if (mTargetIsValidCallback) {
378 return mTargetIsValidCallback(*observed);
380 return true;
381 }());
384 Element* GetReferencedElementWithoutObserving() final {
385 return mTargetIsValid ? mObservedElementTracker.get() : nullptr;
388 void OnRenderingChange() override;
391 * Helper that provides a reference to the element with the ID that our
392 * observer wants to observe, and that will invalidate our observer if the
393 * element that that ID identifies changes to a different element (or none).
395 class ElementTracker final : public IDTracker {
396 public:
397 explicit ElementTracker(SVGIDRenderingObserver* aOwningObserver)
398 : mOwningObserver(aOwningObserver) {}
400 protected:
401 void ElementChanged(Element* aFrom, Element* aTo) override {
402 // Call OnRenderingChange() before the target changes, so that
403 // mIsTargetValid reflects the right state.
404 mOwningObserver->OnRenderingChange();
405 mOwningObserver->StopObserving();
406 IDTracker::ElementChanged(aFrom, aTo);
407 mOwningObserver->TargetChanged();
408 mOwningObserver->StartObserving();
409 // And same after the target changes, for the same reason.
410 mOwningObserver->OnRenderingChange();
413 * Override IsPersistent because we want to keep tracking the element
414 * for the ID even when it changes.
416 bool IsPersistent() override { return true; }
418 private:
419 SVGIDRenderingObserver* mOwningObserver;
422 ElementTracker mObservedElementTracker;
423 RefPtr<Element> mObservingContent;
424 bool mTargetIsValid = false;
425 TargetIsValidCallback mTargetIsValidCallback;
429 * Note that in the current setup there are two separate observer lists.
431 * In SVGIDRenderingObserver's ctor, the new object adds itself to the
432 * mutation observer list maintained by the referenced element. In this way the
433 * SVGIDRenderingObserver is notified if there are any attribute or content
434 * tree changes to the element or any of its *descendants*.
436 * In SVGIDRenderingObserver::GetAndObserveReferencedElement() the
437 * SVGIDRenderingObserver object also adds itself to an
438 * SVGRenderingObserverSet object belonging to the referenced
439 * element.
441 * XXX: it would be nice to have a clear and concise executive summary of the
442 * benefits/necessity of maintaining a second observer list.
444 SVGIDRenderingObserver::SVGIDRenderingObserver(
445 URLAndReferrerInfo* aURI, nsIContent* aObservingContent,
446 bool aReferenceImage, uint32_t aCallbacks,
447 TargetIsValidCallback aTargetIsValidCallback)
448 : SVGRenderingObserver(aCallbacks),
449 mObservedElementTracker(this),
450 mObservingContent(aObservingContent->AsElement()),
451 mTargetIsValidCallback(aTargetIsValidCallback) {
452 // Start watching the target element
453 nsIURI* uri = nullptr;
454 nsIReferrerInfo* referrerInfo = nullptr;
455 if (aURI) {
456 uri = aURI->GetURI();
457 referrerInfo = aURI->GetReferrerInfo();
460 mObservedElementTracker.ResetToURIFragmentID(
461 aObservingContent, uri, referrerInfo, true, aReferenceImage);
462 TargetChanged();
463 StartObserving();
466 void SVGIDRenderingObserver::Traverse(nsCycleCollectionTraversalCallback* aCB) {
467 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCB, "mObservingContent");
468 aCB->NoteXPCOMChild(mObservingContent);
469 mObservedElementTracker.Traverse(aCB);
472 void SVGIDRenderingObserver::OnRenderingChange() {
473 if (mObservedElementTracker.get() && mInObserverSet) {
474 SVGObserverUtils::RemoveRenderingObserver(mObservedElementTracker.get(),
475 this);
476 mInObserverSet = false;
480 class SVGRenderingObserverProperty : public SVGIDRenderingObserver {
481 public:
482 NS_DECL_ISUPPORTS
484 SVGRenderingObserverProperty(
485 URLAndReferrerInfo* aURI, nsIFrame* aFrame, bool aReferenceImage,
486 uint32_t aCallbacks = kAttributeChanged | kContentAppended |
487 kContentInserted | kContentRemoved,
488 TargetIsValidCallback aTargetIsValidCallback = nullptr)
489 : SVGIDRenderingObserver(aURI, aFrame->GetContent(), aReferenceImage,
490 aCallbacks, aTargetIsValidCallback),
491 mFrameReference(aFrame) {}
493 protected:
494 virtual ~SVGRenderingObserverProperty() = default; // non-public
496 void OnRenderingChange() override;
498 SVGFrameReferenceFromProperty mFrameReference;
501 NS_IMPL_ISUPPORTS(SVGRenderingObserverProperty, nsIMutationObserver)
503 void SVGRenderingObserverProperty::OnRenderingChange() {
504 SVGIDRenderingObserver::OnRenderingChange();
506 if (!mTargetIsValid) {
507 return;
510 nsIFrame* frame = mFrameReference.Get();
512 if (frame && frame->HasAllStateBits(NS_FRAME_SVG_LAYOUT)) {
513 // We need to notify anything that is observing the referencing frame or
514 // any of its ancestors that the referencing frame has been invalidated.
515 // Since walking the parent chain checking for observers is expensive we
516 // do that using a change hint (multiple change hints of the same type are
517 // coalesced).
518 nsLayoutUtils::PostRestyleEvent(frame->GetContent()->AsElement(),
519 RestyleHint{0},
520 nsChangeHint_InvalidateRenderingObservers);
524 static bool IsSVGGeometryElement(const Element& aObserved) {
525 return aObserved.IsSVGGeometryElement();
528 class SVGTextPathObserver final : public SVGRenderingObserverProperty {
529 public:
530 SVGTextPathObserver(URLAndReferrerInfo* aURI, nsIFrame* aFrame,
531 bool aReferenceImage)
532 : SVGRenderingObserverProperty(aURI, aFrame, aReferenceImage,
533 kAttributeChanged, IsSVGGeometryElement) {}
535 protected:
536 void OnRenderingChange() override;
539 void SVGTextPathObserver::OnRenderingChange() {
540 SVGRenderingObserverProperty::OnRenderingChange();
542 if (!mTargetIsValid) {
543 return;
546 nsIFrame* frame = mFrameReference.Get();
547 if (!frame) {
548 return;
551 MOZ_ASSERT(frame->IsSVGFrame() || frame->IsInSVGTextSubtree(),
552 "SVG frame expected");
554 MOZ_ASSERT(frame->GetContent()->IsSVGElement(nsGkAtoms::textPath),
555 "expected frame for a <textPath> element");
557 auto* text = static_cast<SVGTextFrame*>(
558 nsLayoutUtils::GetClosestFrameOfType(frame, LayoutFrameType::SVGText));
559 MOZ_ASSERT(text, "expected to find an ancestor SVGTextFrame");
560 if (text) {
561 text->AddStateBits(NS_STATE_SVG_POSITIONING_DIRTY);
563 if (SVGUtils::AnyOuterSVGIsCallingReflowSVG(text)) {
564 text->AddStateBits(NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN);
565 if (text->HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
566 text->ReflowSVGNonDisplayText();
567 } else {
568 text->ReflowSVG();
570 } else {
571 text->ScheduleReflowSVG();
576 class SVGMPathObserver final : public SVGIDRenderingObserver {
577 public:
578 NS_DECL_ISUPPORTS
580 SVGMPathObserver(URLAndReferrerInfo* aURI, SVGMPathElement* aElement)
581 : SVGIDRenderingObserver(aURI, aElement, /* aReferenceImage = */ false,
582 kAttributeChanged, IsSVGGeometryElement) {}
584 protected:
585 virtual ~SVGMPathObserver() = default; // non-public
587 void OnRenderingChange() override;
590 NS_IMPL_ISUPPORTS(SVGMPathObserver, nsIMutationObserver)
592 void SVGMPathObserver::OnRenderingChange() {
593 SVGIDRenderingObserver::OnRenderingChange();
595 if (!mTargetIsValid) {
596 return;
599 auto* element = static_cast<SVGMPathElement*>(mObservingContent.get());
600 element->NotifyParentOfMpathChange();
603 class SVGMarkerObserver final : public SVGRenderingObserverProperty {
604 public:
605 SVGMarkerObserver(URLAndReferrerInfo* aURI, nsIFrame* aFrame,
606 bool aReferenceImage)
607 : SVGRenderingObserverProperty(aURI, aFrame, aReferenceImage,
608 kAttributeChanged | kContentAppended |
609 kContentInserted | kContentRemoved) {}
611 protected:
612 void OnRenderingChange() override;
615 void SVGMarkerObserver::OnRenderingChange() {
616 SVGRenderingObserverProperty::OnRenderingChange();
618 nsIFrame* frame = mFrameReference.Get();
619 if (!frame) {
620 return;
623 MOZ_ASSERT(frame->IsSVGFrame(), "SVG frame expected");
625 // Don't need to request ReflowFrame if we're being reflowed.
626 // Because mRect for SVG frames includes the bounds of any markers
627 // (see the comment for nsIFrame::GetRect), the referencing frame must be
628 // reflowed for any marker changes.
629 if (!frame->HasAnyStateBits(NS_FRAME_IN_REFLOW)) {
630 // XXXjwatt: We need to unify SVG into standard reflow so we can just use
631 // nsChangeHint_NeedReflow | nsChangeHint_NeedDirtyReflow here.
632 // XXXSDL KILL THIS!!!
633 SVGUtils::ScheduleReflowSVG(frame);
635 frame->PresContext()->RestyleManager()->PostRestyleEvent(
636 frame->GetContent()->AsElement(), RestyleHint{0},
637 nsChangeHint_RepaintFrame);
640 class SVGPaintingProperty : public SVGRenderingObserverProperty {
641 public:
642 SVGPaintingProperty(URLAndReferrerInfo* aURI, nsIFrame* aFrame,
643 bool aReferenceImage)
644 : SVGRenderingObserverProperty(aURI, aFrame, aReferenceImage) {}
646 protected:
647 void OnRenderingChange() override;
650 void SVGPaintingProperty::OnRenderingChange() {
651 SVGRenderingObserverProperty::OnRenderingChange();
653 nsIFrame* frame = mFrameReference.Get();
654 if (!frame) {
655 return;
658 if (frame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) {
659 frame->InvalidateFrameSubtree();
660 } else {
661 for (nsIFrame* f = frame; f;
662 f = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(f)) {
663 f->InvalidateFrame();
668 // Observer for -moz-element(#element). Note that the observed element does not
669 // have to be an SVG element.
670 class SVGMozElementObserver final : public SVGPaintingProperty {
671 public:
672 SVGMozElementObserver(URLAndReferrerInfo* aURI, nsIFrame* aFrame)
673 : SVGPaintingProperty(aURI, aFrame, /* aReferenceImage = */ true) {}
675 // We only return true here because GetAndObserveBackgroundImage uses us
676 // to implement observing of arbitrary elements (including HTML elements)
677 // that may require us to repaint if the referenced element is reflowed.
678 // Bug 1496065 has been filed to remove that support though.
679 bool ObservesReflow() override { return true; }
683 * For content with `background-clip: text`.
685 * This observer is unusual in that the observing frame and observed frame are
686 * the same frame. This is because the observing frame is observing for reflow
687 * of its descendant text nodes, since such reflows may not result in the
688 * frame's nsDisplayBackground changing. In other words, Display List Based
689 * Invalidation may not invalidate the frame's background, so we need this
690 * observer to make sure that happens.
692 * XXX: It's questionable whether we should even be [ab]using the SVG observer
693 * mechanism for `background-clip:text`. Since we know that the observed frame
694 * is the frame we need to invalidate, we could just check the computed style
695 * in the (one) place where we pass INVALIDATE_REFLOW and invalidate there...
697 class BackgroundClipRenderingObserver : public SVGRenderingObserver {
698 public:
699 explicit BackgroundClipRenderingObserver(nsIFrame* aFrame) : mFrame(aFrame) {}
701 NS_DECL_ISUPPORTS
703 private:
704 // We do not call StopObserving() since the observing and observed element
705 // are the same element (and because we could crash - see bug 1556441).
706 virtual ~BackgroundClipRenderingObserver() = default;
708 Element* GetReferencedElementWithoutObserving() final {
709 return mFrame->GetContent()->AsElement();
712 void OnRenderingChange() final;
715 * Observing for mutations is not enough. A new font loading and applying
716 * to the text content could cause it to reflow, and we need to invalidate
717 * for that.
719 bool ObservesReflow() final { return true; }
721 // The observer and observee!
722 nsIFrame* mFrame;
725 NS_IMPL_ISUPPORTS(BackgroundClipRenderingObserver, nsIMutationObserver)
727 void BackgroundClipRenderingObserver::OnRenderingChange() {
728 for (nsIFrame* f = mFrame; f;
729 f = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(f)) {
730 f->InvalidateFrame();
734 static bool IsSVGFilterElement(const Element& aObserved) {
735 return aObserved.IsSVGElement(nsGkAtoms::filter);
739 * In a filter chain, there can be multiple SVG reference filters.
740 * e.g. filter: url(#svg-filter-1) blur(10px) url(#svg-filter-2);
742 * This class keeps track of one SVG reference filter in a filter chain.
743 * e.g. url(#svg-filter-1)
745 * It fires invalidations when the SVG filter element's id changes or when
746 * the SVG filter element's content changes.
748 * The SVGFilterObserverList class manages a list of SVGFilterObservers.
750 class SVGFilterObserver final : public SVGIDRenderingObserver {
751 public:
752 SVGFilterObserver(URLAndReferrerInfo* aURI, nsIContent* aObservingContent,
753 SVGFilterObserverList* aFilterChainObserver)
754 : SVGIDRenderingObserver(aURI, aObservingContent, false,
755 kAttributeChanged | kContentAppended |
756 kContentInserted | kContentRemoved,
757 IsSVGFilterElement),
758 mFilterObserverList(aFilterChainObserver) {}
760 void DetachFromChainObserver() { mFilterObserverList = nullptr; }
763 * @return the filter frame, or null if there is no filter frame
765 SVGFilterFrame* GetAndObserveFilterFrame();
767 // nsISupports
768 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
769 NS_DECL_CYCLE_COLLECTION_CLASS(SVGFilterObserver)
771 // SVGIDRenderingObserver
772 void OnRenderingChange() override;
774 protected:
775 virtual ~SVGFilterObserver() = default; // non-public
777 SVGFilterObserverList* mFilterObserverList;
780 NS_IMPL_CYCLE_COLLECTING_ADDREF(SVGFilterObserver)
781 NS_IMPL_CYCLE_COLLECTING_RELEASE(SVGFilterObserver)
783 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SVGFilterObserver)
784 NS_INTERFACE_MAP_ENTRY(nsISupports)
785 NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
786 NS_INTERFACE_MAP_END
788 NS_IMPL_CYCLE_COLLECTION_CLASS(SVGFilterObserver)
790 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(SVGFilterObserver)
791 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mObservedElementTracker)
792 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mObservingContent)
793 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
795 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(SVGFilterObserver)
796 tmp->StopObserving();
797 NS_IMPL_CYCLE_COLLECTION_UNLINK(mObservedElementTracker);
798 NS_IMPL_CYCLE_COLLECTION_UNLINK(mObservingContent)
799 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
801 SVGFilterFrame* SVGFilterObserver::GetAndObserveFilterFrame() {
802 return static_cast<SVGFilterFrame*>(
803 GetAndObserveReferencedFrame(LayoutFrameType::SVGFilter, nullptr));
807 * This class manages a list of SVGFilterObservers, which correspond to
808 * reference to SVG filters in a list of filters in a given 'filter' property.
809 * e.g. filter: url(#svg-filter-1) blur(10px) url(#svg-filter-2);
811 * In the above example, the SVGFilterObserverList will manage two
812 * SVGFilterObservers, one for each of the references to SVG filters. CSS
813 * filters like "blur(10px)" don't reference filter elements, so they don't
814 * need an SVGFilterObserver. The style system invalidates changes to CSS
815 * filters.
817 * FIXME(emilio): Why do we need this as opposed to the individual observers we
818 * create in the constructor?
820 class SVGFilterObserverList : public nsISupports {
821 public:
822 SVGFilterObserverList(Span<const StyleFilter> aFilters,
823 nsIContent* aFilteredElement,
824 nsIFrame* aFilteredFrame = nullptr);
826 const nsTArray<RefPtr<SVGFilterObserver>>& GetObservers() const {
827 return mObservers;
830 // nsISupports
831 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
832 NS_DECL_CYCLE_COLLECTION_CLASS(SVGFilterObserverList)
834 virtual void OnRenderingChange() = 0;
836 protected:
837 virtual ~SVGFilterObserverList();
839 void DetachObservers() {
840 for (auto& observer : mObservers) {
841 observer->DetachFromChainObserver();
845 nsTArray<RefPtr<SVGFilterObserver>> mObservers;
848 void SVGFilterObserver::OnRenderingChange() {
849 SVGIDRenderingObserver::OnRenderingChange();
851 if (mFilterObserverList) {
852 mFilterObserverList->OnRenderingChange();
855 if (!mTargetIsValid) {
856 return;
859 nsIFrame* frame = mObservingContent->GetPrimaryFrame();
860 if (!frame) {
861 return;
864 // Repaint asynchronously in case the filter frame is being torn down
865 nsChangeHint changeHint = nsChangeHint(nsChangeHint_RepaintFrame);
867 // Since we don't call SVGRenderingObserverProperty::
868 // OnRenderingChange, we have to add this bit ourselves.
869 if (frame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) {
870 // Changes should propagate out to things that might be observing
871 // the referencing frame or its ancestors.
872 changeHint |= nsChangeHint_InvalidateRenderingObservers;
875 // Don't need to request UpdateOverflow if we're being reflowed.
876 if (!frame->HasAnyStateBits(NS_FRAME_IN_REFLOW)) {
877 changeHint |= nsChangeHint_UpdateOverflow;
879 frame->PresContext()->RestyleManager()->PostRestyleEvent(
880 mObservingContent, RestyleHint{0}, changeHint);
883 NS_IMPL_CYCLE_COLLECTING_ADDREF(SVGFilterObserverList)
884 NS_IMPL_CYCLE_COLLECTING_RELEASE(SVGFilterObserverList)
886 NS_IMPL_CYCLE_COLLECTION_CLASS(SVGFilterObserverList)
888 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(SVGFilterObserverList)
889 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mObservers)
890 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
892 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(SVGFilterObserverList)
893 tmp->DetachObservers();
894 NS_IMPL_CYCLE_COLLECTION_UNLINK(mObservers);
895 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
897 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SVGFilterObserverList)
898 NS_INTERFACE_MAP_ENTRY(nsISupports)
899 NS_INTERFACE_MAP_END
901 SVGFilterObserverList::SVGFilterObserverList(Span<const StyleFilter> aFilters,
902 nsIContent* aFilteredElement,
903 nsIFrame* aFilteredFrame) {
904 for (const auto& filter : aFilters) {
905 if (!filter.IsUrl()) {
906 continue;
909 const auto& url = filter.AsUrl();
911 // aFilteredFrame can be null if this filter belongs to a
912 // CanvasRenderingContext2D.
913 RefPtr<URLAndReferrerInfo> filterURL;
914 if (aFilteredFrame) {
915 filterURL = ResolveURLUsingLocalRef(aFilteredFrame, url);
916 } else {
917 nsCOMPtr<nsIURI> resolvedURI = url.ResolveLocalRef(aFilteredElement);
918 if (resolvedURI) {
919 filterURL = new URLAndReferrerInfo(resolvedURI, url.ExtraData());
923 RefPtr<SVGFilterObserver> observer =
924 new SVGFilterObserver(filterURL, aFilteredElement, this);
925 mObservers.AppendElement(observer);
929 SVGFilterObserverList::~SVGFilterObserverList() { DetachObservers(); }
931 class SVGFilterObserverListForCSSProp final : public SVGFilterObserverList {
932 public:
933 SVGFilterObserverListForCSSProp(Span<const StyleFilter> aFilters,
934 nsIFrame* aFilteredFrame)
935 : SVGFilterObserverList(aFilters, aFilteredFrame->GetContent(),
936 aFilteredFrame) {}
938 protected:
939 void OnRenderingChange() override;
940 bool mInvalidating = false;
943 void SVGFilterObserverListForCSSProp::OnRenderingChange() {
944 if (mInvalidating) {
945 return;
947 AutoRestore<bool> guard(mInvalidating);
948 mInvalidating = true;
949 for (auto& observer : mObservers) {
950 observer->OnRenderingChange();
954 class SVGFilterObserverListForCanvasContext final
955 : public SVGFilterObserverList {
956 public:
957 SVGFilterObserverListForCanvasContext(CanvasRenderingContext2D* aContext,
958 Element* aCanvasElement,
959 Span<const StyleFilter> aFilters)
960 : SVGFilterObserverList(aFilters, aCanvasElement), mContext(aContext) {}
962 void OnRenderingChange() override;
963 void DetachFromContext() { mContext = nullptr; }
965 private:
966 CanvasRenderingContext2D* mContext;
969 void SVGFilterObserverListForCanvasContext::OnRenderingChange() {
970 if (!mContext) {
971 NS_WARNING(
972 "GFX: This should never be called without a context, except during "
973 "cycle collection (when DetachFromContext has been called)");
974 return;
976 // Refresh the cached FilterDescription in mContext->CurrentState().filter.
977 // If this filter is not at the top of the state stack, we'll refresh the
978 // wrong filter, but that's ok, because we'll refresh the right filter
979 // when we pop the state stack in CanvasRenderingContext2D::Restore().
981 // We don't need to flush, we're called by layout.
982 RefPtr<CanvasRenderingContext2D> kungFuDeathGrip(mContext);
983 kungFuDeathGrip->UpdateFilter(/* aFlushIfNeeded = */ false);
986 class SVGMaskObserverList final : public nsISupports {
987 public:
988 explicit SVGMaskObserverList(nsIFrame* aFrame);
990 // nsISupports
991 NS_DECL_ISUPPORTS
993 const nsTArray<RefPtr<SVGPaintingProperty>>& GetObservers() const {
994 return mProperties;
997 void ResolveImage(uint32_t aIndex);
999 private:
1000 virtual ~SVGMaskObserverList() = default; // non-public
1001 nsTArray<RefPtr<SVGPaintingProperty>> mProperties;
1002 nsIFrame* mFrame;
1005 NS_IMPL_ISUPPORTS(SVGMaskObserverList, nsISupports)
1007 SVGMaskObserverList::SVGMaskObserverList(nsIFrame* aFrame) : mFrame(aFrame) {
1008 const nsStyleSVGReset* svgReset = aFrame->StyleSVGReset();
1010 for (uint32_t i = 0; i < svgReset->mMask.mImageCount; i++) {
1011 const StyleComputedImageUrl* data =
1012 svgReset->mMask.mLayers[i].mImage.GetImageRequestURLValue();
1013 RefPtr<URLAndReferrerInfo> maskUri;
1014 if (data) {
1015 maskUri = ResolveURLUsingLocalRef(aFrame, *data);
1018 bool hasRef = false;
1019 if (maskUri) {
1020 maskUri->GetURI()->GetHasRef(&hasRef);
1023 // Accrording to maskUri, SVGPaintingProperty's ctor may trigger an
1024 // external SVG resource download, so we should pass maskUri in only if
1025 // maskUri has a chance pointing to an SVG mask resource.
1027 // And, an URL may refer to an SVG mask resource if it consists of
1028 // a fragment.
1029 SVGPaintingProperty* prop = new SVGPaintingProperty(
1030 hasRef ? maskUri.get() : nullptr, aFrame, false);
1031 mProperties.AppendElement(prop);
1035 void SVGMaskObserverList::ResolveImage(uint32_t aIndex) {
1036 const nsStyleSVGReset* svgReset = mFrame->StyleSVGReset();
1037 MOZ_ASSERT(aIndex < svgReset->mMask.mImageCount);
1039 const auto& image = svgReset->mMask.mLayers[aIndex].mImage;
1040 if (image.IsResolved()) {
1041 return;
1043 MOZ_ASSERT(image.IsImageRequestType());
1044 Document* doc = mFrame->PresContext()->Document();
1045 const_cast<StyleImage&>(image).ResolveImage(*doc, nullptr);
1046 if (imgRequestProxy* req = image.GetImageRequest()) {
1047 // FIXME(emilio): What disassociates this request?
1048 doc->StyleImageLoader()->AssociateRequestToFrame(req, mFrame);
1053 * Used for gradient-to-gradient, pattern-to-pattern and filter-to-filter
1054 * references to "template" elements (specified via the 'href' attributes).
1056 class SVGTemplateElementObserver : public SVGIDRenderingObserver {
1057 public:
1058 NS_DECL_ISUPPORTS
1060 SVGTemplateElementObserver(URLAndReferrerInfo* aURI, nsIFrame* aFrame,
1061 bool aReferenceImage)
1062 : SVGIDRenderingObserver(aURI, aFrame->GetContent(), aReferenceImage,
1063 kAttributeChanged | kContentAppended |
1064 kContentInserted | kContentRemoved),
1065 mFrameReference(aFrame) {}
1067 protected:
1068 virtual ~SVGTemplateElementObserver() = default; // non-public
1070 void OnRenderingChange() override;
1072 SVGFrameReferenceFromProperty mFrameReference;
1075 NS_IMPL_ISUPPORTS(SVGTemplateElementObserver, nsIMutationObserver)
1077 void SVGTemplateElementObserver::OnRenderingChange() {
1078 SVGIDRenderingObserver::OnRenderingChange();
1080 if (nsIFrame* frame = mFrameReference.Get()) {
1081 SVGObserverUtils::InvalidateRenderingObservers(frame);
1086 * An instance of this class is stored on an observed frame (as a frame
1087 * property) whenever the frame has active rendering observers. It is used to
1088 * store pointers to the SVGRenderingObserver instances belonging to any
1089 * observing frames, allowing invalidations from the observed frame to be sent
1090 * to all observing frames.
1092 * SVGRenderingObserver instances that are added are not strongly referenced,
1093 * so they must remove themselves before they die.
1095 * This class is "single-shot", which is to say that when something about the
1096 * observed element changes, InvalidateAll() clears our hashtable of
1097 * SVGRenderingObservers. SVGRenderingObserver objects will be added back
1098 * again if/when the observing frame looks up our observed frame to use it.
1100 * XXXjwatt: is this the best thing to do nowadays? Back when that mechanism
1101 * landed in bug 330498 we had two pass, recursive invalidation up the frame
1102 * tree, and I think reference loops were a problem. Nowadays maybe a flag
1103 * on the SVGRenderingObserver objects to coalesce invalidations may work
1104 * better?
1106 * InvalidateAll must be called before this object is destroyed, i.e.
1107 * before the referenced frame is destroyed. This should normally happen
1108 * via SVGContainerFrame::RemoveFrame, since only frames in the frame
1109 * tree should be referenced.
1111 class SVGRenderingObserverSet {
1112 public:
1113 SVGRenderingObserverSet() : mObservers(4) {
1114 MOZ_COUNT_CTOR(SVGRenderingObserverSet);
1117 ~SVGRenderingObserverSet() { MOZ_COUNT_DTOR(SVGRenderingObserverSet); }
1119 void Add(SVGRenderingObserver* aObserver) { mObservers.Insert(aObserver); }
1120 void Remove(SVGRenderingObserver* aObserver) { mObservers.Remove(aObserver); }
1121 #ifdef DEBUG
1122 bool Contains(SVGRenderingObserver* aObserver) {
1123 return mObservers.Contains(aObserver);
1125 #endif
1126 bool IsEmpty() { return mObservers.IsEmpty(); }
1129 * Drop all our observers, and notify them that we have changed and dropped
1130 * our reference to them.
1132 void InvalidateAll();
1135 * Drop all observers that observe reflow, and notify them that we have
1136 * changed and dropped our reference to them.
1138 void InvalidateAllForReflow();
1141 * Drop all our observers, and notify them that we have dropped our reference
1142 * to them.
1144 void RemoveAll();
1146 private:
1147 nsTHashSet<SVGRenderingObserver*> mObservers;
1150 void SVGRenderingObserverSet::InvalidateAll() {
1151 if (mObservers.IsEmpty()) {
1152 return;
1155 const auto observers = std::move(mObservers);
1157 // We've moved all the observers from mObservers, effectively
1158 // evicting them so we need to notify all observers of eviction
1159 // before we process any rendering changes. In short, don't
1160 // try to merge these loops.
1161 for (const auto& observer : observers) {
1162 observer->NotifyEvictedFromRenderingObserverSet();
1164 for (const auto& observer : observers) {
1165 observer->OnNonDOMMutationRenderingChange();
1169 void SVGRenderingObserverSet::InvalidateAllForReflow() {
1170 if (mObservers.IsEmpty()) {
1171 return;
1174 AutoTArray<SVGRenderingObserver*, 10> observers;
1176 for (auto it = mObservers.cbegin(), end = mObservers.cend(); it != end;
1177 ++it) {
1178 SVGRenderingObserver* obs = *it;
1179 if (obs->ObservesReflow()) {
1180 observers.AppendElement(obs);
1181 mObservers.Remove(it);
1182 obs->NotifyEvictedFromRenderingObserverSet();
1186 for (const auto& observer : observers) {
1187 observer->OnNonDOMMutationRenderingChange();
1191 void SVGRenderingObserverSet::RemoveAll() {
1192 const auto observers = std::move(mObservers);
1194 // Our list is now cleared. We need to notify the observers we've removed,
1195 // so they can update their state & remove themselves as mutation-observers.
1196 for (const auto& observer : observers) {
1197 observer->NotifyEvictedFromRenderingObserverSet();
1201 static SVGRenderingObserverSet* GetObserverSet(Element* aElement) {
1202 return static_cast<SVGRenderingObserverSet*>(
1203 aElement->GetProperty(nsGkAtoms::renderingobserverset));
1206 #ifdef DEBUG
1207 // Defined down here because we need SVGRenderingObserverSet's definition.
1208 void SVGRenderingObserver::DebugObserverSet() {
1209 Element* referencedElement = GetReferencedElementWithoutObserving();
1210 if (referencedElement) {
1211 SVGRenderingObserverSet* observers = GetObserverSet(referencedElement);
1212 bool inObserverSet = observers && observers->Contains(this);
1213 MOZ_ASSERT(inObserverSet == mInObserverSet,
1214 "failed to track whether we're in our referenced element's "
1215 "observer set!");
1216 } else {
1217 MOZ_ASSERT(!mInObserverSet, "In whose observer set are we, then?");
1220 #endif
1222 using URIObserverHashtable =
1223 nsInterfaceHashtable<URLAndReferrerInfoHashKey, nsIMutationObserver>;
1225 using PaintingPropertyDescriptor =
1226 const FramePropertyDescriptor<SVGPaintingProperty>*;
1228 static void DestroyFilterProperty(SVGFilterObserverListForCSSProp* aProp) {
1229 aProp->Release();
1232 NS_DECLARE_FRAME_PROPERTY_RELEASABLE(HrefToTemplateProperty,
1233 SVGTemplateElementObserver)
1234 NS_DECLARE_FRAME_PROPERTY_WITH_DTOR(BackdropFilterProperty,
1235 SVGFilterObserverListForCSSProp,
1236 DestroyFilterProperty)
1237 NS_DECLARE_FRAME_PROPERTY_WITH_DTOR(FilterProperty,
1238 SVGFilterObserverListForCSSProp,
1239 DestroyFilterProperty)
1240 NS_DECLARE_FRAME_PROPERTY_RELEASABLE(MaskProperty, SVGMaskObserverList)
1241 NS_DECLARE_FRAME_PROPERTY_RELEASABLE(ClipPathProperty, SVGPaintingProperty)
1242 NS_DECLARE_FRAME_PROPERTY_RELEASABLE(MarkerStartProperty, SVGMarkerObserver)
1243 NS_DECLARE_FRAME_PROPERTY_RELEASABLE(MarkerMidProperty, SVGMarkerObserver)
1244 NS_DECLARE_FRAME_PROPERTY_RELEASABLE(MarkerEndProperty, SVGMarkerObserver)
1245 NS_DECLARE_FRAME_PROPERTY_RELEASABLE(FillProperty, SVGPaintingProperty)
1246 NS_DECLARE_FRAME_PROPERTY_RELEASABLE(StrokeProperty, SVGPaintingProperty)
1247 NS_DECLARE_FRAME_PROPERTY_RELEASABLE(HrefAsTextPathProperty,
1248 SVGTextPathObserver)
1249 NS_DECLARE_FRAME_PROPERTY_DELETABLE(BackgroundImageProperty,
1250 URIObserverHashtable)
1251 NS_DECLARE_FRAME_PROPERTY_RELEASABLE(BackgroundClipObserverProperty,
1252 BackgroundClipRenderingObserver)
1253 NS_DECLARE_FRAME_PROPERTY_RELEASABLE(OffsetPathProperty,
1254 SVGRenderingObserverProperty)
1256 template <class T>
1257 static T* GetEffectProperty(URLAndReferrerInfo* aURI, nsIFrame* aFrame,
1258 const FramePropertyDescriptor<T>* aProperty) {
1259 if (!aURI) {
1260 return nullptr;
1263 bool found;
1264 T* prop = aFrame->GetProperty(aProperty, &found);
1265 if (found) {
1266 MOZ_ASSERT(prop, "this property should only store non-null values");
1267 return prop;
1269 prop = new T(aURI, aFrame, false);
1270 NS_ADDREF(prop);
1271 aFrame->AddProperty(aProperty, prop);
1272 return prop;
1275 static SVGPaintingProperty* GetPaintingProperty(
1276 URLAndReferrerInfo* aURI, nsIFrame* aFrame,
1277 const FramePropertyDescriptor<SVGPaintingProperty>* aProperty) {
1278 return GetEffectProperty(aURI, aFrame, aProperty);
1281 static already_AddRefed<URLAndReferrerInfo> GetMarkerURI(
1282 nsIFrame* aFrame, const StyleUrlOrNone nsStyleSVG::*aMarker) {
1283 const StyleUrlOrNone& url = aFrame->StyleSVG()->*aMarker;
1284 if (url.IsNone()) {
1285 return nullptr;
1287 return ResolveURLUsingLocalRef(aFrame, url.AsUrl());
1290 bool SVGObserverUtils::GetAndObserveMarkers(nsIFrame* aMarkedFrame,
1291 SVGMarkerFrame* (*aFrames)[3]) {
1292 MOZ_ASSERT(!aMarkedFrame->GetPrevContinuation() &&
1293 aMarkedFrame->IsSVGGeometryFrame() &&
1294 static_cast<SVGGeometryElement*>(aMarkedFrame->GetContent())
1295 ->IsMarkable(),
1296 "Bad frame");
1298 bool foundMarker = false;
1299 RefPtr<URLAndReferrerInfo> markerURL;
1300 SVGMarkerObserver* observer;
1301 nsIFrame* marker;
1303 #define GET_MARKER(type) \
1304 markerURL = GetMarkerURI(aMarkedFrame, &nsStyleSVG::mMarker##type); \
1305 observer = \
1306 GetEffectProperty(markerURL, aMarkedFrame, Marker##type##Property()); \
1307 marker = observer ? observer->GetAndObserveReferencedFrame( \
1308 LayoutFrameType::SVGMarker, nullptr) \
1309 : nullptr; \
1310 foundMarker = foundMarker || bool(marker); \
1311 (*aFrames)[SVGMark::e##type] = static_cast<SVGMarkerFrame*>(marker);
1313 GET_MARKER(Start)
1314 GET_MARKER(Mid)
1315 GET_MARKER(End)
1317 #undef GET_MARKER
1319 return foundMarker;
1322 // Note that the returned list will be empty in the case of a 'filter' property
1323 // that only specifies CSS filter functions (no url()'s to SVG filters).
1324 template <typename P>
1325 static SVGFilterObserverListForCSSProp* GetOrCreateFilterObserverListForCSS(
1326 nsIFrame* aFrame, bool aHasFilters,
1327 FrameProperties::Descriptor<P> aProperty,
1328 Span<const StyleFilter> aFilters) {
1329 if (!aHasFilters) {
1330 return nullptr;
1333 bool found;
1334 SVGFilterObserverListForCSSProp* observers =
1335 aFrame->GetProperty(aProperty, &found);
1336 if (found) {
1337 MOZ_ASSERT(observers, "this property should only store non-null values");
1338 return observers;
1340 observers = new SVGFilterObserverListForCSSProp(aFilters, aFrame);
1341 NS_ADDREF(observers);
1342 aFrame->AddProperty(aProperty, observers);
1343 return observers;
1346 static SVGFilterObserverListForCSSProp* GetOrCreateFilterObserverListForCSS(
1347 nsIFrame* aFrame, StyleFilterType aStyleFilterType) {
1348 MOZ_ASSERT(!aFrame->GetPrevContinuation(), "Require first continuation");
1350 const nsStyleEffects* effects = aFrame->StyleEffects();
1352 return aStyleFilterType == StyleFilterType::BackdropFilter
1353 ? GetOrCreateFilterObserverListForCSS(
1354 aFrame, effects->HasBackdropFilters(),
1355 BackdropFilterProperty(), effects->mBackdropFilters.AsSpan())
1356 : GetOrCreateFilterObserverListForCSS(
1357 aFrame, effects->HasFilters(), FilterProperty(),
1358 effects->mFilters.AsSpan());
1361 static SVGObserverUtils::ReferenceState GetAndObserveFilters(
1362 SVGFilterObserverList* aObserverList,
1363 nsTArray<SVGFilterFrame*>* aFilterFrames) {
1364 if (!aObserverList) {
1365 return SVGObserverUtils::eHasNoRefs;
1368 const nsTArray<RefPtr<SVGFilterObserver>>& observers =
1369 aObserverList->GetObservers();
1370 if (observers.IsEmpty()) {
1371 return SVGObserverUtils::eHasNoRefs;
1374 for (const auto& observer : observers) {
1375 SVGFilterFrame* filter = observer->GetAndObserveFilterFrame();
1376 if (!filter) {
1377 if (aFilterFrames) {
1378 aFilterFrames->Clear();
1380 return SVGObserverUtils::eHasRefsSomeInvalid;
1382 if (aFilterFrames) {
1383 aFilterFrames->AppendElement(filter);
1387 return SVGObserverUtils::eHasRefsAllValid;
1390 SVGObserverUtils::ReferenceState SVGObserverUtils::GetAndObserveFilters(
1391 nsIFrame* aFilteredFrame, nsTArray<SVGFilterFrame*>* aFilterFrames,
1392 StyleFilterType aStyleFilterType) {
1393 SVGFilterObserverListForCSSProp* observerList =
1394 GetOrCreateFilterObserverListForCSS(aFilteredFrame, aStyleFilterType);
1395 return mozilla::GetAndObserveFilters(observerList, aFilterFrames);
1398 SVGObserverUtils::ReferenceState SVGObserverUtils::GetAndObserveFilters(
1399 nsISupports* aObserverList, nsTArray<SVGFilterFrame*>* aFilterFrames) {
1400 return mozilla::GetAndObserveFilters(
1401 static_cast<SVGFilterObserverListForCanvasContext*>(aObserverList),
1402 aFilterFrames);
1405 SVGObserverUtils::ReferenceState SVGObserverUtils::GetFiltersIfObserving(
1406 nsIFrame* aFilteredFrame, nsTArray<SVGFilterFrame*>* aFilterFrames) {
1407 SVGFilterObserverListForCSSProp* observerList =
1408 aFilteredFrame->GetProperty(FilterProperty());
1409 return mozilla::GetAndObserveFilters(observerList, aFilterFrames);
1412 already_AddRefed<nsISupports> SVGObserverUtils::ObserveFiltersForCanvasContext(
1413 CanvasRenderingContext2D* aContext, Element* aCanvasElement,
1414 const Span<const StyleFilter> aFilters) {
1415 return do_AddRef(new SVGFilterObserverListForCanvasContext(
1416 aContext, aCanvasElement, aFilters));
1419 void SVGObserverUtils::DetachFromCanvasContext(nsISupports* aAutoObserver) {
1420 static_cast<SVGFilterObserverListForCanvasContext*>(aAutoObserver)
1421 ->DetachFromContext();
1424 static SVGPaintingProperty* GetOrCreateClipPathObserver(
1425 nsIFrame* aClippedFrame) {
1426 MOZ_ASSERT(!aClippedFrame->GetPrevContinuation(),
1427 "Require first continuation");
1429 const nsStyleSVGReset* svgStyleReset = aClippedFrame->StyleSVGReset();
1430 if (!svgStyleReset->mClipPath.IsUrl()) {
1431 return nullptr;
1433 const auto& url = svgStyleReset->mClipPath.AsUrl();
1434 RefPtr<URLAndReferrerInfo> pathURI =
1435 ResolveURLUsingLocalRef(aClippedFrame, url);
1436 return GetPaintingProperty(pathURI, aClippedFrame, ClipPathProperty());
1439 SVGObserverUtils::ReferenceState SVGObserverUtils::GetAndObserveClipPath(
1440 nsIFrame* aClippedFrame, SVGClipPathFrame** aClipPathFrame) {
1441 if (aClipPathFrame) {
1442 *aClipPathFrame = nullptr;
1444 SVGPaintingProperty* observers = GetOrCreateClipPathObserver(aClippedFrame);
1445 if (!observers) {
1446 return eHasNoRefs;
1448 bool frameTypeOK = true;
1449 SVGClipPathFrame* frame =
1450 static_cast<SVGClipPathFrame*>(observers->GetAndObserveReferencedFrame(
1451 LayoutFrameType::SVGClipPath, &frameTypeOK));
1452 // Note that, unlike for filters, a reference to an ID that doesn't exist
1453 // is not invalid for clip-path or mask.
1454 if (!frameTypeOK) {
1455 return eHasRefsSomeInvalid;
1457 if (aClipPathFrame) {
1458 *aClipPathFrame = frame;
1460 return frame ? eHasRefsAllValid : eHasNoRefs;
1463 static SVGRenderingObserverProperty* GetOrCreateGeometryObserver(
1464 nsIFrame* aFrame) {
1465 // Now only offset-path property uses this. See MotionPathUtils.cpp.
1466 const nsStyleDisplay* disp = aFrame->StyleDisplay();
1467 if (!disp->mOffsetPath.IsUrl()) {
1468 return nullptr;
1470 const auto& url = disp->mOffsetPath.AsUrl();
1471 RefPtr<URLAndReferrerInfo> pathURI = ResolveURLUsingLocalRef(aFrame, url);
1472 return GetEffectProperty(pathURI, aFrame, OffsetPathProperty());
1475 SVGGeometryElement* SVGObserverUtils::GetAndObserveGeometry(nsIFrame* aFrame) {
1476 SVGRenderingObserverProperty* observers = GetOrCreateGeometryObserver(aFrame);
1477 if (!observers) {
1478 return nullptr;
1481 bool frameTypeOK = true;
1482 SVGGeometryFrame* frame =
1483 do_QueryFrame(observers->GetAndObserveReferencedFrame(
1484 LayoutFrameType::SVGGeometry, &frameTypeOK));
1485 if (!frameTypeOK || !frame) {
1486 return nullptr;
1489 return static_cast<dom::SVGGeometryElement*>(frame->GetContent());
1492 static SVGMaskObserverList* GetOrCreateMaskObserverList(
1493 nsIFrame* aMaskedFrame) {
1494 MOZ_ASSERT(!aMaskedFrame->GetPrevContinuation(),
1495 "Require first continuation");
1497 const nsStyleSVGReset* style = aMaskedFrame->StyleSVGReset();
1498 if (!style->HasMask()) {
1499 return nullptr;
1502 MOZ_ASSERT(style->mMask.mImageCount > 0);
1504 bool found;
1505 SVGMaskObserverList* prop = aMaskedFrame->GetProperty(MaskProperty(), &found);
1506 if (found) {
1507 MOZ_ASSERT(prop, "this property should only store non-null values");
1508 return prop;
1510 prop = new SVGMaskObserverList(aMaskedFrame);
1511 NS_ADDREF(prop);
1512 aMaskedFrame->AddProperty(MaskProperty(), prop);
1513 return prop;
1516 SVGObserverUtils::ReferenceState SVGObserverUtils::GetAndObserveMasks(
1517 nsIFrame* aMaskedFrame, nsTArray<SVGMaskFrame*>* aMaskFrames) {
1518 SVGMaskObserverList* observerList = GetOrCreateMaskObserverList(aMaskedFrame);
1519 if (!observerList) {
1520 return eHasNoRefs;
1523 const nsTArray<RefPtr<SVGPaintingProperty>>& observers =
1524 observerList->GetObservers();
1525 if (observers.IsEmpty()) {
1526 return eHasNoRefs;
1529 ReferenceState state = eHasRefsAllValid;
1531 for (size_t i = 0; i < observers.Length(); i++) {
1532 bool frameTypeOK = true;
1533 SVGMaskFrame* maskFrame =
1534 static_cast<SVGMaskFrame*>(observers[i]->GetAndObserveReferencedFrame(
1535 LayoutFrameType::SVGMask, &frameTypeOK));
1536 MOZ_ASSERT(!maskFrame || frameTypeOK);
1537 // XXXjwatt: this looks fishy
1538 if (!frameTypeOK) {
1539 // We can not find the specific SVG mask resource in the downloaded SVG
1540 // document. There are two possibilities:
1541 // 1. The given resource id is invalid.
1542 // 2. The given resource id refers to a viewbox.
1544 // Hand it over to the style image.
1545 observerList->ResolveImage(i);
1546 state = eHasRefsSomeInvalid;
1548 if (aMaskFrames) {
1549 aMaskFrames->AppendElement(maskFrame);
1553 return state;
1556 SVGGeometryElement* SVGObserverUtils::GetAndObserveTextPathsPath(
1557 nsIFrame* aTextPathFrame) {
1558 // Continuations can come and go during reflow, and we don't need to observe
1559 // the referenced element more than once for a given node.
1560 aTextPathFrame = aTextPathFrame->FirstContinuation();
1562 SVGTextPathObserver* property =
1563 aTextPathFrame->GetProperty(HrefAsTextPathProperty());
1565 if (!property) {
1566 nsIContent* content = aTextPathFrame->GetContent();
1567 nsAutoString href;
1568 static_cast<SVGTextPathElement*>(content)->HrefAsString(href);
1569 if (href.IsEmpty()) {
1570 return nullptr; // no URL
1573 RefPtr<URLAndReferrerInfo> target = ResolveURLUsingLocalRef(content, href);
1575 property =
1576 GetEffectProperty(target, aTextPathFrame, HrefAsTextPathProperty());
1577 if (!property) {
1578 return nullptr;
1582 return SVGGeometryElement::FromNodeOrNull(
1583 property->GetAndObserveReferencedElement());
1586 SVGGeometryElement* SVGObserverUtils::GetAndObserveMPathsPath(
1587 SVGMPathElement* aSVGMPathElement) {
1588 if (!aSVGMPathElement->mMPathObserver) {
1589 nsAutoString href;
1590 aSVGMPathElement->HrefAsString(href);
1591 if (href.IsEmpty()) {
1592 return nullptr; // no URL
1595 RefPtr<URLAndReferrerInfo> target =
1596 ResolveURLUsingLocalRef(aSVGMPathElement, href);
1598 aSVGMPathElement->mMPathObserver =
1599 new SVGMPathObserver(target, aSVGMPathElement);
1602 return SVGGeometryElement::FromNodeOrNull(
1603 static_cast<SVGMPathObserver*>(aSVGMPathElement->mMPathObserver.get())
1604 ->GetAndObserveReferencedElement());
1607 void SVGObserverUtils::TraverseMPathObserver(
1608 SVGMPathElement* aSVGMPathElement,
1609 nsCycleCollectionTraversalCallback* aCB) {
1610 if (aSVGMPathElement->mMPathObserver) {
1611 static_cast<SVGMPathObserver*>(aSVGMPathElement->mMPathObserver.get())
1612 ->Traverse(aCB);
1616 void SVGObserverUtils::InitiateResourceDocLoads(nsIFrame* aFrame) {
1617 // We create observer objects and attach them to aFrame, but we do not
1618 // make aFrame start observing the referenced frames.
1619 Unused << GetOrCreateFilterObserverListForCSS(
1620 aFrame, StyleFilterType::BackdropFilter);
1621 Unused << GetOrCreateFilterObserverListForCSS(aFrame,
1622 StyleFilterType::Filter);
1623 Unused << GetOrCreateClipPathObserver(aFrame);
1624 Unused << GetOrCreateGeometryObserver(aFrame);
1625 Unused << GetOrCreateMaskObserverList(aFrame);
1628 void SVGObserverUtils::RemoveTextPathObserver(nsIFrame* aTextPathFrame) {
1629 aTextPathFrame->RemoveProperty(HrefAsTextPathProperty());
1632 nsIFrame* SVGObserverUtils::GetAndObserveTemplate(
1633 nsIFrame* aFrame, HrefToTemplateCallback aGetHref) {
1634 SVGTemplateElementObserver* observer =
1635 aFrame->GetProperty(HrefToTemplateProperty());
1637 if (!observer) {
1638 nsAutoString href;
1639 aGetHref(href);
1640 if (href.IsEmpty()) {
1641 return nullptr; // no URL
1644 RefPtr<URLAndReferrerInfo> info =
1645 ResolveURLUsingLocalRef(aFrame->GetContent(), href);
1647 observer = GetEffectProperty(info, aFrame, HrefToTemplateProperty());
1650 return observer ? observer->GetAndObserveReferencedFrame() : nullptr;
1653 void SVGObserverUtils::RemoveTemplateObserver(nsIFrame* aFrame) {
1654 aFrame->RemoveProperty(HrefToTemplateProperty());
1657 Element* SVGObserverUtils::GetAndObserveBackgroundImage(nsIFrame* aFrame,
1658 const nsAtom* aHref) {
1659 bool found;
1660 URIObserverHashtable* hashtable =
1661 aFrame->GetProperty(BackgroundImageProperty(), &found);
1662 if (!found) {
1663 hashtable = new URIObserverHashtable();
1664 aFrame->AddProperty(BackgroundImageProperty(), hashtable);
1665 } else {
1666 MOZ_ASSERT(hashtable, "this property should only store non-null values");
1669 nsAutoString elementId = u"#"_ns + nsDependentAtomString(aHref);
1670 nsCOMPtr<nsIURI> targetURI;
1671 nsContentUtils::NewURIWithDocumentCharset(
1672 getter_AddRefs(targetURI), elementId,
1673 aFrame->GetContent()->GetUncomposedDoc(),
1674 aFrame->GetContent()->GetBaseURI());
1675 nsIReferrerInfo* referrerInfo =
1676 aFrame->GetContent()
1677 ->OwnerDoc()
1678 ->ReferrerInfoForInternalCSSAndSVGResources();
1679 RefPtr<URLAndReferrerInfo> url =
1680 new URLAndReferrerInfo(targetURI, referrerInfo);
1682 return static_cast<SVGMozElementObserver*>(
1683 hashtable
1684 ->LookupOrInsertWith(
1685 url,
1686 [&] {
1687 return MakeRefPtr<SVGMozElementObserver>(url, aFrame);
1689 .get())
1690 ->GetAndObserveReferencedElement();
1693 Element* SVGObserverUtils::GetAndObserveBackgroundClip(nsIFrame* aFrame) {
1694 bool found;
1695 BackgroundClipRenderingObserver* obs =
1696 aFrame->GetProperty(BackgroundClipObserverProperty(), &found);
1697 if (!found) {
1698 obs = new BackgroundClipRenderingObserver(aFrame);
1699 NS_ADDREF(obs);
1700 aFrame->AddProperty(BackgroundClipObserverProperty(), obs);
1703 return obs->GetAndObserveReferencedElement();
1706 SVGPaintServerFrame* SVGObserverUtils::GetAndObservePaintServer(
1707 nsIFrame* aPaintedFrame, StyleSVGPaint nsStyleSVG::*aPaint) {
1708 // If we're looking at a frame within SVG text, then we need to look up
1709 // to find the right frame to get the painting property off. We should at
1710 // least look up past a text frame, and if the text frame's parent is the
1711 // anonymous block frame, then we look up to its parent (the SVGTextFrame).
1712 nsIFrame* paintedFrame = aPaintedFrame;
1713 if (paintedFrame->IsInSVGTextSubtree()) {
1714 paintedFrame = paintedFrame->GetParent();
1715 nsIFrame* grandparent = paintedFrame->GetParent();
1716 if (grandparent && grandparent->IsSVGTextFrame()) {
1717 paintedFrame = grandparent;
1721 const nsStyleSVG* svgStyle = paintedFrame->StyleSVG();
1722 if (!(svgStyle->*aPaint).kind.IsPaintServer()) {
1723 return nullptr;
1726 RefPtr<URLAndReferrerInfo> paintServerURL = ResolveURLUsingLocalRef(
1727 paintedFrame, (svgStyle->*aPaint).kind.AsPaintServer());
1729 MOZ_ASSERT(aPaint == &nsStyleSVG::mFill || aPaint == &nsStyleSVG::mStroke);
1730 PaintingPropertyDescriptor propDesc =
1731 (aPaint == &nsStyleSVG::mFill) ? FillProperty() : StrokeProperty();
1732 if (auto* property =
1733 GetPaintingProperty(paintServerURL, paintedFrame, propDesc)) {
1734 return do_QueryFrame(property->GetAndObserveReferencedFrame());
1736 return nullptr;
1739 void SVGObserverUtils::UpdateEffects(nsIFrame* aFrame) {
1740 NS_ASSERTION(aFrame->GetContent()->IsElement(),
1741 "aFrame's content should be an element");
1743 aFrame->RemoveProperty(BackdropFilterProperty());
1744 aFrame->RemoveProperty(FilterProperty());
1745 aFrame->RemoveProperty(MaskProperty());
1746 aFrame->RemoveProperty(ClipPathProperty());
1747 aFrame->RemoveProperty(MarkerStartProperty());
1748 aFrame->RemoveProperty(MarkerMidProperty());
1749 aFrame->RemoveProperty(MarkerEndProperty());
1750 aFrame->RemoveProperty(FillProperty());
1751 aFrame->RemoveProperty(StrokeProperty());
1752 aFrame->RemoveProperty(BackgroundImageProperty());
1754 // Ensure that the filter is repainted correctly
1755 // We can't do that in OnRenderingChange as the referenced frame may
1756 // not be valid
1757 GetOrCreateFilterObserverListForCSS(aFrame, StyleFilterType::BackdropFilter);
1758 GetOrCreateFilterObserverListForCSS(aFrame, StyleFilterType::Filter);
1760 if (aFrame->IsSVGGeometryFrame() &&
1761 static_cast<SVGGeometryElement*>(aFrame->GetContent())->IsMarkable()) {
1762 // Set marker properties here to avoid reference loops
1763 RefPtr<URLAndReferrerInfo> markerURL =
1764 GetMarkerURI(aFrame, &nsStyleSVG::mMarkerStart);
1765 GetEffectProperty(markerURL, aFrame, MarkerStartProperty());
1766 markerURL = GetMarkerURI(aFrame, &nsStyleSVG::mMarkerMid);
1767 GetEffectProperty(markerURL, aFrame, MarkerMidProperty());
1768 markerURL = GetMarkerURI(aFrame, &nsStyleSVG::mMarkerEnd);
1769 GetEffectProperty(markerURL, aFrame, MarkerEndProperty());
1773 void SVGObserverUtils::AddRenderingObserver(Element* aElement,
1774 SVGRenderingObserver* aObserver) {
1775 SVGRenderingObserverSet* observers = GetObserverSet(aElement);
1776 if (!observers) {
1777 observers = new SVGRenderingObserverSet();
1778 // When we call cloneAndAdopt we keep the property. If the referenced
1779 // element doesn't exist in the new document then the observer set and
1780 // observers will be removed by ElementTracker::ElementChanged when we
1781 // get the ChangeNotification.
1782 aElement->SetProperty(nsGkAtoms::renderingobserverset, observers,
1783 nsINode::DeleteProperty<SVGRenderingObserverSet>,
1784 /* aTransfer = */ true);
1786 aElement->SetHasRenderingObservers(true);
1787 observers->Add(aObserver);
1790 void SVGObserverUtils::RemoveRenderingObserver(
1791 Element* aElement, SVGRenderingObserver* aObserver) {
1792 SVGRenderingObserverSet* observers = GetObserverSet(aElement);
1793 if (observers) {
1794 NS_ASSERTION(observers->Contains(aObserver),
1795 "removing observer from an element we're not observing?");
1796 observers->Remove(aObserver);
1797 if (observers->IsEmpty()) {
1798 aElement->RemoveProperty(nsGkAtoms::renderingobserverset);
1799 aElement->SetHasRenderingObservers(false);
1804 void SVGObserverUtils::RemoveAllRenderingObservers(Element* aElement) {
1805 SVGRenderingObserverSet* observers = GetObserverSet(aElement);
1806 if (observers) {
1807 observers->RemoveAll();
1808 aElement->RemoveProperty(nsGkAtoms::renderingobserverset);
1809 aElement->SetHasRenderingObservers(false);
1813 void SVGObserverUtils::InvalidateRenderingObservers(nsIFrame* aFrame) {
1814 NS_ASSERTION(!aFrame->GetPrevContinuation(),
1815 "aFrame must be first continuation");
1817 auto* element = Element::FromNodeOrNull(aFrame->GetContent());
1818 if (!element) {
1819 return;
1822 // If the rendering has changed, the bounds may well have changed too:
1823 aFrame->RemoveProperty(SVGUtils::ObjectBoundingBoxProperty());
1825 if (auto* observers = GetObserverSet(element)) {
1826 observers->InvalidateAll();
1827 return;
1830 if (aFrame->IsRenderingObserverContainer()) {
1831 return;
1834 // Check ancestor SVG containers. The root frame cannot be of type
1835 // eSVGContainer so we don't have to check f for null here.
1836 for (nsIFrame* f = aFrame->GetParent(); f->IsSVGContainerFrame();
1837 f = f->GetParent()) {
1838 if (auto* element = Element::FromNode(f->GetContent())) {
1839 if (auto* observers = GetObserverSet(element)) {
1840 observers->InvalidateAll();
1841 return;
1844 if (f->IsRenderingObserverContainer()) {
1845 return;
1850 void SVGObserverUtils::InvalidateDirectRenderingObservers(
1851 Element* aElement, uint32_t aFlags /* = 0 */) {
1852 if (nsIFrame* frame = aElement->GetPrimaryFrame()) {
1853 // If the rendering has changed, the bounds may well have changed too:
1854 frame->RemoveProperty(SVGUtils::ObjectBoundingBoxProperty());
1857 if (aElement->HasRenderingObservers()) {
1858 SVGRenderingObserverSet* observers = GetObserverSet(aElement);
1859 if (observers) {
1860 if (aFlags & INVALIDATE_REFLOW) {
1861 observers->InvalidateAllForReflow();
1862 } else {
1863 observers->InvalidateAll();
1869 void SVGObserverUtils::InvalidateDirectRenderingObservers(
1870 nsIFrame* aFrame, uint32_t aFlags /* = 0 */) {
1871 if (auto* element = Element::FromNodeOrNull(aFrame->GetContent())) {
1872 InvalidateDirectRenderingObservers(element, aFlags);
1876 } // namespace mozilla