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/. */
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
;
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
{
55 using KeyType
= const URLAndReferrerInfo
*;
56 using KeyTypePointer
= const URLAndReferrerInfo
*;
58 explicit URLAndReferrerInfoHashKey(const URLAndReferrerInfo
* aKey
) noexcept
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 {
74 return *mKey
== *aKey
;
77 static const URLAndReferrerInfo
* KeyToPointer(
78 const URLAndReferrerInfo
* aKey
) {
82 static PLDHashNumber
HashKey(const URLAndReferrerInfo
* 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
);
92 static_cast<ReferrerInfo
*>(aKey
->GetReferrerInfo())->Hash());
95 enum { ALLOW_MEMMOVE
= true };
98 RefPtr
<const URLAndReferrerInfo
> mKey
;
101 static already_AddRefed
<URLAndReferrerInfo
> ResolveURLUsingLocalRef(
102 nsIFrame
* aFrame
, const StyleComputedImageUrl
& aURL
) {
105 nsCOMPtr
<nsIURI
> uri
= aURL
.GetURI();
107 if (aURL
.IsLocalRef()) {
108 uri
= SVGObserverUtils::GetBaseURLForLocalRef(aFrame
->GetContent(), uri
);
109 uri
= aURL
.ResolveLocalRef(uri
);
116 RefPtr
<URLAndReferrerInfo
> info
=
117 new URLAndReferrerInfo(uri
, aURL
.ExtraData());
118 return info
.forget();
121 static already_AddRefed
<URLAndReferrerInfo
> ResolveURLUsingLocalRef(
122 nsIContent
* aContent
, const nsAString
& aURL
,
123 nsIReferrerInfo
* aReferrerInfo
) {
124 // Like SVGObserverUtils::GetBaseURLForLocalRef, we want to resolve the
125 // URL against any <use> element shadow tree's source document.
127 // Unlike GetBaseURLForLocalRef, we are assuming that the URL was specified
128 // directly on mFrame's content (because this ResolveURLUsingLocalRef
129 // overload is used for href="" attributes and not CSS URL values), so there
130 // is no need to check whether the URL was specified / inherited from
131 // outside the shadow tree.
132 nsIURI
* base
= nullptr;
133 const Encoding
* encoding
= nullptr;
134 if (SVGUseElement
* use
= aContent
->GetContainingSVGUseShadowHost()) {
135 base
= use
->GetSourceDocURI();
136 encoding
= use
->GetSourceDocCharacterSet();
140 base
= aContent
->OwnerDoc()->GetDocumentURI();
141 encoding
= aContent
->OwnerDoc()->GetDocumentCharacterSet();
144 nsCOMPtr
<nsIURI
> uri
;
145 Unused
<< NS_NewURI(getter_AddRefs(uri
), aURL
, WrapNotNull(encoding
), base
);
151 RefPtr
<URLAndReferrerInfo
> info
= new URLAndReferrerInfo(uri
, aReferrerInfo
);
152 return info
.forget();
155 static already_AddRefed
<URLAndReferrerInfo
> ResolveURLUsingLocalRef(
156 nsIFrame
* aFrame
, const nsAString
& aURL
, nsIReferrerInfo
* aReferrerInfo
) {
159 return ResolveURLUsingLocalRef(aFrame
->GetContent(), aURL
, aReferrerInfo
);
162 class SVGFilterObserverList
;
165 * A class used as a member of the "observer" classes below to help them
166 * avoid dereferencing their frame during presshell teardown when their frame
167 * may have been destroyed (leaving their pointer to their frame dangling).
169 * When a presshell is torn down, the properties for each frame may not be
170 * deleted until after the frames are destroyed. "Observer" objects (attached
171 * as frame properties) must therefore check whether the presshell is being
172 * torn down before using their pointer to their frame.
174 * mFramePresShell may be null, but when mFrame is non-null, mFramePresShell
175 * is guaranteed to be non-null, too.
177 struct SVGFrameReferenceFromProperty
{
178 explicit SVGFrameReferenceFromProperty(nsIFrame
* aFrame
)
179 : mFrame(aFrame
), mFramePresShell(aFrame
->PresShell()) {}
181 // Clear our reference to the frame.
184 mFramePresShell
= nullptr;
187 // null if the frame has become invalid
189 if (mFramePresShell
&& mFramePresShell
->IsDestroying()) {
190 Detach(); // mFrame is no longer valid.
196 // The frame that our property is attached to (may be null).
198 PresShell
* mFramePresShell
;
201 void SVGRenderingObserver::StartObserving() {
202 Element
* target
= GetReferencedElementWithoutObserving();
204 target
->AddMutationObserver(this);
208 void SVGRenderingObserver::StopObserving() {
209 Element
* target
= GetReferencedElementWithoutObserving();
212 target
->RemoveMutationObserver(this);
213 if (mInObserverSet
) {
214 SVGObserverUtils::RemoveRenderingObserver(target
, this);
215 mInObserverSet
= false;
218 NS_ASSERTION(!mInObserverSet
, "still in an observer set?");
221 Element
* SVGRenderingObserver::GetAndObserveReferencedElement() {
225 Element
* referencedElement
= GetReferencedElementWithoutObserving();
226 if (referencedElement
&& !mInObserverSet
) {
227 SVGObserverUtils::AddRenderingObserver(referencedElement
, this);
228 mInObserverSet
= true;
230 return referencedElement
;
233 nsIFrame
* SVGRenderingObserver::GetAndObserveReferencedFrame() {
234 Element
* referencedElement
= GetAndObserveReferencedElement();
235 return referencedElement
? referencedElement
->GetPrimaryFrame() : nullptr;
238 nsIFrame
* SVGRenderingObserver::GetAndObserveReferencedFrame(
239 LayoutFrameType aFrameType
, bool* aOK
) {
240 nsIFrame
* frame
= GetAndObserveReferencedFrame();
242 if (frame
->Type() == aFrameType
) {
252 void SVGRenderingObserver::OnNonDOMMutationRenderingChange() {
253 mInObserverSet
= false;
257 void SVGRenderingObserver::NotifyEvictedFromRenderingObserverSet() {
258 mInObserverSet
= false; // We've been removed from rendering-obs. set.
259 StopObserving(); // Stop observing mutations too.
262 void SVGRenderingObserver::AttributeChanged(dom::Element
* aElement
,
263 int32_t aNameSpaceID
,
266 const nsAttrValue
* aOldValue
) {
267 if (aElement
->IsInNativeAnonymousSubtree()) {
268 // Don't observe attribute changes in native-anonymous subtrees like
273 // An attribute belonging to the element that we are observing *or one of its
274 // descendants* has changed.
276 // In the case of observing a gradient element, say, we want to know if any
277 // of its 'stop' element children change, but we don't actually want to do
278 // anything for changes to SMIL element children, for example. Maybe it's not
279 // worth having logic to optimize for that, but in most cases it could be a
282 // XXXjwatt: do we really want to blindly break the link between our
283 // observers and ourselves for all attribute changes? For non-ID changes
284 // surely that is unnecessary.
286 if (mFlags
& OBSERVE_ATTRIBUTE_CHANGES
) {
291 void SVGRenderingObserver::ContentAppended(nsIContent
* aFirstNewContent
) {
292 if (mFlags
& OBSERVE_CONTENT_CHANGES
) {
297 void SVGRenderingObserver::ContentInserted(nsIContent
* aChild
) {
298 if (mFlags
& OBSERVE_CONTENT_CHANGES
) {
303 void SVGRenderingObserver::ContentRemoved(nsIContent
* aChild
,
304 nsIContent
* aPreviousSibling
) {
305 if (mFlags
& OBSERVE_CONTENT_CHANGES
) {
311 * SVG elements reference supporting resources by element ID. We need to
312 * track when those resources change and when the document changes in ways
313 * that affect which element is referenced by a given ID (e.g., when
314 * element IDs change). The code here is responsible for that.
316 * When a frame references a supporting resource, we create a property
317 * object derived from SVGIDRenderingObserver to manage the relationship. The
318 * property object is attached to the referencing frame.
320 class SVGIDRenderingObserver
: public SVGRenderingObserver
{
322 // Callback for checking if the element being observed is valid for this
323 // observer. Note that this may be called during construction, before the
324 // deriving class is fully constructed.
325 using TargetIsValidCallback
= bool (*)(const Element
&);
326 SVGIDRenderingObserver(
327 URLAndReferrerInfo
* aURI
, nsIContent
* aObservingContent
,
328 bool aReferenceImage
,
329 uint32_t aFlags
= OBSERVE_ATTRIBUTE_CHANGES
| OBSERVE_CONTENT_CHANGES
,
330 TargetIsValidCallback aTargetIsValidCallback
= nullptr);
332 void Traverse(nsCycleCollectionTraversalCallback
* aCB
);
335 virtual ~SVGIDRenderingObserver() {
336 // This needs to call our GetReferencedElementWithoutObserving override,
337 // so must be called here rather than in our base class's dtor.
341 void TargetChanged() {
342 mTargetIsValid
= ([this] {
343 Element
* observed
= mObservedElementTracker
.get();
347 // If the content is observing an ancestor, then return the target is not
350 // TODO(emilio): Should we allow content observing its own descendants?
351 // That seems potentially-bad as well.
352 if (observed
->OwnerDoc() == mObservingContent
->OwnerDoc() &&
353 nsContentUtils::ContentIsHostIncludingDescendantOf(mObservingContent
,
357 if (mTargetIsValidCallback
) {
358 return mTargetIsValidCallback(*observed
);
364 Element
* GetReferencedElementWithoutObserving() final
{
365 return mTargetIsValid
? mObservedElementTracker
.get() : nullptr;
368 void OnRenderingChange() override
;
371 * Helper that provides a reference to the element with the ID that our
372 * observer wants to observe, and that will invalidate our observer if the
373 * element that that ID identifies changes to a different element (or none).
375 class ElementTracker final
: public IDTracker
{
377 explicit ElementTracker(SVGIDRenderingObserver
* aOwningObserver
)
378 : mOwningObserver(aOwningObserver
) {}
381 void ElementChanged(Element
* aFrom
, Element
* aTo
) override
{
382 // Call OnRenderingChange() before the target changes, so that
383 // mIsTargetValid reflects the right state.
384 mOwningObserver
->OnRenderingChange();
385 mOwningObserver
->StopObserving();
386 IDTracker::ElementChanged(aFrom
, aTo
);
387 mOwningObserver
->TargetChanged();
388 mOwningObserver
->StartObserving();
389 // And same after the target changes, for the same reason.
390 mOwningObserver
->OnRenderingChange();
393 * Override IsPersistent because we want to keep tracking the element
394 * for the ID even when it changes.
396 bool IsPersistent() override
{ return true; }
399 SVGIDRenderingObserver
* mOwningObserver
;
402 ElementTracker mObservedElementTracker
;
403 RefPtr
<Element
> mObservingContent
;
404 bool mTargetIsValid
= false;
405 TargetIsValidCallback mTargetIsValidCallback
;
409 * Note that in the current setup there are two separate observer lists.
411 * In SVGIDRenderingObserver's ctor, the new object adds itself to the
412 * mutation observer list maintained by the referenced element. In this way the
413 * SVGIDRenderingObserver is notified if there are any attribute or content
414 * tree changes to the element or any of its *descendants*.
416 * In SVGIDRenderingObserver::GetAndObserveReferencedElement() the
417 * SVGIDRenderingObserver object also adds itself to an
418 * SVGRenderingObserverSet object belonging to the referenced
421 * XXX: it would be nice to have a clear and concise executive summary of the
422 * benefits/necessity of maintaining a second observer list.
424 SVGIDRenderingObserver::SVGIDRenderingObserver(
425 URLAndReferrerInfo
* aURI
, nsIContent
* aObservingContent
,
426 bool aReferenceImage
, uint32_t aFlags
,
427 TargetIsValidCallback aTargetIsValidCallback
)
428 : SVGRenderingObserver(aFlags
),
429 mObservedElementTracker(this),
430 mObservingContent(aObservingContent
->AsElement()),
431 mTargetIsValidCallback(aTargetIsValidCallback
) {
432 // Start watching the target element
433 nsIURI
* uri
= nullptr;
434 nsIReferrerInfo
* referrerInfo
= nullptr;
436 uri
= aURI
->GetURI();
437 referrerInfo
= aURI
->GetReferrerInfo();
440 mObservedElementTracker
.ResetToURIFragmentID(
441 aObservingContent
, uri
, referrerInfo
, true, aReferenceImage
);
446 void SVGIDRenderingObserver::Traverse(nsCycleCollectionTraversalCallback
* aCB
) {
447 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCB
, "mObservingContent");
448 aCB
->NoteXPCOMChild(mObservingContent
);
449 mObservedElementTracker
.Traverse(aCB
);
452 void SVGIDRenderingObserver::OnRenderingChange() {
453 if (mObservedElementTracker
.get() && mInObserverSet
) {
454 SVGObserverUtils::RemoveRenderingObserver(mObservedElementTracker
.get(),
456 mInObserverSet
= false;
460 class SVGRenderingObserverProperty
: public SVGIDRenderingObserver
{
464 SVGRenderingObserverProperty(
465 URLAndReferrerInfo
* aURI
, nsIFrame
* aFrame
, bool aReferenceImage
,
466 uint32_t aFlags
= OBSERVE_ATTRIBUTE_CHANGES
| OBSERVE_CONTENT_CHANGES
,
467 TargetIsValidCallback aTargetIsValidCallback
= nullptr)
468 : SVGIDRenderingObserver(aURI
, aFrame
->GetContent(), aReferenceImage
,
469 aFlags
, aTargetIsValidCallback
),
470 mFrameReference(aFrame
) {}
473 virtual ~SVGRenderingObserverProperty() = default; // non-public
475 void OnRenderingChange() override
;
477 SVGFrameReferenceFromProperty mFrameReference
;
480 NS_IMPL_ISUPPORTS(SVGRenderingObserverProperty
, nsIMutationObserver
)
482 void SVGRenderingObserverProperty::OnRenderingChange() {
483 SVGIDRenderingObserver::OnRenderingChange();
485 if (!mTargetIsValid
) {
489 nsIFrame
* frame
= mFrameReference
.Get();
491 if (frame
&& frame
->HasAllStateBits(NS_FRAME_SVG_LAYOUT
)) {
492 // We need to notify anything that is observing the referencing frame or
493 // any of its ancestors that the referencing frame has been invalidated.
494 // Since walking the parent chain checking for observers is expensive we
495 // do that using a change hint (multiple change hints of the same type are
497 nsLayoutUtils::PostRestyleEvent(frame
->GetContent()->AsElement(),
499 nsChangeHint_InvalidateRenderingObservers
);
503 static bool IsSVGGeometryElement(const Element
& aObserved
) {
504 return aObserved
.IsSVGGeometryElement();
507 class SVGTextPathObserver final
: public SVGRenderingObserverProperty
{
509 SVGTextPathObserver(URLAndReferrerInfo
* aURI
, nsIFrame
* aFrame
,
510 bool aReferenceImage
)
511 : SVGRenderingObserverProperty(aURI
, aFrame
, aReferenceImage
,
512 OBSERVE_ATTRIBUTE_CHANGES
,
513 IsSVGGeometryElement
) {}
516 void OnRenderingChange() override
;
519 void SVGTextPathObserver::OnRenderingChange() {
520 SVGRenderingObserverProperty::OnRenderingChange();
522 if (!mTargetIsValid
) {
526 nsIFrame
* frame
= mFrameReference
.Get();
532 frame
->IsFrameOfType(nsIFrame::eSVG
) || frame
->IsInSVGTextSubtree(),
533 "SVG frame expected");
535 MOZ_ASSERT(frame
->GetContent()->IsSVGElement(nsGkAtoms::textPath
),
536 "expected frame for a <textPath> element");
538 auto* text
= static_cast<SVGTextFrame
*>(
539 nsLayoutUtils::GetClosestFrameOfType(frame
, LayoutFrameType::SVGText
));
540 MOZ_ASSERT(text
, "expected to find an ancestor SVGTextFrame");
542 text
->AddStateBits(NS_STATE_SVG_POSITIONING_DIRTY
);
544 if (SVGUtils::AnyOuterSVGIsCallingReflowSVG(text
)) {
545 text
->AddStateBits(NS_FRAME_IS_DIRTY
| NS_FRAME_HAS_DIRTY_CHILDREN
);
546 if (text
->HasAnyStateBits(NS_FRAME_IS_NONDISPLAY
)) {
547 text
->ReflowSVGNonDisplayText();
552 text
->ScheduleReflowSVG();
557 class SVGMPathObserver final
: public SVGIDRenderingObserver
{
561 SVGMPathObserver(URLAndReferrerInfo
* aURI
, SVGMPathElement
* aElement
)
562 : SVGIDRenderingObserver(aURI
, aElement
, /* aReferenceImage = */ false,
563 OBSERVE_ATTRIBUTE_CHANGES
,
564 IsSVGGeometryElement
) {}
567 virtual ~SVGMPathObserver() = default; // non-public
569 void OnRenderingChange() override
;
572 NS_IMPL_ISUPPORTS(SVGMPathObserver
, nsIMutationObserver
)
574 void SVGMPathObserver::OnRenderingChange() {
575 SVGIDRenderingObserver::OnRenderingChange();
577 if (!mTargetIsValid
) {
581 auto* element
= static_cast<SVGMPathElement
*>(mObservingContent
.get());
582 element
->NotifyParentOfMpathChange();
585 class SVGMarkerObserver final
: public SVGRenderingObserverProperty
{
587 SVGMarkerObserver(URLAndReferrerInfo
* aURI
, nsIFrame
* aFrame
,
588 bool aReferenceImage
)
589 : SVGRenderingObserverProperty(
590 aURI
, aFrame
, aReferenceImage
,
591 OBSERVE_ATTRIBUTE_CHANGES
| OBSERVE_CONTENT_CHANGES
) {}
594 void OnRenderingChange() override
;
597 void SVGMarkerObserver::OnRenderingChange() {
598 SVGRenderingObserverProperty::OnRenderingChange();
600 nsIFrame
* frame
= mFrameReference
.Get();
605 MOZ_ASSERT(frame
->IsFrameOfType(nsIFrame::eSVG
), "SVG frame expected");
607 // Don't need to request ReflowFrame if we're being reflowed.
608 // Because mRect for SVG frames includes the bounds of any markers
609 // (see the comment for nsIFrame::GetRect), the referencing frame must be
610 // reflowed for any marker changes.
611 if (!frame
->HasAnyStateBits(NS_FRAME_IN_REFLOW
)) {
612 // XXXjwatt: We need to unify SVG into standard reflow so we can just use
613 // nsChangeHint_NeedReflow | nsChangeHint_NeedDirtyReflow here.
614 // XXXSDL KILL THIS!!!
615 SVGUtils::ScheduleReflowSVG(frame
);
617 frame
->PresContext()->RestyleManager()->PostRestyleEvent(
618 frame
->GetContent()->AsElement(), RestyleHint
{0},
619 nsChangeHint_RepaintFrame
);
622 class SVGPaintingProperty
: public SVGRenderingObserverProperty
{
624 SVGPaintingProperty(URLAndReferrerInfo
* aURI
, nsIFrame
* aFrame
,
625 bool aReferenceImage
)
626 : SVGRenderingObserverProperty(aURI
, aFrame
, aReferenceImage
) {}
629 void OnRenderingChange() override
;
632 void SVGPaintingProperty::OnRenderingChange() {
633 SVGRenderingObserverProperty::OnRenderingChange();
635 nsIFrame
* frame
= mFrameReference
.Get();
640 if (frame
->HasAnyStateBits(NS_FRAME_SVG_LAYOUT
)) {
641 frame
->InvalidateFrameSubtree();
643 for (nsIFrame
* f
= frame
; f
;
644 f
= nsLayoutUtils::GetNextContinuationOrIBSplitSibling(f
)) {
645 f
->InvalidateFrame();
650 // Observer for -moz-element(#element). Note that the observed element does not
651 // have to be an SVG element.
652 class SVGMozElementObserver final
: public SVGPaintingProperty
{
654 SVGMozElementObserver(URLAndReferrerInfo
* aURI
, nsIFrame
* aFrame
)
655 : SVGPaintingProperty(aURI
, aFrame
, /* aReferenceImage = */ true) {}
657 // We only return true here because GetAndObserveBackgroundImage uses us
658 // to implement observing of arbitrary elements (including HTML elements)
659 // that may require us to repaint if the referenced element is reflowed.
660 // Bug 1496065 has been filed to remove that support though.
661 bool ObservesReflow() override
{ return true; }
665 * For content with `background-clip: text`.
667 * This observer is unusual in that the observing frame and observed frame are
668 * the same frame. This is because the observing frame is observing for reflow
669 * of its descendant text nodes, since such reflows may not result in the
670 * frame's nsDisplayBackground changing. In other words, Display List Based
671 * Invalidation may not invalidate the frame's background, so we need this
672 * observer to make sure that happens.
674 * XXX: It's questionable whether we should even be [ab]using the SVG observer
675 * mechanism for `background-clip:text`. Since we know that the observed frame
676 * is the frame we need to invalidate, we could just check the computed style
677 * in the (one) place where we pass INVALIDATE_REFLOW and invalidate there...
679 class BackgroundClipRenderingObserver
: public SVGRenderingObserver
{
681 explicit BackgroundClipRenderingObserver(nsIFrame
* aFrame
) : mFrame(aFrame
) {}
686 // We do not call StopObserving() since the observing and observed element
687 // are the same element (and because we could crash - see bug 1556441).
688 virtual ~BackgroundClipRenderingObserver() = default;
690 Element
* GetReferencedElementWithoutObserving() final
{
691 return mFrame
->GetContent()->AsElement();
694 void OnRenderingChange() final
;
697 * Observing for mutations is not enough. A new font loading and applying
698 * to the text content could cause it to reflow, and we need to invalidate
701 bool ObservesReflow() final
{ return true; }
703 // The observer and observee!
707 NS_IMPL_ISUPPORTS(BackgroundClipRenderingObserver
, nsIMutationObserver
)
709 void BackgroundClipRenderingObserver::OnRenderingChange() {
710 for (nsIFrame
* f
= mFrame
; f
;
711 f
= nsLayoutUtils::GetNextContinuationOrIBSplitSibling(f
)) {
712 f
->InvalidateFrame();
716 static bool IsSVGFilterElement(const Element
& aObserved
) {
717 return aObserved
.IsSVGElement(nsGkAtoms::filter
);
721 * In a filter chain, there can be multiple SVG reference filters.
722 * e.g. filter: url(#svg-filter-1) blur(10px) url(#svg-filter-2);
724 * This class keeps track of one SVG reference filter in a filter chain.
725 * e.g. url(#svg-filter-1)
727 * It fires invalidations when the SVG filter element's id changes or when
728 * the SVG filter element's content changes.
730 * The SVGFilterObserverList class manages a list of SVGFilterObservers.
732 class SVGFilterObserver final
: public SVGIDRenderingObserver
{
734 SVGFilterObserver(URLAndReferrerInfo
* aURI
, nsIContent
* aObservingContent
,
735 SVGFilterObserverList
* aFilterChainObserver
)
736 : SVGIDRenderingObserver(
737 aURI
, aObservingContent
, false,
738 OBSERVE_ATTRIBUTE_CHANGES
| OBSERVE_CONTENT_CHANGES
,
740 mFilterObserverList(aFilterChainObserver
) {}
742 void DetachFromChainObserver() { mFilterObserverList
= nullptr; }
745 * @return the filter frame, or null if there is no filter frame
747 SVGFilterFrame
* GetAndObserveFilterFrame();
750 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
751 NS_DECL_CYCLE_COLLECTION_CLASS(SVGFilterObserver
)
753 // SVGIDRenderingObserver
754 void OnRenderingChange() override
;
757 virtual ~SVGFilterObserver() = default; // non-public
759 SVGFilterObserverList
* mFilterObserverList
;
762 NS_IMPL_CYCLE_COLLECTING_ADDREF(SVGFilterObserver
)
763 NS_IMPL_CYCLE_COLLECTING_RELEASE(SVGFilterObserver
)
765 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SVGFilterObserver
)
766 NS_INTERFACE_MAP_ENTRY(nsISupports
)
767 NS_INTERFACE_MAP_ENTRY(nsIMutationObserver
)
770 NS_IMPL_CYCLE_COLLECTION_CLASS(SVGFilterObserver
)
772 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(SVGFilterObserver
)
773 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mObservedElementTracker
)
774 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mObservingContent
)
775 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
777 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(SVGFilterObserver
)
778 tmp
->StopObserving();
779 NS_IMPL_CYCLE_COLLECTION_UNLINK(mObservedElementTracker
);
780 NS_IMPL_CYCLE_COLLECTION_UNLINK(mObservingContent
)
781 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
783 SVGFilterFrame
* SVGFilterObserver::GetAndObserveFilterFrame() {
784 return static_cast<SVGFilterFrame
*>(
785 GetAndObserveReferencedFrame(LayoutFrameType::SVGFilter
, nullptr));
789 * This class manages a list of SVGFilterObservers, which correspond to
790 * reference to SVG filters in a list of filters in a given 'filter' property.
791 * e.g. filter: url(#svg-filter-1) blur(10px) url(#svg-filter-2);
793 * In the above example, the SVGFilterObserverList will manage two
794 * SVGFilterObservers, one for each of the references to SVG filters. CSS
795 * filters like "blur(10px)" don't reference filter elements, so they don't
796 * need an SVGFilterObserver. The style system invalidates changes to CSS
799 * FIXME(emilio): Why do we need this as opposed to the individual observers we
800 * create in the constructor?
802 class SVGFilterObserverList
: public nsISupports
{
804 SVGFilterObserverList(Span
<const StyleFilter
> aFilters
,
805 nsIContent
* aFilteredElement
,
806 nsIFrame
* aFilteredFrame
= nullptr);
808 const nsTArray
<RefPtr
<SVGFilterObserver
>>& GetObservers() const {
813 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
814 NS_DECL_CYCLE_COLLECTION_CLASS(SVGFilterObserverList
)
816 virtual void OnRenderingChange() = 0;
819 virtual ~SVGFilterObserverList();
821 void DetachObservers() {
822 for (auto& observer
: mObservers
) {
823 observer
->DetachFromChainObserver();
827 nsTArray
<RefPtr
<SVGFilterObserver
>> mObservers
;
830 void SVGFilterObserver::OnRenderingChange() {
831 SVGIDRenderingObserver::OnRenderingChange();
833 if (mFilterObserverList
) {
834 mFilterObserverList
->OnRenderingChange();
837 if (!mTargetIsValid
) {
841 nsIFrame
* frame
= mObservingContent
->GetPrimaryFrame();
846 // Repaint asynchronously in case the filter frame is being torn down
847 nsChangeHint changeHint
= nsChangeHint(nsChangeHint_RepaintFrame
);
849 // Since we don't call SVGRenderingObserverProperty::
850 // OnRenderingChange, we have to add this bit ourselves.
851 if (frame
->HasAnyStateBits(NS_FRAME_SVG_LAYOUT
)) {
852 // Changes should propagate out to things that might be observing
853 // the referencing frame or its ancestors.
854 changeHint
|= nsChangeHint_InvalidateRenderingObservers
;
857 // Don't need to request UpdateOverflow if we're being reflowed.
858 if (!frame
->HasAnyStateBits(NS_FRAME_IN_REFLOW
)) {
859 changeHint
|= nsChangeHint_UpdateOverflow
;
861 frame
->PresContext()->RestyleManager()->PostRestyleEvent(
862 mObservingContent
, RestyleHint
{0}, changeHint
);
865 NS_IMPL_CYCLE_COLLECTING_ADDREF(SVGFilterObserverList
)
866 NS_IMPL_CYCLE_COLLECTING_RELEASE(SVGFilterObserverList
)
868 NS_IMPL_CYCLE_COLLECTION_CLASS(SVGFilterObserverList
)
870 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(SVGFilterObserverList
)
871 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mObservers
)
872 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
874 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(SVGFilterObserverList
)
875 tmp
->DetachObservers();
876 NS_IMPL_CYCLE_COLLECTION_UNLINK(mObservers
);
877 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
879 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SVGFilterObserverList
)
880 NS_INTERFACE_MAP_ENTRY(nsISupports
)
883 SVGFilterObserverList::SVGFilterObserverList(Span
<const StyleFilter
> aFilters
,
884 nsIContent
* aFilteredElement
,
885 nsIFrame
* aFilteredFrame
) {
886 for (const auto& filter
: aFilters
) {
887 if (!filter
.IsUrl()) {
891 const auto& url
= filter
.AsUrl();
893 // aFilteredFrame can be null if this filter belongs to a
894 // CanvasRenderingContext2D.
895 RefPtr
<URLAndReferrerInfo
> filterURL
;
896 if (aFilteredFrame
) {
897 filterURL
= ResolveURLUsingLocalRef(aFilteredFrame
, url
);
899 nsCOMPtr
<nsIURI
> resolvedURI
= url
.ResolveLocalRef(aFilteredElement
);
901 filterURL
= new URLAndReferrerInfo(resolvedURI
, url
.ExtraData());
905 RefPtr
<SVGFilterObserver
> observer
=
906 new SVGFilterObserver(filterURL
, aFilteredElement
, this);
907 mObservers
.AppendElement(observer
);
911 SVGFilterObserverList::~SVGFilterObserverList() { DetachObservers(); }
913 class SVGFilterObserverListForCSSProp final
: public SVGFilterObserverList
{
915 SVGFilterObserverListForCSSProp(Span
<const StyleFilter
> aFilters
,
916 nsIFrame
* aFilteredFrame
)
917 : SVGFilterObserverList(aFilters
, aFilteredFrame
->GetContent(),
921 void OnRenderingChange() override
;
922 bool mInvalidating
= false;
925 void SVGFilterObserverListForCSSProp::OnRenderingChange() {
929 AutoRestore
<bool> guard(mInvalidating
);
930 mInvalidating
= true;
931 for (auto& observer
: mObservers
) {
932 observer
->OnRenderingChange();
936 class SVGFilterObserverListForCanvasContext final
937 : public SVGFilterObserverList
{
939 SVGFilterObserverListForCanvasContext(CanvasRenderingContext2D
* aContext
,
940 Element
* aCanvasElement
,
941 Span
<const StyleFilter
> aFilters
)
942 : SVGFilterObserverList(aFilters
, aCanvasElement
), mContext(aContext
) {}
944 void OnRenderingChange() override
;
945 void DetachFromContext() { mContext
= nullptr; }
948 CanvasRenderingContext2D
* mContext
;
951 void SVGFilterObserverListForCanvasContext::OnRenderingChange() {
954 "GFX: This should never be called without a context, except during "
955 "cycle collection (when DetachFromContext has been called)");
958 // Refresh the cached FilterDescription in mContext->CurrentState().filter.
959 // If this filter is not at the top of the state stack, we'll refresh the
960 // wrong filter, but that's ok, because we'll refresh the right filter
961 // when we pop the state stack in CanvasRenderingContext2D::Restore().
962 RefPtr
<CanvasRenderingContext2D
> kungFuDeathGrip(mContext
);
963 kungFuDeathGrip
->UpdateFilter();
966 class SVGMaskObserverList final
: public nsISupports
{
968 explicit SVGMaskObserverList(nsIFrame
* aFrame
);
973 const nsTArray
<RefPtr
<SVGPaintingProperty
>>& GetObservers() const {
977 void ResolveImage(uint32_t aIndex
);
980 virtual ~SVGMaskObserverList() = default; // non-public
981 nsTArray
<RefPtr
<SVGPaintingProperty
>> mProperties
;
985 NS_IMPL_ISUPPORTS(SVGMaskObserverList
, nsISupports
)
987 SVGMaskObserverList::SVGMaskObserverList(nsIFrame
* aFrame
) : mFrame(aFrame
) {
988 const nsStyleSVGReset
* svgReset
= aFrame
->StyleSVGReset();
990 for (uint32_t i
= 0; i
< svgReset
->mMask
.mImageCount
; i
++) {
991 const StyleComputedImageUrl
* data
=
992 svgReset
->mMask
.mLayers
[i
].mImage
.GetImageRequestURLValue();
993 RefPtr
<URLAndReferrerInfo
> maskUri
;
995 maskUri
= ResolveURLUsingLocalRef(aFrame
, *data
);
1000 maskUri
->GetURI()->GetHasRef(&hasRef
);
1003 // Accrording to maskUri, SVGPaintingProperty's ctor may trigger an
1004 // external SVG resource download, so we should pass maskUri in only if
1005 // maskUri has a chance pointing to an SVG mask resource.
1007 // And, an URL may refer to an SVG mask resource if it consists of
1009 SVGPaintingProperty
* prop
= new SVGPaintingProperty(
1010 hasRef
? maskUri
.get() : nullptr, aFrame
, false);
1011 mProperties
.AppendElement(prop
);
1015 void SVGMaskObserverList::ResolveImage(uint32_t aIndex
) {
1016 const nsStyleSVGReset
* svgReset
= mFrame
->StyleSVGReset();
1017 MOZ_ASSERT(aIndex
< svgReset
->mMask
.mImageCount
);
1019 auto& image
= const_cast<StyleImage
&>(svgReset
->mMask
.mLayers
[aIndex
].mImage
);
1020 if (image
.IsResolved()) {
1023 MOZ_ASSERT(image
.IsImageRequestType());
1024 Document
* doc
= mFrame
->PresContext()->Document();
1025 image
.ResolveImage(*doc
, nullptr);
1026 if (imgRequestProxy
* req
= image
.GetImageRequest()) {
1027 // FIXME(emilio): What disassociates this request?
1028 doc
->StyleImageLoader()->AssociateRequestToFrame(req
, mFrame
);
1033 * Used for gradient-to-gradient, pattern-to-pattern and filter-to-filter
1034 * references to "template" elements (specified via the 'href' attributes).
1036 class SVGTemplateElementObserver
: public SVGIDRenderingObserver
{
1040 SVGTemplateElementObserver(URLAndReferrerInfo
* aURI
, nsIFrame
* aFrame
,
1041 bool aReferenceImage
)
1042 : SVGIDRenderingObserver(
1043 aURI
, aFrame
->GetContent(), aReferenceImage
,
1044 OBSERVE_ATTRIBUTE_CHANGES
| OBSERVE_CONTENT_CHANGES
),
1045 mFrameReference(aFrame
) {}
1048 virtual ~SVGTemplateElementObserver() = default; // non-public
1050 void OnRenderingChange() override
;
1052 SVGFrameReferenceFromProperty mFrameReference
;
1055 NS_IMPL_ISUPPORTS(SVGTemplateElementObserver
, nsIMutationObserver
)
1057 void SVGTemplateElementObserver::OnRenderingChange() {
1058 SVGIDRenderingObserver::OnRenderingChange();
1060 if (nsIFrame
* frame
= mFrameReference
.Get()) {
1061 SVGObserverUtils::InvalidateRenderingObservers(frame
);
1066 * An instance of this class is stored on an observed frame (as a frame
1067 * property) whenever the frame has active rendering observers. It is used to
1068 * store pointers to the SVGRenderingObserver instances belonging to any
1069 * observing frames, allowing invalidations from the observed frame to be sent
1070 * to all observing frames.
1072 * SVGRenderingObserver instances that are added are not strongly referenced,
1073 * so they must remove themselves before they die.
1075 * This class is "single-shot", which is to say that when something about the
1076 * observed element changes, InvalidateAll() clears our hashtable of
1077 * SVGRenderingObservers. SVGRenderingObserver objects will be added back
1078 * again if/when the observing frame looks up our observed frame to use it.
1080 * XXXjwatt: is this the best thing to do nowadays? Back when that mechanism
1081 * landed in bug 330498 we had two pass, recursive invalidation up the frame
1082 * tree, and I think reference loops were a problem. Nowadays maybe a flag
1083 * on the SVGRenderingObserver objects to coalesce invalidations may work
1086 * InvalidateAll must be called before this object is destroyed, i.e.
1087 * before the referenced frame is destroyed. This should normally happen
1088 * via SVGContainerFrame::RemoveFrame, since only frames in the frame
1089 * tree should be referenced.
1091 class SVGRenderingObserverSet
{
1093 SVGRenderingObserverSet() : mObservers(4) {
1094 MOZ_COUNT_CTOR(SVGRenderingObserverSet
);
1097 ~SVGRenderingObserverSet() {
1099 MOZ_COUNT_DTOR(SVGRenderingObserverSet
);
1102 void Add(SVGRenderingObserver
* aObserver
) { mObservers
.Insert(aObserver
); }
1103 void Remove(SVGRenderingObserver
* aObserver
) { mObservers
.Remove(aObserver
); }
1105 bool Contains(SVGRenderingObserver
* aObserver
) {
1106 return mObservers
.Contains(aObserver
);
1109 bool IsEmpty() { return mObservers
.IsEmpty(); }
1112 * Drop all our observers, and notify them that we have changed and dropped
1113 * our reference to them.
1115 void InvalidateAll();
1118 * Drop all observers that observe reflow, and notify them that we have
1119 * changed and dropped our reference to them.
1121 void InvalidateAllForReflow();
1124 * Drop all our observers, and notify them that we have dropped our reference
1130 nsTHashSet
<SVGRenderingObserver
*> mObservers
;
1133 void SVGRenderingObserverSet::InvalidateAll() {
1134 if (mObservers
.IsEmpty()) {
1138 const auto observers
= std::move(mObservers
);
1140 for (const auto& observer
: observers
) {
1141 observer
->OnNonDOMMutationRenderingChange();
1145 void SVGRenderingObserverSet::InvalidateAllForReflow() {
1146 if (mObservers
.IsEmpty()) {
1150 AutoTArray
<SVGRenderingObserver
*, 10> observers
;
1152 for (auto it
= mObservers
.cbegin(), end
= mObservers
.cend(); it
!= end
;
1154 SVGRenderingObserver
* obs
= *it
;
1155 if (obs
->ObservesReflow()) {
1156 observers
.AppendElement(obs
);
1157 mObservers
.Remove(it
);
1161 for (uint32_t i
= 0; i
< observers
.Length(); ++i
) {
1162 observers
[i
]->OnNonDOMMutationRenderingChange();
1166 void SVGRenderingObserverSet::RemoveAll() {
1167 const auto observers
= std::move(mObservers
);
1169 // Our list is now cleared. We need to notify the observers we've removed,
1170 // so they can update their state & remove themselves as mutation-observers.
1171 for (const auto& observer
: observers
) {
1172 observer
->NotifyEvictedFromRenderingObserverSet();
1176 static SVGRenderingObserverSet
* GetObserverSet(Element
* aElement
) {
1177 return static_cast<SVGRenderingObserverSet
*>(
1178 aElement
->GetProperty(nsGkAtoms::renderingobserverset
));
1182 // Defined down here because we need SVGRenderingObserverSet's definition.
1183 void SVGRenderingObserver::DebugObserverSet() {
1184 Element
* referencedElement
= GetReferencedElementWithoutObserving();
1185 if (referencedElement
) {
1186 SVGRenderingObserverSet
* observers
= GetObserverSet(referencedElement
);
1187 bool inObserverSet
= observers
&& observers
->Contains(this);
1188 MOZ_ASSERT(inObserverSet
== mInObserverSet
,
1189 "failed to track whether we're in our referenced element's "
1192 MOZ_ASSERT(!mInObserverSet
, "In whose observer set are we, then?");
1197 using URIObserverHashtable
=
1198 nsInterfaceHashtable
<URLAndReferrerInfoHashKey
, nsIMutationObserver
>;
1200 using PaintingPropertyDescriptor
=
1201 const FramePropertyDescriptor
<SVGPaintingProperty
>*;
1203 static void DestroyFilterProperty(SVGFilterObserverListForCSSProp
* aProp
) {
1207 NS_DECLARE_FRAME_PROPERTY_RELEASABLE(HrefToTemplateProperty
,
1208 SVGTemplateElementObserver
)
1209 NS_DECLARE_FRAME_PROPERTY_WITH_DTOR(FilterProperty
,
1210 SVGFilterObserverListForCSSProp
,
1211 DestroyFilterProperty
)
1212 NS_DECLARE_FRAME_PROPERTY_RELEASABLE(MaskProperty
, SVGMaskObserverList
)
1213 NS_DECLARE_FRAME_PROPERTY_RELEASABLE(ClipPathProperty
, SVGPaintingProperty
)
1214 NS_DECLARE_FRAME_PROPERTY_RELEASABLE(MarkerStartProperty
, SVGMarkerObserver
)
1215 NS_DECLARE_FRAME_PROPERTY_RELEASABLE(MarkerMidProperty
, SVGMarkerObserver
)
1216 NS_DECLARE_FRAME_PROPERTY_RELEASABLE(MarkerEndProperty
, SVGMarkerObserver
)
1217 NS_DECLARE_FRAME_PROPERTY_RELEASABLE(FillProperty
, SVGPaintingProperty
)
1218 NS_DECLARE_FRAME_PROPERTY_RELEASABLE(StrokeProperty
, SVGPaintingProperty
)
1219 NS_DECLARE_FRAME_PROPERTY_RELEASABLE(HrefAsTextPathProperty
,
1220 SVGTextPathObserver
)
1221 NS_DECLARE_FRAME_PROPERTY_DELETABLE(BackgroundImageProperty
,
1222 URIObserverHashtable
)
1223 NS_DECLARE_FRAME_PROPERTY_RELEASABLE(BackgroundClipObserverProperty
,
1224 BackgroundClipRenderingObserver
)
1225 NS_DECLARE_FRAME_PROPERTY_RELEASABLE(OffsetPathProperty
,
1226 SVGRenderingObserverProperty
)
1229 static T
* GetEffectProperty(URLAndReferrerInfo
* aURI
, nsIFrame
* aFrame
,
1230 const FramePropertyDescriptor
<T
>* aProperty
) {
1236 T
* prop
= aFrame
->GetProperty(aProperty
, &found
);
1238 MOZ_ASSERT(prop
, "this property should only store non-null values");
1241 prop
= new T(aURI
, aFrame
, false);
1243 aFrame
->AddProperty(aProperty
, prop
);
1247 static SVGPaintingProperty
* GetPaintingProperty(
1248 URLAndReferrerInfo
* aURI
, nsIFrame
* aFrame
,
1249 const FramePropertyDescriptor
<SVGPaintingProperty
>* aProperty
) {
1250 return GetEffectProperty(aURI
, aFrame
, aProperty
);
1253 static already_AddRefed
<URLAndReferrerInfo
> GetMarkerURI(
1254 nsIFrame
* aFrame
, const StyleUrlOrNone
nsStyleSVG::*aMarker
) {
1255 const StyleUrlOrNone
& url
= aFrame
->StyleSVG()->*aMarker
;
1259 return ResolveURLUsingLocalRef(aFrame
, url
.AsUrl());
1262 bool SVGObserverUtils::GetAndObserveMarkers(nsIFrame
* aMarkedFrame
,
1263 SVGMarkerFrame
* (*aFrames
)[3]) {
1264 MOZ_ASSERT(!aMarkedFrame
->GetPrevContinuation() &&
1265 aMarkedFrame
->IsSVGGeometryFrame() &&
1266 static_cast<SVGGeometryElement
*>(aMarkedFrame
->GetContent())
1270 bool foundMarker
= false;
1271 RefPtr
<URLAndReferrerInfo
> markerURL
;
1272 SVGMarkerObserver
* observer
;
1275 #define GET_MARKER(type) \
1276 markerURL = GetMarkerURI(aMarkedFrame, &nsStyleSVG::mMarker##type); \
1278 GetEffectProperty(markerURL, aMarkedFrame, Marker##type##Property()); \
1279 marker = observer ? observer->GetAndObserveReferencedFrame( \
1280 LayoutFrameType::SVGMarker, nullptr) \
1282 foundMarker = foundMarker || bool(marker); \
1283 (*aFrames)[SVGMark::e##type] = static_cast<SVGMarkerFrame*>(marker);
1294 // Note that the returned list will be empty in the case of a 'filter' property
1295 // that only specifies CSS filter functions (no url()'s to SVG filters).
1296 static SVGFilterObserverListForCSSProp
* GetOrCreateFilterObserverListForCSS(
1298 MOZ_ASSERT(!aFrame
->GetPrevContinuation(), "Require first continuation");
1300 const nsStyleEffects
* effects
= aFrame
->StyleEffects();
1301 if (!effects
->HasFilters()) {
1306 SVGFilterObserverListForCSSProp
* observers
=
1307 aFrame
->GetProperty(FilterProperty(), &found
);
1309 MOZ_ASSERT(observers
, "this property should only store non-null values");
1313 new SVGFilterObserverListForCSSProp(effects
->mFilters
.AsSpan(), aFrame
);
1314 NS_ADDREF(observers
);
1315 aFrame
->AddProperty(FilterProperty(), observers
);
1319 static SVGObserverUtils::ReferenceState
GetAndObserveFilters(
1320 SVGFilterObserverList
* aObserverList
,
1321 nsTArray
<SVGFilterFrame
*>* aFilterFrames
) {
1322 if (!aObserverList
) {
1323 return SVGObserverUtils::eHasNoRefs
;
1326 const nsTArray
<RefPtr
<SVGFilterObserver
>>& observers
=
1327 aObserverList
->GetObservers();
1328 if (observers
.IsEmpty()) {
1329 return SVGObserverUtils::eHasNoRefs
;
1332 for (uint32_t i
= 0; i
< observers
.Length(); i
++) {
1333 SVGFilterFrame
* filter
= observers
[i
]->GetAndObserveFilterFrame();
1335 if (aFilterFrames
) {
1336 aFilterFrames
->Clear();
1338 return SVGObserverUtils::eHasRefsSomeInvalid
;
1340 if (aFilterFrames
) {
1341 aFilterFrames
->AppendElement(filter
);
1345 return SVGObserverUtils::eHasRefsAllValid
;
1348 SVGObserverUtils::ReferenceState
SVGObserverUtils::GetAndObserveFilters(
1349 nsIFrame
* aFilteredFrame
, nsTArray
<SVGFilterFrame
*>* aFilterFrames
) {
1350 SVGFilterObserverListForCSSProp
* observerList
=
1351 GetOrCreateFilterObserverListForCSS(aFilteredFrame
);
1352 return mozilla::GetAndObserveFilters(observerList
, aFilterFrames
);
1355 SVGObserverUtils::ReferenceState
SVGObserverUtils::GetAndObserveFilters(
1356 nsISupports
* aObserverList
, nsTArray
<SVGFilterFrame
*>* aFilterFrames
) {
1357 return mozilla::GetAndObserveFilters(
1358 static_cast<SVGFilterObserverListForCanvasContext
*>(aObserverList
),
1362 SVGObserverUtils::ReferenceState
SVGObserverUtils::GetFiltersIfObserving(
1363 nsIFrame
* aFilteredFrame
, nsTArray
<SVGFilterFrame
*>* aFilterFrames
) {
1364 SVGFilterObserverListForCSSProp
* observerList
=
1365 aFilteredFrame
->GetProperty(FilterProperty());
1366 return mozilla::GetAndObserveFilters(observerList
, aFilterFrames
);
1369 already_AddRefed
<nsISupports
> SVGObserverUtils::ObserveFiltersForCanvasContext(
1370 CanvasRenderingContext2D
* aContext
, Element
* aCanvasElement
,
1371 const Span
<const StyleFilter
> aFilters
) {
1372 return do_AddRef(new SVGFilterObserverListForCanvasContext(
1373 aContext
, aCanvasElement
, aFilters
));
1376 void SVGObserverUtils::DetachFromCanvasContext(nsISupports
* aAutoObserver
) {
1377 static_cast<SVGFilterObserverListForCanvasContext
*>(aAutoObserver
)
1378 ->DetachFromContext();
1381 static SVGPaintingProperty
* GetOrCreateClipPathObserver(
1382 nsIFrame
* aClippedFrame
) {
1383 MOZ_ASSERT(!aClippedFrame
->GetPrevContinuation(),
1384 "Require first continuation");
1386 const nsStyleSVGReset
* svgStyleReset
= aClippedFrame
->StyleSVGReset();
1387 if (!svgStyleReset
->mClipPath
.IsUrl()) {
1390 const auto& url
= svgStyleReset
->mClipPath
.AsUrl();
1391 RefPtr
<URLAndReferrerInfo
> pathURI
=
1392 ResolveURLUsingLocalRef(aClippedFrame
, url
);
1393 return GetPaintingProperty(pathURI
, aClippedFrame
, ClipPathProperty());
1396 SVGObserverUtils::ReferenceState
SVGObserverUtils::GetAndObserveClipPath(
1397 nsIFrame
* aClippedFrame
, SVGClipPathFrame
** aClipPathFrame
) {
1398 if (aClipPathFrame
) {
1399 *aClipPathFrame
= nullptr;
1401 SVGPaintingProperty
* observers
= GetOrCreateClipPathObserver(aClippedFrame
);
1405 bool frameTypeOK
= true;
1406 SVGClipPathFrame
* frame
=
1407 static_cast<SVGClipPathFrame
*>(observers
->GetAndObserveReferencedFrame(
1408 LayoutFrameType::SVGClipPath
, &frameTypeOK
));
1409 // Note that, unlike for filters, a reference to an ID that doesn't exist
1410 // is not invalid for clip-path or mask.
1411 if (!frameTypeOK
|| (frame
&& !frame
->IsValid())) {
1412 return eHasRefsSomeInvalid
;
1414 if (aClipPathFrame
) {
1415 *aClipPathFrame
= frame
;
1417 return frame
? eHasRefsAllValid
: eHasNoRefs
;
1420 static SVGRenderingObserverProperty
* GetOrCreateGeometryObserver(
1422 // Now only offset-path property uses this. See MotionPathUtils.cpp.
1423 const nsStyleDisplay
* disp
= aFrame
->StyleDisplay();
1424 if (!disp
->mOffsetPath
.IsUrl()) {
1427 const auto& url
= disp
->mOffsetPath
.AsUrl();
1428 RefPtr
<URLAndReferrerInfo
> pathURI
= ResolveURLUsingLocalRef(aFrame
, url
);
1429 return GetEffectProperty(pathURI
, aFrame
, OffsetPathProperty());
1432 SVGGeometryElement
* SVGObserverUtils::GetAndObserveGeometry(nsIFrame
* aFrame
) {
1433 SVGRenderingObserverProperty
* observers
= GetOrCreateGeometryObserver(aFrame
);
1438 bool frameTypeOK
= true;
1439 SVGGeometryFrame
* frame
=
1440 do_QueryFrame(observers
->GetAndObserveReferencedFrame(
1441 LayoutFrameType::SVGGeometry
, &frameTypeOK
));
1442 if (!frameTypeOK
|| !frame
) {
1446 return static_cast<dom::SVGGeometryElement
*>(frame
->GetContent());
1449 static SVGMaskObserverList
* GetOrCreateMaskObserverList(
1450 nsIFrame
* aMaskedFrame
) {
1451 MOZ_ASSERT(!aMaskedFrame
->GetPrevContinuation(),
1452 "Require first continuation");
1454 const nsStyleSVGReset
* style
= aMaskedFrame
->StyleSVGReset();
1455 if (!style
->HasMask()) {
1459 MOZ_ASSERT(style
->mMask
.mImageCount
> 0);
1462 SVGMaskObserverList
* prop
= aMaskedFrame
->GetProperty(MaskProperty(), &found
);
1464 MOZ_ASSERT(prop
, "this property should only store non-null values");
1467 prop
= new SVGMaskObserverList(aMaskedFrame
);
1469 aMaskedFrame
->AddProperty(MaskProperty(), prop
);
1473 SVGObserverUtils::ReferenceState
SVGObserverUtils::GetAndObserveMasks(
1474 nsIFrame
* aMaskedFrame
, nsTArray
<SVGMaskFrame
*>* aMaskFrames
) {
1475 SVGMaskObserverList
* observerList
= GetOrCreateMaskObserverList(aMaskedFrame
);
1476 if (!observerList
) {
1480 const nsTArray
<RefPtr
<SVGPaintingProperty
>>& observers
=
1481 observerList
->GetObservers();
1482 if (observers
.IsEmpty()) {
1486 ReferenceState state
= eHasRefsAllValid
;
1488 for (size_t i
= 0; i
< observers
.Length(); i
++) {
1489 bool frameTypeOK
= true;
1490 SVGMaskFrame
* maskFrame
=
1491 static_cast<SVGMaskFrame
*>(observers
[i
]->GetAndObserveReferencedFrame(
1492 LayoutFrameType::SVGMask
, &frameTypeOK
));
1493 MOZ_ASSERT(!maskFrame
|| frameTypeOK
);
1494 // XXXjwatt: this looks fishy
1496 // We can not find the specific SVG mask resource in the downloaded SVG
1497 // document. There are two possibilities:
1498 // 1. The given resource id is invalid.
1499 // 2. The given resource id refers to a viewbox.
1501 // Hand it over to the style image.
1502 observerList
->ResolveImage(i
);
1503 state
= eHasRefsSomeInvalid
;
1506 aMaskFrames
->AppendElement(maskFrame
);
1513 SVGGeometryElement
* SVGObserverUtils::GetAndObserveTextPathsPath(
1514 nsIFrame
* aTextPathFrame
) {
1515 // Continuations can come and go during reflow, and we don't need to observe
1516 // the referenced element more than once for a given node.
1517 aTextPathFrame
= aTextPathFrame
->FirstContinuation();
1519 SVGTextPathObserver
* property
=
1520 aTextPathFrame
->GetProperty(HrefAsTextPathProperty());
1523 nsIContent
* content
= aTextPathFrame
->GetContent();
1525 static_cast<SVGTextPathElement
*>(content
)->HrefAsString(href
);
1526 if (href
.IsEmpty()) {
1527 return nullptr; // no URL
1530 // There's no clear refererer policy spec about non-CSS SVG resource
1531 // references Bug 1415044 to investigate which referrer we should use
1532 nsIReferrerInfo
* referrerInfo
=
1533 content
->OwnerDoc()->ReferrerInfoForInternalCSSAndSVGResources();
1534 RefPtr
<URLAndReferrerInfo
> target
=
1535 ResolveURLUsingLocalRef(aTextPathFrame
, href
, referrerInfo
);
1538 GetEffectProperty(target
, aTextPathFrame
, HrefAsTextPathProperty());
1544 return SVGGeometryElement::FromNodeOrNull(
1545 property
->GetAndObserveReferencedElement());
1548 SVGGeometryElement
* SVGObserverUtils::GetAndObserveMPathsPath(
1549 SVGMPathElement
* aSVGMPathElement
) {
1550 if (!aSVGMPathElement
->mMPathObserver
) {
1552 aSVGMPathElement
->HrefAsString(href
);
1553 if (href
.IsEmpty()) {
1554 return nullptr; // no URL
1557 nsIReferrerInfo
* referrerInfo
=
1558 aSVGMPathElement
->OwnerDoc()
1559 ->ReferrerInfoForInternalCSSAndSVGResources();
1561 RefPtr
<URLAndReferrerInfo
> target
=
1562 ResolveURLUsingLocalRef(aSVGMPathElement
, href
, referrerInfo
);
1564 aSVGMPathElement
->mMPathObserver
=
1565 new SVGMPathObserver(target
, aSVGMPathElement
);
1568 return SVGGeometryElement::FromNodeOrNull(
1569 static_cast<SVGMPathObserver
*>(aSVGMPathElement
->mMPathObserver
.get())
1570 ->GetAndObserveReferencedElement());
1573 void SVGObserverUtils::TraverseMPathObserver(
1574 SVGMPathElement
* aSVGMPathElement
,
1575 nsCycleCollectionTraversalCallback
* aCB
) {
1576 if (aSVGMPathElement
->mMPathObserver
) {
1577 static_cast<SVGMPathObserver
*>(aSVGMPathElement
->mMPathObserver
.get())
1582 void SVGObserverUtils::InitiateResourceDocLoads(nsIFrame
* aFrame
) {
1583 // We create observer objects and attach them to aFrame, but we do not
1584 // make aFrame start observing the referenced frames.
1585 Unused
<< GetOrCreateFilterObserverListForCSS(aFrame
);
1586 Unused
<< GetOrCreateClipPathObserver(aFrame
);
1587 Unused
<< GetOrCreateGeometryObserver(aFrame
);
1588 Unused
<< GetOrCreateMaskObserverList(aFrame
);
1591 void SVGObserverUtils::RemoveTextPathObserver(nsIFrame
* aTextPathFrame
) {
1592 aTextPathFrame
->RemoveProperty(HrefAsTextPathProperty());
1595 nsIFrame
* SVGObserverUtils::GetAndObserveTemplate(
1596 nsIFrame
* aFrame
, HrefToTemplateCallback aGetHref
) {
1597 SVGTemplateElementObserver
* observer
=
1598 aFrame
->GetProperty(HrefToTemplateProperty());
1603 if (href
.IsEmpty()) {
1604 return nullptr; // no URL
1607 // Convert href to an nsIURI
1608 nsIContent
* content
= aFrame
->GetContent();
1610 nsCOMPtr
<nsIURI
> baseURI
= content
->GetBaseURI();
1611 if (nsContentUtils::IsLocalRefURL(href
)) {
1612 baseURI
= GetBaseURLForLocalRef(content
, baseURI
);
1614 nsCOMPtr
<nsIURI
> targetURI
;
1615 nsContentUtils::NewURIWithDocumentCharset(
1616 getter_AddRefs(targetURI
), href
, content
->GetUncomposedDoc(), baseURI
);
1618 // There's no clear refererer policy spec about non-CSS SVG resource
1619 // references. Bug 1415044 to investigate which referrer we should use.
1620 nsIReferrerInfo
* referrerInfo
=
1621 content
->OwnerDoc()->ReferrerInfoForInternalCSSAndSVGResources();
1622 RefPtr
<URLAndReferrerInfo
> target
=
1623 new URLAndReferrerInfo(targetURI
, referrerInfo
);
1625 observer
= GetEffectProperty(target
, aFrame
, HrefToTemplateProperty());
1628 return observer
? observer
->GetAndObserveReferencedFrame() : nullptr;
1631 void SVGObserverUtils::RemoveTemplateObserver(nsIFrame
* aFrame
) {
1632 aFrame
->RemoveProperty(HrefToTemplateProperty());
1635 Element
* SVGObserverUtils::GetAndObserveBackgroundImage(nsIFrame
* aFrame
,
1636 const nsAtom
* aHref
) {
1638 URIObserverHashtable
* hashtable
=
1639 aFrame
->GetProperty(BackgroundImageProperty(), &found
);
1641 hashtable
= new URIObserverHashtable();
1642 aFrame
->AddProperty(BackgroundImageProperty(), hashtable
);
1644 MOZ_ASSERT(hashtable
, "this property should only store non-null values");
1647 nsAutoString elementId
= u
"#"_ns
+ nsDependentAtomString(aHref
);
1648 nsCOMPtr
<nsIURI
> targetURI
;
1649 nsContentUtils::NewURIWithDocumentCharset(
1650 getter_AddRefs(targetURI
), elementId
,
1651 aFrame
->GetContent()->GetUncomposedDoc(),
1652 aFrame
->GetContent()->GetBaseURI());
1653 nsIReferrerInfo
* referrerInfo
=
1654 aFrame
->GetContent()
1656 ->ReferrerInfoForInternalCSSAndSVGResources();
1657 RefPtr
<URLAndReferrerInfo
> url
=
1658 new URLAndReferrerInfo(targetURI
, referrerInfo
);
1660 return static_cast<SVGMozElementObserver
*>(
1662 ->LookupOrInsertWith(
1665 return MakeRefPtr
<SVGMozElementObserver
>(url
, aFrame
);
1668 ->GetAndObserveReferencedElement();
1671 Element
* SVGObserverUtils::GetAndObserveBackgroundClip(nsIFrame
* aFrame
) {
1673 BackgroundClipRenderingObserver
* obs
=
1674 aFrame
->GetProperty(BackgroundClipObserverProperty(), &found
);
1676 obs
= new BackgroundClipRenderingObserver(aFrame
);
1678 aFrame
->AddProperty(BackgroundClipObserverProperty(), obs
);
1681 return obs
->GetAndObserveReferencedElement();
1684 SVGPaintServerFrame
* SVGObserverUtils::GetAndObservePaintServer(
1685 nsIFrame
* aPaintedFrame
, StyleSVGPaint
nsStyleSVG::*aPaint
) {
1686 // If we're looking at a frame within SVG text, then we need to look up
1687 // to find the right frame to get the painting property off. We should at
1688 // least look up past a text frame, and if the text frame's parent is the
1689 // anonymous block frame, then we look up to its parent (the SVGTextFrame).
1690 nsIFrame
* paintedFrame
= aPaintedFrame
;
1691 if (paintedFrame
->IsInSVGTextSubtree()) {
1692 paintedFrame
= paintedFrame
->GetParent();
1693 nsIFrame
* grandparent
= paintedFrame
->GetParent();
1694 if (grandparent
&& grandparent
->IsSVGTextFrame()) {
1695 paintedFrame
= grandparent
;
1699 const nsStyleSVG
* svgStyle
= paintedFrame
->StyleSVG();
1700 if (!(svgStyle
->*aPaint
).kind
.IsPaintServer()) {
1704 RefPtr
<URLAndReferrerInfo
> paintServerURL
= ResolveURLUsingLocalRef(
1705 paintedFrame
, (svgStyle
->*aPaint
).kind
.AsPaintServer());
1707 MOZ_ASSERT(aPaint
== &nsStyleSVG::mFill
|| aPaint
== &nsStyleSVG::mStroke
);
1708 PaintingPropertyDescriptor propDesc
=
1709 (aPaint
== &nsStyleSVG::mFill
) ? FillProperty() : StrokeProperty();
1710 if (auto* property
=
1711 GetPaintingProperty(paintServerURL
, paintedFrame
, propDesc
)) {
1712 return do_QueryFrame(property
->GetAndObserveReferencedFrame());
1717 void SVGObserverUtils::UpdateEffects(nsIFrame
* aFrame
) {
1718 NS_ASSERTION(aFrame
->GetContent()->IsElement(),
1719 "aFrame's content should be an element");
1721 aFrame
->RemoveProperty(FilterProperty());
1722 aFrame
->RemoveProperty(MaskProperty());
1723 aFrame
->RemoveProperty(ClipPathProperty());
1724 aFrame
->RemoveProperty(MarkerStartProperty());
1725 aFrame
->RemoveProperty(MarkerMidProperty());
1726 aFrame
->RemoveProperty(MarkerEndProperty());
1727 aFrame
->RemoveProperty(FillProperty());
1728 aFrame
->RemoveProperty(StrokeProperty());
1729 aFrame
->RemoveProperty(BackgroundImageProperty());
1731 // Ensure that the filter is repainted correctly
1732 // We can't do that in OnRenderingChange as the referenced frame may
1734 GetOrCreateFilterObserverListForCSS(aFrame
);
1736 if (aFrame
->IsSVGGeometryFrame() &&
1737 static_cast<SVGGeometryElement
*>(aFrame
->GetContent())->IsMarkable()) {
1738 // Set marker properties here to avoid reference loops
1739 RefPtr
<URLAndReferrerInfo
> markerURL
=
1740 GetMarkerURI(aFrame
, &nsStyleSVG::mMarkerStart
);
1741 GetEffectProperty(markerURL
, aFrame
, MarkerStartProperty());
1742 markerURL
= GetMarkerURI(aFrame
, &nsStyleSVG::mMarkerMid
);
1743 GetEffectProperty(markerURL
, aFrame
, MarkerMidProperty());
1744 markerURL
= GetMarkerURI(aFrame
, &nsStyleSVG::mMarkerEnd
);
1745 GetEffectProperty(markerURL
, aFrame
, MarkerEndProperty());
1749 void SVGObserverUtils::AddRenderingObserver(Element
* aElement
,
1750 SVGRenderingObserver
* aObserver
) {
1751 SVGRenderingObserverSet
* observers
= GetObserverSet(aElement
);
1753 observers
= new SVGRenderingObserverSet();
1754 aElement
->SetProperty(nsGkAtoms::renderingobserverset
, observers
,
1755 nsINode::DeleteProperty
<SVGRenderingObserverSet
>);
1757 aElement
->SetHasRenderingObservers(true);
1758 observers
->Add(aObserver
);
1761 void SVGObserverUtils::RemoveRenderingObserver(
1762 Element
* aElement
, SVGRenderingObserver
* aObserver
) {
1763 SVGRenderingObserverSet
* observers
= GetObserverSet(aElement
);
1765 NS_ASSERTION(observers
->Contains(aObserver
),
1766 "removing observer from an element we're not observing?");
1767 observers
->Remove(aObserver
);
1768 if (observers
->IsEmpty()) {
1769 aElement
->RemoveProperty(nsGkAtoms::renderingobserverset
);
1770 aElement
->SetHasRenderingObservers(false);
1775 void SVGObserverUtils::RemoveAllRenderingObservers(Element
* aElement
) {
1776 SVGRenderingObserverSet
* observers
= GetObserverSet(aElement
);
1778 observers
->RemoveAll();
1779 aElement
->RemoveProperty(nsGkAtoms::renderingobserverset
);
1780 aElement
->SetHasRenderingObservers(false);
1784 void SVGObserverUtils::InvalidateRenderingObservers(nsIFrame
* aFrame
) {
1785 NS_ASSERTION(!aFrame
->GetPrevContinuation(),
1786 "aFrame must be first continuation");
1788 auto* element
= Element::FromNodeOrNull(aFrame
->GetContent());
1793 // If the rendering has changed, the bounds may well have changed too:
1794 aFrame
->RemoveProperty(SVGUtils::ObjectBoundingBoxProperty());
1796 if (auto* observers
= GetObserverSet(element
)) {
1797 observers
->InvalidateAll();
1801 if (aFrame
->IsRenderingObserverContainer()) {
1805 // Check ancestor SVG containers. The root frame cannot be of type
1806 // eSVGContainer so we don't have to check f for null here.
1807 for (nsIFrame
* f
= aFrame
->GetParent();
1808 f
->IsFrameOfType(nsIFrame::eSVGContainer
); f
= f
->GetParent()) {
1809 if (auto* element
= Element::FromNode(f
->GetContent())) {
1810 if (auto* observers
= GetObserverSet(element
)) {
1811 observers
->InvalidateAll();
1815 if (f
->IsRenderingObserverContainer()) {
1821 void SVGObserverUtils::InvalidateDirectRenderingObservers(
1822 Element
* aElement
, uint32_t aFlags
/* = 0 */) {
1823 if (nsIFrame
* frame
= aElement
->GetPrimaryFrame()) {
1824 // If the rendering has changed, the bounds may well have changed too:
1825 frame
->RemoveProperty(SVGUtils::ObjectBoundingBoxProperty());
1828 if (aElement
->HasRenderingObservers()) {
1829 SVGRenderingObserverSet
* observers
= GetObserverSet(aElement
);
1831 if (aFlags
& INVALIDATE_REFLOW
) {
1832 observers
->InvalidateAllForReflow();
1834 observers
->InvalidateAll();
1840 void SVGObserverUtils::InvalidateDirectRenderingObservers(
1841 nsIFrame
* aFrame
, uint32_t aFlags
/* = 0 */) {
1842 if (auto* element
= Element::FromNodeOrNull(aFrame
->GetContent())) {
1843 InvalidateDirectRenderingObservers(element
, aFlags
);
1847 already_AddRefed
<nsIURI
> SVGObserverUtils::GetBaseURLForLocalRef(
1848 nsIContent
* content
, nsIURI
* aDocURI
) {
1849 MOZ_ASSERT(content
);
1851 // Content is in a shadow tree. If this URL was specified in the subtree
1852 // referenced by the <use>, element, and that subtree came from a separate
1853 // resource document, then we want the fragment-only URL to resolve to an
1854 // element from the resource document. Otherwise, the URL was specified
1855 // somewhere in the document with the <use> element, and we want the
1856 // fragment-only URL to resolve to an element in that document.
1857 if (SVGUseElement
* use
= content
->GetContainingSVGUseShadowHost()) {
1858 if (nsIURI
* originalURI
= use
->GetSourceDocURI()) {
1859 bool isEqualsExceptRef
= false;
1860 aDocURI
->EqualsExceptRef(originalURI
, &isEqualsExceptRef
);
1861 if (isEqualsExceptRef
) {
1862 return do_AddRef(originalURI
);
1867 // For a local-reference URL, resolve that fragment against the current
1868 // document that relative URLs are resolved against.
1869 return do_AddRef(content
->OwnerDoc()->GetDocumentURI());
1872 already_AddRefed
<URLAndReferrerInfo
> SVGObserverUtils::GetFilterURI(
1873 nsIFrame
* aFrame
, const StyleFilter
& aFilter
) {
1874 MOZ_ASSERT(!aFrame
->StyleEffects()->mFilters
.IsEmpty() ||
1875 !aFrame
->StyleEffects()->mBackdropFilters
.IsEmpty() ||
1876 !aFrame
->GetContent()->GetParent());
1877 MOZ_ASSERT(aFilter
.IsUrl());
1878 return ResolveURLUsingLocalRef(aFrame
, aFilter
.AsUrl());
1881 } // namespace mozilla